@nimbus-sh/fabric 0.7.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +26 -11
  2. package/dist/bindings.d.ts +1 -0
  3. package/dist/bindings.d.ts.map +1 -1
  4. package/dist/bindings.js +2 -0
  5. package/dist/budgets.d.ts +50 -29
  6. package/dist/budgets.d.ts.map +1 -1
  7. package/dist/budgets.js +88 -40
  8. package/dist/connections.d.ts +1 -1
  9. package/dist/connections.js +1 -1
  10. package/dist/do-calls.d.ts +91 -9
  11. package/dist/do-calls.d.ts.map +1 -1
  12. package/dist/do-calls.js +161 -22
  13. package/dist/facet-pool.d.ts +3 -0
  14. package/dist/facet-pool.d.ts.map +1 -1
  15. package/dist/facet-pool.js +3 -0
  16. package/dist/fanout.d.ts +40 -32
  17. package/dist/fanout.d.ts.map +1 -1
  18. package/dist/fanout.js +50 -53
  19. package/dist/fenced-work.d.ts +3 -3
  20. package/dist/host-wasm.d.ts +29 -0
  21. package/dist/host-wasm.d.ts.map +1 -0
  22. package/dist/host-wasm.js +31 -0
  23. package/dist/image-store.d.ts +1 -1
  24. package/dist/image-store.d.ts.map +1 -1
  25. package/dist/image-store.js +34 -2
  26. package/dist/inner-do-registry.d.ts +9 -0
  27. package/dist/inner-do-registry.d.ts.map +1 -1
  28. package/dist/inner-do-registry.js +35 -0
  29. package/dist/isolate-pool.d.ts +32 -24
  30. package/dist/isolate-pool.d.ts.map +1 -1
  31. package/dist/isolate-pool.js +78 -54
  32. package/dist/process-fabric.d.ts +42 -23
  33. package/dist/process-fabric.d.ts.map +1 -1
  34. package/dist/process-fabric.js +62 -6
  35. package/dist/process-host.d.ts +3 -1
  36. package/dist/process-host.d.ts.map +1 -1
  37. package/dist/process-host.js +13 -14
  38. package/dist/supervisor-props.d.ts +47 -0
  39. package/dist/supervisor-props.d.ts.map +1 -0
  40. package/dist/supervisor-props.js +37 -0
  41. package/dist/timers.d.ts +12 -0
  42. package/dist/timers.d.ts.map +1 -1
  43. package/dist/timers.js +44 -9
  44. package/dist/vendor/types.d.ts +11 -5
  45. package/dist/vendor/types.d.ts.map +1 -1
  46. package/dist/workerd-facet-host.d.ts +4 -17
  47. package/dist/workerd-facet-host.d.ts.map +1 -1
  48. package/dist/workerd-facet-host.js +120 -54
  49. package/package.json +6 -6
  50. package/src/bindings.ts +2 -0
  51. package/src/budgets.ts +96 -49
  52. package/src/connections.ts +1 -1
  53. package/src/do-calls.ts +216 -25
  54. package/src/facet-pool.ts +5 -0
  55. package/src/fanout.ts +64 -56
  56. package/src/fenced-work.ts +3 -3
  57. package/src/host-wasm.ts +41 -0
  58. package/src/image-store.ts +30 -3
  59. package/src/inner-do-registry.ts +31 -0
  60. package/src/isolate-pool.ts +108 -74
  61. package/src/process-fabric.ts +88 -30
  62. package/src/process-host.ts +16 -14
  63. package/src/supervisor-props.ts +56 -0
  64. package/src/timers.ts +45 -9
  65. package/src/vendor/types.ts +11 -5
  66. package/src/workerd-facet-host.ts +123 -58
@@ -73,7 +73,7 @@
73
73
  * bytes, and the host reads them off the coordinator's own disk through the
74
74
  * `ResidentDiskReader` it was given.
75
75
  */
76
- import type { HostRoute } from './composition.js';
76
+ import type { SupervisorBindingProps } from './supervisor-props.js';
77
77
  import { z } from 'zod/v4';
78
78
  import type { RouteableFacetTarget } from '@nimbus-sh/core/runtime/os-contracts.js';
79
79
  import type { ServiceStub } from './vendor/types.js';
@@ -109,6 +109,7 @@ export declare const ResidentCodeSpecSchema: z.ZodObject<{
109
109
  }, z.core.$strip>]>>;
110
110
  vfsWasmModules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
111
111
  vfsTextModules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
112
+ vfsCommonJsPacks: z.ZodOptional<z.ZodArray<z.ZodString>>;
112
113
  env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
113
114
  globalOutbound: z.ZodOptional<z.ZodNullable<z.ZodCustom<ServiceStub, ServiceStub>>>;
114
115
  }, z.core.$strip>;
@@ -136,6 +137,7 @@ export declare function residentBootSpecSchema<Stage extends z.ZodType>(stageSch
136
137
  }, z.core.$strip>]>>;
137
138
  vfsWasmModules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
138
139
  vfsTextModules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
