@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
|
@@ -45,7 +45,7 @@ import { getExportsResolverJS } from '@nimbus-sh/core/_shared/exports-resolver.j
|
|
|
45
45
|
import { getTypescriptSpecifiersJS } from '@nimbus-sh/core/_shared/typescript-specifiers.js';
|
|
46
46
|
import { NIMBUS_AI_CREDENTIAL_HEADERS, NIMBUS_AI_TOKEN_ENV } from '@nimbus-sh/core/_shared/ai-egress.js';
|
|
47
47
|
import { MAX_RPC_SAFE_PAYLOAD_BYTES } from '@nimbus-sh/platform/limits.js';
|
|
48
|
-
import { FACET_PROVIDED_PACKAGES, FS_READ_BATCH_PATH_LIMIT, FS_READ_BATCH_REQUEST_BYTES, NIMBUS_AI_GATEWAY_PORT, NODE_VERSION, NODE_VERSIONS, } from '@nimbus-sh/core/constants.js';
|
|
48
|
+
import { FACET_PROVIDED_PACKAGES, FS_READ_BATCH_PATH_LIMIT, FS_READ_BATCH_REQUEST_BYTES, NIMBUS_AI_GATEWAY_PORT, NODE_VERSION, NODE_VERSIONS, VFS_CAPACITY, } from '@nimbus-sh/core/constants.js';
|
|
49
49
|
const STREAMS_CODE = generateStreamsCode();
|
|
50
50
|
const SQLITE_SHIM_CODE = generateSqliteShimCode();
|
|
51
51
|
const UNDICI_SHIM_CODE = generateUndiciShimCode();
|
|
@@ -634,6 +634,43 @@ function __residentAnyUnder(prefix) {
|
|
|
634
634
|
return false;
|
|
635
635
|
}
|
|
636
636
|
|
|
637
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
638
|
+
// ── fs.constants (linux x64) ───────────────────────────────────────
|
|
639
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
640
|
+
//
|
|
641
|
+
// The ONE table behind fs.constants, fs.promises.constants, and the fs slice
|
|
642
|
+
// of node:constants / process.binding('fs'). Values are real Node on Linux
|
|
643
|
+
// x64 (node -p 'require("fs").constants'). Packages compose open flags from
|
|
644
|
+
// this table at module init — modern-tar (create-astro via @bluwy/giget-core)
|
|
645
|
+
// computes O_WRONLY|O_CREAT|O_TRUNC|O_NOFOLLOW|O_EXCL and passes the number
|
|
646
|
+
// to fs.open — so a partial table silently degrades every such open to
|
|
647
|
+
// O_RDONLY (0) and every extraction fails ENOENT. Frozen, as Node's is.
|
|
648
|
+
const __fsConstants = Object.freeze({
|
|
649
|
+
// ── libuv fs flags ────────────────────────────────────────────────
|
|
650
|
+
UV_FS_SYMLINK_DIR: 1, UV_FS_SYMLINK_JUNCTION: 2,
|
|
651
|
+
// ── open(2) flags ─────────────────────────────────────────────────
|
|
652
|
+
O_RDONLY: 0, O_WRONLY: 1, O_RDWR: 2,
|
|
653
|
+
UV_DIRENT_UNKNOWN: 0, UV_DIRENT_FILE: 1, UV_DIRENT_DIR: 2, UV_DIRENT_LINK: 3,
|
|
654
|
+
UV_DIRENT_FIFO: 4, UV_DIRENT_SOCKET: 5, UV_DIRENT_CHAR: 6, UV_DIRENT_BLOCK: 7,
|
|
655
|
+
EXTENSIONLESS_FORMAT_JAVASCRIPT: 0, EXTENSIONLESS_FORMAT_WASM: 1,
|
|
656
|
+
// ── stat.mode file-type bits ──────────────────────────────────────
|
|
657
|
+
S_IFMT: 61440, S_IFREG: 32768, S_IFDIR: 16384, S_IFCHR: 8192,
|
|
658
|
+
S_IFBLK: 24576, S_IFIFO: 4096, S_IFLNK: 40960, S_IFSOCK: 49152,
|
|
659
|
+
O_CREAT: 64, O_EXCL: 128, UV_FS_O_FILEMAP: 0, O_NOCTTY: 256, O_TRUNC: 512,
|
|
660
|
+
O_APPEND: 1024, O_DIRECTORY: 65536, O_NOATIME: 262144, O_NOFOLLOW: 131072,
|
|
661
|
+
O_SYNC: 1052672, O_DSYNC: 4096, O_DIRECT: 16384, O_NONBLOCK: 2048,
|
|
662
|
+
// ── stat.mode permission bits ─────────────────────────────────────
|
|
663
|
+
S_IRWXU: 448, S_IRUSR: 256, S_IWUSR: 128, S_IXUSR: 64,
|
|
664
|
+
S_IRWXG: 56, S_IRGRP: 32, S_IWGRP: 16, S_IXGRP: 8,
|
|
665
|
+
S_IRWXO: 7, S_IROTH: 4, S_IWOTH: 2, S_IXOTH: 1,
|
|
666
|
+
// ── fs.access modes ───────────────────────────────────────────────
|
|
667
|
+
F_OK: 0, R_OK: 4, W_OK: 2, X_OK: 1,
|
|
668
|
+
// ── fs.copyFile modes (libuv shape + Node alias) ──────────────────
|
|
669
|
+
UV_FS_COPYFILE_EXCL: 1, COPYFILE_EXCL: 1,
|
|
670
|
+
UV_FS_COPYFILE_FICLONE: 2, COPYFILE_FICLONE: 2,
|
|
671
|
+
UV_FS_COPYFILE_FICLONE_FORCE: 4, COPYFILE_FICLONE_FORCE: 4,
|
|
672
|
+
});
|
|
673
|
+
|
|
637
674
|
// ═══════════════════════════════════════════════════════════════════════
|
|
638
675
|
// ── fs shim (VFS-backed) ───────────────────────────────────────────
|
|
639
676
|
// ═══════════════════════════════════════════════════════════════════════
|
|
@@ -1271,6 +1308,10 @@ const __fsMod = (() => {
|
|
|
1271
1308
|
mtimeMs: _coerceTimeMs(mtime, syscall, p),
|
|
1272
1309
|
};
|
|
1273
1310
|
_localTimes[k] = time;
|
|
1311
|
+
// A spawn-time stat record answers statSync ahead of _localTimes, so it
|
|
1312
|
+
// must carry the new times too or the sync view keeps the old mtime.
|
|
1313
|
+
const meta = _metadata(absPath);
|
|
1314
|
+
if (meta) { meta.atime = time.atimeMs; meta.mtime = time.mtimeMs; }
|
|
1274
1315
|
return time;
|
|
1275
1316
|
}
|
|
1276
1317
|
|
|
@@ -1412,6 +1453,27 @@ const __fsMod = (() => {
|
|
|
1412
1453
|
return evicted;
|
|
1413
1454
|
}
|
|
1414
1455
|
|
|
1456
|
+
// Cells an own-mutation lease evicted with no barrier in hand.
|
|
1457
|
+
//
|
|
1458
|
+
// A barrier hands _acquireAndRefetch every path it dropped, which is what
|
|
1459
|
+
// keeps a sync read inside a timer callback reading the peer's new bytes
|
|
1460
|
+
// instead of EAGAIN. A lease's end drops a cell on its own account — the
|
|
1461
|
+
// barrier that would have reported it was suppressed by the lease itself
|
|
1462
|
+
// — so the same debt is recorded here and settled by the next untrusted
|
|
1463
|
+
// resumption.
|
|
1464
|
+
const _owedRefetch = new Set();
|
|
1465
|
+
|
|
1466
|
+
// An own-mutation lease can end in an eviction (a peer wrote inside the
|
|
1467
|
+
// window). The ledger owning that decision is spliced AHEAD of this
|
|
1468
|
+
// closure, so it is handed the one eviction path rather than keeping a
|
|
1469
|
+
// second copy of it: content view, stat view, the invalidation count and
|
|
1470
|
+
// the refetch debt move together here or not at all.
|
|
1471
|
+
globalThis.__nimbusEvictResidentCell = (k) => {
|
|
1472
|
+
if (!_evictResident(k)) return;
|
|
1473
|
+
_stats.invalidations++;
|
|
1474
|
+
_owedRefetch.add(k);
|
|
1475
|
+
};
|
|
1476
|
+
|
|
1415
1477
|
/**
|
|
1416
1478
|
* Install bytes the supervisor just served as the resident cell for a
|
|
1417
1479
|
* path, at the cursor they were served under.
|
|
@@ -1591,6 +1653,11 @@ const __fsMod = (() => {
|
|
|
1591
1653
|
} else if (Array.isArray(result.paths)) {
|
|
1592
1654
|
for (const entry of result.paths) {
|
|
1593
1655
|
const k = entry.path;
|
|
1656
|
+
// This cursor is about to advance past the report, so a path with
|
|
1657
|
+
// one of this facet's own writes in flight keeps the number: only
|
|
1658
|
+
// that write's own revision, when it arrives, can say whether the
|
|
1659
|
+
// report was itself or a peer. Everything else ignores it.
|
|
1660
|
+
__nimbusNoteVfsReport(k, entry.rev);
|
|
1594
1661
|
if (__vfsBundleRevisions[k] >= entry.rev) { _stats.selfWrites++; continue; }
|
|
1595
1662
|
const held = !!(__vfsBundle && k in __vfsBundle);
|
|
1596
1663
|
if (_evictResident(k)) _stats.invalidations++;
|
|
@@ -1624,6 +1691,13 @@ const __fsMod = (() => {
|
|
|
1624
1691
|
*/
|
|
1625
1692
|
async function _acquireAndRefetch(supervisor) {
|
|
1626
1693
|
const stale = await _acquireBarrier(supervisor);
|
|
1694
|
+
// Plus what an own-mutation lease dropped since the last resumption:
|
|
1695
|
+
// the same debt this function exists to settle, owed by an eviction
|
|
1696
|
+
// that had no barrier to report it (see _owedRefetch).
|
|
1697
|
+
if (_owedRefetch.size > 0) {
|
|
1698
|
+
for (const k of _owedRefetch) if (stale.indexOf(k) === -1) stale.push(k);
|
|
1699
|
+
_owedRefetch.clear();
|
|
1700
|
+
}
|
|
1627
1701
|
if (stale.length === 0) return;
|
|
1628
1702
|
await Promise.all(stale.map((k) => _liveReadFile("/" + k, undefined, true).catch(() => {})));
|
|
1629
1703
|
}
|
|
@@ -1724,7 +1798,16 @@ const __fsMod = (() => {
|
|
|
1724
1798
|
// supervisor.mkdir is recursive, so the deepest unannounced one creates all
|
|
1725
1799
|
// of them in a single round trip; a path whose directories are already live
|
|
1726
1800
|
// (the common case) costs none at all.
|
|
1801
|
+
//
|
|
1802
|
+
// This is also the one seam every asynchronous fs entry point crosses on
|
|
1803
|
+
// its way to the authority — directly, or through
|
|
1804
|
+
// _flushLocalPathToSupervisor — so it is where a live call waits for the
|
|
1805
|
+
// structural mutations the program issued synchronously before it:
|
|
1806
|
+
// mkdirSync, rmdirSync, unlinkSync, renameSync queue their authority RPC
|
|
1807
|
+
// (below) rather than land it, and an open/stat/readdir that overtook that
|
|
1808
|
+
// queue would be answered for a tree the program has already changed.
|
|
1727
1809
|
async function _announceLocalDirs(absPath, supervisor) {
|
|
1810
|
+
await _awaitStructuralOrder(absPath);
|
|
1728
1811
|
if (!__vfsDirs || typeof supervisor.mkdir !== "function") return;
|
|
1729
1812
|
const pending = [];
|
|
1730
1813
|
let key = "";
|
|
@@ -1740,24 +1823,142 @@ const __fsMod = (() => {
|
|
|
1740
1823
|
_markVfsStale();
|
|
1741
1824
|
}
|
|
1742
1825
|
|
|
1826
|
+
// The write ledger is spliced ahead of the shims in every runner
|
|
1827
|
+
// (facets/manager.ts, opencode-facet-runner.ts). A harness that evaluates
|
|
1828
|
+
// the shims alone has no mutation queue and therefore nothing to wait for;
|
|
1829
|
+
// an absent ledger must not be an error on a read path that never needed
|
|
1830
|
+
// one before.
|
|
1831
|
+
function _hasVfsMutationQueue() {
|
|
1832
|
+
return typeof __nimbusQueueVfsMutation === "function";
|
|
1833
|
+
}
|
|
1834
|
+
function _awaitStructuralOrder(absPath) {
|
|
1835
|
+
return _hasVfsMutationQueue() ? __nimbusAwaitAncestorMutations(absPath) : Promise.resolve();
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
/**
|
|
1839
|
+
* Carry a synchronous structural mutation to the authority.
|
|
1840
|
+
*
|
|
1841
|
+
* mkdirSync, rmdirSync, unlinkSync and renameSync used to edit the local
|
|
1842
|
+
* tables and stop: a sync syscall cannot make an RPC, and unlike a sync
|
|
1843
|
+
* write they parked nothing the write-back could later flush. Measured
|
|
1844
|
+
* live: node-tar's \`mkdirSync(dir)\` then \`fs.promises.open(dir/file,
|
|
1845
|
+
* "w")\` — once per extracted entry — was answered ENOENT by an authority
|
|
1846
|
+
* that had never heard of \`dir\`; that is create-astro's template copy.
|
|
1847
|
+
*
|
|
1848
|
+
* The sync effect stays exactly as it was. What is added is the same RPC
|
|
1849
|
+
* the asynchronous form of the call issues, queued through the write
|
|
1850
|
+
* ledger so it registers with the exit drain, is ordered behind pending
|
|
1851
|
+
* mutations of its ancestors (the ledger does that for every queued
|
|
1852
|
+
* mutation), and — for the two that act on a subtree — behind pending
|
|
1853
|
+
* mutations beneath it. The async forms are that same queued call, awaited.
|
|
1854
|
+
*
|
|
1855
|
+
* \`null\` when there is no authority to tell: standalone and unit contexts
|
|
1856
|
+
* keep today's local-only behaviour. Without a ledger the RPC is issued
|
|
1857
|
+
* directly, which is what the async forms did before they were queued.
|
|
1858
|
+
*/
|
|
1859
|
+
// \`method\` names the supervisor RPC when it differs from the syscall the
|
|
1860
|
+
// caller reports (lchown rides \`chown\`, rm rides \`fsRemove\`).
|
|
1861
|
+
function _queueStructuralMutation(absPath, syscall, displayPath, rpc, after, method) {
|
|
1862
|
+
const supervisor = _supervisor();
|
|
1863
|
+
if (!supervisor || typeof supervisor[method || syscall] !== "function") return null;
|
|
1864
|
+
const queued = _hasVfsMutationQueue();
|
|
1865
|
+
const before = queued && after ? after() : null;
|
|
1866
|
+
const run = async () => {
|
|
1867
|
+
if (before) await before;
|
|
1868
|
+
// Only chown answers with a receipt. The rest (mkdir, unlink, rmdir,
|
|
1869
|
+
// rename, rm) have already dropped the path's cell and stamp in their
|
|
1870
|
+
// synchronous half, so no lease is taken at all; where one is — the
|
|
1871
|
+
// stamp unlink leaves behind — an absent receipt retires it, which is
|
|
1872
|
+
// what an unstamped cell already costs.
|
|
1873
|
+
await _ownMutation(
|
|
1874
|
+
absPath,
|
|
1875
|
+
() => _fsRpc(rpc(supervisor), syscall, displayPath, (result) => result),
|
|
1876
|
+
);
|
|
1877
|
+
_markVfsStale();
|
|
1878
|
+
};
|
|
1879
|
+
return queued ? __nimbusQueueVfsMutation(absPath, run) : run();
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
/**
|
|
1883
|
+
* Run one of this facet's own partial mutations under a lease.
|
|
1884
|
+
*
|
|
1885
|
+
* See __nimbusBeginOwnMutation: the barrier KEEPS the cell while the RPC
|
|
1886
|
+
* is in flight — a barrier issued after this mutation can be answered
|
|
1887
|
+
* ahead of it, and an eviction there loses bytes no later stamp can
|
|
1888
|
+
* recover — and the receipt decides at the end. \`apply\` lands the local
|
|
1889
|
+
* effect while the lease still holds the cell, so the stamp the end
|
|
1890
|
+
* settles describes a cell that already carries the mutation.
|
|
1891
|
+
*
|
|
1892
|
+
* Heap-side stamps do not describe rows in the resident store, so store
|
|
1893
|
+
* mode has no lease to take and is left to the store's own provenance.
|
|
1894
|
+
*/
|
|
1895
|
+
async function _ownMutation(absPath, rpc, apply) {
|
|
1896
|
+
const key = _strip(absPath);
|
|
1897
|
+
const held = !_residentStorePresent() && __nimbusBeginOwnMutation(key);
|
|
1898
|
+
let receipt;
|
|
1899
|
+
let landed = false;
|
|
1900
|
+
try { receipt = await rpc(); landed = true; }
|
|
1901
|
+
finally {
|
|
1902
|
+
// The local effect follows the mutation landing, not the receipt: the
|
|
1903
|
+
// receipt only settles the stamp, and a supervisor that answers
|
|
1904
|
+
// without one still applied the bytes. The end runs even if applying
|
|
1905
|
+
// throws: a lease left open pins its cell at Infinity, which is the one
|
|
1906
|
+
// state in this protocol that can serve a stale byte forever.
|
|
1907
|
+
try { if (landed && apply) apply(receipt); }
|
|
1908
|
+
finally { __nimbusEndOwnMutation(key, held, receipt); }
|
|
1909
|
+
}
|
|
1910
|
+
return receipt;
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
// A sync caller has no frame to receive the outcome. The ledger already
|
|
1914
|
+
// marks a queued result handled and retains what the exit drain must
|
|
1915
|
+
// report; this only keeps a ledger-less embedding from raising an
|
|
1916
|
+
// unhandled rejection for a verdict nobody could catch.
|
|
1917
|
+
function _detachStructuralMutation(mutation) {
|
|
1918
|
+
if (mutation) mutation.then(undefined, () => undefined);
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
// A rename lives at two names. Its mutation is queued under the source;
|
|
1922
|
+
// this parks a fence under the destination so that anything queued for the
|
|
1923
|
+
// destination, or an ancestor wait that passes through it, is ordered
|
|
1924
|
+
// behind the move as well. The fence never fails on its own account.
|
|
1925
|
+
function _fenceVfsMutation(absPath, mutation) {
|
|
1926
|
+
if (!mutation || !_hasVfsMutationQueue()) return;
|
|
1927
|
+
__nimbusQueueVfsMutation(absPath, () => mutation.then(() => undefined, () => undefined), false);
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
// Queue the parked write for \`absPath\`, if any, on the path's mutation
|
|
1931
|
+
// tail. Registration is synchronous: a caller that queues its own mutation
|
|
1932
|
+
// on the same tail right after this call is ordered behind the flush.
|
|
1933
|
+
function _flushParkedWrite(absPath, supervisor) {
|
|
1934
|
+
const k = _strip(absPath);
|
|
1935
|
+
if (!__vfsWrites || !(k in __vfsWrites) || typeof supervisor.writeFile !== "function") {
|
|
1936
|
+
return Promise.resolve(undefined);
|
|
1937
|
+
}
|
|
1938
|
+
return __nimbusFlushVfsWrite(
|
|
1939
|
+
absPath,
|
|
1940
|
+
(content, snapshot) => _fsRpc(
|
|
1941
|
+
__nimbusPersistVfsWrite(supervisor, absPath, content, snapshot),
|
|
1942
|
+
"write", absPath,
|
|
1943
|
+
(result) => result,
|
|
1944
|
+
),
|
|
1945
|
+
);
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1743
1948
|
async function _flushLocalPathToSupervisor(absPath, supervisor) {
|
|
1744
1949
|
const k = _strip(absPath);
|
|
1745
1950
|
await _announceLocalDirs(absPath, supervisor);
|
|
1746
1951
|
if (__vfsWrites && k in __vfsWrites && typeof supervisor.writeFile === "function") {
|
|
1747
|
-
await
|
|
1748
|
-
absPath,
|
|
1749
|
-
(content, snapshot) => _fsRpc(
|
|
1750
|
-
__nimbusPersistVfsWrite(supervisor, absPath, content, snapshot),
|
|
1751
|
-
"write", absPath,
|
|
1752
|
-
(result) => result,
|
|
1753
|
-
),
|
|
1754
|
-
);
|
|
1952
|
+
await _flushParkedWrite(absPath, supervisor);
|
|
1755
1953
|
_markVfsStale();
|
|
1756
1954
|
}
|
|
1757
1955
|
// Pending sync chmod rides along with any flush of the same path
|
|
1758
1956
|
// (idempotent — the entry stays so local statSync remains coherent).
|
|
1759
1957
|
if (k in _localModes && typeof supervisor.chmod === "function") {
|
|
1760
|
-
await
|
|
1958
|
+
await _ownMutation(
|
|
1959
|
+
absPath,
|
|
1960
|
+
() => _fsRpc(supervisor.chmod(absPath, _localModes[k]), "chmod", absPath, (result) => result),
|
|
1961
|
+
);
|
|
1761
1962
|
_markVfsStale();
|
|
1762
1963
|
}
|
|
1763
1964
|
}
|
|
@@ -1852,16 +2053,26 @@ const __fsMod = (() => {
|
|
|
1852
2053
|
return stat;
|
|
1853
2054
|
}
|
|
1854
2055
|
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
2056
|
+
// The one Dirent shape: readdir({ withFileTypes }) sync and async, and
|
|
2057
|
+
// every Dir.read(). \`parentPath\` is Node's field; \`path\` its deprecated
|
|
2058
|
+
// alias that older callers still read.
|
|
2059
|
+
class __Dirent {
|
|
2060
|
+
constructor(name, type, parentPath) {
|
|
2061
|
+
this.name = name;
|
|
2062
|
+
this.parentPath = parentPath === undefined ? "" : String(parentPath);
|
|
2063
|
+
this.path = this.parentPath;
|
|
2064
|
+
this._isDir = type === "directory" || type === "dir";
|
|
2065
|
+
this._isSymlink = type === "symlink";
|
|
2066
|
+
}
|
|
2067
|
+
isFile() { return !this._isDir && !this._isSymlink; }
|
|
2068
|
+
isDirectory() { return this._isDir; }
|
|
2069
|
+
isSymbolicLink() { return this._isSymlink; }
|
|
2070
|
+
isBlockDevice() { return false; }
|
|
2071
|
+
isCharacterDevice() { return false; }
|
|
2072
|
+
isFIFO() { return false; }
|
|
2073
|
+
isSocket() { return false; }
|
|
1864
2074
|
}
|
|
2075
|
+
function _direntObject(name, type, parentPath) { return new __Dirent(name, type, parentPath); }
|
|
1865
2076
|
|
|
1866
2077
|
// Largest single ranged read issued against the supervisor. Every live
|
|
1867
2078
|
// read path (read streams, whole-file async reads) is expressed as a
|
|
@@ -2125,7 +2336,7 @@ const __fsMod = (() => {
|
|
|
2125
2336
|
}
|
|
2126
2337
|
if (opts?.withFileTypes) {
|
|
2127
2338
|
return entries
|
|
2128
|
-
.map((entry) => _direntObject(entry.name, entry.type))
|
|
2339
|
+
.map((entry) => _direntObject(entry.name, entry.type, absPath))
|
|
2129
2340
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
2130
2341
|
}
|
|
2131
2342
|
return entries.map((entry) => entry.name).sort();
|
|
@@ -2148,7 +2359,9 @@ const __fsMod = (() => {
|
|
|
2148
2359
|
async function _readlinkAsync(p) {
|
|
2149
2360
|
const supervisor = _supervisor();
|
|
2150
2361
|
if (supervisor && typeof supervisor.readlink === "function") {
|
|
2151
|
-
const
|
|
2362
|
+
const absPath = _resolve(p);
|
|
2363
|
+
await _awaitStructuralOrder(absPath);
|
|
2364
|
+
const target = await _fsRpc(supervisor.readlink(absPath), "readlink", p, (result) => result);
|
|
2152
2365
|
if (target !== null && target !== undefined) return target;
|
|
2153
2366
|
}
|
|
2154
2367
|
throw _fsErr("EINVAL", "readlink", p);
|
|
@@ -2157,7 +2370,9 @@ const __fsMod = (() => {
|
|
|
2157
2370
|
async function _symlinkAsync(target, path) {
|
|
2158
2371
|
const supervisor = _supervisor();
|
|
2159
2372
|
if (supervisor && typeof supervisor.symlink === "function") {
|
|
2160
|
-
|
|
2373
|
+
const absPath = _resolve(path);
|
|
2374
|
+
await _awaitStructuralOrder(absPath);
|
|
2375
|
+
await _fsRpc(supervisor.symlink(String(target), absPath), "symlink", path, () => undefined);
|
|
2161
2376
|
_markVfsStale();
|
|
2162
2377
|
return;
|
|
2163
2378
|
}
|
|
@@ -2170,8 +2385,10 @@ const __fsMod = (() => {
|
|
|
2170
2385
|
const supervisor = _supervisor();
|
|
2171
2386
|
if (supervisor && typeof supervisor.writeFile === "function") {
|
|
2172
2387
|
await _announceLocalDirs(absPath, supervisor);
|
|
2388
|
+
// The revision comes back so the ledger can stamp the cell: an async
|
|
2389
|
+
// whole write is the facet's own as much as a parked sync one is.
|
|
2173
2390
|
await __nimbusFlushVfsWrite(absPath, (content) =>
|
|
2174
|
-
_fsRpc(supervisor.writeFile(absPath, content), "write", p, () =>
|
|
2391
|
+
_fsRpc(supervisor.writeFile(absPath, content), "write", p, (result) => result)
|
|
2175
2392
|
);
|
|
2176
2393
|
_markVfsStale();
|
|
2177
2394
|
}
|
|
@@ -2194,41 +2411,13 @@ const __fsMod = (() => {
|
|
|
2194
2411
|
_markVfsStale();
|
|
2195
2412
|
}
|
|
2196
2413
|
|
|
2197
|
-
async
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
}
|
|
2205
|
-
|
|
2206
|
-
async function _unlinkAsync(p) {
|
|
2207
|
-
unlinkSync(p);
|
|
2208
|
-
const supervisor = _supervisor();
|
|
2209
|
-
if (supervisor && typeof supervisor.unlink === "function") {
|
|
2210
|
-
await _fsRpc(supervisor.unlink(_resolve(p)), "unlink", p, () => undefined);
|
|
2211
|
-
_markVfsStale();
|
|
2212
|
-
}
|
|
2213
|
-
}
|
|
2214
|
-
|
|
2215
|
-
async function _rmdirAsync(p) {
|
|
2216
|
-
rmdirSync(p);
|
|
2217
|
-
const supervisor = _supervisor();
|
|
2218
|
-
if (supervisor && typeof supervisor.rmdir === "function") {
|
|
2219
|
-
await _fsRpc(supervisor.rmdir(_resolve(p)), "rmdir", p, () => undefined);
|
|
2220
|
-
_markVfsStale();
|
|
2221
|
-
}
|
|
2222
|
-
}
|
|
2223
|
-
|
|
2224
|
-
async function _renameAsync(oldP, newP) {
|
|
2225
|
-
renameSync(oldP, newP);
|
|
2226
|
-
const supervisor = _supervisor();
|
|
2227
|
-
if (supervisor && typeof supervisor.rename === "function") {
|
|
2228
|
-
await _fsRpc(supervisor.rename(_resolve(oldP), _resolve(newP)), "rename", oldP, () => undefined);
|
|
2229
|
-
_markVfsStale();
|
|
2230
|
-
}
|
|
2231
|
-
}
|
|
2414
|
+
// The async structural calls ARE the sync ones, awaited: the same local
|
|
2415
|
+
// effect and the same queued authority RPC, so the two forms cannot
|
|
2416
|
+
// disagree about order, and a program mixing them sees one sequence.
|
|
2417
|
+
async function _mkdirAsync(p, opts) { await _mkdirQueued(p, opts); }
|
|
2418
|
+
async function _unlinkAsync(p) { await _unlinkQueued(p); }
|
|
2419
|
+
async function _rmdirAsync(p) { await _rmdirQueued(p); }
|
|
2420
|
+
async function _renameAsync(oldP, newP) { await _renameQueued(oldP, newP); }
|
|
2232
2421
|
|
|
2233
2422
|
async function _truncateAsync(p, len) {
|
|
2234
2423
|
const absPath = _resolve(p);
|
|
@@ -2246,6 +2435,9 @@ const __fsMod = (() => {
|
|
|
2246
2435
|
(content, snapshot) =>
|
|
2247
2436
|
__nimbusPersistVfsWrite(supervisor, absPath, content, snapshot),
|
|
2248
2437
|
);
|
|
2438
|
+
// Persisting the append drops the resident cell (the ledger
|
|
2439
|
+
// refetches it on the next live read), so there is no cell to
|
|
2440
|
+
// trim or stamp behind this truncate.
|
|
2249
2441
|
await __nimbusQueueVfsMutation(absPath, async () => {
|
|
2250
2442
|
await flush;
|
|
2251
2443
|
await _fsRpc(
|
|
@@ -2267,12 +2459,15 @@ const __fsMod = (() => {
|
|
|
2267
2459
|
// Live file is the source of truth — supervisor trims only the
|
|
2268
2460
|
// boundary chunk; ENOENT propagates when it does not exist.
|
|
2269
2461
|
const generation = __vfsWriteGenerations[k];
|
|
2270
|
-
await __nimbusQueueVfsMutation(absPath, () =>
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2462
|
+
await __nimbusQueueVfsMutation(absPath, () => _ownMutation(
|
|
2463
|
+
absPath,
|
|
2464
|
+
() => _fsRpc(supervisor.fsTruncate(absPath, size), "truncate", p, (result) => result),
|
|
2465
|
+
() => {
|
|
2466
|
+
if (__vfsWriteGenerations[k] === generation && localCell !== undefined) {
|
|
2467
|
+
_truncateLocalCell(absPath, size);
|
|
2468
|
+
}
|
|
2469
|
+
},
|
|
2470
|
+
));
|
|
2276
2471
|
_markVfsStale();
|
|
2277
2472
|
return;
|
|
2278
2473
|
}
|
|
@@ -2292,9 +2487,9 @@ const __fsMod = (() => {
|
|
|
2292
2487
|
_recordLocalTimes(absPath, atime, mtime, "lutimes", p);
|
|
2293
2488
|
}
|
|
2294
2489
|
|
|
2295
|
-
async function _utimesAsync(p, atime, mtime, opts) {
|
|
2490
|
+
async function _utimesAsync(p, atime, mtime, opts, syscallOverride) {
|
|
2296
2491
|
const followSymlinks = !(opts && opts.followSymlinks === false);
|
|
2297
|
-
const syscall = followSymlinks ? "utimes" : "lutimes";
|
|
2492
|
+
const syscall = syscallOverride || (followSymlinks ? "utimes" : "lutimes");
|
|
2298
2493
|
const absPath = _resolve(p);
|
|
2299
2494
|
const supervisor = _supervisor();
|
|
2300
2495
|
let localExists = false;
|
|
@@ -2305,11 +2500,16 @@ const __fsMod = (() => {
|
|
|
2305
2500
|
const time = _recordLocalTimes(absPath, atime, mtime, syscall, p);
|
|
2306
2501
|
if (supervisor && typeof supervisor.utimes === "function") {
|
|
2307
2502
|
await _flushLocalPathToSupervisor(absPath, supervisor);
|
|
2308
|
-
|
|
2503
|
+
// Ordered behind the path's pending mutations: an fd write queued a
|
|
2504
|
+
// moment ago (modern-tar writes, then futimes, then closes) would
|
|
2505
|
+
// otherwise land AFTER the timestamp and reset it to "now".
|
|
2506
|
+
const leased = () => _ownMutation(absPath, () => _fsRpc(
|
|
2309
2507
|
supervisor.utimes(absPath, time.atimeMs, time.mtimeMs),
|
|
2310
2508
|
syscall, p,
|
|
2311
|
-
() =>
|
|
2312
|
-
);
|
|
2509
|
+
(result) => result,
|
|
2510
|
+
));
|
|
2511
|
+
if (_hasVfsMutationQueue()) await __nimbusQueueVfsMutation(absPath, leased);
|
|
2512
|
+
else await leased();
|
|
2313
2513
|
_markVfsStale();
|
|
2314
2514
|
return;
|
|
2315
2515
|
}
|
|
@@ -2359,11 +2559,46 @@ const __fsMod = (() => {
|
|
|
2359
2559
|
const nextUid = _coerceId(uid, syscall, p);
|
|
2360
2560
|
const nextGid = _coerceId(gid, syscall, p);
|
|
2361
2561
|
await _flushLocalPathToSupervisor(absPath, supervisor);
|
|
2362
|
-
await
|
|
2562
|
+
await _ownMutation(
|
|
2563
|
+
absPath,
|
|
2564
|
+
() => _fsRpc(supervisor.chown(absPath, nextUid, nextGid, opts), syscall, p, (result) => result),
|
|
2565
|
+
() => {
|
|
2566
|
+
const meta = _metadata(absPath);
|
|
2567
|
+
if (meta) { meta.uid = nextUid; meta.gid = nextGid; }
|
|
2568
|
+
},
|
|
2569
|
+
);
|
|
2570
|
+
_markVfsStale();
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
// Sync ownership change: the stat record is updated locally at once and
|
|
2574
|
+
// the authority RPC is queued behind the path's pending flush, the same
|
|
2575
|
+
// parked model mkdirSync/unlinkSync use. Without an authority there is no
|
|
2576
|
+
// owner table to change, so the answer is the same ENOSYS as the async form.
|
|
2577
|
+
function _chownQueued(p, uid, gid, opts, syscall) {
|
|
2578
|
+
const followSymlinks = !(opts && opts.followSymlinks === false);
|
|
2579
|
+
const absPath = _resolve(p);
|
|
2580
|
+
const supervisor = _supervisor();
|
|
2581
|
+
if (!supervisor || typeof supervisor.chown !== "function") {
|
|
2582
|
+
if (!existsSync(p)) throw _fsErr("ENOENT", syscall, p);
|
|
2583
|
+
throw _fsErr("ENOSYS", syscall, p);
|
|
2584
|
+
}
|
|
2585
|
+
const nextUid = _coerceId(uid, syscall, p);
|
|
2586
|
+
const nextGid = _coerceId(gid, syscall, p);
|
|
2363
2587
|
const meta = _metadata(absPath);
|
|
2364
2588
|
if (meta) { meta.uid = nextUid; meta.gid = nextGid; }
|
|
2365
|
-
|
|
2589
|
+
// The parked write must be registered on the tail BEFORE the chown is
|
|
2590
|
+
// (a flush awaited from inside the queued step would queue behind it
|
|
2591
|
+
// and wait on itself), so it is the synchronous-registering flush.
|
|
2592
|
+
return _queueStructuralMutation(
|
|
2593
|
+
absPath, syscall, p,
|
|
2594
|
+
(s) => s.chown(absPath, nextUid, nextGid, followSymlinks ? undefined : { followSymlinks: false }),
|
|
2595
|
+
() => _flushParkedWrite(absPath, supervisor),
|
|
2596
|
+
"chown",
|
|
2597
|
+
);
|
|
2366
2598
|
}
|
|
2599
|
+
function chownSync(p, uid, gid) { _detachStructuralMutation(_chownQueued(p, uid, gid, undefined, "chown")); }
|
|
2600
|
+
function lchownSync(p, uid, gid) { _detachStructuralMutation(_chownQueued(p, uid, gid, { followSymlinks: false }, "lchown")); }
|
|
2601
|
+
function lchmodSync(p, mode) { chmodSync(p, mode); }
|
|
2367
2602
|
|
|
2368
2603
|
function _modeAllows(meta, want) {
|
|
2369
2604
|
if (want === 0) return true;
|
|
@@ -2756,46 +2991,67 @@ const __fsMod = (() => {
|
|
|
2756
2991
|
(!!__vfsDirs && fp in __vfsDirs) ||
|
|
2757
2992
|
_metadata(fp)?.type === "directory" ||
|
|
2758
2993
|
(!!__vfsBundle && __residentAnyUnder(fp + "/"));
|
|
2759
|
-
return
|
|
2994
|
+
return _direntObject(n, isDir ? "directory" : "file", absPath);
|
|
2760
2995
|
});
|
|
2761
2996
|
}
|
|
2762
2997
|
return arr;
|
|
2763
2998
|
}
|
|
2764
2999
|
|
|
2765
3000
|
// ── mkdirSync ──
|
|
2766
|
-
|
|
3001
|
+
// The sync effect, then the authority mutation queued behind it (see
|
|
3002
|
+
// _queueStructuralMutation). One RPC whatever the depth: the async form
|
|
3003
|
+
// sends the path alone and the authority's mkdir creates the ancestors.
|
|
3004
|
+
function _mkdirQueued(p, opts) {
|
|
2767
3005
|
const absPath = _resolve(p);
|
|
2768
3006
|
const k = _strip(absPath);
|
|
3007
|
+
const created = [];
|
|
2769
3008
|
if (opts?.recursive) {
|
|
2770
3009
|
const parts = k.split("/").filter(Boolean);
|
|
2771
3010
|
let cur = "";
|
|
2772
|
-
for (const part of parts) { cur = cur ? cur + "/" + part : part; __vfsDirs[cur] = true; }
|
|
3011
|
+
for (const part of parts) { cur = cur ? cur + "/" + part : part; __vfsDirs[cur] = true; created.push(cur); }
|
|
2773
3012
|
} else {
|
|
2774
3013
|
__vfsDirs[k] = true;
|
|
3014
|
+
created.push(k);
|
|
2775
3015
|
}
|
|
3016
|
+
const queued = _queueStructuralMutation(absPath, "mkdir", p, (supervisor) => supervisor.mkdir(absPath));
|
|
3017
|
+
// Told, not merely known locally: _announceLocalDirs must not issue a
|
|
3018
|
+
// second mkdir for a directory whose own RPC is already in the queue.
|
|
3019
|
+
if (queued) for (const dir of created) _announcedDirs.add(dir);
|
|
3020
|
+
return queued;
|
|
2776
3021
|
}
|
|
3022
|
+
function mkdirSync(p, opts) { _detachStructuralMutation(_mkdirQueued(p, opts)); }
|
|
2777
3023
|
|
|
2778
3024
|
// ── unlinkSync ──
|
|
2779
|
-
function
|
|
3025
|
+
function _unlinkQueued(p) {
|
|
2780
3026
|
const absPath = _resolve(p);
|
|
2781
3027
|
const k = _strip(absPath);
|
|
2782
3028
|
if (__vfsBundle) delete __vfsBundle[k];
|
|
2783
3029
|
if (__vfsWrites) delete __vfsWrites[k];
|
|
2784
3030
|
_forgetSyncPath(k);
|
|
3031
|
+
return _queueStructuralMutation(absPath, "unlink", p, (supervisor) => supervisor.unlink(absPath));
|
|
2785
3032
|
}
|
|
3033
|
+
function unlinkSync(p) { _detachStructuralMutation(_unlinkQueued(p)); }
|
|
2786
3034
|
|
|
2787
3035
|
// ── rmdirSync ──
|
|
2788
|
-
function
|
|
3036
|
+
function _rmdirQueued(p) {
|
|
2789
3037
|
const absPath = _resolve(p);
|
|
2790
3038
|
const k = _strip(absPath);
|
|
2791
3039
|
if (__vfsDirs) delete __vfsDirs[k];
|
|
2792
3040
|
_forgetSyncPath(k);
|
|
3041
|
+
return _queueStructuralMutation(
|
|
3042
|
+
absPath, "rmdir", p,
|
|
3043
|
+
(supervisor) => supervisor.rmdir(absPath),
|
|
3044
|
+
() => __nimbusAwaitSubtreeMutations(absPath),
|
|
3045
|
+
);
|
|
2793
3046
|
}
|
|
3047
|
+
function rmdirSync(p) { _detachStructuralMutation(_rmdirQueued(p)); }
|
|
2794
3048
|
|
|
2795
3049
|
// ── renameSync ──
|
|
2796
|
-
function
|
|
2797
|
-
const
|
|
2798
|
-
const
|
|
3050
|
+
function _renameQueued(oldP, newP) {
|
|
3051
|
+
const oldAbs = _resolve(oldP);
|
|
3052
|
+
const newAbs = _resolve(newP);
|
|
3053
|
+
const oldK = _strip(oldAbs);
|
|
3054
|
+
const newK = _strip(newAbs);
|
|
2799
3055
|
const content = __vfsBundle?.[oldK] ?? __vfsWrites?.[oldK];
|
|
2800
3056
|
if (content !== undefined) {
|
|
2801
3057
|
_parkWrite(newK, content);
|
|
@@ -2809,13 +3065,181 @@ const __fsMod = (() => {
|
|
|
2809
3065
|
const moved = metadata ? metadata[oldK] : undefined;
|
|
2810
3066
|
_forgetSyncPath(oldK);
|
|
2811
3067
|
if (moved) metadata[newK] = moved;
|
|
2812
|
-
}
|
|
3068
|
+
} else if (__vfsDirs && oldK in __vfsDirs) {
|
|
3069
|
+
// A directory this process made travels under its new name, so the
|
|
3070
|
+
// sync view stops listing the old one and _announceLocalDirs cannot
|
|
3071
|
+
// re-create it at the authority behind the rename.
|
|
3072
|
+
const prefix = oldK + "/";
|
|
3073
|
+
for (const dk of Object.keys(__vfsDirs)) {
|
|
3074
|
+
if (dk !== oldK && !dk.startsWith(prefix)) continue;
|
|
3075
|
+
const moved = newK + dk.slice(oldK.length);
|
|
3076
|
+
__vfsDirs[moved] = true;
|
|
3077
|
+
delete __vfsDirs[dk];
|
|
3078
|
+
if (_announcedDirs.has(dk)) _announcedDirs.add(moved);
|
|
3079
|
+
}
|
|
3080
|
+
_forgetSyncTree(oldK);
|
|
3081
|
+
}
|
|
3082
|
+
const queued = _queueStructuralMutation(
|
|
3083
|
+
oldAbs, "rename", oldP,
|
|
3084
|
+
(supervisor) => supervisor.rename(oldAbs, newAbs),
|
|
3085
|
+
// The queue orders the move behind the source's ancestors; the move
|
|
3086
|
+
// also needs the destination's ancestors to exist and every pending
|
|
3087
|
+
// mutation beneath the source to have landed under the old name.
|
|
3088
|
+
() => Promise.all([__nimbusAwaitAncestorMutations(newAbs), __nimbusAwaitSubtreeMutations(oldAbs)]),
|
|
3089
|
+
);
|
|
3090
|
+
_fenceVfsMutation(newAbs, queued);
|
|
3091
|
+
return queued;
|
|
2813
3092
|
}
|
|
3093
|
+
function renameSync(oldP, newP) { _detachStructuralMutation(_renameQueued(oldP, newP)); }
|
|
2814
3094
|
|
|
2815
3095
|
// ── copyFileSync ──
|
|
2816
|
-
|
|
2817
|
-
|
|
3096
|
+
// Bytes, not utf8: a utf8 round trip replaces every byte ≥ 0x80 with
|
|
3097
|
+
// U+FFFD, which is how a copied .png or .woff2 arrived corrupted.
|
|
3098
|
+
function copyFileSync(src, dest, mode) {
|
|
3099
|
+
if ((Number(mode) & __fsConstants.COPYFILE_EXCL) !== 0 && existsSync(dest)) {
|
|
3100
|
+
throw _fsErr("EEXIST", "copyfile", dest);
|
|
3101
|
+
}
|
|
3102
|
+
writeFileSync(dest, readFileSync(src));
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
// ── rmSync / rm ──
|
|
3106
|
+
// The local tables are edited at once (the same retraction unlinkSync and
|
|
3107
|
+
// rmdirSync perform, over the whole subtree when recursive) and ONE
|
|
3108
|
+
// authority RPC — fsRemove, which the bridge serves as unlink or a bounded
|
|
3109
|
+
// recursive removal — is queued behind every pending mutation beneath the
|
|
3110
|
+
// path. ENOENT under \`force\` is the authority's to swallow; locally an
|
|
3111
|
+
// unknown path may still exist live, so it is asked rather than answered.
|
|
3112
|
+
function _rmQueued(p, opts, sync) {
|
|
3113
|
+
const o = opts || {};
|
|
3114
|
+
const absPath = _resolve(p);
|
|
3115
|
+
const k = _strip(absPath);
|
|
3116
|
+
const st = statSync(p, { throwIfNoEntry: false });
|
|
3117
|
+
const supervisor = _supervisor();
|
|
3118
|
+
const canRemove = !!supervisor && typeof supervisor.fsRemove === "function";
|
|
3119
|
+
if (st === undefined) {
|
|
3120
|
+
if (!canRemove) {
|
|
3121
|
+
if (o.force) return null;
|
|
3122
|
+
throw _fsErr("ENOENT", "rm", p);
|
|
3123
|
+
}
|
|
3124
|
+
// A path the sync view cannot map may still exist live (born after
|
|
3125
|
+
// boot). The async form asks the authority, whose ENOENT is the real
|
|
3126
|
+
// one; the sync form has no frame to receive that answer, so it gives
|
|
3127
|
+
// statSync's own provisional not-found unless \`force\` makes the
|
|
3128
|
+
// verdict irrelevant.
|
|
3129
|
+
if (sync && !o.force) throw _fsErr("ENOENT", "rm", p);
|
|
3130
|
+
}
|
|
3131
|
+
if (st !== undefined && st.isDirectory() && !o.recursive) {
|
|
3132
|
+
throw _fsErr("ERR_FS_EISDIR", "rm", p);
|
|
3133
|
+
}
|
|
3134
|
+
const prefix = k + "/";
|
|
3135
|
+
// A parked write is content the authority may never have seen: the
|
|
3136
|
+
// caller's file demonstrably existed here, so the removal succeeds, and
|
|
3137
|
+
// the authority is told "if present" rather than made to fail on a file
|
|
3138
|
+
// that was only ever local.
|
|
3139
|
+
let parked = false;
|
|
3140
|
+
if (__vfsBundle) {
|
|
3141
|
+
if (k in __vfsBundle) delete __vfsBundle[k];
|
|
3142
|
+
if (o.recursive) for (const bk of __residentUnder(prefix)) delete __vfsBundle[bk];
|
|
3143
|
+
}
|
|
3144
|
+
delete __vfsBundleRevisions[k];
|
|
3145
|
+
if (__vfsWrites) {
|
|
3146
|
+
for (const wk of Object.keys(__vfsWrites)) {
|
|
3147
|
+
if (wk === k || (o.recursive && wk.startsWith(prefix))) { delete __vfsWrites[wk]; parked = true; }
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
if (__vfsDirs) {
|
|
3151
|
+
for (const dk of Object.keys(__vfsDirs)) {
|
|
3152
|
+
if (dk === k || (o.recursive && dk.startsWith(prefix))) delete __vfsDirs[dk];
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
if (o.recursive) _forgetSyncTree(k); else _forgetSyncPath(k);
|
|
3156
|
+
if (!canRemove) {
|
|
3157
|
+
// No fsRemove: a plain file still has the unlink RPC.
|
|
3158
|
+
if (st !== undefined && !st.isDirectory()) {
|
|
3159
|
+
return _queueStructuralMutation(absPath, "rm", p, (s) => s.unlink(absPath), undefined, "unlink");
|
|
3160
|
+
}
|
|
3161
|
+
return null;
|
|
3162
|
+
}
|
|
3163
|
+
return _queueStructuralMutation(
|
|
3164
|
+
absPath, "rm", p,
|
|
3165
|
+
(s) => s.fsRemove(absPath, { recursive: !!o.recursive, force: !!o.force || parked }),
|
|
3166
|
+
() => __nimbusAwaitSubtreeMutations(absPath),
|
|
3167
|
+
"fsRemove",
|
|
3168
|
+
);
|
|
3169
|
+
}
|
|
3170
|
+
function rmSync(p, opts) { _detachStructuralMutation(_rmQueued(p, opts, true)); }
|
|
3171
|
+
async function _rmAsync(p, opts) { await _rmQueued(p, opts, false); }
|
|
3172
|
+
|
|
3173
|
+
// ── cpSync ──
|
|
3174
|
+
// A walk of the resident view; every file copy is a parked sync write that
|
|
3175
|
+
// the existing write-back drains, and every directory a queued mkdir.
|
|
3176
|
+
function cpSync(src, dest, opts) {
|
|
3177
|
+
const o = opts || {};
|
|
3178
|
+
const st = statSync(src, { throwIfNoEntry: false });
|
|
3179
|
+
if (st === undefined) throw _fsErr("ENOENT", "cp", src);
|
|
3180
|
+
if (typeof o.filter === "function" && !o.filter(String(src), String(dest))) return;
|
|
3181
|
+
if (!st.isDirectory()) {
|
|
3182
|
+
const destSt = statSync(dest, { throwIfNoEntry: false });
|
|
3183
|
+
if (destSt !== undefined) {
|
|
3184
|
+
if (destSt.isDirectory()) throw _fsErr("EISDIR", "cp", dest);
|
|
3185
|
+
if (o.errorOnExist) throw _fsErr("ERR_FS_CP_EEXIST", "cp", dest);
|
|
3186
|
+
if (o.force === false) return;
|
|
3187
|
+
}
|
|
3188
|
+
copyFileSync(src, dest);
|
|
3189
|
+
return;
|
|
3190
|
+
}
|
|
3191
|
+
if (!o.recursive) throw _fsErr("ERR_FS_EISDIR", "cp", src);
|
|
3192
|
+
mkdirSync(dest, { recursive: true });
|
|
3193
|
+
for (const ent of readdirSync(src, { withFileTypes: true })) {
|
|
3194
|
+
cpSync(__pathMod.join(String(src), ent.name), __pathMod.join(String(dest), ent.name), o);
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
|
|
3198
|
+
// ── mkdtemp ──
|
|
3199
|
+
// Node appends six characters from [a-zA-Z0-9]; a collision with a name
|
|
3200
|
+
// the sync view already knows is retried rather than reused.
|
|
3201
|
+
const _MKDTEMP_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
3202
|
+
function _mkdtempName(prefix) {
|
|
3203
|
+
for (let attempt = 0; attempt < 64; attempt++) {
|
|
3204
|
+
let suffix = "";
|
|
3205
|
+
for (let i = 0; i < 6; i++) suffix += _MKDTEMP_ALPHABET[Math.floor(Math.random() * _MKDTEMP_ALPHABET.length)];
|
|
3206
|
+
const name = String(prefix) + suffix;
|
|
3207
|
+
if (!existsSync(name)) return name;
|
|
3208
|
+
}
|
|
3209
|
+
throw _fsErr("EEXIST", "mkdtemp", String(prefix) + "XXXXXX");
|
|
2818
3210
|
}
|
|
3211
|
+
function mkdtempSync(prefix) {
|
|
3212
|
+
const name = _mkdtempName(prefix);
|
|
3213
|
+
mkdirSync(name);
|
|
3214
|
+
return name;
|
|
3215
|
+
}
|
|
3216
|
+
async function _mkdtempAsync(prefix) {
|
|
3217
|
+
const name = _mkdtempName(prefix);
|
|
3218
|
+
await _mkdirAsync(name);
|
|
3219
|
+
return name;
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3222
|
+
// ── truncateSync ──
|
|
3223
|
+
// The resident-view rule of ftruncateSync applied to a path: trim the
|
|
3224
|
+
// resident cell and park it, refuse EAGAIN when the bytes are not here.
|
|
3225
|
+
function truncateSync(p, len) {
|
|
3226
|
+
const absPath = _resolve(p);
|
|
3227
|
+
const st = statSync(p, { throwIfNoEntry: false });
|
|
3228
|
+
if (st === undefined) throw _fsErr("ENOENT", "truncate", p);
|
|
3229
|
+
if (st.isDirectory()) throw _fsErr("EISDIR", "truncate", p);
|
|
3230
|
+
const size = Math.max(0, Math.trunc(Number(len) || 0));
|
|
3231
|
+
_ensureWritable(absPath, "truncate", p);
|
|
3232
|
+
const base = _residentWriteBase(absPath, p, "truncate");
|
|
3233
|
+
const next = new Uint8Array(size);
|
|
3234
|
+
next.set(base.subarray(0, Math.min(size, base.byteLength)), 0);
|
|
3235
|
+
_parkWrite(_strip(absPath), next);
|
|
3236
|
+
_markVfsStale();
|
|
3237
|
+
}
|
|
3238
|
+
|
|
3239
|
+
// ── link / linkSync ──
|
|
3240
|
+
// The VFS has no hard links and a copy would lie about sharing an inode,
|
|
3241
|
+
// so both forms answer ENOSYS like fs.promises.link always has.
|
|
3242
|
+
function linkSync(existingPath, newPath) { throw _fsErr("ENOSYS", "link", newPath); }
|
|
2819
3243
|
|
|
2820
3244
|
// ── realpathSync (X.5-T per X5Z5-plan §4.3 + X526b-retro §3.1) ──
|
|
2821
3245
|
// Sync realpath stays local and identity-resolves. Async symlink
|
|
@@ -2863,7 +3287,7 @@ const __fsMod = (() => {
|
|
|
2863
3287
|
// ── open-flag parsing for fs.promises.open ──
|
|
2864
3288
|
function _parseOpenFlags(flags) {
|
|
2865
3289
|
if (typeof flags === "number") {
|
|
2866
|
-
const O_WRONLY
|
|
3290
|
+
const { O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_TRUNC, O_APPEND, O_DIRECTORY } = __fsConstants;
|
|
2867
3291
|
return {
|
|
2868
3292
|
read: (flags & O_WRONLY) === 0,
|
|
2869
3293
|
write: (flags & (O_WRONLY | O_RDWR)) !== 0,
|
|
@@ -2871,15 +3295,16 @@ const __fsMod = (() => {
|
|
|
2871
3295
|
create: (flags & O_CREAT) !== 0,
|
|
2872
3296
|
truncate: (flags & O_TRUNC) !== 0,
|
|
2873
3297
|
exclusive: (flags & O_EXCL) !== 0,
|
|
3298
|
+
directory: (flags & O_DIRECTORY) !== 0,
|
|
2874
3299
|
};
|
|
2875
3300
|
}
|
|
2876
3301
|
const s = String(flags === undefined || flags === null ? "r" : flags);
|
|
2877
3302
|
const plus = s.indexOf("+") !== -1;
|
|
2878
3303
|
const exclusive = s.indexOf("x") !== -1;
|
|
2879
3304
|
const base = s.charAt(0);
|
|
2880
|
-
if (base === "w") return { read: plus, write: true, append: false, create: true, truncate: true, exclusive };
|
|
2881
|
-
if (base === "a") return { read: plus, write: true, append: true, create: true, truncate: false, exclusive };
|
|
2882
|
-
return { read: true, write: plus, append: false, create: false, truncate: false, exclusive };
|
|
3305
|
+
if (base === "w") return { read: plus, write: true, append: false, create: true, truncate: true, exclusive, directory: false };
|
|
3306
|
+
if (base === "a") return { read: plus, write: true, append: true, create: true, truncate: false, exclusive, directory: false };
|
|
3307
|
+
return { read: true, write: plus, append: false, create: false, truncate: false, exclusive, directory: false };
|
|
2883
3308
|
}
|
|
2884
3309
|
|
|
2885
3310
|
// ── FileHandle — returned from fs.promises.open ──
|
|
@@ -2889,6 +3314,38 @@ const __fsMod = (() => {
|
|
|
2889
3314
|
// hibernation and partial reads/writes never move whole files. Unflushed
|
|
2890
3315
|
// sync writes (__vfsWrites) take read precedence; the local sync view is
|
|
2891
3316
|
// overlaid on writes so readFileSync stays coherent.
|
|
3317
|
+
// The prior content a sync positional write or truncate lays its bytes
|
|
3318
|
+
// over. Shared by the descriptor forms and truncateSync.
|
|
3319
|
+
function _residentWriteBase(absPath, p, syscall) {
|
|
3320
|
+
const cell = _writtenCell(absPath);
|
|
3321
|
+
if (cell !== undefined) {
|
|
3322
|
+
const denial = _denialCode(cell);
|
|
3323
|
+
if (denial) throw _fsErr(denial, syscall, p);
|
|
3324
|
+
_residencySatisfied(absPath);
|
|
3325
|
+
return _asBytes(cell);
|
|
3326
|
+
}
|
|
3327
|
+
const asyncForm = "the async fs." + syscall + "/fs.promises form";
|
|
3328
|
+
const st = statSync(p, { throwIfNoEntry: false });
|
|
3329
|
+
// Non-resident and non-empty: writing onto a zero-filled base would
|
|
3330
|
+
// silently destroy the bytes we cannot see. Refuse instead.
|
|
3331
|
+
if (st !== undefined && st.size !== 0) throw _notResidentError(absPath, p, syscall, asyncForm);
|
|
3332
|
+
// Absent or empty — an empty base IS the true prior content, but only
|
|
3333
|
+
// where absence is knowledge. A path the view never mapped may hold bytes
|
|
3334
|
+
// this process cannot see, and an O_APPEND descriptor onto a zero-filled
|
|
3335
|
+
// base would overwrite the file with the fragment it meant to add.
|
|
3336
|
+
if (st === undefined && !_absenceIsKnown(absPath)) throw _refuseUnmapped(absPath, p, syscall, asyncForm);
|
|
3337
|
+
return new Uint8Array(0);
|
|
3338
|
+
}
|
|
3339
|
+
|
|
3340
|
+
async function _fsyncAsync(absPath, p) {
|
|
3341
|
+
const supervisor = _supervisor();
|
|
3342
|
+
if (supervisor) {
|
|
3343
|
+
await _flushLocalPathToSupervisor(absPath, supervisor);
|
|
3344
|
+
await _awaitStructuralOrder(absPath);
|
|
3345
|
+
}
|
|
3346
|
+
_markVfsStale();
|
|
3347
|
+
}
|
|
3348
|
+
|
|
2892
3349
|
let __nextFileHandleFd = 3;
|
|
2893
3350
|
const __fileHandles = new Map();
|
|
2894
3351
|
class __FileHandle {
|
|
@@ -2963,16 +3420,21 @@ const __fsMod = (() => {
|
|
|
2963
3420
|
);
|
|
2964
3421
|
if (meta && meta.type === "file") writeAt = Number(meta.size) || 0;
|
|
2965
3422
|
}
|
|
2966
|
-
await
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
3423
|
+
await _ownMutation(
|
|
3424
|
+
this._abs,
|
|
3425
|
+
() => _fsRpc(
|
|
3426
|
+
supervisor.fsWriteRange(this._abs, writeAt, bytes),
|
|
3427
|
+
"write", this._path,
|
|
3428
|
+
(result) => result,
|
|
3429
|
+
),
|
|
3430
|
+
() => {
|
|
3431
|
+
const key = _strip(this._abs);
|
|
3432
|
+
if (!Object.prototype.hasOwnProperty.call(__vfsWrites, key) &&
|
|
3433
|
+
__vfsWriteGenerations[key] === overlayGeneration) {
|
|
3434
|
+
_overlayLocalCell(this._abs, writeAt, bytes);
|
|
3435
|
+
}
|
|
3436
|
+
},
|
|
2970
3437
|
);
|
|
2971
|
-
const key = _strip(this._abs);
|
|
2972
|
-
if (!Object.prototype.hasOwnProperty.call(__vfsWrites, key) &&
|
|
2973
|
-
__vfsWriteGenerations[key] === overlayGeneration) {
|
|
2974
|
-
_overlayLocalCell(this._abs, writeAt, bytes);
|
|
2975
|
-
}
|
|
2976
3438
|
});
|
|
2977
3439
|
_markVfsStale();
|
|
2978
3440
|
} else {
|
|
@@ -3028,24 +3490,7 @@ const __fsMod = (() => {
|
|
|
3028
3490
|
if (bytes === undefined) throw this._notResident(syscall);
|
|
3029
3491
|
return bytes;
|
|
3030
3492
|
}
|
|
3031
|
-
_writeBase(syscall) {
|
|
3032
|
-
const bytes = this._residentBytes(syscall);
|
|
3033
|
-
if (bytes !== undefined) return bytes;
|
|
3034
|
-
const st = statSync(this._path, { throwIfNoEntry: false });
|
|
3035
|
-
// Non-resident and non-empty: writing onto a zero-filled base would
|
|
3036
|
-
// silently destroy the bytes we cannot see. Refuse instead.
|
|
3037
|
-
if (st !== undefined && st.size !== 0) throw this._notResident(syscall);
|
|
3038
|
-
// Absent or empty — an empty base IS the true prior content, but only
|
|
3039
|
-
// where absence is knowledge. A path the view never mapped may hold bytes
|
|
3040
|
-
// this process cannot see, and an O_APPEND descriptor onto a zero-filled
|
|
3041
|
-
// base would overwrite the file with the fragment it meant to add.
|
|
3042
|
-
if (st === undefined && !_absenceIsKnown(this._abs)) {
|
|
3043
|
-
throw _refuseUnmapped(
|
|
3044
|
-
this._abs, this._path, syscall, "the async fs." + syscall + "/fs.promises form",
|
|
3045
|
-
);
|
|
3046
|
-
}
|
|
3047
|
-
return new Uint8Array(0);
|
|
3048
|
-
}
|
|
3493
|
+
_writeBase(syscall) { return _residentWriteBase(this._abs, this._path, syscall); }
|
|
3049
3494
|
_commit(next) {
|
|
3050
3495
|
_parkWrite(_strip(this._abs), next);
|
|
3051
3496
|
_markVfsStale();
|
|
@@ -3095,9 +3540,38 @@ const __fsMod = (() => {
|
|
|
3095
3540
|
}
|
|
3096
3541
|
async chmod(mode) { this._assertOpen("fchmod"); await _chmodAsync(this._path, mode); }
|
|
3097
3542
|
async chown(uid, gid) { this._assertOpen("fchown"); await _chownAsync(this._path, uid, gid, undefined, "fchown"); }
|
|
3098
|
-
async utimes(atime, mtime) { this._assertOpen("futimes"); await _utimesAsync(this._path, atime, mtime); }
|
|
3099
|
-
|
|
3100
|
-
|
|
3543
|
+
async utimes(atime, mtime) { this._assertOpen("futimes"); await _utimesAsync(this._path, atime, mtime, undefined, "futimes"); }
|
|
3544
|
+
// Durability here means: every byte parked for this path has reached
|
|
3545
|
+
// the authority and every mutation queued for it has landed. The bridge
|
|
3546
|
+
// itself is synchronously durable, so no further RPC is owed.
|
|
3547
|
+
async sync() { this._assertOpen("fsync"); await _fsyncAsync(this._abs, this._path); }
|
|
3548
|
+
async datasync() { this._assertOpen("fdatasync"); await _fsyncAsync(this._abs, this._path); }
|
|
3549
|
+
// Scatter/gather over the ranged read/write: one sequential pass per
|
|
3550
|
+
// buffer, stopping at the first short read, positions advanced by hand
|
|
3551
|
+
// when an explicit one was given so the file position stays untouched.
|
|
3552
|
+
async readv(buffers, position) {
|
|
3553
|
+
this._assertOpen("read");
|
|
3554
|
+
let bytesRead = 0;
|
|
3555
|
+
let pos = _isCurrentPos(position) ? null : Number(position);
|
|
3556
|
+
for (const buffer of buffers) {
|
|
3557
|
+
const r = await this.read(buffer, 0, buffer.byteLength, pos);
|
|
3558
|
+
bytesRead += r.bytesRead;
|
|
3559
|
+
if (pos !== null) pos += r.bytesRead;
|
|
3560
|
+
if (r.bytesRead < buffer.byteLength) break;
|
|
3561
|
+
}
|
|
3562
|
+
return { bytesRead, buffers };
|
|
3563
|
+
}
|
|
3564
|
+
async writev(buffers, position) {
|
|
3565
|
+
this._assertOpen("write");
|
|
3566
|
+
let bytesWritten = 0;
|
|
3567
|
+
let pos = _isCurrentPos(position) ? null : Number(position);
|
|
3568
|
+
for (const buffer of buffers) {
|
|
3569
|
+
const r = await this.write(buffer, 0, buffer.byteLength, pos);
|
|
3570
|
+
bytesWritten += r.bytesWritten;
|
|
3571
|
+
if (pos !== null) pos += r.bytesWritten;
|
|
3572
|
+
}
|
|
3573
|
+
return { bytesWritten, buffers };
|
|
3574
|
+
}
|
|
3101
3575
|
async close() { this._assertOpen("close"); this._closed = true; __fileHandles.delete(this.fd); }
|
|
3102
3576
|
[Symbol.asyncDispose]() { return this.close(); }
|
|
3103
3577
|
}
|
|
@@ -3118,6 +3592,7 @@ const __fsMod = (() => {
|
|
|
3118
3592
|
if (localStat && localStat.isDirectory()) throw _fsErr("EISDIR", "open", path);
|
|
3119
3593
|
}
|
|
3120
3594
|
const exists = !!liveMeta || !!localStat;
|
|
3595
|
+
if (exists && fl.directory) throw _fsErr("ENOTDIR", "open", path);
|
|
3121
3596
|
if (!exists && !fl.create) throw _fsErr("ENOENT", "open", path);
|
|
3122
3597
|
if (exists && fl.create && fl.exclusive) throw _fsErr("EEXIST", "open", path);
|
|
3123
3598
|
let size = liveMeta ? (Number(liveMeta.size) || 0) : (localStat ? localStat.size : 0);
|
|
@@ -3141,6 +3616,7 @@ const __fsMod = (() => {
|
|
|
3141
3616
|
const st = statSync(path, { throwIfNoEntry: false });
|
|
3142
3617
|
if (st && st.isDirectory()) throw _fsErr("EISDIR", "open", path);
|
|
3143
3618
|
const exists = st !== undefined;
|
|
3619
|
+
if (exists && fl.directory) throw _fsErr("ENOTDIR", "open", path);
|
|
3144
3620
|
if (!exists && !fl.create) throw _fsErr("ENOENT", "open", path);
|
|
3145
3621
|
if (exists && fl.create && fl.exclusive) throw _fsErr("EEXIST", "open", path);
|
|
3146
3622
|
if (fl.write || !exists) _ensureWritable(absPath, "open", path);
|
|
@@ -3261,6 +3737,49 @@ const __fsMod = (() => {
|
|
|
3261
3737
|
function fsyncSync(fd) { if (!_isStdioFd(fd)) _fdHandle(fd, "fsync"); _markVfsStale(); }
|
|
3262
3738
|
function fdatasyncSync(fd) { if (!_isStdioFd(fd)) _fdHandle(fd, "fdatasync"); _markVfsStale(); }
|
|
3263
3739
|
|
|
3740
|
+
// Descriptor metadata, sync: the path forms on the handle's path, so the
|
|
3741
|
+
// local overlay (times, modes, ownership) and the parked write-through are
|
|
3742
|
+
// exactly what utimesSync/chmodSync/chownSync give.
|
|
3743
|
+
function futimesSync(fd, atime, mtime) {
|
|
3744
|
+
if (_isStdioFd(fd)) throw _fsErr("EINVAL", "futimes", fd);
|
|
3745
|
+
const handle = _fdHandle(fd, "futimes");
|
|
3746
|
+
_recordLocalTimes(handle._abs, atime, mtime, "futimes", handle._path);
|
|
3747
|
+
}
|
|
3748
|
+
function fchmodSync(fd, mode) {
|
|
3749
|
+
if (_isStdioFd(fd)) throw _fsErr("EINVAL", "fchmod", fd);
|
|
3750
|
+
const handle = _fdHandle(fd, "fchmod");
|
|
3751
|
+
_localModes[_strip(handle._abs)] = _coerceMode(mode, "fchmod", handle._path);
|
|
3752
|
+
}
|
|
3753
|
+
function fchownSync(fd, uid, gid) {
|
|
3754
|
+
if (_isStdioFd(fd)) throw _fsErr("EINVAL", "fchown", fd);
|
|
3755
|
+
const handle = _fdHandle(fd, "fchown");
|
|
3756
|
+
_detachStructuralMutation(_chownQueued(handle._path, uid, gid, undefined, "fchown"));
|
|
3757
|
+
}
|
|
3758
|
+
|
|
3759
|
+
// Scatter/gather, sync: readSync/writeSync per buffer; stdio fds keep the
|
|
3760
|
+
// stream behaviour those two already give them.
|
|
3761
|
+
function readvSync(fd, buffers, position) {
|
|
3762
|
+
let bytesRead = 0;
|
|
3763
|
+
let pos = _isCurrentPos(position) ? null : Number(position);
|
|
3764
|
+
for (const buffer of buffers) {
|
|
3765
|
+
const n = readSync(fd, buffer, 0, buffer.byteLength, pos);
|
|
3766
|
+
bytesRead += n;
|
|
3767
|
+
if (pos !== null) pos += n;
|
|
3768
|
+
if (n < buffer.byteLength) break;
|
|
3769
|
+
}
|
|
3770
|
+
return bytesRead;
|
|
3771
|
+
}
|
|
3772
|
+
function writevSync(fd, buffers, position) {
|
|
3773
|
+
let bytesWritten = 0;
|
|
3774
|
+
let pos = _isCurrentPos(position) ? null : Number(position);
|
|
3775
|
+
for (const buffer of buffers) {
|
|
3776
|
+
const n = writeSync(fd, buffer, 0, buffer.byteLength, pos);
|
|
3777
|
+
bytesWritten += n;
|
|
3778
|
+
if (pos !== null) pos += n;
|
|
3779
|
+
}
|
|
3780
|
+
return bytesWritten;
|
|
3781
|
+
}
|
|
3782
|
+
|
|
3264
3783
|
// ── callback forms (live I/O — these CAN reach the supervisor) ──
|
|
3265
3784
|
function open(path, flags, mode, cb) {
|
|
3266
3785
|
if (typeof flags === "function") { cb = flags; flags = undefined; mode = undefined; }
|
|
@@ -3341,21 +3860,56 @@ const __fsMod = (() => {
|
|
|
3341
3860
|
if (!handle) return;
|
|
3342
3861
|
handle.truncate(len).then(() => cb(null)).catch((e) => cb(e));
|
|
3343
3862
|
}
|
|
3344
|
-
|
|
3863
|
+
// The async forms CAN wait for durability: the parked bytes are flushed
|
|
3864
|
+
// and the path's queued mutations awaited before the callback fires.
|
|
3865
|
+
function _fsyncCb(fd, syscall, cb) {
|
|
3866
|
+
let handle = null;
|
|
3345
3867
|
let err = null;
|
|
3346
|
-
try {
|
|
3868
|
+
try { if (!_isStdioFd(fd)) handle = _fdHandle(fd, syscall); } catch (e) { err = e; }
|
|
3347
3869
|
// Without a callback there is nowhere to deliver the failure, so raise
|
|
3348
3870
|
// it here rather than let an EBADF vanish.
|
|
3349
|
-
if (typeof cb !== "function") { if (err) throw err; return; }
|
|
3350
|
-
queueMicrotask(() => cb(err));
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3871
|
+
if (typeof cb !== "function") { if (err) throw err; _markVfsStale(); return; }
|
|
3872
|
+
if (err || !handle) { _markVfsStale(); queueMicrotask(() => cb(err)); return; }
|
|
3873
|
+
_fsyncAsync(handle._abs, handle._path).then(() => cb(null)).catch((e) => cb(e));
|
|
3874
|
+
}
|
|
3875
|
+
function fsync(fd, cb) { _fsyncCb(fd, "fsync", cb); }
|
|
3876
|
+
function fdatasync(fd, cb) { _fsyncCb(fd, "fdatasync", cb); }
|
|
3877
|
+
function futimes(fd, atime, mtime, cb) {
|
|
3878
|
+
if (_isStdioFd(fd)) { queueMicrotask(() => cb(_fsErr("EINVAL", "futimes", fd))); return; }
|
|
3879
|
+
const handle = _fdFor(fd, "futimes", cb);
|
|
3880
|
+
if (!handle) return;
|
|
3881
|
+
handle.utimes(atime, mtime).then(() => cb(null)).catch((e) => cb(e));
|
|
3882
|
+
}
|
|
3883
|
+
function readv(fd, buffers, position, cb) {
|
|
3884
|
+
if (typeof position === "function") { cb = position; position = null; }
|
|
3885
|
+
const run = async () => {
|
|
3886
|
+
let bytesRead = 0;
|
|
3887
|
+
let pos = _isCurrentPos(position) ? null : Number(position);
|
|
3888
|
+
for (const buffer of buffers) {
|
|
3889
|
+
const n = await new Promise((res, rej) =>
|
|
3890
|
+
read(fd, buffer, 0, buffer.byteLength, pos, (e, count) => (e ? rej(e) : res(count))));
|
|
3891
|
+
bytesRead += n;
|
|
3892
|
+
if (pos !== null) pos += n;
|
|
3893
|
+
if (n < buffer.byteLength) break;
|
|
3894
|
+
}
|
|
3895
|
+
return bytesRead;
|
|
3896
|
+
};
|
|
3897
|
+
run().then((n) => cb(null, n, buffers)).catch((e) => cb(e));
|
|
3898
|
+
}
|
|
3899
|
+
function writev(fd, buffers, position, cb) {
|
|
3900
|
+
if (typeof position === "function") { cb = position; position = null; }
|
|
3901
|
+
const run = async () => {
|
|
3902
|
+
let bytesWritten = 0;
|
|
3903
|
+
let pos = _isCurrentPos(position) ? null : Number(position);
|
|
3904
|
+
for (const buffer of buffers) {
|
|
3905
|
+
const n = await new Promise((res, rej) =>
|
|
3906
|
+
write(fd, buffer, 0, buffer.byteLength, pos, (e, count) => (e ? rej(e) : res(count))));
|
|
3907
|
+
bytesWritten += n;
|
|
3908
|
+
if (pos !== null) pos += n;
|
|
3909
|
+
}
|
|
3910
|
+
return bytesWritten;
|
|
3911
|
+
};
|
|
3912
|
+
run().then((n) => cb(null, n, buffers)).catch((e) => cb(e));
|
|
3359
3913
|
}
|
|
3360
3914
|
function fchmod(fd, mode, cb) {
|
|
3361
3915
|
const handle = _fdFor(fd, "fchmod", cb);
|
|
@@ -3368,6 +3922,135 @@ const __fsMod = (() => {
|
|
|
3368
3922
|
handle.chown(uid, gid).then(() => cb(null)).catch((error) => cb(error));
|
|
3369
3923
|
}
|
|
3370
3924
|
|
|
3925
|
+
// ── Dir — fs.opendir / opendirSync / fs.promises.opendir ──
|
|
3926
|
+
// The listing is taken whole at open (both readdir forms already return
|
|
3927
|
+
// one) and handed out one Dirent per read(); closing a closed Dir is
|
|
3928
|
+
// ERR_DIR_CLOSED as in Node.
|
|
3929
|
+
class __Dir {
|
|
3930
|
+
constructor(path, entries) {
|
|
3931
|
+
this.path = path;
|
|
3932
|
+
this._entries = entries;
|
|
3933
|
+
this._at = 0;
|
|
3934
|
+
this._closed = false;
|
|
3935
|
+
}
|
|
3936
|
+
_assertOpen() {
|
|
3937
|
+
if (this._closed) {
|
|
3938
|
+
const err = new Error("Directory handle was closed");
|
|
3939
|
+
err.code = "ERR_DIR_CLOSED";
|
|
3940
|
+
throw err;
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
readSync() {
|
|
3944
|
+
this._assertOpen();
|
|
3945
|
+
return this._at < this._entries.length ? this._entries[this._at++] : null;
|
|
3946
|
+
}
|
|
3947
|
+
read(cb) {
|
|
3948
|
+
const result = new Promise((res, rej) => { try { res(this.readSync()); } catch (e) { rej(e); } });
|
|
3949
|
+
if (typeof cb !== "function") return result;
|
|
3950
|
+
result.then((entry) => cb(null, entry), (e) => cb(e));
|
|
3951
|
+
}
|
|
3952
|
+
closeSync() { this._assertOpen(); this._closed = true; }
|
|
3953
|
+
close(cb) {
|
|
3954
|
+
const result = new Promise((res, rej) => { try { this.closeSync(); res(); } catch (e) { rej(e); } });
|
|
3955
|
+
if (typeof cb !== "function") return result;
|
|
3956
|
+
result.then(() => cb(null), (e) => cb(e));
|
|
3957
|
+
}
|
|
3958
|
+
async *[Symbol.asyncIterator]() {
|
|
3959
|
+
try {
|
|
3960
|
+
for (;;) {
|
|
3961
|
+
const entry = this.readSync();
|
|
3962
|
+
if (entry === null) break;
|
|
3963
|
+
yield entry;
|
|
3964
|
+
}
|
|
3965
|
+
} finally {
|
|
3966
|
+
if (!this._closed) this._closed = true;
|
|
3967
|
+
}
|
|
3968
|
+
}
|
|
3969
|
+
[Symbol.asyncDispose]() { return this._closed ? Promise.resolve() : this.close(); }
|
|
3970
|
+
[Symbol.dispose]() { if (!this._closed) this.closeSync(); }
|
|
3971
|
+
}
|
|
3972
|
+
function opendirSync(p, opts) {
|
|
3973
|
+
return new __Dir(String(p), readdirSync(p, { withFileTypes: true }));
|
|
3974
|
+
}
|
|
3975
|
+
async function _opendirAsync(p, opts) {
|
|
3976
|
+
return new __Dir(String(p), await _readdirAsync(p, { withFileTypes: true }));
|
|
3977
|
+
}
|
|
3978
|
+
function opendir(p, opts, cb) {
|
|
3979
|
+
if (typeof opts === "function") { cb = opts; opts = undefined; }
|
|
3980
|
+
_opendirAsync(p, opts).then((dir) => cb(null, dir)).catch((e) => cb(e));
|
|
3981
|
+
}
|
|
3982
|
+
|
|
3983
|
+
// ── statfs ──
|
|
3984
|
+
// Honest constants for the VFS: the block size is the SQLite chunk unit's
|
|
3985
|
+
// page size, and blocks is the configured VFS capacity in those blocks.
|
|
3986
|
+
// The supervisor surface exposes no usage counter, so bfree/bavail report
|
|
3987
|
+
// the whole capacity; the VFS has no inode table, so files/ffree are 0
|
|
3988
|
+
// (unknown, not "none"). \`type\` is 0: this is no kernel filesystem and
|
|
3989
|
+
// no magic number would be true of it.
|
|
3990
|
+
const _STATFS_BSIZE = 4096;
|
|
3991
|
+
const _STATFS_BLOCKS = Math.floor(${VFS_CAPACITY} / 4096);
|
|
3992
|
+
function _statfsObject(opts) {
|
|
3993
|
+
const wrap = opts && opts.bigint ? BigInt : Number;
|
|
3994
|
+
return {
|
|
3995
|
+
type: wrap(0),
|
|
3996
|
+
bsize: wrap(_STATFS_BSIZE),
|
|
3997
|
+
blocks: wrap(_STATFS_BLOCKS),
|
|
3998
|
+
bfree: wrap(_STATFS_BLOCKS),
|
|
3999
|
+
bavail: wrap(_STATFS_BLOCKS),
|
|
4000
|
+
files: wrap(0),
|
|
4001
|
+
ffree: wrap(0),
|
|
4002
|
+
};
|
|
4003
|
+
}
|
|
4004
|
+
function statfsSync(p, opts) {
|
|
4005
|
+
if (statSync(p, { throwIfNoEntry: false }) === undefined) throw _fsErr("ENOENT", "statfs", p);
|
|
4006
|
+
return _statfsObject(opts);
|
|
4007
|
+
}
|
|
4008
|
+
async function _statfsAsync(p, opts) {
|
|
4009
|
+
await _statAsync(p);
|
|
4010
|
+
return _statfsObject(opts);
|
|
4011
|
+
}
|
|
4012
|
+
function statfs(p, opts, cb) {
|
|
4013
|
+
if (typeof opts === "function") { cb = opts; opts = undefined; }
|
|
4014
|
+
_statfsAsync(p, opts).then((s) => cb(null, s)).catch((e) => cb(e));
|
|
4015
|
+
}
|
|
4016
|
+
|
|
4017
|
+
// ── remaining callback forms of the path operations ──
|
|
4018
|
+
function rm(p, opts, cb) {
|
|
4019
|
+
if (typeof opts === "function") { cb = opts; opts = undefined; }
|
|
4020
|
+
_rmAsync(p, opts).then(() => cb(null)).catch((e) => cb(e));
|
|
4021
|
+
}
|
|
4022
|
+
function cp(src, dest, opts, cb) {
|
|
4023
|
+
if (typeof opts === "function") { cb = opts; opts = undefined; }
|
|
4024
|
+
promises.cp(src, dest, opts).then(() => cb(null)).catch((e) => cb(e));
|
|
4025
|
+
}
|
|
4026
|
+
function truncate(p, len, cb) {
|
|
4027
|
+
if (typeof len === "function") { cb = len; len = 0; }
|
|
4028
|
+
_truncateAsync(p, len || 0).then(() => cb(null)).catch((e) => cb(e));
|
|
4029
|
+
}
|
|
4030
|
+
function copyFile(src, dest, mode, cb) {
|
|
4031
|
+
if (typeof mode === "function") { cb = mode; mode = 0; }
|
|
4032
|
+
promises.copyFile(src, dest, mode).then(() => cb(null)).catch((e) => cb(e));
|
|
4033
|
+
}
|
|
4034
|
+
function mkdtemp(prefix, opts, cb) {
|
|
4035
|
+
if (typeof opts === "function") { cb = opts; opts = undefined; }
|
|
4036
|
+
_mkdtempAsync(prefix).then((name) => cb(null, name)).catch((e) => cb(e));
|
|
4037
|
+
}
|
|
4038
|
+
function link(existingPath, newPath, cb) { queueMicrotask(() => cb(_fsErr("ENOSYS", "link", newPath))); }
|
|
4039
|
+
function symlink(target, path, type, cb) {
|
|
4040
|
+
if (typeof type === "function") { cb = type; type = undefined; }
|
|
4041
|
+
_symlinkAsync(target, path).then(() => cb(null)).catch((e) => cb(e));
|
|
4042
|
+
}
|
|
4043
|
+
function readlink(p, opts, cb) {
|
|
4044
|
+
if (typeof opts === "function") { cb = opts; opts = undefined; }
|
|
4045
|
+
_readlinkAsync(p).then((target) => cb(null, target)).catch((e) => cb(e));
|
|
4046
|
+
}
|
|
4047
|
+
function realpath(p, opts, cb) {
|
|
4048
|
+
if (typeof opts === "function") { cb = opts; opts = undefined; }
|
|
4049
|
+
promises.realpath(p, opts).then((r) => cb(null, r)).catch((e) => cb(e));
|
|
4050
|
+
}
|
|
4051
|
+
realpath.native = realpath;
|
|
4052
|
+
function lchmod(p, mode, cb) { chmod(p, mode, cb); }
|
|
4053
|
+
|
|
3371
4054
|
// ── promises namespace (W3: full surface, VFS-backed) ──
|
|
3372
4055
|
// We can't forward to workerd's node:fs/promises because that operates
|
|
3373
4056
|
// on a real-host filesystem, not our VFS. So every method is shim'd
|
|
@@ -3385,22 +4068,8 @@ const __fsMod = (() => {
|
|
|
3385
4068
|
// W3 additions:
|
|
3386
4069
|
appendFile: async (p, d, o) => { await _appendFileAsync(p, d, o); },
|
|
3387
4070
|
lstat: (p) => new Promise((res, rej) => lstat(p, (e, s) => e ? rej(e) : res(s))),
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
const k = _strip(_resolve(p));
|
|
3391
|
-
const prefix = k + "/";
|
|
3392
|
-
if (o.recursive) {
|
|
3393
|
-
if (__vfsBundle) {
|
|
3394
|
-
if (k in __vfsBundle) delete __vfsBundle[k];
|
|
3395
|
-
for (const bk of __residentUnder(prefix)) delete __vfsBundle[bk];
|
|
3396
|
-
}
|
|
3397
|
-
if (__vfsWrites) for (const wk of Object.keys(__vfsWrites)) if (wk === k || wk.startsWith(prefix)) delete __vfsWrites[wk];
|
|
3398
|
-
if (__vfsDirs) for (const dk of Object.keys(__vfsDirs)) if (dk === k || dk.startsWith(prefix)) delete __vfsDirs[dk];
|
|
3399
|
-
_forgetSyncTree(k);
|
|
3400
|
-
} else {
|
|
3401
|
-
try { await _unlinkAsync(p); } catch (e) { if (!o.force) throw e; }
|
|
3402
|
-
}
|
|
3403
|
-
},
|
|
4071
|
+
// The same local retraction and queued authority removal rmSync does.
|
|
4072
|
+
rm: async (p, opts) => { await _rmAsync(p, opts); },
|
|
3404
4073
|
cp: async (src, dest, opts) => {
|
|
3405
4074
|
const o = opts || {};
|
|
3406
4075
|
const srcAbs = _resolve(src);
|
|
@@ -3436,7 +4105,12 @@ const __fsMod = (() => {
|
|
|
3436
4105
|
};
|
|
3437
4106
|
await walk("");
|
|
3438
4107
|
},
|
|
3439
|
-
copyFile: async (src, dest) => {
|
|
4108
|
+
copyFile: async (src, dest, mode) => {
|
|
4109
|
+
if ((Number(mode) & __fsConstants.COPYFILE_EXCL) !== 0 && await _existsAsync(dest)) {
|
|
4110
|
+
throw _fsErr("EEXIST", "copyfile", dest);
|
|
4111
|
+
}
|
|
4112
|
+
await _writeFileAsync(dest, await _readFileAsync(src));
|
|
4113
|
+
},
|
|
3440
4114
|
rename: async (oldP, newP) => { await _renameAsync(oldP, newP); },
|
|
3441
4115
|
rmdir: async (p) => { await _rmdirAsync(p); },
|
|
3442
4116
|
realpath: async (p) => __pathMod.resolve(String(p)),
|
|
@@ -3450,12 +4124,10 @@ const __fsMod = (() => {
|
|
|
3450
4124
|
symlink: async (target, path) => { await _symlinkAsync(target, path); },
|
|
3451
4125
|
link: async () => { throw _fsErr("ENOSYS", "link", ""); },
|
|
3452
4126
|
readlink: async (p) => _readlinkAsync(p),
|
|
3453
|
-
mkdtemp: async (prefix) =>
|
|
3454
|
-
const name = String(prefix) + Math.random().toString(36).slice(2, 10);
|
|
3455
|
-
mkdirSync(name, { recursive: true });
|
|
3456
|
-
return name;
|
|
3457
|
-
},
|
|
4127
|
+
mkdtemp: async (prefix) => _mkdtempAsync(prefix),
|
|
3458
4128
|
open: async (path, flags, mode) => _openAsync(path, flags, mode),
|
|
4129
|
+
opendir: async (p, opts) => _opendirAsync(p, opts),
|
|
4130
|
+
statfs: async (p, opts) => _statfsAsync(p, opts),
|
|
3459
4131
|
watch: async function* (filename, opts) {
|
|
3460
4132
|
// Minimal async iter — polls _bundleLookup every 500ms and yields
|
|
3461
4133
|
// a single \`change\` event when content differs. Adequate for
|
|
@@ -3500,8 +4172,9 @@ const __fsMod = (() => {
|
|
|
3500
4172
|
},
|
|
3501
4173
|
};
|
|
3502
4174
|
|
|
3503
|
-
// ── constants ──
|
|
3504
|
-
const constants =
|
|
4175
|
+
// ── constants ── the shared linux x64 table (see __fsConstants).
|
|
4176
|
+
const constants = __fsConstants;
|
|
4177
|
+
promises.constants = constants;
|
|
3505
4178
|
|
|
3506
4179
|
// fs.ReadStream / fs.WriteStream classes. Real Node exposes these as
|
|
3507
4180
|
// constructors; graceful-fs (bundled by degit → create-cloudflare)
|
|
@@ -3591,10 +4264,15 @@ const __fsMod = (() => {
|
|
|
3591
4264
|
const __fsExports = {
|
|
3592
4265
|
readFileSync, writeFileSync, appendFileSync, existsSync, statSync, lstatSync,
|
|
3593
4266
|
readdirSync, mkdirSync, unlinkSync, rmdirSync, renameSync, copyFileSync,
|
|
3594
|
-
realpathSync, utimesSync, lutimesSync, chmodSync, accessSync,
|
|
4267
|
+
realpathSync, utimesSync, lutimesSync, chmodSync, lchmodSync, chownSync, lchownSync, accessSync,
|
|
4268
|
+
rmSync, cpSync, mkdtempSync, truncateSync, linkSync, opendirSync, statfsSync,
|
|
3595
4269
|
openSync, closeSync, readSync, writeSync, fstatSync, ftruncateSync, fsyncSync, fdatasyncSync,
|
|
3596
|
-
|
|
3597
|
-
|
|
4270
|
+
futimesSync, fchmodSync, fchownSync, readvSync, writevSync,
|
|
4271
|
+
open, close, read, write, fstat, ftruncate, fsync, fdatasync, fchmod, futimes, readv, writev,
|
|
4272
|
+
readFile, writeFile, appendFile, stat, lstat, readdir, exists, mkdir, unlink, rename, utimes, lutimes, chmod, lchmod, chown, lchown, fchown, access,
|
|
4273
|
+
rm, cp, truncate, copyFile, mkdtemp, link, symlink, readlink, realpath, opendir, statfs,
|
|
4274
|
+
Dirent: __Dirent,
|
|
4275
|
+
Dir: __Dir,
|
|
3598
4276
|
promises, constants,
|
|
3599
4277
|
createReadStream: (p, opts) => new (__getReadStream())(p, opts),
|
|
3600
4278
|
createWriteStream: (p, opts) => {
|
|
@@ -3936,13 +4614,10 @@ globalThis.WebSocket = __NimbusRelayedWebSocket;
|
|
|
3936
4614
|
//
|
|
3937
4615
|
// require('node:constants') (and the legacy bare require('constants'))
|
|
3938
4616
|
// expose a FLAT object of POSIX/Linux/OpenSSL constants. Real Node ships
|
|
3939
|
-
// ~234 constants;
|
|
4617
|
+
// ~234 constants; the full fs slice comes from __fsConstants, and the rest
|
|
4618
|
+
// is what actually gets touched by:
|
|
3940
4619
|
// - create-next-app (touches UV_FS_O_FILEMAP — verified via grep on
|
|
3941
4620
|
// unpkg.com/create-next-app@latest/dist/index.js)
|
|
3942
|
-
// - typical fs.openSync flag composition (O_RDONLY, O_WRONLY, O_RDWR,
|
|
3943
|
-
// O_CREAT, O_EXCL, O_TRUNC, O_APPEND, O_DIRECTORY, etc.)
|
|
3944
|
-
// - typical fs.access mode composition (F_OK, R_OK, W_OK, X_OK)
|
|
3945
|
-
// - typical fs.copyFile mode composition (COPYFILE_EXCL etc.)
|
|
3946
4621
|
// - signal/errno tables (shared shape with __osMod.constants.signals
|
|
3947
4622
|
// etc. — flat here, nested there; both shapes are real-Node-accurate
|
|
3948
4623
|
// and we expose both via the right module).
|
|
@@ -3987,26 +4662,10 @@ const __constantsMod = {
|
|
|
3987
4662
|
SIGTSTP: 20, SIGTTIN: 21, SIGTTOU: 22, SIGURG: 23, SIGXCPU: 24,
|
|
3988
4663
|
SIGXFSZ: 25, SIGVTALRM: 26, SIGPROF: 27, SIGWINCH: 28, SIGIO: 29,
|
|
3989
4664
|
SIGPOLL: 29, SIGPWR: 30, SIGSYS: 31,
|
|
3990
|
-
// ──
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
O_RDONLY: 0, O_WRONLY: 1, O_RDWR: 2,
|
|
3995
|
-
O_CREAT: 64, O_EXCL: 128, O_NOCTTY: 256, O_TRUNC: 512, O_APPEND: 1024,
|
|
3996
|
-
O_DIRECTORY: 65536, O_NOATIME: 262144, O_NOFOLLOW: 131072,
|
|
3997
|
-
O_SYNC: 1052672, O_DSYNC: 4096, O_DIRECT: 16384, O_NONBLOCK: 2048,
|
|
3998
|
-
// libuv-specific fs flag — create-next-app touches this directly.
|
|
3999
|
-
UV_FS_O_FILEMAP: 0,
|
|
4000
|
-
// ── File-permission bits (chmod / stat.mode user/group/other) ─────
|
|
4001
|
-
S_IRWXU: 448, S_IRUSR: 256, S_IWUSR: 128, S_IXUSR: 64,
|
|
4002
|
-
S_IRWXG: 56, S_IRGRP: 32, S_IWGRP: 16, S_IXGRP: 8,
|
|
4003
|
-
S_IRWXO: 7, S_IROTH: 4, S_IWOTH: 2, S_IXOTH: 1,
|
|
4004
|
-
// ── fs.access mode constants ──────────────────────────────────────
|
|
4005
|
-
F_OK: 0, R_OK: 4, W_OK: 2, X_OK: 1,
|
|
4006
|
-
// ── fs.copyFile mode constants (libuv shape + Node alias) ─────────
|
|
4007
|
-
UV_FS_COPYFILE_EXCL: 1, COPYFILE_EXCL: 1,
|
|
4008
|
-
UV_FS_COPYFILE_FICLONE: 2, COPYFILE_FICLONE: 2,
|
|
4009
|
-
UV_FS_COPYFILE_FICLONE_FORCE: 4, COPYFILE_FICLONE_FORCE: 4,
|
|
4665
|
+
// ── fs constants (O_*, S_IF*, S_I*, F_OK.., COPYFILE_*, UV_*) ────
|
|
4666
|
+
// Node's constants module is the union of os, fs and crypto constants;
|
|
4667
|
+
// the fs slice is __fsConstants, the same object fs.constants exposes.
|
|
4668
|
+
...__fsConstants,
|
|
4010
4669
|
// ── OpenSSL / TLS option flags ────────────────────────────────────
|
|
4011
4670
|
// Numeric values from real Node v20. Libraries probe defined-ness;
|
|
4012
4671
|
// we ship the surface so constants.SSL_OP_* doesn't undefined-throw.
|
|
@@ -6424,7 +7083,20 @@ builtins.tty = {
|
|
|
6424
7083
|
// core and must not be reported as such — a package that sniffs this list
|
|
6425
7084
|
// would otherwise conclude e.g. undici ships with node.
|
|
6426
7085
|
const __nimbusFacetProvidedPackages = new Set(${FACET_PROVIDED_PACKAGES_LITERAL});
|
|
6427
|
-
builtins.module = {
|
|
7086
|
+
builtins.module = {
|
|
7087
|
+
get builtinModules() { return Object.keys(builtins).filter((n) => !__nimbusFacetProvidedPackages.has(n)); },
|
|
7088
|
+
createRequire: (specifier) => __makeRequire(__requireBaseDir(specifier)),
|
|
7089
|
+
isBuiltin: (specifier) => Object.hasOwn(builtins, String(specifier).replace(/^node:/, '')) && !__nimbusFacetProvidedPackages.has(String(specifier).replace(/^node:/, '')),
|
|
7090
|
+
// Node 22.1's on-disk compile cache. There is no disk to cache into and
|
|
7091
|
+
// nothing to compile ahead: callers (pi's CLI entry calls it
|
|
7092
|
+
// unconditionally) get Node's own answer for a cache that is off.
|
|
7093
|
+
enableCompileCache: () => ({ status: 3, message: 'compile cache is not available in this runtime' }),
|
|
7094
|
+
getCompileCacheDir: () => undefined,
|
|
7095
|
+
flushCompileCache: () => {},
|
|
7096
|
+
constants: { compileCacheStatus: { FAILED: 0, ENABLED: 1, ALREADY_ENABLED: 2, DISABLED: 3 } },
|
|
7097
|
+
_resolveFilename: (id) => id,
|
|
7098
|
+
_cache: {},
|
|
7099
|
+
};
|
|
6428
7100
|
// Bind to globalThis: workerd's timer globals throw "Illegal invocation"
|
|
6429
7101
|
// when called with a receiver other than globalThis (i.e. as
|
|
6430
7102
|
// timers.setInterval(...)), which clack's spinner — used by
|