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.cjs CHANGED
@@ -8858,7 +8858,7 @@ async function runShell(ctxName, ctx) {
8858
8858
  const args = ctx.args.slice();
8859
8859
  let command;
8860
8860
  let scriptPath;
8861
- let readStdin2 = false;
8861
+ let readStdin = false;
8862
8862
  let interactive = false;
8863
8863
  let login = ctxName.startsWith("-");
8864
8864
  const options = {};
@@ -8871,7 +8871,7 @@ async function runShell(ctxName, ctx) {
8871
8871
  break;
8872
8872
  }
8873
8873
  if (arg === "-s") {
8874
- readStdin2 = true;
8874
+ readStdin = true;
8875
8875
  continue;
8876
8876
  }
8877
8877
  if (arg === "-i") {
@@ -8908,7 +8908,7 @@ async function runShell(ctxName, ctx) {
8908
8908
  break;
8909
8909
  }
8910
8910
  const rest = args.slice(i);
8911
- if (command === void 0 && !readStdin2 && rest.length > 0) {
8911
+ if (command === void 0 && !readStdin && rest.length > 0) {
8912
8912
  scriptPath = rest[0];
8913
8913
  }
8914
8914
  const positional = command !== void 0 ? rest.slice(1) : scriptPath !== void 0 ? rest.slice(1) : rest;
@@ -17803,6 +17803,143 @@ function mountContainerFs(FS, opts) {
17803
17803
  }
17804
17804
  }
17805
17805
 
17806
+ // src/runtime/python-syscalls.ts
17807
+ var EMPTY = new Uint8Array(0);
17808
+ function createStdinHost(ctx) {
17809
+ let pending = EMPTY;
17810
+ let eof = false;
17811
+ const take = (size) => {
17812
+ const n = Math.min(size, pending.length);
17813
+ const out = pending.subarray(0, n);
17814
+ pending = pending.subarray(n);
17815
+ return out;
17816
+ };
17817
+ return {
17818
+ /**
17819
+ * Drain a non-interactive stdin up front.
17820
+ *
17821
+ * A pipe ends, so reading it eagerly costs nothing and leaves every read
17822
+ * answerable without suspending — which is what keeps piped input working
17823
+ * on hosts with no stack-switching. A terminal never ends, so priming one
17824
+ * would hang before the program had printed its prompt.
17825
+ */
17826
+ async prime() {
17827
+ if (ctx.stdin.isTTY || ctx.stdin.interactive) return;
17828
+ try {
17829
+ pending = await ctx.stdin.readAll();
17830
+ } catch {
17831
+ pending = EMPTY;
17832
+ }
17833
+ eof = true;
17834
+ },
17835
+ buffered(size) {
17836
+ if (pending.length > 0) return take(size);
17837
+ return eof ? EMPTY : void 0;
17838
+ },
17839
+ async read(size) {
17840
+ if (pending.length > 0) return take(size);
17841
+ if (eof) return EMPTY;
17842
+ const chunk = await ctx.stdin.read(size);
17843
+ if (chunk === null || chunk.length === 0) {
17844
+ eof = true;
17845
+ return EMPTY;
17846
+ }
17847
+ pending = chunk;
17848
+ return take(size);
17849
+ },
17850
+ isatty() {
17851
+ return Boolean(ctx.stdin.isTTY);
17852
+ }
17853
+ };
17854
+ }
17855
+ var BRIDGE_PY = `
17856
+ import builtins, io, sys
17857
+ import _sbx_host as _host
17858
+ from pyodide.ffi import can_run_sync, run_sync
17859
+
17860
+
17861
+ def _to_bytes(value):
17862
+ if value is None:
17863
+ return b""
17864
+ to_bytes = getattr(value, "to_bytes", None)
17865
+ if to_bytes is not None:
17866
+ return to_bytes()
17867
+ return bytes(value.to_py())
17868
+
17869
+
17870
+ class _SbxStdin(io.RawIOBase):
17871
+ """The container's standard input, as a blocking raw stream."""
17872
+
17873
+ def readable(self):
17874
+ return True
17875
+
17876
+ def fileno(self):
17877
+ return 0
17878
+
17879
+ def isatty(self):
17880
+ return bool(_host.isatty())
17881
+
17882
+ def readinto(self, target):
17883
+ want = len(target)
17884
+ if want == 0:
17885
+ return 0
17886
+ ready = _host.buffered(want)
17887
+ if ready is None:
17888
+ if not can_run_sync():
17889
+ raise OSError(
17890
+ "this host cannot wait for interactive input: WebAssembly "
17891
+ "stack switching (JSPI) is unavailable. Pipe the input in, "
17892
+ "or use a browser or Node build that supports it."
17893
+ )
17894
+ ready = run_sync(_host.read(want))
17895
+ data = _to_bytes(ready)
17896
+ target[: len(data)] = data
17897
+ return len(data)
17898
+
17899
+
17900
+ def _install():
17901
+ stdin = io.TextIOWrapper(
17902
+ io.BufferedReader(_SbxStdin()), encoding="utf-8", errors="replace", line_buffering=True
17903
+ )
17904
+ sys.stdin = stdin
17905
+ sys.__stdin__ = stdin
17906
+
17907
+ def input(prompt=""):
17908
+ # CPython writes the prompt to stdout and strips exactly one trailing
17909
+ # newline; anything else changes what a program reads back.
17910
+ if prompt != "":
17911
+ sys.stdout.write(str(prompt))
17912
+ sys.stdout.flush()
17913
+ line = sys.stdin.readline()
17914
+ if not line:
17915
+ raise EOFError("EOF when reading a line")
17916
+ if line.endswith("\\n"):
17917
+ line = line[:-1]
17918
+ if line.endswith("\\r"):
17919
+ line = line[:-1]
17920
+ return line
17921
+
17922
+ builtins.input = input
17923
+
17924
+
17925
+ _install()
17926
+ `;
17927
+ function installSyscallBridge(py) {
17928
+ const slot = py.__sbxStdin ??= { active: null };
17929
+ py.registerJsModule("_sbx_host", {
17930
+ /* Not `?.` and `??`: `buffered` returns undefined to mean "you will have
17931
+ * to wait", and a nullish default would quietly turn that into end-of-file —
17932
+ * which is exactly the silent EOF this bridge exists to remove. */
17933
+ buffered: (size) => slot.active ? slot.active.buffered(size) : EMPTY,
17934
+ read: (size) => slot.active ? slot.active.read(size) : Promise.resolve(EMPTY),
17935
+ isatty: () => slot.active?.isatty() ?? false
17936
+ });
17937
+ py.runPython(BRIDGE_PY);
17938
+ }
17939
+ function bindStdin(py, host2) {
17940
+ py.__sbxStdin.active = host2;
17941
+ }
17942
+
17806
17943
  // src/runtime/cpython.ts
17807
17944
  var RESERVED_FOR_INTERPRETER = /* @__PURE__ */ new Set(["lib", "dev", "proc"]);
17808
17945
  var pyodideModule = null;
@@ -17875,6 +18012,7 @@ async function interpreterFor(ctx) {
17875
18012
  });
17876
18013
  mountContainerDirs(py, ctx);
17877
18014
  installRunner(py);
18015
+ installSyscallBridge(py);
17878
18016
  return py;
17879
18017
  })();
