@nimbus-sh/worker 0.5.0 → 0.7.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 (164) hide show
  1. package/README.md +2 -2
  2. package/dist/_shared/preview-host.d.ts +26 -8
  3. package/dist/_shared/preview-host.d.ts.map +1 -1
  4. package/dist/_shared/preview-host.js +51 -11
  5. package/dist/cirrus-npm-cjs.generated.d.ts +1 -1
  6. package/dist/cirrus-npm-cjs.generated.js +1 -1
  7. package/dist/facets/cirrus-real.d.ts.map +1 -1
  8. package/dist/facets/cirrus-real.js +6 -5
  9. package/dist/facets/compose.d.ts +148 -0
  10. package/dist/facets/compose.d.ts.map +1 -0
  11. package/dist/facets/compose.js +146 -0
  12. package/dist/facets/durable-images.d.ts +18 -5
  13. package/dist/facets/durable-images.d.ts.map +1 -1
  14. package/dist/facets/durable-images.js +36 -7
  15. package/dist/facets/durable-slots.d.ts +1 -1
  16. package/dist/facets/durable-slots.d.ts.map +1 -1
  17. package/dist/facets/durable-slots.js +2 -1
  18. package/dist/facets/esbuild-bundle-pool.d.ts +49 -0
  19. package/dist/facets/esbuild-bundle-pool.d.ts.map +1 -0
  20. package/dist/facets/esbuild-bundle-pool.js +122 -0
  21. package/dist/facets/exec-telemetry.d.ts +8 -0
  22. package/dist/facets/exec-telemetry.d.ts.map +1 -1
  23. package/dist/facets/manager.d.ts +337 -123
  24. package/dist/facets/manager.d.ts.map +1 -1
  25. package/dist/facets/manager.js +1100 -449
  26. package/dist/facets/process.d.ts +20 -16
  27. package/dist/facets/process.d.ts.map +1 -1
  28. package/dist/facets/process.js +59 -28
  29. package/dist/facets/real-vite-fs-shim.js +2 -2
  30. package/dist/facets/resident-identity.d.ts +23 -0
  31. package/dist/facets/resident-identity.d.ts.map +1 -0
  32. package/dist/facets/resident-identity.js +31 -0
  33. package/dist/facets/vite-dev-server.d.ts +25 -47
  34. package/dist/facets/vite-dev-server.d.ts.map +1 -1
  35. package/dist/facets/vite-dev-server.js +185 -219
  36. package/dist/facets/wasm-image-digest.d.ts +25 -0
  37. package/dist/facets/wasm-image-digest.d.ts.map +1 -0
  38. package/dist/facets/wasm-image-digest.js +25 -0
  39. package/dist/facets/wasm-swap-registry.d.ts +51 -92
  40. package/dist/facets/wasm-swap-registry.d.ts.map +1 -1
  41. package/dist/facets/wasm-swap-registry.js +68 -227
  42. package/dist/git/commands.d.ts +0 -1
  43. package/dist/git/commands.d.ts.map +1 -1
  44. package/dist/git/commands.js +0 -3
  45. package/dist/git/network-facet.d.ts.map +1 -1
  46. package/dist/git/network-facet.js +6 -5
  47. package/dist/loaders/child-process/spawn-pool.d.ts +3 -2
  48. package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -1
  49. package/dist/loaders/child-process/spawn-pool.js +3 -2
  50. package/dist/loaders/generated-workers.d.ts +2 -2
  51. package/dist/loaders/generated-workers.d.ts.map +1 -1
  52. package/dist/loaders/generated-workers.js +3 -3
  53. package/dist/loaders/npm-resolve-preamble.d.ts +3 -6
  54. package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -1
  55. package/dist/loaders/npm-resolve-preamble.js +34 -108
  56. package/dist/node-shims-artifact.generated.js +3 -3
  57. package/dist/npm/install-batch-facet.d.ts +3 -2
  58. package/dist/npm/install-batch-facet.d.ts.map +1 -1
  59. package/dist/npm/install-batch-facet.js +45 -15
  60. package/dist/npm/installer.d.ts +22 -17
  61. package/dist/npm/installer.d.ts.map +1 -1
  62. package/dist/npm/installer.js +209 -193
  63. package/dist/npm/pre-bundle-facet.d.ts +1 -1
  64. package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
  65. package/dist/npm/pre-bundle-facet.js +1 -1
  66. package/dist/npm/resolve-facet.d.ts +6 -0
  67. package/dist/npm/resolve-facet.d.ts.map +1 -1
  68. package/dist/npm/resolve-one-facet.d.ts +21 -20
  69. package/dist/npm/resolve-one-facet.d.ts.map +1 -1
  70. package/dist/npm/resolve-one-facet.js +85 -84
  71. package/dist/npm/semver.d.ts +59 -0
  72. package/dist/npm/semver.d.ts.map +1 -0
  73. package/dist/npm/semver.js +226 -0
  74. package/dist/router/index.d.ts.map +1 -1
  75. package/dist/router/index.js +47 -10
  76. package/dist/router/public-directory.d.ts +5 -1
  77. package/dist/router/public-directory.d.ts.map +1 -1
  78. package/dist/router/public-directory.js +9 -2
  79. package/dist/router/remote-api.d.ts +18 -0
  80. package/dist/router/remote-api.d.ts.map +1 -1
  81. package/dist/router/remote-api.js +64 -5
  82. package/dist/runtime/cpython-resident.d.ts.map +1 -1
  83. package/dist/runtime/cpython-resident.js +3 -1
  84. package/dist/runtime/html-entrypoint.d.ts +7 -0
  85. package/dist/runtime/html-entrypoint.d.ts.map +1 -1
  86. package/dist/runtime/html-entrypoint.js +18 -2
  87. package/dist/runtime/node-runner.d.ts +3 -0
  88. package/dist/runtime/node-runner.d.ts.map +1 -1
  89. package/dist/runtime/node-runner.js +7 -0
  90. package/dist/runtime/node-shims.d.ts.map +1 -1
  91. package/dist/runtime/node-shims.js +305 -29
  92. package/dist/runtime/opencode-facet-runner.js +2 -2
  93. package/dist/runtime/package-manager.d.ts +44 -1
  94. package/dist/runtime/package-manager.d.ts.map +1 -1
  95. package/dist/runtime/package-manager.js +168 -3
  96. package/dist/runtime/ruby-resident.d.ts.map +1 -1
  97. package/dist/runtime/ruby-resident.js +2 -0
  98. package/dist/session/agent.d.ts.map +1 -1
  99. package/dist/session/agent.js +28 -1
  100. package/dist/session/helpers.d.ts +38 -0
  101. package/dist/session/helpers.d.ts.map +1 -1
  102. package/dist/session/helpers.js +76 -0
  103. package/dist/session/init-phases.d.ts +1 -1
  104. package/dist/session/init-phases.d.ts.map +1 -1
  105. package/dist/session/init-phases.js +2 -12
  106. package/dist/session/init.d.ts +15 -1
  107. package/dist/session/init.d.ts.map +1 -1
  108. package/dist/session/init.js +91 -603
  109. package/dist/session/keys.d.ts +4 -0
  110. package/dist/session/keys.d.ts.map +1 -1
  111. package/dist/session/keys.js +4 -0
  112. package/dist/session/nimbus-session.d.ts +90 -23
  113. package/dist/session/nimbus-session.d.ts.map +1 -1
  114. package/dist/session/nimbus-session.js +200 -110
  115. package/dist/session/npm-install-port.d.ts +16 -0
  116. package/dist/session/npm-install-port.d.ts.map +1 -0
  117. package/dist/session/npm-install-port.js +37 -0
  118. package/dist/session/port-capability.d.ts +90 -0
  119. package/dist/session/port-capability.d.ts.map +1 -1
  120. package/dist/session/port-capability.js +219 -9
  121. package/dist/session/programmatic.d.ts +125 -5
  122. package/dist/session/programmatic.d.ts.map +1 -1
  123. package/dist/session/programmatic.js +439 -89
  124. package/dist/session/routes.d.ts +39 -17
  125. package/dist/session/routes.d.ts.map +1 -1
  126. package/dist/session/routes.js +67 -131
  127. package/dist/session/rpc.d.ts +39 -16
  128. package/dist/session/rpc.d.ts.map +1 -1
  129. package/dist/session/rpc.js +137 -130
  130. package/dist/session/serving-port.d.ts +27 -0
  131. package/dist/session/serving-port.d.ts.map +1 -0
  132. package/dist/session/serving-port.js +27 -0
  133. package/dist/session/start-real-vite.d.ts +12 -0
  134. package/dist/session/start-real-vite.d.ts.map +1 -1
  135. package/dist/session/start-real-vite.js +4 -4
  136. package/dist/session/supervisor-op.d.ts +41 -4
  137. package/dist/session/supervisor-op.d.ts.map +1 -1
  138. package/dist/session/supervisor-op.js +101 -73
  139. package/dist/session/supervisor-rpc.d.ts +7 -7
  140. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  141. package/dist/session/supervisor-rpc.js +6 -3
  142. package/dist/session/vite-command.d.ts +18 -0
  143. package/dist/session/vite-command.d.ts.map +1 -0
  144. package/dist/session/vite-command.js +609 -0
  145. package/dist/session/ws.d.ts +61 -0
  146. package/dist/session/ws.d.ts.map +1 -1
  147. package/dist/session/ws.js +122 -12
  148. package/dist/shell/npm-bin-entrypoints.d.ts +0 -35
  149. package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
  150. package/dist/shell/npm-bin-entrypoints.js +12 -85
  151. package/package.json +24 -4
  152. package/public/_assets/runtime/{node-shims-9e1380d9ee430fcc.js → node-shims-8f8c64edd2a1f0f5.js} +305 -29
  153. package/public/index.html +1 -1
  154. package/scripts/bundle-facet-workers.mjs +1 -1
  155. package/scripts/bundle-npm-cjs.mjs +2 -2
  156. package/dist/facets/on-demand-bundle-gate.d.ts +0 -65
  157. package/dist/facets/on-demand-bundle-gate.d.ts.map +0 -1
  158. package/dist/facets/on-demand-bundle-gate.js +0 -125
  159. package/dist/npm/install-args.d.ts +0 -21
  160. package/dist/npm/install-args.d.ts.map +0 -1
  161. package/dist/npm/install-args.js +0 -70
  162. package/dist/npm/npm-log.d.ts +0 -31
  163. package/dist/npm/npm-log.d.ts.map +0 -1
  164. package/dist/npm/npm-log.js +0 -51
