@nimbus-sh/worker 0.6.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 (133) hide show
  1. package/README.md +2 -2
  2. package/dist/cirrus-npm-cjs.generated.d.ts +1 -1
  3. package/dist/cirrus-npm-cjs.generated.js +1 -1
  4. package/dist/facets/cirrus-real.d.ts.map +1 -1
  5. package/dist/facets/cirrus-real.js +6 -5
  6. package/dist/facets/compose.d.ts +148 -0
  7. package/dist/facets/compose.d.ts.map +1 -0
  8. package/dist/facets/compose.js +146 -0
  9. package/dist/facets/durable-images.d.ts +9 -5
  10. package/dist/facets/durable-images.d.ts.map +1 -1
  11. package/dist/facets/durable-images.js +17 -7
  12. package/dist/facets/esbuild-bundle-pool.d.ts +49 -0
  13. package/dist/facets/esbuild-bundle-pool.d.ts.map +1 -0
  14. package/dist/facets/esbuild-bundle-pool.js +122 -0
  15. package/dist/facets/exec-telemetry.d.ts +8 -0
  16. package/dist/facets/exec-telemetry.d.ts.map +1 -1
  17. package/dist/facets/manager.d.ts +240 -109
  18. package/dist/facets/manager.d.ts.map +1 -1
  19. package/dist/facets/manager.js +661 -337
  20. package/dist/facets/process.d.ts +20 -16
  21. package/dist/facets/process.d.ts.map +1 -1
  22. package/dist/facets/process.js +59 -28
  23. package/dist/facets/real-vite-fs-shim.js +2 -2
  24. package/dist/facets/vite-dev-server.d.ts +25 -47
  25. package/dist/facets/vite-dev-server.d.ts.map +1 -1
  26. package/dist/facets/vite-dev-server.js +185 -219
  27. package/dist/facets/wasm-image-digest.d.ts +25 -0
  28. package/dist/facets/wasm-image-digest.d.ts.map +1 -0
  29. package/dist/facets/wasm-image-digest.js +25 -0
  30. package/dist/facets/wasm-swap-registry.d.ts +51 -92
  31. package/dist/facets/wasm-swap-registry.d.ts.map +1 -1
  32. package/dist/facets/wasm-swap-registry.js +68 -227
  33. package/dist/git/network-facet.d.ts.map +1 -1
  34. package/dist/git/network-facet.js +6 -5
  35. package/dist/loaders/child-process/spawn-pool.d.ts +3 -2
  36. package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -1
  37. package/dist/loaders/child-process/spawn-pool.js +3 -2
  38. package/dist/loaders/generated-workers.d.ts +2 -2
  39. package/dist/loaders/generated-workers.d.ts.map +1 -1
  40. package/dist/loaders/generated-workers.js +3 -3
  41. package/dist/loaders/npm-resolve-preamble.d.ts +3 -6
  42. package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -1
  43. package/dist/loaders/npm-resolve-preamble.js +34 -108
  44. package/dist/node-shims-artifact.generated.js +3 -3
  45. package/dist/npm/install-batch-facet.d.ts +3 -2
  46. package/dist/npm/install-batch-facet.d.ts.map +1 -1
  47. package/dist/npm/install-batch-facet.js +45 -15
  48. package/dist/npm/installer.d.ts +22 -17
  49. package/dist/npm/installer.d.ts.map +1 -1
  50. package/dist/npm/installer.js +209 -193
  51. package/dist/npm/pre-bundle-facet.d.ts +1 -1
  52. package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
  53. package/dist/npm/pre-bundle-facet.js +1 -1
  54. package/dist/npm/resolve-facet.d.ts +6 -0
  55. package/dist/npm/resolve-facet.d.ts.map +1 -1
  56. package/dist/npm/resolve-one-facet.d.ts +21 -20
  57. package/dist/npm/resolve-one-facet.d.ts.map +1 -1
  58. package/dist/npm/resolve-one-facet.js +85 -84
  59. package/dist/npm/semver.d.ts +59 -0
  60. package/dist/npm/semver.d.ts.map +1 -0
  61. package/dist/npm/semver.js +226 -0
  62. package/dist/router/remote-api.js +31 -1
  63. package/dist/runtime/html-entrypoint.d.ts +7 -0
  64. package/dist/runtime/html-entrypoint.d.ts.map +1 -1
  65. package/dist/runtime/html-entrypoint.js +18 -2
  66. package/dist/runtime/node-runner.d.ts +3 -0
  67. package/dist/runtime/node-runner.d.ts.map +1 -1
  68. package/dist/runtime/node-runner.js +7 -0
  69. package/dist/runtime/node-shims.d.ts.map +1 -1
  70. package/dist/runtime/node-shims.js +305 -29
  71. package/dist/runtime/opencode-facet-runner.js +2 -2
  72. package/dist/session/helpers.d.ts +38 -0
  73. package/dist/session/helpers.d.ts.map +1 -1
  74. package/dist/session/helpers.js +76 -0
  75. package/dist/session/init-phases.d.ts +1 -1
  76. package/dist/session/init-phases.d.ts.map +1 -1
  77. package/dist/session/init-phases.js +2 -12
  78. package/dist/session/init.d.ts +15 -1
  79. package/dist/session/init.d.ts.map +1 -1
  80. package/dist/session/init.js +82 -603
  81. package/dist/session/nimbus-session.d.ts +56 -22
  82. package/dist/session/nimbus-session.d.ts.map +1 -1
  83. package/dist/session/nimbus-session.js +130 -101
  84. package/dist/session/npm-install-port.d.ts +16 -0
  85. package/dist/session/npm-install-port.d.ts.map +1 -0
  86. package/dist/session/npm-install-port.js +37 -0
  87. package/dist/session/port-capability.d.ts +44 -0
  88. package/dist/session/port-capability.d.ts.map +1 -1
  89. package/dist/session/port-capability.js +110 -0
  90. package/dist/session/programmatic.d.ts +31 -5
  91. package/dist/session/programmatic.d.ts.map +1 -1
  92. package/dist/session/programmatic.js +94 -68
  93. package/dist/session/routes.d.ts +30 -17
  94. package/dist/session/routes.d.ts.map +1 -1
  95. package/dist/session/routes.js +42 -131
  96. package/dist/session/rpc.d.ts +15 -15
  97. package/dist/session/rpc.d.ts.map +1 -1
  98. package/dist/session/rpc.js +97 -47
  99. package/dist/session/serving-port.d.ts +27 -0
  100. package/dist/session/serving-port.d.ts.map +1 -0
  101. package/dist/session/serving-port.js +27 -0
  102. package/dist/session/start-real-vite.d.ts +12 -0
  103. package/dist/session/start-real-vite.d.ts.map +1 -1
  104. package/dist/session/start-real-vite.js +4 -4
  105. package/dist/session/supervisor-op.d.ts +2 -2
  106. package/dist/session/supervisor-op.d.ts.map +1 -1
  107. package/dist/session/supervisor-op.js +13 -7
  108. package/dist/session/supervisor-rpc.d.ts +7 -7
  109. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  110. package/dist/session/supervisor-rpc.js +4 -0
  111. package/dist/session/vite-command.d.ts +18 -0
  112. package/dist/session/vite-command.d.ts.map +1 -0
  113. package/dist/session/vite-command.js +609 -0
  114. package/dist/session/ws.d.ts +61 -0
  115. package/dist/session/ws.d.ts.map +1 -1
  116. package/dist/session/ws.js +122 -12
  117. package/dist/shell/npm-bin-entrypoints.d.ts +0 -35
  118. package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
  119. package/dist/shell/npm-bin-entrypoints.js +12 -85
  120. package/package.json +24 -4
  121. package/public/_assets/runtime/{node-shims-9e1380d9ee430fcc.js → node-shims-8f8c64edd2a1f0f5.js} +305 -29
  122. package/public/index.html +1 -1
  123. package/scripts/bundle-facet-workers.mjs +1 -1
  124. package/scripts/bundle-npm-cjs.mjs +2 -2
  125. package/dist/facets/on-demand-bundle-gate.d.ts +0 -65
  126. package/dist/facets/on-demand-bundle-gate.d.ts.map +0 -1
  127. package/dist/facets/on-demand-bundle-gate.js +0 -125
  128. package/dist/npm/install-args.d.ts +0 -21
  129. package/dist/npm/install-args.d.ts.map +0 -1
  130. package/dist/npm/install-args.js +0 -70
  131. package/dist/npm/npm-log.d.ts +0 -31
  132. package/dist/npm/npm-log.d.ts.map +0 -1
  133. package/dist/npm/npm-log.js +0 -51