140
+ vfsCommonJsPacks: z.ZodOptional<z.ZodArray<z.ZodString>>;
139
141
  env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
140
142
  globalOutbound: z.ZodOptional<z.ZodNullable<z.ZodCustom<ServiceStub, ServiceStub>>>;
141
143
  }, z.core.$strip>;
@@ -178,7 +180,7 @@ export declare const FACET_IMAGE_DIR = "var/lib/nimbus/facet-images";
178
180
  * generated text into `startArgs` would make every image per-PROGRAM and
179
181
  * shareable across spawns and sessions; the sweep bounds the store either way.
180
182
  */
181
- export declare function facetImageDigest(source: string): Promise<string>;
183
+ export declare function facetImageDigest(image: string | Uint8Array): Promise<string>;
182
184
  export declare function facetImagePath(digest: string): string;
183
185
  /**
184
186
  * The digest an image path claims, for the reader's verify-on-read. Content
@@ -214,21 +216,30 @@ export interface ResidentDiskReader {
214
216
  * so the worker config can tell "embedder takes the env" from the default.
215
217
  */
216
218
  export declare function residentLoaderConfig(spec: ResidentCodeSpec, disk: ResidentDiskReader): Promise<Record<string, unknown>>;
219
+ /**
220
+ * One image holding many `{ cjs }` modules: a JSON index of `[name, length]`
221
+ * rows, a newline, and the module texts back to back. Lengths are UTF-16 code
222
+ * units, the unit the decoded text is sliced in, so decoding copies nothing:
223
+ * each module is a slice of the one string read.
224
+ *
225
+ * Encoded as its parts, in order, never joined: the image store encodes them
226
+ * straight into the image's bytes, and a joined copy would be a second full
227
+ * copy of the program's code on the coordinator.
228
+ */
229
+ export declare function encodeCommonJsPack(modules: Record<string, string>): string[];
230
+ export declare function decodeCommonJsPack(pack: string): Record<string, {
231
+ cjs: string;
232
+ }>;
217
233
  /**
218
234
  * The identity a resident process's SUPERVISOR binding is minted for. Always
219
235
  * the COORDINATOR's — a process hosted somewhere else still reads and writes
220
- * the user's disk, and still reports to the user's process table.
236
+ * the user's disk, and still reports to the user's process table. Minted by
237
+ * `supervisorBindingProps` on the coordinator; `route` is absent only when
238
+ * the coordinator's isolate composed nothing, in which case no supervisor
239
+ * binding is minted either.
221
240
  */
