| ... | @@ -24,6 +24,39 @@ export interface DesktopIpcMain { | ... | @@ -24,6 +24,39 @@ export interface DesktopIpcMain { |
| 24 | on(channel: string, listener: DesktopIpcSyncListener): void; | 24 | on(channel: string, listener: DesktopIpcSyncListener): void; |
| 25 | } | 25 | } |
| 26 | | 26 | |
| | 27 | export class DesktopIpcRegistrationError extends Schema.TaggedErrorClass<DesktopIpcRegistrationError>()( |
| | 28 | "DesktopIpcRegistrationError", |
| | 29 | { |
| | 30 | handlerKind: Schema.Literals(["invoke", "sync"]), |
| | 31 | channel: Schema.String, |
| | 32 | cause: Schema.Defect(), |
| | 33 | }, |
| | 34 | ) { |
| | 35 | override get message(): string { |
| | 36 | return `Failed to register the ${this.handlerKind} IPC handler for ${this.channel}.`; |
| | 37 | } |
| | 38 | } |
| | 39 | |
| | 40 | export class DesktopIpcUnregistrationError extends Schema.TaggedErrorClass<DesktopIpcUnregistrationError>()( |
| | 41 | "DesktopIpcUnregistrationError", |
| | 42 | { |
| | 43 | handlerKind: Schema.Literals(["invoke", "sync"]), |
| | 44 | channel: Schema.String, |
| | 45 | cause: Schema.Defect(), |
| | 46 | }, |
| | 47 | ) { |
| | 48 | override get message(): string { |
| | 49 | return `Failed to unregister the ${this.handlerKind} IPC handler for ${this.channel}.`; |
| | 50 | } |
| | 51 | } |
| | 52 | |
| | 53 | export const DesktopIpcError = Schema.Union([ |
| | 54 | DesktopIpcRegistrationError, |
| | 55 | DesktopIpcUnregistrationError, |
| | 56 | ]); |
| | 57 | export type DesktopIpcError = typeof DesktopIpcError.Type; |
| | 58 | export const isDesktopIpcError = Schema.is(DesktopIpcError); |
| | 59 | |
| 27 | export interface DesktopIpcMethod<E, R> { | 60 | export interface DesktopIpcMethod<E, R> { |
| 28 | readonly channel: string; | 61 | readonly channel: string; |
| 29 | readonly handler: (raw: unknown) => Effect.Effect<unknown, E, R>; | 62 | readonly handler: (raw: unknown) => Effect.Effect<unknown, E, R>; |
| ... | @@ -39,10 +72,10 @@ export class DesktopIpc extends Context.Service< | ... | @@ -39,10 +72,10 @@ export class DesktopIpc extends Context.Service< |
| 39 | { | 72 | { |
| 40 | readonly handle: <E, R>( | 73 | readonly handle: <E, R>( |
| 41 | input: DesktopIpcMethod<E, R>, | 74 | input: DesktopIpcMethod<E, R>, |
| 42 | ) => Effect.Effect<void, never, R | Scope.Scope>; | 75 | ) => Effect.Effect<void, DesktopIpcRegistrationError, R | Scope.Scope>; |
| 43 | readonly handleSync: <E, R>( | 76 | readonly handleSync: <E, R>( |
| 44 | input: DesktopSyncIpcMethod<E, R>, | 77 | input: DesktopSyncIpcMethod<E, R>, |
| 45 | ) => Effect.Effect<void, never, R | Scope.Scope>; | 78 | ) => Effect.Effect<void, DesktopIpcRegistrationError, R | Scope.Scope>; |
| 46 | } | 79 | } |
| 47 | >()("@t3tools/desktop/ipc/DesktopIpc") {} | 80 | >()("@t3tools/desktop/ipc/DesktopIpc") {} |
| 48 | | 81 | |
| ... | @@ -57,18 +90,27 @@ export const make = (ipcMain: DesktopIpcMain): DesktopIpc["Service"] => | ... | @@ -57,18 +90,27 @@ export const make = (ipcMain: DesktopIpcMain): DesktopIpc["Service"] => |
| 57 | const runPromise = Effect.runPromiseWith(context); | 90 | const runPromise = Effect.runPromiseWith(context); |
| 58 | | 91 | |
| 59 | yield* Effect.acquireRelease( | 92 | yield* Effect.acquireRelease( |
| 60 | Effect.sync(() => { | 93 | Effect.try({ |
| 61 | ipcMain.removeHandler(channel); | 94 | try: () => { |
| 62 | ipcMain.handle(channel, (_event, raw) => | 95 | ipcMain.removeHandler(channel); |
| 63 | runPromise( | 96 | ipcMain.handle(channel, (_event, raw) => |
| 64 | Effect.gen(function* () { | 97 | runPromise( |
| 65 | yield* Effect.annotateCurrentSpan({ channel }); | 98 | Effect.gen(function* () { |
| 66 | return yield* handler(raw); | 99 | yield* Effect.annotateCurrentSpan({ channel }); |
| 67 | }).pipe(Effect.annotateLogs({ channel }), Effect.withSpan("desktop.ipc.invoke")), | 100 | return yield* handler(raw); |
| 68 | ), | 101 | }).pipe(Effect.annotateLogs({ channel }), Effect.withSpan("desktop.ipc.invoke")), |
| 69 | ); | 102 | ), |
| | 103 | ); |
| | 104 | }, |
| | 105 | catch: (cause) => |
| | 106 | new DesktopIpcRegistrationError({ handlerKind: "invoke", channel, cause }), |
| 70 | }), | 107 | }), |
| 71 | () => Effect.sync(() => ipcMain.removeHandler(channel)), | 108 | () => |
| | 109 | Effect.try({ |
| | 110 | try: () => ipcMain.removeHandler(channel), |
| | 111 | catch: (cause) => |
| | 112 | new DesktopIpcUnregistrationError({ handlerKind: "invoke", channel, cause }), |
| | 113 | }).pipe(Effect.orDie), |
| 72 | ); | 114 | ); |
| 73 | }), | 115 | }), |
| 74 | | 116 | |
| ... | @@ -81,18 +123,30 @@ export const make = (ipcMain: DesktopIpcMain): DesktopIpc["Service"] => | ... | @@ -81,18 +123,30 @@ export const make = (ipcMain: DesktopIpcMain): DesktopIpc["Service"] => |
| 81 | const runSync = Effect.runSyncWith(context); | 123 | const runSync = Effect.runSyncWith(context); |
| 82 | | 124 | |
| 83 | yield* Effect.acquireRelease( | 125 | yield* Effect.acquireRelease( |
| 84 | Effect.sync(() => { | 126 | Effect.try({ |
| 85 | ipcMain.removeAllListeners(channel); | 127 | try: () => { |
| 86 | ipcMain.on(channel, (event) => { | 128 | ipcMain.removeAllListeners(channel); |
| 87 | event.returnValue = runSync( | 129 | ipcMain.on(channel, (event) => { |
| 88 | Effect.gen(function* () { | 130 | event.returnValue = runSync( |
| 89 | yield* Effect.annotateCurrentSpan({ channel }); | 131 | Effect.gen(function* () { |
| 90 | return yield* handler(); | 132 | yield* Effect.annotateCurrentSpan({ channel }); |
| 91 | }).pipe(Effect.annotateLogs({ channel }), Effect.withSpan("desktop.ipc.invokeSync")), | 133 | return yield* handler(); |
| 92 | ); | 134 | }).pipe( |
| 93 | }); | 135 | Effect.annotateLogs({ channel }), |
| | 136 | Effect.withSpan("desktop.ipc.invokeSync"), |
| | 137 | ), |
| | 138 | ); |
| | 139 | }); |
| | 140 | }, |
| | 141 | catch: (cause) => |
| | 142 | new DesktopIpcRegistrationError({ handlerKind: "sync", channel, cause }), |
| 94 | }), | 143 | }), |
| 95 | () => Effect.sync(() => ipcMain.removeAllListeners(channel)), | 144 | () => |
| | 145 | Effect.try({ |
| | 146 | try: () => ipcMain.removeAllListeners(channel), |
| | 147 | catch: (cause) => |
| | 148 | new DesktopIpcUnregistrationError({ handlerKind: "sync", channel, cause }), |
| | 149 | }).pipe(Effect.orDie), |
| 96 | ); | 150 | ); |
| 97 | }), | 151 | }), |
| 98 | }); | 152 | }); |