@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
@@ -45,7 +45,7 @@ import { getExportsResolverJS } from '@nimbus-sh/core/_shared/exports-resolver.j
45
45
  import { getTypescriptSpecifiersJS } from '@nimbus-sh/core/_shared/typescript-specifiers.js';
46
46
  import { NIMBUS_AI_CREDENTIAL_HEADERS, NIMBUS_AI_TOKEN_ENV } from '@nimbus-sh/core/_shared/ai-egress.js';
47
47
  import { MAX_RPC_SAFE_PAYLOAD_BYTES } from '@nimbus-sh/platform/limits.js';
48
- import { FACET_PROVIDED_PACKAGES, FS_READ_BATCH_PATH_LIMIT, FS_READ_BATCH_REQUEST_BYTES, NIMBUS_AI_GATEWAY_PORT, NODE_VERSION, NODE_VERSIONS, } from '@nimbus-sh/core/constants.js';
48
+ import { FACET_PROVIDED_PACKAGES, FS_READ_BATCH_PATH_LIMIT, FS_READ_BATCH_REQUEST_BYTES, NIMBUS_AI_GATEWAY_PORT, NODE_VERSION, NODE_VERSIONS, VFS_CAPACITY, } from '@nimbus-sh/core/constants.js';
49
49
  const STREAMS_CODE = generateStreamsCode();
50
50
  const SQLITE_SHIM_CODE = generateSqliteShimCode();
51
51
  const UNDICI_SHIM_CODE = generateUndiciShimCode();
@@ -634,6 +634,43 @@ function __residentAnyUnder(prefix) {
634
634
  return false;
635
635
  }
636
636
 
637
+ // ═══════════════════════════════════════════════════════════════════════
638
+ // ── fs.constants (linux x64) ───────────────────────────────────────
639
+ // ═══════════════════════════════════════════════════════════════════════
640
+ //
641
+ // The ONE table behind fs.constants, fs.promises.constants, and the fs slice
642
+ // of node:constants / process.binding('fs'). Values are real Node on Linux
643
+ // x64 (node -p 'require("fs").constants'). Packages compose open flags from
644
+ // this table at module init — modern-tar (create-astro via @bluwy/giget-core)
645
+ // computes O_WRONLY|O_CREAT|O_TRUNC|O_NOFOLLOW|O_EXCL and passes the number
646
+ // to fs.open — so a partial table silently degrades every such open to
647
+ // O_RDONLY (0) and every extraction fails ENOENT. Frozen, as Node's is.
648
+ const __fsConstants = Object.freeze({
649
+ // ── libuv fs flags ────────────────────────────────────────────────
650
+ UV_FS_SYMLINK_DIR: 1, UV_FS_SYMLINK_JUNCTION: 2,
651
+ // ── open(2) flags ─────────────────────────────────────────────────
652
+ O_RDONLY: 0, O_WRONLY: 1, O_RDWR: 2,
653
+ UV_DIRENT_UNKNOWN: 0, UV_DIRENT_FILE: 1, UV_DIRENT_DIR: 2, UV_DIRENT_LINK: 3,
654
+ UV_DIRENT_FIFO: 4, UV_DIRENT_SOCKET: 5, UV_DIRENT_CHAR: 6, UV_DIRENT_BLOCK: 7,
655
+ EXTENSIONLESS_FORMAT_JAVASCRIPT: 0, EXTENSIONLESS_FORMAT_WASM: 1,
656
+ // ── stat.mode file-type bits ──────────────────────────────────────
657
+ S_IFMT: 61440, S_IFREG: 32768, S_IFDIR: 16384, S_IFCHR: 8192,
658
+ S_IFBLK: 24576, S_IFIFO: 4096, S_IFLNK: 40960, S_IFSOCK: 49152,
659
+ O_CREAT: 64, O_EXCL: 128, UV_FS_O_FILEMAP: 0, O_NOCTTY: 256, O_TRUNC: 512,
660
+ O_APPEND: 1024, O_DIRECTORY: 65536, O_NOATIME: 262144, O_NOFOLLOW: 131072,
661
+ O_SYNC: 1052672, O_DSYNC: 4096, O_DIRECT: 16384, O_NONBLOCK: 2048,
662
+ // ── stat.mode permission bits ─────────────────────────────────────
663
+ S_IRWXU: 448, S_IRUSR: 256, S_IWUSR: 128, S_IXUSR: 64,
664
+ S_IRWXG: 56, S_IRGRP: 32, S_IWGRP: 16, S_IXGRP: 8,
665
+ S_IRWXO: 7, S_IROTH: 4, S_IWOTH: 2, S_IXOTH: 1,
666
+ // ── fs.access modes ───────────────────────────────────────────────
667
+ F_OK: 0, R_OK: 4, W_OK: 2, X_OK: 1,
668
+ // ── fs.copyFile modes (libuv shape + Node alias) ──────────────────
669
+ UV_FS_COPYFILE_EXCL: 1, COPYFILE_EXCL: 1,
670
+ UV_FS_COPYFILE_FICLONE: 2, COPYFILE_FICLONE: 2,
671
+ UV_FS_COPYFILE_FICLONE_FORCE: 4, COPYFILE_FICLONE_FORCE: 4,
672
+ });
673
+
637
674
  // ═══════════════════════════════════════════════════════════════════════
638
675
  // ── fs shim (VFS-backed) ───────────────────────────────────────────
639
676
  // ═══════════════════════════════════════════════════════════════════════
@@ -1271,6 +1308,10 @@ const __fsMod = (() => {
1271
1308
  mtimeMs: _coerceTimeMs(mtime, syscall, p),
1272
1309
  };
1273
1310
  _localTimes[k] = time;
1311
+ // A spawn-time stat record answers statSync ahead of _localTimes, so it
1312
+ // must carry the new times too or the sync view keeps the old mtime.
1313
+ const meta = _metadata(absPath);
1314
+ if (meta) { meta.atime = time.atimeMs; meta.mtime = time.mtimeMs; }
1274
1315
  return time;
1275
1316
  }
1276
1317
 
