@nimbus-sh/worker 0.10.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 (260) 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/facets/cirrus-real.d.ts.map +1 -1
  19. package/dist/facets/cirrus-real.js +9 -4
  20. package/dist/facets/compose.d.ts +11 -7
  21. package/dist/facets/compose.d.ts.map +1 -1
  22. package/dist/facets/compose.js +8 -38
  23. package/dist/facets/data-plan.d.ts +79 -0
  24. package/dist/facets/data-plan.d.ts.map +1 -0
  25. package/dist/facets/data-plan.js +477 -0
  26. package/dist/facets/esbuild-transform.d.ts +36 -10
  27. package/dist/facets/esbuild-transform.d.ts.map +1 -1
  28. package/dist/facets/esbuild-transform.js +216 -20
  29. package/dist/facets/exec-telemetry.d.ts +7 -3
  30. package/dist/facets/exec-telemetry.d.ts.map +1 -1
  31. package/dist/facets/manager.d.ts +179 -78
  32. package/dist/facets/manager.d.ts.map +1 -1
  33. package/dist/facets/manager.js +1221 -649
  34. package/dist/facets/opencode-staging.d.ts +0 -2
  35. package/dist/facets/opencode-staging.d.ts.map +1 -1
  36. package/dist/facets/opencode-staging.js +5 -11
  37. package/dist/facets/process.d.ts +4 -0
  38. package/dist/facets/process.d.ts.map +1 -1
  39. package/dist/facets/process.js +3 -0
  40. package/dist/facets/read-profile.d.ts +149 -0
  41. package/dist/facets/read-profile.d.ts.map +1 -0
  42. package/dist/facets/read-profile.js +483 -0
  43. package/dist/facets/resident-identity.d.ts +0 -1
  44. package/dist/facets/resident-identity.d.ts.map +1 -1
  45. package/dist/facets/resident-identity.js +0 -3
  46. package/dist/facets/wasm-swap-registry.d.ts +34 -25
  47. package/dist/facets/wasm-swap-registry.d.ts.map +1 -1
  48. package/dist/facets/wasm-swap-registry.js +62 -56
  49. package/dist/facets/ws-terminal.d.ts +4 -4
  50. package/dist/facets/ws-terminal.d.ts.map +1 -1
  51. package/dist/facets/ws-terminal.js +4 -5
  52. package/dist/git/commands.d.ts +16 -9
  53. package/dist/git/commands.d.ts.map +1 -1
  54. package/dist/git/commands.js +1818 -188
  55. package/dist/git/network-facet.d.ts +10 -2
  56. package/dist/git/network-facet.d.ts.map +1 -1
  57. package/dist/git/network-facet.js +62 -16
  58. package/dist/git/unified-diff.d.ts +74 -0
  59. package/dist/git/unified-diff.d.ts.map +1 -0
  60. package/dist/git/unified-diff.js +676 -0
  61. package/dist/git-bundle.generated.d.ts +7 -11
  62. package/dist/git-bundle.generated.d.ts.map +1 -1
  63. package/dist/git-bundle.generated.js +7 -11
  64. package/dist/hosted/commands.d.ts.map +1 -1
  65. package/dist/hosted/commands.js +62 -243
  66. package/dist/hosted/runtime.d.ts +19 -13
  67. package/dist/hosted/runtime.d.ts.map +1 -1
  68. package/dist/hosted/runtime.js +71 -14
  69. package/dist/hosted/services.d.ts +6 -7
  70. package/dist/hosted/services.d.ts.map +1 -1
  71. package/dist/hosted/services.js +10 -14
  72. package/dist/hosted/session.d.ts +165 -0
  73. package/dist/hosted/session.d.ts.map +1 -0
  74. package/dist/hosted/session.js +132 -0
  75. package/dist/loaders/generated-workers.d.ts +5 -2
  76. package/dist/loaders/generated-workers.d.ts.map +1 -1
  77. package/dist/loaders/generated-workers.js +6 -3
  78. package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -1
  79. package/dist/loaders/npm-resolve-preamble.js +0 -27
  80. package/dist/node-shims-artifact.generated.d.ts +13 -4
  81. package/dist/node-shims-artifact.generated.d.ts.map +1 -1
  82. package/dist/node-shims-artifact.generated.js +16 -7
  83. package/dist/npm/bin-links.d.ts +3 -1
  84. package/dist/npm/bin-links.d.ts.map +1 -1
  85. package/dist/npm/bin-links.js +32 -8
  86. package/dist/npm/cache.d.ts +5 -0
  87. package/dist/npm/cache.d.ts.map +1 -1
  88. package/dist/npm/cache.js +13 -1
  89. package/dist/npm/install-batch-facet.d.ts +2 -2
  90. package/dist/npm/install-batch-facet.js +3 -3
  91. package/dist/npm/installer.d.ts +17 -1
  92. package/dist/npm/installer.d.ts.map +1 -1
  93. package/dist/npm/installer.js +159 -20
  94. package/dist/npm/package-lock.d.ts +32 -0
  95. package/dist/npm/package-lock.d.ts.map +1 -0
  96. package/dist/npm/package-lock.js +65 -0
  97. package/dist/npm/pre-bundle-facet.d.ts +1 -30
  98. package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
  99. package/dist/npm/pre-bundle-facet.js +3 -5
  100. package/dist/npm/r2-cache.d.ts +2 -0
  101. package/dist/npm/r2-cache.d.ts.map +1 -1
  102. package/dist/npm/r2-cache.js +2 -0
  103. package/dist/npm/resolve-one-facet.d.ts.map +1 -1
  104. package/dist/npm/resolve-one-facet.js +14 -3
  105. package/dist/npm/resolver.d.ts +0 -7
  106. package/dist/npm/resolver.d.ts.map +1 -1
  107. package/dist/npm/resolver.js +0 -22
  108. package/dist/opencode-artifact.generated.js +2 -2
  109. package/dist/replica/routing.d.ts +2 -38
  110. package/dist/replica/routing.d.ts.map +1 -1
  111. package/dist/replica/routing.js +3 -55
  112. package/dist/router/index.d.ts.map +1 -1
  113. package/dist/router/index.js +15 -2
  114. package/dist/router/public-directory.d.ts +18 -0
  115. package/dist/router/public-directory.d.ts.map +1 -1
  116. package/dist/router/public-directory.js +1 -1
  117. package/dist/router/remote-api.d.ts +3 -1
  118. package/dist/router/remote-api.d.ts.map +1 -1
  119. package/dist/router/remote-api.js +42 -79
  120. package/dist/runtime/bash-repl.js +2 -2
  121. package/dist/runtime/bun-repl.d.ts.map +1 -1
  122. package/dist/runtime/bun-repl.js +2 -13
  123. package/dist/runtime/esbuild-wasm-bytes.d.ts +17 -5
  124. package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -1
  125. package/dist/runtime/esbuild-wasm-bytes.js +43 -94
  126. package/dist/runtime/git-bundle-artifact.d.ts +17 -0
  127. package/dist/runtime/git-bundle-artifact.d.ts.map +1 -0
  128. package/dist/runtime/git-bundle-artifact.js +25 -0
  129. package/dist/runtime/node-repl.d.ts.map +1 -1
  130. package/dist/runtime/node-repl.js +2 -13
  131. package/dist/runtime/node-shims-artifact.d.ts +22 -23
  132. package/dist/runtime/node-shims-artifact.d.ts.map +1 -1
  133. package/dist/runtime/node-shims-artifact.js +48 -78
  134. package/dist/runtime/node-shims.d.ts.map +1 -1
  135. package/dist/runtime/node-shims.js +1567 -864
  136. package/dist/runtime/opencode-artifact.d.ts.map +1 -1
  137. package/dist/runtime/opencode-artifact.js +17 -53
  138. package/dist/runtime/opencode-facet-runner.d.ts +3 -6
  139. package/dist/runtime/opencode-facet-runner.d.ts.map +1 -1
  140. package/dist/runtime/opencode-facet-runner.js +45 -10
  141. package/dist/runtime/opentui-wasm-bytes.d.ts.map +1 -1
  142. package/dist/runtime/opentui-wasm-bytes.js +16 -49
  143. package/dist/runtime/package-manager.d.ts +0 -2
  144. package/dist/runtime/package-manager.d.ts.map +1 -1
  145. package/dist/runtime/package-manager.js +0 -3
  146. package/dist/runtime/python-repl.d.ts.map +1 -1
  147. package/dist/runtime/python-repl.js +2 -2
  148. package/dist/runtime/repl-session.d.ts +10 -0
  149. package/dist/runtime/repl-session.d.ts.map +1 -1
  150. package/dist/runtime/repl-session.js +7 -0
  151. package/dist/runtime/ruby-repl.d.ts.map +1 -1
  152. package/dist/runtime/ruby-repl.js +2 -2
  153. package/dist/runtime/ruby-resident.d.ts.map +1 -1
  154. package/dist/runtime/ruby-resident.js +36 -35
  155. package/dist/runtime/runtime-catalog.d.ts +10 -3
  156. package/dist/runtime/runtime-catalog.d.ts.map +1 -1
  157. package/dist/runtime/runtime-catalog.js +125 -15
  158. package/dist/runtime/sqlite-wasm-bytes.d.ts +4 -4
  159. package/dist/runtime/sqlite-wasm-bytes.d.ts.map +1 -1
  160. package/dist/runtime/sqlite-wasm-bytes.js +21 -57
  161. package/dist/runtime/staged-source.d.ts +67 -0
  162. package/dist/runtime/staged-source.d.ts.map +1 -0
  163. package/dist/runtime/staged-source.js +113 -0
  164. package/dist/runtime/wasi/preamble.d.ts.map +1 -1
  165. package/dist/runtime/wasi/preamble.js +0 -24
  166. package/dist/runtime-catalog.generated.js +1 -1
  167. package/dist/session/agent.d.ts +14 -2
  168. package/dist/session/agent.d.ts.map +1 -1
  169. package/dist/session/agent.js +57 -19
  170. package/dist/session/ai.d.ts +0 -11
  171. package/dist/session/ai.d.ts.map +1 -1
  172. package/dist/session/ai.js +0 -4
  173. package/dist/session/fs-watch.d.ts +0 -11
  174. package/dist/session/fs-watch.d.ts.map +1 -1
  175. package/dist/session/fs-watch.js +0 -21
  176. package/dist/session/hibernation.d.ts +34 -11
  177. package/dist/session/hibernation.d.ts.map +1 -1
  178. package/dist/session/hibernation.js +46 -29
  179. package/dist/session/init-phases.d.ts +1 -2
  180. package/dist/session/init-phases.d.ts.map +1 -1
  181. package/dist/session/init-phases.js +1 -2
  182. package/dist/session/init.d.ts.map +1 -1
  183. package/dist/session/init.js +7 -20
  184. package/dist/session/keys.d.ts +0 -3
  185. package/dist/session/keys.d.ts.map +1 -1
  186. package/dist/session/keys.js +0 -3
  187. package/dist/session/legacy-reset.d.ts +11 -0
  188. package/dist/session/legacy-reset.d.ts.map +1 -0
  189. package/dist/session/legacy-reset.js +16 -0
  190. package/dist/session/nimbus-session.d.ts +41 -15
  191. package/dist/session/nimbus-session.d.ts.map +1 -1
  192. package/dist/session/nimbus-session.js +64 -70
  193. package/dist/session/npm-install-port.d.ts.map +1 -1
  194. package/dist/session/npm-install-port.js +1 -0
  195. package/dist/session/programmatic.d.ts +13 -18
  196. package/dist/session/programmatic.d.ts.map +1 -1
  197. package/dist/session/programmatic.js +53 -39
  198. package/dist/session/replica-routes.d.ts +2 -3
  199. package/dist/session/replica-routes.d.ts.map +1 -1
  200. package/dist/session/replica-routes.js +3 -10
  201. package/dist/session/routes.d.ts.map +1 -1
  202. package/dist/session/routes.js +7 -10
  203. package/dist/session/rpc.d.ts +104 -20
  204. package/dist/session/rpc.d.ts.map +1 -1
  205. package/dist/session/rpc.js +184 -37
  206. package/dist/session/start-real-vite.js +2 -2
  207. package/dist/session/state-store.d.ts +1 -17
  208. package/dist/session/state-store.d.ts.map +1 -1
  209. package/dist/session/state-store.js +3 -53
  210. package/dist/session/supervisor-op.d.ts +7 -0
  211. package/dist/session/supervisor-op.d.ts.map +1 -1
  212. package/dist/session/supervisor-op.js +6 -1
  213. package/dist/session/supervisor-rpc.d.ts +104 -15
  214. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  215. package/dist/session/supervisor-rpc.js +188 -71
  216. package/dist/session/vite-command.d.ts.map +1 -1
  217. package/dist/session/vite-command.js +17 -14
  218. package/dist/session/ws.d.ts.map +1 -1
  219. package/dist/session/ws.js +20 -8
  220. package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
  221. package/dist/shell/npm-bin-entrypoints.js +30 -5
  222. package/dist/vfs/facet-resident-limits.d.ts +33 -0
  223. package/dist/vfs/facet-resident-limits.d.ts.map +1 -0
  224. package/dist/vfs/facet-resident-limits.js +33 -0
  225. package/dist/vfs/facet-resident-store.d.ts +2 -10
  226. package/dist/vfs/facet-resident-store.d.ts.map +1 -1
  227. package/dist/vfs/facet-resident-store.js +1594 -219
  228. package/dist/workspace-host.d.ts +2 -0
  229. package/dist/workspace-host.d.ts.map +1 -1
  230. package/dist/workspace-host.js +2 -0
  231. package/dist/wrangler/nimbus-wrangler.d.ts +0 -2
  232. package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -1
  233. package/dist/wrangler/nimbus-wrangler.js +5 -22
  234. package/package.json +8 -7
  235. package/public/_assets/opencode/1.16.2/index-attach.js +2 -2
  236. package/public/_assets/runtime/esbuild-cli-c4fda380d52befc1.js +6892 -0
  237. package/public/_assets/runtime/git-de3e60c9f3ff006a.js +103 -0
  238. package/public/_assets/runtime/{node-shims-7b4dd7a76d95d21e.js → node-shims-06565b121555c194.js} +2089 -861
  239. package/public/_assets/runtime/resident-store-6ffb1939c53d982f.js +2264 -0
  240. package/public/_assets/runtime/vfs-write-ledger-e501e8fc793bc2c1.js +889 -0
  241. package/scripts/build-opencode-attach-entry.mjs +51 -1
  242. package/scripts/bundle-facet-workers.mjs +114 -3
  243. package/scripts/bundle-git.mjs +88 -27
  244. package/scripts/bundle-node-shims.mjs +72 -46
  245. package/scripts/bundle-runtime.mjs +18 -421
  246. package/scripts/cf-git-patch.mjs +137 -0
  247. package/scripts/patch-install-deps.mjs +49 -96
  248. package/scripts/runtime-specs.mjs +435 -0
  249. package/vendor/git-http.generated.d.ts +82 -0
  250. package/vendor/git-upstream.generated.d.ts +4378 -0
  251. package/vendor/git.LICENSE +7 -0
  252. package/vendor/git.generated.d.mts +21 -0
  253. package/vendor/git.generated.mjs +103 -0
  254. package/dist/replica/suspension.d.ts +0 -36
  255. package/dist/replica/suspension.d.ts.map +0 -1
  256. package/dist/replica/suspension.js +0 -49
  257. package/dist/runtime/static-server.d.ts +0 -3
  258. package/dist/runtime/static-server.d.ts.map +0 -1
  259. package/dist/runtime/static-server.js +0 -133
  260. 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
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) => {
@@ -738,29 +814,205 @@ const __fsMod = (() => {
738
814
  // ── VFS bundle lookup (fast path — in-memory) ──
739
815
  function _bundleLookup(absPath) {
740
816
  const k = _strip(absPath);
741
- if (__vfsBundle && k in __vfsBundle) return __vfsBundle[k];
742
- // 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).
743
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
+ }
744
829
  return undefined;
745
830
  }
746
831
 
747
- // __vfsMetadata is not declared in every embedding of these shims, so the
748
- // one guarded reference lives here and every other reader goes through it.
749
- function _metadataTable() {
750
- return (typeof __vfsMetadata !== "undefined" && __vfsMetadata) ? __vfsMetadata : null;
751
- }
752
-
753
- // Same guard, same reason: the SQLite resident store is spliced into the
754
- // resident-process body only. The one-shot exec path runs in a stateless
755
- // loaded worker with no facet storage at all, so these shims must still work
756
- // 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.
757
834
  function _residentStorePresent() {
758
835
  return typeof __residentAdmit === "function" && typeof __residentReady !== "undefined" && __residentReady;
759
836
  }
760
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
+
761
1012
  function _metadata(absPath) {
762
- const table = _metadataTable();
763
- 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;
764
1016
  }
765
1017
 
766
1018
  function _denialCode(cell) {
@@ -768,52 +1020,13 @@ const __fsMod = (() => {
768
1020
  typeof cell.error === "string" ? cell.error : null;
769
1021
  }
770
1022
 
771
- // Removal retracts the path from the sync existence view — __vfsMetadata
772
- // (spawn-time stat records) and __vfsManifest (directory shape). Without
773
- // it, existsSync keeps reporting a file this process already deleted and
774
- // 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.
775
1026
  function _forgetSyncPath(k) {
1027
+ _createdHere.delete(k);
776
1028
  _announcedDirs.delete(k);
777
- const metadata = _metadataTable();
778
- if (metadata) delete metadata[k];
779
- if (!__vfsManifest) return;
780
- delete __vfsManifest[k];
781
- const slash = k.lastIndexOf("/");
782
- const parent = slash >= 0 ? k.slice(0, slash) : "";
783
- const name = slash >= 0 ? k.slice(slash + 1) : k;
784
- const siblings = __vfsManifest[parent];
785
- if (!siblings) return;
786
- const at = siblings.indexOf(name);
787
- if (at !== -1) siblings.splice(at, 1);
788
- }
789
-
790
- /**
791
- * Put a path into the sync EXISTENCE view — the inverse of _forgetSyncPath.
792
- *
793
- * A content cell is not existence. An invalidation drops content and is
794
- * documented to leave the name behind so existsSync cannot fabricate an
795
- * ENOENT for a file that was merely rewritten — but a file this process just
796
- * created has no manifest entry and no spawn-time stat record, so its cell
797
- * WAS the name, and evicting it retracted a file the program had written
798
- * itself. Recording the name where every other name lives is what makes that
799
- * documented asymmetry true.
800
- *
801
- * The parent's listing is the right home for it. Where the parent has no
802
- * listing the entry is created only for a directory this process made, since
803
- * that is the only case in which it knows the whole contents and calling the
804
- * directory enumerated is honest.
805
- */
806
- function _announceSyncPath(k) {
807
- if (!__vfsManifest || k === "") return;
808
- const slash = k.lastIndexOf("/");
809
- const parent = slash >= 0 ? k.slice(0, slash) : "";
810
- const name = slash >= 0 ? k.slice(slash + 1) : k;
811
- let siblings = __vfsManifest[parent];
812
- if (!Array.isArray(siblings)) {
813
- if (!__vfsDirs || !(parent in __vfsDirs)) return;
814
- siblings = __vfsManifest[parent] = [];
815
- }
816
- if (siblings.indexOf(name) === -1) siblings.push(name);
1029
+ _nsOwnSet(k, "absent");
817
1030
  }
818
1031
 
819
1032
  /**
@@ -822,41 +1035,67 @@ const __fsMod = (() => {
822
1035
  * content carried. One place, because every sync mutation owes all three.
823
1036
  */
824
1037
  function _parkWrite(k, cell) {
1038
+ _noteCreation(k);
825
1039
  __vfsWrites[k] = cell;
826
1040
  if (__vfsBundle) __vfsBundle[k] = cell;
827
1041
  delete __vfsBundleRevisions[k];
828
- _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);
829
1066
  }
830
1067
 
831
1068
  function _forgetSyncTree(k) {
832
1069
  const prefix = k + "/";
1070
+ _forgetCreation(k);
1071
+ _nsOwnSet(k, "absentTree");
833
1072
  for (const dir of _announcedDirs) if (dir.startsWith(prefix)) _announcedDirs.delete(dir);
834
- const metadata = _metadataTable();
835
- if (metadata) {
836
- for (const mk of Object.keys(metadata)) if (mk.startsWith(prefix)) delete metadata[mk];
837
- }
838
- if (__vfsManifest) {
839
- for (const dk of Object.keys(__vfsManifest)) if (dk.startsWith(prefix)) delete __vfsManifest[dk];
840
- }
841
1073
  _forgetSyncPath(k);
842
1074
  }
843
1075
 
844
1076
  /**
845
1077
  * The honest error for a sync read the resident view cannot serve. A facet
846
1078
  * has no synchronous I/O primitive, so a sync read is limited to content
847
- * staged into this process — but the sync existence view (__vfsMetadata +
848
- * __vfsManifest) knows far more paths than the content bundle carries.
849
- * ENOENT is only correct when the path is unknown there too; for a path
850
- * that demonstrably exists it sends the caller hunting for a missing file
851
- * 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.
852
1083
  */
853
1084
  function _notResidentError(absPath, displayPath, syscall, asyncForm) {
854
1085
  const st = _statLadder(absPath);
855
- if (st === undefined) {
856
- if (!_absenceIsKnown(absPath)) _recordUnmapped(absPath, syscall);
857
- return _fsErr("ENOENT", syscall, displayPath);
858
- }
1086
+ if (st === undefined) return _fsErr("ENOENT", syscall, displayPath);
859
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
+ }
860
1099
  _recordResidencyMiss(absPath);
861
1100
  const err = _fsErr("EAGAIN", syscall, displayPath);
862
1101
  err.message += " — '" + String(displayPath) + "' exists but its content is not " +
@@ -990,7 +1229,9 @@ const __fsMod = (() => {
990
1229
  let meta;
991
1230
  // A thrown stat is the authority failing to answer, not an answer:
992
1231
  // nothing is learned, and the miss stands.
993
- 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()); }
994
1235
  catch { return; }
995
1236
  if (meta === null || meta === undefined) { _observedAbsent.add(k); return; }
996
1237
  if (meta.type === "directory") return;
@@ -1028,193 +1269,46 @@ const __fsMod = (() => {
1028
1269
  }
1029
1270
  };
1030
1271
 
1031
- /**
1032
- * Has this directory's content been enumerated, or merely mentioned?
1033
- *
1034
- * __vfsManifest is a walk of SELECTED roots — the cwd subtree, its
1035
- * node_modules, the entry script's own package — not of the filesystem. A
1036
- * directory with an entry there was listed by that walk, so its child list
1037
- * is complete and a name that is not in it is genuinely not there. A
1038
- * directory with no entry was never opened, and knows nothing. __vfsDirs
1039
- * holds the directories this process created itself, which it therefore
1040
- * knows the contents of by construction.
1041
- *
1042
- * Conflating the two is what let a synchronous readdir of a real directory
1043
- * outside the walk answer with an empty array — not an error, not a refusal,
1044
- * a positive assertion that a populated directory is empty. Every caller
1045
- * that used to end in "not found, so absent" goes through here first.
1046
- */
1047
- function _dirEnumerated(k) {
1048
- return (!!__vfsManifest && k in __vfsManifest) || (!!__vfsDirs && k in __vfsDirs);
1049
- }
1050
1272
 
1051
- /** Does the content bundle itself describe children under this directory? */
1052
- function _bundleHasChildren(k) {
1053
- if (!__vfsBundle) return false;
1054
- return __residentAnyUnder(k ? k + "/" : "");
1055
- }
1056
1273
 
1057
- /**
1058
- * Can the view say this path is ABSENT, as opposed to say nothing at all?
1059
- *
1060
- * With no supervisor bound there is no authority to be ignorant of: the
1061
- * staged tables ARE the filesystem, the async form reads out of those same
1062
- * tables, and a name that is not in them is not anywhere. Ignorance is a
1063
- * property of the gap between a process and an authority it cannot reach
1064
- * synchronously, so where there is no authority there is no gap.
1065
- */
1066
- function _absenceIsKnown(absPath) {
1067
- if (!_supervisor()) return true;
1068
- const k = _strip(absPath);
1069
- if (k === "") return true;
1070
- const segments = k.split("/");
1071
- // Deepest enumerated ancestor, because that is the only one that can
1072
- // testify. Walking up rather than looking only at the immediate parent is
1073
- // what makes a whole missing subtree answerable from one listing: if
1074
- // $HOME was enumerated and holds no .config, then nothing under .config
1075
- // is there either, and a program probing three levels down deserves that
1076
- // answer rather than three refusals.
1077
- for (let i = segments.length - 1; i >= 0; i--) {
1078
- const dir = segments.slice(0, i).join("/");
1079
- if (!_dirEnumerated(dir)) continue;
1080
- const child = segments.slice(0, i + 1).join("/");
1081
- const children = (__vfsManifest && __vfsManifest[dir]) || [];
1082
- const present = children.indexOf(segments[i]) !== -1
1083
- || (!!__vfsDirs && child in __vfsDirs)
1084
- || (!!__vfsBundle && child in __vfsBundle);
1085
- // The component is not in a directory that was listed: absent, and so is
1086
- // everything below it. If it IS there, only its own enumeration settles
1087
- // what it contains, so nothing short of the immediate parent will do.
1088
- if (!present) return true;
1089
- return i === segments.length - 1;
1090
- }
1091
- return false;
1092
- }
1093
1274
 
1094
- /**
1095
- * A path the sync view never mapped: record the ignorance, and repair it.
1096
- *
1097
- * Sibling of _notResidentError and the same bargain — the file may well be
1098
- * there, the process simply cannot look without blocking. The callers answer
1099
- * ENOENT, which is provisional rather than fabricated: this records the miss
1100
- * and pulls in the listing that settles the question, and the exit report
1101
- * reads the ledger. If the path was genuinely absent the repair proves it
1102
- * and the entry retires — the program's not-found branch was the right
1103
- * branch and it reached it through the right errno. If the path WAS there,
1104
- * the entry survives and the run is failed by name.
1105
- *
1106
- * EAGAIN was tried here and is the wrong answer to hand a program even
1107
- * though it is the honest description of the situation. It cannot arise from
1108
- * a real POSIX filesystem, so nothing branches on it, and the catch block
1109
- * that receives it was written for a missing file and rethrows instead:
1110
- * measured, create-next-app reads $HOME/.config/<tool>/config.json through
1111
- * the conf package, is handed EAGAIN for a file that was never there, and
1112
- * dies before writing one template file. Read a config, treat ENOENT as "no
1113
- * config yet", rethrow anything else is the near-universal shape. Refusing
1114
- * bought nothing the ledger was not already delivering, and cost every
1115
- * program that idiom.
1116
- *
1117
- * A LISTING is the exception and still refuses — see readdirSync. An array is
1118
- * a complete enumeration by definition, so there is no provisional form of
1119
- * it: an empty one asserts that a directory nothing ever opened is empty,
1120
- * which is how a scaffolder was told its template directory held nothing,
1121
- * wrote nothing, and exited 0.
1122
- */
1123
- function _recordUnmapped(absPath, syscall) {
1124
- const k = _strip(absPath);
1125
- _recordMiss(k);
1126
- // A listing wants the path's own contents; a read wants its bytes. Both
1127
- // also want the boundary listing, which is the only thing that can prove
1128
- // the path was not there — and fetching one repair and then the other
1129
- // would refuse the same access twice, once for being unmapped and once for
1130
- // having no resident content. _faultOnce collapses them where they meet.
1131
- _faultInBoundary(k);
1132
- if (syscall === "scandir") { _faultInDirectory(k); return; }
1133
- _faultIn(k);
1134
- }
1135
1275
 
1136
- /**
1137
- * The shallowest directory on this path whose contents the view does not
1138
- * know: the child of the deepest ancestor that WAS enumerated.
1139
- *
1140
- * That, not the immediate parent, is the listing a refusal has to pull in.
1141
- * For a path that is not there the parent is usually not there either, so
1142
- * its readdir fails, the view learns nothing, and the ledger keeps an entry
1143
- * no repair can ever retire — the run is failed over a file that was simply
1144
- * absent. The boundary directory is different: the enumerated ancestor's own
1145
- * listing already proved it is present, so it can be read, and reading it
1146
- * either names the next component or proves the whole subtree absent.
1147
- *
1148
- * With nothing enumerated at all the boundary is the root, which is one
1149
- * listing away from making the next level answerable.
1150
- */
1151
- function _unknownBoundary(k) {
1152
- const segments = k.split("/");
1153
- for (let i = segments.length - 1; i >= 0; i--) {
1154
- if (_dirEnumerated(segments.slice(0, i).join("/"))) {
1155
- return segments.slice(0, i + 1).join("/");
1156
- }
1157
- }
1158
- return "";
1159
- }
1160
1276
 
1161
- function _faultInBoundary(k) {
1162
- _faultInDirectory(_unknownBoundary(k));
1163
- }
1164
1277
 
1165
- /**
1166
- * The refusal, for the two callers that have no provisional answer to give.
1167
- *
1168
- * A listing cannot say "not found" usefully, and neither can a write that
1169
- * needs the prior content it is splicing onto. Everywhere else the sync view
1170
- * answers ENOENT and lets the exit report settle whether that was true; here
1171
- * there is no branch the program could take that would be right, so the
1172
- * honest EAGAIN is also the useful one.
1173
- */
1174
- function _refuseUnmapped(absPath, displayPath, syscall, asyncForm) {
1175
- _recordUnmapped(absPath, syscall);
1176
- const err = _fsErr("EAGAIN", syscall, displayPath);
1177
- err.message += " — '" + String(displayPath) + "' lies outside the filesystem view "
1178
- + "staged into the process, so a synchronous call cannot tell what is there, "
1179
- + "and synchronous I/O cannot block to find out"
1180
- + (_supervisor() ? "; " + asyncForm + " answers from the live filesystem" : "");
1181
- return err;
1182
- }
1183
1278
 
1184
- /**
1185
- * Pull a directory listing in. _readdirAsync already records what it learns
1186
- * into __vfsManifest, so one call both answers the fault and converges the
1187
- * sync view on the async one — there is no second merge path to keep honest.
1188
- */
1189
- function _faultInDirectory(k) {
1190
- if (!_supervisor() || _dirEnumerated(k) || !_faultOnce("listing", k)) return;
1191
- try { _repairs.push(_readdirAsync("/" + k, undefined).catch(() => {})); }
1192
- 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);
1193
1298
  }
1194
1299
 
1195
1300
  /**
1196
- * Retire the refusals a repair turned into honest absence.
1197
- *
1198
- * A refusal is only a wrong answer when the path was THERE and the process
1199
- * was denied it. Where the listing comes back and shows the name is simply
1200
- * not present, the program's not-found branch was the right branch all
1201
- * along — it merely reached it through the wrong errno — and failing the run
1202
- * over that would fail every module resolver, which probes dozens of paths
1203
- * that do not exist by design. So what stays in the ledger is exactly the
1204
- * 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.
1205
1307
  */
1206
1308
  function _settleProvenAbsences() {
1207
1309
  if (_residencyMisses.size === 0) return;
1208
1310
  for (const k of [..._residencyMisses]) {
1209
- const absPath = "/" + k;
1210
- // The authority did not have it when we asked, so nothing was withheld —
1211
- // whatever is at the path now, this process put there.
1212
- if (_observedAbsent.has(k)) { _residencyMisses.delete(k); continue; }
1213
- if (_statLadder(absPath) !== undefined) continue;
1214
- if (_absenceIsKnown(absPath)) { _residencyMisses.delete(k); continue; }
1215
- // Still unprovable, because the listing that would settle it lies below
1216
- // the one already fetched. Ask for the next one; the settle loop waits.
1217
- _faultInBoundary(k);
1311
+ if (_observedAbsent.has(k) || (_nsActive() && _statLadder("/" + k) === undefined)) _residencyMisses.delete(k);
1218
1312
  }
1219
1313
  }
1220
1314
 
@@ -1283,6 +1377,10 @@ const __fsMod = (() => {
1283
1377
 
1284
1378
  const _localTimes = globalThis.__nimbusVfsTimes || (globalThis.__nimbusVfsTimes = Object.create(null));
1285
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());
1286
1384
 
1287
1385
  function _coerceMode(value, syscall, p) {
1288
1386
  const n = typeof value === "string" ? parseInt(value, 8) : Number(value);
@@ -1307,11 +1405,9 @@ const __fsMod = (() => {
1307
1405
  atimeMs: _coerceTimeMs(atime, syscall, p),
1308
1406
  mtimeMs: _coerceTimeMs(mtime, syscall, p),
1309
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.
1310
1410
  _localTimes[k] = time;
1311
- // A spawn-time stat record answers statSync ahead of _localTimes, so it
1312
- // must carry the new times too or the sync view keeps the old mtime.
1313
- const meta = _metadata(absPath);
1314
- if (meta) { meta.atime = time.atimeMs; meta.mtime = time.mtimeMs; }
1315
1411
  return time;
1316
1412
  }
1317
1413
 
@@ -1365,7 +1461,7 @@ const __fsMod = (() => {
1365
1461
  // ══ Cache coherence: ACQUIRE + read-through fill ═════════════════════
1366
1462
  //
1367
1463
  // The supervisor's SQLite VFS is the only authority. Everything in this
1368
- // facet — __vfsBundle, __vfsMetadata, __vfsManifest, __vfsDirs — is a
1464
+ // facet — the resident store, __vfsBundle, __vfsDirs — is a
1369
1465
  // cache of it, and __vfsWrites is this process's own not-yet-flushed
1370
1466
  // mutations.
1371
1467
  //
@@ -1393,6 +1489,24 @@ const __fsMod = (() => {
1393
1489
  // a frame arrives as a supervisor reply the same barrier
1394
1490
  // rides on (see the relayed WebSocket below).
1395
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
+ //
1396
1510
  // Keep this list empty. An entry here is a documented hole in the owner's
1397
1511
  // invariant, not a TODO: it means some process can be woken by something
1398
1512
  // this system does not mediate, and its next synchronous read can serve
@@ -1414,8 +1528,18 @@ const __fsMod = (() => {
1414
1528
  // envelope — there is no reason to invent a second surface for it.
1415
1529
  const _stats = globalThis.__nimbusVfsCoherence
1416
1530
  || (globalThis.__nimbusVfsCoherence = {
1417
- fills: 0, filledBytes: 0, invalidations: 0, poisons: 0,
1531
+ fills: 0, filledBytes: 0, invalidations: 0, poisons: 0, pushes: 0,
1418
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,
1419
1543
  });
1420
1544
 
1421
1545
  /**
@@ -1448,8 +1572,6 @@ const __fsMod = (() => {
1448
1572
  let evicted = false;
1449
1573
  delete __vfsBundleRevisions[k];
1450
1574
  if (__vfsBundle && k in __vfsBundle) { delete __vfsBundle[k]; evicted = true; }
1451
- const metadata = _metadataTable();
1452
- if (metadata && k in metadata) { delete metadata[k]; evicted = true; }
1453
1575
  return evicted;
1454
1576
  }
1455
1577
 
@@ -1464,15 +1586,107 @@ const __fsMod = (() => {
1464
1586
  const _owedRefetch = new Set();
1465
1587
 
1466
1588
  // An own-mutation lease can end in an eviction (a peer wrote inside the
1467
- // window). The ledger owning that decision is spliced AHEAD of this
1468
- // closure, so it is handed the one eviction path rather than keeping a
1469
- // second copy of it: content view, stat view, the invalidation count and
1470
- // the refetch debt move together here or not at all.
1471
- globalThis.__nimbusEvictResidentCell = (k) => {
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) {
1472
1595
  if (!_evictResident(k)) return;
1473
1596
  _stats.invalidations++;
1474
1597
  _owedRefetch.add(k);
1475
- };
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
+ }
1476
1690
 
1477
1691
  /**
1478
1692
  * Install bytes the supervisor just served as the resident cell for a
@@ -1486,42 +1700,30 @@ const __fsMod = (() => {
1486
1700
  * returned, so it has nothing to invalidate. The bytes are already paid
1487
1701
  * for; caching them costs one map insert.
1488
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
+ *
1489
1709
  * The parent's manifest entry gains the name too, so the existence view
1490
1710
  * cannot go on denying a file whose bytes this process is holding.
1491
1711
  */
1492
- function _installResident(absPath, bytes) {
1712
+ function _installResident(absPath, bytes, fill) {
1493
1713
  if (!__vfsBundle) return;
1494
1714
  const k = _strip(absPath);
1495
1715
  if (k === "") return;
1716
+ if (fill.reported > fill.rev) return;
1496
1717
  // Never over a cell this facet owns.
1497
1718
  //
1498
1719
  // A pending write is strictly newer than anything the authority can
1499
1720
  // report, and __vfsBundle is what sync reads consult first, so installing
1500
1721
  // over it would serve the program bytes older than its own write.
1501
- //
1502
- // A STAMPED cell is this facet's own flushed write, and the barrier this
1503
- // read passed on the way in reported nobody else has touched the path
1504
- // since — so the bytes are already what is being installed, and the only
1505
- // thing the install would change is to drop the stamp. It did: a
1506
- // speculative repair issued by an earlier refused read landed after the
1507
- // flush, unstamped the cell, and the next ACQUIRE evicted the facet's own
1508
- // output. Measured at selfWrites 0 / invalidations 1 / fills 2 for one
1509
- // written file, against 1 / 0 / 0 with the repair absent.
1510
1722
  if (__vfsWrites && k in __vfsWrites) return;
1511
- if (__vfsBundleRevisions[k] !== undefined) return;
1512
- __vfsBundle[k] = bytes;
1513
- // Keep the stat view consistent with the bytes now held. Without this
1514
- // the content view is fresh while statSync still reports the
1515
- // spawn-time length, so a program can read N bytes and be told the
1516
- // file is M — an inconsistency introduced BY the fill. Only the size
1517
- // is corrected: type, mode, ownership and timestamps stay as the
1518
- // authority last reported them, so mtime-based change detection
1519
- // (make, tsc --build, watchers) does not see every read as a change.
1520
- const metadata = _metadataTable();
1521
- if (metadata && k in metadata) {
1522
- metadata[k] = { ...metadata[k], size: _byteLen(bytes) };
1523
- }
1524
- _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;
1525
1727
  _stats.fills++;
1526
1728
  _stats.filledBytes += _byteLen(bytes);
1527
1729
  }
@@ -1557,25 +1759,86 @@ const __fsMod = (() => {
1557
1759
  return _residentRepair;
1558
1760
  }
1559
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
+ */
1560
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();
1561
1816
  let repaired;
1562
1817
  try { repaired = await __residentSynchronizeFromSupervisor(supervisor); }
1563
1818
  catch { repaired = null; }
1564
1819
  if (!repaired || !repaired.cursor) {
1565
1820
  // The pass could not vouch for a single row — a supervisor replaced
1566
- // mid-enumeration, an enumeration that came back short. A row that
1567
- // cannot be dated must not be served, so the cold cache the poison asked
1568
- // for is taken after all, and the pass runs once more to repopulate what
1569
- // it just dropped.
1570
- __residentAdmit(result);
1571
- _cursor.epoch = result.epoch;
1572
- _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
+ }
1573
1834
  try { repaired = await __residentSynchronizeFromSupervisor(supervisor); }
1574
1835
  catch { repaired = null; }
1575
1836
  } else if (repaired.reconciled) {
1576
1837
  _stats.reconciles++;
1577
1838
  }
1578
- if (!repaired || !repaired.cursor) return;
1839
+ _settleSkippedReports(ownAtStart, repaired);
1840
+ _storeRepairOwed = !(repaired && repaired.cursor);
1841
+ if (_storeRepairOwed) return;
1579
1842
  _cursor.epoch = repaired.cursor.epoch;
1580
1843
  _cursor.rev = repaired.cursor.rev;
1581
1844
  _stats.invalidations += repaired.dropped;
@@ -1583,6 +1846,41 @@ const __fsMod = (() => {
1583
1846
  _stats.filledBytes += repaired.bytes;
1584
1847
  }
1585
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
+
1586
1884
  /**
1587
1885
  * ACQUIRE. Apply every invalidation the supervisor has for this facet,
1588
1886
  * then re-stamp the cursor.
@@ -1608,65 +1906,247 @@ const __fsMod = (() => {
1608
1906
  * left. Comparing revisions rather than names keeps the peer case intact:
1609
1907
  * a peer writing the same path afterwards reports a HIGHER revision than
1610
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.
1611
1945
  */
1612
- async function _acquireBarrier(supervisor) {
1946
+ async function _acquireBarrier(supervisor, delivered) {
1613
1947
  if (!supervisor || typeof supervisor.fsAcquire !== "function") return [];
1614
- let result;
1615
- // Through the RPC helper like every other supervisor call, because it IS
1616
- // one: the barrier is the first thing an async read issues, and while it
1617
- // was uncounted __nimbusPendingOps read zero for a whole round trip. A
1618
- // one-shot facet's event loop, which exits when no handle is live, then
1619
- // ended the program mid-read — measured as an fs.promises.readFile whose
1620
- // .then never ran, no error, no output, intermittently, and never when a
1621
- // pending timer happened to hold the program open.
1622
- try { result = await __nimbusUseRpcResult(supervisor.fsAcquire(_cursor.epoch, _cursor.rev), (r) => r); }
1623
- catch { return []; }
1624
- if (!result || typeof result.rev !== "number") return [];
1625
- // Paths that held content before this eviction are the ones worth
1626
- // refetching at a resumption boundary — a later sync read of any of them
1627
- // would otherwise miss. Collected before the delete so the membership
1628
- // test is against the pre-eviction bundle.
1629
- 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);
1630
1954
  // When the resident set lives in the facet's own SQLite, the STORE applies
1631
1955
  // the delta. That is not an optimisation, it is where provenance has to
1632
1956
  // live: a facet's SQLite outlives its module scope, so a new incarnation
1633
1957
  // opens onto rows a previous one wrote while every heap-side stamp that
1634
1958
  // described them is gone. Rows carry their own revision, __vfsBundleRevisions
1635
1959
  // cannot follow them there, and two provenance stores would be one too many.
1636
- if (_residentStorePresent()) {
1637
- if (result.poison) {
1638
- _stats.poisons++;
1639
- await _repairPoisonedStore(supervisor, result);
1640
- // Nothing is returned because a dropped cell is either refetched by
1641
- // the repair or gone from the authority too.
1642
- 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);
1643
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);
1644
2014
  const applied = __residentAdmit(result);
1645
- _cursor.epoch = result.epoch;
1646
- _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);
1647
2020
  _stats.invalidations += applied.dropped.length;
2021
+ _stats.selfWrites += applied.kept;
2022
+ _stats.pushes += applied.pushed || 0;
2023
+ await _awaitReportedOwnWrites();
1648
2024
  return applied.dropped;
1649
2025
  }
1650
- if (result.poison) {
1651
- if (__vfsBundle) for (const k of Object.keys(__vfsBundle)) { wereResident.push(k); _evictResident(k); }
1652
- _stats.poisons++;
1653
- } else if (Array.isArray(result.paths)) {
1654
- for (const entry of result.paths) {
1655
- const k = entry.path;
1656
- // This cursor is about to advance past the report, so a path with
1657
- // one of this facet's own writes in flight keeps the number: only
1658
- // that write's own revision, when it arrives, can say whether the
1659
- // report was itself or a peer. Everything else ignores it.
1660
- __nimbusNoteVfsReport(k, entry.rev);
1661
- if (__vfsBundleRevisions[k] >= entry.rev) { _stats.selfWrites++; continue; }
1662
- const held = !!(__vfsBundle && k in __vfsBundle);
1663
- if (_evictResident(k)) _stats.invalidations++;
1664
- 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");
1665
2104
  }
2105
+ return _currentAnswer(result);
2106
+ } catch (error) {
2107
+ _stats.barrierFailures++;
2108
+ _stats.lastBarrierFailure = (error && error.message) || String(error);
2109
+ return null;
1666
2110
  }
1667
- _cursor.epoch = result.epoch;
1668
- _cursor.rev = result.rev;
1669
- 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);
1670
2150
  }
1671
2151
 
1672
2152
  /**
@@ -1688,9 +2168,22 @@ const __fsMod = (() => {
1688
2168
  * This closes staleness of RESIDENT cells across an untrusted resumption.
1689
2169
  * A first synchronous touch of a NON-resident path is the separate
1690
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.
1691
2184
  */
1692
- async function _acquireAndRefetch(supervisor) {
1693
- const stale = await _acquireBarrier(supervisor);
2185
+ async function _acquireAndRefetch(supervisor, delivered) {
2186
+ const stale = await _acquireBarrier(supervisor, delivered);
1694
2187
  // Plus what an own-mutation lease dropped since the last resumption:
1695
2188
  // the same debt this function exists to settle, owed by an eviction
1696
2189
  // that had no barrier to report it (see _owedRefetch).
@@ -1698,8 +2191,58 @@ const __fsMod = (() => {
1698
2191
  for (const k of _owedRefetch) if (stale.indexOf(k) === -1) stale.push(k);
1699
2192
  _owedRefetch.clear();
1700
2193
  }
1701
- if (stale.length === 0) return;
1702
- await Promise.all(stale.map((k) => _liveReadFile("/" + k, undefined, true).catch(() => {})));
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.
2209
+ */
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;
1703
2246
  }
1704
2247
 
1705
2248
  /**
@@ -1711,14 +2254,15 @@ const __fsMod = (() => {
1711
2254
  * its fetch handler; binding the supervisor when the wrappers are
1712
2255
  * installed would silently leave every resident-TUI timer unbarriered.
1713
2256
  *
1714
- * Published on globalThis because the two other untrusted resumptions —
1715
- * an outbound fetch response and a relayed socket frame — are delivered
1716
- * 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.
1717
2261
  */
1718
- async function _resumptionAcquire() {
2262
+ async function _resumptionAcquire(delivered) {
1719
2263
  const supervisor = _supervisor();
1720
2264
  if (!supervisor || typeof supervisor.fsAcquire !== "function") return;
1721
- await _acquireAndRefetch(supervisor);
2265
+ await _acquireAndRefetch(supervisor, delivered);
1722
2266
  }
1723
2267
 
1724
2268
  /**
@@ -1752,6 +2296,7 @@ const __fsMod = (() => {
1752
2296
  if (globalThis.__nimbusResumptionBarriersInstalled) return;
1753
2297
  globalThis.__nimbusResumptionBarriersInstalled = true;
1754
2298
  globalThis.__nimbusVfsAcquireBarrier = _resumptionAcquire;
2299
+ globalThis.__nimbusVfsAcquireArgs = _acquireArgs;
1755
2300
  globalThis.__nimbusVfsReleaseBarrier = _resumptionRelease;
1756
2301
  const _setTimeout = globalThis.setTimeout;
1757
2302
  const _setInterval = globalThis.setInterval;
@@ -1859,8 +2404,9 @@ const __fsMod = (() => {
1859
2404
  // \`method\` names the supervisor RPC when it differs from the syscall the
1860
2405
  // caller reports (lchown rides \`chown\`, rm rides \`fsRemove\`).
1861
2406
  function _queueStructuralMutation(absPath, syscall, displayPath, rpc, after, method) {
2407
+ const settle = _nsTakeFresh();
1862
2408
  const supervisor = _supervisor();
1863
- if (!supervisor || typeof supervisor[method || syscall] !== "function") return null;
2409
+ if (!supervisor || typeof supervisor[method || syscall] !== "function") { settle(); return null; }
1864
2410
  const queued = _hasVfsMutationQueue();
1865
2411
  const before = queued && after ? after() : null;
1866
2412
  const run = async () => {
@@ -1876,7 +2422,11 @@ const __fsMod = (() => {
1876
2422
  );
1877
2423
  _markVfsStale();
1878
2424
  };
1879
- return queued ? __nimbusQueueVfsMutation(absPath, run) : run();
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;
1880
2430
  }
1881
2431
 
1882
2432
  /**
@@ -1889,25 +2439,33 @@ const __fsMod = (() => {
1889
2439
  * effect while the lease still holds the cell, so the stamp the end
1890
2440
  * settles describes a cell that already carries the mutation.
1891
2441
  *
1892
- * Heap-side stamps do not describe rows in the resident store, so store
1893
- * mode has no lease to take and is left to the store's own provenance.
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.
1894
2450
  */
1895
- async function _ownMutation(absPath, rpc, apply) {
2451
+ function _ownMutation(absPath, rpc, apply) {
1896
2452
  const key = _strip(absPath);
1897
- const held = !_residentStorePresent() && __nimbusBeginOwnMutation(key);
1898
- let receipt;
1899
- let landed = false;
1900
- try { receipt = await rpc(); landed = true; }
1901
- finally {
1902
- // The local effect follows the mutation landing, not the receipt: the
1903
- // receipt only settles the stamp, and a supervisor that answers
1904
- // without one still applied the bytes. The end runs even if applying
1905
- // throws: a lease left open pins its cell at Infinity, which is the one
1906
- // state in this protocol that can serve a stale byte forever.
1907
- try { if (landed && apply) apply(receipt); }
1908
- finally { __nimbusEndOwnMutation(key, held, receipt); }
1909
- }
1910
- return receipt;
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
+ });
1911
2469
  }
1912
2470
 
1913
2471
  // A sync caller has no frame to receive the outcome. The ledger already
@@ -1942,6 +2500,8 @@ const __fsMod = (() => {
1942
2500
  "write", absPath,
1943
2501
  (result) => result,
1944
2502
  ),
2503
+ true,
2504
+ true,
1945
2505
  );
1946
2506
  }
1947
2507
 
@@ -1952,13 +2512,18 @@ const __fsMod = (() => {
1952
2512
  await _flushParkedWrite(absPath, supervisor);
1953
2513
  _markVfsStale();
1954
2514
  }
1955
- // Pending sync chmod rides along with any flush of the same path
1956
- // (idempotent — the entry stays so local statSync remains coherent).
1957
- if (k in _localModes && typeof supervisor.chmod === "function") {
1958
- await _ownMutation(
1959
- absPath,
1960
- () => _fsRpc(supervisor.chmod(absPath, _localModes[k]), "chmod", absPath, (result) => result),
1961
- );
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
+ }
1962
2527
  _markVfsStale();
1963
2528
  }
1964
2529
  }
@@ -2046,8 +2611,11 @@ const __fsMod = (() => {
2046
2611
  const atime = new Date(Number(meta?.atime || meta?.mtime || Date.now()));
2047
2612
  const mode = Number(meta?.mode ?? (isDir ? 0o755 : 0o644));
2048
2613
  const stat = _localStatObject(key, isDir, isSymlink, size, mode, meta?.uid, meta?.gid);
2049
- stat.atime = atime;
2050
- 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;
2051
2619
  stat.ctime = new Date(Number(meta?.ctime ?? meta?.mtime ?? Date.now()));
2052
2620
  stat.birthtime = stat.ctime;
2053
2621
  return stat;
@@ -2080,24 +2648,32 @@ const __fsMod = (() => {
2080
2648
  // multi-MB file for one RPC frame.
2081
2649
  const READ_STREAM_CHUNK_BYTES = 65536;
2082
2650
 
2083
- // 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.
2084
2653
  //
2085
2654
  // A round trip costs an order of magnitude more than the read behind it,
2086
2655
  // so a program awaiting reads one at a time pays for round trips and
2087
2656
  // nothing else. Nothing here changes what a read sees: every request is
2088
- // the same live ranged read, executed in order, in the caller's turn. The
2089
- // gather window is one microtask, so it can only capture reads the program
2090
- // had already issued concurrently — a sequential loop batches nothing
2091
- // 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.
2092
2661
  const READ_BATCH_PATH_LIMIT = ${FS_READ_BATCH_PATH_LIMIT};
2093
2662
  const READ_BATCH_REQUEST_BYTES = ${FS_READ_BATCH_REQUEST_BYTES};
2094
2663
  let _openReadBatch = null;
2095
2664
 
2096
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) {
2097
2673
  let batch = _openReadBatch;
2098
2674
  if (batch && (
2099
2675
  batch.requests.length >= READ_BATCH_PATH_LIMIT
2100
- || batch.bytes + want > READ_BATCH_REQUEST_BYTES
2676
+ || batch.bytes + bytes > READ_BATCH_REQUEST_BYTES
2101
2677
  )) {
2102
2678
  _openReadBatch = null;
2103
2679
  _flushReadBatch(batch);
@@ -2107,17 +2683,24 @@ const __fsMod = (() => {
2107
2683
  batch = { supervisor, requests: [], settlers: [], bytes: 0 };
2108
2684
  _openReadBatch = batch;
2109
2685
  queueMicrotask(() => {
2110
- 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;
2111
2690
  _flushReadBatch(batch);
2112
2691
  });
2113
2692
  }
2114
- batch.bytes += want;
2115
- batch.requests.push({ path: absPath, offset: pos, length: want });
2116
- 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;
2117
2698
  }
2118
2699
 
2119
2700
  async function _flushReadBatch(batch) {
2120
- 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++;
2121
2704
  try {
2122
2705
  const entries = await __nimbusUseRpcResult(
2123
2706
  batch.supervisor.fsReadBatch(batch.requests), (r) => r,
@@ -2128,6 +2711,8 @@ const __fsMod = (() => {
2128
2711
  const err = new Error(entry.error.message);
2129
2712
  if (entry.error.code) err.code = entry.error.code;
2130
2713
  batch.settlers[i].reject(err);
2714
+ } else if (batch.requests[i].lstat === true) {
2715
+ batch.settlers[i].resolve(entry ? entry.stat : null);
2131
2716
  } else {
2132
2717
  batch.settlers[i].resolve(entry ? entry.bytes : null);
2133
2718
  }
@@ -2192,59 +2777,83 @@ const __fsMod = (() => {
2192
2777
  return parts;
2193
2778
  }
2194
2779
 
2195
- async function _liveReadFile(p, opts, skipAcquire) {
2780
+ async function _liveReadFile(p, opts, refetch) {
2196
2781
  const absPath = _resolve(p);
2197
2782
  const encoding = typeof opts === "string" ? opts : opts?.encoding;
2198
2783
  const supervisor = _supervisor();
2199
2784
  if (!supervisor) throw _fsErr("ENOENT", "open", p);
2200
- // The refetch step of _acquireAndRefetch has already acquired against a
2201
- // fresh cursor, so re-acquiring here would be a redundant round trip
2202
- // that always returns "still R". Every other caller acquires.
2203
- if (!skipAcquire) await _acquireBarrier(supervisor);
2204
-
2205
- if (typeof supervisor.fsReadRange === "function") {
2206
- // Chunked: the caller wants the whole file, but nothing upstream has
2207
- // to hold it all at once to produce it.
2208
- //
2209
- // A short chunk is the only signal the file ended, so this walk could
2210
- // only ever have one chunk in flight — 65 sequential round trips for a
2211
- // 4 MiB file, each costing far more than the read behind it. The FIRST
2212
- // chunk still costs exactly one trip and settles it for every file
2213
- // that fits in one. When it comes back full there is demonstrably
2214
- // more, and a stat says how much, so the remainder is issued together
2215
- // and the read batch carries it in a single trip.
2216
- const parts = [];
2217
- let total = 0;
2218
- for (;;) {
2219
- const chunk = await _readRangeAt(absPath, p, total, READ_STREAM_CHUNK_BYTES);
2220
- if (chunk === null) break;
2221
- parts.push(chunk);
2222
- total += chunk.byteLength;
2223
- if (chunk.byteLength < READ_STREAM_CHUNK_BYTES) break;
2224
- if (typeof supervisor.stat !== "function") continue;
2225
- for (const rest of await _readChunksFrom(absPath, p, supervisor, total)) {
2226
- parts.push(rest);
2227
- 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;
2228
2817
  }
2229
- 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);
2230
2822
  }
2231
- const bytes = parts.length === 1 ? parts[0] : _concatBytes(parts, total);
2232
- _installResident(absPath, bytes);
2233
- return encoding ? _asString(bytes) : __BufferMod.from(bytes);
2234
- }
2235
2823
 
2236
- if (typeof supervisor.readFile === "function") {
2237
- await _flushLocalPathToSupervisor(absPath, supervisor);
2238
- const text = await _fsReadRpc(supervisor.readFile(absPath), "open", p, (result) => result);
2239
- if (text !== null && text !== undefined) {
2240
- _installResident(absPath, text);
2241
- 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
+ }
2242
2832
  }
2833
+ } finally {
2834
+ if (!refetch) _endFill(fill);
2243
2835
  }
2244
2836
 
2245
2837
  throw _fsErr("ENOENT", "open", p);
2246
2838
  }
2247
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
+
2248
2857
  function _concatBytes(parts, total) {
2249
2858
  const out = new Uint8Array(total);
2250
2859
  let off = 0;
@@ -2316,24 +2925,11 @@ const __fsMod = (() => {
2316
2925
  // its shape: the sync existence view must not go on contradicting a
2317
2926
  // directory this very process has enumerated. Names only — size, mode
2318
2927
  // and ownership are not observable here and are never invented.
2319
- if (__vfsManifest) __vfsManifest[key] = entries.map((entry) => entry.name);
2320
- // Which of the children are directories IS observable here, and it is
2321
- // the one fact a later synchronous listing cannot recover on its own —
2322
- // an unwalked subdirectory has no manifest entry, so without this it
2323
- // reads back as a file. A directory's size is 0 in this filesystem, so
2324
- // recording it invents nothing; file children are left alone, because
2325
- // their size is not knowable from a listing and must not be guessed.
2326
- const metadataTable = _metadataTable();
2327
- if (metadataTable) {
2328
- for (const entry of entries) {
2329
- if (entry.type !== "directory") continue;
2330
- const child = prefix + entry.name;
2331
- if (child in metadataTable) continue;
2332
- metadataTable[child] = {
2333
- type: "directory", size: 0, mode: 0o755, uid: cred.uid, gid: cred.gid,
2334
- };
2335
- }
2336
- }
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).
2337
2933
  if (opts?.withFileTypes) {
2338
2934
  return entries
2339
2935
  .map((entry) => _direntObject(entry.name, entry.type, absPath))
@@ -2391,6 +2987,8 @@ const __fsMod = (() => {
2391
2987
  _fsRpc(supervisor.writeFile(absPath, content), "write", p, (result) => result)
2392
2988
  );
2393
2989
  _markVfsStale();
2990
+ // Accepted: the authority's stat is what the sync view keeps for it.
2991
+ await _learnLive(absPath, supervisor);
2394
2992
  }
2395
2993
  }
2396
2994
 
@@ -2409,6 +3007,7 @@ const __fsMod = (() => {
2409
3007
  ),
2410
3008
  );
2411
3009
  _markVfsStale();
3010
+ await _learnLive(absPath, supervisor);
2412
3011
  }
2413
3012
 
2414
3013
  // The async structural calls ARE the sync ones, awaited: the same local
@@ -2516,11 +3115,27 @@ const __fsMod = (() => {
2516
3115
  if (!supervisor && !existsSync(p)) throw _fsErr("ENOENT", syscall, p);
2517
3116
  }
2518
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
+
2519
3131
  function chmodSync(p, mode) {
2520
3132
  if (!existsSync(p)) throw _fsErr("ENOENT", "chmod", p);
3133
+ _ensureModeOwner(_resolve(p), "chmod", p);
2521
3134
  // Local-visible immediately (statSync overlay); the live write-through
2522
3135
  // rides the next flush of the same path — same fidelity as utimesSync.
2523
- _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);
2524
3139
  }
2525
3140
 
2526
3141
  async function _chmodAsync(p, mode) {
@@ -2532,7 +3147,9 @@ const __fsMod = (() => {
2532
3147
  throw _fsErr("ENOENT", "chmod", p);
2533
3148
  }
2534
3149
  const m = _coerceMode(mode, "chmod", p);
3150
+ if (localExists) _ensureModeOwner(absPath, "chmod", p);
2535
3151
  _localModes[_strip(absPath)] = m;
3152
+ _pendingModes.add(_strip(absPath));
2536
3153
  if (supervisor && typeof supervisor.chmod === "function") {
2537
3154
  await _flushLocalPathToSupervisor(absPath, supervisor);
2538
3155
  _markVfsStale();
@@ -2584,6 +3201,18 @@ const __fsMod = (() => {
2584
3201
  }
2585
3202
  const nextUid = _coerceId(uid, syscall, p);
2586
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
+ }
2587
3216
  const meta = _metadata(absPath);
2588
3217
  if (meta) { meta.uid = nextUid; meta.gid = nextGid; }
2589
3218
  // The parked write must be registered on the tail BEFORE the chown is
@@ -2600,21 +3229,25 @@ const __fsMod = (() => {
2600
3229
  function lchownSync(p, uid, gid) { _detachStructuralMutation(_chownQueued(p, uid, gid, { followSymlinks: false }, "lchown")); }
2601
3230
  function lchmodSync(p, mode) { chmodSync(p, mode); }
2602
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
+ */
2603
3239
  function _modeAllows(meta, want) {
2604
3240
  if (want === 0) return true;
2605
- 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;
2606
3246
  const currentUid = Number(cred.uid);
2607
- const currentGid = Number(cred.gid);
3247
+ if (currentUid === 0) return (want & 1) === 0 || (bits & 0o111) !== 0;
2608
3248
  const groups = cred.groups.map(Number);
2609
- if (currentUid === 0) {
2610
- if ((want & 1) !== 0 && (mode & 0o111) === 0) return false;
2611
- return true;
2612
- }
2613
- const shift = currentUid === Number(meta?.uid ?? 1000)
2614
- ? 6
2615
- : (currentGid === Number(meta?.gid ?? 1000) || groups.includes(Number(meta?.gid ?? 1000))) ? 3 : 0;
2616
- const available = (mode >> shift) & 7;
2617
- 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;
2618
3251
  }
2619
3252
 
2620
3253
  function _ensureAncestorsTraversable(absPath, syscall, p) {
@@ -2628,34 +3261,31 @@ const __fsMod = (() => {
2628
3261
  function _ensureWritable(absPath, syscall, p) {
2629
3262
  _ensureAncestorsTraversable(absPath, syscall, p);
2630
3263
  const cell = _bundleLookup(absPath);
2631
- const meta = _metadata(absPath);
2632
- // The ladder rather than existsSync: a write to a path outside the staged
2633
- // view is a legitimate create, and must not be turned into a refusal by
2634
- // the check that guards it.
2635
- if (meta !== undefined || cell !== undefined || _statLadder(absPath) !== undefined) {
2636
- const denial = _denialCode(cell);
2637
- 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);
2638
3271
  return;
2639
3272
  }
3273
+ // Nothing there: a create, judged by the parent the namespace describes.
2640
3274
 
2641
3275
  const parent = __pathMod.dirname(absPath);
2642
- const parentMeta = _metadata(parent);
2643
- if (parentMeta) {
2644
- if (parentMeta.type && parentMeta.type !== "directory") throw _fsErr("ENOTDIR", syscall, p);
2645
- 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);
2646
3280
  return;
2647
3281
  }
2648
3282
 
2649
- const parentKey = _strip(parent);
2650
- const parentIsLocal = parent === cwd || parent === "/" ||
2651
- (!!__vfsDirs && parentKey in __vfsDirs) ||
2652
- (!!__vfsManifest && parentKey in __vfsManifest);
2653
- if (parentIsLocal || _supervisor()) return;
2654
3283
  throw _fsErr("ENOENT", syscall, p);
2655
3284
  }
2656
3285
 
2657
3286
  function accessSync(p, mode) {
2658
3287
  const absPath = _resolve(p);
3288
+ _nsRequire("access", p, "fs.promises.access");
2659
3289
  const cell = _bundleLookup(absPath);
2660
3290
  const meta = _metadata(absPath);
2661
3291
  if (cell === undefined && meta === undefined && !existsSync(p)) throw _fsErr("ENOENT", "access", p);
@@ -2666,7 +3296,10 @@ const __fsMod = (() => {
2666
3296
  _ensureAncestorsTraversable(absPath, "access", p);
2667
3297
  const denial = _denialCode(cell);
2668
3298
  if ((requested & 4) !== 0 && denial) throw _fsErr(denial, "access", p);
2669
- 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);
2670
3303
  }
2671
3304
 
2672
3305
  async function _accessAsync(p, mode) {
@@ -2777,42 +3410,12 @@ const __fsMod = (() => {
2777
3410
  // ── existsSync ──
2778
3411
  function existsSync(p) {
2779
3412
  const absPath = _resolve(p);
2780
- const k = _strip(absPath);
2781
- if (_metadata(absPath) !== undefined) { _residencySatisfied(absPath); return true; }
2782
- if (__vfsBundle && k in __vfsBundle) { _residencySatisfied(absPath); return true; }
2783
- if (__vfsWrites && k in __vfsWrites) { _residencySatisfied(absPath); return true; }
2784
- if (__vfsDirs && k in __vfsDirs) { _residencySatisfied(absPath); return true; }
2785
- // W2.5b root-cause fix: consult the manifest BEFORE falling back to
2786
- // the O(N) bundle-prefix scan. The manifest is uncapped and always
2787
- // reflects the real directory shape, even when the file content for
2788
- // a directory's contents was excluded by the 4 MiB / 500-file content
2789
- if (__vfsManifest) {
2790
- if (k in __vfsManifest) { _residencySatisfied(absPath); return true; }
2791
- // Path may be a file listed by its parent's manifest entry.
2792
- const slash = k.lastIndexOf("/");
2793
- const parent = slash >= 0 ? k.slice(0, slash) : "";
2794
- const name = slash >= 0 ? k.slice(slash + 1) : k;
2795
- const sib = __vfsManifest[parent];
2796
- if (sib && sib.indexOf(name) !== -1) { _residencySatisfied(absPath); return true; }
2797
- }
2798
- // Last-resort: bundle dir entries
2799
- if (__vfsBundle && (k in __vfsBundle || __residentAnyUnder(k + "/"))) {
2800
- _residencySatisfied(absPath);
2801
- return true;
2802
- }
2803
- // Nothing found — which is only an answer if the enclosing directory was
2804
- // enumerated. Node's existsSync never throws, so the false below is
2805
- // provisional: the miss is recorded, the listing that settles it is pulled
2806
- // in, and the exit report fails the run if the path turns out to have been
2807
- // there all along.
2808
- if (!_absenceIsKnown(absPath)) {
2809
- _recordUnmapped(absPath, "access");
2810
- return false;
2811
- }
3413
+ _nsRequire("access", p, "fs.promises.access");
2812
3414
  _residencySatisfied(absPath);
2813
- return false;
3415
+ return _statLadder(absPath) !== undefined;
2814
3416
  }
2815
3417
 
3418
+
2816
3419
  // ── statSync ──
2817
3420
  function statSync(p, opts) {
2818
3421
  const absPath = _resolve(p);
@@ -2825,16 +3428,12 @@ const __fsMod = (() => {
2825
3428
  // classify a miss without redoing the resolve and ancestor walk it just
2826
3429
  // performed — that path runs on every module-resolution probe.
2827
3430
  function _statResolved(absPath, p, opts) {
3431
+ _nsRequire("stat", p, "fs.promises.stat");
2828
3432
  const stat = _statLadder(absPath);
2829
- // An answer settles the path, including the honest "not there" below —
2830
- // what the ledger is for is reads that went unanswered, not reads that came
2831
- // back with news the caller did not want. A PROVISIONAL not-there is not an
2832
- // answer, so the unmapped branch deliberately leaves the record standing.
2833
- if (stat !== undefined) { _residencySatisfied(absPath); return stat; }
2834
- // Nothing in the view describes the path, and there are two very different
2835
- // reasons for that. Only one of them means the file is not there.
2836
- if (_absenceIsKnown(absPath)) _residencySatisfied(absPath);
2837
- 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;
2838
3437
  // Node's statSync honors { throwIfNoEntry: false } by returning undefined
2839
3438
  // for a missing path instead of throwing.
2840
3439
  if (opts && opts.throwIfNoEntry === false) return undefined;
@@ -2847,154 +3446,45 @@ const __fsMod = (() => {
2847
3446
  * MEANS — absence and ignorance are different answers and only one of them
2848
3447
  * is a condition a program can act on, so the callers classify.
2849
3448
  */
2850
- function _statLadder(absPath) {
3449
+ function _statLadder(absPath, noFollow) {
2851
3450
  const k = _strip(absPath);
2852
- // Content written this exec session is newer than the spawn-time
2853
- // metadata snapshot, so it — not __vfsMetadata — is authoritative for
2854
- // size. Without this, fstatSync/statSync on a file we just wrote report
2855
- // the pre-write length.
2856
- if (__vfsWrites && k in __vfsWrites && _denialCode(__vfsWrites[k]) === null) {
2857
- const meta = _metadata(absPath);
2858
- const size = _byteLen(__vfsWrites[k]);
2859
- // Correct only the size — reusing the metadata record keeps the type
2860
- // and the recorded timestamps stable, so mtime-based change detection
2861
- // (make, tsc --build, watchers) does not see every call as a change.
2862
- if (meta) return _statObject({ ...meta, size }, k);
2863
- return _localStatObject(
2864
- k, false, false, size, 0o666 & ~__processUmask, cred.uid, cred.gid,
2865
- );
2866
- }
2867
- const metadata = _metadata(absPath);
2868
- if (metadata) return _statObject(metadata, k);
2869
- // Check if it's a known directory written this exec session
2870
- if (__vfsDirs && k in __vfsDirs) {
2871
- return _localStatObject(k, true, false, 0, 0o777 & ~__processUmask, cred.uid, cred.gid);
2872
- }
2873
- // W2.5b: consult uncapped manifest first for directory shape.
2874
- if (__vfsManifest && k in __vfsManifest) {
2875
- return _localStatObject(k, true, false, 0, 0o755, cred.uid, cred.gid);
2876
- }
2877
- // File with content embedded?
2878
- const content = _bundleLookup(absPath);
2879
- if (content !== undefined) {
2880
- // _byteLen handles both string (UTF-8 encode) and Uint8Array
2881
- // (byteLength) — fixes binary writes from reporting the
2882
- // post-corruption byte count.
2883
- const size = _byteLen(content);
2884
- return _localStatObject(k, false, false, size, 0o666 & ~__processUmask, cred.uid, cred.gid);
2885
- }
2886
- // File listed in parent's manifest but content was capped out — return
2887
- // a zero-size file stat so callers like fs.stat / fs.statSync see the
2888
- // file as present (downstream readFileSync will surface ENOENT if it
2889
- // actually tries to read content; many consumers only need stat).
2890
- if (__vfsManifest) {
2891
- const slash = k.lastIndexOf("/");
2892
- const parent = slash >= 0 ? k.slice(0, slash) : "";
2893
- const name = slash >= 0 ? k.slice(slash + 1) : k;
2894
- const sib = __vfsManifest[parent];
2895
- if (sib && sib.indexOf(name) !== -1) {
2896
- return _localStatObject(k, false, false, 0, 0o644, cred.uid, cred.gid);
2897
- }
2898
- }
2899
- // Last-resort: bundle prefix scan (legacy path).
2900
- if (__vfsBundle && __residentAnyUnder(k + "/")) {
2901
- return _localStatObject(k, true, false, 0, 0o755, cred.uid, cred.gid);
2902
- }
2903
- 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);
2904
3457
  }
2905
3458
 
2906
3459
  // ── lstatSync (alias for statSync in our VFS — no symlinks) ──
2907
- 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
+ }
2908
3469
 
2909
3470
  // ── readdirSync ──
2910
- // W2.5b root-cause fix: prefer the uncapped __vfsManifest for directory
2911
- // listings. The legacy bundle-prefix scan is kept as a fallback for paths
2912
- // not in the manifest (e.g. dirs created at exec time via mkdirSync) and
2913
- // 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).
2914
3473
  function readdirSync(p, opts) {
2915
3474
  const absPath = _resolve(p);
3475
+ _nsRequire("scandir", p, "fs.promises.readdir");
2916
3476
  _ensureAncestorsTraversable(absPath, "scandir", p);
2917
3477
  const metadata = _metadata(absPath);
2918
3478
  if (metadata && !_modeAllows(metadata, 4)) throw _fsErr("EACCES", "scandir", p);
2919
3479
  const k = _strip(absPath);
2920
- // A listing is the one answer with no way to express doubt: an array is a
2921
- // complete enumeration by definition, so returning what happens to be
2922
- // known asserts that nothing else is there. Only a directory the walk
2923
- // enumerated, or one this process created, can honestly be listed.
2924
- if (!_dirEnumerated(k) && !_bundleHasChildren(k)) {
2925
- // An authority exists that this call cannot reach, so an empty array
2926
- // would be a claim about a directory nothing ever opened. Refuse, and
2927
- // pull the listing in for the next one.
2928
- //
2929
- // This is the one caller that keeps refusing. Everywhere else the sync
2930
- // view answers a path it cannot map with the not-found the program was
2931
- // written to handle, provisionally, and lets the exit report settle
2932
- // whether that answer was true. A listing has no such answer to give:
2933
- // "no config yet, write one" is an idiom every program implements, and
2934
- // there is no counterpart for a directory — a caller handed ENOENT for a
2935
- // template tree has nowhere to go but the same wrong branch an empty
2936
- // array sent it down, and the array at least it could not detect.
2937
- if (_supervisor()) {
2938
- throw _refuseUnmapped(absPath, p, "scandir", "fs.promises.readdir");
2939
- }
2940
- // No authority: the staged tables are the whole filesystem. A path they
2941
- // do not describe is missing, and one they describe with no children is
2942
- // genuinely empty.
2943
- if (_statLadder(absPath) === undefined) throw _fsErr("ENOENT", "scandir", p);
2944
- }
2945
- const prefix = k ? k + "/" : "";
2946
- const names = new Set();
2947
- // 1. Manifest-supplied children (the authoritative source for installed pkgs).
2948
- if (__vfsManifest && k in __vfsManifest) {
2949
- for (const n of __vfsManifest[k]) names.add(n);
2950
- }
2951
- // 2. Bundle-prefix fallback (covers older paths or runtime-mkdir'd ones
2952
- // that aren't in the manifest yet).
2953
- if (__vfsBundle) {
2954
- for (const bk of __residentUnder(prefix)) {
2955
- const seg = bk.substring(prefix.length).split("/")[0];
2956
- if (seg) names.add(seg);
2957
- }
2958
- }
2959
- // 3. Files written during this exec session.
2960
- if (__vfsWrites) {
2961
- for (const wk in __vfsWrites) {
2962
- if (wk.startsWith(prefix)) {
2963
- const rest = wk.substring(prefix.length);
2964
- const seg = rest.split("/")[0];
2965
- if (seg) names.add(seg);
2966
- }
2967
- }
2968
- }
2969
- // 4. Dirs created during this exec session.
2970
- if (__vfsDirs) {
2971
- for (const dk in __vfsDirs) {
2972
- if (dk.startsWith(prefix)) {
2973
- const rest = dk.substring(prefix.length);
2974
- const seg = rest.split("/")[0];
2975
- if (seg) names.add(seg);
2976
- }
2977
- }
2978
- }
3480
+ const st = _statLadder(absPath);
3481
+ if (st === undefined) throw _fsErr("ENOENT", "scandir", p);
3482
+ if (!st.isDirectory()) throw _fsErr("ENOTDIR", "scandir", p);
2979
3483
  _residencySatisfied(absPath);
2980
- const arr = [...names].sort();
2981
- if (opts?.withFileTypes) {
2982
- return arr.map(n => {
2983
- const fp = prefix + n;
2984
- // Manifest is the definitive isDir source; fall back to bundle scan.
2985
- // The metadata record sits between them: a directory that was listed
2986
- // but never walked has no manifest entry of its own, and calling it a
2987
- // file is how a template tree full of subdirectories reads back as a
2988
- // tree full of nothing.
2989
- const isDir =
2990
- (!!__vfsManifest && fp in __vfsManifest) ||
2991
- (!!__vfsDirs && fp in __vfsDirs) ||
2992
- _metadata(fp)?.type === "directory" ||
2993
- (!!__vfsBundle && __residentAnyUnder(fp + "/"));
2994
- return _direntObject(n, isDir ? "directory" : "file", absPath);
2995
- });
2996
- }
2997
- 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));
2998
3488
  }
2999
3489
 
3000
3490
  // ── mkdirSync ──
@@ -3008,11 +3498,16 @@ const __fsMod = (() => {
3008
3498
  if (opts?.recursive) {
3009
3499
  const parts = k.split("/").filter(Boolean);
3010
3500
  let cur = "";
3011
- for (const part of parts) { cur = cur ? cur + "/" + part : part; __vfsDirs[cur] = true; created.push(cur); }
3501
+ for (const part of parts) { cur = cur ? cur + "/" + part : part; _noteCreation(cur); __vfsDirs[cur] = true; created.push(cur); }
3012
3502
  } else {
3503
+ _noteCreation(k);
3013
3504
  __vfsDirs[k] = true;
3014
3505
  created.push(k);
3015
3506
  }
3507
+ for (const dir of created) {
3508
+ const was = _nsMeta(dir, true);
3509
+ if (was === "absent") _nsOwnSet(dir, "dir", { hide: true });
3510
+ }
3016
3511
  const queued = _queueStructuralMutation(absPath, "mkdir", p, (supervisor) => supervisor.mkdir(absPath));
3017
3512
  // Told, not merely known locally: _announceLocalDirs must not issue a
3018
3513
  // second mkdir for a directory whose own RPC is already in the queue.
@@ -3022,9 +3517,30 @@ const __fsMod = (() => {
3022
3517
  function mkdirSync(p, opts) { _detachStructuralMutation(_mkdirQueued(p, opts)); }
3023
3518
 
3024
3519
  // ── unlinkSync ──
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
+
3025
3540
  function _unlinkQueued(p) {
3026
3541
  const absPath = _resolve(p);
3027
3542
  const k = _strip(absPath);
3543
+ _ensureRemovable(absPath, "unlink", p);
3028
3544
  if (__vfsBundle) delete __vfsBundle[k];
3029
3545
  if (__vfsWrites) delete __vfsWrites[k];
3030
3546
  _forgetSyncPath(k);
@@ -3036,6 +3552,7 @@ const __fsMod = (() => {
3036
3552
  function _rmdirQueued(p) {
3037
3553
  const absPath = _resolve(p);
3038
3554
  const k = _strip(absPath);
3555
+ _ensureRemovable(absPath, "rmdir", p);
3039
3556
  if (__vfsDirs) delete __vfsDirs[k];
3040
3557
  _forgetSyncPath(k);
3041
3558
  return _queueStructuralMutation(
@@ -3050,21 +3567,29 @@ const __fsMod = (() => {
3050
3567
  function _renameQueued(oldP, newP) {
3051
3568
  const oldAbs = _resolve(oldP);
3052
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);
3053
3575
  const oldK = _strip(oldAbs);
3054
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));
3055
3586
  const content = __vfsBundle?.[oldK] ?? __vfsWrites?.[oldK];
3056
3587
  if (content !== undefined) {
3057
3588
  _parkWrite(newK, content);
3058
3589
  if (__vfsBundle) delete __vfsBundle[oldK];
3059
3590
  delete __vfsBundleRevisions[oldK];
3060
3591
  delete __vfsWrites[oldK];
3061
- // The stat record travels with the content: dropping it would lose the
3062
- // mode/ownership, and leaving it behind would keep the source path
3063
- // reporting as an existing file this rename already moved away.
3064
- const metadata = _metadataTable();
3065
- const moved = metadata ? metadata[oldK] : undefined;
3066
3592
  _forgetSyncPath(oldK);
3067
- if (moved) metadata[newK] = moved;
3068
3593
  } else if (__vfsDirs && oldK in __vfsDirs) {
3069
3594
  // A directory this process made travels under its new name, so the
3070
3595
  // sync view stops listing the old one and _announceLocalDirs cannot
@@ -3079,6 +3604,16 @@ const __fsMod = (() => {
3079
3604
  }
3080
3605
  _forgetSyncTree(oldK);
3081
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
+ }
3614
+ }
3615
+ if (nsFrom !== null && !_nsOwn.has(newK)) _nsOwnSet(newK, "alias", { from: nsFrom });
3616
+ _nsOwnSet(oldK, "absentTree");
3082
3617
  const queued = _queueStructuralMutation(
3083
3618
  oldAbs, "rename", oldP,
3084
3619
  (supervisor) => supervisor.rename(oldAbs, newAbs),
@@ -3245,7 +3780,16 @@ const __fsMod = (() => {
3245
3780
  // Sync realpath stays local and identity-resolves. Async symlink
3246
3781
  // operations use the live supervisor bridge below.
3247
3782
  // .native static is required by TypeScript's getNodeSystem at
3248
- 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
+ }
3249
3793
  realpathSync.native = realpathSync;
3250
3794
 
3251
3795
  // ── Async variants (thin wrappers returning via callback) ──
@@ -3273,6 +3817,11 @@ const __fsMod = (() => {
3273
3817
  _mkdirAsync(p, opts).then(() => { if (cb) cb(null); }).catch((e) => { if (cb) cb(e); });
3274
3818
  }
3275
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
+ }
3276
3825
  function rename(oldP, newP, cb) { _renameAsync(oldP, newP).then(() => { if (cb) cb(null); }).catch((e) => { if (cb) cb(e); }); }
3277
3826
  function utimes(p, atime, mtime, cb) { _utimesAsync(p, atime, mtime).then(() => { if (cb) cb(null); }).catch((e) => { if (cb) cb(e); }); }
3278
3827
  function lutimes(p, atime, mtime, cb) { _utimesAsync(p, atime, mtime, { followSymlinks: false }).then(() => { if (cb) cb(null); }).catch((e) => { if (cb) cb(e); }); }
@@ -3329,11 +3878,8 @@ const __fsMod = (() => {
3329
3878
  // Non-resident and non-empty: writing onto a zero-filled base would
3330
3879
  // silently destroy the bytes we cannot see. Refuse instead.
3331
3880
  if (st !== undefined && st.size !== 0) throw _notResidentError(absPath, p, syscall, asyncForm);
3332
- // Absent or empty — an empty base IS the true prior content, but only
3333
- // where absence is knowledge. A path the view never mapped may hold bytes
3334
- // this process cannot see, and an O_APPEND descriptor onto a zero-filled
3335
- // base would overwrite the file with the fragment it meant to add.
3336
- if (st === undefined && !_absenceIsKnown(absPath)) throw _refuseUnmapped(absPath, p, syscall, asyncForm);
3881
+ // Absent or empty: an empty base is the true prior content (the namespace
3882
+ // names every path this credential can see).
3337
3883
  return new Uint8Array(0);
3338
3884
  }
3339
3885
 
@@ -3596,6 +4142,10 @@ const __fsMod = (() => {
3596
4142
  if (!exists && !fl.create) throw _fsErr("ENOENT", "open", path);
3597
4143
  if (exists && fl.create && fl.exclusive) throw _fsErr("EEXIST", "open", path);
3598
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));
3599
4149
  if (!exists) {
3600
4150
  await _writeFileAsync(path, new Uint8Array(0));
3601
4151
  if (mode !== undefined && mode !== null) {
@@ -3620,6 +4170,7 @@ const __fsMod = (() => {
3620
4170
  if (!exists && !fl.create) throw _fsErr("ENOENT", "open", path);
3621
4171
  if (exists && fl.create && fl.exclusive) throw _fsErr("EEXIST", "open", path);
3622
4172
  if (fl.write || !exists) _ensureWritable(absPath, "open", path);
4173
+ if (!exists) _noteCreation(_strip(absPath));
3623
4174
  let size = exists ? st.size : 0;
3624
4175
  // O_TRUNC means "make it empty", so zeroing is the requested semantic,
3625
4176
  // and creating a genuinely absent file is too. O_APPEND must NEVER
@@ -3748,7 +4299,10 @@ const __fsMod = (() => {
3748
4299
  function fchmodSync(fd, mode) {
3749
4300
  if (_isStdioFd(fd)) throw _fsErr("EINVAL", "fchmod", fd);
3750
4301
  const handle = _fdHandle(fd, "fchmod");
3751
- _localModes[_strip(handle._abs)] = _coerceMode(mode, "fchmod", handle._path);
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);
3752
4306
  }
3753
4307
  function fchownSync(fd, uid, gid) {
3754
4308
  if (_isStdioFd(fd)) throw _fsErr("EINVAL", "fchown", fd);
@@ -4088,15 +4642,16 @@ const __fsMod = (() => {
4088
4642
  // not just the local cache — is required so a subsequent async fs op
4089
4643
  // (e.g. fs.promises.rename of a copied file, as create-cloudflare
4090
4644
  // does for __dot__gitignore) sees the copy in the VFS instead of
4091
- // ENOENT.
4092
- __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 });
4093
4648
  const walk = async (relDir) => {
4094
4649
  const absDir = relDir ? srcAbs + "/" + relDir : srcAbs;
4095
4650
  const ents = await _readdirAsync(absDir, { withFileTypes: true });
4096
4651
  for (const ent of ents) {
4097
4652
  const rel = relDir ? relDir + "/" + ent.name : ent.name;
4098
4653
  if (ent.isDirectory && ent.isDirectory()) {
4099
- __vfsDirs[destK + "/" + rel] = true;
4654
+ await _mkdirAsync("/" + destK + "/" + rel, { recursive: true });
4100
4655
  await walk(rel);
4101
4656
  } else {
4102
4657
  await _writeFileAsync("/" + destK + "/" + rel, await _readFileAsync(srcAbs + "/" + rel));
@@ -4269,7 +4824,7 @@ const __fsMod = (() => {
4269
4824
  openSync, closeSync, readSync, writeSync, fstatSync, ftruncateSync, fsyncSync, fdatasyncSync,
4270
4825
  futimesSync, fchmodSync, fchownSync, readvSync, writevSync,
4271
4826
  open, close, read, write, fstat, ftruncate, fsync, fdatasync, fchmod, futimes, readv, writev,
4272
- readFile, writeFile, appendFile, stat, lstat, readdir, exists, mkdir, unlink, rename, utimes, lutimes, chmod, lchmod, chown, lchown, fchown, access,
4827
+ readFile, writeFile, appendFile, stat, lstat, readdir, exists, mkdir, unlink, rmdir, rename, utimes, lutimes, chmod, lchmod, chown, lchown, fchown, access,
4273
4828
  rm, cp, truncate, copyFile, mkdtemp, link, symlink, readlink, realpath, opendir, statfs,
4274
4829
  Dirent: __Dirent,
4275
4830
  Dir: __Dir,
@@ -4446,8 +5001,7 @@ const __NimbusRelayedWebSocket = (() => {
4446
5001
  // The barrier the whole relay exists for. Every frame is now a
4447
5002
  // supervisor reply, so it can carry the invalidation delta, and
4448
5003
  // user code runs only after the resident set has caught up.
4449
- const acquire = globalThis.__nimbusVfsAcquireBarrier;
4450
- if (typeof acquire === "function") await acquire();
5004
+ await __nimbusInboundBarrier();
4451
5005
  this._deliver(event);
4452
5006
  }
4453
5007
  }
@@ -4850,9 +5404,14 @@ ${UNDICI_SHIM_CODE}
4850
5404
  // ── util module ────────────────────────────────────────────────────
4851
5405
  // ═══════════════════════════════════════════════════════════════════════
4852
5406
  const __utilMod = {
4853
- inspect: (o, opts) => { try { return JSON.stringify(o, null, 2); } catch { return String(o); } },
4854
- format: (fmt, ...a) => {
4855
- 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(" ");
4856
5415
  let i = 0;
4857
5416
  return fmt.replace(/%[sdifjoO%]/g, (m) => {
4858
5417
  if (m === "%%") return "%";
@@ -4861,7 +5420,7 @@ const __utilMod = {
4861
5420
  if (m === "%s") return String(v);
4862
5421
  if (m === "%d" || m === "%i" || m === "%f") return Number(v).toString();
4863
5422
  if (m === "%j") { try { return JSON.stringify(v); } catch { return "[Circular]"; } }
4864
- 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);
4865
5424
  return String(v);
4866
5425
  }) + (i < a.length ? " " + a.slice(i).map(__fmt).join(" ") : "");
4867
5426
  },
@@ -5128,11 +5687,31 @@ const __utilMod = {
5128
5687
  // (set+restored by __loadModule per call). When set, the fallback becomes
5129
5688
  // "file:///" + __currentModulePath so relative URLs resolve against
5130
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.
5131
5710
  (() => {
5132
5711
  const _Orig = globalThis.URL;
5133
5712
  class _Shim extends _Orig {
5134
5713
  constructor(input, base) {
5135
- if (base == null && typeof input === "string") {
5714
+ if (arguments.length >= 2 && base == null && typeof input === "string") {
5136
5715
  try { super(input); return; }
5137
5716
  catch {
5138
5717
  // X.5-M3: prefer current module path when known, so
@@ -5714,6 +6293,13 @@ const __childProcessMod = (() => {
5714
6293
  /**
5715
6294
  * Read-loop for a single fd. Long-polls cpReadOutput, pushes chunks
5716
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.
5717
6303
  */
5718
6304
  async function _runReadLoop(child, fd, stream, sinceSeqRef) {
5719
6305
  // Exponential backoff for idle children: start at 100ms, double up
@@ -5726,12 +6312,14 @@ const __childProcessMod = (() => {
5726
6312
  while (HAS_SUPERVISOR && child.pid && !child._streamsClosed) {
5727
6313
  try {
5728
6314
  const r = await __nimbusUseRpcResult(
5729
- __supervisor.cpReadOutput(child.pid, fd, sinceSeqRef.value, backoff),
6315
+ __supervisor.cpReadOutput(child.pid, fd, sinceSeqRef.value, backoff, __nimbusVfsAcquireArgs()),
5730
6316
  (result) => result,
5731
6317
  );
5732
- 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) {
5733
6321
  backoff = 100; // reset — child is producing
5734
- for (const c of r.chunks) {
6322
+ for (const c of chunks) {
5735
6323
  // The queue hands back bytes; a Readable given a string would
5736
6324
  // encode it again.
5737
6325
  stream.write(__BufferMod.from(c.data));
@@ -5759,16 +6347,19 @@ const __childProcessMod = (() => {
5759
6347
 
5760
6348
  /**
5761
6349
  * Wait-loop: long-poll cpWait until the child reports exit. Emits
5762
- * '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.
5763
6353
  */
5764
6354
  async function _runWaitLoop(child) {
5765
6355
  while (HAS_SUPERVISOR && child.pid && !child._exitFired) {
5766
6356
  try {
5767
6357
  const r = await __nimbusUseRpcResult(
5768
- __supervisor.cpWait(child.pid, 1000),
6358
+ __supervisor.cpWait(child.pid, 1000, __nimbusVfsAcquireArgs()),
5769
6359
  (result) => result,
5770
6360
  );
5771
6361
  if (r && r.done) {
6362
+ await __nimbusInboundBarrier(r.acquired);
5772
6363
  child.exitCode = r.exitCode;
5773
6364
  child.signalCode = r.signal;
5774
6365
  child._exitFired = true;
@@ -5949,7 +6540,7 @@ const __childProcessMod = (() => {
5949
6540
  * stdout/stderr/exit events fire. Callers like cross-spawn.sync
5950
6541
  * that read result.status get null until the spawn settles.
5951
6542
  * 3. When the parent facet's main drain settles __pendingIO before
5952
- * reportExit (facet-manager.ts), the result object's fields are
6543
+ * reportExit (facets/manager.ts), the result object's fields are
5953
6544
  * filled in by the time the supervisor sees the parent exit.
5954
6545
  *
5955
6546
  * Cross-spawn.sync's typical pattern is "const r = spawnSync(...);
@@ -6292,7 +6883,7 @@ function __makeProcessStdin() {
6292
6883
  // facet, so counting it as in-flight work would mean the entry drain
6293
6884
  // never sees the program finish.
6294
6885
  packet = await __nimbusUseRpcResultUnref(
6295
- __supervisor.cpReadStdin(liveChildPid, 1000),
6886
+ __supervisor.cpReadStdin(liveChildPid, 1000, __nimbusVfsAcquireArgs()),
6296
6887
  (result) => result,
6297
6888
  );
6298
6889
  readFailures = 0;
@@ -6309,6 +6900,18 @@ function __makeProcessStdin() {
6309
6900
  await new Promise((resolve) => setTimeout(resolve, 500));
6310
6901
  continue;
6311
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
+ }
6312
6915
  if (packet && packet.resize) {
6313
6916
  __nimbusTtyColumns = Number(packet.resize.columns) || __nimbusTtyColumns;
6314
6917
  __nimbusTtyRows = Number(packet.resize.rows) || __nimbusTtyRows;
@@ -6425,6 +7028,7 @@ function __makeProcessOutputStream(streamName) {
6425
7028
  errored: null,
6426
7029
  write(d, enc, cb) {
6427
7030
  if (typeof enc === "function") cb = enc;
7031
+ if (__nimbusProgramStopped) return true;
6428
7032
  // The reported result is text; decode the bytes at this edge only.
6429
7033
  const s = __nimbusOutText(streamName, __nimbusOutBytes(d, enc));
6430
7034
  if (streamName === "stderr") stderr += s;
@@ -6470,6 +7074,8 @@ function __makeProcessOutputStream(streamName) {
6470
7074
  function __nimbusReportProcessExit(code, reason) {
6471
7075
  if (__nimbusProcessExitReported) return;
6472
7076
  __nimbusProcessExitCode = Number(code ?? 0);
7077
+ __nimbusProgramStopped = true;
7078
+ try { if (typeof globalThis.__nimbusStopProgramTimers === "function") globalThis.__nimbusStopProgramTimers(); } catch {}
6473
7079
  try { if (__nimbusProcessExitResolve) __nimbusProcessExitResolve(__nimbusProcessExitCode); } catch {}
6474
7080
  // Generated lifecycle owners defer the terminal supervisor report until
6475
7081
  // their durability boundary has drained. Reporting here would retire the
@@ -6509,7 +7115,7 @@ const __processMod = {
6509
7115
  chdir: (d) => { cwd = __pathMod.resolve(cwd || "/home/user", d); },
6510
7116
  exit: (code) => {
6511
7117
  exitCode = code ?? 0;
6512
- try { __processEvents.emit("exit", exitCode); } catch {}
7118
+ __nimbusEmitExit(exitCode);
6513
7119
  __nimbusReportProcessExit(exitCode, "");
6514
7120
  throw new __ProcessExit(exitCode);
6515
7121
  },
@@ -6923,14 +7529,27 @@ builtins.http = (() => {
6923
7529
  return new __hostResponse("Nimbus: no HTTP server is listening in this process", { status: 502 });
6924
7530
  }
6925
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 {}
6926
7536
  const headers = {};
6927
- request.headers.forEach((v, k) => { headers[k] = v; });
7537
+ request.headers.forEach((v, k) => { if (k !== "x-nimbus-vfs-acquired") headers[k] = v; });
6928
7538
  // Bytes, not text: a UTF-8 decode corrupts every binary upload, and the
6929
7539
  // decoded string is not the TypedArray receiver Buffer methods require.
6930
7540
  let body = null;
6931
7541
  if (request.method !== "GET" && request.method !== "HEAD") {
6932
7542
  body = new Uint8Array(await request.arrayBuffer());
6933
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);
6934
7553
  const res = server._handleRequest(url.pathname + url.search, request.method, headers, body);
6935
7554
  // Return once headers are known. A handler that never sends headers is
6936
7555
  // bounded by a header timeout (NOT a body-finish cap) so a hung handler
@@ -7765,38 +8384,12 @@ builtins["node:util/types"] = builtins["util/types"];
7765
8384
  // skips it via the same FACET_PROVIDED_PACKAGES list.
7766
8385
  builtins.undici = __undiciMod;
7767
8386
 
7768
- /**
7769
- * A cell's dynamic import() call.
7770
- *
7771
- * A cell is compiled with new Function, so an import() left in its body
7772
- * is the RUNTIME's: it resolves against the module map and hands back the
7773
- * platform's own builtins, not this process's. Real Vite reaches
7774
- * node:http that way (config.js:14968), got a server whose listen bound no
7775
- * port here, and the facet exited with no handles the moment boot returned.
7776
- *
7777
- * The ESM→CJS rewriter routes those calls here with the cell's own
7778
- * require, so resolution is the one the process already has. The result is
7779
- * shaped as a module namespace, since import() resolves to one and callers
7780
- * destructure named exports off it.
7781
- */
7782
- globalThis.__nimbusCellImport = (req, id) => Promise.resolve().then(() => {
7783
- const loaded = req(id);
7784
- if (loaded && (loaded.__esModule || loaded[Symbol.toStringTag] === "Module")) return loaded;
7785
- // A CJS module's exports ARE the namespace's named exports, with the whole
7786
- // object as the default export — what an ESM importer of a CJS module sees.
7787
- if (loaded && (typeof loaded === "object" || typeof loaded === "function")) {
7788
- const ns = Object.create(null);
7789
- for (const key of Object.keys(loaded)) ns[key] = loaded[key];
7790
- if (!("default" in ns)) ns.default = loaded;
7791
- Object.defineProperty(ns, Symbol.toStringTag, { value: "Module" });
7792
- return ns;
7793
- }
7794
- return { default: loaded };
7795
- });
7796
8387
  // ═══════════════════════════════════════════════════════════════════════
7797
8388
  // ── require() — full Node.js module resolution ─────────────────────
7798
8389
  // ═══════════════════════════════════════════════════════════════════════
7799
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});
7800
8393
 
7801
8394
  /**
7802
8395
  * Direct VFS bundle access for module resolution.
@@ -7822,24 +8415,8 @@ function __readFileOr(path, fallback) {
7822
8415
  }
7823
8416
  function __fileExists(path) {
7824
8417
  const k = path.replace(/^\\/+/, "");
7825
- if (__vfsBundle && k in __vfsBundle) return true;
7826
- if (__vfsWrites && k in __vfsWrites) return true;
7827
- if (__vfsDirs && k in __vfsDirs) return true;
7828
- // Consult the uncapped manifest (directory shape) so resolution sees
7829
- // installed files whose content was excluded from the bounded snapshot
7830
- // (e.g. web-streams-polyfill's ponyfill/package.json, reached via a
7831
- // parent-relative main). Mirrors existsSync's manifest probe.
7832
- if (__vfsManifest) {
7833
- if (k in __vfsManifest) return true;
7834
- const slash = k.lastIndexOf("/");
7835
- const parent = slash >= 0 ? k.slice(0, slash) : "";
7836
- const name = slash >= 0 ? k.slice(slash + 1) : k;
7837
- const sib = __vfsManifest[parent];
7838
- if (sib && sib.indexOf(name) !== -1) return true;
7839
- }
7840
- // Check for directory by looking for any key with this prefix
7841
- if (__vfsBundle && __residentAnyUnder(k + "/")) return true;
7842
- return false;
8418
+ // The namespace answers exactly (vfs/facet-resident-store.ts).
8419
+ return __fsMod.existsSync("/" + k);
7843
8420
  }
7844
8421
  // W3.5 Fix A: strict-file membership probe. __fileExists also returns true for
7845
8422
  // directories (it has to — __resolveNodeModule and __resolveImportsField call
@@ -7851,26 +8428,8 @@ function __fileExists(path) {
7851
8428
  // the fastify ret/dist/types failure.
7852
8429
  function __pathIsFile(path) {
7853
8430
  const k = path.replace(/^\\/+/, "");
7854
- if (__vfsBundle && k in __vfsBundle) return true;
7855
- if (__vfsWrites && k in __vfsWrites) return true;
7856
- // Consult the manifest as a strict-file probe: a name listed in its
7857
- // parent's manifest entry is a file (directories are manifest KEYS).
7858
- // This lets resolution find files whose content was excluded from the
7859
- // bounded snapshot without re-introducing the directory short-circuit
7860
- // (__vfsManifest[k] being a directory key is deliberately NOT matched).
7861
- if (__vfsManifest) {
7862
- const slash = k.lastIndexOf("/");
7863
- const parent = slash >= 0 ? k.slice(0, slash) : "";
7864
- const name = slash >= 0 ? k.slice(slash + 1) : k;
7865
- const sib = __vfsManifest[parent];
7866
- if (sib && sib.indexOf(name) !== -1 && !(k in __vfsManifest)) return true;
7867
- }
7868
- // Last resort: the full fs view (live SQLite VFS) for paths outside the
7869
- // bounded snapshot/manifest entirely — e.g. the /tmp/.npx-cache tree,
7870
- // which the project snapshot never covers. stat-as-file only.
7871
- try { const st = __fsMod.statSync("/" + k); if (st && st.isFile && st.isFile()) return true; } catch {}
7872
- // Deliberately does NOT consult __vfsDirs nor do the prefix scan.
7873
- return false;
8431
+ const st = __fsMod.statSync("/" + k, { throwIfNoEntry: false });
8432
+ return !!st && st.isFile();
7874
8433
  }
7875
8434
  function __resolveFile(base) {
7876
8435
  // Mirrors Node's LOAD_AS_FILE + LOAD_AS_DIRECTORY (require_2 spec):
@@ -7943,15 +8502,12 @@ function __resolveFile(base) {
7943
8502
  }
7944
8503
 
7945
8504
  // __compiledModules is defined at MODULE TOP LEVEL in the generator code
7946
- // (facet-manager.ts) so new Function() runs during module evaluation.
8505
+ // (facets/manager.ts) so new Function() runs during module evaluation.
7947
8506
 
7948
8507
  // ── Single-source-of-truth exports/imports resolver (W2) ───────────────
7949
8508
  // Emitted from src/_shared/exports-resolver.ts via getExportsResolverJS().
7950
8509
  // Declares: resolveExports, resolveConditionValue, resolvePackageEntry,
7951
8510
  // DEFAULT_ESM_CONDITIONS, DEFAULT_CJS_CONDITIONS.
7952
- // the prior hand-rolled __resolvePkgEntry only honoured top-level
7953
- // require|default|import and dropped subpath maps, wildcards, nested
7954
- // conditions, the imports field, and null-target enforcement.
7955
8511
  ${EXPORTS_RESOLVER_JS}
7956
8512
 
7957
8513
  // ── TypeScript specifier fallbacks ────────────────────────────────────────
@@ -7989,8 +8545,8 @@ function __resolvePkgSubpath(pkgDir, pkg, subpath) {
7989
8545
  }
7990
8546
  let entry = resolvePackageEntry(pkg, subpath, __NIMBUS_CJS_CONDITIONS);
7991
8547
  // X.5-F R3: ESM-condition fallback for pure-ESM packages whose
7992
- // dist/.mjs files were transformed to CJS by transformEsmInBundle
7993
- // 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,
7994
8550
  // packages like nuxt — whose exports map only contains
7995
8551
  // {types, import} for the root subpath — return null from the CJS
7996
8552
  // walk and dead-end with "Cannot find module 'nuxt'" even though
@@ -8033,10 +8589,6 @@ function __resolvePkgSubpath(pkgDir, pkg, subpath) {
8033
8589
  return __resolveFile(pkgDir + "/" + rel);
8034
8590
  }
8035
8591
 
8036
- /** Back-compat name used elsewhere in this file. */
8037
- function __resolvePkgEntry(pkgDir) {
8038
- return __resolvePkgSubpath(pkgDir, null, ".");
8039
- }
8040
8592
 
8041
8593
  /**
8042
8594
  * Resolve a bare specifier (e.g. "react", "@scope/pkg", "pkg/sub/path")
@@ -8085,32 +8637,23 @@ function __resolveNodeModule(name, fromDir) {
8085
8637
  }
8086
8638
 
8087
8639
  /**
8088
- * Resolve a #name imports-field specifier from the nearest enclosing
8089
- * 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.
8090
8649
  */
8091
- function __resolveImportsField(name, fromDir) {
8092
- // Walk up looking for the nearest package.json. Stop at the first one
8093
- // (Node spec: imports field of the importing module's package).
8650
+ function __nearestPackageScope(fromDir) {
8094
8651
  let dir = (fromDir || "").replace(/^\\/+/, "");
8095
8652
  while (true) {
8653
+ if (dir === "node_modules" || dir.endsWith("/node_modules")) return null;
8096
8654
  const pkgJsonPath = (dir ? dir + "/" : "") + "package.json";
8097
8655
  if (__fileExists(pkgJsonPath)) {
8098
- const pkg = __readPkgJson(dir);
8099
- if (pkg && pkg.imports) {
8100
- const target = resolveExports(pkg.imports, name, __NIMBUS_CJS_CONDITIONS);
8101
- if (target) {
8102
- // Imports targets are relative to the package root (dir)
8103
- if (target.startsWith("./")) {
8104
- return __resolveFile((dir ? dir + "/" : "") + target.slice(2));
8105
- }
8106
- if (target.startsWith("/")) {
8107
- return __resolveFile(target.slice(1));
8108
- }
8109
- // Bare specifier — re-resolve as a node_module from this dir
8110
- return __resolveNodeModule(target, dir);
8111
- }
8112
- }
8113
- return null; // first package.json wins, even if no imports field
8656
+ return { dir, pkg: __readPkgJson(dir) };
8114
8657
  }
8115
8658
  if (!dir) return null;
8116
8659
  const lastSlash = dir.lastIndexOf("/");
@@ -8118,37 +8661,68 @@ function __resolveImportsField(name, fromDir) {
8118
8661
  }
8119
8662
  }
8120
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
+
8121
8718
  // ═══════════════════════════════════════════════════════════════════════
8122
- // ── X.5-S: __mkCompiledFn — conditional-param-rename wrap for new Function
8719
+ // ── __mkCompiledFn — the request-time fallback compile ──────────────
8123
8720
  // ═══════════════════════════════════════════════════════════════════════
8124
- //
8125
- // vite's chunks/node.js (transitive bundle of open@10.2.0) contains the
8126
- // ESM idiom \`const __dirname = path.dirname(fileURLToPath(import.meta.url))\`.
8127
- // W3.5 Fix B's esbuild ESM→CJS transform preserves that line verbatim
8128
- // while substituting \`import.meta\` with \`const import_meta = {}\`. Wrapping
8129
- // the body in \`new Function("exports","require","module","__filename","__dirname", code)\`
8130
- // then collides at parse time:
8131
- //
8132
- // SyntaxError: Identifier '__dirname' has already been declared
8133
- //
8134
- // (VERIFY-23417C5 §4 #1 / X5M3-retro §"Next bucket".) The helper RENAMES
8135
- // the conflicting param to a placeholder name so the body's own
8136
- // \`const __dirname\` becomes the single declarer. We rename rather than
8137
- // drop because callers pass 5 positional arguments and dropping a slot
8138
- // would mis-align downstream slots (e.g. the USER_CODE wrap appends
8139
- // \`console\` / \`process\` / etc. after \`__dirname\`). Renaming preserves
8140
- // slot alignment while letting the body's binding win.
8141
- //
8142
- // Symmetric for \`__filename\` because open@10's idiom often emits both.
8143
- function __mkCompiledFn(code) {
8144
- const reFn = /(?:^|\\n|;)\\s*(?:const|let|var)\\s+__filename\\s*=/m;
8145
- const reDn = /(?:^|\\n|;)\\s*(?:const|let|var)\\s+__dirname\\s*=/m;
8146
- const reRequire = /(?:^|\\n|;)\\s*(?:const|let|var)\\s+require\\s*=/m;
8147
- const fnName = reFn.test(code) ? "__filename__nimbus_unused" : "__filename";
8148
- const dnName = reDn.test(code) ? "__dirname__nimbus_unused" : "__dirname";
8149
- const requireName = reRequire.test(code) ? "require__nimbus_unused" : "require";
8150
- return new Function("exports", requireName, "module", fnName, dnName, code);
8151
- }
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}
8152
8726
 
8153
8727
  function __exportsTarget(mod) {
8154
8728
  const value = mod.exports;
@@ -8200,11 +8774,12 @@ function __makeLoadingExports(mod) {
8200
8774
  * Load and execute a JS/JSON module from VFS.
8201
8775
  * Returns the module.exports value.
8202
8776
  */
8203
- function __loadModule(resolvedPath) {
8204
- 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);
8205
8780
 
8206
8781
  const mod = { exports: {} };
8207
- __moduleCache.set(resolvedPath, __makeLoadingExports(mod));
8782
+ __moduleCache.set(evaluationKey, __makeLoadingExports(mod));
8208
8783
 
8209
8784
  const code = __readFileOr(resolvedPath, null);
8210
8785
  if (code === null) throw new Error("Cannot read module: " + resolvedPath);
@@ -8212,7 +8787,7 @@ function __loadModule(resolvedPath) {
8212
8787
  // JSON
8213
8788
  if (resolvedPath.endsWith(".json")) {
8214
8789
  mod.exports = JSON.parse(code);
8215
- __moduleCache.set(resolvedPath, mod.exports);
8790
+ __moduleCache.set(evaluationKey, mod.exports);
8216
8791
  return mod.exports;
8217
8792
  }
8218
8793
 
@@ -8235,6 +8810,16 @@ function __loadModule(resolvedPath) {
8235
8810
  // const import_meta = {}. Save+restore for recursive __loadModule.
8236
8811
  const __prevModulePath = globalThis.__currentModulePath;
8237
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 });
8238
8823
  try {
8239
8824
  // Use pre-compiled function from startup (new Function allowed at module eval time)
8240
8825
  // Normalize path to match VFS bundle key format (no leading /)
@@ -8259,7 +8844,7 @@ function __loadModule(resolvedPath) {
8259
8844
  } catch (evalErr) {
8260
8845
  // W3.5 Fix C: if the file was in the bundle but its pre-compile
8261
8846
  // failed at facet startup, surface the original SyntaxError
8262
- // instead of the misleading "file was not pre-bundled" text.
8847
+ // instead of the misleading "not in this launch's module map" text.
8263
8848
  const normalizedPath2 = resolvedPath.replace(/^\\/+/, "");
8264
8849
  const compileErr =
8265
8850
  (typeof __compileFailures !== "undefined" && __compileFailures &&
@@ -8271,22 +8856,27 @@ function __loadModule(resolvedPath) {
8271
8856
  );
8272
8857
  }
8273
8858
  if (evalErr.message && evalErr.message.includes("Code generation from strings disallowed")) {
8274
- 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.");
8275
8863
  }
8276
8864
  throw evalErr;
8277
8865
  }
8278
8866
  }
8279
8867
  } catch (e) {
8280
- __moduleCache.delete(resolvedPath);
8868
+ __moduleCache.delete(evaluationKey);
8281
8869
  if (e && typeof e === "object" && !e.__nimbusModulePath) {
8282
8870
  try {
8283
- e.__nimbusModulePath = resolvedPath;
8284
- if (typeof e.message === "string") {
8285
- e.message += "\\nNimbus module: " + resolvedPath;
8286
- }
8287
- if (typeof e.stack === "string" && !e.stack.includes("Nimbus module:")) {
8288
- e.stack += "\\nNimbus module: " + resolvedPath;
8289
- }
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;
8290
8880
  } catch {}
8291
8881
  }
8292
8882
  throw e;
@@ -8295,7 +8885,7 @@ function __loadModule(resolvedPath) {
8295
8885
  }
8296
8886
 
8297
8887
  // Update cache with final exports (module.exports may have been reassigned)
8298
- __moduleCache.set(resolvedPath, mod.exports);
8888
+ __moduleCache.set(evaluationKey, mod.exports);
8299
8889
  return mod.exports;
8300
8890
  }
8301
8891
 
@@ -8304,6 +8894,25 @@ function __loadModule(resolvedPath) {
8304
8894
  * Returns the resolved VFS path, or null.
8305
8895
  */
8306
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
+ }
8307
8916
  // X.5-P: literal "." / ".." are CommonJS aliases for "./" / "../".
8308
8917
  // Pre-fix they slipped past the startsWith("./")/("../") guards (which
8309
8918
  // require >= 3 / >= 4 chars respectively) and fell into the bare-spec
@@ -8333,43 +8942,137 @@ function __resolveFrom(id, fromDir) {
8333
8942
  if (id.startsWith("#")) {
8334
8943
  return __resolveImportsField(id, fromDir);
8335
8944
  }
8336
- // 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;
8337
8952
  return __resolveNodeModule(id, fromDir);
8338
8953
  }
8339
8954
 
8340
- globalThis.__nimbusImportMetaResolve = function __nimbusImportMetaResolve(specifier, parentUrl) {
8341
- const currentFromParent = typeof parentUrl === "string" && parentUrl.startsWith("file:")
8342
- ? parentUrl.replace(/^file:\\/\\/\\/+/, "")
8343
- : "";
8344
- const current = currentFromParent || (
8345
- typeof globalThis.__currentModulePath === "string"
8346
- ? globalThis.__currentModulePath.replace(/^\\/+/, "")
8347
- : ""
8348
- );
8349
- const fromDir = current.includes("/") ? current.substring(0, current.lastIndexOf("/")) : "";
8350
- const text = String(specifier);
8351
- if (text.startsWith("file:")) return text;
8352
- if (text.startsWith("./") || text.startsWith("../") || text.startsWith("/")) {
8353
- return new URL(text, current ? "file:///" + current : "file:///").href;
8354
- }
8355
- const resolved = __resolveFrom(text, fromDir);
8356
- // Node throws ERR_MODULE_NOT_FOUND rather than answering with the specifier
8357
- // it could not resolve, and packages are written against that: the standard
8358
- // shape is a try/catch that reports something the user can act on. Handing
8359
- // back the bare specifier passes the "unresolved" case off as a URL, so
8360
- // fileURLToPath downstream yields a path that was never on disk and the
8361
- // eventual error names a phantom file instead of the missing package.
8362
- // typescript@7 does exactly this: with the specifier echoed back it reports
8363
- // "Executable not found: @typescript/typescript-linux-x64/lib/tsc", where
8364
- // its own catch would have said the platform package is missing.
8365
- if (!resolved) {
8366
- const err = new Error(
8367
- "Cannot find package '" + text + "' imported from " + (current || fromDir || "<unknown>"),
8368
- );
8369
- err.code = "ERR_MODULE_NOT_FOUND";
8370
- 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
+ }
8371
9043
  }
8372
- 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);
8373
9076
  };
8374
9077
 
8375
9078
  /**