sandboxedjs 0.1.27 → 0.1.28

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/index.js CHANGED
@@ -7332,15 +7332,15 @@ var Parser = class {
7332
7332
  if (this.isWord("-p")) this.next();
7333
7333
  }
7334
7334
  }
7335
- const commands11 = [this.parseCommand()];
7335
+ const commands12 = [this.parseCommand()];
7336
7336
  const stderrToo = [];
7337
7337
  while (this.isOp("|") || this.isOp("|&")) {
7338
7338
  stderrToo.push(this.next().value === "|&");
7339
7339
  this.skipNewlines();
7340
- commands11.push(this.parseCommand());
7340
+ commands12.push(this.parseCommand());
7341
7341
  }
7342
- if (commands11.length === 1 && !negated && !timed) return commands11[0];
7343
- return { type: "pipeline", commands: commands11, negated, stderrToo, timed };
7342
+ if (commands12.length === 1 && !negated && !timed) return commands12[0];
7343
+ return { type: "pipeline", commands: commands12, negated, stderrToo, timed };
7344
7344
  }
7345
7345
  // ── commands ─────────────────────────────────────────────────────────────
7346
7346
  parseCommand() {
@@ -16963,6 +16963,145 @@ var helpCmd = defineCommand({
16963
16963
  });
16964
16964
  var commands10 = [man, whatis, apropos, less, helpCmd];
16965
16965
 
16966
+ // src/bin/clipboard.ts
16967
+ init_path();
16968
+ var CLIPBOARD_DIR = "/run/clipboard";
16969
+ function selectionPath(selection) {
16970
+ return join(CLIPBOARD_DIR, selection);
16971
+ }
16972
+ function readSelection(ctx, selection) {
16973
+ try {
16974
+ return ctx.vfs.readFile(selectionPath(selection), ctx.cred);
16975
+ } catch {
16976
+ return new Uint8Array();
16977
+ }
16978
+ }
16979
+ function writeSelection(ctx, selection, bytes2) {
16980
+ try {
16981
+ ctx.vfs.mkdir(CLIPBOARD_DIR, { recursive: true, mode: 511 });
16982
+ } catch {
16983
+ }
16984
+ ctx.vfs.writeFile(selectionPath(selection), bytes2, { privileged: true, mode: 438 });
16985
+ }
16986
+ var xsel = defineCommand({
16987
+ name: "xsel",
16988
+ summary: "access an X selection",
16989
+ usage: "xsel [--clipboard|--primary|--secondary] [--input|--output|--clear]",
16990
+ async run(ctx) {
16991
+ let selection = "primary";
16992
+ let mode = "output";
16993
+ for (const arg of ctx.args) {
16994
+ switch (arg) {
16995
+ case "-b":
16996
+ case "--clipboard":
16997
+ selection = "clipboard";
16998
+ break;
16999
+ case "-p":
17000
+ case "--primary":
17001
+ selection = "primary";
17002
+ break;
17003
+ case "-s":
17004
+ case "--secondary":
17005
+ selection = "secondary";
17006
+ break;
17007
+ case "-i":
17008
+ case "--input":
17009
+ case "-a":
17010
+ case "--append":
17011
+ mode = "input";
17012
+ break;
17013
+ case "-o":
17014
+ case "--output":
17015
+ mode = "output";
17016
+ break;
17017
+ case "-c":
17018
+ case "--clear":
17019
+ mode = "clear";
17020
+ break;
17021
+ }
17022
+ }
17023
+ if (mode === "clear") {
17024
+ writeSelection(ctx, selection, new Uint8Array());
17025
+ return 0;
17026
+ }
17027
+ if (mode === "input") {
17028
+ writeSelection(ctx, selection, await ctx.stdin.readAll());
17029
+ return 0;
17030
+ }
17031
+ ctx.write(readSelection(ctx, selection));
17032
+ return 0;
17033
+ }
17034
+ });
17035
+ var xclip = defineCommand({
17036
+ name: "xclip",
17037
+ summary: "access an X selection",
17038
+ usage: "xclip [-selection clipboard|primary|secondary] [-i|-o]",
17039
+ async run(ctx) {
17040
+ let selection = "primary";
17041
+ let mode = "input";
17042
+ const args = ctx.args;
17043
+ for (let index = 0; index < args.length; index++) {
17044
+ const arg = args[index];
17045
+ if (arg === "-selection" || arg === "-sel" || arg === "--selection") {
17046
+ const value = args[++index];
17047
+ if (value === "clipboard" || value === "primary" || value === "secondary") selection = value;
17048
+ continue;
17049
+ }
17050
+ if (arg === "-i" || arg === "-in") mode = "input";
17051
+ else if (arg === "-o" || arg === "-out") mode = "output";
17052
+ }
17053
+ if (mode === "input") {
17054
+ writeSelection(ctx, selection, await ctx.stdin.readAll());
17055
+ return 0;
17056
+ }
17057
+ ctx.write(readSelection(ctx, selection));
17058
+ return 0;
17059
+ }
17060
+ });
17061
+ var pbcopy = defineCommand({
17062
+ name: "pbcopy",
17063
+ summary: "copy standard input to the clipboard",
17064
+ usage: "pbcopy",
17065
+ async run(ctx) {
17066
+ writeSelection(ctx, "clipboard", await ctx.stdin.readAll());
17067
+ return 0;
17068
+ }
17069
+ });
17070
+ var pbpaste = defineCommand({
17071
+ name: "pbpaste",
17072
+ summary: "write the clipboard to standard output",
17073
+ usage: "pbpaste",
17074
+ run(ctx) {
17075
+ ctx.write(readSelection(ctx, "clipboard"));
17076
+ return 0;
17077
+ }
17078
+ });
17079
+ var wlCopy = defineCommand({
17080
+ name: "wl-copy",
17081
+ summary: "copy standard input to the Wayland clipboard",
17082
+ usage: "wl-copy [--primary] [text...]",
17083
+ async run(ctx) {
17084
+ const selection = ctx.args.includes("--primary") || ctx.args.includes("-p") ? "primary" : "clipboard";
17085
+ const operands = ctx.args.filter((arg) => !arg.startsWith("-"));
17086
+ const bytes2 = operands.length ? new TextEncoder().encode(operands.join(" ")) : await ctx.stdin.readAll();
17087
+ writeSelection(ctx, selection, bytes2);
17088
+ return 0;
17089
+ }
17090
+ });
17091
+ var wlPaste = defineCommand({
17092
+ name: "wl-paste",
17093
+ summary: "write the Wayland clipboard to standard output",
17094
+ usage: "wl-paste [--primary] [-n]",
17095
+ run(ctx) {
17096
+ const selection = ctx.args.includes("--primary") || ctx.args.includes("-p") ? "primary" : "clipboard";
17097
+ const bytes2 = readSelection(ctx, selection);
17098
+ ctx.write(bytes2);
17099
+ if (!ctx.args.includes("-n") && !ctx.args.includes("--no-newline")) ctx.write("\n");
17100
+ return 0;
17101
+ }
17102
+ });
17103
+ var commands11 = [xsel, xclip, pbcopy, pbpaste, wlCopy, wlPaste];
17104
+
16966
17105
  // src/runtime/node.ts
16967
17106
  init_path();
16968
17107
  var NODE_VERSION = "v22.12.0";
@@ -19319,6 +19458,7 @@ function allCommands() {
19319
19458
  ...commands8,
19320
19459
  ...commands9,
19321
19460
  ...commands10,
19461
+ ...commands11,
19322
19462
  ...nodeCommands(),
19323
19463
  ...pythonCommands(),
19324
19464
  ...ffmpegCommands(),
@@ -19615,6 +19755,13 @@ var KernelChildProcess = class {
19615
19755
  } catch {
19616
19756
  }
19617
19757
  }
19758
+ /** Signal EOF, so a child reading stdin to the end can finish. */
19759
+ endStdin() {
19760
+ try {
19761
+ this.stdin.end();
19762
+ } catch {
19763
+ }
19764
+ }
19618
19765
  kill(signal = "SIGTERM") {
19619
19766
  if (this.process) {
19620
19767
  this.process.deliver(signal);
@@ -20526,6 +20673,55 @@ function splitSpecifier(specifier) {
20526
20673
  return { name: parts.slice(0, size).join("/"), subpath: parts.slice(size).join("/") };
20527
20674
  }
20528
20675
 
20676
+ // src/runtime/readable-from.ts
20677
+ function createReadableFrom(Readable) {
20678
+ return function from(source, options = {}) {
20679
+ if (source && typeof source.pipe === "function") return source;
20680
+ const iterator = source && typeof source[Symbol.asyncIterator] === "function" ? source[Symbol.asyncIterator]() : source && typeof source[Symbol.iterator] === "function" ? source[Symbol.iterator]() : (function* () {
20681
+ yield source;
20682
+ })();
20683
+ let reading = false;
20684
+ const stream = new Readable({
20685
+ ...options,
20686
+ objectMode: options.objectMode ?? false,
20687
+ read() {
20688
+ if (reading) return;
20689
+ reading = true;
20690
+ void (async () => {
20691
+ try {
20692
+ for (; ; ) {
20693
+ const next = await iterator.next();
20694
+ if (next.done) {
20695
+ stream.push(null);
20696
+ return;
20697
+ }
20698
+ const value = next.value;
20699
+ if (!stream.push(value === void 0 ? null : value)) return;
20700
+ }
20701
+ } catch (error) {
20702
+ stream.destroy(error);
20703
+ } finally {
20704
+ reading = false;
20705
+ }
20706
+ })();
20707
+ },
20708
+ destroy(error, callback) {
20709
+ void (async () => {
20710
+ try {
20711
+ await iterator.return?.();
20712
+ } catch {
20713
+ }
20714
+ callback(error);
20715
+ })();
20716
+ }
20717
+ });
20718
+ return stream;
20719
+ };
20720
+ }
20721
+ function installReadableFrom(streamModule5) {
20722
+ streamModule5.Readable.from = createReadableFrom(streamModule5.Readable);
20723
+ }
20724
+
20529
20725
  // src/runtime/util-module.ts
20530
20726
  var customInspect = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
20531
20727
  var promisifyCustom = /* @__PURE__ */ Symbol.for("nodejs.util.promisify.custom");
@@ -21413,6 +21609,10 @@ var VirtualHttpRouter = class {
21413
21609
  unregister(port, server) {
21414
21610
  if (this.servers.get(port)?.server === server) this.servers.delete(port);
21415
21611
  }
21612
+ /** Whether anything in this container is listening on `port`. */
21613
+ activePortsIncludes(port) {
21614
+ return this.servers.has(port);
21615
+ }
21416
21616
  activePorts(owner) {
21417
21617
  return [...this.servers].filter(([, item]) => owner === void 0 || item.owner === owner).map(([port]) => port).sort((a, b) => a - b);
21418
21618
  }
@@ -21436,13 +21636,269 @@ var VirtualHttpRouter = class {
21436
21636
  }
21437
21637
  }
21438
21638
  };
21439
- function createHttpModule(router, owner) {
21639
+ var VirtualClientResponse = class extends streamModule4.Readable {
21640
+ constructor(statusCode, statusMessage, headers, body) {
21641
+ super();
21642
+ this.statusCode = statusCode;
21643
+ this.statusMessage = statusMessage;
21644
+ this.headers = headers;
21645
+ this.rawHeaders = Object.entries(headers).flatMap(([key, value]) => [key, value]);
21646
+ if (body.length) this.push(Buffer2.from(body));
21647
+ this.push(null);
21648
+ this.complete = true;
21649
+ }
21650
+ statusCode;
21651
+ statusMessage;
21652
+ headers;
21653
+ httpVersion = "1.1";
21654
+ httpVersionMajor = 1;
21655
+ httpVersionMinor = 1;
21656
+ socket = socketStub();
21657
+ connection = this.socket;
21658
+ rawHeaders;
21659
+ complete = false;
21660
+ _read() {
21661
+ }
21662
+ setTimeout(_milliseconds, callback) {
21663
+ if (callback) this.once("timeout", callback);
21664
+ return this;
21665
+ }
21666
+ };
21667
+ function resolveTarget(input, overrides, defaultProtocol) {
21668
+ const options = {};
21669
+ if (typeof input === "string" || input instanceof URL) {
21670
+ const url = new URL(String(input));
21671
+ options.protocol = url.protocol;
21672
+ options.hostname = url.hostname;
21673
+ if (url.port) options.port = Number(url.port);
21674
+ options.path = `${url.pathname}${url.search}`;
21675
+ if (url.username || url.password) options.auth = `${url.username}:${url.password}`;
21676
+ } else if (input && typeof input === "object") {
21677
+ Object.assign(options, input);
21678
+ }
21679
+ if (overrides) Object.assign(options, overrides);
21680
+ const protocol = String(options.protocol ?? defaultProtocol).replace(/:?$/, ":");
21681
+ const headers = {};
21682
+ for (const [name, value] of Object.entries(options.headers ?? {})) {
21683
+ if (value === void 0 || value === null) continue;
21684
+ headers[String(name)] = Array.isArray(value) ? value.join(", ") : String(value);
21685
+ }
21686
+ if (options.auth && headers.authorization === void 0) {
21687
+ headers.authorization = `Basic ${Buffer2.from(String(options.auth)).toString("base64")}`;
21688
+ }
21689
+ const rawHost = String(options.hostname ?? options.host ?? "localhost");
21690
+ const hostMatch = /^(\[[^\]]*\]|[^:]*)(?::(\d+))?$/.exec(rawHost);
21691
+ const hostname = (hostMatch?.[1] ?? rawHost).replace(/^\[|\]$/g, "");
21692
+ const explicitPort = options.port ?? hostMatch?.[2];
21693
+ const port = explicitPort === void 0 || explicitPort === null || explicitPort === "" || Number.isNaN(Number(explicitPort)) ? protocol === "https:" ? 443 : 80 : Number(explicitPort);
21694
+ return {
21695
+ protocol,
21696
+ hostname,
21697
+ port,
21698
+ path: String(options.path ?? "/"),
21699
+ method: String(options.method ?? "GET").toUpperCase(),
21700
+ headers
21701
+ };
21702
+ }
21703
+ function isLoopback(hostname) {
21704
+ return hostname === "localhost" || hostname === "0.0.0.0" || hostname === "::1" || hostname === "::" || hostname.startsWith("127.") || hostname.endsWith(".localhost");
21705
+ }
21706
+ var VirtualClientRequest = class extends streamModule4.Writable {
21707
+ constructor(target, router, fetchImpl, trackRequest) {
21708
+ super();
21709
+ this.target = target;
21710
+ this.router = router;
21711
+ this.fetchImpl = fetchImpl;
21712
+ this.trackRequest = trackRequest;
21713
+ for (const [name, value] of Object.entries(target.headers)) this.setHeader(name, value);
21714
+ }
21715
+ target;
21716
+ router;
21717
+ fetchImpl;
21718
+ trackRequest;
21719
+ socket = socketStub();
21720
+ connection = this.socket;
21721
+ chunks = [];
21722
+ headerMap = /* @__PURE__ */ new Map();
21723
+ dispatched = false;
21724
+ destroyedByUser = false;
21725
+ timer;
21726
+ aborted = false;
21727
+ finished = false;
21728
+ reusedSocket = false;
21729
+ get method() {
21730
+ return this.target.method;
21731
+ }
21732
+ get path() {
21733
+ return this.target.path;
21734
+ }
21735
+ get host() {
21736
+ return this.target.hostname;
21737
+ }
21738
+ get protocol() {
21739
+ return this.target.protocol;
21740
+ }
21741
+ setHeader(name, value) {
21742
+ validateHeaderName(name);
21743
+ validateHeaderValue(name, value);
21744
+ this.headerMap.set(name.toLowerCase(), { name, value: Array.isArray(value) ? [...value].map(String).join(", ") : String(value) });
21745
+ return this;
21746
+ }
21747
+ getHeader(name) {
21748
+ return this.headerMap.get(name.toLowerCase())?.value;
21749
+ }
21750
+ getHeaders() {
21751
+ return Object.fromEntries([...this.headerMap].map(([key, entry]) => [key, entry.value]));
21752
+ }
21753
+ getHeaderNames() {
21754
+ return [...this.headerMap.keys()];
21755
+ }
21756
+ hasHeader(name) {
21757
+ return this.headerMap.has(name.toLowerCase());
21758
+ }
21759
+ removeHeader(name) {
21760
+ this.headerMap.delete(name.toLowerCase());
21761
+ }
21762
+ flushHeaders() {
21763
+ }
21764
+ setNoDelay() {
21765
+ }
21766
+ setSocketKeepAlive() {
21767
+ }
21768
+ ref() {
21769
+ return this;
21770
+ }
21771
+ unref() {
21772
+ return this;
21773
+ }
21774
+ setTimeout(milliseconds, callback) {
21775
+ if (callback) this.once("timeout", callback);
21776
+ clearTimeout(this.timer);
21777
+ this.timer = setTimeout(() => this.emit("timeout"), milliseconds);
21778
+ return this;
21779
+ }
21780
+ abort() {
21781
+ this.destroyRequest();
21782
+ }
21783
+ destroy(error) {
21784
+ this.destroyRequest(error);
21785
+ return this;
21786
+ }
21787
+ destroyRequest(error) {
21788
+ if (this.destroyedByUser) return;
21789
+ this.destroyedByUser = true;
21790
+ this.aborted = true;
21791
+ clearTimeout(this.timer);
21792
+ this.emit("abort");
21793
+ if (error) this.emit("error", error);
21794
+ this.emit("close");
21795
+ }
21796
+ _write(chunk, encoding, callback) {
21797
+ this.chunks.push(Buffer2.isBuffer(chunk) ? chunk : Buffer2.from(chunk, encoding));
21798
+ callback();
21799
+ }
21800
+ _final(callback) {
21801
+ this.finished = true;
21802
+ callback();
21803
+ void this.dispatch();
21804
+ }
21805
+ async dispatch() {
21806
+ if (this.dispatched || this.destroyedByUser) return;
21807
+ this.dispatched = true;
21808
+ const body = Buffer2.concat(this.chunks);
21809
+ const release = this.trackRequest?.();
21810
+ let released = false;
21811
+ const untrack = () => {
21812
+ if (released || !release) return;
21813
+ released = true;
21814
+ release();
21815
+ };
21816
+ let settled = false;
21817
+ try {
21818
+ const response = this.router.activePortsIncludes(this.target.port) && isLoopback(this.target.hostname) ? await this.viaRouter(body) : await this.viaFetch(body);
21819
+ if (this.destroyedByUser) return;
21820
+ clearTimeout(this.timer);
21821
+ settled = true;
21822
+ response.once("end", untrack);
21823
+ response.once("close", untrack);
21824
+ response.once("error", untrack);
21825
+ this.emit("response", response);
21826
+ queueMicrotask(() => {
21827
+ if (this.listenerCount("response") === 0) response.resume();
21828
+ });
21829
+ } catch (error) {
21830
+ clearTimeout(this.timer);
21831
+ if (this.destroyedByUser) return;
21832
+ const failure = error instanceof Error ? error : new Error(String(error));
21833
+ if (!("code" in failure)) Object.assign(failure, { code: "ECONNREFUSED" });
21834
+ this.emit("error", failure);
21835
+ } finally {
21836
+ if (!settled) untrack();
21837
+ }
21838
+ }
21839
+ async viaRouter(body) {
21840
+ const result = await this.router.request(this.target.port, {
21841
+ method: this.target.method,
21842
+ path: this.target.path,
21843
+ headers: this.getHeaders(),
21844
+ body: body.length ? new Uint8Array(body) : null
21845
+ });
21846
+ const body_ = result.body;
21847
+ const bytes2 = typeof body_ === "string" ? new TextEncoder().encode(body_) : body_ instanceof ArrayBuffer ? new Uint8Array(body_) : body_ ?? new Uint8Array();
21848
+ const status = result.statusCode ?? 200;
21849
+ return new VirtualClientResponse(status, result.statusMessage ?? STATUS_CODES[status] ?? "", lowercaseHeaders(result.headers), bytes2);
21850
+ }
21851
+ async viaFetch(body) {
21852
+ const fetchImpl = this.fetchImpl ?? globalThis.fetch;
21853
+ if (typeof fetchImpl !== "function") {
21854
+ throw Object.assign(new Error(`getaddrinfo ENOTFOUND ${this.target.hostname}`), { code: "ENOTFOUND" });
21855
+ }
21856
+ const portSuffix = this.target.protocol === "https:" && this.target.port === 443 || this.target.protocol === "http:" && this.target.port === 80 ? "" : `:${this.target.port}`;
21857
+ const url = `${this.target.protocol}//${this.target.hostname}${portSuffix}${this.target.path}`;
21858
+ const bodyless = this.target.method === "GET" || this.target.method === "HEAD";
21859
+ const headers = this.getHeaders();
21860
+ delete headers.host;
21861
+ delete headers.connection;
21862
+ delete headers["content-length"];
21863
+ const response = await fetchImpl(url, {
21864
+ method: this.target.method,
21865
+ headers,
21866
+ ...bodyless || body.length === 0 ? {} : { body: new Uint8Array(body) },
21867
+ redirect: "follow"
21868
+ });
21869
+ const received = {};
21870
+ response.headers.forEach((value, key) => {
21871
+ received[key.toLowerCase()] = value;
21872
+ });
21873
+ const bytes2 = new Uint8Array(await response.arrayBuffer());
21874
+ return new VirtualClientResponse(response.status, response.statusText, received, bytes2);
21875
+ }
21876
+ };
21877
+ function lowercaseHeaders(headers) {
21878
+ return Object.fromEntries(Object.entries(headers ?? {}).map(([key, value]) => [key.toLowerCase(), String(value)]));
21879
+ }
21880
+ function createHttpModule(router, owner, options = {}, defaultProtocol = "http:") {
21440
21881
  const createServer = (listener) => new VirtualHttpServer(router, owner, listener);
21441
- const module = {
21882
+ const request = (input, second, third) => {
21883
+ const overrides = second && typeof second === "object" && !(second instanceof Function) ? second : void 0;
21884
+ const callback = [second, third].find((value) => typeof value === "function");
21885
+ const req = new VirtualClientRequest(resolveTarget(input, overrides, defaultProtocol), router, options.fetch, options.trackRequest);
21886
+ if (callback) req.on("response", callback);
21887
+ return req;
21888
+ };
21889
+ const get = (input, second, third) => {
21890
+ const req = request(input, second, third);
21891
+ req.end();
21892
+ return req;
21893
+ };
21894
+ return {
21442
21895
  createServer,
21896
+ request,
21897
+ get,
21443
21898
  Server: VirtualHttpServer,
21444
21899
  ServerResponse: VirtualServerResponse,
21445
21900
  IncomingMessage: VirtualIncomingMessage,
21901
+ ClientRequest: VirtualClientRequest,
21446
21902
  METHODS,
21447
21903
  STATUS_CODES,
21448
21904
  maxHeaderSize: 16 * 1024,
@@ -21452,10 +21908,6 @@ function createHttpModule(router, owner) {
21452
21908
  Agent: class Agent {
21453
21909
  }
21454
21910
  };
21455
- return { ...module, request: unsupportedClient, get: unsupportedClient };
21456
- }
21457
- function unsupportedClient() {
21458
- throw Object.assign(new Error("http client requests are not implemented yet"), { code: "ERR_NOT_IMPLEMENTED" });
21459
21911
  }
21460
21912
  function validateHeaderName(name) {
21461
21913
  if (!/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(name)) throw Object.assign(new TypeError(`Header name must be a valid HTTP token [${name}]`), { code: "ERR_INVALID_HTTP_TOKEN" });
@@ -21595,6 +22047,13 @@ var ChildProcess = class extends EventEmitter4 {
21595
22047
  } catch {
21596
22048
  }
21597
22049
  done();
22050
+ },
22051
+ final: (done) => {
22052
+ try {
22053
+ handle.endStdin?.();
22054
+ } catch {
22055
+ }
22056
+ done();
21598
22057
  }
21599
22058
  });
21600
22059
  this.stdio = [this.stdin, this.stdout, this.stderr];
@@ -21833,7 +22292,7 @@ function emitKeypressEvents(stream) {
21833
22292
  }
21834
22293
  });