222
- export interface ResidentSupervisorProps {
223
- doId: string;
224
- pid: number;
241
+ export interface ResidentSupervisorProps extends SupervisorBindingProps {
225
242
  writerId: string;
226
- /**
227
- * The way back to the coordinator, minted with the binding. Absent only
228
- * when the coordinator's isolate composed nothing, in which case no
229
- * supervisor binding is minted either.
230
- */
231
- route?: HostRoute;
232
243
  }
233
244
  /** Everything a host needs to run one process. Substrate-free by construction. */
234
245
  export interface ProcessHostParams {
@@ -253,6 +264,13 @@ export interface ProcessHostParams {
253
264
  name: string;
254
265
  durable: boolean;
255
266
  };
267
+ /**
268
+ * Bytes the process's store is filled with before it runs (its data plan).
269
+ * The hosting actor's storage ledger (N18) admits them under the facet's
270
+ * name before the facet starts: ENOSPC, and no facet, when they would cross
271
+ * the storage limit.
272
+ */
273
+ storageBytes?: number;
256
274
  }
257
275
  /**
258
276
  * One resident process, as its coordinator sees it. Identical in meaning on
@@ -321,17 +339,14 @@ export interface ProcessImageDelivery {
321
339
  * across one. A peer-hosted process can only ever receive an image
322
340
  * through `moduleCeilingBytes` below, or by streaming it.
323
341
  *
324
- * Reachable in PRODUCTION but not from a type checker or `wrangler dev`, and
325
- * the difference is worth stating precisely because inferring one from the
326
- * other is how a wrong claim gets written down. `@cloudflare/workers-types`
327
- * 4.20260605.1 declares `get`/`abort`/`delete` and no `clone`, and the pinned
328
- * workerd is 1.20260603.1 — but the deployed runtime is Cloudflare's, not the
329
- * one wrangler bundles, and there it is present and works: enumerating the
330
- * binding on a live Worker at this repo's own compatibility_date returns
331
- * `["abort","clone","constructor","delete","get"]`, and a clone into a
332
- * destination of a DIFFERENT class had all 500 seeded files readable from the
333
- * destination's CONSTRUCTOR. No compat-date gate. So calling it is a
334
- * lockfile-and-types problem, not a platform one.
342
+ * Present in production, and since the pins moved to
343
+ * `@cloudflare/workers-types` 5.20260928.1 and workerd 1.20260926.1 also in
344
+ * the type checker and `wrangler dev` (DurableObjectFacets.clone;
345
+ * src/workerd/api/actor-state.h). Before that the types declared no `clone`
346
+ * and the pinned workerd 1.20260603.1 lacked it, while on a live Worker the
347
+ * binding enumerated `["abort","clone","constructor","delete","get"]` and a
348
+ * clone into a destination of a DIFFERENT class had all 500 seeded files
349
+ * readable from the destination's CONSTRUCTOR. No compat-date gate.
335
350
  *
336
351
  * The hazard that comes with it, measured rather than assumed: ANY `src`
337
352
  * that does not resolve to a populated facet — a typo, a name not created
@@ -382,6 +397,8 @@ export interface OneShotCodeSpec {
382
397
  mainModule: string;
383
398
  modules: Record<string, string | {
384
399
  wasm: ArrayBuffer;
400
+ } | {
401
+ cjs: string;
385
402
  }>;
386
403
  }
387
404
  /**
@@ -524,6 +541,8 @@ export interface ResidentProcessSpawn {
524
541
  name: string;
525
542
  durable: boolean;
526
543
  };
544
+ /** See {@link ProcessHostParams.storageBytes}. */
545
+ storageBytes?: number;
527
546
  /**
528
547
  * Called before any concrete host capability can expose this writer.
529
548
  * A spawn must not proceed unless the supervisor accepts the authority.
@@ -1 +1 @@
1
- {"version":3,"file":"process-fabric.d.ts","sourceRoot":"","sources":["../src/process-fabric.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"process-fabric.d.ts","sourceRoot":"","sources":["../src/process-fabric.ts"],"names":[],"mappings":"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"}
@@ -115,6 +115,14 @@ export const ResidentCodeSpecSchema = z.object({
115
115
  * image store below and the spec names it.
116
116
  */
117
117
  vfsTextModules: z.record(z.string(), z.string()).optional(),
118
+ /**
119
+ * VFS paths of generated CommonJS PACKS (encodeCommonJsPack): each image
120
+ * carries many `{ cjs }` modules, and the map gains every one of them at
121
+ * load. A node process's module cells travel this way — one module per
122
+ * file, so the guest's registry compiles only what the program requires,
123
+ * and one image per launch, so the boot spec names a path and not thousands.
124
+ */
125
+ vfsCommonJsPacks: z.array(z.string()).optional(),
118
126
  /**
119
127
  * The isolate's exact `env`: one entry per binding the embedder minted,
120
128
  * carried by reference so loopback stubs survive untouched. Defined —
@@ -179,8 +187,10 @@ export const FACET_IMAGE_DIR = 'var/lib/nimbus/facet-images';
179
187
  * generated text into `startArgs` would make every image per-PROGRAM and
180
188
  * shareable across spawns and sessions; the sweep bounds the store either way.
181
189
  */
182
- export async function facetImageDigest(source) {
183
- const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(source));
190
+ export async function facetImageDigest(image) {
191
+ // An image is its UTF-8 bytes; a caller holding them is not made to encode a second copy.
192
+ const bytes = typeof image === 'string' ? new TextEncoder().encode(image) : image;
193
+ const digest = await crypto.subtle.digest('SHA-256', bytes);
184
194
  return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, '0')).join('');
185
195
  }
186
196
  export function facetImagePath(digest) {
@@ -212,13 +222,24 @@ export async function residentLoaderConfig(spec, disk) {
212
222
  const resolved = {};
213
223
  for (const [moduleName, path] of Object.entries(spec.vfsWasmModules ?? {})) {
214
224
  const bytes = await disk.readFile(path);
225
+ // The read's own buffer when it fits exactly, and only otherwise a copy.
226
+ // These are the largest members a spec carries — ruby's interpreter image
227
+ // is 34.3 MiB, esbuild's 13.3 — and an unconditional slice held both
228
+ // copies at once in the coordinator's 128 MiB isolate, at the one moment
229
+ // the module map is also resident.
230
+ const exact = bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
215
231
  resolved[moduleName] = {
216
- wasm: bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength),
232
+ wasm: exact
233
+ ? bytes.buffer
234
+ : bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength),
217
235
  };
218
236
  }
219
237
  for (const [moduleName, path] of Object.entries(spec.vfsTextModules ?? {})) {
220
238
  resolved[moduleName] = await readFacetImage(disk, path);
221
239
  }
240
+ for (const path of spec.vfsCommonJsPacks ?? []) {
241
+ Object.assign(resolved, decodeCommonJsPack(await readFacetImage(disk, path)));
242
+ }
222
243
  return {
223
244
  compatibilityDate: spec.compatibilityDate,
224
245
  compatibilityFlags: spec.compatibilityFlags,
@@ -228,6 +249,39 @@ export async function residentLoaderConfig(spec, disk) {
228
249
  ...(spec.globalOutbound !== undefined ? { globalOutbound: spec.globalOutbound } : {}),
229
250
  };
230
251
  }
