|
13 | 13 | * |
14 | 14 | * * **Synchronous accessors** (``getStorageMode`` etc.) are served from a |
15 | 15 | * locally-cached {@link KernelMirror} that the worker pushes on change. |
16 | | - * * **Callback registrars** (``setDialogHandler``, ``onWorkspaceMutation``) |
17 | | - * keep the JS callback on the main thread and bridge the worker's events |
18 | | - * to it — a function can't cross ``postMessage``. |
| 16 | + * * **Callback registrars** (``setDialogHandler``, ``onWorkspaceMutation``, |
| 17 | + * ``onStorageError``) keep the JS callback on the main thread and bridge |
| 18 | + * the worker's events to it — a function can't cross ``postMessage``. |
19 | 19 | * * Every other method is forwarded generically through a ``Proxy`` trap, |
20 | 20 | * so the ~150-method surface needs no hand-written forwarders and any |
21 | 21 | * new ``RuntimeApi`` method works automatically. |
@@ -65,6 +65,7 @@ export class WorkerRuntimeProxy { |
65 | 65 | private readonly pending = new Map<number, PendingCall>(); |
66 | 66 | private mirror: KernelMirror = INITIAL_MIRROR; |
67 | 67 | private readonly mutationListeners = new Set<(name: string) => void>(); |
| 68 | + private readonly storageErrorListeners = new Set<(message: string) => void>(); |
68 | 69 | private dialogHandler: |
69 | 70 | ((kind: string, payload: unknown) => Promise<unknown>) | null = null; |
70 | 71 |
|
@@ -173,14 +174,18 @@ export class WorkerRuntimeProxy { |
173 | 174 | } |
174 | 175 | if (prop === "onWorkspaceMutation") { |
175 | 176 | return (listener: (name: string) => void) => |
176 | | - this.addMutationListener(listener); |
| 177 | + this.addListener(this.mutationListeners, listener); |
| 178 | + } |
| 179 | + if (prop === "onStorageError") { |
| 180 | + return (listener: (message: string) => void) => |
| 181 | + this.addListener(this.storageErrorListeners, listener); |
177 | 182 | } |
178 | 183 | // Generic async forward for every other method. |
179 | 184 | return (...args: unknown[]) => this.call(prop, args); |
180 | 185 | }, |
181 | 186 | }; |
182 | 187 | // The Proxy structurally satisfies RuntimeApi: every access yields a |
183 | | - // function with the right shape (sync accessors, the two callback |
| 188 | + // function with the right shape (sync accessors, the callback |
184 | 189 | // registrars, or a generic async forward). |
185 | 190 | return new Proxy({}, handler) as unknown as RuntimeApi; |
186 | 191 | } |
@@ -219,13 +224,31 @@ export class WorkerRuntimeProxy { |
219 | 224 | }); |
220 | 225 | } |
221 | 226 |
|
222 | | - private addMutationListener(listener: (name: string) => void): () => void { |
223 | | - this.mutationListeners.add(listener); |
| 227 | + private addListener<T>( |
| 228 | + listeners: Set<(value: T) => void>, |
| 229 | + listener: (value: T) => void, |
| 230 | + ): () => void { |
| 231 | + listeners.add(listener); |
224 | 232 | return () => { |
225 | | - this.mutationListeners.delete(listener); |
| 233 | + listeners.delete(listener); |
226 | 234 | }; |
227 | 235 | } |
228 | 236 |
|
| 237 | + /** Call every listener of *listeners* with *value*, logging their errors. */ |
| 238 | + private notify<T>( |
| 239 | + listeners: Set<(value: T) => void>, |
| 240 | + value: T, |
| 241 | + kind: string, |
| 242 | + ): void { |
| 243 | + for (const cb of listeners) { |
| 244 | + try { |
| 245 | + cb(value); |
| 246 | + } catch (err) { |
| 247 | + console.warn(`[workerRuntime] ${kind} listener threw`, err); |
| 248 | + } |
| 249 | + } |
| 250 | + } |
| 251 | + |
229 | 252 | private handleEvent(event: KernelEvent): void { |
230 | 253 | switch (event.type) { |
231 | 254 | case "progress": |
@@ -255,13 +278,10 @@ export class WorkerRuntimeProxy { |
255 | 278 | break; |
256 | 279 | } |
257 | 280 | case "mutation": |
258 | | - for (const cb of this.mutationListeners) { |
259 | | - try { |
260 | | - cb(event.name); |
261 | | - } catch (err) { |
262 | | - console.warn("[workerRuntime] mutation listener threw", err); |
263 | | - } |
264 | | - } |
| 281 | + this.notify(this.mutationListeners, event.name, "mutation"); |
| 282 | + break; |
| 283 | + case "storage-error": |
| 284 | + this.notify(this.storageErrorListeners, event.message, "storage-error"); |
265 | 285 | break; |
266 | 286 | case "dialog-request": |
267 | 287 | void this.handleDialogRequest(event.id, event.kind, event.payload); |
|
0 commit comments