@nimbus-sh/worker 0.9.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.js +9 -2
- 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 +28 -0
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +89 -37
- 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 +2 -2
- package/dist/hosted/runtime.d.ts +3 -2
- package/dist/hosted/runtime.d.ts.map +1 -1
- package/dist/hosted/runtime.js +1 -0
- package/dist/hosted/services.d.ts +8 -0
- package/dist/hosted/services.d.ts.map +1 -1
- package/dist/hosted/services.js +3 -0
- 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/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 -7
- package/dist/runtime/node-shims.d.ts.map +1 -1
- package/dist/runtime/node-shims.js +839 -180
- package/dist/runtime/python-repl.d.ts.map +1 -1
- package/dist/runtime/python-repl.js +2 -33
- package/dist/runtime/ruby-resident.d.ts.map +1 -1
- package/dist/runtime/ruby-resident.js +5 -16
- 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 +98 -1401
- 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/nimbus-session.d.ts +10 -2
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +13 -1
- package/dist/session/npm-install-port.d.ts.map +1 -1
- package/dist/session/npm-install-port.js +1 -0
- package/dist/session/routes.d.ts.map +1 -1
- package/dist/session/routes.js +4 -2
- package/dist/session/rpc.d.ts +5 -2
- package/dist/session/rpc.d.ts.map +1 -1
- package/dist/session/rpc.js +10 -2
- package/dist/session/supervisor-rpc.d.ts +17 -9
- package/dist/session/supervisor-rpc.d.ts.map +1 -1
- package/dist/session/supervisor-rpc.js +15 -22
- package/dist/workspace-host.d.ts +1 -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-acaa037e6d26ec0d.js → node-shims-7b4dd7a76d95d21e.js} +838 -179
- package/scripts/bundle-esbuild-wasm.mjs +55 -39
- package/scripts/bundle-facet-workers.mjs +45 -7
package/dist/hosted/runtime.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { ProcessLogReadOptions } from '@nimbus-sh/core/runtime/process-logs
|
|
|
4
4
|
import type { VfsCred } from '@nimbus-sh/core/runtime/os-contracts.js';
|
|
5
5
|
import type { SandboxFs } from '@nimbus-sh/core/substrate/lifo/sandbox/types.js';
|
|
6
6
|
import { type SupervisorOpEnvelope } from '@nimbus-sh/core/workspace/supervisor-op.js';
|
|
7
|
-
import type { FacetManagerHostHooks } from '../facets/compose.js';
|
|
7
|
+
import type { ComposedFacetManager, FacetManagerHostHooks } from '../facets/compose.js';
|
|
8
8
|
import { WebSocketTerminal } from '../facets/ws-terminal.js';
|
|
9
9
|
import * as operations from '../session/programmatic.js';
|
|
10
10
|
import * as services from './services.js';
|
|
@@ -38,6 +38,7 @@ export declare function composeHostedRuntime(options: HostedRuntimeOptions): Pro
|
|
|
38
38
|
terminal: WebSocketTerminal;
|
|
39
39
|
files: RuntimeFiles;
|
|
40
40
|
runtimes: import("@nimbus-sh/core/runtime/runtime-manager.js").RuntimeManager;
|
|
41
|
+
facets: () => ComposedFacetManager;
|
|
41
42
|
ready: (options?: operations.ProgrammaticReadyOptions | undefined) => Promise<{
|
|
42
43
|
ok: true;
|
|
43
44
|
preinstalled: string[];
|
|
@@ -121,7 +122,7 @@ export declare function composeHostedRuntime(options: HostedRuntimeOptions): Pro
|
|
|
121
122
|
installed: import("@nimbus-sh/core/runtime/installed-runtimes.js").RuntimeSummary[];
|
|
122
123
|
available: import("@nimbus-sh/core/runtime/runtime-package.js").RuntimeAvailability[];
|
|
123
124
|
}>;
|
|
124
|
-
spawnWorker: (workerCode: string, command: string, cwd: string, opts?: import("../
|
|
125
|
+
spawnWorker: (workerCode: string, command: string, cwd: string, opts?: import("../workspace-host.js").LongRunningWorkerSpawnOptions | undefined) => Promise<import("../facets/manager.js").SpawnedWorker>;
|
|
125
126
|
routeCapabilityPort: (port: number, capability: string, request: Request<unknown, CfProperties<unknown>>, innerPath: string) => Promise<Response>;
|
|
126
127
|
supervisorOp: (envelope: SupervisorOpEnvelope) => Promise<unknown>;
|
|
127
128
|
onScheduled: (task: HostedRuntimeTask) => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/hosted/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0CAA0C,CAAC;AAC7E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AACrF,OAAO,KAAK,EAA6B,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAIlG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iDAAiD,CAAC;AACjF,OAAO,EAAqD,KAAK,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAE1I,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/hosted/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0CAA0C,CAAC;AAC7E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AACrF,OAAO,KAAK,EAA6B,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAIlG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iDAAiD,CAAC;AACjF,OAAO,EAAqD,KAAK,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAE1I,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AASxF,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAO7D,OAAO,KAAK,UAAU,MAAM,4BAA4B,CAAC;AACzD,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAI3B,QAAA,MAAM,UAAU;;;;EAA0D,CAAC;AAC3E,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE3D,MAAM,WAAW,sBAAsB;IACrC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,MAAM,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,eAAe,CAAC;IAC3B,GAAG,EAAE,kBAAkB,CAAC;IACxB,GAAG,EAAE,QAAQ,CAAC,gBAAgB,CAAC;IAC/B,KAAK,EAAE,YAAY,CAAC;IACpB,SAAS,EAAE,sBAAsB,CAAC;IAClC,mBAAmB,CAAC,EAAE,qBAAqB,CAAC,qBAAqB,CAAC,CAAC;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,CAAC;CACjC;AAqRD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAmBjD,MAAM,YAAY,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAiBjC,oBAAoB;wBACzB,iBAAiB;yBAChB,SAAS;wBACV,SAAS,WAAW,MAAM,GAAG,WAAW;wBACxC,SAAS;;GAGhC;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC"}
|
package/dist/hosted/runtime.js
CHANGED
|
@@ -307,6 +307,7 @@ export async function composeHostedRuntime(options) {
|
|
|
307
307
|
terminal: owner.terminal,
|
|
308
308
|
files: owner.files(owner.processes.cred(owner.shellProcessPid)),
|
|
309
309
|
runtimes: owner.runtimeManager,
|
|
310
|
+
facets: () => owner.ensureFacetManager(),
|
|
310
311
|
ready: operations.ensureProgrammaticReady.bind(null, owner),
|
|
311
312
|
exec: operations.rpcExec.bind(null, owner),
|
|
312
313
|
runCode: operations.rpcRunCode.bind(null, owner),
|
|
@@ -18,6 +18,14 @@ export interface RuntimeServiceContext {
|
|
|
18
18
|
notify(line: string): void;
|
|
19
19
|
requestLaunchTurn(notBefore?: number): Promise<void>;
|
|
20
20
|
resolveWorkerLaunch?: FacetManagerHostHooks['resolveWorkerLaunch'];
|
|
21
|
+
/**
|
|
22
|
+
* Hold the host actor in memory while a resident process runs — the
|
|
23
|
+
* session DO's `ensureResidentKeepalive` (session/hibernation.ts), bound
|
|
24
|
+
* to the host and its `ctx`. Optional because it is the one thing only the
|
|
25
|
+
* session DO can do: it alone runs the fabric timer mux this alarm rides,
|
|
26
|
+
* and the hosted runtime schedules through its own lifecycle instead.
|
|
27
|
+
*/
|
|
28
|
+
armResidentKeepalive?: () => void;
|
|
21
29
|
/** The host's own authority: a session has exactly one, and this is it. */
|
|
22
30
|
filesystem: () => NimbusFilesystemAuthority;
|
|
23
31
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/hosted/services.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,KAAK,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAGlH,OAAO,EAAkC,KAAK,yBAAyB,EAAgB,MAAM,yCAAyC,CAAC;AAEvI,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAErE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAKxD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAoBxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAExE,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB,EAAE,cAAc;IACzE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,eAAe,EACnD,aAAa,GAAG,mBAAmB,GAAG,qBAAqB,GAAG,cAAc,GAAG,YAAY,GAAG,kBAAkB,GAAG,oBAAoB,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,gBAAgB,GAAG,sBAAsB,GAAG,qBAAqB,GAAG,sBAAsB,GAAG,cAAc,GAAG,cAAc,GAAG,WAAW,GAAG,OAAO,GAAG,UAAU,GAAG,UAAU,CACjW,GAAG;IAAE,cAAc,EAAE,cAAc,GAAG,IAAI,CAAA;CAAE,CAAC;AAE9C,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,GAAG,EAAE,kBAAkB,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,gBAAgB,CAAC;IAC/B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,mBAAmB,CAAC,EAAE,qBAAqB,CAAC,qBAAqB,CAAC,CAAC;IACnE,2EAA2E;IAC3E,UAAU,EAAE,MAAM,yBAAyB,CAAC;CAC7C;AA0BD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,kBAAkB,EAAE,cAAc,EAAE,qBAAqB,GAAG,iBAAiB,CAGjH;AAEH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,EAAE,cAAc,EAAE,qBAAqB,GAAG,oBAAoB,
|
|
1
|
+
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/hosted/services.ts"],"names":[],"mappings":"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"}
|
package/dist/hosted/services.js
CHANGED
|
@@ -92,6 +92,9 @@ export function ensureFacetManager(self, runtimeContext) {
|
|
|
92
92
|
self.processes.openInput(pid);
|
|
93
93
|
}
|
|
94
94
|
catch { }
|
|
95
|
+
// The one arming site: the journal's re-drive path comes back
|
|
96
|
+
// through this same hook. See ensureResidentKeepalive.
|
|
97
|
+
runtimeContext.armResidentKeepalive?.();
|
|
95
98
|
}
|
|
96
99
|
// Only surface long-running / user-visible spawns to keep
|
|
97
100
|
// the terminal uncluttered. Short `node <file>` evals also
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
* W7-frame symbols: encodeWriteBatchStream, decodeWriteBatchStream,
|
|
16
16
|
* W7_MAGIC, W7_MAX_RECORD_BYTES.
|
|
17
17
|
*
|
|
18
|
-
* Tar size: 4.
|
|
19
|
-
* W7 size: 29.
|
|
18
|
+
* Tar size: 4.58 KiB
|
|
19
|
+
* W7 size: 29.19 KiB
|
|
20
20
|
*/
|
|
21
21
|
export declare const TAR_STREAM_PREAMBLE: string;
|
|
22
22
|
export declare const W7_FRAME_PREAMBLE: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generated-workers.d.ts","sourceRoot":"","sources":["../../src/loaders/generated-workers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,eAAO,MAAM,mBAAmB,EAAE,
|
|
1
|
+
{"version":3,"file":"generated-workers.d.ts","sourceRoot":"","sources":["../../src/loaders/generated-workers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,eAAO,MAAM,mBAAmB,EAAE,MAAwyJ,CAAC;AAE30J,eAAO,MAAM,iBAAiB,EAAE,MAAqv8B,CAAC"}
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
* W7-frame symbols: encodeWriteBatchStream, decodeWriteBatchStream,
|
|
16
16
|
* W7_MAGIC, W7_MAX_RECORD_BYTES.
|
|
17
17
|
*
|
|
18
|
-
* Tar size: 4.
|
|
19
|
-
* W7 size: 29.
|
|
18
|
+
* Tar size: 4.58 KiB
|
|
19
|
+
* W7 size: 29.19 KiB
|
|
20
20
|
*/
|
|
21
|
-
export const TAR_STREAM_PREAMBLE = "
|
|
22
|
-
export const W7_FRAME_PREAMBLE = "// ../platform/src/limits.ts\nvar CHUNK_SIZE = 65536;\nvar MAX_TX_BLOB_BYTES = 1 * 1024 * 1024;\nvar SQLITE_MAX_STATEMENT_BYTES = 100 * 1024;\nvar MAX_RPC_SAFE_PAYLOAD_BYTES = 28 * 1024 * 1024;\nvar SUPERVISOR_HEAP_CEILING_BYTES = 64 * 1024 * 1024;\nvar SUPERVISOR_IN_FLIGHT_ALLOCATION_BUDGET_BYTES = 40 * 1024 * 1024;\nvar SUPERVISOR_READ_RESERVE_BYTES = 1024 * 1024;\nvar MAX_GLOBAL_WRITE_STREAM_CREDIT_BYTES = 8 * 1024 * 1024;\n\n// ../platform/src/w7-frame.ts\nvar W7_MAGIC = new Uint8Array([78, 87, 55, 3]);\nvar ENCODER_QUEUE_HWM = 0;\nvar MAX_METADATA_BYTES = 64 * 1024;\nvar MAX_PATH_BYTES = 64 * 1024;\nvar MAX_BATCH_ID_BYTES = 128;\nvar MAX_CONTENT_ID_BYTES = 256;\nvar W7_MAX_PATHS_PER_BATCH = 128;\nvar W7_MAX_OWNED_PATH_BYTES = 64 * 1024;\nvar W7_MAX_RECORD_BYTES = 5 + 4 + MAX_CONTENT_ID_BYTES + 8 + CHUNK_SIZE;\nvar MODE = \"path-atomic-committed-prefix\";\nfunction encodeWriteBatchStream(payload) {\n const batchId = crypto.randomUUID();\n const { deletes, directories, files } = preparePayload(payload, batchId);\n const iterator = encodeRecords(batchId, deletes, directories, files);\n let closed = false;\n let magicEmitted = false;\n const source = {\n type: \"bytes\",\n pull(controller) {\n if (closed) return;\n try {\n if (!magicEmitted) {\n magicEmitted = true;\n controller.enqueue(W7_MAGIC.slice());\n return;\n }\n const next = iterator.next();\n if (next.done) {\n closed = true;\n controller.close();\n return;\n }\n for (const part of next.value) controller.enqueue(part);\n } catch (error) {\n closed = true;\n controller.error(error);\n }\n },\n cancel() {\n closed = true;\n iterator.return?.(void 0);\n }\n };\n return new ReadableStream(source, {\n highWaterMark: ENCODER_QUEUE_HWM\n });\n}\nasync function decodeWriteBatchStream(stream, options = {}) {\n let reader;\n try {\n reader = stream.getReader({ mode: \"byob\" });\n } catch {\n throw new Error(\"w7-frame: stream must be a byte-oriented ReadableStream\");\n }\n const buffer = new ExactByteReader(reader, options.signal);\n let handedOff = false;\n try {\n throwIfAborted(options.signal);\n const magic = await buffer.readExact(W7_MAGIC.length, \"magic\");\n if (!bytesEqual(magic, W7_MAGIC)) {\n const version = magic.length === 4 && magic[0] === 78 && magic[1] === 87 && magic[2] === 55 ? magic[3] : null;\n if (version !== null) {\n throw new Error(`w7-frame: unsupported protocol version ${version}; expected 3`);\n }\n throw new Error(`w7-frame: bad magic, expected NW7\\\\x03, got ${hex(magic)}`);\n }\n const beginEnvelope = await readEnvelope(buffer, \"batch-begin\");\n if (beginEnvelope.tag !== 1 /* BatchBegin */) {\n throw new Error(`w7-frame: first record must be batch-begin, got tag ${beginEnvelope.tag}`);\n }\n if (beginEnvelope.length > MAX_METADATA_BYTES) {\n throw new Error(\n `w7-frame: batch-begin length ${beginEnvelope.length} exceeds ${MAX_METADATA_BYTES}`\n );\n }\n const beginPayload = await buffer.readExact(beginEnvelope.length, \"batch-begin payload\");\n const begin = parseBatchBegin(beginPayload);\n const initialCheck = updateRecordCheck(CRC_SEED, beginEnvelope.header, beginPayload);\n handedOff = true;\n return {\n batchId: begin.id,\n mode: begin.mode,\n records: decodeRecords(stream, reader, buffer, options, initialCheck)\n };\n } catch (error) {\n if (!handedOff) await cancelReader(reader, error);\n throw error;\n }\n}\nasync function* decodeRecords(stream, reader, buffer, options, initialCheck) {\n const ownedPaths = new PathOwnership();\n const contentIds = /* @__PURE__ */ new Set();\n let active = null;\n let batchCheck = initialCheck;\n const summary = {\n recordCount: 1,\n pathCount: 0,\n deleteCount: 0,\n directoryCount: 0,\n fileCount: 0,\n chunkCount: 0,\n byteCount: 0\n };\n let completed = false;\n let failure = new DOMException(\"W7 consumer cancelled\", \"AbortError\");\n try {\n while (true) {\n throwIfAborted(options.signal);\n const envelope = await readEnvelope(buffer, \"record\");\n if (active && envelope.tag !== 5 /* FileChunk */ && envelope.tag !== 6 /* FileEnd */) {\n throw new Error(`w7-frame: file ${active.inode.path} ended without file-end`);\n }\n if (envelope.tag === 5 /* FileChunk */) {\n if (!active) throw new Error(\"w7-frame: file-chunk without active file\");\n const prefixLength = Math.min(envelope.length, 4 + MAX_CONTENT_ID_BYTES + 8);\n const idLengthBytes = await buffer.readExact(4, \"file-chunk content-id length\");\n const idLength = readU32LE(idLengthBytes, 0);\n if (idLength === 0 || idLength > MAX_CONTENT_ID_BYTES) {\n throw new Error(`w7-frame: invalid file-chunk content-id length ${idLength}`);\n }\n const remainingPrefixLength = idLength + 8;\n if (4 + remainingPrefixLength > prefixLength || 4 + remainingPrefixLength > envelope.length) {\n throw new Error(\"w7-frame: malformed file-chunk record length\");\n }\n const rest = await buffer.readExact(remainingPrefixLength, \"file-chunk header\");\n const contentId = decodeText(rest.subarray(0, idLength), \"file-chunk content id\");\n const chunkId = readU32LE(rest, idLength);\n const dataLength = readU32LE(rest, idLength + 4);\n if (envelope.length !== 4 + idLength + 8 + dataLength) {\n throw new Error(\"w7-frame: file-chunk payload length mismatch\");\n }\n if (contentId !== active.metadata.contentId) {\n throw new Error(`w7-frame: file-chunk content id ${contentId} does not own ${active.inode.path}`);\n }\n if (chunkId !== active.nextChunkId) {\n throw new Error(\n `w7-frame: ${active.inode.path}: expected chunk ${active.nextChunkId}, got ${chunkId}`\n );\n }\n if (chunkId >= active.inode.chunkCount) {\n throw new Error(`w7-frame: ${active.inode.path}: chunk ${chunkId} is out of range`);\n }\n const expectedBytes = Math.min(\n CHUNK_SIZE,\n active.inode.size - chunkId * CHUNK_SIZE\n );\n if (dataLength !== expectedBytes || dataLength > CHUNK_SIZE) {\n throw new Error(\n `w7-frame: ${active.inode.path}: chunk ${chunkId} has ${dataLength} bytes; expected ${expectedBytes}`\n );\n }\n const headerPayload = concatBytes(idLengthBytes, rest);\n let retention = null;\n try {\n retention = options.retainChunk ? await options.retainChunk(dataLength, options.signal) : noopRetention(dataLength);\n throwIfAborted(options.signal);\n const data = await buffer.readExact(dataLength, \"file-chunk data\");\n batchCheck = updateRecordCheck(batchCheck, envelope.header, headerPayload, data);\n active.check = crc32Update(active.check, data);\n active.nextChunkId++;\n active.receivedBytes += dataLength;\n summary.recordCount++;\n summary.chunkCount++;\n summary.byteCount += dataLength;\n const record = {\n type: \"file-chunk\",\n streamContentId: contentId,\n path: active.inode.path,\n chunkId,\n data,\n retention\n };\n retention = null;\n yield record;\n } finally {\n retention?.release();\n }\n continue;\n }\n if (envelope.length > MAX_METADATA_BYTES) {\n throw new Error(`w7-frame: metadata record length ${envelope.length} exceeds ${MAX_METADATA_BYTES}`);\n }\n const payload = await buffer.readExact(envelope.length, \"record payload\");\n if (envelope.tag !== 7 /* BatchEnd */) {\n batchCheck = updateRecordCheck(batchCheck, envelope.header, payload);\n summary.recordCount++;\n }\n switch (envelope.tag) {\n case 2 /* Delete */: {\n const metadata = parseDelete(payload);\n claimPath(ownedPaths, metadata.path);\n summary.pathCount++;\n summary.deleteCount++;\n yield { type: \"delete\", path: metadata.path };\n break;\n }\n case 3 /* Directory */: {\n const metadata = parseDirectory(payload);\n claimPath(ownedPaths, metadata.path);\n summary.pathCount++;\n summary.directoryCount++;\n yield { type: \"directory\", inode: directoryInode(metadata) };\n break;\n }\n case 4 /* FileBegin */: {\n const metadata = parseFileBegin(payload);\n claimPath(ownedPaths, metadata.path);\n if (contentIds.has(metadata.contentId)) {\n throw new Error(`w7-frame: duplicate stream content id ${metadata.contentId}`);\n }\n contentIds.add(metadata.contentId);\n const inode = fileInode(metadata);\n summary.pathCount++;\n summary.fileCount++;\n active = {\n metadata,\n inode,\n nextChunkId: 0,\n receivedBytes: 0,\n check: CRC_SEED\n };\n yield { type: \"file-begin\", streamContentId: metadata.contentId, inode };\n break;\n }\n case 6 /* FileEnd */: {\n if (!active) throw new Error(\"w7-frame: file-end without active file\");\n const metadata = parseFileEnd(payload);\n const actualCheck = crc32Finish(active.check);\n if (metadata.contentId !== active.metadata.contentId) {\n throw new Error(`w7-frame: file-end content id mismatch for ${active.inode.path}`);\n }\n if (metadata.size !== active.receivedBytes || metadata.size !== active.inode.size) {\n throw new Error(`w7-frame: file-end byte total mismatch for ${active.inode.path}`);\n }\n if (metadata.chunkCount !== active.nextChunkId || metadata.chunkCount !== active.inode.chunkCount) {\n throw new Error(`w7-frame: file-end chunk count mismatch for ${active.inode.path}`);\n }\n if (metadata.check !== actualCheck) {\n throw new Error(`w7-frame: file-end check mismatch for ${active.inode.path}`);\n }\n const record = {\n type: \"file-end\",\n streamContentId: metadata.contentId,\n path: active.inode.path,\n size: metadata.size,\n chunkCount: metadata.chunkCount,\n check: metadata.check\n };\n active = null;\n yield record;\n break;\n }\n case 7 /* BatchEnd */: {\n if (active) throw new Error(`w7-frame: batch-end while file ${active.inode.path} is active`);\n const actual = parseBatchEnd(payload);\n const expected = { ...summary, check: crc32Finish(batchCheck) };\n if (!sameSummary(actual, expected)) {\n throw new Error(\n `w7-frame: batch-end summary mismatch; expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`\n );\n }\n await buffer.ensureEof(stream);\n completed = true;\n yield { type: \"batch-end\", summary: actual };\n return;\n }\n case 1 /* BatchBegin */:\n throw new Error(\"w7-frame: duplicate batch-begin\");\n default:\n throw new Error(`w7-frame: unknown record tag ${envelope.tag}`);\n }\n }\n } catch (error) {\n failure = error;\n throw error;\n } finally {\n if (completed) {\n try {\n reader.releaseLock();\n } catch {\n }\n } else {\n await cancelReader(reader, failure);\n }\n }\n}\nfunction* encodeRecords(batchId, deletes, directories, files) {\n const state = {\n batchCheck: CRC_SEED,\n summary: {\n recordCount: 0,\n pathCount: 0,\n deleteCount: 0,\n directoryCount: 0,\n fileCount: 0,\n chunkCount: 0,\n byteCount: 0\n }\n };\n yield encodeMetadataRecord(1 /* BatchBegin */, { id: batchId, mode: MODE }, state);\n for (const path of deletes) {\n state.summary.pathCount++;\n state.summary.deleteCount++;\n yield encodeMetadataRecord(2 /* Delete */, { path }, state);\n }\n for (const inode of directories) {\n state.summary.pathCount++;\n state.summary.directoryCount++;\n yield encodeMetadataRecord(3 /* Directory */, {\n ...inodeMetadata(inode),\n kind: inode.kind\n }, state);\n }\n for (const file of files) {\n state.summary.pathCount++;\n state.summary.fileCount++;\n yield encodeMetadataRecord(4 /* FileBegin */, {\n ...inodeMetadata(file.inode),\n kind: file.inode.kind,\n contentId: file.contentId,\n size: file.inode.size,\n chunkCount: file.inode.chunkCount\n }, state);\n let fileCheck = CRC_SEED;\n for (const chunk of file.chunks) {\n const data = chunk.data;\n const contentBytes = new TextEncoder().encode(file.contentId);\n const prefix = new Uint8Array(4 + contentBytes.length + 8);\n writeU32LE(prefix, 0, contentBytes.length);\n prefix.set(contentBytes, 4);\n writeU32LE(prefix, 4 + contentBytes.length, chunk.chunkId);\n writeU32LE(prefix, 8 + contentBytes.length, data.byteLength);\n const header = recordHeader(5 /* FileChunk */, prefix.byteLength + data.byteLength);\n state.batchCheck = updateRecordCheck(state.batchCheck, header, prefix, data);\n state.summary.recordCount++;\n state.summary.chunkCount++;\n state.summary.byteCount += data.byteLength;\n fileCheck = crc32Update(fileCheck, data);\n yield [concatBytes(header, prefix), data];\n }\n yield encodeMetadataRecord(6 /* FileEnd */, {\n contentId: file.contentId,\n size: file.inode.size,\n chunkCount: file.inode.chunkCount,\n check: crc32Finish(fileCheck)\n }, state);\n }\n const end = {\n ...state.summary,\n check: crc32Finish(state.batchCheck)\n };\n yield encodeMetadataRecord(7 /* BatchEnd */, end);\n}\nfunction encodeMetadataRecord(tag, value, state) {\n const payload = new TextEncoder().encode(JSON.stringify(value));\n if (payload.byteLength > MAX_METADATA_BYTES) {\n throw new Error(`w7-frame: metadata record exceeds ${MAX_METADATA_BYTES} bytes`);\n }\n const header = recordHeader(tag, payload.byteLength);\n if (state && tag !== 7 /* BatchEnd */) {\n state.batchCheck = updateRecordCheck(state.batchCheck, header, payload);\n state.summary.recordCount++;\n }\n return [concatBytes(header, payload)];\n}\nfunction preparePayload(payload, batchId) {\n if (!payload || !Array.isArray(payload.inodes) || !Array.isArray(payload.chunks)) {\n throw new Error(\"w7-frame: payload must contain inode and chunk arrays\");\n }\n const ownedPaths = new PathOwnership();\n const deletes = [...payload.deletePaths ?? []];\n for (const path of deletes) claimPath(ownedPaths, canonicalPath(path, \"delete path\"));\n const chunksByPath = /* @__PURE__ */ new Map();\n for (const chunk of payload.chunks) {\n const path = canonicalPath(chunk.path, \"chunk path\");\n const list = chunksByPath.get(path);\n if (list) list.push(chunk);\n else chunksByPath.set(path, [chunk]);\n }\n const directories = [];\n const files = [];\n let fileIndex = 0;\n for (const inode of payload.inodes) {\n const path = canonicalPath(inode.path, \"inode path\");\n if (path !== inode.path) throw new Error(`w7-frame: noncanonical inode path ${inode.path}`);\n claimPath(ownedPaths, path);\n const normalizedInode = normalizeInode(inode);\n const fileChunks = chunksByPath.get(path) ?? [];\n if (normalizedInode.kind === \"directory\") {\n if (fileChunks.length > 0) throw new Error(`w7-frame: directory ${path} has chunks`);\n directories.push(normalizedInode);\n } else {\n validateChunks(normalizedInode, fileChunks);\n files.push({\n inode: normalizedInode,\n contentId: `${batchId}:${fileIndex++}`,\n chunks: fileChunks\n });\n }\n chunksByPath.delete(path);\n }\n if (chunksByPath.size > 0) {\n throw new Error(`w7-frame: chunk has no inode: ${chunksByPath.keys().next().value}`);\n }\n return { deletes, directories, files };\n}\nfunction parseBatchBegin(bytes) {\n const value = parseObject(bytes, \"batch-begin\", [\"id\", \"mode\"]);\n const id = boundedString(value.id, \"batch id\", MAX_BATCH_ID_BYTES);\n if (value.mode !== MODE) throw new Error(`w7-frame: unsupported batch mode ${String(value.mode)}`);\n return { id, mode: MODE };\n}\nfunction parseDelete(bytes) {\n const value = parseObject(bytes, \"delete\", [\"path\"]);\n return { path: canonicalPath(value.path, \"delete path\") };\n}\nfunction parseDirectory(bytes) {\n const value = parseObject(bytes, \"directory\", [\"path\", \"kind\", \"mtime\", \"mode\"], [\"atime\"]);\n if (value.kind !== \"directory\") {\n throw new Error(`w7-frame: unsupported directory kind ${String(value.kind)}`);\n }\n return { ...parseInodeMetadata(value, \"directory\"), kind: \"directory\" };\n}\nfunction parseFileBegin(bytes) {\n const value = parseObject(\n bytes,\n \"file-begin\",\n [\"path\", \"kind\", \"contentId\", \"size\", \"chunkCount\", \"mtime\", \"mode\"],\n [\"atime\"]\n );\n const base = parseInodeMetadata(value, \"file-begin\");\n if (value.kind !== \"file\" && value.kind !== \"symlink\") {\n throw new Error(`w7-frame: unsupported file-begin kind ${String(value.kind)}`);\n }\n const contentId = boundedString(value.contentId, \"stream content id\", MAX_CONTENT_ID_BYTES);\n if (!/^[A-Za-z0-9._:-]+$/.test(contentId)) {\n throw new Error(`w7-frame: invalid stream content id ${contentId}`);\n }\n const size = safeInteger(value.size, \"file size\");\n const chunkCount = u32(value.chunkCount, \"file chunk count\");\n const expected = size === 0 ? 0 : Math.ceil(size / CHUNK_SIZE);\n if (chunkCount !== expected) {\n throw new Error(`w7-frame: ${base.path}: expected ${expected} chunks, got ${chunkCount}`);\n }\n return { ...base, kind: value.kind, contentId, size, chunkCount };\n}\nfunction parseFileEnd(bytes) {\n const value = parseObject(bytes, \"file-end\", [\"contentId\", \"size\", \"chunkCount\", \"check\"]);\n return {\n contentId: boundedString(value.contentId, \"stream content id\", MAX_CONTENT_ID_BYTES),\n size: safeInteger(value.size, \"file-end size\"),\n chunkCount: u32(value.chunkCount, \"file-end chunk count\"),\n check: u32(value.check, \"file-end check\")\n };\n}\nfunction parseBatchEnd(bytes) {\n const keys = [\n \"recordCount\",\n \"pathCount\",\n \"deleteCount\",\n \"directoryCount\",\n \"fileCount\",\n \"chunkCount\",\n \"byteCount\",\n \"check\"\n ];\n const value = parseObject(bytes, \"batch-end\", keys);\n return {\n recordCount: safeInteger(value.recordCount, \"batch record count\"),\n pathCount: safeInteger(value.pathCount, \"batch path count\"),\n deleteCount: safeInteger(value.deleteCount, \"batch delete count\"),\n directoryCount: safeInteger(value.directoryCount, \"batch directory count\"),\n fileCount: safeInteger(value.fileCount, \"batch file count\"),\n chunkCount: safeInteger(value.chunkCount, \"batch chunk count\"),\n byteCount: safeInteger(value.byteCount, \"batch byte count\"),\n check: u32(value.check, \"batch check\")\n };\n}\nfunction parseInodeMetadata(value, label) {\n return {\n path: canonicalPath(value.path, `${label} path`),\n ...value.atime === void 0 ? {} : { atime: safeInteger(value.atime, `${label} atime`) },\n mtime: safeInteger(value.mtime, `${label} mtime`),\n mode: u32(value.mode, `${label} mode`)\n };\n}\nfunction parseObject(bytes, label, required, optional = []) {\n let value;\n try {\n value = JSON.parse(decodeText(bytes, label));\n } catch (error) {\n if (error instanceof Error && error.message.startsWith(\"w7-frame:\")) throw error;\n throw new Error(`w7-frame: invalid ${label} JSON: ${errorMessage(error)}`);\n }\n if (!isObject(value) || Array.isArray(value)) throw new Error(`w7-frame: ${label} must be an object`);\n const allowed = /* @__PURE__ */ new Set([...required, ...optional]);\n for (const key of required) {\n if (!Object.hasOwn(value, key)) throw new Error(`w7-frame: ${label} missing ${key}`);\n }\n for (const key of Object.keys(value)) {\n if (!allowed.has(key)) throw new Error(`w7-frame: ${label} has unknown field ${key}`);\n }\n return value;\n}\nfunction normalizeInode(inode) {\n canonicalPath(inode.path, \"inode path\");\n if (inode.parentPath !== parentPath(inode.path)) {\n throw new Error(`w7-frame: ${inode.path}: noncanonical parent path ${inode.parentPath}`);\n }\n safeInteger(inode.size, `${inode.path} size`);\n u32(inode.chunkCount, `${inode.path} chunk count`);\n safeInteger(inode.mtime, `${inode.path} mtime`);\n if (inode.atime !== void 0) safeInteger(inode.atime, `${inode.path} atime`);\n u32(inode.mode, `${inode.path} mode`);\n const rawKind = inode.kind ?? (inode.isDir ? \"directory\" : \"file\");\n if (rawKind !== \"file\" && rawKind !== \"directory\" && rawKind !== \"symlink\") {\n throw new Error(`w7-frame: unsupported inode kind ${String(rawKind)}`);\n }\n const kind = rawKind;\n if (kind === \"directory\" && !inode.isDir) {\n throw new Error(`w7-frame: directory inode ${inode.path} must be a directory`);\n }\n if (kind !== \"directory\" && inode.isDir) {\n throw new Error(`w7-frame: ${kind} inode ${inode.path} cannot be a directory`);\n }\n if (kind === \"directory\" && (inode.size !== 0 || inode.chunkCount !== 0)) {\n throw new Error(`w7-frame: directory ${inode.path} must have zero size and chunks`);\n }\n if (kind !== \"directory\") {\n const expected = inode.size === 0 ? 0 : Math.ceil(inode.size / CHUNK_SIZE);\n if (inode.chunkCount !== expected) {\n throw new Error(`w7-frame: ${inode.path}: expected ${expected} chunks, got ${inode.chunkCount}`);\n }\n return { ...inode, kind, isDir: false };\n }\n return { ...inode, kind, isDir: true };\n}\nfunction validateChunks(inode, chunks) {\n if (chunks.length !== inode.chunkCount) {\n throw new Error(`w7-frame: ${inode.path}: expected ${inode.chunkCount} chunks, got ${chunks.length}`);\n }\n chunks.sort((left, right) => left.chunkId - right.chunkId);\n for (let index = 0; index < chunks.length; index++) {\n const chunk = chunks[index];\n if (chunk.chunkId !== index) {\n throw new Error(`w7-frame: ${inode.path}: expected chunk ${index}, got ${chunk.chunkId}`);\n }\n const expected = Math.min(CHUNK_SIZE, inode.size - index * CHUNK_SIZE);\n if (!(chunk.data instanceof Uint8Array) || chunk.data.byteLength !== expected) {\n throw new Error(`w7-frame: ${inode.path}: chunk ${index} must contain ${expected} bytes`);\n }\n }\n}\nfunction directoryInode(metadata) {\n return {\n path: metadata.path,\n parentPath: parentPath(metadata.path),\n kind: metadata.kind,\n isDir: true,\n size: 0,\n atime: metadata.atime,\n mtime: metadata.mtime,\n mode: metadata.mode,\n chunkCount: 0\n };\n}\nfunction fileInode(metadata) {\n return {\n path: metadata.path,\n parentPath: parentPath(metadata.path),\n kind: metadata.kind,\n isDir: false,\n size: metadata.size,\n atime: metadata.atime,\n mtime: metadata.mtime,\n mode: metadata.mode,\n chunkCount: metadata.chunkCount\n };\n}\nfunction inodeMetadata(inode) {\n return {\n path: inode.path,\n ...inode.atime === void 0 ? {} : { atime: inode.atime },\n mtime: inode.mtime,\n mode: inode.mode\n };\n}\nfunction canonicalPath(value, label) {\n const path = boundedString(value, label, MAX_PATH_BYTES);\n if (path.includes(\"\\0\")) throw new Error(`w7-frame: ${label} contains NUL`);\n const normalized = normalizePath(path);\n if (!path || normalized !== path) throw new Error(`w7-frame: noncanonical ${label}: ${path}`);\n return path;\n}\nvar PathOwnership = class {\n paths = /* @__PURE__ */ new Set();\n pathBytes = 0;\n claim(path) {\n if (this.paths.has(path)) throw new Error(`w7-frame: duplicate path ownership: ${path}`);\n if (this.paths.size >= W7_MAX_PATHS_PER_BATCH) {\n throw new Error(`w7-frame: batch exceeds ${W7_MAX_PATHS_PER_BATCH} owned paths`);\n }\n const nextPathBytes = this.pathBytes + new TextEncoder().encode(path).byteLength;\n if (nextPathBytes > W7_MAX_OWNED_PATH_BYTES) {\n throw new Error(\n `w7-frame: owned path bytes exceed ${W7_MAX_OWNED_PATH_BYTES}`\n );\n }\n this.paths.add(path);\n this.pathBytes = nextPathBytes;\n }\n};\nfunction claimPath(paths, path) {\n paths.claim(path);\n}\nfunction normalizePath(path) {\n const out = [];\n for (const segment of path.split(\"/\")) {\n if (segment === \"..\") {\n if (out.length > 0) out.pop();\n } else if (segment !== \"\" && segment !== \".\") {\n out.push(segment);\n }\n }\n return out.join(\"/\");\n}\nfunction parentPath(path) {\n const index = path.lastIndexOf(\"/\");\n return index < 0 ? \"\" : path.slice(0, index);\n}\nfunction boundedString(value, label, maxBytes) {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new Error(`w7-frame: ${label} must be a non-empty string`);\n }\n const length = new TextEncoder().encode(value).byteLength;\n if (length > maxBytes) throw new Error(`w7-frame: ${label} exceeds ${maxBytes} bytes`);\n return value;\n}\nfunction safeInteger(value, label) {\n if (!Number.isSafeInteger(value) || value < 0) {\n throw new Error(`w7-frame: ${label} must be a non-negative safe integer`);\n }\n return value;\n}\nfunction u32(value, label) {\n const integer = safeInteger(value, label);\n if (integer > 4294967295) throw new Error(`w7-frame: ${label} exceeds uint32`);\n return integer;\n}\nfunction sameSummary(left, right) {\n return left.recordCount === right.recordCount && left.pathCount === right.pathCount && left.deleteCount === right.deleteCount && left.directoryCount === right.directoryCount && left.fileCount === right.fileCount && left.chunkCount === right.chunkCount && left.byteCount === right.byteCount && left.check === right.check;\n}\nasync function readEnvelope(buffer, label) {\n const header = await buffer.readExact(5, `${label} header`);\n return { tag: header[0], length: readU32LE(header, 1), header };\n}\nfunction recordHeader(tag, length) {\n const header = new Uint8Array(5);\n header[0] = tag;\n writeU32LE(header, 1, length);\n return header;\n}\nfunction writeU32LE(out, offset, value) {\n out[offset] = value & 255;\n out[offset + 1] = value >>> 8 & 255;\n out[offset + 2] = value >>> 16 & 255;\n out[offset + 3] = value >>> 24 & 255;\n}\nfunction readU32LE(bytes, offset) {\n return (bytes[offset] | bytes[offset + 1] << 8 | bytes[offset + 2] << 16 | bytes[offset + 3] << 24) >>> 0;\n}\nfunction updateRecordCheck(seed, ...parts) {\n let check = seed;\n for (const part of parts) check = crc32Update(check, part);\n return check;\n}\nvar CRC_SEED = 4294967295;\nvar crcTable = null;\nfunction crc32Update(check, bytes) {\n crcTable ??= createCrcTable();\n const table = crcTable;\n let value = check;\n for (const byte of bytes) value = table[(value ^ byte) & 255] ^ value >>> 8;\n return value >>> 0;\n}\nfunction crc32Finish(check) {\n return (check ^ 4294967295) >>> 0;\n}\nfunction createCrcTable() {\n const table = new Uint32Array(256);\n for (let index = 0; index < table.length; index++) {\n let value = index;\n for (let bit = 0; bit < 8; bit++) {\n value = (value & 1) !== 0 ? 3988292384 ^ value >>> 1 : value >>> 1;\n }\n table[index] = value >>> 0;\n }\n return table;\n}\nfunction noopRetention(bytes) {\n return { bytes, release() {\n } };\n}\nfunction decodeText(bytes, label) {\n try {\n return new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: false }).decode(bytes);\n } catch (error) {\n throw new Error(`w7-frame: invalid UTF-8 in ${label}: ${errorMessage(error)}`);\n }\n}\nfunction concatBytes(...parts) {\n const output = new Uint8Array(parts.reduce((total, part) => total + part.byteLength, 0));\n let offset = 0;\n for (const part of parts) {\n output.set(part, offset);\n offset += part.byteLength;\n }\n return output;\n}\nfunction bytesEqual(left, right) {\n return left.length === right.length && left.every((byte, index) => byte === right[index]);\n}\nfunction hex(bytes) {\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\" \");\n}\nfunction isObject(value) {\n return typeof value === \"object\" && value !== null;\n}\nfunction errorMessage(error) {\n return error instanceof Error ? error.message : String(error);\n}\nfunction throwIfAborted(signal) {\n if (!signal?.aborted) return;\n throw new DOMException(\n signal.reason instanceof Error ? signal.reason.message : String(signal.reason ?? \"Aborted\"),\n \"AbortError\"\n );\n}\nasync function cancelReader(reader, reason) {\n try {\n await reader.cancel(reason);\n } catch {\n }\n try {\n reader.releaseLock();\n } catch {\n }\n}\nvar ExactByteReader = class {\n constructor(reader, signal) {\n this.reader = reader;\n this.signal = signal;\n }\n reader;\n signal;\n done = false;\n async read(read, cancel) {\n throwIfAborted(this.signal);\n const abort = () => {\n void cancel().catch(() => {\n });\n };\n this.signal?.addEventListener(\"abort\", abort, { once: true });\n try {\n const result = await read();\n throwIfAborted(this.signal);\n return result;\n } finally {\n this.signal?.removeEventListener(\"abort\", abort);\n }\n }\n async readExact(length, label) {\n if (length === 0) return new Uint8Array(0);\n const output = new Uint8Array(length);\n let offset = 0;\n while (offset < length) {\n if (this.done) {\n throw new Error(\n `w7-frame: stream ended ${offset} bytes into expected ${length}-byte ${label}`\n );\n }\n const next = await this.read(\n () => this.reader.read(new Uint8Array(length - offset)),\n () => this.reader.cancel(this.signal?.reason)\n );\n if (next.done) this.done = true;\n else if (next.value.byteLength > 0) {\n output.set(next.value, offset);\n offset += next.value.byteLength;\n }\n }\n return output;\n }\n async ensureEof(stream) {\n if (this.done) return;\n this.reader.releaseLock();\n const reader = stream.getReader();\n try {\n const next = await this.read(() => reader.read(), () => reader.cancel(this.signal?.reason));\n if (!next.done && next.value.byteLength > 0) {\n await reader.cancel(new Error(\"w7-frame: trailing bytes after batch-end\"));\n throw new Error(\"w7-frame: trailing bytes after batch-end\");\n }\n this.done = true;\n } finally {\n try {\n reader.releaseLock();\n } catch {\n }\n }\n }\n};";
|
|
21
|
+
export const TAR_STREAM_PREAMBLE = "var MAX_FILE_BYTES = 2e7;\nfunction canonicalTarName(name) {\n const out = [];\n for (const seg of name.split(\"/\")) {\n if (seg === \"..\") {\n if (out.length === 0) return \"\";\n out.pop();\n } else if (seg !== \"\" && seg !== \".\") {\n out.push(seg);\n }\n }\n return out.join(\"/\");\n}\nfunction parseTarHeader(block) {\n if (block[0] === 0) return null;\n let name = \"\";\n for (let i = 0; i < 100 && block[i] !== 0; i++) {\n name += String.fromCharCode(block[i]);\n }\n let prefix = \"\";\n for (let i = 345; i < 500 && block[i] !== 0; i++) {\n prefix += String.fromCharCode(block[i]);\n }\n if (prefix) name = prefix + \"/\" + name;\n name = canonicalTarName(name);\n let sizeStr = \"\";\n for (let i = 124; i < 136 && block[i] !== 0; i++) {\n sizeStr += String.fromCharCode(block[i]);\n }\n const size = parseInt(sizeStr.trim(), 8) || 0;\n const typeFlag = block[156];\n return { name, size, typeFlag };\n}\nasync function* readableStreamToAsyncIterable(rs) {\n const reader = rs.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) return;\n if (value && value.length > 0) yield value;\n }\n } finally {\n try {\n reader.releaseLock();\n } catch {\n }\n }\n}\nasync function* streamTarEntries(source, onSkip) {\n let carry = new Uint8Array(0);\n let state = { kind: \"header\" };\n function concat(a, b) {\n if (a.length === 0) return b;\n if (b.length === 0) return a;\n const out = new Uint8Array(a.length + b.length);\n out.set(a, 0);\n out.set(b, a.length);\n return out;\n }\n for await (const chunkRaw of source) {\n let buf = concat(carry, chunkRaw);\n let cursor = 0;\n while (true) {\n if (state.kind === \"header\") {\n if (buf.length - cursor < 512) break;\n const header = buf.subarray(cursor, cursor + 512);\n const parsed = parseTarHeader(header);\n cursor += 512;\n if (!parsed) return;\n const { name, size, typeFlag } = parsed;\n const pad = size === 0 ? 0 : (512 - size % 512) % 512;\n const isRegularFile = typeFlag === 48 || typeFlag === 0;\n if (size === 0) {\n if (isRegularFile && name) {\n yield { name, data: new Uint8Array(0) };\n }\n state = { kind: \"header\" };\n continue;\n }\n if (!isRegularFile || !name || size > MAX_FILE_BYTES) {\n if (onSkip) {\n const reason = !isRegularFile ? \"non-regular\" : !name ? \"no-name\" : \"too-large\";\n try {\n onSkip(name, size, reason);\n } catch {\n }\n }\n state = { kind: \"skip\", remaining: size + pad };\n continue;\n }\n state = {\n kind: \"file\",\n name,\n remaining: size,\n fileBuf: new Uint8Array(size),\n fileOffset: 0,\n pad,\n skip: false\n };\n continue;\n }\n if (state.kind === \"file\") {\n const avail2 = buf.length - cursor;\n if (avail2 === 0) break;\n if (state.remaining > 0) {\n const take2 = Math.min(state.remaining, avail2);\n state.fileBuf.set(buf.subarray(cursor, cursor + take2), state.fileOffset);\n state.fileOffset += take2;\n state.remaining -= take2;\n cursor += take2;\n if (state.remaining > 0) break;\n }\n if (state.pad > 0) {\n const avail22 = buf.length - cursor;\n if (avail22 === 0) break;\n const take2 = Math.min(state.pad, avail22);\n state.pad -= take2;\n cursor += take2;\n if (state.pad > 0) break;\n }\n yield { name: state.name, data: state.fileBuf };\n state = { kind: \"header\" };\n continue;\n }\n const avail = buf.length - cursor;\n if (avail === 0) break;\n const take = Math.min(state.remaining, avail);\n cursor += take;\n state.remaining -= take;\n if (state.remaining > 0) break;\n state = { kind: \"header\" };\n }\n if (cursor >= buf.length) {\n carry = new Uint8Array(0);\n } else if (cursor === 0) {\n carry = buf;\n } else {\n carry = buf.slice(cursor);\n }\n }\n}\nasync function* streamPackageEntries(source, onSkip) {\n let prefix = null;\n for await (const entry of streamTarEntries(source, onSkip)) {\n if (prefix === null) {\n prefix = entry.name.split(\"/\", 1)[0];\n }\n if (entry.name === prefix) continue;\n if (!entry.name.startsWith(prefix + \"/\")) {\n throw new Error(\n `package tarball is not single-rooted: entry \"${entry.name}\" sits outside \"${prefix}/\"`\n );\n }\n yield { name: entry.name.slice(prefix.length + 1), data: entry.data };\n }\n}";
|
|
22
|
+
export const W7_FRAME_PREAMBLE = "var CHUNK_SIZE = 65536;\nvar MAX_TX_BLOB_BYTES = 1 * 1024 * 1024;\nvar SQLITE_MAX_STATEMENT_BYTES = 100 * 1024;\nvar MAX_RPC_SAFE_PAYLOAD_BYTES = 28 * 1024 * 1024;\nvar SUPERVISOR_HEAP_CEILING_BYTES = 64 * 1024 * 1024;\nvar SUPERVISOR_IN_FLIGHT_ALLOCATION_BUDGET_BYTES = 40 * 1024 * 1024;\nvar SUPERVISOR_READ_RESERVE_BYTES = 1024 * 1024;\nvar MAX_GLOBAL_WRITE_STREAM_CREDIT_BYTES = 8 * 1024 * 1024;\n\nvar W7_MAGIC = new Uint8Array([78, 87, 55, 3]);\nvar ENCODER_QUEUE_HWM = 0;\nvar MAX_METADATA_BYTES = 64 * 1024;\nvar MAX_PATH_BYTES = 64 * 1024;\nvar MAX_BATCH_ID_BYTES = 128;\nvar MAX_CONTENT_ID_BYTES = 256;\nvar W7_MAX_PATHS_PER_BATCH = 128;\nvar W7_MAX_OWNED_PATH_BYTES = 64 * 1024;\nvar W7_MAX_RECORD_BYTES = 5 + 4 + MAX_CONTENT_ID_BYTES + 8 + CHUNK_SIZE;\nvar MODE = \"path-atomic-committed-prefix\";\nfunction encodeWriteBatchStream(payload) {\n const batchId = crypto.randomUUID();\n const { deletes, directories, files } = preparePayload(payload, batchId);\n const iterator = encodeRecords(batchId, deletes, directories, files);\n let closed = false;\n let magicEmitted = false;\n const source = {\n type: \"bytes\",\n pull(controller) {\n if (closed) return;\n try {\n if (!magicEmitted) {\n magicEmitted = true;\n controller.enqueue(W7_MAGIC.slice());\n return;\n }\n const next = iterator.next();\n if (next.done) {\n closed = true;\n controller.close();\n return;\n }\n for (const part of next.value) controller.enqueue(part);\n } catch (error) {\n closed = true;\n controller.error(error);\n }\n },\n cancel() {\n closed = true;\n iterator.return?.(void 0);\n }\n };\n return new ReadableStream(source, {\n highWaterMark: ENCODER_QUEUE_HWM\n });\n}\nasync function decodeWriteBatchStream(stream, options = {}) {\n let reader;\n try {\n reader = stream.getReader({ mode: \"byob\" });\n } catch {\n throw new Error(\"w7-frame: stream must be a byte-oriented ReadableStream\");\n }\n const buffer = new ExactByteReader(reader, options.signal);\n let handedOff = false;\n try {\n throwIfAborted(options.signal);\n const magic = await buffer.readExact(W7_MAGIC.length, \"magic\");\n if (!bytesEqual(magic, W7_MAGIC)) {\n const version = magic.length === 4 && magic[0] === 78 && magic[1] === 87 && magic[2] === 55 ? magic[3] : null;\n if (version !== null) {\n throw new Error(`w7-frame: unsupported protocol version ${version}; expected 3`);\n }\n throw new Error(`w7-frame: bad magic, expected NW7\\\\x03, got ${hex(magic)}`);\n }\n const beginEnvelope = await readEnvelope(buffer, \"batch-begin\");\n if (beginEnvelope.tag !== 1 ) {\n throw new Error(`w7-frame: first record must be batch-begin, got tag ${beginEnvelope.tag}`);\n }\n if (beginEnvelope.length > MAX_METADATA_BYTES) {\n throw new Error(\n `w7-frame: batch-begin length ${beginEnvelope.length} exceeds ${MAX_METADATA_BYTES}`\n );\n }\n const beginPayload = await buffer.readExact(beginEnvelope.length, \"batch-begin payload\");\n const begin = parseBatchBegin(beginPayload);\n const initialCheck = updateRecordCheck(CRC_SEED, beginEnvelope.header, beginPayload);\n handedOff = true;\n return {\n batchId: begin.id,\n mode: begin.mode,\n records: decodeRecords(stream, reader, buffer, options, initialCheck)\n };\n } catch (error) {\n if (!handedOff) await cancelReader(reader, error);\n throw error;\n }\n}\nasync function* decodeRecords(stream, reader, buffer, options, initialCheck) {\n const ownedPaths = new PathOwnership();\n const contentIds = new Set();\n let active = null;\n let batchCheck = initialCheck;\n const summary = {\n recordCount: 1,\n pathCount: 0,\n deleteCount: 0,\n directoryCount: 0,\n fileCount: 0,\n chunkCount: 0,\n byteCount: 0\n };\n let completed = false;\n let failure = new DOMException(\"W7 consumer cancelled\", \"AbortError\");\n try {\n while (true) {\n throwIfAborted(options.signal);\n const envelope = await readEnvelope(buffer, \"record\");\n if (active && envelope.tag !== 5 && envelope.tag !== 6 ) {\n throw new Error(`w7-frame: file ${active.inode.path} ended without file-end`);\n }\n if (envelope.tag === 5 ) {\n if (!active) throw new Error(\"w7-frame: file-chunk without active file\");\n const prefixLength = Math.min(envelope.length, 4 + MAX_CONTENT_ID_BYTES + 8);\n const idLengthBytes = await buffer.readExact(4, \"file-chunk content-id length\");\n const idLength = readU32LE(idLengthBytes, 0);\n if (idLength === 0 || idLength > MAX_CONTENT_ID_BYTES) {\n throw new Error(`w7-frame: invalid file-chunk content-id length ${idLength}`);\n }\n const remainingPrefixLength = idLength + 8;\n if (4 + remainingPrefixLength > prefixLength || 4 + remainingPrefixLength > envelope.length) {\n throw new Error(\"w7-frame: malformed file-chunk record length\");\n }\n const rest = await buffer.readExact(remainingPrefixLength, \"file-chunk header\");\n const contentId = decodeText(rest.subarray(0, idLength), \"file-chunk content id\");\n const chunkId = readU32LE(rest, idLength);\n const dataLength = readU32LE(rest, idLength + 4);\n if (envelope.length !== 4 + idLength + 8 + dataLength) {\n throw new Error(\"w7-frame: file-chunk payload length mismatch\");\n }\n if (contentId !== active.metadata.contentId) {\n throw new Error(`w7-frame: file-chunk content id ${contentId} does not own ${active.inode.path}`);\n }\n if (chunkId !== active.nextChunkId) {\n throw new Error(\n `w7-frame: ${active.inode.path}: expected chunk ${active.nextChunkId}, got ${chunkId}`\n );\n }\n if (chunkId >= active.inode.chunkCount) {\n throw new Error(`w7-frame: ${active.inode.path}: chunk ${chunkId} is out of range`);\n }\n const expectedBytes = Math.min(\n CHUNK_SIZE,\n active.inode.size - chunkId * CHUNK_SIZE\n );\n if (dataLength !== expectedBytes || dataLength > CHUNK_SIZE) {\n throw new Error(\n `w7-frame: ${active.inode.path}: chunk ${chunkId} has ${dataLength} bytes; expected ${expectedBytes}`\n );\n }\n const headerPayload = concatBytes(idLengthBytes, rest);\n let retention = null;\n try {\n retention = options.retainChunk ? await options.retainChunk(dataLength, options.signal) : noopRetention(dataLength);\n throwIfAborted(options.signal);\n const data = await buffer.readExact(dataLength, \"file-chunk data\");\n batchCheck = updateRecordCheck(batchCheck, envelope.header, headerPayload, data);\n active.check = crc32Update(active.check, data);\n active.nextChunkId++;\n active.receivedBytes += dataLength;\n summary.recordCount++;\n summary.chunkCount++;\n summary.byteCount += dataLength;\n const record = {\n type: \"file-chunk\",\n streamContentId: contentId,\n path: active.inode.path,\n chunkId,\n data,\n retention\n };\n retention = null;\n yield record;\n } finally {\n retention?.release();\n }\n continue;\n }\n if (envelope.length > MAX_METADATA_BYTES) {\n throw new Error(`w7-frame: metadata record length ${envelope.length} exceeds ${MAX_METADATA_BYTES}`);\n }\n const payload = await buffer.readExact(envelope.length, \"record payload\");\n if (envelope.tag !== 7 ) {\n batchCheck = updateRecordCheck(batchCheck, envelope.header, payload);\n summary.recordCount++;\n }\n switch (envelope.tag) {\n case 2 : {\n const metadata = parseDelete(payload);\n claimPath(ownedPaths, metadata.path);\n summary.pathCount++;\n summary.deleteCount++;\n yield { type: \"delete\", path: metadata.path };\n break;\n }\n case 3 : {\n const metadata = parseDirectory(payload);\n claimPath(ownedPaths, metadata.path);\n summary.pathCount++;\n summary.directoryCount++;\n yield { type: \"directory\", inode: directoryInode(metadata) };\n break;\n }\n case 4 : {\n const metadata = parseFileBegin(payload);\n claimPath(ownedPaths, metadata.path);\n if (contentIds.has(metadata.contentId)) {\n throw new Error(`w7-frame: duplicate stream content id ${metadata.contentId}`);\n }\n contentIds.add(metadata.contentId);\n const inode = fileInode(metadata);\n summary.pathCount++;\n summary.fileCount++;\n active = {\n metadata,\n inode,\n nextChunkId: 0,\n receivedBytes: 0,\n check: CRC_SEED\n };\n yield { type: \"file-begin\", streamContentId: metadata.contentId, inode };\n break;\n }\n case 6 : {\n if (!active) throw new Error(\"w7-frame: file-end without active file\");\n const metadata = parseFileEnd(payload);\n const actualCheck = crc32Finish(active.check);\n if (metadata.contentId !== active.metadata.contentId) {\n throw new Error(`w7-frame: file-end content id mismatch for ${active.inode.path}`);\n }\n if (metadata.size !== active.receivedBytes || metadata.size !== active.inode.size) {\n throw new Error(`w7-frame: file-end byte total mismatch for ${active.inode.path}`);\n }\n if (metadata.chunkCount !== active.nextChunkId || metadata.chunkCount !== active.inode.chunkCount) {\n throw new Error(`w7-frame: file-end chunk count mismatch for ${active.inode.path}`);\n }\n if (metadata.check !== actualCheck) {\n throw new Error(`w7-frame: file-end check mismatch for ${active.inode.path}`);\n }\n const record = {\n type: \"file-end\",\n streamContentId: metadata.contentId,\n path: active.inode.path,\n size: metadata.size,\n chunkCount: metadata.chunkCount,\n check: metadata.check\n };\n active = null;\n yield record;\n break;\n }\n case 7 : {\n if (active) throw new Error(`w7-frame: batch-end while file ${active.inode.path} is active`);\n const actual = parseBatchEnd(payload);\n const expected = { ...summary, check: crc32Finish(batchCheck) };\n if (!sameSummary(actual, expected)) {\n throw new Error(\n `w7-frame: batch-end summary mismatch; expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`\n );\n }\n await buffer.ensureEof(stream);\n completed = true;\n yield { type: \"batch-end\", summary: actual };\n return;\n }\n case 1 :\n throw new Error(\"w7-frame: duplicate batch-begin\");\n default:\n throw new Error(`w7-frame: unknown record tag ${envelope.tag}`);\n }\n }\n } catch (error) {\n failure = error;\n throw error;\n } finally {\n if (completed) {\n try {\n reader.releaseLock();\n } catch {\n }\n } else {\n await cancelReader(reader, failure);\n }\n }\n}\nfunction* encodeRecords(batchId, deletes, directories, files) {\n const state = {\n batchCheck: CRC_SEED,\n summary: {\n recordCount: 0,\n pathCount: 0,\n deleteCount: 0,\n directoryCount: 0,\n fileCount: 0,\n chunkCount: 0,\n byteCount: 0\n }\n };\n yield encodeMetadataRecord(1 , { id: batchId, mode: MODE }, state);\n for (const path of deletes) {\n state.summary.pathCount++;\n state.summary.deleteCount++;\n yield encodeMetadataRecord(2 , { path }, state);\n }\n for (const inode of directories) {\n state.summary.pathCount++;\n state.summary.directoryCount++;\n yield encodeMetadataRecord(3 , {\n ...inodeMetadata(inode),\n kind: inode.kind\n }, state);\n }\n for (const file of files) {\n state.summary.pathCount++;\n state.summary.fileCount++;\n yield encodeMetadataRecord(4 , {\n ...inodeMetadata(file.inode),\n kind: file.inode.kind,\n contentId: file.contentId,\n size: file.inode.size,\n chunkCount: file.inode.chunkCount\n }, state);\n let fileCheck = CRC_SEED;\n for (const chunk of file.chunks) {\n const data = chunk.data;\n const contentBytes = new TextEncoder().encode(file.contentId);\n const prefix = new Uint8Array(4 + contentBytes.length + 8);\n writeU32LE(prefix, 0, contentBytes.length);\n prefix.set(contentBytes, 4);\n writeU32LE(prefix, 4 + contentBytes.length, chunk.chunkId);\n writeU32LE(prefix, 8 + contentBytes.length, data.byteLength);\n const header = recordHeader(5 , prefix.byteLength + data.byteLength);\n state.batchCheck = updateRecordCheck(state.batchCheck, header, prefix, data);\n state.summary.recordCount++;\n state.summary.chunkCount++;\n state.summary.byteCount += data.byteLength;\n fileCheck = crc32Update(fileCheck, data);\n yield [concatBytes(header, prefix), data];\n }\n yield encodeMetadataRecord(6 , {\n contentId: file.contentId,\n size: file.inode.size,\n chunkCount: file.inode.chunkCount,\n check: crc32Finish(fileCheck)\n }, state);\n }\n const end = {\n ...state.summary,\n check: crc32Finish(state.batchCheck)\n };\n yield encodeMetadataRecord(7 , end);\n}\nfunction encodeMetadataRecord(tag, value, state) {\n const payload = new TextEncoder().encode(JSON.stringify(value));\n if (payload.byteLength > MAX_METADATA_BYTES) {\n throw new Error(`w7-frame: metadata record exceeds ${MAX_METADATA_BYTES} bytes`);\n }\n const header = recordHeader(tag, payload.byteLength);\n if (state && tag !== 7 ) {\n state.batchCheck = updateRecordCheck(state.batchCheck, header, payload);\n state.summary.recordCount++;\n }\n return [concatBytes(header, payload)];\n}\nfunction preparePayload(payload, batchId) {\n if (!payload || !Array.isArray(payload.inodes) || !Array.isArray(payload.chunks)) {\n throw new Error(\"w7-frame: payload must contain inode and chunk arrays\");\n }\n const ownedPaths = new PathOwnership();\n const deletes = [...payload.deletePaths ?? []];\n for (const path of deletes) claimPath(ownedPaths, canonicalPath(path, \"delete path\"));\n const chunksByPath = new Map();\n for (const chunk of payload.chunks) {\n const path = canonicalPath(chunk.path, \"chunk path\");\n const list = chunksByPath.get(path);\n if (list) list.push(chunk);\n else chunksByPath.set(path, [chunk]);\n }\n const directories = [];\n const files = [];\n let fileIndex = 0;\n for (const inode of payload.inodes) {\n const path = canonicalPath(inode.path, \"inode path\");\n if (path !== inode.path) throw new Error(`w7-frame: noncanonical inode path ${inode.path}`);\n claimPath(ownedPaths, path);\n const normalizedInode = normalizeInode(inode);\n const fileChunks = chunksByPath.get(path) ?? [];\n if (normalizedInode.kind === \"directory\") {\n if (fileChunks.length > 0) throw new Error(`w7-frame: directory ${path} has chunks`);\n directories.push(normalizedInode);\n } else {\n validateChunks(normalizedInode, fileChunks);\n files.push({\n inode: normalizedInode,\n contentId: `${batchId}:${fileIndex++}`,\n chunks: fileChunks\n });\n }\n chunksByPath.delete(path);\n }\n if (chunksByPath.size > 0) {\n throw new Error(`w7-frame: chunk has no inode: ${chunksByPath.keys().next().value}`);\n }\n return { deletes, directories, files };\n}\nfunction parseBatchBegin(bytes) {\n const value = parseObject(bytes, \"batch-begin\", [\"id\", \"mode\"]);\n const id = boundedString(value.id, \"batch id\", MAX_BATCH_ID_BYTES);\n if (value.mode !== MODE) throw new Error(`w7-frame: unsupported batch mode ${String(value.mode)}`);\n return { id, mode: MODE };\n}\nfunction parseDelete(bytes) {\n const value = parseObject(bytes, \"delete\", [\"path\"]);\n return { path: canonicalPath(value.path, \"delete path\") };\n}\nfunction parseDirectory(bytes) {\n const value = parseObject(bytes, \"directory\", [\"path\", \"kind\", \"mtime\", \"mode\"], [\"atime\"]);\n if (value.kind !== \"directory\") {\n throw new Error(`w7-frame: unsupported directory kind ${String(value.kind)}`);\n }\n return { ...parseInodeMetadata(value, \"directory\"), kind: \"directory\" };\n}\nfunction parseFileBegin(bytes) {\n const value = parseObject(\n bytes,\n \"file-begin\",\n [\"path\", \"kind\", \"contentId\", \"size\", \"chunkCount\", \"mtime\", \"mode\"],\n [\"atime\"]\n );\n const base = parseInodeMetadata(value, \"file-begin\");\n if (value.kind !== \"file\" && value.kind !== \"symlink\") {\n throw new Error(`w7-frame: unsupported file-begin kind ${String(value.kind)}`);\n }\n const contentId = boundedString(value.contentId, \"stream content id\", MAX_CONTENT_ID_BYTES);\n if (!/^[A-Za-z0-9._:-]+$/.test(contentId)) {\n throw new Error(`w7-frame: invalid stream content id ${contentId}`);\n }\n const size = safeInteger(value.size, \"file size\");\n const chunkCount = u32(value.chunkCount, \"file chunk count\");\n const expected = size === 0 ? 0 : Math.ceil(size / CHUNK_SIZE);\n if (chunkCount !== expected) {\n throw new Error(`w7-frame: ${base.path}: expected ${expected} chunks, got ${chunkCount}`);\n }\n return { ...base, kind: value.kind, contentId, size, chunkCount };\n}\nfunction parseFileEnd(bytes) {\n const value = parseObject(bytes, \"file-end\", [\"contentId\", \"size\", \"chunkCount\", \"check\"]);\n return {\n contentId: boundedString(value.contentId, \"stream content id\", MAX_CONTENT_ID_BYTES),\n size: safeInteger(value.size, \"file-end size\"),\n chunkCount: u32(value.chunkCount, \"file-end chunk count\"),\n check: u32(value.check, \"file-end check\")\n };\n}\nfunction parseBatchEnd(bytes) {\n const keys = [\n \"recordCount\",\n \"pathCount\",\n \"deleteCount\",\n \"directoryCount\",\n \"fileCount\",\n \"chunkCount\",\n \"byteCount\",\n \"check\"\n ];\n const value = parseObject(bytes, \"batch-end\", keys);\n return {\n recordCount: safeInteger(value.recordCount, \"batch record count\"),\n pathCount: safeInteger(value.pathCount, \"batch path count\"),\n deleteCount: safeInteger(value.deleteCount, \"batch delete count\"),\n directoryCount: safeInteger(value.directoryCount, \"batch directory count\"),\n fileCount: safeInteger(value.fileCount, \"batch file count\"),\n chunkCount: safeInteger(value.chunkCount, \"batch chunk count\"),\n byteCount: safeInteger(value.byteCount, \"batch byte count\"),\n check: u32(value.check, \"batch check\")\n };\n}\nfunction parseInodeMetadata(value, label) {\n return {\n path: canonicalPath(value.path, `${label} path`),\n ...value.atime === void 0 ? {} : { atime: safeInteger(value.atime, `${label} atime`) },\n mtime: safeInteger(value.mtime, `${label} mtime`),\n mode: u32(value.mode, `${label} mode`)\n };\n}\nfunction parseObject(bytes, label, required, optional = []) {\n let value;\n try {\n value = JSON.parse(decodeText(bytes, label));\n } catch (error) {\n if (error instanceof Error && error.message.startsWith(\"w7-frame:\")) throw error;\n throw new Error(`w7-frame: invalid ${label} JSON: ${errorMessage(error)}`);\n }\n if (!isObject(value) || Array.isArray(value)) throw new Error(`w7-frame: ${label} must be an object`);\n const allowed = new Set([...required, ...optional]);\n for (const key of required) {\n if (!Object.hasOwn(value, key)) throw new Error(`w7-frame: ${label} missing ${key}`);\n }\n for (const key of Object.keys(value)) {\n if (!allowed.has(key)) throw new Error(`w7-frame: ${label} has unknown field ${key}`);\n }\n return value;\n}\nfunction normalizeInode(inode) {\n canonicalPath(inode.path, \"inode path\");\n if (inode.parentPath !== parentPath(inode.path)) {\n throw new Error(`w7-frame: ${inode.path}: noncanonical parent path ${inode.parentPath}`);\n }\n safeInteger(inode.size, `${inode.path} size`);\n u32(inode.chunkCount, `${inode.path} chunk count`);\n safeInteger(inode.mtime, `${inode.path} mtime`);\n if (inode.atime !== void 0) safeInteger(inode.atime, `${inode.path} atime`);\n u32(inode.mode, `${inode.path} mode`);\n const rawKind = inode.kind ?? (inode.isDir ? \"directory\" : \"file\");\n if (rawKind !== \"file\" && rawKind !== \"directory\" && rawKind !== \"symlink\") {\n throw new Error(`w7-frame: unsupported inode kind ${String(rawKind)}`);\n }\n const kind = rawKind;\n if (kind === \"directory\" && !inode.isDir) {\n throw new Error(`w7-frame: directory inode ${inode.path} must be a directory`);\n }\n if (kind !== \"directory\" && inode.isDir) {\n throw new Error(`w7-frame: ${kind} inode ${inode.path} cannot be a directory`);\n }\n if (kind === \"directory\" && (inode.size !== 0 || inode.chunkCount !== 0)) {\n throw new Error(`w7-frame: directory ${inode.path} must have zero size and chunks`);\n }\n if (kind !== \"directory\") {\n const expected = inode.size === 0 ? 0 : Math.ceil(inode.size / CHUNK_SIZE);\n if (inode.chunkCount !== expected) {\n throw new Error(`w7-frame: ${inode.path}: expected ${expected} chunks, got ${inode.chunkCount}`);\n }\n return { ...inode, kind, isDir: false };\n }\n return { ...inode, kind, isDir: true };\n}\nfunction validateChunks(inode, chunks) {\n if (chunks.length !== inode.chunkCount) {\n throw new Error(`w7-frame: ${inode.path}: expected ${inode.chunkCount} chunks, got ${chunks.length}`);\n }\n chunks.sort((left, right) => left.chunkId - right.chunkId);\n for (let index = 0; index < chunks.length; index++) {\n const chunk = chunks[index];\n if (chunk.chunkId !== index) {\n throw new Error(`w7-frame: ${inode.path}: expected chunk ${index}, got ${chunk.chunkId}`);\n }\n const expected = Math.min(CHUNK_SIZE, inode.size - index * CHUNK_SIZE);\n if (!(chunk.data instanceof Uint8Array) || chunk.data.byteLength !== expected) {\n throw new Error(`w7-frame: ${inode.path}: chunk ${index} must contain ${expected} bytes`);\n }\n }\n}\nfunction directoryInode(metadata) {\n return {\n path: metadata.path,\n parentPath: parentPath(metadata.path),\n kind: metadata.kind,\n isDir: true,\n size: 0,\n atime: metadata.atime,\n mtime: metadata.mtime,\n mode: metadata.mode,\n chunkCount: 0\n };\n}\nfunction fileInode(metadata) {\n return {\n path: metadata.path,\n parentPath: parentPath(metadata.path),\n kind: metadata.kind,\n isDir: false,\n size: metadata.size,\n atime: metadata.atime,\n mtime: metadata.mtime,\n mode: metadata.mode,\n chunkCount: metadata.chunkCount\n };\n}\nfunction inodeMetadata(inode) {\n return {\n path: inode.path,\n ...inode.atime === void 0 ? {} : { atime: inode.atime },\n mtime: inode.mtime,\n mode: inode.mode\n };\n}\nfunction canonicalPath(value, label) {\n const path = boundedString(value, label, MAX_PATH_BYTES);\n if (path.includes(\"\\0\")) throw new Error(`w7-frame: ${label} contains NUL`);\n const normalized = normalizePath(path);\n if (!path || normalized !== path) throw new Error(`w7-frame: noncanonical ${label}: ${path}`);\n return path;\n}\nvar PathOwnership = class {\n paths = new Set();\n pathBytes = 0;\n claim(path) {\n if (this.paths.has(path)) throw new Error(`w7-frame: duplicate path ownership: ${path}`);\n if (this.paths.size >= W7_MAX_PATHS_PER_BATCH) {\n throw new Error(`w7-frame: batch exceeds ${W7_MAX_PATHS_PER_BATCH} owned paths`);\n }\n const nextPathBytes = this.pathBytes + new TextEncoder().encode(path).byteLength;\n if (nextPathBytes > W7_MAX_OWNED_PATH_BYTES) {\n throw new Error(\n `w7-frame: owned path bytes exceed ${W7_MAX_OWNED_PATH_BYTES}`\n );\n }\n this.paths.add(path);\n this.pathBytes = nextPathBytes;\n }\n};\nfunction claimPath(paths, path) {\n paths.claim(path);\n}\nfunction normalizePath(path) {\n const out = [];\n for (const segment of path.split(\"/\")) {\n if (segment === \"..\") {\n if (out.length > 0) out.pop();\n } else if (segment !== \"\" && segment !== \".\") {\n out.push(segment);\n }\n }\n return out.join(\"/\");\n}\nfunction parentPath(path) {\n const index = path.lastIndexOf(\"/\");\n return index < 0 ? \"\" : path.slice(0, index);\n}\nfunction boundedString(value, label, maxBytes) {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new Error(`w7-frame: ${label} must be a non-empty string`);\n }\n const length = new TextEncoder().encode(value).byteLength;\n if (length > maxBytes) throw new Error(`w7-frame: ${label} exceeds ${maxBytes} bytes`);\n return value;\n}\nfunction safeInteger(value, label) {\n if (!Number.isSafeInteger(value) || value < 0) {\n throw new Error(`w7-frame: ${label} must be a non-negative safe integer`);\n }\n return value;\n}\nfunction u32(value, label) {\n const integer = safeInteger(value, label);\n if (integer > 4294967295) throw new Error(`w7-frame: ${label} exceeds uint32`);\n return integer;\n}\nfunction sameSummary(left, right) {\n return left.recordCount === right.recordCount && left.pathCount === right.pathCount && left.deleteCount === right.deleteCount && left.directoryCount === right.directoryCount && left.fileCount === right.fileCount && left.chunkCount === right.chunkCount && left.byteCount === right.byteCount && left.check === right.check;\n}\nasync function readEnvelope(buffer, label) {\n const header = await buffer.readExact(5, `${label} header`);\n return { tag: header[0], length: readU32LE(header, 1), header };\n}\nfunction recordHeader(tag, length) {\n const header = new Uint8Array(5);\n header[0] = tag;\n writeU32LE(header, 1, length);\n return header;\n}\nfunction writeU32LE(out, offset, value) {\n out[offset] = value & 255;\n out[offset + 1] = value >>> 8 & 255;\n out[offset + 2] = value >>> 16 & 255;\n out[offset + 3] = value >>> 24 & 255;\n}\nfunction readU32LE(bytes, offset) {\n return (bytes[offset] | bytes[offset + 1] << 8 | bytes[offset + 2] << 16 | bytes[offset + 3] << 24) >>> 0;\n}\nfunction updateRecordCheck(seed, ...parts) {\n let check = seed;\n for (const part of parts) check = crc32Update(check, part);\n return check;\n}\nvar CRC_SEED = 4294967295;\nvar crcTable = null;\nfunction crc32Update(check, bytes) {\n crcTable ??= createCrcTable();\n const table = crcTable;\n let value = check;\n for (const byte of bytes) value = table[(value ^ byte) & 255] ^ value >>> 8;\n return value >>> 0;\n}\nfunction crc32Finish(check) {\n return (check ^ 4294967295) >>> 0;\n}\nfunction createCrcTable() {\n const table = new Uint32Array(256);\n for (let index = 0; index < table.length; index++) {\n let value = index;\n for (let bit = 0; bit < 8; bit++) {\n value = (value & 1) !== 0 ? 3988292384 ^ value >>> 1 : value >>> 1;\n }\n table[index] = value >>> 0;\n }\n return table;\n}\nfunction noopRetention(bytes) {\n return { bytes, release() {\n } };\n}\nfunction decodeText(bytes, label) {\n try {\n return new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: false }).decode(bytes);\n } catch (error) {\n throw new Error(`w7-frame: invalid UTF-8 in ${label}: ${errorMessage(error)}`);\n }\n}\nfunction concatBytes(...parts) {\n const output = new Uint8Array(parts.reduce((total, part) => total + part.byteLength, 0));\n let offset = 0;\n for (const part of parts) {\n output.set(part, offset);\n offset += part.byteLength;\n }\n return output;\n}\nfunction bytesEqual(left, right) {\n return left.length === right.length && left.every((byte, index) => byte === right[index]);\n}\nfunction hex(bytes) {\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\" \");\n}\nfunction isObject(value) {\n return typeof value === \"object\" && value !== null;\n}\nfunction errorMessage(error) {\n return error instanceof Error ? error.message : String(error);\n}\nfunction throwIfAborted(signal) {\n if (!signal?.aborted) return;\n throw new DOMException(\n signal.reason instanceof Error ? signal.reason.message : String(signal.reason ?? \"Aborted\"),\n \"AbortError\"\n );\n}\nasync function cancelReader(reader, reason) {\n try {\n await reader.cancel(reason);\n } catch {\n }\n try {\n reader.releaseLock();\n } catch {\n }\n}\nvar ExactByteReader = class {\n constructor(reader, signal) {\n this.reader = reader;\n this.signal = signal;\n }\n reader;\n signal;\n done = false;\n async read(read, cancel) {\n throwIfAborted(this.signal);\n const abort = () => {\n void cancel().catch(() => {\n });\n };\n this.signal?.addEventListener(\"abort\", abort, { once: true });\n try {\n const result = await read();\n throwIfAborted(this.signal);\n return result;\n } finally {\n this.signal?.removeEventListener(\"abort\", abort);\n }\n }\n async readExact(length, label) {\n if (length === 0) return new Uint8Array(0);\n const output = new Uint8Array(length);\n let offset = 0;\n while (offset < length) {\n if (this.done) {\n throw new Error(\n `w7-frame: stream ended ${offset} bytes into expected ${length}-byte ${label}`\n );\n }\n const next = await this.read(\n () => this.reader.read(new Uint8Array(length - offset)),\n () => this.reader.cancel(this.signal?.reason)\n );\n if (next.done) this.done = true;\n else if (next.value.byteLength > 0) {\n output.set(next.value, offset);\n offset += next.value.byteLength;\n }\n }\n return output;\n }\n async ensureEof(stream) {\n if (this.done) return;\n this.reader.releaseLock();\n const reader = stream.getReader();\n try {\n const next = await this.read(() => reader.read(), () => reader.cancel(this.signal?.reason));\n if (!next.done && next.value.byteLength > 0) {\n await reader.cancel(new Error(\"w7-frame: trailing bytes after batch-end\"));\n throw new Error(\"w7-frame: trailing bytes after batch-end\");\n }\n this.done = true;\n } finally {\n try {\n reader.releaseLock();\n } catch {\n }\n }\n }\n};";
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
* Specifically prebundleOne references:
|
|
11
11
|
* - ESBUILD_WASM_JS_FN_BODY — function-body string (~117 KiB) that,
|
|
12
12
|
* when run via new Function(...)(), returns
|
|
13
|
-
* the esbuild namespace.
|
|
13
|
+
* the esbuild namespace. Staged beside the
|
|
14
|
+
* wasm and fetched by the supervisor at
|
|
15
|
+
* pool construction (fetchEsbuildJsFnBody),
|
|
16
|
+
* then spliced in here.
|
|
14
17
|
* - resolvePackageEntry — the npm-resolver helper used by the
|
|
15
18
|
* bare-specifier resolver
|
|
16
19
|
*
|
|
@@ -35,7 +38,7 @@
|
|
|
35
38
|
/**
|
|
36
39
|
* Preamble string injected ahead of the prebundleOne function in every
|
|
37
40
|
* pre-bundle facet isolate. Must be passed via IsolatePool's
|
|
38
|
-
* `preamble` option.
|
|
41
|
+
* `preamble` option. `jsFnBody` is the staged esbuild adapter.
|
|
39
42
|
*/
|
|
40
|
-
export declare
|
|
43
|
+
export declare function preBundlePreamble(jsFnBody: string): string;
|
|
41
44
|
//# sourceMappingURL=pre-bundle-preamble.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pre-bundle-preamble.d.ts","sourceRoot":"","sources":["../../src/loaders/pre-bundle-preamble.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"pre-bundle-preamble.d.ts","sourceRoot":"","sources":["../../src/loaders/pre-bundle-preamble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAsCH;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAuC1D"}
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
* Specifically prebundleOne references:
|
|
11
11
|
* - ESBUILD_WASM_JS_FN_BODY — function-body string (~117 KiB) that,
|
|
12
12
|
* when run via new Function(...)(), returns
|
|
13
|
-
* the esbuild namespace.
|
|
13
|
+
* the esbuild namespace. Staged beside the
|
|
14
|
+
* wasm and fetched by the supervisor at
|
|
15
|
+
* pool construction (fetchEsbuildJsFnBody),
|
|
16
|
+
* then spliced in here.
|
|
14
17
|
* - resolvePackageEntry — the npm-resolver helper used by the
|
|
15
18
|
* bare-specifier resolver
|
|
16
19
|
*
|
|
@@ -32,7 +35,7 @@
|
|
|
32
35
|
* fine — esbuild boot is the dominant cost and re-paying it once on a
|
|
33
36
|
* deploy is acceptable.
|
|
34
37
|
*/
|
|
35
|
-
import {
|
|
38
|
+
import { ESBUILD_WASM_VERSION } from '../esbuild-wasm-bundle.generated.js';
|
|
36
39
|
import { getExportsResolverJS } from '@nimbus-sh/core/_shared/exports-resolver.js';
|
|
37
40
|
// NOTE: wasm BYTES deliberately NOT in this preamble. They live in
|
|
38
41
|
// env.ASSETS at /_assets/esbuild-<version>.wasm and are fetched by
|
|
@@ -69,16 +72,17 @@ const RESOLVER_HELPERS_SRC = getExportsResolverJS();
|
|
|
69
72
|
/**
|
|
70
73
|
* Preamble string injected ahead of the prebundleOne function in every
|
|
71
74
|
* pre-bundle facet isolate. Must be passed via IsolatePool's
|
|
72
|
-
* `preamble` option.
|
|
75
|
+
* `preamble` option. `jsFnBody` is the staged esbuild adapter.
|
|
73
76
|
*/
|
|
74
|
-
export
|
|
77
|
+
export function preBundlePreamble(jsFnBody) {
|
|
78
|
+
return `
|
|
75
79
|
// ── pre-bundle facet preamble (auto-generated) ──────────────────────────
|
|
76
80
|
// Esbuild JS helpers (small ~117 KiB). The wasm BYTES are NOT here —
|
|
77
|
-
// they'
|
|
78
|
-
//
|
|
79
|
-
//
|
|
81
|
+
// they ride IsolatePool's wasmModules option to keep the per-dispatch
|
|
82
|
+
// worker module source under ~120 KiB instead of ~16 MiB. See this
|
|
83
|
+
// file's header for why.
|
|
80
84
|
const ESBUILD_WASM_VERSION = ${JSON.stringify(ESBUILD_WASM_VERSION)};
|
|
81
|
-
const ESBUILD_WASM_JS_FN_BODY = ${JSON.stringify(
|
|
85
|
+
const ESBUILD_WASM_JS_FN_BODY = ${JSON.stringify(jsFnBody)};
|
|
82
86
|
|
|
83
87
|
// ── Materialise esbuild namespace at MODULE STARTUP ─────────────────────
|
|
84
88
|
// workerd's deployed config disallows \"Code generation from strings\" at
|
|
@@ -109,3 +113,4 @@ try {
|
|
|
109
113
|
${RESOLVER_HELPERS_SRC}
|
|
110
114
|
// ── end pre-bundle facet preamble ───────────────────────────────────────
|
|
111
115
|
`;
|
|
116
|
+
}
|
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
* prefix so cache layers never serve stale bytes after a rebuild;
|
|
8
8
|
* NODE_SHIMS_SHA256 is the full digest verified at fetch time.
|
|
9
9
|
*/
|
|
10
|
-
export const NODE_SHIMS_ENTRY = "/_assets/runtime/node-shims-
|
|
11
|
-
export const NODE_SHIMS_BUILD_ID = "
|
|
12
|
-
export const NODE_SHIMS_SHA256 = "
|
|
10
|
+
export const NODE_SHIMS_ENTRY = "/_assets/runtime/node-shims-7b4dd7a76d95d21e.js";
|
|
11
|
+
export const NODE_SHIMS_BUILD_ID = "7b4dd7a76d95d21e";
|
|
12
|
+
export const NODE_SHIMS_SHA256 = "7b4dd7a76d95d21e03a54a23914d4c3732fcf85844765a64db1be669858981a9";
|
package/dist/npm/cache.d.ts
CHANGED
|
@@ -39,6 +39,11 @@ export interface RegistryCacheEntry {
|
|
|
39
39
|
optionalDepsJson?: string;
|
|
40
40
|
fetchedAt: number;
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* One placement in a project's lockfile. The Map key is the placement path
|
|
44
|
+
* (`name` at root, `<dependent>/node_modules/<name>` nested); it is what the
|
|
45
|
+
* `name` column of `pkg_lockfile` holds, whose primary key predates nesting.
|
|
46
|
+
*/
|
|
42
47
|
export interface LockfileEntry {
|
|
43
48
|
name: string;
|
|
44
49
|
resolvedVer: string;
|
|
@@ -121,7 +126,7 @@ export declare class NpmCache {
|
|
|
121
126
|
written: number;
|
|
122
127
|
failed: number;
|
|
123
128
|
};
|
|
124
|
-
/** Read the lockfile for a project.
|
|
129
|
+
/** Read the lockfile for a project, keyed by placement path. Null if not found. */
|
|
125
130
|
readLockfile(projectPath: string): Map<string, LockfileEntry> | null;
|
|
126
131
|
/** Write/overwrite the lockfile for a project. Atomic via transaction. */
|
|
127
132
|
writeLockfile(projectPath: string, entries: Map<string, LockfileEntry>, ctx?: DurableObjectState): void;
|
package/dist/npm/cache.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/npm/cache.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/npm/cache.ts"],"names":[],"mappings":"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"}
|
package/dist/npm/cache.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* Hit = row(s) returned with size > 0; miss = empty result set. Callers
|
|
19
19
|
* fall through to L2/L3/L4 on miss.
|
|
20
20
|
*/
|
|
21
|
+
import { placementName } from './placement.js';
|
|
21
22
|
import { recordHit as _l1RecordHit, recordMiss as _l1RecordMiss } from '@nimbus-sh/core/_shared/cache-stats.js';
|
|
22
23
|
// ── NpmCache ────────────────────────────────────────────────────────────
|
|
23
24
|
export class NpmCache {
|
|
@@ -233,7 +234,7 @@ export class NpmCache {
|
|
|
233
234
|
return { written, failed };
|
|
234
235
|
}
|
|
235
236
|
// ── Lockfile ──────────────────────────────────────────────────────────
|
|
236
|
-
/** Read the lockfile for a project.
|
|
237
|
+
/** Read the lockfile for a project, keyed by placement path. Null if not found. */
|
|
237
238
|
readLockfile(projectPath) {
|
|
238
239
|
this.ensureSchema();
|
|
239
240
|
const rows = [...this.sql.exec(`SELECT name, resolved_ver, integrity, deps_json, hoisted_path
|
|
@@ -242,8 +243,9 @@ export class NpmCache {
|
|
|
242
243
|
return null;
|
|
243
244
|
const result = new Map();
|
|
244
245
|
for (const r of rows) {
|
|
245
|
-
|
|
246
|
-
|
|
246
|
+
const placement = String(r.name);
|
|
247
|
+
result.set(placement, {
|
|
248
|
+
name: placementName(placement),
|
|
247
249
|
resolvedVer: String(r.resolved_ver),
|
|
248
250
|
integrity: String(r.integrity),
|
|
249
251
|
depsJson: String(r.deps_json),
|
|
@@ -267,14 +269,14 @@ export class NpmCache {
|
|
|
267
269
|
// Clear existing lockfile for this project
|
|
268
270
|
this.sql.exec(`DELETE FROM pkg_lockfile WHERE project_path = ?`, projectPath);
|
|
269
271
|
// Batch insert: DO SQLite ~100 var limit. 6 cols → max 16 rows (16×6=96).
|
|
270
|
-
const entryList = [...entries
|
|
272
|
+
const entryList = [...entries];
|
|
271
273
|
const BATCH = 16;
|
|
272
274
|
for (let i = 0; i < entryList.length; i += BATCH) {
|
|
273
275
|
const batch = entryList.slice(i, i + BATCH);
|
|
274
276
|
const placeholders = batch.map(() => '(?,?,?,?,?,?)').join(',');
|
|
275
277
|
const values = [];
|
|
276
|
-
for (const e of batch) {
|
|
277
|
-
values.push(projectPath,
|
|
278
|
+
for (const [placement, e] of batch) {
|
|
279
|
+
values.push(projectPath, placement, e.resolvedVer, e.integrity, e.depsJson, e.hoistedPath);
|
|
278
280
|
}
|
|
279
281
|
this.sql.exec(`INSERT INTO pkg_lockfile (project_path, name, resolved_ver, integrity, deps_json, hoisted_path) VALUES ${placeholders}`, ...values);
|
|
280
282
|
}
|
|
@@ -49,6 +49,8 @@ export interface InstallBatchSpec {
|
|
|
49
49
|
export interface InstallBatchPerPackage {
|
|
50
50
|
name: string;
|
|
51
51
|
version: string;
|
|
52
|
+
/** The spec's identity: one version may land at two directories. Absent from pre-placement shards. */
|
|
53
|
+
pkgDir?: string;
|
|
52
54
|
fileCount: number;
|
|
53
55
|
bytesWritten: number;
|
|
54
56
|
elapsed: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-batch-facet.d.ts","sourceRoot":"","sources":["../../src/npm/install-batch-facet.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAShF,MAAM,WAAW,gBAAgB;IAC/B;mDAC+C;IAC/C,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,mFAAmF;IACnF,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,gDAAgD;IAChD,UAAU,EAAE,sBAAsB,EAAE,CAAC;IACrC,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB;uFACmF;IACnF,aAAa,EAAE;QACb,iBAAiB,EAAE,MAAM,CAAC;QAC1B,sBAAsB,EAAE,MAAM,CAAC;QAC/B,YAAY,EAAE,MAAM,CAAC;QACrB;;uDAE+C;QAC/C,wBAAwB,EAAE,MAAM,CAAC;QACjC,0BAA0B,EAAE,MAAM,CAAC;QACnC;;;0DAGkD;QAClD,WAAW,EAAE,MAAM,CAAC;QACpB,kBAAkB,EAAE,MAAM,CAAC;QAC3B;;;;qEAI6D;QAC7D,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF;;;;;;;;;;OAUG;IACH,eAAe,EAAE,KAAK,CAClB;QAAE,IAAI,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAC9E;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;QAAC,SAAS,EAAE,SAAS,CAAA;KAAE,CACnE,CAAC;CACH;AAUD,eAAO,MAAM,sBAAsB,GACjC,OAAO,gBAAgB,EACvB,KAAK;IACH,UAAU,EAAE;QAIV,gBAAgB,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAC;QAK1F,gBAAgB,CAAC,EAAE,CACjB,SAAS,EAAE,MAAM,KACd,OAAO,CAAC;YACX,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;YACzB,MAAM,EAAE,KAAK,CACT;gBAAE,IAAI,EAAE,KAAK,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,MAAM,CAAA;aAAE,GAC/D;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,CACpD,CAAC;SACH,CAAC,CAAC;QACH,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;KAC7F,CAAC;CACH,KACA,OAAO,CAAC,kBAAkB,
|
|
1
|
+
{"version":3,"file":"install-batch-facet.d.ts","sourceRoot":"","sources":["../../src/npm/install-batch-facet.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAShF,MAAM,WAAW,gBAAgB;IAC/B;mDAC+C;IAC/C,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,mFAAmF;IACnF,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,sGAAsG;IACtG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,gDAAgD;IAChD,UAAU,EAAE,sBAAsB,EAAE,CAAC;IACrC,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB;uFACmF;IACnF,aAAa,EAAE;QACb,iBAAiB,EAAE,MAAM,CAAC;QAC1B,sBAAsB,EAAE,MAAM,CAAC;QAC/B,YAAY,EAAE,MAAM,CAAC;QACrB;;uDAE+C;QAC/C,wBAAwB,EAAE,MAAM,CAAC;QACjC,0BAA0B,EAAE,MAAM,CAAC;QACnC;;;0DAGkD;QAClD,WAAW,EAAE,MAAM,CAAC;QACpB,kBAAkB,EAAE,MAAM,CAAC;QAC3B;;;;qEAI6D;QAC7D,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF;;;;;;;;;;OAUG;IACH,eAAe,EAAE,KAAK,CAClB;QAAE,IAAI,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAC9E;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;QAAC,SAAS,EAAE,SAAS,CAAA;KAAE,CACnE,CAAC;CACH;AAUD,eAAO,MAAM,sBAAsB,GACjC,OAAO,gBAAgB,EACvB,KAAK;IACH,UAAU,EAAE;QAIV,gBAAgB,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAC;QAK1F,gBAAgB,CAAC,EAAE,CACjB,SAAS,EAAE,MAAM,KACd,OAAO,CAAC;YACX,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;YACzB,MAAM,EAAE,KAAK,CACT;gBAAE,IAAI,EAAE,KAAK,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,MAAM,CAAA;aAAE,GAC/D;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,CACpD,CAAC;SACH,CAAC,CAAC;QACH,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;KAC7F,CAAC;CACH,KACA,OAAO,CAAC,kBAAkB,CAo2B5B,CAAC"}
|
|
@@ -363,6 +363,11 @@ export const installPackagesInFacet = async function installPackagesInFacet(batc
|
|
|
363
363
|
});
|
|
364
364
|
sharedBufferedBytes += additionalBytes;
|
|
365
365
|
});
|
|
366
|
+
// A tarball placed twice in this shard is acquired once: the first owner
|
|
367
|
+
// publishes its bytes here, later owners of the URL wait for them. `null`
|
|
368
|
+
// means it had nothing to share and the waiter acquires on its own.
|
|
369
|
+
// Owners register on entry, so no waiter blocks an unstarted owner.
|
|
370
|
+
const tarballBytesByUrl = new Map();
|
|
366
371
|
// ── Per-package install (inlined fetchAndStagePackage logic) ─────────
|
|
367
372
|
//
|
|
368
373
|
// Kept inline because cloudflare-parallel serializes this whole function
|
|
@@ -374,6 +379,19 @@ export const installPackagesInFacet = async function installPackagesInFacet(batc
|
|
|
374
379
|
inFlight++;
|
|
375
380
|
if (inFlight > inFlightPeak)
|
|
376
381
|
inFlightPeak = inFlight;
|
|
382
|
+
let publishTarballBytes = () => { };
|
|
383
|
+
let sharedBytes = null;
|
|
384
|
+
const owner = tarballBytesByUrl.get(spec.tarballUrl);
|
|
385
|
+
if (owner) {
|
|
386
|
+
sharedBytes = await owner;
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
tarballBytesByUrl.set(spec.tarballUrl, new Promise((rs) => { publishTarballBytes = rs; }));
|
|
390
|
+
}
|
|
391
|
+
// [W4] Compressed bytes for R2 write-back, captured by the integrity
|
|
392
|
+
// tee below (null without integrity); published to duplicates on exit.
|
|
393
|
+
let capturedTgzBytes = null;
|
|
394
|
+
let r2HitBytes = sharedBytes;
|
|
377
395
|
// [W4] 1a. R2 cache lookup, hedged by the network fetch.
|
|
378
396
|
//
|
|
379
397
|
// The R2 GET is bounded by R2_RACE_TIMEOUT_MS. That bound guards against a
|
|
@@ -393,7 +411,7 @@ export const installPackagesInFacet = async function installPackagesInFacet(batc
|
|
|
393
411
|
// supervisor deployment) the R2 leg becomes a noop and there is nothing to
|
|
394
412
|
// overlap with, so no hedge is armed — the retry loop's own first fetch is
|
|
395
413
|
// already the first thing that happens.
|
|
396
|
-
const r2Available = typeof env.SUPERVISOR.getCachedTarball === 'function';
|
|
414
|
+
const r2Available = sharedBytes === null && typeof env.SUPERVISOR.getCachedTarball === 'function';
|
|
397
415
|
const r2WaitStart = Date.now();
|
|
398
416
|
const r2P = r2Available
|
|
399
417
|
? Promise.race([
|
|
@@ -438,13 +456,6 @@ export const installPackagesInFacet = async function installPackagesInFacet(batc
|
|
|
438
456
|
hedgeAbort.abort();
|
|
439
457
|
};
|
|
440
458
|
try {
|
|
441
|
-
// [W4] Captured compressed bytes for write-back to R2 on miss.
|
|
442
|
-
// Populated by the integrity-tee path below; remains null when
|
|
443
|
-
// integrity isn't present (rare; we only writeback when we can
|
|
444
|
-
// verify on next read). Hoisted to installOne scope per W4-plan
|
|
445
|
-
// §11 finding #4 lifecycle correctness.
|
|
446
|
-
let capturedTgzBytes = null;
|
|
447
|
-
let r2HitBytes = null;
|
|
448
459
|
// Acquisition span, measured from the first cache probe to the moment
|
|
449
460
|
// the tarball body is in hand.
|
|
450
461
|
let tarballElapsedMs = 0;
|
|
@@ -501,10 +512,12 @@ export const installPackagesInFacet = async function installPackagesInFacet(batc
|
|
|
501
512
|
// Cache HIT. The cross-tenant store is content-addressed and
|
|
502
513
|
// re-hashes on every read, so bytes that come back are already
|
|
503
514
|
// proven to be spec.integrity's tarball — there is exactly one
|
|
504
|
-
// verification point and it is not here.
|
|
515
|
+
// verification point and it is not here. Bytes shared by another
|
|
516
|
+
// placement were verified by their owner; not an R2 outcome.
|
|
505
517
|
discardPendingNetwork();
|
|
506
518
|
tarballElapsedMs = Date.now() - r2WaitStart;
|
|
507
|
-
|
|
519
|
+
if (!sharedBytes)
|
|
520
|
+
pipelinedTarballRaceWins++;
|
|
508
521
|
tarballsCompleted++;
|
|
509
522
|
cumulativeBytesDecoded += r2HitBytes.length;
|
|
510
523
|
// Synthesize a Response body from the R2 bytes so the existing
|
|
@@ -556,14 +569,14 @@ export const installPackagesInFacet = async function installPackagesInFacet(batc
|
|
|
556
569
|
}
|
|
557
570
|
if (!resp) {
|
|
558
571
|
return {
|
|
559
|
-
name: spec.name, version: spec.version,
|
|
572
|
+
name: spec.name, version: spec.version, pkgDir: spec.pkgDir,
|
|
560
573
|
fileCount: 0, bytesWritten: 0, elapsed: Date.now() - t0, warnings,
|
|
561
574
|
errorText: `fetch failed: ${lastErr?.message || String(lastErr)}`,
|
|
562
575
|
};
|
|
563
576
|
}
|
|
564
577
|
if (!resp.ok) {
|
|
565
578
|
return {
|
|
566
|
-
name: spec.name, version: spec.version,
|
|
579
|
+
name: spec.name, version: spec.version, pkgDir: spec.pkgDir,
|
|
567
580
|
fileCount: 0, bytesWritten: 0, elapsed: Date.now() - t0, warnings,
|
|
568
581
|
errorText: `HTTP ${resp.status}`,
|
|
569
582
|
};
|
|
@@ -571,7 +584,7 @@ export const installPackagesInFacet = async function installPackagesInFacet(batc
|
|
|
571
584
|
const body = resp.body;
|
|
572
585
|
if (!body) {
|
|
573
586
|
return {
|
|
574
|
-
name: spec.name, version: spec.version,
|
|
587
|
+
name: spec.name, version: spec.version, pkgDir: spec.pkgDir,
|
|
575
588
|
fileCount: 0, bytesWritten: 0, elapsed: Date.now() - t0, warnings,
|
|
576
589
|
errorText: 'no response body',
|
|
577
590
|
};
|
|
@@ -767,7 +780,7 @@ export const installPackagesInFacet = async function installPackagesInFacet(batc
|
|
|
767
780
|
}
|
|
768
781
|
}
|
|
769
782
|
return {
|
|
770
|
-
name: spec.name, version: spec.version,
|
|
783
|
+
name: spec.name, version: spec.version, pkgDir: spec.pkgDir,
|
|
771
784
|
fileCount: totalFileInodes, bytesWritten: totalBytesWritten,
|
|
772
785
|
elapsed: Date.now() - t0, warnings,
|
|
773
786
|
tarballSource: r2HitBytes ? 'cache' : 'registry',
|
|
@@ -776,7 +789,7 @@ export const installPackagesInFacet = async function installPackagesInFacet(batc
|
|
|
776
789
|
}
|
|
777
790
|
catch (e) {
|
|
778
791
|
return {
|
|
779
|
-
name: spec.name, version: spec.version,
|
|
792
|
+
name: spec.name, version: spec.version, pkgDir: spec.pkgDir,
|
|
780
793
|
fileCount: 0, bytesWritten: 0, elapsed: Date.now() - t0, warnings,
|
|
781
794
|
errorText: e?.message || String(e),
|
|
782
795
|
};
|
|
@@ -784,6 +797,7 @@ export const installPackagesInFacet = async function installPackagesInFacet(batc
|
|
|
784
797
|
finally {
|
|
785
798
|
// No-op once the retry loop has taken it; closes every early return.
|
|
786
799
|
discardPendingNetwork();
|
|
800
|
+
publishTarballBytes(capturedTgzBytes ?? r2HitBytes);
|
|
787
801
|
inFlight = Math.max(0, inFlight - 1);
|
|
788
802
|
}
|
|
789
803
|
};
|