@nimbus-sh/worker 0.8.0 → 0.10.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 (124) hide show
  1. package/dist/esbuild-wasm-bundle.generated.d.ts +24 -24
  2. package/dist/esbuild-wasm-bundle.generated.d.ts.map +1 -1
  3. package/dist/esbuild-wasm-bundle.generated.js +24 -24
  4. package/dist/facets/cirrus-real.js +3 -3
  5. package/dist/facets/compose.d.ts +3 -0
  6. package/dist/facets/compose.d.ts.map +1 -1
  7. package/dist/facets/compose.js +10 -3
  8. package/dist/facets/esbuild-bundle-pool.d.ts +1 -1
  9. package/dist/facets/esbuild-bundle-pool.js +3 -3
  10. package/dist/facets/esbuild-transform.d.ts +6 -2
  11. package/dist/facets/esbuild-transform.d.ts.map +1 -1
  12. package/dist/facets/esbuild-transform.js +8 -4
  13. package/dist/facets/manager.d.ts +54 -15
  14. package/dist/facets/manager.d.ts.map +1 -1
  15. package/dist/facets/manager.js +227 -144
  16. package/dist/facets/real-vite-hmr.d.ts +2 -0
  17. package/dist/facets/real-vite-hmr.d.ts.map +1 -1
  18. package/dist/facets/real-vite-hmr.js +3 -2
  19. package/dist/git/commands.d.ts +17 -1
  20. package/dist/git/commands.d.ts.map +1 -1
  21. package/dist/git/commands.js +73 -9
  22. package/dist/git/network-facet.d.ts +2 -0
  23. package/dist/git/network-facet.d.ts.map +1 -1
  24. package/dist/git/network-facet.js +10 -5
  25. package/dist/hosted/commands.d.ts.map +1 -1
  26. package/dist/hosted/commands.js +28 -39
  27. package/dist/hosted/runtime.d.ts +24 -2
  28. package/dist/hosted/runtime.d.ts.map +1 -1
  29. package/dist/hosted/runtime.js +70 -13
  30. package/dist/hosted/services.d.ts +14 -2
  31. package/dist/hosted/services.d.ts.map +1 -1
  32. package/dist/hosted/services.js +15 -2
  33. package/dist/loaders/generated-workers.d.ts +2 -2
  34. package/dist/loaders/generated-workers.d.ts.map +1 -1
  35. package/dist/loaders/generated-workers.js +4 -4
  36. package/dist/loaders/pre-bundle-preamble.d.ts +6 -3
  37. package/dist/loaders/pre-bundle-preamble.d.ts.map +1 -1
  38. package/dist/loaders/pre-bundle-preamble.js +13 -8
  39. package/dist/node-shims-artifact.generated.js +3 -3
  40. package/dist/npm/cache.d.ts +6 -1
  41. package/dist/npm/cache.d.ts.map +1 -1
  42. package/dist/npm/cache.js +8 -6
  43. package/dist/npm/install-batch-facet.d.ts +2 -0
  44. package/dist/npm/install-batch-facet.d.ts.map +1 -1
  45. package/dist/npm/install-batch-facet.js +29 -15
  46. package/dist/npm/installer.d.ts +8 -4
  47. package/dist/npm/installer.d.ts.map +1 -1
  48. package/dist/npm/installer.js +173 -113
  49. package/dist/npm/npx-install.d.ts +3 -1
  50. package/dist/npm/npx-install.d.ts.map +1 -1
  51. package/dist/npm/npx-install.js +4 -1
  52. package/dist/npm/placement.d.ts +16 -0
  53. package/dist/npm/placement.d.ts.map +1 -0
  54. package/dist/npm/placement.js +33 -0
  55. package/dist/npm/r2-cache.d.ts +10 -6
  56. package/dist/npm/r2-cache.d.ts.map +1 -1
  57. package/dist/npm/r2-cache.js +32 -16
  58. package/dist/npm/resolve-one-facet.d.ts +3 -0
  59. package/dist/npm/resolve-one-facet.d.ts.map +1 -1
  60. package/dist/npm/resolve-one-facet.js +1 -0
  61. package/dist/npm/resolver.d.ts +11 -6
  62. package/dist/npm/resolver.d.ts.map +1 -1
  63. package/dist/npm/resolver.js +12 -3
  64. package/dist/runtime/bash-repl.d.ts +5 -3
  65. package/dist/runtime/bash-repl.d.ts.map +1 -1
  66. package/dist/runtime/bash-repl.js +8 -3
  67. package/dist/runtime/cpython-resident.d.ts.map +1 -1
  68. package/dist/runtime/cpython-resident.js +2 -10
  69. package/dist/runtime/esbuild-wasm-bytes.d.ts +25 -18
  70. package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -1
  71. package/dist/runtime/esbuild-wasm-bytes.js +55 -30
  72. package/dist/runtime/facet-loader-host.d.ts.map +1 -1
  73. package/dist/runtime/facet-loader-host.js +0 -9
  74. package/dist/runtime/node-shims.d.ts.map +1 -1
  75. package/dist/runtime/node-shims.js +853 -181
  76. package/dist/runtime/python-repl.d.ts +4 -3
  77. package/dist/runtime/python-repl.d.ts.map +1 -1
  78. package/dist/runtime/python-repl.js +10 -39
  79. package/dist/runtime/ruby-repl.d.ts +3 -2
  80. package/dist/runtime/ruby-repl.d.ts.map +1 -1
  81. package/dist/runtime/ruby-repl.js +7 -6
  82. package/dist/runtime/ruby-resident.d.ts.map +1 -1
  83. package/dist/runtime/ruby-resident.js +5 -16
  84. package/dist/runtime/runtime-catalog.js +1 -1
  85. package/dist/runtime/wasi/preamble.d.ts +0 -22
  86. package/dist/runtime/wasi/preamble.d.ts.map +1 -1
  87. package/dist/runtime/wasi/preamble.js +160 -1445
  88. package/dist/runtime-catalog.generated.js +1 -1
  89. package/dist/session/hibernation.d.ts +45 -2
  90. package/dist/session/hibernation.d.ts.map +1 -1
  91. package/dist/session/hibernation.js +78 -1
  92. package/dist/session/init.d.ts.map +1 -1
  93. package/dist/session/init.js +1 -0
  94. package/dist/session/nimbus-session.d.ts +16 -23
  95. package/dist/session/nimbus-session.d.ts.map +1 -1
  96. package/dist/session/nimbus-session.js +25 -36
  97. package/dist/session/npm-install-port.d.ts.map +1 -1
  98. package/dist/session/npm-install-port.js +1 -0
  99. package/dist/session/port-capability.d.ts.map +1 -1
  100. package/dist/session/port-capability.js +11 -1
  101. package/dist/session/programmatic.js +2 -2
  102. package/dist/session/routes.d.ts.map +1 -1
  103. package/dist/session/routes.js +4 -2
  104. package/dist/session/rpc.d.ts +7 -44
  105. package/dist/session/rpc.d.ts.map +1 -1
  106. package/dist/session/rpc.js +17 -67
  107. package/dist/session/supervisor-op.d.ts +8 -3
  108. package/dist/session/supervisor-op.d.ts.map +1 -1
  109. package/dist/session/supervisor-op.js +10 -45
  110. package/dist/session/supervisor-rpc.d.ts +47 -22
  111. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  112. package/dist/session/supervisor-rpc.js +64 -26
  113. package/dist/vfs/facet-resident-store.d.ts.map +1 -1
  114. package/dist/vfs/facet-resident-store.js +4 -2
  115. package/dist/workspace-host.d.ts +2 -1
  116. package/dist/workspace-host.d.ts.map +1 -1
  117. package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -1
  118. package/dist/wrangler/nimbus-wrangler.js +3 -0
  119. package/package.json +30 -4
  120. package/public/_assets/esbuild-0.24.2.js +2380 -0
  121. package/public/_assets/runtime/{node-shims-8f8c64edd2a1f0f5.js → node-shims-7b4dd7a76d95d21e.js} +852 -180
  122. package/scripts/bundle-esbuild-wasm.mjs +55 -39
  123. package/scripts/bundle-facet-workers.mjs +45 -7
  124. package/scripts/bundle-runtime.mjs +35 -8
@@ -21,6 +21,7 @@ import { getRealNodeImportsCode } from '@nimbus-sh/core/_shared/real-node-import
21
21
  import { FACET_RESIDENT_STORE_SOURCE } from '../vfs/facet-resident-store.js';
22
22
  import { VFS_CURSOR_SEED_SOURCE, serializeFacetVfsCursor, } from '@nimbus-sh/core/_shared/facet-vfs-cursor.js';
23
23
  import { VFS_WRITE_LEDGER_SOURCE } from '@nimbus-sh/core/_shared/vfs-write-ledger.js';
