@xyo-network/event-kit-node 1.0.1 → 2.1.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.
@@ -141,6 +141,7 @@ function createBoundedPublisherEthersTransport(options = {}) {
141
141
  }
142
142
 
143
143
  // src/runtime/handshakeDiscovery.ts
144
+ import { isDeepStrictEqual } from "node:util";
144
145
  import { decryptWakeSubscription, sourceWakeFilterHash } from "@xyo-network/event-kit-protocol";
145
146
  import { EndpointChallengeResponseZod } from "@xyo-network/event-kit-schemas";
146
147
  function createPublisherHandshakeDiscovery(options) {
@@ -156,6 +157,9 @@ function createPublisherHandshakeDiscovery(options) {
156
157
  rejectedSubscriptions: 0,
157
158
  challengeFailures: 0
158
159
  };
160
+ function selectedTuple(tuple) {
161
+ 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 || isDeepStrictEqual(tuple.service.body.bestEffort, options.bestEffort));
162
+ }
159
163
  async function activeView(signal) {
160
164
  signal.throwIfAborted();
161
165
  viewing ??= (async () => {
@@ -173,7 +177,7 @@ function createPublisherHandshakeDiscovery(options) {
173
177
  ...status,
174
178
  finalizedHead: view.finalizedHead,
175
179
  indexedThrough: view.indexedThrough,
176
- pendingSubscriptions: view.subscriptions.filter((item) => item.state === "pending").length,
180
+ pendingSubscriptions: view.subscriptions.filter((item) => item.state === "pending" && item.tuples.some(selectedTuple)).length,
177
181
  rejectedSubscriptions: view.rejections.length
178
182
  };
179
183
  return view;
@@ -238,7 +242,7 @@ function createPublisherHandshakeDiscovery(options) {
238
242
  if (!found.ok)
239
243
  throw new Error("Publisher acceptance candidates are unavailable");
240
244
  const offset = options.state.get("challenge-rate", "offset") ?? 0;
241
- const candidates = found.candidates;
245
+ const candidates = found.candidates.filter((candidate) => selectedTuple(candidate.tuple));
242
246
  let attempted = 0;
243
247
  for (let index = 0; index < Math.min(candidates.length, 128) && attempted < 4; index += 1) {
244
248
  const candidate = candidates[(offset + index) % candidates.length];
@@ -254,30 +258,40 @@ function createPublisherHandshakeDiscovery(options) {
254
258
  await acceptCandidate(candidate.subscriber, candidate.tuple.subscription.objectHash, signal);
255
259
  }
256
260
  }
261
+ async function loadScopedKeys(view, active) {
262
+ const scopedKeys = active.flatMap((item) => item.tuples.filter((tuple) => tuple.subscription.objectHash === item.selectedSubscription).map((tuple) => ({
263
+ encryptionKey: tuple.subscription.body.encryptionKey,
264
+ keyId: tuple.subscription.body.keyId,
265
+ publisher: options.publisher
266
+ })));
267
+ if (view.hasCurrentAdvertisement && (options.serviceHash === void 0 || view.service.objectHash === options.serviceHash) && (options.deliveryMode === void 0 || view.service.body.deliveryMode === options.deliveryMode)) {
268
+ scopedKeys.push({
269
+ encryptionKey: view.key.objectHash,
270
+ keyId: view.key.body.keyId,
271
+ publisher: options.publisher
272
+ });
273
+ }
274
+ const loadedKeys = await Promise.all(scopedKeys.map(async (key) => await options.keys.getPrivateKey(key)));
275
+ status = {
276
+ ...status,
277
+ keyLoaded: scopedKeys.length > 0 && loadedKeys.every((key) => key !== void 0),
278
+ activeKeyIds: [...new Set(scopedKeys.map((key) => key.keyId))]
279
+ };
280
+ }
257
281
  return {
258
282
  async discover(signal) {
259
283
  const view = await activeView(signal);
260
- const key = view.hasCurrentAdvertisement ? view.key : void 0;
261
- const loaded = key === void 0 ? void 0 : await options.keys.getPrivateKey({
262
- encryptionKey: key.objectHash,
263
- keyId: key.body.keyId,
264
- publisher: options.publisher
265
- });
266
- status = {
267
- ...status,
268
- keyLoaded: loaded !== void 0,
269
- activeKeyIds: key === void 0 ? [] : [key.body.keyId]
270
- };
271
- const subscriptions = [];
272
- const active = view.subscriptions.filter((item) => item.state === "active");
284
+ const active = view.subscriptions.filter((item) => item.state === "active" && item.tuples.some((tuple) => tuple.subscription.objectHash === item.selectedSubscription && selectedTuple(tuple)));
285
+ await loadScopedKeys(view, active);
273
286
  if (active.length > (options.maxSubscriptions ?? 64))
274
287
  throw new Error("Publisher active subscription limit exceeded");
288
+ const subscriptions = [];
275
289
  for (const lifecycle of active) {
276
290
  if (lifecycle.state !== "active" || lifecycle.activeFromBlock === void 0)
277
291
  continue;
278
292
  try {
279
293
  const tuple = lifecycle.tuples.find((item) => item.subscription.objectHash === lifecycle.selectedSubscription);
280
- if (tuple === void 0)
294
+ if (tuple === void 0 || !selectedTuple(tuple))
281
295
  throw new Error("Active publisher subscription has no selected tuple");
282
296
  const encrypted = tuple.subscription.body;
283
297
  if (encrypted.chainId !== options.authorizationChainId)
@@ -297,6 +311,9 @@ function createPublisherHandshakeDiscovery(options) {
297
311
  options.transport.assertAllowed(plaintext.endpoint);
298
312
  subscriptions.push({
299
313
  subscriptionHash: tuple.subscription.objectHash,
314
+ serviceHash: tuple.service.objectHash,
315
+ deliveryMode: plaintext.filter.deliveryMode,
316
+ ...plaintext.filter.bestEffort === void 0 ? {} : { bestEffort: plaintext.filter.bestEffort },
300
317
  authorizationChainId: options.authorizationChainId,
301
318
  controller: tuple.subscriber,
302
319
  audience: plaintext.audience,
@@ -316,7 +333,7 @@ function createPublisherHandshakeDiscovery(options) {
316
333
  },
317
334
  async isActive(subscription, signal) {
318
335
  const view = await activeView(signal);
319
- 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));
336
+ 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)));
320
337
  },
321
338
  async acceptPending(signal) {
322
339
  if (accepting === void 0) {
@@ -642,18 +659,128 @@ function createPublisherHttpsTransport(input) {
642
659
  }
643
660
 
644
661
  // src/runtime/publisherRuntime.ts
645
- import { randomBytes as randomBytes2 } from "node:crypto";
646
- import { isDeepStrictEqual } from "node:util";
662
+ import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
647
663
  import { createMemoryLocator, PeriodicActor } from "@ariestools/actor";
648
664
  import { creatable } from "@ariestools/sdk";
649
- import { WakeEnvelopeSchema, WakeEnvelopeZod as WakeEnvelopeZod2 } from "@xyo-network/event-kit-schemas";
665
+ import { BestEffortAdmissionAcknowledgmentZod } from "@xyo-network/event-kit-protocol";
666
+ import { BestEffortDeliveryPolicyZod, WakeDeliveryModeZod } from "@xyo-network/event-kit-schemas";
650
667
  import { z as z2 } from "zod";
651
668
 
652
- // src/runtime/sourceSigner.ts
669
+ // src/runtime/publisherDeliveryState.ts
653
670
  import { randomBytes } from "node:crypto";
671
+ import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
672
+ import { WakeEnvelopeSchema, WakeEnvelopeZod } from "@xyo-network/event-kit-schemas";
673
+ function assertDeliveryState(record, subscription) {
674
+ if (!isDeepStrictEqual2(record.subscription, subscription))
675
+ throw new Error("Subscription revision changed without a new identity");
676
+ 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))
677
+ throw new Error("Persisted publisher delivery state is invalid");
678
+ if (record.pending === void 0) return;
679
+ const pending = WakeEnvelopeZod.parse(record.pending);
680
+ 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 || !isDeepStrictEqual2(pending.source, subscription.source))
681
+ throw new Error("Persisted publisher outbox binding is invalid");
682
+ }
683
+ function makeEvent(subscription, fromPosition, toPosition, now, wakeId = randomBytes(16).toString("hex")) {
684
+ return WakeEnvelopeZod.parse({
685
+ schema: WakeEnvelopeSchema,
686
+ authorizationChainId: subscription.authorizationChainId,
687
+ deliveryMode: subscription.deliveryMode,
688
+ deploymentId: subscription.deploymentId,
689
+ filterHash: subscription.filterHash,
690
+ fromPosition,
691
+ issuedAt: now,
692
+ source: subscription.source,
693
+ toPosition,
694
+ wakeId,
695
+ ...subscription.queueId === void 0 ? {} : { queueId: subscription.queueId }
696
+ });
697
+ }
698
+ var DeliveryResponseError = class extends Error {
699
+ retryAfterMs;
700
+ status;
701
+ constructor(status, retryAfterMs) {
702
+ super("Publisher admission failed");
703
+ this.status = status;
704
+ this.retryAfterMs = retryAfterMs;
705
+ }
706
+ get retryable() {
707
+ return this.status === 408 || this.status === 429 || this.status >= 500;
708
+ }
709
+ };
710
+ function prepareBestEffortDelivery(options) {
711
+ const {
712
+ bestEffort,
713
+ first,
714
+ heads,
715
+ now,
716
+ record,
717
+ state
718
+ } = options;
719
+ const subscription = record.subscription;
720
+ const head = heads.get(publisherSourceKey(subscription.source));
721
+ const prior = record.pending;
722
+ const expired = isHintExpired(record, now, bestEffort);
723
+ if (head === void 0 || head instanceof Error || head.position < subscription.fromPosition) {
724
+ if (prior !== void 0 && !expired) return { ...record, pending: prior };
725
+ if (prior !== void 0) state.set("best-effort-deliveries", subscription.subscriptionHash, {
726
+ ...record,
727
+ pending: void 0,
728
+ abandoned: (record.abandoned ?? 0) + 1
729
+ });
730
+ return;
731
+ }
732
+ const refresh = refreshDue(record, now, bestEffort);
733
+ if (freshHintRequired(record, head.position, first, refresh, expired)) {
734
+ const pending = makeEvent(subscription, head.position, head.position, now);
735
+ const prepared = {
736
+ ...record,
737
+ pending,
738
+ attempts: 0,
739
+ lastHintAt: now,
740
+ lastHintTarget: head.position,
741
+ superseded: (record.superseded ?? 0) + (prior !== void 0 && !expired ? 1 : 0),
742
+ abandoned: (record.abandoned ?? 0) + (expired ? 1 : 0),
743
+ nextAttemptMs: 0,
744
+ state: "active"
745
+ };
746
+ state.set("best-effort-deliveries", subscription.subscriptionHash, prepared);
747
+ return prepared;
748
+ }
749
+ if (expired) state.set("best-effort-deliveries", subscription.subscriptionHash, {
750
+ ...record,
751
+ pending: void 0,
752
+ abandoned: (record.abandoned ?? 0) + 1
753
+ });
754
+ return prior === void 0 || expired ? void 0 : { ...record, pending: prior };
755
+ }
756
+ function isHintExpired(record, now, policy) {
757
+ return record.pending !== void 0 && (now > record.pending.issuedAt + policy.maxHintAgeSeconds || (record.attempts ?? 0) >= policy.maxAttempts);
758
+ }
759
+ function refreshDue(record, now, policy) {
760
+ return now - (record.lastHintAt ?? record.lastNotifiedAt ?? 0) >= policy.refreshIntervalSeconds;
761
+ }
762
+ function freshHintRequired(record, head, first, refresh, expired) {
763
+ return first || head > (record.pending?.toPosition ?? record.lastHintTarget ?? record.cursor) || refresh && (record.pending === void 0 || expired);
764
+ }
765
+ function failedDelivery(record, current, mode, error, now) {
766
+ const bestEffort = mode === "best-effort";
767
+ const wait = error instanceof DeliveryResponseError && error.retryAfterMs !== void 0 ? error.retryAfterMs : Math.min(3e5, 1e3 * 2 ** Math.min(record.failures, 8));
768
+ return {
769
+ ...record,
770
+ failures: record.failures + 1,
771
+ totalAttempts: bestEffort ? current?.totalAttempts ?? record.totalAttempts ?? 0 : record.totalAttempts,
772
+ attempts: bestEffort ? current?.attempts ?? record.attempts ?? 0 : record.attempts,
773
+ state: !bestEffort && error instanceof DeliveryResponseError && !error.retryable ? "blocked" : "active",
774
+ nextAttemptMs: now * 1e3 + Math.min(36e5, wait),
775
+ lastError: "delivery-failed"
776
+ };
777
+ }
778
+
779
+ // src/runtime/sourceSigner.ts
780
+ import { randomBytes as randomBytes2 } from "node:crypto";
654
781
  import {
655
782
  NormalizedAddressZod,
656
- WakeEnvelopeZod,
783
+ WakeEnvelopeZod as WakeEnvelopeZod2,
657
784
  WakeJwtSchema
658
785
  } from "@xyo-network/event-kit-schemas";
659
786
  import { PayloadBuilder as PayloadBuilder2 } from "@xyo-network/sdk";
@@ -663,14 +790,14 @@ function createAccountSourceWakeSigner(options) {
663
790
  throw new Error("Publisher signing account mismatch");
664
791
  return {
665
792
  async sign(input, audience) {
666
- const event = WakeEnvelopeZod.parse(input);
793
+ const event = WakeEnvelopeZod2.parse(input);
667
794
  const eventHash = await PayloadBuilder2.dataHash(event);
668
795
  const now = options.nowSeconds?.() ?? Math.floor(Date.now() / 1e3);
669
796
  if (!Number.isSafeInteger(now) || now < 0)
670
797
  throw new Error("Publisher signing clock is invalid");
671
798
  const { token: jwt } = await options.account.signJwt({
672
799
  audience,
673
- claims: { eventHash, jti: randomBytes(16).toString("hex") },
800
+ claims: { eventHash, jti: randomBytes2(16).toString("hex") },
674
801
  now,
675
802
  schema: WakeJwtSchema,
676
803
  ttl: 60
@@ -689,30 +816,6 @@ function createAccountSourceWakeSigner(options) {
689
816
  function activationReady(status) {
690
817
  return (status?.keyLoaded ?? true) && (status?.pendingSubscriptions ?? 0) === 0;
691
818
  }
692
- function assertDeliveryState(record, subscription) {
693
- if (!isDeepStrictEqual(record.subscription, subscription))
694
- throw new Error("Subscription revision changed without a new identity");
695
- 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))
696
- throw new Error("Persisted publisher delivery state is invalid");
697
- if (record.pending === void 0) return;
698
- const pending = WakeEnvelopeZod2.parse(record.pending);
699
- 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 || !isDeepStrictEqual(pending.source, subscription.source))
700
- throw new Error("Persisted publisher outbox binding is invalid");
701
- }
702
- function makeEvent(subscription, fromPosition, toPosition, now, wakeId = randomBytes2(16).toString("hex")) {
703
- return WakeEnvelopeZod2.parse({
704
- schema: WakeEnvelopeSchema,
705
- authorizationChainId: subscription.authorizationChainId,
706
- deploymentId: subscription.deploymentId,
707
- filterHash: subscription.filterHash,
708
- fromPosition,
709
- issuedAt: now,
710
- source: subscription.source,
711
- toPosition,
712
- wakeId,
713
- ...subscription.queueId === void 0 ? {} : { queueId: subscription.queueId }
714
- });
715
- }
716
819
  var PublisherRuntimeActor = class extends PeriodicActor {
717
820
  async runPass(context) {
718
821
  await this.params.tick(context.signal);
@@ -727,20 +830,24 @@ var TimingZod = z2.object({
727
830
  maxSubscriptions: z2.number().int().min(1).max(1024).default(64),
728
831
  requestTimeoutMs: z2.number().int().min(100).max(6e4).default(1e4)
729
832
  });
730
- var DeliveryResponseError = class extends Error {
731
- retryAfterMs;
732
- status;
733
- constructor(status, retryAfterMs) {
734
- super("Publisher admission failed");
735
- this.status = status;
736
- this.retryAfterMs = retryAfterMs;
737
- }
738
- get retryable() {
739
- return this.status === 408 || this.status === 429 || this.status >= 500;
740
- }
741
- };
833
+ function runtimeSettings(options) {
834
+ const deliveryMode = WakeDeliveryModeZod.parse(options.deliveryMode);
835
+ const bestEffort = options.bestEffort === void 0 ? void 0 : BestEffortDeliveryPolicyZod.parse(options.bestEffort);
836
+ if (deliveryMode === "best-effort" !== (bestEffort !== void 0)) throw new TypeError("Explicit delivery mode requires its exact policy");
837
+ return {
838
+ bestEffort,
839
+ deliveryMode,
840
+ deliveryNamespace: deliveryMode === "best-effort" ? "best-effort-deliveries" : "deliveries",
841
+ timing: TimingZod.parse(options)
842
+ };
843
+ }
742
844
  async function createPersistentEventPublisherRuntime(options) {
743
- const timing = TimingZod.parse(options);
845
+ const {
846
+ bestEffort,
847
+ deliveryMode,
848
+ deliveryNamespace,
849
+ timing
850
+ } = runtimeSettings(options);
744
851
  if (options.sources.length < 1 || options.sources.length > 16)
745
852
  throw new Error("Publisher requires between one and sixteen bounded sources");
746
853
  const clock = options.nowSeconds ?? (() => Math.floor(Date.now() / 1e3));
@@ -752,7 +859,12 @@ async function createPersistentEventPublisherRuntime(options) {
752
859
  const sourceReaders = new Map(options.sources.map((reader) => [publisherSourceKey(reader.source), reader]));
753
860
  if (sourceReaders.size !== options.sources.length)
754
861
  throw new Error("Duplicate publisher source identity");
862
+ const state = options.state;
863
+ const existingMode = state.get("metadata", "runtime-delivery-mode");
864
+ if (existingMode !== void 0 && existingMode !== deliveryMode) throw new Error("Publisher state delivery mode mismatch; preserve custody and use an explicitly qualified state binding");
865
+ state.set("metadata", "runtime-delivery-mode", deliveryMode);
755
866
  const abort = new AbortController();
867
+ const seenThisProcess = /* @__PURE__ */ new Set();
756
868
  let running = false;
757
869
  let ready = false;
758
870
  let clockHealthy = true;
@@ -760,11 +872,16 @@ async function createPersistentEventPublisherRuntime(options) {
760
872
  let lastError;
761
873
  let sourcesHealthy = false;
762
874
  let stop;
763
- const state = options.state;
764
875
  const timed = (promise, signal) => boundedPublisherOperation(promise, signal, timing.requestTimeoutMs);
765
876
  function loadDelivery(subscription, now) {
877
+ if (subscription.deliveryMode !== deliveryMode || !isDeepStrictEqual3(subscription.bestEffort, bestEffort))
878
+ throw new Error("Subscription delivery mode or policy disagrees with runtime");
879
+ if (deliveryMode === "best-effort" && (subscription.maxPositionsPerWake !== 1 || subscription.serviceHash === void 0)) throw new Error("Best Effort requires point range");
880
+ if (options.serviceHash !== void 0 && subscription.serviceHash !== options.serviceHash)
881
+ throw new Error("Subscription does not match the configured exact service descriptor");
766
882
  const key = subscription.subscriptionHash;
767
- let record = state.get("deliveries", key);
883
+ 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");
884
+ let record = state.get(deliveryNamespace, key);
768
885
  if (record === void 0) {
769
886
  makeEvent(subscription, subscription.fromPosition, subscription.fromPosition, now, "0".repeat(32));
770
887
  if (!Number.isSafeInteger(subscription.maxPositionsPerWake) || subscription.maxPositionsPerWake < 1 || subscription.maxPositionsPerWake > 4096) {
@@ -778,12 +895,26 @@ async function createPersistentEventPublisherRuntime(options) {
778
895
  nextAttemptMs: 0,
779
896
  state: "active"
780
897
  };
781
- state.set("deliveries", key, record);
898
+ state.set(deliveryNamespace, key, record);
782
899
  }
783
900
  assertDeliveryState(record, subscription);
784
901
  return record;
785
902
  }
786
903
  function prepareDelivery(record, now, heads) {
904
+ if (deliveryMode === "best-effort" && bestEffort !== void 0) {
905
+ const first = !seenThisProcess.has(record.subscription.subscriptionHash);
906
+ const head2 = heads.get(publisherSourceKey(record.subscription.source));
907
+ if (head2 !== void 0 && !(head2 instanceof Error) && head2.position >= record.subscription.fromPosition)
908
+ seenThisProcess.add(record.subscription.subscriptionHash);
909
+ return prepareBestEffortDelivery({
910
+ bestEffort,
911
+ first,
912
+ heads,
913
+ now,
914
+ record,
915
+ state
916
+ });
917
+ }
787
918
  if (record.pending !== void 0) return { ...record, pending: record.pending };
788
919
  const subscription = record.subscription;
789
920
  const head = heads.get(publisherSourceKey(subscription.source));
@@ -795,43 +926,74 @@ async function createPersistentEventPublisherRuntime(options) {
795
926
  state.transaction(() => {
796
927
  const logicalKey = JSON.stringify([subscription.subscriptionHash, pending.fromPosition, pending.toPosition]);
797
928
  if (!state.insert("delivery-events", logicalKey, pending)) throw new Error("Publisher logical range already exists outside its contiguous cursor");
798
- state.set("deliveries", subscription.subscriptionHash, prepared);
929
+ state.set(deliveryNamespace, subscription.subscriptionHash, prepared);
799
930
  });
800
931
  return prepared;
801
932
  }
802
933
  async function stillActive(record, signal) {
803
934
  if (await timed(options.discovery.isActive(record.subscription, signal), signal)) return true;
804
- state.set("deliveries", record.subscription.subscriptionHash, {
935
+ state.set(deliveryNamespace, record.subscription.subscriptionHash, {
805
936
  ...record,
806
937
  pending: void 0,
807
938
  state: "cancelled"
808
939
  });
809
940
  return false;
810
941
  }
942
+ function acknowledgeBestEffort(record, body, eventHash, signal, now) {
943
+ const { pending, subscription } = record;
944
+ const acknowledgment = BestEffortAdmissionAcknowledgmentZod.safeParse(body);
945
+ 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)
946
+ throw new Error("Publisher did not receive a matching finite acknowledgment");
947
+ signal.throwIfAborted();
948
+ state.set(deliveryNamespace, subscription.subscriptionHash, {
949
+ subscription,
950
+ cursor: pending.toPosition,
951
+ delivered: record.delivered + 1,
952
+ failures: 0,
953
+ attempts: 0,
954
+ superseded: record.superseded ?? 0,
955
+ abandoned: record.abandoned ?? 0,
956
+ totalAttempts: (record.totalAttempts ?? 0) + 1,
957
+ lastNotifiedAt: now,
958
+ lastHintAt: pending.issuedAt,
959
+ lastHintTarget: pending.toPosition,
960
+ nextAttemptMs: 0,
961
+ state: "active"
962
+ });
963
+ }
811
964
  async function sendDelivery(record, signal, now) {
812
965
  const { pending, subscription } = record;
813
966
  signal.throwIfAborted();
814
967
  const signed = await signer.sign(pending, subscription.audience);
815
968
  signal.throwIfAborted();
816
- const attempt = state.get("delivery-attempts", pending.wakeId)?.count ?? 0;
817
- state.set("delivery-attempts", pending.wakeId, {
969
+ const attempt = deliveryMode === "best-effort" ? record.attempts ?? 0 : state.get("delivery-attempts", pending.wakeId)?.count ?? 0;
970
+ if (deliveryMode === "best-effort") state.set(deliveryNamespace, subscription.subscriptionHash, {
971
+ ...record,
972
+ attempts: attempt + 1,
973
+ totalAttempts: (record.totalAttempts ?? 0) + 1
974
+ });
975
+ else state.set("delivery-attempts", pending.wakeId, {
818
976
  count: attempt + 1,
819
977
  startedAt: now,
820
978
  status: "in-flight"
821
979
  });
822
980
  const response = await options.transport.post(subscription.endpoint, signed.event, signal, signed.jwt);
823
- state.set("delivery-attempts", pending.wakeId, {
981
+ if (deliveryMode === "durable") state.set("delivery-attempts", pending.wakeId, {
824
982
  count: attempt + 1,
825
983
  startedAt: now,
826
984
  status: response.status
827
985
  });
828
986
  if (response.status !== 200 && response.status !== 202) throw new DeliveryResponseError(response.status, response.retryAfterMs);
987
+ if (deliveryMode === "best-effort") {
988
+ acknowledgeBestEffort(record, response.body, signed.eventHash, signal, now);
989
+ return;
990
+ }
829
991
  const receipt = z2.object({ receiptId: z2.string().min(1), wakeId: z2.literal(pending.wakeId) }).safeParse(response.body);
830
992
  if (!receipt.success) throw new Error("Publisher admission did not return a matching durable receipt");
831
993
  signal.throwIfAborted();
832
994
  state.transaction(() => {
833
995
  state.set("delivery-receipts", pending.wakeId, { receiptId: receipt.data.receiptId, receivedAt: now });
834
- state.set("deliveries", subscription.subscriptionHash, {
996
+ state.set(deliveryNamespace, subscription.subscriptionHash, {
835
997
  subscription,
836
998
  cursor: pending.toPosition,
837
999
  delivered: record.delivered + 1,
@@ -843,7 +1005,7 @@ async function createPersistentEventPublisherRuntime(options) {
843
1005
  }
844
1006
  async function deliver(subscription, signal, now, heads) {
845
1007
  let record = loadDelivery(subscription, now);
846
- if (record.state !== "active" || record.nextAttemptMs > now * 1e3) return;
1008
+ if (deliveryMode === "durable" && (record.state !== "active" || record.nextAttemptMs > now * 1e3)) return;
847
1009
  try {
848
1010
  if (!await stillActive(record, signal)) return;
849
1011
  options.transport.assertAllowed(subscription.endpoint);
@@ -851,17 +1013,16 @@ async function createPersistentEventPublisherRuntime(options) {
851
1013
  const prepared = prepareDelivery(record, now, heads);
852
1014
  if (prepared === void 0) return;
853
1015
  record = prepared;
1016
+ if (record.nextAttemptMs > now * 1e3) return;
854
1017
  if (!await stillActive(record, signal)) return;
855
1018
  await sendDelivery(prepared, signal, now);
856
1019
  } catch (error) {
857
1020
  if (signal.aborted) return;
858
- state.set("deliveries", subscription.subscriptionHash, {
859
- ...record,
860
- failures: record.failures + 1,
861
- state: error instanceof DeliveryResponseError && !error.retryable ? "blocked" : "active",
862
- 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))),
863
- lastError: "delivery-failed"
864
- });
1021
+ state.set(
1022
+ deliveryNamespace,
1023
+ subscription.subscriptionHash,
1024
+ failedDelivery(record, state.get(deliveryNamespace, subscription.subscriptionHash), deliveryMode, error, now)
1025
+ );
865
1026
  }
866
1027
  }
867
1028
  async function readSources(signal) {
@@ -891,13 +1052,13 @@ async function createPersistentEventPublisherRuntime(options) {
891
1052
  if (subscriptions.length > timing.maxSubscriptions) throw new Error("Publisher subscription limit exceeded");
892
1053
  const active = new Set(subscriptions.map((subscription) => subscription.subscriptionHash));
893
1054
  if (active.size !== subscriptions.length) throw new Error("Duplicate subscription identity");
894
- const unavailable = state.entries("deliveries").filter((item) => !active.has(item.key) && item.value.state !== "cancelled");
1055
+ const unavailable = state.entries(deliveryNamespace).filter((item) => !active.has(item.key) && item.value.state !== "cancelled");
895
1056
  let available = true;
896
1057
  for (let offset = 0; offset < unavailable.length; offset += timing.concurrency) {
897
1058
  signal.throwIfAborted();
898
1059
  const results = await Promise.allSettled(unavailable.slice(offset, offset + timing.concurrency).map(async ({ key, value }) => {
899
1060
  const stillAuthorized = await timed(options.discovery.isActive(value.subscription, signal), signal);
900
- if (!stillAuthorized) state.set("deliveries", key, {
1061
+ if (!stillAuthorized) state.set(deliveryNamespace, key, {
901
1062
  ...value,
902
1063
  pending: void 0,
903
1064
  state: "cancelled"
@@ -991,9 +1152,14 @@ async function createPersistentEventPublisherRuntime(options) {
991
1152
  throw new AggregateError(failures, "Publisher runtime shutdown failed");
992
1153
  })(),
993
1154
  status() {
994
- const deliveries = state.entries("deliveries").map((item) => item.value);
1155
+ const deliveries = state.entries(deliveryNamespace).map((item) => item.value);
995
1156
  return {
996
1157
  running,
1158
+ deliveryMode,
1159
+ ...bestEffort === void 0 ? {} : { bestEffort: Object.freeze({ ...bestEffort }) },
1160
+ superseded: deliveries.reduce((sum, item) => sum + (item.superseded ?? 0), 0),
1161
+ abandoned: deliveries.reduce((sum, item) => sum + (item.abandoned ?? 0), 0),
1162
+ totalAttempts: deliveries.reduce((sum, item) => sum + (item.totalAttempts ?? 0), 0),
997
1163
  ready: running && ready,
998
1164
  clockHealthy,
999
1165
  degraded: !ready || deliveries.some((item) => item.state !== "cancelled" && item.failures > 0),
@@ -1005,10 +1171,15 @@ async function createPersistentEventPublisherRuntime(options) {
1005
1171
  passes,
1006
1172
  ...lastError === void 0 ? {} : { lastError },
1007
1173
  ...options.discovery.status === void 0 ? {} : { handshake: options.discovery.status() },
1008
- sources: state.entries("source-heads").map((item) => ({ id: item.key, ...item.value })),
1009
- subscriptions: state.entries("deliveries").map((item) => ({
1174
+ sources: state.entries("source-heads").filter((item) => sourceReaders.has(item.key)).map((item) => ({ id: item.key, ...item.value })),
1175
+ subscriptions: state.entries(deliveryNamespace).map((item) => ({
1010
1176
  id: item.key,
1011
1177
  cursor: item.value.cursor,
1178
+ ...deliveryMode === "best-effort" ? {
1179
+ ...item.value.cursor < item.value.subscription.fromPosition ? {} : { lastAdmissionTarget: item.value.cursor },
1180
+ attempts: item.value.attempts ?? 0,
1181
+ lastHintAt: item.value.lastHintAt
1182
+ } : {},
1012
1183
  state: item.value.state,
1013
1184
  pending: item.value.pending !== void 0,
1014
1185
  failures: item.value.failures
@@ -1099,7 +1270,7 @@ async function openPublisherDataLake(options) {
1099
1270
  }
1100
1271
 
1101
1272
  // src/runtime/sqliteHandshakeStores.ts
1102
- import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
1273
+ import { isDeepStrictEqual as isDeepStrictEqual4 } from "node:util";
1103
1274
  import { endpointChallengeHash } from "@xyo-network/event-kit-protocol";
1104
1275
  import {
1105
1276
  Base64Url32BytesZod as Base64Url32BytesZod2,
@@ -1330,7 +1501,7 @@ function requireTime(value) {
1330
1501
  if (!Number.isSafeInteger(value) || value < 0) throw new Error("Publisher clock is invalid");
1331
1502
  }
1332
1503
  function samePublicationIntent(left, right) {
1333
- return left.chainId === right.chainId && left.source === right.source && left.operationId === right.operationId && isDeepStrictEqual2(left.eventHashes, right.eventHashes) && isDeepStrictEqual2(left.attachmentHashes, right.attachmentHashes);
1504
+ return left.chainId === right.chainId && left.source === right.source && left.operationId === right.operationId && isDeepStrictEqual4(left.eventHashes, right.eventHashes) && isDeepStrictEqual4(left.attachmentHashes, right.attachmentHashes);
1334
1505
  }
1335
1506
  function publicationKey(intent) {
1336
1507
  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) => !HashZod2.safeParse(hash).success)) {
@@ -1338,11 +1509,12 @@ function publicationKey(intent) {
1338
1509
  }
1339
1510
  return JSON.stringify([intent.chainId, intent.source, intent.operationId]);
1340
1511
  }
1341
- function createSqlitePublisherHandshakeStores(state) {
1512
+ function createSqlitePublisherHandshakeStores(state, options = {}) {
1513
+ const replayKey = options.replayNamespace === void 0 ? "checkpoint" : `checkpoint:${z4.string().min(1).max(128).regex(/^[A-Za-z0-9_-]+$/u).parse(options.replayNamespace)}`;
1342
1514
  const replay = {
1343
- load: async () => state.get("replay", "checkpoint"),
1515
+ load: async () => state.get("replay", replayKey),
1344
1516
  async save(checkpoint) {
1345
- state.set("replay", "checkpoint", checkpoint);
1517
+ state.set("replay", replayKey, checkpoint);
1346
1518
  }
1347
1519
  };
1348
1520
  const intents = {
@@ -1380,7 +1552,7 @@ function createSqlitePublisherHandshakeStores(state) {
1380
1552
  const key = `${record.publisher}:${record.keyId}`;
1381
1553
  const prior = state.get("encryption-keys", key);
1382
1554
  if (prior !== void 0) {
1383
- if (!isDeepStrictEqual2(prior, record)) throw new Error("Encryption key collision");
1555
+ if (!isDeepStrictEqual4(prior, record)) throw new Error("Encryption key collision");
1384
1556
  return "duplicate";
1385
1557
  }
1386
1558
  state.set("encryption-keys", key, record);
@@ -1400,7 +1572,7 @@ function createSqlitePublisherHandshakeStores(state) {
1400
1572
  const priorNonce = state.get("challenge-nonces", challenge.nonce);
1401
1573
  if (priorNonce !== void 0 && priorNonce !== hash) return { kind: "collision" };
1402
1574
  const prior = current(hash);
1403
- if (prior !== void 0) return isDeepStrictEqual2(prior.challenge, challenge) ? { kind: "duplicate", record: prior } : { kind: "collision" };
1575
+ if (prior !== void 0) return isDeepStrictEqual4(prior.challenge, challenge) ? { kind: "duplicate", record: prior } : { kind: "collision" };
1404
1576
  const record = {
1405
1577
  challenge,
1406
1578
  challengeHash: hash,
@@ -1453,7 +1625,7 @@ function createSqlitePublisherHandshakeStores(state) {
1453
1625
  for (const phase of phases) {
1454
1626
  const record = state.get(`publication:${phase}`, key);
1455
1627
  if (record === void 0) continue;
1456
- if (record.phase !== phase || !samePublicationIntent(record, intent) || !isDeepStrictEqual2(record.evidence, signed.evidence)) {
1628
+ if (record.phase !== phase || !samePublicationIntent(record, intent) || !isDeepStrictEqual4(record.evidence, signed.evidence)) {
1457
1629
  throw new Error("Publication evidence collision");
1458
1630
  }
1459
1631
  latest = record;
@@ -1467,12 +1639,12 @@ function createSqlitePublisherHandshakeStores(state) {
1467
1639
  state.transaction(() => {
1468
1640
  const signed = state.get("publication:signed", key);
1469
1641
  if (signed === void 0 && record.phase !== "signed") throw new Error("Publication must bind signed evidence first");
1470
- if (signed !== void 0 && (!samePublicationIntent(signed, record) || !isDeepStrictEqual2(signed.evidence, record.evidence))) {
1642
+ if (signed !== void 0 && (!samePublicationIntent(signed, record) || !isDeepStrictEqual4(signed.evidence, record.evidence))) {
1471
1643
  throw new Error("Publication intent or evidence collision");
1472
1644
  }
1473
1645
  const namespace = `publication:${record.phase}`;
1474
1646
  const prior = state.get(namespace, key);
1475
- if (prior !== void 0 && !isDeepStrictEqual2(prior, record)) throw new Error("Publication phase collision");
1647
+ if (prior !== void 0 && !isDeepStrictEqual4(prior, record)) throw new Error("Publication phase collision");
1476
1648
  if (prior === void 0) state.set(namespace, key, record);
1477
1649
  });
1478
1650
  }