@@ -1412,6 +1453,27 @@ const __fsMod = (() => {
1412
1453
  return evicted;
1413
1454
  }
1414
1455
 
1456
+ // Cells an own-mutation lease evicted with no barrier in hand.
1457
+ //
1458
+ // A barrier hands _acquireAndRefetch every path it dropped, which is what
1459
+ // keeps a sync read inside a timer callback reading the peer's new bytes
1460
+ // instead of EAGAIN. A lease's end drops a cell on its own account — the
1461
+ // barrier that would have reported it was suppressed by the lease itself
1462
+ // — so the same debt is recorded here and settled by the next untrusted
1463
+ // resumption.
1464
+ const _owedRefetch = new Set();
1465
+
1466
+ // An own-mutation lease can end in an eviction (a peer wrote inside the
1467
+ // window). The ledger owning that decision is spliced AHEAD of this
1468
+ // closure, so it is handed the one eviction path rather than keeping a
1469
+ // second copy of it: content view, stat view, the invalidation count and
1470
+ // the refetch debt move together here or not at all.
1471
+ globalThis.__nimbusEvictResidentCell = (k) => {
1472
+ if (!_evictResident(k)) return;
1473
+ _stats.invalidations++;
1474
+ _owedRefetch.add(k);
1475
+ };
1476
+
1415
1477
  /**
1416
1478
  * Install bytes the supervisor just served as the resident cell for a
1417
1479
  * path, at the cursor they were served under.
@@ -1591,6 +1653,11 @@ const __fsMod = (() => {
1591
1653
  } else if (Array.isArray(result.paths)) {
1592
1654
  for (const entry of result.paths) {
1593
1655
  const k = entry.path;
1656
+ // This cursor is about to advance past the report, so a path with
1657
+ // one of this facet's own writes in flight keeps the number: only
1658
+ // that write's own revision, when it arrives, can say whether the
1659
+ // report was itself or a peer. Everything else ignores it.
1660
+ __nimbusNoteVfsReport(k, entry.rev);
1594
1661
  if (__vfsBundleRevisions[k] >= entry.rev) { _stats.selfWrites++; continue; }
1595
1662
  const held = !!(__vfsBundle && k in __vfsBundle);
1596
1663
  if (_evictResident(k)) _stats.invalidations++;
@@ -1624,6 +1691,13 @@ const __fsMod = (() => {
1624
1691
  */
1625
1692
  async function _acquireAndRefetch(supervisor) {
1626
1693
  const stale = await _acquireBarrier(supervisor);
1694
+ // Plus what an own-mutation lease dropped since the last resumption:
1695
+ // the same debt this function exists to settle, owed by an eviction
1696
+ // that had no barrier to report it (see _owedRefetch).
1697
+ if (_owedRefetch.size > 0) {
1698
+ for (const k of _owedRefetch) if (stale.indexOf(k) === -1) stale.push(k);
1699
+ _owedRefetch.clear();
1700
+ }
1627
1701
  if (stale.length === 0) return;
1628
1702
  await Promise.all(stale.map((k) => _liveReadFile("/" + k, undefined, true).catch(() => {})));
1629
1703
  }
@@ -1724,7 +1798,16 @@ const __fsMod = (() => {
1724
1798
  // supervisor.mkdir is recursive, so the deepest unannounced one creates all
1725
1799
  // of them in a single round trip; a path whose directories are already live
1726
1800
  // (the common case) costs none at all.
1801
+ //
1802
+ // This is also the one seam every asynchronous fs entry point crosses on
1803
+ // its way to the authority — directly, or through
1804
+ // _flushLocalPathToSupervisor — so it is where a live call waits for the
1805
+ // structural mutations the program issued synchronously before it:
1806
+ // mkdirSync, rmdirSync, unlinkSync, renameSync queue their authority RPC
1807
+ // (below) rather than land it, and an open/stat/readdir that overtook that
1808
+ // queue would be answered for a tree the program has already changed.
1727
1809
  async function _announceLocalDirs(absPath, supervisor) {
1810
+ await _awaitStructuralOrder(absPath);
1728
1811
  if (!__vfsDirs || typeof supervisor.mkdir !== "function") return;
1729
1812
  const pending = [];
1730
1813
  let key = "";
@@ -1740,24 +1823,142 @@ const __fsMod = (() => {
1740
1823
  _markVfsStale();
1741
1824
  }
1742
1825
 
1826
+ // The write ledger is spliced ahead of the shims in every runner
1827
+ // (facets/manager.ts, opencode-facet-runner.ts). A harness that evaluates
1828
+ // the shims alone has no mutation queue and therefore nothing to wait for;
1829
+ // an absent ledger must not be an error on a read path that never needed
1830
+ // one before.
1831
+ function _hasVfsMutationQueue() {
1832
+ return typeof __nimbusQueueVfsMutation === "function";
1833
+ }
1834
+ function _awaitStructuralOrder(absPath) {
1835
+ return _hasVfsMutationQueue() ? __nimbusAwaitAncestorMutations(absPath) : Promise.resolve();
1836
+ }
1837
+
1838
+ /**
1839
+ * Carry a synchronous structural mutation to the authority.
1840
+ *
1841
+ * mkdirSync, rmdirSync, unlinkSync and renameSync used to edit the local
1842
+ * tables and stop: a sync syscall cannot make an RPC, and unlike a sync
1843
+ * write they parked nothing the write-back could later flush. Measured
1844
+ * live: node-tar's \`mkdirSync(dir)\` then \`fs.promises.open(dir/file,
1845
+ * "w")\` — once per extracted entry — was answered ENOENT by an authority
1846
+ * that had never heard of \`dir\`; that is create-astro's template copy.
1847
+ *
1848
+ * The sync effect stays exactly as it was. What is added is the same RPC
1849
+ * the asynchronous form of the call issues, queued through the write
1850
+ * ledger so it registers with the exit drain, is ordered behind pending
1851
+ * mutations of its ancestors (the ledger does that for every queued
1852
+ * mutation), and — for the two that act on a subtree — behind pending
1853
+ * mutations beneath it. The async forms are that same queued call, awaited.
1854
+ *
1855
+ * \`null\` when there is no authority to tell: standalone and unit contexts
1856
+ * keep today's local-only behaviour. Without a ledger the RPC is issued
1857
+ * directly, which is what the async forms did before they were queued.
1858
+ */
1859
+ // \`method\` names the supervisor RPC when it differs from the syscall the
1860
+ // caller reports (lchown rides \`chown\`, rm rides \`fsRemove\`).
1861
+ function _queueStructuralMutation(absPath, syscall, displayPath, rpc, after, method) {
1862
+ const supervisor = _supervisor();
1863
+ if (!supervisor || typeof supervisor[method || syscall] !== "function") return null;
1864
+ const queued = _hasVfsMutationQueue();
1865
+ const before = queued && after ? after() : null;
1866
+ const run = async () => {
1867
+ if (before) await before;
1868
+ // Only chown answers with a receipt. The rest (mkdir, unlink, rmdir,
1869
+ // rename, rm) have already dropped the path's cell and stamp in their
1870
+ // synchronous half, so no lease is taken at all; where one is — the
1871
+ // stamp unlink leaves behind — an absent receipt retires it, which is
1872
+ // what an unstamped cell already costs.
1873
+ await _ownMutation(
1874
+ absPath,
1875
+ () => _fsRpc(rpc(supervisor), syscall, displayPath, (result) => result),
1876
+ );
1877
+ _markVfsStale();
1878
+ };
1879
+ return queued ? __nimbusQueueVfsMutation(absPath, run) : run();
1880
+ }
1881
+
1882
+ /**
1883
+ * Run one of this facet's own partial mutations under a lease.
1884
+ *
1885
+ * See __nimbusBeginOwnMutation: the barrier KEEPS the cell while the RPC
1886
+ * is in flight — a barrier issued after this mutation can be answered
1887
+ * ahead of it, and an eviction there loses bytes no later stamp can
1888
+ * recover — and the receipt decides at the end. \`apply\` lands the local
1889
+ * effect while the lease still holds the cell, so the stamp the end
1890
+ * settles describes a cell that already carries the mutation.
1891
+ *
1892
+ * Heap-side stamps do not describe rows in the resident store, so store
1893
+ * mode has no lease to take and is left to the store's own provenance.
1894
+ */
1895
+ async function _ownMutation(absPath, rpc, apply) {
1896
+ const key = _strip(absPath);
1897
+ const held = !_residentStorePresent() && __nimbusBeginOwnMutation(key);
1898
+ let receipt;
1899
+ let landed = false;
1900
+ try { receipt = await rpc(); landed = true; }
1901
+ finally {
1902
+ // The local effect follows the mutation landing, not the receipt: the
1903
+ // receipt only settles the stamp, and a supervisor that answers
1904
+ // without one still applied the bytes. The end runs even if applying
1905
+ // throws: a lease left open pins its cell at Infinity, which is the one
1906
+ // state in this protocol that can serve a stale byte forever.
1907
+ try { if (landed && apply) apply(receipt); }
1908
+ finally { __nimbusEndOwnMutation(key, held, receipt); }
1909
+ }
1910
+ return receipt;
1911
+ }
1912
+
1913
+ // A sync caller has no frame to receive the outcome. The ledger already
1914
+ // marks a queued result handled and retains what the exit drain must
1915
+ // report; this only keeps a ledger-less embedding from raising an
1916
+ // unhandled rejection for a verdict nobody could catch.
1917
+ function _detachStructuralMutation(mutation) {
1918
+ if (mutation) mutation.then(undefined, () => undefined);
1919
+ }
1920
+
1921
+ // A rename lives at two names. Its mutation is queued under the source;
1922
+ // this parks a fence under the destination so that anything queued for the
1923
+ // destination, or an ancestor wait that passes through it, is ordered
1924
+ // behind the move as well. The fence never fails on its own account.
1925
+ function _fenceVfsMutation(absPath, mutation) {
1926
+ if (!mutation || !_hasVfsMutationQueue()) return;
1927
+ __nimbusQueueVfsMutation(absPath, () => mutation.then(() => undefined, () => undefined), false);
1928
+ }
1929
+
1930
+ // Queue the parked write for \`absPath\`, if any, on the path's mutation
1931
+ // tail. Registration is synchronous: a caller that queues its own mutation
1932
+ // on the same tail right after this call is ordered behind the flush.
1933
+ function _flushParkedWrite(absPath, supervisor) {
1934
+ const k = _strip(absPath);
1935
+ if (!__vfsWrites || !(k in __vfsWrites) || typeof supervisor.writeFile !== "function") {
1936
+ return Promise.resolve(undefined);
1937
+ }
1938
+ return __nimbusFlushVfsWrite(
1939
+ absPath,
1940
+ (content, snapshot) => _fsRpc(
1941
+ __nimbusPersistVfsWrite(supervisor, absPath, content, snapshot),
1942
+ "write", absPath,
1943
+ (result) => result,
1944
+ ),
1945
+ );
1946
+ }
1947
+
1743
1948
  async function _flushLocalPathToSupervisor(absPath, supervisor) {
1744
1949
  const k = _strip(absPath);
1745
1950
  await _announceLocalDirs(absPath, supervisor);
1746
1951
  if (__vfsWrites && k in __vfsWrites && typeof supervisor.writeFile === "function") {
1747
- await __nimbusFlushVfsWrite(
1748
- absPath,
1749
- (content, snapshot) => _fsRpc(
1750
- __nimbusPersistVfsWrite(supervisor, absPath, content, snapshot),
1751
- "write", absPath,
1752
- (result) => result,
1753
- ),
1754
- );
1952
+ await _flushParkedWrite(absPath, supervisor);
1755
1953
  _markVfsStale();
1756
1954
  }
1757
1955
  // Pending sync chmod rides along with any flush of the same path
1758
1956
  // (idempotent — the entry stays so local statSync remains coherent).
1759
1957
  if (k in _localModes && typeof supervisor.chmod === "function") {
1760
- await _fsRpc(supervisor.chmod(absPath, _localModes[k]), "chmod", absPath, () => undefined);
1958
+ await _ownMutation(
1959
+ absPath,
1960
+ () => _fsRpc(supervisor.chmod(absPath, _localModes[k]), "chmod", absPath, (result) => result),
1961
+ );
1761
1962
  _markVfsStale();
1762
1963
  }
1763
1964
  }
@@ -1852,16 +2053,26 @@ const __fsMod = (() => {
1852
2053
  return stat;
1853
2054
  }
1854
2055
 
1855
- function _direntObject(name, type) {
1856
- const isDir = type === "directory" || type === "dir";
1857
- const isSymlink = type === "symlink";
1858
- return {
1859
- name,
1860
- isFile: () => !isDir && !isSymlink,
1861
- isDirectory: () => isDir,
1862
- isSymbolicLink: () => isSymlink,
1863
- };
2056
+ // The one Dirent shape: readdir({ withFileTypes }) sync and async, and
2057
+ // every Dir.read(). \`parentPath\` is Node's field; \`path\` its deprecated
2058
+ // alias that older callers still read.
2059
+ class __Dirent {
2060
+ constructor(name, type, parentPath) {
2061
+ this.name = name;
2062
+ this.parentPath = parentPath === undefined ? "" : String(parentPath);
2063
+ this.path = this.parentPath;
2064
+ this._isDir = type === "directory" || type === "dir";
2065
+ this._isSymlink = type === "symlink";
2066
+ }
2067
+ isFile() { return !this._isDir && !this._isSymlink; }
2068
+ isDirectory() { return this._isDir; }
2069
+ isSymbolicLink() { return this._isSymlink; }
2070
+ isBlockDevice() { return false; }
2071
+ isCharacterDevice() { return false; }
2072
+ isFIFO() { return false; }
2073
+ isSocket() { return false; }
1864
2074
  }
2075
+ function _direntObject(name, type, parentPath) { return new __Dirent(name, type, parentPath); }
1865
2076
 
1866
2077
  // Largest single ranged read issued against the supervisor. Every live
1867
2078
  // read path (read streams, whole-file async reads) is expressed as a
@@ -2125,7 +2336,7 @@ const __fsMod = (() => {
2125
2336
  }
2126
2337
  if (opts?.withFileTypes) {
2127
2338
  return entries
2128
- .map((entry) => _direntObject(entry.name, entry.type))
2339
+ .map((entry) => _direntObject(entry.name, entry.type, absPath))
2129
2340
  .sort((a, b) => a.name.localeCompare(b.name));
2130
2341
  }
2131
2342
  return entries.map((entry) => entry.name).sort();
@@ -2148,7 +2359,9 @@ const __fsMod = (() => {
2148
2359
  async function _readlinkAsync(p) {
2149
2360
  const supervisor = _supervisor();
2150
2361
  if (supervisor && typeof supervisor.readlink === "function") {
2151
- const target = await _fsRpc(supervisor.readlink(_resolve(p)), "readlink", p, (result) => result);
2362
+ const absPath = _resolve(p);
2363
+ await _awaitStructuralOrder(absPath);
2364
+ const target = await _fsRpc(supervisor.readlink(absPath), "readlink", p, (result) => result);
2152
2365
  if (target !== null && target !== undefined) return target;
2153
2366
  }
2154
2367
  throw _fsErr("EINVAL", "readlink", p);
@@ -2157,7 +2370,9 @@ const __fsMod = (() => {
2157
2370
  async function _symlinkAsync(target, path) {
2158
2371
  const supervisor = _supervisor();
2159
2372
  if (supervisor && typeof supervisor.symlink === "function") {
2160
- await _fsRpc(supervisor.symlink(String(target), _resolve(path)), "symlink", path, () => undefined);
2373
+ const absPath = _resolve(path);
2374
+ await _awaitStructuralOrder(absPath);
2375
+ await _fsRpc(supervisor.symlink(String(target), absPath), "symlink", path, () => undefined);
2161
2376
  _markVfsStale();
2162
2377
  return;
2163
2378
  }
@@ -2170,8 +2385,10 @@ const __fsMod = (() => {
2170
2385
  const supervisor = _supervisor();
2171
2386
  if (supervisor && typeof supervisor.writeFile === "function") {
2172
2387
  await _announceLocalDirs(absPath, supervisor);
2388
+ // The revision comes back so the ledger can stamp the cell: an async
2389
+ // whole write is the facet's own as much as a parked sync one is.
2173
2390
  await __nimbusFlushVfsWrite(absPath, (content) =>
2174
- _fsRpc(supervisor.writeFile(absPath, content), "write", p, () => undefined)
2391
+ _fsRpc(supervisor.writeFile(absPath, content), "write", p, (result) => result)
2175
2392
  );
2176
2393
  _markVfsStale();
2177
2394
  }
@@ -2194,41 +2411,13 @@ const __fsMod = (() => {
2194
2411
  _markVfsStale();
2195
2412
  }
2196
2413
 
2197
- async function _mkdirAsync(p, opts) {
2198
- mkdirSync(p, opts);
2199
- const supervisor = _supervisor();
2200
- if (supervisor && typeof supervisor.mkdir === "function") {
2201
- await _fsRpc(supervisor.mkdir(_resolve(p)), "mkdir", p, () => undefined);
2202
- _markVfsStale();
2203
- }
2204
- }
2205
-
2206
- async function _unlinkAsync(p) {
2207
- unlinkSync(p);
2208
- const supervisor = _supervisor();
2209
- if (supervisor && typeof supervisor.unlink === "function") {
2210
- await _fsRpc(supervisor.unlink(_resolve(p)), "unlink", p, () => undefined);
2211
- _markVfsStale();
2212
- }
2213
- }
2214
-
2215
- async function _rmdirAsync(p) {
2216
- rmdirSync(p);
2217
- const supervisor = _supervisor();
2218
- if (supervisor && typeof supervisor.rmdir === "function") {
2219
- await _fsRpc(supervisor.rmdir(_resolve(p)), "rmdir", p, () => undefined);
2220
- _markVfsStale();
2221
- }
2222
- }
2223
-
2224
- async function _renameAsync(oldP, newP) {
2225
- renameSync(oldP, newP);
2226
- const supervisor = _supervisor();
2227
- if (supervisor && typeof supervisor.rename === "function") {
2228
- await _fsRpc(supervisor.rename(_resolve(oldP), _resolve(newP)), "rename", oldP, () => undefined);
2229
- _markVfsStale();
2230
- }
2231
- }
2414
+ // The async structural calls ARE the sync ones, awaited: the same local
2415
+ // effect and the same queued authority RPC, so the two forms cannot
2416
+ // disagree about order, and a program mixing them sees one sequence.
2417
+ async function _mkdirAsync(p, opts) { await _mkdirQueued(p, opts); }
2418
+ async function _unlinkAsync(p) { await _unlinkQueued(p); }
2419
+ async function _rmdirAsync(p) { await _rmdirQueued(p); }
2420
+ async function _renameAsync(oldP, newP) { await _renameQueued(oldP, newP); }
2232
2421
 
2233
2422
  async function _truncateAsync(p, len) {
2234
2423
  const absPath = _resolve(p);
@@ -2246,6 +2435,9 @@ const __fsMod = (() => {
2246
2435
  (content, snapshot) =>
2247
2436
  __nimbusPersistVfsWrite(supervisor, absPath, content, snapshot),
2248
2437
  );
2438
+ // Persisting the append drops the resident cell (the ledger
2439
+ // refetches it on the next live read), so there is no cell to
2440
+ // trim or stamp behind this truncate.
2249
2441
  await __nimbusQueueVfsMutation(absPath, async () => {
2250
2442
  await flush;
2251
2443
  await _fsRpc(
@@ -2267,12 +2459,15 @@ const __fsMod = (() => {
2267
2459
  // Live file is the source of truth — supervisor trims only the
2268
2460
  // boundary chunk; ENOENT propagates when it does not exist.
2269
2461
  const generation = __vfsWriteGenerations[k];
2270
- await __nimbusQueueVfsMutation(absPath, () =>
2271
- _fsRpc(supervisor.fsTruncate(absPath, size), "truncate", p, () => undefined)
2272
- );
2273
- if (__vfsWriteGenerations[k] === generation && localCell !== undefined) {
2274
- _truncateLocalCell(absPath, size);
2275
- }
2462
+ await __nimbusQueueVfsMutation(absPath, () => _ownMutation(
2463
+ absPath,
2464
+ () => _fsRpc(supervisor.fsTruncate(absPath, size), "truncate", p, (result) => result),
2465
+ () => {
2466
+ if (__vfsWriteGenerations[k] === generation && localCell !== undefined) {
2467
+ _truncateLocalCell(absPath, size);
2468
+ }
2469
+ },
2470
+ ));
2276
2471
  _markVfsStale();
2277
2472
  return;
2278
2473
  }
@@ -2292,9 +2487,9 @@ const __fsMod = (() => {
2292
2487
  _recordLocalTimes(absPath, atime, mtime, "lutimes", p);
2293
2488
  }
2294
2489
 
2295
- async function _utimesAsync(p, atime, mtime, opts) {
2490
+ async function _utimesAsync(p, atime, mtime, opts, syscallOverride) {
2296
2491
  const followSymlinks = !(opts && opts.followSymlinks === false);
2297
- const syscall = followSymlinks ? "utimes" : "lutimes";
2492
+ const syscall = syscallOverride || (followSymlinks ? "utimes" : "lutimes");
2298
2493
  const absPath = _resolve(p);
2299
2494
  const supervisor = _supervisor();
2300
2495
  let localExists = false;
@@ -2305,11 +2500,16 @@ const __fsMod = (() => {
2305
2500
  const time = _recordLocalTimes(absPath, atime, mtime, syscall, p);
2306
2501
  if (supervisor && typeof supervisor.utimes === "function") {
2307
2502
  await _flushLocalPathToSupervisor(absPath, supervisor);
2308
- await _fsRpc(
2503
+ // Ordered behind the path's pending mutations: an fd write queued a
2504
+ // moment ago (modern-tar writes, then futimes, then closes) would
2505
+ // otherwise land AFTER the timestamp and reset it to "now".
2506
+ const leased = () => _ownMutation(absPath, () => _fsRpc(
2309
2507
  supervisor.utimes(absPath, time.atimeMs, time.mtimeMs),
2310
2508
  syscall, p,
2311
- () => undefined,
2312
- );
2509
+ (result) => result,
2510
+ ));
2511
+ if (_hasVfsMutationQueue()) await __nimbusQueueVfsMutation(absPath, leased);
2512
+ else await leased();
2313
2513
  _markVfsStale();
2314
2514
  return;
2315
2515
  }
@@ -2359,11 +2559,46 @@ const __fsMod = (() => {
2359
2559
  const nextUid = _coerceId(uid, syscall, p);
2360
2560
  const nextGid = _coerceId(gid, syscall, p);
2361
2561
  await _flushLocalPathToSupervisor(absPath, supervisor);
2362
- await _fsRpc(supervisor.chown(absPath, nextUid, nextGid, opts), syscall, p, () => undefined);
2562
+ await _ownMutation(
2563
+ absPath,
2564
+ () => _fsRpc(supervisor.chown(absPath, nextUid, nextGid, opts), syscall, p, (result) => result),
2565
+ () => {
2566
+ const meta = _metadata(absPath);
2567
+ if (meta) { meta.uid = nextUid; meta.gid = nextGid; }
2568
+ },
2569
+ );
2570
+ _markVfsStale();
2571
+ }
2572
+
2573
+ // Sync ownership change: the stat record is updated locally at once and
2574
+ // the authority RPC is queued behind the path's pending flush, the same
2575
+ // parked model mkdirSync/unlinkSync use. Without an authority there is no
2576
+ // owner table to change, so the answer is the same ENOSYS as the async form.
2577
+ function _chownQueued(p, uid, gid, opts, syscall) {
2578
+ const followSymlinks = !(opts && opts.followSymlinks === false);
2579
+ const absPath = _resolve(p);
2580
+ const supervisor = _supervisor();
2581
+ if (!supervisor || typeof supervisor.chown !== "function") {
2582
+ if (!existsSync(p)) throw _fsErr("ENOENT", syscall, p);
2583
+ throw _fsErr("ENOSYS", syscall, p);
2584
+ }
2585
+ const nextUid = _coerceId(uid, syscall, p);
2586
+ const nextGid = _coerceId(gid, syscall, p);
2363
2587
  const meta = _metadata(absPath);
2364
2588
  if (meta) { meta.uid = nextUid; meta.gid = nextGid; }
2365
- _markVfsStale();
2589
+ // The parked write must be registered on the tail BEFORE the chown is
2590
+ // (a flush awaited from inside the queued step would queue behind it
2591
+ // and wait on itself), so it is the synchronous-registering flush.
2592
+ return _queueStructuralMutation(
2593
+ absPath, syscall, p,
2594
+ (s) => s.chown(absPath, nextUid, nextGid, followSymlinks ? undefined : { followSymlinks: false }),
2595
+ () => _flushParkedWrite(absPath, supervisor),
2596
+ "chown",
2597
+ );
2366
2598
  }
2599
+ function chownSync(p, uid, gid) { _detachStructuralMutation(_chownQueued(p, uid, gid, undefined, "chown")); }
2600
+ function lchownSync(p, uid, gid) { _detachStructuralMutation(_chownQueued(p, uid, gid, { followSymlinks: false }, "lchown")); }
2601
+ function lchmodSync(p, mode) { chmodSync(p, mode); }
2367
2602
 
2368
2603
  function _modeAllows(meta, want) {
2369
2604
  if (want === 0) return true;
@@ -2756,46 +2991,67 @@ const __fsMod = (() => {
2756
2991
  (!!__vfsDirs && fp in __vfsDirs) ||
2757
2992
  _metadata(fp)?.type === "directory" ||
2758
2993
  (!!__vfsBundle && __residentAnyUnder(fp + "/"));
2759
- return { name: n, isFile: () => !isDir, isDirectory: () => isDir, isSymbolicLink: () => false };
2994
+ return _direntObject(n, isDir ? "directory" : "file", absPath);
2760
2995
  });
2761
2996
  }
2762
2997
  return arr;
2763
2998
  }
2764
2999
 
2765
3000
  // ── mkdirSync ──
2766
- function mkdirSync(p, opts) {
3001
+ // The sync effect, then the authority mutation queued behind it (see
3002
+ // _queueStructuralMutation). One RPC whatever the depth: the async form
3003
+ // sends the path alone and the authority's mkdir creates the ancestors.
3004
+ function _mkdirQueued(p, opts) {
2767
3005
  const absPath = _resolve(p);
2768
3006
  const k = _strip(absPath);
3007
+ const created = [];
2769
3008
  if (opts?.recursive) {
2770
3009
  const parts = k.split("/").filter(Boolean);
2771
3010
  let cur = "";
2772
- for (const part of parts) { cur = cur ? cur + "/" + part : part; __vfsDirs[cur] = true; }
3011
+ for (const part of parts) { cur = cur ? cur + "/" + part : part; __vfsDirs[cur] = true; created.push(cur); }
2773
3012
  } else {
2774
3013
  __vfsDirs[k] = true;
3014
+ created.push(k);
2775
3015
  }
3016
+ const queued = _queueStructuralMutation(absPath, "mkdir", p, (supervisor) => supervisor.mkdir(absPath));
3017
+ // Told, not merely known locally: _announceLocalDirs must not issue a
3018
+ // second mkdir for a directory whose own RPC is already in the queue.
3019
+ if (queued) for (const dir of created) _announcedDirs.add(dir);
3020
+ return queued;
2776
3021
  }
3022
+ function mkdirSync(p, opts) { _detachStructuralMutation(_mkdirQueued(p, opts)); }
2777
3023
 
2778
3024
  // ── unlinkSync ──
2779
- function unlinkSync(p) {
3025
+ function _unlinkQueued(p) {
2780
3026
  const absPath = _resolve(p);
2781
3027
  const k = _strip(absPath);
2782
3028
  if (__vfsBundle) delete __vfsBundle[k];
2783
3029
  if (__vfsWrites) delete __vfsWrites[k];
2784
3030
  _forgetSyncPath(k);
3031
+ return _queueStructuralMutation(absPath, "unlink", p, (supervisor) => supervisor.unlink(absPath));
2785
3032
  }
3033
+ function unlinkSync(p) { _detachStructuralMutation(_unlinkQueued(p)); }
2786
3034
 
2787
3035
  // ── rmdirSync ──
2788
- function rmdirSync(p) {
3036
+ function _rmdirQueued(p) {
2789
3037
  const absPath = _resolve(p);
2790
3038
  const k = _strip(absPath);
2791
3039
  if (__vfsDirs) delete __vfsDirs[k];
2792
3040
  _forgetSyncPath(k);
3041
+ return _queueStructuralMutation(
3042
+ absPath, "rmdir", p,
3043
+ (supervisor) => supervisor.rmdir(absPath),
3044
+ () => __nimbusAwaitSubtreeMutations(absPath),
3045
+ );
2793
3046
  }
3047
+ function rmdirSync(p) { _detachStructuralMutation(_rmdirQueued(p)); }
2794
3048
 
2795
3049
  // ── renameSync ──
2796
- function renameSync(oldP, newP) {
2797
- const oldK = _strip(_resolve(oldP));
2798
- const newK = _strip(_resolve(newP));
3050
+ function _renameQueued(oldP, newP) {
3051
+ const oldAbs = _resolve(oldP);
3052
+ const newAbs = _resolve(newP);
3053
+ const oldK = _strip(oldAbs);
3054
+ const newK = _strip(newAbs);
2799
3055
  const content = __vfsBundle?.[oldK] ?? __vfsWrites?.[oldK];
2800
3056
  if (content !== undefined) {
2801
3057
  _parkWrite(newK, content);
@@ -2809,13 +3065,181 @@ const __fsMod = (() => {
2809
3065
  const moved = metadata ? metadata[oldK] : undefined;
2810
3066
  _forgetSyncPath(oldK);
2811
3067
  if (moved) metadata[newK] = moved;
2812
- }
3068
+ } else if (__vfsDirs && oldK in __vfsDirs) {
3069
+ // A directory this process made travels under its new name, so the
3070
+ // sync view stops listing the old one and _announceLocalDirs cannot
3071
+ // re-create it at the authority behind the rename.
3072
+ const prefix = oldK + "/";
3073
+ for (const dk of Object.keys(__vfsDirs)) {
3074
+ if (dk !== oldK && !dk.startsWith(prefix)) continue;
3075
+ const moved = newK + dk.slice(oldK.length);
3076
+ __vfsDirs[moved] = true;
3077
+ delete __vfsDirs[dk];
3078
+ if (_announcedDirs.has(dk)) _announcedDirs.add(moved);
3079
+ }
3080
+ _forgetSyncTree(oldK);
3081
+ }
3082
+ const queued = _queueStructuralMutation(
3083
+ oldAbs, "rename", oldP,
3084
+ (supervisor) => supervisor.rename(oldAbs, newAbs),
3085
+ // The queue orders the move behind the source's ancestors; the move
3086
+ // also needs the destination's ancestors to exist and every pending
3087
+ // mutation beneath the source to have landed under the old name.
3088
+ () => Promise.all([__nimbusAwaitAncestorMutations(newAbs), __nimbusAwaitSubtreeMutations(oldAbs)]),
3089
+ );
3090
+ _fenceVfsMutation(newAbs, queued);
3091
+ return queued;
2813
3092
  }
3093
+ function renameSync(oldP, newP) { _detachStructuralMutation(_renameQueued(oldP, newP)); }
2814
3094
 
2815
3095
  // ── copyFileSync ──
2816
- function copyFileSync(src, dest) {
2817
- writeFileSync(dest, readFileSync(src, "utf8"));
3096
+ // Bytes, not utf8: a utf8 round trip replaces every byte ≥ 0x80 with
3097
+ // U+FFFD, which is how a copied .png or .woff2 arrived corrupted.
3098
+ function copyFileSync(src, dest, mode) {
3099
+ if ((Number(mode) & __fsConstants.COPYFILE_EXCL) !== 0 && existsSync(dest)) {
3100
+ throw _fsErr("EEXIST", "copyfile", dest);
3101
+ }
3102
+ writeFileSync(dest, readFileSync(src));
3103
+ }
3104
+
3105
+ // ── rmSync / rm ──
3106
+ // The local tables are edited at once (the same retraction unlinkSync and
3107
+ // rmdirSync perform, over the whole subtree when recursive) and ONE
3108
+ // authority RPC — fsRemove, which the bridge serves as unlink or a bounded
3109
+ // recursive removal — is queued behind every pending mutation beneath the
3110
+ // path. ENOENT under \`force\` is the authority's to swallow; locally an
3111
+ // unknown path may still exist live, so it is asked rather than answered.
3112
+ function _rmQueued(p, opts, sync) {
3113
+ const o = opts || {};
3114
+ const absPath = _resolve(p);
3115
+ const k = _strip(absPath);
3116
+ const st = statSync(p, { throwIfNoEntry: false });
3117
+ const supervisor = _supervisor();
3118
+ const canRemove = !!supervisor && typeof supervisor.fsRemove === "function";
3119
+ if (st === undefined) {
3120
+ if (!canRemove) {
3121
+ if (o.force) return null;
3122
+ throw _fsErr("ENOENT", "rm", p);
3123
+ }
3124
+ // A path the sync view cannot map may still exist live (born after
3125
+ // boot). The async form asks the authority, whose ENOENT is the real
3126
+ // one; the sync form has no frame to receive that answer, so it gives
3127
+ // statSync's own provisional not-found unless \`force\` makes the
3128
+ // verdict irrelevant.
3129
+ if (sync && !o.force) throw _fsErr("ENOENT", "rm", p);
3130
+ }
3131
+ if (st !== undefined && st.isDirectory() && !o.recursive) {
3132
+ throw _fsErr("ERR_FS_EISDIR", "rm", p);
3133
+ }
3134
+ const prefix = k + "/";
3135
+ // A parked write is content the authority may never have seen: the
3136
+ // caller's file demonstrably existed here, so the removal succeeds, and
3137
+ // the authority is told "if present" rather than made to fail on a file
3138
+ // that was only ever local.
3139
+ let parked = false;
3140
+ if (__vfsBundle) {
3141
+ if (k in __vfsBundle) delete __vfsBundle[k];
3142
+ if (o.recursive) for (const bk of __residentUnder(prefix)) delete __vfsBundle[bk];
3143
+ }
3144
+ delete __vfsBundleRevisions[k];
3145
+ if (__vfsWrites) {
3146
+ for (const wk of Object.keys(__vfsWrites)) {
3147
+ if (wk === k || (o.recursive && wk.startsWith(prefix))) { delete __vfsWrites[wk]; parked = true; }
3148
+ }
3149
+ }
3150
+ if (__vfsDirs) {
3151
+ for (const dk of Object.keys(__vfsDirs)) {
3152
+ if (dk === k || (o.recursive && dk.startsWith(prefix))) delete __vfsDirs[dk];
3153
+ }
3154
+ }
3155
+ if (o.recursive) _forgetSyncTree(k); else _forgetSyncPath(k);
3156
+ if (!canRemove) {
3157
+ // No fsRemove: a plain file still has the unlink RPC.
3158
+ if (st !== undefined && !st.isDirectory()) {
3159
+ return _queueStructuralMutation(absPath, "rm", p, (s) => s.unlink(absPath), undefined, "unlink");
3160
+ }
3161
+ return null;
3162
+ }
3163
+ return _queueStructuralMutation(
3164
+ absPath, "rm", p,
3165
+ (s) => s.fsRemove(absPath, { recursive: !!o.recursive, force: !!o.force || parked }),
3166
+ () => __nimbusAwaitSubtreeMutations(absPath),
3167
+ "fsRemove",
3168
+ );
3169
+ }
3170
+ function rmSync(p, opts) { _detachStructuralMutation(_rmQueued(p, opts, true)); }
3171
+ async function _rmAsync(p, opts) { await _rmQueued(p, opts, false); }
3172
+
3173
+ // ── cpSync ──
3174
+ // A walk of the resident view; every file copy is a parked sync write that
3175
+ // the existing write-back drains, and every directory a queued mkdir.
3176
+ function cpSync(src, dest, opts) {
3177
+ const o = opts || {};
3178
+ const st = statSync(src, { throwIfNoEntry: false });
3179
+ if (st === undefined) throw _fsErr("ENOENT", "cp", src);
3180
+ if (typeof o.filter === "function" && !o.filter(String(src), String(dest))) return;
3181
+ if (!st.isDirectory()) {
3182
+ const destSt = statSync(dest, { throwIfNoEntry: false });
3183
+ if (destSt !== undefined) {
3184
+ if (destSt.isDirectory()) throw _fsErr("EISDIR", "cp", dest);
3185
+ if (o.errorOnExist) throw _fsErr("ERR_FS_CP_EEXIST", "cp", dest);
3186
+ if (o.force === false) return;
3187
+ }
3188
+ copyFileSync(src, dest);
3189
+ return;
3190
+ }
3191
+ if (!o.recursive) throw _fsErr("ERR_FS_EISDIR", "cp", src);
3192
+ mkdirSync(dest, { recursive: true });
3193
+ for (const ent of readdirSync(src, { withFileTypes: true })) {
3194
+ cpSync(__pathMod.join(String(src), ent.name), __pathMod.join(String(dest), ent.name), o);
3195
+ }
3196
+ }
3197
+
3198
+ // ── mkdtemp ──
3199
+ // Node appends six characters from [a-zA-Z0-9]; a collision with a name
3200
+ // the sync view already knows is retried rather than reused.
3201
+ const _MKDTEMP_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
3202
+ function _mkdtempName(prefix) {
3203
+ for (let attempt = 0; attempt < 64; attempt++) {
3204
+ let suffix = "";
3205
+ for (let i = 0; i < 6; i++) suffix += _MKDTEMP_ALPHABET[Math.floor(Math.random() * _MKDTEMP_ALPHABET.length)];
3206
+ const name = String(prefix) + suffix;
3207
+ if (!existsSync(name)) return name;
3208
+ }
3209
+ throw _fsErr("EEXIST", "mkdtemp", String(prefix) + "XXXXXX");
2818
3210
  }
3211
+ function mkdtempSync(prefix) {
3212
+ const name = _mkdtempName(prefix);
3213
+ mkdirSync(name);
3214
+ return name;
3215
+ }
3216
+ async function _mkdtempAsync(prefix) {
3217
+ const name = _mkdtempName(prefix);
3218
+ await _mkdirAsync(name);
3219
+ return name;
3220
+ }
3221
+
3222
+ // ── truncateSync ──
3223
+ // The resident-view rule of ftruncateSync applied to a path: trim the
3224
+ // resident cell and park it, refuse EAGAIN when the bytes are not here.
3225
+ function truncateSync(p, len) {
3226
+ const absPath = _resolve(p);
3227
+ const st = statSync(p, { throwIfNoEntry: false });
3228
+ if (st === undefined) throw _fsErr("ENOENT", "truncate", p);
3229
+ if (st.isDirectory()) throw _fsErr("EISDIR", "truncate", p);
3230
+ const size = Math.max(0, Math.trunc(Number(len) || 0));
3231
+ _ensureWritable(absPath, "truncate", p);
3232
+ const base = _residentWriteBase(absPath, p, "truncate");
3233
+ const next = new Uint8Array(size);
3234
+ next.set(base.subarray(0, Math.min(size, base.byteLength)), 0);
3235
+ _parkWrite(_strip(absPath), next);
3236
+ _markVfsStale();
3237
+ }
3238
+
3239
+ // ── link / linkSync ──
3240
+ // The VFS has no hard links and a copy would lie about sharing an inode,
3241
+ // so both forms answer ENOSYS like fs.promises.link always has.
3242
+ function linkSync(existingPath, newPath) { throw _fsErr("ENOSYS", "link", newPath); }
2819
3243
 
2820
3244
  // ── realpathSync (X.5-T per X5Z5-plan §4.3 + X526b-retro §3.1) ──
2821
3245
  // Sync realpath stays local and identity-resolves. Async symlink
@@ -2863,7 +3287,7 @@ const __fsMod = (() => {
2863
3287
  // ── open-flag parsing for fs.promises.open ──
2864
3288
  function _parseOpenFlags(flags) {
2865
3289
  if (typeof flags === "number") {
2866
- const O_WRONLY = 1, O_RDWR = 2, O_CREAT = 64, O_EXCL = 128, O_TRUNC = 512, O_APPEND = 1024;
3290
+ const { O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_TRUNC, O_APPEND, O_DIRECTORY } = __fsConstants;
2867
3291
  return {
2868
3292
  read: (flags & O_WRONLY) === 0,
2869
3293
  write: (flags & (O_WRONLY | O_RDWR)) !== 0,
@@ -2871,15 +3295,16 @@ const __fsMod = (() => {
2871
3295
  create: (flags & O_CREAT) !== 0,
2872
3296
  truncate: (flags & O_TRUNC) !== 0,
2873
3297
  exclusive: (flags & O_EXCL) !== 0,
3298
+ directory: (flags & O_DIRECTORY) !== 0,
2874
3299
  };
2875
3300
  }
2876
3301
  const s = String(flags === undefined || flags === null ? "r" : flags);
2877
3302
  const plus = s.indexOf("+") !== -1;
2878
3303
  const exclusive = s.indexOf("x") !== -1;
2879
3304
  const base = s.charAt(0);
2880
- if (base === "w") return { read: plus, write: true, append: false, create: true, truncate: true, exclusive };
2881
- if (base === "a") return { read: plus, write: true, append: true, create: true, truncate: false, exclusive };
2882
- return { read: true, write: plus, append: false, create: false, truncate: false, exclusive };
3305
+ if (base === "w") return { read: plus, write: true, append: false, create: true, truncate: true, exclusive, directory: false };
3306
+ if (base === "a") return { read: plus, write: true, append: true, create: true, truncate: false, exclusive, directory: false };
3307
+ return { read: true, write: plus, append: false, create: false, truncate: false, exclusive, directory: false };
2883
3308
  }
2884
3309
 
2885
3310
  // ── FileHandle — returned from fs.promises.open ──
@@ -2889,6 +3314,38 @@ const __fsMod = (() => {
2889
3314
  // hibernation and partial reads/writes never move whole files. Unflushed
2890
3315
  // sync writes (__vfsWrites) take read precedence; the local sync view is
2891
3316
  // overlaid on writes so readFileSync stays coherent.
3317
+ // The prior content a sync positional write or truncate lays its bytes
3318
+ // over. Shared by the descriptor forms and truncateSync.
3319
+ function _residentWriteBase(absPath, p, syscall) {
3320
+ const cell = _writtenCell(absPath);
3321
+ if (cell !== undefined) {
3322
+ const denial = _denialCode(cell);
3323
+ if (denial) throw _fsErr(denial, syscall, p);
3324
+ _residencySatisfied(absPath);
3325
+ return _asBytes(cell);
3326
+ }
3327
+ const asyncForm = "the async fs." + syscall + "/fs.promises form";
3328
+ const st = statSync(p, { throwIfNoEntry: false });
3329
+ // Non-resident and non-empty: writing onto a zero-filled base would
3330
+ // silently destroy the bytes we cannot see. Refuse instead.
3331
+ if (st !== undefined && st.size !== 0) throw _notResidentError(absPath, p, syscall, asyncForm);
3332
+ // Absent or empty — an empty base IS the true prior content, but only
3333
+ // where absence is knowledge. A path the view never mapped may hold bytes
3334
+ // this process cannot see, and an O_APPEND descriptor onto a zero-filled
3335
+ // base would overwrite the file with the fragment it meant to add.
3336
+ if (st === undefined && !_absenceIsKnown(absPath)) throw _refuseUnmapped(absPath, p, syscall, asyncForm);
3337
+ return new Uint8Array(0);
3338
+ }
3339
+
3340
+ async function _fsyncAsync(absPath, p) {
3341
+ const supervisor = _supervisor();
3342
+ if (supervisor) {
3343
+ await _flushLocalPathToSupervisor(absPath, supervisor);
3344
+ await _awaitStructuralOrder(absPath);
3345
+ }
3346
+ _markVfsStale();
3347
+ }
3348
+
2892
3349
  let __nextFileHandleFd = 3;
2893
3350
  const __fileHandles = new Map();
2894
3351
  class __FileHandle {
@@ -2963,16 +3420,21 @@ const __fsMod = (() => {
2963
3420
  );
2964
3421
  if (meta && meta.type === "file") writeAt = Number(meta.size) || 0;
2965
3422
  }
2966
- await _fsRpc(
2967
- supervisor.fsWriteRange(this._abs, writeAt, bytes),
2968
- "write", this._path,
2969
- () => undefined,
3423
+ await _ownMutation(
3424
+ this._abs,
3425
+ () => _fsRpc(
3426
+ supervisor.fsWriteRange(this._abs, writeAt, bytes),
3427
+ "write", this._path,
3428
+ (result) => result,
3429
+ ),
3430
+ () => {
3431
+ const key = _strip(this._abs);
3432
+ if (!Object.prototype.hasOwnProperty.call(__vfsWrites, key) &&
3433
+ __vfsWriteGenerations[key] === overlayGeneration) {
3434
+ _overlayLocalCell(this._abs, writeAt, bytes);
3435
+ }
3436
+ },
2970
3437
  );
2971
- const key = _strip(this._abs);
2972
- if (!Object.prototype.hasOwnProperty.call(__vfsWrites, key) &&
2973
- __vfsWriteGenerations[key] === overlayGeneration) {
2974
- _overlayLocalCell(this._abs, writeAt, bytes);
2975
- }
2976
3438
  });
2977
3439
  _markVfsStale();
2978
3440
  } else {
@@ -3028,24 +3490,7 @@ const __fsMod = (() => {
3028
3490
  if (bytes === undefined) throw this._notResident(syscall);
3029
3491
  return bytes;
3030
3492
  }
3031
- _writeBase(syscall) {
3032
- const bytes = this._residentBytes(syscall);
3033
- if (bytes !== undefined) return bytes;
3034
- const st = statSync(this._path, { throwIfNoEntry: false });
3035
- // Non-resident and non-empty: writing onto a zero-filled base would
3036
- // silently destroy the bytes we cannot see. Refuse instead.
3037
- if (st !== undefined && st.size !== 0) throw this._notResident(syscall);
3038
- // Absent or empty — an empty base IS the true prior content, but only
3039
- // where absence is knowledge. A path the view never mapped may hold bytes
3040
- // this process cannot see, and an O_APPEND descriptor onto a zero-filled
3041
- // base would overwrite the file with the fragment it meant to add.
3042
- if (st === undefined && !_absenceIsKnown(this._abs)) {
3043
- throw _refuseUnmapped(
3044
- this._abs, this._path, syscall, "the async fs." + syscall + "/fs.promises form",
3045
- );
3046
- }
3047
- return new Uint8Array(0);
3048
- }
3493
+ _writeBase(syscall) { return _residentWriteBase(this._abs, this._path, syscall); }
3049
3494
  _commit(next) {
3050
3495
  _parkWrite(_strip(this._abs), next);
3051
3496
  _markVfsStale();
@@ -3095,9 +3540,38 @@ const __fsMod = (() => {
3095
3540
  }
3096
3541
  async chmod(mode) { this._assertOpen("fchmod"); await _chmodAsync(this._path, mode); }
3097
3542
  async chown(uid, gid) { this._assertOpen("fchown"); await _chownAsync(this._path, uid, gid, undefined, "fchown"); }
3098
- async utimes(atime, mtime) { this._assertOpen("futimes"); await _utimesAsync(this._path, atime, mtime); }
3099
- async sync() {}
3100
- async datasync() {}
3543
+ async utimes(atime, mtime) { this._assertOpen("futimes"); await _utimesAsync(this._path, atime, mtime, undefined, "futimes"); }
3544
+ // Durability here means: every byte parked for this path has reached
3545
+ // the authority and every mutation queued for it has landed. The bridge
3546
+ // itself is synchronously durable, so no further RPC is owed.
3547
+ async sync() { this._assertOpen("fsync"); await _fsyncAsync(this._abs, this._path); }
3548
+ async datasync() { this._assertOpen("fdatasync"); await _fsyncAsync(this._abs, this._path); }
3549
+ // Scatter/gather over the ranged read/write: one sequential pass per
3550
+ // buffer, stopping at the first short read, positions advanced by hand
3551
+ // when an explicit one was given so the file position stays untouched.
3552
+ async readv(buffers, position) {
3553
+ this._assertOpen("read");
3554
+ let bytesRead = 0;
3555
+ let pos = _isCurrentPos(position) ? null : Number(position);
3556
+ for (const buffer of buffers) {
3557
+ const r = await this.read(buffer, 0, buffer.byteLength, pos);
3558
+ bytesRead += r.bytesRead;
3559
+ if (pos !== null) pos += r.bytesRead;
3560
+ if (r.bytesRead < buffer.byteLength) break;
3561
+ }
3562
+ return { bytesRead, buffers };
3563
+ }
3564
+ async writev(buffers, position) {
3565
+ this._assertOpen("write");
3566
+ let bytesWritten = 0;
3567
+ let pos = _isCurrentPos(position) ? null : Number(position);
3568
+ for (const buffer of buffers) {
3569
+ const r = await this.write(buffer, 0, buffer.byteLength, pos);
3570
+ bytesWritten += r.bytesWritten;
3571
+ if (pos !== null) pos += r.bytesWritten;
3572
+ }
3573
+ return { bytesWritten, buffers };
3574
+ }
3101
3575
  async close() { this._assertOpen("close"); this._closed = true; __fileHandles.delete(this.fd); }
3102
3576
  [Symbol.asyncDispose]() { return this.close(); }
3103
3577
  }
@@ -3118,6 +3592,7 @@ const __fsMod = (() => {
3118
3592
  if (localStat && localStat.isDirectory()) throw _fsErr("EISDIR", "open", path);
3119
3593
  }
3120
3594
  const exists = !!liveMeta || !!localStat;
3595
+ if (exists && fl.directory) throw _fsErr("ENOTDIR", "open", path);
3121
3596
  if (!exists && !fl.create) throw _fsErr("ENOENT", "open", path);
3122
3597
  if (exists && fl.create && fl.exclusive) throw _fsErr("EEXIST", "open", path);
3123
3598
  let size = liveMeta ? (Number(liveMeta.size) || 0) : (localStat ? localStat.size : 0);
@@ -3141,6 +3616,7 @@ const __fsMod = (() => {
3141
3616
  const st = statSync(path, { throwIfNoEntry: false });
3142
3617
  if (st && st.isDirectory()) throw _fsErr("EISDIR", "open", path);
3143
3618
  const exists = st !== undefined;
3619
+ if (exists && fl.directory) throw _fsErr("ENOTDIR", "open", path);
3144
3620
  if (!exists && !fl.create) throw _fsErr("ENOENT", "open", path);
3145
3621
  if (exists && fl.create && fl.exclusive) throw _fsErr("EEXIST", "open", path);
3146
3622
  if (fl.write || !exists) _ensureWritable(absPath, "open", path);
@@ -3261,6 +3737,49 @@ const __fsMod = (() => {
3261
3737
  function fsyncSync(fd) { if (!_isStdioFd(fd)) _fdHandle(fd, "fsync"); _markVfsStale(); }
3262
3738
  function fdatasyncSync(fd) { if (!_isStdioFd(fd)) _fdHandle(fd, "fdatasync"); _markVfsStale(); }
3263
3739
 
3740
+ // Descriptor metadata, sync: the path forms on the handle's path, so the
3741
+ // local overlay (times, modes, ownership) and the parked write-through are
3742
+ // exactly what utimesSync/chmodSync/chownSync give.
3743
+ function futimesSync(fd, atime, mtime) {
3744
+ if (_isStdioFd(fd)) throw _fsErr("EINVAL", "futimes", fd);
3745
+ const handle = _fdHandle(fd, "futimes");
3746
+ _recordLocalTimes(handle._abs, atime, mtime, "futimes", handle._path);
3747
+ }
3748
+ function fchmodSync(fd, mode) {
3749
+ if (_isStdioFd(fd)) throw _fsErr("EINVAL", "fchmod", fd);
3750
+ const handle = _fdHandle(fd, "fchmod");
3751
+ _localModes[_strip(handle._abs)] = _coerceMode(mode, "fchmod", handle._path);
3752
+ }
3753
+ function fchownSync(fd, uid, gid) {
3754
+ if (_isStdioFd(fd)) throw _fsErr("EINVAL", "fchown", fd);
3755
+ const handle = _fdHandle(fd, "fchown");
3756
+ _detachStructuralMutation(_chownQueued(handle._path, uid, gid, undefined, "fchown"));
3757
+ }
3758
+
3759
+ // Scatter/gather, sync: readSync/writeSync per buffer; stdio fds keep the
3760
+ // stream behaviour those two already give them.
3761
+ function readvSync(fd, buffers, position) {
3762
+ let bytesRead = 0;
3763
+ let pos = _isCurrentPos(position) ? null : Number(position);
3764
+ for (const buffer of buffers) {
3765
+ const n = readSync(fd, buffer, 0, buffer.byteLength, pos);
3766
+ bytesRead += n;
3767
+ if (pos !== null) pos += n;
3768
+ if (n < buffer.byteLength) break;
3769
+ }
3770
+ return bytesRead;
3771
+ }
3772
+ function writevSync(fd, buffers, position) {
3773
+ let bytesWritten = 0;
3774
+ let pos = _isCurrentPos(position) ? null : Number(position);
3775
+ for (const buffer of buffers) {
3776
+ const n = writeSync(fd, buffer, 0, buffer.byteLength, pos);
3777
+ bytesWritten += n;
3778
+ if (pos !== null) pos += n;
3779
+ }
3780
+ return bytesWritten;
3781
+ }
3782
+
3264
3783
  // ── callback forms (live I/O — these CAN reach the supervisor) ──
3265
3784
  function open(path, flags, mode, cb) {
3266
3785
  if (typeof flags === "function") { cb = flags; flags = undefined; mode = undefined; }
@@ -3341,21 +3860,56 @@ const __fsMod = (() => {
3341
3860
  if (!handle) return;
3342
3861
  handle.truncate(len).then(() => cb(null)).catch((e) => cb(e));
3343
3862
  }
3344
- function fsync(fd, cb) {
3863
+ // The async forms CAN wait for durability: the parked bytes are flushed
3864
+ // and the path's queued mutations awaited before the callback fires.
3865
+ function _fsyncCb(fd, syscall, cb) {
3866
+ let handle = null;
3345
3867
  let err = null;
3346
- try { fsyncSync(fd); } catch (e) { err = e; }
3868
+ try { if (!_isStdioFd(fd)) handle = _fdHandle(fd, syscall); } catch (e) { err = e; }
3347
3869
  // Without a callback there is nowhere to deliver the failure, so raise
3348
3870
  // it here rather than let an EBADF vanish.
3349
- if (typeof cb !== "function") { if (err) throw err; return; }
3350
- queueMicrotask(() => cb(err));
3351
- }
3352
- function fdatasync(fd, cb) {
3353
- let err = null;
3354
- try { fdatasyncSync(fd); } catch (e) { err = e; }
3355
- // Without a callback there is nowhere to deliver the failure, so raise
3356
- // it here rather than let an EBADF vanish.
3357
- if (typeof cb !== "function") { if (err) throw err; return; }
3358
- queueMicrotask(() => cb(err));
3871
+ if (typeof cb !== "function") { if (err) throw err; _markVfsStale(); return; }
3872
+ if (err || !handle) { _markVfsStale(); queueMicrotask(() => cb(err)); return; }
3873
+ _fsyncAsync(handle._abs, handle._path).then(() => cb(null)).catch((e) => cb(e));
3874
+ }
3875
+ function fsync(fd, cb) { _fsyncCb(fd, "fsync", cb); }
3876
+ function fdatasync(fd, cb) { _fsyncCb(fd, "fdatasync", cb); }
3877
+ function futimes(fd, atime, mtime, cb) {
3878
+ if (_isStdioFd(fd)) { queueMicrotask(() => cb(_fsErr("EINVAL", "futimes", fd))); return; }
3879
+ const handle = _fdFor(fd, "futimes", cb);
3880
+ if (!handle) return;
3881
+ handle.utimes(atime, mtime).then(() => cb(null)).catch((e) => cb(e));
3882
+ }
3883
+ function readv(fd, buffers, position, cb) {
3884
+ if (typeof position === "function") { cb = position; position = null; }
3885
+ const run = async () => {
3886
+ let bytesRead = 0;
3887
+ let pos = _isCurrentPos(position) ? null : Number(position);
3888
+ for (const buffer of buffers) {
3889
+ const n = await new Promise((res, rej) =>
3890
+ read(fd, buffer, 0, buffer.byteLength, pos, (e, count) => (e ? rej(e) : res(count))));
3891
+ bytesRead += n;
3892
+ if (pos !== null) pos += n;
3893
+ if (n < buffer.byteLength) break;
3894
+ }
3895
+ return bytesRead;
3896
+ };
3897
+ run().then((n) => cb(null, n, buffers)).catch((e) => cb(e));
3898
+ }
3899
+ function writev(fd, buffers, position, cb) {
3900
+ if (typeof position === "function") { cb = position; position = null; }
3901
+ const run = async () => {
3902
+ let bytesWritten = 0;
3903
+ let pos = _isCurrentPos(position) ? null : Number(position);
3904
+ for (const buffer of buffers) {
3905
+ const n = await new Promise((res, rej) =>
3906
+ write(fd, buffer, 0, buffer.byteLength, pos, (e, count) => (e ? rej(e) : res(count))));
3907
+ bytesWritten += n;
3908
+ if (pos !== null) pos += n;
3909
+ }
3910
+ return bytesWritten;
3911
+ };
3912
+ run().then((n) => cb(null, n, buffers)).catch((e) => cb(e));
3359
3913
  }
3360
3914
  function fchmod(fd, mode, cb) {
3361
3915
  const handle = _fdFor(fd, "fchmod", cb);
@@ -3368,6 +3922,135 @@ const __fsMod = (() => {
3368
3922
  handle.chown(uid, gid).then(() => cb(null)).catch((error) => cb(error));
3369
3923
  }
3370
3924
 
3925
+ // ── Dir — fs.opendir / opendirSync / fs.promises.opendir ──
3926
+ // The listing is taken whole at open (both readdir forms already return
3927
+ // one) and handed out one Dirent per read(); closing a closed Dir is
3928
+ // ERR_DIR_CLOSED as in Node.
3929
+ class __Dir {
3930
+ constructor(path, entries) {
3931
+ this.path = path;
3932
+ this._entries = entries;
3933
+ this._at = 0;
3934
+ this._closed = false;
3935
+ }
3936
+ _assertOpen() {
3937
+ if (this._closed) {
3938
+ const err = new Error("Directory handle was closed");
3939
+ err.code = "ERR_DIR_CLOSED";
3940
+ throw err;
3941
+ }
3942
+ }
3943
+ readSync() {
3944
+ this._assertOpen();
3945
+ return this._at < this._entries.length ? this._entries[this._at++] : null;
3946
+ }
3947
+ read(cb) {
3948
+ const result = new Promise((res, rej) => { try { res(this.readSync()); } catch (e) { rej(e); } });
3949
+ if (typeof cb !== "function") return result;
3950
+ result.then((entry) => cb(null, entry), (e) => cb(e));
3951
+ }
3952
+ closeSync() { this._assertOpen(); this._closed = true; }
3953
+ close(cb) {
3954
+ const result = new Promise((res, rej) => { try { this.closeSync(); res(); } catch (e) { rej(e); } });
3955
+ if (typeof cb !== "function") return result;
3956
+ result.then(() => cb(null), (e) => cb(e));
3957
+ }
3958
+ async *[Symbol.asyncIterator]() {
3959
+ try {
3960
+ for (;;) {
3961
+ const entry = this.readSync();
3962
+ if (entry === null) break;
3963
+ yield entry;
3964
+ }
3965
+ } finally {
3966
+ if (!this._closed) this._closed = true;
3967
+ }
3968
+ }
3969
+ [Symbol.asyncDispose]() { return this._closed ? Promise.resolve() : this.close(); }
3970
+ [Symbol.dispose]() { if (!this._closed) this.closeSync(); }
3971
+ }
3972
+ function opendirSync(p, opts) {
3973
+ return new __Dir(String(p), readdirSync(p, { withFileTypes: true }));
3974
+ }
3975
+ async function _opendirAsync(p, opts) {
3976
+ return new __Dir(String(p), await _readdirAsync(p, { withFileTypes: true }));
3977
+ }
3978
+ function opendir(p, opts, cb) {
3979
+ if (typeof opts === "function") { cb = opts; opts = undefined; }
3980
+ _opendirAsync(p, opts).then((dir) => cb(null, dir)).catch((e) => cb(e));
3981
+ }
3982
+
3983
+ // ── statfs ──
3984
+ // Honest constants for the VFS: the block size is the SQLite chunk unit's
3985
+ // page size, and blocks is the configured VFS capacity in those blocks.
3986
+ // The supervisor surface exposes no usage counter, so bfree/bavail report
3987
+ // the whole capacity; the VFS has no inode table, so files/ffree are 0
3988
+ // (unknown, not "none"). \`type\` is 0: this is no kernel filesystem and
3989
+ // no magic number would be true of it.
3990
+ const _STATFS_BSIZE = 4096;
3991
+ const _STATFS_BLOCKS = Math.floor(${VFS_CAPACITY} / 4096);
3992
+ function _statfsObject(opts) {
3993
+ const wrap = opts && opts.bigint ? BigInt : Number;
3994
+ return {
3995
+ type: wrap(0),
3996
+ bsize: wrap(_STATFS_BSIZE),
3997
+ blocks: wrap(_STATFS_BLOCKS),
3998
+ bfree: wrap(_STATFS_BLOCKS),
3999
+ bavail: wrap(_STATFS_BLOCKS),
4000
+ files: wrap(0),
4001
+ ffree: wrap(0),
4002
+ };
4003
+ }
4004
+ function statfsSync(p, opts) {
4005
+ if (statSync(p, { throwIfNoEntry: false }) === undefined) throw _fsErr("ENOENT", "statfs", p);
4006
+ return _statfsObject(opts);
4007
+ }
4008
+ async function _statfsAsync(p, opts) {
4009
+ await _statAsync(p);
4010
+ return _statfsObject(opts);
4011
+ }
4012
+ function statfs(p, opts, cb) {
4013
+ if (typeof opts === "function") { cb = opts; opts = undefined; }
4014
+ _statfsAsync(p, opts).then((s) => cb(null, s)).catch((e) => cb(e));
4015
+ }
4016
+
4017
+ // ── remaining callback forms of the path operations ──
4018
+ function rm(p, opts, cb) {
4019
+ if (typeof opts === "function") { cb = opts; opts = undefined; }
4020
+ _rmAsync(p, opts).then(() => cb(null)).catch((e) => cb(e));
4021
+ }
4022
+ function cp(src, dest, opts, cb) {
4023
+ if (typeof opts === "function") { cb = opts; opts = undefined; }
4024
+ promises.cp(src, dest, opts).then(() => cb(null)).catch((e) => cb(e));
4025
+ }
4026
+ function truncate(p, len, cb) {
4027
+ if (typeof len === "function") { cb = len; len = 0; }
4028
+ _truncateAsync(p, len || 0).then(() => cb(null)).catch((e) => cb(e));
4029
+ }
4030
+ function copyFile(src, dest, mode, cb) {
4031
+ if (typeof mode === "function") { cb = mode; mode = 0; }
4032
+ promises.copyFile(src, dest, mode).then(() => cb(null)).catch((e) => cb(e));
4033
+ }
4034
+ function mkdtemp(prefix, opts, cb) {
4035
+ if (typeof opts === "function") { cb = opts; opts = undefined; }
4036
+ _mkdtempAsync(prefix).then((name) => cb(null, name)).catch((e) => cb(e));
4037
+ }
4038
+ function link(existingPath, newPath, cb) { queueMicrotask(() => cb(_fsErr("ENOSYS", "link", newPath))); }
4039
+ function symlink(target, path, type, cb) {
4040
+ if (typeof type === "function") { cb = type; type = undefined; }
4041
+ _symlinkAsync(target, path).then(() => cb(null)).catch((e) => cb(e));
4042
+ }
4043
+ function readlink(p, opts, cb) {
4044
+ if (typeof opts === "function") { cb = opts; opts = undefined; }
4045
+ _readlinkAsync(p).then((target) => cb(null, target)).catch((e) => cb(e));
4046
+ }
4047
+ function realpath(p, opts, cb) {
4048
+ if (typeof opts === "function") { cb = opts; opts = undefined; }
4049
+ promises.realpath(p, opts).then((r) => cb(null, r)).catch((e) => cb(e));
4050
+ }
4051
+ realpath.native = realpath;
4052
+ function lchmod(p, mode, cb) { chmod(p, mode, cb); }
4053
+
3371
4054
  // ── promises namespace (W3: full surface, VFS-backed) ──
3372
4055
  // We can't forward to workerd's node:fs/promises because that operates
3373
4056
  // on a real-host filesystem, not our VFS. So every method is shim'd
@@ -3385,22 +4068,8 @@ const __fsMod = (() => {
3385
4068
  // W3 additions:
3386
4069
  appendFile: async (p, d, o) => { await _appendFileAsync(p, d, o); },
3387
4070
  lstat: (p) => new Promise((res, rej) => lstat(p, (e, s) => e ? rej(e) : res(s))),
3388
- rm: async (p, opts) => {
3389
- const o = opts || {};
3390
- const k = _strip(_resolve(p));
3391
- const prefix = k + "/";
3392
- if (o.recursive) {
3393
- if (__vfsBundle) {
3394
- if (k in __vfsBundle) delete __vfsBundle[k];
3395
- for (const bk of __residentUnder(prefix)) delete __vfsBundle[bk];
3396
- }
3397
- if (__vfsWrites) for (const wk of Object.keys(__vfsWrites)) if (wk === k || wk.startsWith(prefix)) delete __vfsWrites[wk];
3398
- if (__vfsDirs) for (const dk of Object.keys(__vfsDirs)) if (dk === k || dk.startsWith(prefix)) delete __vfsDirs[dk];
3399
- _forgetSyncTree(k);
3400
- } else {
3401
- try { await _unlinkAsync(p); } catch (e) { if (!o.force) throw e; }
3402
- }
3403
- },
4071
+ // The same local retraction and queued authority removal rmSync does.
4072
+ rm: async (p, opts) => { await _rmAsync(p, opts); },
3404
4073
  cp: async (src, dest, opts) => {
3405
4074
  const o = opts || {};
3406
4075
  const srcAbs = _resolve(src);
@@ -3436,7 +4105,12 @@ const __fsMod = (() => {
3436
4105
  };
3437
4106
  await walk("");
3438
4107
  },
3439
- copyFile: async (src, dest) => { await _writeFileAsync(dest, await _readFileAsync(src)); },
4108
+ copyFile: async (src, dest, mode) => {
4109
+ if ((Number(mode) & __fsConstants.COPYFILE_EXCL) !== 0 && await _existsAsync(dest)) {
4110
+ throw _fsErr("EEXIST", "copyfile", dest);
4111
+ }
4112
+ await _writeFileAsync(dest, await _readFileAsync(src));
4113
+ },
3440
4114
  rename: async (oldP, newP) => { await _renameAsync(oldP, newP); },
3441
4115
  rmdir: async (p) => { await _rmdirAsync(p); },
3442
4116
  realpath: async (p) => __pathMod.resolve(String(p)),
@@ -3450,12 +4124,10 @@ const __fsMod = (() => {
3450
4124
  symlink: async (target, path) => { await _symlinkAsync(target, path); },
3451
4125
  link: async () => { throw _fsErr("ENOSYS", "link", ""); },
3452
4126
  readlink: async (p) => _readlinkAsync(p),
3453
- mkdtemp: async (prefix) => {
3454
- const name = String(prefix) + Math.random().toString(36).slice(2, 10);
3455
- mkdirSync(name, { recursive: true });
3456
- return name;
3457
- },
4127
+ mkdtemp: async (prefix) => _mkdtempAsync(prefix),
3458
4128
  open: async (path, flags, mode) => _openAsync(path, flags, mode),
4129
+ opendir: async (p, opts) => _opendirAsync(p, opts),
4130
+ statfs: async (p, opts) => _statfsAsync(p, opts),
3459
4131
  watch: async function* (filename, opts) {
3460
4132
  // Minimal async iter — polls _bundleLookup every 500ms and yields
3461
4133
  // a single \`change\` event when content differs. Adequate for
@@ -3500,8 +4172,9 @@ const __fsMod = (() => {
3500
4172
  },
3501
4173
  };
3502
4174
 
3503
- // ── constants ──
3504
- const constants = { F_OK: 0, R_OK: 4, W_OK: 2, X_OK: 1 };
4175
+ // ── constants ── the shared linux x64 table (see __fsConstants).
4176
+ const constants = __fsConstants;
4177
+ promises.constants = constants;
3505
4178
 
3506
4179
  // fs.ReadStream / fs.WriteStream classes. Real Node exposes these as
3507
4180
  // constructors; graceful-fs (bundled by degit → create-cloudflare)
@@ -3591,10 +4264,15 @@ const __fsMod = (() => {
3591
4264
  const __fsExports = {
3592
4265
  readFileSync, writeFileSync, appendFileSync, existsSync, statSync, lstatSync,
3593
4266
  readdirSync, mkdirSync, unlinkSync, rmdirSync, renameSync, copyFileSync,
3594
- realpathSync, utimesSync, lutimesSync, chmodSync, accessSync,
4267
+ realpathSync, utimesSync, lutimesSync, chmodSync, lchmodSync, chownSync, lchownSync, accessSync,
4268
+ rmSync, cpSync, mkdtempSync, truncateSync, linkSync, opendirSync, statfsSync,
3595
4269
  openSync, closeSync, readSync, writeSync, fstatSync, ftruncateSync, fsyncSync, fdatasyncSync,
3596
- open, close, read, write, fstat, ftruncate, fsync, fdatasync, fchmod,
3597
- readFile, writeFile, appendFile, stat, lstat, readdir, exists, mkdir, unlink, rename, utimes, lutimes, chmod, chown, lchown, fchown, access,
4270
+ futimesSync, fchmodSync, fchownSync, readvSync, writevSync,
4271
+ open, close, read, write, fstat, ftruncate, fsync, fdatasync, fchmod, futimes, readv, writev,
4272
+ readFile, writeFile, appendFile, stat, lstat, readdir, exists, mkdir, unlink, rename, utimes, lutimes, chmod, lchmod, chown, lchown, fchown, access,
4273
+ rm, cp, truncate, copyFile, mkdtemp, link, symlink, readlink, realpath, opendir, statfs,
4274
+ Dirent: __Dirent,
4275
+ Dir: __Dir,
3598
4276
  promises, constants,
3599
4277
  createReadStream: (p, opts) => new (__getReadStream())(p, opts),
3600
4278
  createWriteStream: (p, opts) => {
@@ -3936,13 +4614,10 @@ globalThis.WebSocket = __NimbusRelayedWebSocket;
3936
4614
  //
3937
4615
  // require('node:constants') (and the legacy bare require('constants'))
3938
4616
  // expose a FLAT object of POSIX/Linux/OpenSSL constants. Real Node ships
3939
- // ~234 constants; we ship the ones that actually get touched by:
4617
+ // ~234 constants; the full fs slice comes from __fsConstants, and the rest
4618
+ // is what actually gets touched by:
3940
4619
  // - create-next-app (touches UV_FS_O_FILEMAP — verified via grep on
3941
4620
  // unpkg.com/create-next-app@latest/dist/index.js)
3942
- // - typical fs.openSync flag composition (O_RDONLY, O_WRONLY, O_RDWR,
3943
- // O_CREAT, O_EXCL, O_TRUNC, O_APPEND, O_DIRECTORY, etc.)
3944
- // - typical fs.access mode composition (F_OK, R_OK, W_OK, X_OK)
3945
- // - typical fs.copyFile mode composition (COPYFILE_EXCL etc.)
3946
4621
  // - signal/errno tables (shared shape with __osMod.constants.signals
3947
4622
  // etc. — flat here, nested there; both shapes are real-Node-accurate
3948
4623
  // and we expose both via the right module).
@@ -3987,26 +4662,10 @@ const __constantsMod = {
3987
4662
  SIGTSTP: 20, SIGTTIN: 21, SIGTTOU: 22, SIGURG: 23, SIGXCPU: 24,
3988
4663
  SIGXFSZ: 25, SIGVTALRM: 26, SIGPROF: 27, SIGWINCH: 28, SIGIO: 29,
3989
4664
  SIGPOLL: 29, SIGPWR: 30, SIGSYS: 31,
3990
- // ── File-type bits (stat.mode masks) ──────────────────────────────
3991
- S_IFMT: 61440, S_IFREG: 32768, S_IFDIR: 16384, S_IFCHR: 8192,
3992
- S_IFBLK: 24576, S_IFIFO: 4096, S_IFLNK: 40960, S_IFSOCK: 49152,
3993
- // ── File-open flags (fs.openSync / fs.constants.O_*) ──────────────
3994
- O_RDONLY: 0, O_WRONLY: 1, O_RDWR: 2,
3995
- O_CREAT: 64, O_EXCL: 128, O_NOCTTY: 256, O_TRUNC: 512, O_APPEND: 1024,
3996
- O_DIRECTORY: 65536, O_NOATIME: 262144, O_NOFOLLOW: 131072,
3997
- O_SYNC: 1052672, O_DSYNC: 4096, O_DIRECT: 16384, O_NONBLOCK: 2048,
3998
- // libuv-specific fs flag — create-next-app touches this directly.
3999
- UV_FS_O_FILEMAP: 0,
4000
- // ── File-permission bits (chmod / stat.mode user/group/other) ─────
4001
- S_IRWXU: 448, S_IRUSR: 256, S_IWUSR: 128, S_IXUSR: 64,
4002
- S_IRWXG: 56, S_IRGRP: 32, S_IWGRP: 16, S_IXGRP: 8,
4003
- S_IRWXO: 7, S_IROTH: 4, S_IWOTH: 2, S_IXOTH: 1,
4004
- // ── fs.access mode constants ──────────────────────────────────────
4005
- F_OK: 0, R_OK: 4, W_OK: 2, X_OK: 1,
4006
- // ── fs.copyFile mode constants (libuv shape + Node alias) ─────────
4007
- UV_FS_COPYFILE_EXCL: 1, COPYFILE_EXCL: 1,
4008
- UV_FS_COPYFILE_FICLONE: 2, COPYFILE_FICLONE: 2,
4009
- UV_FS_COPYFILE_FICLONE_FORCE: 4, COPYFILE_FICLONE_FORCE: 4,
4665
+ // ── fs constants (O_*, S_IF*, S_I*, F_OK.., COPYFILE_*, UV_*) ────
4666
+ // Node's constants module is the union of os, fs and crypto constants;
4667
+ // the fs slice is __fsConstants, the same object fs.constants exposes.
4668
+ ...__fsConstants,
4010
4669
  // ── OpenSSL / TLS option flags ────────────────────────────────────
4011
4670
  // Numeric values from real Node v20. Libraries probe defined-ness;
4012
4671
  // we ship the surface so constants.SSL_OP_* doesn't undefined-throw.
@@ -6424,7 +7083,20 @@ builtins.tty = {
6424
7083
  // core and must not be reported as such — a package that sniffs this list
6425
7084
  // would otherwise conclude e.g. undici ships with node.
6426
7085
  const __nimbusFacetProvidedPackages = new Set(${FACET_PROVIDED_PACKAGES_LITERAL});
6427
- builtins.module = { get builtinModules() { return Object.keys(builtins).filter((n) => !__nimbusFacetProvidedPackages.has(n)); }, createRequire: (specifier) => __makeRequire(__requireBaseDir(specifier)), _resolveFilename: (id) => id, _cache: {} };
7086
+ builtins.module = {
7087
+ get builtinModules() { return Object.keys(builtins).filter((n) => !__nimbusFacetProvidedPackages.has(n)); },
7088
+ createRequire: (specifier) => __makeRequire(__requireBaseDir(specifier)),
7089
+ isBuiltin: (specifier) => Object.hasOwn(builtins, String(specifier).replace(/^node:/, '')) && !__nimbusFacetProvidedPackages.has(String(specifier).replace(/^node:/, '')),
7090
+ // Node 22.1's on-disk compile cache. There is no disk to cache into and
7091
+ // nothing to compile ahead: callers (pi's CLI entry calls it
7092
+ // unconditionally) get Node's own answer for a cache that is off.
7093
+ enableCompileCache: () => ({ status: 3, message: 'compile cache is not available in this runtime' }),
7094
+ getCompileCacheDir: () => undefined,
7095
+ flushCompileCache: () => {},
7096
+ constants: { compileCacheStatus: { FAILED: 0, ENABLED: 1, ALREADY_ENABLED: 2, DISABLED: 3 } },
7097
+ _resolveFilename: (id) => id,
7098
+ _cache: {},
7099
+ };
6428
7100
  // Bind to globalThis: workerd's timer globals throw "Illegal invocation"
6429
7101
  // when called with a receiver other than globalThis (i.e. as
6430
7102
  // timers.setInterval(...)), which clack's spinner — used by