@nimbus-sh/core 0.3.0 → 0.4.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/LICENSE +21 -0
- package/README.md +112 -0
- package/dist/_shared/tarball-stream.d.ts +93 -0
- package/dist/_shared/tarball-stream.d.ts.map +1 -0
- package/dist/_shared/tarball-stream.js +235 -0
- package/dist/_shared/tarball.d.ts +17 -0
- package/dist/_shared/tarball.d.ts.map +1 -0
- package/dist/_shared/tarball.js +39 -0
- package/dist/runtime/clang-runner.d.ts +38 -0
- package/dist/runtime/clang-runner.d.ts.map +1 -0
- package/dist/runtime/clang-runner.js +866 -0
- package/dist/runtime/facet-host.d.ts +12 -0
- package/dist/runtime/facet-host.d.ts.map +1 -1
- package/dist/runtime/local-facet-host.d.ts.map +1 -1
- package/dist/runtime/local-facet-host.js +29 -9
- package/dist/runtime/ruby-gems.d.ts +30 -0
- package/dist/runtime/ruby-gems.d.ts.map +1 -0
- package/dist/runtime/ruby-gems.js +636 -0
- package/dist/runtime/ruby-runner.d.ts +127 -0
- package/dist/runtime/ruby-runner.d.ts.map +1 -0
- package/dist/runtime/ruby-runner.js +1357 -0
- package/dist/runtime/session-process-supervisor.d.ts +15 -0
- package/dist/runtime/session-process-supervisor.d.ts.map +1 -1
- package/dist/runtime/session-process-supervisor.js +30 -0
- package/dist/workspace/nimbus-workspace.d.ts.map +1 -1
- package/dist/workspace/nimbus-workspace.js +9 -2
- package/package.json +4 -2
- package/src/_shared/tarball-stream.ts +263 -0
- package/src/_shared/tarball.ts +46 -0
- package/src/runtime/clang-runner.ts +924 -0
- package/src/runtime/facet-host.ts +12 -0
- package/src/runtime/local-facet-host.ts +28 -8
- package/src/runtime/ruby-gems.ts +682 -0
- package/src/runtime/ruby-runner.ts +1484 -0
- package/src/runtime/session-process-supervisor.ts +27 -0
- package/src/workspace/nimbus-workspace.ts +10 -1
|
@@ -55,6 +55,8 @@ export declare class SessionProcessSupervisor {
|
|
|
55
55
|
private terminators;
|
|
56
56
|
/** Fires after every appendOutput/markExit once log persistence is wired. */
|
|
57
57
|
private logActivity;
|
|
58
|
+
/** Fires once per pid on its first terminal transition; see setOnTerminal. */
|
|
59
|
+
private onTerminalCb;
|
|
58
60
|
/** Allocate a PID and register a new process. */
|
|
59
61
|
spawn(command: string, argv: string[], cwd: string, opts?: ProcessSpawnOptions): ProcessEntry;
|
|
60
62
|
/** Mark an existing entry as long-running. Idempotent. */
|
|
@@ -76,6 +78,19 @@ export declare class SessionProcessSupervisor {
|
|
|
76
78
|
private terminate;
|
|
77
79
|
cred(pid: number): VfsCred;
|
|
78
80
|
setUmask(pid: number, umask: number): number;
|
|
81
|
+
/**
|
|
82
|
+
* Observe every pid's FIRST transition out of `running`, whichever door it
|
|
83
|
+
* leaves by — exit(), kill(), a facet's self-reported exit, a timeout abort:
|
|
84
|
+
* all of them end here, which is what makes this one callback a complete
|
|
85
|
+
* seam for per-pid durable state (the resident-launch journal) that must be
|
|
86
|
+
* released exactly when the process ends and never before.
|
|
87
|
+
*
|
|
88
|
+
* One slot, owned by the FacetManager. A second subscriber would mean two
|
|
89
|
+
* owners of process-end policy; grow this into a list only when a second
|
|
90
|
+
* genuine owner exists.
|
|
91
|
+
*/
|
|
92
|
+
setOnTerminal(cb: (pid: number) => void): void;
|
|
93
|
+
private fireTerminal;
|
|
79
94
|
/** Mark a process as exited. First terminal state wins. */
|
|
80
95
|
exit(pid: number, exitCode: number): void;
|
|
81
96
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-process-supervisor.d.ts","sourceRoot":"","sources":["../../src/runtime/session-process-supervisor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAqB,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,EACL,eAAe,EACf,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,WAAW,mBAAmB;IAClC,sFAAsF;IACtF,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,wFAAwF;IACxF,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,wEAAwE;IACxE,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2B;IACjD,OAAO,CAAC,IAAI,CAAyB;IACrC,2EAA2E;IAC3E,OAAO,CAAC,WAAW,CAAiC;IACpD,6EAA6E;IAC7E,OAAO,CAAC,WAAW,CAA6B;
|
|
1
|
+
{"version":3,"file":"session-process-supervisor.d.ts","sourceRoot":"","sources":["../../src/runtime/session-process-supervisor.ts"],"names":[],"mappings":"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"}
|
|
@@ -30,6 +30,8 @@ export class SessionProcessSupervisor {
|
|
|
30
30
|
terminators = new Map();
|
|
31
31
|
/** Fires after every appendOutput/markExit once log persistence is wired. */
|
|
32
32
|
logActivity = null;
|
|
33
|
+
/** Fires once per pid on its first terminal transition; see setOnTerminal. */
|
|
34
|
+
onTerminalCb = null;
|
|
33
35
|
// ── Lifecycle / PID authority ─────────────────────────────────────────
|
|
34
36
|
/** Allocate a PID and register a new process. */
|
|
35
37
|
spawn(command, argv, cwd, opts = {}) {
|
|
@@ -86,19 +88,47 @@ export class SessionProcessSupervisor {
|
|
|
86
88
|
setUmask(pid, umask) {
|
|
87
89
|
return this.table.setUmask(pid, umask);
|
|
88
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Observe every pid's FIRST transition out of `running`, whichever door it
|
|
93
|
+
* leaves by — exit(), kill(), a facet's self-reported exit, a timeout abort:
|
|
94
|
+
* all of them end here, which is what makes this one callback a complete
|
|
95
|
+
* seam for per-pid durable state (the resident-launch journal) that must be
|
|
96
|
+
* released exactly when the process ends and never before.
|
|
97
|
+
*
|
|
98
|
+
* One slot, owned by the FacetManager. A second subscriber would mean two
|
|
99
|
+
* owners of process-end policy; grow this into a list only when a second
|
|
100
|
+
* genuine owner exists.
|
|
101
|
+
*/
|
|
102
|
+
setOnTerminal(cb) {
|
|
103
|
+
this.onTerminalCb = cb;
|
|
104
|
+
}
|
|
105
|
+
fireTerminal(pid, wasRunning) {
|
|
106
|
+
if (!wasRunning || !this.onTerminalCb)
|
|
107
|
+
return;
|
|
108
|
+
if (this.table.get(pid)?.state === 'running')
|
|
109
|
+
return;
|
|
110
|
+
try {
|
|
111
|
+
this.onTerminalCb(pid);
|
|
112
|
+
}
|
|
113
|
+
catch { /* the process is gone regardless */ }
|
|
114
|
+
}
|
|
89
115
|
/** Mark a process as exited. First terminal state wins. */
|
|
90
116
|
exit(pid, exitCode) {
|
|
117
|
+
const wasRunning = this.table.get(pid)?.state === 'running';
|
|
91
118
|
this.table.exit(pid, exitCode);
|
|
92
119
|
this.terminators.delete(pid);
|
|
120
|
+
this.fireTerminal(pid, wasRunning);
|
|
93
121
|
}
|
|
94
122
|
/**
|
|
95
123
|
* Mark a process as killed and tear down its input channel so queued
|
|
96
124
|
* stdin can't outlive the process.
|
|
97
125
|
*/
|
|
98
126
|
kill(pid) {
|
|
127
|
+
const wasRunning = this.table.get(pid)?.state === 'running';
|
|
99
128
|
const killed = this.table.kill(pid);
|
|
100
129
|
this.terminate(pid);
|
|
101
130
|
this.input.close(pid);
|
|
131
|
+
this.fireTerminal(pid, wasRunning);
|
|
102
132
|
return killed;
|
|
103
133
|
}
|
|
104
134
|
/** Clean up exited processes older than maxAge ms. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nimbus-workspace.d.ts","sourceRoot":"","sources":["../../src/workspace/nimbus-workspace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAC3D,OAAO,EAAE,KAAK,EAAE,MAAM,kCAAkC,CAAC;AACzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAE7E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAI9E,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAC/F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,SAAS,EAAqB,MAAM,sBAAsB,CAAC;AAMpE,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAG/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAK1D,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAKxF,MAAM,WAAW,sBAAsB;IACrC,iEAAiE;IACjE,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,eAAe,CAAC;IACxC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,kDAAkD;IAClD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IACzC;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IAC9C;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;CAC7B;AAED;;;;;;;;;;GAUG;AACH,qBAAa,eAAe;IA4BxB,OAAO,CAAC,QAAQ,CAAC,GAAG;IA3BtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,4EAA4E;IAC5E,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAErC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAE/C,OAAO;WAoBM,MAAM,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC;IAwD9E,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;IAInE;;;;;;OAMG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;;;;;;OAMG;IACH,KAAK,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAK3D;;;;;;;OAOG;IACH,OAAO,IAAI,IAAI;CAKhB;
|
|
1
|
+
{"version":3,"file":"nimbus-workspace.d.ts","sourceRoot":"","sources":["../../src/workspace/nimbus-workspace.ts"],"names":[],"mappings":"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"}
|
|
@@ -220,9 +220,11 @@ function defaultEnv() {
|
|
|
220
220
|
* them.
|
|
221
221
|
*/
|
|
222
222
|
async function registerWasmRuntimes(deps) {
|
|
223
|
-
const [{ makeBashRunnerFactory }, { makeCPythonRunnerFactory }, { wasmRunnerSpec }, { buildRuntimeHandler },] = await Promise.all([
|
|
223
|
+
const [{ makeBashRunnerFactory }, { makeCPythonRunnerFactory }, { makeRubyRunnerFactory }, { makeClangRunnerFactory }, { wasmRunnerSpec }, { buildRuntimeHandler },] = await Promise.all([
|
|
224
224
|
import('../runtime/bash-runner.js'),
|
|
225
225
|
import('../runtime/cpython-runner.js'),
|
|
226
|
+
import('../runtime/ruby-runner.js'),
|
|
227
|
+
import('../runtime/clang-runner.js'),
|
|
226
228
|
import('../runtime/wasm-runner.js'),
|
|
227
229
|
import('../runtime/runtime-registry.js'),
|
|
228
230
|
]);
|
|
@@ -251,8 +253,13 @@ async function registerWasmRuntimes(deps) {
|
|
|
251
253
|
'bash-runner': makeBashRunnerFactory({ facets: deps.facets, vfs: deps.vfs }),
|
|
252
254
|
// No `startResident`: a workspace owns no actor that could outlive the
|
|
253
255
|
// call, so a program that keeps serving is refused by name rather than
|
|
254
|
-
// run as a one-shot that dies with it.
|
|
256
|
+
// run as a one-shot that dies with it. Same for ruby, where a script is
|
|
257
|
+
// the shape that may bind a port.
|
|
255
258
|
'cpython-runner': makeCPythonRunnerFactory({ facets: deps.facets, vfs: deps.vfs }),
|
|
259
|
+
'ruby-runner': makeRubyRunnerFactory({
|
|
260
|
+
facets: deps.facets, vfs: deps.vfs, registry: deps.registry,
|
|
261
|
+
}),
|
|
262
|
+
'clang-runner': makeClangRunnerFactory({ facets: deps.facets, vfs: deps.vfs }),
|
|
256
263
|
};
|
|
257
264
|
rehydrateInstalledRuntimesView(deps.vfs.as(CRED_KERNEL), deps.registry, deps.home, (key) => runners[key]);
|
|
258
265
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nimbus-sh/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Backend-agnostic half of Nimbus \u2014 the durable filesystem, the shell, and the OS/process contracts, over a host-supplied SQL port.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"filesystem",
|
|
@@ -45,7 +45,9 @@
|
|
|
45
45
|
},
|
|
46
46
|
"files": [
|
|
47
47
|
"dist",
|
|
48
|
-
"src"
|
|
48
|
+
"src",
|
|
49
|
+
"README.md",
|
|
50
|
+
"LICENSE"
|
|
49
51
|
],
|
|
50
52
|
"scripts": {
|
|
51
53
|
"build": "tsc -p tsconfig.json --noCheck --noEmit false --declaration true --declarationMap true --outDir dist --rootDir src",
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tarball-stream.ts — pure streaming tar primitives.
|
|
3
|
+
*
|
|
4
|
+
* A leaf with no imports at all, deliberately: `bundle-facet-workers.mjs`
|
|
5
|
+
* esbuilds this file into a string constant the loader pool injects into
|
|
6
|
+
* dynamic workers, and a facet isolate resolves no specifier. Anything this
|
|
7
|
+
* file imported would have to travel with it.
|
|
8
|
+
*
|
|
9
|
+
* Zero dependencies. Works identically on the supervisor and inside a
|
|
10
|
+
* facet isolate. Never buffers the full decompressed tarball — peak
|
|
11
|
+
* transient heap is one file's bytes plus a 512-byte carry.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Maximum size of a single file inside a tarball. Larger entries are skipped.
|
|
16
|
+
*
|
|
17
|
+
* History: 5 MB was too low — it silently dropped `esbuild-wasm/esbuild.wasm`
|
|
18
|
+
* (11.35 MB on v0.24.2), which made Nimbus-in-Nimbus `npm run dev` fail with
|
|
19
|
+
* `No such module "esbuild-wasm/esbuild.wasm"` since the missing file caused
|
|
20
|
+
* esbuild's VFS plugin to mark the import `external`, and workerd's LOADER
|
|
21
|
+
* has no entry for that specifier. 20 MB covers esbuild-wasm with headroom
|
|
22
|
+
* while keeping per-facet peak heap bounded for the streaming extractor.
|
|
23
|
+
*/
|
|
24
|
+
export const MAX_FILE_BYTES = 20_000_000;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Read one tar header (USTAR) out of `block`. Returns parsed fields or
|
|
28
|
+
* `null` for an end-of-archive block (all zeros).
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Collapse "."/".." segments in a tar entry's package-relative path.
|
|
32
|
+
* Returns the canonical relative path, or '' when the entry escapes its
|
|
33
|
+
* package root (a leading ".." that pops above the root) — the caller
|
|
34
|
+
* treats '' as a no-name entry and skips it. Mirrors the segment logic in
|
|
35
|
+
* w7-frame's canonicalPath so joined write paths are always accepted.
|
|
36
|
+
*/
|
|
37
|
+
export function canonicalTarName(name: string): string {
|
|
38
|
+
const out: string[] = [];
|
|
39
|
+
for (const seg of name.split('/')) {
|
|
40
|
+
if (seg === '..') {
|
|
41
|
+
if (out.length === 0) return '';
|
|
42
|
+
out.pop();
|
|
43
|
+
} else if (seg !== '' && seg !== '.') {
|
|
44
|
+
out.push(seg);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return out.join('/');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function parseTarHeader(block: Uint8Array): { name: string; size: number; typeFlag: number } | null {
|
|
51
|
+
if (block[0] === 0) return null;
|
|
52
|
+
|
|
53
|
+
let name = '';
|
|
54
|
+
for (let i = 0; i < 100 && block[i] !== 0; i++) {
|
|
55
|
+
name += String.fromCharCode(block[i]);
|
|
56
|
+
}
|
|
57
|
+
let prefix = '';
|
|
58
|
+
for (let i = 345; i < 500 && block[i] !== 0; i++) {
|
|
59
|
+
prefix += String.fromCharCode(block[i]);
|
|
60
|
+
}
|
|
61
|
+
if (prefix) name = prefix + '/' + name;
|
|
62
|
+
// Strip the npm `package/` convention.
|
|
63
|
+
name = name.replace(/^package\//, '');
|
|
64
|
+
// Canonicalize the entry-relative path. npm tarballs legitimately carry
|
|
65
|
+
// entries like "./dist/index.js" (agent-base, http-proxy-agent,
|
|
66
|
+
// protobufjs, ...); left as-is the "./" survives into the VFS write path
|
|
67
|
+
// and the w7-frame writer rejects the noncanonical path, failing the
|
|
68
|
+
// whole shared install wave and dropping shard-mates' completion markers.
|
|
69
|
+
// Collapsing here (the one place entry names are assembled) keeps every
|
|
70
|
+
// downstream join canonical. An entry that escapes its package root via
|
|
71
|
+
// ".." is dropped to '' → skipped as a no-name entry.
|
|
72
|
+
name = canonicalTarName(name);
|
|
73
|
+
|
|
74
|
+
let sizeStr = '';
|
|
75
|
+
for (let i = 124; i < 136 && block[i] !== 0; i++) {
|
|
76
|
+
sizeStr += String.fromCharCode(block[i]);
|
|
77
|
+
}
|
|
78
|
+
const size = parseInt(sizeStr.trim(), 8) || 0;
|
|
79
|
+
const typeFlag = block[156];
|
|
80
|
+
return { name, size, typeFlag };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Wrap a `ReadableStream<Uint8Array>` as an async iterable. Workerd and
|
|
85
|
+
* Node both support `Symbol.asyncIterator` on ReadableStream, but we
|
|
86
|
+
* spell the reader loop out so we don't depend on ambient lib typings.
|
|
87
|
+
*/
|
|
88
|
+
export async function* readableStreamToAsyncIterable(
|
|
89
|
+
rs: ReadableStream<Uint8Array>,
|
|
90
|
+
): AsyncGenerator<Uint8Array, void, undefined> {
|
|
91
|
+
const reader = rs.getReader();
|
|
92
|
+
try {
|
|
93
|
+
while (true) {
|
|
94
|
+
const { value, done } = await reader.read();
|
|
95
|
+
if (done) return;
|
|
96
|
+
if (value && value.length > 0) yield value;
|
|
97
|
+
}
|
|
98
|
+
} finally {
|
|
99
|
+
try { reader.releaseLock(); } catch { /* ignore */ }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Reason a tar entry was skipped and never yielded. Surfaced to callers
|
|
105
|
+
* via the optional `onSkip` callback on `streamTarEntries`.
|
|
106
|
+
*
|
|
107
|
+
* - 'too-large': size > MAX_FILE_BYTES. The most common reason Nimbus
|
|
108
|
+
* cares about — it's what caused esbuild-wasm/esbuild.wasm to vanish
|
|
109
|
+
* silently in Nimbus-in-Nimbus before the cap was raised.
|
|
110
|
+
* - 'non-regular': typeFlag indicates a symlink / hardlink / directory /
|
|
111
|
+
* PaxHeader / GNU LongName etc. These aren't files we stage.
|
|
112
|
+
* - 'no-name': header parsed but name was empty (malformed or a PaxHeader
|
|
113
|
+
* that our parser didn't recognize as non-regular).
|
|
114
|
+
*/
|
|
115
|
+
export type TarSkipReason = 'too-large' | 'non-regular' | 'no-name';
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Optional skip-observer passed to `streamTarEntries`. Called ONCE per
|
|
119
|
+
* skipped entry, with the declared name (may be empty for 'no-name'
|
|
120
|
+
* skips) and the declared size in bytes.
|
|
121
|
+
*
|
|
122
|
+
* Consumers typically push these into a per-package warnings array so
|
|
123
|
+
* users see what wasn't installed. The callback is synchronous and
|
|
124
|
+
* must not throw — thrown errors are swallowed to keep the extractor
|
|
125
|
+
* best-effort.
|
|
126
|
+
*/
|
|
127
|
+
export type TarSkipCallback = (
|
|
128
|
+
name: string,
|
|
129
|
+
size: number,
|
|
130
|
+
reason: TarSkipReason,
|
|
131
|
+
) => void;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Streaming tar extractor.
|
|
135
|
+
*
|
|
136
|
+
* Consumes an async iterable of Uint8Array chunks (the decompressed tar
|
|
137
|
+
* byte stream) and yields one `{ name, data }` entry per regular file,
|
|
138
|
+
* as each file completes.
|
|
139
|
+
*
|
|
140
|
+
* Memory invariant: holds at most one pending file's bytes (≤ MAX_FILE_BYTES)
|
|
141
|
+
* plus a small carry buffer for the tar header being assembled.
|
|
142
|
+
*
|
|
143
|
+
* Skips: symlinks, directories, hardlinks, long-name extensions (PaxHeader),
|
|
144
|
+
* and any file whose declared size exceeds MAX_FILE_BYTES.
|
|
145
|
+
*
|
|
146
|
+
* If `onSkip` is provided, it is invoked for each skipped entry with the
|
|
147
|
+
* name, declared size, and reason code. Callers that need to surface
|
|
148
|
+
* dropped-file warnings to users should pass one; legacy callers that
|
|
149
|
+
* omit the arg still behave exactly as before (silent skip).
|
|
150
|
+
*/
|
|
151
|
+
export async function* streamTarEntries(
|
|
152
|
+
source: AsyncIterable<Uint8Array>,
|
|
153
|
+
onSkip?: TarSkipCallback,
|
|
154
|
+
): AsyncGenerator<{ name: string; data: Uint8Array }, void, undefined> {
|
|
155
|
+
let carry: Uint8Array<ArrayBufferLike> = new Uint8Array(0);
|
|
156
|
+
|
|
157
|
+
type State =
|
|
158
|
+
| { kind: 'header' }
|
|
159
|
+
| { kind: 'file'; name: string; remaining: number; fileBuf: Uint8Array; fileOffset: number; pad: number; skip: boolean }
|
|
160
|
+
| { kind: 'skip'; remaining: number };
|
|
161
|
+
let state: State = { kind: 'header' };
|
|
162
|
+
|
|
163
|
+
function concat(a: Uint8Array, b: Uint8Array): Uint8Array {
|
|
164
|
+
if (a.length === 0) return b;
|
|
165
|
+
if (b.length === 0) return a;
|
|
166
|
+
const out = new Uint8Array(a.length + b.length);
|
|
167
|
+
out.set(a, 0);
|
|
168
|
+
out.set(b, a.length);
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
for await (const chunkRaw of source) {
|
|
173
|
+
let buf = concat(carry, chunkRaw);
|
|
174
|
+
let cursor = 0;
|
|
175
|
+
|
|
176
|
+
while (true) {
|
|
177
|
+
if (state.kind === 'header') {
|
|
178
|
+
if (buf.length - cursor < 512) break;
|
|
179
|
+
const header = buf.subarray(cursor, cursor + 512);
|
|
180
|
+
const parsed = parseTarHeader(header);
|
|
181
|
+
cursor += 512;
|
|
182
|
+
if (!parsed) return; // end-of-archive
|
|
183
|
+
const { name, size, typeFlag } = parsed;
|
|
184
|
+
const pad = size === 0 ? 0 : (512 - (size % 512)) % 512;
|
|
185
|
+
const isRegularFile = (typeFlag === 48 /* '0' */ || typeFlag === 0);
|
|
186
|
+
if (size === 0) {
|
|
187
|
+
if (isRegularFile && name) {
|
|
188
|
+
yield { name, data: new Uint8Array(0) };
|
|
189
|
+
}
|
|
190
|
+
state = { kind: 'header' };
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (!isRegularFile || !name || size > MAX_FILE_BYTES) {
|
|
194
|
+
if (onSkip) {
|
|
195
|
+
// Classify before entering the skip state so the reason is
|
|
196
|
+
// exact. Precedence matches the condition order above:
|
|
197
|
+
// non-regular first (directories/symlinks/PaxHeaders are
|
|
198
|
+
// skipped regardless of size), then no-name, then too-large.
|
|
199
|
+
const reason: TarSkipReason = !isRegularFile
|
|
200
|
+
? 'non-regular'
|
|
201
|
+
: !name
|
|
202
|
+
? 'no-name'
|
|
203
|
+
: 'too-large';
|
|
204
|
+
try { onSkip(name, size, reason); } catch { /* best-effort */ }
|
|
205
|
+
}
|
|
206
|
+
state = { kind: 'skip', remaining: size + pad };
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
state = {
|
|
210
|
+
kind: 'file',
|
|
211
|
+
name,
|
|
212
|
+
remaining: size,
|
|
213
|
+
fileBuf: new Uint8Array(size),
|
|
214
|
+
fileOffset: 0,
|
|
215
|
+
pad,
|
|
216
|
+
skip: false,
|
|
217
|
+
};
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (state.kind === 'file') {
|
|
222
|
+
const avail = buf.length - cursor;
|
|
223
|
+
if (avail === 0) break;
|
|
224
|
+
if (state.remaining > 0) {
|
|
225
|
+
const take = Math.min(state.remaining, avail);
|
|
226
|
+
state.fileBuf.set(buf.subarray(cursor, cursor + take), state.fileOffset);
|
|
227
|
+
state.fileOffset += take;
|
|
228
|
+
state.remaining -= take;
|
|
229
|
+
cursor += take;
|
|
230
|
+
if (state.remaining > 0) break;
|
|
231
|
+
}
|
|
232
|
+
if (state.pad > 0) {
|
|
233
|
+
const avail2 = buf.length - cursor;
|
|
234
|
+
if (avail2 === 0) break;
|
|
235
|
+
const take = Math.min(state.pad, avail2);
|
|
236
|
+
state.pad -= take;
|
|
237
|
+
cursor += take;
|
|
238
|
+
if (state.pad > 0) break;
|
|
239
|
+
}
|
|
240
|
+
yield { name: state.name, data: state.fileBuf };
|
|
241
|
+
state = { kind: 'header' };
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// state.kind === 'skip'
|
|
246
|
+
const avail = buf.length - cursor;
|
|
247
|
+
if (avail === 0) break;
|
|
248
|
+
const take = Math.min(state.remaining, avail);
|
|
249
|
+
cursor += take;
|
|
250
|
+
state.remaining -= take;
|
|
251
|
+
if (state.remaining > 0) break;
|
|
252
|
+
state = { kind: 'header' };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (cursor >= buf.length) {
|
|
256
|
+
carry = new Uint8Array(0);
|
|
257
|
+
} else if (cursor === 0) {
|
|
258
|
+
carry = buf;
|
|
259
|
+
} else {
|
|
260
|
+
carry = buf.slice(cursor);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tarball.ts — a whole tarball, in memory, as a path→bytes map.
|
|
3
|
+
*
|
|
4
|
+
* The streaming primitives it walks with (`parseTarHeader`, `streamTarEntries`,
|
|
5
|
+
* `readableStreamToAsyncIterable`) live in `./tarball-stream.ts` — a
|
|
6
|
+
* dependency-free leaf, because `bundle-facet-workers.mjs` esbuilds that file
|
|
7
|
+
* into a string the loader pool injects into dynamic workers, where an import
|
|
8
|
+
* would not resolve.
|
|
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.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
streamTarEntries,
|
|
18
|
+
readableStreamToAsyncIterable,
|
|
19
|
+
} from './tarball-stream.js';
|
|
20
|
+
|
|
21
|
+
/** Extract every regular file. Gzipped input is decompressed first. */
|
|
22
|
+
export async function extractTarball(
|
|
23
|
+
tarball: ArrayBuffer,
|
|
24
|
+
): Promise<Map<string, Uint8Array>> {
|
|
25
|
+
const files = new Map<string, Uint8Array>();
|
|
26
|
+
const raw = new Uint8Array(tarball);
|
|
27
|
+
|
|
28
|
+
// Adapter: wrap the single buffer as an async iterable. If gzipped, pipe
|
|
29
|
+
// through DecompressionStream so the streaming parser still sees tar bytes.
|
|
30
|
+
let source: AsyncIterable<Uint8Array>;
|
|
31
|
+
if (raw[0] === 0x1f && raw[1] === 0x8b) {
|
|
32
|
+
const rs = new Blob([tarball]).stream().pipeThrough(new DecompressionStream('gzip'));
|
|
33
|
+
source = readableStreamToAsyncIterable(rs);
|
|
34
|
+
} else {
|
|
35
|
+
source = (async function* () { yield raw; })();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
for await (const entry of streamTarEntries(source)) {
|
|
40
|
+
files.set(entry.name, entry.data);
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
return files;
|
|
44
|
+
}
|
|
45
|
+
return files;
|
|
46
|
+
}
|