sandboxedjs 0.1.49 → 0.1.51
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 +16 -14
- package/dist/index.cjs +214 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +214 -41
- package/dist/index.js.map +1 -1
- package/dist/python/python.data +74 -79
- package/dist/python/python.js +1 -1
- package/dist/python/python.wasm +0 -0
- package/dist/python/runtime.json +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -19090,7 +19090,7 @@ async function startNodeWorker(url, workerData) {
|
|
|
19090
19090
|
/* webpackIgnore: true */
|
|
19091
19091
|
specifier
|
|
19092
19092
|
);
|
|
19093
|
-
const worker = new NodeWorker(url, { workerData });
|
|
19093
|
+
const worker = new NodeWorker(url, { workerData, execArgv: [] });
|
|
19094
19094
|
return {
|
|
19095
19095
|
postMessage: (message) => worker.postMessage(message),
|
|
19096
19096
|
onMessage: (listener) => worker.on("message", listener),
|
|
@@ -19359,27 +19359,40 @@ function compareRelease(left, right) {
|
|
|
19359
19359
|
function isPreRelease(version) {
|
|
19360
19360
|
return /(a|b|rc|dev|post)\d*$/i.test(version.replace(/^\d+(\.\d+)*/, ""));
|
|
19361
19361
|
}
|
|
19362
|
-
async function
|
|
19362
|
+
async function resolvePackageCandidates(client, requirement, environment) {
|
|
19363
19363
|
const index = await client.json(`https://pypi.org/pypi/${requirement.name}/json`);
|
|
19364
19364
|
const candidates = Object.keys(index.releases).filter((version) => requirement.specifiers.every((s) => compareVersions(version, s.version, s.op))).sort((a, b) => compareRelease(releaseOf(a), releaseOf(b)));
|
|
19365
19365
|
const stable = candidates.filter((version) => !isPreRelease(version));
|
|
19366
|
-
const
|
|
19366
|
+
const newestFirst = (stable.length > 0 ? stable : candidates).reverse();
|
|
19367
|
+
const probeIndexes = [
|
|
19368
|
+
0,
|
|
19369
|
+
Math.floor(newestFirst.length / 2),
|
|
19370
|
+
Math.floor(newestFirst.length / 4),
|
|
19371
|
+
Math.floor(newestFirst.length * 3 / 4),
|
|
19372
|
+
newestFirst.length - 1
|
|
19373
|
+
];
|
|
19374
|
+
const probes = [...new Set(probeIndexes)].map((index2) => newestFirst[index2]).filter((v) => Boolean(v));
|
|
19375
|
+
const probeVersions = new Set(probes);
|
|
19376
|
+
const ordered = [...probes, ...newestFirst.filter((version) => !probeVersions.has(version))];
|
|
19367
19377
|
let sawSourceOnly = false;
|
|
19378
|
+
const resolved = [];
|
|
19368
19379
|
for (const version of ordered) {
|
|
19369
19380
|
const files = (index.releases[version] ?? []).filter((file3) => !file3.yanked);
|
|
19370
19381
|
const wheel = pickWheel(files);
|
|
19371
19382
|
if (wheel) {
|
|
19372
|
-
|
|
19383
|
+
resolved.push({
|
|
19373
19384
|
name: requirement.name,
|
|
19374
19385
|
version,
|
|
19375
19386
|
url: wheel.url,
|
|
19376
19387
|
filename: wheel.filename,
|
|
19377
19388
|
sha256: wheel.digests?.sha256 ?? "",
|
|
19378
19389
|
requires: []
|
|
19379
|
-
};
|
|
19390
|
+
});
|
|
19391
|
+
continue;
|
|
19380
19392
|
}
|
|
19381
19393
|
if (files.some((file3) => file3.packagetype === "sdist")) sawSourceOnly = true;
|
|
19382
19394
|
}
|
|
19395
|
+
if (resolved.length > 0) return resolved;
|
|
19383
19396
|
if (ordered.length === 0) {
|
|
19384
19397
|
throw new Error(
|
|
19385
19398
|
`no version of ${requirement.name} matches ${describe(requirement)}`
|
|
@@ -19407,6 +19420,7 @@ function splitOnce(text2, separator) {
|
|
|
19407
19420
|
|
|
19408
19421
|
// src/runtime/python/install.ts
|
|
19409
19422
|
var SITE_PACKAGES = "/usr/lib/python3.13/site-packages";
|
|
19423
|
+
var SCRIPTS = "/usr/local/bin";
|
|
19410
19424
|
function markerEnvironment(pythonVersion) {
|
|
19411
19425
|
const [major = "3", minor = "13"] = pythonVersion.split(".");
|
|
19412
19426
|
return {
|
|
@@ -19427,18 +19441,39 @@ async function installRequirements(options) {
|
|
|
19427
19441
|
const requirement = parseRequirement(text2);
|
|
19428
19442
|
if (requirement) queue.push({ requirement, extras: requirement.extras });
|
|
19429
19443
|
}
|
|
19430
|
-
const
|
|
19431
|
-
const
|
|
19432
|
-
|
|
19433
|
-
|
|
19444
|
+
const archives = /* @__PURE__ */ new Map();
|
|
19445
|
+
const unavailable2 = /* @__PURE__ */ new Set();
|
|
19446
|
+
const unsatisfiable = /* @__PURE__ */ new Map();
|
|
19447
|
+
const candidateCache = /* @__PURE__ */ new Map();
|
|
19448
|
+
let candidateAttempts = 0;
|
|
19449
|
+
const MAX_CANDIDATE_ATTEMPTS = 96;
|
|
19450
|
+
const solve = async (pending, state) => {
|
|
19451
|
+
if (pending.length === 0) return state;
|
|
19452
|
+
const [first, ...tail2] = pending;
|
|
19453
|
+
let { requirement, extras } = first;
|
|
19454
|
+
const rest = [];
|
|
19455
|
+
const mergedSpecifiers = [...requirement.specifiers];
|
|
19456
|
+
const mergedExtras = new Set(extras);
|
|
19457
|
+
for (const item of tail2) {
|
|
19458
|
+
if (item.requirement.name === requirement.name && markerApplies(item.requirement.marker, environment, item.extras)) {
|
|
19459
|
+
mergedSpecifiers.push(...item.requirement.specifiers);
|
|
19460
|
+
for (const extra of item.extras) mergedExtras.add(extra);
|
|
19461
|
+
} else {
|
|
19462
|
+
rest.push(item);
|
|
19463
|
+
}
|
|
19464
|
+
}
|
|
19465
|
+
requirement = { ...requirement, specifiers: mergedSpecifiers };
|
|
19466
|
+
extras = [...mergedExtras];
|
|
19434
19467
|
if (!markerApplies(requirement.marker, environment, extras)) {
|
|
19435
|
-
|
|
19436
|
-
|
|
19468
|
+
return solve(rest, {
|
|
19469
|
+
...state,
|
|
19470
|
+
skipped: [...state.skipped, { name: requirement.name, marker: requirement.marker ?? "" }]
|
|
19471
|
+
});
|
|
19437
19472
|
}
|
|
19438
|
-
const
|
|
19439
|
-
|
|
19440
|
-
|
|
19441
|
-
const already = chosen.get(requirement.name);
|
|
19473
|
+
const solveKey = `${requirement.name}:${requirement.specifiers.map((s) => `${s.op}${s.version}`).sort().join(",")}:${[...extras].sort().join(",")}`;
|
|
19474
|
+
const knownFailure = unsatisfiable.get(solveKey);
|
|
19475
|
+
if (knownFailure) throw knownFailure;
|
|
19476
|
+
const already = state.chosen.get(requirement.name);
|
|
19442
19477
|
if (already) {
|
|
19443
19478
|
const satisfied = requirement.specifiers.every(
|
|
19444
19479
|
(s) => compareVersions(already.version, s.version, s.op)
|
|
@@ -19448,38 +19483,152 @@ async function installRequirements(options) {
|
|
|
19448
19483
|
`${requirement.name} ${already.version} is already selected but another dependency needs ` + requirement.specifiers.map((s) => `${s.op}${s.version}`).join(", ")
|
|
19449
19484
|
);
|
|
19450
19485
|
}
|
|
19451
|
-
|
|
19486
|
+
return solve(rest, state);
|
|
19452
19487
|
}
|
|
19453
19488
|
options.progress.collecting(requirement.name);
|
|
19454
|
-
|
|
19455
|
-
|
|
19456
|
-
|
|
19457
|
-
const
|
|
19458
|
-
|
|
19459
|
-
|
|
19460
|
-
|
|
19461
|
-
|
|
19462
|
-
|
|
19463
|
-
|
|
19464
|
-
|
|
19465
|
-
|
|
19466
|
-
}
|
|
19467
|
-
|
|
19468
|
-
|
|
19469
|
-
|
|
19489
|
+
if (unavailable2.has(requirement.name)) {
|
|
19490
|
+
throw new Error(`${requirement.name} has no wheel this runtime can use`);
|
|
19491
|
+
}
|
|
19492
|
+
const candidateKey = `${requirement.name}:${requirement.specifiers.map((s) => `${s.op}${s.version}`).join(",")}`;
|
|
19493
|
+
let candidatePromise = candidateCache.get(candidateKey);
|
|
19494
|
+
if (!candidatePromise) {
|
|
19495
|
+
candidatePromise = resolvePackageCandidates(options.client, requirement);
|
|
19496
|
+
candidateCache.set(candidateKey, candidatePromise);
|
|
19497
|
+
}
|
|
19498
|
+
let candidates;
|
|
19499
|
+
try {
|
|
19500
|
+
candidates = await candidatePromise;
|
|
19501
|
+
} catch (error) {
|
|
19502
|
+
const anyKey = `${requirement.name}:`;
|
|
19503
|
+
let anyPromise = candidateCache.get(anyKey);
|
|
19504
|
+
if (!anyPromise) {
|
|
19505
|
+
anyPromise = resolvePackageCandidates(
|
|
19506
|
+
options.client,
|
|
19507
|
+
{ ...requirement, specifiers: [] });
|
|
19508
|
+
candidateCache.set(anyKey, anyPromise);
|
|
19509
|
+
}
|
|
19510
|
+
try {
|
|
19511
|
+
await anyPromise;
|
|
19512
|
+
} catch {
|
|
19513
|
+
unavailable2.add(requirement.name);
|
|
19514
|
+
}
|
|
19515
|
+
throw error;
|
|
19516
|
+
}
|
|
19517
|
+
let failure2;
|
|
19518
|
+
for (const resolved of candidates) {
|
|
19519
|
+
try {
|
|
19520
|
+
candidateAttempts += 1;
|
|
19521
|
+
if (candidateAttempts > MAX_CANDIDATE_ATTEMPTS) {
|
|
19522
|
+
throw new Error(
|
|
19523
|
+
"dependency resolution exceeded 96 wheel candidates; add version constraints for packages whose newest generation requires unavailable native WASM extensions"
|
|
19524
|
+
);
|
|
19525
|
+
}
|
|
19526
|
+
let entries = archives.get(resolved.url);
|
|
19527
|
+
if (!entries) {
|
|
19528
|
+
options.progress.downloading(resolved.name, resolved.version);
|
|
19529
|
+
entries = readZip(await options.client.bytes(resolved.url));
|
|
19530
|
+
archives.set(resolved.url, entries);
|
|
19531
|
+
}
|
|
19532
|
+
const dependencies = [];
|
|
19533
|
+
const metadata = entries.find((entry) => /\.dist-info\/METADATA$/.test(entry.name));
|
|
19534
|
+
if (metadata) {
|
|
19535
|
+
for (const line of new TextDecoder().decode(metadata.data()).split(/\r?\n/)) {
|
|
19536
|
+
if (!line.startsWith("Requires-Dist:")) continue;
|
|
19537
|
+
const dependency = parseRequirement(line.slice("Requires-Dist:".length));
|
|
19538
|
+
if (dependency && markerApplies(dependency.marker, environment, extras)) {
|
|
19539
|
+
dependencies.push({ requirement: dependency, extras: dependency.extras });
|
|
19540
|
+
}
|
|
19541
|
+
}
|
|
19542
|
+
}
|
|
19543
|
+
const chosen = new Map(state.chosen);
|
|
19544
|
+
chosen.set(requirement.name, resolved);
|
|
19545
|
+
return await solve([...dependencies, ...rest], {
|
|
19546
|
+
chosen,
|
|
19547
|
+
staged: [...state.staged, ...stageWheel(entries, resolved)],
|
|
19548
|
+
installed: [...state.installed, { name: resolved.name, version: resolved.version }],
|
|
19549
|
+
skipped: state.skipped
|
|
19550
|
+
});
|
|
19551
|
+
} catch (error) {
|
|
19552
|
+
if (error?.message?.startsWith("dependency resolution exceeded")) throw error;
|
|
19553
|
+
failure2 = error;
|
|
19554
|
+
}
|
|
19555
|
+
}
|
|
19556
|
+
const resolutionError = failure2 instanceof Error ? failure2 : new Error(`could not resolve ${requirement.name}`);
|
|
19557
|
+
unsatisfiable.set(solveKey, resolutionError);
|
|
19558
|
+
throw resolutionError;
|
|
19559
|
+
};
|
|
19560
|
+
const solved = await solve(queue, {
|
|
19561
|
+
chosen: /* @__PURE__ */ new Map(),
|
|
19562
|
+
staged: [],
|
|
19563
|
+
installed: [],
|
|
19564
|
+
skipped: []
|
|
19565
|
+
});
|
|
19566
|
+
report.installed = solved.installed;
|
|
19567
|
+
report.skipped = solved.skipped;
|
|
19568
|
+
commit(options.vfs, options.cred, solved.staged);
|
|
19470
19569
|
return report;
|
|
19471
19570
|
}
|
|
19472
|
-
function
|
|
19571
|
+
function stageWheel(entries, resolved) {
|
|
19473
19572
|
const staged = [];
|
|
19474
19573
|
for (const entry of entries) {
|
|
19475
19574
|
if (entry.isDirectory) continue;
|
|
19575
|
+
const dataPath = /^[^/]+\.data\/(purelib|platlib|scripts)\/(.+)$/.exec(entry.name);
|
|
19576
|
+
if (dataPath) {
|
|
19577
|
+
const [, scheme, relative2] = dataPath;
|
|
19578
|
+
staged.push({
|
|
19579
|
+
path: scheme === "scripts" ? `${SCRIPTS}/${relative2}` : `${SITE_PACKAGES}/${relative2}`,
|
|
19580
|
+
data: entry.data(),
|
|
19581
|
+
...scheme === "scripts" ? { mode: 493 } : {}
|
|
19582
|
+
});
|
|
19583
|
+
continue;
|
|
19584
|
+
}
|
|
19476
19585
|
if (/^[^/]+\.data\//.test(entry.name)) continue;
|
|
19477
19586
|
staged.push({ path: `${SITE_PACKAGES}/${entry.name}`, data: entry.data() });
|
|
19478
19587
|
}
|
|
19588
|
+
const entryPoints = entries.find((entry) => /\.dist-info\/entry_points\.txt$/.test(entry.name));
|
|
19589
|
+
if (entryPoints) {
|
|
19590
|
+
for (const [name, target] of consoleScripts(new TextDecoder().decode(entryPoints.data()))) {
|
|
19591
|
+
staged.push({ path: `${SCRIPTS}/${name}`, data: new TextEncoder().encode(consoleLauncher(target)), mode: 493 });
|
|
19592
|
+
}
|
|
19593
|
+
}
|
|
19594
|
+
return staged;
|
|
19595
|
+
}
|
|
19596
|
+
function commit(vfs, cred, staged) {
|
|
19479
19597
|
const directories = /* @__PURE__ */ new Set();
|
|
19480
19598
|
for (const file3 of staged) directories.add(file3.path.slice(0, file3.path.lastIndexOf("/")));
|
|
19481
19599
|
for (const directory2 of directories) vfs.mkdir(directory2, { recursive: true, cred });
|
|
19482
|
-
for (const file3 of staged)
|
|
19600
|
+
for (const file3 of staged) {
|
|
19601
|
+
vfs.writeFile(file3.path, file3.data, { cred, ...file3.mode === void 0 ? {} : { mode: file3.mode } });
|
|
19602
|
+
if (file3.mode !== void 0) vfs.chmod(file3.path, file3.mode, cred);
|
|
19603
|
+
}
|
|
19604
|
+
}
|
|
19605
|
+
function consoleScripts(text2) {
|
|
19606
|
+
const result = [];
|
|
19607
|
+
let section = "";
|
|
19608
|
+
for (const raw of text2.split(/\r?\n/)) {
|
|
19609
|
+
const line = raw.trim();
|
|
19610
|
+
const heading = /^\[([^\]]+)\]$/.exec(line);
|
|
19611
|
+
if (heading) {
|
|
19612
|
+
section = heading[1];
|
|
19613
|
+
continue;
|
|
19614
|
+
}
|
|
19615
|
+
if (section !== "console_scripts" || line === "" || line.startsWith("#")) continue;
|
|
19616
|
+
const assignment = /^([^=\s]+)\s*=\s*([^\s;]+)(?:\s*;.*)?$/.exec(line);
|
|
19617
|
+
if (assignment) result.push([assignment[1], assignment[2].replace(/\s*\[.*\]$/, "")]);
|
|
19618
|
+
}
|
|
19619
|
+
return result;
|
|
19620
|
+
}
|
|
19621
|
+
function consoleLauncher(target) {
|
|
19622
|
+
const [module, attribute = ""] = target.split(":", 2);
|
|
19623
|
+
return `#!/usr/bin/python3
|
|
19624
|
+
import sys
|
|
19625
|
+
from importlib import import_module
|
|
19626
|
+
entry = import_module(${JSON.stringify(module)})
|
|
19627
|
+
for part in ${JSON.stringify(attribute)}.split('.'):
|
|
19628
|
+
if part:
|
|
19629
|
+
entry = getattr(entry, part)
|
|
19630
|
+
raise SystemExit(entry())
|
|
19631
|
+
`;
|
|
19483
19632
|
}
|
|
19484
19633
|
|
|
19485
19634
|
// src/runtime/python/backend.ts
|
|
@@ -19487,7 +19636,11 @@ function withSitePackages(env2) {
|
|
|
19487
19636
|
const existing = env2.PYTHONPATH;
|
|
19488
19637
|
return {
|
|
19489
19638
|
...env2,
|
|
19490
|
-
PYTHONPATH: existing ? `${SITE_PACKAGES}:${existing}` : SITE_PACKAGES
|
|
19639
|
+
PYTHONPATH: existing ? `${SITE_PACKAGES}:${existing}` : SITE_PACKAGES,
|
|
19640
|
+
/* Installed packages live on a host-mounted VFS. Rewriting hundreds of
|
|
19641
|
+
* cache files on every short-lived WASM process is wasted work and can
|
|
19642
|
+
* exhaust the synchronous host-call bridge during large import graphs. */
|
|
19643
|
+
PYTHONDONTWRITEBYTECODE: env2.PYTHONDONTWRITEBYTECODE ?? "1"
|
|
19491
19644
|
};
|
|
19492
19645
|
}
|
|
19493
19646
|
var INTERPRETER_OWNED = /* @__PURE__ */ new Set(["lib", "dev", "proc", "usr"]);
|
|
@@ -19577,6 +19730,12 @@ function containerMounts(ctx) {
|
|
|
19577
19730
|
}
|
|
19578
19731
|
} catch {
|
|
19579
19732
|
}
|
|
19733
|
+
try {
|
|
19734
|
+
if (ctx.vfs.stat(SCRIPTS, { cred: ctx.cred }).isDirectory()) {
|
|
19735
|
+
mounts.push({ guest: SCRIPTS, container: SCRIPTS });
|
|
19736
|
+
}
|
|
19737
|
+
} catch {
|
|
19738
|
+
}
|
|
19580
19739
|
return mounts;
|
|
19581
19740
|
}
|
|
19582
19741
|
|
|
@@ -19630,16 +19789,30 @@ environments. A package with no usable wheel is an error, never a silent skip.`,
|
|
|
19630
19789
|
"outbound network access is disabled for this container (enable with network: { allowOutbound: true })"
|
|
19631
19790
|
);
|
|
19632
19791
|
}
|
|
19792
|
+
const jsonCache = /* @__PURE__ */ new Map();
|
|
19793
|
+
const bytesCache = /* @__PURE__ */ new Map();
|
|
19633
19794
|
const client = {
|
|
19634
19795
|
async json(url) {
|
|
19635
|
-
|
|
19636
|
-
if (!
|
|
19637
|
-
|
|
19796
|
+
let pending = jsonCache.get(url);
|
|
19797
|
+
if (!pending) {
|
|
19798
|
+
pending = fetch(url).then((response) => {
|
|
19799
|
+
if (!response.ok) throw new Error(`${url} answered ${response.status}`);
|
|
19800
|
+
return response.json();
|
|
19801
|
+
});
|
|
19802
|
+
jsonCache.set(url, pending);
|
|
19803
|
+
}
|
|
19804
|
+
return pending;
|
|
19638
19805
|
},
|
|
19639
19806
|
async bytes(url) {
|
|
19640
|
-
|
|
19641
|
-
if (!
|
|
19642
|
-
|
|
19807
|
+
let pending = bytesCache.get(url);
|
|
19808
|
+
if (!pending) {
|
|
19809
|
+
pending = fetch(url).then(async (response) => {
|
|
19810
|
+
if (!response.ok) throw new Error(`${url} answered ${response.status}`);
|
|
19811
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
19812
|
+
});
|
|
19813
|
+
bytesCache.set(url, pending);
|
|
19814
|
+
}
|
|
19815
|
+
return pending;
|
|
19643
19816
|
}
|
|
19644
19817
|
};
|
|
19645
19818
|
try {
|