@nimbus-sh/worker 0.9.0 → 0.11.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 (286) hide show
  1. package/NOTICE.md +5 -0
  2. package/README.md +10 -0
  3. package/dist/_shared/session-router.d.ts +7 -0
  4. package/dist/_shared/session-router.d.ts.map +1 -1
  5. package/dist/_shared/session-router.js +7 -0
  6. package/dist/bindings/body.d.ts +6 -0
  7. package/dist/bindings/body.d.ts.map +1 -0
  8. package/dist/bindings/body.js +37 -0
  9. package/dist/bindings/kv.d.ts +0 -2
  10. package/dist/bindings/kv.d.ts.map +1 -1
  11. package/dist/bindings/kv.js +3 -42
  12. package/dist/bindings/r2.d.ts +0 -2
  13. package/dist/bindings/r2.d.ts.map +1 -1
  14. package/dist/bindings/r2.js +4 -44
  15. package/dist/esbuild-cli-artifact.generated.d.ts +18 -0
  16. package/dist/esbuild-cli-artifact.generated.d.ts.map +1 -0
  17. package/dist/esbuild-cli-artifact.generated.js +17 -0
  18. package/dist/esbuild-wasm-bundle.generated.d.ts +24 -24
  19. package/dist/esbuild-wasm-bundle.generated.d.ts.map +1 -1
  20. package/dist/esbuild-wasm-bundle.generated.js +24 -24
  21. package/dist/facets/cirrus-real.d.ts.map +1 -1
  22. package/dist/facets/cirrus-real.js +11 -6
  23. package/dist/facets/compose.d.ts +11 -7
  24. package/dist/facets/compose.d.ts.map +1 -1
  25. package/dist/facets/compose.js +8 -31
  26. package/dist/facets/data-plan.d.ts +79 -0
  27. package/dist/facets/data-plan.d.ts.map +1 -0
  28. package/dist/facets/data-plan.js +477 -0
  29. package/dist/facets/esbuild-bundle-pool.d.ts +1 -1
  30. package/dist/facets/esbuild-bundle-pool.js +3 -3
  31. package/dist/facets/esbuild-transform.d.ts +38 -8
  32. package/dist/facets/esbuild-transform.d.ts.map +1 -1
  33. package/dist/facets/esbuild-transform.js +219 -19
  34. package/dist/facets/exec-telemetry.d.ts +7 -3
  35. package/dist/facets/exec-telemetry.d.ts.map +1 -1
  36. package/dist/facets/manager.d.ts +198 -69
  37. package/dist/facets/manager.d.ts.map +1 -1
  38. package/dist/facets/manager.js +1276 -652
  39. package/dist/facets/opencode-staging.d.ts +0 -2
  40. package/dist/facets/opencode-staging.d.ts.map +1 -1
  41. package/dist/facets/opencode-staging.js +5 -11
  42. package/dist/facets/process.d.ts +4 -0
  43. package/dist/facets/process.d.ts.map +1 -1
  44. package/dist/facets/process.js +3 -0
  45. package/dist/facets/read-profile.d.ts +149 -0
  46. package/dist/facets/read-profile.d.ts.map +1 -0
  47. package/dist/facets/read-profile.js +483 -0
  48. package/dist/facets/real-vite-hmr.d.ts +2 -0
  49. package/dist/facets/real-vite-hmr.d.ts.map +1 -1
  50. package/dist/facets/real-vite-hmr.js +3 -2
  51. package/dist/facets/resident-identity.d.ts +0 -1
  52. package/dist/facets/resident-identity.d.ts.map +1 -1
  53. package/dist/facets/resident-identity.js +0 -3
  54. package/dist/facets/wasm-swap-registry.d.ts +34 -25
  55. package/dist/facets/wasm-swap-registry.d.ts.map +1 -1
  56. package/dist/facets/wasm-swap-registry.js +62 -56
  57. package/dist/facets/ws-terminal.d.ts +4 -4
  58. package/dist/facets/ws-terminal.d.ts.map +1 -1
  59. package/dist/facets/ws-terminal.js +4 -5
  60. package/dist/git/commands.d.ts +33 -10
  61. package/dist/git/commands.d.ts.map +1 -1
  62. package/dist/git/commands.js +1891 -197
  63. package/dist/git/network-facet.d.ts +12 -2
  64. package/dist/git/network-facet.d.ts.map +1 -1
  65. package/dist/git/network-facet.js +69 -18
  66. package/dist/git/unified-diff.d.ts +74 -0
  67. package/dist/git/unified-diff.d.ts.map +1 -0
  68. package/dist/git/unified-diff.js +676 -0
  69. package/dist/git-bundle.generated.d.ts +7 -11
  70. package/dist/git-bundle.generated.d.ts.map +1 -1
  71. package/dist/git-bundle.generated.js +7 -11
  72. package/dist/hosted/commands.d.ts.map +1 -1
  73. package/dist/hosted/commands.js +64 -245
  74. package/dist/hosted/runtime.d.ts +22 -15
  75. package/dist/hosted/runtime.d.ts.map +1 -1
  76. package/dist/hosted/runtime.js +72 -14
  77. package/dist/hosted/services.d.ts +8 -1
  78. package/dist/hosted/services.d.ts.map +1 -1
  79. package/dist/hosted/services.js +12 -13
  80. package/dist/hosted/session.d.ts +165 -0
  81. package/dist/hosted/session.d.ts.map +1 -0
  82. package/dist/hosted/session.js +132 -0
  83. package/dist/loaders/generated-workers.d.ts +6 -3
  84. package/dist/loaders/generated-workers.d.ts.map +1 -1
  85. package/dist/loaders/generated-workers.js +8 -5
  86. package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -1
  87. package/dist/loaders/npm-resolve-preamble.js +0 -27
  88. package/dist/loaders/pre-bundle-preamble.d.ts +6 -3
  89. package/dist/loaders/pre-bundle-preamble.d.ts.map +1 -1
  90. package/dist/loaders/pre-bundle-preamble.js +13 -8
  91. package/dist/node-shims-artifact.generated.d.ts +13 -4
  92. package/dist/node-shims-artifact.generated.d.ts.map +1 -1
  93. package/dist/node-shims-artifact.generated.js +16 -7
  94. package/dist/npm/bin-links.d.ts +3 -1
  95. package/dist/npm/bin-links.d.ts.map +1 -1
  96. package/dist/npm/bin-links.js +32 -8
  97. package/dist/npm/cache.d.ts +11 -1
  98. package/dist/npm/cache.d.ts.map +1 -1
  99. package/dist/npm/cache.js +21 -7
  100. package/dist/npm/install-batch-facet.d.ts +4 -2
  101. package/dist/npm/install-batch-facet.d.ts.map +1 -1
  102. package/dist/npm/install-batch-facet.js +32 -18
  103. package/dist/npm/installer.d.ts +23 -3
  104. package/dist/npm/installer.d.ts.map +1 -1
  105. package/dist/npm/installer.js +330 -131
  106. package/dist/npm/npx-install.d.ts +3 -1
  107. package/dist/npm/npx-install.d.ts.map +1 -1
  108. package/dist/npm/npx-install.js +4 -1
  109. package/dist/npm/package-lock.d.ts +32 -0
  110. package/dist/npm/package-lock.d.ts.map +1 -0
  111. package/dist/npm/package-lock.js +65 -0
  112. package/dist/npm/placement.d.ts +16 -0
  113. package/dist/npm/placement.d.ts.map +1 -0
  114. package/dist/npm/placement.js +33 -0
  115. package/dist/npm/pre-bundle-facet.d.ts +1 -30
  116. package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
  117. package/dist/npm/pre-bundle-facet.js +3 -5
  118. package/dist/npm/r2-cache.d.ts +12 -6
  119. package/dist/npm/r2-cache.d.ts.map +1 -1
  120. package/dist/npm/r2-cache.js +34 -16
  121. package/dist/npm/resolve-one-facet.d.ts +3 -0
  122. package/dist/npm/resolve-one-facet.d.ts.map +1 -1
  123. package/dist/npm/resolve-one-facet.js +15 -3
  124. package/dist/npm/resolver.d.ts +11 -13
  125. package/dist/npm/resolver.d.ts.map +1 -1
  126. package/dist/npm/resolver.js +12 -25
  127. package/dist/opencode-artifact.generated.js +2 -2
  128. package/dist/replica/routing.d.ts +2 -38
  129. package/dist/replica/routing.d.ts.map +1 -1
  130. package/dist/replica/routing.js +3 -55
  131. package/dist/router/index.d.ts.map +1 -1
  132. package/dist/router/index.js +15 -2
  133. package/dist/router/public-directory.d.ts +18 -0
  134. package/dist/router/public-directory.d.ts.map +1 -1
  135. package/dist/router/public-directory.js +1 -1
  136. package/dist/router/remote-api.d.ts +3 -1
  137. package/dist/router/remote-api.d.ts.map +1 -1
  138. package/dist/router/remote-api.js +42 -79
  139. package/dist/runtime/bash-repl.js +2 -2
  140. package/dist/runtime/bun-repl.d.ts.map +1 -1
  141. package/dist/runtime/bun-repl.js +2 -13
  142. package/dist/runtime/cpython-resident.d.ts.map +1 -1
  143. package/dist/runtime/cpython-resident.js +2 -10
  144. package/dist/runtime/esbuild-wasm-bytes.d.ts +37 -18
  145. package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -1
  146. package/dist/runtime/esbuild-wasm-bytes.js +66 -92
  147. package/dist/runtime/facet-loader-host.d.ts.map +1 -1
  148. package/dist/runtime/facet-loader-host.js +0 -7
  149. package/dist/runtime/git-bundle-artifact.d.ts +17 -0
  150. package/dist/runtime/git-bundle-artifact.d.ts.map +1 -0
  151. package/dist/runtime/git-bundle-artifact.js +25 -0
  152. package/dist/runtime/node-repl.d.ts.map +1 -1
  153. package/dist/runtime/node-repl.js +2 -13
  154. package/dist/runtime/node-shims-artifact.d.ts +22 -23
  155. package/dist/runtime/node-shims-artifact.d.ts.map +1 -1
  156. package/dist/runtime/node-shims-artifact.js +48 -78
  157. package/dist/runtime/node-shims.d.ts.map +1 -1
  158. package/dist/runtime/node-shims.js +2358 -996
  159. package/dist/runtime/opencode-artifact.d.ts.map +1 -1
  160. package/dist/runtime/opencode-artifact.js +17 -53
  161. package/dist/runtime/opencode-facet-runner.d.ts +3 -6
  162. package/dist/runtime/opencode-facet-runner.d.ts.map +1 -1
  163. package/dist/runtime/opencode-facet-runner.js +45 -10
  164. package/dist/runtime/opentui-wasm-bytes.d.ts.map +1 -1
  165. package/dist/runtime/opentui-wasm-bytes.js +16 -49
  166. package/dist/runtime/package-manager.d.ts +0 -2
  167. package/dist/runtime/package-manager.d.ts.map +1 -1
  168. package/dist/runtime/package-manager.js +0 -3
  169. package/dist/runtime/python-repl.d.ts.map +1 -1
  170. package/dist/runtime/python-repl.js +4 -35
  171. package/dist/runtime/repl-session.d.ts +10 -0
  172. package/dist/runtime/repl-session.d.ts.map +1 -1
  173. package/dist/runtime/repl-session.js +7 -0
  174. package/dist/runtime/ruby-repl.d.ts.map +1 -1
  175. package/dist/runtime/ruby-repl.js +2 -2
  176. package/dist/runtime/ruby-resident.d.ts.map +1 -1
  177. package/dist/runtime/ruby-resident.js +41 -51
  178. package/dist/runtime/runtime-catalog.d.ts +10 -3
  179. package/dist/runtime/runtime-catalog.d.ts.map +1 -1
  180. package/dist/runtime/runtime-catalog.js +125 -15
  181. package/dist/runtime/sqlite-wasm-bytes.d.ts +4 -4
  182. package/dist/runtime/sqlite-wasm-bytes.d.ts.map +1 -1
  183. package/dist/runtime/sqlite-wasm-bytes.js +21 -57
  184. package/dist/runtime/staged-source.d.ts +67 -0
  185. package/dist/runtime/staged-source.d.ts.map +1 -0
  186. package/dist/runtime/staged-source.js +113 -0
  187. package/dist/runtime/wasi/preamble.d.ts +0 -22
  188. package/dist/runtime/wasi/preamble.d.ts.map +1 -1
  189. package/dist/runtime/wasi/preamble.js +98 -1425
  190. package/dist/runtime-catalog.generated.js +1 -1
  191. package/dist/session/agent.d.ts +14 -2
  192. package/dist/session/agent.d.ts.map +1 -1
  193. package/dist/session/agent.js +57 -19
  194. package/dist/session/ai.d.ts +0 -11
  195. package/dist/session/ai.d.ts.map +1 -1
  196. package/dist/session/ai.js +0 -4
  197. package/dist/session/fs-watch.d.ts +0 -11
  198. package/dist/session/fs-watch.d.ts.map +1 -1
  199. package/dist/session/fs-watch.js +0 -21
  200. package/dist/session/hibernation.d.ts +68 -2
  201. package/dist/session/hibernation.d.ts.map +1 -1
  202. package/dist/session/hibernation.js +95 -1
  203. package/dist/session/init-phases.d.ts +1 -2
  204. package/dist/session/init-phases.d.ts.map +1 -1
  205. package/dist/session/init-phases.js +1 -2
  206. package/dist/session/init.d.ts.map +1 -1
  207. package/dist/session/init.js +7 -20
  208. package/dist/session/keys.d.ts +0 -3
  209. package/dist/session/keys.d.ts.map +1 -1
  210. package/dist/session/keys.js +0 -3
  211. package/dist/session/legacy-reset.d.ts +11 -0
  212. package/dist/session/legacy-reset.d.ts.map +1 -0
  213. package/dist/session/legacy-reset.js +16 -0
  214. package/dist/session/nimbus-session.d.ts +50 -16
  215. package/dist/session/nimbus-session.d.ts.map +1 -1
  216. package/dist/session/nimbus-session.js +77 -71
  217. package/dist/session/npm-install-port.d.ts.map +1 -1
  218. package/dist/session/npm-install-port.js +2 -0
  219. package/dist/session/programmatic.d.ts +13 -18
  220. package/dist/session/programmatic.d.ts.map +1 -1
  221. package/dist/session/programmatic.js +53 -39
  222. package/dist/session/replica-routes.d.ts +2 -3
  223. package/dist/session/replica-routes.d.ts.map +1 -1
  224. package/dist/session/replica-routes.js +3 -10
  225. package/dist/session/routes.d.ts.map +1 -1
  226. package/dist/session/routes.js +11 -12
  227. package/dist/session/rpc.d.ts +108 -21
  228. package/dist/session/rpc.d.ts.map +1 -1
  229. package/dist/session/rpc.js +194 -39
  230. package/dist/session/start-real-vite.js +2 -2
  231. package/dist/session/state-store.d.ts +1 -17
  232. package/dist/session/state-store.d.ts.map +1 -1
  233. package/dist/session/state-store.js +3 -53
  234. package/dist/session/supervisor-op.d.ts +7 -0
  235. package/dist/session/supervisor-op.d.ts.map +1 -1
  236. package/dist/session/supervisor-op.js +6 -1
  237. package/dist/session/supervisor-rpc.d.ts +116 -19
  238. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  239. package/dist/session/supervisor-rpc.js +194 -84
  240. package/dist/session/vite-command.d.ts.map +1 -1
  241. package/dist/session/vite-command.js +17 -14
  242. package/dist/session/ws.d.ts.map +1 -1
  243. package/dist/session/ws.js +20 -8
  244. package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
  245. package/dist/shell/npm-bin-entrypoints.js +30 -5
  246. package/dist/vfs/facet-resident-limits.d.ts +33 -0
  247. package/dist/vfs/facet-resident-limits.d.ts.map +1 -0
  248. package/dist/vfs/facet-resident-limits.js +33 -0
  249. package/dist/vfs/facet-resident-store.d.ts +2 -10
  250. package/dist/vfs/facet-resident-store.d.ts.map +1 -1
  251. package/dist/vfs/facet-resident-store.js +1594 -219
  252. package/dist/workspace-host.d.ts +3 -1
  253. package/dist/workspace-host.d.ts.map +1 -1
  254. package/dist/workspace-host.js +2 -0
  255. package/dist/wrangler/nimbus-wrangler.d.ts +0 -2
  256. package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -1
  257. package/dist/wrangler/nimbus-wrangler.js +8 -22
  258. package/package.json +34 -7
  259. package/public/_assets/esbuild-0.24.2.js +2380 -0
  260. package/public/_assets/opencode/1.16.2/index-attach.js +2 -2
  261. package/public/_assets/runtime/esbuild-cli-c4fda380d52befc1.js +6892 -0
  262. package/public/_assets/runtime/git-de3e60c9f3ff006a.js +103 -0
  263. package/public/_assets/runtime/{node-shims-acaa037e6d26ec0d.js → node-shims-06565b121555c194.js} +2879 -992
  264. package/public/_assets/runtime/resident-store-6ffb1939c53d982f.js +2264 -0
  265. package/public/_assets/runtime/vfs-write-ledger-e501e8fc793bc2c1.js +889 -0
  266. package/scripts/build-opencode-attach-entry.mjs +51 -1
  267. package/scripts/bundle-esbuild-wasm.mjs +55 -39
  268. package/scripts/bundle-facet-workers.mjs +159 -10
  269. package/scripts/bundle-git.mjs +88 -27
  270. package/scripts/bundle-node-shims.mjs +72 -46
  271. package/scripts/bundle-runtime.mjs +18 -421
  272. package/scripts/cf-git-patch.mjs +137 -0
  273. package/scripts/patch-install-deps.mjs +49 -96
  274. package/scripts/runtime-specs.mjs +435 -0
  275. package/vendor/git-http.generated.d.ts +82 -0
  276. package/vendor/git-upstream.generated.d.ts +4378 -0
  277. package/vendor/git.LICENSE +7 -0
  278. package/vendor/git.generated.d.mts +21 -0
  279. package/vendor/git.generated.mjs +103 -0
  280. package/dist/replica/suspension.d.ts +0 -36
  281. package/dist/replica/suspension.d.ts.map +0 -1
  282. package/dist/replica/suspension.js +0 -49
  283. package/dist/runtime/static-server.d.ts +0 -3
  284. package/dist/runtime/static-server.d.ts.map +0 -1
  285. package/dist/runtime/static-server.js +0 -133
  286. package/scripts/clean-dist.mjs +0 -7
@@ -41,17 +41,23 @@
41
41
  import { generateStreamsCode } from '@nimbus-sh/core/runtime/streams.js';
42
42
  import { generateSqliteShimCode } from './sqlite-shim.js';
43
43
  import { generateUndiciShimCode } from '@nimbus-sh/core/runtime/undici-shim.js';
44
+ import { MK_COMPILED_FN_SOURCE } from '@nimbus-sh/core/_shared/compiled-fn.js';
44
45
  import { getExportsResolverJS } from '@nimbus-sh/core/_shared/exports-resolver.js';
46
+ import { ESM_RESOLVER_PREAMBLE } from '../loaders/generated-workers.js';
45
47
  import { getTypescriptSpecifiersJS } from '@nimbus-sh/core/_shared/typescript-specifiers.js';
46
48
  import { NIMBUS_AI_CREDENTIAL_HEADERS, NIMBUS_AI_TOKEN_ENV } from '@nimbus-sh/core/_shared/ai-egress.js';
47
49
  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';
50
+ 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';
51
+ import { PACKAGE_ABI_POLICY } from '../facets/wasm-swap-registry.js';
49
52
  const STREAMS_CODE = generateStreamsCode();
50
53
  const SQLITE_SHIM_CODE = generateSqliteShimCode();
51
54
  const UNDICI_SHIM_CODE = generateUndiciShimCode();
52
55
  const FACET_PROVIDED_PACKAGES_LITERAL = JSON.stringify(FACET_PROVIDED_PACKAGES);
53
56
  const EXPORTS_RESOLVER_JS = getExportsResolverJS();
54
57
  const TYPESCRIPT_SPECIFIERS_JS = getTypescriptSpecifiersJS();
58
+ // One module-cell compile for every facet (core/_shared/compiled-fn.ts).
59
+ // Interpolated into the emitted shim below as ${COMPILED_FN_JS}.
60
+ const COMPILED_FN_JS = MK_COMPILED_FN_SOURCE;
55
61
  // Node version fingerprint. Single source of truth in constants.ts.
56
62
  // Interpolated as JS literals into the emitted process shim. See
57
63
  // constants.ts for the rationale (create-astro preflight, etc.).
@@ -62,15 +68,43 @@ const NODE_VERSIONS_LITERAL = JSON.stringify(NODE_VERSIONS);
62
68
  // from _shared/ai-egress.ts at build time rather than being written twice.
63
69
  const AI_TOKEN_ENV_LITERAL = JSON.stringify(NIMBUS_AI_TOKEN_ENV);
64
70
  const AI_CREDENTIAL_HEADERS_LITERAL = JSON.stringify(NIMBUS_AI_CREDENTIAL_HEADERS);
