sandboxedjs 0.1.31 → 0.1.32

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
@@ -8841,7 +8841,7 @@ async function runShell(ctxName, ctx) {
8841
8841
  const args = ctx.args.slice();
8842
8842
  let command;
8843
8843
  let scriptPath;
8844
- let readStdin2 = false;
8844
+ let readStdin = false;
8845
8845
  let interactive = false;
8846
8846
  let login = ctxName.startsWith("-");
8847
8847
  const options = {};
@@ -8854,7 +8854,7 @@ async function runShell(ctxName, ctx) {
8854
8854
  break;
8855
8855
  }
8856
8856
  if (arg === "-s") {
8857
- readStdin2 = true;
8857
+ readStdin = true;
8858
8858
  continue;
8859
8859
  }
8860
8860
  if (arg === "-i") {
@@ -8891,7 +8891,7 @@ async function runShell(ctxName, ctx) {
8891
8891
  break;
8892
8892
  }
8893
8893
  const rest = args.slice(i);
8894
- if (command === void 0 && !readStdin2 && rest.length > 0) {
8894
+ if (command === void 0 && !readStdin && rest.length > 0) {
8895
8895
  scriptPath = rest[0];
8896
8896
  }
8897
8897
  const positional = command !== void 0 ? rest.slice(1) : scriptPath !== void 0 ? rest.slice(1) : rest;
@@ -17786,6 +17786,143 @@ function mountContainerFs(FS, opts) {
17786
17786
  }
17787
17787
  }
17788
17788
 
17789
+ // src/runtime/python-syscalls.ts
17790
+ var EMPTY = new Uint8Array(0);
17791
+ function createStdinHost(ctx) {
17792
+ let pending = EMPTY;
17793
+ let eof = false;
17794
+ const take = (size) => {
17795
+ const n = Math.min(size, pending.length);
17796
+ const out = pending.subarray(0, n);
17797
+ pending = pending.subarray(n);
17798
+ return out;
17799
+ };
17800
+ return {
17801
+ /**
17802
+ * Drain a non-interactive stdin up front.
17803
+ *
17804
+ * A pipe ends, so reading it eagerly costs nothing and leaves every read
17805
+ * answerable without suspending — which is what keeps piped input working
17806
+ * on hosts with no stack-switching. A terminal never ends, so priming one
17807
+ * would hang before the program had printed its prompt.
17808
+ */
17809
+ async prime() {
17810
+ if (ctx.stdin.isTTY || ctx.stdin.interactive) return;
17811
+ try {
17812
+ pending = await ctx.stdin.readAll();
17813
+ } catch {
17814
+ pending = EMPTY;
17815
+ }
17816
+ eof = true;
17817
+ },
17818
+ buffered(size) {
17819
+ if (pending.length > 0) return take(size);
17820
+ return eof ? EMPTY : void 0;
17821
+ },
17822
+ async read(size) {
17823
+ if (pending.length > 0) return take(size);
17824
+ if (eof) return EMPTY;
17825
+ const chunk = await ctx.stdin.read(size);
17826
+ if (chunk === null || chunk.length === 0) {
17827
+ eof = true;
17828
+ return EMPTY;
17829
+ }
17830
+ pending = chunk;
17831
+ return take(size);
17832
+ },
17833
+ isatty() {
17834
+ return Boolean(ctx.stdin.isTTY);
17835
+ }
17836
+ };
17837
+ }
17838
+ var BRIDGE_PY = `
17839
+ import builtins, io, sys
17840
+ import _sbx_host as _host
17841
+ from pyodide.ffi import can_run_sync, run_sync
17842
+
17843
+
17844
+ def _to_bytes(value):
17845
+ if value is None:
17846
+ return b""
17847
+ to_bytes = getattr(value, "to_bytes", None)
17848
+ if to_bytes is not None:
17849
+ return to_bytes()
17850
+ return bytes(value.to_py())
17851
+
17852
+
17853
+ class _SbxStdin(io.RawIOBase):
17854
+ """The container's standard input, as a blocking raw stream."""
17855
+
17856
+ def readable(self):
17857
+ return True
17858
+
17859
+ def fileno(self):
17860
+ return 0
17861
+
17862
+ def isatty(self):
17863
+ return bool(_host.isatty())
17864
+
17865
+ def readinto(self, target):
17866
+ want = len(target)
17867
+ if want == 0:
17868
+ return 0
17869
+ ready = _host.buffered(want)
17870
+ if ready is None:
17871
+ if not can_run_sync():
17872
+ raise OSError(
17873
+ "this host cannot wait for interactive input: WebAssembly "
17874
+ "stack switching (JSPI) is unavailable. Pipe the input in, "
17875
+ "or use a browser or Node build that supports it."
17876
+ )
17877
+ ready = run_sync(_host.read(want))
17878
+ data = _to_bytes(ready)
17879
+ target[: len(data)] = data
17880
+ return len(data)
17881
+
17882
+
17883
+ def _install():
17884
+ stdin = io.TextIOWrapper(
17885
+ io.BufferedReader(_SbxStdin()), encoding="utf-8", errors="replace", line_buffering=True
17886
+ )
17887
+ sys.stdin = stdin
17888
+ sys.__stdin__ = stdin
17889
+
17890
+ def input(prompt=""):
17891
+ # CPython writes the prompt to stdout and strips exactly one trailing
17892
+ # newline; anything else changes what a program reads back.
17893
+ if prompt != "":
17894
+ sys.stdout.write(str(prompt))
17895
+ sys.stdout.flush()
17896
+ line = sys.stdin.readline()
17897
+ if not line:
17898
+ raise EOFError("EOF when reading a line")
17899
+ if line.endswith("\\n"):
17900
+ line = line[:-1]
17901
+ if line.endswith("\\r"):
17902
+ line = line[:-1]
17903
+ return line
17904
+
17905
+ builtins.input = input
17906
+
17907
+
17908
+ _install()
17909
+ `;
17910
+ function installSyscallBridge(py) {
17911
+ const slot = py.__sbxStdin ??= { active: null };
17912
+ py.registerJsModule("_sbx_host", {
17913
+ /* Not `?.` and `??`: `buffered` returns undefined to mean "you will have
17914
+ * to wait", and a nullish default would quietly turn that into end-of-file —
17915
+ * which is exactly the silent EOF this bridge exists to remove. */
17916
+ buffered: (size) => slot.active ? slot.active.buffered(size) : EMPTY,
17917
+ read: (size) => slot.active ? slot.active.read(size) : Promise.resolve(EMPTY),
17918
+ isatty: () => slot.active?.isatty() ?? false
17919
+ });
17920
+ py.runPython(BRIDGE_PY);
17921
+ }
17922
+ function bindStdin(py, host2) {
17923
+ py.__sbxStdin.active = host2;
17924
+ }
17925
+
17789
17926
  // src/runtime/cpython.ts
17790
17927
  var RESERVED_FOR_INTERPRETER = /* @__PURE__ */ new Set(["lib", "dev", "proc"]);
17791
17928
  var pyodideModule = null;
@@ -17858,6 +17995,7 @@ async function interpreterFor(ctx) {
17858
17995
  });
17859
17996
  mountContainerDirs(py, ctx);
17860
17997
  installRunner(py);
17998
+ installSyscallBridge(py);
17861
17999
  return py;
17862
18000
  })();
