@xyo-network/event-kit-node 2.1.0 → 2.2.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 (36) hide show
  1. package/README.md +53 -34
  2. package/dist/node/bestEffortSnapshotWorker.d.ts +44 -0
  3. package/dist/node/bestEffortSnapshotWorker.d.ts.map +1 -0
  4. package/dist/node/bestEffortTypes.d.ts +55 -0
  5. package/dist/node/bestEffortTypes.d.ts.map +1 -0
  6. package/dist/node/filesystemCursorStore.d.ts.map +1 -1
  7. package/dist/node/filesystemJson.d.ts.map +1 -1
  8. package/dist/node/filesystemPublisherOutbox.d.ts.map +1 -1
  9. package/dist/node/filesystemWakeInbox.d.ts +3 -20
  10. package/dist/node/filesystemWakeInbox.d.ts.map +1 -1
  11. package/dist/node/filesystemWakeQueue.d.ts +10 -19
  12. package/dist/node/filesystemWakeQueue.d.ts.map +1 -1
  13. package/dist/node/handshake.mjs +12 -11
  14. package/dist/node/handshake.mjs.map +2 -2
  15. package/dist/node/index.d.ts +8 -1
  16. package/dist/node/index.d.ts.map +1 -1
  17. package/dist/node/index.mjs +1909 -609
  18. package/dist/node/index.mjs.map +4 -4
  19. package/dist/node/privateSqlite.d.ts +19 -0
  20. package/dist/node/privateSqlite.d.ts.map +1 -0
  21. package/dist/node/receiverAuthority.d.ts +102 -0
  22. package/dist/node/receiverAuthority.d.ts.map +1 -0
  23. package/dist/node/receiverMigration.d.ts +16 -0
  24. package/dist/node/receiverMigration.d.ts.map +1 -0
  25. package/dist/node/receiverMigrationInventory.d.ts +6 -0
  26. package/dist/node/receiverMigrationInventory.d.ts.map +1 -0
  27. package/dist/node/receiverMigrationSnapshot.d.ts +6 -0
  28. package/dist/node/receiverMigrationSnapshot.d.ts.map +1 -0
  29. package/dist/node/receiverMigrationTypes.d.ts +113 -0
  30. package/dist/node/receiverMigrationTypes.d.ts.map +1 -0
  31. package/dist/node/runtime.mjs.map +2 -2
  32. package/dist/node/sqliteBestEffortInbox.d.ts +20 -0
  33. package/dist/node/sqliteBestEffortInbox.d.ts.map +1 -0
  34. package/dist/node/sqliteWakeQueue.d.ts +83 -17
  35. package/dist/node/sqliteWakeQueue.d.ts.map +1 -1
  36. package/package.json +13 -13
@@ -9,60 +9,93 @@ var __decorateClass = (decorators, target, key, kind) => {
9
9
  return result;
10
10
  };
11
11
 