@@ -26,7 +26,7 @@ import { getSharedRuntimeExternals, BUNDLER_VERSION } from '@nimbus-sh/core/runt
26
26
  import { NpmCache } from '../npm/cache.js';
27
27
  import { sha256Base64Url } from '@nimbus-sh/core/_shared/crypto.js';
28
28
  import { LruMap } from '@nimbus-sh/core/_shared/lru-map.js';
29
- import { OnDemandBundleGate } from './on-demand-bundle-gate.js';
29
+ import { acquireSupervisorAllocation } from '@nimbus-sh/platform/heavy-alloc-coord.js';
30
30
  import { VITE_MODULE_CACHE_MAX_ENTRIES, ON_DEMAND_SLICE_CAP_BYTES } from '@nimbus-sh/core/constants.js';
31
31
  import { countPackageFiles, BARREL_PKG_FILE_THRESHOLD, packageNameFromSpecifier } from '@nimbus-sh/core/runtime/barrel-detect.js';
32
32
  import { scanNamedImports, namedImportSignature, buildSyntheticEntry, buildScopedSliceForSynthetic, syntheticEntryPath, } from '../runtime/barrel-synthesizer.js';
@@ -1168,15 +1168,10 @@ export class ViteDevServer {
1168
1168
  npmCache = null;
1169
1169
  /** Inject React Router basename into entry files? Default: true. */
1170
1170
  injectBasename;
1171
- /** Worker env (LOADER, ctx.exports) for the on-demand-bundle facet path.
1172
- * Null = legacy in-supervisor esbuild fallback. */
1173
1171
  env;
1174
1172
  ctx = null;
1175
- /** Lazily-constructed pool for on-demand bundling. Mirrors the
1176
- * pre-bundle pool's wasm-modules-map shape. Created on first
1177
- * cold-path /preview/@modules/<spec> request. */
1178
- onDemandPool = null;
1179
- onDemandPoolPromise = null;
1173
+ /** The session's esbuild facet pool; null = legacy in-supervisor esbuild. */
1174
+ bundlePool;
1180
1175
  /**
1181
1176
  * In-flight on-demand-bundle coalescing map. When the browser fires
1182
1177
  * multiple parallel requests for the same /preview/@modules/<spec>
@@ -1194,21 +1189,6 @@ export class ViteDevServer {
1194
1189
  * crashes the supervisor for N≥3 on a busy isolate.
1195
1190
  */
1196
1191
  pendingBundles = new Map();
1197
- /**
1198
- * Byte-budget admission gate for the on-demand bundle slow path.
1199
- * Replaces the former single-slot semaphore: instead of serializing
1200
- * every cold bundle (which made a fresh-React first load multi-second
1201
- * because each distinct /@modules/ spec waited for the previous), it
1202
- * bounds the TOTAL slice BYTES resident in the supervisor at once.
1203
- *
1204
- * Many small slices' facet RPC round-trips overlap; a single large
1205
- * (~28 MiB) slice still serializes the rest. Peak resident slice bytes
1206
- * never exceed ON_DEMAND_SLICE_CAP_BYTES — the same one-slice envelope
1207
- * the install-time pre-bundler proved safe on shared DO isolates — so
1208
- * this is a latency win with no supervisor-heap regression. Coupled
1209
- * with pendingBundles (same-spec coalescing) as before.
1210
- */
1211
- onDemandGate = new OnDemandBundleGate(ON_DEMAND_SLICE_CAP_BYTES);
1212
1192
  /**
1213
1193
  * process diagnostics support: the supervisor's per-PID log store. When set
1214
1194
  * (alongside `pid`), every diagnostic emitted by the dev server is
@@ -1238,6 +1218,7 @@ export class ViteDevServer {
1238
1218
  this.aliases = opts.aliases || {};
1239
1219
  this.env = opts.env;
1240
1220
  this.ctx = opts.ctx ?? null;
1221
+ this.bundlePool = opts.bundlePool ?? null;
1241
1222
  if (opts.sql) {
1242
1223
  this.npmCache = new NpmCache(opts.sql);
1243
1224
  }
@@ -1262,48 +1243,16 @@ export class ViteDevServer {
1262
1243
  this.detectTailwind();
1263
1244
  }
1264
1245
  /**
1265
- * Lazily construct the IsolatePool used for on-demand bundling
1266
- * of /preview/@modules/<spec> requests that miss both the in-memory
1267
- * and pkg_esm_bundles caches. Mirrors the pre-bundle pool's
1268
- * configuration: 1 worker, internal pLimit not needed (one bundle
1269
- * per request), wasm shipped via wasmModules.
1270
- *
1271
- * Returns null when env/ctx aren't available (legacy fallback used).
1246
+ * The session's shared esbuild pool for on-demand bundling of
1247
+ * /preview/@modules/<spec> requests that miss both the in-memory and
1248
+ * pkg_esm_bundles caches. Null when no pool was provided (legacy
1249
+ * in-supervisor fallback). Acquired BEFORE the slice lease — see
1250
+ * EsbuildBundlePool.acquire.
1272
1251
  */
1273
1252
  async ensureOnDemandPool() {
1274
- if (this.onDemandPool)
1275
- return this.onDemandPool;
1276
- if (this.onDemandPoolPromise)
1277
- return this.onDemandPoolPromise;
1278
- if (!this.env || !this.ctx)
1253
+ if (!this.bundlePool)
1279
1254
  return null;
1280
- this.onDemandPoolPromise = (async () => {
1281
- const { IsolatePool } = await import('@nimbus-sh/fabric/isolate-pool.js');
1282
- const { PRE_BUNDLE_PREAMBLE } = await import('../loaders/pre-bundle-preamble.js');
1283
- const { fetchEsbuildWasmBytes } = await import('../runtime/esbuild-wasm-bytes.js');
1284
- const wasmBytes = await fetchEsbuildWasmBytes(this.env);
1285
- const pool = new IsolatePool(this.env, this.ctx, {
1286
- concurrency: 1,
1287
- timeoutMs: 60_000,
1288
- retries: 0,
1289
- // Use a distinct tag so the on-demand pool's cached worker
1290
- // doesn't collide with the install-time pre-bundle pool.
1291
- // Sharing fnHash + preamble + wasm fingerprint between pools
1292
- // would otherwise alias them in workerd's loader cache.
1293
- tag: 'on-demand-bundle',
1294
- preamble: PRE_BUNDLE_PREAMBLE,
1295
- wasmModules: { 'esbuild.wasm': wasmBytes },
1296
- });
1297
- this.onDemandPool = pool;
1298
- return pool;
1299
- })();
1300
- try {
1301
- return await this.onDemandPoolPromise;
1302
- }
1303
- catch (e) {
1304
- this.onDemandPoolPromise = null;
1305
- throw e;
1306
- }
1255
+ return this.bundlePool.acquire();
1307
1256
  }
1308
1257
  /** Detect TailwindCSS usage in the project */
1309
1258
  detectTailwind() {
@@ -1756,39 +1705,40 @@ export class ViteDevServer {
1756
1705
  });
1757
1706
  }