@@ -12,16 +12,34 @@
12
12
  * durable spawn exists to survive. Durable images are kept for the
13
13
  * application's life and released only by explicit removal.
14
14
  *
15
- * Two blobs per launch: `runner` is the worker.js source text, `application`
16
- * is a JSON payload of `{ modules, env, vfsWasmModules }`. The digests in the
17
- * journal's `recipe.image` are sha256 hashes of those two payloads, and a
18
- * self-owned spawn mints them at spawn time; an embedder-owned spawn is given
19
- * them by its own bookkeeping instead.
15
+ * Two blobs per launch: `runner` is the main module's source text,
16
+ * `application` is a JSON payload of `{ modules, env, vfsWasmModules }` plus,
17
+ * when the launch set them, `vfsTextModules`, `mainModule` and `startArgs`.
18
+ * The digests in the journal's `recipe.image` are sha256 hashes of those two
19
+ * payloads, and a self-owned spawn mints them at spawn time; an
20
+ * embedder-owned spawn is given them by its own bookkeeping instead.
20
21
  */
21
22
  import { CRED_KERNEL } from '@nimbus-sh/core/runtime/os-contracts.js';
22
23
  /** The directory every durable application's image blobs live under. */
23
24
  export const DURABLE_IMAGE_DIR = '.nimbus/images';