252
+ /**
253
+ * One image holding many `{ cjs }` modules: a JSON index of `[name, length]`
254
+ * rows, a newline, and the module texts back to back. Lengths are UTF-16 code
255
+ * units, the unit the decoded text is sliced in, so decoding copies nothing:
256
+ * each module is a slice of the one string read.
257
+ *
258
+ * Encoded as its parts, in order, never joined: the image store encodes them
259
+ * straight into the image's bytes, and a joined copy would be a second full
260
+ * copy of the program's code on the coordinator.
261
+ */
262
+ export function encodeCommonJsPack(modules) {
263
+ const index = [];
264
+ const parts = [''];
265
+ for (const [name, text] of Object.entries(modules)) {
266
+ index.push([name, text.length]);
267
+ parts.push(text);
268
+ }
269
+ parts[0] = JSON.stringify(index) + '\n';
270
+ return parts;
271
+ }
272
+ export function decodeCommonJsPack(pack) {
273
+ const newline = pack.indexOf('\n');
274
+ const index = z.array(z.tuple([z.string(), z.number().int().nonnegative()])).parse(JSON.parse(pack.slice(0, newline)));
275
+ const modules = {};
276
+ let offset = newline + 1;
277
+ for (const [name, length] of index) {
278
+ modules[name] = { cjs: pack.slice(offset, offset + length) };
279
+ offset += length;
280
+ }
281
+ if (offset !== pack.length)
282
+ throw new Error(`Nimbus: CommonJS pack holds ${pack.length - offset} bytes its index does not name`);
283
+ return modules;
284
+ }
231
285
  /**
232
286
  * Read one content-addressed facet image and verify it against the digest its
233
287
  * path claims. Content addressing is only a guarantee if the bytes are checked
@@ -240,13 +294,14 @@ async function readFacetImage(disk, path) {
240
294
  if (!expected) {
241
295
  throw new Error(`Nimbus: '${path}' is not a content-addressed facet image path`);
242
296
  }
243
- const source = new TextDecoder().decode(await disk.readFile(path));
244
- const actual = await facetImageDigest(source);
297
+ // Verified from the bytes read, and decoded only after: re-encoding the decoded string held a third copy of the largest member.
298
+ const bytes = await disk.readFile(path);
299
+ const actual = await facetImageDigest(bytes);
245
300
  if (actual !== expected) {
246
301
  throw new Error(`Nimbus: facet image '${path}' does not match its digest (read ${actual}); `
247
302
  + 'the image store is corrupt and the process cannot boot from it');
248
303
  }
249
- return source;
304
+ return new TextDecoder().decode(bytes);
250
305
  }
251
306
  // ── Handle ──────────────────────────────────────────────────────────────────
252
307
  /**
@@ -350,6 +405,7 @@ export class ProcessFabric {
350
405
  writerId,
351
406
  startArgs: spawn.startArgs,
352
407
  ...(spawn.facet !== undefined ? { facet: spawn.facet } : {}),
408
+ ...(spawn.storageBytes !== undefined ? { storageBytes: spawn.storageBytes } : {}),
353
409
  });
354
410
  }
355
411
  catch (error) {
@@ -64,7 +64,7 @@
64
64
  * that pair unforgeable by anything that did not open the process.
65
65
  */
66
66
  import { type ProcessHost, type ResidentDiskReader } from './process-fabric.js';
67
- import { type HostRoute } from './composition.js';
67
+ import type { HostRoute } from './composition.js';
68
68
  /** The substrates this deployment can be configured for. */
69
69
  export type ProcessHostMode = 'facet' | 'peer';
70
70
  /**
@@ -84,6 +84,8 @@ export interface HostProcessOpts {
84
84
  route?: HostRoute;
85
85
  pid: number;
86
86
  writerId: string;
87
+ /** The coordinator instance's delivery incarnation, minted into the SUPERVISOR binding (ResidentSupervisorProps). */
88
+ hostIncarnation?: string;
87
89
  workerKey: string;
88
90
  /** Unforgeable capability for the fetch-semantic WebSocket hop. */
89
91
  webSocketCapability: string;