17863
18001
  interpreters.set(ctx.vfs, starting);
@@ -17952,7 +18090,7 @@ function reportError(ctx, error) {
17952
18090
  `);
17953
18091
  return 1;
17954
18092
  }
17955
- async function runCPythonProgram(ctx, source, argv, scriptDir, stdinText) {
18093
+ async function runCPythonProgram(ctx, source, argv, scriptDir = null) {
17956
18094
  let py;
17957
18095
  try {
17958
18096
  py = await interpreterFor(ctx);
@@ -17973,7 +18111,6 @@ async function runCPythonProgram(ctx, source, argv, scriptDir, stdinText) {
17973
18111
  });
17974
18112
  } catch {
17975
18113
  }
17976
- const encoder8 = new TextEncoder();
17977
18114
  const decoder8 = new TextDecoder();
17978
18115
  py.setStdout({
17979
18116
  write: (buffer) => {
@@ -17987,20 +18124,17 @@ async function runCPythonProgram(ctx, source, argv, scriptDir, stdinText) {
17987
18124
  return buffer.length;
17988
18125
  }
17989
18126
  });
17990
- if (stdinText !== null) {
17991
- const bytes2 = encoder8.encode(stdinText);
17992
- let offset = 0;
17993
- py.setStdin({
17994
- read: (buffer) => {
17995
- const take = Math.min(buffer.length, bytes2.length - offset);
17996
- buffer.set(bytes2.subarray(offset, offset + take));
17997
- offset += take;
17998
- return take;
17999
- }
18000
- });
18001
- } else {
18002
- py.setStdin({ read: () => 0 });
18003
- }
18127
+ const stdinHost = createStdinHost(ctx);
18128
+ await stdinHost.prime();
18129
+ bindStdin(py, stdinHost);
18130
+ py.setStdin({
18131
+ read: (buffer) => {
18132
+ const ready = stdinHost.buffered(buffer.length);
18133
+ if (!ready || ready.length === 0) return 0;
18134
+ buffer.set(ready);
18135
+ return ready.length;
18136
+ }
18137
+ });
18004
18138
  try {
18005
18139
  bootstrap(py, ctx, argv, scriptDir);
18006
18140
  } catch (error) {
@@ -18133,11 +18267,6 @@ function configurePython(options = {}) {
18133
18267
  configureCPython({ indexURL: options.indexURL, moduleURL: options.pyodideURL });
18134
18268
  }
18135
18269
  var isPythonAvailable = isCPythonAvailable;
18136
- async function readStdin(ctx) {
18137
- if (ctx.stdin.isTTY || ctx.stdin.interactive) return null;
18138
- const bytes2 = await ctx.stdin.readAll();
18139
- return bytes2.length ? new TextDecoder().decode(bytes2) : null;
18140
- }
18141
18270
  var python = defineCommand({
18142
18271
  name: "python3",
18143
18272
  path: "/usr/bin/python3",
@@ -18186,7 +18315,7 @@ compatible packages can be installed with pip (micropip).`,
18186
18315
  }
