@nimbus-sh/core 0.6.0 → 0.8.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.
- package/README.md +47 -26
- package/dist/_shared/byte-stream.d.ts +5 -4
- package/dist/_shared/byte-stream.d.ts.map +1 -1
- package/dist/_shared/real-node-imports.d.ts +7 -0
- package/dist/_shared/real-node-imports.d.ts.map +1 -1
- package/dist/_shared/real-node-imports.js +8 -0
- package/dist/_shared/tarball.d.ts +24 -5
- package/dist/_shared/tarball.d.ts.map +1 -1
- package/dist/_shared/tarball.js +42 -5
- package/dist/_shared/vfs-write-ledger.d.ts.map +1 -1
- package/dist/_shared/vfs-write-ledger.js +22 -16
- package/dist/runtime/comment-strip.d.ts +32 -29
- package/dist/runtime/comment-strip.d.ts.map +1 -1
- package/dist/runtime/comment-strip.js +273 -51
- package/dist/runtime/cpython-runner.d.ts +1 -0
- package/dist/runtime/cpython-runner.d.ts.map +1 -1
- package/dist/runtime/cpython-runner.js +1 -1
- package/dist/runtime/esbuild-service.d.ts +38 -42
- package/dist/runtime/esbuild-service.d.ts.map +1 -1
- package/dist/runtime/esbuild-service.js +500 -388
- package/dist/runtime/javascript-ast.d.ts.map +1 -1
- package/dist/runtime/javascript-ast.js +45 -7
- package/dist/runtime/opentui-wasm-backend.d.ts.map +1 -1
- package/dist/runtime/opentui-wasm-backend.js +18 -16
- package/dist/runtime/port-registry.d.ts +4 -2
- package/dist/runtime/port-registry.d.ts.map +1 -1
- package/dist/runtime/port-registry.js +60 -8
- package/dist/runtime/ruby-runner.d.ts +1 -0
- package/dist/runtime/ruby-runner.d.ts.map +1 -1
- package/dist/runtime/ruby-runner.js +1 -0
- package/dist/shell/stdin-adapter.d.ts +11 -0
- package/dist/shell/stdin-adapter.d.ts.map +1 -0
- package/dist/shell/stdin-adapter.js +59 -0
- package/dist/shell/unix-commands.d.ts +2 -1
- package/dist/shell/unix-commands.d.ts.map +1 -1
- package/dist/shell/unix-commands.js +1078 -50
- package/dist/substrate/lifo/commands/io/dd.js +1 -1
- package/dist/substrate/lifo/commands/net/curl.d.ts.map +1 -1
- package/dist/substrate/lifo/commands/net/curl.js +270 -54
- package/dist/substrate/lifo/commands/system/npm.d.ts +18 -1
- package/dist/substrate/lifo/commands/system/npm.d.ts.map +1 -1
- package/dist/substrate/lifo/commands/system/npm.js +25 -43
- package/dist/substrate/lifo/commands/text/head.d.ts.map +1 -1
- package/dist/substrate/lifo/commands/text/head.js +2 -1
- package/dist/substrate/lifo/commands/text/sed.d.ts.map +1 -1
- package/dist/substrate/lifo/commands/text/sed.js +499 -117
- package/dist/substrate/lifo/commands/types.d.ts +8 -4
- package/dist/substrate/lifo/commands/types.d.ts.map +1 -1
- package/dist/substrate/lifo/node-compat/zlib.d.ts +37 -18
- package/dist/substrate/lifo/node-compat/zlib.d.ts.map +1 -1
- package/dist/substrate/lifo/node-compat/zlib.js +122 -23
- package/dist/substrate/lifo/shell/interpreter.d.ts +9 -1
- package/dist/substrate/lifo/shell/interpreter.d.ts.map +1 -1
- package/dist/substrate/lifo/shell/interpreter.js +79 -29
- package/dist/substrate/lifo/shell/pipe.d.ts +16 -0
- package/dist/substrate/lifo/shell/pipe.d.ts.map +1 -1
- package/dist/substrate/lifo/shell/pipe.js +62 -18
- package/dist/substrate/lifo/shell/terminal-stdin.d.ts +20 -3
- package/dist/substrate/lifo/shell/terminal-stdin.d.ts.map +1 -1
- package/dist/substrate/lifo/shell/terminal-stdin.js +60 -24
- package/dist/vfs/sqlite-vfs.d.ts +19 -0
- package/dist/vfs/sqlite-vfs.d.ts.map +1 -1
- package/dist/vfs/sqlite-vfs.js +106 -23
- package/dist/workspace/nimbus-workspace.d.ts +22 -1
- package/dist/workspace/nimbus-workspace.d.ts.map +1 -1
- package/dist/workspace/nimbus-workspace.js +19 -3
- package/dist/workspace/supervisor-op.d.ts +107 -0
- package/dist/workspace/supervisor-op.d.ts.map +1 -0
- package/dist/workspace/supervisor-op.js +224 -0
- package/package.json +2 -2
- package/src/_shared/byte-stream.ts +5 -4
- package/src/_shared/real-node-imports.ts +8 -0
- package/src/_shared/tarball.ts +60 -5
- package/src/_shared/vfs-write-ledger.ts +22 -16
- package/src/runtime/comment-strip.ts +218 -52
- package/src/runtime/cpython-runner.ts +2 -1
- package/src/runtime/esbuild-service.ts +572 -372
- package/src/runtime/javascript-ast.ts +40 -9
- package/src/runtime/opentui-wasm-backend.ts +19 -17
- package/src/runtime/port-registry.ts +66 -8
- package/src/runtime/ruby-runner.ts +2 -0
- package/src/shell/stdin-adapter.ts +58 -0
- package/src/shell/unix-commands.ts +1054 -50
- package/src/substrate/lifo/commands/io/dd.ts +2 -2
- package/src/substrate/lifo/commands/net/curl.ts +310 -58
- package/src/substrate/lifo/commands/system/npm.ts +55 -38
- package/src/substrate/lifo/commands/text/head.ts +2 -1
- package/src/substrate/lifo/commands/text/sed.ts +519 -109
- package/src/substrate/lifo/commands/types.ts +8 -5
- package/src/substrate/lifo/node-compat/zlib.ts +155 -26
- package/src/substrate/lifo/shell/interpreter.ts +74 -27
- package/src/substrate/lifo/shell/pipe.ts +65 -24
- package/src/substrate/lifo/shell/terminal-stdin.ts +66 -30
- package/src/vfs/sqlite-vfs.ts +104 -23
- package/src/workspace/nimbus-workspace.ts +30 -3
- package/src/workspace/supervisor-op.ts +309 -0
package/README.md
CHANGED
|
@@ -5,16 +5,23 @@
|
|
|
5
5
|
> presented as-is.
|
|
6
6
|
|
|
7
7
|
The backend-agnostic half of Nimbus: a durable POSIX-like filesystem, a shell
|
|
8
|
-
with 60+ Unix commands, and the WASI runtime layer
|
|
9
|
-
dependency.
|
|
10
|
-
Cloudflare that SQLite is `ctx.storage.sql` inside your Durable Object; in bun
|
|
11
|
-
or node it is `bun:sqlite` or `node:sqlite`.
|
|
8
|
+
with 60+ Unix commands, and the WASI runtime layer. It has no Cloudflare
|
|
9
|
+
dependency.
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
You hand it a SQLite and get back `.fs` and `.exec`. On Cloudflare that
|
|
12
|
+
SQLite is `ctx.storage.sql` inside your Durable Object; in bun or node it is
|
|
13
|
+
`bun:sqlite` or `node:sqlite`. The whole package rests on two narrow ports
|
|
14
|
+
(`SqlDatabase` and `SqlTransactions`), so the same code serves both hosts.
|
|
15
|
+
|
|
16
|
+
Use it when something you already run needs a real workspace. A Durable
|
|
17
|
+
Object that does something else and needs somewhere to work. A local script
|
|
18
|
+
that needs the filesystem semantics the hosted product has.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @nimbus-sh/core
|
|
24
|
+
```
|
|
18
25
|
|
|
19
26
|
## Quick start
|
|
20
27
|
|
|
@@ -73,13 +80,13 @@ export class Workspace extends DurableObject {
|
|
|
73
80
|
```
|
|
74
81
|
|
|
75
82
|
Files written through `.fs` are owned by the session user (uid 1000), not
|
|
76
|
-
root
|
|
77
|
-
|
|
78
|
-
|
|
83
|
+
root. The shell enforces the same permission model either way: a root-owned
|
|
84
|
+
`/etc/passwd` refuses a write from `.fs`, and `id` resolves names through
|
|
85
|
+
it.
|
|
79
86
|
|
|
80
87
|
## Real runtimes, off Cloudflare
|
|
81
88
|
|
|
82
|
-
The wasm runtimes are separate npm packages so nobody downloads a Python
|
|
89
|
+
The wasm runtimes are separate npm packages, so nobody downloads a Python
|
|
83
90
|
interpreter to get a filesystem. Install the ones you want and pass them in:
|
|
84
91
|
|
|
85
92
|
```bash
|
|
@@ -104,35 +111,49 @@ await ws.exec(`python -c "import sqlite3; print('live')"`); // CPython 3.13, re
|
|
|
104
111
|
`@nimbus-sh/runtime-ruby` (Ruby 3.3) and `@nimbus-sh/runtime-clang` (clang →
|
|
105
112
|
`wasm32-wasi`, compile and run C in the workspace) work the same way. Every
|
|
106
113
|
package carries the same manifest and the same sha256-verified blobs the
|
|
107
|
-
hosted product serves from R2
|
|
114
|
+
hosted product serves from R2.
|
|
115
|
+
|
|
116
|
+
Without `facets` and `runtimes` you still get the full shell and coreutils.
|
|
117
|
+
The wasm runtimes are a dependency you add.
|
|
108
118
|
|
|
109
|
-
|
|
110
|
-
the wasm runtimes are a dependency you add, not a mode you enable. One caveat:
|
|
111
|
-
`localFacetHost()` covers bun and node only — on workerd the CSP forbids
|
|
119
|
+
`localFacetHost()` covers bun and node only. On workerd the CSP forbids
|
|
112
120
|
request-time `WebAssembly.instantiate`, so wasm has to ride the Worker Loader
|
|
113
|
-
module map
|
|
114
|
-
`@nimbus-sh/fabric
|
|
121
|
+
module map. That machinery lives in `@nimbus-sh/worker` and
|
|
122
|
+
`@nimbus-sh/fabric`. The shell, coreutils, and filesystem need none of it.
|
|
115
123
|
|
|
116
124
|
## Sharing a database with your own app
|
|
117
125
|
|
|
118
|
-
The workspace is
|
|
119
|
-
of it:
|
|
126
|
+
The workspace is a tenant in a database you own:
|
|
120
127
|
|
|
121
128
|
- It creates and touches only its own tables (`inodes`, `file_chunks`,
|
|
122
129
|
`content_lifecycle`, `vfs_*`).
|
|
123
|
-
- `destroy()` drops
|
|
130
|
+
- `destroy()` drops those tables and does not call `deleteAll()`.
|
|
124
131
|
- `transactionSync` must be a real transaction. An implementation that only
|
|
125
132
|
calls the callback turns every atomic write into a torn one.
|
|
126
|
-
- `generation` must never repeat across restarts of your host
|
|
133
|
+
- `generation` must never repeat across restarts of your host. Pids derive
|
|
127
134
|
from it, and a repeated generation would hand a dead process live write
|
|
128
135
|
authority.
|
|
129
136
|
|
|
130
|
-
|
|
137
|
+
When your own SQL rows must commit with filesystem bytes, use
|
|
138
|
+
`SqliteVFS.withTransaction(callback)` instead of an outer
|
|
139
|
+
`storage.transactionSync`. Use the credentialed synchronous VFS methods inside
|
|
140
|
+
the callback, not the workspace's asynchronous file methods. The callback can
|
|
141
|
+
read its writes; revisions and watch events publish only after commit. On
|
|
142
|
+
rollback, Nimbus clears cached chunks and reloads the always-resident inode
|
|
143
|
+
tree from SQLite before rethrowing with the original error as `cause`.
|
|
144
|
+
|
|
145
|
+
The method must own the outermost transaction on the same SQL host. Do not
|
|
146
|
+
nest it or start asynchronous work inside it. The host's transaction primitive
|
|
147
|
+
must support nested savepoints for individual VFS writes. If rollback reload
|
|
148
|
+
also fails, Nimbus throws an `AggregateError` carrying both failures; discard
|
|
149
|
+
that VFS instance and reopen it after storage recovers.
|
|
150
|
+
|
|
151
|
+
## What the worker package adds
|
|
131
152
|
|
|
132
153
|
Resident processes (long-running servers, attached TUIs), the session
|
|
133
154
|
protocol, port routing to the public internet, and the hosted terminal all
|
|
134
|
-
live in [`@nimbus-sh/worker`](https://www.npmjs.com/package/@nimbus-sh/worker)
|
|
135
|
-
|
|
155
|
+
live in [`@nimbus-sh/worker`](https://www.npmjs.com/package/@nimbus-sh/worker).
|
|
156
|
+
That package composes on this one. If you want the full hosted product shape,
|
|
136
157
|
start from `npx create-nimbus-app`.
|
|
137
158
|
|
|
138
159
|
## License
|
|
@@ -17,10 +17,11 @@
|
|
|
17
17
|
/**
|
|
18
18
|
* A command's output destination.
|
|
19
19
|
*
|
|
20
|
-
* `writeBytes` is present on sinks that store bytes verbatim
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* fallback: a terminal has no way to hold a byte that is
|
|
20
|
+
* `writeBytes` is present on sinks that store bytes verbatim — files,
|
|
21
|
+
* `/dev/null`, and shell pipes, which carry the producer's exact bytes.
|
|
22
|
+
* Sinks without it take decoded text instead. This is a real capability
|
|
23
|
+
* difference, not a fallback: a terminal has no way to hold a byte that is
|
|
24
|
+
* not text.
|
|
24
25
|
*/
|
|
25
26
|
export interface ByteSink {
|
|
26
27
|
write(text: string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"byte-stream.d.ts","sourceRoot":"","sources":["../../src/_shared/byte-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH
|
|
1
|
+
{"version":3,"file":"byte-stream.d.ts","sourceRoot":"","sources":["../../src/_shared/byte-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;;;GAQG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CACtC;AAED,2EAA2E;AAC3E,MAAM,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,UAAU,CAAC;AAEzE,uEAAuE;AACvE,eAAO,MAAM,kBAAkB,QAAY,CAAC;AAE5C;;;;GAIG;AACH,qBAAa,UAAU;IAIT,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,OAAO,CAAK;gBAES,IAAI,EAAE,QAAQ;IAI3C,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAO9B,GAAG,IAAI,IAAI;CAKZ;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GACvE,MAAM,CAkBR"}
|
|
@@ -38,6 +38,13 @@
|
|
|
38
38
|
* constructed Session's connect/post are inert.
|
|
39
39
|
* Tools (e.g. nuxi) that open a Session purely
|
|
40
40
|
* for optional profiling degrade cleanly.
|
|
41
|
+
* - node:zlib — full surface: every *Sync variant, brotli/zstd,
|
|
42
|
+
* crc32, constants, and streaming create* factories
|
|
43
|
+
* (probe-verified 2026-08-23 at compat date
|
|
44
|
+
* 2026-04-01). Forwarded verbatim by the zlib
|
|
45
|
+
* block in node-shims.ts; results are the host
|
|
46
|
+
* realm's own Buffers, which the widened
|
|
47
|
+
* __BufferMod.isBuffer recognizes.
|
|
41
48
|
*/
|
|
42
49
|
export declare function getRealNodeImportsCode(): string;
|
|
43
50
|
//# sourceMappingURL=real-node-imports.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"real-node-imports.d.ts","sourceRoot":"","sources":["../../src/_shared/real-node-imports.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"real-node-imports.d.ts","sourceRoot":"","sources":["../../src/_shared/real-node-imports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,wBAAgB,sBAAsB,IAAI,MAAM,CAW/C"}
|
|
@@ -38,6 +38,13 @@
|
|
|
38
38
|
* constructed Session's connect/post are inert.
|
|
39
39
|
* Tools (e.g. nuxi) that open a Session purely
|
|
40
40
|
* for optional profiling degrade cleanly.
|
|
41
|
+
* - node:zlib — full surface: every *Sync variant, brotli/zstd,
|
|
42
|
+
* crc32, constants, and streaming create* factories
|
|
43
|
+
* (probe-verified 2026-08-23 at compat date
|
|
44
|
+
* 2026-04-01). Forwarded verbatim by the zlib
|
|
45
|
+
* block in node-shims.ts; results are the host
|
|
46
|
+
* realm's own Buffers, which the widened
|
|
47
|
+
* __BufferMod.isBuffer recognizes.
|
|
41
48
|
*/
|
|
42
49
|
export function getRealNodeImportsCode() {
|
|
43
50
|
return `
|
|
@@ -48,5 +55,6 @@ import * as __real_diagnostics_channel from 'node:diagnostics_channel';
|
|
|
48
55
|
import * as __real_repl from 'node:repl';
|
|
49
56
|
import * as __real_vm from 'node:vm';
|
|
50
57
|
import * as __real_inspector from 'node:inspector';
|
|
58
|
+
import * as __real_zlib from 'node:zlib';
|
|
51
59
|
`.trim();
|
|
52
60
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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,11 +7,30 @@
|
|
|
7
7
|
* into a string the loader pool injects into dynamic workers, where an import
|
|
8
8
|
* would not resolve.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
*/
|
|
13
|
+
export interface TarballWriteTarget {
|
|
14
|
+
exists(path: string): boolean;
|
|
15
|
+
mkdir(path: string, options?: {
|
|
16
|
+
recursive?: boolean;
|
|
17
|
+
}): void;
|
|
18
|
+
writeFile(path: string, data: Uint8Array | string): unknown;
|
|
19
|
+
}
|
|
20
|
+
export interface TarballWriteResult {
|
|
21
|
+
/** Regular files written, including the manifest. */
|
|
22
|
+
files: number;
|
|
23
|
+
/** Total decompressed bytes written. */
|
|
24
|
+
bytes: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Stream a gzipped npm archive into a package directory. Entry names are
|
|
28
|
+
* already canonical and prefix-stripped by streamTarEntries. Hold only the
|
|
29
|
+
* current entry and the manifest; write the manifest last so a failed install
|
|
30
|
+
* is not mistaken for a complete package on retry. A second manifest in one
|
|
31
|
+
* archive is a malformed package, not an overwrite. Filesystem failures reject.
|
|
32
|
+
*/
|
|
33
|
+
export declare function writeTarballStream(body: ReadableStream<Uint8Array>, targetDir: string, vfs: TarballWriteTarget): Promise<TarballWriteResult>;
|
|
15
34
|
/** Extract every regular file. Gzipped input is decompressed first. */
|
|
16
35
|
export declare function extractTarball(tarball: ArrayBuffer): Promise<Map<string, Uint8Array>>;
|
|
17
36
|
//# sourceMappingURL=tarball.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tarball.d.ts","sourceRoot":"","sources":["../../src/_shared/tarball.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"tarball.d.ts","sourceRoot":"","sources":["../../src/_shared/tarball.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAC7D,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;CAC7D;AAED,MAAM,WAAW,kBAAkB;IACjC,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;CACf;AAID;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,EAChC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,kBAAkB,GACtB,OAAO,CAAC,kBAAkB,CAAC,CA6B7B;AAED,uEAAuE;AACvE,wBAAsB,cAAc,CAClC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAsBlC"}
|
package/dist/_shared/tarball.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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,12 +7,49 @@
|
|
|
7
7
|
* into a string the loader pool injects into dynamic workers, where an import
|
|
8
8
|
* would not resolve.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
import { streamTarEntries, readableStreamToAsyncIterable, } from './tarball-stream.js';
|
|
14
|
+
const PACKAGE_MANIFEST = 'package.json';
|
|
15
|
+
/**
|
|
16
|
+
* Stream a gzipped npm archive into a package directory. Entry names are
|
|
17
|
+
* already canonical and prefix-stripped by streamTarEntries. Hold only the
|
|
18
|
+
* current entry and the manifest; write the manifest last so a failed install
|
|
19
|
+
* is not mistaken for a complete package on retry. A second manifest in one
|
|
20
|
+
* archive is a malformed package, not an overwrite. Filesystem failures reject.
|
|
21
|
+
*/
|
|
22
|
+
export async function writeTarballStream(body, targetDir, vfs) {
|
|
23
|
+
const ensureDir = (path) => {
|
|
24
|
+
if (!vfs.exists(path))
|
|
25
|
+
vfs.mkdir(path, { recursive: true });
|
|
26
|
+
};
|
|
27
|
+
ensureDir(targetDir);
|
|
28
|
+
let files = 0;
|
|
29
|
+
let bytes = 0;
|
|
30
|
+
let manifest = null;
|
|
31
|
+
const entries = streamTarEntries(readableStreamToAsyncIterable(body.pipeThrough(new DecompressionStream('gzip'))));
|
|
32
|
+
for await (const entry of entries) {
|
|
33
|
+
if (entry.name === PACKAGE_MANIFEST) {
|
|
34
|
+
if (manifest !== null) {
|
|
35
|
+
throw new Error(`tarball for ${targetDir} carried two ${PACKAGE_MANIFEST} entries`);
|
|
36
|
+
}
|
|
37
|
+
manifest = entry.data;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const fullPath = `${targetDir}/${entry.name}`;
|
|
41
|
+
const cut = fullPath.lastIndexOf('/');
|
|
42
|
+
if (cut > 0)
|
|
43
|
+
ensureDir(fullPath.slice(0, cut));
|
|
44
|
+
await vfs.writeFile(fullPath, entry.data);
|
|
45
|
+
files++;
|
|
46
|
+
bytes += entry.data.length;
|
|
47
|
+
}
|
|
48
|
+
if (!manifest)
|
|
49
|
+
throw new Error(`tarball for ${targetDir} carried no ${PACKAGE_MANIFEST}`);
|
|
50
|
+
await vfs.writeFile(`${targetDir}/${PACKAGE_MANIFEST}`, manifest);
|
|
51
|
+
return { files: files + 1, bytes: bytes + manifest.length };
|
|
52
|
+
}
|
|
16
53
|
/** Extract every regular file. Gzipped input is decompressed first. */
|
|
17
54
|
export async function extractTarball(tarball) {
|
|
18
55
|
const files = new Map();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vfs-write-ledger.d.ts","sourceRoot":"","sources":["../../src/_shared/vfs-write-ledger.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,+BAA+B,
|
|
1
|
+
{"version":3,"file":"vfs-write-ledger.d.ts","sourceRoot":"","sources":["../../src/_shared/vfs-write-ledger.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,+BAA+B,QA2ZpC,CAAC;AAET,eAAO,MAAM,uBAAuB,QAgC5B,CAAC"}
|
|
@@ -317,26 +317,32 @@ async function __nimbusPersistVfsWrite(supervisor, path, content, snapshot) {
|
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
/**
|
|
320
|
-
* Write back
|
|
320
|
+
* Write back the cells parked at THIS instant, and only those.
|
|
321
321
|
*
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
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
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
/**
|
|
@@ -1,37 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* One JavaScript source scanner shared by the two comment-stripping
|
|
3
|
+
* call sites — prefetch's import detection and the esbuild transform
|
|
4
|
+
* pipeline's classifiers.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* comment
|
|
7
|
-
*
|
|
8
|
-
* `
|
|
9
|
-
* pipeline).
|
|
6
|
+
* `scanJsSource(src, literals)` walks `src` once and returns a
|
|
7
|
+
* byte-aligned copy in which `//` and `/* … *\/` comments are blanked
|
|
8
|
+
* (each comment becomes a space; newlines inside a block comment are
|
|
9
|
+
* preserved so line numbers still match the input). String, template
|
|
10
|
+
* and regex literals are what `literals` decides:
|
|
10
11
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* - `'blank'`: literal content is replaced too — templates keep their
|
|
13
|
+
* `${…}` interpolation expression as code so a depth-tracked caller
|
|
14
|
+
* sees `await` etc. inside it. This is the transform pipeline's
|
|
15
|
+
* classification view: nothing quoted can read as an `import`.
|
|
16
|
+
* - `'keep'`: literals are copied verbatim. This is prefetch's
|
|
17
|
+
* import-detection view: IMPORT_RE/REQUIRE_RE must see the
|
|
18
|
+
* specifier string, and a `//` or `/*` inside a literal must NOT
|
|
19
|
+
* open a comment that swallows a following real import.
|
|
17
20
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* The output is assembled from input slices and joined once. It used to
|
|
22
|
+
* be built one character at a time (`stripped += c`), which V8 keeps as
|
|
23
|
+
* a rope of one node per append — ~30 bytes of heap per character, all
|
|
24
|
+
* live until the string is first read. On typescript's 6.15 MB
|
|
25
|
+
* `lib/_tsc.js` that rope measured 174 MB against a 128 MB isolate —
|
|
26
|
+
* the session Durable Object was killed inside `tsc`'s spawn before the
|
|
27
|
+
* facet existed. Spans hold the input, the output and a short array.
|
|
23
28
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* same logical pass over comments; only the string/regex-literal
|
|
34
|
-
* handling differs.
|
|
29
|
+
* Serialized by name: `generateEsbuildTransformRuntimeSource` embeds
|
|
30
|
+
* this function's `.toString()` in the transform runtime, so every
|
|
31
|
+
* constant it reads is declared inside the body.
|
|
32
|
+
*/
|
|
33
|
+
export declare function scanJsSource(src: string, literals: 'keep' | 'blank'): string;
|
|
34
|
+
/**
|
|
35
|
+
* Strip comments for import/require detection. Literals are kept: the
|
|
36
|
+
* specifier the regexes extract lives inside a string, and a comment
|
|
37
|
+
* marker inside a string must not swallow the code that follows it.
|
|
35
38
|
*/
|
|
36
39
|
export declare function stripCommentsForImports(src: string): string;
|
|
37
40
|
//# sourceMappingURL=comment-strip.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comment-strip.d.ts","sourceRoot":"","sources":["../../src/runtime/comment-strip.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"comment-strip.d.ts","sourceRoot":"","sources":["../../src/runtime/comment-strip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAqM5E;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3D"}
|