@@ -1 +1 @@
1
- {"version":3,"file":"process-host.d.ts","sourceRoot":"","sources":["../src/process-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAOH,OAAO,EAGL,KAAK,WAAW,EAIhB,KAAK,kBAAkB,EAExB,MAAM,qBAAqB,CAAC;AAO7B,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7D,4DAA4D;AAC5D,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,CAAC;AAE/C;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,eAAe,EACrB,GAAG,EAAE,kBAAkB,EACvB,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,kBAAkB,GAC7B,WAAW,CAIb;AA+ED,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,mBAAmB,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC5B,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC5B,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;CAC7B;AAaD;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,8BAA8B,CAAC;AACvE,eAAO,MAAM,kCAAkC,yCAAyC,CAAC;AAiUzF;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAWhE"}
1
+ {"version":3,"file":"process-host.d.ts","sourceRoot":"","sources":["../src/process-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAOH,OAAO,EAGL,KAAK,WAAW,EAIhB,KAAK,kBAAkB,EAExB,MAAM,qBAAqB,CAAC;AAO7B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGlD,4DAA4D;AAC5D,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,CAAC;AAE/C;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,eAAe,EACrB,GAAG,EAAE,kBAAkB,EACvB,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,kBAAkB,GAC7B,WAAW,CAIb;AA0ED,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,qHAAqH;IACrH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,mBAAmB,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC5B,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC5B,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;CAC7B;AAaD;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,8BAA8B,CAAC;AACvE,eAAO,MAAM,kCAAkC,yCAAyC,CAAC;AAqUzF;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAWhE"}
@@ -64,14 +64,14 @@
64
64
  * that pair unforgeable by anything that did not open the process.
65
65
  */
66
66
  import { disposeRpcResource } from '@nimbus-sh/platform/rpc-dispose.js';
67
- import { isTransientDoReset } from '@nimbus-sh/platform/oom-classify.js';
67
+ import { classifyDoCall, isRetryableDoCall } from '@nimbus-sh/platform/oom-classify.js';
68
68
  import { PEER_RETRY_BACKOFF_MS, PEER_TRANSIENT_RESET_RETRIES } from './fanout.js';
69
69
  import { hostNamespaceBinding, hostOpDispatch } from './host-dispatch.js';
70
70
  import { z } from 'zod/v4';
71
71
  import { DYNAMIC_WORKER_CODE_LIMIT_BYTES } from './budgets.js';
72
72
  import { BindingError } from './vendor/errors.js';
73
73
  import { processes, } from './workerd-facet-host.js';
74
- import { hostRoute } from './composition.js';
74
+ import { supervisorBindingProps } from './supervisor-props.js';
75
75
  /**
76
76
  * The substrate for this deployment, resolved once. The mode arrives already
77
77
  * decided — the embedder owns the config var that picks it, and refuses an
@@ -109,15 +109,10 @@ class FacetProcessHost {
109
109
  this.coordDoId = ctx.id.toString();
110
110
  }
111
111
  runOnce(params, consume) {
112
- return processes(this.ctx, this.env).run({ doId: this.coordDoId, pid: params.pid, writerId: params.writerId, route: hostRoute() ?? undefined }, params, consume);
112
+ return processes(this.ctx, this.env).run({ ...supervisorBindingProps(this.ctx, params.pid), writerId: params.writerId }, params, consume);
113
113
  }
114
114
  async open(params) {
115
- const supervisor = {
116
- doId: this.coordDoId,
117
- pid: params.pid,
118
- writerId: params.writerId,
119
- route: hostRoute() ?? undefined,
120
- };
115
+ const supervisor = { ...supervisorBindingProps(this.ctx, params.pid), writerId: params.writerId };
121
116
  const { name, ...facet } = processes(this.ctx, this.env).spawn(this.disk, supervisor, params);
122
117
  return {
123
118
  ...facet,
@@ -261,7 +256,7 @@ class PeerProcessHost {
261
256
  * worker of the coordinator here exactly as it does on `facet`.
262
257
  */
263
258
  runOnce(params, consume) {
264
- return processes(this.ctx, this.env).run({ doId: this.coordDoId, pid: params.pid, writerId: params.writerId, route: hostRoute() ?? undefined }, params, consume);
259
+ return processes(this.ctx, this.env).run({ ...supervisorBindingProps(this.ctx, params.pid), writerId: params.writerId }, params, consume);
265
260
  }
266
261
  async open(params) {
267
262
  if (params.facet) {
@@ -280,11 +275,15 @@ class PeerProcessHost {
280
275
  // also what keeps the hosting DO resident. It settles when a `lifetime`
281
276
  // runner exits or when a `boot` runner's host is cancelled, and rejects if
282
277
  // the peer dies under either.
278
+ // The peer mints the process's binding from these, for THIS object: the
279
+ // coordinator's doId, route and delivery instance.
280
+ const supervisor = supervisorBindingProps(this.ctx, params.pid);
283
281
  const hostLeg = placement.stub._rpcHostProcess(params.boot, {
284
- coordinatorDoId: this.coordDoId,
285
- route: hostRoute() ?? undefined,
286
- pid: params.pid,
282
+ coordinatorDoId: supervisor.doId,
283
+ route: supervisor.route,
284
+ pid: supervisor.pid,
287
285
  writerId: params.writerId,
286
+ hostIncarnation: supervisor.hostIncarnation,
288
287
  workerKey: params.workerKey,
289
288
  webSocketCapability,
290
289
  startArgs: params.startArgs,
@@ -396,7 +395,7 @@ class PeerProcessHost {
396
395
  return probe;
397
396
  }
398
397
  catch (err) {
399
- if (attempt < PEER_TRANSIENT_RESET_RETRIES && isTransientDoReset(err)) {
398
+ if (attempt < PEER_TRANSIENT_RESET_RETRIES && isRetryableDoCall(classifyDoCall(err))) {
400
399
  await new Promise((r) => setTimeout(r, PEER_RETRY_BACKOFF_MS[Math.min(attempt, PEER_RETRY_BACKOFF_MS.length - 1)]));
401
400
  continue;
402
401
  }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * supervisor-props.ts — what every SUPERVISOR binding a Durable Object mints
3
+ * for a process carries, and the one rule for naming the instance in it.
4
+ *
5
+ * A binding names its host INSTANCE (`hostIncarnation`) exactly when a
6
+ * mutation sent through it can be delivered once
7
+ * (@nimbus-sh/core/workspace/supervisor-delivery.js): the host opened a
8
+ * delivery store, the binding acts as a real process (SupervisorRPC refuses
9
+ * every filesystem mutation of pid 0), and it routes back to this very
10
+ * object rather than another (a fanout pool writes to its coordinator).
11
+ * Minting every binding here is what keeps a new mint site from forgetting
12
+ * that, which would fail silently: its mutations would simply never be
13
+ * re-sent.
14
+ */
15
+ import { type HostRoute } from './composition.js';
16
+ /** The props every SUPERVISOR binding for a process carries. */
17
+ export interface SupervisorBindingProps {
18
+ /** The Durable Object the binding's calls reach. */
19
+ doId: string;
20
+ /** The process the calls act as; 0 is none, and can mutate nothing. */
21
+ pid: number;
22
+ /** The way back, minted with the binding in the host's isolate. */
23
+ route?: HostRoute;
24
+ /** The host instance that applies this binding's mutations once, when there is one. */
25
+ hostIncarnation?: string;
26
+ }
27
+ /**
28
+ * The props of a SUPERVISOR binding minted in the Durable Object whose state
29
+ * is `ctx`, for process `pid`, reaching `options.doId` (this object by
30
+ * default) by `options.route` (this isolate's composition by default).
31
+ */
32
+ export declare function supervisorBindingProps(ctx: {
33
+ readonly id: {
34
+ toString(): string;
35
+ };
36
+ }, pid: number, options?: {
37
+ doId?: string;
38
+ route?: HostRoute;
39
+ }): SupervisorBindingProps;
40
+ /**
41
+ * `key`, for a loader cache entry whose worker holds a binding with `props`:
42
+ * made specific to the host instance the binding names, since the loader
43
+ * outlives that instance and the next one refuses every mutation the binding
44
+ * would deliver. A binding that names none keeps `key`, and its warm worker.
45
+ */
46
+ export declare function supervisorLoaderKey(key: string, props: Pick<SupervisorBindingProps, 'hostIncarnation'>): string;
47
+ //# sourceMappingURL=supervisor-props.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supervisor-props.d.ts","sourceRoot":"","sources":["../src/supervisor-props.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7D,gEAAgE;AAChE,MAAM,WAAW,sBAAsB;IACrC,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,GAAG,EAAE,MAAM,CAAC;IACZ,mEAAmE;IACnE,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,uFAAuF;IACvF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,CAAA;CAAE,EAC5C,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAAO,GACjD,sBAAsB,CAMxB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,sBAAsB,EAAE,iBAAiB,CAAC,GAAG,MAAM,CAE/G"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * supervisor-props.ts — what every SUPERVISOR binding a Durable Object mints
3
+ * for a process carries, and the one rule for naming the instance in it.
4
+ *
5
+ * A binding names its host INSTANCE (`hostIncarnation`) exactly when a
6
+ * mutation sent through it can be delivered once
7
+ * (@nimbus-sh/core/workspace/supervisor-delivery.js): the host opened a
8
+ * delivery store, the binding acts as a real process (SupervisorRPC refuses
9
+ * every filesystem mutation of pid 0), and it routes back to this very
10
+ * object rather than another (a fanout pool writes to its coordinator).
11
+ * Minting every binding here is what keeps a new mint site from forgetting
12
+ * that, which would fail silently: its mutations would simply never be
13
+ * re-sent.
14
+ */
15
+ import { supervisorDeliveryProps } from '@nimbus-sh/core/workspace/supervisor-delivery.js';
16
+ import { hostRoute } from './composition.js';
17
+ /**
18
+ * The props of a SUPERVISOR binding minted in the Durable Object whose state
19
+ * is `ctx`, for process `pid`, reaching `options.doId` (this object by
20
+ * default) by `options.route` (this isolate's composition by default).
21
+ */
22
+ export function supervisorBindingProps(ctx, pid, options = {}) {
23
+ const own = ctx.id.toString();
24
+ const doId = options.doId ?? own;
25
+ const route = options.route ?? hostRoute() ?? undefined;
26
+ const delivery = pid > 0 && doId === own ? supervisorDeliveryProps(ctx) : {};
27
+ return { doId, pid, route, ...delivery };
28
+ }
29
+ /**
30
+ * `key`, for a loader cache entry whose worker holds a binding with `props`:
31
+ * made specific to the host instance the binding names, since the loader
32
+ * outlives that instance and the next one refuses every mutation the binding
33
+ * would deliver. A binding that names none keeps `key`, and its warm worker.
34
+ */
35
+ export function supervisorLoaderKey(key, props) {
36
+ return props.hostIncarnation === undefined ? key : `${key}:${props.hostIncarnation}`;
37
+ }
package/dist/timers.d.ts CHANGED
@@ -51,9 +51,12 @@ export declare const TIMER_REASONS_KEY = "w1_next_alarm_reasons";
51
51
  * The host instance carrying the per-instance timer chain. The field lives on
52
52
  * the embedder's DO instance so one chain serializes every timer-map
53
53
  * read-modify-write for that instance (see {@link Timers.schedule}).
54
+ * `_timerEpoch` counts {@link Timers.reset}s: a schedule or dispatch writes
55
+ * only while the epoch it was requested in is still current.
54
56
  */
55
57
  export interface TimerHost {
56
58
  _timerChain?: Promise<unknown>;
59
+ _timerEpoch?: number;
57
60
  }
58
61
  /**
59
62
  * What one timer handler may return: nothing, or a deadline this reason
@@ -113,6 +116,15 @@ export declare class Timers {
113
116
  * subsystem's in-isolate setTimeout fallback continues to work).
114
117
  */
115
118
  schedule(reason: string, whenMs: number): Promise<boolean>;
119
+ /**
120
+ * Void every timer of this instance: a schedule or dispatch already
121
+ * requested — still queued on the chain, or a dispatch whose handlers are
122
+ * running — writes no reason and arms no alarm from here on. For a
123
+ * deliberate end of the actor's state (a session destroy): call it before
124
+ * wiping storage and deleting the alarm, so nothing in flight writes the
125
+ * map back or re-arms after the wipe. Requests made after it proceed.
126
+ */
127
+ reset(): void;
116
128
  /**
117
129
  * Multi-reason timer dispatcher. Called from the DO's `alarm()` handler
118
130
  * with the embedder's handler map.
@@ -1 +1 @@
1
- {"version":3,"file":"timers.d.ts","sourceRoot":"","sources":["../src/timers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,QAAQ,CAAC,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAED,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,YAAY,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,iBAAiB,0BAA0B,CAAC;AAEzD;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;CAChC;AAmBD;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,qEAAqE;AACrE,MAAM,MAAM,aAAa,GAAG,MAAM,CAChC,MAAM,EACN,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,KAAK,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CACzF,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,YAAY,GAAG,MAAM,CAEjE;AAED,qBAAa,MAAM;IAEf,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,GAAG;gBADH,IAAI,EAAE,SAAS,EACf,GAAG,EAAE,YAAY;IAGpC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA4C1D;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CACN,QAAQ,EAAE,aAAa,EACvB,aAAa,CAAC,EAAE,MAAM,IAAI,EAC1B,SAAS,CAAC,EAAE,cAAc,GACzB,OAAO,CAAC,IAAI,CAAC;CAWjB"}
1
+ {"version":3,"file":"timers.d.ts","sourceRoot":"","sources":["../src/timers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,QAAQ,CAAC,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAED,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,YAAY,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,iBAAiB,0BAA0B,CAAC;AAEzD;;;;;;GAMG;AACH,MAAM,WAAW,SAAS;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAmBD;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,qEAAqE;AACrE,MAAM,MAAM,aAAa,GAAG,MAAM,CAChC,MAAM,EACN,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,KAAK,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CACzF,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,YAAY,GAAG,MAAM,CAEjE;AAED,qBAAa,MAAM;IAEf,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,GAAG;gBADH,IAAI,EAAE,SAAS,EACf,GAAG,EAAE,YAAY;IAGpC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkD1D;;;;;;;OAOG;IACH,KAAK,IAAI,IAAI;IAIb;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CACN,QAAQ,EAAE,aAAa,EACvB,aAAa,CAAC,EAAE,MAAM,IAAI,EAC1B,SAAS,CAAC,EAAE,cAAc,GACzB,OAAO,CAAC,IAAI,CAAC;CAajB"}
package/dist/timers.js CHANGED
@@ -103,6 +103,7 @@ export class Timers {
103
103
  arms.push({ reason, whenMs });
104
104
  return Promise.resolve(true);
105
105
  }
106
+ const epoch = host._timerEpoch ?? 0;
106
107
  // Serialize every read-modify-write of the reasons map through one
107
108
  // per-instance chain: two schedulers firing back-to-back from one activity
108
109
  // hook would otherwise interleave their get→put cycles and silently drop
@@ -113,15 +114,21 @@ export class Timers {
113
114
  if (typeof setAlarmFn !== 'function')
114
115
  return false;
115
116
  const existing = (await ctx.storage.get(TIMER_REASONS_KEY));
117
+ // Requested before a reset: it belongs to the timers the reset voided.
118
+ if ((host._timerEpoch ?? 0) !== epoch)
119
+ return false;
116
120
  const map = { ...(existing || {}) };
117
121
  // Earliest-deadline-first: only update if new request is sooner or
118
122
  // this reason has no pending entry.
123
+ let written;
119
124
  if (!(reason in map) || whenMs < map[reason]) {
120
125
  map[reason] = whenMs;
121
- await ctx.storage.put(TIMER_REASONS_KEY, map);
126
+ written = ctx.storage.put(TIMER_REASONS_KEY, map);
122
127
  }
123
- const earliest = Math.min(...Object.values(map));
124
- setAlarmFn.call(ctx.storage, earliest);
128
+ // Issued in the turn the epoch was checked in: a reset after this
129
+ // point wipes and disarms after these, never before.
130
+ setAlarmFn.call(ctx.storage, Math.min(...Object.values(map)));
131
+ await written;
125
132
  return true;
126
133
  }
127
134
  catch (e) {
@@ -133,6 +140,17 @@ export class Timers {
133
140
  host._timerChain = chained;
134
141
  return chained;
135
142
  }
143
+ /**
144
+ * Void every timer of this instance: a schedule or dispatch already
145
+ * requested — still queued on the chain, or a dispatch whose handlers are
146
+ * running — writes no reason and arms no alarm from here on. For a
147
+ * deliberate end of the actor's state (a session destroy): call it before
148
+ * wiping storage and deleting the alarm, so nothing in flight writes the
149
+ * map back or re-arms after the wipe. Requests made after it proceed.
150
+ */
151
+ reset() {
152
+ this.host._timerEpoch = (this.host._timerEpoch ?? 0) + 1;
153
+ }
136
154
  /**
137
155
  * Multi-reason timer dispatcher. Called from the DO's `alarm()` handler
138
156
  * with the embedder's handler map.
@@ -155,14 +173,22 @@ export class Timers {
155
173
  */
156
174
  dispatch(handlers, onLegacyAlarm, alarmInfo) {
157
175
  const { host, ctx } = this;
176
+ const epoch = host._timerEpoch ?? 0;
177
+ const current = () => (host._timerEpoch ?? 0) === epoch;
158
178
  // Same serialization as schedule: the dispatcher's read→handlers→write
159
179
  // cycle must not interleave with an activity-hook schedule.
160
- const chained = (host._timerChain ?? Promise.resolve()).then(() => dispatchBody(ctx, handlers, onLegacyAlarm, alarmInfo), () => dispatchBody(ctx, handlers, onLegacyAlarm, alarmInfo));
180
+ const chained = (host._timerChain ?? Promise.resolve()).then(() => dispatchBody(ctx, current, handlers, onLegacyAlarm, alarmInfo), () => dispatchBody(ctx, current, handlers, onLegacyAlarm, alarmInfo));
161
181
  host._timerChain = chained;
162
182
  return chained;
163
183
  }
164
184
  }
165
- async function dispatchBody(ctx, handlers, onLegacyAlarm, alarmInfo) {
185
+ /**
186
+ * One dispatch. `current` answers whether the epoch it was requested in still
187
+ * stands: once a reset voids it, no further handler runs and nothing is
188
+ * written or armed — the reset's caller is wiping this state, and a write
189
+ * after the wipe would outlive it.
190
+ */
191
+ async function dispatchBody(ctx, current, handlers, onLegacyAlarm, alarmInfo) {
166
192
  // Collect schedule requests made inside handler context (see
167
193
  // dispatchArms) and fold them into the map below, so an in-dispatch arm
168
194
  // neither deadlocks on the chain nor races the write.
@@ -170,6 +196,8 @@ async function dispatchBody(ctx, handlers, onLegacyAlarm, alarmInfo) {
170
196
  try {
171
197
  const now = Date.now();
172
198
  const existing = (await ctx?.storage?.get?.(TIMER_REASONS_KEY));
199
+ if (!current())
200
+ return;
173
201
  const map = { ...(existing || {}) };
174
202
  const hadMap = Object.keys(map).length > 0;
175
203
  if (!hadMap) {
@@ -185,6 +213,8 @@ async function dispatchBody(ctx, handlers, onLegacyAlarm, alarmInfo) {
185
213
  fired.push(reason);
186
214
  }
187
215
  for (const reason of fired) {
216
+ if (!current())
217
+ return;
188
218
  delete map[reason];
189
219
  const handler = handlers[reason];
190
220
  // Unknown reasons silently dropped (forward-compat).
@@ -201,20 +231,25 @@ async function dispatchBody(ctx, handlers, onLegacyAlarm, alarmInfo) {
201
231
  }
202
232
  }
203
233
  }
234
+ // A reset while the handlers ran: the map read above is gone, and
235
+ // writing it back — or arming for it — would revive what was ended.
236
+ if (!current())
237
+ return;
204
238
  // Fold the in-dispatch arms, earliest-deadline-first per reason.
205
239
  for (const arm of arms) {
206
240
  if (!(arm.reason in map) || arm.whenMs < map[arm.reason]) {
207
241
  map[arm.reason] = arm.whenMs;
208
242
  }
209
243
  }
210
- // Re-arm or clear.
244
+ // Re-arm or clear, issued in the turn the epoch was checked in: a reset
245
+ // after this point wipes and disarms after these, never before.
211
246
  const setAlarmFn = ctx?.storage?.setAlarm;
212
247
  if (Object.keys(map).length > 0) {
213
- await ctx.storage.put(TIMER_REASONS_KEY, map);
214
- const earliest = Math.min(...Object.values(map));
248
+ const written = ctx.storage.put(TIMER_REASONS_KEY, map);
215
249
  if (typeof setAlarmFn === 'function') {
216
- setAlarmFn.call(ctx.storage, earliest);
250
+ setAlarmFn.call(ctx.storage, Math.min(...Object.values(map)));
217
251
  }
252
+ await written;
218
253
  }
219
254
  else if (hadMap) {
220
255
  try {