@nimbus-sh/worker 0.8.0 → 0.10.0
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/esbuild-wasm-bundle.generated.d.ts +24 -24
- package/dist/esbuild-wasm-bundle.generated.d.ts.map +1 -1
- package/dist/esbuild-wasm-bundle.generated.js +24 -24
- package/dist/facets/cirrus-real.js +3 -3
- package/dist/facets/compose.d.ts +3 -0
- package/dist/facets/compose.d.ts.map +1 -1
- package/dist/facets/compose.js +10 -3
- package/dist/facets/esbuild-bundle-pool.d.ts +1 -1
- package/dist/facets/esbuild-bundle-pool.js +3 -3
- package/dist/facets/esbuild-transform.d.ts +6 -2
- package/dist/facets/esbuild-transform.d.ts.map +1 -1
- package/dist/facets/esbuild-transform.js +8 -4
- package/dist/facets/manager.d.ts +54 -15
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +227 -144
- package/dist/facets/real-vite-hmr.d.ts +2 -0
- package/dist/facets/real-vite-hmr.d.ts.map +1 -1
- package/dist/facets/real-vite-hmr.js +3 -2
- package/dist/git/commands.d.ts +17 -1
- package/dist/git/commands.d.ts.map +1 -1
- package/dist/git/commands.js +73 -9
- package/dist/git/network-facet.d.ts +2 -0
- package/dist/git/network-facet.d.ts.map +1 -1
- package/dist/git/network-facet.js +10 -5
- package/dist/hosted/commands.d.ts.map +1 -1
- package/dist/hosted/commands.js +28 -39
- package/dist/hosted/runtime.d.ts +24 -2
- package/dist/hosted/runtime.d.ts.map +1 -1
- package/dist/hosted/runtime.js +70 -13
- package/dist/hosted/services.d.ts +14 -2
- package/dist/hosted/services.d.ts.map +1 -1
- package/dist/hosted/services.js +15 -2
- package/dist/loaders/generated-workers.d.ts +2 -2
- package/dist/loaders/generated-workers.d.ts.map +1 -1
- package/dist/loaders/generated-workers.js +4 -4
- package/dist/loaders/pre-bundle-preamble.d.ts +6 -3
- package/dist/loaders/pre-bundle-preamble.d.ts.map +1 -1
- package/dist/loaders/pre-bundle-preamble.js +13 -8
- package/dist/node-shims-artifact.generated.js +3 -3
- package/dist/npm/cache.d.ts +6 -1
- package/dist/npm/cache.d.ts.map +1 -1
- package/dist/npm/cache.js +8 -6
- package/dist/npm/install-batch-facet.d.ts +2 -0
- package/dist/npm/install-batch-facet.d.ts.map +1 -1
- package/dist/npm/install-batch-facet.js +29 -15
- package/dist/npm/installer.d.ts +8 -4
- package/dist/npm/installer.d.ts.map +1 -1
- package/dist/npm/installer.js +173 -113
- package/dist/npm/npx-install.d.ts +3 -1
- package/dist/npm/npx-install.d.ts.map +1 -1
- package/dist/npm/npx-install.js +4 -1
- package/dist/npm/placement.d.ts +16 -0
- package/dist/npm/placement.d.ts.map +1 -0
- package/dist/npm/placement.js +33 -0
- package/dist/npm/r2-cache.d.ts +10 -6
- package/dist/npm/r2-cache.d.ts.map +1 -1
- package/dist/npm/r2-cache.js +32 -16
- package/dist/npm/resolve-one-facet.d.ts +3 -0
- package/dist/npm/resolve-one-facet.d.ts.map +1 -1
- package/dist/npm/resolve-one-facet.js +1 -0
- package/dist/npm/resolver.d.ts +11 -6
- package/dist/npm/resolver.d.ts.map +1 -1
- package/dist/npm/resolver.js +12 -3
- package/dist/runtime/bash-repl.d.ts +5 -3
- package/dist/runtime/bash-repl.d.ts.map +1 -1
- package/dist/runtime/bash-repl.js +8 -3
- package/dist/runtime/cpython-resident.d.ts.map +1 -1
- package/dist/runtime/cpython-resident.js +2 -10
- package/dist/runtime/esbuild-wasm-bytes.d.ts +25 -18
- package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -1
- package/dist/runtime/esbuild-wasm-bytes.js +55 -30
- package/dist/runtime/facet-loader-host.d.ts.map +1 -1
- package/dist/runtime/facet-loader-host.js +0 -9
- package/dist/runtime/node-shims.d.ts.map +1 -1
- package/dist/runtime/node-shims.js +853 -181
- package/dist/runtime/python-repl.d.ts +4 -3
- package/dist/runtime/python-repl.d.ts.map +1 -1
- package/dist/runtime/python-repl.js +10 -39
- package/dist/runtime/ruby-repl.d.ts +3 -2
- package/dist/runtime/ruby-repl.d.ts.map +1 -1
- package/dist/runtime/ruby-repl.js +7 -6
- package/dist/runtime/ruby-resident.d.ts.map +1 -1
- package/dist/runtime/ruby-resident.js +5 -16
- package/dist/runtime/runtime-catalog.js +1 -1
- package/dist/runtime/wasi/preamble.d.ts +0 -22
- package/dist/runtime/wasi/preamble.d.ts.map +1 -1
- package/dist/runtime/wasi/preamble.js +160 -1445
- package/dist/runtime-catalog.generated.js +1 -1
- package/dist/session/hibernation.d.ts +45 -2
- package/dist/session/hibernation.d.ts.map +1 -1
- package/dist/session/hibernation.js +78 -1
- package/dist/session/init.d.ts.map +1 -1
- package/dist/session/init.js +1 -0
- package/dist/session/nimbus-session.d.ts +16 -23
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +25 -36
- package/dist/session/npm-install-port.d.ts.map +1 -1
- package/dist/session/npm-install-port.js +1 -0
- package/dist/session/port-capability.d.ts.map +1 -1
- package/dist/session/port-capability.js +11 -1
- package/dist/session/programmatic.js +2 -2
- package/dist/session/routes.d.ts.map +1 -1
- package/dist/session/routes.js +4 -2
- package/dist/session/rpc.d.ts +7 -44
- package/dist/session/rpc.d.ts.map +1 -1
- package/dist/session/rpc.js +17 -67
- package/dist/session/supervisor-op.d.ts +8 -3
- package/dist/session/supervisor-op.d.ts.map +1 -1
- package/dist/session/supervisor-op.js +10 -45
- package/dist/session/supervisor-rpc.d.ts +47 -22
- package/dist/session/supervisor-rpc.d.ts.map +1 -1
- package/dist/session/supervisor-rpc.js +64 -26
- package/dist/vfs/facet-resident-store.d.ts.map +1 -1
- package/dist/vfs/facet-resident-store.js +4 -2
- package/dist/workspace-host.d.ts +2 -1
- package/dist/workspace-host.d.ts.map +1 -1
- package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -1
- package/dist/wrangler/nimbus-wrangler.js +3 -0
- package/package.json +30 -4
- package/public/_assets/esbuild-0.24.2.js +2380 -0
- package/public/_assets/runtime/{node-shims-8f8c64edd2a1f0f5.js → node-shims-7b4dd7a76d95d21e.js} +852 -180
- package/scripts/bundle-esbuild-wasm.mjs +55 -39
- package/scripts/bundle-facet-workers.mjs +45 -7
- package/scripts/bundle-runtime.mjs +35 -8
package/dist/facets/manager.js
CHANGED
|
@@ -21,6 +21,7 @@ import { getRealNodeImportsCode } from '@nimbus-sh/core/_shared/real-node-import
|
|
|
21
21
|
import { FACET_RESIDENT_STORE_SOURCE } from '../vfs/facet-resident-store.js';
|
|
22
22
|
import { VFS_CURSOR_SEED_SOURCE, serializeFacetVfsCursor, } from '@nimbus-sh/core/_shared/facet-vfs-cursor.js';
|
|
23
23
|
import { VFS_WRITE_LEDGER_SOURCE } from '@nimbus-sh/core/_shared/vfs-write-ledger.js';
|
|
24
|
+
import { ExecutionFs } from '@nimbus-sh/core/shell/execution-fs.js';
|
|
24
25
|
import { stripLeadingSlashes, vfsPathExtension } from '@nimbus-sh/core/vfs/path.js';
|
|
25
26
|
import { clearPortCapability, listPortReservations, readPortReservation, readPortReservationByOwner, releasePortReservation, restoreReservedPortCapability, } from '../session/port-capability.js';
|
|
26
27
|
import { deriveResidentOwner } from './resident-identity.js';
|
|
@@ -48,12 +49,12 @@ import { persistDurableWorkerImage, purgeDurableWorkerImages, } from './durable-
|
|
|
48
49
|
import { SQLITE_WASM_MODULE_NAME, } from '../runtime/opencode-facet-runner.js';
|
|
49
50
|
import { parsePortFromArgv, resolveLongRunningPort } from '@nimbus-sh/core/runtime/long-running-handle.js';
|
|
50
51
|
import { DEFAULT_FACET_BUNDLE_PROFILE, } from '@nimbus-sh/core/runtime/bundle-profile.js';
|
|
51
|
-
import { CF_COMPAT_DATE, VFS_BUNDLE_MAX_FILES, VFS_BUNDLE_MAX_BYTES, CWD_SNAPSHOT_MAX_FILE_BYTES, BUNDLE_MAX_ENCODED_BYTES, PREFETCH_CACHE_MAX_BYTES, } from '@nimbus-sh/core/constants.js';
|
|
52
|
+
import { CF_COMPAT_DATE, VFS_BUNDLE_MAX_FILES, VFS_BUNDLE_MAX_BYTES, CWD_SNAPSHOT_MAX_FILE_BYTES, BUNDLE_MAX_ENCODED_BYTES, PREFETCH_CACHE_MAX_BYTES, ESM_TRANSFORM_CACHE_MAX_BYTES, } from '@nimbus-sh/core/constants.js';
|
|
52
53
|
import { MAX_RPC_SAFE_PAYLOAD_BYTES } from '@nimbus-sh/platform/limits.js';
|
|
53
54
|
import { CRED_KERNEL } from '@nimbus-sh/core/runtime/os-contracts.js';
|
|
54
55
|
import { acquireSupervisorAllocation } from '@nimbus-sh/platform/heavy-alloc-coord.js';
|
|
55
56
|
import { wasmImageDigest } from './wasm-image-digest.js';
|
|
56
|
-
import { prefetchBundleStart, prefetchBundleEnd, setPrefetchCacheBytes, } from '@nimbus-sh/platform/diag-counters.js';
|
|
57
|
+
import { prefetchBundleStart, prefetchBundleEnd, setPrefetchCacheBytes, setTransformCacheBytes, } from '@nimbus-sh/platform/diag-counters.js';
|
|
57
58
|
const ISOLATED_ESM_TRANSFORM_MIN_BYTES = 512 * 1024;
|
|
58
59
|
/**
|
|
59
60
|
* Detect & restore a Uint8Array that's been JSON-mangled to a
|
|
@@ -383,25 +384,7 @@ ${facetWasmImportsSource(wasmImports)}
|
|
|
383
384
|
const USER_CODE = ${safeCode};
|
|
384
385
|
const __NimbusHostResponse = globalThis.Response;
|
|
385
386
|
|
|
386
|
-
|
|
387
|
-
// Node strips a leading shebang from every module before evaluation;
|
|
388
|
-
// bin scripts are commonly bundled verbatim with their
|
|
389
|
-
// "#!/usr/bin/env node" line, which is a SyntaxError under new Function.
|
|
390
|
-
if (typeof code === "string" && code.charCodeAt(0) === 35 && code.charCodeAt(1) === 33) {
|
|
391
|
-
const __nl = code.indexOf("\\n");
|
|
392
|
-
code = __nl >= 0 ? code.slice(__nl + 1) : "";
|
|
393
|
-
}
|
|
394
|
-
function renameIfDeclared(name) {
|
|
395
|
-
const re = new RegExp("(?:^|\\\\n|;)\\\\s*(?:const|let|var|function|class)\\\\s+" + name + "(?![$\\\\w])", "m");
|
|
396
|
-
return re.test(code) ? name + "__nimbus_unused" : name;
|
|
397
|
-
}
|
|
398
|
-
const baseParams = [
|
|
399
|
-
"exports", renameIfDeclared("require"), "module",
|
|
400
|
-
renameIfDeclared("__filename"),
|
|
401
|
-
renameIfDeclared("__dirname"),
|
|
402
|
-
];
|
|
403
|
-
return new Function(...baseParams, code);
|
|
404
|
-
}
|
|
387
|
+
${FACET_COMPILE_HELPER}
|
|
405
388
|
|
|
406
389
|
let __compiledFn = null;
|
|
407
390
|
let __entryCompileFailure = null;
|
|
@@ -698,18 +681,7 @@ const USER_CODE = ${safeCode};
|
|
|
698
681
|
const __NIMBUS_ARGS = ${safeArgs};
|
|
699
682
|
const __NimbusHostResponse = globalThis.Response;
|
|
700
683
|
|
|
701
|
-
|
|
702
|
-
function renameIfDeclared(name) {
|
|
703
|
-
const re = new RegExp("(?:^|\\\\n|;)\\\\s*(?:const|let|var|function|class)\\\\s+" + name + "(?![$\\\\w])", "m");
|
|
704
|
-
return re.test(code) ? name + "__nimbus_unused" : name;
|
|
705
|
-
}
|
|
706
|
-
const baseParams = [
|
|
707
|
-
"exports", renameIfDeclared("require"), "module",
|
|
708
|
-
renameIfDeclared("__filename"),
|
|
709
|
-
renameIfDeclared("__dirname"),
|
|
710
|
-
];
|
|
711
|
-
return new Function(...baseParams, code);
|
|
712
|
-
}
|
|
684
|
+
${FACET_COMPILE_HELPER}
|
|
713
685
|
|
|
714
686
|
let __compiledFn = null;
|
|
715
687
|
let __entryCompileFailure = null;
|
|
@@ -1148,8 +1120,8 @@ async function facetVfsBundleSourceFor(vfsState, pacer) {
|
|
|
1148
1120
|
* Throws on read errors (caller wraps in try/catch — matches the
|
|
1149
1121
|
* pre-fix readFileString contract).
|
|
1150
1122
|
*/
|
|
1151
|
-
function _readBundleCell(vfs, path) {
|
|
1152
|
-
const bytes = vfs.readFile(path);
|
|
1123
|
+
async function _readBundleCell(vfs, path) {
|
|
1124
|
+
const bytes = await vfs.readFile(path);
|
|
1153
1125
|
try {
|
|
1154
1126
|
return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
|
|
1155
1127
|
}
|
|
@@ -1176,10 +1148,10 @@ function _bundleCellLength(cell) {
|
|
|
1176
1148
|
* `perFileCeiling` is the pass's own cap (e.g. CWD_SNAPSHOT_MAX_FILE_BYTES,
|
|
1177
1149
|
* BIN_PACKAGE_SPECULATIVE_MAX_FILE_BYTES); omit it for budget-only checks.
|
|
1178
1150
|
*/
|
|
1179
|
-
function _bundleAdmits(vfs, path, budgetState, perFileCeiling) {
|
|
1151
|
+
async function _bundleAdmits(vfs, path, budgetState, perFileCeiling) {
|
|
1180
1152
|
let size;
|
|
1181
1153
|
try {
|
|
1182
|
-
size = vfs.lstat(path).size;
|
|
1154
|
+
size = (await vfs.lstat(path)).size;
|
|
1183
1155
|
}
|
|
1184
1156
|
catch {
|
|
1185
1157
|
return false;
|
|
@@ -1650,9 +1622,9 @@ const MANIFEST_MAX_DEPTH = 12;
|
|
|
1650
1622
|
* fs.readdirSync / fs.statSync honest regardless of which subset of
|
|
1651
1623
|
* file CONTENT we ship.
|
|
1652
1624
|
*/
|
|
1653
|
-
function buildManifest(vfs, cwd, scriptPath) {
|
|
1625
|
+
async function buildManifest(vfs, cwd, scriptPath) {
|
|
1654
1626
|
const manifest = {};
|
|
1655
|
-
function walk(dirPath, depth = 0) {
|
|
1627
|
+
async function walk(dirPath, depth = 0) {
|
|
1656
1628
|
if (depth > MANIFEST_MAX_DEPTH)
|
|
1657
1629
|
return;
|
|
1658
1630
|
const stripped = dirPath.replace(/^\/+/, '');
|
|
@@ -1660,7 +1632,7 @@ function buildManifest(vfs, cwd, scriptPath) {
|
|
|
1660
1632
|
return;
|
|
1661
1633
|
let entries;
|
|
1662
1634
|
try {
|
|
1663
|
-
entries = vfs.readdir(stripped);
|
|
1635
|
+
entries = (await vfs.readdir(stripped));
|
|
1664
1636
|
}
|
|
1665
1637
|
catch {
|
|
1666
1638
|
return;
|
|
@@ -1669,7 +1641,7 @@ function buildManifest(vfs, cwd, scriptPath) {
|
|
|
1669
1641
|
for (const entry of entries) {
|
|
1670
1642
|
if (entry.type === 'directory') {
|
|
1671
1643
|
const childPath = stripped ? stripped + '/' + entry.name : entry.name;
|
|
1672
|
-
walk(childPath, depth + 1);
|
|
1644
|
+
(await walk(childPath, depth + 1));
|
|
1673
1645
|
}
|
|
1674
1646
|
}
|
|
1675
1647
|
}
|
|
@@ -1690,13 +1662,13 @@ function buildManifest(vfs, cwd, scriptPath) {
|
|
|
1690
1662
|
{
|
|
1691
1663
|
const segments = cwdStripped ? cwdStripped.split('/') : [];
|
|
1692
1664
|
for (let i = 0; i < segments.length; i++) {
|
|
1693
|
-
walk(segments.slice(0, i).join('/'), MANIFEST_MAX_DEPTH);
|
|
1665
|
+
(await walk(segments.slice(0, i).join('/'), MANIFEST_MAX_DEPTH));
|
|
1694
1666
|
}
|
|
1695
1667
|
}
|
|
1696
|
-
walk(cwdStripped, 0);
|
|
1668
|
+
(await walk(cwdStripped, 0));
|
|
1697
1669
|
const nmDir = cwdStripped + '/node_modules';
|
|
1698
|
-
if (vfs.exists(nmDir) && vfs.isDirectory(nmDir)) {
|
|
1699
|
-
walk(nmDir, 0);
|
|
1670
|
+
if ((await vfs.exists(nmDir)) && (await vfs.isDirectory(nmDir))) {
|
|
1671
|
+
(await walk(nmDir, 0));
|
|
1700
1672
|
}
|
|
1701
1673
|
// ── Bin-target package + hoisted dependency roots ─────────────────────
|
|
1702
1674
|
//
|
|
@@ -1728,19 +1700,19 @@ function buildManifest(vfs, cwd, scriptPath) {
|
|
|
1728
1700
|
const pkgEnd = isScoped ? nmIdx + 3 : nmIdx + 2;
|
|
1729
1701
|
if (pkgEnd <= segs.length) {
|
|
1730
1702
|
const pkgRoot = segs.slice(0, pkgEnd).join('/');
|
|
1731
|
-
if (vfs.exists(pkgRoot) && vfs.isDirectory(pkgRoot)) {
|
|
1732
|
-
walk(pkgRoot, 0);
|
|
1703
|
+
if ((await vfs.exists(pkgRoot)) && (await vfs.isDirectory(pkgRoot))) {
|
|
1704
|
+
(await walk(pkgRoot, 0));
|
|
1733
1705
|
}
|
|
1734
1706
|
}
|
|
1735
1707
|
const nodeModulesRoot = segs.slice(0, nmIdx + 1).join('/');
|
|
1736
|
-
if (vfs.exists(nodeModulesRoot) && vfs.isDirectory(nodeModulesRoot)) {
|
|
1737
|
-
walk(nodeModulesRoot, 0);
|
|
1708
|
+
if ((await vfs.exists(nodeModulesRoot)) && (await vfs.isDirectory(nodeModulesRoot))) {
|
|
1709
|
+
(await walk(nodeModulesRoot, 0));
|
|
1738
1710
|
}
|
|
1739
1711
|
}
|
|
1740
1712
|
}
|
|
1741
1713
|
return manifest;
|
|
1742
1714
|
}
|
|
1743
|
-
function buildVfsMetadata(vfs, manifest, bundle) {
|
|
1715
|
+
async function buildVfsMetadata(vfs, manifest, bundle) {
|
|
1744
1716
|
const paths = new Set(Object.keys(bundle).filter((path) => compiledCellPath(path) === null));
|
|
1745
1717
|
for (const [directory, children] of Object.entries(manifest)) {
|
|
1746
1718
|
paths.add(directory);
|
|
@@ -1751,7 +1723,9 @@ function buildVfsMetadata(vfs, manifest, bundle) {
|
|
|
1751
1723
|
const metadata = {};
|
|
1752
1724
|
for (const path of paths) {
|
|
1753
1725
|
try {
|
|
1754
|
-
const stat = vfs.
|
|
1726
|
+
const stat = await vfs.authority.stat(path, { followSymlinks: false });
|
|
1727
|
+
if (!stat)
|
|
1728
|
+
continue;
|
|
1755
1729
|
metadata[path] = {
|
|
1756
1730
|
type: stat.type,
|
|
1757
1731
|
size: stat.size,
|
|
@@ -1767,12 +1741,12 @@ function buildVfsMetadata(vfs, manifest, bundle) {
|
|
|
1767
1741
|
}
|
|
1768
1742
|
return metadata;
|
|
1769
1743
|
}
|
|
1770
|
-
function addUnreadableDenialCells(vfs, bundle, metadata) {
|
|
1744
|
+
async function addUnreadableDenialCells(vfs, bundle, metadata) {
|
|
1771
1745
|
for (const [path, stat] of Object.entries(metadata)) {
|
|
1772
1746
|
if (stat.type === 'directory' || path in bundle)
|
|
1773
1747
|
continue;
|
|
1774
1748
|
try {
|
|
1775
|
-
vfs.access(path, 0o4);
|
|
1749
|
+
(await vfs.access(path, 0o4));
|
|
1776
1750
|
}
|
|
1777
1751
|
catch (error) {
|
|
1778
1752
|
if (typeof error === 'object'
|
|
@@ -1801,14 +1775,14 @@ function addUnreadableDenialCells(vfs, bundle, metadata) {
|
|
|
1801
1775
|
// was a file-local helper. Adding the named export is a pure surface
|
|
1802
1776
|
// addition — no callers other than buildPrefetchBundle (same file) and
|
|
1803
1777
|
// the new probe.
|
|
1804
|
-
export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPaths = new Set()) {
|
|
1778
|
+
export async function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPaths = new Set()) {
|
|
1805
1779
|
let added = 0;
|
|
1806
1780
|
const cwdStripped = cwd.replace(/^\/+/, '');
|
|
1807
1781
|
const nmDir = cwdStripped + '/node_modules';
|
|
1808
|
-
if (!(vfs.exists(nmDir) && vfs.isDirectory(nmDir)))
|
|
1782
|
+
if (!((await vfs.exists(nmDir)) && (await vfs.isDirectory(nmDir))))
|
|
1809
1783
|
return { added };
|
|
1810
1784
|
const exts = ['', '.js', '.cjs', '.mjs', '/index.js', '/index.cjs'];
|
|
1811
|
-
function addOne(path) {
|
|
1785
|
+
async function addOne(path) {
|
|
1812
1786
|
const stripped = path.replace(/^\/+/, '');
|
|
1813
1787
|
if (stripped in bundle)
|
|
1814
1788
|
return false;
|
|
@@ -1817,7 +1791,7 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
|
|
|
1817
1791
|
if (budgetState.totalBytes >= VFS_BUNDLE_MAX_BYTES)
|
|
1818
1792
|
return false;
|
|
1819
1793
|
try {
|
|
1820
|
-
if (!vfs.exists(stripped) || vfs.isDirectory(stripped))
|
|
1794
|
+
if (!(await vfs.exists(stripped)) || (await vfs.isDirectory(stripped)))
|
|
1821
1795
|
return false;
|
|
1822
1796
|
// This is a guess at what a program might require, and the same
|
|
1823
1797
|
// per-file ceiling the entry-package walk applies bounds it: a
|
|
@@ -1827,7 +1801,7 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
|
|
|
1827
1801
|
// supervisor's headroom — on every invocation that never reads it.
|
|
1828
1802
|
// Anything the program really requires arrives through the closure,
|
|
1829
1803
|
// which is uncapped.
|
|
1830
|
-
if (!_bundleAdmits(vfs, stripped, budgetState, BIN_PACKAGE_SPECULATIVE_MAX_FILE_BYTES))
|
|
1804
|
+
if (!(await _bundleAdmits(vfs, stripped, budgetState, BIN_PACKAGE_SPECULATIVE_MAX_FILE_BYTES)))
|
|
1831
1805
|
return false;
|
|
1832
1806
|
// A guess must not be a native binary. Nothing in a Workers isolate can
|
|
1833
1807
|
// load a `.node` addon or a `.exe` — the ABI policy classifies them
|
|
@@ -1839,7 +1813,7 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
|
|
|
1839
1813
|
if (stripped.endsWith('.node') || stripped.endsWith('.exe'))
|
|
1840
1814
|
return false;
|
|
1841
1815
|
// hardening-r5: preserve binary content as Uint8Array.
|
|
1842
|
-
const content = _readBundleCell(vfs, stripped);
|
|
1816
|
+
const content = (await _readBundleCell(vfs, stripped));
|
|
1843
1817
|
const cellLen = _bundleCellLength(content);
|
|
1844
1818
|
if (budgetState.totalBytes + cellLen > VFS_BUNDLE_MAX_BYTES)
|
|
1845
1819
|
return false;
|
|
@@ -1903,15 +1877,15 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
|
|
|
1903
1877
|
}
|
|
1904
1878
|
return !reached;
|
|
1905
1879
|
}
|
|
1906
|
-
function addPkgEntry(pkgDir) {
|
|
1907
|
-
addOne(pkgDir + '/package.json');
|
|
1880
|
+
async function addPkgEntry(pkgDir) {
|
|
1881
|
+
(await addOne(pkgDir + '/package.json'));
|
|
1908
1882
|
// A package the closure reached keeps exactly what it reached; only an
|
|
1909
1883
|
// unreached one gets the guess below.
|
|
1910
1884
|
if (!mainIsSpeculative(pkgDir))
|
|
1911
1885
|
return;
|
|
1912
1886
|
let meta;
|
|
1913
1887
|
try {
|
|
1914
|
-
meta = JSON.parse(vfs.readFileString(pkgDir + '/package.json'));
|
|
1888
|
+
meta = JSON.parse((await vfs.readFileString(pkgDir + '/package.json')));
|
|
1915
1889
|
}
|
|
1916
1890
|
catch {
|
|
1917
1891
|
meta = null;
|
|
@@ -1943,9 +1917,9 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
|
|
|
1943
1917
|
let landed = false;
|
|
1944
1918
|
const tries = /\.[a-z]+$/.test(norm) ? [base] : exts.map((e) => base + e);
|
|
1945
1919
|
for (const candidate of tries) {
|
|
1946
|
-
if (vfs.exists(candidate.replace(/^\/+/, '')) &&
|
|
1947
|
-
!vfs.isDirectory(candidate.replace(/^\/+/, ''))) {
|
|
1948
|
-
if (addOne(candidate)) {
|
|
1920
|
+
if ((await vfs.exists(candidate.replace(/^\/+/, ''))) &&
|
|
1921
|
+
!(await vfs.isDirectory(candidate.replace(/^\/+/, '')))) {
|
|
1922
|
+
if ((await addOne(candidate))) {
|
|
1949
1923
|
landed = true;
|
|
1950
1924
|
break;
|
|
1951
1925
|
}
|
|
@@ -1963,7 +1937,7 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
|
|
|
1963
1937
|
// the greedy oversample is the safety net for their reachability.
|
|
1964
1938
|
const entryDir = base.replace(/\/[^/]+$/, '');
|
|
1965
1939
|
try {
|
|
1966
|
-
const sibs = vfs.readdir(entryDir);
|
|
1940
|
+
const sibs = (await vfs.readdir(entryDir));
|
|
1967
1941
|
for (const sib of sibs) {
|
|
1968
1942
|
if (sib.type !== 'file')
|
|
1969
1943
|
continue;
|
|
@@ -1983,7 +1957,7 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
|
|
|
1983
1957
|
const hasMixedCase = /[A-Z]/.test(seg) && /[a-z]/.test(seg);
|
|
1984
1958
|
if (!hasDigitOrDash && !hasMixedCase)
|
|
1985
1959
|
continue;
|
|
1986
|
-
addOne(entryDir + '/' + sib.name);
|
|
1960
|
+
(await addOne(entryDir + '/' + sib.name));
|
|
1987
1961
|
}
|
|
1988
1962
|
// Walk one level into `shared/` — unconditionally, since the
|
|
1989
1963
|
// pattern is well-known across unbuild/rolldown/rollup chunked
|
|
@@ -1991,13 +1965,13 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
|
|
|
1991
1965
|
// typical shared/ dir returns 1-5 files.
|
|
1992
1966
|
const sharedDir = entryDir + '/shared';
|
|
1993
1967
|
const sharedStripped = sharedDir.replace(/^\/+/, '');
|
|
1994
|
-
if (vfs.exists(sharedStripped) && vfs.isDirectory(sharedStripped)) {
|
|
1995
|
-
for (const sh of vfs.readdir(sharedDir)) {
|
|
1968
|
+
if ((await vfs.exists(sharedStripped)) && (await vfs.isDirectory(sharedStripped))) {
|
|
1969
|
+
for (const sh of (await vfs.readdir(sharedDir))) {
|
|
1996
1970
|
if (sh.type !== 'file')
|
|
1997
1971
|
continue;
|
|
1998
1972
|
if (!/\.(cjs|mjs|js)$/.test(sh.name))
|
|
1999
1973
|
continue;
|
|
2000
|
-
addOne(sharedDir + '/' + sh.name);
|
|
1974
|
+
(await addOne(sharedDir + '/' + sh.name));
|
|
2001
1975
|
}
|
|
2002
1976
|
}
|
|
2003
1977
|
}
|
|
@@ -2008,8 +1982,8 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
|
|
|
2008
1982
|
}
|
|
2009
1983
|
}
|
|
2010
1984
|
}
|
|
2011
|
-
for (const pkgDir of speculativePackageDirs(vfs, cwdStripped, bundle))
|
|
2012
|
-
addPkgEntry(pkgDir);
|
|
1985
|
+
for (const pkgDir of (await speculativePackageDirs(vfs, cwdStripped, bundle)))
|
|
1986
|
+
(await addPkgEntry(pkgDir));
|
|
2013
1987
|
return { added };
|
|
2014
1988
|
}
|
|
2015
1989
|
/**
|
|
@@ -2029,10 +2003,10 @@ export function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPath
|
|
|
2029
2003
|
* dependency of the package doing the requiring, so the bound is one hop
|
|
2030
2004
|
* over `dependencies` only. Directories, sorted for a stable bundle.
|
|
2031
2005
|
*/
|
|
2032
|
-
export function speculativePackageDirs(vfs, cwdStripped, bundle) {
|
|
2033
|
-
const runtimeDeps = (pkgJsonPath) => {
|
|
2006
|
+
export async function speculativePackageDirs(vfs, cwdStripped, bundle) {
|
|
2007
|
+
const runtimeDeps = async (pkgJsonPath) => {
|
|
2034
2008
|
try {
|
|
2035
|
-
const meta = JSON.parse(vfs.readFileString(pkgJsonPath));
|
|
2009
|
+
const meta = JSON.parse((await vfs.readFileString(pkgJsonPath)));
|
|
2036
2010
|
const names = new Set();
|
|
2037
2011
|
for (const field of ['dependencies', 'optionalDependencies']) {
|
|
2038
2012
|
const deps = meta?.[field];
|
|
@@ -2057,11 +2031,11 @@ export function speculativePackageDirs(vfs, cwdStripped, bundle) {
|
|
|
2057
2031
|
};
|
|
2058
2032
|
// Resolve a bare name the way require does from `fromDir`: the nearest
|
|
2059
2033
|
// node_modules up the tree that has it.
|
|
2060
|
-
const resolveDir = (name, fromDir) => {
|
|
2034
|
+
const resolveDir = async (name, fromDir) => {
|
|
2061
2035
|
let dir = fromDir;
|
|
2062
2036
|
for (;;) {
|
|
2063
2037
|
const candidate = dir + '/node_modules/' + name;
|
|
2064
|
-
if (vfs.exists(candidate + '/package.json'))
|
|
2038
|
+
if ((await vfs.exists(candidate + '/package.json')))
|
|
2065
2039
|
return candidate;
|
|
2066
2040
|
const idx = dir.lastIndexOf('/');
|
|
2067
2041
|
if (idx <= 0)
|
|
@@ -2070,14 +2044,14 @@ export function speculativePackageDirs(vfs, cwdStripped, bundle) {
|
|
|
2070
2044
|
}
|
|
2071
2045
|
};
|
|
2072
2046
|
const reached = new Set();
|
|
2073
|
-
const hop = (fromDir) => {
|
|
2074
|
-
for (const name of runtimeDeps(fromDir + '/package.json')) {
|
|
2075
|
-
const dir = resolveDir(name, fromDir);
|
|
2047
|
+
const hop = async (fromDir) => {
|
|
2048
|
+
for (const name of (await runtimeDeps(fromDir + '/package.json'))) {
|
|
2049
|
+
const dir = (await resolveDir(name, fromDir));
|
|
2076
2050
|
if (dir !== null)
|
|
2077
2051
|
reached.add(dir);
|
|
2078
2052
|
}
|
|
2079
2053
|
};
|
|
2080
|
-
hop(cwdStripped);
|
|
2054
|
+
(await hop(cwdStripped));
|
|
2081
2055
|
const owners = new Set();
|
|
2082
2056
|
for (const path of Object.keys(bundle)) {
|
|
2083
2057
|
const owner = ownerOf(path);
|
|
@@ -2086,7 +2060,7 @@ export function speculativePackageDirs(vfs, cwdStripped, bundle) {
|
|
|
2086
2060
|
}
|
|
2087
2061
|
for (const owner of owners) {
|
|
2088
2062
|
reached.add(owner);
|
|
2089
|
-
hop(owner);
|
|
2063
|
+
(await hop(owner));
|
|
2090
2064
|
}
|
|
2091
2065
|
return [...reached].sort();
|
|
2092
2066
|
}
|
|
@@ -2129,7 +2103,7 @@ export function speculativePackageDirs(vfs, cwdStripped, bundle) {
|
|
|
2129
2103
|
* VFS_BUNDLE_MAX_FILES / VFS_BUNDLE_MAX_BYTES caps via the
|
|
2130
2104
|
* `budgetState` counter.
|
|
2131
2105
|
*/
|
|
2132
|
-
export function addStaticReadFileAssets(vfs, cwd, bundle, budgetState) {
|
|
2106
|
+
export async function addStaticReadFileAssets(vfs, cwd, bundle, budgetState) {
|
|
2133
2107
|
let added = 0;
|
|
2134
2108
|
// Asset extensions covered. Conservative whitelist — txt/json are
|
|
2135
2109
|
// also legit runtime-loaded assets (e.g. mime-db json, license.txt).
|
|
@@ -2147,7 +2121,7 @@ export function addStaticReadFileAssets(vfs, cwd, bundle, budgetState) {
|
|
|
2147
2121
|
// there's no `${` in the captured body (template-literal interpolation
|
|
2148
2122
|
// is rejected).
|
|
2149
2123
|
const RX = /(?:\bfs\s*\.)?readFileSync\s*\(\s*(?:[\w$.]+\s*\.\s*)?resolve\s*\(\s*__dirname\s*,\s*(['"`])([^'"`]+)\1\s*[\),]/g;
|
|
2150
|
-
function addOneAsset(absPath) {
|
|
2124
|
+
async function addOneAsset(absPath) {
|
|
2151
2125
|
const stripped = absPath.replace(/^\/+/, '');
|
|
2152
2126
|
if (stripped in bundle)
|
|
2153
2127
|
return false;
|
|
@@ -2156,12 +2130,12 @@ export function addStaticReadFileAssets(vfs, cwd, bundle, budgetState) {
|
|
|
2156
2130
|
if (budgetState.totalBytes >= VFS_BUNDLE_MAX_BYTES)
|
|
2157
2131
|
return false;
|
|
2158
2132
|
try {
|
|
2159
|
-
if (!vfs.exists(stripped) || vfs.isDirectory(stripped))
|
|
2133
|
+
if (!(await vfs.exists(stripped)) || (await vfs.isDirectory(stripped)))
|
|
2160
2134
|
return false;
|
|
2161
|
-
if (!_bundleAdmits(vfs, stripped, budgetState))
|
|
2135
|
+
if (!(await _bundleAdmits(vfs, stripped, budgetState)))
|
|
2162
2136
|
return false;
|
|
2163
2137
|
// hardening-r5: preserve binary content as Uint8Array.
|
|
2164
|
-
const content = _readBundleCell(vfs, stripped);
|
|
2138
|
+
const content = (await _readBundleCell(vfs, stripped));
|
|
2165
2139
|
const cellLen = _bundleCellLength(content);
|
|
2166
2140
|
if (budgetState.totalBytes + cellLen > VFS_BUNDLE_MAX_BYTES)
|
|
2167
2141
|
return false;
|
|
@@ -2231,7 +2205,7 @@ export function addStaticReadFileAssets(vfs, cwd, bundle, budgetState) {
|
|
|
2231
2205
|
}
|
|
2232
2206
|
resolved = out.join('/');
|
|
2233
2207
|
}
|
|
2234
|
-
addOneAsset(resolved);
|
|
2208
|
+
(await addOneAsset(resolved));
|
|
2235
2209
|
}
|
|
2236
2210
|
}
|
|
2237
2211
|
return { added };
|
|
@@ -2283,7 +2257,7 @@ export function addStaticReadFileAssets(vfs, cwd, bundle, budgetState) {
|
|
|
2283
2257
|
* Errors are swallowed: missing assets, unreadable VFS, and
|
|
2284
2258
|
* non-string readFile inputs are silent skips — matches Z3 posture.
|
|
2285
2259
|
*/
|
|
2286
|
-
export function addStaticReadFileDotfilesAndCompiled(vfs, cwd, bundle, budgetState) {
|
|
2260
|
+
export async function addStaticReadFileDotfilesAndCompiled(vfs, cwd, bundle, budgetState) {
|
|
2287
2261
|
let added = 0;
|
|
2288
2262
|
// The heuristic gate. Filenames matching either branch are eligible.
|
|
2289
2263
|
// - Leading `.` covers `.ts-jest-digest`, `.cache-marker`, `.lintstagedrc`-ish
|
|
@@ -2322,7 +2296,7 @@ export function addStaticReadFileDotfilesAndCompiled(vfs, cwd, bundle, budgetSta
|
|
|
2322
2296
|
'\\s*__dirname\\s*,\\s*' +
|
|
2323
2297
|
// literal: ' " ` (no ${ for backtick)
|
|
2324
2298
|
'([\'"`])([^\'"`]+)\\1', 'g');
|
|
2325
|
-
function addOneAsset(absPath) {
|
|
2299
|
+
async function addOneAsset(absPath) {
|
|
2326
2300
|
const stripped = absPath.replace(/^\/+/, '');
|
|
2327
2301
|
if (stripped in bundle)
|
|
2328
2302
|
return false;
|
|
@@ -2331,12 +2305,12 @@ export function addStaticReadFileDotfilesAndCompiled(vfs, cwd, bundle, budgetSta
|
|
|
2331
2305
|
if (budgetState.totalBytes >= VFS_BUNDLE_MAX_BYTES)
|
|
2332
2306
|
return false;
|
|
2333
2307
|
try {
|
|
2334
|
-
if (!vfs.exists(stripped) || vfs.isDirectory(stripped))
|
|
2308
|
+
if (!(await vfs.exists(stripped)) || (await vfs.isDirectory(stripped)))
|
|
2335
2309
|
return false;
|
|
2336
|
-
if (!_bundleAdmits(vfs, stripped, budgetState))
|
|
2310
|
+
if (!(await _bundleAdmits(vfs, stripped, budgetState)))
|
|
2337
2311
|
return false;
|
|
2338
2312
|
// hardening-r5: preserve binary content as Uint8Array.
|
|
2339
|
-
const content = _readBundleCell(vfs, stripped);
|
|
2313
|
+
const content = (await _readBundleCell(vfs, stripped));
|
|
2340
2314
|
const cellLen = _bundleCellLength(content);
|
|
2341
2315
|
if (budgetState.totalBytes + cellLen > VFS_BUNDLE_MAX_BYTES)
|
|
2342
2316
|
return false;
|
|
@@ -2405,7 +2379,7 @@ export function addStaticReadFileDotfilesAndCompiled(vfs, cwd, bundle, budgetSta
|
|
|
2405
2379
|
const basename = slash >= 0 ? resolved.slice(slash + 1) : resolved;
|
|
2406
2380
|
if (!FILENAME_GATE.test(basename))
|
|
2407
2381
|
continue;
|
|
2408
|
-
addOneAsset(resolved);
|
|
2382
|
+
(await addOneAsset(resolved));
|
|
2409
2383
|
}
|
|
2410
2384
|
}
|
|
2411
2385
|
return { added };
|
|
@@ -2438,7 +2412,7 @@ export function addStaticReadFileDotfilesAndCompiled(vfs, cwd, bundle, budgetSta
|
|
|
2438
2412
|
* (user scripts, npx-cache files outside node_modules, eval) is
|
|
2439
2413
|
* a no-op.
|
|
2440
2414
|
*/
|
|
2441
|
-
export function addBinTargetSiblings(vfs, scriptPath, bundle, budgetState, bundleProfile) {
|
|
2415
|
+
export async function addBinTargetSiblings(vfs, scriptPath, bundle, budgetState, bundleProfile) {
|
|
2442
2416
|
if (!scriptPath)
|
|
2443
2417
|
return { added: 0, wasmPaths: [] };
|
|
2444
2418
|
const stripped = scriptPath.replace(/^\/+/, '');
|
|
@@ -2492,7 +2466,7 @@ export function addBinTargetSiblings(vfs, scriptPath, bundle, budgetState, bundl
|
|
|
2492
2466
|
const dir = queue.shift();
|
|
2493
2467
|
let entries;
|
|
2494
2468
|
try {
|
|
2495
|
-
entries = vfs.readdir(dir);
|
|
2469
|
+
entries = (await vfs.readdir(dir));
|
|
2496
2470
|
}
|
|
2497
2471
|
catch {
|
|
2498
2472
|
continue;
|
|
@@ -2522,7 +2496,7 @@ export function addBinTargetSiblings(vfs, scriptPath, bundle, budgetState, bundl
|
|
|
2522
2496
|
continue;
|
|
2523
2497
|
let size;
|
|
2524
2498
|
try {
|
|
2525
|
-
size = vfs.lstat(child).size;
|
|
2499
|
+
size = (await vfs.lstat(child)).size;
|
|
2526
2500
|
}
|
|
2527
2501
|
catch {
|
|
2528
2502
|
continue;
|
|
@@ -2553,7 +2527,7 @@ export function addBinTargetSiblings(vfs, scriptPath, bundle, budgetState, bundl
|
|
|
2553
2527
|
// hardening-r5: preserve binary content as Uint8Array.
|
|
2554
2528
|
let content;
|
|
2555
2529
|
try {
|
|
2556
|
-
content = _readBundleCell(vfs, candidate.path);
|
|
2530
|
+
content = (await _readBundleCell(vfs, candidate.path));
|
|
2557
2531
|
}
|
|
2558
2532
|
catch {
|
|
2559
2533
|
continue;
|
|
@@ -2582,7 +2556,7 @@ export function addBinTargetSiblings(vfs, scriptPath, bundle, budgetState, bundl
|
|
|
2582
2556
|
* map whether it read the bytes by path or carried them inline. A file that
|
|
2583
2557
|
* cannot be read is left out; the seam's refusal names the module later.
|
|
2584
2558
|
*/
|
|
2585
|
-
export function collectClosureWasmImages(vfs, bundle, unstagedPaths) {
|
|
2559
|
+
export async function collectClosureWasmImages(vfs, bundle, unstagedPaths) {
|
|
2586
2560
|
const byPath = new Map();
|
|
2587
2561
|
for (const [path, cell] of Object.entries(bundle)) {
|
|
2588
2562
|
if (!path.endsWith('.wasm') || !(cell instanceof Uint8Array))
|
|
@@ -2594,7 +2568,7 @@ export function collectClosureWasmImages(vfs, bundle, unstagedPaths) {
|
|
|
2594
2568
|
continue;
|
|
2595
2569
|
let bytes;
|
|
2596
2570
|
try {
|
|
2597
|
-
bytes = vfs.readFile(path);
|
|
2571
|
+
bytes = await vfs.readFile(path);
|
|
2598
2572
|
}
|
|
2599
2573
|
catch {
|
|
2600
2574
|
continue;
|
|
@@ -2619,7 +2593,7 @@ export function collectClosureWasmImages(vfs, bundle, unstagedPaths) {
|
|
|
2619
2593
|
* budget is shared, so ordering by size maximizes the number of misses a
|
|
2620
2594
|
* fixed number of bytes repairs.
|
|
2621
2595
|
*/
|
|
2622
|
-
export function addObservedReads(vfs, observed, bundle, requiredPaths, budgetState) {
|
|
2596
|
+
export async function addObservedReads(vfs, observed, bundle, requiredPaths, budgetState) {
|
|
2623
2597
|
if (!observed || observed.size === 0)
|
|
2624
2598
|
return { added: 0 };
|
|
2625
2599
|
const candidates = [];
|
|
@@ -2628,7 +2602,7 @@ export function addObservedReads(vfs, observed, bundle, requiredPaths, budgetSta
|
|
|
2628
2602
|
continue;
|
|
2629
2603
|
let stat;
|
|
2630
2604
|
try {
|
|
2631
|
-
stat = vfs.lstat(path);
|
|
2605
|
+
stat = (await vfs.lstat(path));
|
|
2632
2606
|
}
|
|
2633
2607
|
catch {
|
|
2634
2608
|
continue;
|
|
@@ -2646,7 +2620,7 @@ export function addObservedReads(vfs, observed, bundle, requiredPaths, budgetSta
|
|
|
2646
2620
|
continue;
|
|
2647
2621
|
let content;
|
|
2648
2622
|
try {
|
|
2649
|
-
content = _readBundleCell(vfs, candidate.path);
|
|
2623
|
+
content = (await _readBundleCell(vfs, candidate.path));
|
|
2650
2624
|
}
|
|
2651
2625
|
catch {
|
|
2652
2626
|
continue;
|
|
@@ -2747,7 +2721,7 @@ function binPackageRootSegment(pkgRoot, path) {
|
|
|
2747
2721
|
function binPackageRelativePath(pkgRoot, path) {
|
|
2748
2722
|
return path.startsWith(pkgRoot + '/') ? path.slice(pkgRoot.length + 1) : path;
|
|
2749
2723
|
}
|
|
2750
|
-
function addCwdProjectFiles(vfs, cwd, bundle, budgetState) {
|
|
2724
|
+
async function addCwdProjectFiles(vfs, cwd, bundle, budgetState) {
|
|
2751
2725
|
const root = (cwd || '/home/user').replace(/^\/+/, '').replace(/\/+$/, '') || 'home/user';
|
|
2752
2726
|
const MAX_PROJECT_FILES = 512;
|
|
2753
2727
|
const SKIP_DIRS = new Set(['node_modules', '.git', '.nimbus']);
|
|
@@ -2758,7 +2732,7 @@ function addCwdProjectFiles(vfs, cwd, bundle, budgetState) {
|
|
|
2758
2732
|
const dir = queue.shift();
|
|
2759
2733
|
let entries;
|
|
2760
2734
|
try {
|
|
2761
|
-
entries = vfs.readdir(dir);
|
|
2735
|
+
entries = (await vfs.readdir(dir));
|
|
2762
2736
|
}
|
|
2763
2737
|
catch {
|
|
2764
2738
|
continue;
|
|
@@ -2785,11 +2759,11 @@ function addCwdProjectFiles(vfs, cwd, bundle, budgetState) {
|
|
|
2785
2759
|
// Skip an oversized file before reading it: this walk guesses at what
|
|
2786
2760
|
// the program might read, and one guess must not spend the budget (or
|
|
2787
2761
|
// the supervisor's headroom) that every later invocation then carries.
|
|
2788
|
-
if (!_bundleAdmits(vfs, child, budgetState, CWD_SNAPSHOT_MAX_FILE_BYTES))
|
|
2762
|
+
if (!(await _bundleAdmits(vfs, child, budgetState, CWD_SNAPSHOT_MAX_FILE_BYTES)))
|
|
2789
2763
|
continue;
|
|
2790
2764
|
let content;
|
|
2791
2765
|
try {
|
|
2792
|
-
content = _readBundleCell(vfs, child);
|
|
2766
|
+
content = (await _readBundleCell(vfs, child));
|
|
2793
2767
|
}
|
|
2794
2768
|
catch {
|
|
2795
2769
|
continue;
|
|
@@ -2832,7 +2806,7 @@ function addCwdProjectFiles(vfs, cwd, bundle, budgetState) {
|
|
|
2832
2806
|
* / openSync / readFile in source are scanned. Entry code is always
|
|
2833
2807
|
* scanned (it's the user's intent).
|
|
2834
2808
|
*/
|
|
2835
|
-
function addEntryAbsPathReads(vfs, entryCode, bundle, budgetState) {
|
|
2809
|
+
async function addEntryAbsPathReads(vfs, entryCode, bundle, budgetState) {
|
|
2836
2810
|
let added = 0;
|
|
2837
2811
|
// Capture absolute-path string literals. The path may not contain
|
|
2838
2812
|
// the quote char; the surrounding regex strips line/block comments
|
|
@@ -2841,7 +2815,7 @@ function addEntryAbsPathReads(vfs, entryCode, bundle, budgetState) {
|
|
|
2841
2815
|
// excludes spaces, glob chars, template syntax — all dynamic.
|
|
2842
2816
|
const RX = /(['"`])(\/[A-Za-z0-9._\-\/]{1,510})\1/g;
|
|
2843
2817
|
const REJECT_PREFIX = /^\/(proc|sys|dev|lib|lib64|boot|root)(\/|$)/;
|
|
2844
|
-
function tryAdd(absPath) {
|
|
2818
|
+
async function tryAdd(absPath) {
|
|
2845
2819
|
if (!absPath || absPath.length < 2 || absPath.length > 512)
|
|
2846
2820
|
return;
|
|
2847
2821
|
if (REJECT_PREFIX.test(absPath))
|
|
@@ -2854,12 +2828,12 @@ function addEntryAbsPathReads(vfs, entryCode, bundle, budgetState) {
|
|
|
2854
2828
|
if (budgetState.totalBytes >= VFS_BUNDLE_MAX_BYTES)
|
|
2855
2829
|
return;
|
|
2856
2830
|
try {
|
|
2857
|
-
if (!vfs.exists(stripped) || vfs.isDirectory(stripped))
|
|
2831
|
+
if (!(await vfs.exists(stripped)) || (await vfs.isDirectory(stripped)))
|
|
2858
2832
|
return;
|
|
2859
|
-
if (!_bundleAdmits(vfs, stripped, budgetState))
|
|
2833
|
+
if (!(await _bundleAdmits(vfs, stripped, budgetState)))
|
|
2860
2834
|
return;
|
|
2861
2835
|
// hardening-r5: preserve binary content as Uint8Array.
|
|
2862
|
-
const content = _readBundleCell(vfs, stripped);
|
|
2836
|
+
const content = (await _readBundleCell(vfs, stripped));
|
|
2863
2837
|
const cellLen = _bundleCellLength(content);
|
|
2864
2838
|
if (budgetState.totalBytes + cellLen > VFS_BUNDLE_MAX_BYTES)
|
|
2865
2839
|
return;
|
|
@@ -2870,7 +2844,7 @@ function addEntryAbsPathReads(vfs, entryCode, bundle, budgetState) {
|
|
|
2870
2844
|
}
|
|
2871
2845
|
catch { /* swallow — file may be binary, race-deleted, etc. */ }
|
|
2872
2846
|
}
|
|
2873
|
-
function scanOne(src) {
|
|
2847
|
+
async function scanOne(src) {
|
|
2874
2848
|
if (!src)
|
|
2875
2849
|
return;
|
|
2876
2850
|
// Strip line + block comments so we don't match commented-out reads.
|
|
@@ -2883,11 +2857,11 @@ function addEntryAbsPathReads(vfs, entryCode, bundle, budgetState) {
|
|
|
2883
2857
|
// forbids most). The check on the captured group is cheap.
|
|
2884
2858
|
if (/[\?\*\[\]\{\}]/.test(literal))
|
|
2885
2859
|
continue;
|
|
2886
|
-
tryAdd(literal);
|
|
2860
|
+
(await tryAdd(literal));
|
|
2887
2861
|
}
|
|
2888
2862
|
}
|
|
2889
2863
|
// Always scan entry code.
|
|
2890
|
-
scanOne(entryCode);
|
|
2864
|
+
(await scanOne(entryCode));
|
|
2891
2865
|
// Optionally scan bundled JS sources too — useful for transitive cases
|
|
2892
2866
|
// where a require'd module hardcodes an absolute path. Use the same
|
|
2893
2867
|
// budget-state so we don't blow caps.
|
|
@@ -2903,7 +2877,7 @@ function addEntryAbsPathReads(vfs, entryCode, bundle, budgetState) {
|
|
|
2903
2877
|
const cell = bundle[k];
|
|
2904
2878
|
if (typeof cell !== 'string')
|
|
2905
2879
|
continue;
|
|
2906
|
-
scanOne(cell);
|
|
2880
|
+
(await scanOne(cell));
|
|
2907
2881
|
}
|
|
2908
2882
|
return { added };
|
|
2909
2883
|
}
|
|
@@ -2919,8 +2893,44 @@ function looksLikeEsm(src) {
|
|
|
2919
2893
|
*
|
|
2920
2894
|
* Lives at module scope so warm exec invocations hit the cache without
|
|
2921
2895
|
* paying the wasm cold-start cost again.
|
|
2896
|
+
*
|
|
2897
|
+
* Bounded by bytes, LRU, and reported to the heap model like the prefetch
|
|
2898
|
+
* cache (see ESM_TRANSFORM_CACHE_MAX_BYTES for the reset it caused
|
|
2899
|
+
* unbounded). An output larger than the whole bound is used for the build
|
|
2900
|
+
* that produced it and not retained: admitting it would evict everything
|
|
2901
|
+
* else to hold something that still does not fit.
|
|
2922
2902
|
*/
|
|
2923
2903
|
const __esmTransformCache = new Map();
|
|
2904
|
+
let __esmTransformCacheBytes = 0;
|
|
2905
|
+
function __esmTransformCacheGet(key) {
|
|
2906
|
+
const code = __esmTransformCache.get(key);
|
|
2907
|
+
if (code === undefined)
|
|
2908
|
+
return undefined;
|
|
2909
|
+
// Refresh recency: a Map iterates in insertion order, so the oldest
|
|
2910
|
+
// entry is the first one.
|
|
2911
|
+
__esmTransformCache.delete(key);
|
|
2912
|
+
__esmTransformCache.set(key, code);
|
|
2913
|
+
return code;
|
|
2914
|
+
}
|
|
2915
|
+
function __esmTransformCacheSet(key, code) {
|
|
2916
|
+
const bytes = key.length + code.length;
|
|
2917
|
+
if (bytes > ESM_TRANSFORM_CACHE_MAX_BYTES)
|
|
2918
|
+
return;
|
|
2919
|
+
const previous = __esmTransformCache.get(key);
|
|
2920
|
+
if (previous !== undefined) {
|
|
2921
|
+
__esmTransformCacheBytes -= key.length + previous.length;
|
|
2922
|
+
__esmTransformCache.delete(key);
|
|
2923
|
+
}
|
|
2924
|
+
__esmTransformCache.set(key, code);
|
|
2925
|
+
__esmTransformCacheBytes += bytes;
|
|
2926
|
+
for (const [oldest, entry] of __esmTransformCache) {
|
|
2927
|
+
if (__esmTransformCacheBytes <= ESM_TRANSFORM_CACHE_MAX_BYTES)
|
|
2928
|
+
break;
|
|
2929
|
+
__esmTransformCache.delete(oldest);
|
|
2930
|
+
__esmTransformCacheBytes -= oldest.length + entry.length;
|
|
2931
|
+
}
|
|
2932
|
+
setTransformCacheBytes(__esmTransformCacheBytes);
|
|
2933
|
+
}
|
|
2924
2934
|
function __cacheKey(src) {
|
|
2925
2935
|
// FNV-1a 32-bit. Only used for cache keys, NEVER for content
|
|
2926
2936
|
// integrity. The ~30-byte string we return is a hex hash + length —
|
|
@@ -3042,6 +3052,35 @@ export function compiledCellKey(path) {
|
|
|
3042
3052
|
export function compiledCellPath(key) {
|
|
3043
3053
|
return key.startsWith(COMPILED_CELL_KEY_PREFIX) ? key.slice(COMPILED_CELL_KEY_PREFIX.length) : null;
|
|
3044
3054
|
}
|
|
3055
|
+
/**
|
|
3056
|
+
* The compile helper both generated facets define: one CommonJS cell to one
|
|
3057
|
+
* function, at module evaluation, the only moment workerd lets a string
|
|
3058
|
+
* become code. One definition so the two facets cannot drift on what a cell
|
|
3059
|
+
* may contain — the long-running facet once lacked the shebang strip, and a
|
|
3060
|
+
* required module that carried one compiled in a short command and failed
|
|
3061
|
+
* in the same tool's attached process.
|
|
3062
|
+
*/
|
|
3063
|
+
export const FACET_COMPILE_HELPER = `
|
|
3064
|
+
function __mkCompiledFn(code) {
|
|
3065
|
+
// Node strips a leading shebang from every module before evaluation, not
|
|
3066
|
+
// only the entry: a required module bundled with its "#!/usr/bin/env node"
|
|
3067
|
+
// line is a SyntaxError under new Function.
|
|
3068
|
+
if (typeof code === "string" && code.charCodeAt(0) === 35 && code.charCodeAt(1) === 33) {
|
|
3069
|
+
const __nl = code.indexOf("\\n");
|
|
3070
|
+
code = __nl >= 0 ? code.slice(__nl + 1) : "";
|
|
3071
|
+
}
|
|
3072
|
+
function renameIfDeclared(name) {
|
|
3073
|
+
const re = new RegExp("(?:^|\\\\n|;)\\\\s*(?:const|let|var|function|class)\\\\s+" + name + "(?![$\\\\w])", "m");
|
|
3074
|
+
return re.test(code) ? name + "__nimbus_unused" : name;
|
|
3075
|
+
}
|
|
3076
|
+
const baseParams = [
|
|
3077
|
+
"exports", renameIfDeclared("require"), "module",
|
|
3078
|
+
renameIfDeclared("__filename"),
|
|
3079
|
+
renameIfDeclared("__dirname"),
|
|
3080
|
+
];
|
|
3081
|
+
return new Function(...baseParams, code);
|
|
3082
|
+
}
|
|
3083
|
+
`;
|
|
3045
3084
|
/**
|
|
3046
3085
|
* The pre-compile loop both generated facets run at module evaluation, the
|
|
3047
3086
|
* only moment workerd lets a string become code. One definition so the two
|
|
@@ -3136,8 +3175,8 @@ async function transformEsmInBundle(bundle, esbuild, pacer, isolatedTransform) {
|
|
|
3136
3175
|
// cache entry and the second file would get the first file's URL.
|
|
3137
3176
|
const absUrl = 'file:///' + path.replace(/^\/+/, '');
|
|
3138
3177
|
const key = __cacheKey(src + '\0' + absUrl);
|
|
3139
|
-
const cached =
|
|
3140
|
-
if (cached) {
|
|
3178
|
+
const cached = __esmTransformCacheGet(key);
|
|
3179
|
+
if (cached !== undefined) {
|
|
3141
3180
|
bundle[target] = cached;
|
|
3142
3181
|
transformed++;
|
|
3143
3182
|
continue;
|
|
@@ -3157,7 +3196,7 @@ async function transformEsmInBundle(bundle, esbuild, pacer, isolatedTransform) {
|
|
|
3157
3196
|
: await esbuild.transform(src, transformOptions));
|
|
3158
3197
|
const code = bindImportMetaResolve(t.code, absUrl);
|
|
3159
3198
|
bundle[target] = code;
|
|
3160
|
-
|
|
3199
|
+
__esmTransformCacheSet(key, code);
|
|
3161
3200
|
transformed++;
|
|
3162
3201
|
}
|
|
3163
3202
|
catch (e) {
|
|
@@ -3189,7 +3228,7 @@ async function transformEsmInBundle(bundle, esbuild, pacer, isolatedTransform) {
|
|
|
3189
3228
|
const diagnosticSrc = '// framework-fixes-F4 diagnostic shim — esbuild rejected the ESM transform\n' +
|
|
3190
3229
|
'(function () { throw new Error(' + escapedReason + '); })();\n';
|
|
3191
3230
|
bundle[target] = diagnosticSrc;
|
|
3192
|
-
|
|
3231
|
+
__esmTransformCacheSet(key, diagnosticSrc);
|
|
3193
3232
|
failed++;
|
|
3194
3233
|
}
|
|
3195
3234
|
}
|
|
@@ -3231,9 +3270,10 @@ export async function buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbui
|
|
|
3231
3270
|
async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bundleProfile = DEFAULT_FACET_BUNDLE_PROFILE, observedReads, pacer, isolatedTransform, maxBundleBytes = VFS_BUNDLE_MAX_BYTES) {
|
|
3232
3271
|
// Read the cursor BEFORE the walk: a mutation that lands while the bundle
|
|
3233
3272
|
// is being assembled must be reported as invalidated, not silently missed.
|
|
3234
|
-
const
|
|
3273
|
+
const admitted = await vfs.authority.acquire(null, 0);
|
|
3274
|
+
const cursor = { epoch: admitted.epoch, rev: admitted.rev };
|
|
3235
3275
|
// 1. Static reachable-set walk from entry.
|
|
3236
|
-
const prefetch = prefetchForRequire(vfs, entryCode || '', cwd, scriptPath, maxBundleBytes);
|
|
3276
|
+
const prefetch = (await prefetchForRequire(vfs, entryCode || '', cwd, scriptPath, maxBundleBytes));
|
|
3237
3277
|
if ('kind' in prefetch) {
|
|
3238
3278
|
// A required closure larger than the bound can never launch as a
|
|
3239
3279
|
// snapshot. Surface it as the process's own failure rather than a
|
|
@@ -3263,14 +3303,14 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
|
|
|
3263
3303
|
// admitted first, ahead of every guess, and they join the required set
|
|
3264
3304
|
// rather than the evictable one, because a file evicted here misses
|
|
3265
3305
|
// again on the next run and the loop never closes.
|
|
3266
|
-
const observedAdd = addObservedReads(vfs, observedReads, bundle, requiredPaths, budgetState);
|
|
3306
|
+
const observedAdd = (await addObservedReads(vfs, observedReads, bundle, requiredPaths, budgetState));
|
|
3267
3307
|
void observedAdd;
|
|
3268
3308
|
await paceAfterPass();
|
|
3269
3309
|
// 2. Greedy oversample — every installed pkg's pkg.json + main.
|
|
3270
3310
|
// Catches dynamic-require / `bindings()` / plugin-loader cases the
|
|
3271
3311
|
// regex prefetch misses. Its budget is independent from the complete
|
|
3272
3312
|
// static require closure, which is correctness-critical.
|
|
3273
|
-
const greedy = greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPaths);
|
|
3313
|
+
const greedy = (await greedyAddMainEntries(vfs, cwd, bundle, budgetState, requiredPaths));
|
|
3274
3314
|
await paceAfterPass();
|
|
3275
3315
|
// 2.25 X.5-Z3: static-readFileSync asset prefetch. Scans every
|
|
3276
3316
|
// bundle .js/.mjs/.cjs source for the canonical jsdom shape:
|
|
@@ -3281,7 +3321,7 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
|
|
|
3281
3321
|
// `default-stylesheet.css` (and similar runtime asset reads in
|
|
3282
3322
|
// tldts, parse5, lookup-table packages, mime-db, etc.) ENOENT
|
|
3283
3323
|
// at facet runtime even though the file is on VFS-disk + in
|
|
3284
|
-
const assetAdd = addStaticReadFileAssets(vfs, cwd, bundle, budgetState);
|
|
3324
|
+
const assetAdd = (await addStaticReadFileAssets(vfs, cwd, bundle, budgetState));
|
|
3285
3325
|
void assetAdd;
|
|
3286
3326
|
await paceAfterPass();
|
|
3287
3327
|
// 2.27 X.5-U: dotfile + SWC-shape readFileSync sentinel prefetch.
|
|
@@ -3291,7 +3331,7 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
|
|
|
3291
3331
|
// pattern AND filenames outside the Z3 ASSET_EXT whitelist
|
|
3292
3332
|
// (dotfiles, no-extension sentinels, "digest/hash/version/sha/md5"
|
|
3293
3333
|
// shapes). Motivating case: ts-jest's `.ts-jest-digest`. See
|
|
3294
|
-
const dotAdd = addStaticReadFileDotfilesAndCompiled(vfs, cwd, bundle, budgetState);
|
|
3334
|
+
const dotAdd = (await addStaticReadFileDotfilesAndCompiled(vfs, cwd, bundle, budgetState));
|
|
3295
3335
|
void dotAdd;
|
|
3296
3336
|
await paceAfterPass();
|
|
3297
3337
|
// 2.30 G3 (runtime-pkg wave): bin-target sibling oversample. Pulls
|
|
@@ -3302,14 +3342,14 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
|
|
|
3302
3342
|
// catches custom extensions (.cow, .pem, .ttf, .wasm bundled
|
|
3303
3343
|
// as data, etc.) without needing a per-pkg whitelist.
|
|
3304
3344
|
// No-op when entry isn't inside node_modules.
|
|
3305
|
-
const binSiblingAdd = addBinTargetSiblings(vfs, scriptPath, bundle, budgetState, bundleProfile);
|
|
3345
|
+
const binSiblingAdd = (await addBinTargetSiblings(vfs, scriptPath, bundle, budgetState, bundleProfile));
|
|
3306
3346
|
await paceAfterPass();
|
|
3307
3347
|
// 2.34 project-data snapshot: sync Node fs cannot await the
|
|
3308
3348
|
// supervisor. Include a bounded snapshot of the current working tree
|
|
3309
3349
|
// for common relative project-file reads while skipping dependency
|
|
3310
3350
|
// and Nimbus cache directories. Async fs still uses live supervisor
|
|
3311
3351
|
// reads and child-process staleness fallback.
|
|
3312
|
-
const cwdProjectAdd = addCwdProjectFiles(vfs, cwd, bundle, budgetState);
|
|
3352
|
+
const cwdProjectAdd = (await addCwdProjectFiles(vfs, cwd, bundle, budgetState));
|
|
3313
3353
|
void cwdProjectAdd;
|
|
3314
3354
|
await paceAfterPass();
|
|
3315
3355
|
// 2.35 shell compatibility: absolute-path readFileSync scanner.
|
|
@@ -3336,7 +3376,7 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
|
|
|
3336
3376
|
// - Byte/file budget guards identical to other passes.
|
|
3337
3377
|
// - Skip paths under known-untrusted prefixes (`/proc`, `/sys`,
|
|
3338
3378
|
// `/dev` — we don't have these mounts; they'd never resolve).
|
|
3339
|
-
const absScanAdd = addEntryAbsPathReads(vfs, entryCode || '', bundle, budgetState);
|
|
3379
|
+
const absScanAdd = (await addEntryAbsPathReads(vfs, entryCode || '', bundle, budgetState));
|
|
3340
3380
|
void absScanAdd;
|
|
3341
3381
|
await paceAfterPass();
|
|
3342
3382
|
// 2.5 W3.5 Fix B: ESM→CJS transform pass. Walks `bundle`, sniffs each
|
|
@@ -3385,7 +3425,7 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
|
|
|
3385
3425
|
// from content cap so fs.readdirSync remains honest even if the
|
|
3386
3426
|
// content for a given file was capped out.
|
|
3387
3427
|
await paceAfterPass();
|
|
3388
|
-
const manifest = buildManifest(vfs, cwd, scriptPath);
|
|
3428
|
+
const manifest = (await buildManifest(vfs, cwd, scriptPath));
|
|
3389
3429
|
await paceAfterPass();
|
|
3390
3430
|
// 4. JSON-encoded-size guard, measured in UTF-8 bytes (not UTF-16 code
|
|
3391
3431
|
// units) because that is what workerd charges against the per-module
|
|
@@ -3418,8 +3458,8 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
|
|
|
3418
3458
|
}
|
|
3419
3459
|
}
|
|
3420
3460
|
const fileCount = Object.keys(bundle).length;
|
|
3421
|
-
const metadata = buildVfsMetadata(vfs, manifest, bundle);
|
|
3422
|
-
addUnreadableDenialCells(vfs, bundle, metadata);
|
|
3461
|
+
const metadata = (await buildVfsMetadata(vfs, manifest, bundle));
|
|
3462
|
+
(await addUnreadableDenialCells(vfs, bundle, metadata));
|
|
3423
3463
|
await paceAfterPass();
|
|
3424
3464
|
// Denial cells land after the eviction pass, so account for them before
|
|
3425
3465
|
// deciding where the bundle has to live.
|
|
@@ -3428,7 +3468,7 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
|
|
|
3428
3468
|
const bundleSideModulesRequired = size.bytes > BUNDLE_MAX_ENCODED_BYTES;
|
|
3429
3469
|
// Suppress lint: `greedy.added` is observed only via diagnostics.
|
|
3430
3470
|
void greedy;
|
|
3431
|
-
const wasmImages = collectClosureWasmImages(vfs, bundle, binSiblingAdd.wasmPaths);
|
|
3471
|
+
const wasmImages = (await collectClosureWasmImages(vfs, bundle, binSiblingAdd.wasmPaths));
|
|
3432
3472
|
return {
|
|
3433
3473
|
bundle,
|
|
3434
3474
|
manifest,
|
|
@@ -3471,6 +3511,7 @@ export class FacetManager {
|
|
|
3471
3511
|
processes;
|
|
3472
3512
|
portRegistry;
|
|
3473
3513
|
vfs = null;
|
|
3514
|
+
filesystem = null;
|
|
3474
3515
|
hooks;
|
|
3475
3516
|
/**
|
|
3476
3517
|
* The resident-process scheduler (loaders/process-fabric.ts). Every
|
|
@@ -3507,6 +3548,8 @@ export class FacetManager {
|
|
|
3507
3548
|
* journal recovery rides the first pump.
|
|
3508
3549
|
*/
|
|
3509
3550
|
launchPump;
|
|
3551
|
+
launchTasks = new Set();
|
|
3552
|
+
launchesClosed = false;
|
|
3510
3553
|
// attach-pid → serve-pid: the resident serve facet a bare-`opencode` dual
|
|
3511
3554
|
// spawn created as an OS-child of the attach TUI. When the attach process
|
|
3512
3555
|
// exits (reported / killed), its serve facet is torn down with it.
|
|
@@ -3549,6 +3592,22 @@ export class FacetManager {
|
|
|
3549
3592
|
* already deliver for the case that matters: running the command again.
|
|
3550
3593
|
*/
|
|
3551
3594
|
residencyProfiles = new Map();
|
|
3595
|
+
/**
|
|
3596
|
+
* What the prefetch cache holds right now, for /api/_diag/memory: each
|
|
3597
|
+
* entry's key, the revision it was built at, and its retained bytes, next
|
|
3598
|
+
* to the filesystem's live revision and the residency profiles. A launch
|
|
3599
|
+
* that rebuilds where a hit was expected is explained by exactly these:
|
|
3600
|
+
* the revision moved, the key changed, or a miss profile dropped the entry.
|
|
3601
|
+
*/
|
|
3602
|
+
prefetchCacheDiag() {
|
|
3603
|
+
return {
|
|
3604
|
+
revision: this.vfs ? this.vfs.revision() : null,
|
|
3605
|
+
entries: [...this.prefetchBundleCache].map(([key, entry]) => ({
|
|
3606
|
+
key, revision: entry.revision, bytes: entry.bytes,
|
|
3607
|
+
})),
|
|
3608
|
+
residencyProfiles: [...this.residencyProfiles].map(([key, paths]) => ({ key, paths: [...paths] })),
|
|
3609
|
+
};
|
|
3610
|
+
}
|
|
3552
3611
|
/** In-flight request-driven durable-app ensures, single-flight per port. */
|
|
3553
3612
|
ensureInflight = new Map();
|
|
3554
3613
|
/** Per-pid chain of journal-row amendments; see `_amendRow`. */
|
|
@@ -3610,7 +3669,7 @@ export class FacetManager {
|
|
|
3610
3669
|
// Rooted on waitUntil: the hook fires synchronously inside whatever turn
|
|
3611
3670
|
// ended the process, and the delete must not be a floating promise there.
|
|
3612
3671
|
this.processes.setOnTerminal((pid) => {
|
|
3613
|
-
this.ctx.waitUntil(this._onResidentTerminal(pid));
|
|
3672
|
+
this.ctx.waitUntil(this.trackLaunchTask(this._onResidentTerminal(pid)));
|
|
3614
3673
|
});
|
|
3615
3674
|
}
|
|
3616
3675
|
/**
|
|
@@ -3630,7 +3689,7 @@ export class FacetManager {
|
|
|
3630
3689
|
const key = `${FENCED_WORK_KEY_PREFIX}${pid}`;
|
|
3631
3690
|
const row = (await this.launchJournal.rows()).get(key);
|
|
3632
3691
|
const crashed = entry?.state === 'exited' && (entry.exitCode ?? 0) !== 0;
|
|
3633
|
-
if (row === undefined || row.restart !== 'on-failure' || !crashed) {
|
|
3692
|
+
if (this.launchesClosed || row === undefined || row.restart !== 'on-failure' || !crashed) {
|
|
3634
3693
|
await this.launchJournal.release(pid);
|
|
3635
3694
|
return;
|
|
3636
3695
|
}
|
|
@@ -3704,7 +3763,10 @@ export class FacetManager {
|
|
|
3704
3763
|
});
|
|
3705
3764
|
return next;
|
|
3706
3765
|
}
|
|
3707
|
-
|
|
3766
|
+
/** The authority is the caller's: a session composes exactly one, and the
|
|
3767
|
+
* manager credentials its processes through that one rather than a second
|
|
3768
|
+
* authority over the same disk. */
|
|
3769
|
+
setVfs(vfs, filesystem) { this.vfs = vfs; this.filesystem = filesystem; }
|
|
3708
3770
|
/**
|
|
3709
3771
|
* The env/ctx pair every loader-backed runtime builds its facet pools
|
|
3710
3772
|
* from. A pool is constructed from exactly these two, so the manager
|
|
@@ -3804,15 +3866,17 @@ export class FacetManager {
|
|
|
3804
3866
|
* be read is left out; the program's compile then meets the seam's own
|
|
3805
3867
|
* refusal, which names the module.
|
|
3806
3868
|
*/
|
|
3807
|
-
_wasmModulesByValue(entry, wasmImports) {
|
|
3869
|
+
async _wasmModulesByValue(entry, wasmImports) {
|
|
3808
3870
|
const modules = {};
|
|
3809
3871
|
if (!this.vfs || wasmImports.length === 0)
|
|
3810
3872
|
return modules;
|
|
3811
|
-
|
|
3873
|
+
if (!this.filesystem)
|
|
3874
|
+
throw new Error('Process filesystem authority is not initialized');
|
|
3875
|
+
const vfs = new ExecutionFs(this.filesystem.bind({ pid: entry.pid, cred: entry.cred }));
|
|
3812
3876
|
for (const image of wasmImports) {
|
|
3813
3877
|
let bytes;
|
|
3814
3878
|
try {
|
|
3815
|
-
bytes = vfs.readFile(stripLeadingSlashes(image.vfsPath));
|
|
3879
|
+
bytes = (await vfs.readFile(stripLeadingSlashes(image.vfsPath)));
|
|
3816
3880
|
}
|
|
3817
3881
|
catch {
|
|
3818
3882
|
continue;
|
|
@@ -3835,12 +3899,14 @@ export class FacetManager {
|
|
|
3835
3899
|
if (!this.esbuild)
|
|
3836
3900
|
this.esbuild = new EsbuildService(this.vfs.as(CRED_KERNEL));
|
|
3837
3901
|
this.imageStore.ensureDir();
|
|
3838
|
-
|
|
3902
|
+
if (!this.filesystem)
|
|
3903
|
+
throw new Error('Process filesystem authority is not initialized');
|
|
3904
|
+
const vfs = new ExecutionFs(this.filesystem.bind({ pid: entry.pid, cred: entry.cred }));
|
|
3839
3905
|
const { cred } = entry;
|
|
3840
3906
|
const profile = spec.bundleProfile ?? DEFAULT_FACET_BUNDLE_PROFILE;
|
|
3841
3907
|
const credKey = `${cred.uid}:${cred.gid}:${cred.groups.join(',')}`;
|
|
3842
3908
|
const key = `${profile}\x00${credKey}\x00${spec.cwd}\x00${spec.scriptPath ?? ''}\x00${_fnv1a(spec.entryCode)}`;
|
|
3843
|
-
const revision = vfs.revision();
|
|
3909
|
+
const revision = (await vfs.revision());
|
|
3844
3910
|
// An entry built at an older revision can never be SERVED again — the
|
|
3845
3911
|
// lookup below requires an exact match — so from the first write after it
|
|
3846
3912
|
// was admitted it is retained garbage, held in the isolate that is
|
|
@@ -4255,7 +4321,7 @@ export class FacetManager {
|
|
|
4255
4321
|
// images ride by value: read here, inside the scope that holds
|
|
4256
4322
|
// the map, and compiled by the loader like the sqlite sidecar.
|
|
4257
4323
|
const wasmImports = facetWasmImports([], vfsState.wasmImages ?? []);
|
|
4258
|
-
const wasmModules = this._wasmModulesByValue(entry, wasmImports);
|
|
4324
|
+
const wasmModules = (await this._wasmModulesByValue(entry, wasmImports));
|
|
4259
4325
|
const generatedWorker = await generateEntrypointCode(code, vfsState, usesSqlite, shims, wasmImports);
|
|
4260
4326
|
if (diagSink) {
|
|
4261
4327
|
diagSink.moduleMapBytes = _encodedSourceBytes(generatedWorker.code);
|
|
@@ -4420,7 +4486,7 @@ export class FacetManager {
|
|
|
4420
4486
|
// other writes flush live through the SUPERVISOR RPC bridge.
|
|
4421
4487
|
if (this.vfs)
|
|
4422
4488
|
this.imageStore.ensureDir();
|
|
4423
|
-
const processVfs = this.
|
|
4489
|
+
const processVfs = this.filesystem ? new ExecutionFs(this.filesystem.bind({ pid: entry.pid, cred: entry.cred })) : null;
|
|
4424
4490
|
const vfsState = processVfs
|
|
4425
4491
|
? await buildPrefetchBundle(processVfs, undefined, opts.cwd, '', this.esbuild || undefined, DEFAULT_FACET_BUNDLE_PROFILE, undefined, undefined, this.hooks.transformLargeEsm)
|
|
4426
4492
|
: { bundle: {}, manifest: {}, metadata: {}, reachableCount: 0, truncated: false };
|
|
@@ -4729,6 +4795,18 @@ export class FacetManager {
|
|
|
4729
4795
|
pumpResidentLaunches() {
|
|
4730
4796
|
return this.launchPump.pump();
|
|
4731
4797
|
}
|
|
4798
|
+
trackLaunchTask(task) {
|
|
4799
|
+
this.launchTasks.add(task);
|
|
4800
|
+
void task.then(() => this.launchTasks.delete(task), () => this.launchTasks.delete(task));
|
|
4801
|
+
return task;
|
|
4802
|
+
}
|
|
4803
|
+
async closeLaunches() {
|
|
4804
|
+
this.launchesClosed = true;
|
|
4805
|
+
this.launchPump.close();
|
|
4806
|
+
while (this.launchTasks.size > 0)
|
|
4807
|
+
await Promise.allSettled([...this.launchTasks]);
|
|
4808
|
+
await Promise.all(this.rowAmendments.values());
|
|
4809
|
+
}
|
|
4732
4810
|
/** Whether any launch is suspended waiting for a turn. */
|
|
4733
4811
|
get hasPendingLaunchTurns() {
|
|
4734
4812
|
return this.launchPump.hasPending;
|
|
@@ -4998,7 +5076,12 @@ export class FacetManager {
|
|
|
4998
5076
|
* terminal WebSocket needs to survive a launch, and what no amount of making
|
|
4999
5077
|
* the launch faster would have given it.
|
|
5000
5078
|
*/
|
|
5001
|
-
|
|
5079
|
+
_runResidentLaunch(entry, code, command, opts, attempt) {
|
|
5080
|
+
if (this.launchesClosed)
|
|
5081
|
+
return Promise.reject(new Error('Launch scheduler is closed'));
|
|
5082
|
+
return this.trackLaunchTask(this._runResidentLaunchBody(entry, code, command, opts, attempt));
|
|
5083
|
+
}
|
|
5084
|
+
async _runResidentLaunchBody(entry, code, command, opts, attempt) {
|
|
5002
5085
|
const cwd = opts.cwd || '/home/user';
|
|
5003
5086
|
const pacer = this._launchPacer(entry.pid);
|
|
5004
5087
|
// Journalled before the first byte of work. A launch that FAILS deletes
|