@nimbus-sh/worker 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/dist/esbuild-wasm-bundle.generated.d.ts +24 -24
  2. package/dist/esbuild-wasm-bundle.generated.d.ts.map +1 -1
  3. package/dist/esbuild-wasm-bundle.generated.js +24 -24
  4. package/dist/facets/cirrus-real.js +3 -3
  5. package/dist/facets/compose.d.ts +3 -0
  6. package/dist/facets/compose.d.ts.map +1 -1
  7. package/dist/facets/compose.js +10 -3
  8. package/dist/facets/esbuild-bundle-pool.d.ts +1 -1
  9. package/dist/facets/esbuild-bundle-pool.js +3 -3
  10. package/dist/facets/esbuild-transform.d.ts +6 -2
  11. package/dist/facets/esbuild-transform.d.ts.map +1 -1
  12. package/dist/facets/esbuild-transform.js +8 -4
  13. package/dist/facets/manager.d.ts +54 -15
  14. package/dist/facets/manager.d.ts.map +1 -1
  15. package/dist/facets/manager.js +227 -144
  16. package/dist/facets/real-vite-hmr.d.ts +2 -0
  17. package/dist/facets/real-vite-hmr.d.ts.map +1 -1
  18. package/dist/facets/real-vite-hmr.js +3 -2
  19. package/dist/git/commands.d.ts +17 -1
  20. package/dist/git/commands.d.ts.map +1 -1
  21. package/dist/git/commands.js +73 -9
  22. package/dist/git/network-facet.d.ts +2 -0
  23. package/dist/git/network-facet.d.ts.map +1 -1
  24. package/dist/git/network-facet.js +10 -5
  25. package/dist/hosted/commands.d.ts.map +1 -1
  26. package/dist/hosted/commands.js +28 -39
  27. package/dist/hosted/runtime.d.ts +24 -2
  28. package/dist/hosted/runtime.d.ts.map +1 -1
  29. package/dist/hosted/runtime.js +70 -13
  30. package/dist/hosted/services.d.ts +14 -2
  31. package/dist/hosted/services.d.ts.map +1 -1
  32. package/dist/hosted/services.js +15 -2
  33. package/dist/loaders/generated-workers.d.ts +2 -2
  34. package/dist/loaders/generated-workers.d.ts.map +1 -1
  35. package/dist/loaders/generated-workers.js +4 -4
  36. package/dist/loaders/pre-bundle-preamble.d.ts +6 -3
  37. package/dist/loaders/pre-bundle-preamble.d.ts.map +1 -1
  38. package/dist/loaders/pre-bundle-preamble.js +13 -8
  39. package/dist/node-shims-artifact.generated.js +3 -3
  40. package/dist/npm/cache.d.ts +6 -1
  41. package/dist/npm/cache.d.ts.map +1 -1
  42. package/dist/npm/cache.js +8 -6
  43. package/dist/npm/install-batch-facet.d.ts +2 -0
  44. package/dist/npm/install-batch-facet.d.ts.map +1 -1
  45. package/dist/npm/install-batch-facet.js +29 -15
  46. package/dist/npm/installer.d.ts +8 -4
  47. package/dist/npm/installer.d.ts.map +1 -1
  48. package/dist/npm/installer.js +173 -113
  49. package/dist/npm/npx-install.d.ts +3 -1
  50. package/dist/npm/npx-install.d.ts.map +1 -1
  51. package/dist/npm/npx-install.js +4 -1
  52. package/dist/npm/placement.d.ts +16 -0
  53. package/dist/npm/placement.d.ts.map +1 -0
  54. package/dist/npm/placement.js +33 -0
  55. package/dist/npm/r2-cache.d.ts +10 -6
  56. package/dist/npm/r2-cache.d.ts.map +1 -1
  57. package/dist/npm/r2-cache.js +32 -16
  58. package/dist/npm/resolve-one-facet.d.ts +3 -0
  59. package/dist/npm/resolve-one-facet.d.ts.map +1 -1
  60. package/dist/npm/resolve-one-facet.js +1 -0
  61. package/dist/npm/resolver.d.ts +11 -6
  62. package/dist/npm/resolver.d.ts.map +1 -1
  63. package/dist/npm/resolver.js +12 -3
  64. package/dist/runtime/bash-repl.d.ts +5 -3
  65. package/dist/runtime/bash-repl.d.ts.map +1 -1
  66. package/dist/runtime/bash-repl.js +8 -3
  67. package/dist/runtime/cpython-resident.d.ts.map +1 -1
  68. package/dist/runtime/cpython-resident.js +2 -10
  69. package/dist/runtime/esbuild-wasm-bytes.d.ts +25 -18
  70. package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -1
  71. package/dist/runtime/esbuild-wasm-bytes.js +55 -30
  72. package/dist/runtime/facet-loader-host.d.ts.map +1 -1
  73. package/dist/runtime/facet-loader-host.js +0 -9
  74. package/dist/runtime/node-shims.d.ts.map +1 -1
  75. package/dist/runtime/node-shims.js +853 -181
  76. package/dist/runtime/python-repl.d.ts +4 -3
  77. package/dist/runtime/python-repl.d.ts.map +1 -1
  78. package/dist/runtime/python-repl.js +10 -39
  79. package/dist/runtime/ruby-repl.d.ts +3 -2
  80. package/dist/runtime/ruby-repl.d.ts.map +1 -1
  81. package/dist/runtime/ruby-repl.js +7 -6
  82. package/dist/runtime/ruby-resident.d.ts.map +1 -1
  83. package/dist/runtime/ruby-resident.js +5 -16
  84. package/dist/runtime/runtime-catalog.js +1 -1
  85. package/dist/runtime/wasi/preamble.d.ts +0 -22
  86. package/dist/runtime/wasi/preamble.d.ts.map +1 -1
  87. package/dist/runtime/wasi/preamble.js +160 -1445
  88. package/dist/runtime-catalog.generated.js +1 -1
  89. package/dist/session/hibernation.d.ts +45 -2
  90. package/dist/session/hibernation.d.ts.map +1 -1
  91. package/dist/session/hibernation.js +78 -1
  92. package/dist/session/init.d.ts.map +1 -1
  93. package/dist/session/init.js +1 -0
  94. package/dist/session/nimbus-session.d.ts +16 -23
  95. package/dist/session/nimbus-session.d.ts.map +1 -1
  96. package/dist/session/nimbus-session.js +25 -36
  97. package/dist/session/npm-install-port.d.ts.map +1 -1
  98. package/dist/session/npm-install-port.js +1 -0
  99. package/dist/session/port-capability.d.ts.map +1 -1
  100. package/dist/session/port-capability.js +11 -1
  101. package/dist/session/programmatic.js +2 -2
  102. package/dist/session/routes.d.ts.map +1 -1
  103. package/dist/session/routes.js +4 -2
  104. package/dist/session/rpc.d.ts +7 -44
  105. package/dist/session/rpc.d.ts.map +1 -1
  106. package/dist/session/rpc.js +17 -67
  107. package/dist/session/supervisor-op.d.ts +8 -3
  108. package/dist/session/supervisor-op.d.ts.map +1 -1
  109. package/dist/session/supervisor-op.js +10 -45
  110. package/dist/session/supervisor-rpc.d.ts +47 -22
  111. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  112. package/dist/session/supervisor-rpc.js +64 -26
  113. package/dist/vfs/facet-resident-store.d.ts.map +1 -1
  114. package/dist/vfs/facet-resident-store.js +4 -2
  115. package/dist/workspace-host.d.ts +2 -1
  116. package/dist/workspace-host.d.ts.map +1 -1
  117. package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -1
  118. package/dist/wrangler/nimbus-wrangler.js +3 -0
  119. package/package.json +30 -4
  120. package/public/_assets/esbuild-0.24.2.js +2380 -0
  121. package/public/_assets/runtime/{node-shims-8f8c64edd2a1f0f5.js → node-shims-7b4dd7a76d95d21e.js} +852 -180
  122. package/scripts/bundle-esbuild-wasm.mjs +55 -39
  123. package/scripts/bundle-facet-workers.mjs +45 -7
  124. package/scripts/bundle-runtime.mjs +35 -8
