import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; import * as Electron from "electron"; import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; const electronSafeStorageErrorFields = { cause: Schema.Defect(), }; export class ElectronSafeStorageAvailabilityError extends Schema.TaggedErrorClass()( "ElectronSafeStorageAvailabilityError", { ...electronSafeStorageErrorFields, }, ) { override get message(): string { return "Electron safe storage failed to check encryption availability."; } } export class ElectronSafeStorageEncryptError extends Schema.TaggedErrorClass()( "ElectronSafeStorageEncryptError", { ...electronSafeStorageErrorFields, }, ) { override get message(): string { return "Electron safe storage failed to encrypt a string."; } } export class ElectronSafeStorageDecryptError extends Schema.TaggedErrorClass()( "ElectronSafeStorageDecryptError", { ...electronSafeStorageErrorFields, }, ) { override get message(): string { return "Electron safe storage failed to decrypt a string."; } } export const ElectronSafeStorageError = Schema.Union([ ElectronSafeStorageAvailabilityError, ElectronSafeStorageEncryptError, ElectronSafeStorageDecryptError, ]); export type ElectronSafeStorageError = typeof ElectronSafeStorageError.Type; export const isElectronSafeStorageError = Schema.is(ElectronSafeStorageError); export class ElectronSafeStorage extends Context.Service< ElectronSafeStorage, { readonly isEncryptionAvailable: Effect.Effect; readonly encryptString: ( value: string, ) => Effect.Effect; readonly decryptString: ( value: Uint8Array, ) => Effect.Effect; readonly selectedStorageBackend: Effect.Effect>; } >()("@t3tools/desktop/electron/ElectronSafeStorage") {} export const make = Effect.gen(function* () { const platform = yield* HostProcessPlatform; return ElectronSafeStorage.of({ isEncryptionAvailable: Effect.try({ try: () => Electron.safeStorage.isEncryptionAvailable(), catch: (cause) => new ElectronSafeStorageAvailabilityError({ cause }), }), encryptString: (value) => Effect.try({ try: () => Electron.safeStorage.encryptString(value), catch: (cause) => new ElectronSafeStorageEncryptError({ cause }), }), decryptString: (value) => Effect.try({ try: () => Electron.safeStorage.decryptString(Buffer.from(value)), catch: (cause) => new ElectronSafeStorageDecryptError({ cause }), }), selectedStorageBackend: Effect.sync(() => { if (platform !== "linux") { return Option.none(); } try { return Option.fromNullishOr(Electron.safeStorage.getSelectedStorageBackend()); } catch { return Option.none(); } }), }); }); export const layer = Layer.effect(ElectronSafeStorage, make);