24
+ import { ExecutionFs } from '@nimbus-sh/core/shell/execution-fs.js';
24
25
  import { stripLeadingSlashes, vfsPathExtension } from '@nimbus-sh/core/vfs/path.js';
25
26
  import { clearPortCapability, listPortReservations, readPortReservation, readPortReservationByOwner, releasePortReservation, restoreReservedPortCapability, } from '../session/port-capability.js';
26
27
  import { deriveResidentOwner } from './resident-identity.js';
@@ -48,12 +49,12 @@ import { persistDurableWorkerImage, purgeDurableWorkerImages, } from './durable-
48
49
  import { SQLITE_WASM_MODULE_NAME, } from '../runtime/opencode-facet-runner.js';
49
50
  import { parsePortFromArgv, resolveLongRunningPort } from '@nimbus-sh/core/runtime/long-running-handle.js';
50
51
  import { DEFAULT_FACET_BUNDLE_PROFILE, } from '@nimbus-sh/core/runtime/bundle-profile.js';
51
- import { CF_COMPAT_DATE, VFS_BUNDLE_MAX_FILES, VFS_BUNDLE_MAX_BYTES, CWD_SNAPSHOT_MAX_FILE_BYTES, BUNDLE_MAX_ENCODED_BYTES, PREFETCH_CACHE_MAX_BYTES, } from '@nimbus-sh/core/constants.js';
52
+ import { CF_COMPAT_DATE, VFS_BUNDLE_MAX_FILES, VFS_BUNDLE_MAX_BYTES, CWD_SNAPSHOT_MAX_FILE_BYTES, BUNDLE_MAX_ENCODED_BYTES, PREFETCH_CACHE_MAX_BYTES, ESM_TRANSFORM_CACHE_MAX_BYTES, } from '@nimbus-sh/core/constants.js';
52
53
  import { MAX_RPC_SAFE_PAYLOAD_BYTES } from '@nimbus-sh/platform/limits.js';
53
54
  import { CRED_KERNEL } from '@nimbus-sh/core/runtime/os-contracts.js';
54
55
  import { acquireSupervisorAllocation } from '@nimbus-sh/platform/heavy-alloc-coord.js';
55
56
  import { wasmImageDigest } from './wasm-image-digest.js';
56
- import { prefetchBundleStart, prefetchBundleEnd, setPrefetchCacheBytes, } from '@nimbus-sh/platform/diag-counters.js';
57
+ import { prefetchBundleStart, prefetchBundleEnd, setPrefetchCacheBytes, setTransformCacheBytes, } from '@nimbus-sh/platform/diag-counters.js';
57
58
  const ISOLATED_ESM_TRANSFORM_MIN_BYTES = 512 * 1024;
58
59
  /**
59
60
  * Detect & restore a Uint8Array that's been JSON-mangled to a
@@ -383,25 +384,7 @@ ${facetWasmImportsSource(wasmImports)}
383
384
  const USER_CODE = ${safeCode};
384
385
  const __NimbusHostResponse = globalThis.Response;
385
386
 
386
- function __mkCompiledFn(code) {
387
- // Node strips a leading shebang from every module before evaluation;
388
- // bin scripts are commonly bundled verbatim with their
389
- // "#!/usr/bin/env node" line, which is a SyntaxError under new Function.
390
- if (typeof code === "string" && code.charCodeAt(0) === 35 && code.charCodeAt(1) === 33) {
391
- const __nl = code.indexOf("\\n");
392
- code = __nl >= 0 ? code.slice(__nl + 1) : "";
393
- }
394
- function renameIfDeclared(name) {
395
- const re = new RegExp("(?:^|\\\\n|;)\\\\s*(?:const|let|var|function|class)\\\\s+" + name + "(?![$\\\\w])", "m");
396
- return re.test(code) ? name + "__nimbus_unused" : name;
397
- }
398
- const baseParams = [
399
- "exports", renameIfDeclared("require"), "module",
400
- renameIfDeclared("__filename"),
401
- renameIfDeclared("__dirname"),
402
- ];
403
- return new Function(...baseParams, code);
404
- }
387
+ ${FACET_COMPILE_HELPER}
405
388
 
406
389
  let __compiledFn = null;
407
390
  let __entryCompileFailure = null;
@@ -698,18 +681,7 @@ const USER_CODE = ${safeCode};
698
681
  const __NIMBUS_ARGS = ${safeArgs};
699
682
  const __NimbusHostResponse = globalThis.Response;
700
683
 
701
- function __mkCompiledFn(code) {
702
- function renameIfDeclared(name) {
703
- const re = new RegExp("(?:^|\\\\n|;)\\\\s*(?:const|let|var|function|class)\\\\s+" + name + "(?![$\\\\w])", "m");
704
- return re.test(code) ? name + "__nimbus_unused" : name;
705
- }
706
- const baseParams = [
707
- "exports", renameIfDeclared("require"), "module",
708
- renameIfDeclared("__filename"),
709
- renameIfDeclared("__dirname"),
710
- ];
711
- return new Function(...baseParams, code);
712
- }
684
+ ${FACET_COMPILE_HELPER}
713
685
 
714
686
  let __compiledFn = null;
715
687
  let __entryCompileFailure = null;
@@ -1148,8 +1120,8 @@ async function facetVfsBundleSourceFor(vfsState, pacer) {
1148
1120
  * Throws on read errors (caller wraps in try/catch — matches the
1149
1121
  * pre-fix readFileString contract).
1150
1122
  */