71
+ const ABI_ADVISORIES_LITERAL = JSON.stringify(PACKAGE_ABI_POLICY.rejects.map((r) => [r.from, r.suggest ? `${r.reason} … try: ${r.suggest}` : r.reason]));
65
72
  export function generateShimsCode() {
66
73
  return `
67
74
  // ═══════════════════════════════════════════════════════════════════════
68
75
  // ── Format helper ──────────────────────────────────────────────────
69
76
  // ═══════════════════════════════════════════════════════════════════════
77
+ function __isErrorValue(v) {
78
+ return v instanceof Error || Object.prototype.toString.call(v) === "[object Error]";
79
+ }
80
+ // Node's util.inspect(err); JSON.stringify drops non-enumerable name/message/stack.
81
+ function __fmtError(e, seen) {
82
+ if (seen.has(e)) return "[Circular *]";
83
+ seen.add(e);
84
+ let text;
85
+ try { text = typeof e.stack === "string" && e.stack ? e.stack : Error.prototype.toString.call(e); }
86
+ catch { text = String(e); }
87
+ const fields = [];
88
+ for (const key of Object.keys(e)) {
89
+ if (key === "cause") continue;
90
+ let value;
91
+ try { value = e[key]; } catch { continue; }
92
+ fields.push(key + ": " + __fmtField(value, seen));
93
+ }
94
+ if (Object.prototype.hasOwnProperty.call(e, "cause")) fields.push("[cause]: " + __fmtField(e.cause, seen));
95
+ if (fields.length === 0) return text;
96
+ return text + " {\\n" + fields.map((f) => " " + f.split("\\n").join("\\n ")).join(",\\n") + "\\n}";
97
+ }
98
+ function __fmtField(v, seen) {
99
+ if (typeof v === "string") return JSON.stringify(v);
100
+ if (v !== null && typeof v === "object" && __isErrorValue(v)) return __fmtError(v, seen);
101
+ return __fmt(v);
102
+ }
70
103
  function __fmt(v) {
71
104
  if (v === null) return "null";
72
105
  if (v === undefined) return "undefined";
73
106
  if (typeof v === "object") {
107
+ if (__isErrorValue(v)) return __fmtError(v, new Set());
74
108
  try { return JSON.stringify(v); } catch { return String(v); }
75
109
  }
76
110
  return String(v);
@@ -123,6 +157,41 @@ async function __nimbusUseRpcResultUnref(promise, use) {
123
157
  finally { __nimbusDisposeRpcResult(value); }
124
158
  }
125
159
 
160
+ /**
161
+ * The ACQUIRE barrier, taken before the program sees anything that arrived
162
+ * from outside it: a response or body from the network, a relayed socket
163
+ * frame, a request routed to one of its ports, a stdin packet or signal, a
164
+ * child's output or exit. Each of those can be the second half of a causal
165
+ * chain that began with a write somewhere else — \`echo v2 > f; curl :3000\`,
166
+ * a child that writes a file and then exits — and the handler's synchronous
167
+ * reads must see that write. Nothing but this barrier carries it.
168
+ *
169
+ * What the supervisor delivers — a request, a stdin packet, a child's output
170
+ * or exit — carries the answer to this barrier with it (\`delivered\`,
171
+ * session/rpc.ts _acquireOnDelivery), computed after the thing delivered was
172
+ * queued, so the barrier applies that answer instead of asking, and the
173
+ * program resumes with no round trip of its own. It asks when there is none
174
+ * or it cannot use the one delivered. A resumption the supervisor does not
175
+ * deliver — a timer, the network — always asks.
176
+ *
177
+ * The barrier is installed by the fs module, which is evaluated after this
178
+ * point; a facet with no supervisor has none and nothing to be coherent with.
179
+ */
180
+ async function __nimbusInboundBarrier(delivered) {
181
+ const acquire = globalThis.__nimbusVfsAcquireBarrier;
182
+ if (typeof acquire === "function") await acquire(delivered);
183
+ }
184
+
185
+ /**
186
+ * The arguments this process asks its ACQUIRE with (the fs module's
187
+ * _acquireArgs), for a delivery to be answered from: a long poll sends them,
188
+ * and what it delivers comes back carrying fsAcquire's answer to them.
189
+ */
190
+ function __nimbusVfsAcquireArgs() {
191
+ const args = globalThis.__nimbusVfsAcquireArgs;
192
+ return typeof args === "function" ? args() : undefined;
193
+ }
194
+
126
195
  // ═══════════════════════════════════════════════════════════════════════
127
196
  // ── Precompiled wasm ───────────────────────────────────────────────
128
197
  // A wasm image can only become a WebAssembly.Module through the Worker
@@ -352,13 +421,11 @@ function __nimbusWasmDigest(bytes) {
352
421
  // supervisor round trip per outbound request, not a hidden one. It is the
353
422
  // price of the guarantee.
354
423
  //
355
- // The barriers live on globalThis because the fs module installs them and
356
- // is evaluated after this one; a facet with no supervisor bound has none,
357
- // and there is nothing to be coherent with.
424
+ // The RELEASE barrier lives on globalThis because the fs module installs it
425
+ // and is evaluated after this one; the ACQUIRE is __nimbusInboundBarrier.
358
426
  const __resumeCoherent = async (pending) => {
359
427
  const value = await pending;
360
- const acquire = globalThis.__nimbusVfsAcquireBarrier;
361
- if (typeof acquire === "function") await acquire();
428
+ await __nimbusInboundBarrier();
362
429
  return value;
363
430
  };
364
431
  const __barriered = async (input, init) => {
@@ -388,6 +455,15 @@ function __nimbusWasmDigest(bytes) {
388
455
 
389
456
  let __nimbusLiveStdinPump = null;
390
457
  let __nimbusProcessExitReported = false;
458
+ // Set when the program has exited: its timers are cleared and its writes refused.
459
+ let __nimbusProgramStopped = false;
460
+ let __nimbusExitEmitted = false;
461
+ // 'exit' listeners run once, synchronously, before the program is stopped.
462
+ function __nimbusEmitExit(code) {
463
+ if (__nimbusExitEmitted) return;
464
+ __nimbusExitEmitted = true;
465
+ try { __processEvents.emit("exit", code); } catch {}
466
+ }
391
467
  let __nimbusProcessExitResolve = null;
392
468
  let __nimbusProcessExitCode = null;
393
469
  const __nimbusProcessExitPromise = new Promise((resolve) => {
@@ -634,6 +710,43 @@ function __residentAnyUnder(prefix) {
634
710
  return false;
635
711
  }
636
712
 
713
+ // ═══════════════════════════════════════════════════════════════════════
714
+ // ── fs.constants (linux x64) ───────────────────────────────────────
715
+ // ═══════════════════════════════════════════════════════════════════════
716
+ //
717
+ // The ONE table behind fs.constants, fs.promises.constants, and the fs slice
718
+ // of node:constants / process.binding('fs'). Values are real Node on Linux
719
+ // x64 (node -p 'require("fs").constants'). Packages compose open flags from
720
+ // this table at module init — modern-tar (create-astro via @bluwy/giget-core)
721
+ // computes O_WRONLY|O_CREAT|O_TRUNC|O_NOFOLLOW|O_EXCL and passes the number
722
+ // to fs.open — so a partial table silently degrades every such open to
723
+ // O_RDONLY (0) and every extraction fails ENOENT. Frozen, as Node's is.
724
+ const __fsConstants = Object.freeze({
725
+ // ── libuv fs flags ────────────────────────────────────────────────
726
+ UV_FS_SYMLINK_DIR: 1, UV_FS_SYMLINK_JUNCTION: 2,
727
+ // ── open(2) flags ─────────────────────────────────────────────────
728
+ O_RDONLY: 0, O_WRONLY: 1, O_RDWR: 2,
729
+ UV_DIRENT_UNKNOWN: 0, UV_DIRENT_FILE: 1, UV_DIRENT_DIR: 2, UV_DIRENT_LINK: 3,
730
+ UV_DIRENT_FIFO: 4, UV_DIRENT_SOCKET: 5, UV_DIRENT_CHAR: 6, UV_DIRENT_BLOCK: 7,
731
+ EXTENSIONLESS_FORMAT_JAVASCRIPT: 0, EXTENSIONLESS_FORMAT_WASM: 1,
732
+ // ── stat.mode file-type bits ──────────────────────────────────────
733
+ S_IFMT: 61440, S_IFREG: 32768, S_IFDIR: 16384, S_IFCHR: 8192,
734
+ S_IFBLK: 24576, S_IFIFO: 4096, S_IFLNK: 40960, S_IFSOCK: 49152,
735
+ O_CREAT: 64, O_EXCL: 128, UV_FS_O_FILEMAP: 0, O_NOCTTY: 256, O_TRUNC: 512,
736
+ O_APPEND: 1024, O_DIRECTORY: 65536, O_NOATIME: 262144, O_NOFOLLOW: 131072,
737
+ O_SYNC: 1052672, O_DSYNC: 4096, O_DIRECT: 16384, O_NONBLOCK: 2048,
738
+ // ── stat.mode permission bits ─────────────────────────────────────
739
+ S_IRWXU: 448, S_IRUSR: 256, S_IWUSR: 128, S_IXUSR: 64,
740
+ S_IRWXG: 56, S_IRGRP: 32, S_IWGRP: 16, S_IXGRP: 8,
741
+ S_IRWXO: 7, S_IROTH: 4, S_IWOTH: 2, S_IXOTH: 1,
742
+ // ── fs.access modes ───────────────────────────────────────────────
743
+ F_OK: 0, R_OK: 4, W_OK: 2, X_OK: 1,
744
+ // ── fs.copyFile modes (libuv shape + Node alias) ──────────────────
745
+ UV_FS_COPYFILE_EXCL: 1, COPYFILE_EXCL: 1,
746
+ UV_FS_COPYFILE_FICLONE: 2, COPYFILE_FICLONE: 2,
747
+ UV_FS_COPYFILE_FICLONE_FORCE: 4, COPYFILE_FICLONE_FORCE: 4,
748
+ });
749
+
637
750
  // ═══════════════════════════════════════════════════════════════════════
638
751
  // ── fs shim (VFS-backed) ───────────────────────────────────────────
639
752
  // ═══════════════════════════════════════════════════════════════════════
@@ -701,29 +814,205 @@ const __fsMod = (() => {
701
814
  // ── VFS bundle lookup (fast path — in-memory) ──
702
815
  function _bundleLookup(absPath) {
703
816
  const k = _strip(absPath);
704
- if (__vfsBundle && k in __vfsBundle) return __vfsBundle[k];
705
- // Also check writes
817
+ if (globalThis.__nimbusProfileStaged) globalThis.__nimbusProfileStaged.delete(k);
818
+ // The parked write first, as _writtenCell reads: it is this process's own,
819
+ // newer than anything the store holds, and it is the live cell (a store
820
+ // read reassembles a copy, which a write loop would pay for per write).
706
821
  if (__vfsWrites && k in __vfsWrites) return __vfsWrites[k];
822
+ if (__vfsBundle && k in __vfsBundle) return __vfsBundle[k];
823
+ // The same bytes under the name they are held by: through a symlink, or
824
+ // under the old name of a rename this process has not seen land yet.
825
+ if (__vfsBundle && _nsActive()) {
826
+ const real = _nsRealKey(k);
827
+ if (real !== null && real !== k && real in __vfsBundle) return __vfsBundle[real];
828
+ }
707
829
  return undefined;
708
830
  }
709
831
 
710
- // __vfsMetadata is not declared in every embedding of these shims, so the
711
- // one guarded reference lives here and every other reader goes through it.
712
- function _metadataTable() {
713
- return (typeof __vfsMetadata !== "undefined" && __vfsMetadata) ? __vfsMetadata : null;
714
- }
715
-
716
- // Same guard, same reason: the SQLite resident store is spliced into the
717
- // resident-process body only. The one-shot exec path runs in a stateless
718
- // loaded worker with no facet storage at all, so these shims must still work
719
- // with the resident set on the heap and this is the single place that asks.
832
+ // Whether the store is bound: every embedding of these shims boots one (a
833
+ // facet's SQLite, or a one-shot's heap), and this is the single place that asks.
720
834
  function _residentStorePresent() {
721
835
  return typeof __residentAdmit === "function" && typeof __residentReady !== "undefined" && __residentReady;
722
836
  }
723
837
 
838
+ // ── The namespace (resident facets) ──
839
+ //
840
+ // A resident facet's store holds every name the credential can see, with
841
+ // its stat, exact at the cursor (vfs/facet-resident-store.ts). While it
842
+ // does, it answers every synchronous metadata question — stat, exists,
843
+ // readdir, realpath, access — and an absent name is known absent, so none
844
+ // of them records a miss or refuses. While it does not (a relist failed,
845
+ // until the next barrier repairs it), each of them is refused by name
846
+ // (_nsRequire).
847
+ //
848
+ // What the table cannot know is this process's own structural effects that
849
+ // the authority has not reported back yet, so those sit in an overlay, one
850
+ // entry per path: "absent" / "absentTree" (unlink, rmdir, rm), "dir"
851
+ // (mkdir; \`hide\` when nothing was there, so no stale child shows through)
852
+ // and "alias" (a rename: the new name denotes what the old one did). An
853
+ // entry retires once its mutation has settled AND a barrier begun after
854
+ // that has applied, because such a barrier's delta is answered after the
855
+ // mutation committed, so the table then shows it or anything later.
856
+ function _nsActive() {
857
+ return typeof __nsReady === "function" && __nsReady();
858
+ }
859
+ // The namespace answers every synchronous metadata question, or none. While
860
+ // it is not ready (a relist failed, until the next barrier repairs it) a
861
+ // synchronous call is refused, naming why and the asynchronous form that
862
+ // answers now. A launch never runs user code before it is ready.
863
+ function _nsRequire(syscall, displayPath, asyncForm) {
864
+ if (_nsActive()) return;
865
+ _stats.namespaceRefusals++;
866
+ const why = typeof __nsNotReadyCause === "function" ? __nsNotReadyCause() : "the process was started without one";
867
+ const err = _fsErr("EAGAIN", syscall, displayPath);
868
+ err.message = "EAGAIN: " + syscall + " '" + String(displayPath) + "': the namespace is being rebuilt after " + why
869
+ + (_supervisor() ? "; use " + asyncForm : "");
870
+ throw err;
871
+ }
872
+ const _nsOwn = new Map();
873
+ let _nsFresh = [];
874
+ let _barrierBegins = 0;
875
+ function _nsOwnSet(k, state, extra) {
876
+ const before = _nsOwn.get(k);
877
+ const entry = { state, from: extra?.from, hide: !!extra?.hide, settled: null };
878
+ try { __residentNamespaceOverlayDelta(_nsOwnCost(k, entry) - (before ? _nsOwnCost(k, before) : 0)); }
879
+ catch (error) {
880
+ // The view is sealed by the store. Let the already-projected structural
881
+ // operation reach its authority/ack; aborting midway would strand
882
+ // earlier overlays with no owning mutation to retire them.
883
+ if (__residentInHeap && error?.code === "ENOSPC") return;
884
+ throw error;
885
+ }
886
+ _nsOwn.set(k, entry);
887
+ _nsFresh.push(entry);
888
+ }
889
+ function _nsOwnCost(k, entry) { return __namespaceRowBytes("", k, entry.from); }
890
+ /** Hand the overlay entries made since the last mutation to the one being queued. */
891
+ function _nsTakeFresh() {
892
+ const mine = _nsFresh;
893
+ _nsFresh = [];
894
+ return () => { for (const entry of mine) if (entry.settled === null) entry.settled = _barrierBegins; };
895
+ }
896
+ function _nsRetire(begin) {
897
+ for (const [k, entry] of _nsOwn) if (entry.settled !== null && entry.settled < begin) {
898
+ _nsOwn.delete(k);
899
+ __residentNamespaceOverlayDelta(-_nsOwnCost(k, entry));
900
+ }
901
+ }
902
+
903
+ /**
904
+ * The overlay's word on \`k\`: "absent", { alias } (look the table up at
905
+ * this key instead), { dir } (own directory), { hide } (the table knows
906
+ * nothing under an own fresh directory), or null (ask the table).
907
+ */
908
+ function _nsOwnView(k) {
909
+ if (_nsOwn.size === 0) return null;
910
+ const own = _nsOwn.get(k);
911
+ if (own) {
912
+ if (own.state === "absent" || own.state === "absentTree") return "absent";
913
+ if (own.state === "alias") return { alias: own.from };
914
+ return { dir: true };
915
+ }
916
+ for (let i = k.lastIndexOf("/"); i > 0; i = k.lastIndexOf("/", i - 1)) {
917
+ const above = _nsOwn.get(k.slice(0, i));
918
+ if (!above) continue;
919
+ if (above.state === "absent" || above.state === "absentTree") return "absent";
920
+ if (above.state === "alias") return { alias: above.from + k.slice(i) };
921
+ if (above.hide) return { hide: true };
922
+ }
923
+ return null;
924
+ }
925
+
926
+ /** The table key \`k\` denotes (overlay and symlinks applied), or null. */
927
+ function _nsRealKey(k) {
928
+ const own = _nsOwnView(k);
929
+ if (own === "absent" || (own && (own.dir || own.hide))) return null;
930
+ const found = __nsResolve(own && own.alias !== undefined ? own.alias : k, true);
931
+ return found && found !== "ELOOP" ? found.path : null;
932
+ }
933
+
934
+ function _nsRowMeta(row) {
935
+ const kind = Number(row.kind);
936
+ return {
937
+ type: kind === 1 ? "directory" : kind === 2 ? "symlink" : "file",
938
+ size: Number(row.size), mode: Number(row.mode), uid: Number(row.uid), gid: Number(row.gid),
939
+ atime: Number(row.atime), mtime: Number(row.mtime), ctime: Number(row.ctime),
940
+ };
941
+ }
942
+
943
+ /**
944
+ * Everything the namespace says about \`k\`: a stat record, "absent", or
945
+ * "ELOOP". Own pending writes and directories first, then the overlay,
946
+ * then the table, then content this process wrote that the table has not
947
+ * caught up with.
948
+ */
949
+ function _nsMeta(k, follow) {
950
+ if (k === "") return _nsRowMeta(__nsResolve("", true).row);
951
+ if (__vfsWrites && k in __vfsWrites && _denialCode(__vfsWrites[k]) === null) {
952
+ const size = _byteLen(__vfsWrites[k]);
953
+ if (_createdHere.has(k)) {
954
+ return { type: "file", size, mode: 0o100666 & ~__processUmask, uid: cred.uid, gid: cred.gid, own: true };
955
+ }
956
+ // A rewrite of a file that was there: its owner and mode stay what the
957
+ // authority says they are.
958
+ const found = __nsResolve(k, follow);
959
+ if (found && found !== "ELOOP" && Number(found.row.kind) === 0) return { ..._nsRowMeta(found.row), size };
960
+ }
961
+ const own = _nsOwnView(k);
962
+ if (own === "absent") return "absent";
963
+ if (own && own.dir) return { type: "directory", size: 0, mode: 0o40777 & ~__processUmask, uid: cred.uid, gid: cred.gid, own: true };
964
+ // A directory this process made hides what the namespace held under its
965
+ // name before, but not what this process has put there since: those rows
966
+ // are its own writes, recorded when the authority accepted them.
967
+ if (!own || own.alias !== undefined || (own.hide && _createdHere.has(k))) {
968
+ const found = __nsResolve(own && own.alias !== undefined ? own.alias : k, follow);
969
+ if (found === "ELOOP") return "ELOOP";
970
+ if (found) return _nsRowMeta(found.row);
971
+ }
972
+ // The namespace is exact, so a name it lacks is not there, whatever bytes
973
+ // the module map carries under it (a file removed since the map was
974
+ // built). Content never stands in for metadata.
975
+ return "absent";
976
+ }
977
+
978
+ /** Names directly under directory \`k\`: Map name → type. */
979
+ function _nsList(k) {
980
+ const names = new Map();
981
+ const own = _nsOwnView(k);
982
+ if (own !== "absent" && !(own && own.hide) && !(own && own.dir && _nsOwn.get(k)?.hide)) {
983
+ const real = __nsResolve(own && own.alias !== undefined ? own.alias : k, true);
984
+ if (real && real !== "ELOOP") {
985
+ for (const child of __nsChildren(real.path)) {
986
+ names.set(child.name, child.kind === 1 ? "directory" : child.kind === 2 ? "symlink" : "file");
987
+ }
988
+ }
989
+ }
990
+ const prefix = k ? k + "/" : "";
991
+ if (__vfsWrites) {
992
+ for (const wk in __vfsWrites) {
993
+ if (!wk.startsWith(prefix)) continue;
994
+ const rest = wk.slice(prefix.length);
995
+ const slash = rest.indexOf("/");
996
+ if (rest) names.set(slash < 0 ? rest : rest.slice(0, slash), slash < 0 ? "file" : "directory");
997
+ }
998
+ }
999
+ for (const [ok, entry] of _nsOwn) {
1000
+ if (!ok.startsWith(prefix) || ok.slice(prefix.length).includes("/")) continue;
1001
+ const name = ok.slice(prefix.length);
1002
+ if (entry.state === "absent" || entry.state === "absentTree") { if (!(__vfsWrites && ok in __vfsWrites)) names.delete(name); }
1003
+ else if (entry.state === "dir") names.set(name, "directory");
1004
+ else {
1005
+ const meta = _nsMeta(ok, false);
1006
+ if (meta !== "absent" && meta !== "ELOOP") names.set(name, meta.type);
1007
+ }
1008
+ }
1009
+ return names;
1010
+ }
1011
+
724
1012
  function _metadata(absPath) {
725
- const table = _metadataTable();
726
- return table ? table[_strip(absPath)] : undefined;
1013
+ _nsRequire("stat", absPath, "fs.promises.stat");
1014
+ const meta = _nsMeta(_strip(absPath), true);
1015
+ return meta === "absent" || meta === "ELOOP" ? undefined : meta;
727
1016
  }
728
1017
 
729
1018
  function _denialCode(cell) {
@@ -731,52 +1020,13 @@ const __fsMod = (() => {
731
1020
  typeof cell.error === "string" ? cell.error : null;
732
1021
  }
733
1022
 
734
- // Removal retracts the path from the sync existence view — __vfsMetadata
735
- // (spawn-time stat records) and __vfsManifest (directory shape). Without
736
- // it, existsSync keeps reporting a file this process already deleted and
737
- // the sync read path reports that file as merely non-resident.
1023
+ // Removal retracts the path from the sync existence view: the namespace's
1024
+ // overlay of this process's own effects says it is gone until a barrier's
1025
+ // delta does.
738
1026
  function _forgetSyncPath(k) {
1027
+ _createdHere.delete(k);
739
1028
  _announcedDirs.delete(k);
740
- const metadata = _metadataTable();
741
- if (metadata) delete metadata[k];
742
- if (!__vfsManifest) return;
743
- delete __vfsManifest[k];
744
- const slash = k.lastIndexOf("/");
745
- const parent = slash >= 0 ? k.slice(0, slash) : "";
746
- const name = slash >= 0 ? k.slice(slash + 1) : k;
747
- const siblings = __vfsManifest[parent];
748
- if (!siblings) return;
749
- const at = siblings.indexOf(name);
750
- if (at !== -1) siblings.splice(at, 1);
751
- }
752
-
753
- /**
754
- * Put a path into the sync EXISTENCE view — the inverse of _forgetSyncPath.
755
- *
756
- * A content cell is not existence. An invalidation drops content and is
757
- * documented to leave the name behind so existsSync cannot fabricate an
758
- * ENOENT for a file that was merely rewritten — but a file this process just
759
- * created has no manifest entry and no spawn-time stat record, so its cell
760
- * WAS the name, and evicting it retracted a file the program had written
761
- * itself. Recording the name where every other name lives is what makes that
762
- * documented asymmetry true.
763
- *
764
- * The parent's listing is the right home for it. Where the parent has no
765
- * listing the entry is created only for a directory this process made, since
766
- * that is the only case in which it knows the whole contents and calling the
767
- * directory enumerated is honest.
768
- */
769
- function _announceSyncPath(k) {
770
- if (!__vfsManifest || k === "") return;
771
- const slash = k.lastIndexOf("/");
772
- const parent = slash >= 0 ? k.slice(0, slash) : "";
773
- const name = slash >= 0 ? k.slice(slash + 1) : k;
774
- let siblings = __vfsManifest[parent];
775
- if (!Array.isArray(siblings)) {
776
- if (!__vfsDirs || !(parent in __vfsDirs)) return;
777
- siblings = __vfsManifest[parent] = [];
778
- }
779
- if (siblings.indexOf(name) === -1) siblings.push(name);
1029
+ _nsOwnSet(k, "absent");
780
1030
  }
781
1031
 
782
1032
  /**
@@ -785,41 +1035,67 @@ const __fsMod = (() => {
785
1035
  * content carried. One place, because every sync mutation owes all three.
786
1036
  */
787
1037
  function _parkWrite(k, cell) {
1038
+ _noteCreation(k);
788
1039
  __vfsWrites[k] = cell;
789
1040
  if (__vfsBundle) __vfsBundle[k] = cell;
790
1041
  delete __vfsBundleRevisions[k];
791
- _announceSyncPath(k);
1042
+ }
1043
+
1044
+ /**
1045
+ * The paths this process itself created, whose owner and mode are its own:
1046
+ * its uid and gid, and what its umask leaves. Every other stat answer comes
1047
+ * from the authority's metadata, and a path the authority never described
1048
+ * is not given one. A stat invented from the reader's own credential made a
1049
+ * root-owned file read as the reader's own and writable.
1050
+ *
1051
+ * A path counts as created when nothing was there before the write and the
1052
+ * view KNOWS nothing was there. Unknown is not created: a file the process
1053
+ * never staged may exist at the authority, owned by someone else.
1054
+ */
1055
+ const _createdHere = new Set();
1056
+ function _noteCreation(k) {
1057
+ if (_createdHere.has(k)) return;
1058
+ const absPath = "/" + k;
1059
+ // Known absent: the namespace names everything this credential can see.
1060
+ if (_statLadder(absPath) !== undefined) return;
1061
+ _createdHere.add(k);
1062
+ }
1063
+ function _forgetCreation(k) {
1064
+ const prefix = k + "/";
1065
+ for (const key of [..._createdHere]) if (key === k || key.startsWith(prefix)) _createdHere.delete(key);
792
1066
  }
793
1067
 
794
1068
  function _forgetSyncTree(k) {
795
1069
  const prefix = k + "/";
1070
+ _forgetCreation(k);
1071
+ _nsOwnSet(k, "absentTree");
796
1072
  for (const dir of _announcedDirs) if (dir.startsWith(prefix)) _announcedDirs.delete(dir);
797
- const metadata = _metadataTable();
798
- if (metadata) {
799
- for (const mk of Object.keys(metadata)) if (mk.startsWith(prefix)) delete metadata[mk];
800
- }
801
- if (__vfsManifest) {
802
- for (const dk of Object.keys(__vfsManifest)) if (dk.startsWith(prefix)) delete __vfsManifest[dk];
803
- }
804
1073
  _forgetSyncPath(k);
805
1074
  }
806
1075
 
807
1076
  /**
808
1077
  * The honest error for a sync read the resident view cannot serve. A facet
809
1078
  * has no synchronous I/O primitive, so a sync read is limited to content
810
- * staged into this process — but the sync existence view (__vfsMetadata +
811
- * __vfsManifest) knows far more paths than the content bundle carries.
812
- * ENOENT is only correct when the path is unknown there too; for a path
813
- * that demonstrably exists it sends the caller hunting for a missing file
814
- * instead of awaiting the read that would return it.
1079
+ * staged into this process, but the namespace names every path the
1080
+ * credential can see. ENOENT is correct only when it names none; for a path
1081
+ * that exists it would send the caller hunting for a missing file instead
1082
+ * of awaiting the read that would return it.
815
1083
  */
816
1084
  function _notResidentError(absPath, displayPath, syscall, asyncForm) {
817
1085
  const st = _statLadder(absPath);
818
- if (st === undefined) {
819
- if (!_absenceIsKnown(absPath)) _recordUnmapped(absPath, syscall);
820
- return _fsErr("ENOENT", syscall, displayPath);
821
- }
1086
+ if (st === undefined) return _fsErr("ENOENT", syscall, displayPath);
822
1087
  if (st.isDirectory()) return _fsErr("EISDIR", syscall, displayPath);
1088
+ // Unreadable to this credential: the namespace's mode says so, and no
1089
+ // bytes could have been staged for it.
1090
+ if (!_modeAllows(st, 4)) return _fsErr("EACCES", syscall, displayPath);
1091
+ if (typeof __residentStorageMiss === "function" && __residentStorageMiss(_strip(absPath))) {
1092
+ // The process's own write, which neither its store nor its heap budget
1093
+ // could hold: the workspace's storage is full.
1094
+ const full = _fsErr("ENOSPC", syscall, displayPath);
1095
+ full.message = "ENOSPC: workspace storage is full; '" + String(displayPath) + "' is readable asynchronously"
1096
+ + (_supervisor() ? " (" + asyncForm + ")" : "");
1097
+ return full;
1098
+ }
823
1099
  _recordResidencyMiss(absPath);
824
1100
  const err = _fsErr("EAGAIN", syscall, displayPath);
825
1101
  err.message += " — '" + String(displayPath) + "' exists but its content is not " +
@@ -953,7 +1229,9 @@ const __fsMod = (() => {
953
1229
  let meta;
954
1230
  // A thrown stat is the authority failing to answer, not an answer:
955
1231
  // nothing is learned, and the miss stands.
956
- try { meta = await __nimbusUseRpcResult(supervisor.stat(absPath), (r) => r); }
1232
+ // Queued on the path: the program's own later mutations of it must not overtake the stat.
1233
+ const observe = () => __nimbusUseRpcResult(supervisor.stat(absPath), (r) => r);
1234
+ try { meta = await (_hasVfsMutationQueue() ? __nimbusQueueVfsMutation(absPath, observe, false) : observe()); }
957
1235
  catch { return; }
958
1236
  if (meta === null || meta === undefined) { _observedAbsent.add(k); return; }
959
1237
  if (meta.type === "directory") return;
@@ -991,193 +1269,46 @@ const __fsMod = (() => {
991
1269
  }
992
1270
  };
993
1271
 
994
- /**
995
- * Has this directory's content been enumerated, or merely mentioned?
996
- *
997
- * __vfsManifest is a walk of SELECTED roots — the cwd subtree, its
998
- * node_modules, the entry script's own package — not of the filesystem. A
999
- * directory with an entry there was listed by that walk, so its child list
1000
- * is complete and a name that is not in it is genuinely not there. A
1001
- * directory with no entry was never opened, and knows nothing. __vfsDirs
1002
- * holds the directories this process created itself, which it therefore
1003
- * knows the contents of by construction.
1004
- *
1005
- * Conflating the two is what let a synchronous readdir of a real directory
1006
- * outside the walk answer with an empty array — not an error, not a refusal,
1007
- * a positive assertion that a populated directory is empty. Every caller
1008
- * that used to end in "not found, so absent" goes through here first.
1009
- */
1010
- function _dirEnumerated(k) {
1011
- return (!!__vfsManifest && k in __vfsManifest) || (!!__vfsDirs && k in __vfsDirs);
1012
- }
1013
1272
 
1014
- /** Does the content bundle itself describe children under this directory? */
1015
- function _bundleHasChildren(k) {
1016
- if (!__vfsBundle) return false;
1017
- return __residentAnyUnder(k ? k + "/" : "");
1018
- }
1019
1273
 
1020
- /**
1021
- * Can the view say this path is ABSENT, as opposed to say nothing at all?
1022
- *
1023
- * With no supervisor bound there is no authority to be ignorant of: the
1024
- * staged tables ARE the filesystem, the async form reads out of those same
1025
- * tables, and a name that is not in them is not anywhere. Ignorance is a
1026
- * property of the gap between a process and an authority it cannot reach
1027
- * synchronously, so where there is no authority there is no gap.
1028
- */
1029
- function _absenceIsKnown(absPath) {
1030
- if (!_supervisor()) return true;
1031
- const k = _strip(absPath);
1032
- if (k === "") return true;
1033
- const segments = k.split("/");
1034
- // Deepest enumerated ancestor, because that is the only one that can
1035
- // testify. Walking up rather than looking only at the immediate parent is
1036
- // what makes a whole missing subtree answerable from one listing: if
1037
- // $HOME was enumerated and holds no .config, then nothing under .config
1038
- // is there either, and a program probing three levels down deserves that
1039
- // answer rather than three refusals.
1040
- for (let i = segments.length - 1; i >= 0; i--) {
1041
- const dir = segments.slice(0, i).join("/");
1042
- if (!_dirEnumerated(dir)) continue;
1043
- const child = segments.slice(0, i + 1).join("/");
1044
- const children = (__vfsManifest && __vfsManifest[dir]) || [];
1045
- const present = children.indexOf(segments[i]) !== -1
1046
- || (!!__vfsDirs && child in __vfsDirs)
1047
- || (!!__vfsBundle && child in __vfsBundle);
1048
- // The component is not in a directory that was listed: absent, and so is
1049
- // everything below it. If it IS there, only its own enumeration settles
1050
- // what it contains, so nothing short of the immediate parent will do.
1051
- if (!present) return true;
1052
- return i === segments.length - 1;
1053
- }
1054
- return false;
1055
- }
1056
1274
 
1057
- /**
1058
- * A path the sync view never mapped: record the ignorance, and repair it.
1059
- *
1060
- * Sibling of _notResidentError and the same bargain — the file may well be
1061
- * there, the process simply cannot look without blocking. The callers answer
1062
- * ENOENT, which is provisional rather than fabricated: this records the miss
1063
- * and pulls in the listing that settles the question, and the exit report
1064
- * reads the ledger. If the path was genuinely absent the repair proves it
1065
- * and the entry retires — the program's not-found branch was the right
1066
- * branch and it reached it through the right errno. If the path WAS there,
1067
- * the entry survives and the run is failed by name.
1068
- *
1069
- * EAGAIN was tried here and is the wrong answer to hand a program even
1070
- * though it is the honest description of the situation. It cannot arise from
1071
- * a real POSIX filesystem, so nothing branches on it, and the catch block
1072
- * that receives it was written for a missing file and rethrows instead:
1073
- * measured, create-next-app reads $HOME/.config/<tool>/config.json through
1074
- * the conf package, is handed EAGAIN for a file that was never there, and
1075
- * dies before writing one template file. Read a config, treat ENOENT as "no
1076
- * config yet", rethrow anything else is the near-universal shape. Refusing
1077
- * bought nothing the ledger was not already delivering, and cost every
1078
- * program that idiom.
1079
- *
1080
- * A LISTING is the exception and still refuses — see readdirSync. An array is
1081
- * a complete enumeration by definition, so there is no provisional form of
1082
- * it: an empty one asserts that a directory nothing ever opened is empty,
1083
- * which is how a scaffolder was told its template directory held nothing,
1084
- * wrote nothing, and exited 0.
1085
- */
1086
- function _recordUnmapped(absPath, syscall) {
1087
- const k = _strip(absPath);
1088
- _recordMiss(k);
1089
- // A listing wants the path's own contents; a read wants its bytes. Both
1090
- // also want the boundary listing, which is the only thing that can prove
1091
- // the path was not there — and fetching one repair and then the other
1092
- // would refuse the same access twice, once for being unmapped and once for
1093
- // having no resident content. _faultOnce collapses them where they meet.
1094
- _faultInBoundary(k);
1095
- if (syscall === "scandir") { _faultInDirectory(k); return; }
1096
- _faultIn(k);
1097
- }
1098
1275
 
1099
- /**
1100
- * The shallowest directory on this path whose contents the view does not
1101
- * know: the child of the deepest ancestor that WAS enumerated.
1102
- *
1103
- * That, not the immediate parent, is the listing a refusal has to pull in.
1104
- * For a path that is not there the parent is usually not there either, so
1105
- * its readdir fails, the view learns nothing, and the ledger keeps an entry
1106
- * no repair can ever retire — the run is failed over a file that was simply
1107
- * absent. The boundary directory is different: the enumerated ancestor's own
1108
- * listing already proved it is present, so it can be read, and reading it
1109
- * either names the next component or proves the whole subtree absent.
1110
- *
1111
- * With nothing enumerated at all the boundary is the root, which is one
1112
- * listing away from making the next level answerable.
1113
- */
1114
- function _unknownBoundary(k) {
1115
- const segments = k.split("/");
1116
- for (let i = segments.length - 1; i >= 0; i--) {
1117
- if (_dirEnumerated(segments.slice(0, i).join("/"))) {
1118
- return segments.slice(0, i + 1).join("/");
1119
- }
1120
- }
1121
- return "";
1122
- }
1123
1276
 
1124
- function _faultInBoundary(k) {
1125
- _faultInDirectory(_unknownBoundary(k));
1126
- }
1127
1277
 
1128
- /**
1129
- * The refusal, for the two callers that have no provisional answer to give.
1130
- *
1131
- * A listing cannot say "not found" usefully, and neither can a write that
1132
- * needs the prior content it is splicing onto. Everywhere else the sync view
1133
- * answers ENOENT and lets the exit report settle whether that was true; here
1134
- * there is no branch the program could take that would be right, so the
1135
- * honest EAGAIN is also the useful one.
1136
- */
1137
- function _refuseUnmapped(absPath, displayPath, syscall, asyncForm) {
1138
- _recordUnmapped(absPath, syscall);
1139
- const err = _fsErr("EAGAIN", syscall, displayPath);
1140
- err.message += " — '" + String(displayPath) + "' lies outside the filesystem view "
1141
- + "staged into the process, so a synchronous call cannot tell what is there, "
1142
- + "and synchronous I/O cannot block to find out"
1143
- + (_supervisor() ? "; " + asyncForm + " answers from the live filesystem" : "");
1144
- return err;
1145
- }
1146
1278
 
1147
- /**
1148
- * Pull a directory listing in. _readdirAsync already records what it learns
1149
- * into __vfsManifest, so one call both answers the fault and converges the
1150
- * sync view on the async one — there is no second merge path to keep honest.
1151
- */
1152
- function _faultInDirectory(k) {
1153
- if (!_supervisor() || _dirEnumerated(k) || !_faultOnce("listing", k)) return;
1154
- try { _repairs.push(_readdirAsync("/" + k, undefined).catch(() => {})); }
1155
- catch { /* speculative */ }
1279
+
1280
+
1281
+ // What the authority says of a path after this process's own async request
1282
+ // (a stat, read or accepted write) is what the sync view answers for it
1283
+ // from then on, as it would after the delta that reports it
1284
+ // (facet-resident-store's __nsNoteLiveStat).
1285
+ //
1286
+ // The lstat rides the read batch (_queueBatchRequest) rather than a call
1287
+ // of its own: a resumption refetches every path it owes at once
1288
+ // (_acquireAndRefetch), and a learn per path made that one lstat call per
1289
+ // path — 1,601 in flight from one facet after a program wrote 1,600 files,
1290
+ // some of which stayed pending and never reached the session
1291
+ // (preview/new/lucide-barrel-cache-widens, measured 2026-09-28). Batched,
1292
+ // the learns cost a round trip per batch, as the reads before them do.
1293
+ async function _learnLive(absPath, supervisor) {
1294
+ if (!supervisor || typeof supervisor.fsReadBatch !== "function") return;
1295
+ let stat;
1296
+ try { stat = await _queueBatchRequest(supervisor, { path: absPath, lstat: true }, 0); } catch { return; }
1297
+ __nsNoteLiveStat(_strip(absPath), stat ?? null);
1156
1298
  }
1157
1299
 
1158
1300
  /**
1159
- * Retire the refusals a repair turned into honest absence.
1160
- *
1161
- * A refusal is only a wrong answer when the path was THERE and the process
1162
- * was denied it. Where the listing comes back and shows the name is simply
1163
- * not present, the program's not-found branch was the right branch all
1164
- * along — it merely reached it through the wrong errno — and failing the run
1165
- * over that would fail every module resolver, which probes dozens of paths
1166
- * that do not exist by design. So what stays in the ledger is exactly the
1167
- * harmful set: content that exists and was not served.
1301
+ * Retire the misses a repair turned into honest absence. A miss is a wrong
1302
+ * answer only when the path was THERE and the process was denied it: one on
1303
+ * a name the namespace does not have (or the authority did not have when it
1304
+ * was asked) was the program's not-found branch, taken for the right
1305
+ * reason. Failing the run over those would fail every module resolver,
1306
+ * which probes dozens of paths that do not exist by design.
1168
1307
  */
1169
1308
  function _settleProvenAbsences() {
1170
1309
  if (_residencyMisses.size === 0) return;
1171
1310
  for (const k of [..._residencyMisses]) {
1172
- const absPath = "/" + k;
1173
- // The authority did not have it when we asked, so nothing was withheld —
1174
- // whatever is at the path now, this process put there.
1175
- if (_observedAbsent.has(k)) { _residencyMisses.delete(k); continue; }
1176
- if (_statLadder(absPath) !== undefined) continue;
1177
- if (_absenceIsKnown(absPath)) { _residencyMisses.delete(k); continue; }
1178
- // Still unprovable, because the listing that would settle it lies below
1179
- // the one already fetched. Ask for the next one; the settle loop waits.
1180
- _faultInBoundary(k);
1311
+ if (_observedAbsent.has(k) || (_nsActive() && _statLadder("/" + k) === undefined)) _residencyMisses.delete(k);
1181
1312
  }
1182
1313
  }
1183
1314
 
@@ -1246,6 +1377,10 @@ const __fsMod = (() => {
1246
1377
 
1247
1378
  const _localTimes = globalThis.__nimbusVfsTimes || (globalThis.__nimbusVfsTimes = Object.create(null));
1248
1379
  const _localModes = globalThis.__nimbusVfsModes || (globalThis.__nimbusVfsModes = Object.create(null));
1380
+ // Modes set locally that the authority has not received yet. Delivered once:
1381
+ // re-sending on every flush made each read of the path a chmod, which bumped
1382
+ // its revision and evicted the process's own cell (create-astro EAGAIN).
1383
+ const _pendingModes = globalThis.__nimbusVfsPendingModes || (globalThis.__nimbusVfsPendingModes = new Set());
1249
1384
 
1250
1385
  function _coerceMode(value, syscall, p) {
1251
1386
  const n = typeof value === "string" ? parseInt(value, 8) : Number(value);
@@ -1270,6 +1405,8 @@ const __fsMod = (() => {
1270
1405
  atimeMs: _coerceTimeMs(atime, syscall, p),
1271
1406
  mtimeMs: _coerceTimeMs(mtime, syscall, p),
1272
1407
  };
1408
+ // The sync view answers these times ahead of the namespace's (_statObject)
1409
+ // until the utimes is reported back, as a write's own bytes are.
1273
1410
  _localTimes[k] = time;
1274
1411
  return time;
1275
1412
  }
@@ -1324,7 +1461,7 @@ const __fsMod = (() => {
1324
1461
  // ══ Cache coherence: ACQUIRE + read-through fill ═════════════════════
1325
1462
  //
1326
1463
  // The supervisor's SQLite VFS is the only authority. Everything in this
1327
- // facet — __vfsBundle, __vfsMetadata, __vfsManifest, __vfsDirs — is a
1464
+ // facet — the resident store, __vfsBundle, __vfsDirs — is a
1328
1465
  // cache of it, and __vfsWrites is this process's own not-yet-flushed
1329
1466
  // mutations.
1330
1467
  //
@@ -1352,6 +1489,24 @@ const __fsMod = (() => {
1352
1489
  // a frame arrives as a supervisor reply the same barrier
1353
1490
  // rides on (see the relayed WebSocket below).
1354
1491
  //
1492
+ // The supervisor's own deliveries are resumptions too. Each takes
1493
+ // __nimbusInboundBarrier before the program sees it, and each carries the
1494
+ // barrier's answer, computed by the supervisor after the thing delivered
1495
+ // was queued (protocol §3's piggybacked inv), so the barrier applies it
1496
+ // instead of asking and the delivery costs no round trip of its own:
1497
+ // - request: a request routed to one of the process's ports, in the
1498
+ // dispatch every resident's server shares (__nimbusServeHttp).
1499
+ // The supervisor starts it, so it holds no cursor of this
1500
+ // process's and answers "nothing since now", which stands in
1501
+ // only when no write preceded the request since this process
1502
+ // last caught up (protocol §9.2); otherwise the barrier asks.
1503
+ // - stdin: a stdin packet — input, its end, a signal, a resize — for an
1504
+ // attached process (__makeProcessStdin's pump).
1505
+ // - child: a spawned child's output and its exit (_runReadLoop,
1506
+ // _runWaitLoop).
1507
+ // Stdin and child output are long polls, so they send this process's
1508
+ // ACQUIRE arguments (_acquireArgs) and are answered from exactly those.
1509
+ //
1355
1510
  // Keep this list empty. An entry here is a documented hole in the owner's
1356
1511
  // invariant, not a TODO: it means some process can be woken by something
1357
1512
  // this system does not mediate, and its next synchronous read can serve
@@ -1373,8 +1528,18 @@ const __fsMod = (() => {
1373
1528
  // envelope — there is no reason to invent a second surface for it.
1374
1529
  const _stats = globalThis.__nimbusVfsCoherence
1375
1530
  || (globalThis.__nimbusVfsCoherence = {
1376
- fills: 0, filledBytes: 0, invalidations: 0, poisons: 0,
1531
+ fills: 0, filledBytes: 0, invalidations: 0, poisons: 0, pushes: 0,
1377
1532
  reconciles: 0, selfWrites: 0, misses: 0,
1533
+ // ACQUIREs that got no answer (see _acquireBarrier), and the last reason.
1534
+ barrierFailures: 0, lastBarrierFailure: "",
1535
+ // Barriers that held their resumption on an own write's acknowledgement
1536
+ // (_awaitReportedOwnWrites).
1537
+ ownWriteWaits: 0,
1538
+ // The namespace's failure state (a relist failed): synchronous calls
1539
+ // refused with its cause (_nsRequire), and barriers that repaired it.
1540
+ namespaceRefusals: 0, namespaceRepairs: 0,
1541
+ // Repairs a delta from the store's durable floor answered, with no listing.
1542
+ floorRepairs: 0,
1378
1543
  });
1379
1544
 
1380
1545
  /**
@@ -1407,11 +1572,122 @@ const __fsMod = (() => {
1407
1572
  let evicted = false;
1408
1573
  delete __vfsBundleRevisions[k];
1409
1574
  if (__vfsBundle && k in __vfsBundle) { delete __vfsBundle[k]; evicted = true; }
1410
- const metadata = _metadataTable();
1411
- if (metadata && k in metadata) { delete metadata[k]; evicted = true; }
1412
1575
  return evicted;
1413
1576
  }
1414
1577
 
1578
+ // Cells an own-mutation lease evicted with no barrier in hand.
1579
+ //
1580
+ // A barrier hands _acquireAndRefetch every path it dropped, which is what
1581
+ // keeps a sync read inside a timer callback reading the peer's new bytes
1582
+ // instead of EAGAIN. A lease's end drops a cell on its own account — the
1583
+ // barrier that would have reported it was suppressed by the lease itself
1584
+ // — so the same debt is recorded here and settled by the next untrusted
1585
+ // resumption.
1586
+ const _owedRefetch = new Set();
1587
+
1588
+ // An own-mutation lease can end in an eviction (a peer wrote inside the
1589
+ // window), and so can a flush whose revision a barrier's report outran.
1590
+ // The ledger owning those decisions is spliced AHEAD of this closure, so it
1591
+ // is handed the one eviction path rather than keeping a second copy of it:
1592
+ // content view, stat view, the invalidation count and the refetch debt
1593
+ // move together here or not at all.
1594
+ function _evictOwed(k) {
1595
+ if (!_evictResident(k)) return;
1596
+ _stats.invalidations++;
1597
+ _owedRefetch.add(k);
1598
+ }
1599
+ globalThis.__nimbusEvictResidentCell = _evictOwed;
1600
+
1601
+ /**
1602
+ * Live reads in flight, per path: each one's fill ticket, which carries
1603
+ * the highest revision a barrier reported for the path while that read was
1604
+ * outstanding.
1605
+ *
1606
+ * A barrier that reports a path nobody holds has nothing to evict and
1607
+ * CONSUMES the report: the cursor moves past it and no later delta names it
1608
+ * again. A read of that path already in flight may have been served before
1609
+ * the mutation the report describes, and its bytes would then be installed
1610
+ * behind the only message that could have evicted them. So the report is
1611
+ * kept on the read until it lands, and the read installs only if nothing
1612
+ * newer than its cursor was reported under it (_installResident).
1613
+ *
1614
+ * One ticket per read, not one per path: a read that begins after a report
1615
+ * or a poison is judged only by what is reported after it began. Sharing the
1616
+ * earlier read's record condemned the read issued to replace it.
1617
+ */
1618
+ const _fillReports = new Map();
1619
+
1620
+ /**
1621
+ * Begin a live read of \`k\`: the cursor it is issued under — every mutation
1622
+ * at or below it is already in the bytes it returns — and what has been
1623
+ * reported against it since, nothing yet. Called after the read's barrier
1624
+ * and before its RPC.
1625
+ */
1626
+ function _beginFill(k) {
1627
+ const fill = { k, rev: _cursor.rev, reported: -1 };
1628
+ let live = _fillReports.get(k);
1629
+ if (!live) {
1630
+ live = new Set();
1631
+ _fillReports.set(k, live);
1632
+ }
1633
+ live.add(fill);
1634
+ return fill;
1635
+ }
1636
+
1637
+ function _endFill(fill) {
1638
+ const live = _fillReports.get(fill.k);
1639
+ if (!live) return;
1640
+ live.delete(fill);
1641
+ if (live.size === 0) _fillReports.delete(fill.k);
1642
+ }
1643
+
1644
+ /** A barrier reported \`k\` at \`rev\`: remember it on every read of it in flight. */
1645
+ function _noteFillReport(k, rev) {
1646
+ const live = _fillReports.get(k);
1647
+ if (live) for (const fill of live) if (rev > fill.reported) fill.reported = rev;
1648
+ }
1649
+
1650
+ /**
1651
+ * Note one delta entry on everything in flight that it covers: its path,
1652
+ * or, for a subtree-scoped or structural entry, every path at or under it.
1653
+ * The ledger does the same for this facet's own writes and mutations.
1654
+ */
1655
+ function _noteReport(entry) {
1656
+ if (!entry.subtree && !entry.structural) {
1657
+ __nimbusNoteVfsReport(entry.path, entry.rev);
1658
+ _noteFillReport(entry.path, entry.rev);
1659
+ return;
1660
+ }
1661
+ __nimbusNoteVfsReportUnder(entry.path, entry.rev);
1662
+ const under = entry.path + "/";
1663
+ for (const k of [..._fillReports.keys()]) {
1664
+ if (k === entry.path || k.startsWith(under)) _noteFillReport(k, entry.rev);
1665
+ }
1666
+ }
1667
+
1668
+ /**
1669
+ * The resident cells a delta entry covers: its path, or, for a
1670
+ * subtree-scoped or structural entry, every cell at or under it. Such an
1671
+ * entry stands for changes it does not name (a directory that went, one
1672
+ * whose mode, owner or group changed, or something beneath it this facet
1673
+ * may not see), so each cell there is judged as a named one would be.
1674
+ */
1675
+ function _coveredCells(entry) {
1676
+ if (!entry.subtree && !entry.structural) return [entry.path];
1677
+ const under = entry.path + "/";
1678
+ const cells = new Set();
1679
+ for (const table of [__vfsBundle, __vfsBundleRevisions]) {
1680
+ if (!table) continue;
1681
+ for (const k of Object.keys(table)) if (k === entry.path || k.startsWith(under)) cells.add(k);
1682
+ }
1683
+ return [...cells];
1684
+ }
1685
+
1686
+ /** The cursor moved without a delta (a poison), so no read in flight can be dated. */
1687
+ function _spoilFills() {
1688
+ for (const live of _fillReports.values()) for (const fill of live) fill.reported = Infinity;
1689
+ }
1690
+
1415
1691
  /**
1416
1692
  * Install bytes the supervisor just served as the resident cell for a
1417
1693
  * path, at the cursor they were served under.
@@ -1424,42 +1700,30 @@ const __fsMod = (() => {
1424
1700
  * returned, so it has nothing to invalidate. The bytes are already paid
1425
1701
  * for; caching them costs one map insert.
1426
1702
  *
1703
+ * \`fill\` (_beginFill) dates them, and it is also the reason an install can
1704
+ * be declined: a barrier that reported the path above the read's cursor
1705
+ * while the read was in flight may describe a mutation the bytes predate,
1706
+ * and that report is gone once consumed. Declining costs the next sync
1707
+ * read a miss; installing would cost a stale byte nothing ever evicts.
1708
+ *
1427
1709
  * The parent's manifest entry gains the name too, so the existence view
1428
1710
  * cannot go on denying a file whose bytes this process is holding.
1429
1711
  */
1430
- function _installResident(absPath, bytes) {
1712
+ function _installResident(absPath, bytes, fill) {
1431
1713
  if (!__vfsBundle) return;
1432
1714
  const k = _strip(absPath);
1433
1715
  if (k === "") return;
1716
+ if (fill.reported > fill.rev) return;
1434
1717
  // Never over a cell this facet owns.
1435
1718
  //
1436
1719
  // A pending write is strictly newer than anything the authority can
1437
1720
  // report, and __vfsBundle is what sync reads consult first, so installing
1438
1721
  // over it would serve the program bytes older than its own write.
1439
- //
1440
- // A STAMPED cell is this facet's own flushed write, and the barrier this
1441
- // read passed on the way in reported nobody else has touched the path
1442
- // since — so the bytes are already what is being installed, and the only
1443
- // thing the install would change is to drop the stamp. It did: a
1444
- // speculative repair issued by an earlier refused read landed after the
1445
- // flush, unstamped the cell, and the next ACQUIRE evicted the facet's own
1446
- // output. Measured at selfWrites 0 / invalidations 1 / fills 2 for one
1447
- // written file, against 1 / 0 / 0 with the repair absent.
1448
1722
  if (__vfsWrites && k in __vfsWrites) return;
1449
- if (__vfsBundleRevisions[k] !== undefined) return;
1450
- __vfsBundle[k] = bytes;
1451
- // Keep the stat view consistent with the bytes now held. Without this
1452
- // the content view is fresh while statSync still reports the
1453
- // spawn-time length, so a program can read N bytes and be told the
1454
- // file is M — an inconsistency introduced BY the fill. Only the size
1455
- // is corrected: type, mode, ownership and timestamps stay as the
1456
- // authority last reported them, so mtime-based change detection
1457
- // (make, tsc --build, watchers) does not see every read as a change.
1458
- const metadata = _metadataTable();
1459
- if (metadata && k in metadata) {
1460
- metadata[k] = { ...metadata[k], size: _byteLen(bytes) };
1461
- }
1462
- _announceSyncPath(k);
1723
+ // The store dates the row at the read's cursor, so the next delta that
1724
+ // names the path above it evicts it. It declines, itself, to replace own
1725
+ // bytes or a row dated later than this read can vouch for.
1726
+ if (!__residentFill(k, bytes, fill.rev)) return;
1463
1727
  _stats.fills++;
1464
1728
  _stats.filledBytes += _byteLen(bytes);
1465
1729
  }
@@ -1495,25 +1759,86 @@ const __fsMod = (() => {
1495
1759
  return _residentRepair;
1496
1760
  }
1497
1761
 
1762
+ /**
1763
+ * Set while the store holds only what a repair could NOT vouch for: rows it
1764
+ * had to drop rather than prove current. Nothing but another repair brings
1765
+ * those back — a delta names what changed, not what this facet lost — so
1766
+ * every barrier repairs until one vouches, however the ACQUIRE is answered.
1767
+ */
1768
+ let _storeRepairOwed = false;
1769
+
1770
+ /**
1771
+ * The repair's first try: the delta from the store's durable floor
1772
+ * (__residentCursor), asked as every barrier asks it. True when it applied
1773
+ * (the repair is done); false when a listing is needed after all.
1774
+ */
1775
+ async function _repairFromFloor(supervisor, result) {
1776
+ if (!supervisor || typeof supervisor.fsAcquire !== "function" || !_nsActive()) return false;
1777
+ if (result !== null && result.poison === true) return false;
1778
+ const floor = __residentCursor();
1779
+ if (floor === null || (result !== null && result.epoch !== floor.epoch)) return false;
1780
+ let delta;
1781
+ try {
1782
+ delta = await __nimbusUseRpcResult(supervisor.fsAcquire(floor.epoch, floor.rev, { namespace: true }), (r) => r);
1783
+ } catch { return false; }
1784
+ if (!delta || delta.poison === true || typeof delta.rev !== "number" || delta.epoch !== floor.epoch) return false;
1785
+ if (Array.isArray(delta.paths)) for (const entry of delta.paths) _noteReport(entry);
1786
+ await __residentRoomForPushed(delta);
1787
+ const applied = __residentAdmit(delta);
1788
+ for (const dir of applied.relist) await __nsRelist(supervisor, dir);
1789
+ if (!_nsActive()) return false;
1790
+ _cursor.epoch = applied.cursor.epoch;
1791
+ _cursor.rev = applied.cursor.rev;
1792
+ _stats.invalidations += applied.dropped.length;
1793
+ // What it should hold and lost (dropped by this delta, or by the barrier
1794
+ // that could not vouch for its rows), refetched by the namespace's stats.
1795
+ const refilled = await __residentRefillFromNamespace(supervisor, applied.dropped);
1796
+ _stats.fills += refilled.filled;
1797
+ _stats.filledBytes += refilled.bytes;
1798
+ _stats.floorRepairs++;
1799
+ _storeRepairOwed = false;
1800
+ return true;
1801
+ }
1802
+
1803
+ /**
1804
+ * \`result\` is the ACQUIRE answer that asked for the repair: a poison, a
1805
+ * delta arriving while a repair is owed, or null for a barrier that got no
1806
+ * answer at all.
1807
+ */
1498
1808
  async function _runResidentRepair(supervisor, result) {
1809
+ // Within one epoch, with the change log intact and the namespace
1810
+ // answering, the store's rows are dated through its own durable cursor, so
1811
+ // the delta from there names everything that moved since. Only an epoch
1812
+ // change, a poison (the log truncated past the cursor), or a namespace
1813
+ // that stopped answering needs the listing.
1814
+ if (await _repairFromFloor(supervisor, result)) return;
1815
+ const ownAtStart = __residentOwnPaths();
1499
1816
  let repaired;
1500
1817
  try { repaired = await __residentSynchronizeFromSupervisor(supervisor); }
1501
1818
  catch { repaired = null; }
1502
1819
  if (!repaired || !repaired.cursor) {
1503
1820
  // The pass could not vouch for a single row — a supervisor replaced
1504
- // mid-enumeration, an enumeration that came back short. A row that
1505
- // cannot be dated must not be served, so the cold cache the poison asked
1506
- // for is taken after all, and the pass runs once more to repopulate what
1507
- // it just dropped.
1508
- __residentAdmit(result);
1509
- _cursor.epoch = result.epoch;
1510
- _cursor.rev = result.rev;
1821
+ // mid-enumeration, an enumeration that came back short, an authority it
1822
+ // could not reach. A row that cannot be dated must not be served, so the
1823
+ // cold cache is taken after all, and the pass runs once more to
1824
+ // repopulate what it just dropped. An answer with a cursor is admitted as
1825
+ // it would have been; a barrier with no answer drops every dated row and
1826
+ // keeps the cursor it had, which the next delta is still owed from.
1827
+ if (result) {
1828
+ __residentAdmit(result);
1829
+ _cursor.epoch = result.epoch;
1830
+ _cursor.rev = result.rev;
1831
+ } else {
1832
+ __residentDropDated();
1833
+ }
1511
1834
  try { repaired = await __residentSynchronizeFromSupervisor(supervisor); }
1512
1835
  catch { repaired = null; }
1513
1836
  } else if (repaired.reconciled) {
1514
1837
  _stats.reconciles++;
1515
1838
  }
1516
- if (!repaired || !repaired.cursor) return;
1839
+ _settleSkippedReports(ownAtStart, repaired);
1840
+ _storeRepairOwed = !(repaired && repaired.cursor);
1841
+ if (_storeRepairOwed) return;
1517
1842
  _cursor.epoch = repaired.cursor.epoch;
1518
1843
  _cursor.rev = repaired.cursor.rev;
1519
1844
  _stats.invalidations += repaired.dropped;
@@ -1521,6 +1846,41 @@ const __fsMod = (() => {
1521
1846
  _stats.filledBytes += repaired.bytes;
1522
1847
  }
1523
1848
 
1849
+ /**
1850
+ * Deliver the reports a repair moved the cursor over.
1851
+ *
1852
+ * A repair moves the cursor without a delta, so what a delta would have
1853
+ * reported reaches nothing on its own. A dated row the pass kept is proven
1854
+ * current by its listed revision and needs nothing more. A row of this
1855
+ * facet's own bytes is kept whatever the listing says — and its report is
1856
+ * exactly what the write or mutation that owns it needs, to judge its own
1857
+ * acknowledgement against. The pass hands those back (\`own\`), and they are
1858
+ * noted here as a delta's would have been; one acknowledged since the pass
1859
+ * looked was dated by a receipt that could not see the report, so it is
1860
+ * judged here directly.
1861
+ *
1862
+ * With nothing datable — across incarnations, against a short listing, or
1863
+ * a pass that failed outright — every own row, and every row that was own
1864
+ * when the repair began, is judged as though a peer wrote it last: an
1865
+ * eviction and a refetch, never a stale byte.
1866
+ */
1867
+ function _settleSkippedReports(ownAtStart, repaired) {
1868
+ const reports = new Map();
1869
+ if (repaired && repaired.cursor && repaired.reconciled && Array.isArray(repaired.own)) {
1870
+ for (const entry of repaired.own) reports.set(entry.path, entry.rev);
1871
+ } else {
1872
+ for (const k of ownAtStart) reports.set(k, Infinity);
1873
+ if (repaired && Array.isArray(repaired.own)) for (const entry of repaired.own) reports.set(entry.path, Infinity);
1874
+ for (const k of Object.keys(__vfsOwnLeases)) reports.set(k, Infinity);
1875
+ for (const k of Object.keys(__vfsWrites)) reports.set(k, Infinity);
1876
+ }
1877
+ for (const [k, rev] of reports) {
1878
+ __nimbusNoteVfsReport(k, rev);
1879
+ const provenance = __residentProvenance(k);
1880
+ if (provenance !== undefined && provenance !== -1 && provenance < rev) _evictOwed(k);
1881
+ }
1882
+ }
1883
+
1524
1884
  /**
1525
1885
  * ACQUIRE. Apply every invalidation the supervisor has for this facet,
1526
1886
  * then re-stamp the cursor.
@@ -1546,60 +1906,247 @@ const __fsMod = (() => {
1546
1906
  * left. Comparing revisions rather than names keeps the peer case intact:
1547
1907
  * a peer writing the same path afterwards reports a HIGHER revision than
1548
1908
  * our stamp, so that invalidation still lands.
1909
+ *
1910
+ * An ACQUIRE that gets no answer is a poison too, never an empty delta. It
1911
+ * has learned nothing about what changed, and the resumption behind it is
1912
+ * about to serve every row it holds. The call is a pure read, so a dropped
1913
+ * one is already retried where the supervisor makes it (SupervisorRPC,
1914
+ * fabric's idempotent), and what arrives here failed those retries, came
1915
+ * from a host that could not serve it, or answered without a cursor. The
1916
+ * rows are repaired against the listing exactly as for a poison; with no
1917
+ * cursor to adopt, the one held is kept and still owed every change since.
1918
+ * The barrier itself never throws: the operation or callback behind it
1919
+ * runs either way, against rows that were vouched for or a colder cache.
1920
+ *
1921
+ * A repair is single-flight, and while one runs no delta may be admitted
1922
+ * at all: the repair has already dropped the rows it could not vouch for,
1923
+ * so a report of one of them finds nothing to evict and is consumed, and
1924
+ * the repair then installs it at its listing's revision and publishes the
1925
+ * listing's cursor over the one the delta advanced. So any barrier answered
1926
+ * while a repair runs joins it, whether its own answer needed a repair or
1927
+ * was an ordinary delta, and the listing may predate that answer — the rows
1928
+ * it vouches for are another barrier's answer, not this one's — so a joiner
1929
+ * waits for it and then asks again: a delta from the repaired cursor names
1930
+ * everything since. Only the barrier that started a repair resumes on it
1931
+ * alone.
1932
+ *
1933
+ * \`delivered\` is an answer the supervisor delivered with the resumption
1934
+ * this barrier is taken for; it stands in for the first ACQUIRE when it can
1935
+ * (_deliveredAnswer). Everything after is the same for an answer delivered
1936
+ * and one asked for, so a repair, a join or a poison follows exactly the
1937
+ * path it always has.
1938
+ *
1939
+ * Last, the barrier waits out every own write or mutation that a report —
1940
+ * this answer's or an earlier one's — is outstanding against, until its
1941
+ * acknowledgement says which came first (__nimbusReportedOwnAcknowledgements).
1942
+ * Until then the resumption could run on own bytes the authority applied
1943
+ * before a peer overwrote them. Nothing is waited on unless such a report
1944
+ * exists.
1549
1945
  */
1550
- async function _acquireBarrier(supervisor) {
1946
+ async function _acquireBarrier(supervisor, delivered) {
1551
1947
  if (!supervisor || typeof supervisor.fsAcquire !== "function") return [];
1552
- let result;
1553
- // Through the RPC helper like every other supervisor call, because it IS
1554
- // one: the barrier is the first thing an async read issues, and while it
1555
- // was uncounted __nimbusPendingOps read zero for a whole round trip. A
1556
- // one-shot facet's event loop, which exits when no handle is live, then
1557
- // ended the program mid-read — measured as an fs.promises.readFile whose
1558
- // .then never ran, no error, no output, intermittently, and never when a
1559
- // pending timer happened to hold the program open.
1560
- try { result = await __nimbusUseRpcResult(supervisor.fsAcquire(_cursor.epoch, _cursor.rev), (r) => r); }
1561
- catch { return []; }
1562
- if (!result || typeof result.rev !== "number") return [];
1563
- // Paths that held content before this eviction are the ones worth
1564
- // refetching at a resumption boundary — a later sync read of any of them
1565
- // would otherwise miss. Collected before the delete so the membership
1566
- // test is against the pre-eviction bundle.
1567
- const wereResident = [];
1948
+ const fromDelivery = _deliveredAnswer(delivered);
1949
+ // The overlay of this facet's own structural effects retires what settled
1950
+ // before the answer was asked for: a delivered answer was asked for when
1951
+ // its arguments were built, a fetched one now.
1952
+ const begin = fromDelivery ? _argsBegin(delivered.args) : ++_barrierBegins;
1953
+ let result = fromDelivery || await _acquire(supervisor);
1568
1954
  // When the resident set lives in the facet's own SQLite, the STORE applies
1569
1955
  // the delta. That is not an optimisation, it is where provenance has to
1570
1956
  // live: a facet's SQLite outlives its module scope, so a new incarnation
1571
1957
  // opens onto rows a previous one wrote while every heap-side stamp that
1572
1958
  // described them is gone. Rows carry their own revision, __vfsBundleRevisions
1573
1959
  // cannot follow them there, and two provenance stores would be one too many.
1574
- if (_residentStorePresent()) {
1575
- if (result.poison) {
1576
- _stats.poisons++;
1577
- await _repairPoisonedStore(supervisor, result);
1578
- // Nothing is returned because a dropped cell is either refetched by
1579
- // the repair or gone from the authority too.
1580
- return [];
1960
+ {
1961
+ // A namespace that stopped answering (a relist failed) is a failure
1962
+ // state, not a mode: every barrier the process reaches repairs it, once,
1963
+ // until a listing restores it.
1964
+ let namespaceAsked = false;
1965
+ for (let joins = 0; ; joins++) {
1966
+ const joining = _residentRepair !== null;
1967
+ const namespaceOwed = !namespaceAsked && !_nsActive();
1968
+ // A sealed heap view can release acknowledged overlays before its
1969
+ // listing; otherwise the old overlay and replacement rows would
1970
+ // consume the same scarce allowance while preventing each other's
1971
+ // repair. Nothing synchronous may consult the sealed view here.
1972
+ if (namespaceOwed && __residentInHeap) _nsRetire(begin);
1973
+ const needsRepair = result === null || result.poison === true || _storeRepairOwed || namespaceOwed;
1974
+ if (!joining && !needsRepair) break;
1975
+ if (namespaceOwed) { namespaceAsked = true; _stats.namespaceRepairs++; }
1976
+ if (needsRepair) {
1977
+ if (result !== null && result.poison === true) _stats.poisons++;
1978
+ _spoilFills();
1979
+ }
1980
+ await (joining ? _residentRepair : _repairPoisonedStore(supervisor, result));
1981
+ // A repair's listing is walked page by page at successive revisions,
1982
+ // so even the barrier that started it asks once more: the delta from
1983
+ // the listing's cursor leaves the namespace exact. Unanswered, it
1984
+ // resumes on the repair's rows as they are, which the listing vouched
1985
+ // for; the namespace then waits for the next barrier.
1986
+ if (!joining) {
1987
+ if (_storeRepairOwed) return [];
1988
+ result = await _acquire(supervisor);
1989
+ if (result === null || result.poison === true) { _nsRetire(begin); return []; }
1990
+ continue;
1991
+ }
1992
+ if (joins + 1 >= _REPAIR_JOINS_MAX) {
1993
+ // Every repair this barrier waited on was someone else's. Rather
1994
+ // than resume on rows none of them vouched for as of its own
1995
+ // answer, it drops them and leaves the repair owed: a colder cache,
1996
+ // never a stale byte. Any repair still to finish lists after this.
1997
+ __residentDropDated();
1998
+ _storeRepairOwed = true;
1999
+ __nimbusNoteUnnamedReports();
2000
+ await _awaitReportedOwnWrites();
2001
+ return [];
2002
+ }
2003
+ result = await _acquire(supervisor);
1581
2004
  }
2005
+ // The store keeps a row of this facet's own bytes over any report, so
2006
+ // the report is noted first: the write or mutation that owns the row
2007
+ // judges it against its own revision when that arrives, as it does for
2008
+ // a heap cell. A read in flight gets the same note.
2009
+ if (Array.isArray(result.paths)) {
2010
+ for (const entry of result.paths) _noteReport(entry);
2011
+ }
2012
+ // N18: room for what it pushes, before it is held.
2013
+ await __residentRoomForPushed(result);
1582
2014
  const applied = __residentAdmit(result);
1583
- _cursor.epoch = result.epoch;
1584
- _cursor.rev = result.rev;
2015
+ _cursor.epoch = applied.cursor.epoch;
2016
+ _cursor.rev = applied.cursor.rev;
2017
+ // A directory that became searchable has descendants no delta names.
2018
+ for (const dir of applied.relist) await __nsRelist(supervisor, dir);
2019
+ _nsRetire(begin);
1585
2020
  _stats.invalidations += applied.dropped.length;
2021
+ _stats.selfWrites += applied.kept;
2022
+ _stats.pushes += applied.pushed || 0;
2023
+ await _awaitReportedOwnWrites();
1586
2024
  return applied.dropped;
1587
2025
  }
1588
- if (result.poison) {
1589
- if (__vfsBundle) for (const k of Object.keys(__vfsBundle)) { wereResident.push(k); _evictResident(k); }
1590
- _stats.poisons++;
1591
- } else if (Array.isArray(result.paths)) {
1592
- for (const entry of result.paths) {
1593
- const k = entry.path;
1594
- if (__vfsBundleRevisions[k] >= entry.rev) { _stats.selfWrites++; continue; }
1595
- const held = !!(__vfsBundle && k in __vfsBundle);
1596
- if (_evictResident(k)) _stats.invalidations++;
1597
- if (held) wereResident.push(k);
2026
+ }
2027
+
2028
+ /**
2029
+ * Hold the resumption behind a barrier on every own write or mutation a
2030
+ * report is outstanding against, until its acknowledgement says which came
2031
+ * first (__nimbusReportedOwnAcknowledgements). Counted, because the wait is
2032
+ * also paid when the report was the write itself coming back, which the
2033
+ * facet cannot tell apart until the acknowledgement lands.
2034
+ */
2035
+ async function _awaitReportedOwnWrites() {
2036
+ const acks = __nimbusReportedOwnAcknowledgements();
2037
+ if (acks === null) return;
2038
+ _stats.ownWriteWaits++;
2039
+ await acks;
2040
+ }
2041
+
2042
+ /**
2043
+ * Repairs someone else started that one barrier waits on, in a row, before
2044
+ * it stops asking again and drops what it cannot vouch for. A bound on the
2045
+ * barrier's own liveness, not a tuning knob: each join waits a whole repair,
2046
+ * so the fourth means repairs keep being started under it.
2047
+ */
2048
+ const _REPAIR_JOINS_MAX = 4;
2049
+
2050
+ /**
2051
+ * What this facet asks its ACQUIRE with: its cursor. Built in one place, so
2052
+ * what a delivery is answered from (a long poll sends these,
2053
+ * \`__nimbusVfsAcquireArgs\`) is exactly what fsAcquire would be asked.
2054
+ */
2055
+ function _acquireArgs() {
2056
+ // Every build is the start of an answer the overlay may retire against.
2057
+ const begin = ++_barrierBegins;
2058
+ const key = _cursor.epoch + "@" + _cursor.rev;
2059
+ if (!_argsBegins.has(key)) _argsBegins.set(key, begin);
2060
+ const options = _residentStorePresent() && typeof __residentAcquireOptions === "function"
2061
+ ? __residentAcquireOptions() : undefined;
2062
+ return options
2063
+ ? { epoch: _cursor.epoch, cursor: _cursor.rev, options }
2064
+ : { epoch: _cursor.epoch, cursor: _cursor.rev };
2065
+ }
2066
+ /**
2067
+ * The earliest begin of any arguments built at a cursor: a delivered answer
2068
+ * may answer any of them, and retiring against the earliest is the one
2069
+ * that is never too late. Cursors left behind are forgotten.
2070
+ */
2071
+ const _argsBegins = new Map();
2072
+ function _argsBegin(args) {
2073
+ const key = args.epoch + "@" + args.cursor;
2074
+ const begin = _argsBegins.get(key) ?? 0;
2075
+ for (const k of [..._argsBegins.keys()]) {
2076
+ const [epoch, rev] = [k.slice(0, k.lastIndexOf("@")), Number(k.slice(k.lastIndexOf("@") + 1))];
2077
+ if (epoch !== _cursor.epoch || rev < _cursor.rev) _argsBegins.delete(k);
2078
+ }
2079
+ return begin;
2080
+ }
2081
+
2082
+ /**
2083
+ * One ACQUIRE: the authority's answer, or null when there is none — the
2084
+ * call failed past the supervisor's own retries, the host could not serve
2085
+ * it, or the answer carried no cursor. A null is counted where the
2086
+ * coherence stats are read, and is never an empty delta (_acquireBarrier).
2087
+ */
2088
+ async function _acquire(supervisor) {
2089
+ // Through the RPC helper like every other supervisor call, because it IS
2090
+ // one: the barrier is the first thing an async read issues, and while it
2091
+ // was uncounted __nimbusPendingOps read zero for a whole round trip. A
2092
+ // one-shot facet's event loop, which exits when no handle is live, then
2093
+ // ended the program mid-read — measured as an fs.promises.readFile whose
2094
+ // .then never ran, no error, no output, intermittently, and never when a
2095
+ // pending timer happened to hold the program open.
2096
+ try {
2097
+ const args = _acquireArgs();
2098
+ const result = await __nimbusUseRpcResult(
2099
+ args.options ? supervisor.fsAcquire(args.epoch, args.cursor, args.options) : supervisor.fsAcquire(args.epoch, args.cursor),
2100
+ (r) => r,
2101
+ );
2102
+ if (!result || typeof result.rev !== "number" || typeof result.epoch !== "string") {
2103
+ throw new Error("fsAcquire answered without a cursor");
1598
2104
  }
2105
+ return _currentAnswer(result);
2106
+ } catch (error) {
2107
+ _stats.barrierFailures++;
2108
+ _stats.lastBarrierFailure = (error && error.message) || String(error);
2109
+ return null;
1599
2110
  }
1600
- _cursor.epoch = result.epoch;
1601
- _cursor.rev = result.rev;
1602
- return wereResident;
2111
+ }
2112
+
2113
+ /**
2114
+ * An answer as it stands against this facet's cursor now.
2115
+ *
2116
+ * Answers can be admitted in another order than they were given: an
2117
+ * answer a long poll delivers was asked for before the barrier that
2118
+ * overtook it. Everything at or below the cursor is admitted already, by
2119
+ * whichever answer took the cursor past it — an answer is complete from
2120
+ * the cursor it was asked from, which is never ahead of the cursor now, and
2121
+ * names each path at its newest revision in its range. So only the entries
2122
+ * above the cursor are news, and the cursor never moves back. An answer in
2123
+ * another epoch, or a poison, is left as it is.
2124
+ */
2125
+ function _currentAnswer(result) {
2126
+ if (result.poison === true || result.epoch !== _cursor.epoch) return result;
2127
+ if (result.rev <= _cursor.rev) return { ...result, rev: _cursor.rev, paths: [] };
2128
+ const paths = result.paths.filter((entry) => entry.rev > _cursor.rev);
2129
+ return paths.length === result.paths.length ? result : { ...result, paths };
2130
+ }
2131
+
2132
+ /**
2133
+ * The answer a delivery carried (session/rpc.ts _acquireOnDelivery), as it
2134
+ * stands against the cursor now, or null when it cannot stand in for
2135
+ * asking. It can when it answers from a cursor this facet has reached, in
2136
+ * this epoch: everything at or below that cursor is admitted, and the
2137
+ * answer names everything after it up to the moment of delivery. The
2138
+ * barrier asks, as it always has, when there is no answer or it is a
2139
+ * poison, in another epoch, or from a cursor this facet has not reached — a
2140
+ * routed request is answered from the supervisor's own cursor, "nothing
2141
+ * since now", which stands in only for a facet that is already there.
2142
+ */
2143
+ function _deliveredAnswer(delivered) {
2144
+ if (!delivered || typeof delivered !== "object") return null;
2145
+ const { args, answer } = delivered;
2146
+ if (!args || !answer || answer.poison === true || !Array.isArray(answer.paths)) return null;
2147
+ if (typeof answer.rev !== "number" || answer.epoch !== _cursor.epoch || args.epoch !== _cursor.epoch) return null;
2148
+ if (!(args.cursor <= _cursor.rev)) return null;
2149
+ return _currentAnswer(answer);
1603
2150
  }
1604
2151
 
1605
2152
  /**
@@ -1621,11 +2168,81 @@ const __fsMod = (() => {
1621
2168
  * This closes staleness of RESIDENT cells across an untrusted resumption.
1622
2169
  * A first synchronous touch of a NON-resident path is the separate
1623
2170
  * residency floor and is unaffected — it still raises EAGAIN, correctly.
2171
+ *
2172
+ * Two resumptions can barrier at once — a child's output and its exit
2173
+ * arrive together — and both deltas name the same changed path. The first
2174
+ * to land evicts it and starts the refetch; the second finds no row and
2175
+ * would release its callback while that refetch is still in flight, onto
2176
+ * a miss. So a barrier also waits for every refetch still in flight.
2177
+ * It does not read those paths again. A refetch in flight installs its
2178
+ * bytes unless a barrier has since reported the path above the cursor it
2179
+ * was issued under, or a poison spoiled it (_installResident). Only then
2180
+ * does a later barrier read the path itself, and only if nothing holds it
2181
+ * by now. Re-reading every in-flight path at every resumption cost a dev
2182
+ * server 50 reads per request while a peer's refetch of 50 files ran: 500
2183
+ * reads over ten resumptions, and under steady load the set never drained.
2184
+ */
2185
+ async function _acquireAndRefetch(supervisor, delivered) {
2186
+ const stale = await _acquireBarrier(supervisor, delivered);
2187
+ // Plus what an own-mutation lease dropped since the last resumption:
2188
+ // the same debt this function exists to settle, owed by an eviction
2189
+ // that had no barrier to report it (see _owedRefetch).
2190
+ if (_owedRefetch.size > 0) {
2191
+ for (const k of _owedRefetch) if (stale.indexOf(k) === -1) stale.push(k);
2192
+ _owedRefetch.clear();
2193
+ }
2194
+ // Taken before this barrier's own refetches join the map.
2195
+ const inFlight = [..._refetching];
2196
+ const reads = stale.map(_refetch);
2197
+ for (const [k, refetch] of inFlight) {
2198
+ if (stale.indexOf(k) !== -1) continue;
2199
+ if (!(refetch.fill.reported > refetch.fill.rev)) reads.push(refetch.done);
2200
+ else if (!(__vfsBundle && k in __vfsBundle)) reads.push(_refetch(k));
2201
+ }
2202
+ if (reads.length > 0) await Promise.all(reads);
2203
+ }
2204
+
2205
+ /**
2206
+ * Refetches in flight, per path: the newest read of it, and the fill
2207
+ * ticket that read installs under — the cursor it was issued at, and the
2208
+ * reports noted against it since.
1624
2209
  */
1625
- async function _acquireAndRefetch(supervisor) {
1626
- const stale = await _acquireBarrier(supervisor);
1627
- if (stale.length === 0) return;
1628
- await Promise.all(stale.map((k) => _liveReadFile("/" + k, undefined, true).catch(() => {})));
2210
+ const _refetching = new Map();
2211
+
2212
+ /**
2213
+ * Read \`k\` live and install it, behind the barrier just taken. Settles
2214
+ * either way: a refetch that fails leaves the path missing, which the next
2215
+ * read of it answers.
2216
+ *
2217
+ * \`read\` settles once this read has landed and its install was made or
2218
+ * declined. \`done\`, which the resumptions wait on, may wait once more. An
2219
+ * install is declined when a barrier reported the path above this read's
2220
+ * cursor, or a poison spoiled it, and settling there would release every
2221
+ * waiter onto the miss it left while something else is already filling
2222
+ * the path: the newer refetch a barrier issued to replace this one, or the
2223
+ * repair that poison started. So \`done\` waits on what is in flight at the
2224
+ * moment this read settles, that refetch's own read and install and that
2225
+ * repair, and then settles whether or not the path is held. It never
2226
+ * follows a refetch issued after that. A peer that rewrites the path
2227
+ * faster than one read round trip outdates every refetch with the next
2228
+ * resumption's report, and following them held every resumption, including
2229
+ * those that never read the path, until the writer stopped (1528 ms against
2230
+ * 33 ms). A path left unheld is read live by its next read.
2231
+ */
2232
+ function _refetch(k) {
2233
+ const fill = _beginFill(k);
2234
+ const refetch = { fill, read: null, done: null };
2235
+ refetch.read = _liveReadFile("/" + k, undefined, fill).then(() => {}, () => {}).then(() => {
2236
+ _endFill(fill);
2237
+ if (_refetching.get(k) === refetch) _refetching.delete(k);
2238
+ });
2239
+ refetch.done = refetch.read.then(() => {
2240
+ if (__vfsBundle && k in __vfsBundle) return undefined;
2241
+ const newer = _refetching.get(k);
2242
+ return Promise.all([newer && newer.read, _residentRepair]);
2243
+ });
2244
+ _refetching.set(k, refetch);
2245
+ return refetch.done;
1629
2246
  }
1630
2247
 
1631
2248
  /**
@@ -1637,14 +2254,15 @@ const __fsMod = (() => {
1637
2254
  * its fetch handler; binding the supervisor when the wrappers are
1638
2255
  * installed would silently leave every resident-TUI timer unbarriered.
1639
2256
  *
1640
- * Published on globalThis because the two other untrusted resumptions —
1641
- * an outbound fetch response and a relayed socket frame — are delivered
1642
- * by code outside this closure. One barrier, three boundaries.
2257
+ * Published on globalThis because the other resumptions — an outbound
2258
+ * fetch response, a relayed socket frame, and everything the supervisor
2259
+ * delivers (\`__nimbusInboundBarrier\`, with the answer it delivered) — are
2260
+ * handed to the program by code outside this closure.
1643
2261
  */
1644
- async function _resumptionAcquire() {
2262
+ async function _resumptionAcquire(delivered) {
1645
2263
  const supervisor = _supervisor();
1646
2264
  if (!supervisor || typeof supervisor.fsAcquire !== "function") return;
1647
- await _acquireAndRefetch(supervisor);
2265
+ await _acquireAndRefetch(supervisor, delivered);
1648
2266
  }
1649
2267
 
1650
2268
  /**
@@ -1678,6 +2296,7 @@ const __fsMod = (() => {
1678
2296
  if (globalThis.__nimbusResumptionBarriersInstalled) return;
1679
2297
  globalThis.__nimbusResumptionBarriersInstalled = true;
1680
2298
  globalThis.__nimbusVfsAcquireBarrier = _resumptionAcquire;
2299
+ globalThis.__nimbusVfsAcquireArgs = _acquireArgs;
1681
2300
  globalThis.__nimbusVfsReleaseBarrier = _resumptionRelease;
1682
2301
  const _setTimeout = globalThis.setTimeout;
1683
2302
  const _setInterval = globalThis.setInterval;
@@ -1724,7 +2343,16 @@ const __fsMod = (() => {
1724
2343
  // supervisor.mkdir is recursive, so the deepest unannounced one creates all
1725
2344
  // of them in a single round trip; a path whose directories are already live
1726
2345
  // (the common case) costs none at all.
2346
+ //
2347
+ // This is also the one seam every asynchronous fs entry point crosses on
2348
+ // its way to the authority — directly, or through
2349
+ // _flushLocalPathToSupervisor — so it is where a live call waits for the
2350
+ // structural mutations the program issued synchronously before it:
2351
+ // mkdirSync, rmdirSync, unlinkSync, renameSync queue their authority RPC
2352
+ // (below) rather than land it, and an open/stat/readdir that overtook that
2353
+ // queue would be answered for a tree the program has already changed.
1727
2354
  async function _announceLocalDirs(absPath, supervisor) {
2355
+ await _awaitStructuralOrder(absPath);
1728
2356
  if (!__vfsDirs || typeof supervisor.mkdir !== "function") return;
1729
2357
  const pending = [];
1730
2358
  let key = "";
@@ -1740,24 +2368,162 @@ const __fsMod = (() => {
1740
2368
  _markVfsStale();
1741
2369
  }
1742
2370
 
2371
+ // The write ledger is spliced ahead of the shims in every runner
2372
+ // (facets/manager.ts, opencode-facet-runner.ts). A harness that evaluates
2373
+ // the shims alone has no mutation queue and therefore nothing to wait for;
2374
+ // an absent ledger must not be an error on a read path that never needed
2375
+ // one before.
2376
+ function _hasVfsMutationQueue() {
2377
+ return typeof __nimbusQueueVfsMutation === "function";
2378
+ }
2379
+ function _awaitStructuralOrder(absPath) {
2380
+ return _hasVfsMutationQueue() ? __nimbusAwaitAncestorMutations(absPath) : Promise.resolve();
2381
+ }
2382
+
2383
+ /**
2384
+ * Carry a synchronous structural mutation to the authority.
2385
+ *
2386
+ * mkdirSync, rmdirSync, unlinkSync and renameSync used to edit the local
2387
+ * tables and stop: a sync syscall cannot make an RPC, and unlike a sync
2388
+ * write they parked nothing the write-back could later flush. Measured
2389
+ * live: node-tar's \`mkdirSync(dir)\` then \`fs.promises.open(dir/file,
2390
+ * "w")\` — once per extracted entry — was answered ENOENT by an authority
2391
+ * that had never heard of \`dir\`; that is create-astro's template copy.
2392
+ *
2393
+ * The sync effect stays exactly as it was. What is added is the same RPC
2394
+ * the asynchronous form of the call issues, queued through the write
2395
+ * ledger so it registers with the exit drain, is ordered behind pending
2396
+ * mutations of its ancestors (the ledger does that for every queued
2397
+ * mutation), and — for the two that act on a subtree — behind pending
2398
+ * mutations beneath it. The async forms are that same queued call, awaited.
2399
+ *
2400
+ * \`null\` when there is no authority to tell: standalone and unit contexts
2401
+ * keep today's local-only behaviour. Without a ledger the RPC is issued
2402
+ * directly, which is what the async forms did before they were queued.
2403
+ */
2404
+ // \`method\` names the supervisor RPC when it differs from the syscall the
2405
+ // caller reports (lchown rides \`chown\`, rm rides \`fsRemove\`).
2406
+ function _queueStructuralMutation(absPath, syscall, displayPath, rpc, after, method) {
2407
+ const settle = _nsTakeFresh();
2408
+ const supervisor = _supervisor();
2409
+ if (!supervisor || typeof supervisor[method || syscall] !== "function") { settle(); return null; }
2410
+ const queued = _hasVfsMutationQueue();
2411
+ const before = queued && after ? after() : null;
2412
+ const run = async () => {
2413
+ if (before) await before;
2414
+ // Only chown answers with a receipt. The rest (mkdir, unlink, rmdir,
2415
+ // rename, rm) have already dropped the path's cell and stamp in their
2416
+ // synchronous half, so no lease is taken at all; where one is — the
2417
+ // stamp unlink leaves behind — an absent receipt retires it, which is
2418
+ // what an unstamped cell already costs.
2419
+ await _ownMutation(
2420
+ absPath,
2421
+ () => _fsRpc(rpc(supervisor), syscall, displayPath, (result) => result),
2422
+ );
2423
+ _markVfsStale();
2424
+ };
2425
+ const out = queued ? __nimbusQueueVfsMutation(absPath, run) : run();
2426
+ // Settled either way: a failed mutation leaves the authority as it was,
2427
+ // which the next barrier's table shows.
2428
+ Promise.resolve(out).then(settle, settle);
2429
+ return out;
2430
+ }
2431
+
2432
+ /**
2433
+ * Run one of this facet's own partial mutations under a lease.
2434
+ *
2435
+ * See __nimbusBeginOwnMutation: the barrier KEEPS the cell while the RPC
2436
+ * is in flight — a barrier issued after this mutation can be answered
2437
+ * ahead of it, and an eviction there loses bytes no later stamp can
2438
+ * recover — and the receipt decides at the end. \`apply\` lands the local
2439
+ * effect while the lease still holds the cell, so the stamp the end
2440
+ * settles describes a cell that already carries the mutation.
2441
+ *
2442
+ * The ledger takes the lease on whichever holds the stamp: a heap stamp,
2443
+ * or the resident store's row. Without it a store row the mutation
2444
+ * overlaid would be left as own bytes nobody ever dates, which the barrier
2445
+ * keeps against every later write by anyone.
2446
+ *
2447
+ * From lease to receipt it is an acknowledgement in flight
2448
+ * (__nimbusOwnAcknowledgement): a barrier that reports the path waits for
2449
+ * it, rather than resume on bytes the receipt may yet show a peer outran.
2450
+ */
2451
+ function _ownMutation(absPath, rpc, apply) {
2452
+ const key = _strip(absPath);
2453
+ return __nimbusOwnAcknowledgement(key, async () => {
2454
+ const held = __nimbusBeginOwnMutation(key);
2455
+ let receipt;
2456
+ let landed = false;
2457
+ try { receipt = await rpc(); landed = true; }
2458
+ finally {
2459
+ // The local effect follows the mutation landing, not the receipt: the
2460
+ // receipt only settles the stamp, and a supervisor that answers
2461
+ // without one still applied the bytes. The end runs even if applying
2462
+ // throws: a lease left open pins its cell at Infinity, which is the one
2463
+ // state in this protocol that can serve a stale byte forever.
2464
+ try { if (landed && apply) apply(receipt); }
2465
+ finally { __nimbusEndOwnMutation(key, held, receipt); }
2466
+ }
2467
+ return receipt;
2468
+ });
2469
+ }
2470
+
2471
+ // A sync caller has no frame to receive the outcome. The ledger already
2472
+ // marks a queued result handled and retains what the exit drain must
2473
+ // report; this only keeps a ledger-less embedding from raising an
2474
+ // unhandled rejection for a verdict nobody could catch.
2475
+ function _detachStructuralMutation(mutation) {
2476
+ if (mutation) mutation.then(undefined, () => undefined);
2477
+ }
2478
+
2479
+ // A rename lives at two names. Its mutation is queued under the source;
2480
+ // this parks a fence under the destination so that anything queued for the
2481
+ // destination, or an ancestor wait that passes through it, is ordered
2482
+ // behind the move as well. The fence never fails on its own account.
2483
+ function _fenceVfsMutation(absPath, mutation) {
2484
+ if (!mutation || !_hasVfsMutationQueue()) return;
2485
+ __nimbusQueueVfsMutation(absPath, () => mutation.then(() => undefined, () => undefined), false);
2486
+ }
2487
+
2488
+ // Queue the parked write for \`absPath\`, if any, on the path's mutation
2489
+ // tail. Registration is synchronous: a caller that queues its own mutation
2490
+ // on the same tail right after this call is ordered behind the flush.
2491
+ function _flushParkedWrite(absPath, supervisor) {
2492
+ const k = _strip(absPath);
2493
+ if (!__vfsWrites || !(k in __vfsWrites) || typeof supervisor.writeFile !== "function") {
2494
+ return Promise.resolve(undefined);
2495
+ }
2496
+ return __nimbusFlushVfsWrite(
2497
+ absPath,
2498
+ (content, snapshot) => _fsRpc(
2499
+ __nimbusPersistVfsWrite(supervisor, absPath, content, snapshot),
2500
+ "write", absPath,
2501
+ (result) => result,
2502
+ ),
2503
+ true,
2504
+ true,
2505
+ );
2506
+ }
2507
+
1743
2508
  async function _flushLocalPathToSupervisor(absPath, supervisor) {
1744
2509
  const k = _strip(absPath);
1745
2510
  await _announceLocalDirs(absPath, supervisor);
1746
2511
  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
- );
2512
+ await _flushParkedWrite(absPath, supervisor);
1755
2513
  _markVfsStale();
1756
2514
  }
1757
- // Pending sync chmod rides along with any flush of the same path
1758
- // (idempotent — the entry stays so local statSync remains coherent).
1759
- if (k in _localModes && typeof supervisor.chmod === "function") {
1760
- await _fsRpc(supervisor.chmod(absPath, _localModes[k]), "chmod", absPath, () => undefined);
2515
+ // A pending sync chmod rides along with the next flush of the same path.
2516
+ if (_pendingModes.has(k) && typeof supervisor.chmod === "function") {
2517
+ _pendingModes.delete(k);
2518
+ try {
2519
+ await _ownMutation(
2520
+ absPath,
2521
+ () => _fsRpc(supervisor.chmod(absPath, _localModes[k]), "chmod", absPath, (result) => result),
2522
+ );
2523
+ } catch (error) {
2524
+ _pendingModes.add(k);
2525
+ throw error;
2526
+ }
1761
2527
  _markVfsStale();
1762
2528
  }
1763
2529
  }
@@ -1845,23 +2611,36 @@ const __fsMod = (() => {
1845
2611
  const atime = new Date(Number(meta?.atime || meta?.mtime || Date.now()));
1846
2612
  const mode = Number(meta?.mode ?? (isDir ? 0o755 : 0o644));
1847
2613
  const stat = _localStatObject(key, isDir, isSymlink, size, mode, meta?.uid, meta?.gid);
1848
- stat.atime = atime;
1849
- stat.mtime = mtime;
2614
+ // This process's own utimes, until it is reported back, ahead of the
2615
+ // namespace's (_recordLocalTimes).
2616
+ const own = key === undefined ? undefined : _localTimes[key];
2617
+ stat.atime = own && Number.isFinite(own.atimeMs) ? new Date(own.atimeMs) : atime;
2618
+ stat.mtime = own && Number.isFinite(own.mtimeMs) ? new Date(own.mtimeMs) : mtime;
1850
2619
  stat.ctime = new Date(Number(meta?.ctime ?? meta?.mtime ?? Date.now()));
1851
2620
  stat.birthtime = stat.ctime;
1852
2621
  return stat;
1853
2622
  }
1854
2623
 
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
- };
2624
+ // The one Dirent shape: readdir({ withFileTypes }) sync and async, and
2625
+ // every Dir.read(). \`parentPath\` is Node's field; \`path\` its deprecated
2626
+ // alias that older callers still read.
2627
+ class __Dirent {
2628
+ constructor(name, type, parentPath) {
2629
+ this.name = name;
2630
+ this.parentPath = parentPath === undefined ? "" : String(parentPath);
2631
+ this.path = this.parentPath;
2632
+ this._isDir = type === "directory" || type === "dir";
2633
+ this._isSymlink = type === "symlink";
2634
+ }
2635
+ isFile() { return !this._isDir && !this._isSymlink; }
2636
+ isDirectory() { return this._isDir; }
2637
+ isSymbolicLink() { return this._isSymlink; }
2638
+ isBlockDevice() { return false; }
2639
+ isCharacterDevice() { return false; }
2640
+ isFIFO() { return false; }
2641
+ isSocket() { return false; }
1864
2642
  }
2643
+ function _direntObject(name, type, parentPath) { return new __Dirent(name, type, parentPath); }
1865
2644
 
1866
2645
  // Largest single ranged read issued against the supervisor. Every live
1867
2646
  // read path (read streams, whole-file async reads) is expressed as a
@@ -1869,24 +2648,32 @@ const __fsMod = (() => {
1869
2648
  // multi-MB file for one RPC frame.
1870
2649
  const READ_STREAM_CHUNK_BYTES = 65536;
1871
2650
 
1872
- // Ranged reads issued in the same microtask turn travel as ONE batch.
2651
+ // Ranged reads issued in the same microtask turn travel as ONE batch, and
2652
+ // so do lstats (_learnLive): one request kind each, one round trip.
1873
2653
  //
1874
2654
  // A round trip costs an order of magnitude more than the read behind it,
1875
2655
  // so a program awaiting reads one at a time pays for round trips and
1876
2656
  // nothing else. Nothing here changes what a read sees: every request is
1877
- // the same live ranged read, executed in order, in the caller's turn. The
1878
- // gather window is one microtask, so it can only capture reads the program
1879
- // had already issued concurrently — a sequential loop batches nothing
1880
- // because it has issued nothing else to batch.
2657
+ // the same live ranged read or lstat, executed in order, in the caller's
2658
+ // turn. The gather window is one microtask, so it can only capture
2659
+ // requests the program had already issued concurrently — a sequential
2660
+ // loop batches nothing because it has issued nothing else to batch.
1881
2661
  const READ_BATCH_PATH_LIMIT = ${FS_READ_BATCH_PATH_LIMIT};
1882
2662
  const READ_BATCH_REQUEST_BYTES = ${FS_READ_BATCH_REQUEST_BYTES};
1883
2663
  let _openReadBatch = null;
1884
2664
 
1885
2665
  function _queueRangeRead(supervisor, absPath, pos, want) {
2666
+ return _queueBatchRequest(supervisor, { path: absPath, offset: pos, length: want }, want);
2667
+ }
2668
+
2669
+ // \`request\` in the open batch, \`bytes\` of file content counted against the
2670
+ // batch's bound. Settles with the entry's bytes for a range, its stat for
2671
+ // an lstat.
2672
+ function _queueBatchRequest(supervisor, request, bytes) {
1886
2673
  let batch = _openReadBatch;
1887
2674
  if (batch && (
1888
2675
  batch.requests.length >= READ_BATCH_PATH_LIMIT
1889
- || batch.bytes + want > READ_BATCH_REQUEST_BYTES
2676
+ || batch.bytes + bytes > READ_BATCH_REQUEST_BYTES
1890
2677
  )) {
1891
2678
  _openReadBatch = null;
1892
2679
  _flushReadBatch(batch);
@@ -1896,17 +2683,24 @@ const __fsMod = (() => {
1896
2683
  batch = { supervisor, requests: [], settlers: [], bytes: 0 };
1897
2684
  _openReadBatch = batch;
1898
2685
  queueMicrotask(() => {
1899
- if (_openReadBatch === batch) _openReadBatch = null;
2686
+ // A batch a bound closed was flushed then; sending it here too would
2687
+ // read every entry of it twice.
2688
+ if (_openReadBatch !== batch) return;
2689
+ _openReadBatch = null;
1900
2690
  _flushReadBatch(batch);
1901
2691
  });
1902
2692
  }
1903
- batch.bytes += want;
1904
- batch.requests.push({ path: absPath, offset: pos, length: want });
1905
- return new Promise((resolve, reject) => { batch.settlers.push({ resolve, reject }); });
2693
+ batch.bytes += bytes;
2694
+ batch.requests.push(request);
2695
+ const settler = Promise.withResolvers();
2696
+ batch.settlers.push(settler);
2697
+ return settler.promise;
1906
2698
  }
1907
2699
 
1908
2700
  async function _flushReadBatch(batch) {
1909
- globalThis.__nimbusFsRpcReads++;
2701
+ // A read round trip is one that carries a range; a batch of learns
2702
+ // (lstat requests) is metadata, which the read count leaves out.
2703
+ if (batch.requests.some((request) => request.lstat !== true)) globalThis.__nimbusFsRpcReads++;
1910
2704
  try {
1911
2705
  const entries = await __nimbusUseRpcResult(
1912
2706
  batch.supervisor.fsReadBatch(batch.requests), (r) => r,
@@ -1917,6 +2711,8 @@ const __fsMod = (() => {
1917
2711
  const err = new Error(entry.error.message);
1918
2712
  if (entry.error.code) err.code = entry.error.code;
1919
2713
  batch.settlers[i].reject(err);
2714
+ } else if (batch.requests[i].lstat === true) {
2715
+ batch.settlers[i].resolve(entry ? entry.stat : null);
1920
2716
  } else {
1921
2717
  batch.settlers[i].resolve(entry ? entry.bytes : null);
1922
2718
  }
@@ -1981,59 +2777,83 @@ const __fsMod = (() => {
1981
2777
  return parts;
1982
2778
  }
1983
2779
 
1984
- async function _liveReadFile(p, opts, skipAcquire) {
2780
+ async function _liveReadFile(p, opts, refetch) {
1985
2781
  const absPath = _resolve(p);
1986
2782
  const encoding = typeof opts === "string" ? opts : opts?.encoding;
1987
2783
  const supervisor = _supervisor();
1988
2784
  if (!supervisor) throw _fsErr("ENOENT", "open", p);
1989
- // The refetch step of _acquireAndRefetch has already acquired against a
1990
- // fresh cursor, so re-acquiring here would be a redundant round trip
1991
- // that always returns "still R". Every other caller acquires.
1992
- if (!skipAcquire) await _acquireBarrier(supervisor);
1993
-
1994
- if (typeof supervisor.fsReadRange === "function") {
1995
- // Chunked: the caller wants the whole file, but nothing upstream has
1996
- // to hold it all at once to produce it.
1997
- //
1998
- // A short chunk is the only signal the file ended, so this walk could
1999
- // only ever have one chunk in flight — 65 sequential round trips for a
2000
- // 4 MiB file, each costing far more than the read behind it. The FIRST
2001
- // chunk still costs exactly one trip and settles it for every file
2002
- // that fits in one. When it comes back full there is demonstrably
2003
- // more, and a stat says how much, so the remainder is issued together
2004
- // and the read batch carries it in a single trip.
2005
- const parts = [];
2006
- let total = 0;
2007
- for (;;) {
2008
- const chunk = await _readRangeAt(absPath, p, total, READ_STREAM_CHUNK_BYTES);
2009
- if (chunk === null) break;
2010
- parts.push(chunk);
2011
- total += chunk.byteLength;
2012
- if (chunk.byteLength < READ_STREAM_CHUNK_BYTES) break;
2013
- if (typeof supervisor.stat !== "function") continue;
2014
- for (const rest of await _readChunksFrom(absPath, p, supervisor, total)) {
2015
- parts.push(rest);
2016
- total += rest.byteLength;
2785
+ // A refetch (_refetch) has already acquired against a fresh cursor and
2786
+ // holds the fill ticket issued under it, so re-acquiring here would be a
2787
+ // redundant round trip that always returns "still R". Every other caller
2788
+ // acquires, and its fill ticket is its own.
2789
+ if (!refetch) await _acquireBarrier(supervisor);
2790
+ const fill = refetch || _beginFill(_strip(absPath));
2791
+ try {
2792
+ if (typeof supervisor.fsReadRange === "function") {
2793
+ // Chunked: the caller wants the whole file, but nothing upstream has
2794
+ // to hold it all at once to produce it.
2795
+ //
2796
+ // A short chunk is the only signal the file ended, so this walk could
2797
+ // only ever have one chunk in flight — 65 sequential round trips for a
2798
+ // 4 MiB file, each costing far more than the read behind it. The FIRST
2799
+ // chunk still costs exactly one trip and settles it for every file
2800
+ // that fits in one. When it comes back full there is demonstrably
2801
+ // more, and a stat says how much, so the remainder is issued together
2802
+ // and the read batch carries it in a single trip.
2803
+ const parts = [];
2804
+ let total = 0;
2805
+ for (;;) {
2806
+ const chunk = await _readRangeAt(absPath, p, total, READ_STREAM_CHUNK_BYTES);
2807
+ if (chunk === null) break;
2808
+ parts.push(chunk);
2809
+ total += chunk.byteLength;
2810
+ if (chunk.byteLength < READ_STREAM_CHUNK_BYTES) break;
2811
+ if (typeof supervisor.stat !== "function") continue;
2812
+ for (const rest of await _readChunksFrom(absPath, p, supervisor, total)) {
2813
+ parts.push(rest);
2814
+ total += rest.byteLength;
2815
+ }
2816
+ break;
2017
2817
  }
2018
- break;
2818
+ const bytes = parts.length === 1 ? parts[0] : _concatBytes(parts, total);
2819
+ _installResident(absPath, bytes, fill);
2820
+ await _learnLive(absPath, supervisor);
2821
+ return encoding ? _asString(bytes) : __BufferMod.from(bytes);
2019
2822
  }
2020
- const bytes = parts.length === 1 ? parts[0] : _concatBytes(parts, total);
2021
- _installResident(absPath, bytes);
2022
- return encoding ? _asString(bytes) : __BufferMod.from(bytes);
2023
- }
2024
2823
 
2025
- if (typeof supervisor.readFile === "function") {
2026
- await _flushLocalPathToSupervisor(absPath, supervisor);
2027
- const text = await _fsReadRpc(supervisor.readFile(absPath), "open", p, (result) => result);
2028
- if (text !== null && text !== undefined) {
2029
- _installResident(absPath, text);
2030
- return encoding ? _asString(text) : __BufferMod.from(text);
2824
+ if (typeof supervisor.readFile === "function") {
2825
+ await _flushLocalPathToSupervisor(absPath, supervisor);
2826
+ const text = await _fsReadRpc(supervisor.readFile(absPath), "open", p, (result) => result);
2827
+ if (text !== null && text !== undefined) {
2828
+ _installResident(absPath, text, fill);
2829
+ await _learnLive(absPath, supervisor);
2830
+ return encoding ? _asString(text) : __BufferMod.from(text);
2831
+ }
2031
2832
  }
2833
+ } finally {
2834
+ if (!refetch) _endFill(fill);
2032
2835
  }
2033
2836
 
2034
2837
  throw _fsErr("ENOENT", "open", p);
2035
2838
  }
2036
2839
 
2840
+
2841
+
2842
+ /** Paths whose write-back this process issued and the authority accepted. */
2843
+ const _acceptedHere = new Set();
2844
+ globalThis.__nimbusVfsWriteLanded = (key) => {
2845
+ const k = String(key).replace(/^\\/+/, "");
2846
+ _acceptedHere.add(k);
2847
+ };
2848
+
2849
+ // The authority refused a parked write (vfs-write-ledger): the record may
2850
+ // be that write's, so it is forgotten and the next access asks the authority.
2851
+ globalThis.__nimbusVfsWriteRefused = (key) => {
2852
+ const k = String(key).replace(/^\\/+/, "");
2853
+ _createdHere.delete(k);
2854
+ _acceptedHere.delete(k);
2855
+ };
2856
+
2037
2857
  function _concatBytes(parts, total) {
2038
2858
  const out = new Uint8Array(total);
2039
2859
  let off = 0;
@@ -2105,27 +2925,14 @@ const __fsMod = (() => {
2105
2925
  // its shape: the sync existence view must not go on contradicting a
2106
2926
  // directory this very process has enumerated. Names only — size, mode
2107
2927
  // and ownership are not observable here and are never invented.
2108
- if (__vfsManifest) __vfsManifest[key] = entries.map((entry) => entry.name);
2109
- // Which of the children are directories IS observable here, and it is
2110
- // the one fact a later synchronous listing cannot recover on its own —
2111
- // an unwalked subdirectory has no manifest entry, so without this it
2112
- // reads back as a file. A directory's size is 0 in this filesystem, so
2113
- // recording it invents nothing; file children are left alone, because
2114
- // their size is not knowable from a listing and must not be guessed.
2115
- const metadataTable = _metadataTable();
2116
- if (metadataTable) {
2117
- for (const entry of entries) {
2118
- if (entry.type !== "directory") continue;
2119
- const child = prefix + entry.name;
2120
- if (child in metadataTable) continue;
2121
- metadataTable[child] = {
2122
- type: "directory", size: 0, mode: 0o755, uid: cred.uid, gid: cred.gid,
2123
- };
2124
- }
2125
- }
2928
+ // The listed directory and its child directories get the authority's
2929
+ // own stat: a later synchronous stat or listing of them needs a record
2930
+ // saying what they are, and a record says who owns them, which a
2931
+ // listing does not. So it is asked for, never assumed (one lstat per
2932
+ // child directory, concurrently).
2126
2933
  if (opts?.withFileTypes) {
2127
2934
  return entries
2128
- .map((entry) => _direntObject(entry.name, entry.type))
2935
+ .map((entry) => _direntObject(entry.name, entry.type, absPath))
2129
2936
  .sort((a, b) => a.name.localeCompare(b.name));
2130
2937
  }
2131
2938
  return entries.map((entry) => entry.name).sort();
@@ -2148,7 +2955,9 @@ const __fsMod = (() => {
2148
2955
  async function _readlinkAsync(p) {
2149
2956
  const supervisor = _supervisor();
2150
2957
  if (supervisor && typeof supervisor.readlink === "function") {
2151
- const target = await _fsRpc(supervisor.readlink(_resolve(p)), "readlink", p, (result) => result);
2958
+ const absPath = _resolve(p);
2959
+ await _awaitStructuralOrder(absPath);
2960
+ const target = await _fsRpc(supervisor.readlink(absPath), "readlink", p, (result) => result);
2152
2961
  if (target !== null && target !== undefined) return target;
2153
2962
  }
2154
2963
  throw _fsErr("EINVAL", "readlink", p);
@@ -2157,7 +2966,9 @@ const __fsMod = (() => {
2157
2966
  async function _symlinkAsync(target, path) {
2158
2967
  const supervisor = _supervisor();
2159
2968
  if (supervisor && typeof supervisor.symlink === "function") {
2160
- await _fsRpc(supervisor.symlink(String(target), _resolve(path)), "symlink", path, () => undefined);
2969
+ const absPath = _resolve(path);
2970
+ await _awaitStructuralOrder(absPath);
2971
+ await _fsRpc(supervisor.symlink(String(target), absPath), "symlink", path, () => undefined);
2161
2972
  _markVfsStale();
2162
2973
  return;
2163
2974
  }
@@ -2170,10 +2981,14 @@ const __fsMod = (() => {
2170
2981
  const supervisor = _supervisor();
2171
2982
  if (supervisor && typeof supervisor.writeFile === "function") {
2172
2983
  await _announceLocalDirs(absPath, supervisor);
2984
+ // The revision comes back so the ledger can stamp the cell: an async
2985
+ // whole write is the facet's own as much as a parked sync one is.
2173
2986
  await __nimbusFlushVfsWrite(absPath, (content) =>
2174
- _fsRpc(supervisor.writeFile(absPath, content), "write", p, () => undefined)
2987
+ _fsRpc(supervisor.writeFile(absPath, content), "write", p, (result) => result)
2175
2988
  );
2176
2989
  _markVfsStale();
2990
+ // Accepted: the authority's stat is what the sync view keeps for it.
2991
+ await _learnLive(absPath, supervisor);
2177
2992
  }
2178
2993
  }
2179
2994
 
@@ -2192,43 +3007,16 @@ const __fsMod = (() => {
2192
3007
  ),
2193
3008
  );
2194
3009
  _markVfsStale();
3010
+ await _learnLive(absPath, supervisor);
2195
3011
  }
2196
3012
 
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
- }
3013
+ // The async structural calls ARE the sync ones, awaited: the same local
3014
+ // effect and the same queued authority RPC, so the two forms cannot
3015
+ // disagree about order, and a program mixing them sees one sequence.
3016
+ async function _mkdirAsync(p, opts) { await _mkdirQueued(p, opts); }
3017
+ async function _unlinkAsync(p) { await _unlinkQueued(p); }
3018
+ async function _rmdirAsync(p) { await _rmdirQueued(p); }
3019
+ async function _renameAsync(oldP, newP) { await _renameQueued(oldP, newP); }
2232
3020
 
2233
3021
  async function _truncateAsync(p, len) {
2234
3022
  const absPath = _resolve(p);
@@ -2246,6 +3034,9 @@ const __fsMod = (() => {
2246
3034
  (content, snapshot) =>
2247
3035
  __nimbusPersistVfsWrite(supervisor, absPath, content, snapshot),
2248
3036
  );
3037
+ // Persisting the append drops the resident cell (the ledger
3038
+ // refetches it on the next live read), so there is no cell to
3039
+ // trim or stamp behind this truncate.
2249
3040
  await __nimbusQueueVfsMutation(absPath, async () => {
2250
3041
  await flush;
2251
3042
  await _fsRpc(
@@ -2267,12 +3058,15 @@ const __fsMod = (() => {
2267
3058
  // Live file is the source of truth — supervisor trims only the
2268
3059
  // boundary chunk; ENOENT propagates when it does not exist.
2269
3060
  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
- }
3061
+ await __nimbusQueueVfsMutation(absPath, () => _ownMutation(
3062
+ absPath,
3063
+ () => _fsRpc(supervisor.fsTruncate(absPath, size), "truncate", p, (result) => result),
3064
+ () => {
3065
+ if (__vfsWriteGenerations[k] === generation && localCell !== undefined) {
3066
+ _truncateLocalCell(absPath, size);
3067
+ }
3068
+ },
3069
+ ));
2276
3070
  _markVfsStale();
2277
3071
  return;
2278
3072
  }
@@ -2292,9 +3086,9 @@ const __fsMod = (() => {
2292
3086
  _recordLocalTimes(absPath, atime, mtime, "lutimes", p);
2293
3087
  }
2294
3088
 
2295
- async function _utimesAsync(p, atime, mtime, opts) {
3089
+ async function _utimesAsync(p, atime, mtime, opts, syscallOverride) {
2296
3090
  const followSymlinks = !(opts && opts.followSymlinks === false);
2297
- const syscall = followSymlinks ? "utimes" : "lutimes";
3091
+ const syscall = syscallOverride || (followSymlinks ? "utimes" : "lutimes");
2298
3092
  const absPath = _resolve(p);
2299
3093
  const supervisor = _supervisor();
2300
3094
  let localExists = false;
@@ -2305,22 +3099,43 @@ const __fsMod = (() => {
2305
3099
  const time = _recordLocalTimes(absPath, atime, mtime, syscall, p);
2306
3100
  if (supervisor && typeof supervisor.utimes === "function") {
2307
3101
  await _flushLocalPathToSupervisor(absPath, supervisor);
2308
- await _fsRpc(
3102
+ // Ordered behind the path's pending mutations: an fd write queued a
3103
+ // moment ago (modern-tar writes, then futimes, then closes) would
3104
+ // otherwise land AFTER the timestamp and reset it to "now".
3105
+ const leased = () => _ownMutation(absPath, () => _fsRpc(
2309
3106
  supervisor.utimes(absPath, time.atimeMs, time.mtimeMs),
2310
3107
  syscall, p,
2311
- () => undefined,
2312
- );
3108
+ (result) => result,
3109
+ ));
3110
+ if (_hasVfsMutationQueue()) await __nimbusQueueVfsMutation(absPath, leased);
3111
+ else await leased();
2313
3112
  _markVfsStale();
2314
3113
  return;
2315
3114
  }
2316
3115
  if (!supervisor && !existsSync(p)) throw _fsErr("ENOENT", syscall, p);
2317
3116
  }
2318
3117
 
3118
+ /**
3119
+ * chmod(2): only the owner or root changes a mode (EPERM). The local
3120
+ * overlay is what statSync reports and what the local permission checks
3121
+ * read, so a mode set here without that check made a root-owned file
3122
+ * writable in the process's own view.
3123
+ */
3124
+ function _ensureModeOwner(absPath, syscall, p) {
3125
+ if (Number(cred.uid) === 0) return;
3126
+ const stat = _statLadder(absPath);
3127
+ if (stat === undefined) throw _fsErr("ENOENT", syscall, p);
3128
+ if (Number(stat.uid) !== Number(cred.uid)) throw _fsErr("EPERM", syscall, p);
3129
+ }
3130
+
2319
3131
  function chmodSync(p, mode) {
2320
3132
  if (!existsSync(p)) throw _fsErr("ENOENT", "chmod", p);
3133
+ _ensureModeOwner(_resolve(p), "chmod", p);
2321
3134
  // Local-visible immediately (statSync overlay); the live write-through
2322
3135
  // rides the next flush of the same path — same fidelity as utimesSync.
2323
- _localModes[_strip(_resolve(p))] = _coerceMode(mode, "chmod", p);
3136
+ const k = _strip(_resolve(p));
3137
+ _localModes[k] = _coerceMode(mode, "chmod", p);
3138
+ _pendingModes.add(k);
2324
3139
  }
2325
3140
 
2326
3141
  async function _chmodAsync(p, mode) {
@@ -2332,7 +3147,9 @@ const __fsMod = (() => {
2332
3147
  throw _fsErr("ENOENT", "chmod", p);
2333
3148
  }
2334
3149
  const m = _coerceMode(mode, "chmod", p);
3150
+ if (localExists) _ensureModeOwner(absPath, "chmod", p);
2335
3151
  _localModes[_strip(absPath)] = m;
3152
+ _pendingModes.add(_strip(absPath));
2336
3153
  if (supervisor && typeof supervisor.chmod === "function") {
2337
3154
  await _flushLocalPathToSupervisor(absPath, supervisor);
2338
3155
  _markVfsStale();
@@ -2359,27 +3176,78 @@ const __fsMod = (() => {
2359
3176
  const nextUid = _coerceId(uid, syscall, p);
2360
3177
  const nextGid = _coerceId(gid, syscall, p);
2361
3178
  await _flushLocalPathToSupervisor(absPath, supervisor);
2362
- await _fsRpc(supervisor.chown(absPath, nextUid, nextGid, opts), syscall, p, () => undefined);
3179
+ await _ownMutation(
3180
+ absPath,
3181
+ () => _fsRpc(supervisor.chown(absPath, nextUid, nextGid, opts), syscall, p, (result) => result),
3182
+ () => {
3183
+ const meta = _metadata(absPath);
3184
+ if (meta) { meta.uid = nextUid; meta.gid = nextGid; }
3185
+ },
3186
+ );
3187
+ _markVfsStale();
3188
+ }
3189
+
3190
+ // Sync ownership change: the stat record is updated locally at once and
3191
+ // the authority RPC is queued behind the path's pending flush, the same
3192
+ // parked model mkdirSync/unlinkSync use. Without an authority there is no
3193
+ // owner table to change, so the answer is the same ENOSYS as the async form.
3194
+ function _chownQueued(p, uid, gid, opts, syscall) {
3195
+ const followSymlinks = !(opts && opts.followSymlinks === false);
3196
+ const absPath = _resolve(p);
3197
+ const supervisor = _supervisor();
3198
+ if (!supervisor || typeof supervisor.chown !== "function") {
3199
+ if (!existsSync(p)) throw _fsErr("ENOENT", syscall, p);
3200
+ throw _fsErr("ENOSYS", syscall, p);
3201
+ }
3202
+ const nextUid = _coerceId(uid, syscall, p);
3203
+ const nextGid = _coerceId(gid, syscall, p);
3204
+ // chown(2): root changes anything; the owner may only set the group to
3205
+ // one of its own groups, and never the owner. Checked before the local
3206
+ // row changes: that row is what the local permission checks read.
3207
+ if (Number(cred.uid) !== 0) {
3208
+ const stat = _statLadder(absPath);
3209
+ if (stat === undefined) throw _fsErr("ENOENT", syscall, p);
3210
+ const groups = [Number(cred.gid), ...cred.groups.map(Number)];
3211
+ if (Number(stat.uid) !== Number(cred.uid) || nextUid !== Number(stat.uid)
3212
+ || (nextGid !== Number(stat.gid) && !groups.includes(nextGid))) {
3213
+ throw _fsErr("EPERM", syscall, p);
3214
+ }
3215
+ }
2363
3216
  const meta = _metadata(absPath);
2364
3217
  if (meta) { meta.uid = nextUid; meta.gid = nextGid; }
2365
- _markVfsStale();
3218
+ // The parked write must be registered on the tail BEFORE the chown is
3219
+ // (a flush awaited from inside the queued step would queue behind it
3220
+ // and wait on itself), so it is the synchronous-registering flush.
3221
+ return _queueStructuralMutation(
3222
+ absPath, syscall, p,
3223
+ (s) => s.chown(absPath, nextUid, nextGid, followSymlinks ? undefined : { followSymlinks: false }),
3224
+ () => _flushParkedWrite(absPath, supervisor),
3225
+ "chown",
3226
+ );
2366
3227
  }
3228
+ function chownSync(p, uid, gid) { _detachStructuralMutation(_chownQueued(p, uid, gid, undefined, "chown")); }
3229
+ function lchownSync(p, uid, gid) { _detachStructuralMutation(_chownQueued(p, uid, gid, { followSymlinks: false }, "lchown")); }
3230
+ function lchmodSync(p, mode) { chmodSync(p, mode); }
2367
3231
 
3232
+ /**
3233
+ * Whether this process may WANT (r=4, w=2, x=1) a path whose stat is
3234
+ * META, by POSIX's owner/group/other rule. Only a stat that states the
3235
+ * owner, group and mode can be judged; one that does not is not judged in
3236
+ * the process's favour: absent fields used to default to mode 0644 and
3237
+ * owner 1000, which made any undescribed file the reader's own.
3238
+ */
2368
3239
  function _modeAllows(meta, want) {
2369
3240
  if (want === 0) return true;
2370
- const mode = Number(meta?.mode ?? 0o644) & 0o777;
3241
+ const mode = Number(meta?.mode);
3242
+ const uid = Number(meta?.uid);
3243
+ const gid = Number(meta?.gid);
3244
+ if (!Number.isInteger(mode) || !Number.isInteger(uid) || !Number.isInteger(gid)) return false;
3245
+ const bits = mode & 0o777;
2371
3246
  const currentUid = Number(cred.uid);
2372
- const currentGid = Number(cred.gid);
3247
+ if (currentUid === 0) return (want & 1) === 0 || (bits & 0o111) !== 0;
2373
3248
  const groups = cred.groups.map(Number);
2374
- if (currentUid === 0) {
2375
- if ((want & 1) !== 0 && (mode & 0o111) === 0) return false;
2376
- return true;
2377
- }
2378
- const shift = currentUid === Number(meta?.uid ?? 1000)
2379
- ? 6
2380
- : (currentGid === Number(meta?.gid ?? 1000) || groups.includes(Number(meta?.gid ?? 1000))) ? 3 : 0;
2381
- const available = (mode >> shift) & 7;
2382
- return (available & want) === want;
3249
+ const shift = currentUid === uid ? 6 : (Number(cred.gid) === gid || groups.includes(gid)) ? 3 : 0;
3250
+ return (((bits >> shift) & 7) & want) === want;
2383
3251
  }
2384
3252
 
2385
3253
  function _ensureAncestorsTraversable(absPath, syscall, p) {
@@ -2393,34 +3261,31 @@ const __fsMod = (() => {
2393
3261
  function _ensureWritable(absPath, syscall, p) {
2394
3262
  _ensureAncestorsTraversable(absPath, syscall, p);
2395
3263
  const cell = _bundleLookup(absPath);
2396
- const meta = _metadata(absPath);
2397
- // The ladder rather than existsSync: a write to a path outside the staged
2398
- // view is a legitimate create, and must not be turned into a refusal by
2399
- // the check that guards it.
2400
- if (meta !== undefined || cell !== undefined || _statLadder(absPath) !== undefined) {
2401
- const denial = _denialCode(cell);
2402
- if (denial || !_modeAllows(meta, 2)) throw _fsErr(denial || "EACCES", syscall, p);
3264
+ const denial = _denialCode(cell);
3265
+ if (denial) throw _fsErr(denial, syscall, p);
3266
+ // Judged on what the authority says of the path (or of a file this
3267
+ // process made), never on the bytes held under its name.
3268
+ const stat = _statLadder(absPath);
3269
+ if (stat !== undefined) {
3270
+ if (!_modeAllows(stat, 2)) throw _fsErr("EACCES", syscall, p);
2403
3271
  return;
2404
3272
  }
3273
+ // Nothing there: a create, judged by the parent the namespace describes.
2405
3274
 
2406
3275
  const parent = __pathMod.dirname(absPath);
2407
- const parentMeta = _metadata(parent);
2408
- if (parentMeta) {
2409
- if (parentMeta.type && parentMeta.type !== "directory") throw _fsErr("ENOTDIR", syscall, p);
2410
- if (!_modeAllows(parentMeta, 3)) throw _fsErr("EACCES", syscall, p);
3276
+ const parentStat = _statLadder(parent);
3277
+ if (parentStat !== undefined) {
3278
+ if (!parentStat.isDirectory()) throw _fsErr("ENOTDIR", syscall, p);
3279
+ if (!_modeAllows(parentStat, 3)) throw _fsErr("EACCES", syscall, p);
2411
3280
  return;
2412
3281
  }
2413
3282
 
2414
- const parentKey = _strip(parent);
2415
- const parentIsLocal = parent === cwd || parent === "/" ||
2416
- (!!__vfsDirs && parentKey in __vfsDirs) ||
2417
- (!!__vfsManifest && parentKey in __vfsManifest);
2418
- if (parentIsLocal || _supervisor()) return;
2419
3283
  throw _fsErr("ENOENT", syscall, p);
2420
3284
  }
2421
3285
 
2422
3286
  function accessSync(p, mode) {
2423
3287
  const absPath = _resolve(p);
3288
+ _nsRequire("access", p, "fs.promises.access");
2424
3289
  const cell = _bundleLookup(absPath);
2425
3290
  const meta = _metadata(absPath);
2426
3291
  if (cell === undefined && meta === undefined && !existsSync(p)) throw _fsErr("ENOENT", "access", p);
@@ -2431,7 +3296,10 @@ const __fsMod = (() => {
2431
3296
  _ensureAncestorsTraversable(absPath, "access", p);
2432
3297
  const denial = _denialCode(cell);
2433
3298
  if ((requested & 4) !== 0 && denial) throw _fsErr(denial, "access", p);
2434
- if (!_modeAllows(meta, requested)) throw _fsErr("EACCES", "access", p);
3299
+ if (requested === 0) return;
3300
+ const stat = _statLadder(absPath);
3301
+ if (stat === undefined) throw _fsErr("ENOENT", "access", p);
3302
+ if (!_modeAllows(stat, requested)) throw _fsErr("EACCES", "access", p);
2435
3303
  }
2436
3304
 
2437
3305
  async function _accessAsync(p, mode) {
@@ -2542,42 +3410,12 @@ const __fsMod = (() => {
2542
3410
  // ── existsSync ──
2543
3411
  function existsSync(p) {
2544
3412
  const absPath = _resolve(p);
2545
- const k = _strip(absPath);
2546
- if (_metadata(absPath) !== undefined) { _residencySatisfied(absPath); return true; }
2547
- if (__vfsBundle && k in __vfsBundle) { _residencySatisfied(absPath); return true; }
2548
- if (__vfsWrites && k in __vfsWrites) { _residencySatisfied(absPath); return true; }
2549
- if (__vfsDirs && k in __vfsDirs) { _residencySatisfied(absPath); return true; }
2550
- // W2.5b root-cause fix: consult the manifest BEFORE falling back to
2551
- // the O(N) bundle-prefix scan. The manifest is uncapped and always
2552
- // reflects the real directory shape, even when the file content for
2553
- // a directory's contents was excluded by the 4 MiB / 500-file content
2554
- if (__vfsManifest) {
2555
- if (k in __vfsManifest) { _residencySatisfied(absPath); return true; }
2556
- // Path may be a file listed by its parent's manifest entry.
2557
- const slash = k.lastIndexOf("/");
2558
- const parent = slash >= 0 ? k.slice(0, slash) : "";
2559
- const name = slash >= 0 ? k.slice(slash + 1) : k;
2560
- const sib = __vfsManifest[parent];
2561
- if (sib && sib.indexOf(name) !== -1) { _residencySatisfied(absPath); return true; }
2562
- }
2563
- // Last-resort: bundle dir entries
2564
- if (__vfsBundle && (k in __vfsBundle || __residentAnyUnder(k + "/"))) {
2565
- _residencySatisfied(absPath);
2566
- return true;
2567
- }
2568
- // Nothing found — which is only an answer if the enclosing directory was
2569
- // enumerated. Node's existsSync never throws, so the false below is
2570
- // provisional: the miss is recorded, the listing that settles it is pulled
2571
- // in, and the exit report fails the run if the path turns out to have been
2572
- // there all along.
2573
- if (!_absenceIsKnown(absPath)) {
2574
- _recordUnmapped(absPath, "access");
2575
- return false;
2576
- }
3413
+ _nsRequire("access", p, "fs.promises.access");
2577
3414
  _residencySatisfied(absPath);
2578
- return false;
3415
+ return _statLadder(absPath) !== undefined;
2579
3416
  }
2580
3417
 
3418
+
2581
3419
  // ── statSync ──
2582
3420
  function statSync(p, opts) {
2583
3421
  const absPath = _resolve(p);
@@ -2590,16 +3428,12 @@ const __fsMod = (() => {
2590
3428
  // classify a miss without redoing the resolve and ancestor walk it just
2591
3429
  // performed — that path runs on every module-resolution probe.
2592
3430
  function _statResolved(absPath, p, opts) {
3431
+ _nsRequire("stat", p, "fs.promises.stat");
2593
3432
  const stat = _statLadder(absPath);
2594
- // An answer settles the path, including the honest "not there" below —
2595
- // what the ledger is for is reads that went unanswered, not reads that came
2596
- // back with news the caller did not want. A PROVISIONAL not-there is not an
2597
- // answer, so the unmapped branch deliberately leaves the record standing.
2598
- if (stat !== undefined) { _residencySatisfied(absPath); return stat; }
2599
- // Nothing in the view describes the path, and there are two very different
2600
- // reasons for that. Only one of them means the file is not there.
2601
- if (_absenceIsKnown(absPath)) _residencySatisfied(absPath);
2602
- else _recordUnmapped(absPath, "stat");
3433
+ // An answer settles the path, including the honest "not there": the
3434
+ // namespace names every path this credential can see.
3435
+ _residencySatisfied(absPath);
3436
+ if (stat !== undefined) return stat;
2603
3437
  // Node's statSync honors { throwIfNoEntry: false } by returning undefined
2604
3438
  // for a missing path instead of throwing.
2605
3439
  if (opts && opts.throwIfNoEntry === false) return undefined;
@@ -2612,216 +3446,350 @@ const __fsMod = (() => {
2612
3446
  * MEANS — absence and ignorance are different answers and only one of them
2613
3447
  * is a condition a program can act on, so the callers classify.
2614
3448
  */
2615
- function _statLadder(absPath) {
3449
+ function _statLadder(absPath, noFollow) {
2616
3450
  const k = _strip(absPath);
2617
- // Content written this exec session is newer than the spawn-time
2618
- // metadata snapshot, so it — not __vfsMetadata — is authoritative for
2619
- // size. Without this, fstatSync/statSync on a file we just wrote report
2620
- // the pre-write length.
2621
- if (__vfsWrites && k in __vfsWrites && _denialCode(__vfsWrites[k]) === null) {
2622
- const meta = _metadata(absPath);
2623
- const size = _byteLen(__vfsWrites[k]);
2624
- // Correct only the size — reusing the metadata record keeps the type
2625
- // and the recorded timestamps stable, so mtime-based change detection
2626
- // (make, tsc --build, watchers) does not see every call as a change.
2627
- if (meta) return _statObject({ ...meta, size }, k);
2628
- return _localStatObject(
2629
- k, false, false, size, 0o666 & ~__processUmask, cred.uid, cred.gid,
2630
- );
2631
- }
2632
- const metadata = _metadata(absPath);
2633
- if (metadata) return _statObject(metadata, k);
2634
- // Check if it's a known directory written this exec session
2635
- if (__vfsDirs && k in __vfsDirs) {
2636
- return _localStatObject(k, true, false, 0, 0o777 & ~__processUmask, cred.uid, cred.gid);
2637
- }
2638
- // W2.5b: consult uncapped manifest first for directory shape.
2639
- if (__vfsManifest && k in __vfsManifest) {
2640
- return _localStatObject(k, true, false, 0, 0o755, cred.uid, cred.gid);
2641
- }
2642
- // File with content embedded?
2643
- const content = _bundleLookup(absPath);
2644
- if (content !== undefined) {
2645
- // _byteLen handles both string (UTF-8 encode) and Uint8Array
2646
- // (byteLength) — fixes binary writes from reporting the
2647
- // post-corruption byte count.
2648
- const size = _byteLen(content);
2649
- return _localStatObject(k, false, false, size, 0o666 & ~__processUmask, cred.uid, cred.gid);
2650
- }
2651
- // File listed in parent's manifest but content was capped out — return
2652
- // a zero-size file stat so callers like fs.stat / fs.statSync see the
2653
- // file as present (downstream readFileSync will surface ENOENT if it
2654
- // actually tries to read content; many consumers only need stat).
2655
- if (__vfsManifest) {
2656
- const slash = k.lastIndexOf("/");
2657
- const parent = slash >= 0 ? k.slice(0, slash) : "";
2658
- const name = slash >= 0 ? k.slice(slash + 1) : k;
2659
- const sib = __vfsManifest[parent];
2660
- if (sib && sib.indexOf(name) !== -1) {
2661
- return _localStatObject(k, false, false, 0, 0o644, cred.uid, cred.gid);
2662
- }
2663
- }
2664
- // Last-resort: bundle prefix scan (legacy path).
2665
- if (__vfsBundle && __residentAnyUnder(k + "/")) {
2666
- return _localStatObject(k, true, false, 0, 0o755, cred.uid, cred.gid);
2667
- }
2668
- return undefined;
3451
+ _nsRequire("stat", absPath, "fs.promises.stat");
3452
+ const meta = _nsMeta(k, !noFollow);
3453
+ if (meta === "absent" || meta === "ELOOP") return undefined;
3454
+ return meta.own
3455
+ ? _localStatObject(k, meta.type === "directory", false, meta.size, meta.mode & 0o7777, meta.uid, meta.gid)
3456
+ : _statObject(meta, k);
2669
3457
  }
2670
3458
 
2671
3459
  // ── lstatSync (alias for statSync in our VFS — no symlinks) ──
2672
- function lstatSync(p, opts) { return statSync(p, opts); }
3460
+ function lstatSync(p, opts) {
3461
+ const absPath = _resolve(p);
3462
+ _nsRequire("lstat", p, "fs.promises.lstat");
3463
+ _ensureAncestorsTraversable(absPath, "lstat", p);
3464
+ const stat = _statLadder(absPath, true);
3465
+ if (stat !== undefined) return stat;
3466
+ if (opts && opts.throwIfNoEntry === false) return undefined;
3467
+ throw _fsErr("ENOENT", "lstat", p);
3468
+ }
2673
3469
 
2674
3470
  // ── readdirSync ──
2675
- // W2.5b root-cause fix: prefer the uncapped __vfsManifest for directory
2676
- // listings. The legacy bundle-prefix scan is kept as a fallback for paths
2677
- // not in the manifest (e.g. dirs created at exec time via mkdirSync) and
2678
- // is unioned with __vfsWrites so newly-written files become visible.
3471
+ // The namespace's listing of the directory, with this process's own
3472
+ // structural effects over it (_nsList).
2679
3473
  function readdirSync(p, opts) {
2680
3474
  const absPath = _resolve(p);
3475
+ _nsRequire("scandir", p, "fs.promises.readdir");
2681
3476
  _ensureAncestorsTraversable(absPath, "scandir", p);
2682
3477
  const metadata = _metadata(absPath);
2683
3478
  if (metadata && !_modeAllows(metadata, 4)) throw _fsErr("EACCES", "scandir", p);
2684
3479
  const k = _strip(absPath);
2685
- // A listing is the one answer with no way to express doubt: an array is a
2686
- // complete enumeration by definition, so returning what happens to be
2687
- // known asserts that nothing else is there. Only a directory the walk
2688
- // enumerated, or one this process created, can honestly be listed.
2689
- if (!_dirEnumerated(k) && !_bundleHasChildren(k)) {
2690
- // An authority exists that this call cannot reach, so an empty array
2691
- // would be a claim about a directory nothing ever opened. Refuse, and
2692
- // pull the listing in for the next one.
2693
- //
2694
- // This is the one caller that keeps refusing. Everywhere else the sync
2695
- // view answers a path it cannot map with the not-found the program was
2696
- // written to handle, provisionally, and lets the exit report settle
2697
- // whether that answer was true. A listing has no such answer to give:
2698
- // "no config yet, write one" is an idiom every program implements, and
2699
- // there is no counterpart for a directory — a caller handed ENOENT for a
2700
- // template tree has nowhere to go but the same wrong branch an empty
2701
- // array sent it down, and the array at least it could not detect.
2702
- if (_supervisor()) {
2703
- throw _refuseUnmapped(absPath, p, "scandir", "fs.promises.readdir");
2704
- }
2705
- // No authority: the staged tables are the whole filesystem. A path they
2706
- // do not describe is missing, and one they describe with no children is
2707
- // genuinely empty.
2708
- if (_statLadder(absPath) === undefined) throw _fsErr("ENOENT", "scandir", p);
2709
- }
2710
- const prefix = k ? k + "/" : "";
2711
- const names = new Set();
2712
- // 1. Manifest-supplied children (the authoritative source for installed pkgs).
2713
- if (__vfsManifest && k in __vfsManifest) {
2714
- for (const n of __vfsManifest[k]) names.add(n);
2715
- }
2716
- // 2. Bundle-prefix fallback (covers older paths or runtime-mkdir'd ones
2717
- // that aren't in the manifest yet).
2718
- if (__vfsBundle) {
2719
- for (const bk of __residentUnder(prefix)) {
2720
- const seg = bk.substring(prefix.length).split("/")[0];
2721
- if (seg) names.add(seg);
2722
- }
2723
- }
2724
- // 3. Files written during this exec session.
2725
- if (__vfsWrites) {
2726
- for (const wk in __vfsWrites) {
2727
- if (wk.startsWith(prefix)) {
2728
- const rest = wk.substring(prefix.length);
2729
- const seg = rest.split("/")[0];
2730
- if (seg) names.add(seg);
2731
- }
2732
- }
2733
- }
2734
- // 4. Dirs created during this exec session.
2735
- if (__vfsDirs) {
2736
- for (const dk in __vfsDirs) {
2737
- if (dk.startsWith(prefix)) {
2738
- const rest = dk.substring(prefix.length);
2739
- const seg = rest.split("/")[0];
2740
- if (seg) names.add(seg);
2741
- }
2742
- }
2743
- }
3480
+ const st = _statLadder(absPath);
3481
+ if (st === undefined) throw _fsErr("ENOENT", "scandir", p);
3482
+ if (!st.isDirectory()) throw _fsErr("ENOTDIR", "scandir", p);
2744
3483
  _residencySatisfied(absPath);
2745
- const arr = [...names].sort();
2746
- if (opts?.withFileTypes) {
2747
- return arr.map(n => {
2748
- const fp = prefix + n;
2749
- // Manifest is the definitive isDir source; fall back to bundle scan.
2750
- // The metadata record sits between them: a directory that was listed
2751
- // but never walked has no manifest entry of its own, and calling it a
2752
- // file is how a template tree full of subdirectories reads back as a
2753
- // tree full of nothing.
2754
- const isDir =
2755
- (!!__vfsManifest && fp in __vfsManifest) ||
2756
- (!!__vfsDirs && fp in __vfsDirs) ||
2757
- _metadata(fp)?.type === "directory" ||
2758
- (!!__vfsBundle && __residentAnyUnder(fp + "/"));
2759
- return { name: n, isFile: () => !isDir, isDirectory: () => isDir, isSymbolicLink: () => false };
2760
- });
2761
- }
2762
- return arr;
3484
+ const listed = _nsList(k);
3485
+ const sorted = [...listed.keys()].sort();
3486
+ if (!opts?.withFileTypes) return sorted;
3487
+ return sorted.map((n) => _direntObject(n, listed.get(n), absPath));
2763
3488
  }
2764
3489
 
2765
3490
  // ── mkdirSync ──
2766
- function mkdirSync(p, opts) {
3491
+ // The sync effect, then the authority mutation queued behind it (see
3492
+ // _queueStructuralMutation). One RPC whatever the depth: the async form
3493
+ // sends the path alone and the authority's mkdir creates the ancestors.
3494
+ function _mkdirQueued(p, opts) {
2767
3495
  const absPath = _resolve(p);
2768
3496
  const k = _strip(absPath);
3497
+ const created = [];
2769
3498
  if (opts?.recursive) {
2770
3499
  const parts = k.split("/").filter(Boolean);
2771
3500
  let cur = "";
2772
- for (const part of parts) { cur = cur ? cur + "/" + part : part; __vfsDirs[cur] = true; }
3501
+ for (const part of parts) { cur = cur ? cur + "/" + part : part; _noteCreation(cur); __vfsDirs[cur] = true; created.push(cur); }
2773
3502
  } else {
3503
+ _noteCreation(k);
2774
3504
  __vfsDirs[k] = true;
3505
+ created.push(k);
3506
+ }
3507
+ for (const dir of created) {
3508
+ const was = _nsMeta(dir, true);
3509
+ if (was === "absent") _nsOwnSet(dir, "dir", { hide: true });
2775
3510
  }
3511
+ const queued = _queueStructuralMutation(absPath, "mkdir", p, (supervisor) => supervisor.mkdir(absPath));
3512
+ // Told, not merely known locally: _announceLocalDirs must not issue a
3513
+ // second mkdir for a directory whose own RPC is already in the queue.
3514
+ if (queued) for (const dir of created) _announcedDirs.add(dir);
3515
+ return queued;
2776
3516
  }
3517
+ function mkdirSync(p, opts) { _detachStructuralMutation(_mkdirQueued(p, opts)); }
2777
3518
 
2778
3519
  // ── unlinkSync ──
2779
- function unlinkSync(p) {
3520
+ /**
3521
+ * unlink(2)/rmdir(2)/rename(2) of an existing name: the parent must be
3522
+ * writable and searchable, and in a sticky directory (/tmp, 1777) only the
3523
+ * file's owner, the directory's owner or root may remove it. Checked before
3524
+ * the local view changes, so a refusal leaves the process's view intact. A
3525
+ * name whose owner this view does not know is refused as a miss.
3526
+ */
3527
+ function _ensureRemovable(absPath, syscall, p) {
3528
+ _ensureAncestorsTraversable(absPath, syscall, p);
3529
+ const k = _strip(absPath);
3530
+ const parent = _statLadder(__pathMod.dirname(absPath));
3531
+ if (parent === undefined) return;
3532
+ if (!_modeAllows(parent, 3)) throw _fsErr("EACCES", syscall, p);
3533
+ if ((Number(parent.mode) & 0o1000) === 0 || Number(cred.uid) === 0 || Number(parent.uid) === Number(cred.uid)) return;
3534
+ if (_createdHere.has(k)) return;
3535
+ const target = _statLadder(absPath, true);
3536
+ if (target === undefined) return;
3537
+ if (Number(target.uid) !== Number(cred.uid)) throw _fsErr("EPERM", syscall, p);
3538
+ }
3539
+
3540
+ function _unlinkQueued(p) {
2780
3541
  const absPath = _resolve(p);
2781
3542
  const k = _strip(absPath);
3543
+ _ensureRemovable(absPath, "unlink", p);
2782
3544
  if (__vfsBundle) delete __vfsBundle[k];
2783
3545
  if (__vfsWrites) delete __vfsWrites[k];
2784
3546
  _forgetSyncPath(k);
3547
+ return _queueStructuralMutation(absPath, "unlink", p, (supervisor) => supervisor.unlink(absPath));
2785
3548
  }
3549
+ function unlinkSync(p) { _detachStructuralMutation(_unlinkQueued(p)); }
2786
3550
 
2787
3551
  // ── rmdirSync ──
2788
- function rmdirSync(p) {
3552
+ function _rmdirQueued(p) {
2789
3553
  const absPath = _resolve(p);
2790
3554
  const k = _strip(absPath);
3555
+ _ensureRemovable(absPath, "rmdir", p);
2791
3556
  if (__vfsDirs) delete __vfsDirs[k];
2792
3557
  _forgetSyncPath(k);
3558
+ return _queueStructuralMutation(
3559
+ absPath, "rmdir", p,
3560
+ (supervisor) => supervisor.rmdir(absPath),
3561
+ () => __nimbusAwaitSubtreeMutations(absPath),
3562
+ );
2793
3563
  }
3564
+ function rmdirSync(p) { _detachStructuralMutation(_rmdirQueued(p)); }
2794
3565
 
2795
3566
  // ── renameSync ──
2796
- function renameSync(oldP, newP) {
2797
- const oldK = _strip(_resolve(oldP));
2798
- const newK = _strip(_resolve(newP));
3567
+ function _renameQueued(oldP, newP) {
3568
+ const oldAbs = _resolve(oldP);
3569
+ const newAbs = _resolve(newP);
3570
+ // The name leaves its directory and lands in another (replacing what is
3571
+ // there): both are removals by POSIX's rule.
3572
+ _ensureRemovable(oldAbs, "rename", oldP);
3573
+ if (_statLadder(newAbs, true) !== undefined) _ensureRemovable(newAbs, "rename", newP);
3574
+ else _ensureWritable(newAbs, "rename", newP);
3575
+ const oldK = _strip(oldAbs);
3576
+ const newK = _strip(newAbs);
3577
+ // The table still holds the old name until the rename is reported, so the
3578
+ // new name reads through to it and the old one reads as gone.
3579
+ const nsFrom = _nsRealKey(oldK);
3580
+ // What this process created travels with the name; what it did not stays
3581
+ // the authority's, whatever the move does to the local tables.
3582
+ const oldPrefix = oldK + "/";
3583
+ const createdMoved = [..._createdHere]
3584
+ .filter((key) => key === oldK || key.startsWith(oldPrefix))
3585
+ .map((key) => newK + key.slice(oldK.length));
2799
3586
  const content = __vfsBundle?.[oldK] ?? __vfsWrites?.[oldK];
2800
3587
  if (content !== undefined) {
2801
3588
  _parkWrite(newK, content);
2802
3589
  if (__vfsBundle) delete __vfsBundle[oldK];
2803
3590
  delete __vfsBundleRevisions[oldK];
2804
3591
  delete __vfsWrites[oldK];
2805
- // The stat record travels with the content: dropping it would lose the
2806
- // mode/ownership, and leaving it behind would keep the source path
2807
- // reporting as an existing file this rename already moved away.
2808
- const metadata = _metadataTable();
2809
- const moved = metadata ? metadata[oldK] : undefined;
2810
3592
  _forgetSyncPath(oldK);
2811
- if (moved) metadata[newK] = moved;
3593
+ } else if (__vfsDirs && oldK in __vfsDirs) {
3594
+ // A directory this process made travels under its new name, so the
3595
+ // sync view stops listing the old one and _announceLocalDirs cannot
3596
+ // re-create it at the authority behind the rename.
3597
+ const prefix = oldK + "/";
3598
+ for (const dk of Object.keys(__vfsDirs)) {
3599
+ if (dk !== oldK && !dk.startsWith(prefix)) continue;
3600
+ const moved = newK + dk.slice(oldK.length);
3601
+ __vfsDirs[moved] = true;
3602
+ delete __vfsDirs[dk];
3603
+ if (_announcedDirs.has(dk)) _announcedDirs.add(moved);
3604
+ }
3605
+ _forgetSyncTree(oldK);
3606
+ }
3607
+ _forgetCreation(oldK);
3608
+ _forgetCreation(newK);
3609
+ for (const key of createdMoved) _createdHere.add(key);
3610
+ for (const [ok, entry] of [..._nsOwn]) {
3611
+ if ((ok === oldK || ok.startsWith(oldPrefix)) && entry.state === "dir") {
3612
+ _nsOwnSet(newK + ok.slice(oldK.length), "dir", { hide: entry.hide });
3613
+ }
2812
3614
  }
3615
+ if (nsFrom !== null && !_nsOwn.has(newK)) _nsOwnSet(newK, "alias", { from: nsFrom });
3616
+ _nsOwnSet(oldK, "absentTree");
3617
+ const queued = _queueStructuralMutation(
3618
+ oldAbs, "rename", oldP,
3619
+ (supervisor) => supervisor.rename(oldAbs, newAbs),
3620
+ // The queue orders the move behind the source's ancestors; the move
3621
+ // also needs the destination's ancestors to exist and every pending
3622
+ // mutation beneath the source to have landed under the old name.
3623
+ () => Promise.all([__nimbusAwaitAncestorMutations(newAbs), __nimbusAwaitSubtreeMutations(oldAbs)]),
3624
+ );
3625
+ _fenceVfsMutation(newAbs, queued);
3626
+ return queued;
2813
3627
  }
3628
+ function renameSync(oldP, newP) { _detachStructuralMutation(_renameQueued(oldP, newP)); }
2814
3629
 
2815
3630
  // ── copyFileSync ──
2816
- function copyFileSync(src, dest) {
2817
- writeFileSync(dest, readFileSync(src, "utf8"));
3631
+ // Bytes, not utf8: a utf8 round trip replaces every byte ≥ 0x80 with
3632
+ // U+FFFD, which is how a copied .png or .woff2 arrived corrupted.
3633
+ function copyFileSync(src, dest, mode) {
3634
+ if ((Number(mode) & __fsConstants.COPYFILE_EXCL) !== 0 && existsSync(dest)) {
3635
+ throw _fsErr("EEXIST", "copyfile", dest);
3636
+ }
3637
+ writeFileSync(dest, readFileSync(src));
3638
+ }
3639
+
3640
+ // ── rmSync / rm ──
3641
+ // The local tables are edited at once (the same retraction unlinkSync and
3642
+ // rmdirSync perform, over the whole subtree when recursive) and ONE
3643
+ // authority RPC — fsRemove, which the bridge serves as unlink or a bounded
3644
+ // recursive removal — is queued behind every pending mutation beneath the
3645
+ // path. ENOENT under \`force\` is the authority's to swallow; locally an
3646
+ // unknown path may still exist live, so it is asked rather than answered.
3647
+ function _rmQueued(p, opts, sync) {
3648
+ const o = opts || {};
3649
+ const absPath = _resolve(p);
3650
+ const k = _strip(absPath);
3651
+ const st = statSync(p, { throwIfNoEntry: false });
3652
+ const supervisor = _supervisor();
3653
+ const canRemove = !!supervisor && typeof supervisor.fsRemove === "function";
3654
+ if (st === undefined) {
3655
+ if (!canRemove) {
3656
+ if (o.force) return null;
3657
+ throw _fsErr("ENOENT", "rm", p);
3658
+ }
3659
+ // A path the sync view cannot map may still exist live (born after
3660
+ // boot). The async form asks the authority, whose ENOENT is the real
3661
+ // one; the sync form has no frame to receive that answer, so it gives
3662
+ // statSync's own provisional not-found unless \`force\` makes the
3663
+ // verdict irrelevant.
3664
+ if (sync && !o.force) throw _fsErr("ENOENT", "rm", p);
3665
+ }
3666
+ if (st !== undefined && st.isDirectory() && !o.recursive) {
3667
+ throw _fsErr("ERR_FS_EISDIR", "rm", p);
3668
+ }
3669
+ const prefix = k + "/";
3670
+ // A parked write is content the authority may never have seen: the
3671
+ // caller's file demonstrably existed here, so the removal succeeds, and
3672
+ // the authority is told "if present" rather than made to fail on a file
3673
+ // that was only ever local.
3674
+ let parked = false;
3675
+ if (__vfsBundle) {
3676
+ if (k in __vfsBundle) delete __vfsBundle[k];
3677
+ if (o.recursive) for (const bk of __residentUnder(prefix)) delete __vfsBundle[bk];
3678
+ }
3679
+ delete __vfsBundleRevisions[k];
3680
+ if (__vfsWrites) {
3681
+ for (const wk of Object.keys(__vfsWrites)) {
3682
+ if (wk === k || (o.recursive && wk.startsWith(prefix))) { delete __vfsWrites[wk]; parked = true; }
3683
+ }
3684
+ }
3685
+ if (__vfsDirs) {
3686
+ for (const dk of Object.keys(__vfsDirs)) {
3687
+ if (dk === k || (o.recursive && dk.startsWith(prefix))) delete __vfsDirs[dk];
3688
+ }
3689
+ }
3690
+ if (o.recursive) _forgetSyncTree(k); else _forgetSyncPath(k);
3691
+ if (!canRemove) {
3692
+ // No fsRemove: a plain file still has the unlink RPC.
3693
+ if (st !== undefined && !st.isDirectory()) {
3694
+ return _queueStructuralMutation(absPath, "rm", p, (s) => s.unlink(absPath), undefined, "unlink");
3695
+ }
3696
+ return null;
3697
+ }
3698
+ return _queueStructuralMutation(
3699
+ absPath, "rm", p,
3700
+ (s) => s.fsRemove(absPath, { recursive: !!o.recursive, force: !!o.force || parked }),
3701
+ () => __nimbusAwaitSubtreeMutations(absPath),
3702
+ "fsRemove",
3703
+ );
3704
+ }
3705
+ function rmSync(p, opts) { _detachStructuralMutation(_rmQueued(p, opts, true)); }
3706
+ async function _rmAsync(p, opts) { await _rmQueued(p, opts, false); }
3707
+
3708
+ // ── cpSync ──
3709
+ // A walk of the resident view; every file copy is a parked sync write that
3710
+ // the existing write-back drains, and every directory a queued mkdir.
3711
+ function cpSync(src, dest, opts) {
3712
+ const o = opts || {};
3713
+ const st = statSync(src, { throwIfNoEntry: false });
3714
+ if (st === undefined) throw _fsErr("ENOENT", "cp", src);
3715
+ if (typeof o.filter === "function" && !o.filter(String(src), String(dest))) return;
3716
+ if (!st.isDirectory()) {
3717
+ const destSt = statSync(dest, { throwIfNoEntry: false });
3718
+ if (destSt !== undefined) {
3719
+ if (destSt.isDirectory()) throw _fsErr("EISDIR", "cp", dest);
3720
+ if (o.errorOnExist) throw _fsErr("ERR_FS_CP_EEXIST", "cp", dest);
3721
+ if (o.force === false) return;
3722
+ }
3723
+ copyFileSync(src, dest);
3724
+ return;
3725
+ }
3726
+ if (!o.recursive) throw _fsErr("ERR_FS_EISDIR", "cp", src);
3727
+ mkdirSync(dest, { recursive: true });
3728
+ for (const ent of readdirSync(src, { withFileTypes: true })) {
3729
+ cpSync(__pathMod.join(String(src), ent.name), __pathMod.join(String(dest), ent.name), o);
3730
+ }
3731
+ }
3732
+
3733
+ // ── mkdtemp ──
3734
+ // Node appends six characters from [a-zA-Z0-9]; a collision with a name
3735
+ // the sync view already knows is retried rather than reused.
3736
+ const _MKDTEMP_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
3737
+ function _mkdtempName(prefix) {
3738
+ for (let attempt = 0; attempt < 64; attempt++) {
3739
+ let suffix = "";
3740
+ for (let i = 0; i < 6; i++) suffix += _MKDTEMP_ALPHABET[Math.floor(Math.random() * _MKDTEMP_ALPHABET.length)];
3741
+ const name = String(prefix) + suffix;
3742
+ if (!existsSync(name)) return name;
3743
+ }
3744
+ throw _fsErr("EEXIST", "mkdtemp", String(prefix) + "XXXXXX");
3745
+ }
3746
+ function mkdtempSync(prefix) {
3747
+ const name = _mkdtempName(prefix);
3748
+ mkdirSync(name);
3749
+ return name;
3750
+ }
3751
+ async function _mkdtempAsync(prefix) {
3752
+ const name = _mkdtempName(prefix);
3753
+ await _mkdirAsync(name);
3754
+ return name;
3755
+ }
3756
+
3757
+ // ── truncateSync ──
3758
+ // The resident-view rule of ftruncateSync applied to a path: trim the
3759
+ // resident cell and park it, refuse EAGAIN when the bytes are not here.
3760
+ function truncateSync(p, len) {
3761
+ const absPath = _resolve(p);
3762
+ const st = statSync(p, { throwIfNoEntry: false });
3763
+ if (st === undefined) throw _fsErr("ENOENT", "truncate", p);
3764
+ if (st.isDirectory()) throw _fsErr("EISDIR", "truncate", p);
3765
+ const size = Math.max(0, Math.trunc(Number(len) || 0));
3766
+ _ensureWritable(absPath, "truncate", p);
3767
+ const base = _residentWriteBase(absPath, p, "truncate");
3768
+ const next = new Uint8Array(size);
3769
+ next.set(base.subarray(0, Math.min(size, base.byteLength)), 0);
3770
+ _parkWrite(_strip(absPath), next);
3771
+ _markVfsStale();
2818
3772
  }
2819
3773
 
3774
+ // ── link / linkSync ──
3775
+ // The VFS has no hard links and a copy would lie about sharing an inode,
3776
+ // so both forms answer ENOSYS like fs.promises.link always has.
3777
+ function linkSync(existingPath, newPath) { throw _fsErr("ENOSYS", "link", newPath); }
3778
+
2820
3779
  // ── realpathSync (X.5-T per X5Z5-plan §4.3 + X526b-retro §3.1) ──
2821
3780
  // Sync realpath stays local and identity-resolves. Async symlink
2822
3781
  // operations use the live supervisor bridge below.
2823
3782
  // .native static is required by TypeScript's getNodeSystem at
2824
- function realpathSync(p, opts) { return _resolve(String(p)); }
3783
+ function realpathSync(p, opts) {
3784
+ const absPath = _resolve(String(p));
3785
+ _nsRequire("realpath", p, "fs.promises.realpath");
3786
+ const k = _strip(absPath);
3787
+ const meta = _nsMeta(k, true);
3788
+ if (meta === "ELOOP") throw _fsErr("ELOOP", "realpath", p);
3789
+ if (meta === "absent") throw _fsErr("ENOENT", "realpath", p);
3790
+ const real = _nsRealKey(k);
3791
+ return real === null ? absPath : "/" + real;
3792
+ }
2825
3793
  realpathSync.native = realpathSync;
2826
3794
 
2827
3795
  // ── Async variants (thin wrappers returning via callback) ──
@@ -2849,6 +3817,11 @@ const __fsMod = (() => {
2849
3817
  _mkdirAsync(p, opts).then(() => { if (cb) cb(null); }).catch((e) => { if (cb) cb(e); });
2850
3818
  }
2851
3819
  function unlink(p, cb) { _unlinkAsync(p).then(() => { if (cb) cb(null); }).catch((e) => { if (cb) cb(e); }); }
3820
+ // The callback form proper-lockfile (via graceful-fs) releases its lock directory with.
3821
+ function rmdir(p, opts, cb) {
3822
+ if (typeof opts === "function") { cb = opts; opts = undefined; }
3823
+ _rmdirAsync(p).then(() => { if (cb) cb(null); }).catch((e) => { if (cb) cb(e); });
3824
+ }
2852
3825
  function rename(oldP, newP, cb) { _renameAsync(oldP, newP).then(() => { if (cb) cb(null); }).catch((e) => { if (cb) cb(e); }); }
2853
3826
  function utimes(p, atime, mtime, cb) { _utimesAsync(p, atime, mtime).then(() => { if (cb) cb(null); }).catch((e) => { if (cb) cb(e); }); }
2854
3827
  function lutimes(p, atime, mtime, cb) { _utimesAsync(p, atime, mtime, { followSymlinks: false }).then(() => { if (cb) cb(null); }).catch((e) => { if (cb) cb(e); }); }
@@ -2863,7 +3836,7 @@ const __fsMod = (() => {
2863
3836
  // ── open-flag parsing for fs.promises.open ──
2864
3837
  function _parseOpenFlags(flags) {
2865
3838
  if (typeof flags === "number") {
2866
- const O_WRONLY = 1, O_RDWR = 2, O_CREAT = 64, O_EXCL = 128, O_TRUNC = 512, O_APPEND = 1024;
3839
+ const { O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_TRUNC, O_APPEND, O_DIRECTORY } = __fsConstants;
2867
3840
  return {
2868
3841
  read: (flags & O_WRONLY) === 0,
2869
3842
  write: (flags & (O_WRONLY | O_RDWR)) !== 0,
@@ -2871,15 +3844,16 @@ const __fsMod = (() => {
2871
3844
  create: (flags & O_CREAT) !== 0,
2872
3845
  truncate: (flags & O_TRUNC) !== 0,
2873
3846
  exclusive: (flags & O_EXCL) !== 0,
3847
+ directory: (flags & O_DIRECTORY) !== 0,
2874
3848
  };
2875
3849
  }
2876
3850
  const s = String(flags === undefined || flags === null ? "r" : flags);
2877
3851
  const plus = s.indexOf("+") !== -1;
2878
3852
  const exclusive = s.indexOf("x") !== -1;
2879
3853
  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 };
3854
+ if (base === "w") return { read: plus, write: true, append: false, create: true, truncate: true, exclusive, directory: false };
3855
+ if (base === "a") return { read: plus, write: true, append: true, create: true, truncate: false, exclusive, directory: false };
3856
+ return { read: true, write: plus, append: false, create: false, truncate: false, exclusive, directory: false };
2883
3857
  }
2884
3858
 
2885
3859
  // ── FileHandle — returned from fs.promises.open ──
@@ -2889,6 +3863,35 @@ const __fsMod = (() => {
2889
3863
  // hibernation and partial reads/writes never move whole files. Unflushed
2890
3864
  // sync writes (__vfsWrites) take read precedence; the local sync view is
2891
3865
  // overlaid on writes so readFileSync stays coherent.
3866
+ // The prior content a sync positional write or truncate lays its bytes
3867
+ // over. Shared by the descriptor forms and truncateSync.
3868
+ function _residentWriteBase(absPath, p, syscall) {
3869
+ const cell = _writtenCell(absPath);
3870
+ if (cell !== undefined) {
3871
+ const denial = _denialCode(cell);
3872
+ if (denial) throw _fsErr(denial, syscall, p);
3873
+ _residencySatisfied(absPath);
3874
+ return _asBytes(cell);
3875
+ }
3876
+ const asyncForm = "the async fs." + syscall + "/fs.promises form";
3877
+ const st = statSync(p, { throwIfNoEntry: false });
3878
+ // Non-resident and non-empty: writing onto a zero-filled base would
3879
+ // silently destroy the bytes we cannot see. Refuse instead.
3880
+ if (st !== undefined && st.size !== 0) throw _notResidentError(absPath, p, syscall, asyncForm);
3881
+ // Absent or empty: an empty base is the true prior content (the namespace
3882
+ // names every path this credential can see).
3883
+ return new Uint8Array(0);
3884
+ }
3885
+
3886
+ async function _fsyncAsync(absPath, p) {
3887
+ const supervisor = _supervisor();
3888
+ if (supervisor) {
3889
+ await _flushLocalPathToSupervisor(absPath, supervisor);
3890
+ await _awaitStructuralOrder(absPath);
3891
+ }
3892
+ _markVfsStale();
3893
+ }
3894
+
2892
3895
  let __nextFileHandleFd = 3;
2893
3896
  const __fileHandles = new Map();
2894
3897
  class __FileHandle {
@@ -2963,16 +3966,21 @@ const __fsMod = (() => {
2963
3966
  );
2964
3967
  if (meta && meta.type === "file") writeAt = Number(meta.size) || 0;
2965
3968
  }
2966
- await _fsRpc(
2967
- supervisor.fsWriteRange(this._abs, writeAt, bytes),
2968
- "write", this._path,
2969
- () => undefined,
3969
+ await _ownMutation(
3970
+ this._abs,
3971
+ () => _fsRpc(
3972
+ supervisor.fsWriteRange(this._abs, writeAt, bytes),
3973
+ "write", this._path,
3974
+ (result) => result,
3975
+ ),
3976
+ () => {
3977
+ const key = _strip(this._abs);
3978
+ if (!Object.prototype.hasOwnProperty.call(__vfsWrites, key) &&
3979
+ __vfsWriteGenerations[key] === overlayGeneration) {
3980
+ _overlayLocalCell(this._abs, writeAt, bytes);
3981
+ }
3982
+ },
2970
3983
  );
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
3984
  });
2977
3985
  _markVfsStale();
2978
3986
  } else {
@@ -3028,24 +4036,7 @@ const __fsMod = (() => {
3028
4036
  if (bytes === undefined) throw this._notResident(syscall);
3029
4037
  return bytes;
3030
4038
  }
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
- }
4039
+ _writeBase(syscall) { return _residentWriteBase(this._abs, this._path, syscall); }
3049
4040
  _commit(next) {
3050
4041
  _parkWrite(_strip(this._abs), next);
3051
4042
  _markVfsStale();
@@ -3095,9 +4086,38 @@ const __fsMod = (() => {
3095
4086
  }
3096
4087
  async chmod(mode) { this._assertOpen("fchmod"); await _chmodAsync(this._path, mode); }
3097
4088
  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() {}
4089
+ async utimes(atime, mtime) { this._assertOpen("futimes"); await _utimesAsync(this._path, atime, mtime, undefined, "futimes"); }
4090
+ // Durability here means: every byte parked for this path has reached
4091
+ // the authority and every mutation queued for it has landed. The bridge
4092
+ // itself is synchronously durable, so no further RPC is owed.
4093
+ async sync() { this._assertOpen("fsync"); await _fsyncAsync(this._abs, this._path); }
4094
+ async datasync() { this._assertOpen("fdatasync"); await _fsyncAsync(this._abs, this._path); }
4095
+ // Scatter/gather over the ranged read/write: one sequential pass per
4096
+ // buffer, stopping at the first short read, positions advanced by hand
4097
+ // when an explicit one was given so the file position stays untouched.
4098
+ async readv(buffers, position) {
4099
+ this._assertOpen("read");
4100
+ let bytesRead = 0;
4101
+ let pos = _isCurrentPos(position) ? null : Number(position);
4102
+ for (const buffer of buffers) {
4103
+ const r = await this.read(buffer, 0, buffer.byteLength, pos);
4104
+ bytesRead += r.bytesRead;
4105
+ if (pos !== null) pos += r.bytesRead;
4106
+ if (r.bytesRead < buffer.byteLength) break;
4107
+ }
4108
+ return { bytesRead, buffers };
4109
+ }
4110
+ async writev(buffers, position) {
4111
+ this._assertOpen("write");
4112
+ let bytesWritten = 0;
4113
+ let pos = _isCurrentPos(position) ? null : Number(position);
4114
+ for (const buffer of buffers) {
4115
+ const r = await this.write(buffer, 0, buffer.byteLength, pos);
4116
+ bytesWritten += r.bytesWritten;
4117
+ if (pos !== null) pos += r.bytesWritten;
4118
+ }
4119
+ return { bytesWritten, buffers };
4120
+ }
3101
4121
  async close() { this._assertOpen("close"); this._closed = true; __fileHandles.delete(this.fd); }
3102
4122
  [Symbol.asyncDispose]() { return this.close(); }
3103
4123
  }
@@ -3118,9 +4138,14 @@ const __fsMod = (() => {
3118
4138
  if (localStat && localStat.isDirectory()) throw _fsErr("EISDIR", "open", path);
3119
4139
  }
3120
4140
  const exists = !!liveMeta || !!localStat;
4141
+ if (exists && fl.directory) throw _fsErr("ENOTDIR", "open", path);
3121
4142
  if (!exists && !fl.create) throw _fsErr("ENOENT", "open", path);
3122
4143
  if (exists && fl.create && fl.exclusive) throw _fsErr("EEXIST", "open", path);
3123
4144
  let size = liveMeta ? (Number(liveMeta.size) || 0) : (localStat ? localStat.size : 0);
4145
+ // The authority answered, so what it said is what the sync view keeps:
4146
+ // its stat when something is there, and when nothing is, the file this
4147
+ // open creates is the process's own.
4148
+ if (!liveMeta && supervisor && !localStat) _createdHere.add(_strip(absPath));
3124
4149
  if (!exists) {
3125
4150
  await _writeFileAsync(path, new Uint8Array(0));
3126
4151
  if (mode !== undefined && mode !== null) {
@@ -3141,9 +4166,11 @@ const __fsMod = (() => {
3141
4166
  const st = statSync(path, { throwIfNoEntry: false });
3142
4167
  if (st && st.isDirectory()) throw _fsErr("EISDIR", "open", path);
3143
4168
  const exists = st !== undefined;
4169
+ if (exists && fl.directory) throw _fsErr("ENOTDIR", "open", path);
3144
4170
  if (!exists && !fl.create) throw _fsErr("ENOENT", "open", path);
3145
4171
  if (exists && fl.create && fl.exclusive) throw _fsErr("EEXIST", "open", path);
3146
4172
  if (fl.write || !exists) _ensureWritable(absPath, "open", path);
4173
+ if (!exists) _noteCreation(_strip(absPath));
3147
4174
  let size = exists ? st.size : 0;
3148
4175
  // O_TRUNC means "make it empty", so zeroing is the requested semantic,
3149
4176
  // and creating a genuinely absent file is too. O_APPEND must NEVER
@@ -3261,6 +4288,52 @@ const __fsMod = (() => {
3261
4288
  function fsyncSync(fd) { if (!_isStdioFd(fd)) _fdHandle(fd, "fsync"); _markVfsStale(); }
3262
4289
  function fdatasyncSync(fd) { if (!_isStdioFd(fd)) _fdHandle(fd, "fdatasync"); _markVfsStale(); }
3263
4290
 
4291
+ // Descriptor metadata, sync: the path forms on the handle's path, so the
4292
+ // local overlay (times, modes, ownership) and the parked write-through are
4293
+ // exactly what utimesSync/chmodSync/chownSync give.
4294
+ function futimesSync(fd, atime, mtime) {
4295
+ if (_isStdioFd(fd)) throw _fsErr("EINVAL", "futimes", fd);
4296
+ const handle = _fdHandle(fd, "futimes");
4297
+ _recordLocalTimes(handle._abs, atime, mtime, "futimes", handle._path);
4298
+ }
4299
+ function fchmodSync(fd, mode) {
4300
+ if (_isStdioFd(fd)) throw _fsErr("EINVAL", "fchmod", fd);
4301
+ const handle = _fdHandle(fd, "fchmod");
4302
+ const k = _strip(handle._abs);
4303
+ _ensureModeOwner(handle._abs, "fchmod", handle._path);
4304
+ _localModes[k] = _coerceMode(mode, "fchmod", handle._path);
4305
+ _pendingModes.add(k);
4306
+ }
4307
+ function fchownSync(fd, uid, gid) {
4308
+ if (_isStdioFd(fd)) throw _fsErr("EINVAL", "fchown", fd);
4309
+ const handle = _fdHandle(fd, "fchown");
4310
+ _detachStructuralMutation(_chownQueued(handle._path, uid, gid, undefined, "fchown"));
4311
+ }
4312
+
4313
+ // Scatter/gather, sync: readSync/writeSync per buffer; stdio fds keep the
4314
+ // stream behaviour those two already give them.
4315
+ function readvSync(fd, buffers, position) {
4316
+ let bytesRead = 0;
4317
+ let pos = _isCurrentPos(position) ? null : Number(position);
4318
+ for (const buffer of buffers) {
4319
+ const n = readSync(fd, buffer, 0, buffer.byteLength, pos);
4320
+ bytesRead += n;
4321
+ if (pos !== null) pos += n;
4322
+ if (n < buffer.byteLength) break;
4323
+ }
4324
+ return bytesRead;
4325
+ }
4326
+ function writevSync(fd, buffers, position) {
4327
+ let bytesWritten = 0;
4328
+ let pos = _isCurrentPos(position) ? null : Number(position);
4329
+ for (const buffer of buffers) {
4330
+ const n = writeSync(fd, buffer, 0, buffer.byteLength, pos);
4331
+ bytesWritten += n;
4332
+ if (pos !== null) pos += n;
4333
+ }
4334
+ return bytesWritten;
4335
+ }
4336
+
3264
4337
  // ── callback forms (live I/O — these CAN reach the supervisor) ──
3265
4338
  function open(path, flags, mode, cb) {
3266
4339
  if (typeof flags === "function") { cb = flags; flags = undefined; mode = undefined; }
@@ -3341,21 +4414,56 @@ const __fsMod = (() => {
3341
4414
  if (!handle) return;
3342
4415
  handle.truncate(len).then(() => cb(null)).catch((e) => cb(e));
3343
4416
  }
3344
- function fsync(fd, cb) {
3345
- let err = null;
3346
- try { fsyncSync(fd); } catch (e) { err = e; }
3347
- // Without a callback there is nowhere to deliver the failure, so raise
3348
- // 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) {
4417
+ // The async forms CAN wait for durability: the parked bytes are flushed
4418
+ // and the path's queued mutations awaited before the callback fires.
4419
+ function _fsyncCb(fd, syscall, cb) {
4420
+ let handle = null;
3353
4421
  let err = null;
3354
- try { fdatasyncSync(fd); } catch (e) { err = e; }
4422
+ try { if (!_isStdioFd(fd)) handle = _fdHandle(fd, syscall); } catch (e) { err = e; }
3355
4423
  // Without a callback there is nowhere to deliver the failure, so raise
3356
4424
  // it here rather than let an EBADF vanish.
3357
- if (typeof cb !== "function") { if (err) throw err; return; }
3358
- queueMicrotask(() => cb(err));
4425
+ if (typeof cb !== "function") { if (err) throw err; _markVfsStale(); return; }
4426
+ if (err || !handle) { _markVfsStale(); queueMicrotask(() => cb(err)); return; }
4427
+ _fsyncAsync(handle._abs, handle._path).then(() => cb(null)).catch((e) => cb(e));
4428
+ }
4429
+ function fsync(fd, cb) { _fsyncCb(fd, "fsync", cb); }
4430
+ function fdatasync(fd, cb) { _fsyncCb(fd, "fdatasync", cb); }
4431
+ function futimes(fd, atime, mtime, cb) {
4432
+ if (_isStdioFd(fd)) { queueMicrotask(() => cb(_fsErr("EINVAL", "futimes", fd))); return; }
4433
+ const handle = _fdFor(fd, "futimes", cb);
4434
+ if (!handle) return;
4435
+ handle.utimes(atime, mtime).then(() => cb(null)).catch((e) => cb(e));
4436
+ }
4437
+ function readv(fd, buffers, position, cb) {
4438
+ if (typeof position === "function") { cb = position; position = null; }
4439
+ const run = async () => {
4440
+ let bytesRead = 0;
4441
+ let pos = _isCurrentPos(position) ? null : Number(position);
4442
+ for (const buffer of buffers) {
4443
+ const n = await new Promise((res, rej) =>
4444
+ read(fd, buffer, 0, buffer.byteLength, pos, (e, count) => (e ? rej(e) : res(count))));
4445
+ bytesRead += n;
4446
+ if (pos !== null) pos += n;
4447
+ if (n < buffer.byteLength) break;
4448
+ }
4449
+ return bytesRead;
4450
+ };
4451
+ run().then((n) => cb(null, n, buffers)).catch((e) => cb(e));
4452
+ }
4453
+ function writev(fd, buffers, position, cb) {
4454
+ if (typeof position === "function") { cb = position; position = null; }
4455
+ const run = async () => {
4456
+ let bytesWritten = 0;
4457
+ let pos = _isCurrentPos(position) ? null : Number(position);
4458
+ for (const buffer of buffers) {
4459
+ const n = await new Promise((res, rej) =>
4460
+ write(fd, buffer, 0, buffer.byteLength, pos, (e, count) => (e ? rej(e) : res(count))));
4461
+ bytesWritten += n;
4462
+ if (pos !== null) pos += n;
4463
+ }
4464
+ return bytesWritten;
4465
+ };
4466
+ run().then((n) => cb(null, n, buffers)).catch((e) => cb(e));
3359
4467
  }
3360
4468
  function fchmod(fd, mode, cb) {
3361
4469
  const handle = _fdFor(fd, "fchmod", cb);
@@ -3368,6 +4476,135 @@ const __fsMod = (() => {
3368
4476
  handle.chown(uid, gid).then(() => cb(null)).catch((error) => cb(error));
3369
4477
  }
3370
4478
 
4479
+ // ── Dir — fs.opendir / opendirSync / fs.promises.opendir ──
4480
+ // The listing is taken whole at open (both readdir forms already return
4481
+ // one) and handed out one Dirent per read(); closing a closed Dir is
4482
+ // ERR_DIR_CLOSED as in Node.
4483
+ class __Dir {
4484
+ constructor(path, entries) {
4485
+ this.path = path;
4486
+ this._entries = entries;
4487
+ this._at = 0;
4488
+ this._closed = false;
4489
+ }
4490
+ _assertOpen() {
4491
+ if (this._closed) {
4492
+ const err = new Error("Directory handle was closed");
4493
+ err.code = "ERR_DIR_CLOSED";
4494
+ throw err;
4495
+ }
4496
+ }
4497
+ readSync() {
4498
+ this._assertOpen();
4499
+ return this._at < this._entries.length ? this._entries[this._at++] : null;
4500
+ }
4501
+ read(cb) {
4502
+ const result = new Promise((res, rej) => { try { res(this.readSync()); } catch (e) { rej(e); } });
4503
+ if (typeof cb !== "function") return result;
4504
+ result.then((entry) => cb(null, entry), (e) => cb(e));
4505
+ }
4506
+ closeSync() { this._assertOpen(); this._closed = true; }
4507
+ close(cb) {
4508
+ const result = new Promise((res, rej) => { try { this.closeSync(); res(); } catch (e) { rej(e); } });
4509
+ if (typeof cb !== "function") return result;
4510
+ result.then(() => cb(null), (e) => cb(e));
4511
+ }
4512
+ async *[Symbol.asyncIterator]() {
4513
+ try {
4514
+ for (;;) {
4515
+ const entry = this.readSync();
4516
+ if (entry === null) break;
4517
+ yield entry;
4518
+ }
4519
+ } finally {
4520
+ if (!this._closed) this._closed = true;
4521
+ }
4522
+ }
4523
+ [Symbol.asyncDispose]() { return this._closed ? Promise.resolve() : this.close(); }
4524
+ [Symbol.dispose]() { if (!this._closed) this.closeSync(); }
4525
+ }
4526
+ function opendirSync(p, opts) {
4527
+ return new __Dir(String(p), readdirSync(p, { withFileTypes: true }));
4528
+ }
4529
+ async function _opendirAsync(p, opts) {
4530
+ return new __Dir(String(p), await _readdirAsync(p, { withFileTypes: true }));
4531
+ }
4532
+ function opendir(p, opts, cb) {
4533
+ if (typeof opts === "function") { cb = opts; opts = undefined; }
4534
+ _opendirAsync(p, opts).then((dir) => cb(null, dir)).catch((e) => cb(e));
4535
+ }
4536
+
4537
+ // ── statfs ──
4538
+ // Honest constants for the VFS: the block size is the SQLite chunk unit's
4539
+ // page size, and blocks is the configured VFS capacity in those blocks.
4540
+ // The supervisor surface exposes no usage counter, so bfree/bavail report
4541
+ // the whole capacity; the VFS has no inode table, so files/ffree are 0
4542
+ // (unknown, not "none"). \`type\` is 0: this is no kernel filesystem and
4543
+ // no magic number would be true of it.
4544
+ const _STATFS_BSIZE = 4096;
4545
+ const _STATFS_BLOCKS = Math.floor(${VFS_CAPACITY} / 4096);
4546
+ function _statfsObject(opts) {
4547
+ const wrap = opts && opts.bigint ? BigInt : Number;
4548
+ return {
4549
+ type: wrap(0),
4550
+ bsize: wrap(_STATFS_BSIZE),
4551
+ blocks: wrap(_STATFS_BLOCKS),
4552
+ bfree: wrap(_STATFS_BLOCKS),
4553
+ bavail: wrap(_STATFS_BLOCKS),
4554
+ files: wrap(0),
4555
+ ffree: wrap(0),
4556
+ };
4557
+ }
4558
+ function statfsSync(p, opts) {
4559
+ if (statSync(p, { throwIfNoEntry: false }) === undefined) throw _fsErr("ENOENT", "statfs", p);
4560
+ return _statfsObject(opts);
4561
+ }
4562
+ async function _statfsAsync(p, opts) {
4563
+ await _statAsync(p);
4564
+ return _statfsObject(opts);
4565
+ }
4566
+ function statfs(p, opts, cb) {
4567
+ if (typeof opts === "function") { cb = opts; opts = undefined; }
4568
+ _statfsAsync(p, opts).then((s) => cb(null, s)).catch((e) => cb(e));
4569
+ }
4570
+
4571
+ // ── remaining callback forms of the path operations ──
4572
+ function rm(p, opts, cb) {
4573
+ if (typeof opts === "function") { cb = opts; opts = undefined; }
4574
+ _rmAsync(p, opts).then(() => cb(null)).catch((e) => cb(e));
4575
+ }
4576
+ function cp(src, dest, opts, cb) {
4577
+ if (typeof opts === "function") { cb = opts; opts = undefined; }
4578
+ promises.cp(src, dest, opts).then(() => cb(null)).catch((e) => cb(e));
4579
+ }
4580
+ function truncate(p, len, cb) {
4581
+ if (typeof len === "function") { cb = len; len = 0; }
4582
+ _truncateAsync(p, len || 0).then(() => cb(null)).catch((e) => cb(e));
4583
+ }
4584
+ function copyFile(src, dest, mode, cb) {
4585
+ if (typeof mode === "function") { cb = mode; mode = 0; }
4586
+ promises.copyFile(src, dest, mode).then(() => cb(null)).catch((e) => cb(e));
4587
+ }
4588
+ function mkdtemp(prefix, opts, cb) {
4589
+ if (typeof opts === "function") { cb = opts; opts = undefined; }
4590
+ _mkdtempAsync(prefix).then((name) => cb(null, name)).catch((e) => cb(e));
4591
+ }
4592
+ function link(existingPath, newPath, cb) { queueMicrotask(() => cb(_fsErr("ENOSYS", "link", newPath))); }
4593
+ function symlink(target, path, type, cb) {
4594
+ if (typeof type === "function") { cb = type; type = undefined; }
4595
+ _symlinkAsync(target, path).then(() => cb(null)).catch((e) => cb(e));
4596
+ }
4597
+ function readlink(p, opts, cb) {
4598
+ if (typeof opts === "function") { cb = opts; opts = undefined; }
4599
+ _readlinkAsync(p).then((target) => cb(null, target)).catch((e) => cb(e));
4600
+ }
4601
+ function realpath(p, opts, cb) {
4602
+ if (typeof opts === "function") { cb = opts; opts = undefined; }
4603
+ promises.realpath(p, opts).then((r) => cb(null, r)).catch((e) => cb(e));
4604
+ }
4605
+ realpath.native = realpath;
4606
+ function lchmod(p, mode, cb) { chmod(p, mode, cb); }
4607
+
3371
4608
  // ── promises namespace (W3: full surface, VFS-backed) ──
3372
4609
  // We can't forward to workerd's node:fs/promises because that operates
3373
4610
  // on a real-host filesystem, not our VFS. So every method is shim'd
@@ -3385,22 +4622,8 @@ const __fsMod = (() => {
3385
4622
  // W3 additions:
3386
4623
  appendFile: async (p, d, o) => { await _appendFileAsync(p, d, o); },
3387
4624
  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
- },
4625
+ // The same local retraction and queued authority removal rmSync does.
4626
+ rm: async (p, opts) => { await _rmAsync(p, opts); },
3404
4627
  cp: async (src, dest, opts) => {
3405
4628
  const o = opts || {};
3406
4629
  const srcAbs = _resolve(src);
@@ -3419,15 +4642,16 @@ const __fsMod = (() => {
3419
4642
  // not just the local cache — is required so a subsequent async fs op
3420
4643
  // (e.g. fs.promises.rename of a copied file, as create-cloudflare
3421
4644
  // does for __dot__gitignore) sees the copy in the VFS instead of
3422
- // ENOENT.
3423
- __vfsDirs[destK] = true;
4645
+ // ENOENT. Each directory is made as mkdir makes it, so the namespace's
4646
+ // overlay lists it for the writes beneath it.
4647
+ await _mkdirAsync("/" + destK, { recursive: true });
3424
4648
  const walk = async (relDir) => {
3425
4649
  const absDir = relDir ? srcAbs + "/" + relDir : srcAbs;
3426
4650
  const ents = await _readdirAsync(absDir, { withFileTypes: true });
3427
4651
  for (const ent of ents) {
3428
4652
  const rel = relDir ? relDir + "/" + ent.name : ent.name;
3429
4653
  if (ent.isDirectory && ent.isDirectory()) {
3430
- __vfsDirs[destK + "/" + rel] = true;
4654
+ await _mkdirAsync("/" + destK + "/" + rel, { recursive: true });
3431
4655
  await walk(rel);
3432
4656
  } else {
3433
4657
  await _writeFileAsync("/" + destK + "/" + rel, await _readFileAsync(srcAbs + "/" + rel));
@@ -3436,7 +4660,12 @@ const __fsMod = (() => {
3436
4660
  };
3437
4661
  await walk("");
3438
4662
  },
3439
- copyFile: async (src, dest) => { await _writeFileAsync(dest, await _readFileAsync(src)); },
4663
+ copyFile: async (src, dest, mode) => {
4664
+ if ((Number(mode) & __fsConstants.COPYFILE_EXCL) !== 0 && await _existsAsync(dest)) {
4665
+ throw _fsErr("EEXIST", "copyfile", dest);
4666
+ }
4667
+ await _writeFileAsync(dest, await _readFileAsync(src));
4668
+ },
3440
4669
  rename: async (oldP, newP) => { await _renameAsync(oldP, newP); },
3441
4670
  rmdir: async (p) => { await _rmdirAsync(p); },
3442
4671
  realpath: async (p) => __pathMod.resolve(String(p)),
@@ -3450,12 +4679,10 @@ const __fsMod = (() => {
3450
4679
  symlink: async (target, path) => { await _symlinkAsync(target, path); },
3451
4680
  link: async () => { throw _fsErr("ENOSYS", "link", ""); },
3452
4681
  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
- },
4682
+ mkdtemp: async (prefix) => _mkdtempAsync(prefix),
3458
4683
  open: async (path, flags, mode) => _openAsync(path, flags, mode),
4684
+ opendir: async (p, opts) => _opendirAsync(p, opts),
4685
+ statfs: async (p, opts) => _statfsAsync(p, opts),
3459
4686
  watch: async function* (filename, opts) {
3460
4687
  // Minimal async iter — polls _bundleLookup every 500ms and yields
3461
4688
  // a single \`change\` event when content differs. Adequate for
@@ -3500,8 +4727,9 @@ const __fsMod = (() => {
3500
4727
  },
3501
4728
  };
3502
4729
 
3503
- // ── constants ──
3504
- const constants = { F_OK: 0, R_OK: 4, W_OK: 2, X_OK: 1 };
4730
+ // ── constants ── the shared linux x64 table (see __fsConstants).
4731
+ const constants = __fsConstants;
4732
+ promises.constants = constants;
3505
4733
 
3506
4734
  // fs.ReadStream / fs.WriteStream classes. Real Node exposes these as
3507
4735
  // constructors; graceful-fs (bundled by degit → create-cloudflare)
@@ -3591,10 +4819,15 @@ const __fsMod = (() => {
3591
4819
  const __fsExports = {
3592
4820
  readFileSync, writeFileSync, appendFileSync, existsSync, statSync, lstatSync,
3593
4821
  readdirSync, mkdirSync, unlinkSync, rmdirSync, renameSync, copyFileSync,
3594
- realpathSync, utimesSync, lutimesSync, chmodSync, accessSync,
4822
+ realpathSync, utimesSync, lutimesSync, chmodSync, lchmodSync, chownSync, lchownSync, accessSync,
4823
+ rmSync, cpSync, mkdtempSync, truncateSync, linkSync, opendirSync, statfsSync,
3595
4824
  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,
4825
+ futimesSync, fchmodSync, fchownSync, readvSync, writevSync,
4826
+ open, close, read, write, fstat, ftruncate, fsync, fdatasync, fchmod, futimes, readv, writev,
4827
+ readFile, writeFile, appendFile, stat, lstat, readdir, exists, mkdir, unlink, rmdir, rename, utimes, lutimes, chmod, lchmod, chown, lchown, fchown, access,
4828
+ rm, cp, truncate, copyFile, mkdtemp, link, symlink, readlink, realpath, opendir, statfs,
4829
+ Dirent: __Dirent,
4830
+ Dir: __Dir,
3598
4831
  promises, constants,
3599
4832
  createReadStream: (p, opts) => new (__getReadStream())(p, opts),
3600
4833
  createWriteStream: (p, opts) => {
@@ -3768,8 +5001,7 @@ const __NimbusRelayedWebSocket = (() => {
3768
5001
  // The barrier the whole relay exists for. Every frame is now a
3769
5002
  // supervisor reply, so it can carry the invalidation delta, and
3770
5003
  // user code runs only after the resident set has caught up.
3771
- const acquire = globalThis.__nimbusVfsAcquireBarrier;
3772
- if (typeof acquire === "function") await acquire();
5004
+ await __nimbusInboundBarrier();
3773
5005
  this._deliver(event);
3774
5006
  }
3775
5007
  }
@@ -3936,13 +5168,10 @@ globalThis.WebSocket = __NimbusRelayedWebSocket;
3936
5168
  //
3937
5169
  // require('node:constants') (and the legacy bare require('constants'))
3938
5170
  // expose a FLAT object of POSIX/Linux/OpenSSL constants. Real Node ships
3939
- // ~234 constants; we ship the ones that actually get touched by:
5171
+ // ~234 constants; the full fs slice comes from __fsConstants, and the rest
5172
+ // is what actually gets touched by:
3940
5173
  // - create-next-app (touches UV_FS_O_FILEMAP — verified via grep on
3941
5174
  // 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
5175
  // - signal/errno tables (shared shape with __osMod.constants.signals
3947
5176
  // etc. — flat here, nested there; both shapes are real-Node-accurate
3948
5177
  // and we expose both via the right module).
@@ -3987,26 +5216,10 @@ const __constantsMod = {
3987
5216
  SIGTSTP: 20, SIGTTIN: 21, SIGTTOU: 22, SIGURG: 23, SIGXCPU: 24,
3988
5217
  SIGXFSZ: 25, SIGVTALRM: 26, SIGPROF: 27, SIGWINCH: 28, SIGIO: 29,
3989
5218
  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,
5219
+ // ── fs constants (O_*, S_IF*, S_I*, F_OK.., COPYFILE_*, UV_*) ────
5220
+ // Node's constants module is the union of os, fs and crypto constants;
5221
+ // the fs slice is __fsConstants, the same object fs.constants exposes.
5222
+ ...__fsConstants,
4010
5223
  // ── OpenSSL / TLS option flags ────────────────────────────────────
4011
5224
  // Numeric values from real Node v20. Libraries probe defined-ness;
4012
5225
  // we ship the surface so constants.SSL_OP_* doesn't undefined-throw.
@@ -4191,9 +5404,14 @@ ${UNDICI_SHIM_CODE}
4191
5404
  // ── util module ────────────────────────────────────────────────────
4192
5405
  // ═══════════════════════════════════════════════════════════════════════
4193
5406
  const __utilMod = {
4194
- inspect: (o, opts) => { try { return JSON.stringify(o, null, 2); } catch { return String(o); } },
4195
- format: (fmt, ...a) => {
4196
- if (typeof fmt !== "string") return [fmt, ...a].map(__fmt).join(" ");
5407
+ inspect: (o, opts) => {
5408
+ if (o !== null && typeof o === "object" && __isErrorValue(o)) return __fmtError(o, new Set());
5409
+ try { return JSON.stringify(o, null, 2); } catch { return String(o); }
5410
+ },
5411
+ format: (...args) => {
5412
+ if (args.length === 0) return "";
5413
+ const [fmt, ...a] = args;
5414
+ if (typeof fmt !== "string") return args.map(__fmt).join(" ");
4197
5415
  let i = 0;
4198
5416
  return fmt.replace(/%[sdifjoO%]/g, (m) => {
4199
5417
  if (m === "%%") return "%";
@@ -4202,7 +5420,7 @@ const __utilMod = {
4202
5420
  if (m === "%s") return String(v);
4203
5421
  if (m === "%d" || m === "%i" || m === "%f") return Number(v).toString();
4204
5422
  if (m === "%j") { try { return JSON.stringify(v); } catch { return "[Circular]"; } }
4205
- if (m === "%o" || m === "%O") { try { return JSON.stringify(v, null, 2); } catch { return String(v); } }
5423
+ if (m === "%o" || m === "%O") return __utilMod.inspect(v);
4206
5424
  return String(v);
4207
5425
  }) + (i < a.length ? " " + a.slice(i).map(__fmt).join(" ") : "");
4208
5426
  },
@@ -4469,11 +5687,31 @@ const __utilMod = {
4469
5687
  // (set+restored by __loadModule per call). When set, the fallback becomes
4470
5688
  // "file:///" + __currentModulePath so relative URLs resolve against
4471
5689
  // the real on-VFS module location — restoring proper import.meta.url
5690
+ //
5691
+ // The leniency is scoped to calls that PASSED a base. \`new URL(x)\` with one
5692
+ // argument is Node's strict absolute-URL parse: it throws
5693
+ // TypeError [ERR_INVALID_URL] for anything that is not already a URL, and
5694
+ // that throw is load-bearing rather than incidental. Node's own ESM
5695
+ // resolver — and every vendored copy of it, including exsolve, which is what
5696
+ // \`nuxt dev\` resolves \`@nuxt/kit\` and \`nuxt\` with — spells the
5697
+ // bare-specifier test as
5698
+ // try { resolved = new URL(specifier); } catch { packageResolve(…); }
5699
+ // Swallowing the throw made \`new URL("@nuxt/kit")\` answer
5700
+ // file:///@nuxt/kit, so moduleResolve never reached packageResolve and
5701
+ // finalizeResolution stat'd /@nuxt/kit — the FILESYSTEM ROOT — instead of
5702
+ // walking <from>/node_modules. Two syscalls and every bare import in the
5703
+ // project was unresolvable. The bundler breakage this wrapper exists for
5704
+ // (\`new URL(rel, import.meta.url)\` where the rolldown/esbuild polyfill
5705
+ // reduced import.meta.url to null/undefined) is always a two-argument call,
5706
+ // so requiring the base argument keeps that fix and restores Node's
5707
+ // contract for the one-argument form. URL.canParse, which is bound
5708
+ // straight off the native constructor below, already answered false for
5709
+ // these strings; the constructor now agrees with it.
4472
5710
  (() => {
4473
5711
  const _Orig = globalThis.URL;
4474
5712
  class _Shim extends _Orig {
4475
5713
  constructor(input, base) {
4476
- if (base == null && typeof input === "string") {
5714
+ if (arguments.length >= 2 && base == null && typeof input === "string") {
4477
5715
  try { super(input); return; }
4478
5716
  catch {
4479
5717
  // X.5-M3: prefer current module path when known, so
@@ -5055,6 +6293,13 @@ const __childProcessMod = (() => {
5055
6293
  /**
5056
6294
  * Read-loop for a single fd. Long-polls cpReadOutput, pushes chunks
5057
6295
  * into the Readable via .push, handles closure.
6296
+ *
6297
+ * What the child printed, and that it closed the stream, is news from
6298
+ * another process, which may have written files before it printed. So a
6299
+ * reply that delivers anything takes the barrier first, and a 'data'
6300
+ * handler that reads what the child wrote reads it current. The poll sends
6301
+ * this process's ACQUIRE arguments and the reply carries the answer, so the
6302
+ * barrier costs no round trip of its own.
5058
6303
  */
5059
6304
  async function _runReadLoop(child, fd, stream, sinceSeqRef) {
5060
6305
  // Exponential backoff for idle children: start at 100ms, double up
@@ -5067,12 +6312,14 @@ const __childProcessMod = (() => {
5067
6312
  while (HAS_SUPERVISOR && child.pid && !child._streamsClosed) {
5068
6313
  try {
5069
6314
  const r = await __nimbusUseRpcResult(
5070
- __supervisor.cpReadOutput(child.pid, fd, sinceSeqRef.value, backoff),
6315
+ __supervisor.cpReadOutput(child.pid, fd, sinceSeqRef.value, backoff, __nimbusVfsAcquireArgs()),
5071
6316
  (result) => result,
5072
6317
  );
5073
- if (r && Array.isArray(r.chunks) && r.chunks.length > 0) {
6318
+ const chunks = r && Array.isArray(r.chunks) ? r.chunks : [];
6319
+ if (chunks.length > 0 || (r && r.closed)) await __nimbusInboundBarrier(r.acquired);
6320
+ if (chunks.length > 0) {
5074
6321
  backoff = 100; // reset — child is producing
5075
- for (const c of r.chunks) {
6322
+ for (const c of chunks) {
5076
6323
  // The queue hands back bytes; a Readable given a string would
5077
6324
  // encode it again.
5078
6325
  stream.write(__BufferMod.from(c.data));
@@ -5100,16 +6347,19 @@ const __childProcessMod = (() => {
5100
6347
 
5101
6348
  /**
5102
6349
  * Wait-loop: long-poll cpWait until the child reports exit. Emits
5103
- * 'exit' once stamped.
6350
+ * 'exit' once stamped, behind the barrier: a child's exit is how a parent
6351
+ * learns the files it wrote are there. Answered on the reply, as for the
6352
+ * child's output.
5104
6353
  */
5105
6354
  async function _runWaitLoop(child) {
5106
6355
  while (HAS_SUPERVISOR && child.pid && !child._exitFired) {
5107
6356
  try {
5108
6357
  const r = await __nimbusUseRpcResult(
5109
- __supervisor.cpWait(child.pid, 1000),
6358
+ __supervisor.cpWait(child.pid, 1000, __nimbusVfsAcquireArgs()),
5110
6359
  (result) => result,
5111
6360
  );
5112
6361
  if (r && r.done) {
6362
+ await __nimbusInboundBarrier(r.acquired);
5113
6363
  child.exitCode = r.exitCode;
5114
6364
  child.signalCode = r.signal;
5115
6365
  child._exitFired = true;
@@ -5290,7 +6540,7 @@ const __childProcessMod = (() => {
5290
6540
  * stdout/stderr/exit events fire. Callers like cross-spawn.sync
5291
6541
  * that read result.status get null until the spawn settles.
5292
6542
  * 3. When the parent facet's main drain settles __pendingIO before
5293
- * reportExit (facet-manager.ts), the result object's fields are
6543
+ * reportExit (facets/manager.ts), the result object's fields are
5294
6544
  * filled in by the time the supervisor sees the parent exit.
5295
6545
  *
5296
6546
  * Cross-spawn.sync's typical pattern is "const r = spawnSync(...);
@@ -5633,7 +6883,7 @@ function __makeProcessStdin() {
5633
6883
  // facet, so counting it as in-flight work would mean the entry drain
5634
6884
  // never sees the program finish.
5635
6885
  packet = await __nimbusUseRpcResultUnref(
5636
- __supervisor.cpReadStdin(liveChildPid, 1000),
6886
+ __supervisor.cpReadStdin(liveChildPid, 1000, __nimbusVfsAcquireArgs()),
5637
6887
  (result) => result,
5638
6888
  );
5639
6889
  readFailures = 0;
@@ -5650,6 +6900,18 @@ function __makeProcessStdin() {
5650
6900
  await new Promise((resolve) => setTimeout(resolve, 500));
5651
6901
  continue;
5652
6902
  }
6903
+ // A packet that delivers anything — input, its end, a signal, a resize
6904
+ // — hands the program news from outside it, and whatever sent the news
6905
+ // may have written files first (a shell's \`echo v2 > f\` before the
6906
+ // keystroke, the editor's save before the signal). So it takes the
6907
+ // barrier before any handler sees it. The poll sent this process's
6908
+ // ACQUIRE arguments and the packet carries the answer, so a keystroke
6909
+ // costs no round trip of its own. An empty poll delivers nothing and
6910
+ // costs nothing.
6911
+ if (packet && (packet.resize || packet.signal || packet.ended
6912
+ || (packet.data && packet.data.byteLength > 0))) {
6913
+ await __nimbusInboundBarrier(packet.acquired);
6914
+ }
5653
6915
  if (packet && packet.resize) {
5654
6916
  __nimbusTtyColumns = Number(packet.resize.columns) || __nimbusTtyColumns;
5655
6917
  __nimbusTtyRows = Number(packet.resize.rows) || __nimbusTtyRows;
@@ -5766,6 +7028,7 @@ function __makeProcessOutputStream(streamName) {
5766
7028
  errored: null,
5767
7029
  write(d, enc, cb) {
5768
7030
  if (typeof enc === "function") cb = enc;
7031
+ if (__nimbusProgramStopped) return true;
5769
7032
  // The reported result is text; decode the bytes at this edge only.
5770
7033
  const s = __nimbusOutText(streamName, __nimbusOutBytes(d, enc));
5771
7034
  if (streamName === "stderr") stderr += s;
@@ -5811,6 +7074,8 @@ function __makeProcessOutputStream(streamName) {
5811
7074
  function __nimbusReportProcessExit(code, reason) {
5812
7075
  if (__nimbusProcessExitReported) return;
5813
7076
  __nimbusProcessExitCode = Number(code ?? 0);
7077
+ __nimbusProgramStopped = true;
7078
+ try { if (typeof globalThis.__nimbusStopProgramTimers === "function") globalThis.__nimbusStopProgramTimers(); } catch {}
5814
7079
  try { if (__nimbusProcessExitResolve) __nimbusProcessExitResolve(__nimbusProcessExitCode); } catch {}
5815
7080
  // Generated lifecycle owners defer the terminal supervisor report until
5816
7081
  // their durability boundary has drained. Reporting here would retire the
@@ -5850,7 +7115,7 @@ const __processMod = {
5850
7115
  chdir: (d) => { cwd = __pathMod.resolve(cwd || "/home/user", d); },
5851
7116
  exit: (code) => {
5852
7117
  exitCode = code ?? 0;
5853
- try { __processEvents.emit("exit", exitCode); } catch {}
7118
+ __nimbusEmitExit(exitCode);
5854
7119
  __nimbusReportProcessExit(exitCode, "");
5855
7120
  throw new __ProcessExit(exitCode);
5856
7121
  },
@@ -6264,14 +7529,27 @@ builtins.http = (() => {
6264
7529
  return new __hostResponse("Nimbus: no HTTP server is listening in this process", { status: 502 });
6265
7530
  }
6266
7531
  const url = new URL(request.url);
7532
+ // The request carries the answer to its own barrier (below). It is
7533
+ // Nimbus's, not the program's, so the program does not see it.
7534
+ let delivered;
7535
+ try { delivered = JSON.parse(request.headers.get("X-Nimbus-Vfs-Acquired") || "null"); } catch {}
6267
7536
  const headers = {};
6268
- request.headers.forEach((v, k) => { headers[k] = v; });
7537
+ request.headers.forEach((v, k) => { if (k !== "x-nimbus-vfs-acquired") headers[k] = v; });
6269
7538
  // Bytes, not text: a UTF-8 decode corrupts every binary upload, and the
6270
7539
  // decoded string is not the TypedArray receiver Buffer methods require.
6271
7540
  let body = null;
6272
7541
  if (request.method !== "GET" && request.method !== "HEAD") {
6273
7542
  body = new Uint8Array(await request.arrayBuffer());
6274
7543
  }
7544
+ // A request is a resumption like any other, and often the second half of
7545
+ // a causal chain — \`echo v2 > f; curl :3000\` — so the handler runs behind
7546
+ // the barrier, after the body has arrived. Sited here, in the dispatch
7547
+ // every resident's server shares, not in a caller that happens to yield
7548
+ // through a barriered timer on the way. The supervisor routed it, so it
7549
+ // arrives with an answer, which is enough when no write preceded the
7550
+ // request since this process last caught up (session/rpc.ts
7551
+ // _acquireForRoutedRequest); otherwise the barrier asks.
7552
+ await __nimbusInboundBarrier(delivered);
6275
7553
  const res = server._handleRequest(url.pathname + url.search, request.method, headers, body);
6276
7554
  // Return once headers are known. A handler that never sends headers is
6277
7555
  // bounded by a header timeout (NOT a body-finish cap) so a hung handler
@@ -7106,38 +8384,12 @@ builtins["node:util/types"] = builtins["util/types"];
7106
8384
  // skips it via the same FACET_PROVIDED_PACKAGES list.
7107
8385
  builtins.undici = __undiciMod;
7108
8386
 
7109
- /**
7110
- * A cell's dynamic import() call.
7111
- *
7112
- * A cell is compiled with new Function, so an import() left in its body
7113
- * is the RUNTIME's: it resolves against the module map and hands back the
7114
- * platform's own builtins, not this process's. Real Vite reaches
7115
- * node:http that way (config.js:14968), got a server whose listen bound no
7116
- * port here, and the facet exited with no handles the moment boot returned.
7117
- *
7118
- * The ESM→CJS rewriter routes those calls here with the cell's own
7119
- * require, so resolution is the one the process already has. The result is
7120
- * shaped as a module namespace, since import() resolves to one and callers
7121
- * destructure named exports off it.
7122
- */
7123
- globalThis.__nimbusCellImport = (req, id) => Promise.resolve().then(() => {
7124
- const loaded = req(id);
7125
- if (loaded && (loaded.__esModule || loaded[Symbol.toStringTag] === "Module")) return loaded;
7126
- // A CJS module's exports ARE the namespace's named exports, with the whole
7127
- // object as the default export — what an ESM importer of a CJS module sees.
7128
- if (loaded && (typeof loaded === "object" || typeof loaded === "function")) {
7129
- const ns = Object.create(null);
7130
- for (const key of Object.keys(loaded)) ns[key] = loaded[key];
7131
- if (!("default" in ns)) ns.default = loaded;
7132
- Object.defineProperty(ns, Symbol.toStringTag, { value: "Module" });
7133
- return ns;
7134
- }
7135
- return { default: loaded };
7136
- });
7137
8387
  // ═══════════════════════════════════════════════════════════════════════
7138
8388
  // ── require() — full Node.js module resolution ─────────────────────
7139
8389
  // ═══════════════════════════════════════════════════════════════════════
7140
8390
  const __moduleCache = new Map();
8391
+ // package → why the package ABI policy says it cannot run here (wasm-swap-registry.ts).
8392
+ const __nimbusAbiAdvisories = new Map(${ABI_ADVISORIES_LITERAL});
7141
8393
 
7142
8394
  /**
7143
8395
  * Direct VFS bundle access for module resolution.
@@ -7163,24 +8415,8 @@ function __readFileOr(path, fallback) {
7163
8415
  }
7164
8416
  function __fileExists(path) {
7165
8417
  const k = path.replace(/^\\/+/, "");
7166
- if (__vfsBundle && k in __vfsBundle) return true;
7167
- if (__vfsWrites && k in __vfsWrites) return true;
7168
- if (__vfsDirs && k in __vfsDirs) return true;
7169
- // Consult the uncapped manifest (directory shape) so resolution sees
7170
- // installed files whose content was excluded from the bounded snapshot
7171
- // (e.g. web-streams-polyfill's ponyfill/package.json, reached via a
7172
- // parent-relative main). Mirrors existsSync's manifest probe.
7173
- if (__vfsManifest) {
7174
- if (k in __vfsManifest) return true;
7175
- const slash = k.lastIndexOf("/");
7176
- const parent = slash >= 0 ? k.slice(0, slash) : "";
7177
- const name = slash >= 0 ? k.slice(slash + 1) : k;
7178
- const sib = __vfsManifest[parent];
7179
- if (sib && sib.indexOf(name) !== -1) return true;
7180
- }
7181
- // Check for directory by looking for any key with this prefix
7182
- if (__vfsBundle && __residentAnyUnder(k + "/")) return true;
7183
- return false;
8418
+ // The namespace answers exactly (vfs/facet-resident-store.ts).
8419
+ return __fsMod.existsSync("/" + k);
7184
8420
  }
7185
8421
  // W3.5 Fix A: strict-file membership probe. __fileExists also returns true for
7186
8422
  // directories (it has to — __resolveNodeModule and __resolveImportsField call
@@ -7192,26 +8428,8 @@ function __fileExists(path) {
7192
8428
  // the fastify ret/dist/types failure.
7193
8429
  function __pathIsFile(path) {
7194
8430
  const k = path.replace(/^\\/+/, "");
7195
- if (__vfsBundle && k in __vfsBundle) return true;
7196
- if (__vfsWrites && k in __vfsWrites) return true;
7197
- // Consult the manifest as a strict-file probe: a name listed in its
7198
- // parent's manifest entry is a file (directories are manifest KEYS).
7199
- // This lets resolution find files whose content was excluded from the
7200
- // bounded snapshot without re-introducing the directory short-circuit
7201
- // (__vfsManifest[k] being a directory key is deliberately NOT matched).
7202
- if (__vfsManifest) {
7203
- const slash = k.lastIndexOf("/");
7204
- const parent = slash >= 0 ? k.slice(0, slash) : "";
7205
- const name = slash >= 0 ? k.slice(slash + 1) : k;
7206
- const sib = __vfsManifest[parent];
7207
- if (sib && sib.indexOf(name) !== -1 && !(k in __vfsManifest)) return true;
7208
- }
7209
- // Last resort: the full fs view (live SQLite VFS) for paths outside the
7210
- // bounded snapshot/manifest entirely — e.g. the /tmp/.npx-cache tree,
7211
- // which the project snapshot never covers. stat-as-file only.
7212
- try { const st = __fsMod.statSync("/" + k); if (st && st.isFile && st.isFile()) return true; } catch {}
7213
- // Deliberately does NOT consult __vfsDirs nor do the prefix scan.
7214
- return false;
8431
+ const st = __fsMod.statSync("/" + k, { throwIfNoEntry: false });
8432
+ return !!st && st.isFile();
7215
8433
  }
7216
8434
  function __resolveFile(base) {
7217
8435
  // Mirrors Node's LOAD_AS_FILE + LOAD_AS_DIRECTORY (require_2 spec):
@@ -7284,15 +8502,12 @@ function __resolveFile(base) {
7284
8502
  }
7285
8503
 
7286
8504
  // __compiledModules is defined at MODULE TOP LEVEL in the generator code
7287
- // (facet-manager.ts) so new Function() runs during module evaluation.
8505
+ // (facets/manager.ts) so new Function() runs during module evaluation.
7288
8506
 
7289
8507
  // ── Single-source-of-truth exports/imports resolver (W2) ───────────────
7290
8508
  // Emitted from src/_shared/exports-resolver.ts via getExportsResolverJS().
7291
8509
  // Declares: resolveExports, resolveConditionValue, resolvePackageEntry,
7292
8510
  // DEFAULT_ESM_CONDITIONS, DEFAULT_CJS_CONDITIONS.
7293
- // the prior hand-rolled __resolvePkgEntry only honoured top-level
7294
- // require|default|import and dropped subpath maps, wildcards, nested
7295
- // conditions, the imports field, and null-target enforcement.
7296
8511
  ${EXPORTS_RESOLVER_JS}
7297
8512
 
7298
8513
  // ── TypeScript specifier fallbacks ────────────────────────────────────────
@@ -7330,8 +8545,8 @@ function __resolvePkgSubpath(pkgDir, pkg, subpath) {
7330
8545
  }
7331
8546
  let entry = resolvePackageEntry(pkg, subpath, __NIMBUS_CJS_CONDITIONS);
7332
8547
  // X.5-F R3: ESM-condition fallback for pure-ESM packages whose
7333
- // dist/.mjs files were transformed to CJS by transformEsmInBundle
7334
- // at install time (facet-manager.ts:842, W3.5 Fix B). Without this,
8548
+ // dist/.mjs files were transformed to CJS
8549
+ // by transformEsmInBundle (facets/manager.ts). Without this,
7335
8550
  // packages like nuxt — whose exports map only contains
7336
8551
  // {types, import} for the root subpath — return null from the CJS
7337
8552
  // walk and dead-end with "Cannot find module 'nuxt'" even though
@@ -7374,10 +8589,6 @@ function __resolvePkgSubpath(pkgDir, pkg, subpath) {
7374
8589
  return __resolveFile(pkgDir + "/" + rel);
7375
8590
  }
7376
8591
 
7377
- /** Back-compat name used elsewhere in this file. */
7378
- function __resolvePkgEntry(pkgDir) {
7379
- return __resolvePkgSubpath(pkgDir, null, ".");
7380
- }
7381
8592
 
7382
8593
  /**
7383
8594
  * Resolve a bare specifier (e.g. "react", "@scope/pkg", "pkg/sub/path")
@@ -7426,32 +8637,23 @@ function __resolveNodeModule(name, fromDir) {
7426
8637
  }
7427
8638
 
7428
8639
  /**
7429
- * Resolve a #name imports-field specifier from the nearest enclosing
7430
- * package.json. Returns the resolved file or null.
8640
+ * Node's "package scope" of a directory (readPackageScope): the nearest
8641
+ * enclosing package.json, walking up from fromDir. The FIRST package.json
8642
+ * found is the scope, even when it lacks the field the caller wants — the
8643
+ * imports field and the self-reference rule both belong to the importing
8644
+ * module's own package, never to an ancestor past it. The walk never
8645
+ * crosses a node_modules directory: a file that sits directly under one
8646
+ * belongs to no package, not to the project above it. Returns { dir, pkg }
8647
+ * (pkg null when the file is unparseable) or null when no package.json
8648
+ * encloses fromDir.
7431
8649
  */
7432
- function __resolveImportsField(name, fromDir) {
7433
- // Walk up looking for the nearest package.json. Stop at the first one
7434
- // (Node spec: imports field of the importing module's package).
8650
+ function __nearestPackageScope(fromDir) {
7435
8651
  let dir = (fromDir || "").replace(/^\\/+/, "");
7436
8652
  while (true) {
8653
+ if (dir === "node_modules" || dir.endsWith("/node_modules")) return null;
7437
8654
  const pkgJsonPath = (dir ? dir + "/" : "") + "package.json";
7438
8655
  if (__fileExists(pkgJsonPath)) {
7439
- const pkg = __readPkgJson(dir);
7440
- if (pkg && pkg.imports) {
7441
- const target = resolveExports(pkg.imports, name, __NIMBUS_CJS_CONDITIONS);
7442
- if (target) {
7443
- // Imports targets are relative to the package root (dir)
7444
- if (target.startsWith("./")) {
7445
- return __resolveFile((dir ? dir + "/" : "") + target.slice(2));
7446
- }
7447
- if (target.startsWith("/")) {
7448
- return __resolveFile(target.slice(1));
7449
- }
7450
- // Bare specifier — re-resolve as a node_module from this dir
7451
- return __resolveNodeModule(target, dir);
7452
- }
7453
- }
7454
- return null; // first package.json wins, even if no imports field
8656
+ return { dir, pkg: __readPkgJson(dir) };
7455
8657
  }
7456
8658
  if (!dir) return null;
7457
8659
  const lastSlash = dir.lastIndexOf("/");
@@ -7459,37 +8661,68 @@ function __resolveImportsField(name, fromDir) {
7459
8661
  }
7460
8662
  }
7461
8663
 
8664
+ /**
8665
+ * Resolve a #name imports-field specifier from the nearest enclosing
8666
+ * package.json. Returns the resolved file or null.
8667
+ */
8668
+ function __resolveImportsField(name, fromDir) {
8669
+ // First package.json wins, even if no imports field (Node spec: imports
8670
+ // field of the importing module's package).
8671
+ const scope = __nearestPackageScope(fromDir);
8672
+ if (!scope || !scope.pkg || !scope.pkg.imports) return null;
8673
+ const dir = scope.dir;
8674
+ const target = resolveExports(scope.pkg.imports, name, __NIMBUS_CJS_CONDITIONS);
8675
+ if (!target) return null;
8676
+ // Imports targets are relative to the package root (dir)
8677
+ if (target.startsWith("./")) {
8678
+ return __resolveFile((dir ? dir + "/" : "") + target.slice(2));
8679
+ }
8680
+ if (target.startsWith("/")) {
8681
+ return __resolveFile(target.slice(1));
8682
+ }
8683
+ // Bare specifier — re-resolve as a node_module from this dir
8684
+ return __resolveNodeModule(target, dir);
8685
+ }
8686
+
8687
+ /**
8688
+ * Node's LOAD_PACKAGE_SELF: a bare specifier naming the enclosing package
8689
+ * itself (\`require('<its-name>')\`, \`require('<its-name>/sub')\`) resolves
8690
+ * through that package's own \`exports\` map — only when the nearest
8691
+ * package.json has \`exports\` AND its \`name\` matches, and only through
8692
+ * \`exports\` (no main/index probing: a subpath the map does not expose is
8693
+ * not exported). Sits between the imports-field branch and the
8694
+ * node_modules walk, where Node puts it. Returns the resolved file or null.
8695
+ *
8696
+ * Conditions are the ones the node_modules walk uses for the same
8697
+ * require: the runtime's CJS set first, the ESM set when the map exposes
8698
+ * the subpath only under \`import\` (dynamic import() is lowered onto this
8699
+ * require chain, see __resolvePkgSubpath).
8700
+ *
8701
+ * Tri-state, as in Node: null when the rule does not apply (the caller
8702
+ * walks node_modules); { resolved: null } when the enclosing package claims
8703
+ * the name but its map does not expose the subpath or the target is
8704
+ * missing — Node throws ERR_PACKAGE_PATH_NOT_EXPORTED / MODULE_NOT_FOUND
8705
+ * there and never consults node_modules, so neither does the caller.
8706
+ */
8707
+ function __resolvePackageSelf(name, fromDir) {
8708
+ const scope = __nearestPackageScope(fromDir);
8709
+ if (!scope || !scope.pkg) return null;
8710
+ const subpath = packageSelfReferenceSubpath(scope.pkg, name);
8711
+ if (subpath === null) return null;
8712
+ let entry = resolveExports(scope.pkg.exports, subpath, __NIMBUS_CJS_CONDITIONS);
8713
+ if (entry == null) entry = resolveExports(scope.pkg.exports, subpath, DEFAULT_ESM_CONDITIONS);
8714
+ if (entry == null) return { resolved: null };
8715
+ return { resolved: __resolveFile((scope.dir ? scope.dir + "/" : "") + entry.replace(/^\\.\\/+/, "")) };
8716
+ }
8717
+
7462
8718
  // ═══════════════════════════════════════════════════════════════════════
7463
- // ── X.5-S: __mkCompiledFn — conditional-param-rename wrap for new Function
8719
+ // ── __mkCompiledFn — the request-time fallback compile ──────────────
7464
8720
  // ═══════════════════════════════════════════════════════════════════════
7465
- //
7466
- // vite's chunks/node.js (transitive bundle of open@10.2.0) contains the
7467
- // ESM idiom \`const __dirname = path.dirname(fileURLToPath(import.meta.url))\`.
7468
- // W3.5 Fix B's esbuild ESM→CJS transform preserves that line verbatim
7469
- // while substituting \`import.meta\` with \`const import_meta = {}\`. Wrapping
7470
- // the body in \`new Function("exports","require","module","__filename","__dirname", code)\`
7471
- // then collides at parse time:
7472
- //
7473
- // SyntaxError: Identifier '__dirname' has already been declared
7474
- //
7475
- // (VERIFY-23417C5 §4 #1 / X5M3-retro §"Next bucket".) The helper RENAMES
7476
- // the conflicting param to a placeholder name so the body's own
7477
- // \`const __dirname\` becomes the single declarer. We rename rather than
7478
- // drop because callers pass 5 positional arguments and dropping a slot
7479
- // would mis-align downstream slots (e.g. the USER_CODE wrap appends
7480
- // \`console\` / \`process\` / etc. after \`__dirname\`). Renaming preserves
7481
- // slot alignment while letting the body's binding win.
7482
- //
7483
- // Symmetric for \`__filename\` because open@10's idiom often emits both.
7484
- function __mkCompiledFn(code) {
7485
- const reFn = /(?:^|\\n|;)\\s*(?:const|let|var)\\s+__filename\\s*=/m;
7486
- const reDn = /(?:^|\\n|;)\\s*(?:const|let|var)\\s+__dirname\\s*=/m;
7487
- const reRequire = /(?:^|\\n|;)\\s*(?:const|let|var)\\s+require\\s*=/m;
7488
- const fnName = reFn.test(code) ? "__filename__nimbus_unused" : "__filename";
7489
- const dnName = reDn.test(code) ? "__dirname__nimbus_unused" : "__dirname";
7490
- const requireName = reRequire.test(code) ? "require__nimbus_unused" : "require";
7491
- return new Function("exports", requireName, "module", fnName, dnName, code);
7492
- }
8721
+ // One definition, from core/_shared/compiled-fn.ts (interpolated above as
8722
+ // MK_COMPILED_FN_SOURCE); here it serves a cell the startup precompile set
8723
+ // did not include, where the runtime permits it. See that file for why the
8724
+ // collision is detected by compiling rather than by scanning the source.
8725
+ ${COMPILED_FN_JS}
7493
8726
 
7494
8727
  function __exportsTarget(mod) {
7495
8728
  const value = mod.exports;
@@ -7541,11 +8774,12 @@ function __makeLoadingExports(mod) {
7541
8774
  * Load and execute a JS/JSON module from VFS.
7542
8775
  * Returns the module.exports value.
7543
8776
  */
7544
- function __loadModule(resolvedPath) {
7545
- if (__moduleCache.has(resolvedPath)) return __moduleCache.get(resolvedPath);
8777
+ function __loadModule(resolvedPath, evaluationKey = resolvedPath) {
8778
+ if (globalThis.__nimbusProfileStaged) globalThis.__nimbusProfileStaged.delete(String(resolvedPath).replace(/^\\/+/, ""));
8779
+ if (__moduleCache.has(evaluationKey)) return __moduleCache.get(evaluationKey);
7546
8780
 
7547
8781
  const mod = { exports: {} };
7548
- __moduleCache.set(resolvedPath, __makeLoadingExports(mod));
8782
+ __moduleCache.set(evaluationKey, __makeLoadingExports(mod));
7549
8783
 
7550
8784
  const code = __readFileOr(resolvedPath, null);
7551
8785
  if (code === null) throw new Error("Cannot read module: " + resolvedPath);
@@ -7553,7 +8787,7 @@ function __loadModule(resolvedPath) {
7553
8787
  // JSON
7554
8788
  if (resolvedPath.endsWith(".json")) {
7555
8789
  mod.exports = JSON.parse(code);
7556
- __moduleCache.set(resolvedPath, mod.exports);
8790
+ __moduleCache.set(evaluationKey, mod.exports);
7557
8791
  return mod.exports;
7558
8792
  }
7559
8793
 
@@ -7576,6 +8810,16 @@ function __loadModule(resolvedPath) {
7576
8810
  // const import_meta = {}. Save+restore for recursive __loadModule.
7577
8811
  const __prevModulePath = globalThis.__currentModulePath;
7578
8812
  globalThis.__currentModulePath = resolvedPath;
8813
+ const moduleUrl = evaluationKey.startsWith("file:")
8814
+ ? evaluationKey : builtins.url.pathToFileURL("/" + resolvedPath).href;
8815
+ // Evaluation metadata lives on the module, not in source text. Keeping
8816
+ // it here preserves the five CommonJS arguments and captures the parent
8817
+ // even when import.meta.resolve is extracted and called later.
8818
+ const importMeta = Object.assign(Object.create(null), {
8819
+ url: moduleUrl,
8820
+ resolve: (specifier) => globalThis.__nimbusImportMetaResolve(specifier, moduleUrl),
8821
+ });
8822
+ Object.defineProperty(mod, "__nimbusImportMeta", { value: importMeta });
7579
8823
  try {
7580
8824
  // Use pre-compiled function from startup (new Function allowed at module eval time)
7581
8825
  // Normalize path to match VFS bundle key format (no leading /)
@@ -7600,7 +8844,7 @@ function __loadModule(resolvedPath) {
7600
8844
  } catch (evalErr) {
7601
8845
  // W3.5 Fix C: if the file was in the bundle but its pre-compile
7602
8846
  // failed at facet startup, surface the original SyntaxError
7603
- // instead of the misleading "file was not pre-bundled" text.
8847
+ // instead of the misleading "not in this launch's module map" text.
7604
8848
  const normalizedPath2 = resolvedPath.replace(/^\\/+/, "");
7605
8849
  const compileErr =
7606
8850
  (typeof __compileFailures !== "undefined" && __compileFailures &&
@@ -7612,22 +8856,27 @@ function __loadModule(resolvedPath) {
7612
8856
  );
7613
8857
  }
7614
8858
  if (evalErr.message && evalErr.message.includes("Code generation from strings disallowed")) {
7615
- throw new Error("Cannot load module '" + resolvedPath + "': file was not pre-bundled. Add it to the VFS bundle.");
8859
+ // Not a read miss (the text is here) and nothing in the process can
8860
+ // answer it, so it is kept apart from the read ledger that settles those.
8861
+ (globalThis.__nimbusModuleMisses ??= new Set()).add(normalizedPath2);
8862
+ throw new Error("Cannot load module '" + resolvedPath + "': it was not in this launch's module map; the next launch of the same command stages it.");
7616
8863
  }
7617
8864
  throw evalErr;
7618
8865
  }
7619
8866
  }
7620
8867
  } catch (e) {
7621
- __moduleCache.delete(resolvedPath);
8868
+ __moduleCache.delete(evaluationKey);
7622
8869
  if (e && typeof e === "object" && !e.__nimbusModulePath) {
7623
8870
  try {
7624
- e.__nimbusModulePath = resolvedPath;
7625
- if (typeof e.message === "string") {
7626
- e.message += "\\nNimbus module: " + resolvedPath;
7627
- }
7628
- if (typeof e.stack === "string" && !e.stack.includes("Nimbus module:")) {
7629
- e.stack += "\\nNimbus module: " + resolvedPath;
7630
- }
8871
+ Object.defineProperty(e, "__nimbusModulePath", { value: resolvedPath, configurable: true, writable: true });
8872
+ const at = resolvedPath.lastIndexOf("node_modules/");
8873
+ const parts = at < 0 ? [] : resolvedPath.slice(at + 13).split("/");
8874
+ const pkg = parts[0] && parts[0].startsWith("@") ? parts[0] + "/" + parts[1] : parts[0];
8875
+ const advisory = pkg ? __nimbusAbiAdvisories.get(pkg) : undefined;
8876
+ const note = "\\nNimbus module: " + resolvedPath
8877
+ + (advisory ? "\\nNimbus: " + pkg + " has no Workers-compatible build: " + advisory : "");
8878
+ if (typeof e.message === "string") e.message += note;
8879
+ if (typeof e.stack === "string" && !e.stack.includes("Nimbus module:")) e.stack += note;
7631
8880
  } catch {}
7632
8881
  }
7633
8882
  throw e;
@@ -7636,7 +8885,7 @@ function __loadModule(resolvedPath) {
7636
8885
  }
7637
8886
 
7638
8887
  // Update cache with final exports (module.exports may have been reassigned)
7639
- __moduleCache.set(resolvedPath, mod.exports);
8888
+ __moduleCache.set(evaluationKey, mod.exports);
7640
8889
  return mod.exports;
7641
8890
  }
7642
8891
 
@@ -7645,6 +8894,25 @@ function __loadModule(resolvedPath) {
7645
8894
  * Returns the resolved VFS path, or null.
7646
8895
  */
7647
8896
  function __resolveFrom(id, fromDir) {
8897
+ // An absolute file: URL is a specifier Node accepts: \`import(href)\` is the
8898
+ // portable way to load a path a resolver just handed back, and it is what
8899
+ // every package that resolves before it imports emits — @nuxt/cli's
8900
+ // loadKit does \`import(pathToFileURL(resolveModulePath('@nuxt/kit', …)).href)\`.
8901
+ // The ESM→CJS transform funnels those imports through this one resolver, so
8902
+ // the scheme has to come off before the specifier is classified: with it on,
8903
+ // the file: URL misses the absolute-path branch below and gets looked up as
8904
+ // if it were the name of a package.
8905
+ if (typeof id === "string" && id.startsWith("file:")) {
8906
+ let filePath;
8907
+ try {
8908
+ const u = new URL(id);
8909
+ // .pathname drops the query a cache-busting importer appends
8910
+ // (\`import(href + "?t=" + Date.now())\` is the standard HMR spelling),
8911
+ // and the decode is what node:url's fileURLToPath does with it.
8912
+ filePath = decodeURIComponent(u.pathname);
8913
+ } catch { filePath = id.replace(/^file:\\/\\//, ""); }
8914
+ id = filePath;
8915
+ }
7648
8916
  // X.5-P: literal "." / ".." are CommonJS aliases for "./" / "../".
7649
8917
  // Pre-fix they slipped past the startsWith("./")/("../") guards (which
7650
8918
  // require >= 3 / >= 4 chars respectively) and fell into the bare-spec
@@ -7674,43 +8942,137 @@ function __resolveFrom(id, fromDir) {
7674
8942
  if (id.startsWith("#")) {
7675
8943
  return __resolveImportsField(id, fromDir);
7676
8944
  }
7677
- // Bare specifier → node_modules resolution
8945
+ // Bare specifier: the enclosing package's own name resolves through its
8946
+ // exports map (Node's LOAD_PACKAGE_SELF), then node_modules resolution.
8947
+ // Once the enclosing package claims the name, its map is the whole
8948
+ // answer: a subpath it does not expose is not found, never a
8949
+ // node_modules copy's.
8950
+ const self = __resolvePackageSelf(id, fromDir);
8951
+ if (self) return self.resolved;
7678
8952
  return __resolveNodeModule(id, fromDir);
7679
8953
  }
7680
8954
 
7681
- globalThis.__nimbusImportMetaResolve = function __nimbusImportMetaResolve(specifier, parentUrl) {
7682
- const currentFromParent = typeof parentUrl === "string" && parentUrl.startsWith("file:")
7683
- ? parentUrl.replace(/^file:\\/\\/\\/+/, "")
7684
- : "";
7685
- const current = currentFromParent || (
7686
- typeof globalThis.__currentModulePath === "string"
7687
- ? globalThis.__currentModulePath.replace(/^\\/+/, "")
7688
- : ""
7689
- );
7690
- const fromDir = current.includes("/") ? current.substring(0, current.lastIndexOf("/")) : "";
7691
- const text = String(specifier);
7692
- if (text.startsWith("file:")) return text;
7693
- if (text.startsWith("./") || text.startsWith("../") || text.startsWith("/")) {
7694
- return new URL(text, current ? "file:///" + current : "file:///").href;
7695
- }
7696
- const resolved = __resolveFrom(text, fromDir);
7697
- // Node throws ERR_MODULE_NOT_FOUND rather than answering with the specifier
7698
- // it could not resolve, and packages are written against that: the standard
7699
- // shape is a try/catch that reports something the user can act on. Handing
7700
- // back the bare specifier passes the "unresolved" case off as a URL, so
7701
- // fileURLToPath downstream yields a path that was never on disk and the
7702
- // eventual error names a phantom file instead of the missing package.
7703
- // typescript@7 does exactly this: with the specifier echoed back it reports
7704
- // "Executable not found: @typescript/typescript-linux-x64/lib/tsc", where
7705
- // its own catch would have said the platform package is missing.
7706
- if (!resolved) {
7707
- const err = new Error(
7708
- "Cannot find package '" + text + "' imported from " + (current || fromDir || "<unknown>"),
7709
- );
7710
- err.code = "ERR_MODULE_NOT_FOUND";
7711
- throw err;
8955
+ // ═══════════════════════════════════════════════════════════════════════
8956
+ // ── import() — Node's ESM loader ────────────────────────────────────
8957
+ // ═══════════════════════════════════════════════════════════════════════
8958
+ // A cell's dynamic import() reaches here with its module's URL
8959
+ // (core/runtime/dynamic-import-rewrite.ts). Resolution is Node's ESM
8960
+ // algorithm (core/_shared/esm-resolver.ts, interpolated below): the "import"
8961
+ // conditions, no extension probing, a directory refused by name, file: URLs,
8962
+ // Node's error codes and messages. What resolves loads through the same
8963
+ // module cache require uses, shaped as the namespace Node would give.
8964
+ // Node's builtins that exist only with the scheme; the rest of the table is
8965
+ // the process's own builtins, undici among them, which the process provides
8966
+ // in place of any installed copy (see builtins.undici above).
8967
+ const __ESM_SCHEME_ONLY_BUILTINS = new Set(["test", "test/reporters", "sqlite", "sea"]);
8968
+ // Node's ESM resolver (core/_shared/esm-resolver.ts, compiled once by
8969
+ // scripts/bundle-facet-workers.mjs): declares createEsmResolver.
8970
+ ${ESM_RESOLVER_PREAMBLE}
8971
+ const __esmResolver = createEsmResolver({
8972
+ kind(path) {
8973
+ const st = __fsMod.statSync(path, { throwIfNoEntry: false });
8974
+ return !st ? null : st.isDirectory() ? "directory" : st.isFile() ? "file" : null;
8975
+ },
8976
+ realpath(path) {
8977
+ try { return __fsMod.realpathSync(path); } catch { return path; }
8978
+ },
8979
+ readText(path) { return __readFileOr(path, null); },
8980
+ isBuiltin(specifier) {
8981
+ const scheme = specifier.startsWith("node:");
8982
+ const name = scheme ? specifier.slice(5) : specifier;
8983
+ if (!scheme && __ESM_SCHEME_ONLY_BUILTINS.has(name)) return false;
8984
+ return Object.prototype.hasOwnProperty.call(builtins, name);
8985
+ },
8986
+ cjsResolve(specifier, parentPath) {
8987
+ try {
8988
+ const dir = parentPath.slice(0, parentPath.lastIndexOf("/")).replace(/^\\/+/, "");
8989
+ const found = __resolveFrom(specifier, dir);
8990
+ return found ? "/" + String(found).replace(/^\\/+/, "") : null;
8991
+ } catch { return null; }
8992
+ },
8993
+ });
8994
+ const __esmNamespaces = new Map();
8995
+ /** A module namespace: its names sorted, read through to the exports. */
8996
+ function __esmNamespaceOf(names, read) {
8997
+ const ns = Object.create(null);
8998
+ for (const name of [...names].sort()) {
8999
+ Object.defineProperty(ns, name, { enumerable: true, get: () => read(name) });
9000
+ }
9001
+ Object.defineProperty(ns, Symbol.toStringTag, { value: "Module" });
9002
+ return Object.preventExtensions(ns);
9003
+ }
9004
+ function __esmLoad(resolution) {
9005
+ const cached = __esmNamespaces.get(resolution.url);
9006
+ if (cached) return cached;
9007
+ let ns;
9008
+ if (resolution.format === "builtin") {
9009
+ const mod = __requireFrom("node:" + resolution.builtin, "");
9010
+ const names = new Set(mod && (typeof mod === "object" || typeof mod === "function") ? Object.keys(mod) : []);
9011
+ names.add("default");
9012
+ ns = __esmNamespaceOf(names, (name) => name === "default" ? mod : mod[name]);
9013
+ } else if (resolution.format === "data") {
9014
+ const match = /^data:application\\/json(;[^,]*)?,/.exec(resolution.url);
9015
+ if (!match) {
9016
+ throw Object.assign(new Error("Nimbus: a data: URL module can only be JSON here; code cannot be compiled after the process starts: " + resolution.url.slice(0, 64)), { code: "ERR_NIMBUS_DATA_MODULE" });
9017
+ }
9018
+ const body = resolution.url.slice(match[0].length);
9019
+ const value = JSON.parse(match[1] && match[1].includes(";base64") ? atob(body) : decodeURIComponent(body));
9020
+ ns = __esmNamespaceOf(["default"], () => value);
9021
+ } else {
9022
+ const key = resolution.path.replace(/^\\/+/, "");
9023
+ const esm = resolution.format === "module"
9024
+ || (resolution.format === "detect" && globalThis.__nimbusEsmModules && globalThis.__nimbusEsmModules.has(key));
9025
+ // Canonical queryless ESM shares evaluation with require() and static
9026
+ // imports lowered to require(). Queries/fragments are distinct jobs.
9027
+ const variant = esm && (resolution.url.includes("?") || resolution.url.includes("#"));
9028
+ const exports = __loadModule(key, variant ? resolution.url : key);
9029
+ if (resolution.format === "json") {
9030
+ ns = __esmNamespaceOf(["default"], () => exports);
9031
+ } else if (esm) {
9032
+ // The ESM→CJS transform's exports: the module's own names, live.
9033
+ const names = exports && typeof exports === "object" ? Object.keys(exports).filter((name) => name !== "__esModule") : [];
9034
+ ns = __esmNamespaceOf(names, (name) => exports[name]);
9035
+ } else {
9036
+ // CommonJS: module.exports is the default, its names the named exports.
9037
+ const mod = exports;
9038
+ const names = new Set(mod && (typeof mod === "object" || typeof mod === "function") ? Object.keys(mod) : []);
9039
+ names.delete("default");
9040
+ names.add("default");
9041
+ ns = __esmNamespaceOf(names, (name) => name === "default" ? mod : mod[name]);
9042
+ }
7712
9043
  }
7713
- return "file:///" + resolved.replace(/^\\/+/, "");
9044
+ __esmNamespaces.set(resolution.url, ns);
9045
+ return ns;
9046
+ }
9047
+ globalThis.__nimbusDynamicImport = function __nimbusDynamicImport(parentUrl, specifier, options) {
9048
+ return Promise.resolve().then(() => {
9049
+ const text = String(specifier);
9050
+ const attributes = {};
9051
+ // V8's own checks and messages (node 22), which run before resolution.
9052
+ if (options !== undefined) {
9053
+ if (options === null || typeof options !== "object") throw new TypeError("The second argument to import() must be an object");
9054
+ const withAttributes = options.with;
9055
+ if (withAttributes !== undefined) {
9056
+ if (withAttributes === null || typeof withAttributes !== "object") throw new TypeError("The 'assert' option must be an object");
9057
+ for (const key of Object.keys(withAttributes)) {
9058
+ if (typeof withAttributes[key] !== "string") throw new TypeError("Import assertion value must be a string");
9059
+ attributes[key] = withAttributes[key];
9060
+ }
9061
+ }
9062
+ }
9063
+ const resolution = __esmResolver.resolveSync(text, parentUrl);
9064
+ __esmResolver.validateAttributes(resolution.url, resolution.format, attributes);
9065
+ return __esmLoad(resolution);
9066
+ });
9067
+ };
9068
+
9069
+ // Node's import.meta.resolve, synchronous as in Node: the URL a specifier
9070
+ // names, even for a file or directory that will not load.
9071
+ globalThis.__nimbusImportMetaResolve = function __nimbusImportMetaResolve(specifier, parentUrl) {
9072
+ const parent = typeof parentUrl === "string" && parentUrl.startsWith("file:")
9073
+ ? parentUrl
9074
+ : "file:///" + String(globalThis.__currentModulePath || "[eval]").replace(/^\\/+/, "");
9075
+ return __esmResolver.metaResolveSync(String(specifier), parent);
7714
9076
  };
7715
9077
 
7716
9078
  /**