sandboxedjs 0.1.14 → 0.1.15
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/README.md +49 -7
- package/dist/index.cjs +46 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -15
- package/dist/index.d.ts +39 -15
- package/dist/index.js +45 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -580,8 +580,10 @@ resolve their implementation at call time (`node:zlib`/`node:crypto` on Node,
|
|
|
580
580
|
builtin in when the module loads and a bundler will not fail on it.
|
|
581
581
|
|
|
582
582
|
**You supply the pod.** The default boot path uses `@scelar/nodepod/headless`, which installs a
|
|
583
|
-
`worker_threads` host.
|
|
584
|
-
|
|
583
|
+
`worker_threads` host. That entry is now loaded lazily, behind a runtime-built specifier, so a
|
|
584
|
+
browser bundle never pulls `node:worker_threads` in through it. In a browser you boot Nodepod's
|
|
585
|
+
browser build yourself — it needs a service worker, per Nodepod's own setup docs — and hand the
|
|
586
|
+
instance over:
|
|
585
587
|
|
|
586
588
|
```ts
|
|
587
589
|
import { Nodepod } from "@scelar/nodepod";
|
|
@@ -594,18 +596,58 @@ await box.exec("ls -la /"); // shell + coreutils
|
|
|
594
596
|
await box.exec("node app.js"); // Node, through Nodepod's browser engine
|
|
595
597
|
```
|
|
596
598
|
|
|
597
|
-
**
|
|
599
|
+
**Verified in a browser.** Booted under Vite with Nodepod's browser build, the following run:
|
|
600
|
+
|
|
601
|
+
```
|
|
602
|
+
$ uname -a Linux sandbox 5.10.0 (sandboxedjs@nodepod) … x86_64 GNU/Linux
|
|
603
|
+
$ ls -la /app the mounted files
|
|
604
|
+
$ wc -l /app/data.txt 3 /app/data.txt
|
|
605
|
+
$ grep beta … beta
|
|
606
|
+
$ node /app/hello.js node runtime says hi
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
Shell, coreutils, the virtual filesystem and the Node runtime all work. Boot costs about ten
|
|
610
|
+
seconds, nearly all of it Nodepod starting.
|
|
611
|
+
|
|
612
|
+
**What does not work in a browser:**
|
|
598
613
|
|
|
599
614
|
- `copyIn()` / `copyOut()` — they read and write the host filesystem, which does not exist.
|
|
600
615
|
- `expose()` — it opens a real `node:http` listener. In a browser you use Nodepod's service
|
|
601
616
|
worker and preview iframe to reach an in-container server instead of a host port.
|
|
617
|
+
- `python3` — see below.
|
|
602
618
|
- The `sandboxedjs` CLI, obviously.
|
|
603
619
|
|
|
604
|
-
Those
|
|
620
|
+
Those use dynamic imports, so they only fail if you call them.
|
|
621
|
+
|
|
622
|
+
### Python in a browser
|
|
623
|
+
|
|
624
|
+
MicroPython does not currently start in a browser. Two separate things are involved.
|
|
625
|
+
|
|
626
|
+
The first is ours and is fixed: Emscripten resolves `micropython.wasm` against the loader's own
|
|
627
|
+
directory, which is right in Node and wrong once a bundler has rewritten the loader into a hashed
|
|
628
|
+
chunk — the guess lands on a path the dev server answers with `index.html`, and instantiation
|
|
629
|
+
dies on `expected magic word 00 61 73 6d, found 3c 21 64 6f` (the first bytes of `<!doctype`).
|
|
630
|
+
Pass the real URL and that goes away:
|
|
631
|
+
|
|
632
|
+
```ts
|
|
633
|
+
import wasm from "@micropython/micropython-webassembly-pyscript/micropython.wasm?url";
|
|
634
|
+
|
|
635
|
+
await createContainer({ pod, python: { wasmUrl: wasm } });
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
`?url` is Vite's spelling; every bundler has its own. `configurePython({ wasmUrl })` does the
|
|
639
|
+
same thing outside a container.
|
|
640
|
+
|
|
641
|
+
The second is upstream and is not fixed. With the correct binary loaded, instantiation fails on
|
|
642
|
+
|
|
643
|
+
```
|
|
644
|
+
LinkError: Import #45 "env" "__syscall_poll_nonblocking": function import requires a callable
|
|
645
|
+
```
|
|
605
646
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
a
|
|
647
|
+
The symbol appears nowhere in the shipped `micropython.mjs`, so the published glue and `.wasm`
|
|
648
|
+
disagree about their import table. Node is unaffected — the Python tests pass there — and nothing
|
|
649
|
+
in this package can supply a symbol the loader never declares. Treat `python3` as Node-only until
|
|
650
|
+
the upstream package ships a matching pair.
|
|
609
651
|
|
|
610
652
|
## Uploading files
|
|
611
653
|
|
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var nodepod = require('@scelar/nodepod');
|
|
6
6
|
var acorn = require('acorn');
|
|
7
7
|
|
|
8
8
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
@@ -8553,6 +8553,15 @@ async function nodeBuiltin(name) {
|
|
|
8553
8553
|
specifier
|
|
8554
8554
|
);
|
|
8555
8555
|
}
|
|
8556
|
+
async function nodeOnlyModule(specifier) {
|
|
8557
|
+
const parts = specifier.split("/");
|
|
8558
|
+
const runtimeSpecifier = parts.join("/");
|
|
8559
|
+
return await import(
|
|
8560
|
+
/* @vite-ignore */
|
|
8561
|
+
/* webpackIgnore: true */
|
|
8562
|
+
runtimeSpecifier
|
|
8563
|
+
);
|
|
8564
|
+
}
|
|
8556
8565
|
var zlibPromise = null;
|
|
8557
8566
|
function nodeZlib() {
|
|
8558
8567
|
zlibPromise ??= nodeBuiltin("zlib");
|
|
@@ -8755,7 +8764,7 @@ function installEsbuildRuntime() {
|
|
|
8755
8764
|
async function install() {
|
|
8756
8765
|
const workerPath = await buildPatchedWorker();
|
|
8757
8766
|
if (!workerPath) return;
|
|
8758
|
-
const { createNodeHost, setRuntimeHost } = await
|
|
8767
|
+
const { createNodeHost, setRuntimeHost } = await nodeOnlyModule("@scelar/nodepod/headless");
|
|
8759
8768
|
setRuntimeHost(createNodeHost({ workerPath }));
|
|
8760
8769
|
}
|
|
8761
8770
|
async function buildPatchedWorker() {
|
|
@@ -22144,6 +22153,10 @@ init_path();
|
|
|
22144
22153
|
var PYTHON_VERSION = "3.4.0";
|
|
22145
22154
|
var MICROPYTHON_BANNER = "MicroPython v1.28.0 on 2026-04-06; SandboxedJS with Emscripten";
|
|
22146
22155
|
var loaderPromise = null;
|
|
22156
|
+
var wasmUrl;
|
|
22157
|
+
function configurePython(options = {}) {
|
|
22158
|
+
if (options.wasmUrl !== void 0) wasmUrl = options.wasmUrl;
|
|
22159
|
+
}
|
|
22147
22160
|
async function getLoader() {
|
|
22148
22161
|
if (!loaderPromise) {
|
|
22149
22162
|
loaderPromise = import('@micropython/micropython-webassembly-pyscript/micropython.mjs').then(
|
|
@@ -22166,6 +22179,7 @@ async function createInterpreter(ctx, opts) {
|
|
|
22166
22179
|
stdout: opts.stdout,
|
|
22167
22180
|
stderr: opts.stderr,
|
|
22168
22181
|
...opts.stdin ? { stdin: opts.stdin } : {},
|
|
22182
|
+
...wasmUrl ? { url: wasmUrl } : {},
|
|
22169
22183
|
linebuffer: false,
|
|
22170
22184
|
heapsize: opts.heapsize ?? 64 * 1024 * 1024
|
|
22171
22185
|
});
|
|
@@ -22890,7 +22904,7 @@ function splitPackageSpec(spec) {
|
|
|
22890
22904
|
return { name: spec.slice(0, at), version: spec.slice(at + 1) };
|
|
22891
22905
|
}
|
|
22892
22906
|
async function installPackages(ctx, specs, opts) {
|
|
22893
|
-
const installer = opts.cwd === "/" ? ctx.kernel.pod.packages : new
|
|
22907
|
+
const installer = opts.cwd === "/" ? ctx.kernel.pod.packages : new nodepod.DependencyInstaller(ctx.vfs.volume, { cwd: opts.cwd });
|
|
22894
22908
|
const onProgress = (message) => {
|
|
22895
22909
|
if (!opts.quiet) ctx.stderr.write(message.endsWith("\n") ? message : message + "\n");
|
|
22896
22910
|
};
|
|
@@ -23182,6 +23196,12 @@ function packageBinaries(ctx, root, packageName) {
|
|
|
23182
23196
|
}
|
|
23183
23197
|
return [];
|
|
23184
23198
|
}
|
|
23199
|
+
var CLI_MOVED_TO = {
|
|
23200
|
+
tailwindcss: "@tailwindcss/cli",
|
|
23201
|
+
postcss: "postcss-cli",
|
|
23202
|
+
autoprefixer: "postcss-cli",
|
|
23203
|
+
sass: "sass-embedded"
|
|
23204
|
+
};
|
|
23185
23205
|
function findLocalBin(ctx, name) {
|
|
23186
23206
|
let dir3 = ctx.cwd;
|
|
23187
23207
|
for (let i = 0; i < 64; i++) {
|
|
@@ -23288,6 +23308,14 @@ unless the container was created with network: { allowOutbound: true }.`,
|
|
|
23288
23308
|
const chosen = binaries.includes(command) ? command : binaries[0];
|
|
23289
23309
|
if (chosen === void 0) {
|
|
23290
23310
|
ctx.warn(`could not determine executable to run: ${packageName} provides no binary`);
|
|
23311
|
+
const replacement = CLI_MOVED_TO[packageName];
|
|
23312
|
+
if (replacement !== void 0) {
|
|
23313
|
+
ctx.warn(`'${packageName}' ships no CLI; its command lives in '${replacement}'.`);
|
|
23314
|
+
ctx.warn(`Try: npx ${replacement} ${rest.join(" ")}`.trimEnd());
|
|
23315
|
+
} else {
|
|
23316
|
+
ctx.warn(`'${packageName}' is installed but declares no "bin" entry.`);
|
|
23317
|
+
ctx.warn("Check which package provides the command, or use `npx -p <pkg> <command>`.");
|
|
23318
|
+
}
|
|
23291
23319
|
return 127;
|
|
23292
23320
|
}
|
|
23293
23321
|
command = chosen;
|
|
@@ -23864,14 +23892,9 @@ var Container = class _Container {
|
|
|
23864
23892
|
}
|
|
23865
23893
|
// ── boot ──────────────────────────────────────────────────────────────────
|
|
23866
23894
|
static async create(opts = {}) {
|
|
23895
|
+
if (opts.python) configurePython(opts.python);
|
|
23867
23896
|
if (!opts.pod) await installEsbuildRuntime();
|
|
23868
|
-
const pod = opts.pod ?? await
|
|
23869
|
-
headless: true,
|
|
23870
|
-
serviceWorker: false,
|
|
23871
|
-
env: opts.env ?? {},
|
|
23872
|
-
workdir: opts.cwd ?? "/",
|
|
23873
|
-
...opts.onServerReady ? { onServerReady: opts.onServerReady } : {}
|
|
23874
|
-
});
|
|
23897
|
+
const pod = opts.pod ?? await bootHeadlessPod(opts);
|
|
23875
23898
|
const kernel = new Kernel({
|
|
23876
23899
|
pod,
|
|
23877
23900
|
hostname: opts.hostname ?? "sandbox",
|
|
@@ -24293,6 +24316,18 @@ function normalizeHeaders(headers) {
|
|
|
24293
24316
|
async function createContainer(opts = {}) {
|
|
24294
24317
|
return Container.create(opts);
|
|
24295
24318
|
}
|
|
24319
|
+
async function bootHeadlessPod(opts) {
|
|
24320
|
+
const { Nodepod } = await nodeOnlyModule(
|
|
24321
|
+
"@scelar/nodepod/headless"
|
|
24322
|
+
);
|
|
24323
|
+
return Nodepod.boot({
|
|
24324
|
+
headless: true,
|
|
24325
|
+
serviceWorker: false,
|
|
24326
|
+
env: opts.env ?? {},
|
|
24327
|
+
workdir: opts.cwd ?? "/",
|
|
24328
|
+
...opts.onServerReady ? { onServerReady: opts.onServerReady } : {}
|
|
24329
|
+
});
|
|
24330
|
+
}
|
|
24296
24331
|
|
|
24297
24332
|
// src/container/terminal.ts
|
|
24298
24333
|
init_path();
|
|
@@ -24771,6 +24806,7 @@ exports.braceExpand = braceExpand;
|
|
|
24771
24806
|
exports.buildRootfs = buildRootfs;
|
|
24772
24807
|
exports.builtinNames = builtinNames;
|
|
24773
24808
|
exports.captureStdio = captureStdio;
|
|
24809
|
+
exports.configurePython = configurePython;
|
|
24774
24810
|
exports.createContainer = createContainer;
|
|
24775
24811
|
exports.createContext = createContext;
|
|
24776
24812
|
exports.default = src_default;
|