sandboxedjs 0.1.28 → 0.1.30
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 +1 -7
- package/README.md +104 -7
- package/dist/index.cjs +1014 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +321 -8
- package/dist/index.d.ts +321 -8
- package/dist/index.js +1010 -54
- package/dist/index.js.map +1 -1
- package/dist/service-worker.js +112 -0
- package/dist/service-worker.js.map +1 -0
- package/dist/worker-entry.js +30825 -0
- package/dist/worker-entry.js.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17942,22 +17942,22 @@ async function runCPythonProgram(ctx, source, argv, scriptDir, stdinText) {
|
|
|
17942
17942
|
});
|
|
17943
17943
|
} catch {
|
|
17944
17944
|
}
|
|
17945
|
-
const
|
|
17946
|
-
const
|
|
17945
|
+
const encoder8 = new TextEncoder();
|
|
17946
|
+
const decoder8 = new TextDecoder();
|
|
17947
17947
|
py.setStdout({
|
|
17948
17948
|
write: (buffer) => {
|
|
17949
|
-
ctx.write(
|
|
17949
|
+
ctx.write(decoder8.decode(buffer));
|
|
17950
17950
|
return buffer.length;
|
|
17951
17951
|
}
|
|
17952
17952
|
});
|
|
17953
17953
|
py.setStderr({
|
|
17954
17954
|
write: (buffer) => {
|
|
17955
|
-
ctx.stderr.write(
|
|
17955
|
+
ctx.stderr.write(decoder8.decode(buffer));
|
|
17956
17956
|
return buffer.length;
|
|
17957
17957
|
}
|
|
17958
17958
|
});
|
|
17959
17959
|
if (stdinText !== null) {
|
|
17960
|
-
const bytes2 =
|
|
17960
|
+
const bytes2 = encoder8.encode(stdinText);
|
|
17961
17961
|
let offset = 0;
|
|
17962
17962
|
py.setStdin({
|
|
17963
17963
|
read: (buffer) => {
|
|
@@ -18008,9 +18008,9 @@ async function runCPythonRepl(ctx) {
|
|
|
18008
18008
|
}
|
|
18009
18009
|
mountContainerDirs(py, ctx);
|
|
18010
18010
|
bootstrap(py, ctx, [""], null);
|
|
18011
|
-
const
|
|
18012
|
-
py.setStdout({ write: (data) => (ctx.write(
|
|
18013
|
-
py.setStderr({ write: (data) => (ctx.stderr.write(
|
|
18011
|
+
const decoder8 = new TextDecoder();
|
|
18012
|
+
py.setStdout({ write: (data) => (ctx.write(decoder8.decode(data)), data.length) });
|
|
18013
|
+
py.setStderr({ write: (data) => (ctx.stderr.write(decoder8.decode(data)), data.length) });
|
|
18014
18014
|
ctx.line(`Python ${String(py.runPython("import sys; sys.version.split()[0]"))} (Pyodide)`);
|
|
18015
18015
|
ctx.line('Type "help()" for more information.');
|
|
18016
18016
|
let source = "";
|
|
@@ -19621,15 +19621,15 @@ var Session = class {
|
|
|
19621
19621
|
async run(command, opts = {}) {
|
|
19622
19622
|
if (this.closed) throw new Error("session is closed");
|
|
19623
19623
|
const combined = [];
|
|
19624
|
-
const
|
|
19624
|
+
const decoder8 = new TextDecoder();
|
|
19625
19625
|
const stdout = new BufferSink((chunk) => {
|
|
19626
|
-
const text2 =
|
|
19626
|
+
const text2 = decoder8.decode(chunk, { stream: true });
|
|
19627
19627
|
combined.push(text2);
|
|
19628
19628
|
opts.onStdout?.(text2);
|
|
19629
19629
|
this.hooks.onStdout?.(text2);
|
|
19630
19630
|
});
|
|
19631
19631
|
const stderr = new BufferSink((chunk) => {
|
|
19632
|
-
const text2 =
|
|
19632
|
+
const text2 = decoder8.decode(chunk, { stream: true });
|
|
19633
19633
|
combined.push(text2);
|
|
19634
19634
|
opts.onStderr?.(text2);
|
|
19635
19635
|
this.hooks.onStderr?.(text2);
|
|
@@ -19695,6 +19695,10 @@ var KernelChildProcess = class {
|
|
|
19695
19695
|
started = false;
|
|
19696
19696
|
cancelled = false;
|
|
19697
19697
|
constructor(kernel, cred, config, pid) {
|
|
19698
|
+
if (config.inheritStdio) {
|
|
19699
|
+
this.stdin.isTTY = true;
|
|
19700
|
+
this.stdin.interactive = true;
|
|
19701
|
+
}
|
|
19698
19702
|
this.kernel = kernel;
|
|
19699
19703
|
this.cred = cred;
|
|
19700
19704
|
this.pid = pid;
|
|
@@ -21815,7 +21819,7 @@ var VirtualClientRequest = class extends streamModule4.Writable {
|
|
|
21815
21819
|
};
|
|
21816
21820
|
let settled = false;
|
|
21817
21821
|
try {
|
|
21818
|
-
const response =
|
|
21822
|
+
const response = isLoopback(this.target.hostname) ? await this.viaRouter(body) : await this.viaFetch(body);
|
|
21819
21823
|
if (this.destroyedByUser) return;
|
|
21820
21824
|
clearTimeout(this.timer);
|
|
21821
21825
|
settled = true;
|
|
@@ -21837,6 +21841,12 @@ var VirtualClientRequest = class extends streamModule4.Writable {
|
|
|
21837
21841
|
}
|
|
21838
21842
|
}
|
|
21839
21843
|
async viaRouter(body) {
|
|
21844
|
+
if (!this.router.activePortsIncludes(this.target.port)) {
|
|
21845
|
+
throw Object.assign(
|
|
21846
|
+
new Error(`connect ECONNREFUSED ${this.target.hostname}:${this.target.port}`),
|
|
21847
|
+
{ code: "ECONNREFUSED", errno: -61, syscall: "connect", address: this.target.hostname, port: this.target.port }
|
|
21848
|
+
);
|
|
21849
|
+
}
|
|
21840
21850
|
const result = await this.router.request(this.target.port, {
|
|
21841
21851
|
method: this.target.method,
|
|
21842
21852
|
path: this.target.path,
|
|
@@ -22093,7 +22103,8 @@ var ChildProcess = class extends EventEmitter4 {
|
|
|
22093
22103
|
queueMicrotask(() => this.emit("close", code, null));
|
|
22094
22104
|
}
|
|
22095
22105
|
};
|
|
22096
|
-
function createChildProcessModule(spawnChild, defaultCwd) {
|
|
22106
|
+
function createChildProcessModule(spawnChild, defaultCwd, syncSpawn, defaultEnv = () => ({})) {
|
|
22107
|
+
const environmentFor = (options) => options.env ? { ...options.env } : defaultEnv();
|
|
22097
22108
|
const throughShell = (command, options) => {
|
|
22098
22109
|
const shell = typeof options.shell === "string" ? options.shell : "/bin/sh";
|
|
22099
22110
|
return { file: shell, args: ["-c", command] };
|
|
@@ -22104,7 +22115,7 @@ function createChildProcessModule(spawnChild, defaultCwd) {
|
|
|
22104
22115
|
command: resolved.file,
|
|
22105
22116
|
args: resolved.args,
|
|
22106
22117
|
cwd: options.cwd ?? defaultCwd(),
|
|
22107
|
-
|
|
22118
|
+
env: environmentFor(options)
|
|
22108
22119
|
});
|
|
22109
22120
|
return new ChildProcess(handle, resolved.file, resolved.args);
|
|
22110
22121
|
};
|
|
@@ -22150,20 +22161,81 @@ ${err.join("")}`),
|
|
|
22150
22161
|
exec,
|
|
22151
22162
|
execFile,
|
|
22152
22163
|
fork: (modulePath, args = [], options = {}) => spawn("node", [modulePath, ...args], options),
|
|
22153
|
-
|
|
22154
|
-
execFileSync: unavailable("execFileSync"),
|
|
22155
|
-
spawnSync: unavailable("spawnSync"),
|
|
22164
|
+
...buildSyncFamily(syncSpawn, throughShell, defaultCwd, environmentFor),
|
|
22156
22165
|
ChildProcess
|
|
22157
22166
|
};
|
|
22158
22167
|
}
|
|
22168
|
+
function buildSyncFamily(syncSpawn, throughShell, defaultCwd, environmentFor) {
|
|
22169
|
+
if (!syncSpawn) {
|
|
22170
|
+
return {
|
|
22171
|
+
execSync: unavailable("execSync"),
|
|
22172
|
+
execFileSync: unavailable("execFileSync"),
|
|
22173
|
+
spawnSync: unavailable("spawnSync")
|
|
22174
|
+
};
|
|
22175
|
+
}
|
|
22176
|
+
const run = (file3, args, options) => {
|
|
22177
|
+
const resolved = options.shell ? throughShell([file3, ...args].join(" "), options) : { file: file3, args };
|
|
22178
|
+
const input = options.input === void 0 ? void 0 : typeof options.input === "string" ? options.input : new TextDecoder().decode(options.input);
|
|
22179
|
+
const inherit = options.stdio === "inherit" || Array.isArray(options.stdio) && options.stdio[0] === "inherit";
|
|
22180
|
+
return syncSpawn({
|
|
22181
|
+
command: resolved.file,
|
|
22182
|
+
args: resolved.args,
|
|
22183
|
+
cwd: options.cwd ?? defaultCwd(),
|
|
22184
|
+
env: environmentFor(options),
|
|
22185
|
+
...input === void 0 ? {} : { input },
|
|
22186
|
+
...inherit ? { inheritStdio: true } : {}
|
|
22187
|
+
});
|
|
22188
|
+
};
|
|
22189
|
+
const asOutput = (text2, options) => options.encoding === "buffer" || options.encoding === void 0 ? Buffer2.from(text2) : text2;
|
|
22190
|
+
const orThrow = (result, command, options) => {
|
|
22191
|
+
if (result.error) {
|
|
22192
|
+
throw Object.assign(new Error(result.error.message), {
|
|
22193
|
+
...result.error.code ? { code: result.error.code } : {},
|
|
22194
|
+
stdout: asOutput(result.stdout, options),
|
|
22195
|
+
stderr: asOutput(result.stderr, options)
|
|
22196
|
+
});
|
|
22197
|
+
}
|
|
22198
|
+
if (result.status !== 0) {
|
|
22199
|
+
throw Object.assign(new Error(`Command failed: ${command}
|
|
22200
|
+
${result.stderr}`), {
|
|
22201
|
+
status: result.status,
|
|
22202
|
+
stdout: asOutput(result.stdout, options),
|
|
22203
|
+
stderr: asOutput(result.stderr, options)
|
|
22204
|
+
});
|
|
22205
|
+
}
|
|
22206
|
+
return asOutput(result.stdout, options);
|
|
22207
|
+
};
|
|
22208
|
+
const spawnSync = (file3, args = [], options = {}) => {
|
|
22209
|
+
const [list, opts] = Array.isArray(args) ? [args, options] : [[], args];
|
|
22210
|
+
const result = run(file3, list, opts);
|
|
22211
|
+
return {
|
|
22212
|
+
pid: 0,
|
|
22213
|
+
status: result.status,
|
|
22214
|
+
signal: result.signal,
|
|
22215
|
+
stdout: asOutput(result.stdout, opts),
|
|
22216
|
+
stderr: asOutput(result.stderr, opts),
|
|
22217
|
+
output: [null, asOutput(result.stdout, opts), asOutput(result.stderr, opts)],
|
|
22218
|
+
...result.error ? { error: Object.assign(new Error(result.error.message), result.error.code ? { code: result.error.code } : {}) } : {}
|
|
22219
|
+
};
|
|
22220
|
+
};
|
|
22221
|
+
const execFileSync = (file3, args = [], options = {}) => {
|
|
22222
|
+
const [list, opts] = Array.isArray(args) ? [args, options] : [[], args];
|
|
22223
|
+
return orThrow(run(file3, list, opts), [file3, ...list].join(" "), opts);
|
|
22224
|
+
};
|
|
22225
|
+
const execSync = (command, options = {}) => orThrow(run(command, [], { ...options, shell: options.shell ?? true }), command, options);
|
|
22226
|
+
return { spawnSync, execFileSync, execSync };
|
|
22227
|
+
}
|
|
22159
22228
|
function normalize2(options, callback) {
|
|
22160
22229
|
if (typeof options === "function") return [{}, options];
|
|
22161
22230
|
return [options ?? {}, callback];
|
|
22162
22231
|
}
|
|
22163
22232
|
function unavailable(name) {
|
|
22164
|
-
return () => {
|
|
22233
|
+
return (...args) => {
|
|
22234
|
+
const file3 = typeof args[0] === "string" ? args[0] : void 0;
|
|
22235
|
+
const list = Array.isArray(args[1]) ? args[1].map(String) : [];
|
|
22236
|
+
const command = file3 ? [file3, ...list].join(" ") : void 0;
|
|
22165
22237
|
const error = new Error(
|
|
22166
|
-
`child_process.${name} is not supported in the sandboxed runtime
|
|
22238
|
+
`child_process.${name} is not supported in the sandboxed runtime` + (command ? ` (tried to run: ${command})` : "") + `: the caller, the child and the event loop share one thread, so blocking the caller would also stop the child. Run it with the asynchronous form (spawn/exec/execFile), or run the command from the container shell.`
|
|
22167
22239
|
);
|
|
22168
22240
|
error.code = "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM";
|
|
22169
22241
|
throw error;
|
|
@@ -22227,7 +22299,11 @@ function parseKeys(input) {
|
|
|
22227
22299
|
const name = CSI_KEYS[sequence];
|
|
22228
22300
|
keys.push({
|
|
22229
22301
|
sequence: `\x1B${sequence}`,
|
|
22230
|
-
|
|
22302
|
+
/* Node leaves this undefined for a sequence it does not recognise.
|
|
22303
|
+
* The string "undefined" is not the same thing: a caller testing
|
|
22304
|
+
* `key.name === undefined` would take an unknown key for a known
|
|
22305
|
+
* one. */
|
|
22306
|
+
name,
|
|
22231
22307
|
ctrl: false,
|
|
22232
22308
|
/* xterm reports modifiers as `;5` (control) and `;2` (shift) before
|
|
22233
22309
|
* the final letter. */
|
|
@@ -22787,7 +22863,7 @@ function createCoreModules(options) {
|
|
|
22787
22863
|
};
|
|
22788
22864
|
const http = options.http ? createHttpModule(options.http.router, options.http.owner, httpOptions, "http:") : createUnsupportedModule("http");
|
|
22789
22865
|
const https = options.http ? createHttpModule(options.http.router, options.http.owner, httpOptions, "https:") : createUnsupportedModule("https");
|
|
22790
|
-
const childProcess = options.spawnChild ? createChildProcessModule(options.spawnChild, () => cwd) : createUnsupportedModule("child_process");
|
|
22866
|
+
const childProcess = options.spawnChild ? createChildProcessModule(options.spawnChild, () => cwd, options.syncSpawn, () => ({ ...processObject.env })) : createUnsupportedModule("child_process");
|
|
22791
22867
|
const readline = createReadlineModule(() => processObject.stdin, () => processObject.stdout);
|
|
22792
22868
|
const dns = createDnsModule();
|
|
22793
22869
|
const builtins = {
|
|
@@ -22848,6 +22924,7 @@ function createCoreModules(options) {
|
|
|
22848
22924
|
pendingUnrefed: timers.pendingUnrefed,
|
|
22849
22925
|
/** Client requests sent but not yet read to completion. */
|
|
22850
22926
|
pendingRequests: () => inFlightRequests,
|
|
22927
|
+
cancelTimers: timers.cancelAll,
|
|
22851
22928
|
writeStdin: (data) => {
|
|
22852
22929
|
if (options.interactiveStdin) stdin.write(data);
|
|
22853
22930
|
},
|
|
@@ -23529,6 +23606,27 @@ function createTrackedTimers() {
|
|
|
23529
23606
|
live.delete(handle);
|
|
23530
23607
|
native(handle);
|
|
23531
23608
|
};
|
|
23609
|
+
const cancelAll = () => {
|
|
23610
|
+
for (const handle of [...live, ...unrefed]) {
|
|
23611
|
+
const state = states.get(handle);
|
|
23612
|
+
if (!state) continue;
|
|
23613
|
+
state.active = false;
|
|
23614
|
+
try {
|
|
23615
|
+
clearTimeout(state.native);
|
|
23616
|
+
} catch {
|
|
23617
|
+
}
|
|
23618
|
+
try {
|
|
23619
|
+
clearInterval(state.native);
|
|
23620
|
+
} catch {
|
|
23621
|
+
}
|
|
23622
|
+
try {
|
|
23623
|
+
(globalThis.clearImmediate ?? clearTimeout)(state.native);
|
|
23624
|
+
} catch {
|
|
23625
|
+
}
|
|
23626
|
+
}
|
|
23627
|
+
live.clear();
|
|
23628
|
+
unrefed.clear();
|
|
23629
|
+
};
|
|
23532
23630
|
return {
|
|
23533
23631
|
api: {
|
|
23534
23632
|
setTimeout: setTimeoutTracked,
|
|
@@ -23539,7 +23637,8 @@ function createTrackedTimers() {
|
|
|
23539
23637
|
clearImmediate: (handle) => clear2(handle, globalThis.clearImmediate ?? clearTimeout)
|
|
23540
23638
|
},
|
|
23541
23639
|
pending: () => live.size,
|
|
23542
|
-
pendingUnrefed: () => unrefed.size
|
|
23640
|
+
pendingUnrefed: () => unrefed.size,
|
|
23641
|
+
cancelAll
|
|
23543
23642
|
};
|
|
23544
23643
|
}
|
|
23545
23644
|
function createTimerPromises() {
|
|
@@ -23993,6 +24092,195 @@ var MemoryVolume = class {
|
|
|
23993
24092
|
}
|
|
23994
24093
|
};
|
|
23995
24094
|
|
|
24095
|
+
// src/runtime/mirroring-volume.ts
|
|
24096
|
+
init_path();
|
|
24097
|
+
var MirroringVolume = class {
|
|
24098
|
+
constructor(inner = new MemoryVolume()) {
|
|
24099
|
+
this.inner = inner;
|
|
24100
|
+
}
|
|
24101
|
+
inner;
|
|
24102
|
+
mirror;
|
|
24103
|
+
root = "/";
|
|
24104
|
+
/**
|
|
24105
|
+
* Start mirroring the tree under `root`, seeding it with what is there now.
|
|
24106
|
+
*
|
|
24107
|
+
* Called once the Rolldown binding is known to be in play; before that a
|
|
24108
|
+
* container pays nothing for this.
|
|
24109
|
+
*/
|
|
24110
|
+
attach(mirror, root) {
|
|
24111
|
+
const cleanRoot = clean(root);
|
|
24112
|
+
if (this.mirror === mirror && this.root === cleanRoot) return;
|
|
24113
|
+
this.mirror = mirror;
|
|
24114
|
+
this.root = cleanRoot;
|
|
24115
|
+
this.seed();
|
|
24116
|
+
}
|
|
24117
|
+
detach() {
|
|
24118
|
+
this.mirror = void 0;
|
|
24119
|
+
}
|
|
24120
|
+
/** Copy the whole subtree across. The only bulk operation that remains. */
|
|
24121
|
+
seed() {
|
|
24122
|
+
const mirror = this.mirror;
|
|
24123
|
+
if (!mirror) return;
|
|
24124
|
+
try {
|
|
24125
|
+
mirror.rmSync?.(this.root, { recursive: true, force: true });
|
|
24126
|
+
} catch {
|
|
24127
|
+
}
|
|
24128
|
+
const copy = (path) => {
|
|
24129
|
+
let stat2;
|
|
24130
|
+
try {
|
|
24131
|
+
stat2 = this.inner.lstatSync(path);
|
|
24132
|
+
} catch {
|
|
24133
|
+
return;
|
|
24134
|
+
}
|
|
24135
|
+
if (stat2.isDirectory()) {
|
|
24136
|
+
this.safely(() => mirror.mkdirSync(path, { recursive: true }));
|
|
24137
|
+
for (const name of this.inner.readdirSync(path)) copy(join(path, name));
|
|
24138
|
+
} else if (stat2.isSymbolicLink()) {
|
|
24139
|
+
this.safely(() => {
|
|
24140
|
+
mirror.mkdirSync(dirname(path), { recursive: true });
|
|
24141
|
+
mirror.symlinkSync(this.inner.readlinkSync(path), path);
|
|
24142
|
+
});
|
|
24143
|
+
} else if (!path.endsWith(".node")) {
|
|
24144
|
+
this.safely(() => {
|
|
24145
|
+
mirror.mkdirSync(dirname(path), { recursive: true });
|
|
24146
|
+
mirror.writeFileSync(path, this.inner.readFileSync(path));
|
|
24147
|
+
});
|
|
24148
|
+
}
|
|
24149
|
+
};
|
|
24150
|
+
copy(this.root);
|
|
24151
|
+
}
|
|
24152
|
+
/** Is this path inside the mirrored subtree? */
|
|
24153
|
+
mirrored(path) {
|
|
24154
|
+
if (!this.mirror) return false;
|
|
24155
|
+
const clean2 = clean(path);
|
|
24156
|
+
return this.root === "/" || clean2 === this.root || clean2.startsWith(`${this.root}/`);
|
|
24157
|
+
}
|
|
24158
|
+
/**
|
|
24159
|
+
* Run a mirror update, swallowing failure.
|
|
24160
|
+
*
|
|
24161
|
+
* The mirror is a read-only cache for another engine. If it rejects
|
|
24162
|
+
* something — an unsupported operation, a path it has not seen — the
|
|
24163
|
+
* container's own write has still happened and must still succeed.
|
|
24164
|
+
*/
|
|
24165
|
+
safely(update) {
|
|
24166
|
+
try {
|
|
24167
|
+
update();
|
|
24168
|
+
} catch {
|
|
24169
|
+
}
|
|
24170
|
+
}
|
|
24171
|
+
/** Push a path's current state across, whatever it is now. */
|
|
24172
|
+
sync(path) {
|
|
24173
|
+
const mirror = this.mirror;
|
|
24174
|
+
if (!mirror || !this.mirrored(path)) return;
|
|
24175
|
+
let stat2;
|
|
24176
|
+
try {
|
|
24177
|
+
stat2 = this.inner.lstatSync(path);
|
|
24178
|
+
} catch {
|
|
24179
|
+
this.safely(() => {
|
|
24180
|
+
if (mirror.rmSync) mirror.rmSync(path, { recursive: true, force: true });
|
|
24181
|
+
else mirror.unlinkSync?.(path);
|
|
24182
|
+
});
|
|
24183
|
+
return;
|
|
24184
|
+
}
|
|
24185
|
+
if (stat2.isDirectory()) {
|
|
24186
|
+
this.safely(() => mirror.mkdirSync(path, { recursive: true }));
|
|
24187
|
+
return;
|
|
24188
|
+
}
|
|
24189
|
+
if (stat2.isSymbolicLink()) {
|
|
24190
|
+
this.safely(() => {
|
|
24191
|
+
mirror.mkdirSync(dirname(path), { recursive: true });
|
|
24192
|
+
mirror.symlinkSync(this.inner.readlinkSync(path), path);
|
|
24193
|
+
});
|
|
24194
|
+
return;
|
|
24195
|
+
}
|
|
24196
|
+
if (path.endsWith(".node")) return;
|
|
24197
|
+
this.safely(() => {
|
|
24198
|
+
mirror.mkdirSync(dirname(path), { recursive: true });
|
|
24199
|
+
mirror.writeFileSync(path, this.inner.readFileSync(path));
|
|
24200
|
+
});
|
|
24201
|
+
}
|
|
24202
|
+
// ── reads: straight through ───────────────────────────────────────────────
|
|
24203
|
+
readFileSync(path) {
|
|
24204
|
+
return this.inner.readFileSync(path);
|
|
24205
|
+
}
|
|
24206
|
+
readdirSync(path) {
|
|
24207
|
+
return this.inner.readdirSync(path);
|
|
24208
|
+
}
|
|
24209
|
+
lstatSync(path) {
|
|
24210
|
+
return this.inner.lstatSync(path);
|
|
24211
|
+
}
|
|
24212
|
+
readlinkSync(path) {
|
|
24213
|
+
return this.inner.readlinkSync(path);
|
|
24214
|
+
}
|
|
24215
|
+
getStats() {
|
|
24216
|
+
return this.inner.getStats();
|
|
24217
|
+
}
|
|
24218
|
+
// ── writes: applied, then mirrored ────────────────────────────────────────
|
|
24219
|
+
writeFileSync(path, data) {
|
|
24220
|
+
this.inner.writeFileSync(path, data);
|
|
24221
|
+
this.sync(path);
|
|
24222
|
+
}
|
|
24223
|
+
appendFileSync(path, data) {
|
|
24224
|
+
this.inner.appendFileSync(path, data);
|
|
24225
|
+
this.sync(path);
|
|
24226
|
+
}
|
|
24227
|
+
mkdirSync(path, options) {
|
|
24228
|
+
this.inner.mkdirSync(path, options);
|
|
24229
|
+
this.sync(path);
|
|
24230
|
+
}
|
|
24231
|
+
rmdirSync(path) {
|
|
24232
|
+
this.inner.rmdirSync(path);
|
|
24233
|
+
this.sync(path);
|
|
24234
|
+
}
|
|
24235
|
+
unlinkSync(path) {
|
|
24236
|
+
this.inner.unlinkSync(path);
|
|
24237
|
+
this.sync(path);
|
|
24238
|
+
}
|
|
24239
|
+
renameSync(from, to) {
|
|
24240
|
+
this.inner.renameSync(from, to);
|
|
24241
|
+
this.sync(from);
|
|
24242
|
+
this.sync(to);
|
|
24243
|
+
}
|
|
24244
|
+
symlinkSync(target, path) {
|
|
24245
|
+
this.inner.symlinkSync(target, path);
|
|
24246
|
+
this.sync(path);
|
|
24247
|
+
}
|
|
24248
|
+
linkSync(existing, path) {
|
|
24249
|
+
this.inner.linkSync(existing, path);
|
|
24250
|
+
this.sync(path);
|
|
24251
|
+
}
|
|
24252
|
+
truncateSync(path, length) {
|
|
24253
|
+
this.inner.truncateSync(path, length);
|
|
24254
|
+
this.sync(path);
|
|
24255
|
+
}
|
|
24256
|
+
/* Permissions and timestamps do not change what a bundler resolves, and
|
|
24257
|
+
* `memfs` is stricter about them than this volume is. Applied here only. */
|
|
24258
|
+
chmodSync(path, mode) {
|
|
24259
|
+
this.inner.chmodSync(path, mode);
|
|
24260
|
+
}
|
|
24261
|
+
lchmodSync(path, mode) {
|
|
24262
|
+
this.inner.lchmodSync(path, mode);
|
|
24263
|
+
}
|
|
24264
|
+
chownSync(path, uid, gid) {
|
|
24265
|
+
this.inner.chownSync(path, uid, gid);
|
|
24266
|
+
}
|
|
24267
|
+
lchownSync(path, uid, gid) {
|
|
24268
|
+
this.inner.lchownSync(path, uid, gid);
|
|
24269
|
+
}
|
|
24270
|
+
utimesSync(path, atime, mtime) {
|
|
24271
|
+
this.inner.utimesSync(path, atime, mtime);
|
|
24272
|
+
}
|
|
24273
|
+
// ── snapshots ─────────────────────────────────────────────────────────────
|
|
24274
|
+
snapshot() {
|
|
24275
|
+
return this.inner.snapshot();
|
|
24276
|
+
}
|
|
24277
|
+
/** A restore replaces everything, so the mirror is rebuilt rather than patched. */
|
|
24278
|
+
restore(entries) {
|
|
24279
|
+
this.inner.restore(entries);
|
|
24280
|
+
this.seed();
|
|
24281
|
+
}
|
|
24282
|
+
};
|
|
24283
|
+
|
|
23996
24284
|
// src/runtime/host-esbuild.ts
|
|
23997
24285
|
var ASYNC_API = ["transform", "build", "context", "formatMessages", "analyzeMetafile"];
|
|
23998
24286
|
function createHostEsbuild() {
|
|
@@ -24062,6 +24350,7 @@ function ensureProcessGlobal() {
|
|
|
24062
24350
|
}
|
|
24063
24351
|
|
|
24064
24352
|
// src/runtime/host-rolldown.ts
|
|
24353
|
+
var BUNDLER_HINT = "If this is a bundler pre-bundling the worker away, exclude the binding from dependency optimisation \u2014 in Vite:\n\n optimizeDeps: { exclude: ['@rolldown/binding-wasm32-wasi'] }";
|
|
24065
24354
|
async function loadHostRolldownBinding() {
|
|
24066
24355
|
if (typeof window !== "undefined" && globalThis.crossOriginIsolated !== true) {
|
|
24067
24356
|
throw new Error(
|
|
@@ -24075,7 +24364,13 @@ async function loadHostRolldownBinding() {
|
|
|
24075
24364
|
if (typeof process !== "undefined" && process.env?.SANDBOXEDJS_DEBUG) {
|
|
24076
24365
|
console.error("[sandboxedjs] Rolldown WASI binding unavailable:", error);
|
|
24077
24366
|
}
|
|
24078
|
-
|
|
24367
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
24368
|
+
throw new Error(
|
|
24369
|
+
`The optional Rolldown WASI binding could not be loaded: ${reason}` + (typeof window === "undefined" ? "" : `
|
|
24370
|
+
|
|
24371
|
+
${BUNDLER_HINT}`),
|
|
24372
|
+
{ cause: error }
|
|
24373
|
+
);
|
|
24079
24374
|
}
|
|
24080
24375
|
}
|
|
24081
24376
|
|
|
@@ -24115,6 +24410,7 @@ var LocalProcess = class extends EventEmitter4 {
|
|
|
24115
24410
|
inputEnded = false;
|
|
24116
24411
|
pendingInput = [];
|
|
24117
24412
|
resolveKilled;
|
|
24413
|
+
cleanup;
|
|
24118
24414
|
killedPromise = new Promise((resolve2) => {
|
|
24119
24415
|
this.resolveKilled = resolve2;
|
|
24120
24416
|
});
|
|
@@ -24153,13 +24449,32 @@ var LocalProcess = class extends EventEmitter4 {
|
|
|
24153
24449
|
for (const chunk of this.pendingInput.splice(0)) input(chunk);
|
|
24154
24450
|
if (this.inputEnded) end();
|
|
24155
24451
|
}
|
|
24452
|
+
/**
|
|
24453
|
+
* End the process now.
|
|
24454
|
+
*
|
|
24455
|
+
* Killing has to settle `completion`, not merely record the intent. A
|
|
24456
|
+
* program with an interval outstanding has work pending forever, so the
|
|
24457
|
+
* runtime's own idle check will never end it — before this, killing such a
|
|
24458
|
+
* process left the caller awaiting a promise that could not resolve.
|
|
24459
|
+
*/
|
|
24156
24460
|
kill(_signal = "SIGTERM") {
|
|
24461
|
+
if (this.killed) return;
|
|
24157
24462
|
this.killed = true;
|
|
24158
24463
|
this.resolveKilled();
|
|
24464
|
+
this.cleanup?.();
|
|
24465
|
+
this.finish(137);
|
|
24159
24466
|
}
|
|
24160
24467
|
waitForKill() {
|
|
24161
24468
|
return this.killedPromise;
|
|
24162
24469
|
}
|
|
24470
|
+
isKilled() {
|
|
24471
|
+
return this.killed;
|
|
24472
|
+
}
|
|
24473
|
+
/** Registered by the task so a kill can release what the program still holds. */
|
|
24474
|
+
onKill(cleanup) {
|
|
24475
|
+
this.cleanup = cleanup;
|
|
24476
|
+
if (this.killed) cleanup();
|
|
24477
|
+
}
|
|
24163
24478
|
/** End the process now, for an asynchronous `process.exit`. */
|
|
24164
24479
|
exitNow(code) {
|
|
24165
24480
|
this.finish(code);
|
|
@@ -24321,7 +24636,10 @@ function removeEscapedErrorReporting() {
|
|
|
24321
24636
|
}
|
|
24322
24637
|
var DRAIN_TURNS = 4;
|
|
24323
24638
|
var LocalRuntimePod = class _LocalRuntimePod {
|
|
24324
|
-
|
|
24639
|
+
/* Wrapped so that a foreign filesystem — Rolldown's WebAssembly memfs — can
|
|
24640
|
+
* be kept in step as writes happen, rather than deep-copied once per spawn.
|
|
24641
|
+
* With nothing attached this is a straight pass-through. */
|
|
24642
|
+
volume = new MirroringVolume(new MemoryVolume());
|
|
24325
24643
|
packages;
|
|
24326
24644
|
instanceId = `sbx-${Math.random().toString(36).slice(2)}`;
|
|
24327
24645
|
router = new VirtualHttpRouter();
|
|
@@ -24412,6 +24730,10 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
24412
24730
|
onRawMode: (enabled) => proc.emit("rawmode", enabled)
|
|
24413
24731
|
});
|
|
24414
24732
|
proc.acceptInput((data) => core.writeStdin(data), () => core.endStdin());
|
|
24733
|
+
proc.onKill(() => {
|
|
24734
|
+
core.cancelTimers();
|
|
24735
|
+
this.router.closeOwner(owner);
|
|
24736
|
+
});
|
|
24415
24737
|
engine = new CommonJsEngine(this.volume, {
|
|
24416
24738
|
cwd,
|
|
24417
24739
|
builtins: core.builtins,
|
|
@@ -24421,7 +24743,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
24421
24743
|
});
|
|
24422
24744
|
try {
|
|
24423
24745
|
await engine.run(script);
|
|
24424
|
-
await this.settle(owner, core.pendingHandles, core.pendingUnrefed, core.readingStdin, core.pendingRequests);
|
|
24746
|
+
await this.settle(owner, core.pendingHandles, core.pendingUnrefed, core.readingStdin, core.pendingRequests, () => proc.isKilled());
|
|
24425
24747
|
if (this.router.activePorts(owner).length) {
|
|
24426
24748
|
await proc.waitForKill();
|
|
24427
24749
|
return 137;
|
|
@@ -24442,13 +24764,13 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
24442
24764
|
*/
|
|
24443
24765
|
async prepareRolldown(cwd, env2) {
|
|
24444
24766
|
const specifier = "@rolldown/binding-wasm32-wasi";
|
|
24445
|
-
if (!this.modules[specifier] &&
|
|
24767
|
+
if (!this.modules[specifier] && packageIsInstalled(this.volume, cwd, "rolldown")) {
|
|
24446
24768
|
this.rolldownBinding ??= loadHostRolldownBinding();
|
|
24447
24769
|
const binding = await this.rolldownBinding;
|
|
24448
24770
|
if (binding) this.modules[specifier] = binding;
|
|
24449
24771
|
}
|
|
24450
24772
|
if (this.modules[specifier]) {
|
|
24451
|
-
|
|
24773
|
+
attachRolldownMirror(this.modules[specifier], this.volume, cwd);
|
|
24452
24774
|
env2.NAPI_RS_FORCE_WASI ??= "true";
|
|
24453
24775
|
}
|
|
24454
24776
|
}
|
|
@@ -24467,18 +24789,18 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
24467
24789
|
* A plain script that has genuinely finished falls straight through both,
|
|
24468
24790
|
* costing a handful of empty turns.
|
|
24469
24791
|
*/
|
|
24470
|
-
async settle(owner, pendingHandles, pendingUnrefed, readingStdin, pendingRequests) {
|
|
24792
|
+
async settle(owner, pendingHandles, pendingUnrefed, readingStdin, pendingRequests, killed) {
|
|
24471
24793
|
for (let turn = 0; turn < DRAIN_TURNS; turn++) {
|
|
24472
|
-
if (this.router.activePorts(owner).length) return;
|
|
24794
|
+
if (this.router.activePorts(owner).length || killed()) return;
|
|
24473
24795
|
await new Promise((resolve2) => setTimeout(resolve2, 0));
|
|
24474
24796
|
}
|
|
24475
24797
|
if (pendingHandles() === 0 && pendingRequests() === 0 && pendingUnrefed() > 0 && !readingStdin()) {
|
|
24476
24798
|
const deadline = Date.now() + 1e3;
|
|
24477
|
-
while (!this.router.activePorts(owner).length && Date.now() < deadline) {
|
|
24799
|
+
while (!this.router.activePorts(owner).length && !killed() && Date.now() < deadline) {
|
|
24478
24800
|
await new Promise((resolve2) => setTimeout(resolve2, 5));
|
|
24479
24801
|
}
|
|
24480
24802
|
}
|
|
24481
|
-
while (!this.router.activePorts(owner).length && (pendingHandles() > 0 || pendingRequests() > 0 || readingStdin())) {
|
|
24803
|
+
while (!this.router.activePorts(owner).length && !killed() && (pendingHandles() > 0 || pendingRequests() > 0 || readingStdin())) {
|
|
24482
24804
|
await new Promise((resolve2) => setTimeout(resolve2, 5));
|
|
24483
24805
|
}
|
|
24484
24806
|
}
|
|
@@ -24530,7 +24852,7 @@ function formatError(error) {
|
|
|
24530
24852
|
function isRecord(value) {
|
|
24531
24853
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24532
24854
|
}
|
|
24533
|
-
function
|
|
24855
|
+
function packageIsInstalled(volume, cwd, wanted) {
|
|
24534
24856
|
for (let dir3 = clean(cwd); ; dir3 = dirname(dir3)) {
|
|
24535
24857
|
if (packageTreeContains(volume, join(dir3, "node_modules"), wanted, /* @__PURE__ */ new Set())) return true;
|
|
24536
24858
|
if (dir3 === "/") return false;
|
|
@@ -24559,30 +24881,571 @@ function directory(volume, path) {
|
|
|
24559
24881
|
return false;
|
|
24560
24882
|
}
|
|
24561
24883
|
}
|
|
24562
|
-
function
|
|
24884
|
+
function attachRolldownMirror(binding, volume, root) {
|
|
24563
24885
|
const fs = binding?.__fs;
|
|
24564
24886
|
if (!fs?.mkdirSync || !fs?.writeFileSync) return;
|
|
24565
|
-
|
|
24566
|
-
|
|
24567
|
-
|
|
24887
|
+
volume.attach(fs, root);
|
|
24888
|
+
}
|
|
24889
|
+
|
|
24890
|
+
// src/runtime/sync-channel.ts
|
|
24891
|
+
var STATE = 0;
|
|
24892
|
+
var LENGTH = 1;
|
|
24893
|
+
var MORE = 2;
|
|
24894
|
+
var CONTROL_WORDS = 4;
|
|
24895
|
+
var STATE_REQUEST = 1;
|
|
24896
|
+
var STATE_RESPONSE = 2;
|
|
24897
|
+
var STATE_CONTINUE = 3;
|
|
24898
|
+
var STATE_CLOSED = 4;
|
|
24899
|
+
function createSyncChannelBuffers(capacityBytes = 1 << 20) {
|
|
24900
|
+
return {
|
|
24901
|
+
control: new SharedArrayBuffer(CONTROL_WORDS * Int32Array.BYTES_PER_ELEMENT),
|
|
24902
|
+
data: new SharedArrayBuffer(capacityBytes)
|
|
24903
|
+
};
|
|
24904
|
+
}
|
|
24905
|
+
function syncChannelSupported() {
|
|
24906
|
+
if (typeof SharedArrayBuffer !== "function") return false;
|
|
24907
|
+
if (typeof Atomics !== "object" || typeof Atomics.wait !== "function") return false;
|
|
24908
|
+
if (typeof window !== "undefined" && globalThis.crossOriginIsolated !== true) return false;
|
|
24909
|
+
return true;
|
|
24910
|
+
}
|
|
24911
|
+
var SyncChannelServer = class {
|
|
24912
|
+
constructor(buffers, handle) {
|
|
24913
|
+
this.handle = handle;
|
|
24914
|
+
this.control = new Int32Array(buffers.control);
|
|
24915
|
+
this.data = new Uint8Array(buffers.data);
|
|
24916
|
+
this.capacity = this.data.length;
|
|
24568
24917
|
}
|
|
24569
|
-
|
|
24570
|
-
|
|
24571
|
-
|
|
24572
|
-
|
|
24573
|
-
|
|
24574
|
-
|
|
24575
|
-
|
|
24918
|
+
handle;
|
|
24919
|
+
control;
|
|
24920
|
+
data;
|
|
24921
|
+
capacity;
|
|
24922
|
+
/** Request chunks gathered so far, and the response still to be sent. */
|
|
24923
|
+
incoming = [];
|
|
24924
|
+
outgoing = null;
|
|
24925
|
+
sent = 0;
|
|
24926
|
+
closed = false;
|
|
24927
|
+
/** Call from the wake message the client sends after each chunk. */
|
|
24928
|
+
async pump() {
|
|
24929
|
+
if (this.closed) return;
|
|
24930
|
+
const state = Atomics.load(this.control, STATE);
|
|
24931
|
+
if (state === STATE_REQUEST) {
|
|
24932
|
+
const size = Atomics.load(this.control, LENGTH);
|
|
24933
|
+
this.incoming.push(this.data.slice(0, size));
|
|
24934
|
+
if (Atomics.load(this.control, MORE) === 1) {
|
|
24935
|
+
this.publish(STATE_CONTINUE);
|
|
24936
|
+
return;
|
|
24937
|
+
}
|
|
24938
|
+
const request = concat3(this.incoming);
|
|
24939
|
+
this.incoming = [];
|
|
24940
|
+
let response;
|
|
24576
24941
|
try {
|
|
24577
|
-
|
|
24942
|
+
response = await this.handle(request);
|
|
24578
24943
|
} catch {
|
|
24944
|
+
response = new Uint8Array();
|
|
24945
|
+
}
|
|
24946
|
+
if (this.closed) return;
|
|
24947
|
+
this.outgoing = response;
|
|
24948
|
+
this.sent = 0;
|
|
24949
|
+
this.sendChunk();
|
|
24950
|
+
return;
|
|
24951
|
+
}
|
|
24952
|
+
if (state === STATE_CONTINUE) this.sendChunk();
|
|
24953
|
+
}
|
|
24954
|
+
/** Release a blocked client, e.g. when the container is torn down. */
|
|
24955
|
+
close() {
|
|
24956
|
+
if (this.closed) return;
|
|
24957
|
+
this.closed = true;
|
|
24958
|
+
Atomics.store(this.control, STATE, STATE_CLOSED);
|
|
24959
|
+
Atomics.notify(this.control, STATE);
|
|
24960
|
+
}
|
|
24961
|
+
sendChunk() {
|
|
24962
|
+
const payload = this.outgoing ?? new Uint8Array();
|
|
24963
|
+
const size = Math.min(this.capacity, payload.length - this.sent);
|
|
24964
|
+
this.data.set(payload.subarray(this.sent, this.sent + size), 0);
|
|
24965
|
+
this.sent += size;
|
|
24966
|
+
Atomics.store(this.control, LENGTH, size);
|
|
24967
|
+
Atomics.store(this.control, MORE, this.sent < payload.length ? 1 : 0);
|
|
24968
|
+
this.publish(STATE_RESPONSE);
|
|
24969
|
+
}
|
|
24970
|
+
publish(state) {
|
|
24971
|
+
Atomics.store(this.control, STATE, state);
|
|
24972
|
+
Atomics.notify(this.control, STATE);
|
|
24973
|
+
}
|
|
24974
|
+
};
|
|
24975
|
+
function concat3(parts) {
|
|
24976
|
+
if (parts.length === 1) return parts[0];
|
|
24977
|
+
const total = parts.reduce((sum, part) => sum + part.length, 0);
|
|
24978
|
+
const joined = new Uint8Array(total);
|
|
24979
|
+
let at = 0;
|
|
24980
|
+
for (const part of parts) {
|
|
24981
|
+
joined.set(part, at);
|
|
24982
|
+
at += part.length;
|
|
24983
|
+
}
|
|
24984
|
+
return joined;
|
|
24985
|
+
}
|
|
24986
|
+
|
|
24987
|
+
// src/runtime/remote-volume.ts
|
|
24988
|
+
var encoder7 = new TextEncoder();
|
|
24989
|
+
var decoder7 = new TextDecoder();
|
|
24990
|
+
function encodeFrame(header, body) {
|
|
24991
|
+
const json = encoder7.encode(JSON.stringify(header));
|
|
24992
|
+
const frame = new Uint8Array(4 + json.length + (body?.length ?? 0));
|
|
24993
|
+
new DataView(frame.buffer).setUint32(0, json.length, true);
|
|
24994
|
+
frame.set(json, 4);
|
|
24995
|
+
if (body?.length) frame.set(body, 4 + json.length);
|
|
24996
|
+
return frame;
|
|
24997
|
+
}
|
|
24998
|
+
function decodeFrame(frame) {
|
|
24999
|
+
const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
|
|
25000
|
+
const headerLength = view.getUint32(0, true);
|
|
25001
|
+
const header = JSON.parse(decoder7.decode(frame.subarray(4, 4 + headerLength)));
|
|
25002
|
+
return { header, body: frame.subarray(4 + headerLength) };
|
|
25003
|
+
}
|
|
25004
|
+
function flattenStat(stat2) {
|
|
25005
|
+
return {
|
|
25006
|
+
kind: stat2.isDirectory() ? "directory" : stat2.isSymbolicLink() ? "symlink" : "file",
|
|
25007
|
+
mode: stat2.mode,
|
|
25008
|
+
size: stat2.size,
|
|
25009
|
+
uid: stat2.uid,
|
|
25010
|
+
gid: stat2.gid,
|
|
25011
|
+
ino: stat2.ino,
|
|
25012
|
+
nlink: stat2.nlink,
|
|
25013
|
+
atimeMs: stat2.atimeMs,
|
|
25014
|
+
mtimeMs: stat2.mtimeMs,
|
|
25015
|
+
ctimeMs: stat2.ctimeMs,
|
|
25016
|
+
birthtimeMs: stat2.birthtimeMs
|
|
25017
|
+
};
|
|
25018
|
+
}
|
|
25019
|
+
var BINARY_RESULTS = /* @__PURE__ */ new Set(["readFileSync"]);
|
|
25020
|
+
var BINARY_ARGUMENTS = /* @__PURE__ */ new Set(["writeFileSync", "appendFileSync"]);
|
|
25021
|
+
function serveVolume(volume) {
|
|
25022
|
+
return (request) => {
|
|
25023
|
+
const { header, body } = decodeFrame(request);
|
|
25024
|
+
const target = volume;
|
|
25025
|
+
try {
|
|
25026
|
+
const method = target[header.op];
|
|
25027
|
+
if (typeof method !== "function") {
|
|
25028
|
+
return encodeFrame({ ok: false, error: { message: `unknown volume operation: ${header.op}` } });
|
|
25029
|
+
}
|
|
25030
|
+
const plain = header.args.map((value) => value === null ? void 0 : value);
|
|
25031
|
+
const args = BINARY_ARGUMENTS.has(header.op) ? [plain[0], body] : header.op === "utimesSync" ? [plain[0], new Date(plain[1]), new Date(plain[2])] : plain;
|
|
25032
|
+
const result = method.apply(volume, args);
|
|
25033
|
+
if (BINARY_RESULTS.has(header.op)) {
|
|
25034
|
+
return encodeFrame({ ok: true }, result);
|
|
24579
25035
|
}
|
|
24580
|
-
|
|
24581
|
-
|
|
24582
|
-
|
|
25036
|
+
if (header.op === "lstatSync") {
|
|
25037
|
+
return encodeFrame({ ok: true, value: flattenStat(result) });
|
|
25038
|
+
}
|
|
25039
|
+
return encodeFrame({ ok: true, value: result });
|
|
25040
|
+
} catch (error) {
|
|
25041
|
+
const failure = error;
|
|
25042
|
+
return encodeFrame({
|
|
25043
|
+
ok: false,
|
|
25044
|
+
error: { ...failure.code ? { code: failure.code } : {}, message: failure.message ?? String(error) }
|
|
25045
|
+
});
|
|
25046
|
+
}
|
|
25047
|
+
};
|
|
25048
|
+
}
|
|
25049
|
+
|
|
25050
|
+
// src/runtime/sync-syscalls.ts
|
|
25051
|
+
var SPAWN_OP = "spawnSync";
|
|
25052
|
+
function serveSyncSyscalls(options) {
|
|
25053
|
+
const volumeHandler = serveVolume(options.volume);
|
|
25054
|
+
return async (request) => {
|
|
25055
|
+
const { header } = decodeFrame(request);
|
|
25056
|
+
if (header.op !== SPAWN_OP) return volumeHandler(request);
|
|
25057
|
+
try {
|
|
25058
|
+
const result = await options.spawnChild(header.args[0]);
|
|
25059
|
+
return encodeFrame({ ok: true, value: result });
|
|
25060
|
+
} catch (error) {
|
|
25061
|
+
const failure = error;
|
|
25062
|
+
return encodeFrame({
|
|
25063
|
+
ok: true,
|
|
25064
|
+
value: {
|
|
25065
|
+
status: null,
|
|
25066
|
+
stdout: "",
|
|
25067
|
+
stderr: "",
|
|
25068
|
+
signal: null,
|
|
25069
|
+
error: { ...failure.code ? { code: failure.code } : {}, message: failure.message ?? String(error) }
|
|
25070
|
+
}
|
|
25071
|
+
});
|
|
24583
25072
|
}
|
|
24584
25073
|
};
|
|
24585
|
-
|
|
25074
|
+
}
|
|
25075
|
+
|
|
25076
|
+
// src/runtime/worker-host.ts
|
|
25077
|
+
function defaultWorkerUrl() {
|
|
25078
|
+
return new URL("./worker-entry.js", import.meta.url);
|
|
25079
|
+
}
|
|
25080
|
+
function hasDomWorker() {
|
|
25081
|
+
return typeof Worker === "function" && typeof document !== "undefined";
|
|
25082
|
+
}
|
|
25083
|
+
async function startRuntimeWorker(options = {}) {
|
|
25084
|
+
const url = options.url ?? defaultWorkerUrl();
|
|
25085
|
+
const worker = hasDomWorker() ? await startDomWorker(url) : await startNodeWorker(url, options.workerData ?? {});
|
|
25086
|
+
await new Promise((resolve2, reject) => {
|
|
25087
|
+
const timer = setTimeout(() => reject(new Error("the runtime worker did not start in time")), options.timeoutMs ?? 1e4);
|
|
25088
|
+
worker.onMessage((message) => {
|
|
25089
|
+
if (message?.type === "sandboxedjs:ready") {
|
|
25090
|
+
clearTimeout(timer);
|
|
25091
|
+
resolve2();
|
|
25092
|
+
}
|
|
25093
|
+
});
|
|
25094
|
+
worker.onError((error) => {
|
|
25095
|
+
clearTimeout(timer);
|
|
25096
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
25097
|
+
});
|
|
25098
|
+
});
|
|
25099
|
+
return worker;
|
|
25100
|
+
}
|
|
25101
|
+
async function startDomWorker(url) {
|
|
25102
|
+
const worker = new Worker(url, { type: "module" });
|
|
25103
|
+
const listeners = [];
|
|
25104
|
+
const errors = [];
|
|
25105
|
+
worker.addEventListener("message", (event) => {
|
|
25106
|
+
for (const listener of listeners) listener(event.data);
|
|
25107
|
+
});
|
|
25108
|
+
worker.addEventListener("error", (event) => {
|
|
25109
|
+
const detail = event.message || "the runtime worker failed to load";
|
|
25110
|
+
for (const listener of errors) listener(new Error(detail));
|
|
25111
|
+
});
|
|
25112
|
+
return {
|
|
25113
|
+
postMessage: (message) => worker.postMessage(message),
|
|
25114
|
+
onMessage: (listener) => {
|
|
25115
|
+
listeners.push(listener);
|
|
25116
|
+
},
|
|
25117
|
+
onError: (listener) => {
|
|
25118
|
+
errors.push(listener);
|
|
25119
|
+
},
|
|
25120
|
+
terminate: () => worker.terminate()
|
|
25121
|
+
};
|
|
25122
|
+
}
|
|
25123
|
+
async function startNodeWorker(url, workerData) {
|
|
25124
|
+
const specifier = ["node", "worker_threads"].join(":");
|
|
25125
|
+
const { Worker: NodeWorker } = await import(
|
|
25126
|
+
/* @vite-ignore */
|
|
25127
|
+
/* webpackIgnore: true */
|
|
25128
|
+
specifier
|
|
25129
|
+
);
|
|
25130
|
+
const worker = new NodeWorker(url, { workerData });
|
|
25131
|
+
return {
|
|
25132
|
+
postMessage: (message) => worker.postMessage(message),
|
|
25133
|
+
onMessage: (listener) => worker.on("message", listener),
|
|
25134
|
+
onError: (listener) => worker.on("error", listener),
|
|
25135
|
+
terminate: () => worker.terminate()
|
|
25136
|
+
};
|
|
25137
|
+
}
|
|
25138
|
+
var WorkerProcess = class extends EventEmitter4 {
|
|
25139
|
+
constructor(worker, release) {
|
|
25140
|
+
super();
|
|
25141
|
+
this.worker = worker;
|
|
25142
|
+
this.release = release;
|
|
25143
|
+
super.on("error", () => {
|
|
25144
|
+
});
|
|
25145
|
+
this.completion = new Promise((resolve2) => {
|
|
25146
|
+
this.resolveCompletion = resolve2;
|
|
25147
|
+
});
|
|
25148
|
+
}
|
|
25149
|
+
worker;
|
|
25150
|
+
release;
|
|
25151
|
+
completion;
|
|
25152
|
+
resolveCompletion;
|
|
25153
|
+
settled = false;
|
|
25154
|
+
out = [];
|
|
25155
|
+
err = [];
|
|
25156
|
+
started = false;
|
|
25157
|
+
pendingInput = [];
|
|
25158
|
+
inputEnded = false;
|
|
25159
|
+
on(event, listener) {
|
|
25160
|
+
return super.on(event, listener);
|
|
25161
|
+
}
|
|
25162
|
+
/** Called once the guest has been told to start; releases anything buffered. */
|
|
25163
|
+
begin() {
|
|
25164
|
+
this.started = true;
|
|
25165
|
+
for (const chunk of this.pendingInput.splice(0)) this.worker.postMessage({ type: "stdin", data: chunk });
|
|
25166
|
+
if (this.inputEnded) this.worker.postMessage({ type: "stdin-end" });
|
|
25167
|
+
}
|
|
25168
|
+
output(text2) {
|
|
25169
|
+
this.out.push(text2);
|
|
25170
|
+
this.emit("output", text2);
|
|
25171
|
+
}
|
|
25172
|
+
error(text2) {
|
|
25173
|
+
this.err.push(text2);
|
|
25174
|
+
this.emit("error", text2);
|
|
25175
|
+
}
|
|
25176
|
+
/**
|
|
25177
|
+
* Deliver input to the running program.
|
|
25178
|
+
*
|
|
25179
|
+
* Held until the guest has been started, for the same reason the in-realm
|
|
25180
|
+
* pod holds it: an interactive session's first keystrokes arrive while the
|
|
25181
|
+
* program is still loading, and dropping them loses the answer to a prompt.
|
|
25182
|
+
*/
|
|
25183
|
+
write(data) {
|
|
25184
|
+
if (this.started) this.worker.postMessage({ type: "stdin", data });
|
|
25185
|
+
else this.pendingInput.push(data);
|
|
25186
|
+
}
|
|
25187
|
+
endInput() {
|
|
25188
|
+
this.inputEnded = true;
|
|
25189
|
+
if (this.started) this.worker.postMessage({ type: "stdin-end" });
|
|
25190
|
+
}
|
|
25191
|
+
kill(_signal = "SIGTERM") {
|
|
25192
|
+
this.worker.postMessage({ type: "kill" });
|
|
25193
|
+
this.finish(137);
|
|
25194
|
+
}
|
|
25195
|
+
finish(exitCode) {
|
|
25196
|
+
if (this.settled) return;
|
|
25197
|
+
this.settled = true;
|
|
25198
|
+
this.emit("exit", exitCode);
|
|
25199
|
+
this.resolveCompletion({ exitCode, stdout: this.out.join(""), stderr: this.err.join("") });
|
|
25200
|
+
this.release();
|
|
25201
|
+
}
|
|
25202
|
+
};
|
|
25203
|
+
var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
|
|
25204
|
+
workerUrl;
|
|
25205
|
+
/** Live workers, so teardown can stop them all. */
|
|
25206
|
+
live = /* @__PURE__ */ new Set();
|
|
25207
|
+
constructor(options) {
|
|
25208
|
+
super(options);
|
|
25209
|
+
this.workerUrl = options.workerUrl;
|
|
25210
|
+
}
|
|
25211
|
+
/**
|
|
25212
|
+
* Boot a Worker-backed pod, or return null when this host cannot support one.
|
|
25213
|
+
*
|
|
25214
|
+
* Declining is a first-class outcome. `SharedArrayBuffer` needs cross-origin
|
|
25215
|
+
* isolation, a bundler may have made the guest script unreachable, and a host
|
|
25216
|
+
* that supplied its own module objects has handed over things no thread
|
|
25217
|
+
* boundary can carry. In every case the caller falls back to the in-realm pod
|
|
25218
|
+
* and keeps working.
|
|
25219
|
+
*/
|
|
25220
|
+
static async tryBoot(options = {}) {
|
|
25221
|
+
if (!syncChannelSupported()) return null;
|
|
25222
|
+
if (options.modules && Object.keys(options.modules).length > 0) return null;
|
|
25223
|
+
const pod = new _WorkerRuntimePod(options);
|
|
25224
|
+
try {
|
|
25225
|
+
const probe = await startRuntimeWorker({ ...options.workerUrl ? { url: options.workerUrl } : {}, timeoutMs: 1e4 });
|
|
25226
|
+
await probe.terminate();
|
|
25227
|
+
return pod;
|
|
25228
|
+
} catch {
|
|
25229
|
+
pod.teardown();
|
|
25230
|
+
return null;
|
|
25231
|
+
}
|
|
25232
|
+
}
|
|
25233
|
+
async spawn(command, args = [], options = {}) {
|
|
25234
|
+
if (command !== "node" && command !== "nodejs") return super.spawn(command, args, options);
|
|
25235
|
+
const script = args[0];
|
|
25236
|
+
if (!script) return super.spawn(command, args, options);
|
|
25237
|
+
const cwd = typeof options.cwd === "string" ? options.cwd : this.workdir;
|
|
25238
|
+
if (this.needsHostModules(cwd)) return super.spawn(command, args, options);
|
|
25239
|
+
return await this.spawnInWorker(script, args, cwd, options);
|
|
25240
|
+
}
|
|
25241
|
+
async spawnInWorker(script, args, cwd, options) {
|
|
25242
|
+
const env2 = { ...this.env, ...isRecord2(options.env) ? options.env : {} };
|
|
25243
|
+
const owner = `${this.instanceId}:${Math.random().toString(36).slice(2)}`;
|
|
25244
|
+
const buffers = createSyncChannelBuffers();
|
|
25245
|
+
const worker = await startRuntimeWorker({
|
|
25246
|
+
...this.workerUrl ? { url: this.workerUrl } : {},
|
|
25247
|
+
timeoutMs: 15e3
|
|
25248
|
+
});
|
|
25249
|
+
const streams = { target: null };
|
|
25250
|
+
const server = new SyncChannelServer(buffers, serveSyncSyscalls({
|
|
25251
|
+
volume: this.volume,
|
|
25252
|
+
spawnChild: (request) => this.runChildToCompletion(request, streams.target)
|
|
25253
|
+
}));
|
|
25254
|
+
const entry = { worker, server };
|
|
25255
|
+
this.live.add(entry);
|
|
25256
|
+
const process2 = new WorkerProcess(worker, () => {
|
|
25257
|
+
this.live.delete(entry);
|
|
25258
|
+
server.close();
|
|
25259
|
+
this.closeProxies(owner);
|
|
25260
|
+
void worker.terminate();
|
|
25261
|
+
});
|
|
25262
|
+
streams.target = process2;
|
|
25263
|
+
const children = /* @__PURE__ */ new Map();
|
|
25264
|
+
worker.onMessage((raw) => {
|
|
25265
|
+
const message = raw;
|
|
25266
|
+
switch (message?.type) {
|
|
25267
|
+
case "wake":
|
|
25268
|
+
void server.pump();
|
|
25269
|
+
return;
|
|
25270
|
+
case "output":
|
|
25271
|
+
process2.output(String(message.text));
|
|
25272
|
+
return;
|
|
25273
|
+
case "error":
|
|
25274
|
+
process2.error(String(message.text));
|
|
25275
|
+
return;
|
|
25276
|
+
case "rawmode":
|
|
25277
|
+
process2.emit("rawmode", Boolean(message.enabled));
|
|
25278
|
+
return;
|
|
25279
|
+
case "exit":
|
|
25280
|
+
process2.finish(Number(message.code));
|
|
25281
|
+
return;
|
|
25282
|
+
case "listen":
|
|
25283
|
+
this.proxyPort(Number(message.port), owner, worker);
|
|
25284
|
+
return;
|
|
25285
|
+
case "http-response":
|
|
25286
|
+
this.settleProxied(Number(message.id), message.response);
|
|
25287
|
+
return;
|
|
25288
|
+
case "child-start":
|
|
25289
|
+
this.startChild(worker, children, message);
|
|
25290
|
+
return;
|
|
25291
|
+
case "child-stdin":
|
|
25292
|
+
children.get(message.id)?.sendStdin?.(String(message.data));
|
|
25293
|
+
return;
|
|
25294
|
+
case "child-stdin-end":
|
|
25295
|
+
children.get(message.id)?.endStdin?.();
|
|
25296
|
+
return;
|
|
25297
|
+
case "child-kill":
|
|
25298
|
+
children.get(message.id)?.kill(String(message.signal));
|
|
25299
|
+
return;
|
|
25300
|
+
default:
|
|
25301
|
+
return;
|
|
25302
|
+
}
|
|
25303
|
+
});
|
|
25304
|
+
worker.postMessage({
|
|
25305
|
+
type: "start",
|
|
25306
|
+
buffers,
|
|
25307
|
+
script,
|
|
25308
|
+
cwd,
|
|
25309
|
+
env: env2,
|
|
25310
|
+
argv: Array.isArray(options.argv) ? options.argv : ["/usr/bin/node", script, ...args.slice(1)],
|
|
25311
|
+
aliases: this.aliases,
|
|
25312
|
+
...typeof options.stdinPath === "string" ? { stdinPath: options.stdinPath } : {},
|
|
25313
|
+
...options.interactiveStdin ? { interactiveStdin: true } : {},
|
|
25314
|
+
...options.tty ? { tty: true } : {}
|
|
25315
|
+
});
|
|
25316
|
+
process2.begin();
|
|
25317
|
+
return process2;
|
|
25318
|
+
}
|
|
25319
|
+
/** Start an asynchronous child on the host's behalf and relay its events. */
|
|
25320
|
+
startChild(worker, children, message) {
|
|
25321
|
+
const handle = this.processManager.spawn(message.config);
|
|
25322
|
+
children.set(message.id, handle);
|
|
25323
|
+
for (const event of ["stdout", "stderr", "exit"]) {
|
|
25324
|
+
handle.on(event, (value) => worker.postMessage({ type: "child-event", id: message.id, event, value }));
|
|
25325
|
+
}
|
|
25326
|
+
handle.exec();
|
|
25327
|
+
}
|
|
25328
|
+
/** Run a child to completion and collect it, for the guest's `spawnSync`. */
|
|
25329
|
+
runChildToCompletion(request, streamTo) {
|
|
25330
|
+
return new Promise((resolve2) => {
|
|
25331
|
+
let handle;
|
|
25332
|
+
try {
|
|
25333
|
+
handle = this.processManager.spawn({
|
|
25334
|
+
command: request.command,
|
|
25335
|
+
args: request.args,
|
|
25336
|
+
cwd: request.cwd,
|
|
25337
|
+
...request.env ? { env: request.env } : {},
|
|
25338
|
+
...request.inheritStdio ? { inheritStdio: true } : {}
|
|
25339
|
+
});
|
|
25340
|
+
} catch (error) {
|
|
25341
|
+
const failure = error;
|
|
25342
|
+
resolve2({ status: null, stdout: "", stderr: "", signal: null, error: { ...failure.code ? { code: failure.code } : {}, message: failure.message } });
|
|
25343
|
+
return;
|
|
25344
|
+
}
|
|
25345
|
+
let stdout = "";
|
|
25346
|
+
let stderr = "";
|
|
25347
|
+
const live = request.inheritStdio ? streamTo : null;
|
|
25348
|
+
handle.on("stdout", (text2) => {
|
|
25349
|
+
stdout += text2;
|
|
25350
|
+
live?.output(text2);
|
|
25351
|
+
});
|
|
25352
|
+
handle.on("stderr", (text2) => {
|
|
25353
|
+
stderr += text2;
|
|
25354
|
+
live?.error(text2);
|
|
25355
|
+
});
|
|
25356
|
+
handle.on("exit", (code) => resolve2({ status: code, stdout, stderr, signal: null }));
|
|
25357
|
+
handle.exec();
|
|
25358
|
+
if (request.input !== void 0) {
|
|
25359
|
+
handle.sendStdin?.(request.input);
|
|
25360
|
+
}
|
|
25361
|
+
if (!request.inheritStdio) handle.endStdin?.();
|
|
25362
|
+
});
|
|
25363
|
+
}
|
|
25364
|
+
// ── HTTP servers living on another thread ─────────────────────────────────
|
|
25365
|
+
proxies = /* @__PURE__ */ new Map();
|
|
25366
|
+
waiting = /* @__PURE__ */ new Map();
|
|
25367
|
+
nextRequestId = 1;
|
|
25368
|
+
/**
|
|
25369
|
+
* Register a stand-in for a server that is actually running in the Worker.
|
|
25370
|
+
*
|
|
25371
|
+
* The router only knows how to reach servers on this thread, so each bound
|
|
25372
|
+
* port gets a local server whose whole job is to forward and wait.
|
|
25373
|
+
*/
|
|
25374
|
+
proxyPort(port, owner, worker) {
|
|
25375
|
+
if (this.proxies.has(port)) return;
|
|
25376
|
+
const server = new VirtualHttpServer(this.router, owner, (request, response) => {
|
|
25377
|
+
void this.forward(worker, port, request, response);
|
|
25378
|
+
});
|
|
25379
|
+
try {
|
|
25380
|
+
server.listen(port);
|
|
25381
|
+
this.proxies.set(port, server);
|
|
25382
|
+
} catch {
|
|
25383
|
+
}
|
|
25384
|
+
}
|
|
25385
|
+
async forward(worker, port, request, response) {
|
|
25386
|
+
const chunks = [];
|
|
25387
|
+
for await (const chunk of request) chunks.push(chunk);
|
|
25388
|
+
const body = concat4(chunks);
|
|
25389
|
+
const id = this.nextRequestId++;
|
|
25390
|
+
const answered = new Promise((resolve2) => this.waiting.set(id, resolve2));
|
|
25391
|
+
worker.postMessage({
|
|
25392
|
+
type: "http-request",
|
|
25393
|
+
id,
|
|
25394
|
+
port,
|
|
25395
|
+
init: { method: request.method, path: request.url, headers: request.headers, body }
|
|
25396
|
+
});
|
|
25397
|
+
let result;
|
|
25398
|
+
try {
|
|
25399
|
+
result = await answered;
|
|
25400
|
+
} catch (error) {
|
|
25401
|
+
this.waiting.delete(id);
|
|
25402
|
+
response.writeHead(500, "Internal Server Error", {});
|
|
25403
|
+
response.end(new TextEncoder().encode(error instanceof Error ? error.message : String(error)));
|
|
25404
|
+
return;
|
|
25405
|
+
}
|
|
25406
|
+
response.writeHead(Number(result.statusCode ?? 200), String(result.statusMessage ?? ""), result.headers ?? {});
|
|
25407
|
+
response.end(result.body ?? new Uint8Array());
|
|
25408
|
+
}
|
|
25409
|
+
settleProxied(id, response) {
|
|
25410
|
+
const resolve2 = this.waiting.get(id);
|
|
25411
|
+
if (!resolve2) return;
|
|
25412
|
+
this.waiting.delete(id);
|
|
25413
|
+
resolve2(response);
|
|
25414
|
+
}
|
|
25415
|
+
closeProxies(owner) {
|
|
25416
|
+
for (const [port, server] of [...this.proxies]) {
|
|
25417
|
+
if (server.owner !== owner) continue;
|
|
25418
|
+
server.close();
|
|
25419
|
+
this.proxies.delete(port);
|
|
25420
|
+
}
|
|
25421
|
+
}
|
|
25422
|
+
teardown() {
|
|
25423
|
+
for (const { worker, server } of [...this.live]) {
|
|
25424
|
+
server.close();
|
|
25425
|
+
void worker.terminate();
|
|
25426
|
+
}
|
|
25427
|
+
this.live.clear();
|
|
25428
|
+
super.teardown();
|
|
25429
|
+
}
|
|
25430
|
+
/** Does anything under `cwd` need a module only the host can supply? */
|
|
25431
|
+
needsHostModules(cwd) {
|
|
25432
|
+
return packageIsInstalled(this.volume, cwd, "rolldown");
|
|
25433
|
+
}
|
|
25434
|
+
};
|
|
25435
|
+
function isRecord2(value) {
|
|
25436
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25437
|
+
}
|
|
25438
|
+
function concat4(parts) {
|
|
25439
|
+
if (parts.length === 0) return new Uint8Array();
|
|
25440
|
+
if (parts.length === 1) return parts[0];
|
|
25441
|
+
const total = parts.reduce((sum, part) => sum + part.length, 0);
|
|
25442
|
+
const joined = new Uint8Array(total);
|
|
25443
|
+
let at = 0;
|
|
25444
|
+
for (const part of parts) {
|
|
25445
|
+
joined.set(part, at);
|
|
25446
|
+
at += part.length;
|
|
25447
|
+
}
|
|
25448
|
+
return joined;
|
|
24586
25449
|
}
|
|
24587
25450
|
|
|
24588
25451
|
// src/container/container.ts
|
|
@@ -24629,11 +25492,13 @@ var Container = class _Container {
|
|
|
24629
25492
|
// ── boot ──────────────────────────────────────────────────────────────────
|
|
24630
25493
|
static async create(opts = {}) {
|
|
24631
25494
|
if (opts.python) configurePython(opts.python);
|
|
24632
|
-
const
|
|
25495
|
+
const podOptions = {
|
|
24633
25496
|
workdir: opts.cwd ?? "/",
|
|
24634
25497
|
env: opts.env ?? {},
|
|
24635
25498
|
...opts.onServerReady ? { onServerReady: opts.onServerReady } : {}
|
|
24636
|
-
}
|
|
25499
|
+
};
|
|
25500
|
+
const workerOptions = { ...podOptions, ...opts.workerUrl ? { workerUrl: opts.workerUrl } : {} };
|
|
25501
|
+
const pod = opts.pod ?? (opts.isolation === "realm" ? null : await WorkerRuntimePod.tryBoot(workerOptions)) ?? await LocalRuntimePod.boot(podOptions);
|
|
24637
25502
|
const kernel = new Kernel({
|
|
24638
25503
|
pod,
|
|
24639
25504
|
hostname: opts.hostname ?? "sandbox",
|
|
@@ -24875,15 +25740,15 @@ var Container = class _Container {
|
|
|
24875
25740
|
}
|
|
24876
25741
|
makeStdio(opts) {
|
|
24877
25742
|
const combined = [];
|
|
24878
|
-
const
|
|
25743
|
+
const decoder8 = new TextDecoder();
|
|
24879
25744
|
const stdout = new BufferSink((chunk) => {
|
|
24880
|
-
const text2 =
|
|
25745
|
+
const text2 = decoder8.decode(chunk, { stream: true });
|
|
24881
25746
|
combined.push(text2);
|
|
24882
25747
|
opts.onStdout?.(text2);
|
|
24883
25748
|
this.hooks.onStdout?.(text2);
|
|
24884
25749
|
});
|
|
24885
25750
|
const stderr = new BufferSink((chunk) => {
|
|
24886
|
-
const text2 =
|
|
25751
|
+
const text2 = decoder8.decode(chunk, { stream: true });
|
|
24887
25752
|
combined.push(text2);
|
|
24888
25753
|
opts.onStderr?.(text2);
|
|
24889
25754
|
this.hooks.onStderr?.(text2);
|
|
@@ -25494,8 +26359,99 @@ init_variables();
|
|
|
25494
26359
|
init_arith();
|
|
25495
26360
|
init_expand();
|
|
25496
26361
|
init_builtins();
|
|
26362
|
+
|
|
26363
|
+
// src/preview/register.ts
|
|
26364
|
+
function serveContainerOn(port, box) {
|
|
26365
|
+
port.onmessage = async (event) => {
|
|
26366
|
+
const request = event.data;
|
|
26367
|
+
try {
|
|
26368
|
+
const response = await box.request(request.port, {
|
|
26369
|
+
method: request.method,
|
|
26370
|
+
path: request.path,
|
|
26371
|
+
headers: request.headers,
|
|
26372
|
+
...request.body ? { body: new Uint8Array(request.body) } : {}
|
|
26373
|
+
});
|
|
26374
|
+
const bytes2 = response.bytes.slice();
|
|
26375
|
+
port.postMessage(
|
|
26376
|
+
{
|
|
26377
|
+
id: request.id,
|
|
26378
|
+
response: {
|
|
26379
|
+
status: response.status,
|
|
26380
|
+
statusText: response.statusText,
|
|
26381
|
+
headers: response.headers,
|
|
26382
|
+
body: bytes2.buffer
|
|
26383
|
+
}
|
|
26384
|
+
},
|
|
26385
|
+
[bytes2.buffer]
|
|
26386
|
+
);
|
|
26387
|
+
} catch (error) {
|
|
26388
|
+
const message = new TextEncoder().encode(error instanceof Error ? error.message : String(error));
|
|
26389
|
+
port.postMessage({
|
|
26390
|
+
id: request.id,
|
|
26391
|
+
response: { status: 502, statusText: "Bad Gateway", headers: {}, body: message.buffer }
|
|
26392
|
+
});
|
|
26393
|
+
}
|
|
26394
|
+
};
|
|
26395
|
+
port.start?.();
|
|
26396
|
+
}
|
|
26397
|
+
async function createPreview(box, options = {}) {
|
|
26398
|
+
if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) return null;
|
|
26399
|
+
const scriptUrl = options.scriptUrl ?? new URL("./service-worker.js", import.meta.url);
|
|
26400
|
+
let registration;
|
|
26401
|
+
try {
|
|
26402
|
+
registration = await navigator.serviceWorker.register(scriptUrl, {
|
|
26403
|
+
type: "module",
|
|
26404
|
+
...options.scope ? { scope: options.scope } : {}
|
|
26405
|
+
});
|
|
26406
|
+
} catch {
|
|
26407
|
+
return null;
|
|
26408
|
+
}
|
|
26409
|
+
const worker = registration.active ?? registration.waiting ?? registration.installing;
|
|
26410
|
+
if (!worker) return null;
|
|
26411
|
+
if (worker.state !== "activated") {
|
|
26412
|
+
const activated = await new Promise((resolve2) => {
|
|
26413
|
+
const check = () => {
|
|
26414
|
+
if (worker.state === "activated") {
|
|
26415
|
+
worker.removeEventListener("statechange", check);
|
|
26416
|
+
resolve2(true);
|
|
26417
|
+
} else if (worker.state === "redundant") {
|
|
26418
|
+
worker.removeEventListener("statechange", check);
|
|
26419
|
+
resolve2(false);
|
|
26420
|
+
}
|
|
26421
|
+
};
|
|
26422
|
+
worker.addEventListener("statechange", check);
|
|
26423
|
+
setTimeout(() => resolve2(worker.state === "activated"), 1e4);
|
|
26424
|
+
check();
|
|
26425
|
+
});
|
|
26426
|
+
if (!activated) return null;
|
|
26427
|
+
}
|
|
26428
|
+
const channel = new MessageChannel();
|
|
26429
|
+
serveContainerOn(channel.port1, box);
|
|
26430
|
+
worker.postMessage({ type: "sandboxedjs:connect" }, [channel.port2]);
|
|
26431
|
+
const base2 = registration.scope.replace(/\/$/, "");
|
|
26432
|
+
return {
|
|
26433
|
+
urlFor: (port) => `${base2}/__sbx__/${port}/`,
|
|
26434
|
+
dispose: async () => {
|
|
26435
|
+
channel.port1.close();
|
|
26436
|
+
await registration.unregister();
|
|
26437
|
+
}
|
|
26438
|
+
};
|
|
26439
|
+
}
|
|
26440
|
+
async function renderInto(box, element, options = { port: 80 }) {
|
|
26441
|
+
const response = await box.request(options.port, { path: options.path ?? "/" });
|
|
26442
|
+
const frame = document.createElement("iframe");
|
|
26443
|
+
frame.setAttribute("sandbox", "allow-scripts");
|
|
26444
|
+
frame.style.width = "100%";
|
|
26445
|
+
frame.style.height = "100%";
|
|
26446
|
+
frame.style.border = "0";
|
|
26447
|
+
frame.srcdoc = response.body;
|
|
26448
|
+
element.replaceChildren(frame);
|
|
26449
|
+
return frame;
|
|
26450
|
+
}
|
|
26451
|
+
|
|
26452
|
+
// src/index.ts
|
|
25497
26453
|
var src_default = createContainer;
|
|
25498
26454
|
|
|
25499
|
-
export { ArithError, BufferSink, CallbackSink, CleanPackageInstaller, CommandRegistry, CommonJsEngine, Container, ContainerFs, ERRNO, FileInput, FileOutput, IncompleteInputError, KERNEL_NAME, KERNEL_RELEASE, Kernel, LocalRuntimePod, MemoryVolume, NODE_VERSION, NPM_VERSION, NetworkStack, NullInput, NullOutput, OS_RELEASE, PYTHON_VERSION, Pipe, Process, ProcessTable, ROOT_CRED, SIGNALS, SIGNAL_NAMES, Session, Shell, ShellExit, Lexer as ShellLexer, ShellSyntaxError, Stats, SysError, TeeOutput, Terminal, UserDatabase, Variables, Vfs, VirtualHttpRouter, VirtualHttpServer, VirtualIncomingMessage, VirtualServerResponse, WASM_ALIASES, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, configureCPython, configurePython, createChildProcessModule, createContainer, createContext, createCoreModules, src_default as default, defineCommand, evalArith, exitCodeForSignal, expandPrompt, expandWord, expandWords, extractNpmTarball, fnmatch, formatMode, getBuiltin, glob, globToRegex, hasMagic, installUserland, isBuiltinName, isCPythonAvailable, isPythonAvailable, isSysError, looksLikeEsm, makeCred, normalizeSignal, octalMode, parse as parseShell, parseUmask, path_exports as posixPath, resetPidCounter, shellQuote, strerror, transformEsm, unameInfo };
|
|
26455
|
+
export { ArithError, BufferSink, CallbackSink, CleanPackageInstaller, CommandRegistry, CommonJsEngine, Container, ContainerFs, ERRNO, FileInput, FileOutput, IncompleteInputError, KERNEL_NAME, KERNEL_RELEASE, Kernel, LocalRuntimePod, MemoryVolume, NODE_VERSION, NPM_VERSION, NetworkStack, NullInput, NullOutput, OS_RELEASE, PYTHON_VERSION, Pipe, Process, ProcessTable, ROOT_CRED, SIGNALS, SIGNAL_NAMES, Session, Shell, ShellExit, Lexer as ShellLexer, ShellSyntaxError, Stats, SysError, TeeOutput, Terminal, UserDatabase, Variables, Vfs, VirtualHttpRouter, VirtualHttpServer, VirtualIncomingMessage, VirtualServerResponse, WASM_ALIASES, WorkerRuntimePod, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, configureCPython, configurePython, createChildProcessModule, createContainer, createContext, createCoreModules, createPreview, src_default as default, defineCommand, evalArith, exitCodeForSignal, expandPrompt, expandWord, expandWords, extractNpmTarball, fnmatch, formatMode, getBuiltin, glob, globToRegex, hasMagic, installUserland, isBuiltinName, isCPythonAvailable, isPythonAvailable, isSysError, looksLikeEsm, makeCred, normalizeSignal, octalMode, parse as parseShell, parseUmask, path_exports as posixPath, renderInto, resetPidCounter, shellQuote, startRuntimeWorker, strerror, syncChannelSupported, transformEsm, unameInfo };
|
|
25500
26456
|
//# sourceMappingURL=index.js.map
|
|
25501
26457
|
//# sourceMappingURL=index.js.map
|