1758
1707
  }
1759
- // ── Cold path coalescing + byte-budget admission ──────────────
1708
+ // ── Cold path coalescing ──────────────────────────────────────
1760
1709
  // Multiple parallel browser requests for the same module are
1761
1710
  // common on first preview load; coalesce so exactly ONE bundle
1762
- // attempt runs per spec. Across DIFFERENT specs, the byte-budget
1763
- // gate bounds total resident slice bytes so small slices overlap
1764
- // while peak supervisor memory stays at one slice. See
1765
- // `pendingBundles` and `onDemandGate` field docs for context.
1711
+ // attempt runs per spec. Across DIFFERENT specs, the cold path
1712
+ // leases its slice bytes from the shared supervisor allocation
1713
+ // budget before building, so it queues behind every other heavy
1714
+ // owner in this isolate (the pre-bundler included) instead of
1715
+ // stacking slices beside them. See `pendingBundles`.
1716
+ //
1717
+ // The shared promise resolves to a Response nobody consumes
1718
+ // directly: a Response body is readable once, so every coalesced
1719
+ // consumer — the initiator included — takes its own clone.
1766
1720
  const inflight = this.pendingBundles.get(cacheKey);
1767
1721
  if (inflight)
1768
- return inflight;
1769
- const coldPromise = this.onDemandGate.run((admit) => this.serveModuleCold(specifier, headers, base, barrelInfo, admit));
1722
+ return inflight.then((response) => response.clone());
1723
+ const coldPromise = this.serveModuleCold(specifier, headers, base, barrelInfo);
1770
1724
  this.pendingBundles.set(cacheKey, coldPromise);
