@nimbus-sh/worker 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (286) hide show
  1. package/NOTICE.md +5 -0
  2. package/README.md +10 -0
  3. package/dist/_shared/session-router.d.ts +7 -0
  4. package/dist/_shared/session-router.d.ts.map +1 -1
  5. package/dist/_shared/session-router.js +7 -0
  6. package/dist/bindings/body.d.ts +6 -0
  7. package/dist/bindings/body.d.ts.map +1 -0
  8. package/dist/bindings/body.js +37 -0
  9. package/dist/bindings/kv.d.ts +0 -2
  10. package/dist/bindings/kv.d.ts.map +1 -1
  11. package/dist/bindings/kv.js +3 -42
  12. package/dist/bindings/r2.d.ts +0 -2
  13. package/dist/bindings/r2.d.ts.map +1 -1
  14. package/dist/bindings/r2.js +4 -44
  15. package/dist/esbuild-cli-artifact.generated.d.ts +18 -0
  16. package/dist/esbuild-cli-artifact.generated.d.ts.map +1 -0
  17. package/dist/esbuild-cli-artifact.generated.js +17 -0
  18. package/dist/esbuild-wasm-bundle.generated.d.ts +24 -24
  19. package/dist/esbuild-wasm-bundle.generated.d.ts.map +1 -1
  20. package/dist/esbuild-wasm-bundle.generated.js +24 -24
  21. package/dist/facets/cirrus-real.d.ts.map +1 -1
  22. package/dist/facets/cirrus-real.js +11 -6
  23. package/dist/facets/compose.d.ts +11 -7
  24. package/dist/facets/compose.d.ts.map +1 -1
  25. package/dist/facets/compose.js +8 -31
  26. package/dist/facets/data-plan.d.ts +79 -0
  27. package/dist/facets/data-plan.d.ts.map +1 -0
  28. package/dist/facets/data-plan.js +477 -0
  29. package/dist/facets/esbuild-bundle-pool.d.ts +1 -1
  30. package/dist/facets/esbuild-bundle-pool.js +3 -3
  31. package/dist/facets/esbuild-transform.d.ts +38 -8
  32. package/dist/facets/esbuild-transform.d.ts.map +1 -1
  33. package/dist/facets/esbuild-transform.js +219 -19
  34. package/dist/facets/exec-telemetry.d.ts +7 -3
  35. package/dist/facets/exec-telemetry.d.ts.map +1 -1
  36. package/dist/facets/manager.d.ts +198 -69
  37. package/dist/facets/manager.d.ts.map +1 -1
  38. package/dist/facets/manager.js +1276 -652
  39. package/dist/facets/opencode-staging.d.ts +0 -2
  40. package/dist/facets/opencode-staging.d.ts.map +1 -1
  41. package/dist/facets/opencode-staging.js +5 -11
  42. package/dist/facets/process.d.ts +4 -0
  43. package/dist/facets/process.d.ts.map +1 -1
  44. package/dist/facets/process.js +3 -0
  45. package/dist/facets/read-profile.d.ts +149 -0
  46. package/dist/facets/read-profile.d.ts.map +1 -0
  47. package/dist/facets/read-profile.js +483 -0
  48. package/dist/facets/real-vite-hmr.d.ts +2 -0
  49. package/dist/facets/real-vite-hmr.d.ts.map +1 -1
  50. package/dist/facets/real-vite-hmr.js +3 -2
  51. package/dist/facets/resident-identity.d.ts +0 -1
  52. package/dist/facets/resident-identity.d.ts.map +1 -1
  53. package/dist/facets/resident-identity.js +0 -3
  54. package/dist/facets/wasm-swap-registry.d.ts +34 -25
  55. package/dist/facets/wasm-swap-registry.d.ts.map +1 -1
  56. package/dist/facets/wasm-swap-registry.js +62 -56
  57. package/dist/facets/ws-terminal.d.ts +4 -4
  58. package/dist/facets/ws-terminal.d.ts.map +1 -1
  59. package/dist/facets/ws-terminal.js +4 -5
  60. package/dist/git/commands.d.ts +33 -10
  61. package/dist/git/commands.d.ts.map +1 -1
  62. package/dist/git/commands.js +1891 -197
  63. package/dist/git/network-facet.d.ts +12 -2
  64. package/dist/git/network-facet.d.ts.map +1 -1
  65. package/dist/git/network-facet.js +69 -18
  66. package/dist/git/unified-diff.d.ts +74 -0
  67. package/dist/git/unified-diff.d.ts.map +1 -0
  68. package/dist/git/unified-diff.js +676 -0
  69. package/dist/git-bundle.generated.d.ts +7 -11
  70. package/dist/git-bundle.generated.d.ts.map +1 -1
  71. package/dist/git-bundle.generated.js +7 -11
  72. package/dist/hosted/commands.d.ts.map +1 -1
  73. package/dist/hosted/commands.js +64 -245
  74. package/dist/hosted/runtime.d.ts +22 -15
  75. package/dist/hosted/runtime.d.ts.map +1 -1
  76. package/dist/hosted/runtime.js +72 -14
  77. package/dist/hosted/services.d.ts +8 -1
  78. package/dist/hosted/services.d.ts.map +1 -1
  79. package/dist/hosted/services.js +12 -13
  80. package/dist/hosted/session.d.ts +165 -0
  81. package/dist/hosted/session.d.ts.map +1 -0
  82. package/dist/hosted/session.js +132 -0
  83. package/dist/loaders/generated-workers.d.ts +6 -3
  84. package/dist/loaders/generated-workers.d.ts.map +1 -1
  85. package/dist/loaders/generated-workers.js +8 -5
  86. package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -1
  87. package/dist/loaders/npm-resolve-preamble.js +0 -27
  88. package/dist/loaders/pre-bundle-preamble.d.ts +6 -3
  89. package/dist/loaders/pre-bundle-preamble.d.ts.map +1 -1
  90. package/dist/loaders/pre-bundle-preamble.js +13 -8
  91. package/dist/node-shims-artifact.generated.d.ts +13 -4
  92. package/dist/node-shims-artifact.generated.d.ts.map +1 -1
  93. package/dist/node-shims-artifact.generated.js +16 -7
  94. package/dist/npm/bin-links.d.ts +3 -1
  95. package/dist/npm/bin-links.d.ts.map +1 -1
  96. package/dist/npm/bin-links.js +32 -8
  97. package/dist/npm/cache.d.ts +11 -1
  98. package/dist/npm/cache.d.ts.map +1 -1
  99. package/dist/npm/cache.js +21 -7
  100. package/dist/npm/install-batch-facet.d.ts +4 -2
  101. package/dist/npm/install-batch-facet.d.ts.map +1 -1
  102. package/dist/npm/install-batch-facet.js +32 -18
  103. package/dist/npm/installer.d.ts +23 -3
  104. package/dist/npm/installer.d.ts.map +1 -1
  105. package/dist/npm/installer.js +330 -131
  106. package/dist/npm/npx-install.d.ts +3 -1
  107. package/dist/npm/npx-install.d.ts.map +1 -1
  108. package/dist/npm/npx-install.js +4 -1
  109. package/dist/npm/package-lock.d.ts +32 -0
  110. package/dist/npm/package-lock.d.ts.map +1 -0
  111. package/dist/npm/package-lock.js +65 -0
  112. package/dist/npm/placement.d.ts +16 -0
  113. package/dist/npm/placement.d.ts.map +1 -0
  114. package/dist/npm/placement.js +33 -0
  115. package/dist/npm/pre-bundle-facet.d.ts +1 -30
  116. package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
  117. package/dist/npm/pre-bundle-facet.js +3 -5
  118. package/dist/npm/r2-cache.d.ts +12 -6
  119. package/dist/npm/r2-cache.d.ts.map +1 -1
  120. package/dist/npm/r2-cache.js +34 -16
  121. package/dist/npm/resolve-one-facet.d.ts +3 -0
  122. package/dist/npm/resolve-one-facet.d.ts.map +1 -1
  123. package/dist/npm/resolve-one-facet.js +15 -3
  124. package/dist/npm/resolver.d.ts +11 -13
  125. package/dist/npm/resolver.d.ts.map +1 -1
  126. package/dist/npm/resolver.js +12 -25
  127. package/dist/opencode-artifact.generated.js +2 -2
  128. package/dist/replica/routing.d.ts +2 -38
  129. package/dist/replica/routing.d.ts.map +1 -1
  130. package/dist/replica/routing.js +3 -55
  131. package/dist/router/index.d.ts.map +1 -1
  132. package/dist/router/index.js +15 -2
  133. package/dist/router/public-directory.d.ts +18 -0
  134. package/dist/router/public-directory.d.ts.map +1 -1
  135. package/dist/router/public-directory.js +1 -1
  136. package/dist/router/remote-api.d.ts +3 -1
  137. package/dist/router/remote-api.d.ts.map +1 -1
  138. package/dist/router/remote-api.js +42 -79
  139. package/dist/runtime/bash-repl.js +2 -2
  140. package/dist/runtime/bun-repl.d.ts.map +1 -1
  141. package/dist/runtime/bun-repl.js +2 -13
  142. package/dist/runtime/cpython-resident.d.ts.map +1 -1
  143. package/dist/runtime/cpython-resident.js +2 -10
  144. package/dist/runtime/esbuild-wasm-bytes.d.ts +37 -18
  145. package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -1
  146. package/dist/runtime/esbuild-wasm-bytes.js +66 -92
  147. package/dist/runtime/facet-loader-host.d.ts.map +1 -1
  148. package/dist/runtime/facet-loader-host.js +0 -7
  149. package/dist/runtime/git-bundle-artifact.d.ts +17 -0
  150. package/dist/runtime/git-bundle-artifact.d.ts.map +1 -0
  151. package/dist/runtime/git-bundle-artifact.js +25 -0
  152. package/dist/runtime/node-repl.d.ts.map +1 -1
  153. package/dist/runtime/node-repl.js +2 -13
  154. package/dist/runtime/node-shims-artifact.d.ts +22 -23
  155. package/dist/runtime/node-shims-artifact.d.ts.map +1 -1
  156. package/dist/runtime/node-shims-artifact.js +48 -78
  157. package/dist/runtime/node-shims.d.ts.map +1 -1
  158. package/dist/runtime/node-shims.js +2358 -996
  159. package/dist/runtime/opencode-artifact.d.ts.map +1 -1
  160. package/dist/runtime/opencode-artifact.js +17 -53
  161. package/dist/runtime/opencode-facet-runner.d.ts +3 -6
  162. package/dist/runtime/opencode-facet-runner.d.ts.map +1 -1
  163. package/dist/runtime/opencode-facet-runner.js +45 -10
  164. package/dist/runtime/opentui-wasm-bytes.d.ts.map +1 -1
  165. package/dist/runtime/opentui-wasm-bytes.js +16 -49
  166. package/dist/runtime/package-manager.d.ts +0 -2
  167. package/dist/runtime/package-manager.d.ts.map +1 -1
  168. package/dist/runtime/package-manager.js +0 -3
  169. package/dist/runtime/python-repl.d.ts.map +1 -1
  170. package/dist/runtime/python-repl.js +4 -35
  171. package/dist/runtime/repl-session.d.ts +10 -0
  172. package/dist/runtime/repl-session.d.ts.map +1 -1
  173. package/dist/runtime/repl-session.js +7 -0
  174. package/dist/runtime/ruby-repl.d.ts.map +1 -1
  175. package/dist/runtime/ruby-repl.js +2 -2
  176. package/dist/runtime/ruby-resident.d.ts.map +1 -1
  177. package/dist/runtime/ruby-resident.js +41 -51
  178. package/dist/runtime/runtime-catalog.d.ts +10 -3
  179. package/dist/runtime/runtime-catalog.d.ts.map +1 -1
  180. package/dist/runtime/runtime-catalog.js +125 -15
  181. package/dist/runtime/sqlite-wasm-bytes.d.ts +4 -4
  182. package/dist/runtime/sqlite-wasm-bytes.d.ts.map +1 -1
  183. package/dist/runtime/sqlite-wasm-bytes.js +21 -57
  184. package/dist/runtime/staged-source.d.ts +67 -0
  185. package/dist/runtime/staged-source.d.ts.map +1 -0
  186. package/dist/runtime/staged-source.js +113 -0
  187. package/dist/runtime/wasi/preamble.d.ts +0 -22
  188. package/dist/runtime/wasi/preamble.d.ts.map +1 -1
  189. package/dist/runtime/wasi/preamble.js +98 -1425
  190. package/dist/runtime-catalog.generated.js +1 -1
  191. package/dist/session/agent.d.ts +14 -2
  192. package/dist/session/agent.d.ts.map +1 -1
  193. package/dist/session/agent.js +57 -19
  194. package/dist/session/ai.d.ts +0 -11
  195. package/dist/session/ai.d.ts.map +1 -1
  196. package/dist/session/ai.js +0 -4
  197. package/dist/session/fs-watch.d.ts +0 -11
  198. package/dist/session/fs-watch.d.ts.map +1 -1
  199. package/dist/session/fs-watch.js +0 -21
  200. package/dist/session/hibernation.d.ts +68 -2
  201. package/dist/session/hibernation.d.ts.map +1 -1
  202. package/dist/session/hibernation.js +95 -1
  203. package/dist/session/init-phases.d.ts +1 -2
  204. package/dist/session/init-phases.d.ts.map +1 -1
  205. package/dist/session/init-phases.js +1 -2
  206. package/dist/session/init.d.ts.map +1 -1
  207. package/dist/session/init.js +7 -20
  208. package/dist/session/keys.d.ts +0 -3
  209. package/dist/session/keys.d.ts.map +1 -1
  210. package/dist/session/keys.js +0 -3
  211. package/dist/session/legacy-reset.d.ts +11 -0
  212. package/dist/session/legacy-reset.d.ts.map +1 -0
  213. package/dist/session/legacy-reset.js +16 -0
  214. package/dist/session/nimbus-session.d.ts +50 -16
  215. package/dist/session/nimbus-session.d.ts.map +1 -1
  216. package/dist/session/nimbus-session.js +77 -71
  217. package/dist/session/npm-install-port.d.ts.map +1 -1
  218. package/dist/session/npm-install-port.js +2 -0
  219. package/dist/session/programmatic.d.ts +13 -18
  220. package/dist/session/programmatic.d.ts.map +1 -1
  221. package/dist/session/programmatic.js +53 -39
  222. package/dist/session/replica-routes.d.ts +2 -3
  223. package/dist/session/replica-routes.d.ts.map +1 -1
  224. package/dist/session/replica-routes.js +3 -10
  225. package/dist/session/routes.d.ts.map +1 -1
  226. package/dist/session/routes.js +11 -12
  227. package/dist/session/rpc.d.ts +108 -21
  228. package/dist/session/rpc.d.ts.map +1 -1
  229. package/dist/session/rpc.js +194 -39
  230. package/dist/session/start-real-vite.js +2 -2
  231. package/dist/session/state-store.d.ts +1 -17
  232. package/dist/session/state-store.d.ts.map +1 -1
  233. package/dist/session/state-store.js +3 -53
  234. package/dist/session/supervisor-op.d.ts +7 -0
  235. package/dist/session/supervisor-op.d.ts.map +1 -1
  236. package/dist/session/supervisor-op.js +6 -1
  237. package/dist/session/supervisor-rpc.d.ts +116 -19
  238. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  239. package/dist/session/supervisor-rpc.js +194 -84
  240. package/dist/session/vite-command.d.ts.map +1 -1
  241. package/dist/session/vite-command.js +17 -14
  242. package/dist/session/ws.d.ts.map +1 -1
  243. package/dist/session/ws.js +20 -8
  244. package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
  245. package/dist/shell/npm-bin-entrypoints.js +30 -5
  246. package/dist/vfs/facet-resident-limits.d.ts +33 -0
  247. package/dist/vfs/facet-resident-limits.d.ts.map +1 -0
  248. package/dist/vfs/facet-resident-limits.js +33 -0
  249. package/dist/vfs/facet-resident-store.d.ts +2 -10
  250. package/dist/vfs/facet-resident-store.d.ts.map +1 -1
  251. package/dist/vfs/facet-resident-store.js +1594 -219
  252. package/dist/workspace-host.d.ts +3 -1
  253. package/dist/workspace-host.d.ts.map +1 -1
  254. package/dist/workspace-host.js +2 -0
  255. package/dist/wrangler/nimbus-wrangler.d.ts +0 -2
  256. package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -1
  257. package/dist/wrangler/nimbus-wrangler.js +8 -22
  258. package/package.json +34 -7
  259. package/public/_assets/esbuild-0.24.2.js +2380 -0
  260. package/public/_assets/opencode/1.16.2/index-attach.js +2 -2
  261. package/public/_assets/runtime/esbuild-cli-c4fda380d52befc1.js +6892 -0
  262. package/public/_assets/runtime/git-de3e60c9f3ff006a.js +103 -0
  263. package/public/_assets/runtime/{node-shims-acaa037e6d26ec0d.js → node-shims-06565b121555c194.js} +2879 -992
  264. package/public/_assets/runtime/resident-store-6ffb1939c53d982f.js +2264 -0
  265. package/public/_assets/runtime/vfs-write-ledger-e501e8fc793bc2c1.js +889 -0
  266. package/scripts/build-opencode-attach-entry.mjs +51 -1
  267. package/scripts/bundle-esbuild-wasm.mjs +55 -39
  268. package/scripts/bundle-facet-workers.mjs +159 -10
  269. package/scripts/bundle-git.mjs +88 -27
  270. package/scripts/bundle-node-shims.mjs +72 -46
  271. package/scripts/bundle-runtime.mjs +18 -421
  272. package/scripts/cf-git-patch.mjs +137 -0
  273. package/scripts/patch-install-deps.mjs +49 -96
  274. package/scripts/runtime-specs.mjs +435 -0
  275. package/vendor/git-http.generated.d.ts +82 -0
  276. package/vendor/git-upstream.generated.d.ts +4378 -0
  277. package/vendor/git.LICENSE +7 -0
  278. package/vendor/git.generated.d.mts +21 -0
  279. package/vendor/git.generated.mjs +103 -0
  280. package/dist/replica/suspension.d.ts +0 -36
  281. package/dist/replica/suspension.d.ts.map +0 -1
  282. package/dist/replica/suspension.js +0 -49
  283. package/dist/runtime/static-server.d.ts +0 -3
  284. package/dist/runtime/static-server.d.ts.map +0 -1
  285. package/dist/runtime/static-server.js +0 -133
  286. package/scripts/clean-dist.mjs +0 -7
