@nimbus-sh/fabric 0.1.0 → 0.2.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 (103) hide show
  1. package/README.md +84 -55
  2. package/dist/bindings.js +5 -5
  3. package/dist/budgets.d.ts +132 -0
  4. package/dist/budgets.d.ts.map +1 -0
  5. package/dist/budgets.js +248 -0
  6. package/dist/composition.d.ts +87 -0
  7. package/dist/composition.d.ts.map +1 -0
  8. package/dist/composition.js +76 -0
  9. package/dist/connections.d.ts +81 -0
  10. package/dist/connections.d.ts.map +1 -0
  11. package/dist/connections.js +114 -0
  12. package/dist/derived.d.ts +65 -0
  13. package/dist/derived.d.ts.map +1 -0
  14. package/dist/derived.js +95 -0
  15. package/dist/do-calls.d.ts +94 -0
  16. package/dist/do-calls.d.ts.map +1 -0
  17. package/dist/do-calls.js +111 -0
  18. package/dist/facet-pool.d.ts +90 -0
  19. package/dist/facet-pool.d.ts.map +1 -0
  20. package/dist/facet-pool.js +113 -0
  21. package/dist/{fanout-pool.d.ts → fanout.d.ts} +20 -20
  22. package/dist/fanout.d.ts.map +1 -0
  23. package/dist/{fanout-pool.js → fanout.js} +20 -20
  24. package/dist/{launch-journal.d.ts → fenced-work.d.ts} +25 -13
  25. package/dist/fenced-work.d.ts.map +1 -0
  26. package/dist/{launch-journal.js → fenced-work.js} +47 -13
  27. package/dist/generation.d.ts +69 -0
  28. package/dist/generation.d.ts.map +1 -0
  29. package/dist/generation.js +118 -0
  30. package/dist/{facet-image-store.d.ts → image-store.d.ts} +8 -8
  31. package/dist/image-store.d.ts.map +1 -0
  32. package/dist/{facet-image-store.js → image-store.js} +4 -4
  33. package/dist/index.d.ts +16 -8
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +16 -8
  36. package/dist/{loader-pool.d.ts → isolate-pool.d.ts} +19 -19
  37. package/dist/isolate-pool.d.ts.map +1 -0
  38. package/dist/{loader-pool.js → isolate-pool.js} +20 -20
  39. package/dist/journal.d.ts +111 -0
  40. package/dist/journal.d.ts.map +1 -0
  41. package/dist/journal.js +177 -0
  42. package/dist/outbox.d.ts +249 -0
  43. package/dist/outbox.d.ts.map +1 -0
  44. package/dist/outbox.js +355 -0
  45. package/dist/process-fabric.d.ts +2 -14
  46. package/dist/process-fabric.d.ts.map +1 -1
  47. package/dist/process-fabric.js +6 -15
  48. package/dist/process-host.d.ts +1 -1
  49. package/dist/process-host.d.ts.map +1 -1
  50. package/dist/process-host.js +10 -9
  51. package/dist/sealed.d.ts +78 -0
  52. package/dist/sealed.d.ts.map +1 -0
  53. package/dist/sealed.js +145 -0
  54. package/dist/timers.d.ts +138 -0
  55. package/dist/timers.d.ts.map +1 -0
  56. package/dist/timers.js +231 -0
  57. package/dist/{launch-pacer.d.ts → turn-budget.d.ts} +19 -21
  58. package/dist/turn-budget.d.ts.map +1 -0
  59. package/dist/{launch-pacer.js → turn-budget.js} +22 -11
  60. package/dist/workerd-facet-host.d.ts +28 -67
  61. package/dist/workerd-facet-host.d.ts.map +1 -1
  62. package/dist/workerd-facet-host.js +49 -171
  63. package/examples/agent-core-adapter.ts +191 -0
  64. package/package.json +4 -2
  65. package/src/bindings.ts +6 -6
  66. package/src/budgets.ts +308 -0
  67. package/src/composition.ts +127 -0
  68. package/src/connections.ts +140 -0
  69. package/src/derived.ts +135 -0
  70. package/src/do-calls.ts +156 -0
  71. package/src/facet-pool.ts +157 -0
  72. package/src/{fanout-pool.ts → fanout.ts} +35 -35
  73. package/src/{launch-journal.ts → fenced-work.ts} +58 -22
  74. package/src/generation.ts +144 -0
  75. package/src/{facet-image-store.ts → image-store.ts} +9 -9
  76. package/src/index.ts +16 -8
  77. package/src/{loader-pool.ts → isolate-pool.ts} +34 -34
  78. package/src/journal.ts +242 -0
  79. package/src/node-async-hooks.d.ts +14 -0
  80. package/src/outbox.ts +520 -0
  81. package/src/process-fabric.ts +6 -33
  82. package/src/process-host.ts +10 -15
  83. package/src/sealed.ts +150 -0
  84. package/src/timers.ts +294 -0
  85. package/src/{launch-pacer.ts → turn-budget.ts} +30 -24
  86. package/src/workerd-facet-host.ts +67 -193
  87. package/dist/alarms.d.ts +0 -134
  88. package/dist/alarms.d.ts.map +0 -1
  89. package/dist/alarms.js +0 -214
  90. package/dist/ctx-exports.d.ts +0 -47
  91. package/dist/ctx-exports.d.ts.map +0 -1
  92. package/dist/ctx-exports.js +0 -54
  93. package/dist/facet-image-store.d.ts.map +0 -1
  94. package/dist/fanout-pool.d.ts.map +0 -1
  95. package/dist/launch-journal.d.ts.map +0 -1
  96. package/dist/launch-pacer.d.ts.map +0 -1
  97. package/dist/loader-ledger.d.ts +0 -57
  98. package/dist/loader-ledger.d.ts.map +0 -1
  99. package/dist/loader-ledger.js +0 -91
  100. package/dist/loader-pool.d.ts.map +0 -1
  101. package/src/alarms.ts +0 -275
  102. package/src/ctx-exports.ts +0 -77
  103. package/src/loader-ledger.ts +0 -112