24
25
  const imagePath = (digest) => `${DURABLE_IMAGE_DIR}/${digest}`;
26
+ /** Remove only the owner's blobs that no other retained recipe references. */
27
+ export function purgeDurableWorkerImages(vfs, owned, retained) {
28
+ const keep = new Set([...retained].flatMap((image) => [image.runner, image.application]));
29
+ const candidates = new Set([...owned].flatMap((image) => [image.runner, image.application]));
30
+ const kernel = vfs.as(CRED_KERNEL);
31
+ let removed = 0;
32
+ for (const digest of candidates) {
33
+ if (keep.has(digest) || !/^[a-f0-9]{64}$/.test(digest))
34
+ continue;
35
+ const path = imagePath(digest);
36
+ if (!kernel.exists(path))
37
+ continue;
38
+ kernel.unlink(path);
39
+ removed += 1;
40
+ }
41
+ return removed;
42
+ }
25
43
  async function sha256Hex(text) {
26
44
  const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text));
27
45
  return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, '0')).join('');
@@ -37,10 +55,15 @@ export async function persistDurableWorkerImage(vfs, workerCode, payload) {
37
55
  kernel.mkdir(DURABLE_IMAGE_DIR, { recursive: true });
38
56
  const runner = await sha256Hex(workerCode);
39
57
  kernel.writeFile(imagePath(runner), workerCode);
58
+ // Optional members are omitted rather than written null so an unchanged
59
+ // launch keeps the digest it had before the member existed.
40
60
  const applicationPayload = JSON.stringify({
41
61
  modules: payload.modules,
42
62
  env: payload.env ?? null,
43
63
  vfsWasmModules: payload.vfsWasmModules ?? null,
64
+ ...(payload.vfsTextModules !== undefined ? { vfsTextModules: payload.vfsTextModules } : {}),
65
+ ...(payload.mainModule !== undefined ? { mainModule: payload.mainModule } : {}),
66
+ ...(payload.startArgs !== undefined ? { startArgs: payload.startArgs } : {}),
44
67
  });
45
68
  const application = await sha256Hex(applicationPayload);
46
69
  kernel.writeFile(imagePath(application), applicationPayload);
@@ -65,11 +88,17 @@ export async function resolveDurableWorkerImage(vfs, recipe) {
65
88
  return null;
66
89
  }
67
90
  const runner = new TextDecoder().decode(runnerBytes);
68
- const { modules = {}, env = null, vfsWasmModules = undefined } = JSON.parse(new TextDecoder().decode(applicationBytes));
91
+ const { modules = {}, env = null, vfsWasmModules = undefined, vfsTextModules, mainModule, startArgs, } = JSON.parse(new TextDecoder().decode(applicationBytes));
92
+ // The recipe's own record of the main module wins when the blob predates
93
+ // the member; both say the same thing for any launch written since.
94
+ const main = mainModule ?? recipe.mainModule ?? 'worker.js';
69
95
  return {
70
96
  env: env ?? null,
71
97
  globalOutbound: undefined,
72
- modules: { 'worker.js': runner, ...modules },
98
+ modules: { ...modules, [main]: runner },
99
+ mainModule: main,
100
+ ...(startArgs !== undefined ? { startArgs } : {}),
73
101
  ...(vfsWasmModules !== null ? { vfsWasmModules } : {}),
102
+ ...(vfsTextModules !== undefined ? { vfsTextModules } : {}),
74
103
  };
75
104
  }
