@xyo-network/event-kit-node 1.0.1 → 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 (51) 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 +24 -11
  14. package/dist/node/handshake.mjs.map +2 -2
  15. package/dist/node/handshakeClients.d.ts +5 -1
  16. package/dist/node/handshakeClients.d.ts.map +1 -1
  17. package/dist/node/handshakeMutationIntent.d.ts +128 -66
  18. package/dist/node/handshakeMutationIntent.d.ts.map +1 -1
  19. package/dist/node/index.d.ts +8 -1
  20. package/dist/node/index.d.ts.map +1 -1
  21. package/dist/node/index.mjs +2190 -706
  22. package/dist/node/index.mjs.map +4 -4
  23. package/dist/node/privateSqlite.d.ts +19 -0
  24. package/dist/node/privateSqlite.d.ts.map +1 -0
  25. package/dist/node/receiverAuthority.d.ts +102 -0
  26. package/dist/node/receiverAuthority.d.ts.map +1 -0
  27. package/dist/node/receiverMigration.d.ts +16 -0
  28. package/dist/node/receiverMigration.d.ts.map +1 -0
  29. package/dist/node/receiverMigrationInventory.d.ts +6 -0
  30. package/dist/node/receiverMigrationInventory.d.ts.map +1 -0
  31. package/dist/node/receiverMigrationSnapshot.d.ts +6 -0
  32. package/dist/node/receiverMigrationSnapshot.d.ts.map +1 -0
  33. package/dist/node/receiverMigrationTypes.d.ts +113 -0
  34. package/dist/node/receiverMigrationTypes.d.ts.map +1 -0
  35. package/dist/node/runtime/handshakeDiscovery.d.ts +4 -0
  36. package/dist/node/runtime/handshakeDiscovery.d.ts.map +1 -1
  37. package/dist/node/runtime/publisherDeliveryState.d.ts +107 -0
  38. package/dist/node/runtime/publisherDeliveryState.d.ts.map +1 -0
  39. package/dist/node/runtime/publisherRuntime.d.ts +17 -1
  40. package/dist/node/runtime/publisherRuntime.d.ts.map +1 -1
  41. package/dist/node/runtime/sourceSigner.d.ts +1 -0
  42. package/dist/node/runtime/sourceSigner.d.ts.map +1 -1
  43. package/dist/node/runtime/sqliteHandshakeStores.d.ts +3 -1
  44. package/dist/node/runtime/sqliteHandshakeStores.d.ts.map +1 -1
  45. package/dist/node/runtime.mjs +265 -93
  46. package/dist/node/runtime.mjs.map +4 -4
  47. package/dist/node/sqliteBestEffortInbox.d.ts +20 -0
  48. package/dist/node/sqliteBestEffortInbox.d.ts.map +1 -0
  49. package/dist/node/sqliteWakeQueue.d.ts +83 -17
  50. package/dist/node/sqliteWakeQueue.d.ts.map +1 -1
  51. 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;
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");
153
335
  }
154
- await unlinkIfMissing(pendingPath);
155
- const wake = await readJsonFile(inflightPath);
156
- if (wake === void 0) {
157
- await unlinkIfMissing(inflightPath);
158
- continue;
159
- }
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
342
  },
172
- async pendingCount() {
173
- await ensureDirs();
174
- return (await readdir(pendingDir)).length;
175
- },
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;
230
406
  } catch (error) {
231
- if (!isNotFound3(error)) throw error;
407
+ if (!isNotFound(error)) throw error;
408
+ }
409
+ try {
410
+ await syncDirectory(directory);
411
+ } catch (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
  }
@@ -1349,6 +1813,8 @@ async function persistMutationIntent(store, intent) {
1349
1813
  }