@@ -0,0 +1,118 @@
1
+ /**
2
+ * generation.ts — the isolate-generation clock, and the deferred
3
+ * reconciliation that runs once per fresh incarnation.
4
+ *
5
+ * Workerd hibernates Durable Objects between requests to free memory. On
6
+ * wake, the new isolate must rebuild its in-memory state from SQL — but it
7
+ * also needs to know "is this the same lifecycle as before, or did workerd
8
+ * recycle me?" That distinction matters for recovery (warmJoin vs cold init)
9
+ * and is captured by the isolate generation, a counter persisted across
10
+ * hibernations.
11
+ *
12
+ * State is keyed weakly off the actor's `ctx`, which lives exactly as long as
13
+ * the incarnation: a fresh isolate gets a fresh `ctx`, adopts the persisted
14
+ * counter, and bumps it once. The embedder reads `generation(ctx)` wherever it
15
+ * needs the incarnation number — one source of truth instead of mirrored host
16
+ * fields.
17
+ */
18
+ import { errorText } from '@nimbus-sh/core/_shared/error-text.js';
19
+ /**
20
+ * Storage key for the isolate-generation counter (cold-start +
21
+ * post-hibernation wake; one increment per fresh isolate).
22
+ *
23
+ * The VALUE is live production DO storage ('w9_isolate_gen') and must never
24
+ * change — renaming a storage key is a migration, and orphaned rows are the
25
+ * least of what it breaks.
26
+ */
27
+ export const GENERATION_KEY = 'w9_isolate_gen';
28
+ const states = new WeakMap();
29
+ function stateOf(ctx) {
30
+ let state = states.get(ctx);
31
+ if (!state) {
32
+ state = { value: 0, adopted: false, coldStart: [], coldStartChain: Promise.resolve() };
33
+ states.set(ctx, state);
34
+ }
35
+ return state;
36
+ }
37
+ /** This incarnation's generation. Zero until {@link adoptGeneration} ran. */
38
+ export function generation(ctx) {
39
+ return states.get(ctx)?.value ?? 0;
40
+ }
41
+ /** Increment + persist the generation counter once per fresh isolate. */
42
+ export async function adoptGeneration(ctx) {
43
+ const state = stateOf(ctx);
44
+ if (state.adopted)
45
+ return;
46
+ state.adopted = true;
47
+ try {
48
+ const prev = (await ctx.storage.get(GENERATION_KEY));
49
+ // Adopt the persisted truth first, and adopt the bump only after the
50
+ // put resolves. An unpersisted `next` would be re-read as `prev` by the
51
+ // NEXT boot and re-issued — two instances sharing one generation is
52
+ // exactly the pid-aliasing this counter exists to prevent. Running on
53
+ // the previous persisted generation is the lesser lapse, and the
54
+ // put-failure case is replica-only in practice (replicas never spawn).
55
+ //
56
+ // What holds the guarantee is the output gate, not this await: measured,
57
+ // the block body resolves in 0 ms even with a confirmed put, because
58
+ // `await storage.put()` returns before durability. The gate is what
59
+ // keeps a pid from generation N from escaping before N is durable, which
60
+ // is why marking this put `allowUnconfirmed` is not a free speedup — see
61
+ // scratchpad/coldstart-s1.md.
62
+ state.value = typeof prev === 'number' ? prev : 0;
63
+ const next = state.value + 1;
64
+ await ctx.storage.put(GENERATION_KEY, next);
65
+ state.value = next;
66
+ }
67
+ catch (e) {
68
+ console.warn('[nimbus/W9] generation bump failed:', errorText(e));
69
+ }
70
+ }
71
+ /**
72
+ * Take on a generation without persisting it, and clear the adopted guard so
73
+ * a later {@link adoptGeneration} re-derives from storage. The
74
+ * destroy-and-recreate path uses this: it wipes storage, re-persists the
75
+ * pre-destroy counter, and runs the rest of this incarnation on the successor
76
+ * generation the next boot will derive.
77
+ */
78
+ export function assumeGeneration(ctx, value) {
79
+ const state = stateOf(ctx);
80
+ state.value = value;
81
+ state.adopted = false;
82
+ }
83
+ /**
84
+ * Queue deferred async reconciliation for this incarnation.
85
+ *
86
+ * The task runs on the first {@link runColdStart} after registration — a turn
87
+ * the embedder already owns, NEVER the constructor's init gate. Awaiting
88
+ * recovery on the gate path is the trap this helper exists to avoid: the gate
89
+ * blocks every request to the object, and reconciliation wants a filesystem
90
+ * and a terminal that only a later turn has. The gate is also a wall, not
91
+ * just a stall: a `blockConcurrencyWhile` callback still pending at ~30 s
92
+ * (BLOCK_CONCURRENCY_CANCEL_MS, proven by probe) is cancelled and RESETS the
93
+ * object with every queued event — so never call {@link runColdStart} from
94
+ * inside one; the pump and the embedder's own turns are the places it runs.
95
+ */
96
+ export function onColdStart(ctx, task) {
97
+ stateOf(ctx).coldStart.push(task);
98
+ }
99
+ /**
100
+ * Drain the queued cold-start tasks, serialized, each awaited so the turn
101
+ * that runs them pays for them. Idempotent between registrations: a drained
102
+ * queue is a cheap no-op, and a task registered after a drain runs on the
103
+ * next call.
104
+ */
105
+ export function runColdStart(ctx) {
106
+ const state = stateOf(ctx);
107
+ if (state.coldStart.length === 0)
108
+ return state.coldStartChain;
109
+ const tasks = state.coldStart;
110
+ state.coldStart = [];
111
+ const chained = state.coldStartChain.then(async () => {
112
+ for (const task of tasks) {
113
+ await task();
114
+ }
115
+ });
116
+ state.coldStartChain = chained.catch(() => { });
117
+ return chained;
118
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * facet-image-store.ts — materializing resident-process boot images into the
2
+ * image-store.ts — materializing resident-process boot images into the
3
3
  * content-addressed image store, and sweeping the ones nothing boots from.
4
4
  *
5
5
  * A resident process's module map is sized by the user's disk, so it does not
@@ -12,11 +12,11 @@
12
12
  * table.
13
13
  *
14
14
  * The filesystem itself stays the embedder's, reached through the
15
- * {@link FacetImageBlobStore} port — the store decides what is written where
15
+ * {@link ImageBlobStore} port — the store decides what is written where
16
16
  * and when; the port decides how bytes land on a disk and with what modes and
17
17
  * credentials.
18
18
  */
19
- import type { LaunchPacer } from './launch-pacer.js';
19
+ import type { TurnBudget } from './turn-budget.js';
20
20
  /**
21
21
  * Bytes of an image written in one storage transaction.
22
22
  *
@@ -34,7 +34,7 @@ export declare const FACET_IMAGE_WRITE_SLICE_BYTES: number;
34
34
  * normalization are the implementation's: the store passes the same
35
35
  * store-relative paths it later roots and sweeps by.
36
36
  */
37
- export interface FacetImageBlobStore {
37
+ export interface ImageBlobStore {
38
38
  /** Create a directory (and its parents) if it does not exist. */
39
39
  mkdirp(dir: string): void;
40
40
  /** The file's current size in bytes, or null when it does not exist. */
@@ -56,7 +56,7 @@ export interface FacetImageBlobStore {
56
56
  * hash's problem; everything else is idempotent — an image already present
57
57
  * at its own digest is already the bytes we were about to write.
58
58
  */
59
- export declare class FacetImageStore {
59
+ export declare class ImageStore {
60
60
  private readonly blobs;
61
61
  private readonly isLive;
62
62
  /** pid → the boot images its facet loads from; the image sweep's root set. */
@@ -69,7 +69,7 @@ export declare class FacetImageStore {
69
69
  * @param isLive Whether a pid still names a running process. The root set
70
70
  * is the process table, reached through this one predicate.
71
71
  */
72
- constructor(blobs: () => FacetImageBlobStore, isLive: (pid: number) => boolean);
72
+ constructor(blobs: () => ImageBlobStore, isLive: (pid: number) => boolean);
73
73
  /**
74
74
  * The image store's directory, created before the first filesystem view is
75
75
  * built rather than on the first image write.
@@ -95,7 +95,7 @@ export declare class FacetImageStore {
95
95
  * Writing the sources here, once, is what lets the session stop holding
96
96
  * them: after this returns, the only thing it keeps is a path.
97
97
  */
98
- materialize(pid: number, modules: Record<string, string>, pacer: LaunchPacer): Promise<Record<string, string>>;
98
+ materialize(pid: number, modules: Record<string, string>, pacer: TurnBudget): Promise<Record<string, string>>;
99
99
  /**
100
100
  * Drop every image no running process boots from.
101
101
  *
@@ -109,4 +109,4 @@ export declare class FacetImageStore {
109
109
  */
110
110
  private sweep;
111
111
  }
112
- //# sourceMappingURL=facet-image-store.d.ts.map
112
+ //# sourceMappingURL=image-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-store.d.ts","sourceRoot":"","sources":["../src/image-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;;;;;;GASG;AACH,eAAO,MAAM,6BAA6B,QAA0D,CAAC;AAErG;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,wEAAwE;IACxE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACpC,wEAAwE;IACxE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACjD,kDAAkD;IAClD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAClE,2EAA2E;IAC3E,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,qDAAqD;IACrD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,qBAAa,UAAU;IAanB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAbzB,8EAA8E;IAC9E,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAS;IAEzB;;;;;;OAMG;gBAEgB,KAAK,EAAE,MAAM,cAAc,EAC3B,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO;IAGnD;;;;;;;;;;;;;;;;OAgBG;IACH,SAAS,IAAI,IAAI;IAOjB;;;;;;OAMG;IACG,WAAW,CACf,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAoDlC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,KAAK;CAgBd"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * facet-image-store.ts — materializing resident-process boot images into the
2
+ * image-store.ts — materializing resident-process boot images into the
3
3
  * content-addressed image store, and sweeping the ones nothing boots from.
4
4
  *
5
5
  * A resident process's module map is sized by the user's disk, so it does not
@@ -12,11 +12,11 @@
12
12
  * table.
13
13
  *
14
14
  * The filesystem itself stays the embedder's, reached through the
15
- * {@link FacetImageBlobStore} port — the store decides what is written where
15
+ * {@link ImageBlobStore} port — the store decides what is written where
16
16
  * and when; the port decides how bytes land on a disk and with what modes and
17
17
  * credentials.
18
18
  */
19
- import { MAX_TX_BLOB_BYTES, CHUNK_SIZE } from '@nimbus-sh/core/constants.js';
19
+ import { MAX_TX_BLOB_BYTES, CHUNK_SIZE } from '@nimbus-sh/platform/limits.js';
20
20
  import { FACET_IMAGE_DIR, facetImageDigest, facetImagePath } from './process-fabric.js';
21
21
  /**
22
22
  * Bytes of an image written in one storage transaction.
@@ -37,7 +37,7 @@ export const FACET_IMAGE_WRITE_SLICE_BYTES = Math.floor(MAX_TX_BLOB_BYTES / CHUN
37
37
  * hash's problem; everything else is idempotent — an image already present
38
38
  * at its own digest is already the bytes we were about to write.
39
39
  */
40
- export class FacetImageStore {
40
+ export class ImageStore {
41
41
  blobs;
42
42
  isLive;
43
43
  /** pid → the boot images its facet loads from; the image sweep's root set. */
package/dist/index.d.ts CHANGED
@@ -6,16 +6,24 @@
6
6
  * at resolution; non-workerd consumers (tests, tooling) import the subpath
7
7
  * modules they need instead.
8
8
  */
9
- export * from './alarms.js';
9
+ export * from './generation.js';
10
+ export * from './timers.js';
11
+ export * from './outbox.js';
12
+ export * from './journal.js';
13
+ export * from './do-calls.js';
14
+ export * from './facet-pool.js';
15
+ export * from './derived.js';
16
+ export * from './connections.js';
17
+ export * from './sealed.js';
10
18
  export * from './bindings.js';
11
- export * from './ctx-exports.js';
12
- export * from './facet-image-store.js';
13
- export * from './fanout-pool.js';
19
+ export * from './composition.js';
20
+ export * from './image-store.js';
21
+ export * from './fanout.js';
14
22
  export * from './inner-do-registry.js';
15
- export * from './launch-journal.js';
16
- export * from './launch-pacer.js';
17
- export * from './loader-ledger.js';
18
- export * from './loader-pool.js';
23
+ export * from './fenced-work.js';
24
+ export * from './turn-budget.js';
25
+ export * from './budgets.js';
26
+ export * from './isolate-pool.js';
19
27
  export * from './process-fabric.js';
20
28
  export * from './process-host.js';
21
29
  export * from './workerd-facet-host.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -6,16 +6,24 @@
6
6
  * at resolution; non-workerd consumers (tests, tooling) import the subpath
7
7
  * modules they need instead.
8
8
  */
9
- export * from './alarms.js';
9
+ export * from './generation.js';
10
+ export * from './timers.js';
11
+ export * from './outbox.js';
12
+ export * from './journal.js';
13
+ export * from './do-calls.js';
14
+ export * from './facet-pool.js';
15
+ export * from './derived.js';
16
+ export * from './connections.js';
17
+ export * from './sealed.js';
10
18
  export * from './bindings.js';
11
- export * from './ctx-exports.js';
12
- export * from './facet-image-store.js';
13
- export * from './fanout-pool.js';
19
+ export * from './composition.js';
20
+ export * from './image-store.js';
21
+ export * from './fanout.js';
14
22
  export * from './inner-do-registry.js';
15
- export * from './launch-journal.js';
16
- export * from './launch-pacer.js';
17
- export * from './loader-ledger.js';
18
- export * from './loader-pool.js';
23
+ export * from './fenced-work.js';
24
+ export * from './turn-budget.js';
25
+ export * from './budgets.js';
26
+ export * from './isolate-pool.js';
19
27
  export * from './process-fabric.js';
20
28
  export * from './process-host.js';
21
29
  export * from './workerd-facet-host.js';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * loader-pool.ts — Nimbus loader-isolate pool based on cloudflare-parallel.
2
+ * isolate-pool.ts — Nimbus loader-isolate pool based on cloudflare-parallel.
3
3
  *
4
4
  * Adds Nimbus-specific behavior to the upstream pool design:
5
5
  * 1. **Stable-slot isolate reuse**. Upstream's #counter++ gives every
@@ -14,7 +14,7 @@
14
14
  * reach https://registry.npmjs.org without a proxy binding).
15
15
  * 3. **Supervisor autoinjection**. The pool grabs the embedder's
16
16
  * registered supervisor entrypoint stub (see `supervisorEntrypoint` in
17
- * ctx-exports.ts) and forwards it as `env.SUPERVISOR` to every facet,
17
+ * composition.ts) and forwards it as `env.SUPERVISOR` to every facet,
18
18
  * same pattern as git-network-facet.ts. Callers can add more bindings
19
19
  * via `extraBindings`.
20
20
  * 4. **Fail-loud defaults**: timeout 60s, retries 0, onError 'throw'.
@@ -38,11 +38,11 @@ export type FacetTaskFn<A, R> = {
38
38
  task(args: A, env: FacetBindings): R | Promise<R>;
39
39
  }['task'];
40
40
  /** The one binding a pool needs off whichever env its host hands it. */
41
- export interface LoaderPoolEnv {
41
+ export interface IsolatePoolEnv {
42
42
  LOADER?: WorkerLoader;
43
43
  }
44
- /** Options handed to LoaderPool's constructor. */
45
- export interface LoaderPoolOptions {
44
+ /** Options handed to IsolatePool's constructor. */
45
+ export interface IsolatePoolOptions {
46
46
  /** Maximum concurrent in-flight facets. Default 4. */
47
47
  concurrency?: number;
48
48
  /** Per-task timeout in ms. Default 60_000. */
@@ -79,8 +79,8 @@ export interface LoaderPoolOptions {
79
79
  * Override the `doId` baked into the auto-injected SUPERVISOR binding.
80
80
  * Default: `ctx.id.toString()` (the DO that constructs the pool).
81
81
  *
82
- * Used by FanoutPool's peer-DO branch (peer-DO fanout): peer DOs
83
- * construct their per-task LoaderPool from inside
82
+ * Used by Fanout's peer-DO branch (peer-DO fanout): peer DOs
83
+ * construct their per-task IsolatePool from inside
84
84
  * `_rpcFanoutExecute`, where `ctx` is the PEER DO's ctx. Without this
85
85
  * override the peer's auto-injected SUPERVISOR routes back to the
86
86
  * peer DO itself — so writes (e.g. install-batch-facet's
@@ -151,7 +151,7 @@ export interface LoaderPoolOptions {
151
151
  wasmModules?: Record<string, ArrayBuffer>;
152
152
  }
153
153
  /** Per-call override (merged with pool defaults). */
154
- export interface LoaderCallOptions {
154
+ export interface IsolateCallOptions {
155
155
  timeoutMs?: number;
156
156
  retries?: number;
157
157
  /**
@@ -186,7 +186,7 @@ export interface LoaderCallOptions {
186
186
  wasmModules?: Record<string, ArrayBuffer>;
187
187
  }
188
188
  /** Per-map override. Adds onError strategy for partial failures. */
189
- export interface LoaderMapOptions extends LoaderCallOptions {
189
+ export interface IsolateMapOptions extends IsolateCallOptions {
190
190
  /** Concurrency override for this call. Defaults to pool's concurrency. */
191
191
  concurrency?: number;
192
192
  /**
@@ -216,7 +216,7 @@ export declare function assembleLoaderWorkerModuleSource(options: LoaderWorkerMo
216
216
  *
217
217
  * Typical use:
218
218
  *
219
- * const pool = new LoaderPool(env, ctx, {
219
+ * const pool = new IsolatePool(env, ctx, {
220
220
  * concurrency: 4,
221
221
  * tag: 'npm-install',
222
222
  * });
@@ -225,10 +225,10 @@ export declare function assembleLoaderWorkerModuleSource(options: LoaderWorkerMo
225
225
  * toFetch,
226
226
  * );
227
227
  */
228
- export declare class LoaderPool {
228
+ export declare class IsolatePool {
229
229
  #private;
230
230
  private readonly loader;
231
- /** The hosting actor, as the loader-ledger's per-DO key. */
231
+ /** The hosting actor, as the loader budget ledger's per-DO key. */
232
232
  private readonly ctx;
233
233
  private readonly concurrency;
234
234
  private readonly defaultTimeoutMs;
@@ -240,7 +240,7 @@ export declare class LoaderPool {
240
240
  private readonly preambleHash;
241
241
  /**
242
242
  * WASM modules to ship in the LOADER `modules` map. See
243
- * LoaderPoolOptions.wasmModules for the rationale. Stored in
243
+ * IsolatePoolOptions.wasmModules for the rationale. Stored in
244
244
  * insertion order so the per-import preamble we generate matches
245
245
  * across pool dispatches (cache-key stability).
246
246
  */
@@ -262,25 +262,25 @@ export declare class LoaderPool {
262
262
  * 12 chars is enough entropy for DO ids to collide-free per process.
263
263
  */
264
264
  private readonly doIdShort;
265
- constructor(env: unknown, ctx: DurableObjectState, opts?: LoaderPoolOptions);
265
+ constructor(env: unknown, ctx: DurableObjectState, opts?: IsolatePoolOptions);
266
266
  /** Effective concurrency used when no per-call override is supplied. */
267
267
  get defaultConcurrency(): number;
268
268
  /**
269
269
  * Run `fn` once with `arg` on a slot isolate. Returns the result or
270
270
  * throws TimeoutError / RetryExhaustedError / ExecutionError.
271
271
  */
272
- submit<T, R>(fn: FacetTaskFn<T, R>, arg: T, opts?: LoaderCallOptions): Promise<Awaited<R>>;
272
+ submit<T, R>(fn: FacetTaskFn<T, R>, arg: T, opts?: IsolateCallOptions): Promise<Awaited<R>>;
273
273
  /**
274
274
  * Run `fn` on every item in `items`, at most `concurrency` at a time,
275
275
  * pinned to stable slots so warm isolates are reused.
276
276
  *
277
277
  * Results are returned in input order. Failure handling per `onError`.
278
278
  */
279
- map<T, R>(fn: FacetTaskFn<T, R>, items: T[], opts?: LoaderMapOptions): Promise<Array<Awaited<R> | null>>;
279
+ map<T, R>(fn: FacetTaskFn<T, R>, items: T[], opts?: IsolateMapOptions): Promise<Array<Awaited<R> | null>>;
280
280
  /**
281
281
  * Same shape as `map`, but accepts a pre-serialized function source
282
282
  * string instead of a live function reference. Used by
283
- * `FanoutPool`'s peer-DO leg, where the function was already
283
+ * `Fanout`'s peer-DO leg, where the function was already
284
284
  * serialized on the coordinator side and forwarded over RPC.
285
285
  *
286
286
  * The fnSource MUST be the output of `serializeFunction(fn)`
@@ -294,7 +294,7 @@ export declare class LoaderPool {
294
294
  * No fn-validation runs here (it already ran on the coordinator);
295
295
  * the peer trusts the caller to forward a valid serialization.
296
296
  */
297
- mapSource<T, R>(fnSource: string, items: T[], opts?: LoaderMapOptions): Promise<Array<Awaited<R> | null>>;
297
+ mapSource<T, R>(fnSource: string, items: T[], opts?: IsolateMapOptions): Promise<Array<Awaited<R> | null>>;
298
298
  /**
299
299
  * Release any RPC stubs held by the pool. Call this once the caller
300
300
  * is done with the pool (post-`map`/`submit`) so the underlying
@@ -312,4 +312,4 @@ export declare class LoaderPool {
312
312
  */
313
313
  dispose(): void;
314
314
  }
315
- //# sourceMappingURL=loader-pool.d.ts.map
315
+ //# sourceMappingURL=isolate-pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isolate-pool.d.ts","sourceRoot":"","sources":["../src/isolate-pool.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * loader-pool.ts — Nimbus loader-isolate pool based on cloudflare-parallel.
2
+ * isolate-pool.ts — Nimbus loader-isolate pool based on cloudflare-parallel.
3
3
  *
4
4
  * Adds Nimbus-specific behavior to the upstream pool design:
5
5
  * 1. **Stable-slot isolate reuse**. Upstream's #counter++ gives every
@@ -14,7 +14,7 @@
14
14
  * reach https://registry.npmjs.org without a proxy binding).
15
15
  * 3. **Supervisor autoinjection**. The pool grabs the embedder's
16
16
  * registered supervisor entrypoint stub (see `supervisorEntrypoint` in
17
- * ctx-exports.ts) and forwards it as `env.SUPERVISOR` to every facet,
17
+ * composition.ts) and forwards it as `env.SUPERVISOR` to every facet,
18
18
  * same pattern as git-network-facet.ts. Callers can add more bindings
19
19
  * via `extraBindings`.
20
20
  * 4. **Fail-loud defaults**: timeout 60s, retries 0, onError 'throw'.
@@ -24,13 +24,13 @@
24
24
  * and binding types used by this implementation.
25
25
  */
26
26
  import { CF_COMPAT_DATE } from '@nimbus-sh/core/constants.js';
27
- import { supervisorEntrypoint } from './ctx-exports.js';
28
- import { disposeRpcResource } from '@nimbus-sh/core/_shared/rpc-dispose.js';
27
+ import { supervisorEntrypoint } from './composition.js';
28
+ import { disposeRpcResource } from '@nimbus-sh/platform/rpc-dispose.js';
29
29
  import { serializeFunction, hashSource } from './vendor/serialize.js';
30
- import { beginLoaderFetch, recordLoaderId, withDynamicWorkerCapNamed } from './loader-ledger.js';
31
- import { assertModuleMapWithinCodeLimit } from './workerd-facet-host.js';
32
- import { recordFailure, setLastFacetId, getLastRpcFrame } from '@nimbus-sh/core/observability/oom-discriminator.js';
33
- import { classifyError } from '@nimbus-sh/core/observability/oom-classify.js';
30
+ import { beginLoaderFetch, recordLoaderId, withDynamicWorkerCapNamed } from './budgets.js';
31
+ import { assertModuleMapWithinCodeLimit } from './budgets.js';
32
+ import { recordFailure, setLastFacetId, getLastRpcFrame } from '@nimbus-sh/platform/oom-discriminator.js';
33
+ import { classifyError } from '@nimbus-sh/platform/oom-classify.js';
34
34
  import { BindingError, ExecutionError, RetryExhaustedError, TimeoutError, } from './vendor/errors.js';
35
35
  /**
36
36
  * esbuild runtime helpers re-declared at the top of every generated facet
@@ -95,7 +95,7 @@ export function assembleLoaderWorkerModuleSource(options) {
95
95
  *
96
96
  * Typical use:
97
97
  *
98
- * const pool = new LoaderPool(env, ctx, {
98
+ * const pool = new IsolatePool(env, ctx, {
99
99
  * concurrency: 4,
100
100
  * tag: 'npm-install',
101
101
  * });
@@ -104,9 +104,9 @@ export function assembleLoaderWorkerModuleSource(options) {
104
104
  * toFetch,
105
105
  * );
106
106
  */
107
- export class LoaderPool {
107
+ export class IsolatePool {
108
108
  loader;
109
- /** The hosting actor, as the loader-ledger's per-DO key. */
109
+ /** The hosting actor, as the loader budget ledger's per-DO key. */
110
110
  ctx;
111
111
  concurrency;
112
112
  defaultTimeoutMs;
@@ -118,7 +118,7 @@ export class LoaderPool {
118
118
  preambleHash;
119
119
  /**
120
120
  * WASM modules to ship in the LOADER `modules` map. See
121
- * LoaderPoolOptions.wasmModules for the rationale. Stored in
121
+ * IsolatePoolOptions.wasmModules for the rationale. Stored in
122
122
  * insertion order so the per-import preamble we generate matches
123
123
  * across pool dispatches (cache-key stability).
124
124
  */
@@ -145,7 +145,7 @@ export class LoaderPool {
145
145
  // claim is checked on the next line.
146
146
  const loader = env?.LOADER;
147
147
  if (!loader || typeof loader.get !== 'function') {
148
- throw new BindingError('LoaderPool: env.LOADER binding missing or invalid. ' +
148
+ throw new BindingError('IsolatePool: env.LOADER binding missing or invalid. ' +
149
149
  'Add a [[worker_loaders]] entry to wrangler.jsonc.');
150
150
  }
151
151
  this.loader = loader;
@@ -174,12 +174,12 @@ export class LoaderPool {
174
174
  // Reached only when a caller broke the declared option type, so the
175
175
  // value is whatever it really was rather than the ArrayBuffer here.
176
176
  const got = bytes?.constructor?.name;
177
- throw new BindingError(`LoaderPool: wasmModules['${name}'] must be ArrayBuffer ` +
177
+ throw new BindingError(`IsolatePool: wasmModules['${name}'] must be ArrayBuffer ` +
178
178
  `(got ${got || typeof bytes}).`);
179
179
  }
180
180
  const id = name.replace(/[^A-Za-z0-9_]/g, '_').replace(/^[^A-Za-z_]/, '_');
181
181
  if (seenIds.has(id)) {
182
- throw new BindingError(`LoaderPool: wasmModules key '${name}' collides with another after ` +
182
+ throw new BindingError(`IsolatePool: wasmModules key '${name}' collides with another after ` +
183
183
  `identifier-sanitisation (id='${id}'). Pick distinct module names.`);
184
184
  }
185
185
  seenIds.add(id);
@@ -259,17 +259,17 @@ export class LoaderPool {
259
259
  for (const [name, bytes] of Object.entries(perCall)) {
260
260
  if (!(bytes instanceof ArrayBuffer)) {
261
261
  const got = bytes?.constructor?.name;
262
- throw new BindingError(`LoaderPool: per-call wasmModules['${name}'] must be ` +
262
+ throw new BindingError(`IsolatePool: per-call wasmModules['${name}'] must be ` +
263
263
  `ArrayBuffer (got ${got || typeof bytes}).`);
264
264
  }
265
265
  const id = name.replace(/[^A-Za-z0-9_]/g, '_').replace(/^[^A-Za-z_]/, '_');
266
266
  if (ctorIds.has(id)) {
267
- throw new BindingError(`LoaderPool: per-call wasmModules key '${name}' (sanitised ` +
267
+ throw new BindingError(`IsolatePool: per-call wasmModules key '${name}' (sanitised ` +
268
268
  `id='${id}') collides with a constructor-time wasm module. ` +
269
269
  `Per-call modules cannot shadow pool-defaults. Pick a distinct name.`);
270
270
  }
271
271
  if (seen.has(id)) {
272
- throw new BindingError(`LoaderPool: per-call wasmModules key '${name}' (sanitised ` +
272
+ throw new BindingError(`IsolatePool: per-call wasmModules key '${name}' (sanitised ` +
273
273
  `id='${id}') collides with another per-call key. Pick distinct names.`);
274
274
  }
275
275
  seen.add(id);
@@ -352,7 +352,7 @@ export class LoaderPool {
352
352
  // re-import (the user fn is serialized via fn.toString and doesn't
353
353
  // carry import statements).
354
354
  //
355
- // Per-call entries (passed via LoaderCallOptions.wasmModules
355
+ // Per-call entries (passed via IsolateCallOptions.wasmModules
356
356
  // — used by the wasm-runner shell command) are appended to the same
357
357
  // table. Naming collision with constructor entries is rejected
358
358
  // upstream in #materialisePerCallWasm so the import block here
@@ -584,7 +584,7 @@ export class LoaderPool {
584
584
  /**
585
585
  * Same shape as `map`, but accepts a pre-serialized function source
586
586
  * string instead of a live function reference. Used by
587
- * `FanoutPool`'s peer-DO leg, where the function was already
587
+ * `Fanout`'s peer-DO leg, where the function was already
588
588
  * serialized on the coordinator side and forwarded over RPC.
589
589
  *
590
590
  * The fnSource MUST be the output of `serializeFunction(fn)`