21835
22294
  }
21836
- function createReadlineModule(defaultInput) {
22295
+ function createReadlineModule(defaultInput, defaultOutput = () => void 0) {
21837
22296
  class Interface extends EventEmitter4 {
21838
22297
  constructor(input, output, terminal = false) {
21839
22298
  super();
@@ -21844,6 +22303,14 @@ function createReadlineModule(defaultInput) {
21844
22303
  if (terminal) {
21845
22304
  emitKeypressEvents(input);
21846
22305
  input?.on?.("keypress", this.onTerminalKeypress);
22306
+ if (typeof input?.setRawMode === "function") {
22307
+ try {
22308
+ input.setRawMode(true);
22309
+ this.ownsRawMode = true;
22310
+ } catch {
22311
+ }
22312
+ }
22313
+ input?.resume?.();
21847
22314
  } else {
21848
22315
  input?.on?.("data", (chunk) => this.receive(String(chunk)));
21849
22316
  }
@@ -21855,6 +22322,10 @@ function createReadlineModule(defaultInput) {
21855
22322
  buffer = "";
21856
22323
  pending = [];
21857
22324
  lines = [];
22325
+ /** Set while `question` is waiting, so the echoed line carries the query. */
22326
+ questionPrompt = null;
22327
+ /** Raw mode is only ours to restore if we were the one to enable it. */
22328
+ ownsRawMode = false;
21858
22329
  line = "";
21859
22330
  cursor = 0;
21860
22331
  terminal;
@@ -21862,20 +22333,31 @@ function createReadlineModule(defaultInput) {
21862
22333
  /** Split incoming data into lines, answering any waiting `question`. */
21863
22334
  receive(text2) {
21864
22335
  this.buffer += text2;
21865
- let newline = this.buffer.indexOf("\n");
21866
- while (newline >= 0) {
21867
- const line = this.buffer.slice(0, newline).replace(/\r$/, "");
21868
- this.buffer = this.buffer.slice(newline + 1);
21869
- const waiting = this.pending.shift();
21870
- if (waiting) waiting(line);
21871
- else {
21872
- this.lines.push(line);
21873
- this.emit("line", line);
21874
- }
21875
- newline = this.buffer.indexOf("\n");
22336
+ let match2 = /\r\n|\r|\n/.exec(this.buffer);
22337
+ while (match2) {
22338
+ const line = this.buffer.slice(0, match2.index);
22339
+ this.buffer = this.buffer.slice(match2.index + match2[0].length);
22340
+ this.deliver(line);
22341
+ match2 = /\r\n|\r|\n/.exec(this.buffer);
22342
+ }
22343
+ }
22344
+ /** Hand a completed line to whoever is waiting for it. */
22345
+ deliver(line) {
22346
+ const waiting = this.pending.shift();
22347
+ if (waiting) {
22348
+ this.questionPrompt = null;
22349
+ waiting(line);
22350
+ return;
21876
22351
  }
22352
+ this.lines.push(line);
22353
+ this.emit("line", line);
21877
22354
  }
21878
22355
  question(query, callback) {
22356
+ if (this.terminal) {
22357
+ this.questionPrompt = query;
22358
+ this.line = "";
22359
+ this.cursor = 0;
22360
+ }
21879
22361
  this.output?.write?.(query);
21880
22362
  if (callback) {
21881
22363
  this.pending.push(callback);
@@ -21890,6 +22372,9 @@ function createReadlineModule(defaultInput) {
21890
22372
  setPrompt(text2) {
21891
22373
  this.promptText = text2;
21892
22374
  }
22375
+ getPrompt() {
22376
+ return this.promptText;
22377
+ }
21893
22378
  pause() {
21894
22379
  this.input?.pause?.();
21895
22380
  return this;
@@ -21917,21 +22402,48 @@ function createReadlineModule(defaultInput) {
21917
22402
  if (text2) {
21918
22403
  this.line = this.line.slice(0, this.cursor) + text2 + this.line.slice(this.cursor);
21919
22404
  this.cursor += text2.length;
22405
+ this.refresh();
21920
22406
  }
21921
22407
  }
21922
22408
  getCursorPos() {
21923
- return { rows: 0, cols: this.cursor + this.promptText.length };
22409
+ return { rows: 0, cols: this.cursor + this.currentPrompt().length };
21924
22410
  }
21925
22411
  close() {
21926
22412
  if (this.closed) return;
21927
22413
  this.closed = true;
21928
22414
  if (this.terminal) {
21929
22415
  this.input?.off?.("keypress", this.onTerminalKeypress);
22416
+ if (this.ownsRawMode) {
22417
+ this.ownsRawMode = false;
22418
+ try {
22419
+ this.input?.setRawMode?.(false);
22420
+ } catch {
22421
+ }
22422
+ }
21930
22423
  this.input?.pause?.();
21931
22424
  }
21932
22425
  for (const waiting of this.pending.splice(0)) waiting("");
21933
22426
  this.emit("close");
21934
22427
  }
22428
+ /** The text shown to the left of the edited line. */
22429
+ currentPrompt() {
22430
+ return this.questionPrompt ?? this.promptText;
22431
+ }
22432
+ /**
22433
+ * Repaint the edited line.
22434
+ *
22435
+ * In raw mode nothing echoes on its own, so an Interface that does not do
22436
+ * this leaves the user typing blind. Libraries that draw their own frame
22437
+ * (clack, inquirer) simply overwrite this, exactly as they do on Node.
22438
+ */
22439
+ refresh() {
22440
+ if (!this.terminal) return;
22441
+ const prompt = this.currentPrompt();
22442
+ let text2 = `\r\x1B[0K${prompt}${this.line}`;
22443
+ const back = this.line.length - this.cursor;
22444
+ if (back > 0) text2 += `\x1B[${back}D`;
22445
+ this.output?.write?.(text2);
22446
+ }
21935
22447
  edit(sequence, key) {
21936
22448
  if (this.closed) return;
21937
22449
  const name = key.name;
@@ -21939,48 +22451,62 @@ function createReadlineModule(defaultInput) {
21939
22451
  const value = this.line;
21940
22452
  this.line = "";
21941
22453
  this.cursor = 0;
21942
- this.emit("line", value);
22454
+ this.output?.write?.("\r\n");
22455
+ this.deliver(value);
21943
22456
  return;
21944
22457
  }
21945
22458
  if (name === "left") {
21946
22459
  this.cursor = Math.max(0, this.cursor - 1);
22460
+ this.refresh();
21947
22461
  return;
21948
22462
  }
21949
22463
  if (name === "right") {
21950
22464
  this.cursor = Math.min(this.line.length, this.cursor + 1);
22465
+ this.refresh();
21951
22466
  return;
21952
22467
  }
21953
22468
  if (name === "home" || key.ctrl && name === "a") {
21954
22469
  this.cursor = 0;
22470
+ this.refresh();
21955
22471
  return;
21956
22472
  }
21957
22473
  if (name === "end" || key.ctrl && name === "e") {
21958
22474
  this.cursor = this.line.length;
22475
+ this.refresh();
21959
22476
  return;
21960
22477
  }
21961
22478
  if (name === "backspace" || key.ctrl && name === "h") {
21962
22479
  if (this.cursor > 0) {
21963
22480
  this.line = this.line.slice(0, this.cursor - 1) + this.line.slice(this.cursor);
21964
22481
  this.cursor--;
22482
+ this.refresh();
21965
22483
  }
21966
22484
  return;
21967
22485
  }
21968
22486
  if (name === "delete") {
21969
- if (this.cursor < this.line.length) this.line = this.line.slice(0, this.cursor) + this.line.slice(this.cursor + 1);
22487
+ if (this.cursor < this.line.length) {
22488
+ this.line = this.line.slice(0, this.cursor) + this.line.slice(this.cursor + 1);
22489
+ this.refresh();
22490
+ }
21970
22491
  return;
21971
22492
  }
21972
22493
  if (key.ctrl && name === "u") {
21973
22494
  this.line = this.line.slice(this.cursor);
21974
22495
  this.cursor = 0;
22496
+ this.refresh();
21975
22497
  return;
21976
22498
  }
21977
22499
  if (key.ctrl && name === "k") {
21978
22500
  this.line = this.line.slice(0, this.cursor);
22501
+ this.refresh();
21979
22502
  return;
21980
22503
  }
21981
22504
  if (key.ctrl || key.meta || sequence.length !== 1 || sequence < " ") return;
22505
+ const atEnd = this.cursor === this.line.length;
21982
22506
  this.line = this.line.slice(0, this.cursor) + sequence + this.line.slice(this.cursor);
21983
22507
  this.cursor += sequence.length;
22508
+ if (atEnd) this.output?.write?.(sequence);
22509
+ else this.refresh();
21984
22510
  }
21985
22511
  async *[Symbol.asyncIterator]() {
21986
22512
  while (!this.closed || this.lines.length) {
@@ -22010,13 +22536,23 @@ function createReadlineModule(defaultInput) {
22010
22536
  }
22011
22537
  }
22012
22538
  }
22539
+ const isTerminal = (options, output) => {
22540
+ if (typeof options?.terminal === "boolean") return options.terminal;
22541
+ return Boolean(output?.isTTY);
22542
+ };
22013
22543
  const createInterface = (options, output) => {
22014
22544
  if (options && typeof options === "object" && !options.on) {
22015
- const instance = new Interface(options.input ?? defaultInput(), options.output, options.terminal === true);
22545
+ const resolvedOutput2 = options.output ?? defaultOutput();
22546
+ const instance = new Interface(
22547
+ options.input ?? defaultInput(),
22548
+ resolvedOutput2,
22549
+ isTerminal(options, resolvedOutput2)
22550
+ );
22016
22551
  if (typeof options.prompt === "string") instance.setPrompt(options.prompt);
22017
22552
  return instance;
22018
22553
  }
22019
- return new Interface(options ?? defaultInput(), output, Boolean(output));
22554
+ const resolvedOutput = output ?? defaultOutput();
22555
+ return new Interface(options ?? defaultInput(), resolvedOutput, isTerminal(void 0, resolvedOutput));
22020
22556
  };
22021
22557
  const noop = () => {
22022
22558
  };
@@ -22033,6 +22569,7 @@ function createReadlineModule(defaultInput) {
22033
22569
  }
22034
22570
 
22035
22571
  // src/runtime/core-modules.ts
22572
+ installReadableFrom(streamModule4);
22036
22573
  var Dirent = class {
22037
22574
  /** Node 20+ exposes the containing directory, and `fs.glob` consumers read it. */
22038
22575
  constructor(name, stat2, parentPath = "") {
@@ -22238,9 +22775,20 @@ function createCoreModules(options) {
22238
22775
  throw new Error("createRequire is only available inside a loaded module");
22239
22776
  }
22240
22777
  };
22241
- const http = options.http ? createHttpModule(options.http.router, options.http.owner) : createUnsupportedModule("http");
22778
+ let inFlightRequests = 0;
22779
+ const httpOptions = {
22780
+ ...options.http?.fetch ? { fetch: options.http.fetch } : {},
22781
+ trackRequest: () => {
22782
+ inFlightRequests++;
22783
+ return () => {
22784
+ inFlightRequests--;
22785
+ };
22786
+ }
22787
+ };
22788
+ const http = options.http ? createHttpModule(options.http.router, options.http.owner, httpOptions, "http:") : createUnsupportedModule("http");
22789
+ const https = options.http ? createHttpModule(options.http.router, options.http.owner, httpOptions, "https:") : createUnsupportedModule("https");
22242
22790
  const childProcess = options.spawnChild ? createChildProcessModule(options.spawnChild, () => cwd) : createUnsupportedModule("child_process");
22243
- const readline = createReadlineModule(() => processObject.stdin);
22791
+ const readline = createReadlineModule(() => processObject.stdin, () => processObject.stdout);
22244
22792
  const dns = createDnsModule();
22245
22793
  const builtins = {
22246
22794
  assert: assert_module_default,
@@ -22258,7 +22806,7 @@ function createCoreModules(options) {
22258
22806
  "fs/promises": fs.promises,
22259
22807
  module: moduleBuiltin,
22260
22808
  http,
22261
- https: http,
22809
+ https,
22262
22810
  os,
22263
22811
  path,
22264
22812
  "path/posix": path,
@@ -22298,6 +22846,8 @@ function createCoreModules(options) {
22298
22846
  process: processObject,
22299
22847
  pendingHandles: timers.pending,
22300
22848
  pendingUnrefed: timers.pendingUnrefed,
22849
+ /** Client requests sent but not yet read to completion. */
22850
+ pendingRequests: () => inFlightRequests,
22301
22851
  writeStdin: (data) => {
22302
22852
  if (options.interactiveStdin) stdin.write(data);
22303
22853
  },
@@ -22525,9 +23075,20 @@ function createFsModule(volume, cwd, stdinPath) {
22525
23075
  if (position == null) file3.position = start2 + chunk.length;
22526
23076
  return chunk.length;
22527
23077
  },
22528
- createReadStream: (path) => {
22529
- const data = Buffer2.from(volume.readFileSync(resolveLinks(volume, abs(path))));
22530
- return streamModule4.Readable.from([data]);
23078
+ createReadStream: (path, options) => {
23079
+ const target = abs(path);
23080
+ const settings = typeof options === "string" ? { encoding: options } : options ?? {};
23081
+ const whole = Buffer2.from(volume.readFileSync(resolveLinks(volume, target)));
23082
+ const start2 = settings.start ?? 0;
23083
+ const end = settings.end === void 0 ? whole.length : Math.min(settings.end + 1, whole.length);
23084
+ const slice = whole.subarray(start2, Math.max(start2, end));
23085
+ const stream = streamModule4.Readable.from([settings.encoding ? slice.toString(settings.encoding) : slice]);
23086
+ Object.assign(stream, { path: target, bytesRead: slice.length, close: () => stream.destroy() });
23087
+ queueMicrotask(() => {
23088
+ stream.emit("open", 0);
23089
+ stream.emit("ready");
23090
+ });
23091
+ return stream;
22531
23092
  },
22532
23093
  createWriteStream: (path, options) => {
22533
23094
  const target = abs(path);
@@ -23644,6 +24205,11 @@ var PodChildProcess = class {
23644
24205
  started = false;
23645
24206
  cancelled = false;
23646
24207
  child;
24208
+ /* The child is launched asynchronously, but a parent may write and close
24209
+ * its input synchronously right after spawning. Holding both until the
24210
+ * child exists is what keeps `echo hi | child` from losing the `hi`. */
24211
+ pendingInput = "";
24212
+ inputEnded = false;
23647
24213
  on(event, listener) {
23648
24214
  let set = this.listeners.get(event);
23649
24215
  if (!set) this.listeners.set(event, set = /* @__PURE__ */ new Set());
@@ -23666,6 +24232,11 @@ var PodChildProcess = class {
23666
24232
  (child) => {
23667
24233
  this.child = child;
23668
24234
  if (this.cancelled) child.kill();
24235
+ if (this.pendingInput !== "") {
24236
+ child.write(this.pendingInput);
24237
+ this.pendingInput = "";
24238
+ }
24239
+ if (this.inputEnded) child.endInput?.();
23669
24240
  child.on("output", (text2) => {
23670
24241
  this.stdout += text2;
23671
24242
  this.emit("stdout", text2);
@@ -23688,7 +24259,13 @@ var PodChildProcess = class {
23688
24259
  });
23689
24260
  }
23690
24261
  sendStdin(data) {
23691
- this.child?.write(data);
24262
+ if (this.child) this.child.write(data);
24263
+ else this.pendingInput += data;
24264
+ }
24265
+ /** Signal EOF, so a child reading stdin to the end can finish. */
24266
+ endStdin() {
24267
+ if (this.child) this.child.endInput?.();
24268
+ else this.inputEnded = true;
23692
24269
  }
23693
24270
  kill(signal = "SIGTERM") {
23694
24271
  if (this.child) this.child.kill(signal);
@@ -23771,6 +24348,8 @@ var LocalRuntimePod = class _LocalRuntimePod {
23771
24348
  modules;
23772
24349
  esbuild;
23773
24350
  rolldownBinding;
24351
+ /** Backs outbound `http`/`https` client requests from inside the sandbox. */
24352
+ fetch;
23774
24353
  constructor(options) {
23775
24354
  ensureProcessGlobal();
23776
24355
  this.workdir = options.workdir ?? "/";
@@ -23785,6 +24364,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
23785
24364
  const notify = options.onServerReady;
23786
24365
  this.router.onListen = (port) => notify(port, `http://localhost:${port}`);
23787
24366
  }
24367
+ if (options.fetch) this.fetch = options.fetch;
23788
24368
  this.packages = new CleanPackageInstaller(this.volume, {
23789
24369
  cwd: this.workdir,
23790
24370
  ...options.registry ? { registry: options.registry } : {},
@@ -23824,7 +24404,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
23824
24404
  proc.exitNow(code);
23825
24405
  if (engine?.isEvaluating) throw new ProcessExit(code);
23826
24406
  },
23827
- http: { router: this.router, owner },
24407
+ http: { router: this.router, owner, ...this.fetch ? { fetch: this.fetch } : {} },
23828
24408
  spawnChild: (config) => this.processManager.spawn(config),
23829
24409
  ...typeof options.stdinPath === "string" ? { stdinPath: options.stdinPath } : {},
23830
24410
  ...options.interactiveStdin ? { interactiveStdin: true } : {},
@@ -23841,7 +24421,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
23841
24421
  });
23842
24422
  try {
23843
24423
  await engine.run(script);
23844
- await this.settle(owner, core.pendingHandles, core.pendingUnrefed, core.readingStdin);
24424
+ await this.settle(owner, core.pendingHandles, core.pendingUnrefed, core.readingStdin, core.pendingRequests);
23845
24425
  if (this.router.activePorts(owner).length) {
23846
24426
  await proc.waitForKill();
23847
24427
  return 137;
@@ -23887,18 +24467,18 @@ var LocalRuntimePod = class _LocalRuntimePod {
23887
24467
  * A plain script that has genuinely finished falls straight through both,
23888
24468
  * costing a handful of empty turns.
23889
24469
  */
23890
- async settle(owner, pendingHandles, pendingUnrefed, readingStdin) {
24470
+ async settle(owner, pendingHandles, pendingUnrefed, readingStdin, pendingRequests) {
23891
24471
  for (let turn = 0; turn < DRAIN_TURNS; turn++) {
23892
24472
  if (this.router.activePorts(owner).length) return;
23893
24473
  await new Promise((resolve2) => setTimeout(resolve2, 0));
23894
24474
  }
23895
- if (pendingHandles() === 0 && pendingUnrefed() > 0 && !readingStdin()) {
24475
+ if (pendingHandles() === 0 && pendingRequests() === 0 && pendingUnrefed() > 0 && !readingStdin()) {
23896
24476
  const deadline = Date.now() + 1e3;
23897
24477
  while (!this.router.activePorts(owner).length && Date.now() < deadline) {
23898
24478
  await new Promise((resolve2) => setTimeout(resolve2, 5));
23899
24479
  }
23900
24480
  }
23901
- while (!this.router.activePorts(owner).length && (pendingHandles() > 0 || readingStdin())) {
24481
+ while (!this.router.activePorts(owner).length && (pendingHandles() > 0 || pendingRequests() > 0 || readingStdin())) {
23902
24482
  await new Promise((resolve2) => setTimeout(resolve2, 5));
23903
24483
  }
23904
24484
  }
@@ -24559,17 +25139,18 @@ var Terminal = class {
24559
25139
  // ── key handling ──────────────────────────────────────────────────────────
24560
25140
  key(ch) {
24561
25141
  if (this.running) {
24562
- if (ch === CTRL_C) {
25142
+ const raw = this.currentStdin?.rawMode === true;
25143
+ if (ch === CTRL_C && !raw) {
24563
25144
  this.session.proc.deliver("SIGINT");
24564
25145
  this.write("^C\r\n");
24565
25146
  return;
24566
25147
  }
24567
- if (ch === CTRL_D) {
25148
+ if (ch === CTRL_D && !raw) {
24568
25149
  this.currentStdin?.end();
24569
25150
  return;
24570
25151
  }
24571
- if (!this.currentStdin?.rawMode) this.write(ch === "\r" ? "\r\n" : ch);
24572
- this.currentStdin?.write(ch === "\r" ? "\n" : ch);
25152
+ if (!raw) this.write(ch === "\r" ? "\r\n" : ch);
25153
+ this.currentStdin?.write(!raw && ch === "\r" ? "\n" : ch);
24573
25154
  return;
24574
25155
  }
24575
25156
  if (this.escapeBuffer !== "") {