1350
1814
  async function publisherServiceRequestFingerprint(input) {
1351
1815
  return await handshakeMutationRequestFingerprint({
1816
+ deliveryMode: input.deliveryMode,
1817
+ bestEffort: input.bestEffort ?? null,
1352
1818
  expiresAtBlock: input.expiresAtBlock ?? null,
1353
1819
  maxPositionsPerWake: input.maxPositionsPerWake,
1354
1820
  sources: input.sources,
@@ -1362,12 +1828,14 @@ async function publisherRotationRequestFingerprint(input) {
1362
1828
  keyExpiresAtBlock: input.keyExpiresAtBlock ?? null,
1363
1829
  maxPositionsPerWake: input.maxPositionsPerWake ?? null,
1364
1830
  maxSubscriptionLifetimeBlocks: input.maxSubscriptionLifetimeBlocks ?? null,
1365
- service: HashZod4.parse(input.service),
1831
+ service: HashZod6.parse(input.service),
1366
1832
  validFromBlock: input.validFromBlock ?? null
1367
1833
  });
1368
1834
  }
1369
1835
  async function subscriberSubscriptionRequestFingerprint(input) {
1370
1836
  return await handshakeMutationRequestFingerprint({
1837
+ deliveryMode: input.deliveryMode,
1838
+ bestEffort: input.bestEffort ?? null,
1371
1839
  audience: input.audience,
1372
1840
  challengeSecret: input.challengeSecret,
1373
1841
  deploymentId: input.deploymentId,
@@ -1392,28 +1860,28 @@ function protocolCandidate(reader, discovery, candidate, keys) {
1392
1860
  finalizedHead: discovery.finalizedHead,
1393
1861
  grant: {
1394
1862
  body: candidate.tuple.grant.body,
1395
- objectHash: HashZod4.parse(candidate.tuple.grant.objectHash)
1863
+ objectHash: HashZod6.parse(candidate.tuple.grant.objectHash)
1396
1864
  },
1397
1865
  key: {
1398
1866
  body: candidate.key.body,
1399
- objectHash: HashZod4.parse(candidate.key.objectHash)
1867
+ objectHash: HashZod6.parse(candidate.key.objectHash)
1400
1868
  },
1401
1869
  keys,
1402
1870
  publisher: reader.publisher,
1403
1871
  service: {
1404
1872
  body: candidate.tuple.service.body,
1405
- objectHash: HashZod4.parse(candidate.tuple.service.objectHash)
1873
+ objectHash: HashZod6.parse(candidate.tuple.service.objectHash)
1406
1874
  },
1407
- subscriber: NormalizedAddressZod3.parse(candidate.subscriber),
1875
+ subscriber: NormalizedAddressZod4.parse(candidate.subscriber),
1408
1876
  subscription: {
1409
1877
  body: candidate.tuple.subscription.body,
1410
- objectHash: HashZod4.parse(candidate.tuple.subscription.objectHash)
1878
+ objectHash: HashZod6.parse(candidate.tuple.subscription.objectHash)
1411
1879
  },
1412
1880
  tupleBlock: candidate.tuple.tupleEvent.block,
1413
1881
  tupleObjectHashes: [
1414
- HashZod4.parse(candidate.tuple.service.objectHash),
1415
- HashZod4.parse(candidate.tuple.grant.objectHash),
1416
- 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)
1417
1885
  ],
1418
1886
  tupleSubject: reader.publisher
1419
1887
  };
@@ -1438,7 +1906,7 @@ function createPublisherHandshakeClient(options) {
1438
1906
  const current = await snapshot();
1439
1907
  return await current.readModel.acceptanceCandidates({
1440
1908
  ...serviceQuery(options.reader, current, serviceId),
1441
- ...selector.subscriber === void 0 ? {} : { subscriber: NormalizedAddressZod3.parse(selector.subscriber) },
1909
+ ...selector.subscriber === void 0 ? {} : { subscriber: NormalizedAddressZod4.parse(selector.subscriber) },
1442
1910
  ...selector.subscriptionId === void 0 ? {} : { subscriptionId: HandshakeIdZod3.parse(selector.subscriptionId) }
1443
1911
  });
1444
1912
  }
@@ -1446,8 +1914,8 @@ function createPublisherHandshakeClient(options) {
1446
1914
  const found = await candidateDiscovery(selector);
1447
1915
  if (!found.ok)
1448
1916
  throw new EventKitHandshakeClientError("service-unavailable", `Publisher service is unavailable: ${found.reason}`);
1449
- const subscriber = NormalizedAddressZod3.parse(selector.subscriber);
1450
- 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);
1451
1919
  const candidates = found.candidates.filter((candidate2) => candidate2.subscriber === subscriber && (subscription === void 0 || candidate2.tuple.subscription.objectHash === subscription));
1452
1920
  if (candidates.length === 0) {
1453
1921
  throw new EventKitHandshakeClientError("candidate-not-found", "No exact finalized acceptance candidate was found");
@@ -1560,7 +2028,7 @@ function createPublisherHandshakeClient(options) {
1560
2028
  const reconciliation = await reconcileAcceptance(acceptanceHash, publication, intent.challenge.subscriber, intent.challenge.subscription);
1561
2029
  return Object.freeze({
1562
2030
  acceptance: Object.freeze({ body: intent.acceptance, objectHash: acceptanceHash }),
1563
- challenge: intent.operationId,
2031
+ challenge: intent.acceptance.challenge,
1564
2032
  intentDisposition,
1565
2033
  operationId: intent.operationId,
1566
2034
  publication,
@@ -1603,14 +2071,14 @@ function createPublisherHandshakeClient(options) {
1603
2071
  const found = await candidateDiscovery(selector);
1604
2072
  if (!found.ok || selector.subscription === void 0)
1605
2073
  return found;
1606
- const subscription = HashZod4.parse(selector.subscription);
2074
+ const subscription = HashZod6.parse(selector.subscription);
1607
2075
  return Object.freeze({
1608
2076
  ...found,
1609
2077
  candidates: found.candidates.filter((candidate) => candidate.tuple.subscription.objectHash === subscription)
1610
2078
  });
1611
2079
  },
1612
2080
  async beginChallenge(input) {
1613
- const challengeHash = HashZod4.parse(input.challengeHash);
2081
+ const challengeHash = HashZod6.parse(input.challengeHash);
1614
2082
  const record = await options.challenges.get(challengeHash);
1615
2083
  if (record === void 0)
1616
2084
  return { kind: "missing" };
@@ -1640,9 +2108,9 @@ function createPublisherHandshakeClient(options) {
1640
2108
  },
1641
2109
  async completeChallengeAndAccept(input) {
1642
2110
  await assertPublicationSource(options.publication, options.reader.publisher);
1643
- const challengeHash = HashZod4.parse(input.challengeHash);
1644
- const subscriber = NormalizedAddressZod3.parse(input.subscriber);
1645
- 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);
1646
2114
  const record = await options.challenges.get(challengeHash);
1647
2115
  if (record === void 0) {
1648
2116
  throw new EventKitHandshakeClientError("challenge-not-found", "Endpoint challenge was not found");
@@ -1754,6 +2222,8 @@ function createPublisherHandshakeClient(options) {
1754
2222
  validFromBlock
1755
2223
  };
1756
2224
  const service = createPublisherService({
2225
+ deliveryMode: input.deliveryMode,
2226
+ ...input.bestEffort === void 0 ? {} : { bestEffort: input.bestEffort },
1757
2227
  ...serviceInput,
1758
2228
  authorizationChainId: options.reader.chainId,
1759
2229
  sources: input.sources,
@@ -1775,7 +2245,7 @@ function createPublisherHandshakeClient(options) {
1775
2245
  },
1776
2246
  async retireEncryptionKey(key) {
1777
2247
  await assertPublicationSource(options.publication, options.reader.publisher);
1778
- const keyHash = HashZod4.parse(key);
2248
+ const keyHash = HashZod6.parse(key);
1779
2249
  const requestFingerprint = await exactMutationRequestFingerprint({ key: keyHash });
1780
2250
  let resolvedIntent = await loadMutationIntent(options.intents, options.reader, "publisher-encryption-key-retire", keyHash, options.reader.publisher, requestFingerprint);
1781
2251
  let intentDisposition = "resumed";
@@ -1813,7 +2283,7 @@ function createPublisherHandshakeClient(options) {
1813
2283
  },
1814
2284
  async retireService(expectedService) {
1815
2285
  await assertPublicationSource(options.publication, options.reader.publisher);
1816
- const service = HashZod4.parse(expectedService);
2286
+ const service = HashZod6.parse(expectedService);
1817
2287
  const requestFingerprint = await exactMutationRequestFingerprint({ service });
1818
2288
  let resolvedIntent = await loadMutationIntent(options.intents, options.reader, "publisher-service-retire", service, options.reader.publisher, requestFingerprint);
1819
2289
  let intentDisposition = "resumed";
@@ -1869,10 +2339,12 @@ function createPublisherHandshakeClient(options) {
1869
2339
  preferredEncryptionKey: generated.descriptorHash,
1870
2340
  revision: selected.service.body.revision + 1,
1871
2341
  serviceId: selected.service.body.serviceId,
1872
- supersedes: HashZod4.parse(selected.service.objectHash),
2342
+ supersedes: HashZod6.parse(selected.service.objectHash),
1873
2343
  validFromBlock
1874
2344
  };
1875
2345
  const service = createPublisherService({
2346
+ deliveryMode: selected.service.body.deliveryMode,
2347
+ ...selected.service.body.bestEffort === void 0 ? {} : { bestEffort: selected.service.body.bestEffort },
1876
2348
  ...serviceInput,
1877
2349
  authorizationChainId: options.reader.chainId,
1878
2350
  sources: selected.service.body.events.map((event) => event.source),
@@ -1886,8 +2358,8 @@ function createPublisherHandshakeClient(options) {
1886
2358
  privateKey: generated.privateKey,
1887
2359
  publisher: options.reader.publisher,
1888
2360
  requestFingerprint,
1889
- selectedKey: HashZod4.parse(selected.key.objectHash),
1890
- selectedService: HashZod4.parse(selected.service.objectHash),
2361
+ selectedKey: HashZod6.parse(selected.key.objectHash),
2362
+ selectedService: HashZod6.parse(selected.service.objectHash),
1891
2363
  service,
1892
2364
  source: options.reader.publisher,
1893
2365
  version: 1
@@ -1914,7 +2386,7 @@ function tupleHashes(view) {
1914
2386
  }
1915
2387
  function createSubscriberHandshakeClient(options) {
1916
2388
  assertPublicationIdentity(options.publication, options.reader);
1917
- const subscriber = NormalizedAddressZod3.parse(options.subscriber);
2389
+ const subscriber = NormalizedAddressZod4.parse(options.subscriber);
1918
2390
  const serviceId = options.serviceId === void 0 ? void 0 : HandshakeIdZod3.parse(options.serviceId);
1919
2391
  async function snapshot() {
1920
2392
  return readySnapshot(await options.reader.sync());
@@ -1994,7 +2466,7 @@ function createSubscriberHandshakeClient(options) {
1994
2466
  const client = {
1995
2467
  async revokeGrant(grant) {
1996
2468
  await assertPublicationSource(options.publication, subscriber);
1997
- const grantHash = HashZod4.parse(grant);
2469
+ const grantHash = HashZod6.parse(grant);
1998
2470
  const requestFingerprint = await exactMutationRequestFingerprint({ grant: grantHash });
1999
2471
  let resolvedIntent = await loadMutationIntent(options.intents, options.reader, "subscriber-grant-revoke", grantHash, subscriber, requestFingerprint);
2000
2472
  let intentDisposition = "resumed";
@@ -2033,7 +2505,7 @@ function createSubscriberHandshakeClient(options) {
2033
2505
  },
2034
2506
  async revokeSubscription(subscription) {
2035
2507
  await assertPublicationSource(options.publication, subscriber);
2036
- const subscriptionHash = HashZod4.parse(subscription);
2508
+ const subscriptionHash = HashZod6.parse(subscription);
2037
2509
  const requestFingerprint = await exactMutationRequestFingerprint({ subscription: subscriptionHash });
2038
2510
  let resolvedIntent = await loadMutationIntent(options.intents, options.reader, "subscriber-subscription-revoke", subscriptionHash, subscriber, requestFingerprint);
2039
2511
  let intentDisposition = "resumed";
@@ -2075,7 +2547,7 @@ function createSubscriberHandshakeClient(options) {
2075
2547
  return await current.readModel.discoverPublisherService(serviceQuery(options.reader, current, serviceId));
2076
2548
  },
2077
2549
  async subscription(subscription) {
2078
- const subscriptionHash = HashZod4.parse(subscription);
2550
+ const subscriptionHash = HashZod6.parse(subscription);
2079
2551
  const current = await options.reader.sync();
2080
2552
  const view = await lookupAt(current);
2081
2553
  if (!view.ok)
@@ -2133,12 +2605,12 @@ function createSubscriberHandshakeClient(options) {
2133
2605
  grant: grantHash,
2134
2606
  publisher: options.reader.publisher,
2135
2607
  publisherKey: {
2136
- encryptionKey: HashZod4.parse(selected.key.objectHash),
2608
+ encryptionKey: HashZod6.parse(selected.key.objectHash),
2137
2609
  keyId: selected.key.body.keyId,
2138
2610
  publicKey: selected.key.body.publicKey
2139
2611
  },
2140
2612
  ...input.revision === void 0 ? {} : { revision: input.revision },
2141
- service: HashZod4.parse(selected.service.objectHash),
2613
+ service: HashZod6.parse(selected.service.objectHash),
2142
2614
  subscriber,
2143
2615
  ...input.subscriptionId === void 0 ? {} : { subscriptionId: input.subscriptionId },
2144
2616
  ...input.supersedes === void 0 ? {} : { supersedes: input.supersedes }
@@ -2152,8 +2624,8 @@ function createSubscriberHandshakeClient(options) {
2152
2624
  operationId,
2153
2625
  publisher: options.reader.publisher,
2154
2626
  requestFingerprint,
2155
- selectedKey: HashZod4.parse(selected.key.objectHash),
2156
- selectedService: HashZod4.parse(selected.service.objectHash),
2627
+ selectedKey: HashZod6.parse(selected.key.objectHash),
2628
+ selectedService: HashZod6.parse(selected.service.objectHash),
2157
2629
  service: selected.service.body,
2158
2630
  source: subscriber,
2159
2631
  subscription,
@@ -2163,30 +2635,570 @@ function createSubscriberHandshakeClient(options) {
2163
2635
  },
2164
2636
  subscriptions: async (subscriptionId) => await lookup2(subscriptionId)
2165
2637
  };
2166
- return Object.freeze(client);
2638
+ return Object.freeze(client);
2639
+ }
2640
+ function subscriberPlaintext(service, input, maxPositionsPerWake) {
2641
+ if (input.deliveryMode !== service.deliveryMode || canonicalJson2(input.bestEffort ?? null) !== canonicalJson2(service.bestEffort ?? null))
2642
+ throw new Error("Requested delivery terms do not match exact selected service");
2643
+ const plaintextInput = {
2644
+ audience: input.audience,
2645
+ challengeSecret: input.challengeSecret,
2646
+ deploymentId: input.deploymentId,
2647
+ endpoint: input.endpoint,
2648
+ queueId: input.queueId
2649
+ };
2650
+ const source = input.source;
2651
+ if (service.events.every((event) => canonicalJson2(event.source) !== canonicalJson2(source))) {
2652
+ throw new Error("Requested source is not advertised by the selected service");
2653
+ }
2654
+ return createWakeSubscriptionPlaintext({
2655
+ deliveryMode: input.deliveryMode,
2656
+ ...input.bestEffort === void 0 ? {} : { bestEffort: input.bestEffort },
2657
+ ...plaintextInput,
2658
+ source,
2659
+ fromPosition: input.fromPosition,
2660
+ maxPositionsPerWake
2661
+ });
2662
+ }
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
+ };
2167
3118
  }
2168
- function subscriberPlaintext(service, input, maxPositionsPerWake) {
2169
- const plaintextInput = {
2170
- audience: input.audience,
2171
- challengeSecret: input.challengeSecret,
2172
- deploymentId: input.deploymentId,
2173
- endpoint: input.endpoint,
2174
- queueId: input.queueId
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 }
2175
3160
  };
2176
- const source = input.source;
2177
- if (service.events.every((event) => canonicalJson(event.source) !== canonicalJson(source))) {
2178
- throw new Error("Requested source is not advertised by the selected service");
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 });
2179
3180
  }
2180
- return createWakeSubscriptionPlaintext({
2181
- ...plaintextInput,
2182
- source,
2183
- fromPosition: input.fromPosition,
2184
- maxPositionsPerWake
2185
- });
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 };
2186
3198
  }
2187
3199
 
2188
3200
  // src/runtime/boundedFetch.ts
2189
- import { Buffer as Buffer2 } from "node:buffer";
3201
+ import { Buffer as Buffer3 } from "node:buffer";
2190
3202
 
2191
3203
  // src/runtime/abort.ts
2192
3204
  import { clearTimeout, setTimeout } from "node:timers";
@@ -2228,13 +3240,13 @@ function createBoundedPublisherFetch(options = {}) {
2228
3240
  ...init?.signal == null ? [] : [init.signal],
2229
3241
  ...input instanceof Request ? [input.signal] : []
2230
3242
  ];
2231
- const scope = publisherAbortScope(signals, timeout);
3243
+ const scope2 = publisherAbortScope(signals, timeout);
2232
3244
  try {
2233
- scope.signal.throwIfAborted();
3245
+ scope2.signal.throwIfAborted();
2234
3246
  const response = await nativeFetch(input, {
2235
3247
  ...init,
2236
3248
  redirect: "error",
2237
- signal: scope.signal
3249
+ signal: scope2.signal
2238
3250
  });
2239
3251
  if (response.body === null)
2240
3252
  return response;
@@ -2262,13 +3274,13 @@ function createBoundedPublisherFetch(options = {}) {
2262
3274
  headers.delete("content-encoding");
2263
3275
  headers.delete("transfer-encoding");
2264
3276
  headers.set("content-length", String(size));
2265
- return new Response(Buffer2.concat(chunks), {
3277
+ return new Response(Buffer3.concat(chunks), {
2266
3278
  status: response.status,
2267
3279
  statusText: response.statusText,
2268
3280
  headers
2269
3281
  });
2270
3282
  } finally {
2271
- scope.dispose();
3283
+ scope2.dispose();
2272
3284
  }
2273
3285
  })();
2274
3286
  active.add(operation);
@@ -2293,7 +3305,7 @@ function createBoundedPublisherEthersTransport(options = {}) {
2293
3305
  cancellation?.addListener(() => controller.abort());
2294
3306
  cancellation?.checkSignal();
2295
3307
  const response = await bounded.fetchFn(request2.url, {
2296
- body: request2.body === null ? void 0 : Buffer2.from(request2.body),
3308
+ body: request2.body === null ? void 0 : Buffer3.from(request2.body),
2297
3309
  headers: request2.headers,
2298
3310
  method: request2.method,
2299
3311
  signal: controller.signal
@@ -2317,6 +3329,7 @@ function createBoundedPublisherEthersTransport(options = {}) {
2317
3329
  }
2318
3330
 
2319
3331
  // src/runtime/handshakeDiscovery.ts
3332
+ import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
2320
3333
  import { decryptWakeSubscription, sourceWakeFilterHash } from "@xyo-network/event-kit-protocol";
2321
3334
  import { EndpointChallengeResponseZod as EndpointChallengeResponseZod2 } from "@xyo-network/event-kit-schemas";
2322
3335
  function createPublisherHandshakeDiscovery(options) {
@@ -2332,6 +3345,9 @@ function createPublisherHandshakeDiscovery(options) {
2332
3345
  rejectedSubscriptions: 0,
2333
3346
  challengeFailures: 0
2334
3347
  };
3348
+ function selectedTuple(tuple) {
3349
+ return (options.serviceHash === void 0 || tuple.service.objectHash === options.serviceHash) && (options.deliveryMode === void 0 || tuple.service.body.deliveryMode === options.deliveryMode) && (options.deliveryMode === void 0 || tuple.subscription.body.deliveryMode === options.deliveryMode) && (options.deliveryMode === void 0 || isDeepStrictEqual2(tuple.service.body.bestEffort, options.bestEffort));
3350
+ }
2335
3351
  async function activeView(signal) {
2336
3352
  signal.throwIfAborted();
2337
3353
  viewing ??= (async () => {
@@ -2349,7 +3365,7 @@ function createPublisherHandshakeDiscovery(options) {
2349
3365
  ...status,
2350
3366
  finalizedHead: view.finalizedHead,
2351
3367
  indexedThrough: view.indexedThrough,
2352
- pendingSubscriptions: view.subscriptions.filter((item) => item.state === "pending").length,
3368
+ pendingSubscriptions: view.subscriptions.filter((item) => item.state === "pending" && item.tuples.some(selectedTuple)).length,
2353
3369
  rejectedSubscriptions: view.rejections.length
2354
3370
  };
2355
3371
  return view;
@@ -2414,7 +3430,7 @@ function createPublisherHandshakeDiscovery(options) {
2414
3430
  if (!found.ok)
2415
3431
  throw new Error("Publisher acceptance candidates are unavailable");
2416
3432
  const offset = options.state.get("challenge-rate", "offset") ?? 0;
2417
- const candidates = found.candidates;
3433
+ const candidates = found.candidates.filter((candidate) => selectedTuple(candidate.tuple));
2418
3434
  let attempted = 0;
2419
3435
  for (let index = 0; index < Math.min(candidates.length, 128) && attempted < 4; index += 1) {
2420
3436
  const candidate = candidates[(offset + index) % candidates.length];
@@ -2430,30 +3446,40 @@ function createPublisherHandshakeDiscovery(options) {
2430
3446
  await acceptCandidate(candidate.subscriber, candidate.tuple.subscription.objectHash, signal);
2431
3447
  }
2432
3448
  }
3449
+ async function loadScopedKeys(view, active) {
3450
+ const scopedKeys = active.flatMap((item) => item.tuples.filter((tuple) => tuple.subscription.objectHash === item.selectedSubscription).map((tuple) => ({
3451
+ encryptionKey: tuple.subscription.body.encryptionKey,
3452
+ keyId: tuple.subscription.body.keyId,
3453
+ publisher: options.publisher
3454
+ })));
3455
+ if (view.hasCurrentAdvertisement && (options.serviceHash === void 0 || view.service.objectHash === options.serviceHash) && (options.deliveryMode === void 0 || view.service.body.deliveryMode === options.deliveryMode)) {
3456
+ scopedKeys.push({
3457
+ encryptionKey: view.key.objectHash,
3458
+ keyId: view.key.body.keyId,
3459
+ publisher: options.publisher
3460
+ });
3461
+ }
3462
+ const loadedKeys = await Promise.all(scopedKeys.map(async (key) => await options.keys.getPrivateKey(key)));
3463
+ status = {
3464
+ ...status,
3465
+ keyLoaded: scopedKeys.length > 0 && loadedKeys.every((key) => key !== void 0),
3466
+ activeKeyIds: [...new Set(scopedKeys.map((key) => key.keyId))]
3467
+ };
3468
+ }
2433
3469
  return {
2434
3470
  async discover(signal) {
2435
3471
  const view = await activeView(signal);
2436
- const key = view.hasCurrentAdvertisement ? view.key : void 0;
2437
- const loaded = key === void 0 ? void 0 : await options.keys.getPrivateKey({
2438
- encryptionKey: key.objectHash,
2439
- keyId: key.body.keyId,
2440
- publisher: options.publisher
2441
- });
2442
- status = {
2443
- ...status,
2444
- keyLoaded: loaded !== void 0,
2445
- activeKeyIds: key === void 0 ? [] : [key.body.keyId]
2446
- };
2447
- const subscriptions = [];
2448
- const active = view.subscriptions.filter((item) => item.state === "active");
3472
+ const active = view.subscriptions.filter((item) => item.state === "active" && item.tuples.some((tuple) => tuple.subscription.objectHash === item.selectedSubscription && selectedTuple(tuple)));
3473
+ await loadScopedKeys(view, active);
2449
3474
  if (active.length > (options.maxSubscriptions ?? 64))
2450
3475
  throw new Error("Publisher active subscription limit exceeded");
3476
+ const subscriptions = [];
2451
3477
  for (const lifecycle of active) {
2452
3478
  if (lifecycle.state !== "active" || lifecycle.activeFromBlock === void 0)
2453
3479
  continue;
2454
3480
  try {
2455
3481
  const tuple = lifecycle.tuples.find((item) => item.subscription.objectHash === lifecycle.selectedSubscription);
2456
- if (tuple === void 0)
3482
+ if (tuple === void 0 || !selectedTuple(tuple))
2457
3483
  throw new Error("Active publisher subscription has no selected tuple");
2458
3484
  const encrypted = tuple.subscription.body;
2459
3485
  if (encrypted.chainId !== options.authorizationChainId)
@@ -2473,6 +3499,9 @@ function createPublisherHandshakeDiscovery(options) {
2473
3499
  options.transport.assertAllowed(plaintext.endpoint);
2474
3500
  subscriptions.push({
2475
3501
  subscriptionHash: tuple.subscription.objectHash,
3502
+ serviceHash: tuple.service.objectHash,
3503
+ deliveryMode: plaintext.filter.deliveryMode,
3504
+ ...plaintext.filter.bestEffort === void 0 ? {} : { bestEffort: plaintext.filter.bestEffort },
2476
3505
  authorizationChainId: options.authorizationChainId,
2477
3506
  controller: tuple.subscriber,
2478
3507
  audience: plaintext.audience,
@@ -2492,16 +3521,16 @@ function createPublisherHandshakeDiscovery(options) {
2492
3521
  },
2493
3522
  async isActive(subscription, signal) {
2494
3523
  const view = await activeView(signal);
2495
- return view.subscriptions.some((item) => item.state === "active" && item.selectedSubscription === subscription.subscriptionHash && item.tuples.some((tuple) => tuple.subscription.objectHash === subscription.subscriptionHash && tuple.subscriber === subscription.controller));
3524
+ return view.subscriptions.some((item) => item.state === "active" && item.selectedSubscription === subscription.subscriptionHash && item.tuples.some((tuple) => tuple.subscription.objectHash === subscription.subscriptionHash && tuple.subscriber === subscription.controller && selectedTuple(tuple)));
2496
3525
  },
2497
3526
  async acceptPending(signal) {
2498
3527
  if (accepting === void 0) {
2499
- const scope = publisherAbortScope([owner.signal, signal]);
3528
+ const scope2 = publisherAbortScope([owner.signal, signal]);
2500
3529
  accepting = (async () => {
2501
3530
  try {
2502
- await accept(scope.signal);
3531
+ await accept(scope2.signal);
2503
3532
  } finally {
2504
- scope.dispose();
3533
+ scope2.dispose();
2505
3534
  accepting = void 0;
2506
3535
  }
2507
3536
  })();
@@ -2516,25 +3545,25 @@ function createPublisherHandshakeDiscovery(options) {
2516
3545
  }
2517
3546
 
2518
3547
  // src/runtime/httpsTransport.ts
2519
- import { Buffer as Buffer3 } from "node:buffer";
3548
+ import { Buffer as Buffer4 } from "node:buffer";
2520
3549
  import { lookup } from "node:dns/promises";
2521
3550
  import { request } from "node:https";
2522
3551
  import { BlockList, isIP } from "node:net";
2523
- import { z as z2 } from "zod";
3552
+ import { z as z4 } from "zod";
2524
3553
 
2525
3554
  // src/runtime/sources.ts
2526
- import { createHash as createHash3 } from "node:crypto";
2527
- import { canonicalJson as canonicalJson2 } from "@xyo-network/event-kit-protocol";
2528
- 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";
2529
3558
  import { PayloadBuilder } from "@xyo-network/sdk";
2530
3559
  import { asXL1BlockNumber } from "@xyo-network/xl1-sdk";
2531
3560
  import { FetchRequest, JsonRpcProvider } from "ethers";
2532
3561
  function publisherSourceKey(source) {
2533
- return createHash3("sha256").update(canonicalJson2(WakeSourceZod.parse(source))).digest("hex");
3562
+ return createHash4("sha256").update(canonicalJson3(WakeSourceZod2.parse(source))).digest("hex");
2534
3563
  }
2535
3564
  async function boundedPublisherOperation(operation, signal, timeoutMs = 1e4) {
2536
- const scope = publisherAbortScope([signal], timeoutMs);
2537
- const combined = scope.signal;
3565
+ const scope2 = publisherAbortScope([signal], timeoutMs);
3566
+ const combined = scope2.signal;
2538
3567
  let rejectAbort;
2539
3568
  const aborted = new Promise((_resolve, reject) => {
2540
3569
  rejectAbort = () => reject(new Error("Publisher operation aborted or timed out"));
@@ -2546,11 +3575,11 @@ async function boundedPublisherOperation(operation, signal, timeoutMs = 1e4) {
2546
3575
  } finally {
2547
3576
  if (rejectAbort !== void 0)
2548
3577
  combined.removeEventListener("abort", rejectAbort);
2549
- scope.dispose();
3578
+ scope2.dispose();
2550
3579
  }
2551
3580
  }
2552
3581
  function createXl1PublisherSource(options) {
2553
- const source = WakeSourceZod.parse(options.source);
3582
+ const source = WakeSourceZod2.parse(options.source);
2554
3583
  if (source.kind !== "xl1-finalized")
2555
3584
  throw new Error("Expected an XL1 finalized source");
2556
3585
  return {
@@ -2574,7 +3603,7 @@ function createXl1PublisherSource(options) {
2574
3603
  };
2575
3604
  }
2576
3605
  function createEthereumFinalizedPublisherSource(options) {
2577
- const source = WakeSourceZod.parse(options.source);
3606
+ const source = WakeSourceZod2.parse(options.source);
2578
3607
  if (source.kind !== "ethereum-finalized")
2579
3608
  throw new Error("Expected an Ethereum finalized source");
2580
3609
  const { chainId, genesisHash } = source;
@@ -2640,7 +3669,7 @@ function createEthereumFinalizedPublisherSource(options) {
2640
3669
  };
2641
3670
  }
2642
3671
  function createUtcIntervalPublisherSource(options) {
2643
- const source = WakeSourceZod.parse(options.source);
3672
+ const source = WakeSourceZod2.parse(options.source);
2644
3673
  if (source.kind !== "utc-interval")
2645
3674
  throw new Error("Expected a UTC interval source");
2646
3675
  const nowSeconds = options.nowSeconds ?? (() => Math.floor(Date.now() / 1e3));
@@ -2657,14 +3686,14 @@ function createUtcIntervalPublisherSource(options) {
2657
3686
  }
2658
3687
 
2659
3688
  // src/runtime/httpsTransport.ts
2660
- var OptionsZod = z2.strictObject({
2661
- allowedEndpoints: z2.array(z2.url()).min(1).max(64),
2662
- endpointHeaders: z2.array(z2.strictObject({
2663
- endpoint: z2.url(),
2664
- 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) })
2665
3694
  })).max(64).default([]),
2666
- maxResponseBytes: z2.number().int().min(1).max(1048576).default(65536),
2667
- 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)
2668
3697
  });
2669
3698
  var NON_PUBLIC_IPV4_SUBNETS = [
2670
3699
  ["0.0.0.0", 8],
@@ -2744,8 +3773,8 @@ function createPublisherHttpsTransport(input) {
2744
3773
  assertAllowed(endpoint);
2745
3774
  signal.throwIfAborted();
2746
3775
  const url = new URL(endpoint);
2747
- const scope = publisherAbortScope([signal], options.requestTimeoutMs);
2748
- const combined = scope.signal;
3776
+ const scope2 = publisherAbortScope([signal], options.requestTimeoutMs);
3777
+ const combined = scope2.signal;
2749
3778
  try {
2750
3779
  const addresses = await boundedPublisherOperation(lookup(url.hostname.replaceAll(/^\[|\]$/gu, ""), { all: true, verbatim: true }), combined, options.requestTimeoutMs);
2751
3780
  combined.throwIfAborted();
@@ -2754,7 +3783,7 @@ function createPublisherHttpsTransport(input) {
2754
3783
  const selected = addresses[0];
2755
3784
  if (selected === void 0)
2756
3785
  throw new Error("Publisher DNS lookup was empty");
2757
- const encoded = Buffer3.from(JSON.stringify(body));
3786
+ const encoded = Buffer4.from(JSON.stringify(body));
2758
3787
  if (encoded.byteLength > 65536)
2759
3788
  throw new Error("Publisher request body is too large");
2760
3789
  return await new Promise((resolve, reject) => {
@@ -2795,7 +3824,7 @@ function createPublisherHttpsTransport(input) {
2795
3824
  response.on("error", () => reject(new Error("Publisher HTTPS response failed")));
2796
3825
  response.on("end", () => {
2797
3826
  try {
2798
- 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"));
2799
3828
  const retry = Number(response.headers["retry-after"]);
2800
3829
  resolve({
2801
3830
  status,
@@ -2811,42 +3840,152 @@ function createPublisherHttpsTransport(input) {
2811
3840
  call.end(encoded);
2812
3841
  });
2813
3842
  } finally {
2814
- scope.dispose();
3843
+ scope2.dispose();
2815
3844
  }
2816
3845
  }
2817
3846
  };
2818
3847
  }
2819
3848
 
2820
3849
  // src/runtime/publisherRuntime.ts
2821
- import { randomBytes as randomBytes6 } from "node:crypto";
2822
- import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
3850
+ import { isDeepStrictEqual as isDeepStrictEqual4 } from "node:util";
2823
3851
  import { createMemoryLocator, PeriodicActor } from "@ariestools/actor";
2824
3852
  import { creatable } from "@ariestools/sdk";
2825
- import { WakeEnvelopeSchema, WakeEnvelopeZod as WakeEnvelopeZod2 } from "@xyo-network/event-kit-schemas";
2826
- import { z as z3 } from "zod";
3853
+ import { BestEffortAdmissionAcknowledgmentZod } from "@xyo-network/event-kit-protocol";
3854
+ import { BestEffortDeliveryPolicyZod, WakeDeliveryModeZod } from "@xyo-network/event-kit-schemas";
3855
+ import { z as z5 } from "zod";
3856
+
3857
+ // src/runtime/publisherDeliveryState.ts
3858
+ import { randomBytes as randomBytes6 } from "node:crypto";
3859
+ import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
3860
+ import { WakeEnvelopeSchema as WakeEnvelopeSchema2, WakeEnvelopeZod as WakeEnvelopeZod2 } from "@xyo-network/event-kit-schemas";
3861
+ function assertDeliveryState(record, subscription) {
3862
+ if (!isDeepStrictEqual3(record.subscription, subscription))
3863
+ throw new Error("Subscription revision changed without a new identity");
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))
3865
+ throw new Error("Persisted publisher delivery state is invalid");
3866
+ if (record.pending === void 0) return;
3867
+ const pending = WakeEnvelopeZod2.parse(record.pending);
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))
3869
+ throw new Error("Persisted publisher outbox binding is invalid");
3870
+ }
3871
+ function makeEvent(subscription, fromPosition, toPosition, now, wakeId = randomBytes6(16).toString("hex")) {
3872
+ return WakeEnvelopeZod2.parse({
3873
+ schema: WakeEnvelopeSchema2,
3874
+ authorizationChainId: subscription.authorizationChainId,
3875
+ deliveryMode: subscription.deliveryMode,
3876
+ deploymentId: subscription.deploymentId,
3877
+ filterHash: subscription.filterHash,
3878
+ fromPosition,
3879
+ issuedAt: now,
3880
+ source: subscription.source,
3881
+ toPosition,
3882
+ wakeId,
3883
+ ...subscription.queueId === void 0 ? {} : { queueId: subscription.queueId }
3884
+ });
3885
+ }
3886
+ var DeliveryResponseError = class extends Error {
3887
+ retryAfterMs;
3888
+ status;
3889
+ constructor(status, retryAfterMs) {
3890
+ super("Publisher admission failed");
3891
+ this.status = status;
3892
+ this.retryAfterMs = retryAfterMs;
3893
+ }
3894
+ get retryable() {
3895
+ return this.status === 408 || this.status === 429 || this.status >= 500;
3896
+ }
3897
+ };
3898
+ function prepareBestEffortDelivery(options) {
3899
+ const {
3900
+ bestEffort,
3901
+ first,
3902
+ heads,
3903
+ now,
3904
+ record,
3905
+ state
3906
+ } = options;
3907
+ const subscription = record.subscription;
3908
+ const head = heads.get(publisherSourceKey(subscription.source));
3909
+ const prior = record.pending;
3910
+ const expired = isHintExpired(record, now, bestEffort);
3911
+ if (head === void 0 || head instanceof Error || head.position < subscription.fromPosition) {
3912
+ if (prior !== void 0 && !expired) return { ...record, pending: prior };
3913
+ if (prior !== void 0) state.set("best-effort-deliveries", subscription.subscriptionHash, {
3914
+ ...record,
3915
+ pending: void 0,
3916
+ abandoned: (record.abandoned ?? 0) + 1
3917
+ });
3918
+ return;
3919
+ }
3920
+ const refresh = refreshDue(record, now, bestEffort);
3921
+ if (freshHintRequired(record, head.position, first, refresh, expired)) {
3922
+ const pending = makeEvent(subscription, head.position, head.position, now);
3923
+ const prepared = {
3924
+ ...record,
3925
+ pending,
3926
+ attempts: 0,
3927
+ lastHintAt: now,
3928
+ lastHintTarget: head.position,
3929
+ superseded: (record.superseded ?? 0) + (prior !== void 0 && !expired ? 1 : 0),
3930
+ abandoned: (record.abandoned ?? 0) + (expired ? 1 : 0),
3931
+ nextAttemptMs: 0,
3932
+ state: "active"
3933
+ };
3934
+ state.set("best-effort-deliveries", subscription.subscriptionHash, prepared);
3935
+ return prepared;
3936
+ }
3937
+ if (expired) state.set("best-effort-deliveries", subscription.subscriptionHash, {
3938
+ ...record,
3939
+ pending: void 0,
3940
+ abandoned: (record.abandoned ?? 0) + 1
3941
+ });
3942
+ return prior === void 0 || expired ? void 0 : { ...record, pending: prior };
3943
+ }
3944
+ function isHintExpired(record, now, policy) {
3945
+ return record.pending !== void 0 && (now > record.pending.issuedAt + policy.maxHintAgeSeconds || (record.attempts ?? 0) >= policy.maxAttempts);
3946
+ }
3947
+ function refreshDue(record, now, policy) {
3948
+ return now - (record.lastHintAt ?? record.lastNotifiedAt ?? 0) >= policy.refreshIntervalSeconds;
3949
+ }
3950
+ function freshHintRequired(record, head, first, refresh, expired) {
3951
+ return first || head > (record.pending?.toPosition ?? record.lastHintTarget ?? record.cursor) || refresh && (record.pending === void 0 || expired);
3952
+ }
3953
+ function failedDelivery(record, current, mode, error, now) {
3954
+ const bestEffort = mode === "best-effort";
3955
+ const wait = error instanceof DeliveryResponseError && error.retryAfterMs !== void 0 ? error.retryAfterMs : Math.min(3e5, 1e3 * 2 ** Math.min(record.failures, 8));
3956
+ return {
3957
+ ...record,
3958
+ failures: record.failures + 1,
3959
+ totalAttempts: bestEffort ? current?.totalAttempts ?? record.totalAttempts ?? 0 : record.totalAttempts,
3960
+ attempts: bestEffort ? current?.attempts ?? record.attempts ?? 0 : record.attempts,
3961
+ state: !bestEffort && error instanceof DeliveryResponseError && !error.retryable ? "blocked" : "active",
3962
+ nextAttemptMs: now * 1e3 + Math.min(36e5, wait),
3963
+ lastError: "delivery-failed"
3964
+ };
3965
+ }
2827
3966
 
2828
3967
  // src/runtime/sourceSigner.ts
2829
- import { randomBytes as randomBytes5 } from "node:crypto";
3968
+ import { randomBytes as randomBytes7 } from "node:crypto";
2830
3969
  import {
2831
- NormalizedAddressZod as NormalizedAddressZod4,
2832
- WakeEnvelopeZod,
3970
+ NormalizedAddressZod as NormalizedAddressZod7,
3971
+ WakeEnvelopeZod as WakeEnvelopeZod3,
2833
3972
  WakeJwtSchema
2834
3973
  } from "@xyo-network/event-kit-schemas";
2835
3974
  import { PayloadBuilder as PayloadBuilder2 } from "@xyo-network/sdk";
2836
3975
  function createAccountSourceWakeSigner(options) {
2837
- const publisher = NormalizedAddressZod4.parse(options.publisher);
3976
+ const publisher = NormalizedAddressZod7.parse(options.publisher);
2838
3977
  if (String(options.account.address).replace(/^0x/u, "").toLowerCase() !== publisher)
2839
3978
  throw new Error("Publisher signing account mismatch");
2840
3979
  return {
2841
3980
  async sign(input, audience) {
2842
- const event = WakeEnvelopeZod.parse(input);
3981
+ const event = WakeEnvelopeZod3.parse(input);
2843
3982
  const eventHash = await PayloadBuilder2.dataHash(event);
2844
3983
  const now = options.nowSeconds?.() ?? Math.floor(Date.now() / 1e3);
2845
3984
  if (!Number.isSafeInteger(now) || now < 0)
2846
3985
  throw new Error("Publisher signing clock is invalid");
2847
3986
  const { token: jwt } = await options.account.signJwt({
2848
3987
  audience,
2849
- claims: { eventHash, jti: randomBytes5(16).toString("hex") },
3988
+ claims: { eventHash, jti: randomBytes7(16).toString("hex") },
2850
3989
  now,
2851
3990
  schema: WakeJwtSchema,
2852
3991
  ttl: 60
@@ -2865,30 +4004,6 @@ function createAccountSourceWakeSigner(options) {
2865
4004
  function activationReady(status) {
2866
4005
  return (status?.keyLoaded ?? true) && (status?.pendingSubscriptions ?? 0) === 0;
2867
4006
  }
2868
- function assertDeliveryState(record, subscription) {
2869
- if (!isDeepStrictEqual2(record.subscription, subscription))
2870
- throw new Error("Subscription revision changed without a new identity");
2871
- if (!Number.isSafeInteger(record.cursor) || record.cursor < subscription.fromPosition - 1 || [record.delivered, record.failures, record.nextAttemptMs].some((value) => !(Number.isSafeInteger(value) && value >= 0)) || !["active", "blocked", "cancelled"].includes(record.state))
2872
- throw new Error("Persisted publisher delivery state is invalid");
2873
- if (record.pending === void 0) return;
2874
- const pending = WakeEnvelopeZod2.parse(record.pending);
2875
- if (pending.fromPosition !== record.cursor + 1 || pending.toPosition - pending.fromPosition + 1 > subscription.maxPositionsPerWake || pending.authorizationChainId !== subscription.authorizationChainId || pending.deploymentId !== subscription.deploymentId || pending.queueId !== subscription.queueId || pending.filterHash !== subscription.filterHash || !isDeepStrictEqual2(pending.source, subscription.source))
2876
- throw new Error("Persisted publisher outbox binding is invalid");
2877
- }
2878
- function makeEvent(subscription, fromPosition, toPosition, now, wakeId = randomBytes6(16).toString("hex")) {
2879
- return WakeEnvelopeZod2.parse({
2880
- schema: WakeEnvelopeSchema,
2881
- authorizationChainId: subscription.authorizationChainId,
2882
- deploymentId: subscription.deploymentId,
2883
- filterHash: subscription.filterHash,
2884
- fromPosition,
2885
- issuedAt: now,
2886
- source: subscription.source,
2887
- toPosition,
2888
- wakeId,
2889
- ...subscription.queueId === void 0 ? {} : { queueId: subscription.queueId }
2890
- });
2891
- }
2892
4007
  var PublisherRuntimeActor = class extends PeriodicActor {
2893
4008
  async runPass(context) {
2894
4009
  await this.params.tick(context.signal);
@@ -2897,26 +4012,30 @@ var PublisherRuntimeActor = class extends PeriodicActor {
2897
4012
  PublisherRuntimeActor = __decorateClass([
2898
4013
  creatable()
2899
4014
  ], PublisherRuntimeActor);
2900
- var TimingZod = z3.object({
2901
- concurrency: z3.number().int().min(1).max(32).default(4),
2902
- intervalMs: z3.number().int().min(100).max(6e4).default(1e3),
2903
- maxSubscriptions: z3.number().int().min(1).max(1024).default(64),
2904
- 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)
2905
4020
  });
2906
- var DeliveryResponseError = class extends Error {
2907
- retryAfterMs;
2908
- status;
2909
- constructor(status, retryAfterMs) {
2910
- super("Publisher admission failed");
2911
- this.status = status;
2912
- this.retryAfterMs = retryAfterMs;
2913
- }
2914
- get retryable() {
2915
- return this.status === 408 || this.status === 429 || this.status >= 500;
2916
- }
2917
- };
4021
+ function runtimeSettings(options) {
4022
+ const deliveryMode = WakeDeliveryModeZod.parse(options.deliveryMode);
4023
+ const bestEffort = options.bestEffort === void 0 ? void 0 : BestEffortDeliveryPolicyZod.parse(options.bestEffort);
4024
+ if (deliveryMode === "best-effort" !== (bestEffort !== void 0)) throw new TypeError("Explicit delivery mode requires its exact policy");
4025
+ return {
4026
+ bestEffort,
4027
+ deliveryMode,
4028
+ deliveryNamespace: deliveryMode === "best-effort" ? "best-effort-deliveries" : "deliveries",
4029
+ timing: TimingZod.parse(options)
4030
+ };
4031
+ }
2918
4032
  async function createPersistentEventPublisherRuntime(options) {
2919
- const timing = TimingZod.parse(options);
4033
+ const {
4034
+ bestEffort,
4035
+ deliveryMode,
4036
+ deliveryNamespace,
4037
+ timing
4038
+ } = runtimeSettings(options);
2920
4039
  if (options.sources.length < 1 || options.sources.length > 16)
2921
4040
  throw new Error("Publisher requires between one and sixteen bounded sources");
2922
4041
  const clock = options.nowSeconds ?? (() => Math.floor(Date.now() / 1e3));
@@ -2928,7 +4047,12 @@ async function createPersistentEventPublisherRuntime(options) {
2928
4047
  const sourceReaders = new Map(options.sources.map((reader) => [publisherSourceKey(reader.source), reader]));
2929
4048
  if (sourceReaders.size !== options.sources.length)
2930
4049
  throw new Error("Duplicate publisher source identity");
4050
+ const state = options.state;
4051
+ const existingMode = state.get("metadata", "runtime-delivery-mode");
4052
+ if (existingMode !== void 0 && existingMode !== deliveryMode) throw new Error("Publisher state delivery mode mismatch; preserve custody and use an explicitly qualified state binding");
4053
+ state.set("metadata", "runtime-delivery-mode", deliveryMode);
2931
4054
  const abort = new AbortController();
4055
+ const seenThisProcess = /* @__PURE__ */ new Set();
2932
4056
  let running = false;
2933
4057
  let ready = false;
2934
4058
  let clockHealthy = true;
@@ -2936,11 +4060,16 @@ async function createPersistentEventPublisherRuntime(options) {
2936
4060
  let lastError;
2937
4061
  let sourcesHealthy = false;
2938
4062
  let stop;
2939
- const state = options.state;
2940
4063
  const timed = (promise, signal) => boundedPublisherOperation(promise, signal, timing.requestTimeoutMs);
2941
4064
  function loadDelivery(subscription, now) {
4065
+ if (subscription.deliveryMode !== deliveryMode || !isDeepStrictEqual4(subscription.bestEffort, bestEffort))
4066
+ throw new Error("Subscription delivery mode or policy disagrees with runtime");
4067
+ if (deliveryMode === "best-effort" && (subscription.maxPositionsPerWake !== 1 || subscription.serviceHash === void 0)) throw new Error("Best Effort requires point range");
4068
+ if (options.serviceHash !== void 0 && subscription.serviceHash !== options.serviceHash)
4069
+ throw new Error("Subscription does not match the configured exact service descriptor");
2942
4070
  const key = subscription.subscriptionHash;
2943
- let record = state.get("deliveries", key);
4071
+ if (deliveryMode === "best-effort" && state.get(deliveryNamespace, key) === void 0 && state.entries(deliveryNamespace).length >= timing.maxSubscriptions) throw new Error("Best Effort state capacity exceeded; live records are not evicted");
4072
+ let record = state.get(deliveryNamespace, key);
2944
4073
  if (record === void 0) {
2945
4074
  makeEvent(subscription, subscription.fromPosition, subscription.fromPosition, now, "0".repeat(32));
2946
4075
  if (!Number.isSafeInteger(subscription.maxPositionsPerWake) || subscription.maxPositionsPerWake < 1 || subscription.maxPositionsPerWake > 4096) {
@@ -2954,12 +4083,26 @@ async function createPersistentEventPublisherRuntime(options) {
2954
4083
  nextAttemptMs: 0,
2955
4084
  state: "active"
2956
4085
  };
2957
- state.set("deliveries", key, record);
4086
+ state.set(deliveryNamespace, key, record);
2958
4087
  }
2959
4088
  assertDeliveryState(record, subscription);
2960
4089
  return record;
2961
4090
  }
2962
4091
  function prepareDelivery(record, now, heads) {
4092
+ if (deliveryMode === "best-effort" && bestEffort !== void 0) {
4093
+ const first = !seenThisProcess.has(record.subscription.subscriptionHash);
4094
+ const head2 = heads.get(publisherSourceKey(record.subscription.source));
4095
+ if (head2 !== void 0 && !(head2 instanceof Error) && head2.position >= record.subscription.fromPosition)
4096
+ seenThisProcess.add(record.subscription.subscriptionHash);
4097
+ return prepareBestEffortDelivery({
4098
+ bestEffort,
4099
+ first,
4100
+ heads,
4101
+ now,
4102
+ record,
4103
+ state
4104
+ });
4105
+ }
2963
4106
  if (record.pending !== void 0) return { ...record, pending: record.pending };
2964
4107
  const subscription = record.subscription;
2965
4108
  const head = heads.get(publisherSourceKey(subscription.source));
@@ -2971,43 +4114,74 @@ async function createPersistentEventPublisherRuntime(options) {
2971
4114
  state.transaction(() => {
2972
4115
  const logicalKey = JSON.stringify([subscription.subscriptionHash, pending.fromPosition, pending.toPosition]);
2973
4116
  if (!state.insert("delivery-events", logicalKey, pending)) throw new Error("Publisher logical range already exists outside its contiguous cursor");
2974
- state.set("deliveries", subscription.subscriptionHash, prepared);
4117
+ state.set(deliveryNamespace, subscription.subscriptionHash, prepared);
2975
4118
  });
2976
4119
  return prepared;
2977
4120
  }
2978
4121
  async function stillActive(record, signal) {
2979
4122
  if (await timed(options.discovery.isActive(record.subscription, signal), signal)) return true;
2980
- state.set("deliveries", record.subscription.subscriptionHash, {
4123
+ state.set(deliveryNamespace, record.subscription.subscriptionHash, {
2981
4124
  ...record,
2982
4125
  pending: void 0,
2983
4126
  state: "cancelled"
2984
4127
  });
2985
4128
  return false;
2986
4129
  }
4130
+ function acknowledgeBestEffort(record, body, eventHash, signal, now) {
4131
+ const { pending, subscription } = record;
4132
+ const acknowledgment = BestEffortAdmissionAcknowledgmentZod.safeParse(body);
4133
+ if (!acknowledgment.success || acknowledgment.data.eventHash !== eventHash || acknowledgment.data.wakeId !== pending.wakeId || acknowledgment.data.expiresAt !== pending.issuedAt + (bestEffort?.maxHintAgeSeconds ?? 0) + 60 || acknowledgment.data.acceptedAt < pending.issuedAt - 60 || acknowledgment.data.acceptedAt > now + 60)
4134
+ throw new Error("Publisher did not receive a matching finite acknowledgment");
4135
+ signal.throwIfAborted();
4136
+ state.set(deliveryNamespace, subscription.subscriptionHash, {
4137
+ subscription,
4138
+ cursor: pending.toPosition,
4139
+ delivered: record.delivered + 1,
4140
+ failures: 0,
4141
+ attempts: 0,
4142
+ superseded: record.superseded ?? 0,
4143
+ abandoned: record.abandoned ?? 0,
4144
+ totalAttempts: (record.totalAttempts ?? 0) + 1,
4145
+ lastNotifiedAt: now,
4146
+ lastHintAt: pending.issuedAt,
4147
+ lastHintTarget: pending.toPosition,
4148
+ nextAttemptMs: 0,
4149
+ state: "active"
4150
+ });
4151
+ }
2987
4152
  async function sendDelivery(record, signal, now) {
2988
4153
  const { pending, subscription } = record;
2989
4154
  signal.throwIfAborted();
2990
4155
  const signed = await signer.sign(pending, subscription.audience);
2991
4156
  signal.throwIfAborted();
2992
- const attempt = state.get("delivery-attempts", pending.wakeId)?.count ?? 0;
2993
- state.set("delivery-attempts", pending.wakeId, {
4157
+ const attempt = deliveryMode === "best-effort" ? record.attempts ?? 0 : state.get("delivery-attempts", pending.wakeId)?.count ?? 0;
4158
+ if (deliveryMode === "best-effort") state.set(deliveryNamespace, subscription.subscriptionHash, {
4159
+ ...record,
4160
+ attempts: attempt + 1,
4161
+ totalAttempts: (record.totalAttempts ?? 0) + 1
4162
+ });
4163
+ else state.set("delivery-attempts", pending.wakeId, {
2994
4164
  count: attempt + 1,
2995
4165
  startedAt: now,
2996
4166
  status: "in-flight"
2997
4167
  });
2998
4168
  const response = await options.transport.post(subscription.endpoint, signed.event, signal, signed.jwt);
2999
- state.set("delivery-attempts", pending.wakeId, {
4169
+ if (deliveryMode === "durable") state.set("delivery-attempts", pending.wakeId, {
3000
4170
  count: attempt + 1,
3001
4171
  startedAt: now,
3002
4172
  status: response.status
3003
4173
  });
3004
4174
  if (response.status !== 200 && response.status !== 202) throw new DeliveryResponseError(response.status, response.retryAfterMs);
3005
- const receipt = z3.object({ receiptId: z3.string().min(1), wakeId: z3.literal(pending.wakeId) }).safeParse(response.body);
4175
+ if (deliveryMode === "best-effort") {
4176
+ acknowledgeBestEffort(record, response.body, signed.eventHash, signal, now);
4177
+ return;
4178
+ }
4179
+ const receipt = z5.object({ receiptId: z5.string().min(1), wakeId: z5.literal(pending.wakeId) }).safeParse(response.body);
3006
4180
  if (!receipt.success) throw new Error("Publisher admission did not return a matching durable receipt");
3007
4181
  signal.throwIfAborted();
3008
4182
  state.transaction(() => {
3009
4183
  state.set("delivery-receipts", pending.wakeId, { receiptId: receipt.data.receiptId, receivedAt: now });
3010
- state.set("deliveries", subscription.subscriptionHash, {
4184
+ state.set(deliveryNamespace, subscription.subscriptionHash, {
3011
4185
  subscription,
3012
4186
  cursor: pending.toPosition,
3013
4187
  delivered: record.delivered + 1,
@@ -3019,7 +4193,7 @@ async function createPersistentEventPublisherRuntime(options) {
3019
4193
  }
3020
4194
  async function deliver(subscription, signal, now, heads) {
3021
4195
  let record = loadDelivery(subscription, now);
3022
- if (record.state !== "active" || record.nextAttemptMs > now * 1e3) return;
4196
+ if (deliveryMode === "durable" && (record.state !== "active" || record.nextAttemptMs > now * 1e3)) return;
3023
4197
  try {
3024
4198
  if (!await stillActive(record, signal)) return;
3025
4199
  options.transport.assertAllowed(subscription.endpoint);
@@ -3027,17 +4201,16 @@ async function createPersistentEventPublisherRuntime(options) {
3027
4201
  const prepared = prepareDelivery(record, now, heads);
3028
4202
  if (prepared === void 0) return;
3029
4203
  record = prepared;
4204
+ if (record.nextAttemptMs > now * 1e3) return;
3030
4205
  if (!await stillActive(record, signal)) return;
3031
4206
  await sendDelivery(prepared, signal, now);
3032
4207
  } catch (error) {
3033
4208
  if (signal.aborted) return;
3034
- state.set("deliveries", subscription.subscriptionHash, {
3035
- ...record,
3036
- failures: record.failures + 1,
3037
- state: error instanceof DeliveryResponseError && !error.retryable ? "blocked" : "active",
3038
- nextAttemptMs: now * 1e3 + Math.min(36e5, error instanceof DeliveryResponseError && error.retryAfterMs !== void 0 ? error.retryAfterMs : Math.min(3e5, 1e3 * 2 ** Math.min(record.failures, 8))),
3039
- lastError: "delivery-failed"
3040
- });
4209
+ state.set(
4210
+ deliveryNamespace,
4211
+ subscription.subscriptionHash,
4212
+ failedDelivery(record, state.get(deliveryNamespace, subscription.subscriptionHash), deliveryMode, error, now)
4213
+ );
3041
4214
  }
3042
4215
  }
3043
4216
  async function readSources(signal) {
@@ -3067,13 +4240,13 @@ async function createPersistentEventPublisherRuntime(options) {
3067
4240
  if (subscriptions.length > timing.maxSubscriptions) throw new Error("Publisher subscription limit exceeded");
3068
4241
  const active = new Set(subscriptions.map((subscription) => subscription.subscriptionHash));
3069
4242
  if (active.size !== subscriptions.length) throw new Error("Duplicate subscription identity");
3070
- const unavailable = state.entries("deliveries").filter((item) => !active.has(item.key) && item.value.state !== "cancelled");
4243
+ const unavailable = state.entries(deliveryNamespace).filter((item) => !active.has(item.key) && item.value.state !== "cancelled");
3071
4244
  let available = true;
3072
4245
  for (let offset = 0; offset < unavailable.length; offset += timing.concurrency) {
3073
4246
  signal.throwIfAborted();
3074
4247
  const results = await Promise.allSettled(unavailable.slice(offset, offset + timing.concurrency).map(async ({ key, value }) => {
3075
4248
  const stillAuthorized = await timed(options.discovery.isActive(value.subscription, signal), signal);
3076
- if (!stillAuthorized) state.set("deliveries", key, {
4249
+ if (!stillAuthorized) state.set(deliveryNamespace, key, {
3077
4250
  ...value,
3078
4251
  pending: void 0,
3079
4252
  state: "cancelled"
@@ -3085,8 +4258,8 @@ async function createPersistentEventPublisherRuntime(options) {
3085
4258
  return available;
3086
4259
  }
3087
4260
  async function tick(actorSignal) {
3088
- const scope = publisherAbortScope([abort.signal, actorSignal]);
3089
- const signal = scope.signal;
4261
+ const scope2 = publisherAbortScope([abort.signal, actorSignal]);
4262
+ const signal = scope2.signal;
3090
4263
  try {
3091
4264
  signal.throwIfAborted();
3092
4265
  const now = clock();
@@ -3117,7 +4290,7 @@ async function createPersistentEventPublisherRuntime(options) {
3117
4290
  if (!signal.aborted)
3118
4291
  throw error;
3119
4292
  } finally {
3120
- scope.dispose();
4293
+ scope2.dispose();
3121
4294
  }
3122
4295
  }
3123
4296
  const actor = await PublisherRuntimeActor.create({
@@ -3167,9 +4340,14 @@ async function createPersistentEventPublisherRuntime(options) {
3167
4340
  throw new AggregateError(failures, "Publisher runtime shutdown failed");
3168
4341
  })(),
3169
4342
  status() {
3170
- const deliveries = state.entries("deliveries").map((item) => item.value);
4343
+ const deliveries = state.entries(deliveryNamespace).map((item) => item.value);
3171
4344
  return {
3172
4345
  running,
4346
+ deliveryMode,
4347
+ ...bestEffort === void 0 ? {} : { bestEffort: Object.freeze({ ...bestEffort }) },
4348
+ superseded: deliveries.reduce((sum, item) => sum + (item.superseded ?? 0), 0),
4349
+ abandoned: deliveries.reduce((sum, item) => sum + (item.abandoned ?? 0), 0),
4350
+ totalAttempts: deliveries.reduce((sum, item) => sum + (item.totalAttempts ?? 0), 0),
3173
4351
  ready: running && ready,
3174
4352
  clockHealthy,
3175
4353
  degraded: !ready || deliveries.some((item) => item.state !== "cancelled" && item.failures > 0),
@@ -3181,10 +4359,15 @@ async function createPersistentEventPublisherRuntime(options) {
3181
4359
  passes,
3182
4360
  ...lastError === void 0 ? {} : { lastError },
3183
4361
  ...options.discovery.status === void 0 ? {} : { handshake: options.discovery.status() },
3184
- sources: state.entries("source-heads").map((item) => ({ id: item.key, ...item.value })),
3185
- subscriptions: state.entries("deliveries").map((item) => ({
4362
+ sources: state.entries("source-heads").filter((item) => sourceReaders.has(item.key)).map((item) => ({ id: item.key, ...item.value })),
4363
+ subscriptions: state.entries(deliveryNamespace).map((item) => ({
3186
4364
  id: item.key,
3187
4365
  cursor: item.value.cursor,
4366
+ ...deliveryMode === "best-effort" ? {
4367
+ ...item.value.cursor < item.value.subscription.fromPosition ? {} : { lastAdmissionTarget: item.value.cursor },
4368
+ attempts: item.value.attempts ?? 0,
4369
+ lastHintAt: item.value.lastHintAt
4370
+ } : {},
3188
4371
  state: item.value.state,
3189
4372
  pending: item.value.pending !== void 0,
3190
4373
  failures: item.value.failures
@@ -3275,40 +4458,41 @@ async function openPublisherDataLake(options) {
3275
4458
  }
3276
4459
 
3277
4460
  // src/runtime/sqliteHandshakeStores.ts
3278
- import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
4461
+ import { isDeepStrictEqual as isDeepStrictEqual5 } from "node:util";
3279
4462
  import { endpointChallengeHash as endpointChallengeHash2 } from "@xyo-network/event-kit-protocol";
3280
4463
  import {
3281
4464
  Base64Url32BytesZod as Base64Url32BytesZod3,
3282
4465
  EndpointChallengeZod as EndpointChallengeZod3,
3283
4466
  HandshakeIdZod as HandshakeIdZod4,
3284
- HashZod as HashZod5,
3285
- NormalizedAddressZod as NormalizedAddressZod5
4467
+ HashZod as HashZod9,
4468
+ NormalizedAddressZod as NormalizedAddressZod8
3286
4469
  } from "@xyo-network/event-kit-schemas";
3287
- import { z as z4 } from "zod";
3288
- var KeyZod = z4.strictObject({
3289
- encryptionKey: HashZod5,
4470
+ import { z as z6 } from "zod";
4471
+ var KeyZod = z6.strictObject({
4472
+ encryptionKey: HashZod9,
3290
4473
  keyId: HandshakeIdZod4,
3291
4474
  privateKey: Base64Url32BytesZod3,
3292
- publisher: NormalizedAddressZod5
4475
+ publisher: NormalizedAddressZod8
3293
4476
  });
3294
4477
  var phases = ["signed", "content-written", "content-verified", "broadcast", "included", "finalized"];
3295
4478
  function requireTime2(value) {
3296
4479
  if (!Number.isSafeInteger(value) || value < 0) throw new Error("Publisher clock is invalid");
3297
4480
  }
3298
4481
  function samePublicationIntent(left, right) {
3299
- return left.chainId === right.chainId && left.source === right.source && left.operationId === right.operationId && isDeepStrictEqual3(left.eventHashes, right.eventHashes) && isDeepStrictEqual3(left.attachmentHashes, right.attachmentHashes);
4482
+ return left.chainId === right.chainId && left.source === right.source && left.operationId === right.operationId && isDeepStrictEqual5(left.eventHashes, right.eventHashes) && isDeepStrictEqual5(left.attachmentHashes, right.attachmentHashes);
3300
4483
  }
3301
4484
  function publicationKey(intent) {
3302
- 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)) {
3303
4486
  throw new Error("Invalid publication intent");
3304
4487
  }
3305
4488
  return JSON.stringify([intent.chainId, intent.source, intent.operationId]);
3306
4489
  }
3307
- function createSqlitePublisherHandshakeStores(state) {
4490
+ function createSqlitePublisherHandshakeStores(state, options = {}) {
4491
+ const replayKey = options.replayNamespace === void 0 ? "checkpoint" : `checkpoint:${z6.string().min(1).max(128).regex(/^[A-Za-z0-9_-]+$/u).parse(options.replayNamespace)}`;
3308
4492
  const replay = {
3309
- load: async () => state.get("replay", "checkpoint"),
4493
+ load: async () => state.get("replay", replayKey),
3310
4494
  async save(checkpoint) {
3311
- state.set("replay", "checkpoint", checkpoint);
4495
+ state.set("replay", replayKey, checkpoint);
3312
4496
  }
3313
4497
  };
3314
4498
  const intents = {
@@ -3332,11 +4516,11 @@ function createSqlitePublisherHandshakeStores(state) {
3332
4516
  };
3333
4517
  const keys = {
3334
4518
  async getPrivateKey(input) {
3335
- const key = `${NormalizedAddressZod5.parse(input.publisher)}:${HandshakeIdZod4.parse(input.keyId)}`;
4519
+ const key = `${NormalizedAddressZod8.parse(input.publisher)}:${HandshakeIdZod4.parse(input.keyId)}`;
3336
4520
  const raw = state.get("encryption-keys", key);
3337
4521
  if (raw === void 0) return;
3338
4522
  const record = KeyZod.parse(raw);
3339
- 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");
3340
4524
  const { publisher: _publisher, ...privateKey } = record;
3341
4525
  return privateKey;
3342
4526
  },
@@ -3346,7 +4530,7 @@ function createSqlitePublisherHandshakeStores(state) {
3346
4530
  const key = `${record.publisher}:${record.keyId}`;
3347
4531
  const prior = state.get("encryption-keys", key);
3348
4532
  if (prior !== void 0) {
3349
- if (!isDeepStrictEqual3(prior, record)) throw new Error("Encryption key collision");
4533
+ if (!isDeepStrictEqual5(prior, record)) throw new Error("Encryption key collision");
3350
4534
  return "duplicate";
3351
4535
  }
3352
4536
  state.set("encryption-keys", key, record);
@@ -3354,19 +4538,19 @@ function createSqlitePublisherHandshakeStores(state) {
3354
4538
  });
3355
4539
  }
3356
4540
  };
3357
- const current = (hash) => state.get("challenges", HashZod5.parse(hash));
4541
+ const current = (hash) => state.get("challenges", HashZod9.parse(hash));
3358
4542
  const live = (record, now) => record.challenge.issuedAt <= now && now < record.challenge.expiresAt;
3359
4543
  const challenges = {
3360
4544
  get: async (hash) => current(hash),
3361
4545
  async issue(input) {
3362
4546
  const challenge = EndpointChallengeZod3.parse(input.challenge);
3363
- const hash = HashZod5.parse(input.challengeHash);
4547
+ const hash = HashZod9.parse(input.challengeHash);
3364
4548
  if (await endpointChallengeHash2(challenge) !== hash) return { kind: "collision" };
3365
4549
  return state.transaction(() => {
3366
4550
  const priorNonce = state.get("challenge-nonces", challenge.nonce);
3367
4551
  if (priorNonce !== void 0 && priorNonce !== hash) return { kind: "collision" };
3368
4552
  const prior = current(hash);
3369
- if (prior !== void 0) return isDeepStrictEqual3(prior.challenge, challenge) ? { kind: "duplicate", record: prior } : { kind: "collision" };
4553
+ if (prior !== void 0) return isDeepStrictEqual5(prior.challenge, challenge) ? { kind: "duplicate", record: prior } : { kind: "collision" };
3370
4554
  const record = {
3371
4555
  challenge,
3372
4556
  challengeHash: hash,
@@ -3392,7 +4576,7 @@ function createSqlitePublisherHandshakeStores(state) {
3392
4576
  },
3393
4577
  async completeVerified(input) {
3394
4578
  requireTime2(input.nowSeconds);
3395
- const responseHash = HashZod5.parse(input.responseHash);
4579
+ const responseHash = HashZod9.parse(input.responseHash);
3396
4580
  return state.transaction(() => {
3397
4581
  const record = current(input.challengeHash);
3398
4582
  if (record === void 0) return { kind: "missing" };
@@ -3419,7 +4603,7 @@ function createSqlitePublisherHandshakeStores(state) {
3419
4603
  for (const phase of phases) {
3420
4604
  const record = state.get(`publication:${phase}`, key);
3421
4605
  if (record === void 0) continue;
3422
- if (record.phase !== phase || !samePublicationIntent(record, intent) || !isDeepStrictEqual3(record.evidence, signed.evidence)) {
4606
+ if (record.phase !== phase || !samePublicationIntent(record, intent) || !isDeepStrictEqual5(record.evidence, signed.evidence)) {
3423
4607
  throw new Error("Publication evidence collision");
3424
4608
  }
3425
4609
  latest = record;
@@ -3428,17 +4612,17 @@ function createSqlitePublisherHandshakeStores(state) {
3428
4612
  },
3429
4613
  async save(record) {
3430
4614
  const key = publicationKey(record);
3431
- HashZod5.parse(record.evidence.transactionHash);
4615
+ HashZod9.parse(record.evidence.transactionHash);
3432
4616
  if (!phases.includes(record.phase)) throw new Error("Unknown publication phase");
3433
4617
  state.transaction(() => {
3434
4618
  const signed = state.get("publication:signed", key);
3435
4619
  if (signed === void 0 && record.phase !== "signed") throw new Error("Publication must bind signed evidence first");
3436
- if (signed !== void 0 && (!samePublicationIntent(signed, record) || !isDeepStrictEqual3(signed.evidence, record.evidence))) {
4620
+ if (signed !== void 0 && (!samePublicationIntent(signed, record) || !isDeepStrictEqual5(signed.evidence, record.evidence))) {
3437
4621
  throw new Error("Publication intent or evidence collision");
3438
4622
  }
3439
4623
  const namespace = `publication:${record.phase}`;
3440
4624
  const prior = state.get(namespace, key);
3441
- if (prior !== void 0 && !isDeepStrictEqual3(prior, record)) throw new Error("Publication phase collision");
4625
+ if (prior !== void 0 && !isDeepStrictEqual5(prior, record)) throw new Error("Publication phase collision");
3442
4626
  if (prior === void 0) state.set(namespace, key, record);
3443
4627
  });
3444
4628
  }
@@ -3453,15 +4637,15 @@ function createSqlitePublisherHandshakeStores(state) {
3453
4637
  }
3454
4638
 
3455
4639
  // src/runtime/sqlitePublisherState.ts
3456
- import { randomBytes as randomBytes7 } from "node:crypto";
3457
- import { constants } from "node:fs";
4640
+ import { randomBytes as randomBytes8 } from "node:crypto";
4641
+ import { constants as constants3 } from "node:fs";
3458
4642
  import {
3459
- lstat,
3460
- mkdir as mkdir5,
3461
- open as open5
4643
+ lstat as lstat5,
4644
+ mkdir as mkdir4,
4645
+ open as open6
3462
4646
  } from "node:fs/promises";
3463
4647
  import { hostname } from "node:os";
3464
- import PATH12 from "node:path";
4648
+ import PATH16 from "node:path";
3465
4649
  function decode(value) {
3466
4650
  return JSON.parse(value, (_key, candidate) => {
3467
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;
@@ -3482,13 +4666,13 @@ function processExists(pid) {
3482
4666
  async function openSqlitePublisherState(options) {
3483
4667
  if (options.path === ":memory:" || options.identity.length === 0)
3484
4668
  throw new Error("Persistent publisher needs a file and an identity");
3485
- const path = PATH12.resolve(options.path);
3486
- const directory = PATH12.dirname(path);
3487
- await mkdir5(directory, { recursive: true, mode: 448 });
3488
- 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);
3489
4673
  if (!metadata.isDirectory() || (metadata.mode & 63) !== 0)
3490
4674
  throw new Error("Publisher state directory must be a real directory with 0700 permissions");
3491
- 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);
3492
4676
  try {
3493
4677
  const metadata2 = await file.stat();
3494
4678
  if (!metadata2.isFile() || (metadata2.mode & 63) !== 0)
@@ -3497,9 +4681,9 @@ async function openSqlitePublisherState(options) {
3497
4681
  } finally {
3498
4682
  await file.close();
3499
4683
  }
3500
- const { DatabaseSync: DatabaseSync2 } = await import("node:sqlite");
3501
- const database = new DatabaseSync2(path);
3502
- 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");
3503
4687
  let closed = false;
3504
4688
  let inTransaction = false;
3505
4689
  const transaction = (work) => {
@@ -3590,67 +4774,362 @@ async function openSqlitePublisherState(options) {
3590
4774
  };
3591
4775
  }
3592
4776
 
3593
- // src/sqliteWakeQueue.ts
3594
- import { mkdir as mkdir6 } from "node:fs/promises";
3595
- import PATH13 from "node:path";
3596
- import { DatabaseSync } from "node:sqlite";
3597
- async function createSqliteWakeQueue(options) {
3598
- const inMemory = options.path === ":memory:";
3599
- const file = inMemory ? options.path : PATH13.resolve(options.path);
3600
- if (!inMemory) await mkdir6(PATH13.dirname(file), { recursive: true });
3601
- const database = new DatabaseSync(file);
3602
- database.exec(`
3603
- PRAGMA journal_mode = WAL;
3604
- PRAGMA synchronous = FULL;
3605
- CREATE TABLE IF NOT EXISTS wake_queue (
3606
- id INTEGER PRIMARY KEY AUTOINCREMENT,
3607
- body TEXT NOT NULL,
3608
- state TEXT NOT NULL DEFAULT 'pending'
3609
- );
3610
- CREATE INDEX IF NOT EXISTS wake_queue_state ON wake_queue (state, id);
3611
- `);
3612
- const insert = database.prepare("INSERT INTO wake_queue (body) VALUES (?)");
3613
- const nextPending = database.prepare(
3614
- "SELECT id, body FROM wake_queue WHERE state = 'pending' ORDER BY id LIMIT 1"
3615
- );
3616
- const lease = database.prepare(
3617
- "UPDATE wake_queue SET state = 'inflight' WHERE id = ? AND state = 'pending'"
3618
- );
3619
- const remove = database.prepare("DELETE FROM wake_queue WHERE id = ?");
3620
- const restore = database.prepare(
3621
- "UPDATE wake_queue SET state = 'pending' WHERE id = ? AND state = 'inflight'"
3622
- );
3623
- const countPending = database.prepare(
3624
- "SELECT COUNT(*) AS total FROM wake_queue WHERE state = 'pending'"
3625
- );
3626
- const recoverAll = database.prepare(
3627
- "UPDATE wake_queue SET state = 'pending' WHERE state = 'inflight'"
3628
- );
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) {
3629
4857
  return {
3630
- async append(wake) {
3631
- insert.run(JSON.stringify(wake));
3632
- },
3633
- async claim() {
3634
- for (; ; ) {
3635
- const row = nextPending.get();
3636
- if (row === void 0) return void 0;
3637
- 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;
3638
5079
  return {
3639
- wake: JSON.parse(row.body),
3640
- async ack() {
3641
- 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
+ });
3642
5103
  },
3643
5104
  async release() {
3644
- 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
+ });
3645
5110
  }
3646
5111
  };
3647
- }
3648
- },
3649
- close() {
3650
- database.close();
3651
- },
3652
- pendingCount: async () => Number(countPending.get().total),
3653
- 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)
3654
5133
  };
3655
5134
  }
3656
5135
  export {
@@ -3661,6 +5140,7 @@ export {
3661
5140
  assertSameIntentBinding,
3662
5141
  boundedPublisherOperation,
3663
5142
  createAccountSourceWakeSigner,
5143
+ createBestEffortSnapshotWorker,
3664
5144
  createBoundedPublisherEthersTransport,
3665
5145
  createBoundedPublisherFetch,
3666
5146
  createEthereumFinalizedPublisherSource,
@@ -3679,8 +5159,10 @@ export {
3679
5159
  createPublisherHandshakeClient,
3680
5160
  createPublisherHandshakeDiscovery,
3681
5161
  createPublisherHttpsTransport,
5162
+ createSqliteBestEffortInbox,
3682
5163
  createSqlitePublisherHandshakeStores,
3683
5164
  createSqliteWakeQueue,
5165
+ createSqliteWakeReceiver,
3684
5166
  createSubscriberHandshakeClient,
3685
5167
  createSynchronizedEventKitHandshakeReader,
3686
5168
  createUtcIntervalPublisherSource,
@@ -3688,6 +5170,8 @@ export {
3688
5170
  handshakeMutationRequestFingerprint,
3689
5171
  intentLookupKey,
3690
5172
  isPublicPublisherAddress,
5173
+ migrateIdOnlyReducerCheckpoint,
5174
+ migrateReceiverStore,
3691
5175
  openPublisherDataLake,
3692
5176
  openPublisherRestGateway,
3693
5177
  openSqlitePublisherState,