@nimbus-sh/core 0.5.0 → 0.7.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 (145) hide show
  1. package/README.md +47 -26
  2. package/dist/_shared/byte-stream.d.ts +5 -4
  3. package/dist/_shared/byte-stream.d.ts.map +1 -1
  4. package/dist/_shared/cache-stats.d.ts +23 -1
  5. package/dist/_shared/cache-stats.d.ts.map +1 -1
  6. package/dist/_shared/cache-stats.js +13 -1
  7. package/dist/_shared/exports-resolver.d.ts +1 -1
  8. package/dist/_shared/exports-resolver.js +1 -1
  9. package/dist/_shared/real-node-imports.d.ts +7 -0
  10. package/dist/_shared/real-node-imports.d.ts.map +1 -1
  11. package/dist/_shared/real-node-imports.js +8 -0
  12. package/dist/_shared/retry.js +1 -1
  13. package/dist/_shared/tarball.d.ts +23 -5
  14. package/dist/_shared/tarball.d.ts.map +1 -1
  15. package/dist/_shared/tarball.js +38 -5
  16. package/dist/_shared/vfs-write-ledger.d.ts.map +1 -1
  17. package/dist/_shared/vfs-write-ledger.js +22 -16
  18. package/dist/constants.d.ts +5 -11
  19. package/dist/constants.d.ts.map +1 -1
  20. package/dist/constants.js +16 -56
  21. package/dist/runtime/assets-loader.js +1 -1
  22. package/dist/runtime/comment-strip.d.ts +0 -35
  23. package/dist/runtime/comment-strip.d.ts.map +1 -1
  24. package/dist/runtime/comment-strip.js +48 -20
  25. package/dist/runtime/esbuild-service.d.ts +38 -42
  26. package/dist/runtime/esbuild-service.d.ts.map +1 -1
  27. package/dist/runtime/esbuild-service.js +494 -197
  28. package/dist/runtime/javascript-ast.d.ts.map +1 -1
  29. package/dist/runtime/javascript-ast.js +45 -7
  30. package/dist/runtime/opentui-wasm-backend.d.ts.map +1 -1
  31. package/dist/runtime/opentui-wasm-backend.js +18 -16
  32. package/dist/runtime/port-registry.d.ts +4 -2
  33. package/dist/runtime/port-registry.d.ts.map +1 -1
  34. package/dist/runtime/port-registry.js +60 -8
  35. package/dist/runtime/virtual-socket-kernel.generated.d.ts +1 -1
  36. package/dist/runtime/virtual-socket-kernel.generated.js +1 -1
  37. package/dist/shell/stdin-adapter.d.ts +11 -0
  38. package/dist/shell/stdin-adapter.d.ts.map +1 -0
  39. package/dist/shell/stdin-adapter.js +59 -0
  40. package/dist/shell/unix-commands.d.ts +2 -1
  41. package/dist/shell/unix-commands.d.ts.map +1 -1
  42. package/dist/shell/unix-commands.js +1078 -50
  43. package/dist/substrate/lifo/commands/io/dd.js +1 -1
  44. package/dist/substrate/lifo/commands/net/curl.d.ts.map +1 -1
  45. package/dist/substrate/lifo/commands/net/curl.js +270 -54
  46. package/dist/substrate/lifo/commands/system/npm.d.ts +18 -1
  47. package/dist/substrate/lifo/commands/system/npm.d.ts.map +1 -1
  48. package/dist/substrate/lifo/commands/system/npm.js +26 -43
  49. package/dist/substrate/lifo/commands/text/head.d.ts.map +1 -1
  50. package/dist/substrate/lifo/commands/text/head.js +2 -1
  51. package/dist/substrate/lifo/commands/text/sed.d.ts.map +1 -1
  52. package/dist/substrate/lifo/commands/text/sed.js +499 -117
  53. package/dist/substrate/lifo/commands/types.d.ts +8 -4
  54. package/dist/substrate/lifo/commands/types.d.ts.map +1 -1
  55. package/dist/substrate/lifo/node-compat/zlib.d.ts +37 -18
  56. package/dist/substrate/lifo/node-compat/zlib.d.ts.map +1 -1
  57. package/dist/substrate/lifo/node-compat/zlib.js +122 -23
  58. package/dist/substrate/lifo/shell/interpreter.d.ts +9 -1
  59. package/dist/substrate/lifo/shell/interpreter.d.ts.map +1 -1
  60. package/dist/substrate/lifo/shell/interpreter.js +79 -29
  61. package/dist/substrate/lifo/shell/pipe.d.ts +16 -0
  62. package/dist/substrate/lifo/shell/pipe.d.ts.map +1 -1
  63. package/dist/substrate/lifo/shell/pipe.js +62 -18
  64. package/dist/substrate/lifo/shell/terminal-stdin.d.ts +20 -3
  65. package/dist/substrate/lifo/shell/terminal-stdin.d.ts.map +1 -1
  66. package/dist/substrate/lifo/shell/terminal-stdin.js +60 -24
  67. package/dist/vfs/seed-project.d.ts.map +1 -1
  68. package/dist/vfs/seed-project.js +1 -1
  69. package/dist/vfs/sqlite-vfs.d.ts +21 -30
  70. package/dist/vfs/sqlite-vfs.d.ts.map +1 -1
  71. package/dist/vfs/sqlite-vfs.js +113 -29
  72. package/dist/workspace/nimbus-workspace.d.ts +15 -0
  73. package/dist/workspace/nimbus-workspace.d.ts.map +1 -1
  74. package/dist/workspace/nimbus-workspace.js +20 -3
  75. package/dist/workspace/supervisor-op.d.ts +23 -0
  76. package/dist/workspace/supervisor-op.d.ts.map +1 -0
  77. package/dist/workspace/supervisor-op.js +92 -0
  78. package/package.json +3 -2
  79. package/src/_shared/byte-stream.ts +5 -4
  80. package/src/_shared/cache-stats.ts +27 -1
  81. package/src/_shared/exports-resolver.ts +1 -1
  82. package/src/_shared/real-node-imports.ts +8 -0
  83. package/src/_shared/retry.ts +1 -1
  84. package/src/_shared/tarball.ts +56 -5
  85. package/src/_shared/vfs-write-ledger.ts +22 -16
  86. package/src/constants.ts +30 -60
  87. package/src/runtime/assets-loader.ts +1 -1
  88. package/src/runtime/comment-strip.ts +42 -23
  89. package/src/runtime/esbuild-service.ts +566 -217
  90. package/src/runtime/javascript-ast.ts +40 -9
  91. package/src/runtime/opentui-wasm-backend.ts +19 -17
  92. package/src/runtime/port-registry.ts +66 -8
  93. package/src/runtime/virtual-socket-kernel.generated.ts +1 -1
  94. package/src/shell/stdin-adapter.ts +58 -0
  95. package/src/shell/unix-commands.ts +1054 -50
  96. package/src/substrate/lifo/commands/io/dd.ts +2 -2
  97. package/src/substrate/lifo/commands/net/curl.ts +310 -58
  98. package/src/substrate/lifo/commands/system/npm.ts +58 -38
  99. package/src/substrate/lifo/commands/text/head.ts +2 -1
  100. package/src/substrate/lifo/commands/text/sed.ts +519 -109
  101. package/src/substrate/lifo/commands/types.ts +8 -5
  102. package/src/substrate/lifo/node-compat/zlib.ts +155 -26
  103. package/src/substrate/lifo/shell/interpreter.ts +74 -27
  104. package/src/substrate/lifo/shell/pipe.ts +65 -24
  105. package/src/substrate/lifo/shell/terminal-stdin.ts +66 -30
  106. package/src/vfs/seed-project.ts +3 -3
  107. package/src/vfs/sqlite-vfs.ts +128 -64
  108. package/src/workspace/nimbus-workspace.ts +26 -2
  109. package/src/workspace/supervisor-op.ts +117 -0
  110. package/dist/_shared/install-phase.d.ts +0 -47
  111. package/dist/_shared/install-phase.d.ts.map +0 -1
  112. package/dist/_shared/install-phase.js +0 -1
  113. package/dist/_shared/rpc-dispose.d.ts +0 -4
  114. package/dist/_shared/rpc-dispose.d.ts.map +0 -1
  115. package/dist/_shared/rpc-dispose.js +0 -30
  116. package/dist/_shared/w7-frame.d.ts +0 -79
  117. package/dist/_shared/w7-frame.d.ts.map +0 -1
  118. package/dist/_shared/w7-frame.js +0 -840
  119. package/dist/_shared/weighted-credit-pool.d.ts +0 -61
  120. package/dist/_shared/weighted-credit-pool.d.ts.map +0 -1
  121. package/dist/_shared/weighted-credit-pool.js +0 -196
  122. package/dist/observability/diag-counters.d.ts +0 -236
  123. package/dist/observability/diag-counters.d.ts.map +0 -1
  124. package/dist/observability/diag-counters.js +0 -195
  125. package/dist/observability/heap-estimate.d.ts +0 -225
  126. package/dist/observability/heap-estimate.d.ts.map +0 -1
  127. package/dist/observability/heap-estimate.js +0 -185
  128. package/dist/observability/heavy-alloc-coord.d.ts +0 -93
  129. package/dist/observability/heavy-alloc-coord.d.ts.map +0 -1
  130. package/dist/observability/heavy-alloc-coord.js +0 -181
  131. package/dist/observability/oom-classify.d.ts +0 -138
  132. package/dist/observability/oom-classify.d.ts.map +0 -1
  133. package/dist/observability/oom-classify.js +0 -268
  134. package/dist/observability/oom-discriminator.d.ts +0 -176
  135. package/dist/observability/oom-discriminator.d.ts.map +0 -1
  136. package/dist/observability/oom-discriminator.js +0 -272
  137. package/src/_shared/install-phase.ts +0 -56
  138. package/src/_shared/rpc-dispose.ts +0 -31
  139. package/src/_shared/w7-frame.ts +0 -1013
  140. package/src/_shared/weighted-credit-pool.ts +0 -244
  141. package/src/observability/diag-counters.ts +0 -343
  142. package/src/observability/heap-estimate.ts +0 -331
  143. package/src/observability/heavy-alloc-coord.ts +0 -241
  144. package/src/observability/oom-classify.ts +0 -260
  145. package/src/observability/oom-discriminator.ts +0 -405
