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.cjs
CHANGED
|
@@ -17959,22 +17959,22 @@ async function runCPythonProgram(ctx, source, argv, scriptDir, stdinText) {
|
|
|
17959
17959
|
});
|
|
17960
17960
|
} catch {
|
|
17961
17961
|
}
|
|
17962
|
-
const
|
|
17963
|
-
const
|
|
17962
|
+
const encoder8 = new TextEncoder();
|
|
17963
|
+
const decoder8 = new TextDecoder();
|
|
17964
17964
|
py.setStdout({
|
|
17965
17965
|
write: (buffer) => {
|
|
17966
|
-
ctx.write(
|
|
17966
|
+
ctx.write(decoder8.decode(buffer));
|
|
17967
17967
|
return buffer.length;
|
|
17968
17968
|
}
|
|
17969
17969
|
});
|
|
17970
17970
|
py.setStderr({
|
|
17971
17971
|
write: (buffer) => {
|
|
17972
|
-
ctx.stderr.write(
|
|
17972
|
+
ctx.stderr.write(decoder8.decode(buffer));
|
|
17973
17973
|
return buffer.length;
|
|
17974
17974
|
}
|
|
17975
17975
|
});
|
|
17976
17976
|
if (stdinText !== null) {
|
|
17977
|
-
const bytes2 =
|
|
17977
|
+
const bytes2 = encoder8.encode(stdinText);
|
|
17978
17978
|
let offset = 0;
|
|
17979
17979
|
py.setStdin({
|
|
17980
17980
|
read: (buffer) => {
|
|
@@ -18025,9 +18025,9 @@ async function runCPythonRepl(ctx) {
|
|
|
18025
18025
|
}
|
|
18026
18026
|
mountContainerDirs(py, ctx);
|
|
18027
18027
|
bootstrap(py, ctx, [""], null);
|
|
18028
|
-
const
|
|
18029
|
-
py.setStdout({ write: (data) => (ctx.write(
|
|
18030
|
-
py.setStderr({ write: (data) => (ctx.stderr.write(
|
|
18028
|
+
const decoder8 = new TextDecoder();
|
|
18029
|
+
py.setStdout({ write: (data) => (ctx.write(decoder8.decode(data)), data.length) });
|
|
18030
|
+
py.setStderr({ write: (data) => (ctx.stderr.write(decoder8.decode(data)), data.length) });
|
|
18031
18031
|
ctx.line(`Python ${String(py.runPython("import sys; sys.version.split()[0]"))} (Pyodide)`);
|
|
18032
18032
|
ctx.line('Type "help()" for more information.');
|
|
18033
18033
|
let source = "";
|
|
@@ -19638,15 +19638,15 @@ var Session = class {
|
|
|
19638
19638
|
async run(command, opts = {}) {
|
|
19639
19639
|
if (this.closed) throw new Error("session is closed");
|
|
19640
19640
|
const combined = [];
|
|
19641
|
-
const
|
|
19641
|
+
const decoder8 = new TextDecoder();
|
|
19642
19642
|
const stdout = new BufferSink((chunk) => {
|
|
19643
|
-
const text2 =
|
|
19643
|
+
const text2 = decoder8.decode(chunk, { stream: true });
|
|
19644
19644
|
combined.push(text2);
|
|
19645
19645
|
opts.onStdout?.(text2);
|
|
19646
19646
|
this.hooks.onStdout?.(text2);
|
|
19647
19647
|
});
|
|
19648
19648
|
const stderr = new BufferSink((chunk) => {
|
|
19649
|
-
const text2 =
|
|
19649
|
+
const text2 = decoder8.decode(chunk, { stream: true });
|
|
19650
19650
|
combined.push(text2);
|
|
19651
19651
|
opts.onStderr?.(text2);
|
|
19652
19652
|
this.hooks.onStderr?.(text2);
|
|
@@ -19712,6 +19712,10 @@ var KernelChildProcess = class {
|
|
|
19712
19712
|
started = false;
|
|
19713
19713
|
cancelled = false;
|
|
19714
19714
|
constructor(kernel, cred, config, pid) {
|
|
19715
|
+
if (config.inheritStdio) {
|
|
19716
|
+
this.stdin.isTTY = true;
|
|
19717
|
+
this.stdin.interactive = true;
|
|
19718
|
+
}
|
|
19715
19719
|
this.kernel = kernel;
|
|
19716
19720
|
this.cred = cred;
|
|
19717
19721
|
this.pid = pid;
|
|
@@ -21832,7 +21836,7 @@ var VirtualClientRequest = class extends streamModule4__default.default.Writable
|
|
|
21832
21836
|
};
|
|
21833
21837
|
let settled = false;
|
|
21834
21838
|
try {
|
|
21835
|
-
const response =
|
|
21839
|
+
const response = isLoopback(this.target.hostname) ? await this.viaRouter(body) : await this.viaFetch(body);
|
|
21836
21840
|
if (this.destroyedByUser) return;
|
|
21837
21841
|
clearTimeout(this.timer);
|
|
21838
21842
|
settled = true;
|
|
@@ -21854,6 +21858,12 @@ var VirtualClientRequest = class extends streamModule4__default.default.Writable
|
|
|
21854
21858
|
}
|
|
21855
21859
|
}
|
|
21856
21860
|
async viaRouter(body) {
|
|
21861
|
+
if (!this.router.activePortsIncludes(this.target.port)) {
|
|
21862
|
+
throw Object.assign(
|
|
21863
|
+
new Error(`connect ECONNREFUSED ${this.target.hostname}:${this.target.port}`),
|
|
21864
|
+
{ code: "ECONNREFUSED", errno: -61, syscall: "connect", address: this.target.hostname, port: this.target.port }
|
|
21865
|
+
);
|
|
21866
|
+
}
|
|
21857
21867
|
const result = await this.router.request(this.target.port, {
|
|
21858
21868
|
method: this.target.method,
|
|
21859
21869
|
path: this.target.path,
|
|
@@ -22110,7 +22120,8 @@ var ChildProcess = class extends EventEmitter4__default.default {
|
|
|
22110
22120
|
queueMicrotask(() => this.emit("close", code, null));
|
|
22111
22121
|
}
|
|
22112
22122
|
};
|
|
22113
|
-
function createChildProcessModule(spawnChild, defaultCwd) {
|
|
22123
|
+
function createChildProcessModule(spawnChild, defaultCwd, syncSpawn, defaultEnv = () => ({})) {
|
|
22124
|
+
const environmentFor = (options) => options.env ? { ...options.env } : defaultEnv();
|
|
22114
22125
|
const throughShell = (command, options) => {
|
|
22115
22126
|
const shell = typeof options.shell === "string" ? options.shell : "/bin/sh";
|
|
22116
22127
|
return { file: shell, args: ["-c", command] };
|
|
@@ -22121,7 +22132,7 @@ function createChildProcessModule(spawnChild, defaultCwd) {
|
|
|
22121
22132
|
command: resolved.file,
|
|
22122
22133
|
args: resolved.args,
|
|
22123
22134
|
cwd: options.cwd ?? defaultCwd(),
|
|
22124
|
-
|
|
22135
|
+
env: environmentFor(options)
|
|
22125
22136
|
});
|
|
22126
22137
|
return new ChildProcess(handle, resolved.file, resolved.args);
|
|
22127
22138
|
};
|
|
@@ -22167,20 +22178,81 @@ ${err.join("")}`),
|
|
|
22167
22178
|
exec,
|
|
22168
22179
|
execFile,
|
|
22169
22180
|
fork: (modulePath, args = [], options = {}) => spawn("node", [modulePath, ...args], options),
|
|
22170
|
-
|
|
22171
|
-
execFileSync: unavailable("execFileSync"),
|
|
22172
|
-
spawnSync: unavailable("spawnSync"),
|
|
22181
|
+
...buildSyncFamily(syncSpawn, throughShell, defaultCwd, environmentFor),
|
|
22173
22182
|
ChildProcess
|
|
22174
22183
|
};
|
|
22175
22184
|
}
|
|
22185
|
+
function buildSyncFamily(syncSpawn, throughShell, defaultCwd, environmentFor) {
|
|
22186
|
+
if (!syncSpawn) {
|
|
22187
|
+
return {
|
|
22188
|
+
execSync: unavailable("execSync"),
|
|
22189
|
+
execFileSync: unavailable("execFileSync"),
|
|
22190
|
+
spawnSync: unavailable("spawnSync")
|
|
22191
|
+
};
|
|
22192
|
+
}
|
|
22193
|
+
const run = (file3, args, options) => {
|
|
22194
|
+
const resolved = options.shell ? throughShell([file3, ...args].join(" "), options) : { file: file3, args };
|
|
22195
|
+
const input = options.input === void 0 ? void 0 : typeof options.input === "string" ? options.input : new TextDecoder().decode(options.input);
|
|
22196
|
+
const inherit = options.stdio === "inherit" || Array.isArray(options.stdio) && options.stdio[0] === "inherit";
|
|
22197
|
+
return syncSpawn({
|
|
22198
|
+
command: resolved.file,
|
|
22199
|
+
args: resolved.args,
|
|
22200
|
+
cwd: options.cwd ?? defaultCwd(),
|
|
22201
|
+
env: environmentFor(options),
|
|
22202
|
+
...input === void 0 ? {} : { input },
|
|
22203
|
+
...inherit ? { inheritStdio: true } : {}
|
|
22204
|
+
});
|
|
22205
|
+
};
|
|
22206
|
+
const asOutput = (text2, options) => options.encoding === "buffer" || options.encoding === void 0 ? Buffer2.from(text2) : text2;
|
|
22207
|
+
const orThrow = (result, command, options) => {
|
|
22208
|
+
if (result.error) {
|
|
22209
|
+
throw Object.assign(new Error(result.error.message), {
|
|
22210
|
+
...result.error.code ? { code: result.error.code } : {},
|
|
22211
|
+
stdout: asOutput(result.stdout, options),
|
|
22212
|
+
stderr: asOutput(result.stderr, options)
|
|
22213
|
+
});
|
|
22214
|
+
}
|
|
22215
|
+
if (result.status !== 0) {
|
|
22216
|
+
throw Object.assign(new Error(`Command failed: ${command}
|
|
22217
|
+
${result.stderr}`), {
|
|
22218
|
+
status: result.status,
|
|
22219
|
+
stdout: asOutput(result.stdout, options),
|
|
22220
|
+
stderr: asOutput(result.stderr, options)
|
|
22221
|
+
});
|
|
22222
|
+
}
|
|
22223
|
+
return asOutput(result.stdout, options);
|
|
22224
|
+
};
|
|
22225
|
+
const spawnSync = (file3, args = [], options = {}) => {
|
|
22226
|
+
const [list, opts] = Array.isArray(args) ? [args, options] : [[], args];
|
|
22227
|
+
const result = run(file3, list, opts);
|
|
22228
|
+
return {
|
|
22229
|
+
pid: 0,
|
|
22230
|
+
status: result.status,
|
|
22231
|
+
signal: result.signal,
|
|
22232
|
+
stdout: asOutput(result.stdout, opts),
|
|
22233
|
+
stderr: asOutput(result.stderr, opts),
|
|
22234
|
+
output: [null, asOutput(result.stdout, opts), asOutput(result.stderr, opts)],
|
|
22235
|
+
...result.error ? { error: Object.assign(new Error(result.error.message), result.error.code ? { code: result.error.code } : {}) } : {}
|
|
22236
|
+
};
|
|
22237
|
+
};
|
|
22238
|
+
const execFileSync = (file3, args = [], options = {}) => {
|
|
22239
|
+
const [list, opts] = Array.isArray(args) ? [args, options] : [[], args];
|
|
22240
|
+
return orThrow(run(file3, list, opts), [file3, ...list].join(" "), opts);
|
|
22241
|
+
};
|
|
22242
|
+
const execSync = (command, options = {}) => orThrow(run(command, [], { ...options, shell: options.shell ?? true }), command, options);
|
|
22243
|
+
return { spawnSync, execFileSync, execSync };
|
|
22244
|
+
}
|
|
22176
22245
|
function normalize2(options, callback) {
|
|
22177
22246
|
if (typeof options === "function") return [{}, options];
|
|
22178
22247
|
return [options ?? {}, callback];
|
|
22179
22248
|
}
|
|
22180
22249
|
function unavailable(name) {
|
|
22181
|
-
return () => {
|
|
22250
|
+
return (...args) => {
|
|
22251
|
+
const file3 = typeof args[0] === "string" ? args[0] : void 0;
|
|
22252
|
+
const list = Array.isArray(args[1]) ? args[1].map(String) : [];
|
|
22253
|
+
const command = file3 ? [file3, ...list].join(" ") : void 0;
|
|
22182
22254
|
const error = new Error(
|
|
22183
|
-
`child_process.${name} is not supported in the sandboxed runtime
|
|
22255
|
+
`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.`
|
|
22184
22256
|
);
|
|
22185
22257
|
error.code = "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM";
|
|
22186
22258
|
throw error;
|
|
@@ -22244,7 +22316,11 @@ function parseKeys(input) {
|
|
|
22244
22316
|
const name = CSI_KEYS[sequence];
|
|
22245
22317
|
keys.push({
|
|
22246
22318
|
sequence: `\x1B${sequence}`,
|
|
22247
|
-
|
|
22319
|
+
/* Node leaves this undefined for a sequence it does not recognise.
|
|
22320
|
+
* The string "undefined" is not the same thing: a caller testing
|
|
22321
|
+
* `key.name === undefined` would take an unknown key for a known
|
|
22322
|
+
* one. */
|
|
22323
|
+
name,
|
|
22248
22324
|
ctrl: false,
|
|
22249
22325
|
/* xterm reports modifiers as `;5` (control) and `;2` (shift) before
|
|
22250
22326
|
* the final letter. */
|
|
@@ -22804,7 +22880,7 @@ function createCoreModules(options) {
|
|
|
22804
22880
|
};
|
|
22805
22881
|
const http = options.http ? createHttpModule(options.http.router, options.http.owner, httpOptions, "http:") : createUnsupportedModule("http");
|
|
22806
22882
|
const https = options.http ? createHttpModule(options.http.router, options.http.owner, httpOptions, "https:") : createUnsupportedModule("https");
|
|
22807
|
-
const childProcess = options.spawnChild ? createChildProcessModule(options.spawnChild, () => cwd) : createUnsupportedModule("child_process");
|
|
22883
|
+
const childProcess = options.spawnChild ? createChildProcessModule(options.spawnChild, () => cwd, options.syncSpawn, () => ({ ...processObject.env })) : createUnsupportedModule("child_process");
|
|
22808
22884
|
const readline = createReadlineModule(() => processObject.stdin, () => processObject.stdout);
|
|
22809
22885
|
const dns = createDnsModule();
|
|
22810
22886
|
const builtins = {
|
|
@@ -22865,6 +22941,7 @@ function createCoreModules(options) {
|
|
|
22865
22941
|
pendingUnrefed: timers.pendingUnrefed,
|
|
22866
22942
|
/** Client requests sent but not yet read to completion. */
|
|
22867
22943
|
pendingRequests: () => inFlightRequests,
|
|
22944
|
+
cancelTimers: timers.cancelAll,
|
|
22868
22945
|
writeStdin: (data) => {
|
|
22869
22946
|
if (options.interactiveStdin) stdin.write(data);
|
|
22870
22947
|
},
|
|
@@ -23546,6 +23623,27 @@ function createTrackedTimers() {
|
|
|
23546
23623
|
live.delete(handle);
|
|
23547
23624
|
native(handle);
|
|
23548
23625
|
};
|
|
23626
|
+
const cancelAll = () => {
|
|
23627
|
+
for (const handle of [...live, ...unrefed]) {
|
|
23628
|
+
const state = states.get(handle);
|
|
23629
|
+
if (!state) continue;
|
|
23630
|
+
state.active = false;
|
|
23631
|
+
try {
|
|
23632
|
+
clearTimeout(state.native);
|
|
23633
|
+
} catch {
|
|
23634
|
+
}
|
|
23635
|
+
try {
|
|
23636
|
+
clearInterval(state.native);
|
|
23637
|
+
} catch {
|
|
23638
|
+
}
|
|
23639
|
+
try {
|
|
23640
|
+
(globalThis.clearImmediate ?? clearTimeout)(state.native);
|
|
23641
|
+
} catch {
|
|
23642
|
+
}
|
|
23643
|
+
}
|
|
23644
|
+
live.clear();
|
|
23645
|
+
unrefed.clear();
|
|
23646
|
+
};
|
|
23549
23647
|
return {
|
|
23550
23648
|
api: {
|
|
23551
23649
|
setTimeout: setTimeoutTracked,
|
|
@@ -23556,7 +23654,8 @@ function createTrackedTimers() {
|
|
|
23556
23654
|
clearImmediate: (handle) => clear2(handle, globalThis.clearImmediate ?? clearTimeout)
|
|
23557
23655
|
},
|
|
23558
23656
|
pending: () => live.size,
|
|
23559
|
-
pendingUnrefed: () => unrefed.size
|
|
23657
|
+
pendingUnrefed: () => unrefed.size,
|
|
23658
|
+
cancelAll
|
|
23560
23659
|
};
|
|
23561
23660
|
}
|
|
23562
23661
|
function createTimerPromises() {
|
|
@@ -24010,6 +24109,195 @@ var MemoryVolume = class {
|
|
|
24010
24109
|
}
|
|
24011
24110
|
};
|
|
24012
24111
|
|
|
24112
|
+
// src/runtime/mirroring-volume.ts
|
|
24113
|
+
init_path();
|
|
24114
|
+
var MirroringVolume = class {
|
|
24115
|
+
constructor(inner = new MemoryVolume()) {
|
|
24116
|
+
this.inner = inner;
|
|
24117
|
+
}
|
|
24118
|
+
inner;
|
|
24119
|
+
mirror;
|
|
24120
|
+
root = "/";
|
|
24121
|
+
/**
|
|
24122
|
+
* Start mirroring the tree under `root`, seeding it with what is there now.
|
|
24123
|
+
*
|
|
24124
|
+
* Called once the Rolldown binding is known to be in play; before that a
|
|
24125
|
+
* container pays nothing for this.
|
|
24126
|
+
*/
|
|
24127
|
+
attach(mirror, root) {
|
|
24128
|
+
const cleanRoot = clean(root);
|
|
24129
|
+
if (this.mirror === mirror && this.root === cleanRoot) return;
|
|
24130
|
+
this.mirror = mirror;
|
|
24131
|
+
this.root = cleanRoot;
|
|
24132
|
+
this.seed();
|
|
24133
|
+
}
|
|
24134
|
+
detach() {
|
|
24135
|
+
this.mirror = void 0;
|
|
24136
|
+
}
|
|
24137
|
+
/** Copy the whole subtree across. The only bulk operation that remains. */
|
|
24138
|
+
seed() {
|
|
24139
|
+
const mirror = this.mirror;
|
|
24140
|
+
if (!mirror) return;
|
|
24141
|
+
try {
|
|
24142
|
+
mirror.rmSync?.(this.root, { recursive: true, force: true });
|
|
24143
|
+
} catch {
|
|
24144
|
+
}
|
|
24145
|
+
const copy = (path) => {
|
|
24146
|
+
let stat2;
|
|
24147
|
+
try {
|
|
24148
|
+
stat2 = this.inner.lstatSync(path);
|
|
24149
|
+
} catch {
|
|
24150
|
+
return;
|
|
24151
|
+
}
|
|
24152
|
+
if (stat2.isDirectory()) {
|
|
24153
|
+
this.safely(() => mirror.mkdirSync(path, { recursive: true }));
|
|
24154
|
+
for (const name of this.inner.readdirSync(path)) copy(join(path, name));
|
|
24155
|
+
} else if (stat2.isSymbolicLink()) {
|
|
24156
|
+
this.safely(() => {
|
|
24157
|
+
mirror.mkdirSync(dirname(path), { recursive: true });
|
|
24158
|
+
mirror.symlinkSync(this.inner.readlinkSync(path), path);
|
|
24159
|
+
});
|
|
24160
|
+
} else if (!path.endsWith(".node")) {
|
|
24161
|
+
this.safely(() => {
|
|
24162
|
+
mirror.mkdirSync(dirname(path), { recursive: true });
|
|
24163
|
+
mirror.writeFileSync(path, this.inner.readFileSync(path));
|
|
24164
|
+
});
|
|
24165
|
+
}
|
|
24166
|
+
};
|
|
24167
|
+
copy(this.root);
|
|
24168
|
+
}
|
|
24169
|
+
/** Is this path inside the mirrored subtree? */
|
|
24170
|
+
mirrored(path) {
|
|
24171
|
+
if (!this.mirror) return false;
|
|
24172
|
+
const clean2 = clean(path);
|
|
24173
|
+
return this.root === "/" || clean2 === this.root || clean2.startsWith(`${this.root}/`);
|
|
24174
|
+
}
|
|
24175
|
+
/**
|
|
24176
|
+
* Run a mirror update, swallowing failure.
|
|
24177
|
+
*
|
|
24178
|
+
* The mirror is a read-only cache for another engine. If it rejects
|
|
24179
|
+
* something — an unsupported operation, a path it has not seen — the
|
|
24180
|
+
* container's own write has still happened and must still succeed.
|
|
24181
|
+
*/
|
|
24182
|
+
safely(update) {
|
|
24183
|
+
try {
|
|
24184
|
+
update();
|
|
24185
|
+
} catch {
|
|
24186
|
+
}
|
|
24187
|
+
}
|
|
24188
|
+
/** Push a path's current state across, whatever it is now. */
|
|
24189
|
+
sync(path) {
|
|
24190
|
+
const mirror = this.mirror;
|
|
24191
|
+
if (!mirror || !this.mirrored(path)) return;
|
|
24192
|
+
let stat2;
|
|
24193
|
+
try {
|
|
24194
|
+
stat2 = this.inner.lstatSync(path);
|
|
24195
|
+
} catch {
|
|
24196
|
+
this.safely(() => {
|
|
24197
|
+
if (mirror.rmSync) mirror.rmSync(path, { recursive: true, force: true });
|
|
24198
|
+
else mirror.unlinkSync?.(path);
|
|
24199
|
+
});
|
|
24200
|
+
return;
|
|
24201
|
+
}
|
|
24202
|
+
if (stat2.isDirectory()) {
|
|
24203
|
+
this.safely(() => mirror.mkdirSync(path, { recursive: true }));
|
|
24204
|
+
return;
|
|
24205
|
+
}
|
|
24206
|
+
if (stat2.isSymbolicLink()) {
|
|
24207
|
+
this.safely(() => {
|
|
24208
|
+
mirror.mkdirSync(dirname(path), { recursive: true });
|
|
24209
|
+
mirror.symlinkSync(this.inner.readlinkSync(path), path);
|
|
24210
|
+
});
|
|
24211
|
+
return;
|
|
24212
|
+
}
|
|
24213
|
+
if (path.endsWith(".node")) return;
|
|
24214
|
+
this.safely(() => {
|
|
24215
|
+
mirror.mkdirSync(dirname(path), { recursive: true });
|
|
24216
|
+
mirror.writeFileSync(path, this.inner.readFileSync(path));
|
|
24217
|
+
});
|
|
24218
|
+
}
|
|
24219
|
+
// ── reads: straight through ───────────────────────────────────────────────
|
|
24220
|
+
readFileSync(path) {
|
|
24221
|
+
return this.inner.readFileSync(path);
|
|
24222
|
+
}
|
|
24223
|
+
readdirSync(path) {
|
|
24224
|
+
return this.inner.readdirSync(path);
|
|
24225
|
+
}
|
|
24226
|
+
lstatSync(path) {
|
|
24227
|
+
return this.inner.lstatSync(path);
|
|
24228
|
+
}
|
|
24229
|
+
readlinkSync(path) {
|
|
24230
|
+
return this.inner.readlinkSync(path);
|
|
24231
|
+
}
|
|
24232
|
+
getStats() {
|
|
24233
|
+
return this.inner.getStats();
|
|
24234
|
+
}
|
|
24235
|
+
// ── writes: applied, then mirrored ────────────────────────────────────────
|
|
24236
|
+
writeFileSync(path, data) {
|
|
24237
|
+
this.inner.writeFileSync(path, data);
|
|
24238
|
+
this.sync(path);
|
|
24239
|
+
}
|
|
24240
|
+
appendFileSync(path, data) {
|
|
24241
|
+
this.inner.appendFileSync(path, data);
|
|
24242
|
+
this.sync(path);
|
|
24243
|
+
}
|
|
24244
|
+
mkdirSync(path, options) {
|
|
24245
|
+
this.inner.mkdirSync(path, options);
|
|
24246
|
+
this.sync(path);
|
|
24247
|
+
}
|
|
24248
|
+
rmdirSync(path) {
|
|
24249
|
+
this.inner.rmdirSync(path);
|
|
24250
|
+
this.sync(path);
|
|
24251
|
+
}
|
|
24252
|
+
unlinkSync(path) {
|
|
24253
|
+
this.inner.unlinkSync(path);
|
|
24254
|
+
this.sync(path);
|
|
24255
|
+
}
|
|
24256
|
+
renameSync(from, to) {
|
|
24257
|
+
this.inner.renameSync(from, to);
|
|
24258
|
+
this.sync(from);
|
|
24259
|
+
this.sync(to);
|
|
24260
|
+
}
|
|
24261
|
+
symlinkSync(target, path) {
|
|
24262
|
+
this.inner.symlinkSync(target, path);
|
|
24263
|
+
this.sync(path);
|
|
24264
|
+
}
|
|
24265
|
+
linkSync(existing, path) {
|
|
24266
|
+
this.inner.linkSync(existing, path);
|
|
24267
|
+
this.sync(path);
|
|
24268
|
+
}
|
|
24269
|
+
truncateSync(path, length) {
|
|
24270
|
+
this.inner.truncateSync(path, length);
|
|
24271
|
+
this.sync(path);
|
|
24272
|
+
}
|
|
24273
|
+
/* Permissions and timestamps do not change what a bundler resolves, and
|
|
24274
|
+
* `memfs` is stricter about them than this volume is. Applied here only. */
|
|
24275
|
+
chmodSync(path, mode) {
|
|
24276
|
+
this.inner.chmodSync(path, mode);
|
|
24277
|
+
}
|
|
24278
|
+
lchmodSync(path, mode) {
|
|
24279
|
+
this.inner.lchmodSync(path, mode);
|
|
24280
|
+
}
|
|
24281
|
+
chownSync(path, uid, gid) {
|
|
24282
|
+
this.inner.chownSync(path, uid, gid);
|
|
24283
|
+
}
|
|
24284
|
+
lchownSync(path, uid, gid) {
|
|
24285
|
+
this.inner.lchownSync(path, uid, gid);
|
|
24286
|
+
}
|
|
24287
|
+
utimesSync(path, atime, mtime) {
|
|
24288
|
+
this.inner.utimesSync(path, atime, mtime);
|
|
24289
|
+
}
|
|
24290
|
+
// ── snapshots ─────────────────────────────────────────────────────────────
|
|
24291
|
+
snapshot() {
|
|
24292
|
+
return this.inner.snapshot();
|
|
24293
|
+
}
|
|
24294
|
+
/** A restore replaces everything, so the mirror is rebuilt rather than patched. */
|
|
24295
|
+
restore(entries) {
|
|
24296
|
+
this.inner.restore(entries);
|
|
24297
|
+
this.seed();
|
|
24298
|
+
}
|
|
24299
|
+
};
|
|
24300
|
+
|
|
24013
24301
|
// src/runtime/host-esbuild.ts
|
|
24014
24302
|
var ASYNC_API = ["transform", "build", "context", "formatMessages", "analyzeMetafile"];
|
|
24015
24303
|
function createHostEsbuild() {
|
|
@@ -24079,6 +24367,7 @@ function ensureProcessGlobal() {
|
|
|
24079
24367
|
}
|
|
24080
24368
|
|
|
24081
24369
|
// src/runtime/host-rolldown.ts
|
|
24370
|
+
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'] }";
|
|
24082
24371
|
async function loadHostRolldownBinding() {
|
|
24083
24372
|
if (typeof window !== "undefined" && globalThis.crossOriginIsolated !== true) {
|
|
24084
24373
|
throw new Error(
|
|
@@ -24092,7 +24381,13 @@ async function loadHostRolldownBinding() {
|
|
|
24092
24381
|
if (typeof process !== "undefined" && process.env?.SANDBOXEDJS_DEBUG) {
|
|
24093
24382
|
console.error("[sandboxedjs] Rolldown WASI binding unavailable:", error);
|
|
24094
24383
|
}
|
|
24095
|
-
|
|
24384
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
24385
|
+
throw new Error(
|
|
24386
|
+
`The optional Rolldown WASI binding could not be loaded: ${reason}` + (typeof window === "undefined" ? "" : `
|
|
24387
|
+
|
|
24388
|
+
${BUNDLER_HINT}`),
|
|
24389
|
+
{ cause: error }
|
|
24390
|
+
);
|
|
24096
24391
|
}
|
|
24097
24392
|
}
|
|
24098
24393
|
|
|
@@ -24132,6 +24427,7 @@ var LocalProcess = class extends EventEmitter4__default.default {
|
|
|
24132
24427
|
inputEnded = false;
|
|
24133
24428
|
pendingInput = [];
|
|
24134
24429
|
resolveKilled;
|
|
24430
|
+
cleanup;
|
|
24135
24431
|
killedPromise = new Promise((resolve2) => {
|
|
24136
24432
|
this.resolveKilled = resolve2;
|
|
24137
24433
|
});
|
|
@@ -24170,13 +24466,32 @@ var LocalProcess = class extends EventEmitter4__default.default {
|
|
|
24170
24466
|
for (const chunk of this.pendingInput.splice(0)) input(chunk);
|
|
24171
24467
|
if (this.inputEnded) end();
|
|
24172
24468
|
}
|
|
24469
|
+
/**
|
|
24470
|
+
* End the process now.
|
|
24471
|
+
*
|
|
24472
|
+
* Killing has to settle `completion`, not merely record the intent. A
|
|
24473
|
+
* program with an interval outstanding has work pending forever, so the
|
|
24474
|
+
* runtime's own idle check will never end it — before this, killing such a
|
|
24475
|
+
* process left the caller awaiting a promise that could not resolve.
|
|
24476
|
+
*/
|
|
24173
24477
|
kill(_signal = "SIGTERM") {
|
|
24478
|
+
if (this.killed) return;
|
|
24174
24479
|
this.killed = true;
|
|
24175
24480
|
this.resolveKilled();
|
|
24481
|
+
this.cleanup?.();
|
|
24482
|
+
this.finish(137);
|
|
24176
24483
|
}
|
|
24177
24484
|
waitForKill() {
|
|
24178
24485
|
return this.killedPromise;
|
|
24179
24486
|
}
|
|
24487
|
+
isKilled() {
|
|
24488
|
+
return this.killed;
|
|
24489
|
+
}
|
|
24490
|
+
/** Registered by the task so a kill can release what the program still holds. */
|
|
24491
|
+
onKill(cleanup) {
|
|
24492
|
+
this.cleanup = cleanup;
|
|
24493
|
+
if (this.killed) cleanup();
|
|
24494
|
+
}
|
|
24180
24495
|
/** End the process now, for an asynchronous `process.exit`. */
|
|
24181
24496
|
exitNow(code) {
|
|
24182
24497
|
this.finish(code);
|
|
@@ -24338,7 +24653,10 @@ function removeEscapedErrorReporting() {
|
|
|
24338
24653
|
}
|
|
24339
24654
|
var DRAIN_TURNS = 4;
|
|
24340
24655
|
var LocalRuntimePod = class _LocalRuntimePod {
|
|
24341
|
-
|
|
24656
|
+
/* Wrapped so that a foreign filesystem — Rolldown's WebAssembly memfs — can
|
|
24657
|
+
* be kept in step as writes happen, rather than deep-copied once per spawn.
|
|
24658
|
+
* With nothing attached this is a straight pass-through. */
|
|
24659
|
+
volume = new MirroringVolume(new MemoryVolume());
|
|
24342
24660
|
packages;
|
|
24343
24661
|
instanceId = `sbx-${Math.random().toString(36).slice(2)}`;
|
|
24344
24662
|
router = new VirtualHttpRouter();
|
|
@@ -24429,6 +24747,10 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
24429
24747
|
onRawMode: (enabled) => proc.emit("rawmode", enabled)
|
|
24430
24748
|
});
|
|
24431
24749
|
proc.acceptInput((data) => core.writeStdin(data), () => core.endStdin());
|
|
24750
|
+
proc.onKill(() => {
|
|
24751
|
+
core.cancelTimers();
|
|
24752
|
+
this.router.closeOwner(owner);
|
|
24753
|
+
});
|
|
24432
24754
|
engine = new CommonJsEngine(this.volume, {
|
|
24433
24755
|
cwd,
|
|
24434
24756
|
builtins: core.builtins,
|
|
@@ -24438,7 +24760,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
24438
24760
|
});
|
|
24439
24761
|
try {
|
|
24440
24762
|
await engine.run(script);
|
|
24441
|
-
await this.settle(owner, core.pendingHandles, core.pendingUnrefed, core.readingStdin, core.pendingRequests);
|
|
24763
|
+
await this.settle(owner, core.pendingHandles, core.pendingUnrefed, core.readingStdin, core.pendingRequests, () => proc.isKilled());
|
|
24442
24764
|
if (this.router.activePorts(owner).length) {
|
|
24443
24765
|
await proc.waitForKill();
|
|
24444
24766
|
return 137;
|
|
@@ -24459,13 +24781,13 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
24459
24781
|
*/
|
|
24460
24782
|
async prepareRolldown(cwd, env2) {
|
|
24461
24783
|
const specifier = "@rolldown/binding-wasm32-wasi";
|
|
24462
|
-
if (!this.modules[specifier] &&
|
|
24784
|
+
if (!this.modules[specifier] && packageIsInstalled(this.volume, cwd, "rolldown")) {
|
|
24463
24785
|
this.rolldownBinding ??= loadHostRolldownBinding();
|
|
24464
24786
|
const binding = await this.rolldownBinding;
|
|
24465
24787
|
if (binding) this.modules[specifier] = binding;
|
|
24466
24788
|
}
|
|
24467
24789
|
if (this.modules[specifier]) {
|
|
24468
|
-
|
|
24790
|
+
attachRolldownMirror(this.modules[specifier], this.volume, cwd);
|
|
24469
24791
|
env2.NAPI_RS_FORCE_WASI ??= "true";
|
|
24470
24792
|
}
|
|
24471
24793
|
}
|
|
@@ -24484,18 +24806,18 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
24484
24806
|
* A plain script that has genuinely finished falls straight through both,
|
|
24485
24807
|
* costing a handful of empty turns.
|
|
24486
24808
|
*/
|
|
24487
|
-
async settle(owner, pendingHandles, pendingUnrefed, readingStdin, pendingRequests) {
|
|
24809
|
+
async settle(owner, pendingHandles, pendingUnrefed, readingStdin, pendingRequests, killed) {
|
|
24488
24810
|
for (let turn = 0; turn < DRAIN_TURNS; turn++) {
|
|
24489
|
-
if (this.router.activePorts(owner).length) return;
|
|
24811
|
+
if (this.router.activePorts(owner).length || killed()) return;
|
|
24490
24812
|
await new Promise((resolve2) => setTimeout(resolve2, 0));
|
|
24491
24813
|
}
|
|
24492
24814
|
if (pendingHandles() === 0 && pendingRequests() === 0 && pendingUnrefed() > 0 && !readingStdin()) {
|
|
24493
24815
|
const deadline = Date.now() + 1e3;
|
|
24494
|
-
while (!this.router.activePorts(owner).length && Date.now() < deadline) {
|
|
24816
|
+
while (!this.router.activePorts(owner).length && !killed() && Date.now() < deadline) {
|
|
24495
24817
|
await new Promise((resolve2) => setTimeout(resolve2, 5));
|
|
24496
24818
|
}
|
|
24497
24819
|
}
|
|
24498
|
-
while (!this.router.activePorts(owner).length && (pendingHandles() > 0 || pendingRequests() > 0 || readingStdin())) {
|
|
24820
|
+
while (!this.router.activePorts(owner).length && !killed() && (pendingHandles() > 0 || pendingRequests() > 0 || readingStdin())) {
|
|
24499
24821
|
await new Promise((resolve2) => setTimeout(resolve2, 5));
|
|
24500
24822
|
}
|
|
24501
24823
|
}
|
|
@@ -24547,7 +24869,7 @@ function formatError(error) {
|
|
|
24547
24869
|
function isRecord(value) {
|
|
24548
24870
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24549
24871
|
}
|
|
24550
|
-
function
|
|
24872
|
+
function packageIsInstalled(volume, cwd, wanted) {
|
|
24551
24873
|
for (let dir3 = clean(cwd); ; dir3 = dirname(dir3)) {
|
|
24552
24874
|
if (packageTreeContains(volume, join(dir3, "node_modules"), wanted, /* @__PURE__ */ new Set())) return true;
|
|
24553
24875
|
if (dir3 === "/") return false;
|
|
@@ -24576,30 +24898,571 @@ function directory(volume, path) {
|
|
|
24576
24898
|
return false;
|
|
24577
24899
|
}
|
|
24578
24900
|
}
|
|
24579
|
-
function
|
|
24901
|
+
function attachRolldownMirror(binding, volume, root) {
|
|
24580
24902
|
const fs = binding?.__fs;
|
|
24581
24903
|
if (!fs?.mkdirSync || !fs?.writeFileSync) return;
|
|
24582
|
-
|
|
24583
|
-
|
|
24584
|
-
|
|
24904
|
+
volume.attach(fs, root);
|
|
24905
|
+
}
|
|
24906
|
+
|
|
24907
|
+
// src/runtime/sync-channel.ts
|
|
24908
|
+
var STATE = 0;
|
|
24909
|
+
var LENGTH = 1;
|
|
24910
|
+
var MORE = 2;
|
|
24911
|
+
var CONTROL_WORDS = 4;
|
|
24912
|
+
var STATE_REQUEST = 1;
|
|
24913
|
+
var STATE_RESPONSE = 2;
|
|
24914
|
+
var STATE_CONTINUE = 3;
|
|
24915
|
+
var STATE_CLOSED = 4;
|
|
24916
|
+
function createSyncChannelBuffers(capacityBytes = 1 << 20) {
|
|
24917
|
+
return {
|
|
24918
|
+
control: new SharedArrayBuffer(CONTROL_WORDS * Int32Array.BYTES_PER_ELEMENT),
|
|
24919
|
+
data: new SharedArrayBuffer(capacityBytes)
|
|
24920
|
+
};
|
|
24921
|
+
}
|
|
24922
|
+
function syncChannelSupported() {
|
|
24923
|
+
if (typeof SharedArrayBuffer !== "function") return false;
|
|
24924
|
+
if (typeof Atomics !== "object" || typeof Atomics.wait !== "function") return false;
|
|
24925
|
+
if (typeof window !== "undefined" && globalThis.crossOriginIsolated !== true) return false;
|
|
24926
|
+
return true;
|
|
24927
|
+
}
|
|
24928
|
+
var SyncChannelServer = class {
|
|
24929
|
+
constructor(buffers, handle) {
|
|
24930
|
+
this.handle = handle;
|
|
24931
|
+
this.control = new Int32Array(buffers.control);
|
|
24932
|
+
this.data = new Uint8Array(buffers.data);
|
|
24933
|
+
this.capacity = this.data.length;
|
|
24585
24934
|
}
|
|
24586
|
-
|
|
24587
|
-
|
|
24588
|
-
|
|
24589
|
-
|
|
24590
|
-
|
|
24591
|
-
|
|
24592
|
-
|
|
24935
|
+
handle;
|
|
24936
|
+
control;
|
|
24937
|
+
data;
|
|
24938
|
+
capacity;
|
|
24939
|
+
/** Request chunks gathered so far, and the response still to be sent. */
|
|
24940
|
+
incoming = [];
|
|
24941
|
+
outgoing = null;
|
|
24942
|
+
sent = 0;
|
|
24943
|
+
closed = false;
|
|
24944
|
+
/** Call from the wake message the client sends after each chunk. */
|
|
24945
|
+
async pump() {
|
|
24946
|
+
if (this.closed) return;
|
|
24947
|
+
const state = Atomics.load(this.control, STATE);
|
|
24948
|
+
if (state === STATE_REQUEST) {
|
|
24949
|
+
const size = Atomics.load(this.control, LENGTH);
|
|
24950
|
+
this.incoming.push(this.data.slice(0, size));
|
|
24951
|
+
if (Atomics.load(this.control, MORE) === 1) {
|
|
24952
|
+
this.publish(STATE_CONTINUE);
|
|
24953
|
+
return;
|
|
24954
|
+
}
|
|
24955
|
+
const request = concat3(this.incoming);
|
|
24956
|
+
this.incoming = [];
|
|
24957
|
+
let response;
|
|
24593
24958
|
try {
|
|
24594
|
-
|
|
24959
|
+
response = await this.handle(request);
|
|
24595
24960
|
} catch {
|
|
24961
|
+
response = new Uint8Array();
|
|
24962
|
+
}
|
|
24963
|
+
if (this.closed) return;
|
|
24964
|
+
this.outgoing = response;
|
|
24965
|
+
this.sent = 0;
|
|
24966
|
+
this.sendChunk();
|
|
24967
|
+
return;
|
|
24968
|
+
}
|
|
24969
|
+
if (state === STATE_CONTINUE) this.sendChunk();
|
|
24970
|
+
}
|
|
24971
|
+
/** Release a blocked client, e.g. when the container is torn down. */
|
|
24972
|
+
close() {
|
|
24973
|
+
if (this.closed) return;
|
|
24974
|
+
this.closed = true;
|
|
24975
|
+
Atomics.store(this.control, STATE, STATE_CLOSED);
|
|
24976
|
+
Atomics.notify(this.control, STATE);
|
|
24977
|
+
}
|
|
24978
|
+
sendChunk() {
|
|
24979
|
+
const payload = this.outgoing ?? new Uint8Array();
|
|
24980
|
+
const size = Math.min(this.capacity, payload.length - this.sent);
|
|
24981
|
+
this.data.set(payload.subarray(this.sent, this.sent + size), 0);
|
|
24982
|
+
this.sent += size;
|
|
24983
|
+
Atomics.store(this.control, LENGTH, size);
|
|
24984
|
+
Atomics.store(this.control, MORE, this.sent < payload.length ? 1 : 0);
|
|
24985
|
+
this.publish(STATE_RESPONSE);
|
|
24986
|
+
}
|
|
24987
|
+
publish(state) {
|
|
24988
|
+
Atomics.store(this.control, STATE, state);
|
|
24989
|
+
Atomics.notify(this.control, STATE);
|
|
24990
|
+
}
|
|
24991
|
+
};
|
|
24992
|
+
function concat3(parts) {
|
|
24993
|
+
if (parts.length === 1) return parts[0];
|
|
24994
|
+
const total = parts.reduce((sum, part) => sum + part.length, 0);
|
|
24995
|
+
const joined = new Uint8Array(total);
|
|
24996
|
+
let at = 0;
|
|
24997
|
+
for (const part of parts) {
|
|
24998
|
+
joined.set(part, at);
|
|
24999
|
+
at += part.length;
|
|
25000
|
+
}
|
|
25001
|
+
return joined;
|
|
25002
|
+
}
|
|
25003
|
+
|
|
25004
|
+
// src/runtime/remote-volume.ts
|
|
25005
|
+
var encoder7 = new TextEncoder();
|
|
25006
|
+
var decoder7 = new TextDecoder();
|
|
25007
|
+
function encodeFrame(header, body) {
|
|
25008
|
+
const json = encoder7.encode(JSON.stringify(header));
|
|
25009
|
+
const frame = new Uint8Array(4 + json.length + (body?.length ?? 0));
|
|
25010
|
+
new DataView(frame.buffer).setUint32(0, json.length, true);
|
|
25011
|
+
frame.set(json, 4);
|
|
25012
|
+
if (body?.length) frame.set(body, 4 + json.length);
|
|
25013
|
+
return frame;
|
|
25014
|
+
}
|
|
25015
|
+
function decodeFrame(frame) {
|
|
25016
|
+
const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
|
|
25017
|
+
const headerLength = view.getUint32(0, true);
|
|
25018
|
+
const header = JSON.parse(decoder7.decode(frame.subarray(4, 4 + headerLength)));
|
|
25019
|
+
return { header, body: frame.subarray(4 + headerLength) };
|
|
25020
|
+
}
|
|
25021
|
+
function flattenStat(stat2) {
|
|
25022
|
+
return {
|
|
25023
|
+
kind: stat2.isDirectory() ? "directory" : stat2.isSymbolicLink() ? "symlink" : "file",
|
|
25024
|
+
mode: stat2.mode,
|
|
25025
|
+
size: stat2.size,
|
|
25026
|
+
uid: stat2.uid,
|
|
25027
|
+
gid: stat2.gid,
|
|
25028
|
+
ino: stat2.ino,
|
|
25029
|
+
nlink: stat2.nlink,
|
|
25030
|
+
atimeMs: stat2.atimeMs,
|
|
25031
|
+
mtimeMs: stat2.mtimeMs,
|
|
25032
|
+
ctimeMs: stat2.ctimeMs,
|
|
25033
|
+
birthtimeMs: stat2.birthtimeMs
|
|
25034
|
+
};
|
|
25035
|
+
}
|
|
25036
|
+
var BINARY_RESULTS = /* @__PURE__ */ new Set(["readFileSync"]);
|
|
25037
|
+
var BINARY_ARGUMENTS = /* @__PURE__ */ new Set(["writeFileSync", "appendFileSync"]);
|
|
25038
|
+
function serveVolume(volume) {
|
|
25039
|
+
return (request) => {
|
|
25040
|
+
const { header, body } = decodeFrame(request);
|
|
25041
|
+
const target = volume;
|
|
25042
|
+
try {
|
|
25043
|
+
const method = target[header.op];
|
|
25044
|
+
if (typeof method !== "function") {
|
|
25045
|
+
return encodeFrame({ ok: false, error: { message: `unknown volume operation: ${header.op}` } });
|
|
25046
|
+
}
|
|
25047
|
+
const plain = header.args.map((value) => value === null ? void 0 : value);
|
|
25048
|
+
const args = BINARY_ARGUMENTS.has(header.op) ? [plain[0], body] : header.op === "utimesSync" ? [plain[0], new Date(plain[1]), new Date(plain[2])] : plain;
|
|
25049
|
+
const result = method.apply(volume, args);
|
|
25050
|
+
if (BINARY_RESULTS.has(header.op)) {
|
|
25051
|
+
return encodeFrame({ ok: true }, result);
|
|
24596
25052
|
}
|
|
24597
|
-
|
|
24598
|
-
|
|
24599
|
-
|
|
25053
|
+
if (header.op === "lstatSync") {
|
|
25054
|
+
return encodeFrame({ ok: true, value: flattenStat(result) });
|
|
25055
|
+
}
|
|
25056
|
+
return encodeFrame({ ok: true, value: result });
|
|
25057
|
+
} catch (error) {
|
|
25058
|
+
const failure = error;
|
|
25059
|
+
return encodeFrame({
|
|
25060
|
+
ok: false,
|
|
25061
|
+
error: { ...failure.code ? { code: failure.code } : {}, message: failure.message ?? String(error) }
|
|
25062
|
+
});
|
|
25063
|
+
}
|
|
25064
|
+
};
|
|
25065
|
+
}
|
|
25066
|
+
|
|
25067
|
+
// src/runtime/sync-syscalls.ts
|
|
25068
|
+
var SPAWN_OP = "spawnSync";
|
|
25069
|
+
function serveSyncSyscalls(options) {
|
|
25070
|
+
const volumeHandler = serveVolume(options.volume);
|
|
25071
|
+
return async (request) => {
|
|
25072
|
+
const { header } = decodeFrame(request);
|
|
25073
|
+
if (header.op !== SPAWN_OP) return volumeHandler(request);
|
|
25074
|
+
try {
|
|
25075
|
+
const result = await options.spawnChild(header.args[0]);
|
|
25076
|
+
return encodeFrame({ ok: true, value: result });
|
|
25077
|
+
} catch (error) {
|
|
25078
|
+
const failure = error;
|
|
25079
|
+
return encodeFrame({
|
|
25080
|
+
ok: true,
|
|
25081
|
+
value: {
|
|
25082
|
+
status: null,
|
|
25083
|
+
stdout: "",
|
|
25084
|
+
stderr: "",
|
|
25085
|
+
signal: null,
|
|
25086
|
+
error: { ...failure.code ? { code: failure.code } : {}, message: failure.message ?? String(error) }
|
|
25087
|
+
}
|
|
25088
|
+
});
|
|
24600
25089
|
}
|
|
24601
25090
|
};
|
|
24602
|
-
|
|
25091
|
+
}
|
|
25092
|
+
|
|
25093
|
+
// src/runtime/worker-host.ts
|
|
25094
|
+
function defaultWorkerUrl() {
|
|
25095
|
+
return new URL("./worker-entry.js", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
25096
|
+
}
|
|
25097
|
+
function hasDomWorker() {
|
|
25098
|
+
return typeof Worker === "function" && typeof document !== "undefined";
|
|
25099
|
+
}
|
|
25100
|
+
async function startRuntimeWorker(options = {}) {
|
|
25101
|
+
const url = options.url ?? defaultWorkerUrl();
|
|
25102
|
+
const worker = hasDomWorker() ? await startDomWorker(url) : await startNodeWorker(url, options.workerData ?? {});
|
|
25103
|
+
await new Promise((resolve2, reject) => {
|
|
25104
|
+
const timer = setTimeout(() => reject(new Error("the runtime worker did not start in time")), options.timeoutMs ?? 1e4);
|
|
25105
|
+
worker.onMessage((message) => {
|
|
25106
|
+
if (message?.type === "sandboxedjs:ready") {
|
|
25107
|
+
clearTimeout(timer);
|
|
25108
|
+
resolve2();
|
|
25109
|
+
}
|
|
25110
|
+
});
|
|
25111
|
+
worker.onError((error) => {
|
|
25112
|
+
clearTimeout(timer);
|
|
25113
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
25114
|
+
});
|
|
25115
|
+
});
|
|
25116
|
+
return worker;
|
|
25117
|
+
}
|
|
25118
|
+
async function startDomWorker(url) {
|
|
25119
|
+
const worker = new Worker(url, { type: "module" });
|
|
25120
|
+
const listeners = [];
|
|
25121
|
+
const errors = [];
|
|
25122
|
+
worker.addEventListener("message", (event) => {
|
|
25123
|
+
for (const listener of listeners) listener(event.data);
|
|
25124
|
+
});
|
|
25125
|
+
worker.addEventListener("error", (event) => {
|
|
25126
|
+
const detail = event.message || "the runtime worker failed to load";
|
|
25127
|
+
for (const listener of errors) listener(new Error(detail));
|
|
25128
|
+
});
|
|
25129
|
+
return {
|
|
25130
|
+
postMessage: (message) => worker.postMessage(message),
|
|
25131
|
+
onMessage: (listener) => {
|
|
25132
|
+
listeners.push(listener);
|
|
25133
|
+
},
|
|
25134
|
+
onError: (listener) => {
|
|
25135
|
+
errors.push(listener);
|
|
25136
|
+
},
|
|
25137
|
+
terminate: () => worker.terminate()
|
|
25138
|
+
};
|
|
25139
|
+
}
|
|
25140
|
+
async function startNodeWorker(url, workerData) {
|
|
25141
|
+
const specifier = ["node", "worker_threads"].join(":");
|
|
25142
|
+
const { Worker: NodeWorker } = await import(
|
|
25143
|
+
/* @vite-ignore */
|
|
25144
|
+
/* webpackIgnore: true */
|
|
25145
|
+
specifier
|
|
25146
|
+
);
|
|
25147
|
+
const worker = new NodeWorker(url, { workerData });
|
|
25148
|
+
return {
|
|
25149
|
+
postMessage: (message) => worker.postMessage(message),
|
|
25150
|
+
onMessage: (listener) => worker.on("message", listener),
|
|
25151
|
+
onError: (listener) => worker.on("error", listener),
|
|
25152
|
+
terminate: () => worker.terminate()
|
|
25153
|
+
};
|
|
25154
|
+
}
|
|
25155
|
+
var WorkerProcess = class extends EventEmitter4__default.default {
|
|
25156
|
+
constructor(worker, release) {
|
|
25157
|
+
super();
|
|
25158
|
+
this.worker = worker;
|
|
25159
|
+
this.release = release;
|
|
25160
|
+
super.on("error", () => {
|
|
25161
|
+
});
|
|
25162
|
+
this.completion = new Promise((resolve2) => {
|
|
25163
|
+
this.resolveCompletion = resolve2;
|
|
25164
|
+
});
|
|
25165
|
+
}
|
|
25166
|
+
worker;
|
|
25167
|
+
release;
|
|
25168
|
+
completion;
|
|
25169
|
+
resolveCompletion;
|
|
25170
|
+
settled = false;
|
|
25171
|
+
out = [];
|
|
25172
|
+
err = [];
|
|
25173
|
+
started = false;
|
|
25174
|
+
pendingInput = [];
|
|
25175
|
+
inputEnded = false;
|
|
25176
|
+
on(event, listener) {
|
|
25177
|
+
return super.on(event, listener);
|
|
25178
|
+
}
|
|
25179
|
+
/** Called once the guest has been told to start; releases anything buffered. */
|
|
25180
|
+
begin() {
|
|
25181
|
+
this.started = true;
|
|
25182
|
+
for (const chunk of this.pendingInput.splice(0)) this.worker.postMessage({ type: "stdin", data: chunk });
|
|
25183
|
+
if (this.inputEnded) this.worker.postMessage({ type: "stdin-end" });
|
|
25184
|
+
}
|
|
25185
|
+
output(text2) {
|
|
25186
|
+
this.out.push(text2);
|
|
25187
|
+
this.emit("output", text2);
|
|
25188
|
+
}
|
|
25189
|
+
error(text2) {
|
|
25190
|
+
this.err.push(text2);
|
|
25191
|
+
this.emit("error", text2);
|
|
25192
|
+
}
|
|
25193
|
+
/**
|
|
25194
|
+
* Deliver input to the running program.
|
|
25195
|
+
*
|
|
25196
|
+
* Held until the guest has been started, for the same reason the in-realm
|
|
25197
|
+
* pod holds it: an interactive session's first keystrokes arrive while the
|
|
25198
|
+
* program is still loading, and dropping them loses the answer to a prompt.
|
|
25199
|
+
*/
|
|
25200
|
+
write(data) {
|
|
25201
|
+
if (this.started) this.worker.postMessage({ type: "stdin", data });
|
|
25202
|
+
else this.pendingInput.push(data);
|
|
25203
|
+
}
|
|
25204
|
+
endInput() {
|
|
25205
|
+
this.inputEnded = true;
|
|
25206
|
+
if (this.started) this.worker.postMessage({ type: "stdin-end" });
|
|
25207
|
+
}
|
|
25208
|
+
kill(_signal = "SIGTERM") {
|
|
25209
|
+
this.worker.postMessage({ type: "kill" });
|
|
25210
|
+
this.finish(137);
|
|
25211
|
+
}
|
|
25212
|
+
finish(exitCode) {
|
|
25213
|
+
if (this.settled) return;
|
|
25214
|
+
this.settled = true;
|
|
25215
|
+
this.emit("exit", exitCode);
|
|
25216
|
+
this.resolveCompletion({ exitCode, stdout: this.out.join(""), stderr: this.err.join("") });
|
|
25217
|
+
this.release();
|
|
25218
|
+
}
|
|
25219
|
+
};
|
|
25220
|
+
var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
|
|
25221
|
+
workerUrl;
|
|
25222
|
+
/** Live workers, so teardown can stop them all. */
|
|
25223
|
+
live = /* @__PURE__ */ new Set();
|
|
25224
|
+
constructor(options) {
|
|
25225
|
+
super(options);
|
|
25226
|
+
this.workerUrl = options.workerUrl;
|
|
25227
|
+
}
|
|
25228
|
+
/**
|
|
25229
|
+
* Boot a Worker-backed pod, or return null when this host cannot support one.
|
|
25230
|
+
*
|
|
25231
|
+
* Declining is a first-class outcome. `SharedArrayBuffer` needs cross-origin
|
|
25232
|
+
* isolation, a bundler may have made the guest script unreachable, and a host
|
|
25233
|
+
* that supplied its own module objects has handed over things no thread
|
|
25234
|
+
* boundary can carry. In every case the caller falls back to the in-realm pod
|
|
25235
|
+
* and keeps working.
|
|
25236
|
+
*/
|
|
25237
|
+
static async tryBoot(options = {}) {
|
|
25238
|
+
if (!syncChannelSupported()) return null;
|
|
25239
|
+
if (options.modules && Object.keys(options.modules).length > 0) return null;
|
|
25240
|
+
const pod = new _WorkerRuntimePod(options);
|
|
25241
|
+
try {
|
|
25242
|
+
const probe = await startRuntimeWorker({ ...options.workerUrl ? { url: options.workerUrl } : {}, timeoutMs: 1e4 });
|
|
25243
|
+
await probe.terminate();
|
|
25244
|
+
return pod;
|
|
25245
|
+
} catch {
|
|
25246
|
+
pod.teardown();
|
|
25247
|
+
return null;
|
|
25248
|
+
}
|
|
25249
|
+
}
|
|
25250
|
+
async spawn(command, args = [], options = {}) {
|
|
25251
|
+
if (command !== "node" && command !== "nodejs") return super.spawn(command, args, options);
|
|
25252
|
+
const script = args[0];
|
|
25253
|
+
if (!script) return super.spawn(command, args, options);
|
|
25254
|
+
const cwd = typeof options.cwd === "string" ? options.cwd : this.workdir;
|
|
25255
|
+
if (this.needsHostModules(cwd)) return super.spawn(command, args, options);
|
|
25256
|
+
return await this.spawnInWorker(script, args, cwd, options);
|
|
25257
|
+
}
|
|
25258
|
+
async spawnInWorker(script, args, cwd, options) {
|
|
25259
|
+
const env2 = { ...this.env, ...isRecord2(options.env) ? options.env : {} };
|
|
25260
|
+
const owner = `${this.instanceId}:${Math.random().toString(36).slice(2)}`;
|
|
25261
|
+
const buffers = createSyncChannelBuffers();
|
|
25262
|
+
const worker = await startRuntimeWorker({
|
|
25263
|
+
...this.workerUrl ? { url: this.workerUrl } : {},
|
|
25264
|
+
timeoutMs: 15e3
|
|
25265
|
+
});
|
|
25266
|
+
const streams = { target: null };
|
|
25267
|
+
const server = new SyncChannelServer(buffers, serveSyncSyscalls({
|
|
25268
|
+
volume: this.volume,
|
|
25269
|
+
spawnChild: (request) => this.runChildToCompletion(request, streams.target)
|
|
25270
|
+
}));
|
|
25271
|
+
const entry = { worker, server };
|
|
25272
|
+
this.live.add(entry);
|
|
25273
|
+
const process2 = new WorkerProcess(worker, () => {
|
|
25274
|
+
this.live.delete(entry);
|
|
25275
|
+
server.close();
|
|
25276
|
+
this.closeProxies(owner);
|
|
25277
|
+
void worker.terminate();
|
|
25278
|
+
});
|
|
25279
|
+
streams.target = process2;
|
|
25280
|
+
const children = /* @__PURE__ */ new Map();
|
|
25281
|
+
worker.onMessage((raw) => {
|
|
25282
|
+
const message = raw;
|
|
25283
|
+
switch (message?.type) {
|
|
25284
|
+
case "wake":
|
|
25285
|
+
void server.pump();
|
|
25286
|
+
return;
|
|
25287
|
+
case "output":
|
|
25288
|
+
process2.output(String(message.text));
|
|
25289
|
+
return;
|
|
25290
|
+
case "error":
|
|
25291
|
+
process2.error(String(message.text));
|
|
25292
|
+
return;
|
|
25293
|
+
case "rawmode":
|
|
25294
|
+
process2.emit("rawmode", Boolean(message.enabled));
|
|
25295
|
+
return;
|
|
25296
|
+
case "exit":
|
|
25297
|
+
process2.finish(Number(message.code));
|
|
25298
|
+
return;
|
|
25299
|
+
case "listen":
|
|
25300
|
+
this.proxyPort(Number(message.port), owner, worker);
|
|
25301
|
+
return;
|
|
25302
|
+
case "http-response":
|
|
25303
|
+
this.settleProxied(Number(message.id), message.response);
|
|
25304
|
+
return;
|
|
25305
|
+
case "child-start":
|
|
25306
|
+
this.startChild(worker, children, message);
|
|
25307
|
+
return;
|
|
25308
|
+
case "child-stdin":
|
|
25309
|
+
children.get(message.id)?.sendStdin?.(String(message.data));
|
|
25310
|
+
return;
|
|
25311
|
+
case "child-stdin-end":
|
|
25312
|
+
children.get(message.id)?.endStdin?.();
|
|
25313
|
+
return;
|
|
25314
|
+
case "child-kill":
|
|
25315
|
+
children.get(message.id)?.kill(String(message.signal));
|
|
25316
|
+
return;
|
|
25317
|
+
default:
|
|
25318
|
+
return;
|
|
25319
|
+
}
|
|
25320
|
+
});
|
|
25321
|
+
worker.postMessage({
|
|
25322
|
+
type: "start",
|
|
25323
|
+
buffers,
|
|
25324
|
+
script,
|
|
25325
|
+
cwd,
|
|
25326
|
+
env: env2,
|
|
25327
|
+
argv: Array.isArray(options.argv) ? options.argv : ["/usr/bin/node", script, ...args.slice(1)],
|
|
25328
|
+
aliases: this.aliases,
|
|
25329
|
+
...typeof options.stdinPath === "string" ? { stdinPath: options.stdinPath } : {},
|
|
25330
|
+
...options.interactiveStdin ? { interactiveStdin: true } : {},
|
|
25331
|
+
...options.tty ? { tty: true } : {}
|
|
25332
|
+
});
|
|
25333
|
+
process2.begin();
|
|
25334
|
+
return process2;
|
|
25335
|
+
}
|
|
25336
|
+
/** Start an asynchronous child on the host's behalf and relay its events. */
|
|
25337
|
+
startChild(worker, children, message) {
|
|
25338
|
+
const handle = this.processManager.spawn(message.config);
|
|
25339
|
+
children.set(message.id, handle);
|
|
25340
|
+
for (const event of ["stdout", "stderr", "exit"]) {
|
|
25341
|
+
handle.on(event, (value) => worker.postMessage({ type: "child-event", id: message.id, event, value }));
|
|
25342
|
+
}
|
|
25343
|
+
handle.exec();
|
|
25344
|
+
}
|
|
25345
|
+
/** Run a child to completion and collect it, for the guest's `spawnSync`. */
|
|
25346
|
+
runChildToCompletion(request, streamTo) {
|
|
25347
|
+
return new Promise((resolve2) => {
|
|
25348
|
+
let handle;
|
|
25349
|
+
try {
|
|
25350
|
+
handle = this.processManager.spawn({
|
|
25351
|
+
command: request.command,
|
|
25352
|
+
args: request.args,
|
|
25353
|
+
cwd: request.cwd,
|
|
25354
|
+
...request.env ? { env: request.env } : {},
|
|
25355
|
+
...request.inheritStdio ? { inheritStdio: true } : {}
|
|
25356
|
+
});
|
|
25357
|
+
} catch (error) {
|
|
25358
|
+
const failure = error;
|
|
25359
|
+
resolve2({ status: null, stdout: "", stderr: "", signal: null, error: { ...failure.code ? { code: failure.code } : {}, message: failure.message } });
|
|
25360
|
+
return;
|
|
25361
|
+
}
|
|
25362
|
+
let stdout = "";
|
|
25363
|
+
let stderr = "";
|
|
25364
|
+
const live = request.inheritStdio ? streamTo : null;
|
|
25365
|
+
handle.on("stdout", (text2) => {
|
|
25366
|
+
stdout += text2;
|
|
25367
|
+
live?.output(text2);
|
|
25368
|
+
});
|
|
25369
|
+
handle.on("stderr", (text2) => {
|
|
25370
|
+
stderr += text2;
|
|
25371
|
+
live?.error(text2);
|
|
25372
|
+
});
|
|
25373
|
+
handle.on("exit", (code) => resolve2({ status: code, stdout, stderr, signal: null }));
|
|
25374
|
+
handle.exec();
|
|
25375
|
+
if (request.input !== void 0) {
|
|
25376
|
+
handle.sendStdin?.(request.input);
|
|
25377
|
+
}
|
|
25378
|
+
if (!request.inheritStdio) handle.endStdin?.();
|
|
25379
|
+
});
|
|
25380
|
+
}
|
|
25381
|
+
// ── HTTP servers living on another thread ─────────────────────────────────
|
|
25382
|
+
proxies = /* @__PURE__ */ new Map();
|
|
25383
|
+
waiting = /* @__PURE__ */ new Map();
|
|
25384
|
+
nextRequestId = 1;
|
|
25385
|
+
/**
|
|
25386
|
+
* Register a stand-in for a server that is actually running in the Worker.
|
|
25387
|
+
*
|
|
25388
|
+
* The router only knows how to reach servers on this thread, so each bound
|
|
25389
|
+
* port gets a local server whose whole job is to forward and wait.
|
|
25390
|
+
*/
|
|
25391
|
+
proxyPort(port, owner, worker) {
|
|
25392
|
+
if (this.proxies.has(port)) return;
|
|
25393
|
+
const server = new VirtualHttpServer(this.router, owner, (request, response) => {
|
|
25394
|
+
void this.forward(worker, port, request, response);
|
|
25395
|
+
});
|
|
25396
|
+
try {
|
|
25397
|
+
server.listen(port);
|
|
25398
|
+
this.proxies.set(port, server);
|
|
25399
|
+
} catch {
|
|
25400
|
+
}
|
|
25401
|
+
}
|
|
25402
|
+
async forward(worker, port, request, response) {
|
|
25403
|
+
const chunks = [];
|
|
25404
|
+
for await (const chunk of request) chunks.push(chunk);
|
|
25405
|
+
const body = concat4(chunks);
|
|
25406
|
+
const id = this.nextRequestId++;
|
|
25407
|
+
const answered = new Promise((resolve2) => this.waiting.set(id, resolve2));
|
|
25408
|
+
worker.postMessage({
|
|
25409
|
+
type: "http-request",
|
|
25410
|
+
id,
|
|
25411
|
+
port,
|
|
25412
|
+
init: { method: request.method, path: request.url, headers: request.headers, body }
|
|
25413
|
+
});
|
|
25414
|
+
let result;
|
|
25415
|
+
try {
|
|
25416
|
+
result = await answered;
|
|
25417
|
+
} catch (error) {
|
|
25418
|
+
this.waiting.delete(id);
|
|
25419
|
+
response.writeHead(500, "Internal Server Error", {});
|
|
25420
|
+
response.end(new TextEncoder().encode(error instanceof Error ? error.message : String(error)));
|
|
25421
|
+
return;
|
|
25422
|
+
}
|
|
25423
|
+
response.writeHead(Number(result.statusCode ?? 200), String(result.statusMessage ?? ""), result.headers ?? {});
|
|
25424
|
+
response.end(result.body ?? new Uint8Array());
|
|
25425
|
+
}
|
|
25426
|
+
settleProxied(id, response) {
|
|
25427
|
+
const resolve2 = this.waiting.get(id);
|
|
25428
|
+
if (!resolve2) return;
|
|
25429
|
+
this.waiting.delete(id);
|
|
25430
|
+
resolve2(response);
|
|
25431
|
+
}
|
|
25432
|
+
closeProxies(owner) {
|
|
25433
|
+
for (const [port, server] of [...this.proxies]) {
|
|
25434
|
+
if (server.owner !== owner) continue;
|
|
25435
|
+
server.close();
|
|
25436
|
+
this.proxies.delete(port);
|
|
25437
|
+
}
|
|
25438
|
+
}
|
|
25439
|
+
teardown() {
|
|
25440
|
+
for (const { worker, server } of [...this.live]) {
|
|
25441
|
+
server.close();
|
|
25442
|
+
void worker.terminate();
|
|
25443
|
+
}
|
|
25444
|
+
this.live.clear();
|
|
25445
|
+
super.teardown();
|
|
25446
|
+
}
|
|
25447
|
+
/** Does anything under `cwd` need a module only the host can supply? */
|
|
25448
|
+
needsHostModules(cwd) {
|
|
25449
|
+
return packageIsInstalled(this.volume, cwd, "rolldown");
|
|
25450
|
+
}
|
|
25451
|
+
};
|
|
25452
|
+
function isRecord2(value) {
|
|
25453
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25454
|
+
}
|
|
25455
|
+
function concat4(parts) {
|
|
25456
|
+
if (parts.length === 0) return new Uint8Array();
|
|
25457
|
+
if (parts.length === 1) return parts[0];
|
|
25458
|
+
const total = parts.reduce((sum, part) => sum + part.length, 0);
|
|
25459
|
+
const joined = new Uint8Array(total);
|
|
25460
|
+
let at = 0;
|
|
25461
|
+
for (const part of parts) {
|
|
25462
|
+
joined.set(part, at);
|
|
25463
|
+
at += part.length;
|
|
25464
|
+
}
|
|
25465
|
+
return joined;
|
|
24603
25466
|
}
|
|
24604
25467
|
|
|
24605
25468
|
// src/container/container.ts
|
|
@@ -24646,11 +25509,13 @@ var Container = class _Container {
|
|
|
24646
25509
|
// ── boot ──────────────────────────────────────────────────────────────────
|
|
24647
25510
|
static async create(opts = {}) {
|
|
24648
25511
|
if (opts.python) configurePython(opts.python);
|
|
24649
|
-
const
|
|
25512
|
+
const podOptions = {
|
|
24650
25513
|
workdir: opts.cwd ?? "/",
|
|
24651
25514
|
env: opts.env ?? {},
|
|
24652
25515
|
...opts.onServerReady ? { onServerReady: opts.onServerReady } : {}
|
|
24653
|
-
}
|
|
25516
|
+
};
|
|
25517
|
+
const workerOptions = { ...podOptions, ...opts.workerUrl ? { workerUrl: opts.workerUrl } : {} };
|
|
25518
|
+
const pod = opts.pod ?? (opts.isolation === "realm" ? null : await WorkerRuntimePod.tryBoot(workerOptions)) ?? await LocalRuntimePod.boot(podOptions);
|
|
24654
25519
|
const kernel = new Kernel({
|
|
24655
25520
|
pod,
|
|
24656
25521
|
hostname: opts.hostname ?? "sandbox",
|
|
@@ -24892,15 +25757,15 @@ var Container = class _Container {
|
|
|
24892
25757
|
}
|
|
24893
25758
|
makeStdio(opts) {
|
|
24894
25759
|
const combined = [];
|
|
24895
|
-
const
|
|
25760
|
+
const decoder8 = new TextDecoder();
|
|
24896
25761
|
const stdout = new BufferSink((chunk) => {
|
|
24897
|
-
const text2 =
|
|
25762
|
+
const text2 = decoder8.decode(chunk, { stream: true });
|
|
24898
25763
|
combined.push(text2);
|
|
24899
25764
|
opts.onStdout?.(text2);
|
|
24900
25765
|
this.hooks.onStdout?.(text2);
|
|
24901
25766
|
});
|
|
24902
25767
|
const stderr = new BufferSink((chunk) => {
|
|
24903
|
-
const text2 =
|
|
25768
|
+
const text2 = decoder8.decode(chunk, { stream: true });
|
|
24904
25769
|
combined.push(text2);
|
|
24905
25770
|
opts.onStderr?.(text2);
|
|
24906
25771
|
this.hooks.onStderr?.(text2);
|
|
@@ -25511,6 +26376,97 @@ init_variables();
|
|
|
25511
26376
|
init_arith();
|
|
25512
26377
|
init_expand();
|
|
25513
26378
|
init_builtins();
|
|
26379
|
+
|
|
26380
|
+
// src/preview/register.ts
|
|
26381
|
+
function serveContainerOn(port, box) {
|
|
26382
|
+
port.onmessage = async (event) => {
|
|
26383
|
+
const request = event.data;
|
|
26384
|
+
try {
|
|
26385
|
+
const response = await box.request(request.port, {
|
|
26386
|
+
method: request.method,
|
|
26387
|
+
path: request.path,
|
|
26388
|
+
headers: request.headers,
|
|
26389
|
+
...request.body ? { body: new Uint8Array(request.body) } : {}
|
|
26390
|
+
});
|
|
26391
|
+
const bytes2 = response.bytes.slice();
|
|
26392
|
+
port.postMessage(
|
|
26393
|
+
{
|
|
26394
|
+
id: request.id,
|
|
26395
|
+
response: {
|
|
26396
|
+
status: response.status,
|
|
26397
|
+
statusText: response.statusText,
|
|
26398
|
+
headers: response.headers,
|
|
26399
|
+
body: bytes2.buffer
|
|
26400
|
+
}
|
|
26401
|
+
},
|
|
26402
|
+
[bytes2.buffer]
|
|
26403
|
+
);
|
|
26404
|
+
} catch (error) {
|
|
26405
|
+
const message = new TextEncoder().encode(error instanceof Error ? error.message : String(error));
|
|
26406
|
+
port.postMessage({
|
|
26407
|
+
id: request.id,
|
|
26408
|
+
response: { status: 502, statusText: "Bad Gateway", headers: {}, body: message.buffer }
|
|
26409
|
+
});
|
|
26410
|
+
}
|
|
26411
|
+
};
|
|
26412
|
+
port.start?.();
|
|
26413
|
+
}
|
|
26414
|
+
async function createPreview(box, options = {}) {
|
|
26415
|
+
if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) return null;
|
|
26416
|
+
const scriptUrl = options.scriptUrl ?? new URL("./service-worker.js", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
26417
|
+
let registration;
|
|
26418
|
+
try {
|
|
26419
|
+
registration = await navigator.serviceWorker.register(scriptUrl, {
|
|
26420
|
+
type: "module",
|
|
26421
|
+
...options.scope ? { scope: options.scope } : {}
|
|
26422
|
+
});
|
|
26423
|
+
} catch {
|
|
26424
|
+
return null;
|
|
26425
|
+
}
|
|
26426
|
+
const worker = registration.active ?? registration.waiting ?? registration.installing;
|
|
26427
|
+
if (!worker) return null;
|
|
26428
|
+
if (worker.state !== "activated") {
|
|
26429
|
+
const activated = await new Promise((resolve2) => {
|
|
26430
|
+
const check = () => {
|
|
26431
|
+
if (worker.state === "activated") {
|
|
26432
|
+
worker.removeEventListener("statechange", check);
|
|
26433
|
+
resolve2(true);
|
|
26434
|
+
} else if (worker.state === "redundant") {
|
|
26435
|
+
worker.removeEventListener("statechange", check);
|
|
26436
|
+
resolve2(false);
|
|
26437
|
+
}
|
|
26438
|
+
};
|
|
26439
|
+
worker.addEventListener("statechange", check);
|
|
26440
|
+
setTimeout(() => resolve2(worker.state === "activated"), 1e4);
|
|
26441
|
+
check();
|
|
26442
|
+
});
|
|
26443
|
+
if (!activated) return null;
|
|
26444
|
+
}
|
|
26445
|
+
const channel = new MessageChannel();
|
|
26446
|
+
serveContainerOn(channel.port1, box);
|
|
26447
|
+
worker.postMessage({ type: "sandboxedjs:connect" }, [channel.port2]);
|
|
26448
|
+
const base2 = registration.scope.replace(/\/$/, "");
|
|
26449
|
+
return {
|
|
26450
|
+
urlFor: (port) => `${base2}/__sbx__/${port}/`,
|
|
26451
|
+
dispose: async () => {
|
|
26452
|
+
channel.port1.close();
|
|
26453
|
+
await registration.unregister();
|
|
26454
|
+
}
|
|
26455
|
+
};
|
|
26456
|
+
}
|
|
26457
|
+
async function renderInto(box, element, options = { port: 80 }) {
|
|
26458
|
+
const response = await box.request(options.port, { path: options.path ?? "/" });
|
|
26459
|
+
const frame = document.createElement("iframe");
|
|
26460
|
+
frame.setAttribute("sandbox", "allow-scripts");
|
|
26461
|
+
frame.style.width = "100%";
|
|
26462
|
+
frame.style.height = "100%";
|
|
26463
|
+
frame.style.border = "0";
|
|
26464
|
+
frame.srcdoc = response.body;
|
|
26465
|
+
element.replaceChildren(frame);
|
|
26466
|
+
return frame;
|
|
26467
|
+
}
|
|
26468
|
+
|
|
26469
|
+
// src/index.ts
|
|
25514
26470
|
var src_default = createContainer;
|
|
25515
26471
|
|
|
25516
26472
|
exports.BufferSink = BufferSink;
|
|
@@ -25554,6 +26510,7 @@ exports.VirtualHttpServer = VirtualHttpServer;
|
|
|
25554
26510
|
exports.VirtualIncomingMessage = VirtualIncomingMessage;
|
|
25555
26511
|
exports.VirtualServerResponse = VirtualServerResponse;
|
|
25556
26512
|
exports.WASM_ALIASES = WASM_ALIASES;
|
|
26513
|
+
exports.WorkerRuntimePod = WorkerRuntimePod;
|
|
25557
26514
|
exports.allCommands = allCommands;
|
|
25558
26515
|
exports.applyChmod = applyChmod;
|
|
25559
26516
|
exports.braceExpand = braceExpand;
|
|
@@ -25566,6 +26523,7 @@ exports.createChildProcessModule = createChildProcessModule;
|
|
|
25566
26523
|
exports.createContainer = createContainer;
|
|
25567
26524
|
exports.createContext = createContext;
|
|
25568
26525
|
exports.createCoreModules = createCoreModules;
|
|
26526
|
+
exports.createPreview = createPreview;
|
|
25569
26527
|
exports.default = src_default;
|
|
25570
26528
|
exports.defineCommand = defineCommand;
|
|
25571
26529
|
exports.evalArith = evalArith;
|
|
@@ -25592,9 +26550,12 @@ exports.octalMode = octalMode;
|
|
|
25592
26550
|
exports.parseShell = parse;
|
|
25593
26551
|
exports.parseUmask = parseUmask;
|
|
25594
26552
|
exports.posixPath = path_exports;
|
|
26553
|
+
exports.renderInto = renderInto;
|
|
25595
26554
|
exports.resetPidCounter = resetPidCounter;
|
|
25596
26555
|
exports.shellQuote = shellQuote;
|
|
26556
|
+
exports.startRuntimeWorker = startRuntimeWorker;
|
|
25597
26557
|
exports.strerror = strerror;
|
|
26558
|
+
exports.syncChannelSupported = syncChannelSupported;
|
|
25598
26559
|
exports.transformEsm = transformEsm;
|
|
25599
26560
|
exports.unameInfo = unameInfo;
|
|
25600
26561
|
//# sourceMappingURL=index.cjs.map
|