17880
18018
  interpreters.set(ctx.vfs, starting);
@@ -17969,7 +18107,7 @@ function reportError(ctx, error) {
17969
18107
  `);
17970
18108
  return 1;
17971
18109
  }
17972
- async function runCPythonProgram(ctx, source, argv, scriptDir, stdinText) {
18110
+ async function runCPythonProgram(ctx, source, argv, scriptDir = null) {
17973
18111
  let py;
17974
18112
  try {
17975
18113
  py = await interpreterFor(ctx);
@@ -17990,7 +18128,6 @@ async function runCPythonProgram(ctx, source, argv, scriptDir, stdinText) {
17990
18128
  });
17991
18129
  } catch {
17992
18130
  }
17993
- const encoder8 = new TextEncoder();
17994
18131
  const decoder8 = new TextDecoder();
17995
18132
  py.setStdout({
17996
18133
  write: (buffer) => {
@@ -18004,20 +18141,17 @@ async function runCPythonProgram(ctx, source, argv, scriptDir, stdinText) {
18004
18141
  return buffer.length;
18005
18142
  }
18006
18143
  });
18007
- if (stdinText !== null) {
18008
- const bytes2 = encoder8.encode(stdinText);
18009
- let offset = 0;
18010
- py.setStdin({
18011
- read: (buffer) => {
18012
- const take = Math.min(buffer.length, bytes2.length - offset);
18013
- buffer.set(bytes2.subarray(offset, offset + take));
18014
- offset += take;
18015
- return take;
18016
- }
18017
- });
18018
- } else {
18019
- py.setStdin({ read: () => 0 });
18020
- }
18144
+ const stdinHost = createStdinHost(ctx);
18145
+ await stdinHost.prime();
18146
+ bindStdin(py, stdinHost);
18147
+ py.setStdin({
18148
+ read: (buffer) => {
18149
+ const ready = stdinHost.buffered(buffer.length);
18150
+ if (!ready || ready.length === 0) return 0;
18151
+ buffer.set(ready);
18152
+ return ready.length;
18153
+ }
18154
+ });
18021
18155
  try {
18022
18156
  bootstrap(py, ctx, argv, scriptDir);
18023
18157
  } catch (error) {
@@ -18150,11 +18284,6 @@ function configurePython(options = {}) {
18150
18284
  configureCPython({ indexURL: options.indexURL, moduleURL: options.pyodideURL });
18151
18285
  }
18152
18286
  var isPythonAvailable = isCPythonAvailable;
18153
- async function readStdin(ctx) {
18154
- if (ctx.stdin.isTTY || ctx.stdin.interactive) return null;
18155
- const bytes2 = await ctx.stdin.readAll();
18156
- return bytes2.length ? new TextDecoder().decode(bytes2) : null;
18157
- }
18158
18287
  var python = defineCommand({
18159
18288
  name: "python3",
18160
18289
  path: "/usr/bin/python3",
@@ -18203,7 +18332,7 @@ compatible packages can be installed with pip (micropip).`,
18203
18332
  }
