@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
@@ -0,0 +1,889 @@
1
+ const __vfsWriteGenerations = Object.create(null);
2
+ // Per-path: the authority revision the resident cell in __vfsBundle is
3
+ // known-good at. Only a flush of this facet's own bytes sets one, this
4
+ // facet's own partial mutations of a stamped cell advance it, and the
5
+ // ACQUIRE barrier is the only reader. An unstamped cell is simply evicted,
6
+ // so a mutation path that forgets to stamp costs a refetch and never a
7
+ // stale byte. Infinity is not a revision: it is the lease below, held
8
+ // while one of this facet's own mutations of the path is in flight.
9
+ // Heap cells only: the resident store dates each row in the row itself
10
+ // (__nimbusResidentRows), and this map says nothing about those.
11
+ const __vfsBundleRevisions = Object.create(null);
12
+ // Own mutations in flight per path: the stamp the first lease began with,
13
+ // how many are outstanding, whether a receipt has already proven a peer
14
+ // wrote inside the window, and the highest revision a barrier reported for
15
+ // the path while the lease suppressed it.
16
+ //
17
+ // Overlapping own mutations of one path are legal — chown and the chmod
18
+ // ride-along are not queued behind each other — so a second begin reuses
19
+ // the record rather than opening a second window, and an end whose receipt
20
+ // reports a `before` past the record's stamp marks `peer`. That verdict
21
+ // cannot distinguish a stranger's write from the sibling own mutation still
22
+ // in flight, and does not try to: it is a conservative refetch, never
23
+ // staleness.
24
+ const __vfsOwnLeases = Object.create(null);
25
+ // Per path with a PARKED write: the highest revision a barrier reported for
26
+ // it while that write was in flight. A parked cell is unstamped, so the
27
+ // barrier evicts it and consumes the report; the flush's own revision is
28
+ // what finally says whether that report was its own write coming back.
29
+ // Bounded by the parked set — the entry is retired with the cell below.
30
+ const __vfsParkedReports = Object.create(null);
31
+ const __vfsAppendWrites = Object.create(null);
32
+ const __vfsWrites = new Proxy(Object.create(null), {
33
+ set(target, path, value) {
34
+ target[path] = value;
35
+ delete __vfsAppendWrites[path];
36
+ delete __vfsParkedReports[path];
37
+ __vfsWriteGenerations[path] = (__vfsWriteGenerations[path] || 0) + 1;
38
+ // Parking a cell is the only signal a synchronous write leaves. It is
39
+ // therefore the one place a write-back can be scheduled from, and it
40
+ // covers every sync mutation — writeFileSync, appendFileSync, the fd
41
+ // writes, rename — with no per-call-site duplication.
42
+ __nimbusScheduleVfsWriteBack();
43
+ return true;
44
+ },
45
+ deleteProperty(target, path) {
46
+ delete __vfsAppendWrites[path];
47
+ delete __vfsParkedReports[path];
48
+ if (Object.prototype.hasOwnProperty.call(target, path)) {
49
+ delete target[path];
50
+ __vfsWriteGenerations[path] = (__vfsWriteGenerations[path] || 0) + 1;
51
+ }
52
+ return true;
53
+ },
54
+ });
55
+
56
+ const __vfsMutationTails = new Map();
57
+ const __nimbusPendingVfsMutations = new Set();
58
+ let __nimbusPendingVfsMutationFailure;
59
+ let __nimbusHasPendingVfsMutationFailure = false;
60
+ const __vfsWriteClaims = new Map();
61
+ // Per path: this facet's own writes and mutations the authority has been
62
+ // asked to apply whose acknowledgement is not adjudicated yet
63
+ // (__nimbusOwnAcknowledgement). Each entry settles, never rejects.
64
+ const __vfsOwnAcks = new Map();
65
+ const __nimbusVfsAppendRangeResult = {};
66
+ // Operation sequences reset when this generated module is evaluated again.
67
+ // The nonce namespaces those retries without pretending a new application
68
+ // request is the same logical append. Minted lazily: this source is spliced
69
+ // into the opencode runner's module scope, where workerd forbids global-scope
70
+ // RNG; the first append always runs in handler context.
71
+ let __nimbusVfsModuleIncarnationNonce;
72
+ function __nimbusVfsModuleIncarnation() {
73
+ return (__nimbusVfsModuleIncarnationNonce ??= crypto.randomUUID());
74
+ }
75
+ let __nimbusVfsAppendOperationSequence = 0;
76
+
77
+ function __nimbusVfsPathKey(path) {
78
+ return String(path).replace(/^\/+/, "");
79
+ }
80
+
81
+ // Write-backs in flight at once. Unbounded, a burst of ~250 left some
82
+ // SupervisorRPC-to-DO calls undelivered with no error, so the process never
83
+ // exited (measured 2026-09-22); capped at 6, none stalled.
84
+ const __NIMBUS_VFS_RPC_MAX_IN_FLIGHT = 6;
85
+ let __nimbusVfsRpcInFlight = 0;
86
+ const __nimbusVfsRpcWaiters = [];
87
+
88
+ /**
89
+ * A supervisor round trip the ledger issues on its own account.
90
+ *
91
+ * A facet's event loop exits when no handle is live, and an in-flight
92
+ * supervisor RPC is one of the handles it counts — but the debounced
93
+ * write-back runs from a raw timer, outside any call the program is awaiting,
94
+ * so nothing else was counting it. Measured: a template copy parked a cell,
95
+ * the debounce fired 10ms later and issued the write, the explicit flush
96
+ * behind it joined that same in-flight promise rather than starting its own,
97
+ * and the loop saw zero handles and ended the program with two files copied
98
+ * out of twenty-eight — silently, exit 0. fs.promises.cp is how
99
+ * create-cloudflare copies its template.
100
+ *
101
+ * The counter is the shims' __nimbusPendingOps, reached through globalThis
102
+ * rather than by calling their RPC helper: this source is spliced ahead of
103
+ * them and into embeddings that are not the one-shot entrypoint, and an
104
+ * absent counter must not be an error.
105
+ */
106
+ async function __nimbusVfsRpc(issue) {
107
+ if (typeof globalThis.__nimbusPendingOps !== "number") globalThis.__nimbusPendingOps = 0;
108
+ globalThis.__nimbusPendingOps++;
109
+ try {
110
+ if (__nimbusVfsRpcInFlight < __NIMBUS_VFS_RPC_MAX_IN_FLIGHT) {
111
+ __nimbusVfsRpcInFlight++;
112
+ } else {
113
+ const slot = Promise.withResolvers();
114
+ __nimbusVfsRpcWaiters.push(slot.resolve);
115
+ await slot.promise;
116
+ }
117
+ try { return await issue(); }
118
+ finally {
119
+ // Hand the slot straight to the next waiter; the count only drops when none waits.
120
+ const next = __nimbusVfsRpcWaiters.shift();
121
+ if (next) next();
122
+ else __nimbusVfsRpcInFlight--;
123
+ }
124
+ } finally { globalThis.__nimbusPendingOps--; }
125
+ }
126
+
127
+ /**
128
+ * Errno values that are the filesystem ANSWERING the syscall: the path is not
129
+ * there, it is a directory, the descriptor is closed. The operation did not
130
+ * apply, no bytes were in flight, and nothing the program believes is saved
131
+ * has been lost. Node hands these to the caller and lets it decide — which is
132
+ * why `fs.truncate(missing).catch(() => {})` is ordinary, correct code.
133
+ *
134
+ * ENOSPC is one of them: the session's storage ledger (N18) refuses a write
135
+ * before any of it is made.
136
+ *
137
+ * Everything else — EIO, a dropped RPC, an authority that died, an
138
+ * error carrying no errno at all — is not an answer. It means the outcome of
139
+ * a write is UNKNOWN, and that is a durability event no matter what the
140
+ * program caught. Unrecognised is treated as durability-class on purpose: the
141
+ * safe direction is to surface.
142
+ */
143
+ const __NIMBUS_SYSCALL_VERDICT_CODES = new Set([
144
+ "ENOENT", "EEXIST", "EISDIR", "ENOTDIR", "ENOTEMPTY",
145
+ "EBADF", "EINVAL", "EPERM", "EACCES", "ELOOP", "ENAMETOOLONG", "ENOSPC",
146
+ ]);
147
+
148
+ function __nimbusIsDurabilityFailure(error) {
149
+ if (error && typeof error === "object" && error.nimbusRefusedWriteBack === true) return true;
150
+ const code = error && typeof error === "object" ? error.code : undefined;
151
+ return typeof code !== "string" || !__NIMBUS_SYSCALL_VERDICT_CODES.has(code);
152
+ }
153
+
154
+ /**
155
+ * Everything already queued for `path` and for every proper ancestor of it,
156
+ * as of NOW — a snapshot, not a subscription.
157
+ *
158
+ * The queue orders mutations per path and nothing else, so `mkdirSync(a)`
159
+ * followed by anything under `a` — `mkdirSync(a/b)`, a flushed write of
160
+ * `a/f`, an `fs.promises.open(a/f, "w")` — could reach the authority
161
+ * ahead of the directory it lives in and be answered ENOENT for a parent
162
+ * the program had demonstrably created. node-tar does exactly this for
163
+ * every entry it extracts.
164
+ *
165
+ * Snapshotting at call time is what keeps the graph acyclic: a mutation
166
+ * only ever waits on mutations that were queued before it, in program
167
+ * order. Capturing the tails inside the mutation body instead would let a
168
+ * rename queued behind a slow ancestor pick up a descendant that was
169
+ * queued after it — and that descendant is already waiting on the rename.
170
+ *
171
+ * Map lookups only; resolves on the next tick when nothing is pending.
172
+ * Tails never reject, so neither does this.
173
+ */
174
+ function __nimbusAwaitAncestorMutations(path) {
175
+ const pending = [];
176
+ let prefix = "";
177
+ for (const segment of __nimbusVfsPathKey(path).split("/")) {
178
+ if (!segment) continue;
179
+ prefix = prefix ? prefix + "/" + segment : segment;
180
+ const tail = __vfsMutationTails.get(prefix);
181
+ if (tail) pending.push(tail);
182
+ }
183
+ return pending.length === 0 ? Promise.resolve() : Promise.all(pending).then(() => undefined);
184
+ }
185
+
186
+ /**
187
+ * Everything already queued strictly BELOW `path`, as of now. The
188
+ * complement of the ancestor wait, for the two mutations that act on a
189
+ * whole subtree: rmdir needs the children gone first, and a rename must
190
+ * not carry the old name across while a mutation under it is still bound
191
+ * for the old name.
192
+ */
193
+ function __nimbusAwaitSubtreeMutations(path) {
194
+ const prefix = __nimbusVfsPathKey(path) + "/";
195
+ const pending = [];
196
+ for (const [key, tail] of __vfsMutationTails) {
197
+ if (key.startsWith(prefix)) pending.push(tail);
198
+ }
199
+ return pending.length === 0 ? Promise.resolve() : Promise.all(pending).then(() => undefined);
200
+ }
201
+
202
+ /**
203
+ * Order a mutation behind the others queued for the same path.
204
+ *
205
+ * A rejection is ALSO reported to the drain when it is durability-class. That
206
+ * second channel is not redundancy: the tail handler below marks `result`
207
+ * handled, which suppresses the platform's own `unhandledrejection` signal,
208
+ * so retention is the only thing that can reach a durability boundary. Losing
209
+ * it is how a handler whose write failed still answers 200 — silent wrong
210
+ * data, which is what this ledger exists to prevent.
211
+ *
212
+ * What must NOT be retained is a plain syscall verdict. The queue used to
213
+ * retain those too, so an error the program had already caught was delivered
214
+ * a second time at teardown and killed the process: `opencode --help`
215
+ * rendered its whole help surface and then exited 1 on the
216
+ * `fs.truncate(logfile).catch(() => {})` in its logger init.
217
+ *
218
+ * The seam is the ERROR, not the call site. The same source line —
219
+ * `fs.promises.truncate(p).catch(() => {})` — must exit 0 when the file was
220
+ * simply absent, and must fail the response when the authority could not say
221
+ * whether the write landed. No per-call-site flag can express that, because
222
+ * both cases arrive through the same call site.
223
+ */
224
+ function __nimbusQueueVfsMutation(path, mutation, retainFailure = true) {
225
+ const key = __nimbusVfsPathKey(path);
226
+ const previous = __vfsMutationTails.get(key) || Promise.resolve();
227
+ // Every queued mutation is ordered behind the structural mutations
228
+ // pending for its ancestors — one place, so a flushed write, an fd write
229
+ // and a queued mkdir all obey the same rule without each site knowing it.
230
+ const ancestors = __nimbusAwaitAncestorMutations(key);
231
+ const result = previous.then(() => ancestors).then(mutation);
232
+ __nimbusPendingVfsMutations.add(result);
233
+ // A failed mutation rejects its own caller but must not poison later writes
234
+ // for the same path or become an unhandled queue-cleanup rejection.
235
+ let tail;
236
+ const clearTail = () => {
237
+ __nimbusPendingVfsMutations.delete(result);
238
+ if (__vfsMutationTails.get(key) === tail) {
239
+ __vfsMutationTails.delete(key);
240
+ }
241
+ };
242
+ tail = result.then(clearTail, (error) => {
243
+ if (retainFailure
244
+ && __nimbusIsDurabilityFailure(error)
245
+ && !__nimbusHasPendingVfsMutationFailure) {
246
+ __nimbusHasPendingVfsMutationFailure = true;
247
+ __nimbusPendingVfsMutationFailure = error;
248
+ }
249
+ clearTail();
250
+ });
251
+ __vfsMutationTails.set(key, tail);
252
+ return result;
253
+ }
254
+
255
+ /**
256
+ * Run `work` as an acknowledgement in flight for `path`: one of this
257
+ * facet's own writes or mutations of it, from being issued to the authority
258
+ * through adjudicating what the authority answered — the stamp that dates
259
+ * the cell, or the eviction when a barrier's report outran it.
260
+ *
261
+ * Until then the facet cannot tell whether the authority has applied it, or
262
+ * at which revision, and a barrier that reported the path cannot tell whether
263
+ * that report was this very write or a peer's after it. Served, the facet's
264
+ * own bytes would then be read past a peer that overwrote them — beside
265
+ * another path the same peer wrote afterwards, read new. So no resumption
266
+ * runs while such a report is outstanding against one of these
267
+ * (__nimbusReportedOwnAcknowledgements). A write that is only parked is not
268
+ * in flight: it has not reached the authority, cannot have been applied
269
+ * before any report, and will be applied after every one it has missed.
270
+ *
271
+ * `generation` is the parked cell a whole-file write-back carries
272
+ * (__vfsWriteGenerations): once a newer one is parked over it, that newer
273
+ * cell is what the facet serves, and this acknowledgement no longer says
274
+ * anything about the bytes a resumption would read.
275
+ */
276
+ function __nimbusOwnAcknowledgement(path, work, generation) {
277
+ const key = __nimbusVfsPathKey(path);
278
+ const acked = work();
279
+ const ack = { settled: acked.then(() => undefined, () => undefined), generation };
280
+ let held = __vfsOwnAcks.get(key);
281
+ if (!held) {
282
+ held = new Set();
283
+ __vfsOwnAcks.set(key, held);
284
+ }
285
+ held.add(ack);
286
+ ack.settled.then(() => {
287
+ held.delete(ack);
288
+ if (held.size === 0 && __vfsOwnAcks.get(key) === held) __vfsOwnAcks.delete(key);
289
+ });
290
+ return acked;
291
+ }
292
+
293
+ /**
294
+ * The own acknowledgements in flight for every path that carries a report
295
+ * noted while its write or mutation was out (__nimbusNoteVfsReport) — by the
296
+ * barrier asking or by any before it — as of NOW, settling together; null
297
+ * when there are none. A barrier waits on these before its resumption runs.
298
+ *
299
+ * The reports an answer names are noted before it is admitted, so they are
300
+ * among them. So is one an earlier barrier noted and is still waiting on:
301
+ * that barrier moved the cursor past it, and an answer asked for from there
302
+ * does not name it again, but the own bytes are no fresher for that.
303
+ * Once an acknowledgement lands, its adjudication has decided — a report at
304
+ * or below its revision was this facet's own write coming back and the cell
305
+ * is dated; one above it was a peer writing after, and the cell is evicted
306
+ * and owed a refetch — and the report is retired with it.
307
+ *
308
+ * A write-back whose cell has been superseded is left out: a newer write of
309
+ * the path is parked over it, and no mutation of the path is out beside it.
310
+ * The facet serves that newer cell, which is only parked, so it will be
311
+ * applied above every report made so far. Waiting on the older one would
312
+ * stall a program that writes and yields in a loop on each of its own writes
313
+ * coming back, for nothing it reads.
314
+ */
315
+ function __nimbusReportedOwnAcknowledgements() {
316
+ const pending = [];
317
+ for (const [key, held] of __vfsOwnAcks) {
318
+ const lease = __vfsOwnLeases[key];
319
+ if (__vfsParkedReports[key] === undefined && !(lease && lease.reported !== -1)) continue;
320
+ const parkedOver = lease === undefined && Object.prototype.hasOwnProperty.call(__vfsWrites, key);
321
+ for (const ack of held) {
322
+ if (parkedOver && ack.generation !== undefined && ack.generation !== __vfsWriteGenerations[key]) continue;
323
+ pending.push(ack.settled);
324
+ }
325
+ }
326
+ return pending.length === 0 ? null : Promise.all(pending);
327
+ }
328
+
329
+ /**
330
+ * Note a report no answer could name, on every path with an own
331
+ * acknowledgement in flight. A poison, a barrier with no answer, a repair
332
+ * that vouched for nothing: each moves on without saying what changed, so
333
+ * each of those writes is adjudicated as though a peer wrote after it — a
334
+ * refetch, never a stale byte.
335
+ */
336
+ function __nimbusNoteUnnamedReports() {
337
+ for (const key of __vfsOwnAcks.keys()) __nimbusNoteVfsReport(key, Infinity);
338
+ }
339
+
340
+ async function __nimbusDrainVfsMutations() {
341
+ while (__nimbusPendingVfsMutations.size > 0) {
342
+ await Promise.allSettled([...__nimbusPendingVfsMutations]);
343
+ }
344
+ if (__nimbusHasPendingVfsMutationFailure) {
345
+ const failure = __nimbusPendingVfsMutationFailure;
346
+ __nimbusHasPendingVfsMutationFailure = false;
347
+ __nimbusPendingVfsMutationFailure = undefined;
348
+ throw failure;
349
+ }
350
+ }
351
+
352
+ function __nimbusCapturePendingVfsAppend(path) {
353
+ const key = __nimbusVfsPathKey(path);
354
+ const append = __vfsAppendWrites[key];
355
+ return append && append.generation === __vfsWriteGenerations[key]
356
+ ? append
357
+ : null;
358
+ }
359
+
360
+ function __nimbusConcatVfsBytes(left, right) {
361
+ const bytes = new Uint8Array(left.byteLength + right.byteLength);
362
+ bytes.set(left, 0);
363
+ bytes.set(right, left.byteLength);
364
+ return bytes;
365
+ }
366
+
367
+ function __nimbusRecordVfsAppend(path, delta, fragment, previous) {
368
+ const key = __nimbusVfsPathKey(path);
369
+ const chain = previous ? previous.chain : { pending: [] };
370
+ let operation;
371
+ if (previous &&
372
+ !previous.claimed &&
373
+ !chain.pending.includes(previous.operation)) {
374
+ operation = previous.operation;
375
+ operation.bytes = __nimbusConcatVfsBytes(operation.bytes, delta);
376
+ } else {
377
+ operation = {
378
+ id: String(++__nimbusVfsAppendOperationSequence),
379
+ bytes: delta.slice(),
380
+ };
381
+ }
382
+ __vfsAppendWrites[key] = {
383
+ generation: __vfsWriteGenerations[key],
384
+ fragment,
385
+ chain,
386
+ operation,
387
+ claimed: false,
388
+ };
389
+ }
390
+
391
+ function __nimbusCaptureVfsWrite(path) {
392
+ const key = __nimbusVfsPathKey(path);
393
+ if (!Object.prototype.hasOwnProperty.call(__vfsWrites, key)) return null;
394
+ return {
395
+ key,
396
+ content: __vfsWrites[key],
397
+ generation: __vfsWriteGenerations[key],
398
+ append: __nimbusCapturePendingVfsAppend(key),
399
+ };
400
+ }
401
+
402
+ function __nimbusVfsAppendOperations(snapshot) {
403
+ const operations = snapshot.append.chain.pending.slice();
404
+ if (!operations.includes(snapshot.append.operation)) {
405
+ operations.push(snapshot.append.operation);
406
+ }
407
+ return operations;
408
+ }
409
+
410
+ function __nimbusBeginVfsAppendOperation(snapshot, operation) {
411
+ if (!snapshot.append.chain.pending.includes(operation)) {
412
+ snapshot.append.chain.pending.push(operation);
413
+ }
414
+ }
415
+
416
+ function __nimbusCommitVfsAppendOperation(snapshot, operation) {
417
+ const index = snapshot.append.chain.pending.indexOf(operation);
418
+ if (index !== -1) snapshot.append.chain.pending.splice(index, 1);
419
+ }
420
+
421
+ function __nimbusUnsupportedVfsAppend(path) {
422
+ const error = new Error(
423
+ "ENOSYS: preserving a nonresident append requires fsAppend and fsAppendAck: " + path,
424
+ );
425
+ error.code = "ENOSYS";
426
+ return error;
427
+ }
428
+
429
+ /**
430
+ * The authority refused a parked write outright (a syscall verdict:
431
+ * EACCES, EPERM, EISDIR...). The bytes are not the file's and never will
432
+ * be, so the process stops serving them: the parked cell of that generation
433
+ * and any resident copy go, and the shims forget what they recorded of the
434
+ * path, so the next read asks the authority. When no caller will see the
435
+ * rejection (`unseen`: a sync write carried across later), the refusal is
436
+ * retained and reported at exit; an async writer gets it as its verdict.
437
+ */
438
+ function __nimbusRefuseParkedWrite(snapshot, error, unseen) {
439
+ if (__vfsWriteGenerations[snapshot.key] === snapshot.generation) {
440
+ delete __vfsWrites[snapshot.key];
441
+ if (typeof __vfsBundle !== "undefined" && __vfsBundle) delete __vfsBundle[snapshot.key];
442
+ const refused = globalThis.__nimbusVfsWriteRefused;
443
+ if (typeof refused === "function") refused(snapshot.key);
444
+ }
445
+ if (unseen && error && typeof error === "object") {
446
+ try { error.nimbusRefusedWriteBack = true; } catch {}
447
+ }
448
+ return error;
449
+ }
450
+
451
+ function __nimbusRunVfsWriteMutation(snapshot, mutation, retainFailure, unseen) {
452
+ return __nimbusQueueVfsMutation(snapshot.key, () => __nimbusOwnAcknowledgement(snapshot.key, async () => {
453
+ let value;
454
+ try {
455
+ value = await mutation(snapshot.content, snapshot);
456
+ } catch (error) {
457
+ // A verdict is the authority's answer; anything else (a lost RPC) may
458
+ // have landed, and the parked bytes stay until that is known.
459
+ if (!__nimbusIsDurabilityFailure(error)) throw __nimbusRefuseParkedWrite(snapshot, error, unseen);
460
+ throw error;
461
+ }
462
+ if (__vfsWriteGenerations[snapshot.key] === snapshot.generation) {
463
+ // What the barriers reported for this path while the write was in
464
+ // flight. Read before the parked cell is retired below, which drops it.
465
+ const reported = __vfsParkedReports[snapshot.key];
466
+ if (snapshot.append &&
467
+ value === __nimbusVfsAppendRangeResult &&
468
+ typeof __vfsBundle !== "undefined" &&
469
+ __vfsBundle) {
470
+ delete __vfsBundle[snapshot.key];
471
+ } else if (typeof value === "number" &&
472
+ typeof __vfsBundle !== "undefined" &&
473
+ __vfsBundle &&
474
+ !(snapshot.key in __vfsBundle) &&
475
+ !(reported > value)) {
476
+ // The barrier that evicted the resident copy was answered AHEAD of
477
+ // this very write (see __nimbusBeginOwnMutation for why that
478
+ // ordering is ordinary): it reported the path at the revision this
479
+ // write produced, which is the revision about to be stamped, and
480
+ // the bytes in hand are the authority's content AT that revision.
481
+ // So reinstall them rather than let the identity check below
482
+ // decline and leave the facet holding nothing — a sync read of a
483
+ // file the program just wrote whole would fail EAGAIN.
484
+ //
485
+ // A peer's earlier write was overwritten by this whole-file write.
486
+ // A peer's LATER one is reported ABOVE this revision, and the guard
487
+ // above is that test: reported later, the bytes in hand are not
488
+ // what the authority serves and the eviction stands — a refetch,
489
+ // never a stale byte. The parked cell is dropped right after, so a
490
+ // reinstalled copy is the one the sync view serves.
491
+ __vfsBundle[snapshot.key] = snapshot.content;
492
+ }
493
+ delete __vfsWrites[snapshot.key];
494
+ __nimbusStampFlushedCell(snapshot, value, reported);
495
+ }
496
+ return value;
497
+ }, snapshot.generation), retainFailure);
498
+ }
499
+
500
+ /**
501
+ * Whether this facet's resident set is the SQLite store
502
+ * (vfs/facet-resident-store.ts), which the resident node body splices ahead
503
+ * of this ledger. Its rows carry their own revision, so a stamp is written
504
+ * into the row, and __vfsBundleRevisions — which describes heap cells — says
505
+ * nothing about them.
506
+ */
507
+ function __nimbusResidentRows() {
508
+ return typeof __residentStamp === "function"
509
+ && typeof __residentReady !== "undefined"
510
+ && __residentReady === true;
511
+ }
512
+
513
+ /**
514
+ * Record the authority revision a just-flushed cell is known-good at.
515
+ *
516
+ * The barrier reports back every path mutated since the facet's cursor,
517
+ * which includes the facet's OWN writes — the invalidation log has no way
518
+ * to know who caused an entry. Without a stamp the facet drops the cells it
519
+ * authored the instant it flushes them, and a resumption then refetches
520
+ * bytes it is already holding: a self-inflicted cold cache on exactly the
521
+ * files a scaffolder or a build just wrote.
522
+ *
523
+ * A "skip paths I wrote" rule would be unsound — a peer may write the same
524
+ * path after us, and that invalidation is real. The revision separates
525
+ * them: a report AT our revision is our own write coming back, a report
526
+ * ABOVE it is somebody else's and still evicts.
527
+ *
528
+ * Guarded on cell identity rather than on the flush alone. A read that
529
+ * raced the flush may have refilled the cell from an older revision, and
530
+ * stamping that with our newer one would pin a stale byte — the one
531
+ * outcome this whole protocol exists to prevent.
532
+ *
533
+ * Only the written path is stamped, never its parent — deliberately, and it
534
+ * is why a batch of writes still costs ONE invalidation rather than none.
535
+ * Every mutation reports its parent directory too, so stamping the parent
536
+ * here looks like the obvious way to reach zero. It is not: the same
537
+ * revision on the directory would also vouch for a peer's earlier change to
538
+ * the DIRECTORY ITSELF — a chmod at a revision this facet never acquired —
539
+ * and that stale mode would then survive the barrier. The write knows what
540
+ * it did to the file and nothing about the directory. Do not "finish" this
541
+ * by stamping the parent.
542
+ */
543
+ function __nimbusStampFlushedCell(snapshot, revision, reported) {
544
+ if (typeof revision !== "number") return;
545
+ if (typeof __vfsBundle === "undefined" || !__vfsBundle) return;
546
+ if (__nimbusResidentRows()) {
547
+ // The row held this write's bytes as the store's own-write revision,
548
+ // which no barrier evicts, so every barrier inside the window KEPT it and
549
+ // consumed its report. Those reports are judged here, against the
550
+ // revision this write produced: at or below it is this write coming
551
+ // back, or a write it overwrote; above it, a peer wrote after, and the
552
+ // eviction those barriers could not perform is owed now. The store dates
553
+ // only a row still holding own bytes, which is the identity guard below
554
+ // in the form rows allow: a fill never replaces own bytes, and a later
555
+ // write of the path fails the generation test before this is reached.
556
+ if (reported > revision) {
557
+ __nimbusEvictLeasedCell(snapshot.key);
558
+ return;
559
+ }
560
+ __residentStamp(snapshot.key, revision);
561
+ return;
562
+ }
563
+ if (__vfsBundle[snapshot.key] !== snapshot.content) return;
564
+ __vfsBundleRevisions[snapshot.key] = revision;
565
+ }
566
+
567
+ /**
568
+ * Take an own-mutation lease on a held cell.
569
+ *
570
+ * One of the facet's OWN partial mutations (ranged write, truncate, utimes,
571
+ * chmod, chown) bumps the path's revision exactly as a flush does, and the
572
+ * ACQUIRE barrier that reports it back cannot tell who caused it. Stamping
573
+ * once the RPC answers is not enough, because the two are CONCURRENT: the
574
+ * supervisor answers `fsAcquire` from memory at once, while a write's
575
+ * response waits on the Durable Object's output gate for durability. So a
576
+ * barrier issued AFTER this facet's own write can be ANSWERED BEFORE that
577
+ * write's response arrives. Its delta lists the path at the write's new
578
+ * revision, the facet's stamp is still the old one, the cell the facet is
579
+ * about to overlay is evicted out from under it, and the next
580
+ * `readFileSync` fails EAGAIN on bytes the program wrote itself.
581
+ * Intermittent, and it is what create-astro shows on staging — the README
582
+ * its extract wrote, read synchronously right after.
583
+ *
584
+ * So the cell is HELD for the whole window instead, and the receipt decides
585
+ * at the end (`__nimbusEndOwnMutation`). `Infinity` is what the barrier's
586
+ * `stamp >= entry.rev` reads as "mine" for every revision it could report
587
+ * while the RPC is in flight.
588
+ *
589
+ * An UNSTAMPED cell is not leased: it keeps today's evict-and-refetch, so a
590
+ * mutation path that never stamps still costs a refetch and never a stale
591
+ * byte. `false` says no lease was taken and the end is a no-op.
592
+ *
593
+ * In the resident store the row is the stamp, so the store holds the row as
594
+ * own bytes for the window (`__residentLease`, which the barrier keeps the
595
+ * way it keeps an Infinity stamp) and hands back the revision it was dated
596
+ * at; the lease record carries that revision exactly as it does here.
597
+ */
598
+ function __nimbusBeginOwnMutation(key) {
599
+ if (__nimbusResidentRows()) {
600
+ const open = __vfsOwnLeases[key];
601
+ if (open) { open.pending++; return true; }
602
+ const dated = __residentLease(key);
603
+ if (dated === undefined) return false;
604
+ __vfsOwnLeases[key] = { stamp: dated, pending: 1, peer: false, reported: -1 };
605
+ return true;
606
+ }
607
+ const stamp = __vfsBundleRevisions[key];
608
+ if (stamp === undefined) return false;
609
+ const lease = __vfsOwnLeases[key]
610
+ || (__vfsOwnLeases[key] = { stamp, pending: 0, peer: false, reported: -1 });
611
+ lease.pending++;
612
+ __vfsBundleRevisions[key] = Infinity;
613
+ return true;
614
+ }
615
+
616
+ /**
617
+ * Remember a revision the ACQUIRE barrier has just reported for a path one
618
+ * of this facet's own writes is in flight for. Called for every reported
619
+ * path; a no-op for the ones nothing of ours is touching.
620
+ *
621
+ * A barrier answered inside that window is the whole problem this file is
622
+ * solving, and its report is CONSUMED — the cursor advances past it and
623
+ * nothing will ever raise that revision again. Whether it was this facet's
624
+ * own write coming back or a peer's cannot be decided then, so the number
625
+ * is kept until the write's own revision arrives and can decide it:
626
+ *
627
+ * - a leased cell had the report SUPPRESSED (the stamp reads Infinity for
628
+ * every revision), so `__nimbusEndOwnMutation` applies the barrier's own
629
+ * test against the stamp the receipt settles on.
630
+ * - a PARKED cell is unstamped, so the barrier evicted it legitimately;
631
+ * `__nimbusRunVfsWriteMutation` uses this to tell a report of its own
632
+ * write (put the bytes back) from a peer's later one (leave them gone).
633
+ *
634
+ * Neither can be answered by the receipt alone: it is read in the
635
+ * mutation's own turn, so a peer writing after the mutation landed and
636
+ * before its response arrived is invisible to it.
637
+ */
638
+ function __nimbusNoteVfsReport(key, revision) {
639
+ const lease = __vfsOwnLeases[key];
640
+ if (lease && revision > lease.reported) lease.reported = revision;
641
+ if (Object.prototype.hasOwnProperty.call(__vfsWrites, key) &&
642
+ !(__vfsParkedReports[key] >= revision)) {
643
+ __vfsParkedReports[key] = revision;
644
+ }
645
+ }
646
+
647
+ /**
648
+ * `__nimbusNoteVfsReport` for a report that covers a subtree: a
649
+ * subtree-scoped or structural delta entry, which stands for changes at or
650
+ * under `prefix` that it does not name. Every path there that one of this
651
+ * facet's own writes or mutations is in flight for gets the report.
652
+ */
653
+ function __nimbusNoteVfsReportUnder(prefix, revision) {
654
+ const under = prefix + "/";
655
+ const keys = new Set([...Object.keys(__vfsOwnLeases), ...Object.keys(__vfsWrites)]);
656
+ for (const key of keys) {
657
+ if (key === prefix || key.startsWith(under)) __nimbusNoteVfsReport(key, revision);
658
+ }
659
+ }
660
+
661
+ /**
662
+ * End one own-mutation lease, and settle the stamp when it was the last.
663
+ *
664
+ * The receipt carries the path's revision on either side of the mutation,
665
+ * both read in the RPC's own synchronous turn:
666
+ *
667
+ * - `before` at or below the stamp the lease began with — nobody else
668
+ * touched the path in the window, so the cell with the local effect
669
+ * applied IS what the authority serves at `after`, and the stamp
670
+ * advances there.
671
+ * - `before` past it — a peer wrote inside the window, and the barrier
672
+ * that would have evicted was suppressed by this lease. The end owes
673
+ * that eviction, so it performs it.
674
+ * - no receipt at all (the RPC threw) — the outcome is unknown, which is
675
+ * handled exactly as a peer's write is.
676
+ *
677
+ * Then every report the lease suppressed is adjudicated against the stamp
678
+ * it settled on: a report ABOVE it is a mutation this receipt does not
679
+ * account for, so the eviction the barrier did not perform is performed
680
+ * here (see `__nimbusNoteVfsReport`).
681
+ *
682
+ * A cell that stopped being held during the window (a poison drop, a
683
+ * parked sync write retiring the stamp) has nothing left to vouch for, so
684
+ * no stamp is restored for it. In the resident store a write parked over
685
+ * the row inside the window owns it from then on, and its flush dates it.
686
+ */
687
+ function __nimbusEndOwnMutation(key, held, receipt) {
688
+ if (!held) return;
689
+ const lease = __vfsOwnLeases[key];
690
+ if (!lease) return;
691
+ lease.pending--;
692
+ if (receipt && typeof receipt.before === "number" && typeof receipt.after === "number") {
693
+ if (lease.stamp >= receipt.before) lease.stamp = receipt.after;
694
+ else lease.peer = true;
695
+ } else {
696
+ lease.peer = true;
697
+ }
698
+ if (lease.pending > 0) return;
699
+ delete __vfsOwnLeases[key];
700
+ const evict = lease.peer || lease.reported > lease.stamp;
701
+ if (__nimbusResidentRows()) {
702
+ if (evict) __nimbusEvictLeasedCell(key);
703
+ else if (!Object.prototype.hasOwnProperty.call(__vfsWrites, key)) __residentStamp(key, lease.stamp);
704
+ return;
705
+ }
706
+ const resident = typeof __vfsBundle !== "undefined" && __vfsBundle && key in __vfsBundle;
707
+ if (evict || !resident) {
708
+ delete __vfsBundleRevisions[key];
709
+ if (evict) __nimbusEvictLeasedCell(key);
710
+ return;
711
+ }
712
+ __vfsBundleRevisions[key] = lease.stamp;
713
+ }
714
+
715
+ /**
716
+ * Drop a cell the way the shims' own invalidation does.
717
+ *
718
+ * Content view, stat view and the invalidation count move together in the
719
+ * shims' `_evictResident`, and this source is spliced AHEAD of that
720
+ * closure, so the lease asks through the hook it publishes rather than
721
+ * keeping a second eviction path in step with it. A ledger embedded without
722
+ * the shims has no stat view to keep coherent, and the content view is then
723
+ * all there is to drop.
724
+ */
725
+ function __nimbusEvictLeasedCell(key) {
726
+ const evict = globalThis.__nimbusEvictResidentCell;
727
+ if (typeof evict === "function") { evict(key); return; }
728
+ if (typeof __vfsBundle !== "undefined" && __vfsBundle) delete __vfsBundle[key];
729
+ }
730
+
731
+ /** `unseen`: no caller awaits this flush, so a refusal must be retained to be heard. */
732
+ function __nimbusFlushVfsWrite(path, mutation, retainFailure = true, unseen = false) {
733
+ const snapshot = __nimbusCaptureVfsWrite(path);
734
+ if (!snapshot) return Promise.resolve(undefined);
735
+ const existing = __vfsWriteClaims.get(snapshot.key);
736
+ if (existing && existing.generation === snapshot.generation) {
737
+ return existing.promise;
738
+ }
739
+ if (snapshot.append) snapshot.append.claimed = true;
740
+ const result = __nimbusRunVfsWriteMutation(snapshot, mutation, retainFailure, unseen);
741
+ const claim = { generation: snapshot.generation, promise: result };
742
+ __vfsWriteClaims.set(snapshot.key, claim);
743
+ const release = () => {
744
+ if (__vfsWriteClaims.get(snapshot.key) === claim) {
745
+ __vfsWriteClaims.delete(snapshot.key);
746
+ }
747
+ };
748
+ result.then(() => {
749
+ release();
750
+ // The authority accepted the bytes: the shims learn what it made of the
751
+ // path (owner, mode) for their sync view.
752
+ const landed = globalThis.__nimbusVfsWriteLanded;
753
+ if (typeof landed === "function") landed(snapshot.key);
754
+ }, () => {
755
+ release();
756
+ if (snapshot.append &&
757
+ __vfsAppendWrites[snapshot.key] === snapshot.append) {
758
+ snapshot.append.claimed = false;
759
+ }
760
+ });
761
+ return result;
762
+ }
763
+
764
+ async function __nimbusPersistVfsWrite(supervisor, path, content, snapshot) {
765
+ if (snapshot.append) {
766
+ if (typeof supervisor.fsAppend !== "function" ||
767
+ typeof supervisor.fsAppendAck !== "function") {
768
+ throw __nimbusUnsupportedVfsAppend(path);
769
+ }
770
+ for (const operation of __nimbusVfsAppendOperations(snapshot)) {
771
+ __nimbusBeginVfsAppendOperation(snapshot, operation);
772
+ await __nimbusVfsRpc(() => supervisor.fsAppend(
773
+ path,
774
+ __nimbusVfsModuleIncarnation(),
775
+ operation.id,
776
+ operation.bytes,
777
+ ));
778
+ __nimbusCommitVfsAppendOperation(snapshot, operation);
779
+ try {
780
+ await __nimbusVfsRpc(() => supervisor.fsAppendAck(__nimbusVfsModuleIncarnation(), operation.id));
781
+ } catch {
782
+ // The client has already relinquished retry ownership after the
783
+ // append success. A lost acknowledgement may retain a receipt, but
784
+ // must never turn a committed append into a failed/retried write.
785
+ }
786
+ }
787
+ return __nimbusVfsAppendRangeResult;
788
+ }
789
+ // The revision this write produced. It is what lets the ACQUIRE barrier
790
+ // tell this facet's own mutation apart from a peer's.
791
+ return __nimbusVfsRpc(() => supervisor.writeFile(path, content));
792
+ }
793
+
794
+ /**
795
+ * Write back the cells parked at THIS instant, and only those.
796
+ *
797
+ * Bounded on purpose, and deliberately not routed through
798
+ * `__nimbusDrainVfsWrites`, whose `while (pending > 0)` waits for the mutation
799
+ * queue to be EMPTY. That wait is correct at process exit, where no new writes
800
+ * are coming. Anywhere else it is a livelock: a facet unpacking a tarball adds
801
+ * mutations faster than the loop retires them, so the loop never returns.
802
+ * Sited ahead of egress — where it was — that stopped the request from ever
803
+ * leaving the facet, and `npx sv create` ran, printed its intro, and then
804
+ * never reported an exit at all. A barrier may delay a request; it may not
805
+ * wait on a condition a busy process never reaches.
806
+ *
807
+ * Failures are retained rather than thrown. The two callers — the debounce
808
+ * below, and the RELEASE barrier ahead of egress — have no frame that could
809
+ * act on one: rejecting the fetch that happened to trigger the flush would
810
+ * blame the wrong operation. The exit drain reports what is retained, so a
811
+ * lost write is loud exactly once and never silent.
812
+ */
813
+ async function __nimbusFlushVfsWriteBack(supervisor) {
814
+ if (!supervisor) return;
815
+ const paths = Object.keys(__vfsWrites);
816
+ if (paths.length === 0) return;
817
+ await Promise.allSettled(paths.map((path) => __nimbusFlushVfsWrite(
818
+ path,
819
+ (content, snapshot) => __nimbusPersistVfsWrite(supervisor, path, content, snapshot),
820
+ true,
821
+ true,
822
+ )));
823
+ }
824
+
825
+ /**
826
+ * A synchronous write can only park bytes in `__vfsWrites`: a sync syscall
827
+ * has no channel to the authority. Something else therefore has to carry
828
+ * them across, and the only thing that did was the drain at process exit —
829
+ * so a resident server that writes synchronously never wrote back at all,
830
+ * and a peer reading the same path got the pre-write bytes for the whole
831
+ * life of the process. Measured, not theorised: `writeFileSync` then 50 ms
832
+ * left the authority at null with zero write RPCs issued.
833
+ *
834
+ * Flushing on every write is not the repair — 500 sync writes would become
835
+ * 500 round trips, and an npm install writes thousands. Debounce instead:
836
+ * parking a cell schedules one write-back, and every write that lands before
837
+ * it fires joins that same batch. Steady state costs no more round trips
838
+ * than the exit drain already paid; what changes is when they happen.
839
+ *
840
+ * The timer is the raw platform one, captured before the shims wrap
841
+ * `setTimeout` with the VFS resumption barrier: a write-back is the shim's
842
+ * own infrastructure, not a user resumption, and must not pay an ACQUIRE to
843
+ * deliver an ACQUIRE.
844
+ */
845
+ const __NIMBUS_VFS_WRITE_BACK_DELAY_MS = 10;
846
+ const __nimbusRawTimer = globalThis.setTimeout;
847
+ let __nimbusVfsWriteBackTimer = null;
848
+ function __nimbusScheduleVfsWriteBack() {
849
+ if (__nimbusVfsWriteBackTimer !== null) return;
850
+ if (typeof __nimbusRawTimer !== 'function') return;
851
+ __nimbusVfsWriteBackTimer = __nimbusRawTimer(() => {
852
+ __nimbusVfsWriteBackTimer = null;
853
+ const supervisor = typeof __supervisor !== 'undefined' ? __supervisor : null;
854
+ if (!supervisor) return;
855
+ // Not registered in __nimbusPendingVfsMutations: each write it starts
856
+ // registers itself there through __nimbusQueueVfsMutation, so the exit
857
+ // drain already awaits the work. Registering the orchestration too would
858
+ // add an entry nothing ever removes, and that set is drained by a
859
+ // while-loop on its size.
860
+ void __nimbusFlushVfsWriteBack(supervisor);
861
+ }, __NIMBUS_VFS_WRITE_BACK_DELAY_MS);
862
+ }
863
+
864
+ async function __nimbusDrainVfsWrites(supervisor) {
865
+ const paths = Object.keys(__vfsWrites);
866
+ const outcomes = await Promise.allSettled([
867
+ ...paths.map(async (path) => {
868
+ const persist = () => __nimbusFlushVfsWrite(
869
+ path,
870
+ (content, snapshot) =>
871
+ __nimbusPersistVfsWrite(supervisor, path, content, snapshot),
872
+ false,
873
+ true,
874
+ );
875
+ try {
876
+ await persist();
877
+ } catch (error) {
878
+ // The authority may have committed before the RPC response was lost.
879
+ // One retry is safe: full writes are idempotent, while appends retain
880
+ // the same module/operation identity and are deduplicated by authority.
881
+ if (error && typeof error.code === "string") throw error;
882
+ await persist();
883
+ }
884
+ }),
885
+ __nimbusDrainVfsMutations(),
886
+ ]);
887
+ const failure = outcomes.find((outcome) => outcome.status === "rejected");
888
+ if (failure) throw failure.reason;
889
+ }