1771
- coldPromise.finally(() => {
1772
- // Drop the coalescing entry once settled. Subsequent requests
1773
- // for the same spec will hit the moduleCache (set inside the
1774
- // cold path) on the hot path, not re-enter the slow path.
1775
- this.pendingBundles.delete(cacheKey);
1776
- });
1777
- return coldPromise;
1725
+ // Drop the coalescing entry once settled. Subsequent requests
1726
+ // for the same spec will hit the moduleCache (set inside the
1727
+ // cold path) on the hot path, not re-enter the slow path.
1728
+ coldPromise.then(() => this.pendingBundles.delete(cacheKey), () => this.pendingBundles.delete(cacheKey));
1729
+ return coldPromise.then((response) => response.clone());
1778
1730
  }
1779
1731
  /**
1780
1732
  * Cold path of serveModule: package resolution → on-demand facet
1781
1733
  * bundle (synthetic-entry for barrels) → hard-error if bundle fails.
1782
1734
  * NO CDN fallback (100% edge contract). Extracted so the coalescing
1783
- * + gate wrapper in serveModule() reads cleanly. Always runs inside
1784
- * the on-demand byte-budget gate — see serveModule's wrapper.
1785
- *
1786
- * `admit` reserves the built slice's real byte size against the gate's
1787
- * budget and releases the build lock for the next spec. It is called
1788
- * exactly once, right after the slice is built and before the facet
1789
- * submit; bail-out paths that never build a slice simply never call it.
1735
+ * wrapper in serveModule() reads cleanly. The slice this body builds is
1736
+ * leased from the shared supervisor allocation budget for its worst
1737
+ * case before it is built, shrunk to its real size once built, and
1738
+ * released only after the facet result has been rewritten, cached and
1739
+ * wrapped in the Response — the pre-bundler's per-slice pattern.
1790
1740
  */