18204
18333
  const rest = argv.slice(i);
18205
18334
  if (command !== void 0) {
18206
- return (await runCPythonProgram(ctx, command, ["-c", ...rest], null, await readStdin(ctx))).exitCode;
18335
+ return (await runCPythonProgram(ctx, command, ["-c", ...rest], null)).exitCode;
18207
18336
  }
18208
18337
  if (moduleName !== void 0) {
18209
18338
  const program = `
@@ -18215,16 +18344,16 @@ except ImportError:
18215
18344
  print("/usr/bin/python3: No module named " + ${JSON.stringify(moduleName)}, file=sys.stderr)
18216
18345
  raise SystemExit(1)
18217
18346
  `;
18218
- return (await runCPythonProgram(ctx, program, [moduleName, ...rest], null, await readStdin(ctx))).exitCode;
18347
+ return (await runCPythonProgram(ctx, program, [moduleName, ...rest], null)).exitCode;
18219
18348
  }
18220
18349
  if (script === void 0) {
18221
18350
  if (ctx.stdin.isTTY) return runCPythonRepl(ctx);
18222
18351
  const source2 = new TextDecoder().decode(await ctx.stdin.readAll());
18223
- return source2.trim() ? (await runCPythonProgram(ctx, source2, ["", ...rest], null, null)).exitCode : 0;
18352
+ return source2.trim() ? (await runCPythonProgram(ctx, source2, ["", ...rest])).exitCode : 0;
18224
18353
  }
18225
18354
  if (script === "-") {
18226
18355
  const source2 = new TextDecoder().decode(await ctx.stdin.readAll());
18227
- return (await runCPythonProgram(ctx, source2, ["-", ...rest], null, null)).exitCode;
18356
+ return (await runCPythonProgram(ctx, source2, ["-", ...rest])).exitCode;
18228
18357
  }
18229
18358
  const abs = ctx.path(script);
18230
18359
  let source;
@@ -18235,7 +18364,7 @@ except ImportError:
18235
18364
  `);
18236
18365
  return 2;
18237
18366
  }
18238
- return (await runCPythonProgram(ctx, source, [abs, ...rest], dirname(abs), await readStdin(ctx))).exitCode;
18367
+ return (await runCPythonProgram(ctx, source, [abs, ...rest], dirname(abs))).exitCode;
18239
18368
  }
18240
18369
  });
18241
18370
  function printHelp2(ctx) {