@@ -1,6 +1,7 @@
1
1
  import { requireVfsCred } from '@nimbus-sh/core/runtime/os-contracts.js';
2
2
  import { SandboxFsImpl } from '@nimbus-sh/core/substrate/lifo/sandbox/SandboxFs.js';
3
- import { SUPERVISOR_OP_ROUTES } from '@nimbus-sh/core/workspace/supervisor-op.js';
3
+ import { ExecutionFs } from '@nimbus-sh/core/shell/execution-fs.js';
4
+ import { SUPERVISOR_OP_ROUTES, createSupervisorBridgeStore } from '@nimbus-sh/core/workspace/supervisor-op.js';
4
5
  import { WebSocketTerminal } from '../facets/ws-terminal.js';
5
6
  import { buildSessionSupervisorOps } from '../session/supervisor-op.js';
6
7
  import { installLogPersistence } from '../session/hibernation.js';
@@ -48,6 +49,8 @@ class RuntimeOwner {
48
49
  flushScheduled = false;
49
50
  janitorScheduled = false;
50
51
  recoveryNotice = false;
52
+ scheduling = new Set();
53
+ fileLeases = new Map();
51
54
  services;
52
55
  constructor(options) {
53
56
  this.options = options;
@@ -68,6 +71,8 @@ class RuntimeOwner {
68
71
  env: options.env,
69
72
  notify: (line) => this._notifySession(line),
70
73
  requestLaunchTurn: (at) => this._scheduleLaunchTurn(at),
74
+ resolveWorkerLaunch: options.resolveWorkerLaunch,
75
+ filesystem: () => options.workspace.filesystem,
71
76
  });
72
77
  options.workspace.shell.bindTerminal(this.terminal);
73
78
  installLogPersistence(this, options.ctx, () => this.scheduleLogs());
@@ -122,9 +127,25 @@ class RuntimeOwner {
122
127
  throw new Error('Nimbus runtime cannot replace the workspace registry');
123
128
  }
124
129
  _notifySession(line) { this.terminal.write(`${line}\r\n`); }
125
- async _scheduleLaunchTurn(notBefore = Date.now()) {
126
- await this.options.lifecycle.schedule('resident-launch', Math.max(Date.now(), notBefore));
127
- return true;
130
+ _scheduleLaunchTurn(notBefore = Date.now()) {
131
+ const pending = this.schedule('resident-launch', Math.max(Date.now(), notBefore));
132
+ this.options.lifecycle.waitUntil(pending);
133
+ return pending;
134
+ }
135
+ schedule(reason, at) {
136
+ if (this._w1SessionDestroyed)
137
+ return Promise.reject(new Error('Nimbus runtime is closed'));
138
+ // Registered before the deferred call runs, so close() waits for a turn
139
+ // issued in the same tick; the second closed check keeps that turn from
140
+ // arming an alarm the shutdown has already decided against.
141
+ const pending = Promise.resolve().then(() => {
142
+ if (this._w1SessionDestroyed)
143
+ return;
144
+ return this.options.lifecycle.schedule(reason, at);
145
+ });
146
+ this.scheduling.add(pending);
147
+ void pending.then(() => this.scheduling.delete(pending), () => this.scheduling.delete(pending));
148
+ return pending;
128
149
  }
129
150
  _reportExternalExit(pid, code, reason) { return rpc._reportExternalExit(this, pid, code, reason); }
130
151
  _emitExitDump(pid, code) { return rpc._emitExitDump(this, pid, code); }
@@ -132,7 +153,11 @@ class RuntimeOwner {
132
153
  _rpcStdout(pid, data) { return rpc._rpcStdout(this, pid, data); }
133
154
  _rpcStderr(pid, data) { return rpc._rpcStderr(this, pid, data); }
134
155
  supervisorOps() {
135
- this.supervisor ??= buildSessionSupervisorOps(this, undefined, Object.fromEntries(Object.values(SUPERVISOR_OP_ROUTES).map(({ method }) => {
156
+ this.supervisor ??= buildSessionSupervisorOps(this, createSupervisorBridgeStore({
157
+ vfs: this.options.workspace.vfs,
158
+ filesystem: this.options.workspace.filesystem,
159
+ processes: this.processes,
160
+ }), Object.fromEntries(Object.values(SUPERVISOR_OP_ROUTES).map(({ method }) => {
136
161
  const handler = Reflect.get(rpc, method);
137
162
  if (typeof handler !== 'function')
138
163
  throw new Error(`Missing Nimbus supervisor implementation: ${method}`);
@@ -151,11 +176,17 @@ class RuntimeOwner {
151
176
  return;
152
177
  if (!this.flushScheduled) {
153
178
  this.flushScheduled = true;
154
- this.options.lifecycle.waitUntil(this.options.lifecycle.schedule('log-flush', Date.now() + 250));
179
+ this.options.lifecycle.waitUntil(this.schedule('log-flush', Date.now() + 250).catch((error) => {
180
+ this.flushScheduled = false;
181
+ throw error;
182
+ }));
155
183
  }
156
184
  if (!this.janitorScheduled) {
157
185
  this.janitorScheduled = true;
158
- this.options.lifecycle.waitUntil(this.options.lifecycle.schedule('log-janitor', Date.now() + 60_000));
186
+ this.options.lifecycle.waitUntil(this.schedule('log-janitor', Date.now() + 60_000).catch((error) => {
187
+ this.janitorScheduled = false;
188
+ throw error;
189
+ }));
159
190
  }
160
191
  }
161
192
  async onScheduled(task) {
@@ -206,6 +237,22 @@ class RuntimeOwner {
206
237
  this.terminal.detach();
207
238
  this.processes.flushLogs();
208
239
  }
240
+ files(cred) {
241
+ this.assertOpen();
242
+ const workspace = this.options.workspace;
243
+ const identity = requireVfsCred(cred, 'runtime files');
244
+ // One host lease per credential, not per `.as()` call: the descriptor
245
+ // scope behind a lease lives until close(), so re-deriving a view for an
246
+ // identity the runtime already opened must reuse that scope.
247
+ const key = `${identity.uid}:${identity.gid}:${identity.groups.join(',')}:${identity.umask}`;
248
+ let lease = this.fileLeases.get(key);
249
+ if (!lease) {
250
+ lease = workspace.filesystem.openHost(identity);
251
+ this.fileLeases.set(key, lease);
252
+ }
253
+ const view = new SandboxFsImpl(new ExecutionFs(lease.fs), () => workspace.shell.getCwd());
254
+ return Object.assign(view, { as: (next) => this.files(next) });
255
+ }
209
256
  close() {
210
257
  this.closing ??= Promise.resolve().then(async () => {
211
258
  const failures = [];
@@ -237,6 +284,13 @@ class RuntimeOwner {
237
284
  for (const ws of this.ctx.getWebSockets('process-logs')) {
238
285
  await clean(() => ws.close(1000, 'runtime closed'));
239
286
  }
287
+ await clean(() => this.facetManager?.closeLaunches());
288
+ await Promise.allSettled(this.scheduling);
289
+ await clean(() => this.supervisor?.dispose());
290
+ for (const lease of this.fileLeases.values())
291
+ await clean(() => lease.dispose());
292
+ this.fileLeases.clear();
293
+ await clean(() => this.options.workspace.close());
240
294
  for (const task of HostedTask.options)
241
295
  await clean(() => this.options.lifecycle.cancel(task));
242
296
  if (failures.length > 0)
@@ -245,18 +299,15 @@ class RuntimeOwner {
245
299
  return this.closing;
246
300
  }
247
301
  }
248
- function runtimeFiles(workspace, cred) {
249
- const view = new SandboxFsImpl(workspace.kernel.vfs.as(cred), () => workspace.shell.getCwd());
250
- return Object.assign(view, { as: (next) => runtimeFiles(workspace, requireVfsCred(next, 'runtime files')) });
251
- }
252
302
  export async function composeHostedRuntime(options) {
253
303
  const owner = new RuntimeOwner(options);
254
304
  await owner.ensureRuntimeReady();
255
305
  return {
256
306
  workspace: options.workspace,
257
307
  terminal: owner.terminal,
258
- files: runtimeFiles(options.workspace, owner.processes.cred(owner.shellProcessPid)),
308
+ files: owner.files(owner.processes.cred(owner.shellProcessPid)),
259
309
  runtimes: owner.runtimeManager,
310
+ facets: () => owner.ensureFacetManager(),
260
311
  ready: operations.ensureProgrammaticReady.bind(null, owner),
261
312
  exec: operations.rpcExec.bind(null, owner),
262
313
  runCode: operations.rpcRunCode.bind(null, owner),
@@ -270,6 +321,9 @@ export async function composeHostedRuntime(options) {
270
321
  processLogs: (pid, options) => operations.rpcProcessLogs(owner, pid, options),
271
322
  listPorts: operations.rpcListPorts.bind(null, owner),
272
323
  listApps: operations.rpcListApps.bind(null, owner),
324
+ ensureDurableApp: operations.rpcEnsureDurableApp.bind(null, owner),
325
+ unexposePort: operations.rpcUnexposePort.bind(null, owner),
326
+ removeDurableApp: operations.rpcRemoveDurableApp.bind(null, owner),
273
327
  exposeApp: operations.rpcExposeApp.bind(null, owner),
274
328
  removeApp: operations.rpcRemoveApp.bind(null, owner),
275
329
  rotateLink: operations.rpcRotateLink.bind(null, owner),
@@ -277,7 +331,10 @@ export async function composeHostedRuntime(options) {
277
331
  ensureRuntimes: operations.rpcEnsureRuntimes.bind(null, owner),
278
332
  listRuntimes: operations.rpcListRuntimes.bind(null, owner),
279
333
  spawnWorker: operations.rpcSpawnWorker.bind(null, owner),
280
- routeCapabilityPort: operations.rpcRouteCapabilityPort.bind(null, owner),
334
+ routeCapabilityPort: async (...args) => {
335
+ await operations.ensureProgrammaticReady(owner);
336
+ return owner.ensureFacetManager().apps.routeCapabilityPort(...args);
337
+ },
281
338
  supervisorOp: (envelope) => owner.supervisorOp(envelope),
282
339
  onScheduled: (task) => owner.onScheduled(task),
283
340
  attachTerminal: (ws) => owner.attachTerminal(ws),
@@ -1,4 +1,5 @@
1
- import { type ComposedFacetManager } from "../facets/compose.js";
1
+ import { type ComposedFacetManager, type FacetManagerHostHooks } from "../facets/compose.js";
2
+ import { type NimbusFilesystemAuthority } from "@nimbus-sh/core/runtime/os-contracts.js";
2
3
  import { EsbuildBundlePool } from "../facets/esbuild-bundle-pool.js";
3
4
  import type { NpmInstaller } from "../npm/installer.js";
4
5
  import { WebSocketRelay } from "../session/ws-relay.js";
@@ -15,7 +16,18 @@ export interface RuntimeServiceContext {
15
16
  readonly ctx: DurableObjectState;
16
17
  readonly env: HostedRuntimeEnv;
17
18
  notify(line: string): void;
18
- requestLaunchTurn(notBefore?: number): Promise<boolean>;
19
+ requestLaunchTurn(notBefore?: number): Promise<void>;
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;
29
+ /** The host's own authority: a session has exactly one, and this is it. */
30
+ filesystem: () => NimbusFilesystemAuthority;
19
31
  }
20
32
  export declare function ensureBundlePool(self: RuntimeServiceHost, runtimeContext: RuntimeServiceContext): EsbuildBundlePool;
21
33
  export declare function ensureFacetManager(self: RuntimeServiceHost, runtimeContext: RuntimeServiceContext): ComposedFacetManager;
@@ -1 +1 @@
1
- {"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/hosted/services.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/hosted/services.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ import { composeFacetManager } from "../facets/compose.js";
3
3
  import { FacetProcessManager, textBytes } from "../facets/process.js";
4
4
  import { ChildProcessSpawnPool } from "../loaders/child-process/spawn-pool.js";
5
5
  import { CRED_KERNEL, CRED_SESSION_USER } from "@nimbus-sh/core/runtime/os-contracts.js";
6
+ import { SqliteFilesystemAuthority } from "@nimbus-sh/core/runtime/filesystem-authority.js";
6
7
  import { EsbuildBundlePool } from "../facets/esbuild-bundle-pool.js";
7
8
  import { EsbuildService } from "@nimbus-sh/core/runtime/esbuild-service.js";
8
9
  import { CF_COMPAT_DATE } from "@nimbus-sh/core/constants.js";
@@ -64,16 +65,25 @@ export function ensureFacetManager(self, runtimeContext) {
64
65
  // first. Cheap and idempotent; every caller already stood it up or is
65
66
  // about to.
66
67
  self.ensureSqliteFs();
68
+ const filesystem = runtimeContext.filesystem();
69
+ // The manager reaches the disk behind the authority (boot images, the
70
+ // launch journal), so a host that credentials something other than this
71
+ // session's SQLite filesystem cannot compose one.
72
+ if (!(filesystem instanceof SqliteFilesystemAuthority)) {
73
+ throw new Error('Nimbus: the facet manager needs the session SqliteFilesystemAuthority, not a foreign filesystem authority');
74
+ }
67
75
  self.facetManagerComposed = composeFacetManager({
68
76
  ctx: runtimeContext.ctx,
69
77
  env: runtimeContext.env,
70
78
  processes: self.processes,
71
79
  portRegistry: self.portRegistry,
72
- vfs: self.sqliteFs,
80
+ vfs: filesystem.vfs,
81
+ filesystem,
73
82
  ...(self.esbuildService ? { esbuild: self.esbuildService } : {}),
74
83
  hooks: {
75
84
  onExternalExit: (pid, code, reason) => self._reportExternalExit(pid, code, reason),
76
- requestLaunchTurn: (notBefore) => { void runtimeContext.requestLaunchTurn(notBefore); },
85
+ requestLaunchTurn: (notBefore) => runtimeContext.requestLaunchTurn(notBefore),
86
+ resolveWorkerLaunch: runtimeContext.resolveWorkerLaunch,
77
87
  notify: (line) => runtimeContext.notify(line),
78
88
  onSpawn: (pid, command, longRunning) => {
79
89
  const attachedTty = self.processes.get(pid)?.attachedTty === true;
@@ -82,6 +92,9 @@ export function ensureFacetManager(self, runtimeContext) {
82
92
  self.processes.openInput(pid);
83
93
  }
84
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?.();
85
98
  }
86
99
  // Only surface long-running / user-visible spawns to keep
87
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.62 KiB
19
- * W7 size: 28.97 KiB
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,MAAk1J,CAAC;AAEr3J,eAAO,MAAM,iBAAiB,EAAE,MAA4/7B,CAAC"}
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.62 KiB
19
- * W7 size: 28.97 KiB
18
+ * Tar size: 4.58 KiB
19
+ * W7 size: 29.19 KiB
20
20
  */
21
- export const TAR_STREAM_PREAMBLE = "// ../core/src/_shared/tarball-stream.ts\nvar 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 = "// ../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);\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) {\n this.reader = reader;\n }\n reader;\n done = false;\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.reader.read(new Uint8Array(length - offset));\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 reader.read();\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. SMALL — kept inline.
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 const PRE_BUNDLE_PREAMBLE: string;
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAyCH;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAqCjC,CAAC"}
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. SMALL — kept inline.
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 { ESBUILD_WASM_JS_FN_BODY, ESBUILD_WASM_VERSION, } from '../esbuild-wasm-bundle.generated.js';
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 const PRE_BUNDLE_PREAMBLE = `
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're fetched at facet boot via env.SUPERVISOR.getEsbuildWasm() to
78
- // keep the per-dispatch worker module source under ~120 KiB instead of
79
- // ~16 MiB. See this file's header for why.
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(ESBUILD_WASM_JS_FN_BODY)};
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-8f8c64edd2a1f0f5.js";
11
- export const NODE_SHIMS_BUILD_ID = "8f8c64edd2a1f0f5";
12
- export const NODE_SHIMS_SHA256 = "8f8c64edd2a1f0f54037793c4f2dc8d77aca0da808aa5fdb9b1c179208c191cc";
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";
@@ -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. Returns null if not found. */
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;
@@ -1 +1 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/npm/cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/npm/cache.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;;;;;OAWG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,wBAAwB;IACvC,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAID,qBAAa,QAAQ;IACnB,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,WAAW,CAAS;gBAEhB,GAAG,EAAE,UAAU;IAM3B,YAAY,IAAI,IAAI;IA6FpB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CACwH;IAEhK,OAAO,CAAC,kBAAkB;IAkB1B;;;;;;;;mBAQe;IACf,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI;IA0B1E;;;;;;;;;OASG;IACH,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAU1D,kDAAkD;IAClD,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAUvD,8DAA8D;IAC9D,gBAAgB,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI;IAcjD;;;;;;;;;;;;;OAaG;IACH,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IA2BtF,mFAAmF;IACnF,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,IAAI;IAsBpE,0EAA0E;IAC1E,aAAa,CACX,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,EACnC,GAAG,CAAC,EAAE,kBAAkB,GACvB,IAAI;IAmCP,wEAAwE;IACxE,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAOzC,oCAAoC;IACpC,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAkBtD,sCAAsC;IACtC,YAAY,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAUzC,oEAAoE;IACpE,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKxC,2DAA2D;IAC3D,eAAe,IAAI,IAAI;IAOvB;;;;;;;;OAQG;IACH,sBAAsB,CACpB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,GACrB,wBAAwB,GAAG,IAAI;IAsBlC,iFAAiF;IACjF,sBAAsB,CAAC,KAAK,EAAE,wBAAwB,GAAG,IAAI;IAW7D;8BAC0B;IAC1B,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAOhD,QAAQ,IAAI;QACV,eAAe,EAAE,MAAM,CAAC;QACxB,gBAAgB,EAAE,MAAM,CAAC;QACzB,UAAU,EAAE,MAAM,CAAC;QACnB,oBAAoB,EAAE,MAAM,CAAC;KAC9B;CAaF"}