12
- // src/filesystemCursorStore.ts
12
+ // src/bestEffortSnapshotWorker.ts
13
+ import { canonicalJson, wakeSourcesEqual } from "@xyo-network/event-kit-protocol";
13
14
  import {
14
- mkdir,
15
- readFile,
16
- writeFile
17
- } from "node:fs/promises";
18
- import PATH from "node:path";
19
- function createFilesystemCursorStore(options) {
20
- const filePath = PATH.resolve(options.path);
15
+ HashZod,
16
+ NonnegativeJsonSafeIntegerZod,
17
+ WakeSourceZod
18
+ } from "@xyo-network/event-kit-schemas";
19
+ function createBestEffortSnapshotWorker(options) {
20
+ if (!Number.isSafeInteger(options.maxSnapshotBytes) || options.maxSnapshotBytes < 1)
21
+ throw new TypeError("Invalid bounded snapshot byte limit");
22
+ const nowSeconds = options.nowSeconds ?? (() => Math.floor(Date.now() / 1e3));
23
+ const leaseSeconds = options.leaseSeconds ?? 60;
24
+ if (!Number.isSafeInteger(leaseSeconds) || leaseSeconds < 1)
25
+ throw new TypeError("Invalid Best Effort worker lease lifetime");
26
+ async function processSnapshot(lease, signal) {
27
+ const source = lease.binding.source;
28
+ const observed = await options.source.readCurrent({
29
+ source,
30
+ signal,
31
+ maxBytes: options.maxSnapshotBytes
32
+ });
33
+ signal.throwIfAborted();
34
+ const snapshot = await options.source.verifySnapshot({
35
+ source,
36
+ signal,
37
+ snapshot: observed
38
+ });
39
+ signal.throwIfAborted();
40
+ WakeSourceZod.parse(snapshot.source);
41
+ NonnegativeJsonSafeIntegerZod.parse(snapshot.position);
42
+ if (snapshot.hash !== void 0) HashZod.parse(snapshot.hash);
43
+ if (!wakeSourcesEqual(source, snapshot.source) || source.kind !== "utc-interval" && snapshot.hash === void 0)
44
+ throw new TypeError("Independent snapshot does not match its pinned source");
45
+ if (Buffer.byteLength(canonicalJson(snapshot.value), "utf8") > options.maxSnapshotBytes)
46
+ throw new RangeError("Independent snapshot exceeds its configured byte bound");
47
+ const previous = lease.previousSnapshot;
48
+ if (previous !== void 0 && (snapshot.position < previous.position || snapshot.position === previous.position && snapshot.hash !== previous.hash))
49
+ throw new TypeError("Independent snapshot regressed or changed at the committed position");
50
+ if (!await lease.isCurrent()) return "superseded";
51
+ await options.application.commitSnapshot({ lease, snapshot });
52
+ signal.throwIfAborted();
53
+ return await lease.complete(snapshot) ? "completed" : "superseded";
54
+ }
21
55
  return {
22
- async load() {
56
+ async runOnce(inputSignal) {
57
+ const controller = new AbortController();
58
+ const signal = inputSignal ?? controller.signal;
59
+ signal.throwIfAborted();
60
+ const now = nowSeconds();
61
+ await options.inbox.recoverExpired(now);
62
+ const lease = await options.inbox.claim({ nowSeconds: now, leaseSeconds });
63
+ if (lease === void 0) return "idle";
64
+ let failed = false;
65
+ let failure;
66
+ let releaseFailed = false;
67
+ let releaseFailure;
68
+ let result = "superseded";
23
69
  try {
24
- const raw = await readFile(filePath, "utf8");
25
- const parsed = JSON.parse(raw);
26
- return typeof parsed.lastProcessedBlock !== "number" || !Number.isFinite(parsed.lastProcessedBlock) ? void 0 : parsed.lastProcessedBlock;
70
+ result = await processSnapshot(lease, signal);
27
71
  } catch (error) {
28
- if (isNotFound(error)) return void 0;
29
- throw error;
72
+ failed = true;
73
+ failure = error;
30
74
  }
31
- },
32
- async save(lastProcessedBlock) {
33
- await mkdir(PATH.dirname(filePath), { recursive: true });
34
- const body = { lastProcessedBlock };
35
- await writeFile(filePath, `${JSON.stringify(body)}
36
- `, "utf8");
75
+ try {
76
+ await lease.release();
77
+ } catch (error) {
78
+ releaseFailed = true;
79
+ releaseFailure = error;
80
+ }
81
+ if (failed && releaseFailed)
82
+ throw new AggregateError([failure, releaseFailure], "Best Effort snapshot work failed and its lease release also failed", { cause: failure });
83
+ if (failed) throw failure;
84
+ if (releaseFailed) throw releaseFailure;
85
+ return result;
37
86
  }
38
87
  };
39
88
  }
40
- function isNotFound(error) {
41
- return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
42
- }
43
89
 
44
- // src/filesystemPublisherOutbox.ts
45
- import { randomBytes as randomBytes2 } from "node:crypto";
46
- import {
47
- mkdir as mkdir3,
48
- rename,
49
- unlink as unlink2
50
- } from "node:fs/promises";
90
+ // src/filesystemCursorStore.ts
91
+ import { lstat } from "node:fs/promises";
51
92
  import PATH4 from "node:path";
52
-
53
- // src/filesystemWakeQueue.ts
54
- import { randomBytes } from "node:crypto";
55
- import {
56
- link,
57
- open as open2,
58
- readdir,
59
- unlink
60
- } from "node:fs/promises";
61
- import PATH3 from "node:path";
93
+ import { NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod2 } from "@xyo-network/event-kit-schemas";
94
+ import { z } from "zod";
62
95
 
63
96
  // src/filesystemDurability.ts
64
- import { mkdir as mkdir2, open } from "node:fs/promises";
65
- import PATH2 from "node:path";
97
+ import { mkdir, open } from "node:fs/promises";
98
+ import PATH from "node:path";
66
99
  async function writeDurableFile(path, body, mode) {
67
100
  const handle = await open(path, "w", mode);
68
101
  try {
@@ -73,15 +106,15 @@ async function writeDurableFile(path, body, mode) {
73
106
  }
74
107
  }
75
108
  async function ensureDurableDirectory(path, mode) {
76
- const directory = PATH2.resolve(path);
77
- const firstCreated = await mkdir2(directory, { mode, recursive: true });
109
+ const directory = PATH.resolve(path);
110
+ const firstCreated = await mkdir(directory, { mode, recursive: true });
78
111
  if (firstCreated === void 0) return;
79
- const existingParent = PATH2.dirname(PATH2.resolve(firstCreated));
112
+ const existingParent = PATH.dirname(PATH.resolve(firstCreated));
80
113
  let cursor = directory;
81
114
  while (true) {
82
115
  await syncDirectory(cursor);
83
116
  if (cursor === existingParent) return;
84
- const parent = PATH2.dirname(cursor);
117
+ const parent = PATH.dirname(cursor);
85
118
  if (parent === cursor) throw new Error("Created directory is not below its reported existing parent");
86
119
  cursor = parent;
87
120
  }
@@ -105,140 +138,299 @@ function errorCode(error) {
105
138
  return typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
106
139
  }
107
140
 
108
- // src/filesystemWakeQueue.ts
109
- function createFilesystemWakeQueue(options) {
110
- const root = PATH3.resolve(options.path);
111
- const pendingDir = PATH3.join(root, "pending");
112
- const inflightDir = PATH3.join(root, "inflight");
113
- const tempDir = PATH3.join(root, "tmp");
114
- let sequence = 0;
115
- async function ensureDirs() {
116
- await ensureDurableDirectory(pendingDir);
117
- await ensureDurableDirectory(inflightDir);
118
- await ensureDurableDirectory(tempDir);
119
- }
120
- async function returnToPending(name) {
121
- try {
122
- await link(PATH3.join(inflightDir, name), PATH3.join(pendingDir, name));
123
- } catch (error) {
124
- if (!isAlreadyExists(error) && !isNotFound2(error)) throw error;
141
+ // src/filesystemJson.ts
142
+ import { randomBytes } from "node:crypto";
143
+ import {
144
+ link,
145
+ open as open2,
146
+ rename,
147
+ unlink
148
+ } from "node:fs/promises";
149
+ import PATH2 from "node:path";
150
+ async function readJsonFileStrict(path) {
151
+ let handle;
152
+ try {
153
+ handle = await open2(path, "r");
154
+ } catch (error) {
155
+ if (errorCode2(error) === "ENOENT") return void 0;
156
+ throw error;
157
+ }
158
+ try {
159
+ return JSON.parse(await handle.readFile("utf8"), reviveJson);
160
+ } finally {
161
+ await handle.close();
162
+ }
163
+ }
164
+ async function replaceJsonFile(path, value, options = {}) {
165
+ const directory = PATH2.dirname(path);
166
+ await ensureDurableDirectory(directory, options.directoryMode);
167
+ const tempPath = PATH2.join(directory, `.${PATH2.basename(path)}.${process.pid}.${suffix()}.tmp`);
168
+ try {
169
+ await writeDurableFile(tempPath, `${stringifyJson(value)}
170
+ `, options.mode);
171
+ await rename(tempPath, path);
172
+ await syncDirectory(directory);
173
+ } catch (error) {
174
+ await unlinkIfMissing(tempPath);
175
+ throw error;
176
+ }
177
+ }
178
+ async function publishJsonFile(path, value, options = {}) {
179
+ const directory = PATH2.dirname(path);
180
+ await ensureDurableDirectory(directory, options.directoryMode);
181
+ const tempPath = PATH2.join(directory, `.${PATH2.basename(path)}.${process.pid}.${suffix()}.tmp`);
182
+ try {
183
+ await writeDurableFile(tempPath, `${stringifyJson(value)}
184
+ `, options.mode);
185
+ await link(tempPath, path);
186
+ await syncDirectory(directory);
187
+ return true;
188
+ } catch (error) {
189
+ if (errorCode2(error) === "EEXIST") return false;
190
+ throw error;
191
+ } finally {
192
+ await unlinkIfMissing(tempPath);
193
+ }
194
+ }
195
+ function suffix() {
196
+ return randomBytes(8).toString("hex");
197
+ }
198
+ function stringifyJson(value) {
199
+ return JSON.stringify(value, replaceJsonValue);
200
+ }
201
+ function replaceJsonValue(_key, candidate) {
202
+ return typeof candidate === "bigint" ? { $eventKitBigInt: candidate.toString() } : candidate;
203
+ }
204
+ function reviveJson(_key, candidate) {
205
+ return typeof candidate === "object" && candidate !== null && Object.keys(candidate).length === 1 && "$eventKitBigInt" in candidate && typeof candidate.$eventKitBigInt === "string" && /^(?:0|[1-9][0-9]*)$/u.test(candidate.$eventKitBigInt) ? BigInt(candidate.$eventKitBigInt) : candidate;
206
+ }
207
+ async function unlinkIfMissing(path) {
208
+ try {
209
+ await unlink(path);
210
+ } catch (error) {
211
+ if (errorCode2(error) !== "ENOENT") throw error;
212
+ }
213
+ }
214
+ function errorCode2(error) {
215
+ return typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
216
+ }
217
+
218
+ // src/privateSqlite.ts
219
+ import {
220
+ closeSync,
221
+ constants,
222
+ fstatSync,
223
+ fsyncSync,
224
+ lstatSync,
225
+ mkdirSync,
226
+ openSync
227
+ } from "node:fs";
228
+ import PATH3 from "node:path";
229
+ import { DatabaseSync } from "node:sqlite";
230
+ var LegacyReceiverStoreError = class extends Error {
231
+ name = "LegacyReceiverStoreError";
232
+ constructor(path) {
233
+ super(`Receiver store requires explicit offline migration: ${path}`);
234
+ }
235
+ };
236
+ function ensurePrivateDirectory(path) {
237
+ const created = mkdirSync(path, { recursive: true, mode: 448 });
238
+ const info = lstatSync(path);
239
+ if (!info.isDirectory() || info.isSymbolicLink() || (info.mode & 63) !== 0 || process.getuid !== void 0 && info.uid !== process.getuid()) {
240
+ throw new Error(`Receiver directory must be owned and private (0700): ${path}`);
241
+ }
242
+ if (created === void 0) return;
243
+ const existingParent = PATH3.dirname(PATH3.resolve(created));
244
+ let cursor = PATH3.resolve(path);
245
+ for (; ; ) {
246
+ syncPrivateDirectory(cursor);
247
+ if (cursor === existingParent) break;
248
+ cursor = PATH3.dirname(cursor);
249
+ }
250
+ }
251
+ function syncPrivateDirectory(path) {
252
+ let descriptor;
253
+ try {
254
+ descriptor = openSync(path, "r");
255
+ fsyncSync(descriptor);
256
+ } catch (error) {
257
+ const code = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
258
+ if (!["EISDIR", "EINVAL", "ENOTSUP", "EOPNOTSUPP"].includes(String(code))) throw error;
259
+ } finally {
260
+ if (descriptor !== void 0) closeSync(descriptor);
261
+ }
262
+ }
263
+ function checkPrivateStoreFile(path, create = false) {
264
+ let descriptor;
265
+ try {
266
+ descriptor = openSync(path, (create ? constants.O_RDWR : constants.O_RDONLY) | constants.O_NOFOLLOW | (create ? constants.O_CREAT : 0), 384);
267
+ } catch (error) {
268
+ if (!create && typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") return;
269
+ throw error;
270
+ }
271
+ try {
272
+ const info = fstatSync(descriptor);
273
+ if (!info.isFile() || (info.mode & 63) !== 0 || process.getuid !== void 0 && info.uid !== process.getuid()) {
274
+ throw new Error(`Receiver file must be owned and private (0600): ${path}`);
125
275
  }
126
- await unlinkIfMissing(PATH3.join(inflightDir, name));
276
+ } finally {
277
+ closeSync(descriptor);
278
+ }
279
+ }
280
+ function initializeDatabase(database, file, format) {
281
+ database.exec("PRAGMA busy_timeout=100");
282
+ const tables = database.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").all();
283
+ if (tables.length > 0 && tables.every((row) => row.name !== "eventkit_store")) throw new LegacyReceiverStoreError(file);
284
+ if (tables.some((row) => row.name === "eventkit_store")) {
285
+ const columns = database.prepare("PRAGMA table_info(eventkit_store)").all();
286
+ if (columns.every((row) => row.name !== "initialized")) throw new LegacyReceiverStoreError(file);
287
+ const priorFormat = database.prepare("SELECT format FROM eventkit_store WHERE id=1").get();
288
+ if (priorFormat?.format !== format) throw new Error("Receiver store format/namespace mismatch");
289
+ }
290
+ database.exec("PRAGMA journal_mode=WAL; PRAGMA synchronous=FULL; PRAGMA foreign_keys=ON;");
291
+ database.exec("BEGIN IMMEDIATE");
292
+ try {
293
+ database.exec("CREATE TABLE IF NOT EXISTS eventkit_store (id INTEGER PRIMARY KEY CHECK(id=1), format TEXT NOT NULL, initialized INTEGER NOT NULL DEFAULT 0)");
294
+ const prior = database.prepare("SELECT format FROM eventkit_store WHERE id=1").get();
295
+ if (prior !== void 0 && prior.format !== format) throw new Error("Receiver store format/namespace mismatch");
296
+ database.prepare("INSERT OR IGNORE INTO eventkit_store(id,format) VALUES(1,?)").run(format);
297
+ database.exec("COMMIT");
298
+ } catch (error) {
299
+ database.exec("ROLLBACK");
300
+ throw error;
301
+ }
302
+ }
303
+ function openPrivateSqlite(options) {
304
+ const file = options.path === ":memory:" ? options.path : PATH3.resolve(options.path);
305
+ if (file !== ":memory:") {
306
+ ensurePrivateDirectory(PATH3.dirname(file));
307
+ checkPrivateStoreFile(file, true);
308
+ checkPrivateStoreFile(`${file}-wal`);
309
+ checkPrivateStoreFile(`${file}-shm`);
310
+ checkPrivateStoreFile(`${file}-journal`);
311
+ }
312
+ const database = new DatabaseSync(file);
313
+ let closed = false;
314
+ try {
315
+ initializeDatabase(database, file, options.format);
316
+ if (file !== ":memory:") {
317
+ checkPrivateStoreFile(file);
318
+ checkPrivateStoreFile(`${file}-wal`);
319
+ checkPrivateStoreFile(`${file}-shm`);
320
+ syncPrivateDirectory(PATH3.dirname(file));
321
+ }
322
+ } catch (error) {
323
+ database.close();
324
+ throw error;
127
325
  }
128
326
  return {
129
- async append(wake) {
130
- await ensureDirs();
131
- const name = entryName(wake, sequence += 1);
132
- const tempPath = PATH3.join(tempDir, name);
133
- await writeDurableFile(tempPath, `${JSON.stringify(wake)}
134
- `);
327
+ database,
328
+ transaction(work) {
329
+ if (closed) throw new Error("Receiver store is closed");
330
+ database.exec("BEGIN IMMEDIATE");
135
331
  try {
136
- await link(tempPath, PATH3.join(pendingDir, name));
137
- await syncDirectory(pendingDir);
138
- } finally {
139
- await unlinkIfMissing(tempPath);
140
- }
141
- },
142
- async claim() {
143
- await ensureDirs();
144
- const names = (await readdir(pendingDir)).toSorted();
145
- for (const name of names) {
146
- const inflightPath = PATH3.join(inflightDir, name);
147
- const pendingPath = PATH3.join(pendingDir, name);
148
- try {
149
- await link(pendingPath, inflightPath);
150
- } catch (error) {
151
- if (isAlreadyExists(error) || isNotFound2(error)) continue;
152
- throw error;
153
- }
154
- await unlinkIfMissing(pendingPath);
155
- const wake = await readJsonFile(inflightPath);
156
- if (wake === void 0) {
157
- await unlinkIfMissing(inflightPath);
158
- continue;
332
+ const result = work();
333
+ if (result !== null && (typeof result === "object" || typeof result === "function") && "then" in result) {
334
+ throw new TypeError("Receiver transactions must be synchronous");
159
335
  }
160
- return {
161
- wake,
162
- async ack() {
163
- await unlinkIfMissing(inflightPath);
164
- },
165
- async release() {
166
- await returnToPending(name);
167
- }
168
- };
336
+ database.exec("COMMIT");
337
+ return result;
338
+ } catch (error) {
339
+ database.exec("ROLLBACK");
340
+ throw error;
169
341
  }
170
- return void 0;
171
- },
172
- async pendingCount() {
173
- await ensureDirs();
174
- return (await readdir(pendingDir)).length;
175
342
  },
176
- async recoverInflight() {
177
- await ensureDirs();
178
- const names = await readdir(inflightDir);
179
- let recovered = 0;
180
- for (const name of names) {
181
- await returnToPending(name);
182
- recovered += 1;
183
- }
184
- return recovered;
343
+ close() {
344
+ if (closed) return;
345
+ database.close();
346
+ closed = true;
185
347
  }
186
348
  };
187
349
  }
188
- async function readJsonFile(path) {
189
- try {
190
- const handle = await open2(path, "r");
191
- try {
192
- const raw = await handle.readFile("utf8");
193
- return JSON.parse(raw);
194
- } finally {
195
- await handle.close();
350
+
351
+ // src/filesystemCursorStore.ts
352
+ var CursorFileZod = z.strictObject({ lastProcessedBlock: NonnegativeJsonSafeIntegerZod2 });
353
+ function createFilesystemCursorStore(options) {
354
+ const filePath = PATH4.resolve(options.path);
355
+ const directory = PATH4.dirname(filePath);
356
+ return {
357
+ async load() {
358
+ if (!await existingPrivateDirectory(directory)) return void 0;
359
+ checkPrivateStoreFile(filePath);
360
+ const raw = await readJsonFileStrict(filePath);
361
+ if (raw === void 0) return void 0;
362
+ const cursor = CursorFileZod.parse(raw);
363
+ await syncDirectory(directory);
364
+ return cursor.lastProcessedBlock;
365
+ },
366
+ async save(lastProcessedBlock) {
367
+ const body = CursorFileZod.parse({ lastProcessedBlock });
368
+ await ensureDurableDirectory(directory, 448);
369
+ ensurePrivateDirectory(directory);
370
+ checkPrivateStoreFile(filePath);
371
+ await replaceJsonFile(filePath, body, { mode: 384, directoryMode: 448 });
196
372
  }
197
- } catch {
198
- return void 0;
199
- }
200
- }
201
- function entryName(wake, sequence) {
202
- const stamp = String(Date.now()).padStart(15, "0");
203
- const ordinal = String(sequence).padStart(6, "0");
204
- return `${stamp}-${ordinal}-${randomBytes(6).toString("hex")}-${wake.wakeId}.json`;
373
+ };
205
374
  }
206
- async function unlinkIfMissing(path) {
375
+ async function existingPrivateDirectory(directory) {
207
376
  try {
208
- await unlink(path);
377
+ await lstat(directory);
209
378
  } catch (error) {
210
- if (!isNotFound2(error)) throw error;
379
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") return false;
380
+ throw error;
211
381
  }
212
- }
213
- function isNotFound2(error) {
214
- return errorCode2(error) === "ENOENT";
215
- }
216
- function isAlreadyExists(error) {
217
- return errorCode2(error) === "EEXIST";
218
- }
219
- function errorCode2(error) {
220
- return typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
382
+ ensurePrivateDirectory(directory);
383
+ return true;
221
384
  }
222
385
 
223
386
  // src/filesystemPublisherOutbox.ts
387
+ import { randomBytes as randomBytes2 } from "node:crypto";
388
+ import {
389
+ lstat as lstat2,
390
+ rename as rename2,
391
+ unlink as unlink2
392
+ } from "node:fs/promises";
393
+ import PATH5 from "node:path";
394
+ import { WakeEnvelopeSchema, WakeEnvelopeZod } from "@xyo-network/event-kit-schemas";
224
395
  function createFilesystemPublisherOutbox(options) {
225
- const filePath = PATH4.resolve(options.path);
396
+ const filePath = PATH5.resolve(options.path);
397
+ const directory = PATH5.dirname(filePath);
226
398
  return {
227
399
  async clear() {
400
+ if (!await existingPrivateDirectory2(directory)) return;
401
+ checkPrivateStoreFile(filePath);
402
+ let removed = false;
228
403
  try {
229
404
  await unlink2(filePath);
405
+ removed = true;
406
+ } catch (error) {
407
+ if (!isNotFound(error)) throw error;
408
+ }
409
+ try {
410
+ await syncDirectory(directory);
230
411
  } catch (error) {
231
- if (!isNotFound3(error)) throw error;
412
+ if (removed || !isNotFound(error)) throw error;
232
413
  }
233
414
  },
234
- load: async () => await readJsonFile(filePath),
415
+ async load() {
416
+ if (!await existingPrivateDirectory2(directory)) return void 0;
417
+ checkPrivateStoreFile(filePath);
418
+ const raw = await readJsonFileStrict(filePath);
419
+ if (raw === void 0) return void 0;
420
+ const pending = validatePendingWake(raw);
421
+ await syncDirectory(directory);
422
+ return pending;
423
+ },
235
424
  async save(pending) {
236
- await mkdir3(PATH4.dirname(filePath), { recursive: true });
425
+ validatePendingWake(pending);
426
+ await ensureDurableDirectory(directory, 448);
427
+ ensurePrivateDirectory(directory);
428
+ checkPrivateStoreFile(filePath);
237
429
  const tempPath = `${filePath}.${process.pid}.${randomBytes2(6).toString("hex")}.tmp`;
238
- await writeDurableFile(tempPath, `${JSON.stringify(pending)}
239
- `);
240
430
  try {
241
- await rename(tempPath, filePath);
431
+ await writeDurableFile(tempPath, `${JSON.stringify(pending)}
432
+ `, 384);
433
+ await rename2(tempPath, filePath);
242
434
  } catch (error) {
243
435
  try {
244
436
  await unlink2(tempPath);
@@ -246,254 +438,526 @@ function createFilesystemPublisherOutbox(options) {
246
438
  }
247
439
  throw error;
248
440
  }
441
+ await syncDirectory(directory);
249
442
  }
250
443
  };
251
444
  }
252
- function isNotFound3(error) {
445
+ function validatePendingWake(raw) {
446
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw) || "schema" in raw || "deliveryMode" in raw) {
447
+ throw new Error("Invalid pending wake record");
448
+ }
449
+ WakeEnvelopeZod.parse({
450
+ ...raw,
451
+ schema: WakeEnvelopeSchema,
452
+ deliveryMode: "durable"
453
+ });
454
+ return raw;
455
+ }
456
+ function isNotFound(error) {
253
457
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
254
458
  }
459
+ async function existingPrivateDirectory2(directory) {
460
+ try {
461
+ await lstat2(directory);
462
+ } catch (error) {
463
+ if (isNotFound(error)) return false;
464
+ throw error;
465
+ }
466
+ ensurePrivateDirectory(directory);
467
+ return true;
468
+ }
255
469
 
256
470
  // src/filesystemWakeInbox.ts
257
- import { createHash, randomBytes as randomBytes3 } from "node:crypto";
471
+ import { existsSync as existsSync2, readdirSync as readdirSync2 } from "node:fs";
472
+ import PATH7 from "node:path";
473
+
474
+ // src/filesystemWakeQueue.ts
475
+ import { randomBytes as randomBytes4 } from "node:crypto";
476
+ import { existsSync, readdirSync } from "node:fs";
258
477
  import {
259
478
  link as link2,
260
- mkdir as mkdir4,
261
479
  open as open3,
262
- readdir as readdir2,
480
+ readdir,
263
481
  unlink as unlink3
264
482
  } from "node:fs/promises";
265
- import PATH5 from "node:path";
266
- function createFilesystemWakeInbox(options) {
267
- const root = PATH5.resolve(options.path);
268
- const tokensDir = PATH5.join(root, "tokens");
269
- const wakesDir = PATH5.join(root, "wakes");
270
- const tempDir = PATH5.join(root, "tmp");
271
- let tail = Promise.resolve();
272
- function exclusive(work) {
273
- const previous = tail;
274
- const run = (async () => {
275
- try {
276
- await previous;
277
- } catch {
483
+ import PATH6 from "node:path";
484
+ import { parseAdmittedWake as parseAdmittedWake2, StaleWakeLeaseError as StaleWakeLeaseError2 } from "@xyo-network/event-kit-protocol";
485
+
486
+ // src/receiverAuthority.ts
487
+ import { Buffer as Buffer2 } from "node:buffer";
488
+ import { createHash, randomBytes as randomBytes3 } from "node:crypto";
489
+ import { parseAdmittedWake, StaleWakeLeaseError } from "@xyo-network/event-kit-protocol";
490
+ import {
491
+ HashZod as HashZod2,
492
+ NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod3,
493
+ NormalizedAddressZod,
494
+ WakeIdZod,
495
+ WakeJwtAudienceZod,
496
+ WakeJwtIdZod
497
+ } from "@xyo-network/event-kit-schemas";
498
+ function receiverIdentity(parts) {
499
+ return createHash("sha256").update(JSON.stringify(parts)).digest("hex");
500
+ }
501
+ function validateAttempt(attempt) {
502
+ WakeJwtAudienceZod.parse(attempt.audience);
503
+ NormalizedAddressZod.parse(attempt.publisher);
504
+ WakeIdZod.parse(attempt.wakeId);
505
+ WakeJwtIdZod.parse(attempt.jwtId);
506
+ HashZod2.parse(attempt.eventHash);
507
+ NonnegativeJsonSafeIntegerZod3.parse(attempt.retainJwtUntil);
508
+ }
509
+ function boundedBody(wake) {
510
+ const body = JSON.stringify(wake);
511
+ if (Buffer2.byteLength(body, "utf8") > 65536) throw new RangeError("Receiver work exceeds its 64 KiB bound");
512
+ return body;
513
+ }
514
+ function sameValues(pairs) {
515
+ return pairs.every(([actual, expected]) => actual === expected);
516
+ }
517
+ function validateQueueState(row) {
518
+ if (!Number.isSafeInteger(row.id) || row.id < 1) throw new Error("Invalid persisted queue identity");
519
+ if (row.state === "completed") {
520
+ if (row.body !== null || row.lease_token !== null || row.terminal_operation !== "ack" || row.terminal_token === null || !/^[0-9a-f]{32}$/u.test(row.terminal_token)) throw new Error("Invalid persisted completion state");
521
+ return;
522
+ }
523
+ if (typeof row.body !== "string") throw new Error("Active receiver work has no bounded body");
524
+ if (row.state === "pending" && row.lease_token === null) return;
525
+ if (row.state === "inflight" && row.lease_token !== null && /^[0-9a-f]{32}$/u.test(row.lease_token)) return;
526
+ throw new Error("Invalid persisted lease state");
527
+ }
528
+ function openReceiverAuthority(path) {
529
+ const owner = openPrivateSqlite({ path, format: "event-kit-receiver" });
530
+ const database = owner.database;
531
+ const transaction = (work) => owner.transaction(work);
532
+ const requiredTables = ["receiver_queue", "receiver_wakes", "receiver_tokens"];
533
+ try {
534
+ transaction(() => {
535
+ const existingTables = new Set(database.prepare("SELECT name FROM sqlite_master WHERE type='table'").all().map((row) => row.name));
536
+ const initialized = database.prepare("SELECT initialized FROM eventkit_store WHERE id=1").get()?.initialized;
537
+ if (initialized !== 0 && initialized !== 1 || initialized === 1 && requiredTables.some((name) => !existingTables.has(name)) || initialized === 0 && requiredTables.some((name) => existingTables.has(name))) {
538
+ throw new Error("Incomplete persisted receiver schema; refusing to recreate admission/completion history");
278
539
  }
279
- return await work();
280
- })();
281
- tail = Promise.allSettled([run]);
282
- return run;
540
+ if (initialized === 1) return;
541
+ database.exec(`
542
+ CREATE TABLE IF NOT EXISTS receiver_queue (
543
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
544
+ body TEXT,
545
+ state TEXT NOT NULL CHECK(state IN ('pending','inflight','completed')),
546
+ lease_token TEXT,
547
+ terminal_token TEXT,
548
+ terminal_operation TEXT,
549
+ CHECK ((state='inflight' AND lease_token IS NOT NULL) OR (state!='inflight' AND lease_token IS NULL))
550
+ );
551
+ CREATE INDEX IF NOT EXISTS receiver_pending ON receiver_queue(state,id);
552
+ CREATE TABLE IF NOT EXISTS receiver_wakes (
553
+ identity TEXT PRIMARY KEY,
554
+ audience TEXT NOT NULL, publisher TEXT NOT NULL, wake_id TEXT NOT NULL,
555
+ event_hash TEXT NOT NULL, receipt TEXT NOT NULL, authorization TEXT NOT NULL,
556
+ queue_id INTEGER NOT NULL UNIQUE REFERENCES receiver_queue(id)
557
+ );
558
+ CREATE TABLE IF NOT EXISTS receiver_tokens (
559
+ identity TEXT PRIMARY KEY, wake_identity TEXT NOT NULL REFERENCES receiver_wakes(identity),
560
+ event_hash TEXT NOT NULL, wake_id TEXT NOT NULL, retain_until INTEGER NOT NULL
561
+ );
562
+ CREATE INDEX IF NOT EXISTS receiver_token_expiry ON receiver_tokens(retain_until);
563
+ `);
564
+ database.prepare("UPDATE eventkit_store SET initialized=1 WHERE id=1").run();
565
+ });
566
+ } catch (error) {
567
+ owner.close();
568
+ throw error;
283
569
  }
284
- async function ensureDirs() {
285
- await mkdir4(tokensDir, { recursive: true });
286
- await mkdir4(wakesDir, { recursive: true });
287
- await mkdir4(tempDir, { recursive: true });
570
+ const boundedRow = `id,state,lease_token,terminal_token,terminal_operation,
571
+ CASE WHEN length(CAST(body AS BLOB)) <= 65536 THEN body ELSE NULL END AS body`;
572
+ const pending = database.prepare(`SELECT ${boundedRow} FROM receiver_queue WHERE state='pending' ORDER BY id LIMIT 1`);
573
+ const getRow = database.prepare(`SELECT ${boundedRow} FROM receiver_queue WHERE id=?`);
574
+ const insert = database.prepare("INSERT INTO receiver_queue(body,state) VALUES(?,'pending')");
575
+ const count = database.prepare("SELECT COUNT(*) AS total FROM receiver_queue WHERE state='pending'");
576
+ const wakeKey = (attempt) => receiverIdentity([attempt.audience, attempt.publisher, attempt.wakeId]);
577
+ const tokenKey = (attempt) => receiverIdentity([attempt.audience, attempt.publisher, attempt.jwtId]);
578
+ function validateAssociation(row, wake) {
579
+ validateQueueState(row);
580
+ const record = database.prepare("SELECT * FROM receiver_wakes WHERE queue_id=?").get(row.id);
581
+ if (record === void 0) return;
582
+ const receipt = JSON.parse(String(record.receipt));
583
+ const receiptMatches = sameValues([
584
+ [record.identity, receiverIdentity([String(record.audience), String(record.publisher), String(record.wake_id)])],
585
+ [receipt.id, record.identity],
586
+ [receipt.audience, record.audience],
587
+ [receipt.publisher, record.publisher],
588
+ [receipt.wakeId, record.wake_id],
589
+ [receipt.eventHash, record.event_hash]
590
+ ]);
591
+ const wakeMatches = wake === void 0 || sameValues([
592
+ [wake.audience, record.audience],
593
+ [wake.publisher, record.publisher],
594
+ [wake.wakeId, record.wake_id],
595
+ [wake.eventHash, record.event_hash]
596
+ ]);
597
+ if (!receiptMatches || !wakeMatches) throw new Error("Queued body does not match its admitted identity/receipt");
288
598
  }
289
- function tokenPath(attempt) {
290
- return PATH5.join(tokensDir, `${digest([attempt.audience, attempt.publisher, attempt.jwtId])}.json`);
599
+ async function preparedWork(attempt) {
600
+ const record = database.prepare("SELECT queue_id FROM receiver_wakes WHERE identity=?").get(wakeKey(attempt));
601
+ if (record === void 0) return void 0;
602
+ const row = currentRow(Number(record.queue_id));
603
+ if (row === void 0) throw new Error("Receiver receipt has no work/completion record");
604
+ const wake = row.body === null ? void 0 : await parseAdmittedWake(JSON.parse(row.body));
605
+ validateAssociation(row, wake);
606
+ return { id: row.id, body: row.body };
291
607
  }
292
- function wakePath(attempt) {
293
- return PATH5.join(wakesDir, `${digest([attempt.audience, attempt.publisher, attempt.wakeId])}.json`);
608
+ class WorkChanged extends Error {
294
609
  }
295
- async function bindToken(attempt, wake) {
296
- const record = {
297
- ...wake,
298
- jwtId: attempt.jwtId,
299
- retainJwtUntil: attempt.retainJwtUntil
300
- };
301
- await writeRecordExclusive(tempDir, tokenPath(attempt), record);
610
+ async function validatedTransaction(attempt, work) {
611
+ for (let tries = 0; tries < 4; tries++) {
612
+ const prepared = await preparedWork(attempt);
613
+ try {
614
+ return transaction(() => work(prepared));
615
+ } catch (error) {
616
+ if (!(error instanceof WorkChanged)) throw error;
617
+ }
618
+ }
619
+ throw new Error("Receiver work changed during validation; retry admission");
302
620
  }
303
- async function matchRecorded(attempt) {
304
- const token = await readRecord(tokenPath(attempt));
305
- if (token !== void 0) {
306
- return token.eventHash === attempt.eventHash && token.wakeId === attempt.wakeId ? { kind: "duplicate", receipt: token.receipt } : { kind: "jwt-id-collision" };
621
+ function resolve(attempt, prepared) {
622
+ const token = database.prepare("SELECT * FROM receiver_tokens WHERE identity=?").get(tokenKey(attempt));
623
+ if (token !== void 0 && (token.event_hash !== attempt.eventHash || token.wake_id !== attempt.wakeId)) return { kind: "jwt-id-collision" };
624
+ const record = database.prepare("SELECT * FROM receiver_wakes WHERE identity=?").get(wakeKey(attempt));
625
+ if (record === void 0) {
626
+ if (token !== void 0) throw new Error("Receiver token points to missing admitted work");
627
+ return { kind: "new" };
628
+ }
629
+ if (record.event_hash !== attempt.eventHash) return { kind: "wake-id-collision" };
630
+ const receipt = JSON.parse(String(record.receipt));
631
+ if (!sameValues([
632
+ [receipt.id, wakeKey(attempt)],
633
+ [receipt.eventHash, attempt.eventHash],
634
+ [receipt.publisher, attempt.publisher],
635
+ [receipt.audience, attempt.audience],
636
+ [receipt.wakeId, attempt.wakeId]
637
+ ])) {
638
+ throw new Error("Receiver receipt does not match its identity");
639
+ }
640
+ const row = currentRow(Number(record.queue_id));
641
+ if (row === void 0) throw new Error("Receiver receipt has no pending/completed work record");
642
+ validateAssociation(row);
643
+ if (row.state !== "completed" && (prepared?.id !== row.id || prepared.body !== row.body)) throw new WorkChanged();
644
+ if (token !== void 0 && (token.wake_identity !== record.identity || !Number.isSafeInteger(token.retain_until) || Number(token.retain_until) < 0)) {
645
+ throw new Error("Persisted token does not match its admitted work");
307
646
  }
308
- const wake = await readRecord(wakePath(attempt));
309
- if (wake === void 0) return void 0;
310
- if (wake.eventHash !== attempt.eventHash) return { kind: "wake-id-collision" };
311
- await bindToken(attempt, wake);
312
- return { kind: "duplicate", receipt: wake.receipt };
647
+ database.prepare(`INSERT INTO receiver_tokens(identity,wake_identity,event_hash,wake_id,retain_until) VALUES(?,?,?,?,?)
648
+ ON CONFLICT(identity) DO UPDATE SET retain_until=MAX(retain_until,excluded.retain_until)`).run(tokenKey(attempt), wakeKey(attempt), attempt.eventHash, attempt.wakeId, attempt.retainJwtUntil);
649
+ return { kind: "duplicate", receipt };
313
650
  }
314
- return {
315
- resolveAttempt: async (attempt) => await exclusive(async () => {
316
- await ensureDirs();
317
- return await matchRecorded(attempt) ?? { kind: "new" };
318
- }),
319
- commitAuthorized: async ({ attempt, wake }) => await exclusive(async () => {
320
- await ensureDirs();
321
- const recorded = await matchRecorded(attempt);
322
- if (recorded !== void 0) return recorded;
323
- const receipt = Object.freeze({
324
- audience: attempt.audience,
325
- eventHash: attempt.eventHash,
326
- id: digest([attempt.audience, attempt.publisher, attempt.wakeId]),
327
- publisher: attempt.publisher,
328
- wakeId: attempt.wakeId
651
+ const inbox = {
652
+ async resolveAttempt(attempt) {
653
+ validateAttempt(attempt);
654
+ return await validatedTransaction(attempt, (prepared) => resolve(attempt, prepared));
655
+ },
656
+ async commitAuthorized(input) {
657
+ validateAttempt(input.attempt);
658
+ const wake = await parseAdmittedWake(input.wake);
659
+ const { attempt, grant } = input;
660
+ if (attempt.audience !== wake.audience || attempt.publisher !== wake.publisher || attempt.wakeId !== wake.wakeId || attempt.eventHash !== wake.eventHash || attempt.jwtId !== wake.jti || grant.grant.authorizationKind !== "activated-subscription" || grant.grant.target !== wake.publisher || grant.grant.deploymentId !== wake.deploymentId || grant.grant.queueId !== wake.queueId || grant.grant.filterHash !== wake.event.filterHash || grant.grant.authorizationChainId !== wake.event.authorizationChainId || wake.event.deliveryMode !== "durable") {
661
+ throw new TypeError("Authorized admission does not match its exact wake/identity binding");
662
+ }
663
+ return await validatedTransaction(attempt, (prepared) => {
664
+ const previous = resolve(attempt, prepared);
665
+ if (previous.kind !== "new") return previous;
666
+ const receipt = Object.freeze({
667
+ audience: attempt.audience,
668
+ eventHash: attempt.eventHash,
669
+ id: wakeKey(attempt),
670
+ publisher: attempt.publisher,
671
+ wakeId: attempt.wakeId
672
+ });
673
+ const row = insert.run(boundedBody(wake));
674
+ database.prepare("INSERT INTO receiver_wakes(identity,audience,publisher,wake_id,event_hash,receipt,authorization,queue_id) VALUES(?,?,?,?,?,?,?,?)").run(
675
+ wakeKey(attempt),
676
+ attempt.audience,
677
+ attempt.publisher,
678
+ attempt.wakeId,
679
+ attempt.eventHash,
680
+ JSON.stringify(receipt),
681
+ JSON.stringify(grant),
682
+ row.lastInsertRowid
683
+ );
684
+ database.prepare("INSERT INTO receiver_tokens(identity,wake_identity,event_hash,wake_id,retain_until) VALUES(?,?,?,?,?)").run(tokenKey(attempt), wakeKey(attempt), attempt.eventHash, attempt.wakeId, attempt.retainJwtUntil);
685
+ return { kind: "enqueued", receipt };
329
686
  });
330
- const record = {
331
- audience: attempt.audience,
332
- eventHash: attempt.eventHash,
333
- publisher: attempt.publisher,
334
- receipt,
335
- wakeId: attempt.wakeId
687
+ }
688
+ };
689
+ function currentRow(id) {
690
+ return getRow.get(id);
691
+ }
692
+ function rows() {
693
+ return database.prepare(`SELECT ${boundedRow} FROM receiver_queue WHERE state!='completed' ORDER BY id`).all();
694
+ }
695
+ function finish(id, token, operation) {
696
+ transaction(() => {
697
+ const row = currentRow(id);
698
+ if (row?.terminal_token === token && row.terminal_operation === operation) return;
699
+ if (row?.state !== "inflight" || row.lease_token !== token) throw new StaleWakeLeaseError();
700
+ if (operation === "ack") database.prepare(`UPDATE receiver_queue
701
+ SET state='completed',body=NULL,lease_token=NULL,terminal_token=?,terminal_operation='ack'
702
+ WHERE id=? AND lease_token=?`).run(token, id, token);
703
+ else database.prepare(`UPDATE receiver_queue
704
+ SET state='pending',lease_token=NULL,terminal_token=?,terminal_operation='release'
705
+ WHERE id=? AND lease_token=?`).run(token, id, token);
706
+ });
707
+ }
708
+ async function claim(validate) {
709
+ for (; ; ) {
710
+ const row = pending.get();
711
+ if (row === void 0) return void 0;
712
+ if (typeof row.body !== "string") throw new Error("Pending receiver work has no body");
713
+ const wake = await parseAdmittedWake(JSON.parse(row.body));
714
+ validateAssociation(row, wake);
715
+ await validate?.(row);
716
+ const token = randomBytes3(16).toString("hex");
717
+ const won = transaction(() => database.prepare(`UPDATE receiver_queue SET state='inflight',lease_token=?
718
+ WHERE id=? AND state='pending' AND body=?`).run(token, row.id, row.body).changes === 1);
719
+ if (!won) continue;
720
+ let completed;
721
+ const complete = (operation) => {
722
+ if (completed !== void 0) {
723
+ if (completed !== operation) throw new StaleWakeLeaseError();
724
+ return;
725
+ }
726
+ finish(row.id, token, operation);
727
+ completed = operation;
336
728
  };
337
- await options.queue.append(wake);
338
- const claimed = await writeRecordExclusive(tempDir, wakePath(attempt), record);
339
- if (!claimed) {
340
- const winner = await readRecord(wakePath(attempt));
341
- if (winner === void 0) throw new Error("wake record vanished during commit");
342
- if (winner.eventHash !== attempt.eventHash) return { kind: "wake-id-collision" };
343
- await bindToken(attempt, winner);
344
- return { kind: "duplicate", receipt: winner.receipt };
345
- }
346
- await bindToken(attempt, record);
347
- return { kind: "enqueued", receipt };
348
- }),
349
- async pruneExpiredTokens(nowSeconds) {
350
- await ensureDirs();
351
- let removed = 0;
352
- for (const name of await readdir2(tokensDir)) {
353
- const path = PATH5.join(tokensDir, name);
354
- const token = await readRecord(path);
355
- if (token === void 0 || token.retainJwtUntil > nowSeconds) continue;
356
- await unlinkIfMissing2(path);
357
- removed += 1;
358
- }
359
- return removed;
360
- }
361
- };
362
- }
363
- async function writeRecordExclusive(tempDir, path, record) {
364
- const tempPath = PATH5.join(tempDir, `${PATH5.basename(path)}.${process.pid}.${randomSuffix()}`);
365
- await writeDurableFile(tempPath, `${JSON.stringify(record)}
366
- `);
367
- try {
368
- await link2(tempPath, path);
369
- await syncDirectory(PATH5.dirname(path));
370
- return true;
371
- } catch (error) {
372
- if (isAlreadyExists2(error)) return false;
373
- throw error;
374
- } finally {
375
- await unlinkIfMissing2(tempPath);
729
+ return {
730
+ id: row.id,
731
+ token,
732
+ wake,
733
+ async ack() {
734
+ complete("ack");
735
+ },
736
+ async release() {
737
+ complete("release");
738
+ }
739
+ };
740
+ }
376
741
  }
742
+ const queue = {
743
+ async append(wake) {
744
+ const parsed = await parseAdmittedWake(wake);
745
+ transaction(() => insert.run(boundedBody(parsed)));
746
+ },
747
+ claim: () => claim(),
748
+ pendingCount: async () => Number(count.get()?.total),
749
+ async recoverInflight(options) {
750
+ if (options?.consumersStopped !== true) throw new TypeError("Recovery requires all consumers to be stopped");
751
+ return transaction(() => Number(database.prepare("UPDATE receiver_queue SET state='pending',lease_token=NULL WHERE state='inflight'").run().changes));
752
+ }
753
+ };
754
+ return {
755
+ queue,
756
+ inbox,
757
+ database,
758
+ claim,
759
+ currentRow,
760
+ rows,
761
+ async append(wake) {
762
+ const parsed = await parseAdmittedWake(wake);
763
+ return transaction(() => Number(insert.run(boundedBody(parsed)).lastInsertRowid));
764
+ },
765
+ receiptQueueId(receipt) {
766
+ const row = database.prepare("SELECT queue_id FROM receiver_wakes WHERE identity=?").get(receipt.id);
767
+ if (row === void 0) throw new Error("Committed receipt has no work/completion record");
768
+ return Number(row.queue_id);
769
+ },
770
+ close: () => owner.close(),
771
+ pruneExpiredTokens(nowSeconds) {
772
+ NonnegativeJsonSafeIntegerZod3.parse(nowSeconds);
773
+ return transaction(() => Number(database.prepare("DELETE FROM receiver_tokens WHERE retain_until<=?").run(nowSeconds).changes));
774
+ }
775
+ };
377
776
  }
378
- async function readRecord(path) {
379
- try {
380
- const handle = await open3(path, "r");
777
+
778
+ // src/filesystemWakeQueue.ts
779
+ var authorities = /* @__PURE__ */ new WeakMap();
780
+ function filesystemReceiverAuthority(queue) {
781
+ const authority = authorities.get(queue);
782
+ if (authority === void 0) throw new TypeError("Filesystem inbox requires its coordinated filesystem receiver queue");
783
+ return authority;
784
+ }
785
+ function createFilesystemWakeQueue(options) {
786
+ const root = PATH6.resolve(options.path);
787
+ const file = PATH6.join(root, "receiver.sqlite");
788
+ if (!existsSync(file) && ["pending", "inflight", "tmp"].some((name) => {
789
+ const directory = PATH6.join(root, name);
790
+ return existsSync(directory) && readdirSync(directory).length > 0;
791
+ })) throw new LegacyReceiverStoreError(root);
792
+ ensurePrivateDirectory(root);
793
+ const pendingDir = PATH6.join(root, "pending");
794
+ const inflightDir = PATH6.join(root, "inflight");
795
+ const tempDir = PATH6.join(root, "tmp");
796
+ for (const directory of [pendingDir, inflightDir, tempDir]) ensurePrivateDirectory(directory);
797
+ const authority = openReceiverAuthority(file);
798
+ async function publishProjection(path, body) {
799
+ if (existsSync(path)) {
800
+ checkPrivateStoreFile(path);
801
+ return;
802
+ }
803
+ const temporary = PATH6.join(tempDir, `${randomBytes4(16).toString("hex")}.json`);
804
+ await writeDurableFile(temporary, body, 384);
381
805
  try {
382
- const raw = await handle.readFile("utf8");
383
- return JSON.parse(raw);
806
+ try {
807
+ await link2(temporary, path);
808
+ } catch (error) {
809
+ if (!hasCode(error, "EEXIST")) throw error;
810
+ }
811
+ await syncDirectory(PATH6.dirname(path));
384
812
  } finally {
385
- await handle.close();
813
+ await unlinkIfMissing2(temporary);
386
814
  }
387
- } catch (error) {
388
- if (isNotFound4(error)) return void 0;
389
- throw error;
390
815
  }
391
- }
392
- function digest(parts) {
393
- return createHash("sha256").update(JSON.stringify(parts)).digest("hex");
394
- }
395
- function randomSuffix() {
396
- return randomBytes3(8).toString("hex");
397
- }
398
- async function unlinkIfMissing2(path) {
399
- try {
400
- await unlink3(path);
401
- } catch (error) {
402
- if (!isNotFound4(error)) throw error;
816
+ async function validateProjection(row) {
817
+ if (row.body === null) throw new Error("Receiver work has no committed body");
818
+ const path = PATH6.join(pendingDir, `${row.id}.json`);
819
+ checkPrivateStoreFile(path);
820
+ let raw = await readJsonFile(path);
821
+ if (raw === void 0) {
822
+ await publishProjection(path, row.body);
823
+ checkPrivateStoreFile(path);
824
+ raw = await readJsonFile(path);
825
+ }
826
+ checkPrivateStoreFile(path);
827
+ const projected = await parseAdmittedWake2(raw);
828
+ if (JSON.stringify(projected) !== row.body) throw new Error("Filesystem projection differs from committed receiver work");
403
829
  }
404
- }
405
- function isNotFound4(error) {
406
- return errorCode3(error) === "ENOENT";
407
- }
408
- function isAlreadyExists2(error) {
409
- return errorCode3(error) === "EEXIST";
410
- }
411
- function errorCode3(error) {
412
- return typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
413
- }
414
-
415
- // src/filesystemEventKitReplayCheckpointStore.ts
416
- import PATH7 from "node:path";
417
-
418
- // src/filesystemJson.ts
419
- import { randomBytes as randomBytes4 } from "node:crypto";
420
- import {
421
- link as link3,
422
- open as open4,
423
- rename as rename2,
424
- unlink as unlink4
425
- } from "node:fs/promises";
426
- import PATH6 from "node:path";
427
- async function readJsonFileStrict(path) {
428
- try {
429
- const handle = await open4(path, "r");
430
- try {
431
- return JSON.parse(await handle.readFile("utf8"), reviveJson);
432
- } finally {
433
- await handle.close();
830
+ async function repairRow(id) {
831
+ const row = authority.currentRow(id);
832
+ if (row === void 0) throw new Error("Receiver work record vanished");
833
+ if (row.state === "completed") {
834
+ await unlinkIfMissing2(PATH6.join(pendingDir, `${id}.json`));
835
+ } else {
836
+ if (row.body === null) throw new Error("Active receiver work is corrupt");
837
+ await publishProjection(PATH6.join(pendingDir, `${id}.json`), row.body);
838
+ if (row.state === "inflight" && row.lease_token !== null) {
839
+ await publishProjection(PATH6.join(inflightDir, `${id}-${row.lease_token}.json`), row.body);
840
+ }
434
841
  }
435
- } catch (error) {
436
- if (errorCode4(error) === "ENOENT") return void 0;
437
- throw error;
842
+ await syncDirectory(pendingDir);
843
+ }
844
+ async function repairProjections() {
845
+ for (const row of authority.rows()) {
846
+ if (row.body === null) throw new Error("Active receiver work is corrupt");
847
+ await publishProjection(PATH6.join(pendingDir, `${row.id}.json`), row.body);
848
+ if (row.state === "inflight" && row.lease_token !== null) {
849
+ await publishProjection(PATH6.join(inflightDir, `${row.id}-${row.lease_token}.json`), row.body);
850
+ }
851
+ }
852
+ for (const name of await readdir(pendingDir)) {
853
+ const match = /^(\d+)\.json$/u.exec(name);
854
+ if (match !== null && authority.currentRow(Number(match[1]))?.state === "completed") await unlinkIfMissing2(PATH6.join(pendingDir, name));
855
+ }
856
+ for (const name of await readdir(inflightDir)) {
857
+ const match = /^(\d+)-([0-9a-f]{32})\.json$/u.exec(name);
858
+ if (match !== null && authority.currentRow(Number(match[1]))?.lease_token !== match[2]) await unlinkIfMissing2(PATH6.join(inflightDir, name));
859
+ }
860
+ await syncDirectory(pendingDir);
861
+ await syncDirectory(inflightDir);
438
862
  }
863
+ const queue = {
864
+ async append(wake) {
865
+ const id = await authority.append(wake);
866
+ await repairRow(id);
867
+ },
868
+ async claim() {
869
+ const leased = await authority.claim(validateProjection);
870
+ if (leased === void 0) return;
871
+ try {
872
+ await repairRow(leased.id);
873
+ if (authority.currentRow(leased.id)?.lease_token !== leased.token) throw new StaleWakeLeaseError2();
874
+ } catch (error) {
875
+ try {
876
+ await leased.release();
877
+ } catch (releaseError) {
878
+ throw new AggregateError([error, releaseError], "Claim projection failed and its lease could not be released", { cause: releaseError });
879
+ }
880
+ throw error;
881
+ }
882
+ return {
883
+ wake: leased.wake,
884
+ async ack() {
885
+ await leased.ack();
886
+ await unlinkIfMissing2(PATH6.join(inflightDir, `${leased.id}-${leased.token}.json`));
887
+ await repairRow(leased.id);
888
+ await syncDirectory(inflightDir);
889
+ },
890
+ async release() {
891
+ await leased.release();
892
+ await unlinkIfMissing2(PATH6.join(inflightDir, `${leased.id}-${leased.token}.json`));
893
+ await repairRow(leased.id);
894
+ await syncDirectory(inflightDir);
895
+ }
896
+ };
897
+ },
898
+ pendingCount: authority.queue.pendingCount,
899
+ async recoverInflight(options2) {
900
+ const moved = await authority.queue.recoverInflight(options2);
901
+ await repairProjections();
902
+ return moved;
903
+ },
904
+ close: authority.close,
905
+ repairProjections,
906
+ repairRow
907
+ };
908
+ authorities.set(queue, authority);
909
+ return queue;
439
910
  }
440
- async function replaceJsonFile(path, value, options = {}) {
441
- const directory = PATH6.dirname(path);
442
- await ensureDurableDirectory(directory, options.directoryMode);
443
- const tempPath = PATH6.join(directory, `.${PATH6.basename(path)}.${process.pid}.${suffix()}.tmp`);
444
- await writeDurableFile(tempPath, `${stringifyJson(value)}
445
- `, options.mode);
911
+ async function readJsonFile(path) {
912
+ let handle;
446
913
  try {
447
- await rename2(tempPath, path);
448
- await syncDirectory(directory);
914
+ handle = await open3(path, "r");
449
915
  } catch (error) {
450
- await unlinkIfMissing3(tempPath);
916
+ if (hasCode(error, "ENOENT")) return void 0;
451
917
  throw error;
452
918
  }
453
- }
454
- async function publishJsonFile(path, value, options = {}) {
455
- const directory = PATH6.dirname(path);
456
- await ensureDurableDirectory(directory, options.directoryMode);
457
- const tempPath = PATH6.join(directory, `.${PATH6.basename(path)}.${process.pid}.${suffix()}.tmp`);
458
- await writeDurableFile(tempPath, `${stringifyJson(value)}
459
- `, options.mode);
460
919
  try {
461
- await link3(tempPath, path);
462
- await syncDirectory(directory);
463
- return true;
464
- } catch (error) {
465
- if (errorCode4(error) === "EEXIST") return false;
466
- throw error;
920
+ if ((await handle.stat()).size > 65536) throw new RangeError("Receiver JSON record exceeds its 64 KiB bound");
921
+ return JSON.parse(await handle.readFile("utf8"));
467
922
  } finally {
468
- await unlinkIfMissing3(tempPath);
923
+ await handle.close();
469
924
  }
470
925
  }
471
- function suffix() {
472
- return randomBytes4(8).toString("hex");
473
- }
474
- function stringifyJson(value) {
475
- return JSON.stringify(value, replaceJsonValue);
476
- }
477
- function replaceJsonValue(_key, candidate) {
478
- return typeof candidate === "bigint" ? { $eventKitBigInt: candidate.toString() } : candidate;
479
- }
480
- function reviveJson(_key, candidate) {
481
- return typeof candidate === "object" && candidate !== null && Object.keys(candidate).length === 1 && "$eventKitBigInt" in candidate && typeof candidate.$eventKitBigInt === "string" && /^(?:0|[1-9][0-9]*)$/u.test(candidate.$eventKitBigInt) ? BigInt(candidate.$eventKitBigInt) : candidate;
482
- }
483
- async function unlinkIfMissing3(path) {
926
+ async function unlinkIfMissing2(path) {
484
927
  try {
485
- await unlink4(path);
928
+ await unlink3(path);
486
929
  } catch (error) {
487
- if (errorCode4(error) !== "ENOENT") throw error;
930
+ if (!hasCode(error, "ENOENT")) throw error;
488
931
  }
489
932
  }
490
- function errorCode4(error) {
491
- return typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
933
+ function hasCode(error, code) {
934
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
935
+ }
936
+
937
+ // src/filesystemWakeInbox.ts
938
+ function createFilesystemWakeInbox(options) {
939
+ const root = PATH7.resolve(options.path);
940
+ if (["tokens", "wakes"].some((name) => {
941
+ const directory = PATH7.join(root, name);
942
+ return existsSync2(directory) && readdirSync2(directory).length > 0;
943
+ })) throw new LegacyReceiverStoreError(root);
944
+ ensurePrivateDirectory(root);
945
+ const authority = filesystemReceiverAuthority(options.queue);
946
+ return {
947
+ resolveAttempt: authority.inbox.resolveAttempt,
948
+ async commitAuthorized(input) {
949
+ const committed = await authority.inbox.commitAuthorized(input);
950
+ if ("receipt" in committed) await options.queue.repairRow(authority.receiptQueueId(committed.receipt));
951
+ return committed;
952
+ },
953
+ pruneExpiredTokens: async (nowSeconds) => authority.pruneExpiredTokens(nowSeconds)
954
+ };
492
955
  }
493
956
 
494
957
  // src/filesystemEventKitReplayCheckpointStore.ts
958
+ import PATH8 from "node:path";
495
959
  function createFilesystemEventKitReplayCheckpointStore(options) {
496
- const path = PATH7.resolve(options.path);
960
+ const path = PATH8.resolve(options.path);
497
961
  let tail = Promise.resolve();
498
962
  return {
499
963
  async load() {
@@ -520,7 +984,7 @@ function createFilesystemEventKitReplayCheckpointStore(options) {
520
984
 
521
985
  // src/filesystemFinalizedStatementPublicationJournal.ts
522
986
  import { createHash as createHash2 } from "node:crypto";
523
- import PATH8 from "node:path";
987
+ import PATH9 from "node:path";
524
988
  import { isDeepStrictEqual } from "node:util";
525
989
  var PublicationPhases = [
526
990
  "signed",
@@ -566,7 +1030,7 @@ function publicationDirectory(root, intent) {
566
1030
  operationId: intent.operationId,
567
1031
  source: intent.source
568
1032
  })).digest("hex");
569
- return PATH8.join(root, key);
1033
+ return PATH9.join(root, key);
570
1034
  }
571
1035
  function asPublicationRecord(value, path) {
572
1036
  if (typeof value !== "object" || value === null) throw new Error(`malformed publication journal record at ${path}`);
@@ -581,12 +1045,12 @@ function assertBoundRecord(record, intent, signed) {
581
1045
  }
582
1046
  }
583
1047
  function createFilesystemFinalizedStatementPublicationJournal(options) {
584
- const root = PATH8.resolve(options.path);
1048
+ const root = PATH9.resolve(options.path);
585
1049
  return {
586
1050
  async load(intent) {
587
1051
  assertIntentShape(intent);
588
1052
  const directory = publicationDirectory(root, intent);
589
- const intentPath = PATH8.join(directory, "intent.json");
1053
+ const intentPath = PATH9.join(directory, "intent.json");
590
1054
  const rawSigned = await readJsonFileStrict(intentPath);
591
1055
  if (rawSigned === void 0) return void 0;
592
1056
  const signed = asPublicationRecord(rawSigned, intentPath);
@@ -594,7 +1058,7 @@ function createFilesystemFinalizedStatementPublicationJournal(options) {
594
1058
  assertBoundRecord(signed, intent, signed);
595
1059
  let latest = signed;
596
1060
  for (const phase of PublicationPhases.slice(1)) {
597
- const path = PATH8.join(directory, `${phase}.json`);
1061
+ const path = PATH9.join(directory, `${phase}.json`);
598
1062
  const rawRecord = await readJsonFileStrict(path);
599
1063
  if (rawRecord === void 0) continue;
600
1064
  const record = asPublicationRecord(rawRecord, path);
@@ -612,7 +1076,7 @@ function createFilesystemFinalizedStatementPublicationJournal(options) {
612
1076
  }
613
1077
  if (!PublicationPhaseSet.has(record.phase)) throw new TypeError("unknown publication phase");
614
1078
  const directory = publicationDirectory(root, intent);
615
- const intentPath = PATH8.join(directory, "intent.json");
1079
+ const intentPath = PATH9.join(directory, "intent.json");
616
1080
  if (record.phase === "signed") {
617
1081
  if (await publishJsonFile(intentPath, record)) return;
618
1082
  const existing2 = asPublicationRecord(await readJsonFileStrict(intentPath), intentPath);
@@ -627,7 +1091,7 @@ function createFilesystemFinalizedStatementPublicationJournal(options) {
627
1091
  const signed = asPublicationRecord(rawSigned, intentPath);
628
1092
  if (signed.phase !== "signed") throw new Error(`publication intent record is not signed at ${intentPath}`);
629
1093
  assertBoundRecord(record, intent, signed);
630
- const path = PATH8.join(directory, `${record.phase}.json`);
1094
+ const path = PATH9.join(directory, `${record.phase}.json`);
631
1095
  if (await publishJsonFile(path, record)) return;
632
1096
  const existing = asPublicationRecord(await readJsonFileStrict(path), path);
633
1097
  if (!isDeepStrictEqual(existing, record)) {
@@ -639,7 +1103,7 @@ function createFilesystemFinalizedStatementPublicationJournal(options) {
639
1103
 
640
1104
  // src/filesystemHandshakeMutationIntentStore.ts
641
1105
  import { stat } from "node:fs/promises";
642
- import PATH9 from "node:path";
1106
+ import PATH10 from "node:path";
643
1107
 
644
1108
  // src/handshakeMutationIntent.ts
645
1109
  import {
@@ -653,8 +1117,8 @@ import {
653
1117
  EndpointChallengeResponseZod,
654
1118
  EndpointChallengeZod,
655
1119
  HandshakeIdZod,
656
- HashZod,
657
- NormalizedAddressZod,
1120
+ HashZod as HashZod3,
1121
+ NormalizedAddressZod as NormalizedAddressZod2,
658
1122
  PublisherEncryptionKeyRevocationZod,
659
1123
  PublisherEncryptionKeyZod,
660
1124
  PublisherServiceRevocationZod,
@@ -664,7 +1128,7 @@ import {
664
1128
  WakeSubscriptionAcceptanceZod,
665
1129
  WakeSubscriptionRevocationZod
666
1130
  } from "@xyo-network/event-kit-schemas";
667
- import { z } from "zod";
1131
+ import { z as z2 } from "zod";
668
1132
  var HandshakeMutationIntentKinds = [
669
1133
  "publisher-encryption-key-retire",
670
1134
  "publisher-service-publish",
@@ -675,83 +1139,83 @@ var HandshakeMutationIntentKinds = [
675
1139
  "subscriber-subscription-revoke",
676
1140
  "subscriber-subscription-subscribe"
677
1141
  ];
678
- var HandshakeMutationIntentKindZod = z.enum(HandshakeMutationIntentKinds);
679
- var HandshakeMutationOperationIdZod = z.union([HandshakeIdZod, HashZod]);
680
- var WakeSubscriptionPrivateKeyZod = z.strictObject({
681
- encryptionKey: HashZod,
1142
+ var HandshakeMutationIntentKindZod = z2.enum(HandshakeMutationIntentKinds);
1143
+ var HandshakeMutationOperationIdZod = z2.union([HandshakeIdZod, HashZod3]);
1144
+ var WakeSubscriptionPrivateKeyZod = z2.strictObject({
1145
+ encryptionKey: HashZod3,
682
1146
  keyId: HandshakeIdZod,
683
1147
  privateKey: Base64Url32BytesZod
684
1148
  });
685
1149
  var IntentBaseFields = {
686
1150
  chainId: ChainIdZod,
687
1151
  operationId: HandshakeMutationOperationIdZod,
688
- publisher: NormalizedAddressZod,
689
- requestFingerprint: HashZod,
690
- source: NormalizedAddressZod,
691
- version: z.literal(1)
1152
+ publisher: NormalizedAddressZod2,
1153
+ requestFingerprint: HashZod3,
1154
+ source: NormalizedAddressZod2,
1155
+ version: z2.literal(1)
692
1156
  };
693
- var PublisherServicePublishIntentZod = z.strictObject({
1157
+ var PublisherServicePublishIntentZod = z2.strictObject({
694
1158
  ...IntentBaseFields,
695
- kind: z.literal("publisher-service-publish"),
1159
+ kind: z2.literal("publisher-service-publish"),
696
1160
  operationId: HandshakeIdZod,
697
1161
  encryptionKey: PublisherEncryptionKeyZod,
698
1162
  privateKey: WakeSubscriptionPrivateKeyZod,
699
1163
  service: PublisherServiceZod
700
1164
  });
701
- var PublisherServiceRotateKeyIntentZod = z.strictObject({
1165
+ var PublisherServiceRotateKeyIntentZod = z2.strictObject({
702
1166
  ...IntentBaseFields,
703
- kind: z.literal("publisher-service-rotate-key"),
1167
+ kind: z2.literal("publisher-service-rotate-key"),
704
1168
  operationId: HandshakeIdZod,
705
1169
  encryptionKey: PublisherEncryptionKeyZod,
706
1170
  privateKey: WakeSubscriptionPrivateKeyZod,
707
- selectedKey: HashZod,
708
- selectedService: HashZod,
1171
+ selectedKey: HashZod3,
1172
+ selectedService: HashZod3,
709
1173
  service: PublisherServiceZod
710
1174
  });
711
- var SubscriberSubscriptionIntentZod = z.strictObject({
1175
+ var SubscriberSubscriptionIntentZod = z2.strictObject({
712
1176
  ...IntentBaseFields,
713
- kind: z.literal("subscriber-subscription-subscribe"),
1177
+ kind: z2.literal("subscriber-subscription-subscribe"),
714
1178
  operationId: HandshakeIdZod,
715
1179
  encryptionKey: PublisherEncryptionKeyZod,
716
1180
  grant: WakeGrantObjectZod,
717
- selectedKey: HashZod,
718
- selectedService: HashZod,
1181
+ selectedKey: HashZod3,
1182
+ selectedService: HashZod3,
719
1183
  service: PublisherServiceZod,
720
1184
  subscription: EncryptedWakeSubscriptionZod
721
1185
  });
722
- var PublisherAcceptanceIntentZod = z.strictObject({
1186
+ var PublisherAcceptanceIntentZod = z2.strictObject({
723
1187
  ...IntentBaseFields,
724
1188
  acceptance: WakeSubscriptionAcceptanceZod,
725
1189
  challenge: EndpointChallengeZod,
726
- kind: z.literal("publisher-subscription-accept"),
727
- operationId: HashZod,
1190
+ kind: z2.literal("publisher-subscription-accept"),
1191
+ operationId: HashZod3,
728
1192
  response: EndpointChallengeResponseZod
729
1193
  });
730
- var PublisherServiceRetireIntentZod = z.strictObject({
1194
+ var PublisherServiceRetireIntentZod = z2.strictObject({
731
1195
  ...IntentBaseFields,
732
- kind: z.literal("publisher-service-retire"),
733
- operationId: HashZod,
1196
+ kind: z2.literal("publisher-service-retire"),
1197
+ operationId: HashZod3,
734
1198
  revocation: PublisherServiceRevocationZod
735
1199
  });
736
- var PublisherEncryptionKeyRetireIntentZod = z.strictObject({
1200
+ var PublisherEncryptionKeyRetireIntentZod = z2.strictObject({
737
1201
  ...IntentBaseFields,
738
- kind: z.literal("publisher-encryption-key-retire"),
739
- operationId: HashZod,
1202
+ kind: z2.literal("publisher-encryption-key-retire"),
1203
+ operationId: HashZod3,
740
1204
  revocation: PublisherEncryptionKeyRevocationZod
741
1205
  });
742
- var SubscriberGrantRevokeIntentZod = z.strictObject({
1206
+ var SubscriberGrantRevokeIntentZod = z2.strictObject({
743
1207
  ...IntentBaseFields,
744
- kind: z.literal("subscriber-grant-revoke"),
745
- operationId: HashZod,
1208
+ kind: z2.literal("subscriber-grant-revoke"),
1209
+ operationId: HashZod3,
746
1210
  revocation: WakeGrantRevocationZod
747
1211
  });
748
- var SubscriberSubscriptionRevokeIntentZod = z.strictObject({
1212
+ var SubscriberSubscriptionRevokeIntentZod = z2.strictObject({
749
1213
  ...IntentBaseFields,
750
- kind: z.literal("subscriber-subscription-revoke"),
751
- operationId: HashZod,
1214
+ kind: z2.literal("subscriber-subscription-revoke"),
1215
+ operationId: HashZod3,
752
1216
  revocation: WakeSubscriptionRevocationZod
753
1217
  });
754
- var HandshakeMutationIntentZod = z.discriminatedUnion("kind", [
1218
+ var HandshakeMutationIntentZod = z2.discriminatedUnion("kind", [
755
1219
  PublisherEncryptionKeyRetireIntentZod,
756
1220
  PublisherServicePublishIntentZod,
757
1221
  PublisherServiceRetireIntentZod,
@@ -771,17 +1235,17 @@ async function handshakeMutationRequestFingerprint(value) {
771
1235
  const canonical = canonicalJsonBytes(value);
772
1236
  const digestInput = new Uint8Array(canonical.byteLength);
773
1237
  digestInput.set(canonical);
774
- const digest2 = await globalThis.crypto.subtle.digest("SHA-256", digestInput);
775
- const hex = Array.from(new Uint8Array(digest2), (byte) => byte.toString(16).padStart(2, "0")).join("");
776
- return HashZod.parse(hex);
1238
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", digestInput);
1239
+ const hex = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
1240
+ return HashZod3.parse(hex);
777
1241
  }
778
1242
  function parseHandshakeMutationIntentLookup(value) {
779
1243
  return Object.freeze({
780
1244
  chainId: ChainIdZod.parse(value.chainId),
781
1245
  kind: HandshakeMutationIntentKindZod.parse(value.kind),
782
1246
  operationId: HandshakeMutationOperationIdZod.parse(value.operationId),
783
- publisher: NormalizedAddressZod.parse(value.publisher),
784
- source: NormalizedAddressZod.parse(value.source)
1247
+ publisher: NormalizedAddressZod2.parse(value.publisher),
1248
+ source: NormalizedAddressZod2.parse(value.source)
785
1249
  });
786
1250
  }
787
1251
  async function parseHandshakeMutationIntent(value) {
@@ -879,10 +1343,10 @@ async function assertRevocationIntent(intent) {
879
1343
 
880
1344
  // src/filesystemHandshakeMutationIntentStore.ts
881
1345
  function createFilesystemHandshakeMutationIntentStore(options) {
882
- const root = PATH9.resolve(options.path);
1346
+ const root = PATH10.resolve(options.path);
883
1347
  function pathFor(rawLookup) {
884
1348
  const lookup2 = parseHandshakeMutationIntentLookup(rawLookup);
885
- return PATH9.join(
1349
+ return PATH10.join(
886
1350
  root,
887
1351
  lookup2.chainId,
888
1352
  lookup2.publisher,
@@ -923,29 +1387,29 @@ function createFilesystemHandshakeMutationIntentStore(options) {
923
1387
 
924
1388
  // src/filesystemPublisherEncryptionKeyStore.ts
925
1389
  import { stat as stat2 } from "node:fs/promises";
926
- import PATH10 from "node:path";
1390
+ import PATH11 from "node:path";
927
1391
  import {
928
1392
  Base64Url32BytesZod as Base64Url32BytesZod2,
929
1393
  HandshakeIdZod as HandshakeIdZod2,
930
- HashZod as HashZod2,
931
- NormalizedAddressZod as NormalizedAddressZod2
1394
+ HashZod as HashZod4,
1395
+ NormalizedAddressZod as NormalizedAddressZod3
932
1396
  } from "@xyo-network/event-kit-schemas";
933
1397
  function createFilesystemPublisherEncryptionKeyStore(options) {
934
- const root = PATH10.resolve(options.path);
1398
+ const root = PATH11.resolve(options.path);
935
1399
  function parseLookup(lookup2) {
936
1400
  return {
937
- encryptionKey: HashZod2.parse(lookup2.encryptionKey),
1401
+ encryptionKey: HashZod4.parse(lookup2.encryptionKey),
938
1402
  keyId: HandshakeIdZod2.parse(lookup2.keyId),
939
- publisher: NormalizedAddressZod2.parse(lookup2.publisher)
1403
+ publisher: NormalizedAddressZod3.parse(lookup2.publisher)
940
1404
  };
941
1405
  }
942
1406
  function pathFor(lookup2) {
943
- return PATH10.join(root, lookup2.publisher, `${lookup2.encryptionKey}-${lookup2.keyId}.json`);
1407
+ return PATH11.join(root, lookup2.publisher, `${lookup2.encryptionKey}-${lookup2.keyId}.json`);
944
1408
  }
945
1409
  function keyIdPath(lookup2) {
946
- return PATH10.join(root, lookup2.publisher, "key-ids", `${lookup2.keyId}.json`);
1410
+ return PATH11.join(root, lookup2.publisher, "key-ids", `${lookup2.keyId}.json`);
947
1411
  }
948
- async function readRecord2(lookup2) {
1412
+ async function readRecord(lookup2) {
949
1413
  const path = pathFor(lookup2);
950
1414
  const raw = await readJsonFileStrict(path);
951
1415
  if (raw === void 0) return void 0;
@@ -960,7 +1424,7 @@ function createFilesystemPublisherEncryptionKeyStore(options) {
960
1424
  return {
961
1425
  async getPrivateKey(rawLookup) {
962
1426
  const lookup2 = parseLookup(rawLookup);
963
- const record = await readRecord2(lookup2);
1427
+ const record = await readRecord(lookup2);
964
1428
  if (record === void 0) return void 0;
965
1429
  return Object.freeze({
966
1430
  encryptionKey: record.encryptionKey,
@@ -988,7 +1452,7 @@ function createFilesystemPublisherEncryptionKeyStore(options) {
988
1452
  }
989
1453
  }
990
1454
  if (await publishJsonFile(path, record, { directoryMode: 448, mode: 384 })) return "stored";
991
- const existing = await readRecord2(lookup2);
1455
+ const existing = await readRecord(lookup2);
992
1456
  if (stringifyJson(existing) !== stringifyJson(record)) {
993
1457
  throw new Error(`publisher encryption-key collision for ${record.encryptionKey}/${record.keyId}`);
994
1458
  }
@@ -1004,9 +1468,9 @@ function parseKeyIdRecord(value) {
1004
1468
  throw new TypeError("invalid publisher encryption-key keyId record shape");
1005
1469
  }
1006
1470
  return {
1007
- encryptionKey: HashZod2.parse(record.encryptionKey),
1471
+ encryptionKey: HashZod4.parse(record.encryptionKey),
1008
1472
  keyId: HandshakeIdZod2.parse(record.keyId),
1009
- publisher: NormalizedAddressZod2.parse(record.publisher)
1473
+ publisher: NormalizedAddressZod3.parse(record.publisher)
1010
1474
  };
1011
1475
  }
1012
1476
  function parsePrivateKeyRecord(value) {
@@ -1017,28 +1481,28 @@ function parsePrivateKeyRecord(value) {
1017
1481
  throw new TypeError("invalid publisher encryption-key record shape");
1018
1482
  }
1019
1483
  return {
1020
- encryptionKey: HashZod2.parse(record.encryptionKey),
1484
+ encryptionKey: HashZod4.parse(record.encryptionKey),
1021
1485
  keyId: HandshakeIdZod2.parse(record.keyId),
1022
1486
  privateKey: Base64Url32BytesZod2.parse(record.privateKey),
1023
- publisher: NormalizedAddressZod2.parse(record.publisher)
1487
+ publisher: NormalizedAddressZod3.parse(record.publisher)
1024
1488
  };
1025
1489
  }
1026
1490
 
1027
1491
  // src/filesystemPublisherEndpointChallengeStore.ts
1028
- import PATH11 from "node:path";
1492
+ import PATH12 from "node:path";
1029
1493
  import { endpointChallengeHash } from "@xyo-network/event-kit-protocol";
1030
1494
  import {
1031
1495
  ChallengeNonceZod,
1032
1496
  EndpointChallengeZod as EndpointChallengeZod2,
1033
- HashZod as HashZod3
1497
+ HashZod as HashZod5
1034
1498
  } from "@xyo-network/event-kit-schemas";
1035
1499
  function createFilesystemPublisherEndpointChallengeStore(options) {
1036
- const root = PATH11.resolve(options.path);
1500
+ const root = PATH12.resolve(options.path);
1037
1501
  function statePath(challengeHash, state) {
1038
- return PATH11.join(root, challengeHash, `${state}.json`);
1502
+ return PATH12.join(root, challengeHash, `${state}.json`);
1039
1503
  }
1040
1504
  function noncePath(nonce) {
1041
- return PATH11.join(root, "nonces", `${nonce}.json`);
1505
+ return PATH12.join(root, "nonces", `${nonce}.json`);
1042
1506
  }
1043
1507
  async function readState(challengeHash, state) {
1044
1508
  const raw = await readJsonFileStrict(statePath(challengeHash, state));
@@ -1059,7 +1523,7 @@ function createFilesystemPublisherEndpointChallengeStore(options) {
1059
1523
  }
1060
1524
  return {
1061
1525
  async beginAttempt(input) {
1062
- const challengeHash = HashZod3.parse(input.challengeHash);
1526
+ const challengeHash = HashZod5.parse(input.challengeHash);
1063
1527
  requireTime(input.nowSeconds);
1064
1528
  const observed = await current(challengeHash);
1065
1529
  if (observed === void 0) return { kind: "missing" };
@@ -1073,8 +1537,8 @@ function createFilesystemPublisherEndpointChallengeStore(options) {
1073
1537
  return { kind: winner.state === "verified" ? "verified" : "already-attempted", record: winner };
1074
1538
  },
1075
1539
  async completeVerified(input) {
1076
- const challengeHash = HashZod3.parse(input.challengeHash);
1077
- const responseHash = HashZod3.parse(input.responseHash);
1540
+ const challengeHash = HashZod5.parse(input.challengeHash);
1541
+ const responseHash = HashZod5.parse(input.responseHash);
1078
1542
  requireTime(input.nowSeconds);
1079
1543
  const observed = await current(challengeHash);
1080
1544
  if (observed === void 0) return { kind: "missing" };
@@ -1093,10 +1557,10 @@ function createFilesystemPublisherEndpointChallengeStore(options) {
1093
1557
  if (winner === void 0) throw new Error("verified challenge winner vanished");
1094
1558
  return { kind: winner.responseHash === responseHash ? "duplicate" : "collision", record: winner };
1095
1559
  },
1096
- get: async (rawHash) => await current(HashZod3.parse(rawHash)),
1560
+ get: async (rawHash) => await current(HashZod5.parse(rawHash)),
1097
1561
  async issue(input) {
1098
1562
  const challenge = EndpointChallengeZod2.parse(input.challenge);
1099
- const challengeHash = HashZod3.parse(input.challengeHash);
1563
+ const challengeHash = HashZod5.parse(input.challengeHash);
1100
1564
  if (await endpointChallengeHash(challenge) !== challengeHash) return { kind: "collision" };
1101
1565
  const nonceRecord = { challengeHash, nonce: challenge.nonce };
1102
1566
  if (!await publishJsonFile(
@@ -1127,7 +1591,7 @@ function parseNonceRecord(value) {
1127
1591
  const keys = Object.keys(record).toSorted();
1128
1592
  if (keys.join("|") !== "challengeHash|nonce") throw new TypeError("invalid endpoint-challenge nonce record shape");
1129
1593
  return {
1130
- challengeHash: HashZod3.parse(record.challengeHash),
1594
+ challengeHash: HashZod5.parse(record.challengeHash),
1131
1595
  nonce: ChallengeNonceZod.parse(record.nonce)
1132
1596
  };
1133
1597
  }
@@ -1137,13 +1601,13 @@ async function parseChallengeRecord(value, expectedState) {
1137
1601
  const state = record.state;
1138
1602
  if (state !== expectedState) throw new Error("endpoint-challenge record state does not match its path");
1139
1603
  const challenge = EndpointChallengeZod2.parse(record.challenge);
1140
- const challengeHash = HashZod3.parse(record.challengeHash);
1604
+ const challengeHash = HashZod5.parse(record.challengeHash);
1141
1605
  if (await endpointChallengeHash(challenge) !== challengeHash) throw new Error("endpoint-challenge record hash mismatch");
1142
1606
  if (expectedState === "verified") {
1143
1607
  return {
1144
1608
  challenge,
1145
1609
  challengeHash,
1146
- responseHash: HashZod3.parse(record.responseHash),
1610
+ responseHash: HashZod5.parse(record.responseHash),
1147
1611
  state: expectedState
1148
1612
  };
1149
1613
  }
@@ -1167,7 +1631,7 @@ function requireTime(value) {
1167
1631
  // src/handshakeClients.ts
1168
1632
  import {
1169
1633
  beginPublisherEndpointChallengeAttempt,
1170
- canonicalJson,
1634
+ canonicalJson as canonicalJson2,
1171
1635
  completePublisherEndpointChallenge,
1172
1636
  createEncryptedWakeSubscription,
1173
1637
  createPublisherEncryptionKeyRevocation,
@@ -1187,9 +1651,9 @@ import {
1187
1651
  import {
1188
1652
  ChainIdZod as ChainIdZod2,
1189
1653
  HandshakeIdZod as HandshakeIdZod3,
1190
- HashZod as HashZod4,
1654
+ HashZod as HashZod6,
1191
1655
  normalizeAddress,
1192
- NormalizedAddressZod as NormalizedAddressZod3
1656
+ NormalizedAddressZod as NormalizedAddressZod4
1193
1657
  } from "@xyo-network/event-kit-schemas";
1194
1658
  import { createEventKitHandshakeReadModel } from "@xyo-network/event-kit-statement-graph";
1195
1659
  import {
@@ -1211,7 +1675,7 @@ var EventKitHandshakeClientError = class extends Error {
1211
1675
  }
1212
1676
  };
1213
1677
  function createSynchronizedEventKitHandshakeReader(options) {
1214
- const publisher = NormalizedAddressZod3.parse(options.publisher);
1678
+ const publisher = NormalizedAddressZod4.parse(options.publisher);
1215
1679
  const chainId = ChainIdZod2.parse(options.session.chainId);
1216
1680
  function assertIdentity() {
1217
1681
  const checkpoint = options.replay.snapshot();
@@ -1253,7 +1717,7 @@ function createEventKitHandshakePublicationPort(transport) {
1253
1717
  publishWakeSubscriptionRevocation: async (input) => await publishWakeSubscriptionRevocation({ ...transport, ...input }),
1254
1718
  async source() {
1255
1719
  const raw = String(await transport.signer.address());
1256
- const normalized = NormalizedAddressZod3.parse(normalizeAddress(raw));
1720
+ const normalized = NormalizedAddressZod4.parse(normalizeAddress(raw));
1257
1721
  if (raw !== normalized) {
1258
1722
  throw new EventKitHandshakeClientError("identity-mismatch", "XL1 publication signer address is not canonical lowercase hexadecimal without 0x");
1259
1723
  }
@@ -1299,7 +1763,7 @@ function requireService(discovery) {
1299
1763
  return discovery;
1300
1764
  }
1301
1765
  function requireExpectedService(discovery, expected) {
1302
- const expectedHash = HashZod4.parse(expected);
1766
+ const expectedHash = HashZod6.parse(expected);
1303
1767
  if (discovery.service.objectHash !== expectedHash) {
1304
1768
  throw new EventKitHandshakeClientError("service-mismatch", "Currently selected publisher service does not match the expected object hash");
1305
1769
  }
@@ -1364,7 +1828,7 @@ async function publisherRotationRequestFingerprint(input) {
1364
1828
  keyExpiresAtBlock: input.keyExpiresAtBlock ?? null,
1365
1829
  maxPositionsPerWake: input.maxPositionsPerWake ?? null,
1366
1830
  maxSubscriptionLifetimeBlocks: input.maxSubscriptionLifetimeBlocks ?? null,
1367
- service: HashZod4.parse(input.service),
1831
+ service: HashZod6.parse(input.service),
1368
1832
  validFromBlock: input.validFromBlock ?? null
1369
1833
  });
1370
1834
  }
@@ -1396,28 +1860,28 @@ function protocolCandidate(reader, discovery, candidate, keys) {
1396
1860
  finalizedHead: discovery.finalizedHead,
1397
1861
  grant: {
1398
1862
  body: candidate.tuple.grant.body,
1399
- objectHash: HashZod4.parse(candidate.tuple.grant.objectHash)
1863
+ objectHash: HashZod6.parse(candidate.tuple.grant.objectHash)
1400
1864
  },
1401
1865
  key: {
1402
1866
  body: candidate.key.body,
1403
- objectHash: HashZod4.parse(candidate.key.objectHash)
1867
+ objectHash: HashZod6.parse(candidate.key.objectHash)
1404
1868
  },
1405
1869
  keys,
1406
1870
  publisher: reader.publisher,
1407
1871
  service: {
1408
1872
  body: candidate.tuple.service.body,
1409
- objectHash: HashZod4.parse(candidate.tuple.service.objectHash)
1873
+ objectHash: HashZod6.parse(candidate.tuple.service.objectHash)
1410
1874
  },
1411
- subscriber: NormalizedAddressZod3.parse(candidate.subscriber),
1875
+ subscriber: NormalizedAddressZod4.parse(candidate.subscriber),
1412
1876
  subscription: {
1413
1877
  body: candidate.tuple.subscription.body,
1414
- objectHash: HashZod4.parse(candidate.tuple.subscription.objectHash)
1878
+ objectHash: HashZod6.parse(candidate.tuple.subscription.objectHash)
1415
1879
  },
1416
1880
  tupleBlock: candidate.tuple.tupleEvent.block,
1417
1881
  tupleObjectHashes: [
1418
- HashZod4.parse(candidate.tuple.service.objectHash),
1419
- HashZod4.parse(candidate.tuple.grant.objectHash),
1420
- HashZod4.parse(candidate.tuple.subscription.objectHash)
1882
+ HashZod6.parse(candidate.tuple.service.objectHash),
1883
+ HashZod6.parse(candidate.tuple.grant.objectHash),
1884
+ HashZod6.parse(candidate.tuple.subscription.objectHash)
1421
1885
  ],
1422
1886
  tupleSubject: reader.publisher
1423
1887
  };
@@ -1442,7 +1906,7 @@ function createPublisherHandshakeClient(options) {
1442
1906
  const current = await snapshot();
1443
1907
  return await current.readModel.acceptanceCandidates({
1444
1908
  ...serviceQuery(options.reader, current, serviceId),
1445
- ...selector.subscriber === void 0 ? {} : { subscriber: NormalizedAddressZod3.parse(selector.subscriber) },
1909
+ ...selector.subscriber === void 0 ? {} : { subscriber: NormalizedAddressZod4.parse(selector.subscriber) },
1446
1910
  ...selector.subscriptionId === void 0 ? {} : { subscriptionId: HandshakeIdZod3.parse(selector.subscriptionId) }
1447
1911
  });
1448
1912
  }
@@ -1450,8 +1914,8 @@ function createPublisherHandshakeClient(options) {
1450
1914
  const found = await candidateDiscovery(selector);
1451
1915
  if (!found.ok)
1452
1916
  throw new EventKitHandshakeClientError("service-unavailable", `Publisher service is unavailable: ${found.reason}`);
1453
- const subscriber = NormalizedAddressZod3.parse(selector.subscriber);
1454
- const subscription = selector.subscription === void 0 ? void 0 : HashZod4.parse(selector.subscription);
1917
+ const subscriber = NormalizedAddressZod4.parse(selector.subscriber);
1918
+ const subscription = selector.subscription === void 0 ? void 0 : HashZod6.parse(selector.subscription);
1455
1919
  const candidates = found.candidates.filter((candidate2) => candidate2.subscriber === subscriber && (subscription === void 0 || candidate2.tuple.subscription.objectHash === subscription));
1456
1920
  if (candidates.length === 0) {
1457
1921
  throw new EventKitHandshakeClientError("candidate-not-found", "No exact finalized acceptance candidate was found");
@@ -1564,7 +2028,7 @@ function createPublisherHandshakeClient(options) {
1564
2028
  const reconciliation = await reconcileAcceptance(acceptanceHash, publication, intent.challenge.subscriber, intent.challenge.subscription);
1565
2029
  return Object.freeze({
1566
2030
  acceptance: Object.freeze({ body: intent.acceptance, objectHash: acceptanceHash }),
1567
- challenge: intent.operationId,
2031
+ challenge: intent.acceptance.challenge,
1568
2032
  intentDisposition,
1569
2033
  operationId: intent.operationId,
1570
2034
  publication,
@@ -1607,14 +2071,14 @@ function createPublisherHandshakeClient(options) {
1607
2071
  const found = await candidateDiscovery(selector);
1608
2072
  if (!found.ok || selector.subscription === void 0)
1609
2073
  return found;
1610
- const subscription = HashZod4.parse(selector.subscription);
2074
+ const subscription = HashZod6.parse(selector.subscription);
1611
2075
  return Object.freeze({
1612
2076
  ...found,
1613
2077
  candidates: found.candidates.filter((candidate) => candidate.tuple.subscription.objectHash === subscription)
1614
2078
  });
1615
2079
  },
1616
2080
  async beginChallenge(input) {
1617
- const challengeHash = HashZod4.parse(input.challengeHash);
2081
+ const challengeHash = HashZod6.parse(input.challengeHash);
1618
2082
  const record = await options.challenges.get(challengeHash);
1619
2083
  if (record === void 0)
1620
2084
  return { kind: "missing" };
@@ -1644,9 +2108,9 @@ function createPublisherHandshakeClient(options) {
1644
2108
  },
1645
2109
  async completeChallengeAndAccept(input) {
1646
2110
  await assertPublicationSource(options.publication, options.reader.publisher);
1647
- const challengeHash = HashZod4.parse(input.challengeHash);
1648
- const subscriber = NormalizedAddressZod3.parse(input.subscriber);
1649
- const subscription = HashZod4.parse(input.subscription);
2111
+ const challengeHash = HashZod6.parse(input.challengeHash);
2112
+ const subscriber = NormalizedAddressZod4.parse(input.subscriber);
2113
+ const subscription = HashZod6.parse(input.subscription);
1650
2114
  const record = await options.challenges.get(challengeHash);
1651
2115
  if (record === void 0) {
1652
2116
  throw new EventKitHandshakeClientError("challenge-not-found", "Endpoint challenge was not found");
@@ -1781,7 +2245,7 @@ function createPublisherHandshakeClient(options) {
1781
2245
  },
1782
2246
  async retireEncryptionKey(key) {
1783
2247
  await assertPublicationSource(options.publication, options.reader.publisher);
1784
- const keyHash = HashZod4.parse(key);
2248
+ const keyHash = HashZod6.parse(key);
1785
2249
  const requestFingerprint = await exactMutationRequestFingerprint({ key: keyHash });
1786
2250
  let resolvedIntent = await loadMutationIntent(options.intents, options.reader, "publisher-encryption-key-retire", keyHash, options.reader.publisher, requestFingerprint);
1787
2251
  let intentDisposition = "resumed";
@@ -1819,7 +2283,7 @@ function createPublisherHandshakeClient(options) {
1819
2283
  },
1820
2284
  async retireService(expectedService) {
1821
2285
  await assertPublicationSource(options.publication, options.reader.publisher);
1822
- const service = HashZod4.parse(expectedService);
2286
+ const service = HashZod6.parse(expectedService);
1823
2287
  const requestFingerprint = await exactMutationRequestFingerprint({ service });
1824
2288
  let resolvedIntent = await loadMutationIntent(options.intents, options.reader, "publisher-service-retire", service, options.reader.publisher, requestFingerprint);
1825
2289
  let intentDisposition = "resumed";
@@ -1875,7 +2339,7 @@ function createPublisherHandshakeClient(options) {
1875
2339
  preferredEncryptionKey: generated.descriptorHash,
1876
2340
  revision: selected.service.body.revision + 1,
1877
2341
  serviceId: selected.service.body.serviceId,
1878
- supersedes: HashZod4.parse(selected.service.objectHash),
2342
+ supersedes: HashZod6.parse(selected.service.objectHash),
1879
2343
  validFromBlock
1880
2344
  };
1881
2345
  const service = createPublisherService({
@@ -1894,8 +2358,8 @@ function createPublisherHandshakeClient(options) {
1894
2358
  privateKey: generated.privateKey,
1895
2359
  publisher: options.reader.publisher,
1896
2360
  requestFingerprint,
1897
- selectedKey: HashZod4.parse(selected.key.objectHash),
1898
- selectedService: HashZod4.parse(selected.service.objectHash),
2361
+ selectedKey: HashZod6.parse(selected.key.objectHash),
2362
+ selectedService: HashZod6.parse(selected.service.objectHash),
1899
2363
  service,
1900
2364
  source: options.reader.publisher,
1901
2365
  version: 1
@@ -1922,7 +2386,7 @@ function tupleHashes(view) {
1922
2386
  }
1923
2387
  function createSubscriberHandshakeClient(options) {
1924
2388
  assertPublicationIdentity(options.publication, options.reader);
1925
- const subscriber = NormalizedAddressZod3.parse(options.subscriber);
2389
+ const subscriber = NormalizedAddressZod4.parse(options.subscriber);
1926
2390
  const serviceId = options.serviceId === void 0 ? void 0 : HandshakeIdZod3.parse(options.serviceId);
1927
2391
  async function snapshot() {
1928
2392
  return readySnapshot(await options.reader.sync());
@@ -2002,7 +2466,7 @@ function createSubscriberHandshakeClient(options) {
2002
2466
  const client = {
2003
2467
  async revokeGrant(grant) {
2004
2468
  await assertPublicationSource(options.publication, subscriber);
2005
- const grantHash = HashZod4.parse(grant);
2469
+ const grantHash = HashZod6.parse(grant);
2006
2470
  const requestFingerprint = await exactMutationRequestFingerprint({ grant: grantHash });
2007
2471
  let resolvedIntent = await loadMutationIntent(options.intents, options.reader, "subscriber-grant-revoke", grantHash, subscriber, requestFingerprint);
2008
2472
  let intentDisposition = "resumed";
@@ -2041,7 +2505,7 @@ function createSubscriberHandshakeClient(options) {
2041
2505
  },
2042
2506
  async revokeSubscription(subscription) {
2043
2507
  await assertPublicationSource(options.publication, subscriber);
2044
- const subscriptionHash = HashZod4.parse(subscription);
2508
+ const subscriptionHash = HashZod6.parse(subscription);
2045
2509
  const requestFingerprint = await exactMutationRequestFingerprint({ subscription: subscriptionHash });
2046
2510
  let resolvedIntent = await loadMutationIntent(options.intents, options.reader, "subscriber-subscription-revoke", subscriptionHash, subscriber, requestFingerprint);
2047
2511
  let intentDisposition = "resumed";
@@ -2083,7 +2547,7 @@ function createSubscriberHandshakeClient(options) {
2083
2547
  return await current.readModel.discoverPublisherService(serviceQuery(options.reader, current, serviceId));
2084
2548
  },
2085
2549
  async subscription(subscription) {
2086
- const subscriptionHash = HashZod4.parse(subscription);
2550
+ const subscriptionHash = HashZod6.parse(subscription);
2087
2551
  const current = await options.reader.sync();
2088
2552
  const view = await lookupAt(current);
2089
2553
  if (!view.ok)
@@ -2141,12 +2605,12 @@ function createSubscriberHandshakeClient(options) {
2141
2605
  grant: grantHash,
2142
2606
  publisher: options.reader.publisher,
2143
2607
  publisherKey: {
2144
- encryptionKey: HashZod4.parse(selected.key.objectHash),
2608
+ encryptionKey: HashZod6.parse(selected.key.objectHash),
2145
2609
  keyId: selected.key.body.keyId,
2146
2610
  publicKey: selected.key.body.publicKey
2147
2611
  },
2148
2612
  ...input.revision === void 0 ? {} : { revision: input.revision },
2149
- service: HashZod4.parse(selected.service.objectHash),
2613
+ service: HashZod6.parse(selected.service.objectHash),
2150
2614
  subscriber,
2151
2615
  ...input.subscriptionId === void 0 ? {} : { subscriptionId: input.subscriptionId },
2152
2616
  ...input.supersedes === void 0 ? {} : { supersedes: input.supersedes }
@@ -2160,8 +2624,8 @@ function createSubscriberHandshakeClient(options) {
2160
2624
  operationId,
2161
2625
  publisher: options.reader.publisher,
2162
2626
  requestFingerprint,
2163
- selectedKey: HashZod4.parse(selected.key.objectHash),
2164
- selectedService: HashZod4.parse(selected.service.objectHash),
2627
+ selectedKey: HashZod6.parse(selected.key.objectHash),
2628
+ selectedService: HashZod6.parse(selected.service.objectHash),
2165
2629
  service: selected.service.body,
2166
2630
  source: subscriber,
2167
2631
  subscription,
@@ -2174,7 +2638,7 @@ function createSubscriberHandshakeClient(options) {
2174
2638
  return Object.freeze(client);
2175
2639
  }
2176
2640
  function subscriberPlaintext(service, input, maxPositionsPerWake) {
2177
- if (input.deliveryMode !== service.deliveryMode || canonicalJson(input.bestEffort ?? null) !== canonicalJson(service.bestEffort ?? null))
2641
+ if (input.deliveryMode !== service.deliveryMode || canonicalJson2(input.bestEffort ?? null) !== canonicalJson2(service.bestEffort ?? null))
2178
2642
  throw new Error("Requested delivery terms do not match exact selected service");
2179
2643
  const plaintextInput = {
2180
2644
  audience: input.audience,
@@ -2184,7 +2648,7 @@ function subscriberPlaintext(service, input, maxPositionsPerWake) {
2184
2648
  queueId: input.queueId
2185
2649
  };
2186
2650
  const source = input.source;
2187
- if (service.events.every((event) => canonicalJson(event.source) !== canonicalJson(source))) {
2651
+ if (service.events.every((event) => canonicalJson2(event.source) !== canonicalJson2(source))) {
2188
2652
  throw new Error("Requested source is not advertised by the selected service");
2189
2653
  }
2190
2654
  return createWakeSubscriptionPlaintext({
@@ -2197,8 +2661,544 @@ function subscriberPlaintext(service, input, maxPositionsPerWake) {
2197
2661
  });
2198
2662
  }
2199
2663
 
2664
+ // src/receiverMigration.ts
2665
+ import { randomBytes as randomBytes5 } from "node:crypto";
2666
+ import {
2667
+ link as link3,
2668
+ lstat as lstat4,
2669
+ mkdir as mkdir3,
2670
+ mkdtemp,
2671
+ open as open5,
2672
+ realpath,
2673
+ rm,
2674
+ unlink as unlink4
2675
+ } from "node:fs/promises";
2676
+ import OS from "node:os";
2677
+ import PATH15 from "node:path";
2678
+ import {
2679
+ HashZod as HashZod8,
2680
+ NormalizedAddressZod as NormalizedAddressZod6,
2681
+ WakeIdZod as WakeIdZod3,
2682
+ WakeJwtAudienceZod as WakeJwtAudienceZod3
2683
+ } from "@xyo-network/event-kit-schemas";
2684
+
2685
+ // src/receiverMigrationInventory.ts
2686
+ import { readFile } from "node:fs/promises";
2687
+ import PATH14 from "node:path";
2688
+ import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
2689
+ import { parseAdmittedWake as parseAdmittedWake3 } from "@xyo-network/event-kit-protocol";
2690
+ import {
2691
+ HashZod as HashZod7,
2692
+ NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod4,
2693
+ NormalizedAddressZod as NormalizedAddressZod5,
2694
+ WakeIdZod as WakeIdZod2,
2695
+ WakeJwtAudienceZod as WakeJwtAudienceZod2,
2696
+ WakeJwtIdZod as WakeJwtIdZod2
2697
+ } from "@xyo-network/event-kit-schemas";
2698
+ import { z as z3 } from "zod";
2699
+
2700
+ // src/receiverMigrationSnapshot.ts
2701
+ import { createHash as createHash3 } from "node:crypto";
2702
+ import { constants as constants2 } from "node:fs";
2703
+ import {
2704
+ lstat as lstat3,
2705
+ mkdir as mkdir2,
2706
+ open as open4,
2707
+ readdir as readdir2
2708
+ } from "node:fs/promises";
2709
+ import PATH13 from "node:path";
2710
+ function migrationRelativePath(root, relative) {
2711
+ if (PATH13.isAbsolute(relative)) throw new TypeError("Migration store paths must be relative");
2712
+ const resolved = PATH13.resolve(root, relative);
2713
+ if (resolved !== root && !resolved.startsWith(`${root}${PATH13.sep}`)) throw new TypeError("Migration path escapes its source store");
2714
+ return resolved;
2715
+ }
2716
+ async function readExact(path) {
2717
+ const file = await open4(path, constants2.O_RDONLY | constants2.O_NOFOLLOW);
2718
+ try {
2719
+ const before = await file.stat();
2720
+ if (!before.isFile()) throw new TypeError("Migration source must contain ordinary files only");
2721
+ const bytes = await file.readFile();
2722
+ const after = await file.stat();
2723
+ if (before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ino !== after.ino) {
2724
+ throw new Error("Migration source changed while being copied");
2725
+ }
2726
+ return { bytes, mode: before.mode & 511 };
2727
+ } finally {
2728
+ await file.close();
2729
+ }
2730
+ }
2731
+ async function snapshotMigrationSource(root, destination) {
2732
+ const files = [];
2733
+ async function visit(relative) {
2734
+ const source = PATH13.join(root, relative);
2735
+ const info = await lstat3(source);
2736
+ if (info.isSymbolicLink()) throw new TypeError("Migration source symlinks are unsupported");
2737
+ if (info.isDirectory()) {
2738
+ if (destination !== void 0) await mkdir2(PATH13.join(destination, relative), { mode: 448 });
2739
+ for (const child of (await readdir2(source)).toSorted()) await visit(PATH13.join(relative, child));
2740
+ if (destination !== void 0) await syncDirectory(PATH13.join(destination, relative));
2741
+ return;
2742
+ }
2743
+ const { bytes, mode } = await readExact(source);
2744
+ files.push({
2745
+ bytes: bytes.length,
2746
+ mode,
2747
+ path: relative,
2748
+ sha256: createHash3("sha256").update(bytes).digest("hex")
2749
+ });
2750
+ if (destination === void 0) return;
2751
+ const target = await open4(PATH13.join(destination, relative), "wx", 384);
2752
+ try {
2753
+ await target.writeFile(bytes);
2754
+ await target.sync();
2755
+ } finally {
2756
+ await target.close();
2757
+ }
2758
+ }
2759
+ await visit("");
2760
+ return files;
2761
+ }
2762
+ async function verifyMigrationSource(root, files) {
2763
+ const current = await snapshotMigrationSource(root);
2764
+ if (JSON.stringify(current) !== JSON.stringify(files)) throw new Error("Migration source changed; stop every owner and repeat on a new copy");
2765
+ }
2766
+
2767
+ // src/receiverMigrationInventory.ts
2768
+ var ReceiptZod = z3.strictObject({
2769
+ audience: WakeJwtAudienceZod2,
2770
+ eventHash: HashZod7,
2771
+ id: HashZod7,
2772
+ publisher: NormalizedAddressZod5,
2773
+ wakeId: WakeIdZod2
2774
+ });
2775
+ var recordShape = {
2776
+ audience: WakeJwtAudienceZod2,
2777
+ eventHash: HashZod7,
2778
+ publisher: NormalizedAddressZod5,
2779
+ receipt: ReceiptZod,
2780
+ wakeId: WakeIdZod2
2781
+ };
2782
+ var WakeRecordZod = z3.strictObject(recordShape);
2783
+ var TokenRecordZod = z3.strictObject({
2784
+ ...recordShape,
2785
+ jwtId: WakeJwtIdZod2,
2786
+ retainJwtUntil: NonnegativeJsonSafeIntegerZod4
2787
+ });
2788
+ function validateMigrationReceipt(receipt) {
2789
+ const parsed = ReceiptZod.parse(receipt);
2790
+ if (parsed.id !== receiverIdentity([parsed.audience, parsed.publisher, parsed.wakeId])) throw new TypeError("Legacy receipt ID does not match its scoped identity");
2791
+ return parsed;
2792
+ }
2793
+ function validateRecord(record) {
2794
+ const receipt = validateMigrationReceipt(record.receipt);
2795
+ if (receipt.audience !== record.audience || receipt.publisher !== record.publisher || receipt.wakeId !== record.wakeId || receipt.eventHash !== record.eventHash) {
2796
+ throw new TypeError("Legacy receipt does not match its record");
2797
+ }
2798
+ }
2799
+ function scope(value) {
2800
+ return receiverIdentity([value.audience, value.publisher, value.wakeId]);
2801
+ }
2802
+ function issue(group, reason) {
2803
+ return {
2804
+ identity: group.identity,
2805
+ paths: [...group.paths],
2806
+ reason
2807
+ };
2808
+ }
2809
+ function admissionIssue(group, receipt, wake, review) {
2810
+ if (receipt?.id !== group.identity) return issue(group, "Admission receipt is missing or disagrees with reviewed identity");
2811
+ if (review?.action !== "completed" && (wake === void 0 || scope(wake) !== group.identity || wake.eventHash !== receipt.eventHash)) {
2812
+ return issue(group, "Receipt has no exact validated pending body; completion remains unknown");
2813
+ }
2814
+ if (group.tokens.some((token) => token.eventHash !== receipt.eventHash || JSON.stringify(token.receipt) !== JSON.stringify(receipt))) {
2815
+ return issue(group, "Token history conflicts with the reviewed receipt");
2816
+ }
2817
+ return review === void 0 && (group.reasons.length > 0 || group.receipts.some((record) => record.eventHash !== receipt.eventHash) || group.bodies.some((body) => body.wake.eventHash !== receipt.eventHash)) ? issue(group, "Legacy body/receipt mappings disagree; explicit review is required") : void 0;
2818
+ }
2819
+ function queueIssue(group, receipt, wake, review) {
2820
+ return group.reasons.length > 0 || wake === void 0 || receipt !== void 0 || review?.action === "completed" ? issue(group, "Standalone queue migration cannot infer receipt or completion history") : void 0;
2821
+ }
2822
+ async function selectEntry(group, review) {
2823
+ const receipt = review?.receipt === void 0 ? group.receipts[0]?.receipt : validateMigrationReceipt(review.receipt);
2824
+ const wake = review?.wake === void 0 ? group.bodies.find((body) => receipt === void 0 || body.wake.eventHash === receipt.eventHash)?.wake : await parseAdmittedWake3(review.wake);
2825
+ const invalid = group.queueOnly ? queueIssue(group, receipt, wake, review) : admissionIssue(group, receipt, wake, review);
2826
+ if (invalid !== void 0) return invalid;
2827
+ const queueOrder = review?.action === "completed" ? void 0 : selectQueueOrder(group, wake, review);
2828
+ return queueOrder !== void 0 && !("kind" in queueOrder) ? queueOrder : migrationEntry(group, receipt, wake, queueOrder, review);
2829
+ }
2830
+ function migrationEntry(group, receipt, wake, queueOrder, review) {
2831
+ return {
2832
+ evidence: review?.evidence ?? "Exact validated legacy receipt and pending/inflight body; no fresh grant inferred",
2833
+ identity: group.identity,
2834
+ paths: [...group.paths],
2835
+ ...receipt === void 0 ? {} : { receipt },
2836
+ ...queueOrder === void 0 ? {} : { queueOrder },
2837
+ state: review?.action === "completed" ? "completed" : "pending",
2838
+ tokens: group.tokens,
2839
+ ...wake === void 0 ? {} : { wake }
2840
+ };
2841
+ }
2842
+ function compareQueueOrder(left, right) {
2843
+ if (left.kind === "reviewed-after-known") return right.kind === "reviewed-after-known" ? 0 : 1;
2844
+ if (right.kind === "reviewed-after-known") return -1;
2845
+ if (left.kind === "sqlite-row-id" && right.kind === "sqlite-row-id") return left.id - right.id;
2846
+ if (left.kind === "filesystem-entry-name" && right.kind === "filesystem-entry-name") {
2847
+ return left.name < right.name ? -1 : left.name > right.name ? 1 : 0;
2848
+ }
2849
+ throw new TypeError("Migration cannot combine unrelated source queue orders");
2850
+ }
2851
+ function selectQueueOrder(group, wake, review) {
2852
+ const matches = group.bodies.filter((body) => body.wake.eventHash === wake.eventHash).toSorted((left, right) => compareQueueOrder(left.order, right.order));
2853
+ const original = matches[0]?.order;
2854
+ if (original !== void 0) return review?.queueOrder === void 0 ? original : issue(group, "Reviewed placement cannot override a matching original queue reference");
2855
+ return review?.queueOrder === void 0 ? issue(group, "Original queue order is unknown; an explicit after-known placement review is required") : { kind: "reviewed-after-known", evidence: review.queueOrder.evidence };
2856
+ }
2857
+ function compareEntries(left, right) {
2858
+ if (left.state === "completed") return right.state === "completed" ? 0 : 1;
2859
+ return right.state === "completed" ? -1 : compareQueueOrder(left.queueOrder, right.queueOrder);
2860
+ }
2861
+ function createCollector() {
2862
+ const groups = /* @__PURE__ */ new Map();
2863
+ const groupFor = (identity, path, queueOnly = false) => {
2864
+ let group = groups.get(identity);
2865
+ if (group === void 0) {
2866
+ group = {
2867
+ identity,
2868
+ paths: /* @__PURE__ */ new Set(),
2869
+ bodies: [],
2870
+ receipts: [],
2871
+ reasons: [],
2872
+ tokens: [],
2873
+ queueOnly
2874
+ };
2875
+ groups.set(identity, group);
2876
+ }
2877
+ group.paths.add(path);
2878
+ return group;
2879
+ };
2880
+ return {
2881
+ groups,
2882
+ groupFor,
2883
+ recognized: /* @__PURE__ */ new Set(),
2884
+ invalid(path, reason) {
2885
+ groupFor(`artifact:${path}`, path).reasons.push(reason);
2886
+ }
2887
+ };
2888
+ }
2889
+ function filesystemKind(queueRelative, inboxRelative) {
2890
+ if (/^(pending|inflight)\/[\w.-]+\.json$/.test(queueRelative)) return "body";
2891
+ if (/^wakes\/[a-f0-9]{64}\.json$/.test(inboxRelative)) return "receipt";
2892
+ return /^tokens\/[a-f0-9]{64}\.json$/.test(inboxRelative) ? "token" : void 0;
2893
+ }
2894
+ async function readFilesystemRecord(collector, absolute, path, kind) {
2895
+ const raw = JSON.parse(await readFile(absolute, "utf8"));
2896
+ if (kind === "body") {
2897
+ const wake = await parseAdmittedWake3(raw);
2898
+ collector.groupFor(scope(wake), path).bodies.push({
2899
+ order: {
2900
+ kind: "filesystem-entry-name",
2901
+ name: PATH14.basename(path),
2902
+ path
2903
+ },
2904
+ path,
2905
+ wake
2906
+ });
2907
+ return;
2908
+ }
2909
+ const record = kind === "receipt" ? WakeRecordZod.parse(raw) : TokenRecordZod.parse(raw);
2910
+ validateRecord(record);
2911
+ const key = kind === "receipt" ? scope(record) : receiverIdentity([record.audience, record.publisher, record.jwtId]);
2912
+ if (PATH14.basename(path) !== `${key}.json`) throw new TypeError("Legacy record filename disagrees with identity");
2913
+ const group = collector.groupFor(scope(record), path);
2914
+ if (kind === "receipt") group.receipts.push(record);
2915
+ else group.tokens.push(record);
2916
+ }
2917
+ async function scanFilesystem(source, root, files, collector) {
2918
+ const queueRoot = migrationRelativePath(root, source.queueDirectory);
2919
+ const inboxRoot = migrationRelativePath(root, source.inboxDirectory);
2920
+ for (const file of files) {
2921
+ const absolute = PATH14.join(root, file.path);
2922
+ const kind = filesystemKind(PATH14.relative(queueRoot, absolute), PATH14.relative(inboxRoot, absolute));
2923
+ if (kind === void 0) continue;
2924
+ collector.recognized.add(file.path);
2925
+ try {
2926
+ await readFilesystemRecord(collector, absolute, file.path, kind);
2927
+ } catch {
2928
+ collector.invalid(file.path, "Malformed, corrupt, or incorrectly bound legacy record; exact bytes archived");
2929
+ }
2930
+ }
2931
+ }
2932
+ async function scanSqlite(source, root, collector) {
2933
+ if (source.scope !== "queue-only") throw new TypeError("SQLite queue migration requires explicit queue-only scope");
2934
+ const file = migrationRelativePath(root, source.databaseFile);
2935
+ for (const name of [source.databaseFile, `${source.databaseFile}-wal`, `${source.databaseFile}-shm`, `${source.databaseFile}-journal`]) collector.recognized.add(PATH14.normalize(name));
2936
+ const database = new DatabaseSync2(file, { readOnly: true });
2937
+ try {
2938
+ const tables = database.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").all();
2939
+ if (tables.length !== 1 || tables[0]?.name !== "wake_queue") throw new TypeError("Expected a legacy queue-only SQLite store");
2940
+ for (const row of database.prepare("SELECT id,body,state FROM wake_queue ORDER BY id").all()) {
2941
+ const path = `${source.databaseFile}#wake_queue/${String(row.id)}`;
2942
+ try {
2943
+ if (typeof row.id !== "number" || !Number.isSafeInteger(row.id) || typeof row.body !== "string" || row.state !== "pending" && row.state !== "inflight") throw new TypeError("Invalid legacy queue row");
2944
+ const wake = await parseAdmittedWake3(JSON.parse(row.body));
2945
+ collector.groupFor(`queue:${String(row.id)}`, path, true).bodies.push({
2946
+ order: {
2947
+ kind: "sqlite-row-id",
2948
+ id: row.id,
2949
+ path
2950
+ },
2951
+ path,
2952
+ wake
2953
+ });
2954
+ } catch {
2955
+ collector.invalid(path, "Invalid legacy SQLite queue row; exact database bytes archived");
2956
+ }
2957
+ }
2958
+ } finally {
2959
+ database.close();
2960
+ }
2961
+ }
2962
+ async function inventoryReceiverMigration(source, copiedRoot, files, reviews) {
2963
+ const collector = createCollector();
2964
+ if (source.kind === "filesystem") await scanFilesystem(source, copiedRoot, files, collector);
2965
+ else await scanSqlite(source, copiedRoot, collector);
2966
+ for (const file of files) {
2967
+ if (!collector.recognized.has(file.path)) collector.invalid(file.path, "Unclassified source artifact; explicit review or quarantine is required");
2968
+ }
2969
+ return await finalizeGroups(collector.groups, reviews);
2970
+ }
2971
+ function reviewMap(groups, reviews) {
2972
+ const reviewed = /* @__PURE__ */ new Map();
2973
+ for (const review of reviews) {
2974
+ if (!["pending", "completed", "quarantine"].includes(review.action) || review.evidence.trim().length === 0 || reviewed.has(review.identity)) throw new TypeError("Reviews require unique identities, an explicit action and external evidence references");
2975
+ if (!groups.has(review.identity)) throw new TypeError("Review does not correspond to an inventoried identity");
2976
+ if (review.queueOrder !== void 0 && (review.action !== "pending" || review.queueOrder.kind !== "after-known" || review.queueOrder.evidence.trim().length === 0)) throw new TypeError("Queue placement requires a pending review, after-known choice and separate placement evidence");
2977
+ reviewed.set(review.identity, review);
2978
+ }
2979
+ return reviewed;
2980
+ }
2981
+ async function finalizeGroups(groups, reviews) {
2982
+ const reviewed = reviewMap(groups, reviews);
2983
+ const entries = [];
2984
+ const quarantined = [];
2985
+ const unknown = [];
2986
+ for (const group of groups.values()) {
2987
+ const review = reviewed.get(group.identity);
2988
+ if (review?.action === "quarantine") {
2989
+ quarantined.push(issue(group, review.evidence));
2990
+ continue;
2991
+ }
2992
+ if (group.identity.startsWith("artifact:")) {
2993
+ unknown.push(issue(group, group.reasons.join("; ")));
2994
+ continue;
2995
+ }
2996
+ const selected = await selectEntry(group, review);
2997
+ if ("reason" in selected) unknown.push(selected);
2998
+ else {
2999
+ entries.push(selected);
3000
+ const rejectedBodies = group.bodies.filter((body) => selected.receipt !== void 0 && body.wake.eventHash !== selected.receipt.eventHash);
3001
+ if (rejectedBodies.length > 0) quarantined.push({
3002
+ identity: `reviewed-conflicts:${group.identity}`,
3003
+ paths: rejectedBodies.map((body) => body.path),
3004
+ reason: "Reviewed mapping excluded conflicting bodies; exact bytes remain in the backup"
3005
+ });
3006
+ }
3007
+ }
3008
+ return {
3009
+ entries: entries.toSorted(compareEntries),
3010
+ quarantined,
3011
+ unknown
3012
+ };
3013
+ }
3014
+
3015
+ // src/receiverMigration.ts
3016
+ var rollback = "restore-backup-only-before-new-writers-resume; after-that-repair-forward; reverse-migration-unsupported";
3017
+ async function outsideSource(source, destination) {
3018
+ let parent = PATH15.dirname(PATH15.resolve(destination));
3019
+ const suffix2 = [PATH15.basename(destination)];
3020
+ let canonicalParent;
3021
+ for (; ; ) {
3022
+ try {
3023
+ canonicalParent = await realpath(parent);
3024
+ break;
3025
+ } catch (error) {
3026
+ if (typeof error !== "object" || error === null || !("code" in error) || error.code !== "ENOENT") throw error;
3027
+ suffix2.unshift(PATH15.basename(parent));
3028
+ parent = PATH15.dirname(parent);
3029
+ }
3030
+ }
3031
+ const resolved = PATH15.join(canonicalParent, ...suffix2);
3032
+ if (resolved === source || resolved.startsWith(`${source}${PATH15.sep}`)) throw new TypeError("Migration output must be outside the source store");
3033
+ return resolved;
3034
+ }
3035
+ async function writeReport(path, report) {
3036
+ const file = await open5(path, "wx", 384);
3037
+ try {
3038
+ await file.writeFile(`${JSON.stringify(report, null, 2)}
3039
+ `);
3040
+ await file.sync();
3041
+ } finally {
3042
+ await file.close();
3043
+ }
3044
+ await syncDirectory(PATH15.dirname(path));
3045
+ }
3046
+ function importEntries(database, entries) {
3047
+ database.exec("BEGIN IMMEDIATE");
3048
+ try {
3049
+ for (const entry of entries) {
3050
+ const terminalToken = entry.state === "completed" ? randomBytes5(16).toString("hex") : null;
3051
+ const result = database.prepare("INSERT INTO receiver_queue(body,state,terminal_token,terminal_operation) VALUES(?,?,?,?)").run(entry.state === "completed" ? null : JSON.stringify(entry.wake), entry.state, terminalToken, terminalToken === null ? null : "ack");
3052
+ if (entry.receipt === void 0) continue;
3053
+ const receipt = entry.receipt;
3054
+ database.prepare("INSERT INTO receiver_wakes(identity,audience,publisher,wake_id,event_hash,receipt,authorization,queue_id) VALUES(?,?,?,?,?,?,?,?)").run(
3055
+ receipt.id,
3056
+ receipt.audience,
3057
+ receipt.publisher,
3058
+ receipt.wakeId,
3059
+ receipt.eventHash,
3060
+ JSON.stringify(receipt),
3061
+ JSON.stringify({
3062
+ kind: "legacy-migration",
3063
+ evidence: entry.evidence,
3064
+ artifacts: entry.paths,
3065
+ freshAuthorization: false,
3066
+ ...terminalToken === null ? {} : { completion: { kind: "reviewed-migration-decision", terminalToken } }
3067
+ }),
3068
+ result.lastInsertRowid
3069
+ );
3070
+ for (const token of entry.tokens) {
3071
+ database.prepare("INSERT INTO receiver_tokens(identity,wake_identity,event_hash,wake_id,retain_until) VALUES(?,?,?,?,?)").run(receiverIdentity([token.audience, token.publisher, token.jwtId]), receipt.id, token.eventHash, token.wakeId, token.retainJwtUntil);
3072
+ }
3073
+ }
3074
+ database.exec("COMMIT");
3075
+ } catch (error) {
3076
+ database.exec("ROLLBACK");
3077
+ throw error;
3078
+ }
3079
+ }
3080
+ async function publishMigratedStore(target, entries, verifySource) {
3081
+ ensurePrivateDirectory(PATH15.dirname(target));
3082
+ const staging = PATH15.join(PATH15.dirname(target), `.receiver-migration-${randomBytes5(8).toString("hex")}`);
3083
+ await mkdir3(staging, { mode: 448 });
3084
+ const file = PATH15.join(staging, "receiver.sqlite");
3085
+ try {
3086
+ const receiver = openReceiverAuthority(file);
3087
+ try {
3088
+ importEntries(receiver.database, entries);
3089
+ receiver.database.exec("PRAGMA wal_checkpoint(TRUNCATE)");
3090
+ } finally {
3091
+ receiver.close();
3092
+ }
3093
+ await verifySource();
3094
+ await link3(file, target);
3095
+ await syncDirectory(PATH15.dirname(target));
3096
+ await unlink4(file);
3097
+ } finally {
3098
+ await rm(staging, { recursive: true, force: true });
3099
+ }
3100
+ }
3101
+ async function prepareLocations(options) {
3102
+ if (options?.consumersStopped !== true) throw new TypeError("Migration requires every source writer and consumer to be stopped");
3103
+ if (!["filesystem", "sqlite-queue"].includes(options.source.kind)) throw new TypeError("Unknown receiver migration source format");
3104
+ if (options.source.kind === "sqlite-queue" && options.source.scope !== "queue-only") throw new TypeError("SQLite queue migration requires explicit queue-only scope");
3105
+ const sourcePath = await realpath(options.source.path);
3106
+ if (!(await lstat4(options.source.path)).isDirectory()) throw new TypeError("Migration source must be an owned store directory");
3107
+ const backupPath = options.backupPath === void 0 ? void 0 : await outsideSource(sourcePath, options.backupPath);
3108
+ const targetPath = options.targetPath === void 0 ? void 0 : await outsideSource(sourcePath, options.targetPath);
3109
+ if (!options.dryRun && (backupPath === void 0 || targetPath === void 0)) throw new TypeError("Execution requires distinct backupPath and targetPath");
3110
+ if (backupPath !== void 0 && targetPath !== void 0 && (targetPath === backupPath || targetPath.startsWith(`${backupPath}${PATH15.sep}`) || backupPath.startsWith(`${targetPath}${PATH15.sep}`))) {
3111
+ throw new TypeError("Backup and target paths must be separate");
3112
+ }
3113
+ return {
3114
+ backupPath,
3115
+ sourcePath,
3116
+ targetPath
3117
+ };
3118
+ }
3119
+ async function prepareCopy(options, paths, scratch) {
3120
+ if (options.dryRun) return await snapshotMigrationSource(paths.sourcePath, PATH15.join(scratch, "source"));
3121
+ await mkdir3(PATH15.dirname(paths.backupPath), { recursive: true, mode: 448 });
3122
+ await mkdir3(paths.backupPath, { mode: 448 });
3123
+ const files = await snapshotMigrationSource(paths.sourcePath, PATH15.join(paths.backupPath, "source"));
3124
+ await syncDirectory(paths.backupPath);
3125
+ await snapshotMigrationSource(PATH15.join(paths.backupPath, "source"), PATH15.join(scratch, "source"));
3126
+ return files;
3127
+ }
3128
+ function migrationReport(paths, files, plan, options) {
3129
+ const dryRun = options.dryRun === true;
3130
+ const ready = plan.unknown.length === 0;
3131
+ return {
3132
+ ...paths.backupPath === void 0 || dryRun ? {} : { backupPath: paths.backupPath },
3133
+ completedIdentities: plan.entries.filter((entry) => entry.state === "completed").length,
3134
+ executed: ready && !dryRun,
3135
+ files,
3136
+ pendingIdentities: plan.entries.filter((entry) => entry.state === "pending").length,
3137
+ quarantined: plan.quarantined,
3138
+ ready,
3139
+ reviews: (options.reviews ?? []).map((review) => ({
3140
+ action: review.action,
3141
+ evidence: review.evidence,
3142
+ identity: review.identity,
3143
+ ...review.queueOrder === void 0 ? {} : { queueOrder: review.queueOrder }
3144
+ })),
3145
+ rollback,
3146
+ selected: plan.entries.map((entry) => ({
3147
+ ...entry.state === "completed" ? { completionProvenance: "Fresh reviewed migration tombstone; no historical lease or ack inferred" } : {},
3148
+ eventHash: entry.receipt?.eventHash ?? entry.wake?.eventHash,
3149
+ identity: entry.identity,
3150
+ paths: entry.paths,
3151
+ ...entry.queueOrder === void 0 ? {} : { queueOrder: entry.queueOrder },
3152
+ state: entry.state
3153
+ })),
3154
+ sourceBytes: files.reduce((total, file) => total + file.bytes, 0),
3155
+ sourcePath: paths.sourcePath,
3156
+ sourceUnchanged: true,
3157
+ tokenRecords: plan.entries.reduce((total, entry) => total + entry.tokens.length, 0),
3158
+ unknown: plan.unknown,
3159
+ ...paths.targetPath === void 0 || dryRun ? {} : { targetPath: paths.targetPath }
3160
+ };
3161
+ }
3162
+ async function migrateReceiverStore(options) {
3163
+ const paths = await prepareLocations(options);
3164
+ const scratch = await mkdtemp(PATH15.join(OS.tmpdir(), "event-kit-receiver-migration-"));
3165
+ try {
3166
+ const files = await prepareCopy(options, paths, scratch);
3167
+ const verify = async () => {
3168
+ await verifyMigrationSource(paths.sourcePath, files);
3169
+ };
3170
+ await verify();
3171
+ const plan = await inventoryReceiverMigration(options.source, PATH15.join(scratch, "source"), files, options.reviews ?? []);
3172
+ if (!options.dryRun) await writeReport(PATH15.join(paths.backupPath, "migration-plan.json"), { ...migrationReport(paths, files, plan, options), executed: false });
3173
+ if (plan.unknown.length === 0 && !options.dryRun) await publishMigratedStore(paths.targetPath, plan.entries, verify);
3174
+ await verify();
3175
+ const report = migrationReport(paths, files, plan, options);
3176
+ if (!options.dryRun) await writeReport(PATH15.join(paths.backupPath, "migration-report.json"), report);
3177
+ return report;
3178
+ } finally {
3179
+ await rm(scratch, { recursive: true, force: true });
3180
+ }
3181
+ }
3182
+ function migrateIdOnlyReducerCheckpoint(checkpoint, mappings) {
3183
+ if (!Number.isSafeInteger(checkpoint.lastProcessedPosition) || checkpoint.lastProcessedPosition < -1) throw new TypeError("Invalid historical checkpoint position");
3184
+ const ids = new Set(checkpoint.appliedWakeIds.map((id) => WakeIdZod3.parse(id)));
3185
+ const records = /* @__PURE__ */ new Map();
3186
+ for (const mapping of mappings) {
3187
+ const record = {
3188
+ audience: WakeJwtAudienceZod3.parse(mapping.audience),
3189
+ eventHash: HashZod8.parse(mapping.eventHash),
3190
+ publisher: NormalizedAddressZod6.parse(mapping.publisher),
3191
+ wakeId: WakeIdZod3.parse(mapping.wakeId)
3192
+ };
3193
+ if (!ids.has(record.wakeId) || records.has(record.wakeId)) throw new TypeError("Checkpoint mapping is extraneous or ambiguous");
3194
+ records.set(record.wakeId, record);
3195
+ }
3196
+ if (records.size !== ids.size) throw new TypeError("Every historical wake ID requires an exact scoped hash mapping");
3197
+ return { appliedWakes: [...ids].map((id) => records.get(id)), lastProcessedPosition: checkpoint.lastProcessedPosition };
3198
+ }
3199
+
2200
3200
  // src/runtime/boundedFetch.ts
2201
- import { Buffer as Buffer2 } from "node:buffer";
3201
+ import { Buffer as Buffer3 } from "node:buffer";
2202
3202
 
2203
3203
  // src/runtime/abort.ts
2204
3204
  import { clearTimeout, setTimeout } from "node:timers";
@@ -2240,13 +3240,13 @@ function createBoundedPublisherFetch(options = {}) {
2240
3240
  ...init?.signal == null ? [] : [init.signal],
2241
3241
  ...input instanceof Request ? [input.signal] : []
2242
3242
  ];
2243
- const scope = publisherAbortScope(signals, timeout);
3243
+ const scope2 = publisherAbortScope(signals, timeout);
2244
3244
  try {
2245
- scope.signal.throwIfAborted();
3245
+ scope2.signal.throwIfAborted();
2246
3246
  const response = await nativeFetch(input, {
2247
3247
  ...init,
2248
3248
  redirect: "error",
2249
- signal: scope.signal
3249
+ signal: scope2.signal
2250
3250
  });
2251
3251
  if (response.body === null)
2252
3252
  return response;
@@ -2274,13 +3274,13 @@ function createBoundedPublisherFetch(options = {}) {
2274
3274
  headers.delete("content-encoding");
2275
3275
  headers.delete("transfer-encoding");
2276
3276
  headers.set("content-length", String(size));
2277
- return new Response(Buffer2.concat(chunks), {
3277
+ return new Response(Buffer3.concat(chunks), {
2278
3278
  status: response.status,
2279
3279
  statusText: response.statusText,
2280
3280
  headers
2281
3281
  });
2282
3282
  } finally {
2283
- scope.dispose();
3283
+ scope2.dispose();
2284
3284
  }
2285
3285
  })();
2286
3286
  active.add(operation);
@@ -2305,7 +3305,7 @@ function createBoundedPublisherEthersTransport(options = {}) {
2305
3305
  cancellation?.addListener(() => controller.abort());
2306
3306
  cancellation?.checkSignal();
2307
3307
  const response = await bounded.fetchFn(request2.url, {
2308
- body: request2.body === null ? void 0 : Buffer2.from(request2.body),
3308
+ body: request2.body === null ? void 0 : Buffer3.from(request2.body),
2309
3309
  headers: request2.headers,
2310
3310
  method: request2.method,
2311
3311
  signal: controller.signal
@@ -2525,12 +3525,12 @@ function createPublisherHandshakeDiscovery(options) {
2525
3525
  },
2526
3526
  async acceptPending(signal) {
2527
3527
  if (accepting === void 0) {
2528
- const scope = publisherAbortScope([owner.signal, signal]);
3528
+ const scope2 = publisherAbortScope([owner.signal, signal]);
2529
3529
  accepting = (async () => {
2530
3530
  try {
2531
- await accept(scope.signal);
3531
+ await accept(scope2.signal);
2532
3532
  } finally {
2533
- scope.dispose();
3533
+ scope2.dispose();
2534
3534
  accepting = void 0;
2535
3535
  }
2536
3536
  })();
@@ -2545,25 +3545,25 @@ function createPublisherHandshakeDiscovery(options) {
2545
3545
  }
2546
3546
 
2547
3547
  // src/runtime/httpsTransport.ts
2548
- import { Buffer as Buffer3 } from "node:buffer";
3548
+ import { Buffer as Buffer4 } from "node:buffer";
2549
3549
  import { lookup } from "node:dns/promises";
2550
3550
  import { request } from "node:https";
2551
3551
  import { BlockList, isIP } from "node:net";
2552
- import { z as z2 } from "zod";
3552
+ import { z as z4 } from "zod";
2553
3553
 
2554
3554
  // src/runtime/sources.ts
2555
- import { createHash as createHash3 } from "node:crypto";
2556
- import { canonicalJson as canonicalJson2 } from "@xyo-network/event-kit-protocol";
2557
- import { completedUtcIntervalPosition, WakeSourceZod } from "@xyo-network/event-kit-schemas";
3555
+ import { createHash as createHash4 } from "node:crypto";
3556
+ import { canonicalJson as canonicalJson3 } from "@xyo-network/event-kit-protocol";
3557
+ import { completedUtcIntervalPosition, WakeSourceZod as WakeSourceZod2 } from "@xyo-network/event-kit-schemas";
2558
3558
  import { PayloadBuilder } from "@xyo-network/sdk";
2559
3559
  import { asXL1BlockNumber } from "@xyo-network/xl1-sdk";
2560
3560
  import { FetchRequest, JsonRpcProvider } from "ethers";
2561
3561
  function publisherSourceKey(source) {
2562
- return createHash3("sha256").update(canonicalJson2(WakeSourceZod.parse(source))).digest("hex");
3562
+ return createHash4("sha256").update(canonicalJson3(WakeSourceZod2.parse(source))).digest("hex");
2563
3563
  }
2564
3564
  async function boundedPublisherOperation(operation, signal, timeoutMs = 1e4) {
2565
- const scope = publisherAbortScope([signal], timeoutMs);
2566
- const combined = scope.signal;
3565
+ const scope2 = publisherAbortScope([signal], timeoutMs);
3566
+ const combined = scope2.signal;
2567
3567
  let rejectAbort;
2568
3568
  const aborted = new Promise((_resolve, reject) => {
2569
3569
  rejectAbort = () => reject(new Error("Publisher operation aborted or timed out"));
@@ -2575,11 +3575,11 @@ async function boundedPublisherOperation(operation, signal, timeoutMs = 1e4) {
2575
3575
  } finally {
2576
3576
  if (rejectAbort !== void 0)
2577
3577
  combined.removeEventListener("abort", rejectAbort);
2578
- scope.dispose();
3578
+ scope2.dispose();
2579
3579
  }
2580
3580
  }
2581
3581
  function createXl1PublisherSource(options) {
2582
- const source = WakeSourceZod.parse(options.source);
3582
+ const source = WakeSourceZod2.parse(options.source);
2583
3583
  if (source.kind !== "xl1-finalized")
2584
3584
  throw new Error("Expected an XL1 finalized source");
2585
3585
  return {
@@ -2603,7 +3603,7 @@ function createXl1PublisherSource(options) {
2603
3603
  };
2604
3604
  }
2605
3605
  function createEthereumFinalizedPublisherSource(options) {
2606
- const source = WakeSourceZod.parse(options.source);
3606
+ const source = WakeSourceZod2.parse(options.source);
2607
3607
  if (source.kind !== "ethereum-finalized")
2608
3608
  throw new Error("Expected an Ethereum finalized source");
2609
3609
  const { chainId, genesisHash } = source;
@@ -2669,7 +3669,7 @@ function createEthereumFinalizedPublisherSource(options) {
2669
3669
  };
2670
3670
  }
2671
3671
  function createUtcIntervalPublisherSource(options) {
2672
- const source = WakeSourceZod.parse(options.source);
3672
+ const source = WakeSourceZod2.parse(options.source);
2673
3673
  if (source.kind !== "utc-interval")
2674
3674
  throw new Error("Expected a UTC interval source");
2675
3675
  const nowSeconds = options.nowSeconds ?? (() => Math.floor(Date.now() / 1e3));
@@ -2686,14 +3686,14 @@ function createUtcIntervalPublisherSource(options) {
2686
3686
  }
2687
3687
 
2688
3688
  // src/runtime/httpsTransport.ts
2689
- var OptionsZod = z2.strictObject({
2690
- allowedEndpoints: z2.array(z2.url()).min(1).max(64),
2691
- endpointHeaders: z2.array(z2.strictObject({
2692
- endpoint: z2.url(),
2693
- headers: z2.strictObject({ "x-vercel-protection-bypass": z2.string().min(1).max(4096).regex(/^[\u{21}-\u{7E}]+$/u) })
3689
+ var OptionsZod = z4.strictObject({
3690
+ allowedEndpoints: z4.array(z4.url()).min(1).max(64),
3691
+ endpointHeaders: z4.array(z4.strictObject({
3692
+ endpoint: z4.url(),
3693
+ headers: z4.strictObject({ "x-vercel-protection-bypass": z4.string().min(1).max(4096).regex(/^[\u{21}-\u{7E}]+$/u) })
2694
3694
  })).max(64).default([]),
2695
- maxResponseBytes: z2.number().int().min(1).max(1048576).default(65536),
2696
- requestTimeoutMs: z2.number().int().min(100).max(6e4).default(1e4)
3695
+ maxResponseBytes: z4.number().int().min(1).max(1048576).default(65536),
3696
+ requestTimeoutMs: z4.number().int().min(100).max(6e4).default(1e4)
2697
3697
  });
2698
3698
  var NON_PUBLIC_IPV4_SUBNETS = [
2699
3699
  ["0.0.0.0", 8],
@@ -2773,8 +3773,8 @@ function createPublisherHttpsTransport(input) {
2773
3773
  assertAllowed(endpoint);
2774
3774
  signal.throwIfAborted();
2775
3775
  const url = new URL(endpoint);
2776
- const scope = publisherAbortScope([signal], options.requestTimeoutMs);
2777
- const combined = scope.signal;
3776
+ const scope2 = publisherAbortScope([signal], options.requestTimeoutMs);
3777
+ const combined = scope2.signal;
2778
3778
  try {
2779
3779
  const addresses = await boundedPublisherOperation(lookup(url.hostname.replaceAll(/^\[|\]$/gu, ""), { all: true, verbatim: true }), combined, options.requestTimeoutMs);
2780
3780
  combined.throwIfAborted();
@@ -2783,7 +3783,7 @@ function createPublisherHttpsTransport(input) {
2783
3783
  const selected = addresses[0];
2784
3784
  if (selected === void 0)
2785
3785
  throw new Error("Publisher DNS lookup was empty");
2786
- const encoded = Buffer3.from(JSON.stringify(body));
3786
+ const encoded = Buffer4.from(JSON.stringify(body));
2787
3787
  if (encoded.byteLength > 65536)
2788
3788
  throw new Error("Publisher request body is too large");
2789
3789
  return await new Promise((resolve, reject) => {
@@ -2824,7 +3824,7 @@ function createPublisherHttpsTransport(input) {
2824
3824
  response.on("error", () => reject(new Error("Publisher HTTPS response failed")));
2825
3825
  response.on("end", () => {
2826
3826
  try {
2827
- const responseBody = size === 0 ? null : JSON.parse(Buffer3.concat(chunks).toString("utf8"));
3827
+ const responseBody = size === 0 ? null : JSON.parse(Buffer4.concat(chunks).toString("utf8"));
2828
3828
  const retry = Number(response.headers["retry-after"]);
2829
3829
  resolve({
2830
3830
  status,
@@ -2840,7 +3840,7 @@ function createPublisherHttpsTransport(input) {
2840
3840
  call.end(encoded);
2841
3841
  });
2842
3842
  } finally {
2843
- scope.dispose();
3843
+ scope2.dispose();
2844
3844
  }
2845
3845
  }
2846
3846
  };
@@ -2852,25 +3852,25 @@ import { createMemoryLocator, PeriodicActor } from "@ariestools/actor";
2852
3852
  import { creatable } from "@ariestools/sdk";
2853
3853
  import { BestEffortAdmissionAcknowledgmentZod } from "@xyo-network/event-kit-protocol";
2854
3854
  import { BestEffortDeliveryPolicyZod, WakeDeliveryModeZod } from "@xyo-network/event-kit-schemas";
2855
- import { z as z3 } from "zod";
3855
+ import { z as z5 } from "zod";
2856
3856
 
2857
3857
  // src/runtime/publisherDeliveryState.ts
2858
- import { randomBytes as randomBytes5 } from "node:crypto";
3858
+ import { randomBytes as randomBytes6 } from "node:crypto";
2859
3859
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
2860
- import { WakeEnvelopeSchema, WakeEnvelopeZod } from "@xyo-network/event-kit-schemas";
3860
+ import { WakeEnvelopeSchema as WakeEnvelopeSchema2, WakeEnvelopeZod as WakeEnvelopeZod2 } from "@xyo-network/event-kit-schemas";
2861
3861
  function assertDeliveryState(record, subscription) {
2862
3862
  if (!isDeepStrictEqual3(record.subscription, subscription))
2863
3863
  throw new Error("Subscription revision changed without a new identity");
2864
3864
  if (!Number.isSafeInteger(record.cursor) || record.cursor < subscription.fromPosition - 1 || [record.delivered, record.failures, record.nextAttemptMs].some((value) => !(Number.isSafeInteger(value) && value >= 0)) || [record.attempts, record.superseded, record.abandoned, record.totalAttempts, record.lastHintAt, record.lastNotifiedAt, record.lastHintTarget].some((value) => value !== void 0 && !(Number.isSafeInteger(value) && value >= 0)) || !["active", "blocked", "cancelled"].includes(record.state))
2865
3865
  throw new Error("Persisted publisher delivery state is invalid");
2866
3866
  if (record.pending === void 0) return;
2867
- const pending = WakeEnvelopeZod.parse(record.pending);
3867
+ const pending = WakeEnvelopeZod2.parse(record.pending);
2868
3868
  if (subscription.deliveryMode === "durable" && pending.fromPosition !== record.cursor + 1 || pending.deliveryMode !== subscription.deliveryMode || pending.toPosition - pending.fromPosition + 1 > subscription.maxPositionsPerWake || pending.authorizationChainId !== subscription.authorizationChainId || pending.deploymentId !== subscription.deploymentId || pending.queueId !== subscription.queueId || pending.filterHash !== subscription.filterHash || !isDeepStrictEqual3(pending.source, subscription.source))
2869
3869
  throw new Error("Persisted publisher outbox binding is invalid");
2870
3870
  }
2871
- function makeEvent(subscription, fromPosition, toPosition, now, wakeId = randomBytes5(16).toString("hex")) {
2872
- return WakeEnvelopeZod.parse({
2873
- schema: WakeEnvelopeSchema,
3871
+ function makeEvent(subscription, fromPosition, toPosition, now, wakeId = randomBytes6(16).toString("hex")) {
3872
+ return WakeEnvelopeZod2.parse({
3873
+ schema: WakeEnvelopeSchema2,
2874
3874
  authorizationChainId: subscription.authorizationChainId,
2875
3875
  deliveryMode: subscription.deliveryMode,
2876
3876
  deploymentId: subscription.deploymentId,
@@ -2965,27 +3965,27 @@ function failedDelivery(record, current, mode, error, now) {
2965
3965
  }
2966
3966
 
2967
3967
  // src/runtime/sourceSigner.ts
2968
- import { randomBytes as randomBytes6 } from "node:crypto";
3968
+ import { randomBytes as randomBytes7 } from "node:crypto";
2969
3969
  import {
2970
- NormalizedAddressZod as NormalizedAddressZod4,
2971
- WakeEnvelopeZod as WakeEnvelopeZod2,
3970
+ NormalizedAddressZod as NormalizedAddressZod7,
3971
+ WakeEnvelopeZod as WakeEnvelopeZod3,
2972
3972
  WakeJwtSchema
2973
3973
  } from "@xyo-network/event-kit-schemas";
2974
3974
  import { PayloadBuilder as PayloadBuilder2 } from "@xyo-network/sdk";
2975
3975
  function createAccountSourceWakeSigner(options) {
2976
- const publisher = NormalizedAddressZod4.parse(options.publisher);
3976
+ const publisher = NormalizedAddressZod7.parse(options.publisher);
2977
3977
  if (String(options.account.address).replace(/^0x/u, "").toLowerCase() !== publisher)
2978
3978
  throw new Error("Publisher signing account mismatch");
2979
3979
  return {
2980
3980
  async sign(input, audience) {
2981
- const event = WakeEnvelopeZod2.parse(input);
3981
+ const event = WakeEnvelopeZod3.parse(input);
2982
3982
  const eventHash = await PayloadBuilder2.dataHash(event);
2983
3983
  const now = options.nowSeconds?.() ?? Math.floor(Date.now() / 1e3);
2984
3984
  if (!Number.isSafeInteger(now) || now < 0)
2985
3985
  throw new Error("Publisher signing clock is invalid");
2986
3986
  const { token: jwt } = await options.account.signJwt({
2987
3987
  audience,
2988
- claims: { eventHash, jti: randomBytes6(16).toString("hex") },
3988
+ claims: { eventHash, jti: randomBytes7(16).toString("hex") },
2989
3989
  now,
2990
3990
  schema: WakeJwtSchema,
2991
3991
  ttl: 60
@@ -3012,11 +4012,11 @@ var PublisherRuntimeActor = class extends PeriodicActor {
3012
4012
  PublisherRuntimeActor = __decorateClass([
3013
4013
  creatable()
3014
4014
  ], PublisherRuntimeActor);
3015
- var TimingZod = z3.object({
3016
- concurrency: z3.number().int().min(1).max(32).default(4),
3017
- intervalMs: z3.number().int().min(100).max(6e4).default(1e3),
3018
- maxSubscriptions: z3.number().int().min(1).max(1024).default(64),
3019
- requestTimeoutMs: z3.number().int().min(100).max(6e4).default(1e4)
4015
+ var TimingZod = z5.object({
4016
+ concurrency: z5.number().int().min(1).max(32).default(4),
4017
+ intervalMs: z5.number().int().min(100).max(6e4).default(1e3),
4018
+ maxSubscriptions: z5.number().int().min(1).max(1024).default(64),
4019
+ requestTimeoutMs: z5.number().int().min(100).max(6e4).default(1e4)
3020
4020
  });
3021
4021
  function runtimeSettings(options) {
3022
4022
  const deliveryMode = WakeDeliveryModeZod.parse(options.deliveryMode);
@@ -3176,7 +4176,7 @@ async function createPersistentEventPublisherRuntime(options) {
3176
4176
  acknowledgeBestEffort(record, response.body, signed.eventHash, signal, now);
3177
4177
  return;
3178
4178
  }
3179
- const receipt = z3.object({ receiptId: z3.string().min(1), wakeId: z3.literal(pending.wakeId) }).safeParse(response.body);
4179
+ const receipt = z5.object({ receiptId: z5.string().min(1), wakeId: z5.literal(pending.wakeId) }).safeParse(response.body);
3180
4180
  if (!receipt.success) throw new Error("Publisher admission did not return a matching durable receipt");
3181
4181
  signal.throwIfAborted();
3182
4182
  state.transaction(() => {
@@ -3258,8 +4258,8 @@ async function createPersistentEventPublisherRuntime(options) {
3258
4258
  return available;
3259
4259
  }
3260
4260
  async function tick(actorSignal) {
3261
- const scope = publisherAbortScope([abort.signal, actorSignal]);
3262
- const signal = scope.signal;
4261
+ const scope2 = publisherAbortScope([abort.signal, actorSignal]);
4262
+ const signal = scope2.signal;
3263
4263
  try {
3264
4264
  signal.throwIfAborted();
3265
4265
  const now = clock();
@@ -3290,7 +4290,7 @@ async function createPersistentEventPublisherRuntime(options) {
3290
4290
  if (!signal.aborted)
3291
4291
  throw error;
3292
4292
  } finally {
3293
- scope.dispose();
4293
+ scope2.dispose();
3294
4294
  }
3295
4295
  }
3296
4296
  const actor = await PublisherRuntimeActor.create({
@@ -3464,15 +4464,15 @@ import {
3464
4464
  Base64Url32BytesZod as Base64Url32BytesZod3,
3465
4465
  EndpointChallengeZod as EndpointChallengeZod3,
3466
4466
  HandshakeIdZod as HandshakeIdZod4,
3467
- HashZod as HashZod5,
3468
- NormalizedAddressZod as NormalizedAddressZod5
4467
+ HashZod as HashZod9,
4468
+ NormalizedAddressZod as NormalizedAddressZod8
3469
4469
  } from "@xyo-network/event-kit-schemas";
3470
- import { z as z4 } from "zod";
3471
- var KeyZod = z4.strictObject({
3472
- encryptionKey: HashZod5,
4470
+ import { z as z6 } from "zod";
4471
+ var KeyZod = z6.strictObject({
4472
+ encryptionKey: HashZod9,
3473
4473
  keyId: HandshakeIdZod4,
3474
4474
  privateKey: Base64Url32BytesZod3,
3475
- publisher: NormalizedAddressZod5
4475
+ publisher: NormalizedAddressZod8
3476
4476
  });
3477
4477
  var phases = ["signed", "content-written", "content-verified", "broadcast", "included", "finalized"];
3478
4478
  function requireTime2(value) {
@@ -3482,13 +4482,13 @@ function samePublicationIntent(left, right) {
3482
4482
  return left.chainId === right.chainId && left.source === right.source && left.operationId === right.operationId && isDeepStrictEqual5(left.eventHashes, right.eventHashes) && isDeepStrictEqual5(left.attachmentHashes, right.attachmentHashes);
3483
4483
  }
3484
4484
  function publicationKey(intent) {
3485
- if (!/^[0-9a-f]{40}$/u.test(intent.chainId) || !/^[0-9a-f]{40}$/u.test(intent.source) || intent.operationId.length === 0 || intent.operationId.length > 256 || [...intent.eventHashes, ...intent.attachmentHashes].some((hash) => !HashZod5.safeParse(hash).success)) {
4485
+ if (!/^[0-9a-f]{40}$/u.test(intent.chainId) || !/^[0-9a-f]{40}$/u.test(intent.source) || intent.operationId.length === 0 || intent.operationId.length > 256 || [...intent.eventHashes, ...intent.attachmentHashes].some((hash) => !HashZod9.safeParse(hash).success)) {
3486
4486
  throw new Error("Invalid publication intent");
3487
4487
  }
3488
4488
  return JSON.stringify([intent.chainId, intent.source, intent.operationId]);
3489
4489
  }
3490
4490
  function createSqlitePublisherHandshakeStores(state, options = {}) {
3491
- const replayKey = options.replayNamespace === void 0 ? "checkpoint" : `checkpoint:${z4.string().min(1).max(128).regex(/^[A-Za-z0-9_-]+$/u).parse(options.replayNamespace)}`;
4491
+ const replayKey = options.replayNamespace === void 0 ? "checkpoint" : `checkpoint:${z6.string().min(1).max(128).regex(/^[A-Za-z0-9_-]+$/u).parse(options.replayNamespace)}`;
3492
4492
  const replay = {
3493
4493
  load: async () => state.get("replay", replayKey),
3494
4494
  async save(checkpoint) {
@@ -3516,11 +4516,11 @@ function createSqlitePublisherHandshakeStores(state, options = {}) {
3516
4516
  };
3517
4517
  const keys = {
3518
4518
  async getPrivateKey(input) {
3519
- const key = `${NormalizedAddressZod5.parse(input.publisher)}:${HandshakeIdZod4.parse(input.keyId)}`;
4519
+ const key = `${NormalizedAddressZod8.parse(input.publisher)}:${HandshakeIdZod4.parse(input.keyId)}`;
3520
4520
  const raw = state.get("encryption-keys", key);
3521
4521
  if (raw === void 0) return;
3522
4522
  const record = KeyZod.parse(raw);
3523
- if (record.encryptionKey !== HashZod5.parse(input.encryptionKey)) throw new Error("Encryption key identity mismatch");
4523
+ if (record.encryptionKey !== HashZod9.parse(input.encryptionKey)) throw new Error("Encryption key identity mismatch");
3524
4524
  const { publisher: _publisher, ...privateKey } = record;
3525
4525
  return privateKey;
3526
4526
  },
@@ -3538,13 +4538,13 @@ function createSqlitePublisherHandshakeStores(state, options = {}) {
3538
4538
  });
3539
4539
  }
3540
4540
  };
3541
- const current = (hash) => state.get("challenges", HashZod5.parse(hash));
4541
+ const current = (hash) => state.get("challenges", HashZod9.parse(hash));
3542
4542
  const live = (record, now) => record.challenge.issuedAt <= now && now < record.challenge.expiresAt;
3543
4543
  const challenges = {
3544
4544
  get: async (hash) => current(hash),
3545
4545
  async issue(input) {
3546
4546
  const challenge = EndpointChallengeZod3.parse(input.challenge);
3547
- const hash = HashZod5.parse(input.challengeHash);
4547
+ const hash = HashZod9.parse(input.challengeHash);
3548
4548
  if (await endpointChallengeHash2(challenge) !== hash) return { kind: "collision" };
3549
4549
  return state.transaction(() => {
3550
4550
  const priorNonce = state.get("challenge-nonces", challenge.nonce);
@@ -3576,7 +4576,7 @@ function createSqlitePublisherHandshakeStores(state, options = {}) {
3576
4576
  },
3577
4577
  async completeVerified(input) {
3578
4578
  requireTime2(input.nowSeconds);
3579
- const responseHash = HashZod5.parse(input.responseHash);
4579
+ const responseHash = HashZod9.parse(input.responseHash);
3580
4580
  return state.transaction(() => {
3581
4581
  const record = current(input.challengeHash);
3582
4582
  if (record === void 0) return { kind: "missing" };
@@ -3612,7 +4612,7 @@ function createSqlitePublisherHandshakeStores(state, options = {}) {
3612
4612
  },
3613
4613
  async save(record) {
3614
4614
  const key = publicationKey(record);
3615
- HashZod5.parse(record.evidence.transactionHash);
4615
+ HashZod9.parse(record.evidence.transactionHash);
3616
4616
  if (!phases.includes(record.phase)) throw new Error("Unknown publication phase");
3617
4617
  state.transaction(() => {
3618
4618
  const signed = state.get("publication:signed", key);
@@ -3637,15 +4637,15 @@ function createSqlitePublisherHandshakeStores(state, options = {}) {
3637
4637
  }
3638
4638
 
3639
4639
  // src/runtime/sqlitePublisherState.ts
3640
- import { randomBytes as randomBytes7 } from "node:crypto";
3641
- import { constants } from "node:fs";
4640
+ import { randomBytes as randomBytes8 } from "node:crypto";
4641
+ import { constants as constants3 } from "node:fs";
3642
4642
  import {
3643
- lstat,
3644
- mkdir as mkdir5,
3645
- open as open5
4643
+ lstat as lstat5,
4644
+ mkdir as mkdir4,
4645
+ open as open6
3646
4646
  } from "node:fs/promises";
3647
4647
  import { hostname } from "node:os";
3648
- import PATH12 from "node:path";
4648
+ import PATH16 from "node:path";
3649
4649
  function decode(value) {
3650
4650
  return JSON.parse(value, (_key, candidate) => {
3651
4651
  return typeof candidate === "object" && candidate !== null && Object.keys(candidate).length === 1 && "$eventKitBigInt" in candidate && typeof candidate.$eventKitBigInt === "string" && /^(?:0|[1-9][0-9]*)$/u.test(candidate.$eventKitBigInt) ? BigInt(candidate.$eventKitBigInt) : candidate;
@@ -3666,13 +4666,13 @@ function processExists(pid) {
3666
4666
  async function openSqlitePublisherState(options) {
3667
4667
  if (options.path === ":memory:" || options.identity.length === 0)
3668
4668
  throw new Error("Persistent publisher needs a file and an identity");
3669
- const path = PATH12.resolve(options.path);
3670
- const directory = PATH12.dirname(path);
3671
- await mkdir5(directory, { recursive: true, mode: 448 });
3672
- const metadata = await lstat(directory);
4669
+ const path = PATH16.resolve(options.path);
4670
+ const directory = PATH16.dirname(path);
4671
+ await mkdir4(directory, { recursive: true, mode: 448 });
4672
+ const metadata = await lstat5(directory);
3673
4673
  if (!metadata.isDirectory() || (metadata.mode & 63) !== 0)
3674
4674
  throw new Error("Publisher state directory must be a real directory with 0700 permissions");
3675
- const file = await open5(path, constants.O_CREAT | constants.O_RDWR | constants.O_NOFOLLOW, 384);
4675
+ const file = await open6(path, constants3.O_CREAT | constants3.O_RDWR | constants3.O_NOFOLLOW, 384);
3676
4676
  try {
3677
4677
  const metadata2 = await file.stat();
3678
4678
  if (!metadata2.isFile() || (metadata2.mode & 63) !== 0)
@@ -3681,9 +4681,9 @@ async function openSqlitePublisherState(options) {
3681
4681
  } finally {
3682
4682
  await file.close();
3683
4683
  }
3684
- const { DatabaseSync: DatabaseSync2 } = await import("node:sqlite");
3685
- const database = new DatabaseSync2(path);
3686
- const token = randomBytes7(16).toString("hex");
4684
+ const { DatabaseSync: DatabaseSync3 } = await import("node:sqlite");
4685
+ const database = new DatabaseSync3(path);
4686
+ const token = randomBytes8(16).toString("hex");
3687
4687
  let closed = false;
3688
4688
  let inTransaction = false;
3689
4689
  const transaction = (work) => {
@@ -3774,67 +4774,362 @@ async function openSqlitePublisherState(options) {
3774
4774
  };
3775
4775
  }
3776
4776
 
3777
- // src/sqliteWakeQueue.ts
3778
- import { mkdir as mkdir6 } from "node:fs/promises";
3779
- import PATH13 from "node:path";
3780
- import { DatabaseSync } from "node:sqlite";
3781
- async function createSqliteWakeQueue(options) {
3782
- const inMemory = options.path === ":memory:";
3783
- const file = inMemory ? options.path : PATH13.resolve(options.path);
3784
- if (!inMemory) await mkdir6(PATH13.dirname(file), { recursive: true });
3785
- const database = new DatabaseSync(file);
3786
- database.exec(`
3787
- PRAGMA journal_mode = WAL;
3788
- PRAGMA synchronous = FULL;
3789
- CREATE TABLE IF NOT EXISTS wake_queue (
3790
- id INTEGER PRIMARY KEY AUTOINCREMENT,
3791
- body TEXT NOT NULL,
3792
- state TEXT NOT NULL DEFAULT 'pending'
3793
- );
3794
- CREATE INDEX IF NOT EXISTS wake_queue_state ON wake_queue (state, id);
3795
- `);
3796
- const insert = database.prepare("INSERT INTO wake_queue (body) VALUES (?)");
3797
- const nextPending = database.prepare(
3798
- "SELECT id, body FROM wake_queue WHERE state = 'pending' ORDER BY id LIMIT 1"
3799
- );
3800
- const lease = database.prepare(
3801
- "UPDATE wake_queue SET state = 'inflight' WHERE id = ? AND state = 'pending'"
3802
- );
3803
- const remove = database.prepare("DELETE FROM wake_queue WHERE id = ?");
3804
- const restore = database.prepare(
3805
- "UPDATE wake_queue SET state = 'pending' WHERE id = ? AND state = 'inflight'"
3806
- );
3807
- const countPending = database.prepare(
3808
- "SELECT COUNT(*) AS total FROM wake_queue WHERE state = 'pending'"
3809
- );
3810
- const recoverAll = database.prepare(
3811
- "UPDATE wake_queue SET state = 'pending' WHERE state = 'inflight'"
3812
- );
4777
+ // src/sqliteBestEffortInbox.ts
4778
+ import { createHash as createHash5, randomBytes as randomBytes9 } from "node:crypto";
4779
+ import {
4780
+ canonicalJson as canonicalJson4,
4781
+ commitBestEffortSecurity,
4782
+ createBestEffortSecurityState,
4783
+ parseAdmittedWake as parseAdmittedWake4,
4784
+ parseBestEffortSecurityState,
4785
+ wakeSourceHash
4786
+ } from "@xyo-network/event-kit-protocol";
4787
+ import {
4788
+ ChainIdZod as ChainIdZod3,
4789
+ DeploymentIdZod,
4790
+ HashZod as HashZod10,
4791
+ NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod5,
4792
+ NormalizedAddressZod as NormalizedAddressZod9,
4793
+ WakeJwtAudienceZod as WakeJwtAudienceZod4,
4794
+ WakeSourceZod as WakeSourceZod3
4795
+ } from "@xyo-network/event-kit-schemas";
4796
+ import { z as z7 } from "zod";
4797
+ var BindingZod = z7.strictObject({
4798
+ audience: WakeJwtAudienceZod4,
4799
+ authorizationChainId: ChainIdZod3,
4800
+ consumerId: z7.string().min(1).max(255),
4801
+ controller: NormalizedAddressZod9,
4802
+ deploymentId: DeploymentIdZod,
4803
+ filterHash: HashZod10,
4804
+ publisher: NormalizedAddressZod9,
4805
+ queueId: z7.string().min(1).max(255).optional(),
4806
+ source: WakeSourceZod3,
4807
+ subscriptionHash: HashZod10
4808
+ });
4809
+ var SnapshotPositionZod = z7.strictObject({
4810
+ position: NonnegativeJsonSafeIntegerZod5,
4811
+ hash: HashZod10.optional()
4812
+ });
4813
+ var TargetZod = z7.strictObject({
4814
+ binding: BindingZod,
4815
+ completedGeneration: NonnegativeJsonSafeIntegerZod5,
4816
+ generation: NonnegativeJsonSafeIntegerZod5,
4817
+ id: HashZod10,
4818
+ requestedPosition: NonnegativeJsonSafeIntegerZod5,
4819
+ fence: NonnegativeJsonSafeIntegerZod5,
4820
+ previousSnapshot: SnapshotPositionZod.optional(),
4821
+ lease: z7.strictObject({
4822
+ expiresAt: NonnegativeJsonSafeIntegerZod5,
4823
+ token: z7.string().regex(/^[0-9a-f]{32}$/u)
4824
+ }).optional()
4825
+ }).refine(
4826
+ (target) => target.generation > 0 && target.completedGeneration <= target.generation && (target.lease === void 0 || target.fence > 0 && target.generation > target.completedGeneration) && (target.previousSnapshot === void 0 || target.binding.source.kind === "utc-interval" || target.previousSnapshot.hash !== void 0),
4827
+ "Invalid Best Effort dirty generation"
4828
+ );
4829
+ var CatalogZod = z7.strictObject({
4830
+ id: HashZod10,
4831
+ binding: z7.string(),
4832
+ target_hash: HashZod10,
4833
+ fence: NonnegativeJsonSafeIntegerZod5,
4834
+ generation: NonnegativeJsonSafeIntegerZod5
4835
+ }).refine((record) => record.generation > 0, "Invalid Best Effort catalog generation");
4836
+ function bindingId(binding) {
4837
+ return createHash5("sha256").update(canonicalJson4(binding)).digest("hex");
4838
+ }
4839
+ function targetHash(body) {
4840
+ return createHash5("sha256").update(body).digest("hex");
4841
+ }
4842
+ function parseCatalog(value) {
4843
+ const record = CatalogZod.parse(value);
4844
+ const binding = BindingZod.parse(JSON.parse(record.binding));
4845
+ if (record.id !== bindingId(binding) || record.binding !== canonicalJson4(binding))
4846
+ throw new TypeError("Persisted Best Effort catalog changed exact binding");
4847
+ return record;
4848
+ }
4849
+ function parseTarget(body, expectedId) {
4850
+ if (typeof body !== "string") throw new TypeError("Malformed persisted Best Effort target");
4851
+ const parsed = TargetZod.parse(JSON.parse(body));
4852
+ if (parsed.id !== expectedId || bindingId(parsed.binding) !== expectedId)
4853
+ throw new TypeError("Persisted Best Effort target changed exact scope");
4854
+ return parsed;
4855
+ }
4856
+ function publicTarget(target) {
3813
4857
  return {
3814
- async append(wake) {
3815
- insert.run(JSON.stringify(wake));
3816
- },
3817
- async claim() {
3818
- for (; ; ) {
3819
- const row = nextPending.get();
3820
- if (row === void 0) return void 0;
3821
- if (lease.run(row.id).changes !== 1) continue;
4858
+ binding: target.binding,
4859
+ completedGeneration: target.completedGeneration,
4860
+ generation: target.generation,
4861
+ id: target.id,
4862
+ requestedPosition: target.requestedPosition
4863
+ };
4864
+ }
4865
+ function increment(value) {
4866
+ if (!Number.isSafeInteger(value + 1)) throw new RangeError("Best Effort fence/generation exhausted");
4867
+ return value + 1;
4868
+ }
4869
+ async function createSqliteBestEffortInbox(options) {
4870
+ const consumerId = z7.string().min(1).max(255).parse(options.consumerId);
4871
+ const maxTargets = options.maxTargets ?? 1024;
4872
+ if (!Number.isSafeInteger(options.maxSecurityRecords) || options.maxSecurityRecords < 1 || options.maxSecurityRecords > 65536)
4873
+ throw new TypeError("Invalid finite Best Effort security capacity");
4874
+ if (!Number.isSafeInteger(maxTargets) || maxTargets < 1 || maxTargets > 65536)
4875
+ throw new TypeError("Invalid finite Best Effort target capacity");
4876
+ const nowSeconds = options.nowSeconds ?? (() => Math.floor(Date.now() / 1e3));
4877
+ const connection = openPrivateSqlite({ path: options.path, format: "event-kit-best-effort-inbox" });
4878
+ const { database } = connection;
4879
+ const transaction = (work) => connection.transaction(work);
4880
+ try {
4881
+ const settings = canonicalJson4({
4882
+ consumerId,
4883
+ maxSecurityRecords: options.maxSecurityRecords,
4884
+ maxTargets
4885
+ });
4886
+ transaction(() => {
4887
+ const marker = database.prepare("SELECT initialized FROM eventkit_store WHERE id = 1").get();
4888
+ if (marker?.initialized !== 0 && marker?.initialized !== 1)
4889
+ throw new TypeError("Invalid Best Effort store initialization authority");
4890
+ const tables = database.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").all();
4891
+ const names = new Set(tables.map((row) => row.name));
4892
+ if (marker.initialized === 1) {
4893
+ if (["best_effort_security", "best_effort_targets", "best_effort_settings", "best_effort_catalog"].some((name) => !names.has(name)))
4894
+ throw new TypeError("Initialized Best Effort store lost authoritative tables; restore its custody before reopening");
4895
+ const previous = database.prepare("SELECT body FROM best_effort_settings WHERE id = 1").get();
4896
+ if (previous === void 0) throw new TypeError("Missing immutable Best Effort store settings");
4897
+ if (previous.body !== settings) throw new TypeError("Best Effort store consumer or capacity binding changed");
4898
+ return;
4899
+ }
4900
+ if (tables.some((row) => row.name !== "eventkit_store"))
4901
+ throw new TypeError("Uninitialized Best Effort bootstrap contains unexpected custody");
4902
+ database.exec(`
4903
+ CREATE TABLE best_effort_security (id INTEGER PRIMARY KEY CHECK (id = 1), body TEXT NOT NULL);
4904
+ CREATE TABLE best_effort_targets (id TEXT PRIMARY KEY, body TEXT NOT NULL);
4905
+ CREATE TABLE best_effort_settings (id INTEGER PRIMARY KEY CHECK (id = 1), body TEXT NOT NULL, catalog_count INTEGER NOT NULL);
4906
+ CREATE TABLE best_effort_catalog (id TEXT PRIMARY KEY, binding TEXT NOT NULL, target_hash TEXT NOT NULL, fence INTEGER NOT NULL, generation INTEGER NOT NULL);
4907
+ `);
4908
+ database.prepare("INSERT INTO best_effort_settings (id, body, catalog_count) VALUES (1, ?, 0)").run(settings);
4909
+ database.prepare("INSERT INTO best_effort_security (id, body) VALUES (1, ?)").run(JSON.stringify(createBestEffortSecurityState()));
4910
+ database.prepare("UPDATE eventkit_store SET initialized = 1 WHERE id = 1").run();
4911
+ });
4912
+ const readSecurity = () => {
4913
+ const row = database.prepare("SELECT body FROM best_effort_security WHERE id = 1").get();
4914
+ if (typeof row?.body !== "string") throw new TypeError("Missing persisted Best Effort security state");
4915
+ const parsed = parseBestEffortSecurityState(JSON.parse(row.body));
4916
+ if (parsed.jwtRecords.length > options.maxSecurityRecords || parsed.wakeRecords.length > options.maxSecurityRecords)
4917
+ throw new TypeError("Persisted Best Effort security exceeds its configured bound");
4918
+ return parsed;
4919
+ };
4920
+ const readCatalog = (id) => {
4921
+ const row = database.prepare("SELECT id, binding, target_hash, fence, generation FROM best_effort_catalog WHERE id = ?").get(id);
4922
+ return row === void 0 ? void 0 : parseCatalog(row);
4923
+ };
4924
+ const verifiedTarget = (body, catalog) => {
4925
+ const target = parseTarget(body, catalog.id);
4926
+ if (target.binding.consumerId !== consumerId) throw new TypeError("Persisted Best Effort target changed consumer");
4927
+ if (typeof body !== "string" || catalog.target_hash !== targetHash(body) || catalog.binding !== canonicalJson4(target.binding) || catalog.fence !== target.fence || catalog.generation !== target.generation)
4928
+ throw new TypeError("Persisted Best Effort target differs from its permanent catalog authority");
4929
+ return target;
4930
+ };
4931
+ const readTargets = () => {
4932
+ const setting = database.prepare("SELECT catalog_count FROM best_effort_settings WHERE id = 1").get();
4933
+ const expectedCount = NonnegativeJsonSafeIntegerZod5.parse(setting?.catalog_count);
4934
+ if (expectedCount > maxTargets) throw new TypeError("Persisted Best Effort catalog capacity exceeded");
4935
+ const catalogs = database.prepare("SELECT id, binding, target_hash, fence, generation FROM best_effort_catalog ORDER BY id").all().map(parseCatalog);
4936
+ const rows = database.prepare("SELECT id, body FROM best_effort_targets ORDER BY id").all();
4937
+ if (catalogs.length !== expectedCount || rows.length !== expectedCount)
4938
+ throw new TypeError("Persisted Best Effort binding catalog lost authoritative target custody");
4939
+ const byId = new Map(catalogs.map((catalog) => [catalog.id, catalog]));
4940
+ return rows.map((row) => {
4941
+ if (typeof row.id !== "string") throw new TypeError("Malformed persisted Best Effort target identity");
4942
+ const catalog = byId.get(row.id);
4943
+ if (catalog === void 0) throw new TypeError("Persisted Best Effort target lost permanent binding custody");
4944
+ return verifiedTarget(row.body, catalog);
4945
+ });
4946
+ };
4947
+ const readTarget = (id) => {
4948
+ const row = database.prepare("SELECT body FROM best_effort_targets WHERE id = ?").get(id);
4949
+ const catalog = readCatalog(id);
4950
+ if (row === void 0 && catalog === void 0) return;
4951
+ if (row === void 0 || catalog === void 0)
4952
+ throw new TypeError("Persisted Best Effort binding lost its authoritative target/catalog custody");
4953
+ return verifiedTarget(row.body, catalog);
4954
+ };
4955
+ const saveTarget = (target) => {
4956
+ const catalog = readCatalog(target.id);
4957
+ const body = JSON.stringify(target);
4958
+ const binding = canonicalJson4(target.binding);
4959
+ if (catalog === void 0) {
4960
+ if (database.prepare("SELECT id FROM best_effort_targets WHERE id = ?").get(target.id) !== void 0)
4961
+ throw new TypeError("Existing Best Effort target lost its permanent binding catalog");
4962
+ database.prepare("INSERT INTO best_effort_catalog (id, binding, target_hash, fence, generation) VALUES (?, ?, ?, ?, ?)").run(target.id, binding, targetHash(body), target.fence, target.generation);
4963
+ database.prepare("UPDATE best_effort_settings SET catalog_count = catalog_count + 1 WHERE id = 1").run();
4964
+ } else {
4965
+ if (catalog.binding !== binding || target.fence < catalog.fence || target.generation < catalog.generation)
4966
+ throw new TypeError("Best Effort target mutation would regress permanent binding authority");
4967
+ database.prepare("UPDATE best_effort_catalog SET target_hash = ?, fence = ?, generation = ? WHERE id = ?").run(targetHash(body), target.fence, target.generation, target.id);
4968
+ }
4969
+ database.prepare("INSERT INTO best_effort_targets (id, body) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET body = excluded.body").run(target.id, body);
4970
+ };
4971
+ transaction(() => {
4972
+ const security = readSecurity();
4973
+ const targets = readTargets();
4974
+ if (security.jwtRecords.length > options.maxSecurityRecords || security.wakeRecords.length > options.maxSecurityRecords || targets.length > maxTargets || targets.some((target) => target.binding.consumerId !== consumerId))
4975
+ throw new TypeError("Persisted Best Effort state exceeds its exact bound");
4976
+ });
4977
+ async function requestWork(target) {
4978
+ if (target.generation === target.completedGeneration || options.requestWork === void 0) return false;
4979
+ try {
4980
+ await options.requestWork(publicTarget(target));
4981
+ return true;
4982
+ } catch {
4983
+ return false;
4984
+ }
4985
+ }
4986
+ const currentTarget = (captured) => {
4987
+ const current = readTarget(captured.id);
4988
+ const now = NonnegativeJsonSafeIntegerZod5.parse(nowSeconds());
4989
+ return current?.lease?.token === captured.lease?.token && current?.fence === captured.fence && current?.generation === captured.generation && current.lease !== void 0 && current.lease.expiresAt > now ? current : void 0;
4990
+ };
4991
+ return {
4992
+ async commitAuthorized(input) {
4993
+ input = structuredClone(input);
4994
+ const wake = await parseAdmittedWake4(input.wake);
4995
+ const grant = input.grant.grant;
4996
+ if (grant.authorizationKind !== "activated-subscription" || grant.target !== wake.publisher || grant.authorizationChainId !== wake.event.authorizationChainId || grant.deploymentId !== wake.deploymentId || grant.filterHash !== wake.event.filterHash || grant.queueId !== wake.queueId || grant.sourceHash !== await wakeSourceHash(wake.event.source))
4997
+ throw new TypeError("Best Effort grant does not match its exact admitted profile");
4998
+ const binding = BindingZod.parse({
4999
+ consumerId,
5000
+ audience: wake.audience,
5001
+ authorizationChainId: wake.event.authorizationChainId,
5002
+ controller: grant.controller,
5003
+ deploymentId: wake.deploymentId,
5004
+ filterHash: wake.event.filterHash,
5005
+ publisher: wake.publisher,
5006
+ source: wake.event.source,
5007
+ subscriptionHash: grant.subscriptionHash,
5008
+ ...wake.queueId === void 0 ? {} : { queueId: wake.queueId }
5009
+ });
5010
+ const id = bindingId(binding);
5011
+ const committed = transaction(() => {
5012
+ const authority = { security: readSecurity(), targets: readTargets() };
5013
+ const next = commitBestEffortSecurity(authority.security, { ...input, wake }, options.maxSecurityRecords);
5014
+ if (next.result.kind !== "accepted" && next.result.kind !== "duplicate") return { result: next.result };
5015
+ const previous = authority.targets.find((target2) => target2.id === id);
5016
+ if (previous === void 0 && next.result.kind === "duplicate")
5017
+ throw new TypeError("Persisted duplicate lost its authoritative Best Effort target");
5018
+ if (previous === void 0 && authority.targets.length >= maxTargets)
5019
+ return { result: { kind: "capacity-exceeded" } };
5020
+ const target = previous === void 0 ? {
5021
+ binding,
5022
+ id,
5023
+ completedGeneration: 0,
5024
+ generation: 1,
5025
+ fence: 0,
5026
+ requestedPosition: wake.toPosition
5027
+ } : next.result.kind === "duplicate" ? previous : {
5028
+ ...previous,
5029
+ generation: increment(previous.generation),
5030
+ requestedPosition: Math.max(previous.requestedPosition, wake.toPosition)
5031
+ };
5032
+ database.prepare("UPDATE best_effort_security SET body = ? WHERE id = 1").run(JSON.stringify(next.state));
5033
+ saveTarget(target);
5034
+ return { result: next.result, target };
5035
+ });
5036
+ if (committed.target !== void 0) await requestWork(committed.target);
5037
+ return committed.result;
5038
+ },
5039
+ dirtyTargets: async () => transaction(() => readTargets().filter((target) => target.generation > target.completedGeneration).map(publicTarget)),
5040
+ async repairDirty() {
5041
+ let failed = 0;
5042
+ let scheduled = 0;
5043
+ const targets = transaction(() => readTargets());
5044
+ for (const target of targets) {
5045
+ if (options.requestWork === void 0 || target.generation === target.completedGeneration) continue;
5046
+ if (await requestWork(target)) scheduled += 1;
5047
+ else failed += 1;
5048
+ }
5049
+ return { failed, scheduled };
5050
+ },
5051
+ async recoverExpired(now) {
5052
+ NonnegativeJsonSafeIntegerZod5.parse(now);
5053
+ return transaction(() => {
5054
+ let recovered = 0;
5055
+ for (const target of readTargets()) {
5056
+ if (target.lease === void 0 || target.lease.expiresAt > now) continue;
5057
+ saveTarget({ ...target, lease: void 0 });
5058
+ recovered += 1;
5059
+ }
5060
+ return recovered;
5061
+ });
5062
+ },
5063
+ async claim(input) {
5064
+ NonnegativeJsonSafeIntegerZod5.parse(input.nowSeconds);
5065
+ if (!Number.isSafeInteger(input.leaseSeconds) || input.leaseSeconds < 1 || !Number.isSafeInteger(input.nowSeconds + input.leaseSeconds))
5066
+ throw new TypeError("Invalid Best Effort worker lease lifetime");
5067
+ const captured = transaction(() => {
5068
+ const target = readTargets().find((item) => item.generation > item.completedGeneration && item.lease === void 0);
5069
+ if (target === void 0) return;
5070
+ const claimed = {
5071
+ ...target,
5072
+ fence: increment(target.fence),
5073
+ lease: { token: randomBytes9(16).toString("hex"), expiresAt: input.nowSeconds + input.leaseSeconds }
5074
+ };
5075
+ saveTarget(claimed);
5076
+ return claimed;
5077
+ });
5078
+ if (captured?.lease === void 0) return void 0;
3822
5079
  return {
3823
- wake: JSON.parse(row.body),
3824
- async ack() {
3825
- remove.run(row.id);
5080
+ ...publicTarget(captured),
5081
+ fence: captured.fence,
5082
+ leaseToken: captured.lease.token,
5083
+ ...captured.previousSnapshot === void 0 ? {} : { previousSnapshot: captured.previousSnapshot },
5084
+ isCurrent: async () => transaction(() => currentTarget(captured) !== void 0),
5085
+ async complete(snapshot) {
5086
+ const position = SnapshotPositionZod.parse({ position: snapshot.position, ...snapshot.hash === void 0 ? {} : { hash: snapshot.hash } });
5087
+ return transaction(() => {
5088
+ const current = currentTarget(captured);
5089
+ if (current === void 0) return false;
5090
+ if (current.binding.source.kind !== "utc-interval" && position.hash === void 0)
5091
+ throw new TypeError("Finalized snapshot completion requires its verified hash");
5092
+ const previous = current.previousSnapshot;
5093
+ if (previous !== void 0 && (position.position < previous.position || position.position === previous.position && position.hash !== previous.hash))
5094
+ throw new TypeError("Best Effort completion regressed its committed snapshot");
5095
+ saveTarget({
5096
+ ...current,
5097
+ completedGeneration: current.generation,
5098
+ previousSnapshot: position,
5099
+ lease: void 0
5100
+ });
5101
+ return true;
5102
+ });
3826
5103
  },
3827
5104
  async release() {
3828
- restore.run(row.id);
5105
+ transaction(() => {
5106
+ const current = readTarget(captured.id);
5107
+ if (current?.lease?.token === captured.lease?.token && current?.fence === captured.fence)
5108
+ saveTarget({ ...current, lease: void 0 });
5109
+ });
3829
5110
  }
3830
5111
  };
3831
- }
3832
- },
3833
- close() {
3834
- database.close();
3835
- },
3836
- pendingCount: async () => Number(countPending.get().total),
3837
- recoverInflight: async () => Number(recoverAll.run().changes)
5112
+ },
5113
+ close: () => connection.close()
5114
+ };
5115
+ } catch (error) {
5116
+ connection.close();
5117
+ throw error;
5118
+ }
5119
+ }
5120
+
5121
+ // src/sqliteWakeQueue.ts
5122
+ async function createSqliteWakeQueue(options) {
5123
+ const authority = openReceiverAuthority(options.path);
5124
+ return { ...authority.queue, close: authority.close };
5125
+ }
5126
+ async function createSqliteWakeReceiver(options) {
5127
+ const authority = openReceiverAuthority(options.path);
5128
+ return {
5129
+ queue: authority.queue,
5130
+ inbox: authority.inbox,
5131
+ close: authority.close,
5132
+ pruneExpiredTokens: async (nowSeconds) => authority.pruneExpiredTokens(nowSeconds)
3838
5133
  };
3839
5134
  }
3840
5135
  export {
@@ -3845,6 +5140,7 @@ export {
3845
5140
  assertSameIntentBinding,
3846
5141
  boundedPublisherOperation,
3847
5142
  createAccountSourceWakeSigner,
5143
+ createBestEffortSnapshotWorker,
3848
5144
  createBoundedPublisherEthersTransport,
3849
5145
  createBoundedPublisherFetch,
3850
5146
  createEthereumFinalizedPublisherSource,
@@ -3863,8 +5159,10 @@ export {
3863
5159
  createPublisherHandshakeClient,
3864
5160
  createPublisherHandshakeDiscovery,
3865
5161
  createPublisherHttpsTransport,
5162
+ createSqliteBestEffortInbox,
3866
5163
  createSqlitePublisherHandshakeStores,
3867
5164
  createSqliteWakeQueue,
5165
+ createSqliteWakeReceiver,
3868
5166
  createSubscriberHandshakeClient,
3869
5167
  createSynchronizedEventKitHandshakeReader,
3870
5168
  createUtcIntervalPublisherSource,
@@ -3872,6 +5170,8 @@ export {
3872
5170
  handshakeMutationRequestFingerprint,
3873
5171
  intentLookupKey,
3874
5172
  isPublicPublisherAddress,
5173
+ migrateIdOnlyReducerCheckpoint,
5174
+ migrateReceiverStore,
3875
5175
  openPublisherDataLake,
3876
5176
  openPublisherRestGateway,
3877
5177
  openSqlitePublisherState,