@@ -110,6 +110,56 @@ function stripEmbeddedModelsCatalog(pack) {
110
110
  return pack;
111
111
  }
112
112
 
113
+ const unicodeEscape = (code) => '\\u' + code.toString(16).padStart(4, '0');
114
+
115
+ /** One regex literal with every non-ASCII code unit written as `\uXXXX`; `\…` is an identity escape of `…`. */
116
+ function escapeRegexLiteral(literal) {
117
+ let out = '';
118
+ for (let i = 0; i < literal.length; i++) {
119
+ const code = literal.charCodeAt(i);
120
+ if (code === 0x5c && i + 1 < literal.length) {
121
+ const next = literal.charCodeAt(i + 1);
122
+ out += next > 0x7f ? unicodeEscape(next) : literal.slice(i, i + 2);
123
+ i++;
124
+ } else {
125
+ out += code > 0x7f ? unicodeEscape(code) : literal[i];
126
+ }
127
+ }
128
+ return out;
129
+ }
130
+
131
+ /**
132
+ * Make the attach entry pure ASCII. esbuild's default ASCII charset escapes
133
+ * every literal except regular expressions, so opencode's typographic-quote
134
+ * regexes (`/[‘’‚‛]/g`, `/…/g`) survive, and one character above U+00FF makes
135
+ * V8 hold the whole 14 MB source as a two-byte string: 29 MB in the session
136
+ * that assembles the facet's module map and again in the facet that runs it.
137
+ * Fails loud if non-ASCII remains anywhere else.
138
+ */
139
+ function asciiOnlyAttachEntry(src) {
140
+ const acorn = require('acorn');
141
+ const parts = [];
142
+ let last = 0;
143
+ for (const token of acorn.tokenizer(src, { ecmaVersion: 'latest', sourceType: 'module' })) {
144
+ if (token.type !== acorn.tokTypes.regexp) continue;
145
+ const literal = src.slice(token.start, token.end);
146
+ if (!/[^\x00-\x7f]/.test(literal)) continue;
147
+ parts.push(src.slice(last, token.start), escapeRegexLiteral(literal));
148
+ last = token.end;
149
+ }
150
+ parts.push(src.slice(last));
151
+ const out = parts.join('');
152
+ const residual = out.search(/[^\x00-\x7f]/);
153
+ if (residual >= 0) {
154
+ throw new Error(
155
+ '[build-opencode-attach-entry] non-ASCII outside a regex literal at offset ' +
156
+ `${residual} (${JSON.stringify(out.slice(Math.max(0, residual - 40), residual + 10))}) — ` +
157
+ 'escape it here, or the attach source is held as a two-byte string',
158
+ );
159
+ }
160
+ return out;
161
+ }
162
+
113
163
  /**
114
164
  * Build the attach entry text from in-memory sources.
115
165
  * @param entry index.js source text.
@@ -178,7 +228,7 @@ export async function buildOpencodeAttachEntryFromSources(entry, packInput) {
178
228
  },
179
229
  ],
180
230
  });
181
- const out = result.outputFiles[0].text;
231
+ const out = asciiOnlyAttachEntry(result.outputFiles[0].text);
182
232
  const residual = [...out.matchAll(/import\(\s*["'](?:\.\/)?(chunk-[a-z0-9]+\.js)["']\s*\)/g)];
183
233
  if (residual.length > 0) {
184
234
  throw new Error(
@@ -10,8 +10,10 @@
10
10
  * than the supervisor's. The facet needs two pieces of esbuild-wasm
11
11
  * at module-load time:
12
12
  *
13
- * 1. The JS adapter (`esbuild-wasm/esm/browser.js`) — small, stays
14
- * in the worker bundle as a string constant.
13
+ * 1. The JS adapter (`esbuild-wasm/esm/browser.js`, ~117 KiB), rewritten
14
+ * into a function body. The supervisor already imports the same
15
+ * build as a module for its own transforms, so the adapter is staged
16
+ * beside the wasm rather than embedded a second time as a string.
15
17
  *
16
18
  * 2. The wasm binary (`esbuild.wasm`) — 12 MiB. Lives in the
17
19
  * static-assets layer (Workers ASSETS binding) at
@@ -48,11 +50,15 @@
48
50
  * Output:
49
51
  * src/esbuild-wasm-bundle.generated.ts
50
52
  * export const ESBUILD_WASM_VERSION: string;
53
+ * export const ESBUILD_WASM_ASSET_PATH: string; // /_assets/esbuild-<version>.wasm
54
+ * export const ESBUILD_JS_ASSET_PATH: string; // /_assets/esbuild-<version>.js
51
55
  * export const ESBUILD_WASM_SHA256: string; // staged wasm digest
52
- * export const ESBUILD_WASM_JS_FN_BODY: string; // ESM browser source
56
+ * export const ESBUILD_JS_SHA256: string; // staged adapter digest
53
57
  *
54
58
  * public/_assets/esbuild-<version>.wasm
55
59
  * The raw wasm, picked up by the wrangler ASSETS binding.
60
+ * public/_assets/esbuild-<version>.js
61
+ * The adapter as a function body, same binding.
56
62
  *
57
63
  * Run via:
58
64
  * node scripts/bundle-esbuild-wasm.mjs
@@ -144,8 +150,8 @@ async function main() {
144
150
  // The ASSETS binding picks up everything under `assets.directory`
145
151
  // (./public per wrangler.jsonc). We version-name the file so a
146
152
  // future esbuild-wasm bump produces a different asset filename and
147
- // forces a fresh fetch (no stale-cache risk; cf. ESBUILD_WASM_ASSET_PATH
148
- // in src/esbuild-wasm-bytes.ts).
153
+ // forces a fresh fetch (no stale-cache risk; the generated module names
154
+ // the path and src/runtime/esbuild-wasm-bytes.ts reads it).
149
155
  await fs.mkdir(OUT_ASSETS_DIR, { recursive: true });
150
156
  const assetName = `esbuild-${version}.wasm`;
151
157
  const assetOut = path.join(OUT_ASSETS_DIR, assetName);
@@ -155,53 +161,48 @@ async function main() {
155
161
  console.log(
156
162
  `[bundle-esbuild-wasm] copied esbuild.wasm → ${path.relative(ROOT, assetOut)} (${(wasmBytes.length / (1024 * 1024)).toFixed(1)} MiB)`,
157
163
  );
164
+ // The JS adapter is staged the same way. Facets splice it into their
165
+ // source as a string; the supervisor imports esbuild-wasm as a module
166
+ // for its own transforms, so carrying the adapter inline as well
167
+ // shipped it twice in the Worker bundle.
168
+ const jsAssetName = `esbuild-${version}.js`;
169
+ const jsAssetOut = path.join(OUT_ASSETS_DIR, jsAssetName);
170
+ const jsBytes = Buffer.from(jsFn, 'utf8');
171
+ await fs.writeFile(jsAssetOut, jsBytes);
172
+ const jsSha256 = createHash('sha256').update(jsBytes).digest('hex');
173
+ console.log(
174
+ `[bundle-esbuild-wasm] wrote JS adapter → ${path.relative(ROOT, jsAssetOut)} (${(jsBytes.length / 1024).toFixed(1)} KiB)`,
175
+ );
158
176
 
159
177
  // ── 3. Clean up any stale-versioned wasm assets in public/_assets/ ──
160
178
  // Keeps the deploy lean: a developer who bumped the esbuild-wasm
161
179
  // version locally won't accidentally ship two copies.
162
180
  for (const entry of await fs.readdir(OUT_ASSETS_DIR)) {
163
- if (entry.startsWith('esbuild-') && entry.endsWith('.wasm') && entry !== assetName) {
181
+ if (entry.startsWith('esbuild-') && (entry.endsWith('.wasm') || entry.endsWith('.js'))
182
+ && entry !== assetName && entry !== jsAssetName) {
164
183
  const stale = path.join(OUT_ASSETS_DIR, entry);
165
184
  await fs.unlink(stale);
166
185
  console.log(`[bundle-esbuild-wasm] removed stale asset: ${entry}`);
167
186
  }
168
187
  }
169
188
 
170
- // ── 4. Emit the JS-only generated TS module ──────────────────────────
171
- // No ESBUILD_WASM_BASE64 export. The base64 string was 16 MiB and
172
- // sat in supervisor module scope for the isolate's lifetime; A'.5
173
- // moved the bytes to env.ASSETS to drop that residency.
189
+ // ── 4. Emit the generated TS module: versions and digests only ──────
190
+ // Neither artifact is inline. The base64 wasm string was 16 MiB and
191
+ // sat in supervisor module scope for the isolate's lifetime (A'.5
192
+ // moved it to env.ASSETS); the JS adapter followed so the bundle
193
+ // carries esbuild-wasm once, as the module the supervisor imports.
174
194
  const header = `/**
175
195
  * esbuild-wasm-bundle.generated.ts — AUTO-GENERATED by scripts/bundle-esbuild-wasm.mjs
176
196
  * DO NOT EDIT.
177
197
  *
178
- * Bundled esbuild-wasm ${version} adapter for use inside NimbusIsolatePool
179
- * isolates. Only the JS adapter is bundled here (~117 KiB); the wasm
180
- * binary itself lives in the static-assets layer at
181
- * public/_assets/esbuild-${version}.wasm and is fetched on demand by
182
- * src/esbuild-wasm-bytes.ts.
198
+ * Digests of the staged esbuild-wasm ${version} artifacts. Both live in
199
+ * the static-assets layer, public/_assets/esbuild-${version}.{wasm,js},
200
+ * and are fetched on demand and verified by src/runtime/esbuild-wasm-bytes.ts.
183
201
  *
184
- * Phase 2 A'.5 moved the base64 wasm string OUT of the supervisor
185
- * bundle (was 16 MiB UTF-8 → 21 MiB UTF-16 in JS string overhead).
186
- * If you're reading this and wondering where the bytes are: env.ASSETS.
187
- */
188
-
189
- export const ESBUILD_WASM_VERSION: string = ${JSON.stringify(version)};
190
-
191
- /**
192
- * SHA-256 of the staged public/_assets/esbuild-${version}.wasm bytes.
193
- * src/runtime/esbuild-wasm-bytes.ts verifies every fetch against it — the L2
194
- * (caches.default) tier included — before the bytes reach workerd's loader.
195
- */
196
- export const ESBUILD_WASM_SHA256: string = ${JSON.stringify(wasmSha256)};
197
-
198
- /**
199
- * Function body that, when wrapped in \`new Function(...)()\`, returns
200
- * the esbuild-wasm namespace (build, transform, initialize, ...).
201
- *
202
- * Source: node_modules/esbuild-wasm/esm/browser.js with the trailing
203
- * \`export { ... }\` rewritten to \`return { ... };\`. Why the browser
204
- * build:
202
+ * The JS adapter is node_modules/esbuild-wasm/esm/browser.js with the
203
+ * trailing \`export { ... }\` rewritten to \`return { ... };\`, so that
204
+ * \`new Function(body)()\` yields the esbuild namespace inside a facet.
205
+ * Why the browser build:
205
206
  * - Pure ESM with no \`createRequire\`, no \`require("fs")\`, and
206
207
  * no \`process.binding\` — safe to evaluate inside a workerd
207
208
  * dynamic-worker isolate.
@@ -219,13 +220,28 @@ export const ESBUILD_WASM_SHA256: string = ${JSON.stringify(wasmSha256)};
219
220
  * patterns (createRequire / dynamic node-builtin require / process.binding)
220
221
  * and bails loud if a future release reintroduces them.
221
222
  */