1151
- function _readBundleCell(vfs, path) {
1152
- const bytes = vfs.readFile(path);
1123
+ async function _readBundleCell(vfs, path) {
1124
+ const bytes = await vfs.readFile(path);
1153
1125
  try {
1154
1126
  return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
1155
1127
  }
@@ -1176,10 +1148,10 @@ function _bundleCellLength(cell) {
1176
1148
  * `perFileCeiling` is the pass's own cap (e.g. CWD_SNAPSHOT_MAX_FILE_BYTES,
1177
1149
  * BIN_PACKAGE_SPECULATIVE_MAX_FILE_BYTES); omit it for budget-only checks.
1178
1150
  */
1179
- function _bundleAdmits(vfs, path, budgetState, perFileCeiling) {
1151
+ async function _bundleAdmits(vfs, path, budgetState, perFileCeiling) {
1180
1152
  let size;
1181
1153
  try {
1182
- size = vfs.lstat(path).size;
1154
+ size = (await vfs.lstat(path)).size;
1183
1155
  }
1184
1156
  catch {
1185
1157
  return false;
@@ -1650,9 +1622,9 @@ const MANIFEST_MAX_DEPTH = 12;
1650
1622
  * fs.readdirSync / fs.statSync honest regardless of which subset of
1651
1623
  * file CONTENT we ship.
1652
1624
  */
1653
- function buildManifest(vfs, cwd, scriptPath) {
1625
+ async function buildManifest(vfs, cwd, scriptPath) {
1654
1626
  const manifest = {};
1655
- function walk(dirPath, depth = 0) {
1627
+ async function walk(dirPath, depth = 0) {
1656
1628
  if (depth > MANIFEST_MAX_DEPTH)
1657
1629
  return;
1658
1630
  const stripped = dirPath.replace(/^\/+/, '');
@@ -1660,7 +1632,7 @@ function buildManifest(vfs, cwd, scriptPath) {
1660
1632
  return;
1661
1633
  let entries;
1662
1634
  try {
1663
- entries = vfs.readdir(stripped);
1635
+ entries = (await vfs.readdir(stripped));
1664
1636
  }
1665
1637
  catch {
1666
1638
  return;
@@ -1669,7 +1641,7 @@ function buildManifest(vfs, cwd, scriptPath) {
1669
1641
  for (const entry of entries) {
1670
1642
  if (entry.type === 'directory') {
1671
1643
  const childPath = stripped ? stripped + '/' + entry.name : entry.name;
1672
- walk(childPath, depth + 1);
1644
+ (await walk(childPath, depth + 1));
1673
1645
  }
1674
1646
  }
1675
1647
  }
@@ -1690,13 +1662,13 @@ function buildManifest(vfs, cwd, scriptPath) {
1690
1662
  {
1691
1663
  const segments = cwdStripped ? cwdStripped.split('/') : [];
1692
1664
  for (let i = 0; i < segments.length; i++) {
1693
- walk(segments.slice(0, i).join('/'), MANIFEST_MAX_DEPTH);
1665
+ (await walk(segments.slice(0, i).join('/'), MANIFEST_MAX_DEPTH));
1694
1666
  }
1695
1667
  }
1696
- walk(cwdStripped, 0);
1668
+ (await walk(cwdStripped, 0));
1697
1669
  const nmDir = cwdStripped + '/node_modules';
1698
- if (vfs.exists(nmDir) && vfs.isDirectory(nmDir)) {
1699
- walk(nmDir, 0);
1670
+ if ((await vfs.exists(nmDir)) && (await vfs.isDirectory(nmDir))) {
1671
+ (await walk(nmDir, 0));
1700
1672
  }
1701
1673
  // ── Bin-target package + hoisted dependency roots ─────────────────────
1702
1674
  //
@@ -1728,19 +1700,19 @@ function buildManifest(vfs, cwd, scriptPath) {
1728
1700
  const pkgEnd = isScoped ? nmIdx + 3 : nmIdx + 2;
1729
1701
  if (pkgEnd <= segs.length) {
1730
1702
  const pkgRoot = segs.slice(0, pkgEnd).join('/');
1731
- if (vfs.exists(pkgRoot) && vfs.isDirectory(pkgRoot)) {
1732
- walk(pkgRoot, 0);
1703
+ if ((await vfs.exists(pkgRoot)) && (await vfs.isDirectory(pkgRoot))) {
1704
+ (await walk(pkgRoot, 0));
1733
1705
  }
1734
1706
  }
1735
1707
  const nodeModulesRoot = segs.slice(0, nmIdx + 1).join('/');
1736
- if (vfs.exists(nodeModulesRoot) && vfs.isDirectory(nodeModulesRoot)) {
1737
- walk(nodeModulesRoot, 0);
1708
+ if ((await vfs.exists(nodeModulesRoot)) && (await vfs.isDirectory(nodeModulesRoot))) {
1709
+ (await walk(nodeModulesRoot, 0));
1738
1710
  }
1739
1711
  }
1740
1712
  }
1741
1713
  return manifest;
1742
1714
  }
1743
- function buildVfsMetadata(vfs, manifest, bundle) {
1715
+ async function buildVfsMetadata(vfs, manifest, bundle) {
1744
1716
  const paths = new Set(Object.keys(bundle).filter((path) => compiledCellPath(path) === null));
1745
1717
  for (const [directory, children] of Object.entries(manifest)) {
1746
1718
  paths.add(directory);
@@ -1751,7 +1723,9 @@ function buildVfsMetadata(vfs, manifest, bundle) {
1751
1723
  const metadata = {};
1752
1724
  for (const path of paths) {
1753
1725
  try {
1754
- const stat = vfs.lstat(path);
1726
+ const stat = await vfs.authority.stat(path, { followSymlinks: false });
1727
+ if (!stat)
1728
+ continue;
1755
1729
  metadata[path] = {
1756
1730
  type: stat.type,
1757
1731
  size: stat.size,
@@ -1767,12 +1741,12 @@ function buildVfsMetadata(vfs, manifest, bundle) {
1767
1741
  }
1768
1742
  return metadata;
1769
1743
  }
1770
- function addUnreadableDenialCells(vfs, bundle, metadata) {
1744
+ async function addUnreadableDenialCells(vfs, bundle, metadata) {
1771
1745
  for (const [path, stat] of Object.entries(metadata)) {
1772
1746
  if (stat.type === 'directory' || path in bundle)
1773
1747
  continue;
1774
1748
  try {
1775
- vfs.access(path, 0o4);
1749
+ (await vfs.access(path, 0o4));
1776
1750
  }
1777
1751
  catch (error) {
1778
1752
  if (typeof error === 'object'
@@ -1801,14 +1775,14 @@ function addUnreadableDenialCells(vfs, bundle, metadata) {
1801
1775
  // was a file-local helper. Adding the named export is a pure surface
1802
1776
  // addition — no callers other than buildPrefetchBundle (same file) and
1803
1777
  // the new probe.
1804
- export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPaths = new Set()) {
1778
+ export async function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPaths = new Set()) {
1805
1779
  let added = 0;
1806
1780
  const cwdStripped = cwd.replace(/^\/+/, '');
1807
1781
  const nmDir = cwdStripped + '/node_modules';
1808
- if (!(vfs.exists(nmDir) && vfs.isDirectory(nmDir)))
1782
+ if (!((await vfs.exists(nmDir)) && (await vfs.isDirectory(nmDir))))
1809
1783
  return { added };
1810
1784
  const exts = ['', '.js', '.cjs', '.mjs', '/index.js', '/index.cjs'];
1811
- function addOne(path) {
1785
+ async function addOne(path) {
1812
1786
  const stripped = path.replace(/^\/+/, '');
1813
1787
  if (stripped in bundle)
1814
1788
  return false;
@@ -1817,7 +1791,7 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
1817
1791
  if (budgetState.totalBytes >= VFS_BUNDLE_MAX_BYTES)
1818
1792
  return false;
1819
1793
  try {
1820
- if (!vfs.exists(stripped) || vfs.isDirectory(stripped))
1794
+ if (!(await vfs.exists(stripped)) || (await vfs.isDirectory(stripped)))
1821
1795
  return false;
1822
1796
  // This is a guess at what a program might require, and the same
1823
1797
  // per-file ceiling the entry-package walk applies bounds it: a
@@ -1827,7 +1801,7 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
1827
1801
  // supervisor's headroom — on every invocation that never reads it.
1828
1802
  // Anything the program really requires arrives through the closure,
1829
1803
  // which is uncapped.
1830
- if (!_bundleAdmits(vfs, stripped, budgetState, BIN_PACKAGE_SPECULATIVE_MAX_FILE_BYTES))
1804
+ if (!(await _bundleAdmits(vfs, stripped, budgetState, BIN_PACKAGE_SPECULATIVE_MAX_FILE_BYTES)))
1831
1805
  return false;
1832
1806
  // A guess must not be a native binary. Nothing in a Workers isolate can
1833
1807
  // load a `.node` addon or a `.exe` — the ABI policy classifies them
@@ -1839,7 +1813,7 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
1839
1813
  if (stripped.endsWith('.node') || stripped.endsWith('.exe'))
1840
1814
  return false;
1841
1815
  // hardening-r5: preserve binary content as Uint8Array.
1842
- const content = _readBundleCell(vfs, stripped);
1816
+ const content = (await _readBundleCell(vfs, stripped));
1843
1817
  const cellLen = _bundleCellLength(content);
1844
1818
  if (budgetState.totalBytes + cellLen > VFS_BUNDLE_MAX_BYTES)
1845
1819
  return false;
@@ -1903,15 +1877,15 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
1903
1877
  }
1904
1878
  return !reached;
1905
1879
  }
1906
- function addPkgEntry(pkgDir) {
1907
- addOne(pkgDir + '/package.json');
1880
+ async function addPkgEntry(pkgDir) {
1881
+ (await addOne(pkgDir + '/package.json'));
1908
1882
  // A package the closure reached keeps exactly what it reached; only an
1909
1883
  // unreached one gets the guess below.
1910
1884
  if (!mainIsSpeculative(pkgDir))
1911
1885
  return;
1912
1886
  let meta;
1913
1887
  try {
1914
- meta = JSON.parse(vfs.readFileString(pkgDir + '/package.json'));
1888
+ meta = JSON.parse((await vfs.readFileString(pkgDir + '/package.json')));
1915
1889
  }
1916
1890
  catch {
1917
1891
  meta = null;
@@ -1943,9 +1917,9 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
1943
1917
  let landed = false;
1944
1918
  const tries = /\.[a-z]+$/.test(norm) ? [base] : exts.map((e) => base + e);
1945
1919
  for (const candidate of tries) {
1946
- if (vfs.exists(candidate.replace(/^\/+/, '')) &&
1947
- !vfs.isDirectory(candidate.replace(/^\/+/, ''))) {
1948
- if (addOne(candidate)) {
1920
+ if ((await vfs.exists(candidate.replace(/^\/+/, ''))) &&
1921
+ !(await vfs.isDirectory(candidate.replace(/^\/+/, '')))) {
1922
+ if ((await addOne(candidate))) {
1949
1923
  landed = true;
1950
1924
  break;
1951
1925
  }
@@ -1963,7 +1937,7 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
1963
1937
  // the greedy oversample is the safety net for their reachability.
1964
1938
  const entryDir = base.replace(/\/[^/]+$/, '');
1965
1939
  try {
1966
- const sibs = vfs.readdir(entryDir);
1940
+ const sibs = (await vfs.readdir(entryDir));
1967
1941
  for (const sib of sibs) {
1968
1942
  if (sib.type !== 'file')
1969
1943
  continue;
@@ -1983,7 +1957,7 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
1983
1957
  const hasMixedCase = /[A-Z]/.test(seg) && /[a-z]/.test(seg);
1984
1958
  if (!hasDigitOrDash && !hasMixedCase)
1985
1959
  continue;
1986
- addOne(entryDir + '/' + sib.name);
1960
+ (await addOne(entryDir + '/' + sib.name));
1987
1961
  }
1988
1962
  // Walk one level into `shared/` — unconditionally, since the
1989
1963
  // pattern is well-known across unbuild/rolldown/rollup chunked
@@ -1991,13 +1965,13 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
1991
1965
  // typical shared/ dir returns 1-5 files.
1992
1966
  const sharedDir = entryDir + '/shared';
1993
1967
  const sharedStripped = sharedDir.replace(/^\/+/, '');
1994
- if (vfs.exists(sharedStripped) && vfs.isDirectory(sharedStripped)) {
1995
- for (const sh of vfs.readdir(sharedDir)) {
1968
+ if ((await vfs.exists(sharedStripped)) && (await vfs.isDirectory(sharedStripped))) {
1969
+ for (const sh of (await vfs.readdir(sharedDir))) {
1996
1970
  if (sh.type !== 'file')
1997
1971
  continue;
1998
1972
  if (!/\.(cjs|mjs|js)$/.test(sh.name))
1999
1973
  continue;
2000
- addOne(sharedDir + '/' + sh.name);
1974
+ (await addOne(sharedDir + '/' + sh.name));
2001
1975
  }
2002
1976
  }
2003
1977
  }
@@ -2008,8 +1982,8 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
2008
1982
  }
2009
1983
  }
2010
1984
  }
2011
- for (const pkgDir of speculativePackageDirs(vfs, cwdStripped, bundle))
2012
- addPkgEntry(pkgDir);
1985
+ for (const pkgDir of (await speculativePackageDirs(vfs, cwdStripped, bundle)))
1986
+ (await addPkgEntry(pkgDir));
2013
1987
  return { added };
2014
1988
  }
2015
1989
  /**
@@ -2029,10 +2003,10 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
2029
2003
  * dependency of the package doing the requiring, so the bound is one hop
2030
2004
  * over `dependencies` only. Directories, sorted for a stable bundle.
2031
2005
  */
2032
- export function speculativePackageDirs(vfs, cwdStripped, bundle) {
2033
- const runtimeDeps = (pkgJsonPath) => {
2006
+ export async function speculativePackageDirs(vfs, cwdStripped, bundle) {
2007
+ const runtimeDeps = async (pkgJsonPath) => {
2034
2008
  try {
2035
- const meta = JSON.parse(vfs.readFileString(pkgJsonPath));
2009
+ const meta = JSON.parse((await vfs.readFileString(pkgJsonPath)));
2036
2010
  const names = new Set();
2037
2011
  for (const field of ['dependencies', 'optionalDependencies']) {
2038
2012
  const deps = meta?.[field];
@@ -2057,11 +2031,11 @@ export function speculativePackageDirs(vfs, cwdStripped, bundle) {
2057
2031
  };
2058
2032
  // Resolve a bare name the way require does from `fromDir`: the nearest
2059
2033
  // node_modules up the tree that has it.
2060
- const resolveDir = (name, fromDir) => {
2034
+ const resolveDir = async (name, fromDir) => {
2061
2035
  let dir = fromDir;
2062
2036
  for (;;) {
2063
2037
  const candidate = dir + '/node_modules/' + name;
2064
- if (vfs.exists(candidate + '/package.json'))
2038
+ if ((await vfs.exists(candidate + '/package.json')))
2065
2039
  return candidate;
2066
2040
  const idx = dir.lastIndexOf('/');
2067
2041
  if (idx <= 0)
@@ -2070,14 +2044,14 @@ export function speculativePackageDirs(vfs, cwdStripped, bundle) {
2070
2044
  }
2071
2045
  };
2072
2046
  const reached = new Set();
2073
- const hop = (fromDir) => {
2074
- for (const name of runtimeDeps(fromDir + '/package.json')) {
2075
- const dir = resolveDir(name, fromDir);
2047
+ const hop = async (fromDir) => {
2048
+ for (const name of (await runtimeDeps(fromDir + '/package.json'))) {
2049
+ const dir = (await resolveDir(name, fromDir));
2076
2050
  if (dir !== null)
2077
2051
  reached.add(dir);
2078
2052
  }
2079
2053
  };
2080
- hop(cwdStripped);
2054
+ (await hop(cwdStripped));
2081
2055
  const owners = new Set();
2082
2056
  for (const path of Object.keys(bundle)) {
2083
2057
  const owner = ownerOf(path);
@@ -2086,7 +2060,7 @@ export function speculativePackageDirs(vfs, cwdStripped, bundle) {
2086
2060
  }
2087
2061
  for (const owner of owners) {
2088
2062
  reached.add(owner);
2089
- hop(owner);
2063
+ (await hop(owner));
2090
2064
  }
2091
2065
  return [...reached].sort();
2092
2066
  }
@@ -2129,7 +2103,7 @@ export function speculativePackageDirs(vfs, cwdStripped, bundle) {
2129
2103
  * VFS_BUNDLE_MAX_FILES / VFS_BUNDLE_MAX_BYTES caps via the
2130
2104
  * `budgetState` counter.
2131
2105
  */
2132
- export function addStaticReadFileAssets(vfs, cwd, bundle, budgetState) {
2106
+ export async function addStaticReadFileAssets(vfs, cwd, bundle, budgetState) {
2133
2107
  let added = 0;
2134
2108
  // Asset extensions covered. Conservative whitelist — txt/json are
2135
2109
  // also legit runtime-loaded assets (e.g. mime-db json, license.txt).
@@ -2147,7 +2121,7 @@ export function addStaticReadFileAssets(vfs, cwd, bundle, budgetState) {
2147
2121
  // there's no `${` in the captured body (template-literal interpolation
2148
2122
  // is rejected).
2149
2123
  const RX = /(?:\bfs\s*\.)?readFileSync\s*\(\s*(?:[\w$.]+\s*\.\s*)?resolve\s*\(\s*__dirname\s*,\s*(['"`])([^'"`]+)\1\s*[\),]/g;
2150
- function addOneAsset(absPath) {
2124
+ async function addOneAsset(absPath) {
2151
2125
  const stripped = absPath.replace(/^\/+/, '');
2152
2126
  if (stripped in bundle)
2153
2127
  return false;
@@ -2156,12 +2130,12 @@ export function addStaticReadFileAssets(vfs, cwd, bundle, budgetState) {
2156
2130
  if (budgetState.totalBytes >= VFS_BUNDLE_MAX_BYTES)
2157
2131
  return false;
2158
2132
  try {
2159
- if (!vfs.exists(stripped) || vfs.isDirectory(stripped))
2133
+ if (!(await vfs.exists(stripped)) || (await vfs.isDirectory(stripped)))
2160
2134
  return false;
2161
- if (!_bundleAdmits(vfs, stripped, budgetState))
2135
+ if (!(await _bundleAdmits(vfs, stripped, budgetState)))
2162
2136
  return false;
2163
2137
  // hardening-r5: preserve binary content as Uint8Array.
2164
- const content = _readBundleCell(vfs, stripped);
2138
+ const content = (await _readBundleCell(vfs, stripped));
2165
2139
  const cellLen = _bundleCellLength(content);
2166
2140
  if (budgetState.totalBytes + cellLen > VFS_BUNDLE_MAX_BYTES)
2167
2141
  return false;
@@ -2231,7 +2205,7 @@ export function addStaticReadFileAssets(vfs, cwd, bundle, budgetState) {
2231
2205
  }
2232
2206
  resolved = out.join('/');
2233
2207
  }
2234
- addOneAsset(resolved);
2208
+ (await addOneAsset(resolved));
2235
2209
  }
2236
2210
  }
2237
2211
  return { added };
@@ -2283,7 +2257,7 @@ export function addStaticReadFileAssets(vfs, cwd, bundle, budgetState) {
2283
2257
  * Errors are swallowed: missing assets, unreadable VFS, and
2284
2258
  * non-string readFile inputs are silent skips — matches Z3 posture.
2285
2259
  */
2286
- export function addStaticReadFileDotfilesAndCompiled(vfs, cwd, bundle, budgetState) {
2260
+ export async function addStaticReadFileDotfilesAndCompiled(vfs, cwd, bundle, budgetState) {
2287
2261
  let added = 0;
2288
2262
  // The heuristic gate. Filenames matching either branch are eligible.
2289
2263
  // - Leading `.` covers `.ts-jest-digest`, `.cache-marker`, `.lintstagedrc`-ish
@@ -2322,7 +2296,7 @@ export function addStaticReadFileDotfilesAndCompiled(vfs, cwd, bundle, budgetSta
2322
2296
  '\\s*__dirname\\s*,\\s*' +
2323
2297
  // literal: ' " ` (no ${ for backtick)
2324
2298
  '([\'"`])([^\'"`]+)\\1', 'g');
2325
- function addOneAsset(absPath) {
2299
+ async function addOneAsset(absPath) {
2326
2300
  const stripped = absPath.replace(/^\/+/, '');
2327
2301
  if (stripped in bundle)
2328
2302
  return false;
@@ -2331,12 +2305,12 @@ export function addStaticReadFileDotfilesAndCompiled(vfs, cwd, bundle, budgetSta
2331
2305
  if (budgetState.totalBytes >= VFS_BUNDLE_MAX_BYTES)
2332
2306
  return false;
2333
2307
  try {
2334
- if (!vfs.exists(stripped) || vfs.isDirectory(stripped))
2308
+ if (!(await vfs.exists(stripped)) || (await vfs.isDirectory(stripped)))
2335
2309
  return false;
2336
- if (!_bundleAdmits(vfs, stripped, budgetState))
2310
+ if (!(await _bundleAdmits(vfs, stripped, budgetState)))
2337
2311
  return false;
2338
2312
  // hardening-r5: preserve binary content as Uint8Array.
2339
- const content = _readBundleCell(vfs, stripped);
2313
+ const content = (await _readBundleCell(vfs, stripped));
2340
2314
  const cellLen = _bundleCellLength(content);
2341
2315
  if (budgetState.totalBytes + cellLen > VFS_BUNDLE_MAX_BYTES)
2342
2316
  return false;
@@ -2405,7 +2379,7 @@ export function addStaticReadFileDotfilesAndCompiled(vfs, cwd, bundle, budgetSta
2405
2379
  const basename = slash >= 0 ? resolved.slice(slash + 1) : resolved;
2406
2380
  if (!FILENAME_GATE.test(basename))
2407
2381
  continue;
2408
- addOneAsset(resolved);
2382
+ (await addOneAsset(resolved));
2409
2383
  }
2410
2384
  }
2411
2385
  return { added };
@@ -2438,7 +2412,7 @@ export function addStaticReadFileDotfilesAndCompiled(vfs, cwd, bundle, budgetSta
2438
2412
  * (user scripts, npx-cache files outside node_modules, eval) is
2439
2413
  * a no-op.
2440
2414
  */
2441
- export function addBinTargetSiblings(vfs, scriptPath, bundle, budgetState, bundleProfile) {
2415
+ export async function addBinTargetSiblings(vfs, scriptPath, bundle, budgetState, bundleProfile) {
2442
2416
  if (!scriptPath)
2443
2417
  return { added: 0, wasmPaths: [] };
2444
2418
  const stripped = scriptPath.replace(/^\/+/, '');
@@ -2492,7 +2466,7 @@ export function addBinTargetSiblings(vfs, scriptPath, bundle, budgetState, bundl
2492
2466
  const dir = queue.shift();
2493
2467
  let entries;
2494
2468
  try {
2495
- entries = vfs.readdir(dir);
2469
+ entries = (await vfs.readdir(dir));
2496
2470
  }
2497
2471
  catch {
2498
2472
  continue;
@@ -2522,7 +2496,7 @@ export function addBinTargetSiblings(vfs, scriptPath, bundle, budgetState, bundl
2522
2496
  continue;
2523
2497
  let size;
2524
2498
  try {
2525
- size = vfs.lstat(child).size;
2499
+ size = (await vfs.lstat(child)).size;
2526
2500
  }
2527
2501
  catch {
2528
2502
  continue;
@@ -2553,7 +2527,7 @@ export function addBinTargetSiblings(vfs, scriptPath, bundle, budgetState, bundl
2553
2527
  // hardening-r5: preserve binary content as Uint8Array.
2554
2528
  let content;
2555
2529
  try {
2556
- content = _readBundleCell(vfs, candidate.path);
2530
+ content = (await _readBundleCell(vfs, candidate.path));
2557
2531
  }
2558
2532
  catch {
2559
2533
  continue;
@@ -2582,7 +2556,7 @@ export function addBinTargetSiblings(vfs, scriptPath, bundle, budgetState, bundl
2582
2556
  * map whether it read the bytes by path or carried them inline. A file that
2583
2557
  * cannot be read is left out; the seam's refusal names the module later.
2584
2558
  */
2585
- export function collectClosureWasmImages(vfs, bundle, unstagedPaths) {
2559
+ export async function collectClosureWasmImages(vfs, bundle, unstagedPaths) {
2586
2560
  const byPath = new Map();
2587
2561
  for (const [path, cell] of Object.entries(bundle)) {
2588
2562
  if (!path.endsWith('.wasm') || !(cell instanceof Uint8Array))
@@ -2594,7 +2568,7 @@ export function collectClosureWasmImages(vfs, bundle, unstagedPaths) {
2594
2568
  continue;
2595
2569
  let bytes;
2596
2570
  try {
2597
- bytes = vfs.readFile(path);
2571
+ bytes = await vfs.readFile(path);
2598
2572
  }
2599
2573
  catch {
2600
2574
  continue;
@@ -2619,7 +2593,7 @@ export function collectClosureWasmImages(vfs, bundle, unstagedPaths) {
2619
2593
  * budget is shared, so ordering by size maximizes the number of misses a
2620
2594
  * fixed number of bytes repairs.
2621
2595
  */
2622
- export function addObservedReads(vfs, observed, bundle, requiredPaths, budgetState) {
2596
+ export async function addObservedReads(vfs, observed, bundle, requiredPaths, budgetState) {
2623
2597
  if (!observed || observed.size === 0)
2624
2598
  return { added: 0 };
2625
2599
  const candidates = [];
@@ -2628,7 +2602,7 @@ export function addObservedReads(vfs, observed, bundle, requiredPaths, budgetSta
2628
2602
  continue;
2629
2603
  let stat;
2630
2604
  try {
2631
- stat = vfs.lstat(path);
2605
+ stat = (await vfs.lstat(path));
2632
2606
  }
2633
2607
  catch {
2634
2608
  continue;
@@ -2646,7 +2620,7 @@ export function addObservedReads(vfs, observed, bundle, requiredPaths, budgetSta
2646
2620
  continue;
2647
2621
  let content;
2648
2622
  try {
2649
- content = _readBundleCell(vfs, candidate.path);
2623
+ content = (await _readBundleCell(vfs, candidate.path));
2650
2624
  }
2651
2625
  catch {
2652
2626
  continue;
@@ -2747,7 +2721,7 @@ function binPackageRootSegment(pkgRoot, path) {
2747
2721
  function binPackageRelativePath(pkgRoot, path) {
2748
2722
  return path.startsWith(pkgRoot + '/') ? path.slice(pkgRoot.length + 1) : path;
2749
2723
  }
2750
- function addCwdProjectFiles(vfs, cwd, bundle, budgetState) {
2724
+ async function addCwdProjectFiles(vfs, cwd, bundle, budgetState) {
2751
2725
  const root = (cwd || '/home/user').replace(/^\/+/, '').replace(/\/+$/, '') || 'home/user';
2752
2726
  const MAX_PROJECT_FILES = 512;
2753
2727
  const SKIP_DIRS = new Set(['node_modules', '.git', '.nimbus']);
@@ -2758,7 +2732,7 @@ function addCwdProjectFiles(vfs, cwd, bundle, budgetState) {
2758
2732
  const dir = queue.shift();
2759
2733
  let entries;
2760
2734
  try {
2761
- entries = vfs.readdir(dir);
2735
+ entries = (await vfs.readdir(dir));
2762
2736
  }
2763
2737
  catch {
2764
2738
  continue;
@@ -2785,11 +2759,11 @@ function addCwdProjectFiles(vfs, cwd, bundle, budgetState) {
2785
2759
  // Skip an oversized file before reading it: this walk guesses at what
2786
2760
  // the program might read, and one guess must not spend the budget (or
2787
2761
  // the supervisor's headroom) that every later invocation then carries.
2788
- if (!_bundleAdmits(vfs, child, budgetState, CWD_SNAPSHOT_MAX_FILE_BYTES))
2762
+ if (!(await _bundleAdmits(vfs, child, budgetState, CWD_SNAPSHOT_MAX_FILE_BYTES)))
2789
2763
  continue;
2790
2764
  let content;
2791
2765
  try {
2792
- content = _readBundleCell(vfs, child);
2766
+ content = (await _readBundleCell(vfs, child));
2793
2767
  }
2794
2768
  catch {
2795
2769
  continue;
@@ -2832,7 +2806,7 @@ function addCwdProjectFiles(vfs, cwd, bundle, budgetState) {
2832
2806
  * / openSync / readFile in source are scanned. Entry code is always
2833
2807
  * scanned (it's the user's intent).
2834
2808
  */
2835
- function addEntryAbsPathReads(vfs, entryCode, bundle, budgetState) {
2809
+ async function addEntryAbsPathReads(vfs, entryCode, bundle, budgetState) {
2836
2810
  let added = 0;
2837
2811
  // Capture absolute-path string literals. The path may not contain
2838
2812
  // the quote char; the surrounding regex strips line/block comments
@@ -2841,7 +2815,7 @@ function addEntryAbsPathReads(vfs, entryCode, bundle, budgetState) {
2841
2815
  // excludes spaces, glob chars, template syntax — all dynamic.
2842
2816
  const RX = /(['"`])(\/[A-Za-z0-9._\-\/]{1,510})\1/g;
2843
2817
  const REJECT_PREFIX = /^\/(proc|sys|dev|lib|lib64|boot|root)(\/|$)/;
2844
- function tryAdd(absPath) {
2818
+ async function tryAdd(absPath) {
2845
2819
  if (!absPath || absPath.length < 2 || absPath.length > 512)
2846
2820
  return;
2847
2821
  if (REJECT_PREFIX.test(absPath))
@@ -2854,12 +2828,12 @@ function addEntryAbsPathReads(vfs, entryCode, bundle, budgetState) {
2854
2828
  if (budgetState.totalBytes >= VFS_BUNDLE_MAX_BYTES)
2855
2829
  return;
2856
2830
  try {
2857
- if (!vfs.exists(stripped) || vfs.isDirectory(stripped))
2831
+ if (!(await vfs.exists(stripped)) || (await vfs.isDirectory(stripped)))
2858
2832
  return;
2859
- if (!_bundleAdmits(vfs, stripped, budgetState))
2833
+ if (!(await _bundleAdmits(vfs, stripped, budgetState)))
2860
2834
  return;
2861
2835
  // hardening-r5: preserve binary content as Uint8Array.
2862
- const content = _readBundleCell(vfs, stripped);
2836
+ const content = (await _readBundleCell(vfs, stripped));
2863
2837
  const cellLen = _bundleCellLength(content);
2864
2838
  if (budgetState.totalBytes + cellLen > VFS_BUNDLE_MAX_BYTES)
2865
2839
  return;
@@ -2870,7 +2844,7 @@ function addEntryAbsPathReads(vfs, entryCode, bundle, budgetState) {
2870
2844
  }
2871
2845
  catch { /* swallow — file may be binary, race-deleted, etc. */ }
2872
2846
  }
2873
- function scanOne(src) {
2847
+ async function scanOne(src) {
2874
2848
  if (!src)
2875
2849
  return;
2876
2850
  // Strip line + block comments so we don't match commented-out reads.
@@ -2883,11 +2857,11 @@ function addEntryAbsPathReads(vfs, entryCode, bundle, budgetState) {
2883
2857
  // forbids most). The check on the captured group is cheap.
2884
2858
  if (/[\?\*\[\]\{\}]/.test(literal))
2885
2859
  continue;
2886
- tryAdd(literal);
2860
+ (await tryAdd(literal));
2887
2861
  }
2888
2862
  }
2889
2863
  // Always scan entry code.
2890
- scanOne(entryCode);
2864
+ (await scanOne(entryCode));
2891
2865
  // Optionally scan bundled JS sources too — useful for transitive cases
2892
2866
  // where a require'd module hardcodes an absolute path. Use the same
2893
2867
  // budget-state so we don't blow caps.
@@ -2903,7 +2877,7 @@ function addEntryAbsPathReads(vfs, entryCode, bundle, budgetState) {
2903
2877
  const cell = bundle[k];
2904
2878
  if (typeof cell !== 'string')
2905
2879
  continue;
2906
- scanOne(cell);
2880
+ (await scanOne(cell));
2907
2881
  }
2908
2882
  return { added };
2909
2883
  }
@@ -2919,8 +2893,44 @@ function looksLikeEsm(src) {
2919
2893
  *
2920
2894
  * Lives at module scope so warm exec invocations hit the cache without
2921
2895
  * paying the wasm cold-start cost again.
2896
+ *
2897
+ * Bounded by bytes, LRU, and reported to the heap model like the prefetch
2898
+ * cache (see ESM_TRANSFORM_CACHE_MAX_BYTES for the reset it caused
2899
+ * unbounded). An output larger than the whole bound is used for the build
2900
+ * that produced it and not retained: admitting it would evict everything
2901
+ * else to hold something that still does not fit.
2922
2902
  */
2923
2903
  const __esmTransformCache = new Map();
2904
+ let __esmTransformCacheBytes = 0;
2905
+ function __esmTransformCacheGet(key) {
2906
+ const code = __esmTransformCache.get(key);
2907
+ if (code === undefined)
2908
+ return undefined;
2909
+ // Refresh recency: a Map iterates in insertion order, so the oldest
2910
+ // entry is the first one.
2911
+ __esmTransformCache.delete(key);
2912
+ __esmTransformCache.set(key, code);
2913
+ return code;
2914
+ }
2915
+ function __esmTransformCacheSet(key, code) {
2916
+ const bytes = key.length + code.length;
2917
+ if (bytes > ESM_TRANSFORM_CACHE_MAX_BYTES)
2918
+ return;
2919
+ const previous = __esmTransformCache.get(key);
2920
+ if (previous !== undefined) {
2921
+ __esmTransformCacheBytes -= key.length + previous.length;
2922
+ __esmTransformCache.delete(key);
2923
+ }
2924
+ __esmTransformCache.set(key, code);
2925
+ __esmTransformCacheBytes += bytes;
2926
+ for (const [oldest, entry] of __esmTransformCache) {
2927
+ if (__esmTransformCacheBytes <= ESM_TRANSFORM_CACHE_MAX_BYTES)
2928
+ break;
2929
+ __esmTransformCache.delete(oldest);
2930
+ __esmTransformCacheBytes -= oldest.length + entry.length;
2931
+ }
2932
+ setTransformCacheBytes(__esmTransformCacheBytes);
2933
+ }
2924
2934
  function __cacheKey(src) {
2925
2935
  // FNV-1a 32-bit. Only used for cache keys, NEVER for content
2926
2936
  // integrity. The ~30-byte string we return is a hex hash + length —
@@ -3042,6 +3052,35 @@ export function compiledCellKey(path) {
3042
3052
  export function compiledCellPath(key) {
3043
3053
  return key.startsWith(COMPILED_CELL_KEY_PREFIX) ? key.slice(COMPILED_CELL_KEY_PREFIX.length) : null;
3044
3054
  }
3055
+ /**
3056
+ * The compile helper both generated facets define: one CommonJS cell to one
3057
+ * function, at module evaluation, the only moment workerd lets a string
3058
+ * become code. One definition so the two facets cannot drift on what a cell
3059
+ * may contain — the long-running facet once lacked the shebang strip, and a
3060
+ * required module that carried one compiled in a short command and failed
3061
+ * in the same tool's attached process.
3062
+ */
3063
+ export const FACET_COMPILE_HELPER = `
3064
+ function __mkCompiledFn(code) {
3065
+ // Node strips a leading shebang from every module before evaluation, not
3066
+ // only the entry: a required module bundled with its "#!/usr/bin/env node"
3067
+ // line is a SyntaxError under new Function.
3068
+ if (typeof code === "string" && code.charCodeAt(0) === 35 && code.charCodeAt(1) === 33) {
3069
+ const __nl = code.indexOf("\\n");
3070
+ code = __nl >= 0 ? code.slice(__nl + 1) : "";
3071
+ }
3072
+ function renameIfDeclared(name) {
3073
+ const re = new RegExp("(?:^|\\\\n|;)\\\\s*(?:const|let|var|function|class)\\\\s+" + name + "(?![$\\\\w])", "m");
3074
+ return re.test(code) ? name + "__nimbus_unused" : name;
3075
+ }
3076
+ const baseParams = [
3077
+ "exports", renameIfDeclared("require"), "module",
3078
+ renameIfDeclared("__filename"),
3079
+ renameIfDeclared("__dirname"),
3080
+ ];
3081
+ return new Function(...baseParams, code);
3082
+ }
3083
+ `;
3045
3084
  /**
3046
3085
  * The pre-compile loop both generated facets run at module evaluation, the
3047
3086
  * only moment workerd lets a string become code. One definition so the two
@@ -3136,8 +3175,8 @@ async function transformEsmInBundle(bundle, esbuild, pacer, isolatedTransform) {
3136
3175
  // cache entry and the second file would get the first file's URL.
3137
3176
  const absUrl = 'file:///' + path.replace(/^\/+/, '');
3138
3177
  const key = __cacheKey(src + '\0' + absUrl);
3139
- const cached = __esmTransformCache.get(key);
3140
- if (cached) {
3178
+ const cached = __esmTransformCacheGet(key);
3179
+ if (cached !== undefined) {
3141
3180
  bundle[target] = cached;
3142
3181
  transformed++;
3143
3182
  continue;
@@ -3157,7 +3196,7 @@ async function transformEsmInBundle(bundle, esbuild, pacer, isolatedTransform) {
3157
3196
  : await esbuild.transform(src, transformOptions));
3158
3197
  const code = bindImportMetaResolve(t.code, absUrl);
3159
3198
  bundle[target] = code;
3160
- __esmTransformCache.set(key, code);
3199
+ __esmTransformCacheSet(key, code);
3161
3200
  transformed++;
3162
3201
  }
3163
3202
  catch (e) {
@@ -3189,7 +3228,7 @@ async function transformEsmInBundle(bundle, esbuild, pacer, isolatedTransform) {
3189
3228
  const diagnosticSrc = '// framework-fixes-F4 diagnostic shim — esbuild rejected the ESM transform\n' +
3190
3229
  '(function () { throw new Error(' + escapedReason + '); })();\n';
3191
3230
  bundle[target] = diagnosticSrc;
3192
- __esmTransformCache.set(key, diagnosticSrc);
3231
+ __esmTransformCacheSet(key, diagnosticSrc);
3193
3232
  failed++;
3194
3233
  }
3195
3234
  }
@@ -3231,9 +3270,10 @@ export async function buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbui
3231
3270
  async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bundleProfile = DEFAULT_FACET_BUNDLE_PROFILE, observedReads, pacer, isolatedTransform, maxBundleBytes = VFS_BUNDLE_MAX_BYTES) {
3232
3271
  // Read the cursor BEFORE the walk: a mutation that lands while the bundle
3233
3272
  // is being assembled must be reported as invalidated, not silently missed.
3234
- const cursor = { epoch: vfs.epoch, rev: vfs.revision() };
3273
+ const admitted = await vfs.authority.acquire(null, 0);
3274
+ const cursor = { epoch: admitted.epoch, rev: admitted.rev };
3235
3275
  // 1. Static reachable-set walk from entry.
3236
- const prefetch = prefetchForRequire(vfs, entryCode || '', cwd, scriptPath, maxBundleBytes);
3276
+ const prefetch = (await prefetchForRequire(vfs, entryCode || '', cwd, scriptPath, maxBundleBytes));
3237
3277
  if ('kind' in prefetch) {
3238
3278
  // A required closure larger than the bound can never launch as a
3239
3279
  // snapshot. Surface it as the process's own failure rather than a
@@ -3263,14 +3303,14 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
3263
3303
  // admitted first, ahead of every guess, and they join the required set
3264
3304
  // rather than the evictable one, because a file evicted here misses
3265
3305
  // again on the next run and the loop never closes.
3266
- const observedAdd = addObservedReads(vfs, observedReads, bundle, requiredPaths, budgetState);
3306
+ const observedAdd = (await addObservedReads(vfs, observedReads, bundle, requiredPaths, budgetState));
3267
3307
  void observedAdd;
3268
3308
  await paceAfterPass();
3269
3309
  // 2. Greedy oversample — every installed pkg's pkg.json + main.
3270
3310
  // Catches dynamic-require / `bindings()` / plugin-loader cases the
3271
3311
  // regex prefetch misses. Its budget is independent from the complete
3272
3312
  // static require closure, which is correctness-critical.
3273
- const greedy = greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPaths);
3313
+ const greedy = (await greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPaths));
3274
3314
  await paceAfterPass();
3275
3315
  // 2.25 X.5-Z3: static-readFileSync asset prefetch. Scans every
3276
3316
  // bundle .js/.mjs/.cjs source for the canonical jsdom shape:
@@ -3281,7 +3321,7 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
3281
3321
  // `default-stylesheet.css` (and similar runtime asset reads in
3282
3322
  // tldts, parse5, lookup-table packages, mime-db, etc.) ENOENT
3283
3323
  // at facet runtime even though the file is on VFS-disk + in
3284
- const assetAdd = addStaticReadFileAssets(vfs, cwd, bundle, budgetState);
3324
+ const assetAdd = (await addStaticReadFileAssets(vfs, cwd, bundle, budgetState));
3285
3325
  void assetAdd;
3286
3326
  await paceAfterPass();
3287
3327
  // 2.27 X.5-U: dotfile + SWC-shape readFileSync sentinel prefetch.
@@ -3291,7 +3331,7 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
3291
3331
  // pattern AND filenames outside the Z3 ASSET_EXT whitelist
3292
3332
  // (dotfiles, no-extension sentinels, "digest/hash/version/sha/md5"
3293
3333
  // shapes). Motivating case: ts-jest's `.ts-jest-digest`. See
3294
- const dotAdd = addStaticReadFileDotfilesAndCompiled(vfs, cwd, bundle, budgetState);
3334
+ const dotAdd = (await addStaticReadFileDotfilesAndCompiled(vfs, cwd, bundle, budgetState));
3295
3335
  void dotAdd;
3296
3336
  await paceAfterPass();
3297
3337
  // 2.30 G3 (runtime-pkg wave): bin-target sibling oversample. Pulls
@@ -3302,14 +3342,14 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
3302
3342
  // catches custom extensions (.cow, .pem, .ttf, .wasm bundled
3303
3343
  // as data, etc.) without needing a per-pkg whitelist.
3304
3344
  // No-op when entry isn't inside node_modules.
3305
- const binSiblingAdd = addBinTargetSiblings(vfs, scriptPath, bundle, budgetState, bundleProfile);
3345
+ const binSiblingAdd = (await addBinTargetSiblings(vfs, scriptPath, bundle, budgetState, bundleProfile));
3306
3346
  await paceAfterPass();
3307
3347
  // 2.34 project-data snapshot: sync Node fs cannot await the
3308
3348
  // supervisor. Include a bounded snapshot of the current working tree
3309
3349
  // for common relative project-file reads while skipping dependency
3310
3350
  // and Nimbus cache directories. Async fs still uses live supervisor
3311
3351
  // reads and child-process staleness fallback.
3312
- const cwdProjectAdd = addCwdProjectFiles(vfs, cwd, bundle, budgetState);
3352
+ const cwdProjectAdd = (await addCwdProjectFiles(vfs, cwd, bundle, budgetState));
3313
3353
  void cwdProjectAdd;
3314
3354
  await paceAfterPass();
3315
3355
  // 2.35 shell compatibility: absolute-path readFileSync scanner.
@@ -3336,7 +3376,7 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
3336
3376
  // - Byte/file budget guards identical to other passes.
3337
3377
  // - Skip paths under known-untrusted prefixes (`/proc`, `/sys`,
3338
3378
  // `/dev` — we don't have these mounts; they'd never resolve).
3339
- const absScanAdd = addEntryAbsPathReads(vfs, entryCode || '', bundle, budgetState);
3379
+ const absScanAdd = (await addEntryAbsPathReads(vfs, entryCode || '', bundle, budgetState));
3340
3380
  void absScanAdd;
3341
3381
  await paceAfterPass();
3342
3382
  // 2.5 W3.5 Fix B: ESM→CJS transform pass. Walks `bundle`, sniffs each
@@ -3385,7 +3425,7 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
3385
3425
  // from content cap so fs.readdirSync remains honest even if the
3386
3426
  // content for a given file was capped out.
3387
3427
  await paceAfterPass();
3388
- const manifest = buildManifest(vfs, cwd, scriptPath);
3428
+ const manifest = (await buildManifest(vfs, cwd, scriptPath));
3389
3429
  await paceAfterPass();
3390
3430
  // 4. JSON-encoded-size guard, measured in UTF-8 bytes (not UTF-16 code
3391
3431
  // units) because that is what workerd charges against the per-module
@@ -3418,8 +3458,8 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
3418
3458
  }
3419
3459
  }
3420
3460
  const fileCount = Object.keys(bundle).length;
3421
- const metadata = buildVfsMetadata(vfs, manifest, bundle);
3422
- addUnreadableDenialCells(vfs, bundle, metadata);
3461
+ const metadata = (await buildVfsMetadata(vfs, manifest, bundle));
3462
+ (await addUnreadableDenialCells(vfs, bundle, metadata));
3423
3463
  await paceAfterPass();
3424
3464
  // Denial cells land after the eviction pass, so account for them before
3425
3465
  // deciding where the bundle has to live.
@@ -3428,7 +3468,7 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
3428
3468
  const bundleSideModulesRequired = size.bytes > BUNDLE_MAX_ENCODED_BYTES;
3429
3469
  // Suppress lint: `greedy.added` is observed only via diagnostics.
3430
3470
  void greedy;
3431
- const wasmImages = collectClosureWasmImages(vfs, bundle, binSiblingAdd.wasmPaths);
3471
+ const wasmImages = (await collectClosureWasmImages(vfs, bundle, binSiblingAdd.wasmPaths));
3432
3472
  return {
3433
3473
  bundle,
3434
3474
  manifest,
@@ -3471,6 +3511,7 @@ export class FacetManager {
3471
3511
  processes;
3472
3512
  portRegistry;
3473
3513
  vfs = null;
3514
+ filesystem = null;
3474
3515
  hooks;
3475
3516
  /**
3476
3517
  * The resident-process scheduler (loaders/process-fabric.ts). Every
@@ -3507,6 +3548,8 @@ export class FacetManager {
3507
3548
  * journal recovery rides the first pump.
3508
3549
  */
3509
3550
  launchPump;
3551
+ launchTasks = new Set();
3552
+ launchesClosed = false;
3510
3553
  // attach-pid → serve-pid: the resident serve facet a bare-`opencode` dual
3511
3554
  // spawn created as an OS-child of the attach TUI. When the attach process
3512
3555
  // exits (reported / killed), its serve facet is torn down with it.
@@ -3549,6 +3592,22 @@ export class FacetManager {
3549
3592
  * already deliver for the case that matters: running the command again.
3550
3593
  */
3551
3594
  residencyProfiles = new Map();
3595
+ /**
3596
+ * What the prefetch cache holds right now, for /api/_diag/memory: each
3597
+ * entry's key, the revision it was built at, and its retained bytes, next
3598
+ * to the filesystem's live revision and the residency profiles. A launch
3599
+ * that rebuilds where a hit was expected is explained by exactly these:
3600
+ * the revision moved, the key changed, or a miss profile dropped the entry.
3601
+ */
3602
+ prefetchCacheDiag() {
3603
+ return {
3604
+ revision: this.vfs ? this.vfs.revision() : null,
3605
+ entries: [...this.prefetchBundleCache].map(([key, entry]) => ({
3606
+ key, revision: entry.revision, bytes: entry.bytes,
3607
+ })),
3608
+ residencyProfiles: [...this.residencyProfiles].map(([key, paths]) => ({ key, paths: [...paths] })),
3609
+ };
3610
+ }
3552
3611
  /** In-flight request-driven durable-app ensures, single-flight per port. */
3553
3612
  ensureInflight = new Map();
3554
3613
  /** Per-pid chain of journal-row amendments; see `_amendRow`. */
@@ -3610,7 +3669,7 @@ export class FacetManager {
3610
3669
  // Rooted on waitUntil: the hook fires synchronously inside whatever turn
3611
3670
  // ended the process, and the delete must not be a floating promise there.
3612
3671
  this.processes.setOnTerminal((pid) => {
3613
- this.ctx.waitUntil(this._onResidentTerminal(pid));
3672
+ this.ctx.waitUntil(this.trackLaunchTask(this._onResidentTerminal(pid)));
3614
3673
  });
3615
3674
  }
3616
3675
  /**
@@ -3630,7 +3689,7 @@ export class FacetManager {
3630
3689
  const key = `${FENCED_WORK_KEY_PREFIX}${pid}`;
3631
3690
  const row = (await this.launchJournal.rows()).get(key);
3632
3691
  const crashed = entry?.state === 'exited' && (entry.exitCode ?? 0) !== 0;
3633
- if (row === undefined || row.restart !== 'on-failure' || !crashed) {
3692
+ if (this.launchesClosed || row === undefined || row.restart !== 'on-failure' || !crashed) {
3634
3693
  await this.launchJournal.release(pid);
3635
3694
  return;
3636
3695
  }
@@ -3704,7 +3763,10 @@ export class FacetManager {
3704
3763
  });
3705
3764
  return next;
3706
3765
  }
3707
- setVfs(vfs) { this.vfs = vfs; }
3766
+ /** The authority is the caller's: a session composes exactly one, and the
3767
+ * manager credentials its processes through that one rather than a second
3768
+ * authority over the same disk. */
3769
+ setVfs(vfs, filesystem) { this.vfs = vfs; this.filesystem = filesystem; }
3708
3770
  /**
3709
3771
  * The env/ctx pair every loader-backed runtime builds its facet pools
3710
3772
  * from. A pool is constructed from exactly these two, so the manager
@@ -3804,15 +3866,17 @@ export class FacetManager {
3804
3866
  * be read is left out; the program's compile then meets the seam's own
3805
3867
  * refusal, which names the module.
3806
3868
  */
3807
- _wasmModulesByValue(entry, wasmImports) {
3869
+ async _wasmModulesByValue(entry, wasmImports) {
3808
3870
  const modules = {};
3809
3871
  if (!this.vfs || wasmImports.length === 0)
3810
3872
  return modules;
3811
- const vfs = this.vfs.as(entry.cred);
3873
+ if (!this.filesystem)
3874
+ throw new Error('Process filesystem authority is not initialized');
3875
+ const vfs = new ExecutionFs(this.filesystem.bind({ pid: entry.pid, cred: entry.cred }));
3812
3876
  for (const image of wasmImports) {
3813
3877
  let bytes;
3814
3878
  try {
3815
- bytes = vfs.readFile(stripLeadingSlashes(image.vfsPath));
3879
+ bytes = (await vfs.readFile(stripLeadingSlashes(image.vfsPath)));
3816
3880
  }
3817
3881
  catch {
3818
3882
  continue;
@@ -3835,12 +3899,14 @@ export class FacetManager {
3835
3899
  if (!this.esbuild)
3836
3900
  this.esbuild = new EsbuildService(this.vfs.as(CRED_KERNEL));
3837
3901
  this.imageStore.ensureDir();
3838
- const vfs = this.vfs.as(entry.cred);
3902
+ if (!this.filesystem)
3903
+ throw new Error('Process filesystem authority is not initialized');
3904
+ const vfs = new ExecutionFs(this.filesystem.bind({ pid: entry.pid, cred: entry.cred }));
3839
3905
  const { cred } = entry;
3840
3906
  const profile = spec.bundleProfile ?? DEFAULT_FACET_BUNDLE_PROFILE;
3841
3907
  const credKey = `${cred.uid}:${cred.gid}:${cred.groups.join(',')}`;
3842
3908
  const key = `${profile}\x00${credKey}\x00${spec.cwd}\x00${spec.scriptPath ?? ''}\x00${_fnv1a(spec.entryCode)}`;
3843
- const revision = vfs.revision();
3909
+ const revision = (await vfs.revision());
3844
3910
  // An entry built at an older revision can never be SERVED again — the
3845
3911
  // lookup below requires an exact match — so from the first write after it
3846
3912
  // was admitted it is retained garbage, held in the isolate that is
@@ -4255,7 +4321,7 @@ export class FacetManager {
4255
4321
  // images ride by value: read here, inside the scope that holds
4256
4322
  // the map, and compiled by the loader like the sqlite sidecar.
4257
4323
  const wasmImports = facetWasmImports([], vfsState.wasmImages ?? []);
4258
- const wasmModules = this._wasmModulesByValue(entry, wasmImports);
4324
+ const wasmModules = (await this._wasmModulesByValue(entry, wasmImports));
4259
4325
  const generatedWorker = await generateEntrypointCode(code, vfsState, usesSqlite, shims, wasmImports);
4260
4326
  if (diagSink) {
4261
4327
  diagSink.moduleMapBytes = _encodedSourceBytes(generatedWorker.code);
@@ -4420,7 +4486,7 @@ export class FacetManager {
4420
4486
  // other writes flush live through the SUPERVISOR RPC bridge.
4421
4487
  if (this.vfs)
4422
4488
  this.imageStore.ensureDir();
4423
- const processVfs = this.vfs?.as(entry.cred);
4489
+ const processVfs = this.filesystem ? new ExecutionFs(this.filesystem.bind({ pid: entry.pid, cred: entry.cred })) : null;
4424
4490
  const vfsState = processVfs
4425
4491
  ? await buildPrefetchBundle(processVfs, undefined, opts.cwd, '', this.esbuild || undefined, DEFAULT_FACET_BUNDLE_PROFILE, undefined, undefined, this.hooks.transformLargeEsm)
4426
4492
  : { bundle: {}, manifest: {}, metadata: {}, reachableCount: 0, truncated: false };
@@ -4729,6 +4795,18 @@ export class FacetManager {
4729
4795
  pumpResidentLaunches() {
4730
4796
  return this.launchPump.pump();
4731
4797
  }
4798
+ trackLaunchTask(task) {
4799
+ this.launchTasks.add(task);
4800
+ void task.then(() => this.launchTasks.delete(task), () => this.launchTasks.delete(task));
4801
+ return task;
4802
+ }
4803
+ async closeLaunches() {
4804
+ this.launchesClosed = true;
4805
+ this.launchPump.close();
4806
+ while (this.launchTasks.size > 0)
4807
+ await Promise.allSettled([...this.launchTasks]);
4808
+ await Promise.all(this.rowAmendments.values());
4809
+ }
4732
4810
  /** Whether any launch is suspended waiting for a turn. */
4733
4811
  get hasPendingLaunchTurns() {
4734
4812
  return this.launchPump.hasPending;
@@ -4998,7 +5076,12 @@ export class FacetManager {
4998
5076
  * terminal WebSocket needs to survive a launch, and what no amount of making
4999
5077
  * the launch faster would have given it.
5000
5078
  */
5001
- async _runResidentLaunch(entry, code, command, opts, attempt) {
5079
+ _runResidentLaunch(entry, code, command, opts, attempt) {
5080
+ if (this.launchesClosed)
5081
+ return Promise.reject(new Error('Launch scheduler is closed'));
5082
+ return this.trackLaunchTask(this._runResidentLaunchBody(entry, code, command, opts, attempt));
5083
+ }
5084
+ async _runResidentLaunchBody(entry, code, command, opts, attempt) {
5002
5085
  const cwd = opts.cwd || '/home/user';
5003
5086
  const pacer = this._launchPacer(entry.pid);
5004
5087
  // Journalled before the first byte of work. A launch that FAILS deletes