@@ -41,5 +41,5 @@ export declare function acquireDurableFacetSlot(ctx: DurableObjectState, owner:
41
41
  * step of explicit removal, after the facet's SQLite is already gone. Answers
42
42
  * the freed name, or null when the owner held nothing.
43
43
  */
44
- export declare function freeDurableFacetSlot(ctx: DurableObjectState, owner: string): Promise<string | null>;
44
+ export declare function freeDurableFacetSlot(ctx: DurableObjectState, owner: string, beforeFree?: (name: string) => void): Promise<string | null>;
45
45
  //# sourceMappingURL=durable-slots.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"durable-slots.d.ts","sourceRoot":"","sources":["../../src/facets/durable-slots.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAUH,kDAAkD;AAClD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;;;;GASG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,kBAAkB,EACvB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,kBAAkB,EACvB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYxB"}
1
+ {"version":3,"file":"durable-slots.d.ts","sourceRoot":"","sources":["../../src/facets/durable-slots.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAUH,kDAAkD;AAClD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;;;;GASG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,kBAAkB,EACvB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,kBAAkB,EACvB,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAClC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxB"}
@@ -78,11 +78,12 @@ export async function acquireDurableFacetSlot(ctx, owner) {
78
78
  * step of explicit removal, after the facet's SQLite is already gone. Answers
79
79
  * the freed name, or null when the owner held nothing.
80
80
  */
81
- export async function freeDurableFacetSlot(ctx, owner) {
81
+ export async function freeDurableFacetSlot(ctx, owner, beforeFree) {
82
82
  return ctx.storage.transaction(async (txn) => {
83
83
  const held = await txn.get(ownerKey(owner));
84
84
  if (typeof held !== 'number')
85
85
  return null;
86
+ beforeFree?.(durableFacetName(held));
86
87
  await txn.delete(ownerKey(owner));
87
88
  const free = await txn.get(FREE_KEY);
88
89
  const freeList = Array.isArray(free) ? free.filter((n) => typeof n === 'number') : [];
@@ -0,0 +1,49 @@
1
+ /**
2
+ * EsbuildBundlePool — the session's single esbuild facet pool.
3
+ *
4
+ * Install-time pre-bundling and on-demand /@modules/ bundling dispatch the
5
+ * same worker (PRE_BUNDLE_PREAMBLE + esbuild.wasm, one slot). When each
6
+ * pipeline built its own IsolatePool the supervisor retained two copies of
7
+ * the ~12 MiB wasm bytes and warmed two loader slots per session, and the
8
+ * two pools never queued behind each other. One pool, owned by the session,
9
+ * holds one wasm copy — leased once from the shared supervisor allocation
10
+ * budget, the way the pre-bundler already accounted for its own — for as
11
+ * long as the installer and dev server live, and is disposed with them.
12
+ *
13
+ * Construction is lazy and everything heavy is imported on first use so
14
+ * the fabric/preamble/wasm subgraph stays out of the cold script-eval
15
+ * graph of sessions that never bundle.
16
+ */
17
+ import type { IsolatePool } from '@nimbus-sh/fabric/isolate-pool.js';
18
+ /** The pool surface both bundling pipelines dispatch through. */
19
+ export type BundlePool = Pick<IsolatePool, 'submit'>;
20
+ /** What a bundling pipeline receives from its host. */
21
+ export interface BundlePoolProvider {
22
+ /** The session's pool, constructed on first call. */
23
+ acquire(): Promise<BundlePool>;
24
+ }
25
+ export declare class EsbuildBundlePool implements BundlePoolProvider {
26
+ private readonly env;
27
+ private readonly ctx;
28
+ private pool;
29
+ private pending;
30
+ /** Releases the wasm bytes' supervisor credit; held until dispose(). */
31
+ private releaseWasmCredit;
32
+ /** Bumped by dispose() so a construction it interrupted tears itself down. */
33
+ private generation;
34
+ constructor(env: unknown, ctx: DurableObjectState);
35
+ /**
36
+ * Callers MUST acquire the pool before taking any slice lease: the
37
+ * first construction reserves the full supervisor budget while the wasm
38
+ * bytes are fetched, so a caller already holding slice credit would
39
+ * wait on itself.
40
+ */
41
+ acquire(): Promise<IsolatePool>;
42
+ private construct;
43
+ /**
44
+ * Tear the pool down with its host. A later acquire() constructs a fresh
45
+ * pool, so a session that installs again after a teardown still bundles.
46
+ */
47
+ dispose(): void;
48
+ }
49
+ //# sourceMappingURL=esbuild-bundle-pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"esbuild-bundle-pool.d.ts","sourceRoot":"","sources":["../../src/facets/esbuild-bundle-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AASrE,iEAAiE;AACjE,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;AAErD,uDAAuD;AACvD,MAAM,WAAW,kBAAkB;IACjC,qDAAqD;IACrD,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAChC;AAQD,qBAAa,iBAAkB,YAAW,kBAAkB;IASxD,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,GAAG;IATtB,OAAO,CAAC,IAAI,CAA4B;IACxC,OAAO,CAAC,OAAO,CAAqC;IACpD,wEAAwE;IACxE,OAAO,CAAC,iBAAiB,CAA6B;IACtD,8EAA8E;IAC9E,OAAO,CAAC,UAAU,CAAK;gBAGJ,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,kBAAkB;IAG1C;;;;;OAKG;IACH,OAAO,IAAI,OAAO,CAAC,WAAW,CAAC;YAqBjB,SAAS;IAmDvB;;;OAGG;IACH,OAAO,IAAI,IAAI;CAYhB"}
@@ -0,0 +1,122 @@
1
+ import { acquireResidentSupervisorAllocation } from '@nimbus-sh/platform/heavy-alloc-coord.js';
2
+ import { PRE_BUNDLE_CONCURRENCY, PRE_BUNDLE_SLICE_CAP_BYTES, SUPERVISOR_IN_FLIGHT_ALLOCATION_BUDGET_BYTES, } from '@nimbus-sh/platform/limits.js';
3
+ function hasAssetsFetcher(env) {
4
+ if (typeof env !== 'object' || env === null)
5
+ return false;
6
+ const assets = Reflect.get(env, 'ASSETS');
7
+ return typeof assets === 'object' && assets !== null && typeof Reflect.get(assets, 'fetch') === 'function';
8
+ }
9
+ export class EsbuildBundlePool {
10
+ env;
11
+ ctx;
12
+ pool = null;
13
+ pending = null;
14
+ /** Releases the wasm bytes' supervisor credit; held until dispose(). */
15
+ releaseWasmCredit = null;
16
+ /** Bumped by dispose() so a construction it interrupted tears itself down. */
17
+ generation = 0;
18
+ constructor(env, ctx) {
19
+ this.env = env;
20
+ this.ctx = ctx;
21
+ }
22
+ /**
23
+ * Callers MUST acquire the pool before taking any slice lease: the
24
+ * first construction reserves the full supervisor budget while the wasm
25
+ * bytes are fetched, so a caller already holding slice credit would
26
+ * wait on itself.
27
+ */
28
+ acquire() {
29
+ if (this.pool)
30
+ return Promise.resolve(this.pool);
31
+ if (this.pending)
32
+ return this.pending;
33
+ const generation = this.generation;
34
+ const pending = this.construct().then(({ pool, releaseWasmCredit }) => {
35
+ if (this.generation !== generation) {
36
+ // dispose() ran while the wasm bytes were in flight.
37
+ try {
38
+ pool.dispose();
39
+ }
40
+ catch { /* best-effort */ }
41
+ releaseWasmCredit();
42
+ throw new Error('EsbuildBundlePool: disposed during construction');
43
+ }
44
+ this.pool = pool;
45
+ this.releaseWasmCredit = releaseWasmCredit;
46
+ return pool;
47
+ }).finally(() => {
48
+ if (this.pending === pending)
49
+ this.pending = null;
50
+ });
51
+ this.pending = pending;
52
+ return pending;
53
+ }
54
+ async construct() {
55
+ if (!hasAssetsFetcher(this.env)) {
56
+ throw new Error('EsbuildBundlePool: env.ASSETS binding missing — the esbuild wasm asset cannot be fetched');
57
+ }
58
+ const env = this.env;
59
+ const [{ IsolatePool }, { PRE_BUNDLE_PREAMBLE }, { fetchEsbuildWasmBytes }] = await Promise.all([
60
+ import('@nimbus-sh/fabric/isolate-pool.js'),
61
+ import('../loaders/pre-bundle-preamble.js'),
62
+ import('../runtime/esbuild-wasm-bytes.js'),
63
+ ]);
64
+ // Resident, and sized to what the pool will KEEP — not the whole budget.
65
+ //
66
+ // The full-budget claim this used to make is grantable exactly once: the
67
+ // lease it shrinks to becomes a permanent floor, so a second pool
68
+ // construction asked for 40 MiB against 28.1 MiB that could ever be free
69
+ // and parked in the FIFO with no error and no CPU. Measured on a
70
+ // deployed worker: capacity 41,943,040, resident 11,907,565, queued 1 for
71
+ // 222 s with the isolate healthy the whole time — and because the queue
72
+ // refuses everyone behind a waiter, the whole session stopped making
73
+ // progress. The bound below is the same one the payload is checked
74
+ // against, so the slice cap stays free for the work the pool exists for.
75
+ const maxRetainedWasmBytes = SUPERVISOR_IN_FLIGHT_ALLOCATION_BUDGET_BYTES - PRE_BUNDLE_SLICE_CAP_BYTES;
76
+ const setupAllocation = await acquireResidentSupervisorAllocation(maxRetainedWasmBytes);
77
+ let retained = false;
78
+ try {
79
+ const wasmBytes = await fetchEsbuildWasmBytes(env);
80
+ if (wasmBytes.byteLength > maxRetainedWasmBytes) {
81
+ throw new RangeError(`esbuild wasm payload ${wasmBytes.byteLength} exceeds the ${maxRetainedWasmBytes}-byte retained budget`);
82
+ }
83
+ // IsolatePool keeps the constructor-time module bytes until dispose(),
84
+ // so retain their exact credit rather than treating construction as a
85
+ // handoff that immediately frees the ArrayBuffer.
86
+ setupAllocation.shrinkTo(wasmBytes.byteLength);
87
+ const pool = new IsolatePool(env, this.ctx, {
88
+ concurrency: PRE_BUNDLE_CONCURRENCY,
89
+ timeoutMs: 60_000,
90
+ retries: 0,
91
+ tag: 'esbuild-bundle',
92
+ preamble: PRE_BUNDLE_PREAMBLE,
93
+ wasmModules: { 'esbuild.wasm': wasmBytes },
94
+ });
95
+ retained = true;
96
+ return { pool, releaseWasmCredit: setupAllocation.release };
97
+ }
98
+ finally {
99
+ if (!retained)
100
+ setupAllocation.release();
101
+ }
102
+ }
103
+ /**
104
+ * Tear the pool down with its host. A later acquire() constructs a fresh
105
+ * pool, so a session that installs again after a teardown still bundles.
106
+ */
107
+ dispose() {
108
+ this.generation++;
109
+ const pool = this.pool;
110
+ this.pool = null;
111
+ this.pending = null;
112
+ if (pool) {
113
+ try {
114
+ pool.dispose();
115
+ }
116
+ catch { /* best-effort */ }
117
+ }
118
+ const release = this.releaseWasmCredit;
119
+ this.releaseWasmCredit = null;
120
+ release?.();
121
+ }
122
+ }
@@ -57,6 +57,14 @@ export interface ExecTelemetryRecord {
57
57
  fsRpcReads: number;
58
58
  /** Whether the prefetch bundle was served from cache (no VFS walk). */
59
59
  cacheHit: boolean;
60
+ /**
61
+ * Durable Object turns the bundle build was paged across — the alarm
62
+ * round trips `TurnBudget` took before the process could start. 0 when the
63
+ * whole build fit in the turn that asked for it, which is every cache hit
64
+ * and every small tree; the count is what says a large tree cost turns
65
+ * rather than a held thread.
66
+ */
67
+ turns: number;
60
68
  /** Exit code, for cross-referencing telemetry against failures. */
61
69
  exitCode: number;
62
70
  /** Wall-clock at record time (supervisor side). */
@@ -1 +1 @@
1
- {"version":3,"file":"exec-telemetry.d.ts","sourceRoot":"","sources":["../../src/facets/exec-telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,yDAAyD;AACzD,MAAM,WAAW,mBAAmB;IAClC,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd;0EACsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;;;;OASG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB;;gEAE4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,QAAQ,EAAE,OAAO,CAAC;IAClB,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,CAC7B,mBAAmB,EACnB,QAAQ,GAAG,OAAO,GAAG,gBAAgB,GAAG,aAAa,GAAG,eAAe,GAAG,eAAe,CAC1F,CAAC;AAKF;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,mBAAmB,GAAG,IAAI,CAIlE;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,IAAI,mBAAmB,EAAE,CAEzD;AAED,6EAA6E;AAC7E,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
1
+ {"version":3,"file":"exec-telemetry.d.ts","sourceRoot":"","sources":["../../src/facets/exec-telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,yDAAyD;AACzD,MAAM,WAAW,mBAAmB;IAClC,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd;0EACsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;;;;OASG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB;;gEAE4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,CAC7B,mBAAmB,EACnB,QAAQ,GAAG,OAAO,GAAG,gBAAgB,GAAG,aAAa,GAAG,eAAe,GAAG,eAAe,CAC1F,CAAC;AAKF;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,mBAAmB,GAAG,IAAI,CAIlE;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,IAAI,mBAAmB,EAAE,CAEzD;AAED,6EAA6E;AAC7E,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}