222
- export const ESBUILD_WASM_JS_FN_BODY: string = ${JSON.stringify(jsFn)};
223
+ export const ESBUILD_WASM_VERSION: string = ${JSON.stringify(version)};
224
+ /**
225
+ * Where the artifacts live inside env.ASSETS. Version-named, so a bump
226
+ * lands under a new name and never serves a stale cache entry. Naming them
227
+ * here is what puts them under scripts/dist-integrity.mjs's staged-asset
228
+ * check, which discovers asset paths by value in generated modules.
229
+ */
230
+ export const ESBUILD_WASM_ASSET_PATH: string = ${JSON.stringify('/_assets/' + assetName)};
231
+ export const ESBUILD_JS_ASSET_PATH: string = ${JSON.stringify('/_assets/' + jsAssetName)};
232
+ /**
233
+ * SHA-256 of the staged public/_assets/esbuild-${version}.wasm bytes.
234
+ * src/runtime/esbuild-wasm-bytes.ts verifies every fetch against it — the L2
235
+ * (caches.default) tier included — before the bytes reach workerd's loader.
236
+ */
237
+ export const ESBUILD_WASM_SHA256: string = ${JSON.stringify(wasmSha256)};
238
+ /** SHA-256 of the staged public/_assets/esbuild-${version}.js adapter, checked the same way. */
239
+ export const ESBUILD_JS_SHA256: string = ${JSON.stringify(jsSha256)};
223
240
  `;
224
-
225
241
  await fs.writeFile(OUT_TS, header, 'utf8');
226
242
  const stat = await fs.stat(OUT_TS);
227
243
  console.log(
228
- `[bundle-esbuild-wasm] wrote ${path.relative(ROOT, OUT_TS)} (${(stat.size / 1024).toFixed(1)} KiB — JS adapter only)`,
244
+ `[bundle-esbuild-wasm] wrote ${path.relative(ROOT, OUT_TS)} (${(stat.size / 1024).toFixed(1)} KiB — digests only)`,
229
245
  );
230
246
  }
231
247
 
@@ -39,20 +39,69 @@
39
39
  * WASI_INSTANCE_BODY_SRC: string
40
40
  * @nimbus-sh/core src/runtime/bash-runner.generated.ts — exports
41
41
  * BASH_RUNNER_BODY_SRC: string
42
+ * public/_assets/runtime/esbuild-cli-<buildId>.js — the `esbuild` command's
43
+ * runner, which only the session's esbuild facet evaluates. Staged as an
44
+ * asset rather than a string in the Worker bundle, like the esbuild
45
+ * adapter and the node shims; src/esbuild-cli-artifact.generated.ts
46
+ * exports ESBUILD_CLI_ASSET_PATH, ESBUILD_CLI_BUILD_ID, ESBUILD_CLI_SHA256.
42
47
  *
43
48
  * Runs as a postinstall + predev + predeploy step via package.json.
44
49
  */
45
50
 
46
51
  import { build } from 'esbuild';
47
- import { writeFileSync, mkdirSync } from 'node:fs';
52
+ import { createHash } from 'node:crypto';
53
+ import { readFileSync, writeFileSync, mkdirSync, readdirSync, unlinkSync } from 'node:fs';
48
54
  import { dirname, join } from 'node:path';
49
55
  import { fileURLToPath, pathToFileURL } from 'node:url';
56
+ // core's own parser dependency, reached through the workspace hoist; the
57
+ // script runs under plain node at postinstall, so nothing here is TypeScript.
58
+ import { parse } from 'acorn';
59
+
60
+ import { resolvePackageDir } from './resolve-package-dir.mjs';
50
61
 
51
62
  const __dirname = dirname(fileURLToPath(import.meta.url));
52
63
  const root = join(__dirname, '..');
53
64
  const coreRoot = join(root, '..', 'core');
54
65
  const platformRoot = join(root, '..', 'platform');
55
66
 
67
+ /**
68
+ * The bundle without its comments. esbuild keeps every comment that sits
69
+ * inside an expression or object literal, plus its own `// path` module
70
+ * markers, and nothing reads any of them: the strings are evaluated, never
71
+ * shown, mapped or `toString()`ed. The comment ranges come from a real
72
+ * parse of the output, so a `//` inside a string, template or regex is
73
+ * untouched; only comments go, and a line that held nothing but a comment
74
+ * goes with it. Every other character, identifier and line stays put.
75
+ */
76
+ function withoutComments(text) {
77
+ const comments = [];
78
+ parse(text, { ecmaVersion: 'latest', sourceType: 'module', allowAwaitOutsideFunction: true, onComment: comments });
79
+ let out = '';
80
+ let cursor = 0;
81
+ for (const { start, end } of comments) {
82
+ if (start < cursor) continue;
83
+ const lineStart = text.lastIndexOf('\n', start - 1) + 1;
84
+ const lineEndAt = text.indexOf('\n', end);
85
+ const lineEnd = lineEndAt < 0 ? text.length : lineEndAt;
86
+ const leading = text.slice(lineStart, start);
87
+ const trailing = text.slice(end, lineEnd);
88
+ if (leading.trim() === '' && trailing.trim() === '') {
89
+ // The comment is the whole line (or lines): drop them, newline included.
90
+ out += text.slice(cursor, lineStart);
91
+ cursor = lineEndAt < 0 ? text.length : lineEnd + 1;
92
+ } else if (trailing.trim() === '') {
93
+ // Trailing comment: drop it and the blank that led to it.
94
+ out += text.slice(cursor, start).replace(/[ \t]+$/, '');
95
+ cursor = end;
96
+ } else {
97
+ // Mid-line block comment: one space keeps the tokens on either side apart.
98
+ out += `${text.slice(cursor, start)} `;
99
+ cursor = end;
100
+ }
101
+ }
102
+ return out + text.slice(cursor);
103
+ }
104
+
56
105
  /**
57
106
  * Bundle one TS source into a self-contained ESM string suitable for
58
107
  * inlining as a facet preamble. Strips the leading `export` on
@@ -76,7 +125,7 @@ async function bundleAsPreamble(entryPath, label) {
76
125
  if (!result.outputFiles || result.outputFiles.length === 0) {
77
126
  throw new Error(`[bundle-facet-workers/${label}] esbuild produced no output`);
78
127
  }
79
- let stripped = result.outputFiles[0].text;
128
+ let stripped = withoutComments(result.outputFiles[0].text);
80
129
  stripped = stripped.replace(/^export\s+(async\s+function|function|const|class)\b/gm, '$1');
81
130
  stripped = stripped.replace(/\n?export\s*\{[^}]*\}\s*;\s*$/g, '');
82
131
  return stripped;
@@ -112,7 +161,7 @@ async function bundleVirtualSocketKernel() {
112
161
  if (!result.outputFiles || result.outputFiles.length === 0) {
113
162
  throw new Error('[bundle-facet-workers/virtual-socket-kernel] esbuild produced no output');
114
163
  }
115
- return result.outputFiles[0].text;
164
+ return withoutComments(result.outputFiles[0].text);
116
165
  }
117
166
 
118
167
  /**
@@ -126,10 +175,7 @@ const WASI_REQUIRED_TOP_LEVEL = [
126
175
  '__wasiMakeImports',
127
176
  '__wasiRunStart',
128
177
  '__wasiRunStartAsync',
129
- '__wasiReadFilesB64',
130
178
  '__wasiAdoptSupervisor',
131
- '__wasiDrainPersist',
132
- '__wasiRevalidateFS',
133
179
  'fdTable',
134
180
  ];
135
181
 
@@ -168,7 +214,7 @@ async function bundleWasiInstance() {
168
214
  if (!result.outputFiles || result.outputFiles.length === 0) {
169
215
  throw new Error('[bundle-facet-workers/wasi-instance] esbuild produced no output');
170
216
  }
171
- let src = result.outputFiles[0].text;
217
+ let src = withoutComments(result.outputFiles[0].text);
172
218
  // The body is spliced into another module; a re-export block there is a
173
219
  // syntax error, and callers append their own `export { … }`.
174
220
  src = src.replace(/^export\s+(async\s+function|function|const|let|var|class)\b/gm, '$1');
@@ -233,7 +279,7 @@ async function bundleBashRunner() {
233
279
  if (!result.outputFiles || result.outputFiles.length === 0) {
234
280
  throw new Error('[bundle-facet-workers/bash-runner] esbuild produced no output');
235
281
  }
236
- const src = result.outputFiles[0].text;
282
+ const src = withoutComments(result.outputFiles[0].text);
237
283
  for (const name of ['__bashBoot', '__bashFeed']) {
238
284
  if (!new RegExp(`^\\s*globalThis\\.${name}\\s*=`, 'm').test(src)) {
239
285
  throw new Error(
@@ -260,6 +306,87 @@ async function bundleBashRunner() {
260
306
  return src;
261
307
  }
262
308
 
309
+ /**
310
+ * The `esbuild` command's runner: Go's own js/wasm glue from the installed
311
+ * esbuild-wasm (the package whose esbuild.wasm is staged to ASSETS, so the glue
312
+ * always matches the binary), wrapped so each run hands it its own global and
313
+ * `fs`, followed by the typed runner as an IIFE that installs
314
+ * globalThis.__esbuildCliRun. wasm_exec.js is spliced in verbatim, license
315
+ * header included.
316
+ */
317
+ async function bundleEsbuildCli() {
318
+ const result = await build({
319
+ entryPoints: [join(coreRoot, 'src', 'runtime', 'esbuild-cli', 'preamble.ts')],
320
+ bundle: true,
321
+ format: 'iife',
322
+ target: 'esnext',
323
+ platform: 'neutral',
324
+ absWorkingDir: root,
325
+ write: false,
326
+ logLevel: 'warning',
327
+ legalComments: 'none',
328
+ });
329
+ if (!result.outputFiles || result.outputFiles.length === 0) {
330
+ throw new Error('[bundle-facet-workers/esbuild-cli] esbuild produced no output');
331
+ }
332
+ const runner = withoutComments(result.outputFiles[0].text);
333
+ if (!/^\s*globalThis\.__esbuildCliRun\s*=/m.test(runner)) {
334
+ throw new Error(
335
+ '[bundle-facet-workers/esbuild-cli] the bundle no longer assigns globalThis.__esbuildCliRun — ' +
336
+ 'every esbuild command would fail inside the esbuild facet',
337
+ );
338
+ }
339
+ if (/^\s*(?:import|export)\b/m.test(runner)) {
340
+ throw new Error('[bundle-facet-workers/esbuild-cli] an import/export survived bundling');
341
+ }
342
+ const glue = readFileSync(join(resolvePackageDir('esbuild-wasm', { start: root }), 'wasm_exec.js'), 'utf8');
343
+ if (!/globalThis\.Go\s*=\s*class\b/.test(glue)) {
344
+ throw new Error('[bundle-facet-workers/esbuild-cli] esbuild-wasm/wasm_exec.js no longer defines globalThis.Go');
345
+ }
346
+ return `const __esbuildGoRuntime = function (globalThis, fs) {\n${glue}\nreturn globalThis.Go;\n};\n${runner}`;
347
+ }
348
+
349
+ /**
350
+ * Stage the esbuild CLI runner under public/_assets/runtime/, named by a
351
+ * prefix of its own digest so no cache layer can serve another build's bytes
352
+ * under its name, and pin it in src/esbuild-cli-artifact.generated.ts.
353
+ */
354
+ function stageEsbuildCli(src) {
355
+ const sha256 = createHash('sha256').update(src, 'utf8').digest('hex');
356
+ const buildId = sha256.slice(0, 16);
357
+ const assetDir = join(root, 'public', '_assets', 'runtime');
358
+ const assetName = `esbuild-cli-${buildId}.js`;
359
+ mkdirSync(assetDir, { recursive: true });
360
+ for (const entry of readdirSync(assetDir)) {
361
+ if (entry.startsWith('esbuild-cli-') && entry !== assetName) unlinkSync(join(assetDir, entry));
362
+ }
363
+ writeFileSync(join(assetDir, assetName), src, 'utf8');
364
+ const assetPath = `/_assets/runtime/${assetName}`;
365
+ const generatedPath = join(root, 'src', 'esbuild-cli-artifact.generated.ts');
366
+ writeFileSync(generatedPath, [
367
+ '/**',
368
+ ' * esbuild-cli-artifact.generated.ts — AUTO-GENERATED by',
369
+ ' * scripts/bundle-facet-workers.mjs. DO NOT EDIT.',
370
+ ' *',
371
+ ' * Pins the staged runner of the `esbuild` command: esbuild-wasm/wasm_exec.js',
372
+ ' * (Go js/wasm glue, as __esbuildGoRuntime) followed by @nimbus-sh/core',
373
+ ' * src/runtime/esbuild-cli/preamble.ts as an IIFE. Only the session\'s esbuild',
374
+ ' * facet evaluates it, so src/runtime/esbuild-wasm-bytes.ts fetches it from',
375
+ ' * ASSETS when that facet is built, instead of the Worker bundle carrying it.',
376
+ ' * ESBUILD_CLI_BUILD_ID is a content-hash prefix, ESBUILD_CLI_SHA256 the',
377
+ ' * digest every fetch is verified against.',
378
+ ' *',
379
+ ` * Size: ${(src.length / 1024).toFixed(2)} KiB`,
380
+ ' */',
381
+ '',
382
+ `export const ESBUILD_CLI_ASSET_PATH: string = ${JSON.stringify(assetPath)};`,
383
+ `export const ESBUILD_CLI_BUILD_ID: string = ${JSON.stringify(buildId)};`,
384
+ `export const ESBUILD_CLI_SHA256: string = ${JSON.stringify(sha256)};`,
385
+ '',
386
+ ].join('\n'));
387
+ return { assetPath, generatedPath };
388
+ }
389
+
263
390
  async function main() {
264
391
  // 1. Tar-parser preamble (existing W2.5/W4 hot-path helpers).
265
392
  const tarStripped = await bundleAsPreamble(
@@ -279,6 +406,17 @@ async function main() {
279
406
  'w7-frame',
280
407
  );
281
408
 
409
+ // 3. Node's ESM resolver, which the node shims embed as source (their
410
+ // process's import() loader). One compile of it, here, so the shims'
411
+ // copy is the same text whatever toolchain later evaluates the shims.
412
+ const esmResolver = await bundleAsPreamble(
413
+ join(coreRoot, 'src', '_shared', 'esm-resolver.ts'),
414
+ 'esm-resolver',
415
+ );
416
+ if (!/^function createEsmResolver\(/m.test(esmResolver)) {
417
+ throw new Error('[bundle-facet-workers/esm-resolver] the bundle no longer declares function createEsmResolver');
418
+ }
419
+
282
420
  const tarEncoded = JSON.stringify(tarStripped);
283
421
  const w7Encoded = JSON.stringify(w7Stripped);
284
422
  const outPath = join(root, 'src', 'loaders', 'generated-workers.ts');
@@ -290,8 +428,9 @@ async function main() {
290
428
  ' * Produced by scripts/bundle-facet-workers.mjs from:',
291
429
  ' * - @nimbus-sh/core src/_shared/tarball-stream.ts (streaming tar primitives)',
292
430
  ' * - @nimbus-sh/platform src/w7-frame.ts (W7 streaming bulk-write encoder)',
431
+ ' * - @nimbus-sh/core src/_shared/esm-resolver.ts (Node\'s ESM resolver, for the node shims)',
293
432
  ' *',
294
- ' * Consumed by src/loaders/loader-pool.ts callers via the `preamble`',
433
+ ' * Consumed by fabric/isolate-pool.ts callers via the `preamble`',
295
434
  ' * option. The preamble is injected at the top of every generated',
296
435
  ' * worker module so user functions can reference the exported',
297
436
  ' * helpers by name.',
@@ -309,6 +448,9 @@ async function main() {
309
448
  '',
310
449
  `export const W7_FRAME_PREAMBLE: string = ${w7Encoded};`,
311
450
  '',
451
+ '/** Declares `function createEsmResolver(host)`; the node shims call it. */',
452
+ `export const ESM_RESOLVER_PREAMBLE: string = ${JSON.stringify(esmResolver)};`,
453
+ '',
312
454
  ].join('\n');
313
455
 
314
456
  mkdirSync(dirname(outPath), { recursive: true });
@@ -380,6 +522,9 @@ async function main() {
380
522
  '',
381
523
  ].join('\n'));
382
524
 
525
+ const esbuildCliSrc = await bundleEsbuildCli();
526
+ const esbuildCli = stageEsbuildCli(esbuildCliSrc);
527
+
383
528
  console.log(
384
529
  `[bundle-facet-workers] wrote ${kernelOutPath} ` +
385
530
  `(kernel=${(kernelSrc.length / 1024).toFixed(2)} KiB)`,
@@ -392,13 +537,17 @@ async function main() {
392
537
  `[bundle-facet-workers] wrote ${bashOutPath} ` +
393
538
  `(bash=${(bashSrc.length / 1024).toFixed(2)} KiB)`,
394
539
  );
540
+ console.log(
541
+ `[bundle-facet-workers] staged ${esbuildCli.assetPath} and wrote ${esbuildCli.generatedPath} ` +
542
+ `(esbuild-cli=${(esbuildCliSrc.length / 1024).toFixed(2)} KiB)`,
543
+ );
395
544
  }
396
545
 
397
546
  // The bundle functions are exported so the parity test can re-derive the
398
547
  // generated files from source and compare, rather than restating the esbuild
399
548
  // settings — a second copy of those settings is exactly the drift such a test
400
549
  // exists to catch. main() therefore runs only when this file is the entry point.
401
- export { bundleWasiInstance, bundleBashRunner };
550
+ export { bundleWasiInstance, bundleBashRunner, bundleEsbuildCli };
402
551
 
403
552
  if (process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url) {
404
553
  main().catch((e) => {