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.js
CHANGED
|
@@ -19359,27 +19359,30 @@ 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
19366
|
const ordered = (stable.length > 0 ? stable : candidates).reverse();
|
|
19367
19367
|
let sawSourceOnly = false;
|
|
19368
|
+
const resolved = [];
|
|
19368
19369
|
for (const version of ordered) {
|
|
19369
19370
|
const files = (index.releases[version] ?? []).filter((file3) => !file3.yanked);
|
|
19370
19371
|
const wheel = pickWheel(files);
|
|
19371
19372
|
if (wheel) {
|
|
19372
|
-
|
|
19373
|
+
resolved.push({
|
|
19373
19374
|
name: requirement.name,
|
|
19374
19375
|
version,
|
|
19375
19376
|
url: wheel.url,
|
|
19376
19377
|
filename: wheel.filename,
|
|
19377
19378
|
sha256: wheel.digests?.sha256 ?? "",
|
|
19378
19379
|
requires: []
|
|
19379
|
-
};
|
|
19380
|
+
});
|
|
19381
|
+
continue;
|
|
19380
19382
|
}
|
|
19381
19383
|
if (files.some((file3) => file3.packagetype === "sdist")) sawSourceOnly = true;
|
|
19382
19384
|
}
|
|
19385
|
+
if (resolved.length > 0) return resolved;
|
|
19383
19386
|
if (ordered.length === 0) {
|
|
19384
19387
|
throw new Error(
|
|
19385
19388
|
`no version of ${requirement.name} matches ${describe(requirement)}`
|
|
@@ -19407,6 +19410,7 @@ function splitOnce(text2, separator) {
|
|
|
19407
19410
|
|
|
19408
19411
|
// src/runtime/python/install.ts
|
|
19409
19412
|
var SITE_PACKAGES = "/usr/lib/python3.13/site-packages";
|
|
19413
|
+
var SCRIPTS = "/usr/local/bin";
|
|
19410
19414
|
function markerEnvironment(pythonVersion) {
|
|
19411
19415
|
const [major = "3", minor = "13"] = pythonVersion.split(".");
|
|
19412
19416
|
return {
|
|
@@ -19427,18 +19431,33 @@ async function installRequirements(options) {
|
|
|
19427
19431
|
const requirement = parseRequirement(text2);
|
|
19428
19432
|
if (requirement) queue.push({ requirement, extras: requirement.extras });
|
|
19429
19433
|
}
|
|
19430
|
-
const
|
|
19431
|
-
const
|
|
19432
|
-
|
|
19433
|
-
|
|
19434
|
+
const archives = /* @__PURE__ */ new Map();
|
|
19435
|
+
const unavailable2 = /* @__PURE__ */ new Set();
|
|
19436
|
+
const candidateCache = /* @__PURE__ */ new Map();
|
|
19437
|
+
const solve = async (pending, state) => {
|
|
19438
|
+
if (pending.length === 0) return state;
|
|
19439
|
+
const [first, ...tail2] = pending;
|
|
19440
|
+
let { requirement, extras } = first;
|
|
19441
|
+
const rest = [];
|
|
19442
|
+
const mergedSpecifiers = [...requirement.specifiers];
|
|
19443
|
+
const mergedExtras = new Set(extras);
|
|
19444
|
+
for (const item of tail2) {
|
|
19445
|
+
if (item.requirement.name === requirement.name && markerApplies(item.requirement.marker, environment, item.extras)) {
|
|
19446
|
+
mergedSpecifiers.push(...item.requirement.specifiers);
|
|
19447
|
+
for (const extra of item.extras) mergedExtras.add(extra);
|
|
19448
|
+
} else {
|
|
19449
|
+
rest.push(item);
|
|
19450
|
+
}
|
|
19451
|
+
}
|
|
19452
|
+
requirement = { ...requirement, specifiers: mergedSpecifiers };
|
|
19453
|
+
extras = [...mergedExtras];
|
|
19434
19454
|
if (!markerApplies(requirement.marker, environment, extras)) {
|
|
19435
|
-
|
|
19436
|
-
|
|
19455
|
+
return solve(rest, {
|
|
19456
|
+
...state,
|
|
19457
|
+
skipped: [...state.skipped, { name: requirement.name, marker: requirement.marker ?? "" }]
|
|
19458
|
+
});
|
|
19437
19459
|
}
|
|
19438
|
-
const
|
|
19439
|
-
if (seen.has(key)) continue;
|
|
19440
|
-
seen.add(key);
|
|
19441
|
-
const already = chosen.get(requirement.name);
|
|
19460
|
+
const already = state.chosen.get(requirement.name);
|
|
19442
19461
|
if (already) {
|
|
19443
19462
|
const satisfied = requirement.specifiers.every(
|
|
19444
19463
|
(s) => compareVersions(already.version, s.version, s.op)
|
|
@@ -19448,38 +19467,143 @@ async function installRequirements(options) {
|
|
|
19448
19467
|
`${requirement.name} ${already.version} is already selected but another dependency needs ` + requirement.specifiers.map((s) => `${s.op}${s.version}`).join(", ")
|
|
19449
19468
|
);
|
|
19450
19469
|
}
|
|
19451
|
-
|
|
19470
|
+
return solve(rest, state);
|
|
19452
19471
|
}
|
|
19453
19472
|
options.progress.collecting(requirement.name);
|
|
19454
|
-
|
|
19455
|
-
|
|
19456
|
-
|
|
19457
|
-
const
|
|
19458
|
-
|
|
19459
|
-
|
|
19460
|
-
|
|
19461
|
-
|
|
19462
|
-
|
|
19463
|
-
|
|
19464
|
-
|
|
19465
|
-
|
|
19466
|
-
}
|
|
19467
|
-
|
|
19468
|
-
|
|
19469
|
-
|
|
19473
|
+
if (unavailable2.has(requirement.name)) {
|
|
19474
|
+
throw new Error(`${requirement.name} has no wheel this runtime can use`);
|
|
19475
|
+
}
|
|
19476
|
+
const candidateKey = `${requirement.name}:${requirement.specifiers.map((s) => `${s.op}${s.version}`).join(",")}`;
|
|
19477
|
+
let candidatePromise = candidateCache.get(candidateKey);
|
|
19478
|
+
if (!candidatePromise) {
|
|
19479
|
+
candidatePromise = resolvePackageCandidates(options.client, requirement);
|
|
19480
|
+
candidateCache.set(candidateKey, candidatePromise);
|
|
19481
|
+
}
|
|
19482
|
+
let candidates;
|
|
19483
|
+
try {
|
|
19484
|
+
candidates = await candidatePromise;
|
|
19485
|
+
} catch (error) {
|
|
19486
|
+
const anyKey = `${requirement.name}:`;
|
|
19487
|
+
let anyPromise = candidateCache.get(anyKey);
|
|
19488
|
+
if (!anyPromise) {
|
|
19489
|
+
anyPromise = resolvePackageCandidates(
|
|
19490
|
+
options.client,
|
|
19491
|
+
{ ...requirement, specifiers: [] });
|
|
19492
|
+
candidateCache.set(anyKey, anyPromise);
|
|
19493
|
+
}
|
|
19494
|
+
try {
|
|
19495
|
+
await anyPromise;
|
|
19496
|
+
} catch {
|
|
19497
|
+
unavailable2.add(requirement.name);
|
|
19498
|
+
}
|
|
19499
|
+
throw error;
|
|
19500
|
+
}
|
|
19501
|
+
let failure2;
|
|
19502
|
+
for (const resolved of candidates) {
|
|
19503
|
+
try {
|
|
19504
|
+
let entries = archives.get(resolved.url);
|
|
19505
|
+
if (!entries) {
|
|
19506
|
+
options.progress.downloading(resolved.name, resolved.version);
|
|
19507
|
+
entries = readZip(await options.client.bytes(resolved.url));
|
|
19508
|
+
archives.set(resolved.url, entries);
|
|
19509
|
+
}
|
|
19510
|
+
const dependencies = [];
|
|
19511
|
+
const metadata = entries.find((entry) => /\.dist-info\/METADATA$/.test(entry.name));
|
|
19512
|
+
if (metadata) {
|
|
19513
|
+
for (const line of new TextDecoder().decode(metadata.data()).split(/\r?\n/)) {
|
|
19514
|
+
if (!line.startsWith("Requires-Dist:")) continue;
|
|
19515
|
+
const dependency = parseRequirement(line.slice("Requires-Dist:".length));
|
|
19516
|
+
if (dependency && markerApplies(dependency.marker, environment, extras)) {
|
|
19517
|
+
dependencies.push({ requirement: dependency, extras: dependency.extras });
|
|
19518
|
+
}
|
|
19519
|
+
}
|
|
19520
|
+
}
|
|
19521
|
+
const chosen = new Map(state.chosen);
|
|
19522
|
+
chosen.set(requirement.name, resolved);
|
|
19523
|
+
return await solve([...dependencies, ...rest], {
|
|
19524
|
+
chosen,
|
|
19525
|
+
staged: [...state.staged, ...stageWheel(entries, resolved)],
|
|
19526
|
+
installed: [...state.installed, { name: resolved.name, version: resolved.version }],
|
|
19527
|
+
skipped: state.skipped
|
|
19528
|
+
});
|
|
19529
|
+
} catch (error) {
|
|
19530
|
+
failure2 = error;
|
|
19531
|
+
}
|
|
19532
|
+
}
|
|
19533
|
+
throw failure2 instanceof Error ? failure2 : new Error(`could not resolve ${requirement.name}`);
|
|
19534
|
+
};
|
|
19535
|
+
const solved = await solve(queue, {
|
|
19536
|
+
chosen: /* @__PURE__ */ new Map(),
|
|
19537
|
+
staged: [],
|
|
19538
|
+
installed: [],
|
|
19539
|
+
skipped: []
|
|
19540
|
+
});
|
|
19541
|
+
report.installed = solved.installed;
|
|
19542
|
+
report.skipped = solved.skipped;
|
|
19543
|
+
commit(options.vfs, options.cred, solved.staged);
|
|
19470
19544
|
return report;
|
|
19471
19545
|
}
|
|
19472
|
-
function
|
|
19546
|
+
function stageWheel(entries, resolved) {
|
|
19473
19547
|
const staged = [];
|
|
19474
19548
|
for (const entry of entries) {
|
|
19475
19549
|
if (entry.isDirectory) continue;
|
|
19550
|
+
const dataPath = /^[^/]+\.data\/(purelib|platlib|scripts)\/(.+)$/.exec(entry.name);
|
|
19551
|
+
if (dataPath) {
|
|
19552
|
+
const [, scheme, relative2] = dataPath;
|
|
19553
|
+
staged.push({
|
|
19554
|
+
path: scheme === "scripts" ? `${SCRIPTS}/${relative2}` : `${SITE_PACKAGES}/${relative2}`,
|
|
19555
|
+
data: entry.data(),
|
|
19556
|
+
...scheme === "scripts" ? { mode: 493 } : {}
|
|
19557
|
+
});
|
|
19558
|
+
continue;
|
|
19559
|
+
}
|
|
19476
19560
|
if (/^[^/]+\.data\//.test(entry.name)) continue;
|
|
19477
19561
|
staged.push({ path: `${SITE_PACKAGES}/${entry.name}`, data: entry.data() });
|
|
19478
19562
|
}
|
|
19563
|
+
const entryPoints = entries.find((entry) => /\.dist-info\/entry_points\.txt$/.test(entry.name));
|
|
19564
|
+
if (entryPoints) {
|
|
19565
|
+
for (const [name, target] of consoleScripts(new TextDecoder().decode(entryPoints.data()))) {
|
|
19566
|
+
staged.push({ path: `${SCRIPTS}/${name}`, data: new TextEncoder().encode(consoleLauncher(target)), mode: 493 });
|
|
19567
|
+
}
|
|
19568
|
+
}
|
|
19569
|
+
return staged;
|
|
19570
|
+
}
|
|
19571
|
+
function commit(vfs, cred, staged) {
|
|
19479
19572
|
const directories = /* @__PURE__ */ new Set();
|
|
19480
19573
|
for (const file3 of staged) directories.add(file3.path.slice(0, file3.path.lastIndexOf("/")));
|
|
19481
19574
|
for (const directory2 of directories) vfs.mkdir(directory2, { recursive: true, cred });
|
|
19482
|
-
for (const file3 of staged)
|
|
19575
|
+
for (const file3 of staged) {
|
|
19576
|
+
vfs.writeFile(file3.path, file3.data, { cred, ...file3.mode === void 0 ? {} : { mode: file3.mode } });
|
|
19577
|
+
if (file3.mode !== void 0) vfs.chmod(file3.path, file3.mode, cred);
|
|
19578
|
+
}
|
|
19579
|
+
}
|
|
19580
|
+
function consoleScripts(text2) {
|
|
19581
|
+
const result = [];
|
|
19582
|
+
let section = "";
|
|
19583
|
+
for (const raw of text2.split(/\r?\n/)) {
|
|
19584
|
+
const line = raw.trim();
|
|
19585
|
+
const heading = /^\[([^\]]+)\]$/.exec(line);
|
|
19586
|
+
if (heading) {
|
|
19587
|
+
section = heading[1];
|
|
19588
|
+
continue;
|
|
19589
|
+
}
|
|
19590
|
+
if (section !== "console_scripts" || line === "" || line.startsWith("#")) continue;
|
|
19591
|
+
const assignment = /^([^=\s]+)\s*=\s*([^\s;]+)(?:\s*;.*)?$/.exec(line);
|
|
19592
|
+
if (assignment) result.push([assignment[1], assignment[2].replace(/\s*\[.*\]$/, "")]);
|
|
19593
|
+
}
|
|
19594
|
+
return result;
|
|
19595
|
+
}
|
|
19596
|
+
function consoleLauncher(target) {
|
|
19597
|
+
const [module, attribute = ""] = target.split(":", 2);
|
|
19598
|
+
return `#!/usr/bin/python3
|
|
19599
|
+
import sys
|
|
19600
|
+
from importlib import import_module
|
|
19601
|
+
entry = import_module(${JSON.stringify(module)})
|
|
19602
|
+
for part in ${JSON.stringify(attribute)}.split('.'):
|
|
19603
|
+
if part:
|
|
19604
|
+
entry = getattr(entry, part)
|
|
19605
|
+
raise SystemExit(entry())
|
|
19606
|
+
`;
|
|
19483
19607
|
}
|
|
19484
19608
|
|
|
19485
19609
|
// src/runtime/python/backend.ts
|
|
@@ -19487,7 +19611,11 @@ function withSitePackages(env2) {
|
|
|
19487
19611
|
const existing = env2.PYTHONPATH;
|
|
19488
19612
|
return {
|
|
19489
19613
|
...env2,
|
|
19490
|
-
PYTHONPATH: existing ? `${SITE_PACKAGES}:${existing}` : SITE_PACKAGES
|
|
19614
|
+
PYTHONPATH: existing ? `${SITE_PACKAGES}:${existing}` : SITE_PACKAGES,
|
|
19615
|
+
/* Installed packages live on a host-mounted VFS. Rewriting hundreds of
|
|
19616
|
+
* cache files on every short-lived WASM process is wasted work and can
|
|
19617
|
+
* exhaust the synchronous host-call bridge during large import graphs. */
|
|
19618
|
+
PYTHONDONTWRITEBYTECODE: env2.PYTHONDONTWRITEBYTECODE ?? "1"
|
|
19491
19619
|
};
|
|
19492
19620
|
}
|
|
19493
19621
|
var INTERPRETER_OWNED = /* @__PURE__ */ new Set(["lib", "dev", "proc", "usr"]);
|
|
@@ -19577,6 +19705,12 @@ function containerMounts(ctx) {
|
|
|
19577
19705
|
}
|
|
19578
19706
|
} catch {
|
|
19579
19707
|
}
|
|
19708
|
+
try {
|
|
19709
|
+
if (ctx.vfs.stat(SCRIPTS, { cred: ctx.cred }).isDirectory()) {
|
|
19710
|
+
mounts.push({ guest: SCRIPTS, container: SCRIPTS });
|
|
19711
|
+
}
|
|
19712
|
+
} catch {
|
|
19713
|
+
}
|
|
19580
19714
|
return mounts;
|
|
19581
19715
|
}
|
|
19582
19716
|
|
|
@@ -19630,16 +19764,30 @@ environments. A package with no usable wheel is an error, never a silent skip.`,
|
|
|
19630
19764
|
"outbound network access is disabled for this container (enable with network: { allowOutbound: true })"
|
|
19631
19765
|
);
|
|
19632
19766
|
}
|
|
19767
|
+
const jsonCache = /* @__PURE__ */ new Map();
|
|
19768
|
+
const bytesCache = /* @__PURE__ */ new Map();
|
|
19633
19769
|
const client = {
|
|
19634
19770
|
async json(url) {
|
|
19635
|
-
|
|
19636
|
-
if (!
|
|
19637
|
-
|
|
19771
|
+
let pending = jsonCache.get(url);
|
|
19772
|
+
if (!pending) {
|
|
19773
|
+
pending = fetch(url).then((response) => {
|
|
19774
|
+
if (!response.ok) throw new Error(`${url} answered ${response.status}`);
|
|
19775
|
+
return response.json();
|
|
19776
|
+
});
|
|
19777
|
+
jsonCache.set(url, pending);
|
|
19778
|
+
}
|
|
19779
|
+
return pending;
|
|
19638
19780
|
},
|
|
19639
19781
|
async bytes(url) {
|
|
19640
|
-
|
|
19641
|
-
if (!
|
|
19642
|
-
|
|
19782
|
+
let pending = bytesCache.get(url);
|
|
19783
|
+
if (!pending) {
|
|
19784
|
+
pending = fetch(url).then(async (response) => {
|
|
19785
|
+
if (!response.ok) throw new Error(`${url} answered ${response.status}`);
|
|
19786
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
19787
|
+
});
|
|
19788
|
+
bytesCache.set(url, pending);
|
|
19789
|
+
}
|
|
19790
|
+
return pending;
|
|
19643
19791
|
}
|
|
19644
19792
|
};
|
|
19645
19793
|
try {
|