sandboxedjs 0.1.29 → 0.1.31
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 +137 -92
- package/bin/sandboxedjs-serve.mjs +24 -0
- package/dist/browser-host.cjs +112 -0
- package/dist/browser-host.cjs.map +1 -0
- package/dist/browser-host.d.cts +25 -0
- package/dist/browser-host.d.ts +25 -0
- package/dist/browser-host.js +109 -0
- package/dist/browser-host.js.map +1 -0
- package/dist/index.cjs +464 -178
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +139 -16
- package/dist/index.d.ts +139 -16
- package/dist/index.js +462 -179
- 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 +140 -116
- package/dist/worker-entry.js.map +1 -1
- package/package.json +9 -3
package/dist/index.cjs
CHANGED
|
@@ -4795,7 +4795,15 @@ var Pipe = class _Pipe {
|
|
|
4795
4795
|
* whole line on every keystroke — so the terminal must stop echoing, or
|
|
4796
4796
|
* every character appears twice.
|
|
4797
4797
|
*/
|
|
4798
|
-
|
|
4798
|
+
raw = false;
|
|
4799
|
+
onRawMode;
|
|
4800
|
+
get rawMode() {
|
|
4801
|
+
return this.raw;
|
|
4802
|
+
}
|
|
4803
|
+
set rawMode(enabled) {
|
|
4804
|
+
this.raw = enabled;
|
|
4805
|
+
this.onRawMode?.(enabled);
|
|
4806
|
+
}
|
|
4799
4807
|
columns;
|
|
4800
4808
|
rows;
|
|
4801
4809
|
get closed() {
|
|
@@ -5923,13 +5931,19 @@ var Kernel = class {
|
|
|
5923
5931
|
stderr: opts.stderr ?? new NullOutput()
|
|
5924
5932
|
}
|
|
5925
5933
|
});
|
|
5934
|
+
const cancelWithParent = () => {
|
|
5935
|
+
if (parent?.termSignal) proc.deliver(parent.termSignal);
|
|
5936
|
+
};
|
|
5937
|
+
parent?.signal.addEventListener("abort", cancelWithParent, { once: true });
|
|
5938
|
+
if (parent?.signal.aborted) cancelWithParent();
|
|
5939
|
+
void proc.wait().then(() => parent?.signal.removeEventListener("abort", cancelWithParent));
|
|
5926
5940
|
let timer;
|
|
5927
5941
|
if (opts.timeoutMs !== void 0) {
|
|
5928
5942
|
timer = setTimeout(() => {
|
|
5929
5943
|
proc.deliver("SIGKILL");
|
|
5930
5944
|
}, opts.timeoutMs);
|
|
5931
5945
|
}
|
|
5932
|
-
void this.dispatch(proc).then((code) => {
|
|
5946
|
+
void (proc.signal.aborted ? Promise.resolve(proc.exitCode ?? 137) : this.dispatch(proc)).then((code) => {
|
|
5933
5947
|
proc.exit(code);
|
|
5934
5948
|
}).catch((e) => {
|
|
5935
5949
|
try {
|
|
@@ -7921,6 +7935,22 @@ var Shell = class _Shell {
|
|
|
7921
7935
|
}
|
|
7922
7936
|
return this.lastStatus;
|
|
7923
7937
|
}
|
|
7938
|
+
/** Signal the current terminal pipeline without terminating the interactive shell. */
|
|
7939
|
+
interruptForeground(signal, stdin) {
|
|
7940
|
+
const processes = this.kernel.procs.list().filter((process2) => process2.running);
|
|
7941
|
+
const selected = new Set(processes.filter((process2) => process2.ppid === this.proc.pid && process2.stdin === stdin).map((process2) => process2.pid));
|
|
7942
|
+
for (let changed = true; changed; ) {
|
|
7943
|
+
changed = false;
|
|
7944
|
+
const outputs = processes.filter((process2) => selected.has(process2.pid)).flatMap((process2) => [process2.stdout, process2.stderr]);
|
|
7945
|
+
for (const process2 of processes) {
|
|
7946
|
+
if (!selected.has(process2.pid) && (selected.has(process2.ppid) || outputs.some((output) => output === process2.stdin))) {
|
|
7947
|
+
selected.add(process2.pid);
|
|
7948
|
+
changed = true;
|
|
7949
|
+
}
|
|
7950
|
+
}
|
|
7951
|
+
}
|
|
7952
|
+
for (const process2 of processes) if (selected.has(process2.pid)) process2.deliver(signal);
|
|
7953
|
+
}
|
|
7924
7954
|
get isExiting() {
|
|
7925
7955
|
return this.exiting;
|
|
7926
7956
|
}
|
|
@@ -8570,6 +8600,7 @@ sys ${fmt2(Math.floor(ms * 0.2))}
|
|
|
8570
8600
|
for (const assignment of assignments) {
|
|
8571
8601
|
env2[assignment.name] = await expandWordToString(assignment.value, this.expandContext());
|
|
8572
8602
|
}
|
|
8603
|
+
if (this.proc.signal.aborted) return this.proc.exitCode ?? 137;
|
|
8573
8604
|
const proc = this.kernel.spawn(words, {
|
|
8574
8605
|
cwd: this.cwd,
|
|
8575
8606
|
env: env2,
|
|
@@ -19712,6 +19743,11 @@ var KernelChildProcess = class {
|
|
|
19712
19743
|
started = false;
|
|
19713
19744
|
cancelled = false;
|
|
19714
19745
|
constructor(kernel, cred, config, pid) {
|
|
19746
|
+
if (config.inheritStdio) {
|
|
19747
|
+
this.stdin.isTTY = true;
|
|
19748
|
+
this.stdin.interactive = true;
|
|
19749
|
+
}
|
|
19750
|
+
this.stdin.onRawMode = (enabled) => this.emit("rawmode", enabled);
|
|
19715
19751
|
this.kernel = kernel;
|
|
19716
19752
|
this.cred = cred;
|
|
19717
19753
|
this.pid = pid;
|
|
@@ -19828,7 +19864,7 @@ function installNodeChildProcessBridge(pod, kernel, cred) {
|
|
|
19828
19864
|
const originalSpawn = manager.spawn;
|
|
19829
19865
|
manager.spawn = (config) => {
|
|
19830
19866
|
const resolved = kernel.resolveExecutable(config.command, config.cwd ?? "/", config.env ?? {}, cred);
|
|
19831
|
-
if (resolved?.kind === "builtin"
|
|
19867
|
+
if (resolved?.kind === "builtin") {
|
|
19832
19868
|
return new KernelChildProcess(kernel, cred, config, nextBridgePid++);
|
|
19833
19869
|
}
|
|
19834
19870
|
return originalSpawn.call(manager, config);
|
|
@@ -19839,6 +19875,68 @@ function installNodeChildProcessBridge(pod, kernel, cred) {
|
|
|
19839
19875
|
};
|
|
19840
19876
|
}
|
|
19841
19877
|
|
|
19878
|
+
// src/runtime/host-module-tracker.ts
|
|
19879
|
+
var HostModuleTracker = class {
|
|
19880
|
+
active = 0;
|
|
19881
|
+
disposed = false;
|
|
19882
|
+
dispose() {
|
|
19883
|
+
this.disposed = true;
|
|
19884
|
+
}
|
|
19885
|
+
wrapped = /* @__PURE__ */ new WeakMap();
|
|
19886
|
+
originals = /* @__PURE__ */ new WeakMap();
|
|
19887
|
+
pending = () => this.active;
|
|
19888
|
+
unwrap = (value) => value && (typeof value === "object" || typeof value === "function") ? this.originals.get(value) ?? value : value;
|
|
19889
|
+
wrap(value) {
|
|
19890
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") return value;
|
|
19891
|
+
const object = value;
|
|
19892
|
+
if (Array.isArray(value) || ArrayBuffer.isView(value) || value instanceof ArrayBuffer) return value;
|
|
19893
|
+
if (this.wrapped.has(object)) return this.wrapped.get(object);
|
|
19894
|
+
const tracker = this;
|
|
19895
|
+
const proxy = new Proxy(object, {
|
|
19896
|
+
// CommonJS native loaders re-export by assigning exports back onto the
|
|
19897
|
+
// binding object. Never store a process-owned proxy in a shared module.
|
|
19898
|
+
set(target, key, member) {
|
|
19899
|
+
return Reflect.set(target, key, tracker.unwrap(member), target);
|
|
19900
|
+
},
|
|
19901
|
+
defineProperty(target, key, descriptor) {
|
|
19902
|
+
return Reflect.defineProperty(target, key, "value" in descriptor ? { ...descriptor, value: tracker.unwrap(descriptor.value) } : descriptor);
|
|
19903
|
+
},
|
|
19904
|
+
get(target, key) {
|
|
19905
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
|
|
19906
|
+
const member = Reflect.get(target, key, target);
|
|
19907
|
+
if (descriptor && !descriptor.configurable && "value" in descriptor && !descriptor.writable) return member;
|
|
19908
|
+
return typeof member === "function" ? tracker.wrap(member) : member;
|
|
19909
|
+
},
|
|
19910
|
+
apply(target, receiver, args) {
|
|
19911
|
+
const result = Reflect.apply(target, tracker.unwrap(receiver), args.map(tracker.unwrap));
|
|
19912
|
+
if (result && typeof result.then === "function") {
|
|
19913
|
+
tracker.active++;
|
|
19914
|
+
return Promise.resolve(result).then(
|
|
19915
|
+
(value2) => {
|
|
19916
|
+
tracker.active--;
|
|
19917
|
+
return tracker.disposed ? new Promise(() => {
|
|
19918
|
+
}) : tracker.wrap(value2);
|
|
19919
|
+
},
|
|
19920
|
+
(error) => {
|
|
19921
|
+
tracker.active--;
|
|
19922
|
+
if (tracker.disposed) return new Promise(() => {
|
|
19923
|
+
});
|
|
19924
|
+
throw error;
|
|
19925
|
+
}
|
|
19926
|
+
);
|
|
19927
|
+
}
|
|
19928
|
+
return tracker.wrap(result);
|
|
19929
|
+
},
|
|
19930
|
+
construct(target, args, newTarget) {
|
|
19931
|
+
return tracker.wrap(Reflect.construct(target, args.map(tracker.unwrap), tracker.unwrap(newTarget)));
|
|
19932
|
+
}
|
|
19933
|
+
});
|
|
19934
|
+
this.wrapped.set(object, proxy);
|
|
19935
|
+
this.originals.set(proxy, object);
|
|
19936
|
+
return proxy;
|
|
19937
|
+
}
|
|
19938
|
+
};
|
|
19939
|
+
|
|
19842
19940
|
// src/runtime/commonjs-engine.ts
|
|
19843
19941
|
init_path();
|
|
19844
19942
|
var HELPERS = {
|
|
@@ -21576,6 +21674,7 @@ var VirtualHttpServer = class extends EventEmitter4__default.default {
|
|
|
21576
21674
|
owner;
|
|
21577
21675
|
listening = false;
|
|
21578
21676
|
portValue = null;
|
|
21677
|
+
referenced = true;
|
|
21579
21678
|
listen(...args) {
|
|
21580
21679
|
const callback = typeof args.at(-1) === "function" ? args.pop() : void 0;
|
|
21581
21680
|
const first = args[0];
|
|
@@ -21604,11 +21703,16 @@ var VirtualHttpServer = class extends EventEmitter4__default.default {
|
|
|
21604
21703
|
return this.portValue === null ? null : { address: "0.0.0.0", family: "IPv4", port: this.portValue };
|
|
21605
21704
|
}
|
|
21606
21705
|
ref() {
|
|
21706
|
+
this.referenced = true;
|
|
21607
21707
|
return this;
|
|
21608
21708
|
}
|
|
21609
21709
|
unref() {
|
|
21710
|
+
this.referenced = false;
|
|
21610
21711
|
return this;
|
|
21611
21712
|
}
|
|
21713
|
+
hasRef() {
|
|
21714
|
+
return this.referenced;
|
|
21715
|
+
}
|
|
21612
21716
|
setTimeout(_milliseconds, callback) {
|
|
21613
21717
|
if (callback) this.on("timeout", callback);
|
|
21614
21718
|
return this;
|
|
@@ -21618,13 +21722,16 @@ var VirtualHttpRouter = class {
|
|
|
21618
21722
|
servers = /* @__PURE__ */ new Map();
|
|
21619
21723
|
/** Notified when a server begins listening, for `onServerReady`. */
|
|
21620
21724
|
onListen;
|
|
21725
|
+
onClose;
|
|
21621
21726
|
register(port, server, owner) {
|
|
21622
21727
|
if (this.servers.has(port)) throw Object.assign(new Error(`listen EADDRINUSE: address already in use 0.0.0.0:${port}`), { code: "EADDRINUSE", port });
|
|
21623
21728
|
this.servers.set(port, { server, owner });
|
|
21624
21729
|
this.onListen?.(port);
|
|
21625
21730
|
}
|
|
21626
21731
|
unregister(port, server) {
|
|
21627
|
-
if (this.servers.get(port)?.server
|
|
21732
|
+
if (this.servers.get(port)?.server !== server) return;
|
|
21733
|
+
this.servers.delete(port);
|
|
21734
|
+
this.onClose?.(port);
|
|
21628
21735
|
}
|
|
21629
21736
|
/** Whether anything in this container is listening on `port`. */
|
|
21630
21737
|
activePortsIncludes(port) {
|
|
@@ -21633,6 +21740,9 @@ var VirtualHttpRouter = class {
|
|
|
21633
21740
|
activePorts(owner) {
|
|
21634
21741
|
return [...this.servers].filter(([, item]) => owner === void 0 || item.owner === owner).map(([port]) => port).sort((a, b) => a - b);
|
|
21635
21742
|
}
|
|
21743
|
+
referencedPorts(owner) {
|
|
21744
|
+
return [...this.servers].filter(([, item]) => item.owner === owner && item.server.hasRef()).map(([port]) => port);
|
|
21745
|
+
}
|
|
21636
21746
|
closeOwner(owner) {
|
|
21637
21747
|
for (const { server, owner: value } of [...this.servers.values()]) if (value === owner) server.close();
|
|
21638
21748
|
}
|
|
@@ -22044,6 +22154,108 @@ function hashFor(algorithm) {
|
|
|
22044
22154
|
function toBytes2(data, encoding) {
|
|
22045
22155
|
return typeof data === "string" ? Buffer2.from(data, encoding) : data;
|
|
22046
22156
|
}
|
|
22157
|
+
|
|
22158
|
+
// src/runtime/sync-channel.ts
|
|
22159
|
+
var STATE = 0;
|
|
22160
|
+
var LENGTH = 1;
|
|
22161
|
+
var MORE = 2;
|
|
22162
|
+
var CONTROL_WORDS = 4;
|
|
22163
|
+
var STATE_REQUEST = 1;
|
|
22164
|
+
var STATE_RESPONSE = 2;
|
|
22165
|
+
var STATE_CONTINUE = 3;
|
|
22166
|
+
var STATE_CLOSED = 4;
|
|
22167
|
+
function createSyncChannelBuffers(capacityBytes = 1 << 20) {
|
|
22168
|
+
return {
|
|
22169
|
+
control: new SharedArrayBuffer(CONTROL_WORDS * Int32Array.BYTES_PER_ELEMENT),
|
|
22170
|
+
data: new SharedArrayBuffer(capacityBytes)
|
|
22171
|
+
};
|
|
22172
|
+
}
|
|
22173
|
+
function syncChannelUnavailableReason() {
|
|
22174
|
+
if (typeof globalThis.crossOriginIsolated === "boolean" && !globalThis.crossOriginIsolated) {
|
|
22175
|
+
return "The browser host is not cross-origin isolated. Serve the host app over HTTPS or localhost with Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp. For a built app use sandboxedjs-serve <directory>. Embedded browsers or iframe policies may also prevent isolation.";
|
|
22176
|
+
}
|
|
22177
|
+
if (typeof SharedArrayBuffer !== "function") return "SharedArrayBuffer is unavailable in this host.";
|
|
22178
|
+
if (typeof Atomics !== "object" || typeof Atomics.wait !== "function") return "Atomics.wait is unavailable in this host.";
|
|
22179
|
+
return null;
|
|
22180
|
+
}
|
|
22181
|
+
function syncChannelSupported() {
|
|
22182
|
+
return syncChannelUnavailableReason() === null;
|
|
22183
|
+
}
|
|
22184
|
+
var SyncChannelServer = class {
|
|
22185
|
+
constructor(buffers, handle) {
|
|
22186
|
+
this.handle = handle;
|
|
22187
|
+
this.control = new Int32Array(buffers.control);
|
|
22188
|
+
this.data = new Uint8Array(buffers.data);
|
|
22189
|
+
this.capacity = this.data.length;
|
|
22190
|
+
}
|
|
22191
|
+
handle;
|
|
22192
|
+
control;
|
|
22193
|
+
data;
|
|
22194
|
+
capacity;
|
|
22195
|
+
/** Request chunks gathered so far, and the response still to be sent. */
|
|
22196
|
+
incoming = [];
|
|
22197
|
+
outgoing = null;
|
|
22198
|
+
sent = 0;
|
|
22199
|
+
closed = false;
|
|
22200
|
+
/** Call from the wake message the client sends after each chunk. */
|
|
22201
|
+
async pump() {
|
|
22202
|
+
if (this.closed) return;
|
|
22203
|
+
const state = Atomics.load(this.control, STATE);
|
|
22204
|
+
if (state === STATE_REQUEST) {
|
|
22205
|
+
const size = Atomics.load(this.control, LENGTH);
|
|
22206
|
+
this.incoming.push(this.data.slice(0, size));
|
|
22207
|
+
if (Atomics.load(this.control, MORE) === 1) {
|
|
22208
|
+
this.publish(STATE_CONTINUE);
|
|
22209
|
+
return;
|
|
22210
|
+
}
|
|
22211
|
+
const request = concat3(this.incoming);
|
|
22212
|
+
this.incoming = [];
|
|
22213
|
+
let response;
|
|
22214
|
+
try {
|
|
22215
|
+
response = await this.handle(request);
|
|
22216
|
+
} catch {
|
|
22217
|
+
response = new Uint8Array();
|
|
22218
|
+
}
|
|
22219
|
+
if (this.closed) return;
|
|
22220
|
+
this.outgoing = response;
|
|
22221
|
+
this.sent = 0;
|
|
22222
|
+
this.sendChunk();
|
|
22223
|
+
return;
|
|
22224
|
+
}
|
|
22225
|
+
if (state === STATE_CONTINUE) this.sendChunk();
|
|
22226
|
+
}
|
|
22227
|
+
/** Release a blocked client, e.g. when the container is torn down. */
|
|
22228
|
+
close() {
|
|
22229
|
+
if (this.closed) return;
|
|
22230
|
+
this.closed = true;
|
|
22231
|
+
Atomics.store(this.control, STATE, STATE_CLOSED);
|
|
22232
|
+
Atomics.notify(this.control, STATE);
|
|
22233
|
+
}
|
|
22234
|
+
sendChunk() {
|
|
22235
|
+
const payload = this.outgoing ?? new Uint8Array();
|
|
22236
|
+
const size = Math.min(this.capacity, payload.length - this.sent);
|
|
22237
|
+
this.data.set(payload.subarray(this.sent, this.sent + size), 0);
|
|
22238
|
+
this.sent += size;
|
|
22239
|
+
Atomics.store(this.control, LENGTH, size);
|
|
22240
|
+
Atomics.store(this.control, MORE, this.sent < payload.length ? 1 : 0);
|
|
22241
|
+
this.publish(STATE_RESPONSE);
|
|
22242
|
+
}
|
|
22243
|
+
publish(state) {
|
|
22244
|
+
Atomics.store(this.control, STATE, state);
|
|
22245
|
+
Atomics.notify(this.control, STATE);
|
|
22246
|
+
}
|
|
22247
|
+
};
|
|
22248
|
+
function concat3(parts) {
|
|
22249
|
+
if (parts.length === 1) return parts[0];
|
|
22250
|
+
const total = parts.reduce((sum, part) => sum + part.length, 0);
|
|
22251
|
+
const joined = new Uint8Array(total);
|
|
22252
|
+
let at = 0;
|
|
22253
|
+
for (const part of parts) {
|
|
22254
|
+
joined.set(part, at);
|
|
22255
|
+
at += part.length;
|
|
22256
|
+
}
|
|
22257
|
+
return joined;
|
|
22258
|
+
}
|
|
22047
22259
|
var ChildProcess = class extends EventEmitter4__default.default {
|
|
22048
22260
|
stdout = new streamModule4__default.default.PassThrough();
|
|
22049
22261
|
stderr = new streamModule4__default.default.PassThrough();
|
|
@@ -22116,7 +22328,8 @@ var ChildProcess = class extends EventEmitter4__default.default {
|
|
|
22116
22328
|
queueMicrotask(() => this.emit("close", code, null));
|
|
22117
22329
|
}
|
|
22118
22330
|
};
|
|
22119
|
-
function createChildProcessModule(spawnChild, defaultCwd, syncSpawn) {
|
|
22331
|
+
function createChildProcessModule(spawnChild, defaultCwd, syncSpawn, defaultEnv = () => ({})) {
|
|
22332
|
+
const environmentFor = (options) => options.env ? { ...options.env } : defaultEnv();
|
|
22120
22333
|
const throughShell = (command, options) => {
|
|
22121
22334
|
const shell = typeof options.shell === "string" ? options.shell : "/bin/sh";
|
|
22122
22335
|
return { file: shell, args: ["-c", command] };
|
|
@@ -22127,7 +22340,7 @@ function createChildProcessModule(spawnChild, defaultCwd, syncSpawn) {
|
|
|
22127
22340
|
command: resolved.file,
|
|
22128
22341
|
args: resolved.args,
|
|
22129
22342
|
cwd: options.cwd ?? defaultCwd(),
|
|
22130
|
-
|
|
22343
|
+
env: environmentFor(options)
|
|
22131
22344
|
});
|
|
22132
22345
|
return new ChildProcess(handle, resolved.file, resolved.args);
|
|
22133
22346
|
};
|
|
@@ -22173,11 +22386,11 @@ ${err.join("")}`),
|
|
|
22173
22386
|
exec,
|
|
22174
22387
|
execFile,
|
|
22175
22388
|
fork: (modulePath, args = [], options = {}) => spawn("node", [modulePath, ...args], options),
|
|
22176
|
-
...buildSyncFamily(syncSpawn, throughShell, defaultCwd),
|
|
22389
|
+
...buildSyncFamily(syncSpawn, throughShell, defaultCwd, environmentFor),
|
|
22177
22390
|
ChildProcess
|
|
22178
22391
|
};
|
|
22179
22392
|
}
|
|
22180
|
-
function buildSyncFamily(syncSpawn, throughShell, defaultCwd) {
|
|
22393
|
+
function buildSyncFamily(syncSpawn, throughShell, defaultCwd, environmentFor) {
|
|
22181
22394
|
if (!syncSpawn) {
|
|
22182
22395
|
return {
|
|
22183
22396
|
execSync: unavailable("execSync"),
|
|
@@ -22188,12 +22401,14 @@ function buildSyncFamily(syncSpawn, throughShell, defaultCwd) {
|
|
|
22188
22401
|
const run = (file3, args, options) => {
|
|
22189
22402
|
const resolved = options.shell ? throughShell([file3, ...args].join(" "), options) : { file: file3, args };
|
|
22190
22403
|
const input = options.input === void 0 ? void 0 : typeof options.input === "string" ? options.input : new TextDecoder().decode(options.input);
|
|
22404
|
+
const inherit = options.stdio === "inherit" || Array.isArray(options.stdio) && options.stdio[0] === "inherit";
|
|
22191
22405
|
return syncSpawn({
|
|
22192
22406
|
command: resolved.file,
|
|
22193
22407
|
args: resolved.args,
|
|
22194
22408
|
cwd: options.cwd ?? defaultCwd(),
|
|
22195
|
-
|
|
22196
|
-
...input === void 0 ? {} : { input }
|
|
22409
|
+
env: environmentFor(options),
|
|
22410
|
+
...input === void 0 ? {} : { input },
|
|
22411
|
+
...inherit ? { inheritStdio: true } : {}
|
|
22197
22412
|
});
|
|
22198
22413
|
};
|
|
22199
22414
|
const asOutput = (text2, options) => options.encoding === "buffer" || options.encoding === void 0 ? Buffer2.from(text2) : text2;
|
|
@@ -22245,7 +22460,7 @@ function unavailable(name) {
|
|
|
22245
22460
|
const list = Array.isArray(args[1]) ? args[1].map(String) : [];
|
|
22246
22461
|
const command = file3 ? [file3, ...list].join(" ") : void 0;
|
|
22247
22462
|
const error = new Error(
|
|
22248
|
-
`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.
|
|
22463
|
+
`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. Enable the SandboxedJs worker runtime with createContainer({ isolation: "worker" }) and fix any boot prerequisite it reports. ` + (syncChannelUnavailableReason() ?? "This process is using the realm runtime, which cannot provide synchronous child processes.")
|
|
22249
22464
|
);
|
|
22250
22465
|
error.code = "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM";
|
|
22251
22466
|
throw error;
|
|
@@ -22533,6 +22748,11 @@ function createReadlineModule(defaultInput, defaultOutput = () => void 0) {
|
|
|
22533
22748
|
edit(sequence, key) {
|
|
22534
22749
|
if (this.closed) return;
|
|
22535
22750
|
const name = key.name;
|
|
22751
|
+
if (key.ctrl && name === "c") {
|
|
22752
|
+
if (this.listenerCount("SIGINT") > 0) this.emit("SIGINT");
|
|
22753
|
+
else this.close();
|
|
22754
|
+
return;
|
|
22755
|
+
}
|
|
22536
22756
|
if (name === "return" || name === "enter") {
|
|
22537
22757
|
const value = this.line;
|
|
22538
22758
|
this.line = "";
|
|
@@ -22628,7 +22848,7 @@ function createReadlineModule(defaultInput, defaultOutput = () => void 0) {
|
|
|
22628
22848
|
};
|
|
22629
22849
|
const createInterface = (options, output) => {
|
|
22630
22850
|
if (options && typeof options === "object" && !options.on) {
|
|
22631
|
-
const resolvedOutput2 = options.output
|
|
22851
|
+
const resolvedOutput2 = options.output;
|
|
22632
22852
|
const instance = new Interface(
|
|
22633
22853
|
options.input ?? defaultInput(),
|
|
22634
22854
|
resolvedOutput2,
|
|
@@ -22637,7 +22857,7 @@ function createReadlineModule(defaultInput, defaultOutput = () => void 0) {
|
|
|
22637
22857
|
if (typeof options.prompt === "string") instance.setPrompt(options.prompt);
|
|
22638
22858
|
return instance;
|
|
22639
22859
|
}
|
|
22640
|
-
const resolvedOutput = output
|
|
22860
|
+
const resolvedOutput = output;
|
|
22641
22861
|
return new Interface(options ?? defaultInput(), resolvedOutput, isTerminal(void 0, resolvedOutput));
|
|
22642
22862
|
};
|
|
22643
22863
|
const noop = () => {
|
|
@@ -22873,7 +23093,7 @@ function createCoreModules(options) {
|
|
|
22873
23093
|
};
|
|
22874
23094
|
const http = options.http ? createHttpModule(options.http.router, options.http.owner, httpOptions, "http:") : createUnsupportedModule("http");
|
|
22875
23095
|
const https = options.http ? createHttpModule(options.http.router, options.http.owner, httpOptions, "https:") : createUnsupportedModule("https");
|
|
22876
|
-
const childProcess = options.spawnChild ? createChildProcessModule(options.spawnChild, () => cwd, options.syncSpawn) : createUnsupportedModule("child_process");
|
|
23096
|
+
const childProcess = options.spawnChild ? createChildProcessModule(options.spawnChild, () => cwd, options.syncSpawn, () => ({ ...processObject.env })) : createUnsupportedModule("child_process");
|
|
22877
23097
|
const readline = createReadlineModule(() => processObject.stdin, () => processObject.stdout);
|
|
22878
23098
|
const dns = createDnsModule();
|
|
22879
23099
|
const builtins = {
|
|
@@ -22906,7 +23126,7 @@ function createCoreModules(options) {
|
|
|
22906
23126
|
"stream/promises": createStreamPromises(),
|
|
22907
23127
|
string_decoder: stringDecoderModule__default.default,
|
|
22908
23128
|
timers: { ...timersModule__default.default, ...timers.api },
|
|
22909
|
-
"timers/promises": createTimerPromises(),
|
|
23129
|
+
"timers/promises": createTimerPromises(timers.api),
|
|
22910
23130
|
tty: { isatty: () => false, ReadStream: streamModule4__default.default.Readable, WriteStream: streamModule4__default.default.Writable },
|
|
22911
23131
|
url: url_module_default,
|
|
22912
23132
|
util: util_module_default,
|
|
@@ -23651,10 +23871,10 @@ function createTrackedTimers() {
|
|
|
23651
23871
|
cancelAll
|
|
23652
23872
|
};
|
|
23653
23873
|
}
|
|
23654
|
-
function createTimerPromises() {
|
|
23874
|
+
function createTimerPromises(timers) {
|
|
23655
23875
|
return {
|
|
23656
|
-
setTimeout: (delay, value) => new Promise((resolve2) => setTimeout(resolve2, delay, value)),
|
|
23657
|
-
setImmediate: (value) => new Promise((resolve2) =>
|
|
23876
|
+
setTimeout: (delay, value) => new Promise((resolve2) => timers.setTimeout(resolve2, delay, value)),
|
|
23877
|
+
setImmediate: (value) => new Promise((resolve2) => timers.setImmediate(resolve2, value))
|
|
23658
23878
|
};
|
|
23659
23879
|
}
|
|
23660
23880
|
function createAsyncHooksModule() {
|
|
@@ -24118,8 +24338,10 @@ var MirroringVolume = class {
|
|
|
24118
24338
|
* container pays nothing for this.
|
|
24119
24339
|
*/
|
|
24120
24340
|
attach(mirror, root) {
|
|
24341
|
+
const cleanRoot = clean(root);
|
|
24342
|
+
if (this.mirror === mirror && this.root === cleanRoot) return;
|
|
24121
24343
|
this.mirror = mirror;
|
|
24122
|
-
this.root =
|
|
24344
|
+
this.root = cleanRoot;
|
|
24123
24345
|
this.seed();
|
|
24124
24346
|
}
|
|
24125
24347
|
detach() {
|
|
@@ -24360,11 +24582,8 @@ function ensureProcessGlobal() {
|
|
|
24360
24582
|
// src/runtime/host-rolldown.ts
|
|
24361
24583
|
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'] }";
|
|
24362
24584
|
async function loadHostRolldownBinding() {
|
|
24363
|
-
|
|
24364
|
-
|
|
24365
|
-
"Vite 8/Rolldown requires cross-origin isolation. Serve the app with Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp headers."
|
|
24366
|
-
);
|
|
24367
|
-
}
|
|
24585
|
+
const reason = syncChannelUnavailableReason();
|
|
24586
|
+
if (reason) throw new Error(`SandboxedJs threaded WASM unavailable: ${reason}`);
|
|
24368
24587
|
try {
|
|
24369
24588
|
const loaded = await import('@rolldown/binding-wasm32-wasi');
|
|
24370
24589
|
return "__fs" in loaded ? { ...loaded } : loaded.default ?? loaded;
|
|
@@ -24372,9 +24591,9 @@ async function loadHostRolldownBinding() {
|
|
|
24372
24591
|
if (typeof process !== "undefined" && process.env?.SANDBOXEDJS_DEBUG) {
|
|
24373
24592
|
console.error("[sandboxedjs] Rolldown WASI binding unavailable:", error);
|
|
24374
24593
|
}
|
|
24375
|
-
const
|
|
24594
|
+
const reason2 = error instanceof Error ? error.message : String(error);
|
|
24376
24595
|
throw new Error(
|
|
24377
|
-
`The optional Rolldown WASI binding could not be loaded: ${
|
|
24596
|
+
`The optional Rolldown WASI binding could not be loaded: ${reason2}` + (typeof window === "undefined" ? "" : `
|
|
24378
24597
|
|
|
24379
24598
|
${BUNDLER_HINT}`),
|
|
24380
24599
|
{ cause: error }
|
|
@@ -24485,6 +24704,8 @@ var LocalProcess = class extends EventEmitter4__default.default {
|
|
|
24485
24704
|
}
|
|
24486
24705
|
/** End the process now, for an asynchronous `process.exit`. */
|
|
24487
24706
|
exitNow(code) {
|
|
24707
|
+
this.killed = true;
|
|
24708
|
+
this.cleanup?.();
|
|
24488
24709
|
this.finish(code);
|
|
24489
24710
|
this.resolveKilled();
|
|
24490
24711
|
}
|
|
@@ -24668,6 +24889,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
24668
24889
|
)
|
|
24669
24890
|
};
|
|
24670
24891
|
disposed = false;
|
|
24892
|
+
running = /* @__PURE__ */ new Set();
|
|
24671
24893
|
workdir;
|
|
24672
24894
|
env;
|
|
24673
24895
|
aliases;
|
|
@@ -24709,8 +24931,9 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
24709
24931
|
const cwd = typeof options.cwd === "string" ? options.cwd : this.workdir;
|
|
24710
24932
|
const env2 = { ...this.env, ...isRecord(options.env) ? options.env : {} };
|
|
24711
24933
|
const owner = `${this.instanceId}:${Math.random().toString(36).slice(2)}`;
|
|
24712
|
-
|
|
24934
|
+
const process2 = new LocalProcess(async (proc) => {
|
|
24713
24935
|
await this.prepareRolldown(cwd, env2);
|
|
24936
|
+
if (proc.isKilled()) return 137;
|
|
24714
24937
|
const untrack = trackProcess(proc);
|
|
24715
24938
|
let requestedExit = 0;
|
|
24716
24939
|
let engine;
|
|
@@ -24738,7 +24961,9 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
24738
24961
|
onRawMode: (enabled) => proc.emit("rawmode", enabled)
|
|
24739
24962
|
});
|
|
24740
24963
|
proc.acceptInput((data) => core.writeStdin(data), () => core.endStdin());
|
|
24964
|
+
const hostWork = new HostModuleTracker();
|
|
24741
24965
|
proc.onKill(() => {
|
|
24966
|
+
hostWork.dispose();
|
|
24742
24967
|
core.cancelTimers();
|
|
24743
24968
|
this.router.closeOwner(owner);
|
|
24744
24969
|
});
|
|
@@ -24747,21 +24972,22 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
24747
24972
|
builtins: core.builtins,
|
|
24748
24973
|
globals: core.globals,
|
|
24749
24974
|
aliases: this.aliases,
|
|
24750
|
-
overrides: this.modules
|
|
24975
|
+
overrides: Object.fromEntries(Object.entries(this.modules).map(([key, value]) => [key, hostWork.wrap(value)]))
|
|
24751
24976
|
});
|
|
24752
24977
|
try {
|
|
24753
|
-
await engine.run(script);
|
|
24754
|
-
await this.settle(owner, core.pendingHandles, core.
|
|
24755
|
-
if (this.router.activePorts(owner).length) {
|
|
24756
|
-
await proc.waitForKill();
|
|
24757
|
-
return 137;
|
|
24758
|
-
}
|
|
24978
|
+
await Promise.race([engine.run(script), proc.waitForKill()]);
|
|
24979
|
+
await this.settle(owner, core.pendingHandles, core.readingStdin, () => core.pendingRequests() + hostWork.pending(), () => proc.isKilled());
|
|
24759
24980
|
return requestedExit;
|
|
24760
24981
|
} finally {
|
|
24982
|
+
hostWork.dispose();
|
|
24983
|
+
core.cancelTimers();
|
|
24761
24984
|
untrack();
|
|
24762
24985
|
this.router.closeOwner(owner);
|
|
24763
24986
|
}
|
|
24764
24987
|
});
|
|
24988
|
+
this.running.add(process2);
|
|
24989
|
+
void process2.completion.then(() => this.running.delete(process2));
|
|
24990
|
+
return process2;
|
|
24765
24991
|
}
|
|
24766
24992
|
/**
|
|
24767
24993
|
* Rolldown's JavaScript API synchronously requires its compiled binding.
|
|
@@ -24797,19 +25023,13 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
24797
25023
|
* A plain script that has genuinely finished falls straight through both,
|
|
24798
25024
|
* costing a handful of empty turns.
|
|
24799
25025
|
*/
|
|
24800
|
-
async settle(owner, pendingHandles,
|
|
24801
|
-
|
|
24802
|
-
|
|
24803
|
-
|
|
24804
|
-
|
|
24805
|
-
|
|
24806
|
-
|
|
24807
|
-
while (!this.router.activePorts(owner).length && !killed() && Date.now() < deadline) {
|
|
24808
|
-
await new Promise((resolve2) => setTimeout(resolve2, 5));
|
|
24809
|
-
}
|
|
24810
|
-
}
|
|
24811
|
-
while (!this.router.activePorts(owner).length && !killed() && (pendingHandles() > 0 || pendingRequests() > 0 || readingStdin())) {
|
|
24812
|
-
await new Promise((resolve2) => setTimeout(resolve2, 5));
|
|
25026
|
+
async settle(owner, pendingHandles, readingStdin, pendingRequests, killed) {
|
|
25027
|
+
let idleTurns = 0;
|
|
25028
|
+
while (!killed()) {
|
|
25029
|
+
const busy = this.router.referencedPorts(owner).length > 0 || pendingHandles() > 0 || pendingRequests() > 0 || readingStdin();
|
|
25030
|
+
idleTurns = busy ? 0 : idleTurns + 1;
|
|
25031
|
+
if (idleTurns >= DRAIN_TURNS) return;
|
|
25032
|
+
await new Promise((resolve2) => setTimeout(resolve2, busy ? 5 : 0));
|
|
24813
25033
|
}
|
|
24814
25034
|
}
|
|
24815
25035
|
async request(_port, _init = {}) {
|
|
@@ -24826,6 +25046,8 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
24826
25046
|
}
|
|
24827
25047
|
teardown() {
|
|
24828
25048
|
this.disposed = true;
|
|
25049
|
+
for (const process2 of this.running) process2.kill();
|
|
25050
|
+
this.running.clear();
|
|
24829
25051
|
this.router.closeAll();
|
|
24830
25052
|
this.esbuild?.stop();
|
|
24831
25053
|
}
|
|
@@ -24895,103 +25117,6 @@ function attachRolldownMirror(binding, volume, root) {
|
|
|
24895
25117
|
volume.attach(fs, root);
|
|
24896
25118
|
}
|
|
24897
25119
|
|
|
24898
|
-
// src/runtime/sync-channel.ts
|
|
24899
|
-
var STATE = 0;
|
|
24900
|
-
var LENGTH = 1;
|
|
24901
|
-
var MORE = 2;
|
|
24902
|
-
var CONTROL_WORDS = 4;
|
|
24903
|
-
var STATE_REQUEST = 1;
|
|
24904
|
-
var STATE_RESPONSE = 2;
|
|
24905
|
-
var STATE_CONTINUE = 3;
|
|
24906
|
-
var STATE_CLOSED = 4;
|
|
24907
|
-
function createSyncChannelBuffers(capacityBytes = 1 << 20) {
|
|
24908
|
-
return {
|
|
24909
|
-
control: new SharedArrayBuffer(CONTROL_WORDS * Int32Array.BYTES_PER_ELEMENT),
|
|
24910
|
-
data: new SharedArrayBuffer(capacityBytes)
|
|
24911
|
-
};
|
|
24912
|
-
}
|
|
24913
|
-
function syncChannelSupported() {
|
|
24914
|
-
if (typeof SharedArrayBuffer !== "function") return false;
|
|
24915
|
-
if (typeof Atomics !== "object" || typeof Atomics.wait !== "function") return false;
|
|
24916
|
-
if (typeof window !== "undefined" && globalThis.crossOriginIsolated !== true) return false;
|
|
24917
|
-
return true;
|
|
24918
|
-
}
|
|
24919
|
-
var SyncChannelServer = class {
|
|
24920
|
-
constructor(buffers, handle) {
|
|
24921
|
-
this.handle = handle;
|
|
24922
|
-
this.control = new Int32Array(buffers.control);
|
|
24923
|
-
this.data = new Uint8Array(buffers.data);
|
|
24924
|
-
this.capacity = this.data.length;
|
|
24925
|
-
}
|
|
24926
|
-
handle;
|
|
24927
|
-
control;
|
|
24928
|
-
data;
|
|
24929
|
-
capacity;
|
|
24930
|
-
/** Request chunks gathered so far, and the response still to be sent. */
|
|
24931
|
-
incoming = [];
|
|
24932
|
-
outgoing = null;
|
|
24933
|
-
sent = 0;
|
|
24934
|
-
closed = false;
|
|
24935
|
-
/** Call from the wake message the client sends after each chunk. */
|
|
24936
|
-
async pump() {
|
|
24937
|
-
if (this.closed) return;
|
|
24938
|
-
const state = Atomics.load(this.control, STATE);
|
|
24939
|
-
if (state === STATE_REQUEST) {
|
|
24940
|
-
const size = Atomics.load(this.control, LENGTH);
|
|
24941
|
-
this.incoming.push(this.data.slice(0, size));
|
|
24942
|
-
if (Atomics.load(this.control, MORE) === 1) {
|
|
24943
|
-
this.publish(STATE_CONTINUE);
|
|
24944
|
-
return;
|
|
24945
|
-
}
|
|
24946
|
-
const request = concat3(this.incoming);
|
|
24947
|
-
this.incoming = [];
|
|
24948
|
-
let response;
|
|
24949
|
-
try {
|
|
24950
|
-
response = await this.handle(request);
|
|
24951
|
-
} catch {
|
|
24952
|
-
response = new Uint8Array();
|
|
24953
|
-
}
|
|
24954
|
-
if (this.closed) return;
|
|
24955
|
-
this.outgoing = response;
|
|
24956
|
-
this.sent = 0;
|
|
24957
|
-
this.sendChunk();
|
|
24958
|
-
return;
|
|
24959
|
-
}
|
|
24960
|
-
if (state === STATE_CONTINUE) this.sendChunk();
|
|
24961
|
-
}
|
|
24962
|
-
/** Release a blocked client, e.g. when the container is torn down. */
|
|
24963
|
-
close() {
|
|
24964
|
-
if (this.closed) return;
|
|
24965
|
-
this.closed = true;
|
|
24966
|
-
Atomics.store(this.control, STATE, STATE_CLOSED);
|
|
24967
|
-
Atomics.notify(this.control, STATE);
|
|
24968
|
-
}
|
|
24969
|
-
sendChunk() {
|
|
24970
|
-
const payload = this.outgoing ?? new Uint8Array();
|
|
24971
|
-
const size = Math.min(this.capacity, payload.length - this.sent);
|
|
24972
|
-
this.data.set(payload.subarray(this.sent, this.sent + size), 0);
|
|
24973
|
-
this.sent += size;
|
|
24974
|
-
Atomics.store(this.control, LENGTH, size);
|
|
24975
|
-
Atomics.store(this.control, MORE, this.sent < payload.length ? 1 : 0);
|
|
24976
|
-
this.publish(STATE_RESPONSE);
|
|
24977
|
-
}
|
|
24978
|
-
publish(state) {
|
|
24979
|
-
Atomics.store(this.control, STATE, state);
|
|
24980
|
-
Atomics.notify(this.control, STATE);
|
|
24981
|
-
}
|
|
24982
|
-
};
|
|
24983
|
-
function concat3(parts) {
|
|
24984
|
-
if (parts.length === 1) return parts[0];
|
|
24985
|
-
const total = parts.reduce((sum, part) => sum + part.length, 0);
|
|
24986
|
-
const joined = new Uint8Array(total);
|
|
24987
|
-
let at = 0;
|
|
24988
|
-
for (const part of parts) {
|
|
24989
|
-
joined.set(part, at);
|
|
24990
|
-
at += part.length;
|
|
24991
|
-
}
|
|
24992
|
-
return joined;
|
|
24993
|
-
}
|
|
24994
|
-
|
|
24995
25120
|
// src/runtime/remote-volume.ts
|
|
24996
25121
|
var encoder7 = new TextEncoder();
|
|
24997
25122
|
var decoder7 = new TextDecoder();
|
|
@@ -25086,24 +25211,32 @@ function defaultWorkerUrl() {
|
|
|
25086
25211
|
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)));
|
|
25087
25212
|
}
|
|
25088
25213
|
function hasDomWorker() {
|
|
25089
|
-
return typeof Worker === "function"
|
|
25214
|
+
return typeof Worker === "function";
|
|
25090
25215
|
}
|
|
25091
25216
|
async function startRuntimeWorker(options = {}) {
|
|
25092
25217
|
const url = options.url ?? defaultWorkerUrl();
|
|
25093
25218
|
const worker = hasDomWorker() ? await startDomWorker(url) : await startNodeWorker(url, options.workerData ?? {});
|
|
25094
|
-
|
|
25095
|
-
|
|
25096
|
-
|
|
25097
|
-
|
|
25219
|
+
try {
|
|
25220
|
+
await new Promise((resolve2, reject) => {
|
|
25221
|
+
const timer = setTimeout(() => reject(new Error("the runtime worker did not start in time")), options.timeoutMs ?? 1e4);
|
|
25222
|
+
worker.onMessage((message) => {
|
|
25223
|
+
if (message?.type === "sandboxedjs:ready") {
|
|
25224
|
+
clearTimeout(timer);
|
|
25225
|
+
resolve2();
|
|
25226
|
+
}
|
|
25227
|
+
});
|
|
25228
|
+
worker.onError((error) => {
|
|
25098
25229
|
clearTimeout(timer);
|
|
25099
|
-
|
|
25100
|
-
}
|
|
25101
|
-
});
|
|
25102
|
-
worker.onError((error) => {
|
|
25103
|
-
clearTimeout(timer);
|
|
25104
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
25230
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
25231
|
+
});
|
|
25105
25232
|
});
|
|
25106
|
-
})
|
|
25233
|
+
} catch (error) {
|
|
25234
|
+
try {
|
|
25235
|
+
await worker.terminate();
|
|
25236
|
+
} catch {
|
|
25237
|
+
}
|
|
25238
|
+
throw error;
|
|
25239
|
+
}
|
|
25107
25240
|
return worker;
|
|
25108
25241
|
}
|
|
25109
25242
|
async function startDomWorker(url) {
|
|
@@ -25164,6 +25297,8 @@ var WorkerProcess = class extends EventEmitter4__default.default {
|
|
|
25164
25297
|
started = false;
|
|
25165
25298
|
pendingInput = [];
|
|
25166
25299
|
inputEnded = false;
|
|
25300
|
+
inheritedInput;
|
|
25301
|
+
ownRawMode = false;
|
|
25167
25302
|
on(event, listener) {
|
|
25168
25303
|
return super.on(event, listener);
|
|
25169
25304
|
}
|
|
@@ -25189,13 +25324,38 @@ var WorkerProcess = class extends EventEmitter4__default.default {
|
|
|
25189
25324
|
* program is still loading, and dropping them loses the answer to a prompt.
|
|
25190
25325
|
*/
|
|
25191
25326
|
write(data) {
|
|
25327
|
+
if (this.inheritedInput) {
|
|
25328
|
+
this.inheritedInput.sendStdin(data);
|
|
25329
|
+
return;
|
|
25330
|
+
}
|
|
25192
25331
|
if (this.started) this.worker.postMessage({ type: "stdin", data });
|
|
25193
25332
|
else this.pendingInput.push(data);
|
|
25194
25333
|
}
|
|
25195
25334
|
endInput() {
|
|
25196
25335
|
this.inputEnded = true;
|
|
25336
|
+
if (this.inheritedInput) {
|
|
25337
|
+
this.inheritedInput.endStdin?.();
|
|
25338
|
+
return;
|
|
25339
|
+
}
|
|
25197
25340
|
if (this.started) this.worker.postMessage({ type: "stdin-end" });
|
|
25198
25341
|
}
|
|
25342
|
+
rawMode(enabled) {
|
|
25343
|
+
this.ownRawMode = enabled;
|
|
25344
|
+
if (!this.inheritedInput) this.emit("rawmode", enabled);
|
|
25345
|
+
}
|
|
25346
|
+
/** The parent is blocked in Atomics.wait, so inherited input must bypass it. */
|
|
25347
|
+
inheritInput(child) {
|
|
25348
|
+
this.inheritedInput = child;
|
|
25349
|
+
this.emit("rawmode", false);
|
|
25350
|
+
child.on("rawmode", (enabled) => {
|
|
25351
|
+
if (this.inheritedInput === child) this.emit("rawmode", Boolean(enabled));
|
|
25352
|
+
});
|
|
25353
|
+
return () => {
|
|
25354
|
+
if (this.inheritedInput !== child) return;
|
|
25355
|
+
this.inheritedInput = void 0;
|
|
25356
|
+
this.emit("rawmode", this.ownRawMode);
|
|
25357
|
+
};
|
|
25358
|
+
}
|
|
25199
25359
|
kill(_signal = "SIGTERM") {
|
|
25200
25360
|
this.worker.postMessage({ type: "kill" });
|
|
25201
25361
|
this.finish(137);
|
|
@@ -25216,25 +25376,30 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
|
|
|
25216
25376
|
super(options);
|
|
25217
25377
|
this.workerUrl = options.workerUrl;
|
|
25218
25378
|
}
|
|
25219
|
-
/**
|
|
25220
|
-
|
|
25221
|
-
|
|
25222
|
-
|
|
25223
|
-
|
|
25224
|
-
|
|
25225
|
-
|
|
25226
|
-
* and keeps working.
|
|
25227
|
-
*/
|
|
25228
|
-
static async tryBoot(options = {}) {
|
|
25229
|
-
if (!syncChannelSupported()) return null;
|
|
25230
|
-
if (options.modules && Object.keys(options.modules).length > 0) return null;
|
|
25379
|
+
/** Boot without silently dropping synchronous child-process support. */
|
|
25380
|
+
static async boot(options = {}) {
|
|
25381
|
+
const reason = syncChannelUnavailableReason();
|
|
25382
|
+
if (reason) throw new Error(`SandboxedJs worker runtime unavailable: ${reason}`);
|
|
25383
|
+
if (options.modules && Object.keys(options.modules).length > 0) {
|
|
25384
|
+
throw new Error("SandboxedJs worker runtime cannot clone host-supplied modules. Use isolation: 'realm' explicitly.");
|
|
25385
|
+
}
|
|
25231
25386
|
const pod = new _WorkerRuntimePod(options);
|
|
25232
25387
|
try {
|
|
25233
25388
|
const probe = await startRuntimeWorker({ ...options.workerUrl ? { url: options.workerUrl } : {}, timeoutMs: 1e4 });
|
|
25234
25389
|
await probe.terminate();
|
|
25235
25390
|
return pod;
|
|
25236
|
-
} catch {
|
|
25391
|
+
} catch (cause) {
|
|
25237
25392
|
pod.teardown();
|
|
25393
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
25394
|
+
throw new Error(`SandboxedJs guest worker failed to start: ${detail}. Check that worker-entry.js is served as JavaScript; set workerUrl if your bundler moved it.`, { cause });
|
|
25395
|
+
}
|
|
25396
|
+
}
|
|
25397
|
+
/** Compatibility mode, with an observable explanation for every fallback. */
|
|
25398
|
+
static async tryBoot(options = {}, onFallback = (error) => console.warn(`[sandboxedjs] Falling back to the realm runtime; synchronous child processes are unavailable. ${error.message}`)) {
|
|
25399
|
+
try {
|
|
25400
|
+
return await _WorkerRuntimePod.boot(options);
|
|
25401
|
+
} catch (error) {
|
|
25402
|
+
onFallback(error instanceof Error ? error : new Error(String(error)));
|
|
25238
25403
|
return null;
|
|
25239
25404
|
}
|
|
25240
25405
|
}
|
|
@@ -25254,18 +25419,23 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
|
|
|
25254
25419
|
...this.workerUrl ? { url: this.workerUrl } : {},
|
|
25255
25420
|
timeoutMs: 15e3
|
|
25256
25421
|
});
|
|
25422
|
+
const streams = { target: null };
|
|
25257
25423
|
const server = new SyncChannelServer(buffers, serveSyncSyscalls({
|
|
25258
25424
|
volume: this.volume,
|
|
25259
|
-
spawnChild: (request) => this.runChildToCompletion(request)
|
|
25425
|
+
spawnChild: (request) => this.runChildToCompletion(request, streams.target, ownedChildren)
|
|
25260
25426
|
}));
|
|
25261
25427
|
const entry = { worker, server };
|
|
25262
25428
|
this.live.add(entry);
|
|
25429
|
+
const ownedChildren = /* @__PURE__ */ new Set();
|
|
25263
25430
|
const process2 = new WorkerProcess(worker, () => {
|
|
25431
|
+
for (const child of ownedChildren) child.kill("SIGTERM");
|
|
25432
|
+
ownedChildren.clear();
|
|
25264
25433
|
this.live.delete(entry);
|
|
25265
25434
|
server.close();
|
|
25266
25435
|
this.closeProxies(owner);
|
|
25267
25436
|
void worker.terminate();
|
|
25268
25437
|
});
|
|
25438
|
+
streams.target = process2;
|
|
25269
25439
|
const children = /* @__PURE__ */ new Map();
|
|
25270
25440
|
worker.onMessage((raw) => {
|
|
25271
25441
|
const message = raw;
|
|
@@ -25280,7 +25450,7 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
|
|
|
25280
25450
|
process2.error(String(message.text));
|
|
25281
25451
|
return;
|
|
25282
25452
|
case "rawmode":
|
|
25283
|
-
process2.
|
|
25453
|
+
process2.rawMode(Boolean(message.enabled));
|
|
25284
25454
|
return;
|
|
25285
25455
|
case "exit":
|
|
25286
25456
|
process2.finish(Number(message.code));
|
|
@@ -25288,11 +25458,20 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
|
|
|
25288
25458
|
case "listen":
|
|
25289
25459
|
this.proxyPort(Number(message.port), owner, worker);
|
|
25290
25460
|
return;
|
|
25461
|
+
case "close-port": {
|
|
25462
|
+
const port = Number(message.port);
|
|
25463
|
+
const proxy = this.proxies.get(port);
|
|
25464
|
+
if (proxy?.owner === owner) {
|
|
25465
|
+
proxy.close();
|
|
25466
|
+
this.proxies.delete(port);
|
|
25467
|
+
}
|
|
25468
|
+
return;
|
|
25469
|
+
}
|
|
25291
25470
|
case "http-response":
|
|
25292
25471
|
this.settleProxied(Number(message.id), message.response);
|
|
25293
25472
|
return;
|
|
25294
25473
|
case "child-start":
|
|
25295
|
-
this.startChild(worker, children, message);
|
|
25474
|
+
this.startChild(worker, children, message, ownedChildren);
|
|
25296
25475
|
return;
|
|
25297
25476
|
case "child-stdin":
|
|
25298
25477
|
children.get(message.id)?.sendStdin?.(String(message.data));
|
|
@@ -25323,16 +25502,21 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
|
|
|
25323
25502
|
return process2;
|
|
25324
25503
|
}
|
|
25325
25504
|
/** Start an asynchronous child on the host's behalf and relay its events. */
|
|
25326
|
-
startChild(worker, children, message) {
|
|
25505
|
+
startChild(worker, children, message, owned) {
|
|
25327
25506
|
const handle = this.processManager.spawn(message.config);
|
|
25328
25507
|
children.set(message.id, handle);
|
|
25508
|
+
owned.add(handle);
|
|
25509
|
+
handle.on("exit", () => {
|
|
25510
|
+
owned.delete(handle);
|
|
25511
|
+
children.delete(message.id);
|
|
25512
|
+
});
|
|
25329
25513
|
for (const event of ["stdout", "stderr", "exit"]) {
|
|
25330
25514
|
handle.on(event, (value) => worker.postMessage({ type: "child-event", id: message.id, event, value }));
|
|
25331
25515
|
}
|
|
25332
25516
|
handle.exec();
|
|
25333
25517
|
}
|
|
25334
25518
|
/** Run a child to completion and collect it, for the guest's `spawnSync`. */
|
|
25335
|
-
runChildToCompletion(request) {
|
|
25519
|
+
runChildToCompletion(request, streamTo, owned) {
|
|
25336
25520
|
return new Promise((resolve2) => {
|
|
25337
25521
|
let handle;
|
|
25338
25522
|
try {
|
|
@@ -25340,27 +25524,35 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
|
|
|
25340
25524
|
command: request.command,
|
|
25341
25525
|
args: request.args,
|
|
25342
25526
|
cwd: request.cwd,
|
|
25343
|
-
...request.env ? { env: request.env } : {}
|
|
25527
|
+
...request.env ? { env: request.env } : {},
|
|
25528
|
+
...request.inheritStdio ? { inheritStdio: true } : {}
|
|
25344
25529
|
});
|
|
25345
25530
|
} catch (error) {
|
|
25346
25531
|
const failure = error;
|
|
25347
25532
|
resolve2({ status: null, stdout: "", stderr: "", signal: null, error: { ...failure.code ? { code: failure.code } : {}, message: failure.message } });
|
|
25348
25533
|
return;
|
|
25349
25534
|
}
|
|
25535
|
+
owned.add(handle);
|
|
25536
|
+
handle.on("exit", () => owned.delete(handle));
|
|
25350
25537
|
let stdout = "";
|
|
25351
25538
|
let stderr = "";
|
|
25539
|
+
const live = request.inheritStdio ? streamTo : null;
|
|
25540
|
+
const restoreInput = live?.inheritInput(handle);
|
|
25541
|
+
if (restoreInput) handle.on("exit", restoreInput);
|
|
25352
25542
|
handle.on("stdout", (text2) => {
|
|
25353
25543
|
stdout += text2;
|
|
25544
|
+
live?.output(text2);
|
|
25354
25545
|
});
|
|
25355
25546
|
handle.on("stderr", (text2) => {
|
|
25356
25547
|
stderr += text2;
|
|
25548
|
+
live?.error(text2);
|
|
25357
25549
|
});
|
|
25358
25550
|
handle.on("exit", (code) => resolve2({ status: code, stdout, stderr, signal: null }));
|
|
25359
25551
|
handle.exec();
|
|
25360
25552
|
if (request.input !== void 0) {
|
|
25361
25553
|
handle.sendStdin?.(request.input);
|
|
25362
25554
|
}
|
|
25363
|
-
handle.endStdin?.();
|
|
25555
|
+
if (!request.inheritStdio) handle.endStdin?.();
|
|
25364
25556
|
});
|
|
25365
25557
|
}
|
|
25366
25558
|
// ── HTTP servers living on another thread ─────────────────────────────────
|
|
@@ -25500,7 +25692,7 @@ var Container = class _Container {
|
|
|
25500
25692
|
...opts.onServerReady ? { onServerReady: opts.onServerReady } : {}
|
|
25501
25693
|
};
|
|
25502
25694
|
const workerOptions = { ...podOptions, ...opts.workerUrl ? { workerUrl: opts.workerUrl } : {} };
|
|
25503
|
-
const pod = opts.pod ?? (opts.isolation === "realm" ? null : await WorkerRuntimePod.tryBoot(workerOptions)) ?? await LocalRuntimePod.boot(podOptions);
|
|
25695
|
+
const pod = opts.pod ?? (opts.isolation === "realm" ? null : opts.isolation === "worker" ? await WorkerRuntimePod.boot(workerOptions) : await WorkerRuntimePod.tryBoot(workerOptions, opts.onRuntimeFallback)) ?? await LocalRuntimePod.boot(podOptions);
|
|
25504
25696
|
const kernel = new Kernel({
|
|
25505
25697
|
pod,
|
|
25506
25698
|
hostname: opts.hostname ?? "sandbox",
|
|
@@ -26008,7 +26200,7 @@ var Terminal = class {
|
|
|
26008
26200
|
if (this.running) {
|
|
26009
26201
|
const raw = this.currentStdin?.rawMode === true;
|
|
26010
26202
|
if (ch === CTRL_C && !raw) {
|
|
26011
|
-
this.session.
|
|
26203
|
+
if (this.currentStdin) this.session.shell.interruptForeground("SIGINT", this.currentStdin);
|
|
26012
26204
|
this.write("^C\r\n");
|
|
26013
26205
|
return;
|
|
26014
26206
|
}
|
|
@@ -26361,6 +26553,97 @@ init_variables();
|
|
|
26361
26553
|
init_arith();
|
|
26362
26554
|
init_expand();
|
|
26363
26555
|
init_builtins();
|
|
26556
|
+
|
|
26557
|
+
// src/preview/register.ts
|
|
26558
|
+
function serveContainerOn(port, box) {
|
|
26559
|
+
port.onmessage = async (event) => {
|
|
26560
|
+
const request = event.data;
|
|
26561
|
+
try {
|
|
26562
|
+
const response = await box.request(request.port, {
|
|
26563
|
+
method: request.method,
|
|
26564
|
+
path: request.path,
|
|
26565
|
+
headers: request.headers,
|
|
26566
|
+
...request.body ? { body: new Uint8Array(request.body) } : {}
|
|
26567
|
+
});
|
|
26568
|
+
const bytes2 = response.bytes.slice();
|
|
26569
|
+
port.postMessage(
|
|
26570
|
+
{
|
|
26571
|
+
id: request.id,
|
|
26572
|
+
response: {
|
|
26573
|
+
status: response.status,
|
|
26574
|
+
statusText: response.statusText,
|
|
26575
|
+
headers: response.headers,
|
|
26576
|
+
body: bytes2.buffer
|
|
26577
|
+
}
|
|
26578
|
+
},
|
|
26579
|
+
[bytes2.buffer]
|
|
26580
|
+
);
|
|
26581
|
+
} catch (error) {
|
|
26582
|
+
const message = new TextEncoder().encode(error instanceof Error ? error.message : String(error));
|
|
26583
|
+
port.postMessage({
|
|
26584
|
+
id: request.id,
|
|
26585
|
+
response: { status: 502, statusText: "Bad Gateway", headers: {}, body: message.buffer }
|
|
26586
|
+
});
|
|
26587
|
+
}
|
|
26588
|
+
};
|
|
26589
|
+
port.start?.();
|
|
26590
|
+
}
|
|
26591
|
+
async function createPreview(box, options = {}) {
|
|
26592
|
+
if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) return null;
|
|
26593
|
+
const scriptUrl = options.scriptUrl ?? new URL("./service-worker.js?no-inline", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
26594
|
+
let registration;
|
|
26595
|
+
try {
|
|
26596
|
+
registration = await navigator.serviceWorker.register(scriptUrl, {
|
|
26597
|
+
type: "module",
|
|
26598
|
+
...options.scope ? { scope: options.scope } : {}
|
|
26599
|
+
});
|
|
26600
|
+
} catch {
|
|
26601
|
+
return null;
|
|
26602
|
+
}
|
|
26603
|
+
const worker = registration.active ?? registration.waiting ?? registration.installing;
|
|
26604
|
+
if (!worker) return null;
|
|
26605
|
+
if (worker.state !== "activated") {
|
|
26606
|
+
const activated = await new Promise((resolve2) => {
|
|
26607
|
+
const check = () => {
|
|
26608
|
+
if (worker.state === "activated") {
|
|
26609
|
+
worker.removeEventListener("statechange", check);
|
|
26610
|
+
resolve2(true);
|
|
26611
|
+
} else if (worker.state === "redundant") {
|
|
26612
|
+
worker.removeEventListener("statechange", check);
|
|
26613
|
+
resolve2(false);
|
|
26614
|
+
}
|
|
26615
|
+
};
|
|
26616
|
+
worker.addEventListener("statechange", check);
|
|
26617
|
+
setTimeout(() => resolve2(worker.state === "activated"), 1e4);
|
|
26618
|
+
check();
|
|
26619
|
+
});
|
|
26620
|
+
if (!activated) return null;
|
|
26621
|
+
}
|
|
26622
|
+
const channel = new MessageChannel();
|
|
26623
|
+
serveContainerOn(channel.port1, box);
|
|
26624
|
+
worker.postMessage({ type: "sandboxedjs:connect" }, [channel.port2]);
|
|
26625
|
+
const base2 = registration.scope.replace(/\/$/, "");
|
|
26626
|
+
return {
|
|
26627
|
+
urlFor: (port) => `${base2}/__sbx__/${port}/`,
|
|
26628
|
+
dispose: async () => {
|
|
26629
|
+
channel.port1.close();
|
|
26630
|
+
await registration.unregister();
|
|
26631
|
+
}
|
|
26632
|
+
};
|
|
26633
|
+
}
|
|
26634
|
+
async function renderInto(box, element, options = { port: 80 }) {
|
|
26635
|
+
const response = await box.request(options.port, { path: options.path ?? "/" });
|
|
26636
|
+
const frame = document.createElement("iframe");
|
|
26637
|
+
frame.setAttribute("sandbox", "allow-scripts");
|
|
26638
|
+
frame.style.width = "100%";
|
|
26639
|
+
frame.style.height = "100%";
|
|
26640
|
+
frame.style.border = "0";
|
|
26641
|
+
frame.srcdoc = response.body;
|
|
26642
|
+
element.replaceChildren(frame);
|
|
26643
|
+
return frame;
|
|
26644
|
+
}
|
|
26645
|
+
|
|
26646
|
+
// src/index.ts
|
|
26364
26647
|
var src_default = createContainer;
|
|
26365
26648
|
|
|
26366
26649
|
exports.BufferSink = BufferSink;
|
|
@@ -26404,6 +26687,7 @@ exports.VirtualHttpServer = VirtualHttpServer;
|
|
|
26404
26687
|
exports.VirtualIncomingMessage = VirtualIncomingMessage;
|
|
26405
26688
|
exports.VirtualServerResponse = VirtualServerResponse;
|
|
26406
26689
|
exports.WASM_ALIASES = WASM_ALIASES;
|
|
26690
|
+
exports.WorkerRuntimePod = WorkerRuntimePod;
|
|
26407
26691
|
exports.allCommands = allCommands;
|
|
26408
26692
|
exports.applyChmod = applyChmod;
|
|
26409
26693
|
exports.braceExpand = braceExpand;
|
|
@@ -26416,6 +26700,7 @@ exports.createChildProcessModule = createChildProcessModule;
|
|
|
26416
26700
|
exports.createContainer = createContainer;
|
|
26417
26701
|
exports.createContext = createContext;
|
|
26418
26702
|
exports.createCoreModules = createCoreModules;
|
|
26703
|
+
exports.createPreview = createPreview;
|
|
26419
26704
|
exports.default = src_default;
|
|
26420
26705
|
exports.defineCommand = defineCommand;
|
|
26421
26706
|
exports.evalArith = evalArith;
|
|
@@ -26442,6 +26727,7 @@ exports.octalMode = octalMode;
|
|
|
26442
26727
|
exports.parseShell = parse;
|
|
26443
26728
|
exports.parseUmask = parseUmask;
|
|
26444
26729
|
exports.posixPath = path_exports;
|
|
26730
|
+
exports.renderInto = renderInto;
|
|
26445
26731
|
exports.resetPidCounter = resetPidCounter;
|
|
26446
26732
|
exports.shellQuote = shellQuote;
|
|
26447
26733
|
exports.startRuntimeWorker = startRuntimeWorker;
|