1791
- async serveModuleCold(specifier, headers, base, knownBarrelInfo = null, admit) {
1741
+ async serveModuleCold(specifier, headers, base, knownBarrelInfo = null) {
1792
1742
  const JS_CT = 'application/javascript; charset=utf-8';
1793
1743
  const cacheKey = this.ck(base, `@modules/${specifier}`);
1794
1744
  // 3. On-demand bundle (cold path — resolve from node_modules, bundle via esbuild)
@@ -1797,12 +1747,13 @@ export class ViteDevServer {
1797
1747
  // supervisor isolate. For large modules (lucide-react, ~18 MiB
1798
1748
  // unpacked) that OOM'd the supervisor and surfaced as CF error
1799
1749
  // 1101 on /preview/@modules/lucide-react, taking down the entire
1800
- // preview. We now dispatch the bundle work to a IsolatePool
1801
- // isolate via its own 128 MiB heap — same pattern as install-time
1802
- // pre-bundling. Supervisor never bundles esbuild for any path.
1750
+ // preview. We now dispatch the bundle work to the session's shared
1751
+ // esbuild IsolatePool — the same pool, isolate and 128 MiB heap the
1752
+ // install-time pre-bundler uses. Supervisor never bundles esbuild
1753
+ // for any path.
1803
1754
  //
1804
- // Falls back to in-supervisor esbuild ONLY if env/ctx aren't
1805
- // available (e.g. legacy callers / tests).
1755
+ // Falls back to in-supervisor esbuild ONLY if no bundle pool was
1756
+ // provided (legacy callers / tests).
1806
1757
  const resolved = this.resolvePackage(specifier);
1807
1758
  // Barrel packages (lucide-react, @phosphor-icons/react, react-icons,
1808
1759
  // @mui/icons-material, …) ship hundreds/thousands of tiny re-export
@@ -1894,148 +1845,163 @@ export class ViteDevServer {
1894
1845
  this.log('warn', `[vite-dev] synthesized entry for ${specifier} ` +
1895
1846
  `(barrel: ${fileCount} files; ${names.size} static imports → tree-shaken bundle)`);
1896
1847
  }
1897
- if (onDemandPool) {
1898
- // Facet path — supervisor stays at 0 esbuild bytes.
1899
- try {
1900
- const { buildSliceForSpecifierWithCap, prebundleOne, BUNDLER_VERSION, } = await import('../npm/pre-bundle-facet.js');
1901
- const SLICE_CAP_BYTES = ON_DEMAND_SLICE_CAP_BYTES;
1902
- const projDir = this.root;
1903
- const nmDir = projDir + '/node_modules';
1904
- let slice = null;
1905
- if (synthetic && syntheticReferencedFiles) {
1906
- // SCOPED slice: only the files the synthetic entry directly
1907
- // references (+ transitive relative imports + package.json).
1908
- // Skips the full package walk so icon-libraries with
1909
- // thousands of files don't blow the 28 MiB cap.
1910
- const scoped = buildScopedSliceForSynthetic(this.vfs, nmDir, pkgName, syntheticReferencedFiles);
1911
- const built = {
1912
- slice: scoped.entries,
1913
- totalBytes: scoped.totalBytes,
1914
- };
1915
- try {
1916
- const bytes = this.vfs.readFile(bundleEntryPath);
1917
- const parentDir = bundleEntryPath.substring(0, bundleEntryPath.lastIndexOf('/'));
1918
- built.slice.push({ path: '/' + parentDir.replace(/^\/+/, ''), isDir: true });
1919
- built.slice.push({
1920
- path: '/' + bundleEntryPath.replace(/^\/+/, ''),
1921
- bytes,
1922
- isDir: false,
1923
- });
1924
- built.totalBytes += bytes.length + bundleEntryPath.length;
1848
+ // Held from before the slice is built until the Response wrapping
1849
+ // its bundle exists — see the method doc. Null on the legacy path.
1850
+ let sliceLease = null;
1851
+ try {
1852
+ if (onDemandPool) {
1853
+ // Facet path — supervisor stays at 0 esbuild bytes.
1854
+ try {
1855
+ const { buildSliceForSpecifierWithCap, prebundleOne, BUNDLER_VERSION, } = await import('../npm/pre-bundle-facet.js');
1856
+ const SLICE_CAP_BYTES = ON_DEMAND_SLICE_CAP_BYTES;
1857
+ const projDir = this.root;
1858
+ const nmDir = projDir + '/node_modules';
1859
+ // Lease the slice's worst-case supervisor footprint BEFORE it is
1860
+ // allocated. FIFO byte credit is what stops this slice from
1861
+ // stacking beside a pre-bundle slice, a streamed install write or
1862
+ // a VFS read that independently claimed the same shared-isolate
1863
+ // headroom. The pool was acquired above, so nothing this waits
1864
+ // on is waiting on us.
1865
+ sliceLease = await acquireSupervisorAllocation(SLICE_CAP_BYTES);
1866
+ let slice = null;
1867
+ if (synthetic && syntheticReferencedFiles) {
1868
+ // SCOPED slice: only the files the synthetic entry directly
1869
+ // references (+ transitive relative imports + package.json).
1870
+ // Skips the full package walk so icon-libraries with
1871
+ // thousands of files don't blow the 28 MiB cap.
1872
+ const scoped = buildScopedSliceForSynthetic(this.vfs, nmDir, pkgName, syntheticReferencedFiles);
1873
+ const built = {
1874
+ slice: scoped.entries,
1875
+ totalBytes: scoped.totalBytes,
1876
+ };
1877
+ try {
1878
+ const bytes = this.vfs.readFile(bundleEntryPath);
1879
+ const parentDir = bundleEntryPath.substring(0, bundleEntryPath.lastIndexOf('/'));
1880
+ built.slice.push({ path: '/' + parentDir.replace(/^\/+/, ''), isDir: true });
1881
+ built.slice.push({
1882
+ path: '/' + bundleEntryPath.replace(/^\/+/, ''),
1883
+ bytes,
1884
+ isDir: false,
1885
+ });
1886
+ built.totalBytes += bytes.length + bundleEntryPath.length;
1887
+ }
1888
+ catch (e) {
1889
+ this.log('error', '[vite-dev] synthetic entry unreadable for ' + specifier + ': ' + (e?.message || e));
1890
+ }
1891
+ slice = built;
1892
+ }
1893
+ else {
1894
+ slice = buildSliceForSpecifierWithCap(this.vfs, specifier, nmDir, SLICE_CAP_BYTES);
1925
1895
  }
1926
- catch (e) {
1927
- this.log('error', '[vite-dev] synthetic entry unreadable for ' + specifier + ': ' + (e?.message || e));
1896
+ if (slice) {
1897
+ // The real size is known now: give back the credit the slice
1898
+ // did not use so smaller owners can proceed beside it. The
1899
+ // lease only shrinks to a positive size no larger than itself.
1900
+ if (slice.totalBytes > 0 && slice.totalBytes < sliceLease.bytes) {
1901
+ sliceLease.shrinkTo(slice.totalBytes);
1902
+ }
1903
+ // Build the spec, then drop our supervisor-side handle to
1904
+ // the slice array immediately — `spec` is the only thing
1905
+ // that needs to keep it alive until the RPC structured-clone
1906
+ // completes. Mirrors the install-time runSlot pattern (see
1907
+ // commit 40cfc01); the lease above keeps every other budget
1908
+ // owner from allocating beside it until the Response exists.
1909
+ let spec = {
1910
+ specifier,
1911
+ entryPath: bundleEntryPath,
1912
+ externals,
1913
+ slice: slice.slice,
1914
+ bundlerVersion: BUNDLER_VERSION,
1915
+ // Base-neutral: the @modules bundle is persisted raw and shared
1916
+ // across mounts, so BASE_URL is fixed to '/' here (module URLs
1917
+ // get the per-request base applied at serve time, not baked in).
1918
+ define: this.defineFor(''),
1919
+ };
1920
+ slice = null;
1921
+ let result = null;
1922
+ try {
1923
+ result = await onDemandPool.submit(prebundleOne, spec);
1924
+ }
1925
+ finally {
1926
+ spec = null;
1927
+ }
1928
+ if (result && result.ok && result.esmCode) {
1929
+ bundled = result.esmCode;
1930
+ }
1931
+ else if (result && result.errorText) {
1932
+ this.log('error', '[vite-dev] facet bundle failed for ' + specifier + ': ' + result.errorText);
1933
+ }
1934
+ result = null;
1935
+ }
1936
+ else {
1937
+ this.log('error', '[vite-dev] slice walker exceeded cap for ' + specifier);
1928
1938
  }
1929
- slice = built;
1930
1939
  }
1931
- else {
1932
- slice = buildSliceForSpecifierWithCap(this.vfs, specifier, nmDir, SLICE_CAP_BYTES);
1940
+ catch (e) {
1941
+ this.log('error', '[vite-dev] on-demand facet dispatch failed for ' + specifier + ': ' + (e?.message || e));
1933
1942
  }
1934
- if (slice) {
1935
- // Slice is built and its real size is known — reserve that
1936
- // many bytes against the gate's budget (blocking if a large
1937
- // slice is already in flight) and release the build lock so
1938
- // the next spec can build while this one's facet RPC runs.
1939
- if (admit)
1940
- await admit(slice.totalBytes);
1941
- // Build the spec, then drop our supervisor-side handle to
1942
- // the slice array immediately — `spec` is the only thing
1943
- // that needs to keep it alive until the RPC structured-clone
1944
- // completes. Mirrors the install-time runSlot pattern (see
1945
- // commit 40cfc01) so peak supervisor heap during a flurry of
1946
- // /preview/@modules/* requests stays bounded by the gate.
1947
- let spec = {
1948
- specifier,
1949
- entryPath: bundleEntryPath,
1950
- externals,
1951
- slice: slice.slice,
1952
- bundlerVersion: BUNDLER_VERSION,
1953
- // Base-neutral: the @modules bundle is persisted raw and shared
1954
- // across mounts, so BASE_URL is fixed to '/' here (module URLs
1955
- // get the per-request base applied at serve time, not baked in).
1943
+ }
1944
+ else {
1945
+ // Legacy fallback — in-supervisor esbuild. Used only when no
1946
+ // bundle pool was provided.
1947
+ try {
1948
+ const result = await this.esbuild.build([bundleEntryPath], {
1949
+ bundle: true,
1950
+ format: 'esm',
1951
+ platform: 'browser',
1952
+ target: 'esnext',
1953
+ // Base-neutral (see the pooled build above).
1956
1954
  define: this.defineFor(''),
1957
- };
1958
- slice = null;
1959
- let result = null;
1960
- try {
1961
- result = await onDemandPool.submit(prebundleOne, spec);
1962
- }
1963
- finally {
1964
- spec = null;
1965
- }
1966
- if (result && result.ok && result.esmCode) {
1967
- bundled = result.esmCode;
1968
- }
1969
- else if (result && result.errorText) {
1970
- this.log('error', '[vite-dev] facet bundle failed for ' + specifier + ': ' + result.errorText);
1955
+ external: externals.length > 0 ? externals : undefined,
1956
+ });
1957
+ if (result.outputFiles?.length) {
1958
+ bundled = result.outputFiles[0].contents;
1971
1959
  }
1972
- result = null;
1973
1960
  }
1974
- else {
1975
- this.log('error', '[vite-dev] slice walker exceeded cap for ' + specifier);
1961
+ catch (e) {
1962
+ this.log('error', '[vite-dev] esbuild bundle failed for ' + specifier + ': ' + (e?.message || e));
1976
1963
  }
1977
1964
  }
1978
- catch (e) {
1979
- this.log('error', '[vite-dev] on-demand facet dispatch failed for ' + specifier + ': ' + (e?.message || e));
1980
- }
1981
- }
1982
- else {
1983
- // Legacy fallback — in-supervisor esbuild. Used only when
1984
- // env/ctx weren't passed in (no facet pool can be built).
1985
- try {
1986
- const result = await this.esbuild.build([bundleEntryPath], {
1987
- bundle: true,
1988
- format: 'esm',
1989
- platform: 'browser',
1990
- target: 'esnext',
1991
- // Base-neutral (see the pooled build above).
1992
- define: this.defineFor(''),
1993
- external: externals.length > 0 ? externals : undefined,
1994
- });
1995
- if (result.outputFiles?.length) {
1996
- bundled = result.outputFiles[0].contents;
1965
+ if (bundled !== null) {
1966
+ // Persist the RAW esbuild output. It is base-independent — the only
1967
+ // base-dependent step is the module-URL rewrite below, applied at
1968
+ // serve time — so one persisted bundle serves every mount (the
1969
+ // `/preview/` path and the `<port>--<sid>` host alike). Caching the
1970
+ // post-rewrite text instead would pin the bundle to whichever base
1971
+ // built it first and 404 the other. Cache ONLY successful bundles;
1972
+ // a failed build left `bundled` null and never reaches here.
1973
+ if (this.npmCache) {
1974
+ try {
1975
+ this.npmCache.putEsmBundle({
1976
+ specifier,
1977
+ bundleHash: BUNDLER_VERSION,
1978
+ esmCode: bundled,
1979
+ builtAt: Date.now(),
1980
+ inputHash: barrelInfo?.inputHash ?? '',
1981
+ });
1982
+ }
1983
+ catch { /* non-fatal */ }
1997
1984
  }
1998
- }
1999
- catch (e) {
2000
- this.log('error', '[vite-dev] esbuild bundle failed for ' + specifier + ': ' + (e?.message || e));
1985
+ // Convert `__require("external")` calls (from CJS source with esbuild
1986
+ // externals) into ESM `import * as` + dispatch, and rewrite any bare
1987
+ // imports esbuild marked external so they carry the mount base.
1988
+ // Without the base prefix, `import X from "scheduler"` 404s.
1989
+ let code = rewriteExternalRequires(bundled, base);
1990
+ code = rewriteAllImports(code, this.aliases, base);
1991
+ // For CJS-only packages (react, react-dom), esbuild's __commonJS
1992
+ // wrapper only emits `export default` — named imports like
1993
+ // `import { createRoot } from "react-dom/client"` would fail. Statically
1994
+ // scan the bundled source for CJS export patterns and synthesize
1995
+ // named exports.
1996
+ code = synthesizeCjsNamedExports(code);
1997
+ this.moduleCache.set(cacheKey, { code, timestamp: Date.now(), inputHash: barrelInfo?.inputHash ?? '' });
1998
+ return new Response(code, {
1999
+ headers: { ...headers, 'Content-Type': JS_CT },
2000
+ });
2001
2001
  }
2002
2002
  }
2003
- if (bundled !== null) {
2004
- // Persist the RAW esbuild output. It is base-independent — the only
2005
- // base-dependent step is the module-URL rewrite below, applied at
2006
- // serve time — so one persisted bundle serves every mount (the
2007
- // `/preview/` path and the `<port>--<sid>` host alike). Caching the
2008
- // post-rewrite text instead would pin the bundle to whichever base
2009
- // built it first and 404 the other. Cache ONLY successful bundles;
2010
- // a failed build left `bundled` null and never reaches here.
2011
- if (this.npmCache) {
2012
- try {
2013
- this.npmCache.putEsmBundle({
2014
- specifier,
2015
- bundleHash: BUNDLER_VERSION,
2016
- esmCode: bundled,
2017
- builtAt: Date.now(),
2018
- inputHash: barrelInfo?.inputHash ?? '',
2019
- });
2020
- }
2021
- catch { /* non-fatal */ }
2022
- }
2023
- // Convert `__require("external")` calls (from CJS source with esbuild
2024
- // externals) into ESM `import * as` + dispatch, and rewrite any bare
2025
- // imports esbuild marked external so they carry the mount base.
2026
- // Without the base prefix, `import X from "scheduler"` 404s.
2027
- let code = rewriteExternalRequires(bundled, base);
2028
- code = rewriteAllImports(code, this.aliases, base);
2029
- // For CJS-only packages (react, react-dom), esbuild's __commonJS
2030
- // wrapper only emits `export default` — named imports like
2031
- // `import { createRoot } from "react-dom/client"` would fail. Statically
2032
- // scan the bundled source for CJS export patterns and synthesize
2033
- // named exports.
2034
- code = synthesizeCjsNamedExports(code);
2035
- this.moduleCache.set(cacheKey, { code, timestamp: Date.now(), inputHash: barrelInfo?.inputHash ?? '' });
2036
- return new Response(code, {
2037
- headers: { ...headers, 'Content-Type': JS_CT },
2038
- });
2003
+ finally {
2004
+ sliceLease?.release();
2039
2005
  }
2040
2006
  // Bundle failed — fall through to the hard-error response below.
2041
2007
  // Previously fell through to a CDN fallback (esm.sh) but that
@@ -0,0 +1,25 @@
1
+ /**
2
+ * wasm-image-digest.ts — the content key a wasm image is registered under.
3
+ *
4
+ * A launch stages every wasm image its program's closure holds as a
5
+ * module-map member, compiled by the loader, and registers it by VFS path
6
+ * AND by a digest of its bytes. The node-shims WebAssembly seam answers
7
+ * `new WebAssembly.Module(bytes)` / `WebAssembly.compile(bytes)` with the
8
+ * compiled module when the bytes match a registered digest — whether the
9
+ * program read them off the filesystem, inlined them as base64 in its own
10
+ * source, or received them some other way. The key is what wasm IS, not how
11
+ * it arrived.
12
+ *
13
+ * Length and FNV-1a over every byte. Synchronous because
14
+ * `new WebAssembly.Module(bytes)` is, and SubtleCrypto is not. Duplicated in
15
+ * node-shims.ts (`__nimbusWasmDigest`) because that copy runs inside a facet
16
+ * with no imports; both must compute the same value, which a unit test pins.
17
+ */
18
+ export declare function wasmImageDigest(bytes: Uint8Array): string;
19
+ /** One wasm image a launch registers: where the program reads it, and its content key. */
20
+ export interface WasmImageRecord {
21
+ /** Absolute VFS path (leading slash). */
22
+ vfsPath: string;
23
+ digest: string;
24
+ }
25
+ //# sourceMappingURL=wasm-image-digest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wasm-image-digest.d.ts","sourceRoot":"","sources":["../../src/facets/wasm-image-digest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAOzD;AAED,0FAA0F;AAC1F,MAAM,WAAW,eAAe;IAC9B,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * wasm-image-digest.ts — the content key a wasm image is registered under.
3
+ *
4
+ * A launch stages every wasm image its program's closure holds as a
5
+ * module-map member, compiled by the loader, and registers it by VFS path
6
+ * AND by a digest of its bytes. The node-shims WebAssembly seam answers
7
+ * `new WebAssembly.Module(bytes)` / `WebAssembly.compile(bytes)` with the
8
+ * compiled module when the bytes match a registered digest — whether the
9
+ * program read them off the filesystem, inlined them as base64 in its own
10
+ * source, or received them some other way. The key is what wasm IS, not how
11
+ * it arrived.
12
+ *
13
+ * Length and FNV-1a over every byte. Synchronous because
14
+ * `new WebAssembly.Module(bytes)` is, and SubtleCrypto is not. Duplicated in
15
+ * node-shims.ts (`__nimbusWasmDigest`) because that copy runs inside a facet
16
+ * with no imports; both must compute the same value, which a unit test pins.
17
+ */
18
+ export function wasmImageDigest(bytes) {
19
+ let hash = 0x811c9dc5;
20
+ for (let i = 0; i < bytes.length; i++) {
21
+ hash ^= bytes[i];
22
+ hash = Math.imul(hash, 0x01000193) >>> 0;
23
+ }
24
+ return bytes.length + ':' + hash.toString(16);
25
+ }