@@ -1,5 +1,5 @@
1
1
  /**
2
- * tarball.ts a whole tarball, in memory, as a path→bytes map.
2
+ * Tarball extraction for streaming installers and buffered archive consumers.
3
3
  *
4
4
  * The streaming primitives it walks with (`parseTarHeader`, `streamTarEntries`,
5
5
  * `readableStreamToAsyncIterable`) live in `./tarball-stream.ts` — a
@@ -7,10 +7,8 @@
7
7
  * into a string the loader pool injects into dynamic workers, where an import
8
8
  * would not resolve.
9
9
  *
10
- * This half is for a caller that already holds the bytes: `gem install`, which
11
- * fetches a `.gem` (a tar holding a gzipped tar) and needs both layers open at
12
- * once. An installer streaming a tarball it is still downloading should drive
13
- * `streamTarEntries` itself and never hold the whole thing.
10
+ * Installers use writeTarballStream. extractTarball retains a map for callers
11
+ * such as gem install, which must open an archive nested inside another one.
14
12
  */
15
13
 
16
14
  import {
@@ -18,6 +16,59 @@ import {
18
16
  readableStreamToAsyncIterable,
19
17
  } from './tarball-stream.js';
20
18
 
19
+ export interface TarballWriteTarget {
20
+ exists(path: string): boolean;
21
+ mkdir(path: string, options?: { recursive?: boolean }): void;
22
+ writeFile(path: string, data: Uint8Array | string): unknown;
23
+ }
24
+
25
+ export interface TarballWriteResult {
26
+ /** Regular files written, including the manifest. */
27
+ files: number;
28
+ /** Total decompressed bytes written. */
29
+ bytes: number;
30
+ }
31
+
32
+ const PACKAGE_MANIFEST = 'package.json';
33
+
34
+ /**
35
+ * Stream a gzipped npm archive into a package directory. Entry names are
36
+ * already canonical and prefix-stripped by streamTarEntries. Hold only the
37
+ * current entry and the manifest; write the manifest last so a failed install
38
+ * is not mistaken for a complete package on retry. Filesystem failures reject.
39
+ */
40
+ export async function writeTarballStream(
41
+ body: ReadableStream<Uint8Array>,
42
+ targetDir: string,
43
+ vfs: TarballWriteTarget,
44
+ ): Promise<TarballWriteResult> {
45
+ const ensureDir = (path: string): void => {
46
+ if (!vfs.exists(path)) vfs.mkdir(path, { recursive: true });
47
+ };
48
+ ensureDir(targetDir);
49
+ let files = 0;
50
+ let bytes = 0;
51
+ let manifest: Uint8Array | null = null;
52
+ const entries = streamTarEntries(
53
+ readableStreamToAsyncIterable(body.pipeThrough(new DecompressionStream('gzip'))),
54
+ );
55
+ for await (const entry of entries) {
56
+ if (entry.name === PACKAGE_MANIFEST) {
57
+ manifest = entry.data;
58
+ continue;
59
+ }
60
+ const fullPath = `${targetDir}/${entry.name}`;
61
+ const cut = fullPath.lastIndexOf('/');
62
+ if (cut > 0) ensureDir(fullPath.slice(0, cut));
63
+ await vfs.writeFile(fullPath, entry.data);
64
+ files++;
65
+ bytes += entry.data.length;
66
+ }
67
+ if (!manifest) throw new Error(`tarball for ${targetDir} carried no ${PACKAGE_MANIFEST}`);
68
+ await vfs.writeFile(`${targetDir}/${PACKAGE_MANIFEST}`, manifest);
69
+ return { files: files + 1, bytes: bytes + manifest.length };
70
+ }
71
+
21
72
  /** Extract every regular file. Gzipped input is decompressed first. */
22
73
  export async function extractTarball(
23
74
  tarball: ArrayBuffer,
@@ -317,26 +317,32 @@ async function __nimbusPersistVfsWrite(supervisor, path, content, snapshot) {
317
317
  }
318
318
 
319
319
  /**
320
- * Write back everything parked, keeping any failure for the exit drain.
320
+ * Write back the cells parked at THIS instant, and only those.
321
321
  *
322
- * The write-back sites that are not the exit drain — the debounce below and
323
- * the RELEASE barrier ahead of egress have no caller who could act on a
324
- * failure: there is no user frame to throw into, and rejecting the fetch that
325
- * happened to trigger the flush would blame the wrong operation. Retaining
326
- * the failure in the channel \`__nimbusDrainVfsMutations\` already drains means
327
- * the exit path still reports it, so a lost write is loud exactly once and
328
- * never silent.
322
+ * Bounded on purpose, and deliberately not routed through
323
+ * \`__nimbusDrainVfsWrites\`, whose \`while (pending > 0)\` waits for the mutation
324
+ * queue to be EMPTY. That wait is correct at process exit, where no new writes
325
+ * are coming. Anywhere else it is a livelock: a facet unpacking a tarball adds
326
+ * mutations faster than the loop retires them, so the loop never returns.
327
+ * Sited ahead of egress where it was that stopped the request from ever
328
+ * leaving the facet, and \`npx sv create\` ran, printed its intro, and then
329
+ * never reported an exit at all. A barrier may delay a request; it may not
330
+ * wait on a condition a busy process never reaches.
331
+ *
332
+ * Failures are retained rather than thrown. The two callers — the debounce
333
+ * below, and the RELEASE barrier ahead of egress — have no frame that could
334
+ * act on one: rejecting the fetch that happened to trigger the flush would
335
+ * blame the wrong operation. The exit drain reports what is retained, so a
336
+ * lost write is loud exactly once and never silent.
329
337
  */
330
338
  async function __nimbusFlushVfsWriteBack(supervisor) {
331
339
  if (!supervisor) return;
332
- try {
333
- await __nimbusDrainVfsWrites(supervisor);
334
- } catch (error) {
335
- if (!__nimbusHasPendingVfsMutationFailure) {
336
- __nimbusHasPendingVfsMutationFailure = true;
337
- __nimbusPendingVfsMutationFailure = error;
338
- }
339
- }
340
+ const paths = Object.keys(__vfsWrites);
341
+ if (paths.length === 0) return;
342
+ await Promise.allSettled(paths.map((path) => __nimbusFlushVfsWrite(
343
+ path,
344
+ (content, snapshot) => __nimbusPersistVfsWrite(supervisor, path, content, snapshot),
345
+ )));
340
346
  }
341
347
 
342
348
  /**
package/src/constants.ts CHANGED
@@ -1,7 +1,32 @@
1
1
  /**
2
2
  * constants.ts — Single source of truth for all Nimbus configuration.
3
+ *
4
+ * Nimbus POLICY only. The measured platform limits these policies are
5
+ * derived from (tx bounds, the RPC envelope, supervisor budgets) live in
6
+ * `@nimbus-sh/platform/limits.js`.
3
7
  */
4
8
 
9
+ import { MAX_RPC_SAFE_PAYLOAD_BYTES } from '@nimbus-sh/platform/limits.js';
10
+
11
+ // Published-surface forwarding: @nimbus-sh/core@0.5.0 exports these from
12
+ // this module and real consumers import them here (Proteus merge-back.ts
13
+ // pulls CHUNK_SIZE and the MAX_TX_* set). The platform split moved the
14
+ // definitions to @nimbus-sh/platform/limits.js, which stays the single
15
+ // source of truth; core's own modules import platform directly.
16
+ export {
17
+ CHUNK_SIZE,
18
+ MAX_GLOBAL_WRITE_STREAM_CREDIT_BYTES,
19
+ MAX_RPC_SAFE_PAYLOAD_BYTES,
20
+ MAX_TX_BLOB_BYTES,
21
+ MAX_TX_LOGICAL_ROWS,
22
+ MAX_TX_SQL_EXECS,
23
+ PRE_BUNDLE_CONCURRENCY,
24
+ PRE_BUNDLE_SLICE_CAP_BYTES,
25
+ SUPERVISOR_HEAP_CEILING_BYTES,
26
+ SUPERVISOR_IN_FLIGHT_ALLOCATION_BUDGET_BYTES,
27
+ SUPERVISOR_READ_RESERVE_BYTES,
28
+ } from '@nimbus-sh/platform/limits.js';
29
+
5
30
  // ── Versions ────────────────────────────────────────────────────────────
6
31
  export const NIMBUS_VERSION = '2.0.0';
7
32
  //
@@ -55,19 +80,11 @@ export const SQLJS_VERSION = '1.14.1';
55
80
  export const OPENCODE_VERSION = '1.16.2';
56
81
 
57
82
  // ── VFS Constants ───────────────────────────────────────────────────────
58
- export const CHUNK_SIZE = 65_536; // 64KB per content chunk
83
+ // The chunk size itself (CHUNK_SIZE) and the transaction bounds are
84
+ // platform limits — see @nimbus-sh/platform/limits.js.
59
85
  export const LRU_MAX_ENTRIES = 512; // 512 × 64KB = 32MB hot cache
60
86
  export const BATCH_SIZE = 64; // rows per batch INSERT
61
87
  export const VFS_CAPACITY = 10 * 1024 * 1024 * 1024; // 10 GB
62
- export const MAX_TX_BLOB_BYTES = 1 * 1024 * 1024;
63
- export const MAX_TX_LOGICAL_ROWS = 256;
64
- export const MAX_TX_SQL_EXECS = 64;
65
- export const MAX_GLOBAL_WRITE_STREAM_CREDIT_BYTES = 8 * 1024 * 1024;
66
-
67
- // Largest byte payload Nimbus sends through an ordinary Workers RPC value.
68
- // The platform limit is 32 MiB; 28 MiB leaves room for structured-clone
69
- // metadata and matches the proven on-demand facet transfer envelope.
70
- export const MAX_RPC_SAFE_PAYLOAD_BYTES = 28 * 1024 * 1024;
71
88
 
72
89
  // ── Batched filesystem reads ────────────────────────────────────────────
73
90
  // A round trip costs an order of magnitude more than the SQLite lookup
@@ -201,11 +218,12 @@ export const PREFETCH_CACHE_MAX_BYTES = 16 * 1024 * 1024;
201
218
  export const CWD_SNAPSHOT_MAX_FILE_BYTES = 2 * 1024 * 1024;
202
219
 
203
220
  // ── npm Constants ───────────────────────────────────────────────────────
221
+ // The pre-bundle admission envelope (PRE_BUNDLE_SLICE_CAP_BYTES,
222
+ // PRE_BUNDLE_CONCURRENCY) is a measured platform envelope — see
223
+ // @nimbus-sh/platform/limits.js.
204
224
  export const NPM_REGISTRY = 'https://registry.npmjs.org';
205
225
  export const NPM_CONCURRENCY = 12;
206
226
  export const NPM_DECOMPRESS_TIMEOUT = 15_000;
207
- export const PRE_BUNDLE_SLICE_CAP_BYTES = MAX_RPC_SAFE_PAYLOAD_BYTES;
208
- export const PRE_BUNDLE_CONCURRENCY = 1;
209
227
 
210
228
  // ── Dev Server Constants ────────────────────────────────────────────────
211
229
  export const DEFAULT_VITE_PORT = 5173;
@@ -225,54 +243,6 @@ export const NIMBUS_AI_GATEWAY_PORT = 8790;
225
243
  // ── Compatibility ───────────────────────────────────────────────────────
226
244
  export const CF_COMPAT_DATE = '2026-04-01';
227
245
 
228
- // ── Supervisor heap budget [C'.1] ───────────────────────────────────────
229
- //
230
- // Three distinct memory regimes, not one shared pool:
231
- //
232
- // - Supervisor DO isolate: 128 MiB HARD platform ceiling ("Each isolate
233
- // can consume up to 128 MB of memory" —
234
- // https://developers.cloudflare.com/workers/platform/limits/).
235
- // - Facets: ~208-256 MiB EACH, measured — memory independent of the
236
- // supervisor and of each other (1,664 MiB live across 8 facets +
237
- // parent) but CPU SHARED across the actor thread. See
238
- // packages/worker/src/loaders/process-fabric.ts.
239
- // - Peer DOs: independent memory AND CPU budgets, measured 1.2 GiB
240
- // live across 8 peers.
241
- //
242
- // Nimbus targets HALF of the supervisor's 128 MiB as a self-imposed soft
243
- // ceiling so the supervisor always has runway when workerd LRU-evicts
244
- // neighbours or AIR (Asynchronous Isolate Recreation) folds growing
245
- // isolates.
246
- //
247
- // 64 MiB is a soft admission budget, not a measurement — the right value
248
- // is unmeasured — and nothing enforces it directly.
249
- // src/observability/heap-estimate.ts sums the INSTRUMENTED contributors —
250
- // the supervisor baseline, VFS LRU and in-flight writes, pre-bundle
251
- // slices, streaming RPC buffers, and the prefetch-bundle build lease and
252
- // cache gauge — which is a lower bound, not full coverage; allocation
253
- // sites known to be missing are listed in HEAP_BLIND_SPOTS. Read a low
254
- // percentOfCeiling accordingly.
255
- export const SUPERVISOR_HEAP_CEILING_BYTES = 64 * 1024 * 1024;
256
-
257
- // Shared allowance for transient allocations in the supervisor DO. With the
258
- // VFS LRU shrunk to 8 MiB during an active reservation, 40 MiB of admitted
259
- // payload plus the 9 MiB bundle baseline stays below the 64 MiB soft ceiling
260
- // with 7 MiB left for metadata, structured-clone overhead, and runtime state.
261
- // This is 31.25% of the platform's 128 MiB hard isolate ceiling.
262
- export const SUPERVISOR_IN_FLIGHT_ALLOCATION_BUDGET_BYTES = 40 * 1024 * 1024;
263
-
264
- // Reserved slice of that allowance for chunk-sized filesystem reads, so a
265
- // read is never queued behind a multi-megabyte owner for a wait that has
266
- // nothing to do with its own cost. A read only draws on this when the shared
267
- // budget is already contended — which is exactly when the VFS LRU is shrunk
268
- // to 8 MiB — so peak accounting is 40 + 1 admitted, 8 LRU, 9 bundle baseline:
269
- // 58 MiB, still under the 64 MiB soft ceiling.
270
- //
271
- // 1 MiB is 16 concurrent READ_STREAM_CHUNK_BYTES reads. The point is not
272
- // depth, it is that a read loop keeps moving while a heavy owner works
273
- // instead of stopping dead for the duration.
274
- export const SUPERVISOR_READ_RESERVE_BYTES = 1024 * 1024;
275
-
276
246
  // ── OS Defaults ─────────────────────────────────────────────────────────
277
247
  export const DEFAULT_HOSTNAME = 'nimbus';
278
248
  export const DEFAULT_HOME = '/home/user';
@@ -35,7 +35,7 @@
35
35
  * same content.
36
36
  */
37
37
 
38
- import { disposeRpcResource } from '../_shared/rpc-dispose.js';
38
+ import { disposeRpcResource } from '@nimbus-sh/platform/rpc-dispose.js';
39
39
 
40
40
  /**
41
41
  * Per-isolate cache. Keys are asset paths (e.g. `/_assets/foo.js`).
@@ -27,47 +27,66 @@
27
27
  * so it's a minor wasted-work cost — see IMPORT_RE header comment in
28
28
  * `require-resolver.ts:84-86`.
29
29
  *
30
+ * Memory: the output is assembled from slices of the input — one per
31
+ * comment-free span — and joined once. It used to be built one character
32
+ * at a time with `out += c`, which every JS engine represents as a rope
33
+ * of one node per append: ~30 bytes of heap per character of output, all
34
+ * of it live until the string is first read. The require walk runs this
35
+ * over every file in the closure, and on typescript's 6.15 MB `lib/_tsc.js`
36
+ * that rope measured 174 MB (V8) / 172 MB (JSC) against a 128 MB isolate —
37
+ * the session Durable Object was killed inside `tsc`'s spawn before the
38
+ * facet existed. A span-sliced build holds the input, the output and a
39
+ * short array of slices, and nothing else.
40
+ *
30
41
  * TODO(CLN-X): when the `src/runtime/esbuild-service.ts` anti-touch
31
42
  * window opens, unify with the canonical
32
43
  * `stripCommentsAndStrings(src)` helper there. Both implement the
33
44
  * same logical pass over comments; only the string/regex-literal
34
45
  * handling differs.
35
46
  */
47
+ const SLASH = 0x2f;
48
+ const STAR = 0x2a;
49
+ const NEWLINE = 0x0a;
50
+
36
51
  export function stripCommentsForImports(src: string): string {
37
- let out = '';
38
- let i = 0;
39
52
  const N = src.length;
53
+ const parts: string[] = [];
54
+ // Start of the comment-free span not yet copied to `parts`.
55
+ let spanStart = 0;
56
+ let i = 0;
40
57
  while (i < N) {
41
- const c = src[i];
42
- // Line comment: `//` to end-of-line.
43
- if (c === '/' && src[i + 1] === '/') {
44
- out += ' ';
58
+ if (src.charCodeAt(i) !== SLASH) { i++; continue; }
59
+ const next = src.charCodeAt(i + 1);
60
+ // Line comment: `//` to end-of-line, replaced by one space. The
61
+ // newline is not part of the comment and stays in the next span.
62
+ if (next === SLASH) {
63
+ parts.push(src.slice(spanStart, i), ' ');
45
64
  i += 2;
46
- while (i < N && src[i] !== '\n') i++;
47
- // Newline preserved by falling through; do NOT consume it.
65
+ while (i < N && src.charCodeAt(i) !== NEWLINE) i++;
66
+ spanStart = i;
48
67
  continue;
49
68
  }
50
- // Block comment: `/*` to `*/`.
51
- if (c === '/' && src[i + 1] === '*') {
69
+ // Block comment: `/*` to `*/`, replaced by its own newlines (so line
70
+ // numbers stay aligned) and one space. An unterminated one runs to
71
+ // the end of input and gets no closing space.
72
+ if (next === STAR) {
73
+ parts.push(src.slice(spanStart, i));
52
74
  i += 2;
75
+ let newlines = 0;
76
+ let closed = false;
53
77
  while (i < N) {
54
- if (src[i] === '\n') {
55
- // Preserve newlines so line numbers stay aligned.
56
- out += '\n';
57
- i++;
58
- continue;
59
- }
60
- if (src[i] === '*' && src[i + 1] === '/') {
61
- i += 2;
62
- out += ' ';
63
- break;
64
- }
78
+ const c = src.charCodeAt(i);
79
+ if (c === NEWLINE) newlines++;
80
+ else if (c === STAR && src.charCodeAt(i + 1) === SLASH) { i += 2; closed = true; break; }
65
81
  i++;
66
82
  }
83
+ if (newlines > 0) parts.push('\n'.repeat(newlines));
84
+ if (closed) parts.push(' ');
85
+ spanStart = i;
67
86
  continue;
68
87
  }
69
- out += c;
70
88
  i++;
71
89
  }
72
- return out;
90
+ parts.push(src.slice(spanStart));
91
+ return parts.join('');
73
92
  }