import * as Context from "effect/Context"; import * as Data from "effect/Data"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Scope from "effect/Scope"; import { autoUpdater } from "electron-updater"; type AutoUpdater = typeof autoUpdater; export type ElectronUpdaterFeedUrl = Parameters[0]; export class ElectronUpdaterCheckForUpdatesError extends Data.TaggedError( "ElectronUpdaterCheckForUpdatesError", )<{ readonly cause: unknown; }> { override get message() { return "Electron updater failed to check for updates."; } } export class ElectronUpdaterDownloadUpdateError extends Data.TaggedError( "ElectronUpdaterDownloadUpdateError", )<{ readonly cause: unknown; }> { override get message() { return "Electron updater failed to download the update."; } } export class ElectronUpdaterQuitAndInstallError extends Data.TaggedError( "ElectronUpdaterQuitAndInstallError", )<{ readonly cause: unknown; }> { override get message() { return "Electron updater failed to quit and install the update."; } } export type ElectronUpdaterError = | ElectronUpdaterCheckForUpdatesError | ElectronUpdaterDownloadUpdateError | ElectronUpdaterQuitAndInstallError; export interface ElectronUpdaterShape { readonly setFeedURL: (options: ElectronUpdaterFeedUrl) => Effect.Effect; readonly setAutoDownload: (value: boolean) => Effect.Effect; readonly setAutoInstallOnAppQuit: (value: boolean) => Effect.Effect; readonly setChannel: (channel: string) => Effect.Effect; readonly setAllowPrerelease: (value: boolean) => Effect.Effect; readonly allowDowngrade: Effect.Effect; readonly setAllowDowngrade: (value: boolean) => Effect.Effect; readonly setDisableDifferentialDownload: (value: boolean) => Effect.Effect; readonly checkForUpdates: Effect.Effect; readonly downloadUpdate: Effect.Effect; readonly quitAndInstall: (options: { readonly isSilent: boolean; readonly isForceRunAfter: boolean; }) => Effect.Effect; readonly on: >( eventName: string, listener: (...args: Args) => void, ) => Effect.Effect; } export class ElectronUpdater extends Context.Service()( "t3/desktop/electron/Updater", ) {} export const layer = Layer.succeed(ElectronUpdater, { setFeedURL: (options) => Effect.suspend(() => { autoUpdater.setFeedURL(options); return Effect.void; }), setAutoDownload: (value) => Effect.suspend(() => { autoUpdater.autoDownload = value; return Effect.void; }), setAutoInstallOnAppQuit: (value) => Effect.suspend(() => { autoUpdater.autoInstallOnAppQuit = value; return Effect.void; }), setChannel: (channel) => Effect.suspend(() => { autoUpdater.channel = channel; return Effect.void; }), setAllowPrerelease: (value) => Effect.suspend(() => { autoUpdater.allowPrerelease = value; return Effect.void; }), allowDowngrade: Effect.sync(() => autoUpdater.allowDowngrade), setAllowDowngrade: (value) => Effect.suspend(() => { autoUpdater.allowDowngrade = value; return Effect.void; }), setDisableDifferentialDownload: (value) => Effect.suspend(() => { autoUpdater.disableDifferentialDownload = value; return Effect.void; }), checkForUpdates: Effect.tryPromise({ try: () => autoUpdater.checkForUpdates(), catch: (cause) => new ElectronUpdaterCheckForUpdatesError({ cause }), }).pipe(Effect.asVoid), downloadUpdate: Effect.tryPromise({ try: () => autoUpdater.downloadUpdate(), catch: (cause) => new ElectronUpdaterDownloadUpdateError({ cause }), }).pipe(Effect.asVoid), quitAndInstall: ({ isSilent, isForceRunAfter }) => Effect.try({ try: () => autoUpdater.quitAndInstall(isSilent, isForceRunAfter), catch: (cause) => new ElectronUpdaterQuitAndInstallError({ cause }), }), on: (eventName, listener) => { const eventTarget = autoUpdater as unknown as { on: (eventName: string, listener: (...args: Array) => void) => void; removeListener: (eventName: string, listener: (...args: Array) => void) => void; }; const untypedListener = listener as unknown as (...args: Array) => void; return Effect.acquireRelease( Effect.sync(() => { eventTarget.on(eventName, untypedListener); }), () => Effect.sync(() => { eventTarget.removeListener(eventName, untypedListener); }), ).pipe(Effect.asVoid); }, } satisfies ElectronUpdaterShape);