18187
18316
  const rest = argv.slice(i);
18188
18317
  if (command !== void 0) {
18189
- return (await runCPythonProgram(ctx, command, ["-c", ...rest], null, await readStdin(ctx))).exitCode;
18318
+ return (await runCPythonProgram(ctx, command, ["-c", ...rest], null)).exitCode;
18190
18319
  }
18191
18320
  if (moduleName !== void 0) {
18192
18321
  const program = `
@@ -18198,16 +18327,16 @@ except ImportError:
18198
18327
  print("/usr/bin/python3: No module named " + ${JSON.stringify(moduleName)}, file=sys.stderr)
18199
18328
  raise SystemExit(1)
18200
18329
  `;
18201
- return (await runCPythonProgram(ctx, program, [moduleName, ...rest], null, await readStdin(ctx))).exitCode;
18330
+ return (await runCPythonProgram(ctx, program, [moduleName, ...rest], null)).exitCode;
18202
18331
  }
18203
18332
  if (script === void 0) {
18204
18333
  if (ctx.stdin.isTTY) return runCPythonRepl(ctx);
18205
18334
  const source2 = new TextDecoder().decode(await ctx.stdin.readAll());
18206
- return source2.trim() ? (await runCPythonProgram(ctx, source2, ["", ...rest], null, null)).exitCode : 0;
18335
+ return source2.trim() ? (await runCPythonProgram(ctx, source2, ["", ...rest])).exitCode : 0;
18207
18336
  }
18208
18337
  if (script === "-") {
18209
18338
  const source2 = new TextDecoder().decode(await ctx.stdin.readAll());
18210
- return (await runCPythonProgram(ctx, source2, ["-", ...rest], null, null)).exitCode;
18339
+ return (await runCPythonProgram(ctx, source2, ["-", ...rest])).exitCode;
18211
18340
  }
18212
18341
  const abs = ctx.path(script);
18213
18342
  let source;
@@ -18218,7 +18347,7 @@ except ImportError:
18218
18347
  `);
18219
18348
  return 2;
18220
18349
  }
18221
- return (await runCPythonProgram(ctx, source, [abs, ...rest], dirname(abs), await readStdin(ctx))).exitCode;
18350
+ return (await runCPythonProgram(ctx, source, [abs, ...rest], dirname(abs))).exitCode;
18222
18351
  }
18223
18352
  });
18224
18353
  function printHelp2(ctx) {