@volter/tabnode 0.5.48 → 0.5.49
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/dist/assets/{runtime-worker-B-BP9mj-.js → runtime-worker-C1IV4qnr.js} +177 -22
- package/dist/fetch-transport.d.ts +0 -1
- package/dist/index.mjs +170 -22
- package/dist/node-body.d.ts +9 -0
- package/package.json +1 -1
- package/src/fetch-transport.ts +7 -3
- package/src/node-body.ts +88 -0
- package/src/node-response.ts +5 -2
- package/src/runtime.ts +3 -0
- package/src/shims/child_process.ts +49 -6
- package/src/shims/shell-jobs.ts +72 -0
|
@@ -5425,7 +5425,7 @@ function parse(urlString, parseQueryString = false, slashesDenoteHost = false) {
|
|
|
5425
5425
|
hostname: null,
|
|
5426
5426
|
hash: __hash,
|
|
5427
5427
|
search: __search,
|
|
5428
|
-
query: parseQueryString ? Object.fromEntries(new URLSearchParams(__search || "")) : __search ? __search.slice(1) : null,
|
|
5428
|
+
query: parseQueryString ? Object.fromEntries(new URLSearchParams$1(__search || "")) : __search ? __search.slice(1) : null,
|
|
5429
5429
|
pathname: __pathname || null,
|
|
5430
5430
|
path: __pathname + (__search || "") || null,
|
|
5431
5431
|
href: urlString
|
|
@@ -5499,7 +5499,7 @@ function format(urlObject) {
|
|
|
5499
5499
|
if (typeof urlObject.query === "string") {
|
|
5500
5500
|
result += "?" + urlObject.query;
|
|
5501
5501
|
} else {
|
|
5502
|
-
const params = new URLSearchParams();
|
|
5502
|
+
const params = new URLSearchParams$1();
|
|
5503
5503
|
for (const [key, value] of Object.entries(urlObject.query)) {
|
|
5504
5504
|
if (Array.isArray(value)) {
|
|
5505
5505
|
for (const v2 of value) {
|
|
@@ -5528,7 +5528,7 @@ function resolve$2(from, to2) {
|
|
|
5528
5528
|
}
|
|
5529
5529
|
}
|
|
5530
5530
|
const URL$1 = globalThis.URL;
|
|
5531
|
-
const URLSearchParams = globalThis.URLSearchParams;
|
|
5531
|
+
const URLSearchParams$1 = globalThis.URLSearchParams;
|
|
5532
5532
|
function fileURLToPath(url2) {
|
|
5533
5533
|
const urlObj = typeof url2 === "string" ? new globalThis.URL(url2) : url2;
|
|
5534
5534
|
if (urlObj.protocol !== "file:") {
|
|
@@ -5551,7 +5551,7 @@ var url$1 = {
|
|
|
5551
5551
|
format,
|
|
5552
5552
|
resolve: resolve$2,
|
|
5553
5553
|
URL: URL$1,
|
|
5554
|
-
URLSearchParams,
|
|
5554
|
+
URLSearchParams: URLSearchParams$1,
|
|
5555
5555
|
URLPattern: URLPattern$1,
|
|
5556
5556
|
fileURLToPath,
|
|
5557
5557
|
pathToFileURL
|
|
@@ -5560,7 +5560,7 @@ var urlShim = /* @__PURE__ */ Object.freeze({
|
|
|
5560
5560
|
__proto__: null,
|
|
5561
5561
|
URL: URL$1,
|
|
5562
5562
|
URLPattern: URLPattern$1,
|
|
5563
|
-
URLSearchParams,
|
|
5563
|
+
URLSearchParams: URLSearchParams$1,
|
|
5564
5564
|
default: url$1,
|
|
5565
5565
|
fileURLToPath,
|
|
5566
5566
|
format,
|
|
@@ -21191,10 +21191,17 @@ var implementation$1 = function bind2(that) {
|
|
|
21191
21191
|
var implementation = implementation$1;
|
|
21192
21192
|
var functionBind = Function.prototype.bind || implementation;
|
|
21193
21193
|
var functionCall = Function.prototype.call;
|
|
21194
|
-
var functionApply
|
|
21194
|
+
var functionApply;
|
|
21195
|
+
var hasRequiredFunctionApply;
|
|
21196
|
+
function requireFunctionApply() {
|
|
21197
|
+
if (hasRequiredFunctionApply) return functionApply;
|
|
21198
|
+
hasRequiredFunctionApply = 1;
|
|
21199
|
+
functionApply = Function.prototype.apply;
|
|
21200
|
+
return functionApply;
|
|
21201
|
+
}
|
|
21195
21202
|
var reflectApply = typeof Reflect !== "undefined" && Reflect && Reflect.apply;
|
|
21196
21203
|
var bind$2 = functionBind;
|
|
21197
|
-
var $apply$1 =
|
|
21204
|
+
var $apply$1 = requireFunctionApply();
|
|
21198
21205
|
var $call$2 = functionCall;
|
|
21199
21206
|
var $reflectApply = reflectApply;
|
|
21200
21207
|
var actualApply = $reflectApply || bind$2.call($call$2, $apply$1);
|
|
@@ -21314,7 +21321,7 @@ var hasSymbols = requireHasSymbols()();
|
|
|
21314
21321
|
var getProto = requireGetProto();
|
|
21315
21322
|
var $ObjectGPO = requireObject_getPrototypeOf();
|
|
21316
21323
|
var $ReflectGPO = requireReflect_getPrototypeOf();
|
|
21317
|
-
var $apply =
|
|
21324
|
+
var $apply = requireFunctionApply();
|
|
21318
21325
|
var $call = functionCall;
|
|
21319
21326
|
var needsEval = {};
|
|
21320
21327
|
var TypedArray = typeof Uint8Array === "undefined" || !getProto ? undefined$1 : getProto(Uint8Array);
|
|
@@ -21955,7 +21962,7 @@ function requireApplyBind() {
|
|
|
21955
21962
|
if (hasRequiredApplyBind) return applyBind;
|
|
21956
21963
|
hasRequiredApplyBind = 1;
|
|
21957
21964
|
var bind3 = functionBind;
|
|
21958
|
-
var $apply2 =
|
|
21965
|
+
var $apply2 = requireFunctionApply();
|
|
21959
21966
|
var actualApply$1 = actualApply;
|
|
21960
21967
|
applyBind = function applyBind2() {
|
|
21961
21968
|
return actualApply$1(bind3, $apply2, arguments);
|
|
@@ -32263,6 +32270,68 @@ function guestPromise(owner) {
|
|
|
32263
32270
|
}
|
|
32264
32271
|
return constructor;
|
|
32265
32272
|
}
|
|
32273
|
+
const kIsDisturbed = Symbol.for("nodejs.stream.disturbed");
|
|
32274
|
+
function isAsyncIterableBody(body) {
|
|
32275
|
+
if (body === null || typeof body !== "object") return false;
|
|
32276
|
+
if (typeof ReadableStream === "function" && body instanceof ReadableStream) return false;
|
|
32277
|
+
if (typeof Blob === "function" && body instanceof Blob) return false;
|
|
32278
|
+
if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) return false;
|
|
32279
|
+
if (typeof FormData === "function" && body instanceof FormData) return false;
|
|
32280
|
+
if (body instanceof URLSearchParams) return false;
|
|
32281
|
+
return typeof body[Symbol.asyncIterator] === "function";
|
|
32282
|
+
}
|
|
32283
|
+
function refuseDisturbed(body) {
|
|
32284
|
+
const stream = body;
|
|
32285
|
+
const disturbed = stream[kIsDisturbed] !== void 0 ? Boolean(stream[kIsDisturbed]) : Boolean(stream.readableDidRead || stream.readableAborted);
|
|
32286
|
+
if (disturbed || stream.locked === true) throw new TypeError("Response body object should not be disturbed or locked");
|
|
32287
|
+
}
|
|
32288
|
+
function streamFrom(iterable) {
|
|
32289
|
+
let iterator;
|
|
32290
|
+
return new ReadableStream({
|
|
32291
|
+
async start() {
|
|
32292
|
+
iterator = iterable[Symbol.asyncIterator]();
|
|
32293
|
+
},
|
|
32294
|
+
async pull(controller) {
|
|
32295
|
+
for (; ; ) {
|
|
32296
|
+
const { done, value } = await iterator.next();
|
|
32297
|
+
if (done) {
|
|
32298
|
+
controller.close();
|
|
32299
|
+
return;
|
|
32300
|
+
}
|
|
32301
|
+
const bytes2 = Buffer$9.isBuffer(value) ? value : Buffer$9.from(value);
|
|
32302
|
+
if (bytes2.byteLength) {
|
|
32303
|
+
controller.enqueue(new Uint8Array(bytes2));
|
|
32304
|
+
return;
|
|
32305
|
+
}
|
|
32306
|
+
}
|
|
32307
|
+
},
|
|
32308
|
+
async cancel() {
|
|
32309
|
+
var _a3;
|
|
32310
|
+
await ((_a3 = iterator == null ? void 0 : iterator.return) == null ? void 0 : _a3.call(iterator));
|
|
32311
|
+
}
|
|
32312
|
+
}, { highWaterMark: 0 });
|
|
32313
|
+
}
|
|
32314
|
+
const REQUEST_INIT_MEMBERS = ["method", "headers", "referrer", "referrerPolicy", "mode", "credentials", "cache", "redirect", "integrity", "keepalive", "signal", "window", "duplex", "priority"];
|
|
32315
|
+
function withNodeRequestBody(init) {
|
|
32316
|
+
if (!init) return init;
|
|
32317
|
+
const body = init.body;
|
|
32318
|
+
if (!isAsyncIterableBody(body)) return init;
|
|
32319
|
+
refuseDisturbed(body);
|
|
32320
|
+
const copy2 = {};
|
|
32321
|
+
for (const member of Object.keys(init)) if (member !== "body") copy2[member] = init[member];
|
|
32322
|
+
for (const member of REQUEST_INIT_MEMBERS) {
|
|
32323
|
+
if (member in copy2) continue;
|
|
32324
|
+
const value = init[member];
|
|
32325
|
+
if (value !== void 0) copy2[member] = value;
|
|
32326
|
+
}
|
|
32327
|
+
copy2.body = streamFrom(body);
|
|
32328
|
+
return copy2;
|
|
32329
|
+
}
|
|
32330
|
+
function nodeResponseBody(body) {
|
|
32331
|
+
if (!isAsyncIterableBody(body)) return body;
|
|
32332
|
+
refuseDisturbed(body);
|
|
32333
|
+
return streamFrom(body);
|
|
32334
|
+
}
|
|
32266
32335
|
const staticRequestBodies = /* @__PURE__ */ new WeakSet();
|
|
32267
32336
|
function rememberRequestBodySource(request, input, init) {
|
|
32268
32337
|
if (!request.body) return;
|
|
@@ -32272,6 +32341,11 @@ function rememberRequestBodySource(request, input, init) {
|
|
|
32272
32341
|
}
|
|
32273
32342
|
function guestFetch(process2, fallback) {
|
|
32274
32343
|
return function fetch2(input, init) {
|
|
32344
|
+
try {
|
|
32345
|
+
init = withNodeRequestBody(init);
|
|
32346
|
+
} catch (error) {
|
|
32347
|
+
return Promise.reject(error);
|
|
32348
|
+
}
|
|
32275
32349
|
return fallback.call(globalThis, input, init);
|
|
32276
32350
|
};
|
|
32277
32351
|
}
|
|
@@ -32310,7 +32384,8 @@ function installNodeResponse(target) {
|
|
|
32310
32384
|
const Native = target.Response;
|
|
32311
32385
|
if (typeof Native !== "function" || Native[INSTALLED]) return;
|
|
32312
32386
|
const proto = Native.prototype;
|
|
32313
|
-
|
|
32387
|
+
let nativeHeaders;
|
|
32388
|
+
for (let at3 = proto; at3 && !nativeHeaders; at3 = Object.getPrototypeOf(at3)) nativeHeaders = (_a3 = Object.getOwnPropertyDescriptor(at3, "headers")) == null ? void 0 : _a3.get;
|
|
32314
32389
|
const nativeFormData = proto.formData;
|
|
32315
32390
|
const nativeBlob = proto.blob;
|
|
32316
32391
|
const nativeArrayBuffer = proto.arrayBuffer;
|
|
@@ -32320,7 +32395,7 @@ function installNodeResponse(target) {
|
|
|
32320
32395
|
const NodeResponse = function Response2(body, init) {
|
|
32321
32396
|
if (!new.target) throw new TypeError("Class constructor Response cannot be invoked without 'new'");
|
|
32322
32397
|
const given = givenHeaders(init);
|
|
32323
|
-
const response = Reflect.construct(Native, [body, given ? withHeaders(init, given) : init], new.target === NodeResponse ? Native : new.target);
|
|
32398
|
+
const response = Reflect.construct(Native, [nodeResponseBody(body), given ? withHeaders(init, given) : init], new.target === NodeResponse ? Native : new.target);
|
|
32324
32399
|
kept.set(response, withCookies(nativeHeaders.call(response), given));
|
|
32325
32400
|
return response;
|
|
32326
32401
|
};
|
|
@@ -74207,6 +74282,63 @@ function installShellJobs(bash, host) {
|
|
|
74207
74282
|
if (shell.jobs.size === 0) shells$1.delete(id2);
|
|
74208
74283
|
return { stdout: held.stdout, stderr: held.stderr + stderr, exitCode: status };
|
|
74209
74284
|
}));
|
|
74285
|
+
for (const shell of ["sh", "bash"]) {
|
|
74286
|
+
bash.registerCommand(uA(shell, async (args, ctx) => {
|
|
74287
|
+
var _a3;
|
|
74288
|
+
const exec = ctx.exec;
|
|
74289
|
+
if (!exec) return { stdout: "", stderr: `bash: ${shell}: needs a shell that can run a script
|
|
74290
|
+
`, exitCode: 1 };
|
|
74291
|
+
let script;
|
|
74292
|
+
let zero2;
|
|
74293
|
+
let positional;
|
|
74294
|
+
let sourced;
|
|
74295
|
+
let at3 = 0;
|
|
74296
|
+
while (at3 < args.length && /^[-+][a-bd-zA-Z]+$/u.test(args[at3])) at3++;
|
|
74297
|
+
args = args.slice(at3);
|
|
74298
|
+
if (args[0] === "-c" && args.length >= 2) {
|
|
74299
|
+
script = args[1];
|
|
74300
|
+
zero2 = args[2] ?? shell;
|
|
74301
|
+
positional = args.slice(3);
|
|
74302
|
+
} else if (args.length === 0) {
|
|
74303
|
+
if (!((_a3 = ctx.stdin) == null ? void 0 : _a3.trim())) return { stdout: "", stderr: "", exitCode: 0 };
|
|
74304
|
+
script = ctx.stdin;
|
|
74305
|
+
zero2 = shell;
|
|
74306
|
+
positional = [];
|
|
74307
|
+
} else {
|
|
74308
|
+
const path2 = ctx.fs.resolvePath(ctx.cwd, args[0]);
|
|
74309
|
+
try {
|
|
74310
|
+
script = String(await ctx.fs.readFile(path2));
|
|
74311
|
+
} catch {
|
|
74312
|
+
return { stdout: "", stderr: `${shell}: ${args[0]}: No such file or directory
|
|
74313
|
+
`, exitCode: 127 };
|
|
74314
|
+
}
|
|
74315
|
+
zero2 = args[0];
|
|
74316
|
+
positional = args.slice(1);
|
|
74317
|
+
sourced = path2;
|
|
74318
|
+
}
|
|
74319
|
+
if (script.startsWith("#!")) {
|
|
74320
|
+
const newline = script.indexOf("\n");
|
|
74321
|
+
if (newline !== -1) script = script.slice(newline + 1);
|
|
74322
|
+
}
|
|
74323
|
+
const env = { ...ctx.exportedEnv ?? {}, "0": zero2, "#": String(positional.length), "@": positional.join(" "), "*": positional.join(" ") };
|
|
74324
|
+
positional.forEach((word, index2) => {
|
|
74325
|
+
env[String(index2 + 1)] = word;
|
|
74326
|
+
});
|
|
74327
|
+
const token = ctx.env.get(PROCESS_TOKEN_ENV);
|
|
74328
|
+
if (token) env[PROCESS_TOKEN_ENV] = token;
|
|
74329
|
+
if (sourced !== void 0) script = `. ${sourced.replace(/'/gu, "'\\''").replace(/^(.*)$/su, "'$1'")}`;
|
|
74330
|
+
const result = await exec(script, { env, cwd: ctx.cwd, stdin: ctx.stdin, signal: ctx.signal });
|
|
74331
|
+
return { stdout: result.stdout ?? "", stderr: result.stderr ?? "", exitCode: result.exitCode ?? 0 };
|
|
74332
|
+
}));
|
|
74333
|
+
}
|
|
74334
|
+
bash.registerCommand(uA("exec", async (args, ctx) => {
|
|
74335
|
+
if (args.length === 0) return { stdout: "", stderr: "", exitCode: 0 };
|
|
74336
|
+
if (!ctx.exec) return { stdout: "", stderr: "bash: exec: needs a shell that can run a line\n", exitCode: 1 };
|
|
74337
|
+
const line = args.map((word) => /^[A-Za-z0-9_./:=@%+,-]+$/u.test(word) ? word : `'${word.replace(/'/gu, "'\\''")}'`).join(" ");
|
|
74338
|
+
const env = Object.fromEntries(ctx.env);
|
|
74339
|
+
const result = await ctx.exec(line, { env, cwd: ctx.cwd, replaceEnv: true });
|
|
74340
|
+
return { stdout: result.stdout ?? "", stderr: result.stderr ?? "", exitCode: result.exitCode ?? 0 };
|
|
74341
|
+
}));
|
|
74210
74342
|
bash.registerCommand(uA("kill", async (args) => {
|
|
74211
74343
|
let signal = "SIGTERM";
|
|
74212
74344
|
let index2 = 0;
|
|
@@ -74388,6 +74520,18 @@ function registerRunStreams(token, streams2) {
|
|
|
74388
74520
|
}
|
|
74389
74521
|
function releaseRunStreams(token) {
|
|
74390
74522
|
_runStreams.delete(token);
|
|
74523
|
+
shellRuns.delete(token);
|
|
74524
|
+
hostedNodes.delete(token);
|
|
74525
|
+
}
|
|
74526
|
+
const shellRuns = /* @__PURE__ */ new Set();
|
|
74527
|
+
const hostedNodes = /* @__PURE__ */ new Set();
|
|
74528
|
+
function nestedNodeToken(parentToken, args, cwd) {
|
|
74529
|
+
var _a3;
|
|
74530
|
+
const token = `child-${__nextChildRun++}`;
|
|
74531
|
+
setRunPid(token, mintPid(), ((_a3 = runPid(parentToken)) == null ? void 0 : _a3.pid) ?? 0, { argv: ["node", ...args], ...cwd ? { cwd } : {} });
|
|
74532
|
+
const parentStreams = runStreamsFor(parentToken);
|
|
74533
|
+
if (parentStreams) registerRunStreams(token, parentStreams);
|
|
74534
|
+
return token;
|
|
74391
74535
|
}
|
|
74392
74536
|
function runStreamsFor(token) {
|
|
74393
74537
|
return _runStreams.get(token);
|
|
@@ -74456,19 +74600,26 @@ function initChildProcess(vfs2) {
|
|
|
74456
74600
|
if (!tree2) {
|
|
74457
74601
|
return { stdout: "", stderr: "VFS not initialized\n", exitCode: 1 };
|
|
74458
74602
|
}
|
|
74459
|
-
const
|
|
74603
|
+
const parentToken = runTokenOf(ctx);
|
|
74604
|
+
const nested = parentToken !== null && (shellRuns.has(parentToken) || hostedNodes.has(parentToken));
|
|
74605
|
+
const runToken = nested ? nestedNodeToken(parentToken, args, ctx.cwd) : parentToken;
|
|
74460
74606
|
const streams2 = runToken === null ? void 0 : runStreamsFor(runToken);
|
|
74461
74607
|
const processHost = nodeProcessHostFor();
|
|
74462
74608
|
if (processHost && runToken !== null) {
|
|
74463
|
-
|
|
74464
|
-
|
|
74465
|
-
|
|
74466
|
-
|
|
74467
|
-
|
|
74468
|
-
|
|
74469
|
-
|
|
74470
|
-
|
|
74471
|
-
|
|
74609
|
+
if (!nested) hostedNodes.add(runToken);
|
|
74610
|
+
try {
|
|
74611
|
+
return await runHostedNode(processHost, {
|
|
74612
|
+
token: runToken,
|
|
74613
|
+
argv: [...args],
|
|
74614
|
+
cwd: ctx.cwd,
|
|
74615
|
+
filesystem: tree2,
|
|
74616
|
+
env: guestEnvironmentOf(ctx),
|
|
74617
|
+
...typeof ctx.stdin === "string" ? { stdin: ctx.stdin } : {},
|
|
74618
|
+
...streams2 ? { streams: streams2 } : {}
|
|
74619
|
+
});
|
|
74620
|
+
} finally {
|
|
74621
|
+
if (nested) releaseRunStreams(runToken);
|
|
74622
|
+
}
|
|
74472
74623
|
}
|
|
74473
74624
|
const execArgv = [];
|
|
74474
74625
|
let first = 0;
|
|
@@ -75354,6 +75505,7 @@ function existsInTree(path2) {
|
|
|
75354
75505
|
return false;
|
|
75355
75506
|
}
|
|
75356
75507
|
}
|
|
75508
|
+
const SHELL_PROGRAMS = /* @__PURE__ */ new Set(["sh", "bash", "dash", "/bin/sh", "/bin/bash", "/usr/bin/sh", "/usr/bin/bash"]);
|
|
75357
75509
|
function engineProgramFor(file, cwd) {
|
|
75358
75510
|
const name = __substrateProgramName(file);
|
|
75359
75511
|
if (!name.includes("/")) return name;
|
|
@@ -75400,6 +75552,7 @@ function startChildRun(request) {
|
|
|
75400
75552
|
const controller = new AbortController();
|
|
75401
75553
|
const pendingStdin = [];
|
|
75402
75554
|
const admittedNode = nodeProcessHostInstalled();
|
|
75555
|
+
shellRuns.add(token);
|
|
75403
75556
|
const hostTerminal = request.terminal !== void 0 && hostExecutor() !== null && !admittedNode;
|
|
75404
75557
|
let wakeInput;
|
|
75405
75558
|
let liveInput = false;
|
|
@@ -75498,7 +75651,8 @@ function startChildRun(request) {
|
|
|
75498
75651
|
const begin = () => {
|
|
75499
75652
|
if (started || finished) return;
|
|
75500
75653
|
started = true;
|
|
75501
|
-
const
|
|
75654
|
+
const shellChild = SHELL_PROGRAMS.has(engineProgramFor(request.file, request.cwd)) && hostExecutor() !== null;
|
|
75655
|
+
const engineFirst = !hostTerminal && !shellChild && programExists(request.file, request.cwd, request.env);
|
|
75502
75656
|
liveInput = !hostTerminal && !admittedNode && request.stdinIsPipe && !engineFirst;
|
|
75503
75657
|
if (liveInput) {
|
|
75504
75658
|
pendingStdin.unshift(...initialStdin);
|
|
@@ -80548,6 +80702,7 @@ forGuestRealm(() => {
|
|
|
80548
80702
|
const kept = /* @__PURE__ */ new WeakMap();
|
|
80549
80703
|
class NodeRequest extends Native {
|
|
80550
80704
|
constructor(input, init) {
|
|
80705
|
+
init = withNodeRequestBody(init);
|
|
80551
80706
|
super(input, init);
|
|
80552
80707
|
rememberRequestBodySource(this, input, init);
|
|
80553
80708
|
const asked = new Headers((init == null ? void 0 : init.headers) ?? (input instanceof Native ? input.headers : void 0));
|
package/dist/index.mjs
CHANGED
|
@@ -5132,7 +5132,7 @@ function parse$1(urlString, parseQueryString = false, slashesDenoteHost = false)
|
|
|
5132
5132
|
hostname: null,
|
|
5133
5133
|
hash: __hash,
|
|
5134
5134
|
search: __search,
|
|
5135
|
-
query: parseQueryString ? Object.fromEntries(new URLSearchParams(__search || "")) : __search ? __search.slice(1) : null,
|
|
5135
|
+
query: parseQueryString ? Object.fromEntries(new URLSearchParams$1(__search || "")) : __search ? __search.slice(1) : null,
|
|
5136
5136
|
pathname: __pathname || null,
|
|
5137
5137
|
path: __pathname + (__search || "") || null,
|
|
5138
5138
|
href: urlString
|
|
@@ -5206,7 +5206,7 @@ function format$1(urlObject) {
|
|
|
5206
5206
|
if (typeof urlObject.query === "string") {
|
|
5207
5207
|
result += "?" + urlObject.query;
|
|
5208
5208
|
} else {
|
|
5209
|
-
const params = new URLSearchParams();
|
|
5209
|
+
const params = new URLSearchParams$1();
|
|
5210
5210
|
for (const [key, value] of Object.entries(urlObject.query)) {
|
|
5211
5211
|
if (Array.isArray(value)) {
|
|
5212
5212
|
for (const v of value) {
|
|
@@ -5235,7 +5235,7 @@ function resolve$2(from, to) {
|
|
|
5235
5235
|
}
|
|
5236
5236
|
}
|
|
5237
5237
|
const URL$1 = globalThis.URL;
|
|
5238
|
-
const URLSearchParams = globalThis.URLSearchParams;
|
|
5238
|
+
const URLSearchParams$1 = globalThis.URLSearchParams;
|
|
5239
5239
|
function fileURLToPath(url2) {
|
|
5240
5240
|
const urlObj = typeof url2 === "string" ? new globalThis.URL(url2) : url2;
|
|
5241
5241
|
if (urlObj.protocol !== "file:") {
|
|
@@ -5258,7 +5258,7 @@ const url$1 = {
|
|
|
5258
5258
|
format: format$1,
|
|
5259
5259
|
resolve: resolve$2,
|
|
5260
5260
|
URL: URL$1,
|
|
5261
|
-
URLSearchParams,
|
|
5261
|
+
URLSearchParams: URLSearchParams$1,
|
|
5262
5262
|
URLPattern: URLPattern$1,
|
|
5263
5263
|
fileURLToPath,
|
|
5264
5264
|
pathToFileURL
|
|
@@ -5267,7 +5267,7 @@ const urlShim = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
|
|
|
5267
5267
|
__proto__: null,
|
|
5268
5268
|
URL: URL$1,
|
|
5269
5269
|
URLPattern: URLPattern$1,
|
|
5270
|
-
URLSearchParams,
|
|
5270
|
+
URLSearchParams: URLSearchParams$1,
|
|
5271
5271
|
default: url$1,
|
|
5272
5272
|
fileURLToPath,
|
|
5273
5273
|
format: format$1,
|
|
@@ -32424,6 +32424,68 @@ function guestPromise(owner) {
|
|
|
32424
32424
|
}
|
|
32425
32425
|
return constructor;
|
|
32426
32426
|
}
|
|
32427
|
+
const kIsDisturbed = Symbol.for("nodejs.stream.disturbed");
|
|
32428
|
+
function isAsyncIterableBody(body) {
|
|
32429
|
+
if (body === null || typeof body !== "object") return false;
|
|
32430
|
+
if (typeof ReadableStream === "function" && body instanceof ReadableStream) return false;
|
|
32431
|
+
if (typeof Blob === "function" && body instanceof Blob) return false;
|
|
32432
|
+
if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) return false;
|
|
32433
|
+
if (typeof FormData === "function" && body instanceof FormData) return false;
|
|
32434
|
+
if (body instanceof URLSearchParams) return false;
|
|
32435
|
+
return typeof body[Symbol.asyncIterator] === "function";
|
|
32436
|
+
}
|
|
32437
|
+
function refuseDisturbed(body) {
|
|
32438
|
+
const stream = body;
|
|
32439
|
+
const disturbed = stream[kIsDisturbed] !== void 0 ? Boolean(stream[kIsDisturbed]) : Boolean(stream.readableDidRead || stream.readableAborted);
|
|
32440
|
+
if (disturbed || stream.locked === true) throw new TypeError("Response body object should not be disturbed or locked");
|
|
32441
|
+
}
|
|
32442
|
+
function streamFrom(iterable) {
|
|
32443
|
+
let iterator;
|
|
32444
|
+
return new ReadableStream({
|
|
32445
|
+
async start() {
|
|
32446
|
+
iterator = iterable[Symbol.asyncIterator]();
|
|
32447
|
+
},
|
|
32448
|
+
async pull(controller) {
|
|
32449
|
+
for (; ; ) {
|
|
32450
|
+
const { done, value } = await iterator.next();
|
|
32451
|
+
if (done) {
|
|
32452
|
+
controller.close();
|
|
32453
|
+
return;
|
|
32454
|
+
}
|
|
32455
|
+
const bytes2 = Buffer$9.isBuffer(value) ? value : Buffer$9.from(value);
|
|
32456
|
+
if (bytes2.byteLength) {
|
|
32457
|
+
controller.enqueue(new Uint8Array(bytes2));
|
|
32458
|
+
return;
|
|
32459
|
+
}
|
|
32460
|
+
}
|
|
32461
|
+
},
|
|
32462
|
+
async cancel() {
|
|
32463
|
+
var _a2;
|
|
32464
|
+
await ((_a2 = iterator == null ? void 0 : iterator.return) == null ? void 0 : _a2.call(iterator));
|
|
32465
|
+
}
|
|
32466
|
+
}, { highWaterMark: 0 });
|
|
32467
|
+
}
|
|
32468
|
+
const REQUEST_INIT_MEMBERS = ["method", "headers", "referrer", "referrerPolicy", "mode", "credentials", "cache", "redirect", "integrity", "keepalive", "signal", "window", "duplex", "priority"];
|
|
32469
|
+
function withNodeRequestBody(init) {
|
|
32470
|
+
if (!init) return init;
|
|
32471
|
+
const body = init.body;
|
|
32472
|
+
if (!isAsyncIterableBody(body)) return init;
|
|
32473
|
+
refuseDisturbed(body);
|
|
32474
|
+
const copy2 = {};
|
|
32475
|
+
for (const member of Object.keys(init)) if (member !== "body") copy2[member] = init[member];
|
|
32476
|
+
for (const member of REQUEST_INIT_MEMBERS) {
|
|
32477
|
+
if (member in copy2) continue;
|
|
32478
|
+
const value = init[member];
|
|
32479
|
+
if (value !== void 0) copy2[member] = value;
|
|
32480
|
+
}
|
|
32481
|
+
copy2.body = streamFrom(body);
|
|
32482
|
+
return copy2;
|
|
32483
|
+
}
|
|
32484
|
+
function nodeResponseBody(body) {
|
|
32485
|
+
if (!isAsyncIterableBody(body)) return body;
|
|
32486
|
+
refuseDisturbed(body);
|
|
32487
|
+
return streamFrom(body);
|
|
32488
|
+
}
|
|
32427
32489
|
let transport;
|
|
32428
32490
|
const staticRequestBodies = /* @__PURE__ */ new WeakSet();
|
|
32429
32491
|
function rememberRequestBodySource(request, input, init) {
|
|
@@ -32478,14 +32540,19 @@ function activity(owner, cancel) {
|
|
|
32478
32540
|
function guestFetch(process2, fallback) {
|
|
32479
32541
|
return function fetch2(input, init) {
|
|
32480
32542
|
const adapter = transport;
|
|
32481
|
-
|
|
32482
|
-
const owner = __tokenForProcess(process2);
|
|
32543
|
+
const owner = adapter ? __tokenForProcess(process2) : null;
|
|
32483
32544
|
const ended = () => {
|
|
32484
32545
|
var _a2;
|
|
32485
32546
|
return owner !== null && ((_a2 = __runFor(owner)) == null ? void 0 : _a2.process) !== process2;
|
|
32486
32547
|
};
|
|
32487
32548
|
const stopped2 = () => new DOMException("The requesting process has ended.", "AbortError");
|
|
32488
|
-
if (ended()) return Promise.reject(stopped2());
|
|
32549
|
+
if (adapter && ended()) return Promise.reject(stopped2());
|
|
32550
|
+
try {
|
|
32551
|
+
init = withNodeRequestBody(init);
|
|
32552
|
+
} catch (error) {
|
|
32553
|
+
return Promise.reject(error);
|
|
32554
|
+
}
|
|
32555
|
+
if (!adapter) return fallback.call(globalThis, input, init);
|
|
32489
32556
|
const context2 = { begin(cancel) {
|
|
32490
32557
|
if (ended()) throw stopped2();
|
|
32491
32558
|
return activity(owner, cancel);
|
|
@@ -32529,7 +32596,8 @@ function installNodeResponse(target) {
|
|
|
32529
32596
|
const Native = target.Response;
|
|
32530
32597
|
if (typeof Native !== "function" || Native[INSTALLED]) return;
|
|
32531
32598
|
const proto = Native.prototype;
|
|
32532
|
-
|
|
32599
|
+
let nativeHeaders;
|
|
32600
|
+
for (let at2 = proto; at2 && !nativeHeaders; at2 = Object.getPrototypeOf(at2)) nativeHeaders = (_a2 = Object.getOwnPropertyDescriptor(at2, "headers")) == null ? void 0 : _a2.get;
|
|
32533
32601
|
const nativeFormData = proto.formData;
|
|
32534
32602
|
const nativeBlob = proto.blob;
|
|
32535
32603
|
const nativeArrayBuffer = proto.arrayBuffer;
|
|
@@ -32539,7 +32607,7 @@ function installNodeResponse(target) {
|
|
|
32539
32607
|
const NodeResponse = function Response(body, init) {
|
|
32540
32608
|
if (!new.target) throw new TypeError("Class constructor Response cannot be invoked without 'new'");
|
|
32541
32609
|
const given = givenHeaders(init);
|
|
32542
|
-
const response = Reflect.construct(Native, [body, given ? withHeaders(init, given) : init], new.target === NodeResponse ? Native : new.target);
|
|
32610
|
+
const response = Reflect.construct(Native, [nodeResponseBody(body), given ? withHeaders(init, given) : init], new.target === NodeResponse ? Native : new.target);
|
|
32543
32611
|
kept.set(response, withCookies(nativeHeaders.call(response), given));
|
|
32544
32612
|
return response;
|
|
32545
32613
|
};
|
|
@@ -33809,6 +33877,63 @@ function installShellJobs(bash, host2) {
|
|
|
33809
33877
|
if (shell.jobs.size === 0) shells$1.delete(id);
|
|
33810
33878
|
return { stdout: held.stdout, stderr: held.stderr + stderr, exitCode: status };
|
|
33811
33879
|
}));
|
|
33880
|
+
for (const shell of ["sh", "bash"]) {
|
|
33881
|
+
bash.registerCommand(defineCommand(shell, async (args, ctx) => {
|
|
33882
|
+
var _a2;
|
|
33883
|
+
const exec = ctx.exec;
|
|
33884
|
+
if (!exec) return { stdout: "", stderr: `bash: ${shell}: needs a shell that can run a script
|
|
33885
|
+
`, exitCode: 1 };
|
|
33886
|
+
let script;
|
|
33887
|
+
let zero2;
|
|
33888
|
+
let positional;
|
|
33889
|
+
let sourced;
|
|
33890
|
+
let at2 = 0;
|
|
33891
|
+
while (at2 < args.length && /^[-+][a-bd-zA-Z]+$/u.test(args[at2])) at2++;
|
|
33892
|
+
args = args.slice(at2);
|
|
33893
|
+
if (args[0] === "-c" && args.length >= 2) {
|
|
33894
|
+
script = args[1];
|
|
33895
|
+
zero2 = args[2] ?? shell;
|
|
33896
|
+
positional = args.slice(3);
|
|
33897
|
+
} else if (args.length === 0) {
|
|
33898
|
+
if (!((_a2 = ctx.stdin) == null ? void 0 : _a2.trim())) return { stdout: "", stderr: "", exitCode: 0 };
|
|
33899
|
+
script = ctx.stdin;
|
|
33900
|
+
zero2 = shell;
|
|
33901
|
+
positional = [];
|
|
33902
|
+
} else {
|
|
33903
|
+
const path2 = ctx.fs.resolvePath(ctx.cwd, args[0]);
|
|
33904
|
+
try {
|
|
33905
|
+
script = String(await ctx.fs.readFile(path2));
|
|
33906
|
+
} catch {
|
|
33907
|
+
return { stdout: "", stderr: `${shell}: ${args[0]}: No such file or directory
|
|
33908
|
+
`, exitCode: 127 };
|
|
33909
|
+
}
|
|
33910
|
+
zero2 = args[0];
|
|
33911
|
+
positional = args.slice(1);
|
|
33912
|
+
sourced = path2;
|
|
33913
|
+
}
|
|
33914
|
+
if (script.startsWith("#!")) {
|
|
33915
|
+
const newline = script.indexOf("\n");
|
|
33916
|
+
if (newline !== -1) script = script.slice(newline + 1);
|
|
33917
|
+
}
|
|
33918
|
+
const env = { ...ctx.exportedEnv ?? {}, "0": zero2, "#": String(positional.length), "@": positional.join(" "), "*": positional.join(" ") };
|
|
33919
|
+
positional.forEach((word, index2) => {
|
|
33920
|
+
env[String(index2 + 1)] = word;
|
|
33921
|
+
});
|
|
33922
|
+
const token = ctx.env.get(PROCESS_TOKEN_ENV);
|
|
33923
|
+
if (token) env[PROCESS_TOKEN_ENV] = token;
|
|
33924
|
+
if (sourced !== void 0) script = `. ${sourced.replace(/'/gu, "'\\''").replace(/^(.*)$/su, "'$1'")}`;
|
|
33925
|
+
const result = await exec(script, { env, cwd: ctx.cwd, stdin: ctx.stdin, signal: ctx.signal });
|
|
33926
|
+
return { stdout: result.stdout ?? "", stderr: result.stderr ?? "", exitCode: result.exitCode ?? 0 };
|
|
33927
|
+
}));
|
|
33928
|
+
}
|
|
33929
|
+
bash.registerCommand(defineCommand("exec", async (args, ctx) => {
|
|
33930
|
+
if (args.length === 0) return { stdout: "", stderr: "", exitCode: 0 };
|
|
33931
|
+
if (!ctx.exec) return { stdout: "", stderr: "bash: exec: needs a shell that can run a line\n", exitCode: 1 };
|
|
33932
|
+
const line = args.map((word) => /^[A-Za-z0-9_./:=@%+,-]+$/u.test(word) ? word : `'${word.replace(/'/gu, "'\\''")}'`).join(" ");
|
|
33933
|
+
const env = Object.fromEntries(ctx.env);
|
|
33934
|
+
const result = await ctx.exec(line, { env, cwd: ctx.cwd, replaceEnv: true });
|
|
33935
|
+
return { stdout: result.stdout ?? "", stderr: result.stderr ?? "", exitCode: result.exitCode ?? 0 };
|
|
33936
|
+
}));
|
|
33812
33937
|
bash.registerCommand(defineCommand("kill", async (args) => {
|
|
33813
33938
|
let signal = "SIGTERM";
|
|
33814
33939
|
let index2 = 0;
|
|
@@ -34009,6 +34134,18 @@ function registerRunStreams(token, streams2) {
|
|
|
34009
34134
|
}
|
|
34010
34135
|
function releaseRunStreams(token) {
|
|
34011
34136
|
_runStreams.delete(token);
|
|
34137
|
+
shellRuns.delete(token);
|
|
34138
|
+
hostedNodes.delete(token);
|
|
34139
|
+
}
|
|
34140
|
+
const shellRuns = /* @__PURE__ */ new Set();
|
|
34141
|
+
const hostedNodes = /* @__PURE__ */ new Set();
|
|
34142
|
+
function nestedNodeToken(parentToken, args, cwd) {
|
|
34143
|
+
var _a2;
|
|
34144
|
+
const token = `child-${__nextChildRun++}`;
|
|
34145
|
+
setRunPid(token, mintPid(), ((_a2 = runPid(parentToken)) == null ? void 0 : _a2.pid) ?? 0, { argv: ["node", ...args], ...cwd ? { cwd } : {} });
|
|
34146
|
+
const parentStreams = runStreamsFor(parentToken);
|
|
34147
|
+
if (parentStreams) registerRunStreams(token, parentStreams);
|
|
34148
|
+
return token;
|
|
34012
34149
|
}
|
|
34013
34150
|
function runStreamsFor(token) {
|
|
34014
34151
|
return _runStreams.get(token);
|
|
@@ -34100,19 +34237,26 @@ function initChildProcess(vfs2) {
|
|
|
34100
34237
|
if (!tree2) {
|
|
34101
34238
|
return { stdout: "", stderr: "VFS not initialized\n", exitCode: 1 };
|
|
34102
34239
|
}
|
|
34103
|
-
const
|
|
34240
|
+
const parentToken = runTokenOf(ctx);
|
|
34241
|
+
const nested = parentToken !== null && (shellRuns.has(parentToken) || hostedNodes.has(parentToken));
|
|
34242
|
+
const runToken = nested ? nestedNodeToken(parentToken, args, ctx.cwd) : parentToken;
|
|
34104
34243
|
const streams2 = runToken === null ? void 0 : runStreamsFor(runToken);
|
|
34105
34244
|
const processHost = nodeProcessHostFor(runToken);
|
|
34106
34245
|
if (processHost && runToken !== null) {
|
|
34107
|
-
|
|
34108
|
-
|
|
34109
|
-
|
|
34110
|
-
|
|
34111
|
-
|
|
34112
|
-
|
|
34113
|
-
|
|
34114
|
-
|
|
34115
|
-
|
|
34246
|
+
if (!nested) hostedNodes.add(runToken);
|
|
34247
|
+
try {
|
|
34248
|
+
return await runHostedNode(processHost, {
|
|
34249
|
+
token: runToken,
|
|
34250
|
+
argv: [...args],
|
|
34251
|
+
cwd: ctx.cwd,
|
|
34252
|
+
filesystem: tree2,
|
|
34253
|
+
env: guestEnvironmentOf(ctx),
|
|
34254
|
+
...typeof ctx.stdin === "string" ? { stdin: ctx.stdin } : {},
|
|
34255
|
+
...streams2 ? { streams: streams2 } : {}
|
|
34256
|
+
});
|
|
34257
|
+
} finally {
|
|
34258
|
+
if (nested) releaseRunStreams(runToken);
|
|
34259
|
+
}
|
|
34116
34260
|
}
|
|
34117
34261
|
const execArgv = [];
|
|
34118
34262
|
let first = 0;
|
|
@@ -35018,6 +35162,7 @@ function existsInTree(path2) {
|
|
|
35018
35162
|
return false;
|
|
35019
35163
|
}
|
|
35020
35164
|
}
|
|
35165
|
+
const SHELL_PROGRAMS = /* @__PURE__ */ new Set(["sh", "bash", "dash", "/bin/sh", "/bin/bash", "/usr/bin/sh", "/usr/bin/bash"]);
|
|
35021
35166
|
function engineProgramFor(file, cwd) {
|
|
35022
35167
|
const name = __substrateProgramName(file);
|
|
35023
35168
|
if (!name.includes("/")) return name;
|
|
@@ -35064,6 +35209,7 @@ function startChildRun(request) {
|
|
|
35064
35209
|
const controller = new AbortController();
|
|
35065
35210
|
const pendingStdin = [];
|
|
35066
35211
|
const admittedNode = nodeProcessHostInstalled() && engineProgramFor(request.file, request.cwd) === "node";
|
|
35212
|
+
if (!admittedNode) shellRuns.add(token);
|
|
35067
35213
|
const hostTerminal = request.terminal !== void 0 && hostExecutor() !== null && !admittedNode;
|
|
35068
35214
|
let wakeInput;
|
|
35069
35215
|
let liveInput = false;
|
|
@@ -35162,7 +35308,8 @@ function startChildRun(request) {
|
|
|
35162
35308
|
const begin = () => {
|
|
35163
35309
|
if (started2 || finished) return;
|
|
35164
35310
|
started2 = true;
|
|
35165
|
-
const
|
|
35311
|
+
const shellChild = SHELL_PROGRAMS.has(engineProgramFor(request.file, request.cwd)) && hostExecutor() !== null;
|
|
35312
|
+
const engineFirst = !hostTerminal && !shellChild && programExists(request.file, request.cwd, request.env);
|
|
35166
35313
|
liveInput = !hostTerminal && !admittedNode && request.stdinIsPipe && !engineFirst;
|
|
35167
35314
|
if (liveInput) {
|
|
35168
35315
|
pendingStdin.unshift(...initialStdin);
|
|
@@ -40220,6 +40367,7 @@ forGuestRealm(() => {
|
|
|
40220
40367
|
const kept = /* @__PURE__ */ new WeakMap();
|
|
40221
40368
|
class NodeRequest extends Native {
|
|
40222
40369
|
constructor(input, init) {
|
|
40370
|
+
init = withNodeRequestBody(init);
|
|
40223
40371
|
super(input, init);
|
|
40224
40372
|
rememberRequestBodySource(this, input, init);
|
|
40225
40373
|
const asked = new Headers((init == null ? void 0 : init.headers) ?? (input instanceof Native ? input.headers : void 0));
|
|
@@ -40548,7 +40696,7 @@ class WorkerRuntime {
|
|
|
40548
40696
|
this.worker = new Worker(
|
|
40549
40697
|
new URL(
|
|
40550
40698
|
/* @vite-ignore */
|
|
40551
|
-
"/assets/runtime-worker-
|
|
40699
|
+
"/assets/runtime-worker-C1IV4qnr.js",
|
|
40552
40700
|
import.meta.url
|
|
40553
40701
|
),
|
|
40554
40702
|
{ type: "module" }
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The init with an async-iterable body read as a stream, or the init itself.
|
|
3
|
+
* The body is read once; the replacement is built member by member (an init
|
|
4
|
+
* may carry its members on getters of its prototype, as a `Request` passed as
|
|
5
|
+
* an init does), with the init's own other members as they are.
|
|
6
|
+
*/
|
|
7
|
+
export declare function withNodeRequestBody<T extends RequestInit | undefined>(init: T): T;
|
|
8
|
+
/** A response body as undici takes it: an async iterable read as a stream, anything else as given. */
|
|
9
|
+
export declare function nodeResponseBody(body: unknown): unknown;
|
package/package.json
CHANGED
package/src/fetch-transport.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** Host Fetch I/O uses the same process-owned liveness registry as sockets. */
|
|
2
|
+
import { withNodeRequestBody } from './node-body';
|
|
2
3
|
import type { Process } from './shims/process';
|
|
3
4
|
import { __tokenForProcess, __runFor, enterRun, type ProcessToken } from './process-tokens';
|
|
4
5
|
import { registerHandle, __adoptHandle, refHandle, unrefHandle, releaseHandle } from './node-lib/binding/handles';
|
|
@@ -83,11 +84,14 @@ function activity(owner: ProcessToken | null, cancel: () => void): FetchActivity
|
|
|
83
84
|
export function guestFetch(process: Process, fallback: typeof globalThis.fetch): typeof globalThis.fetch {
|
|
84
85
|
return function fetch(input, init) {
|
|
85
86
|
const adapter = transport;
|
|
86
|
-
|
|
87
|
-
const owner = __tokenForProcess(process);
|
|
87
|
+
const owner = adapter ? __tokenForProcess(process) : null;
|
|
88
88
|
const ended = () => owner !== null && __runFor(owner)?.process !== process;
|
|
89
89
|
const stopped = () => new DOMException('The requesting process has ended.', 'AbortError');
|
|
90
|
-
if (ended()) return Promise.reject(stopped());
|
|
90
|
+
if (adapter && ended()) return Promise.reject(stopped());
|
|
91
|
+
// A stream body is read as undici reads it, whichever door the fetch
|
|
92
|
+
// leaves by; a body undici refuses rejects the fetch, as Node's does.
|
|
93
|
+
try { init = withNodeRequestBody(init); } catch (error) { return Promise.reject(error); }
|
|
94
|
+
if (!adapter) return fallback.call(globalThis, input, init);
|
|
91
95
|
const context: FetchTransportContext = { begin(cancel) {
|
|
92
96
|
if (ended()) throw stopped();
|
|
93
97
|
return activity(owner, cancel);
|
package/src/node-body.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A body as Node's fetch (undici's `extractBody`) accepts it: besides what a
|
|
3
|
+
* browser takes, any async iterable (a Node `Readable`, an `http.IncomingMessage`,
|
|
4
|
+
* an async generator) is read as a stream of its chunks, each a `Buffer`,
|
|
5
|
+
* a typed array or a string (UTF-8). A browser's `Request` and `Response` take
|
|
6
|
+
* such an object for a string: `new Request(url, { method: 'POST', body:
|
|
7
|
+
* incomingMessage, duplex: 'half' })` has the body "[object Object]". Next's
|
|
8
|
+
* App Router builds every route handler's request exactly that way (its
|
|
9
|
+
* request adapter passes the Node request as the body, "handled by undici"),
|
|
10
|
+
* so every POST a route handler read was that string, and NextAuth's
|
|
11
|
+
* credentials callback found no CSRF token in it.
|
|
12
|
+
*
|
|
13
|
+
* Only such a body is changed; every other init is handed on untouched.
|
|
14
|
+
*/
|
|
15
|
+
import { Buffer } from './node-lib/buffer-module';
|
|
16
|
+
|
|
17
|
+
/** The mark Node's streams carry once read (`internal/streams/utils`, `kIsDisturbed`). */
|
|
18
|
+
const kIsDisturbed = Symbol.for('nodejs.stream.disturbed');
|
|
19
|
+
|
|
20
|
+
function isAsyncIterableBody(body: unknown): body is AsyncIterable<unknown> {
|
|
21
|
+
if (body === null || typeof body !== 'object') return false;
|
|
22
|
+
if (typeof ReadableStream === 'function' && body instanceof ReadableStream) return false;
|
|
23
|
+
if (typeof Blob === 'function' && body instanceof Blob) return false;
|
|
24
|
+
if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) return false;
|
|
25
|
+
if (typeof FormData === 'function' && body instanceof FormData) return false;
|
|
26
|
+
if (body instanceof URLSearchParams) return false;
|
|
27
|
+
return typeof (body as { [Symbol.asyncIterator]?: unknown })[Symbol.asyncIterator] === 'function';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Node's `isDisturbed`, and undici's refusal of a body already read or locked. */
|
|
31
|
+
function refuseDisturbed(body: object): void {
|
|
32
|
+
const stream = body as { locked?: unknown; readableDidRead?: unknown; readableAborted?: unknown; [kIsDisturbed]?: unknown };
|
|
33
|
+
const disturbed = stream[kIsDisturbed] !== undefined ? Boolean(stream[kIsDisturbed]) : Boolean(stream.readableDidRead || stream.readableAborted);
|
|
34
|
+
if (disturbed || stream.locked === true) throw new TypeError('Response body object should not be disturbed or locked');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* undici's `ReadableStreamFrom`: the iterator made when the stream starts,
|
|
39
|
+
* each chunk `Buffer.from`'d as Node's is (a chunk `Buffer.from` refuses
|
|
40
|
+
* errors the stream), an empty chunk passed over for the next, and the
|
|
41
|
+
* iterator returned, with no argument, on cancel.
|
|
42
|
+
*/
|
|
43
|
+
function streamFrom(iterable: AsyncIterable<unknown>): ReadableStream<Uint8Array> {
|
|
44
|
+
let iterator: AsyncIterator<unknown> | undefined;
|
|
45
|
+
return new ReadableStream<Uint8Array>({
|
|
46
|
+
async start() { iterator = iterable[Symbol.asyncIterator](); },
|
|
47
|
+
async pull(controller) {
|
|
48
|
+
for (;;) {
|
|
49
|
+
const { done, value } = await iterator!.next();
|
|
50
|
+
if (done) { controller.close(); return; }
|
|
51
|
+
const bytes = (Buffer.isBuffer(value) ? value : (Buffer.from as (chunk: unknown) => Uint8Array)(value)) as Uint8Array;
|
|
52
|
+
if (bytes.byteLength) { controller.enqueue(new Uint8Array(bytes)); return; }
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
async cancel() { await iterator?.return?.(); },
|
|
56
|
+
}, { highWaterMark: 0 });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const REQUEST_INIT_MEMBERS = ['method', 'headers', 'referrer', 'referrerPolicy', 'mode', 'credentials', 'cache', 'redirect', 'integrity', 'keepalive', 'signal', 'window', 'duplex', 'priority'] as const;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The init with an async-iterable body read as a stream, or the init itself.
|
|
63
|
+
* The body is read once; the replacement is built member by member (an init
|
|
64
|
+
* may carry its members on getters of its prototype, as a `Request` passed as
|
|
65
|
+
* an init does), with the init's own other members as they are.
|
|
66
|
+
*/
|
|
67
|
+
export function withNodeRequestBody<T extends RequestInit | undefined>(init: T): T {
|
|
68
|
+
if (!init) return init;
|
|
69
|
+
const body = (init as RequestInit).body as unknown;
|
|
70
|
+
if (!isAsyncIterableBody(body)) return init;
|
|
71
|
+
refuseDisturbed(body);
|
|
72
|
+
const copy: Record<string, unknown> = {};
|
|
73
|
+
for (const member of Object.keys(init)) if (member !== 'body') copy[member] = (init as Record<string, unknown>)[member];
|
|
74
|
+
for (const member of REQUEST_INIT_MEMBERS) {
|
|
75
|
+
if (member in copy) continue;
|
|
76
|
+
const value = (init as Record<string, unknown>)[member];
|
|
77
|
+
if (value !== undefined) copy[member] = value;
|
|
78
|
+
}
|
|
79
|
+
copy.body = streamFrom(body);
|
|
80
|
+
return copy as T;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** A response body as undici takes it: an async iterable read as a stream, anything else as given. */
|
|
84
|
+
export function nodeResponseBody(body: unknown): unknown {
|
|
85
|
+
if (!isAsyncIterableBody(body)) return body;
|
|
86
|
+
refuseDisturbed(body);
|
|
87
|
+
return streamFrom(body);
|
|
88
|
+
}
|
package/src/node-response.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineOnHost, takeFromHost } from './host-globals';
|
|
2
|
+
import { nodeResponseBody } from './node-body';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* A web `Response` that keeps its `Set-Cookie` headers, as Node's does.
|
|
@@ -76,7 +77,9 @@ export function installNodeResponse(target: { Response?: typeof Response }): voi
|
|
|
76
77
|
const Native = target.Response as ResponseConstructor | undefined;
|
|
77
78
|
if (typeof Native !== 'function' || Native[INSTALLED]) return;
|
|
78
79
|
const proto = Native.prototype;
|
|
79
|
-
|
|
80
|
+
// The getter is the prototype's own in a browser; found up the chain wherever it is.
|
|
81
|
+
let nativeHeaders: (() => unknown) | undefined;
|
|
82
|
+
for (let at: object | null = proto; at && !nativeHeaders; at = Object.getPrototypeOf(at)) nativeHeaders = Object.getOwnPropertyDescriptor(at, 'headers')?.get;
|
|
80
83
|
const nativeFormData = proto.formData;
|
|
81
84
|
const nativeBlob = proto.blob;
|
|
82
85
|
const nativeArrayBuffer = proto.arrayBuffer;
|
|
@@ -87,7 +90,7 @@ export function installNodeResponse(target: { Response?: typeof Response }): voi
|
|
|
87
90
|
const NodeResponse = function Response(this: unknown, body?: BodyInit | null, init?: ResponseInit): Response {
|
|
88
91
|
if (!new.target) throw new TypeError("Class constructor Response cannot be invoked without 'new'");
|
|
89
92
|
const given = givenHeaders(init);
|
|
90
|
-
const response = Reflect.construct(Native, [body, given ? withHeaders(init, given) : init], (new.target as unknown) === NodeResponse ? Native : new.target) as Response;
|
|
93
|
+
const response = Reflect.construct(Native, [nodeResponseBody(body) as BodyInit | null | undefined, given ? withHeaders(init, given) : init], (new.target as unknown) === NodeResponse ? Native : new.target) as Response;
|
|
91
94
|
kept.set(response, withCookies(nativeHeaders.call(response) as Headers, given));
|
|
92
95
|
return response;
|
|
93
96
|
} as unknown as ResponseConstructor;
|
package/src/runtime.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { VirtualFS } from './virtual-fs';
|
|
|
9
9
|
import { guestPromise, intrinsicPromise } from './promise-ownership';
|
|
10
10
|
import { guestFetch, rememberRequestBodySource } from './fetch-transport';
|
|
11
11
|
import { installNodeResponse } from './node-response';
|
|
12
|
+
import { withNodeRequestBody } from './node-body';
|
|
12
13
|
import { forGuestRealm, installGuestRealm, takeFromHost, defineOnHost, heldWork } from './host-globals';
|
|
13
14
|
import type { IRuntime, IExecuteResult, IRuntimeOptions } from './runtime-interface';
|
|
14
15
|
import type { PackageJson } from './types/package-json';
|
|
@@ -2953,6 +2954,8 @@ forGuestRealm(() => {
|
|
|
2953
2954
|
const kept = new WeakMap<object, Headers>();
|
|
2954
2955
|
class NodeRequest extends Native {
|
|
2955
2956
|
constructor(input: RequestInfo | URL, init?: RequestInit) {
|
|
2957
|
+
// An async-iterable body (a Node stream) is read as undici reads it.
|
|
2958
|
+
init = withNodeRequestBody(init);
|
|
2956
2959
|
super(input as RequestInfo, init);
|
|
2957
2960
|
rememberRequestBodySource(this, input, init);
|
|
2958
2961
|
const asked = new Headers(init?.headers ?? (input instanceof Native ? (input as Request).headers : undefined));
|
|
@@ -268,6 +268,22 @@ export function registerRunStreams(token: ProcessToken, streams: RunStreams): vo
|
|
|
268
268
|
/** Forget a run that has ended. */
|
|
269
269
|
export function releaseRunStreams(token: ProcessToken): void {
|
|
270
270
|
_runStreams.delete(token);
|
|
271
|
+
shellRuns.delete(token);
|
|
272
|
+
hostedNodes.delete(token);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** Runs whose program is a shell or another non-node program: a `node` they run is their child. */
|
|
276
|
+
const shellRuns = new Set<ProcessToken>();
|
|
277
|
+
/** Runs that are themselves a hosted `node`: a further `node` under them is a child. */
|
|
278
|
+
const hostedNodes = new Set<ProcessToken>();
|
|
279
|
+
|
|
280
|
+
/** A child identity for a `node` a run's shell runs: its own token and pid under the run's, on the run's streams. */
|
|
281
|
+
function nestedNodeToken(parentToken: ProcessToken, args: readonly string[], cwd: string | undefined): ProcessToken {
|
|
282
|
+
const token: ProcessToken = `child-${__nextChildRun++}`;
|
|
283
|
+
setRunPid(token, mintPid(), runPid(parentToken)?.pid ?? 0, { argv: ['node', ...args], ...(cwd ? { cwd } : {}) });
|
|
284
|
+
const parentStreams = runStreamsFor(parentToken);
|
|
285
|
+
if (parentStreams) registerRunStreams(token, parentStreams);
|
|
286
|
+
return token;
|
|
271
287
|
}
|
|
272
288
|
|
|
273
289
|
/** What the host gave the named run, while it lasts. */
|
|
@@ -429,14 +445,30 @@ export function initChildProcess(vfs: VirtualFS): void {
|
|
|
429
445
|
|
|
430
446
|
// Capture this run's name, streams and cancellation before another Node
|
|
431
447
|
// entry can start. Forks and shell entries reach this same dispatch seam.
|
|
432
|
-
const
|
|
448
|
+
const parentToken = runTokenOf(ctx);
|
|
449
|
+
// A `node` a shell runs is a process of its own unless it is the run
|
|
450
|
+
// itself: the first `node` of a run that is not a shell's child is the
|
|
451
|
+
// run (the CMD `node server.js`, the page's `node -e`), and every other
|
|
452
|
+
// one, a line of a start script, the second command of a `-c` line, a
|
|
453
|
+
// step of a `sh file` child, gets a child token under the run's pid,
|
|
454
|
+
// the run's streams, and its own admission at the host. Admitted under
|
|
455
|
+
// the shell's own token, its exit was read as the shell's: a script
|
|
456
|
+
// ended at its first `node` line with nothing after it (measured
|
|
457
|
+
// 2026-09-28: `sh start.sh` printed its first step and stopped).
|
|
458
|
+
const nested = parentToken !== null && (shellRuns.has(parentToken) || hostedNodes.has(parentToken));
|
|
459
|
+
const runToken = nested ? nestedNodeToken(parentToken!, args, ctx.cwd) : parentToken;
|
|
433
460
|
const streams = runToken === null ? undefined : runStreamsFor(runToken);
|
|
434
461
|
const processHost = nodeProcessHostFor(runToken);
|
|
435
462
|
if (processHost && runToken !== null) {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
463
|
+
if (!nested) hostedNodes.add(runToken);
|
|
464
|
+
try {
|
|
465
|
+
return await runHostedNode(processHost, {
|
|
466
|
+
token: runToken, argv: [...args], cwd: ctx.cwd, filesystem: tree, env: guestEnvironmentOf(ctx),
|
|
467
|
+
...(typeof ctx.stdin === 'string' ? { stdin: ctx.stdin } : {}), ...(streams ? { streams } : {}),
|
|
468
|
+
});
|
|
469
|
+
} finally {
|
|
470
|
+
if (nested) releaseRunStreams(runToken);
|
|
471
|
+
}
|
|
440
472
|
}
|
|
441
473
|
|
|
442
474
|
// Node reads its own options before the script: `node --turbo-fast-api-calls
|
|
@@ -1719,6 +1751,9 @@ function existsInTree(path: string): boolean {
|
|
|
1719
1751
|
* a program the engine can run; the engine's own `node`, written to
|
|
1720
1752
|
* `/usr/local/bin/node` when the shim is initialized, is one of those.
|
|
1721
1753
|
*/
|
|
1754
|
+
/** The shells a script or a `-c` line runs under; a child of these goes to the host when there is one. */
|
|
1755
|
+
const SHELL_PROGRAMS = new Set(['sh', 'bash', 'dash', '/bin/sh', '/bin/bash', '/usr/bin/sh', '/usr/bin/bash']);
|
|
1756
|
+
|
|
1722
1757
|
function engineProgramFor(file: string, cwd?: string): string {
|
|
1723
1758
|
const name = __substrateProgramName(file);
|
|
1724
1759
|
if (!name.includes('/')) return name;
|
|
@@ -1794,6 +1829,7 @@ function startChildRun(request: RunRequest): StartedRun {
|
|
|
1794
1829
|
// A host terminal consumes input incrementally. The old string-only
|
|
1795
1830
|
// route dropped every keystroke that arrived after a child was launched.
|
|
1796
1831
|
const admittedNode = nodeProcessHostInstalled() && engineProgramFor(request.file, request.cwd) === 'node';
|
|
1832
|
+
if (!admittedNode) shellRuns.add(token);
|
|
1797
1833
|
const hostTerminal = request.terminal !== undefined && hostExecutor() !== null && !admittedNode;
|
|
1798
1834
|
let wakeInput: (() => void) | undefined;
|
|
1799
1835
|
/**
|
|
@@ -1904,7 +1940,14 @@ function startChildRun(request: RunRequest): StartedRun {
|
|
|
1904
1940
|
const begin = (): void => {
|
|
1905
1941
|
if (started || finished) return;
|
|
1906
1942
|
started = true;
|
|
1907
|
-
|
|
1943
|
+
// A shell a hosted program spawns (`sh start.sh`, `sh -c '…'`) runs at
|
|
1944
|
+
// the host when there is one, not in this worker's own bash: the host is
|
|
1945
|
+
// where the programs the page registered live (`npx` under /usr/bin),
|
|
1946
|
+
// and where a `node` inside the script is given a named run. Run here,
|
|
1947
|
+
// the script's `node` found no name and was refused, and its `npx` was
|
|
1948
|
+
// not found at all.
|
|
1949
|
+
const shellChild = SHELL_PROGRAMS.has(engineProgramFor(request.file, request.cwd)) && hostExecutor() !== null;
|
|
1950
|
+
const engineFirst = !hostTerminal && !shellChild && programExists(request.file, request.cwd, request.env);
|
|
1908
1951
|
liveInput = !hostTerminal && !admittedNode && request.stdinIsPipe && !engineFirst;
|
|
1909
1952
|
if (liveInput) {
|
|
1910
1953
|
pendingStdin.unshift(...initialStdin);
|
package/src/shims/shell-jobs.ts
CHANGED
|
@@ -386,6 +386,78 @@ export function installShellJobs(bash: Bash, host: ShellJobsHost): void {
|
|
|
386
386
|
return { stdout: held.stdout, stderr: held.stderr + stderr, exitCode: status };
|
|
387
387
|
}));
|
|
388
388
|
|
|
389
|
+
// `sh file`, `sh -c line` and `sh` on stdin: the shell's own ran the script
|
|
390
|
+
// with the caller's exported variables alone, and a run's name travels in
|
|
391
|
+
// a variable the shell never exports, so a `node` on any line of a script
|
|
392
|
+
// found no named run and was refused ("Isolated Node execution requires a
|
|
393
|
+
// named run", a start script in browser-substrate's tab, 2026-09-28). The
|
|
394
|
+
// script is run as the shell's own would run it, with the name carried.
|
|
395
|
+
for (const shell of ['sh', 'bash'] as const) {
|
|
396
|
+
bash.registerCommand(defineCommand(shell, async (args, ctx) => {
|
|
397
|
+
const exec = ctx.exec;
|
|
398
|
+
if (!exec) return { stdout: '', stderr: `bash: ${shell}: needs a shell that can run a script\n`, exitCode: 1 };
|
|
399
|
+
let script: string;
|
|
400
|
+
let zero: string;
|
|
401
|
+
let positional: string[];
|
|
402
|
+
let sourced: string | undefined;
|
|
403
|
+
// Options before the script (`sh -e start.sh`, `bash -eu -c …`) are the
|
|
404
|
+
// shell's own; they are passed over rather than read as the file's name.
|
|
405
|
+
let at = 0;
|
|
406
|
+
while (at < args.length && /^[-+][a-bd-zA-Z]+$/u.test(args[at]!)) at++;
|
|
407
|
+
args = args.slice(at);
|
|
408
|
+
if (args[0] === '-c' && args.length >= 2) {
|
|
409
|
+
script = args[1]!;
|
|
410
|
+
zero = args[2] ?? shell;
|
|
411
|
+
positional = args.slice(3);
|
|
412
|
+
} else if (args.length === 0) {
|
|
413
|
+
if (!ctx.stdin?.trim()) return { stdout: '', stderr: '', exitCode: 0 };
|
|
414
|
+
script = ctx.stdin;
|
|
415
|
+
zero = shell;
|
|
416
|
+
positional = [];
|
|
417
|
+
} else {
|
|
418
|
+
const path = ctx.fs.resolvePath(ctx.cwd, args[0]!);
|
|
419
|
+
try { script = String(await ctx.fs.readFile(path)); }
|
|
420
|
+
catch { return { stdout: '', stderr: `${shell}: ${args[0]}: No such file or directory\n`, exitCode: 127 }; }
|
|
421
|
+
zero = args[0]!;
|
|
422
|
+
positional = args.slice(1);
|
|
423
|
+
sourced = path;
|
|
424
|
+
}
|
|
425
|
+
if (script.startsWith('#!')) {
|
|
426
|
+
const newline = script.indexOf('\n');
|
|
427
|
+
if (newline !== -1) script = script.slice(newline + 1);
|
|
428
|
+
}
|
|
429
|
+
const env: Record<string, string> = { ...(ctx.exportedEnv ?? {}), '0': zero, '#': String(positional.length), '@': positional.join(' '), '*': positional.join(' ') };
|
|
430
|
+
positional.forEach((word, index) => { env[String(index + 1)] = word; });
|
|
431
|
+
const token = ctx.env.get(PROCESS_TOKEN_ENV);
|
|
432
|
+
if (token) env[PROCESS_TOKEN_ENV] = token;
|
|
433
|
+
// A file is sourced by one statement of the nested run rather than run
|
|
434
|
+
// as the nested run's own script: a nested script of several lines
|
|
435
|
+
// stopped after its first hosted `node` in a tab (2026-09-28), while
|
|
436
|
+
// the same lines sourced from one statement ran whole. The shebang
|
|
437
|
+
// line is a comment to the shell that sources it.
|
|
438
|
+
if (sourced !== undefined) script = `. ${sourced.replace(/'/gu, "'\\''").replace(/^(.*)$/su, "'$1'")}`;
|
|
439
|
+
const result = await exec(script, { env, cwd: ctx.cwd, stdin: ctx.stdin, signal: (ctx as { signal?: AbortSignal }).signal });
|
|
440
|
+
return { stdout: result.stdout ?? '', stderr: result.stderr ?? '', exitCode: result.exitCode ?? 0 };
|
|
441
|
+
}));
|
|
442
|
+
}
|
|
443
|
+
// `exec command…` runs the command in the script's place, with the
|
|
444
|
+
// script's environment and its run's name, and answers with its status.
|
|
445
|
+
// The shell's own `exec` ran a builtin but a program the engine defines,
|
|
446
|
+
// `node`, ran nothing: a start script's `exec node server.js` ended with
|
|
447
|
+
// no server and no word. This one hands the line back to the shell, which
|
|
448
|
+
// is where `node`, `npm` and the host's programs are found. The shell is
|
|
449
|
+
// not replaced: a line after `exec` runs once the command ends, where
|
|
450
|
+
// bash would never reach it. A start script's `exec` is its last line, a
|
|
451
|
+
// server that does not end.
|
|
452
|
+
bash.registerCommand(defineCommand('exec', async (args, ctx) => {
|
|
453
|
+
if (args.length === 0) return { stdout: '', stderr: '', exitCode: 0 };
|
|
454
|
+
if (!ctx.exec) return { stdout: '', stderr: 'bash: exec: needs a shell that can run a line\n', exitCode: 1 };
|
|
455
|
+
const line = args.map((word) => /^[A-Za-z0-9_./:=@%+,-]+$/u.test(word) ? word : `'${word.replace(/'/gu, "'\\''")}'`).join(' ');
|
|
456
|
+
const env: Record<string, string> = Object.fromEntries(ctx.env);
|
|
457
|
+
const result = await ctx.exec(line, { env, cwd: ctx.cwd, replaceEnv: true });
|
|
458
|
+
return { stdout: result.stdout ?? '', stderr: result.stderr ?? '', exitCode: result.exitCode ?? 0 };
|
|
459
|
+
}));
|
|
460
|
+
|
|
389
461
|
bash.registerCommand(defineCommand('kill', async (args) => {
|
|
390
462
|
let signal = 'SIGTERM';
|
|
391
463
|
let index = 0;
|