sandboxedjs 0.1.49 → 0.1.50
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 +187 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +187 -39
- 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.cjs
CHANGED
|
@@ -19376,27 +19376,30 @@ function compareRelease(left, right) {
|
|
|
19376
19376
|
function isPreRelease(version) {
|
|
19377
19377
|
return /(a|b|rc|dev|post)\d*$/i.test(version.replace(/^\d+(\.\d+)*/, ""));
|
|
19378
19378
|
}
|
|
19379
|
-
async function
|
|
19379
|
+
async function resolvePackageCandidates(client, requirement, environment) {
|
|
19380
19380
|
const index = await client.json(`https://pypi.org/pypi/${requirement.name}/json`);
|
|
19381
19381
|
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)));
|
|
19382
19382
|
const stable = candidates.filter((version) => !isPreRelease(version));
|
|
19383
19383
|
const ordered = (stable.length > 0 ? stable : candidates).reverse();
|
|
19384
19384
|
let sawSourceOnly = false;
|
|
19385
|
+
const resolved = [];
|
|
19385
19386
|
for (const version of ordered) {
|
|
19386
19387
|
const files = (index.releases[version] ?? []).filter((file3) => !file3.yanked);
|
|
19387
19388
|
const wheel = pickWheel(files);
|
|
19388
19389
|
if (wheel) {
|
|
19389
|
-
|
|
19390
|
+
resolved.push({
|
|
19390
19391
|
name: requirement.name,
|
|
19391
19392
|
version,
|
|
19392
19393
|
url: wheel.url,
|
|
19393
19394
|
filename: wheel.filename,
|
|
19394
19395
|
sha256: wheel.digests?.sha256 ?? "",
|
|
19395
19396
|
requires: []
|
|
19396
|
-
};
|
|
19397
|
+
});
|
|
19398
|
+
continue;
|
|
19397
19399
|
}
|
|
19398
19400
|
if (files.some((file3) => file3.packagetype === "sdist")) sawSourceOnly = true;
|
|
19399
19401
|
}
|
|
19402
|
+
if (resolved.length > 0) return resolved;
|
|
19400
19403
|
if (ordered.length === 0) {
|
|
19401
19404
|
throw new Error(
|
|
19402
19405
|
`no version of ${requirement.name} matches ${describe(requirement)}`
|
|
@@ -19424,6 +19427,7 @@ function splitOnce(text2, separator) {
|
|
|
19424
19427
|
|
|
19425
19428
|
// src/runtime/python/install.ts
|
|
19426
19429
|
var SITE_PACKAGES = "/usr/lib/python3.13/site-packages";
|
|
19430
|
+
var SCRIPTS = "/usr/local/bin";
|
|
19427
19431
|
function markerEnvironment(pythonVersion) {
|
|
19428
19432
|
const [major = "3", minor = "13"] = pythonVersion.split(".");
|
|
19429
19433
|
return {
|
|
@@ -19444,18 +19448,33 @@ async function installRequirements(options) {
|
|
|
19444
19448
|
const requirement = parseRequirement(text2);
|
|
19445
19449
|
if (requirement) queue.push({ requirement, extras: requirement.extras });
|
|
19446
19450
|
}
|
|
19447
|
-
const
|
|
19448
|
-
const
|
|
19449
|
-
|
|
19450
|
-
|
|
19451
|
+
const archives = /* @__PURE__ */ new Map();
|
|
19452
|
+
const unavailable2 = /* @__PURE__ */ new Set();
|
|
19453
|
+
const candidateCache = /* @__PURE__ */ new Map();
|
|
19454
|
+
const solve = async (pending, state) => {
|
|
19455
|
+
if (pending.length === 0) return state;
|
|
19456
|
+
const [first, ...tail2] = pending;
|
|
19457
|
+
let { requirement, extras } = first;
|
|
19458
|
+
const rest = [];
|
|
19459
|
+
const mergedSpecifiers = [...requirement.specifiers];
|
|
19460
|
+
const mergedExtras = new Set(extras);
|
|
19461
|
+
for (const item of tail2) {
|
|
19462
|
+
if (item.requirement.name === requirement.name && markerApplies(item.requirement.marker, environment, item.extras)) {
|
|
19463
|
+
mergedSpecifiers.push(...item.requirement.specifiers);
|
|
19464
|
+
for (const extra of item.extras) mergedExtras.add(extra);
|
|
19465
|
+
} else {
|
|
19466
|
+
rest.push(item);
|
|
19467
|
+
}
|
|
19468
|
+
}
|
|
19469
|
+
requirement = { ...requirement, specifiers: mergedSpecifiers };
|
|
19470
|
+
extras = [...mergedExtras];
|
|
19451
19471
|
if (!markerApplies(requirement.marker, environment, extras)) {
|
|
19452
|
-
|
|
19453
|
-
|
|
19472
|
+
return solve(rest, {
|
|
19473
|
+
...state,
|
|
19474
|
+
skipped: [...state.skipped, { name: requirement.name, marker: requirement.marker ?? "" }]
|
|
19475
|
+
});
|
|
19454
19476
|
}
|
|
19455
|
-
const
|
|
19456
|
-
if (seen.has(key)) continue;
|
|
19457
|
-
seen.add(key);
|
|
19458
|
-
const already = chosen.get(requirement.name);
|
|
19477
|
+
const already = state.chosen.get(requirement.name);
|
|
19459
19478
|
if (already) {
|
|
19460
19479
|
const satisfied = requirement.specifiers.every(
|
|
19461
19480
|
(s) => compareVersions(already.version, s.version, s.op)
|
|
@@ -19465,38 +19484,143 @@ async function installRequirements(options) {
|
|
|
19465
19484
|
`${requirement.name} ${already.version} is already selected but another dependency needs ` + requirement.specifiers.map((s) => `${s.op}${s.version}`).join(", ")
|
|
19466
19485
|
);
|
|
19467
19486
|
}
|
|
19468
|
-
|
|
19487
|
+
return solve(rest, state);
|
|
19469
19488
|
}
|
|
19470
19489
|
options.progress.collecting(requirement.name);
|
|
19471
|
-
|
|
19472
|
-
|
|
19473
|
-
|
|
19474
|
-
const
|
|
19475
|
-
|
|
19476
|
-
|
|
19477
|
-
|
|
19478
|
-
|
|
19479
|
-
|
|
19480
|
-
|
|
19481
|
-
|
|
19482
|
-
|
|
19483
|
-
}
|
|
19484
|
-
|
|
19485
|
-
|
|
19486
|
-
|
|
19490
|
+
if (unavailable2.has(requirement.name)) {
|
|
19491
|
+
throw new Error(`${requirement.name} has no wheel this runtime can use`);
|
|
19492
|
+
}
|
|
19493
|
+
const candidateKey = `${requirement.name}:${requirement.specifiers.map((s) => `${s.op}${s.version}`).join(",")}`;
|
|
19494
|
+
let candidatePromise = candidateCache.get(candidateKey);
|
|
19495
|
+
if (!candidatePromise) {
|
|
19496
|
+
candidatePromise = resolvePackageCandidates(options.client, requirement);
|
|
19497
|
+
candidateCache.set(candidateKey, candidatePromise);
|
|
19498
|
+
}
|
|
19499
|
+
let candidates;
|
|
19500
|
+
try {
|
|
19501
|
+
candidates = await candidatePromise;
|
|
19502
|
+
} catch (error) {
|
|
19503
|
+
const anyKey = `${requirement.name}:`;
|
|
19504
|
+
let anyPromise = candidateCache.get(anyKey);
|
|
19505
|
+
if (!anyPromise) {
|
|
19506
|
+
anyPromise = resolvePackageCandidates(
|
|
19507
|
+
options.client,
|
|
19508
|
+
{ ...requirement, specifiers: [] });
|
|
19509
|
+
candidateCache.set(anyKey, anyPromise);
|
|
19510
|
+
}
|
|
19511
|
+
try {
|
|
19512
|
+
await anyPromise;
|
|
19513
|
+
} catch {
|
|
19514
|
+
unavailable2.add(requirement.name);
|
|
19515
|
+
}
|
|
19516
|
+
throw error;
|
|
19517
|
+
}
|
|
19518
|
+
let failure2;
|
|
19519
|
+
for (const resolved of candidates) {
|
|
19520
|
+
try {
|
|
19521
|
+
let entries = archives.get(resolved.url);
|
|
19522
|
+
if (!entries) {
|
|
19523
|
+
options.progress.downloading(resolved.name, resolved.version);
|
|
19524
|
+
entries = readZip(await options.client.bytes(resolved.url));
|
|
19525
|
+
archives.set(resolved.url, entries);
|
|
19526
|
+
}
|
|
19527
|
+
const dependencies = [];
|
|
19528
|
+
const metadata = entries.find((entry) => /\.dist-info\/METADATA$/.test(entry.name));
|
|
19529
|
+
if (metadata) {
|
|
19530
|
+
for (const line of new TextDecoder().decode(metadata.data()).split(/\r?\n/)) {
|
|
19531
|
+
if (!line.startsWith("Requires-Dist:")) continue;
|
|
19532
|
+
const dependency = parseRequirement(line.slice("Requires-Dist:".length));
|
|
19533
|
+
if (dependency && markerApplies(dependency.marker, environment, extras)) {
|
|
19534
|
+
dependencies.push({ requirement: dependency, extras: dependency.extras });
|
|
19535
|
+
}
|
|
19536
|
+
}
|
|
19537
|
+
}
|
|
19538
|
+
const chosen = new Map(state.chosen);
|
|
19539
|
+
chosen.set(requirement.name, resolved);
|
|
19540
|
+
return await solve([...dependencies, ...rest], {
|
|
19541
|
+
chosen,
|
|
19542
|
+
staged: [...state.staged, ...stageWheel(entries, resolved)],
|
|
19543
|
+
installed: [...state.installed, { name: resolved.name, version: resolved.version }],
|
|
19544
|
+
skipped: state.skipped
|
|
19545
|
+
});
|
|
19546
|
+
} catch (error) {
|
|
19547
|
+
failure2 = error;
|
|
19548
|
+
}
|
|
19549
|
+
}
|
|
19550
|
+
throw failure2 instanceof Error ? failure2 : new Error(`could not resolve ${requirement.name}`);
|
|
19551
|
+
};
|
|
19552
|
+
const solved = await solve(queue, {
|
|
19553
|
+
chosen: /* @__PURE__ */ new Map(),
|
|
19554
|
+
staged: [],
|
|
19555
|
+
installed: [],
|
|
19556
|
+
skipped: []
|
|
19557
|
+
});
|
|
19558
|
+
report.installed = solved.installed;
|
|
19559
|
+
report.skipped = solved.skipped;
|
|
19560
|
+
commit(options.vfs, options.cred, solved.staged);
|
|
19487
19561
|
return report;
|
|
19488
19562
|
}
|
|
19489
|
-
function
|
|
19563
|
+
function stageWheel(entries, resolved) {
|
|
19490
19564
|
const staged = [];
|
|
19491
19565
|
for (const entry of entries) {
|
|
19492
19566
|
if (entry.isDirectory) continue;
|
|
19567
|
+
const dataPath = /^[^/]+\.data\/(purelib|platlib|scripts)\/(.+)$/.exec(entry.name);
|
|
19568
|
+
if (dataPath) {
|
|
19569
|
+
const [, scheme, relative2] = dataPath;
|
|
19570
|
+
staged.push({
|
|
19571
|
+
path: scheme === "scripts" ? `${SCRIPTS}/${relative2}` : `${SITE_PACKAGES}/${relative2}`,
|
|
19572
|
+
data: entry.data(),
|
|
19573
|
+
...scheme === "scripts" ? { mode: 493 } : {}
|
|
19574
|
+
});
|
|
19575
|
+
continue;
|
|
19576
|
+
}
|
|
19493
19577
|
if (/^[^/]+\.data\//.test(entry.name)) continue;
|
|
19494
19578
|
staged.push({ path: `${SITE_PACKAGES}/${entry.name}`, data: entry.data() });
|
|
19495
19579
|
}
|
|
19580
|
+
const entryPoints = entries.find((entry) => /\.dist-info\/entry_points\.txt$/.test(entry.name));
|
|
19581
|
+
if (entryPoints) {
|
|
19582
|
+
for (const [name, target] of consoleScripts(new TextDecoder().decode(entryPoints.data()))) {
|
|
19583
|
+
staged.push({ path: `${SCRIPTS}/${name}`, data: new TextEncoder().encode(consoleLauncher(target)), mode: 493 });
|
|
19584
|
+
}
|
|
19585
|
+
}
|
|
19586
|
+
return staged;
|
|
19587
|
+
}
|
|
19588
|
+
function commit(vfs, cred, staged) {
|
|
19496
19589
|
const directories = /* @__PURE__ */ new Set();
|
|
19497
19590
|
for (const file3 of staged) directories.add(file3.path.slice(0, file3.path.lastIndexOf("/")));
|
|
19498
19591
|
for (const directory2 of directories) vfs.mkdir(directory2, { recursive: true, cred });
|
|
19499
|
-
for (const file3 of staged)
|
|
19592
|
+
for (const file3 of staged) {
|
|
19593
|
+
vfs.writeFile(file3.path, file3.data, { cred, ...file3.mode === void 0 ? {} : { mode: file3.mode } });
|
|
19594
|
+
if (file3.mode !== void 0) vfs.chmod(file3.path, file3.mode, cred);
|
|
19595
|
+
}
|
|
19596
|
+
}
|
|
19597
|
+
function consoleScripts(text2) {
|
|
19598
|
+
const result = [];
|
|
19599
|
+
let section = "";
|
|
19600
|
+
for (const raw of text2.split(/\r?\n/)) {
|
|
19601
|
+
const line = raw.trim();
|
|
19602
|
+
const heading = /^\[([^\]]+)\]$/.exec(line);
|
|
19603
|
+
if (heading) {
|
|
19604
|
+
section = heading[1];
|
|
19605
|
+
continue;
|
|
19606
|
+
}
|
|
19607
|
+
if (section !== "console_scripts" || line === "" || line.startsWith("#")) continue;
|
|
19608
|
+
const assignment = /^([^=\s]+)\s*=\s*([^\s;]+)(?:\s*;.*)?$/.exec(line);
|
|
19609
|
+
if (assignment) result.push([assignment[1], assignment[2].replace(/\s*\[.*\]$/, "")]);
|
|
19610
|
+
}
|
|
19611
|
+
return result;
|
|
19612
|
+
}
|
|
19613
|
+
function consoleLauncher(target) {
|
|
19614
|
+
const [module, attribute = ""] = target.split(":", 2);
|
|
19615
|
+
return `#!/usr/bin/python3
|
|
19616
|
+
import sys
|
|
19617
|
+
from importlib import import_module
|
|
19618
|
+
entry = import_module(${JSON.stringify(module)})
|
|
19619
|
+
for part in ${JSON.stringify(attribute)}.split('.'):
|
|
19620
|
+
if part:
|
|
19621
|
+
entry = getattr(entry, part)
|
|
19622
|
+
raise SystemExit(entry())
|
|
19623
|
+
`;
|
|
19500
19624
|
}
|
|
19501
19625
|
|
|
19502
19626
|
// src/runtime/python/backend.ts
|
|
@@ -19504,7 +19628,11 @@ function withSitePackages(env2) {
|
|
|
19504
19628
|
const existing = env2.PYTHONPATH;
|
|
19505
19629
|
return {
|
|
19506
19630
|
...env2,
|
|
19507
|
-
PYTHONPATH: existing ? `${SITE_PACKAGES}:${existing}` : SITE_PACKAGES
|
|
19631
|
+
PYTHONPATH: existing ? `${SITE_PACKAGES}:${existing}` : SITE_PACKAGES,
|
|
19632
|
+
/* Installed packages live on a host-mounted VFS. Rewriting hundreds of
|
|
19633
|
+
* cache files on every short-lived WASM process is wasted work and can
|
|
19634
|
+
* exhaust the synchronous host-call bridge during large import graphs. */
|
|
19635
|
+
PYTHONDONTWRITEBYTECODE: env2.PYTHONDONTWRITEBYTECODE ?? "1"
|
|
19508
19636
|
};
|
|
19509
19637
|
}
|
|
19510
19638
|
var INTERPRETER_OWNED = /* @__PURE__ */ new Set(["lib", "dev", "proc", "usr"]);
|
|
@@ -19594,6 +19722,12 @@ function containerMounts(ctx) {
|
|
|
19594
19722
|
}
|
|
19595
19723
|
} catch {
|
|
19596
19724
|
}
|
|
19725
|
+
try {
|
|
19726
|
+
if (ctx.vfs.stat(SCRIPTS, { cred: ctx.cred }).isDirectory()) {
|
|
19727
|
+
mounts.push({ guest: SCRIPTS, container: SCRIPTS });
|
|
19728
|
+
}
|
|
19729
|
+
} catch {
|
|
19730
|
+
}
|
|
19597
19731
|
return mounts;
|
|
19598
19732
|
}
|
|
19599
19733
|
|
|
@@ -19647,16 +19781,30 @@ environments. A package with no usable wheel is an error, never a silent skip.`,
|
|
|
19647
19781
|
"outbound network access is disabled for this container (enable with network: { allowOutbound: true })"
|
|
19648
19782
|
);
|
|
19649
19783
|
}
|
|
19784
|
+
const jsonCache = /* @__PURE__ */ new Map();
|
|
19785
|
+
const bytesCache = /* @__PURE__ */ new Map();
|
|
19650
19786
|
const client = {
|
|
19651
19787
|
async json(url) {
|
|
19652
|
-
|
|
19653
|
-
if (!
|
|
19654
|
-
|
|
19788
|
+
let pending = jsonCache.get(url);
|
|
19789
|
+
if (!pending) {
|
|
19790
|
+
pending = fetch(url).then((response) => {
|
|
19791
|
+
if (!response.ok) throw new Error(`${url} answered ${response.status}`);
|
|
19792
|
+
return response.json();
|
|
19793
|
+
});
|
|
19794
|
+
jsonCache.set(url, pending);
|
|
19795
|
+
}
|
|
19796
|
+
return pending;
|
|
19655
19797
|
},
|
|
19656
19798
|
async bytes(url) {
|
|
19657
|
-
|
|
19658
|
-
if (!
|
|
19659
|
-
|
|
19799
|
+
let pending = bytesCache.get(url);
|
|
19800
|
+
if (!pending) {
|
|
19801
|
+
pending = fetch(url).then(async (response) => {
|
|
19802
|
+
if (!response.ok) throw new Error(`${url} answered ${response.status}`);
|
|
19803
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
19804
|
+
});
|
|
19805
|
+
bytesCache.set(url, pending);
|
|
19806
|
+
}
|
|
19807
|
+
return pending;
|
|
19660
19808
|
}
|
|
19661
19809
|
};
|
|
19662
19810
|
try {
|