@xyo-network/event-kit-node 1.0.0 → 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) {
@@ -535,11 +552,7 @@ function loadPublisherAddressPolicy() {
535
552
  }
536
553
  function isPublicPublisherAddress(address) {
537
554
  const policy = loadPublisherAddressPolicy();
538
- if (isIP(address) === 4)
539
- return !policy.nonPublicIpv4.check(address, "ipv4");
540
- if (isIP(address) !== 6)
541
- return false;
542
- return policy.globalIpv6.check(address, "ipv6") && !policy.nonPublicIpv6.check(address, "ipv6");
555
+ return isIP(address) === 4 ? !policy.nonPublicIpv4.check(address, "ipv4") : isIP(address) === 6 && policy.globalIpv6.check(address, "ipv6") && !policy.nonPublicIpv6.check(address, "ipv6");
543
556
  }
544
557
  function createPublisherHttpsTransport(input) {
545
558
  const parsed = OptionsZod.safeParse(input);
@@ -646,18 +659,128 @@ function createPublisherHttpsTransport(input) {
646
659
  }
647
660
 
648
661
  // src/runtime/publisherRuntime.ts
649
- import { randomBytes as randomBytes2 } from "node:crypto";
650
- import { isDeepStrictEqual } from "node:util";
662
+ import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
651
663
  import { createMemoryLocator, PeriodicActor } from "@ariestools/actor";
652
664
  import { creatable } from "@ariestools/sdk";
653
- 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";
654
667
  import { z as z2 } from "zod";
655
668
 
656
- // src/runtime/sourceSigner.ts
669
+ // src/runtime/publisherDeliveryState.ts
657
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";
658
781
  import {
659
782
  NormalizedAddressZod,
660
- WakeEnvelopeZod,
783
+ WakeEnvelopeZod as WakeEnvelopeZod2,
661
784
  WakeJwtSchema
662
785
  } from "@xyo-network/event-kit-schemas";
663
786
  import { PayloadBuilder as PayloadBuilder2 } from "@xyo-network/sdk";
@@ -667,14 +790,14 @@ function createAccountSourceWakeSigner(options) {
667
790
  throw new Error("Publisher signing account mismatch");
668
791
  return {
669
792
  async sign(input, audience) {
670
- const event = WakeEnvelopeZod.parse(input);
793
+ const event = WakeEnvelopeZod2.parse(input);
671
794
  const eventHash = await PayloadBuilder2.dataHash(event);
672
795
  const now = options.nowSeconds?.() ?? Math.floor(Date.now() / 1e3);
673
796
  if (!Number.isSafeInteger(now) || now < 0)
674
797
  throw new Error("Publisher signing clock is invalid");
675
798
  const { token: jwt } = await options.account.signJwt({
676
799
  audience,
677
- claims: { eventHash, jti: randomBytes(16).toString("hex") },
800
+ claims: { eventHash, jti: randomBytes2(16).toString("hex") },
678
801
  now,
679
802
  schema: WakeJwtSchema,
680
803
  ttl: 60
@@ -693,31 +816,6 @@ function createAccountSourceWakeSigner(options) {
693
816
  function activationReady(status) {
694
817
  return (status?.keyLoaded ?? true) && (status?.pendingSubscriptions ?? 0) === 0;
695
818
  }
696
- function assertDeliveryState(record, subscription) {
697
- if (!isDeepStrictEqual(record.subscription, subscription))
698
- throw new Error("Subscription revision changed without a new identity");
699
- 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))
700
- throw new Error("Persisted publisher delivery state is invalid");
701
- if (record.pending !== void 0) {
702
- const pending = WakeEnvelopeZod2.parse(record.pending);
703
- 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))
704
- throw new Error("Persisted publisher outbox binding is invalid");
705
- }
706
- }
707
- function makeEvent(subscription, fromPosition, toPosition, now, wakeId = randomBytes2(16).toString("hex")) {
708
- return WakeEnvelopeZod2.parse({
709
- schema: WakeEnvelopeSchema,
710
- authorizationChainId: subscription.authorizationChainId,
711
- deploymentId: subscription.deploymentId,
712
- filterHash: subscription.filterHash,
713
- fromPosition,
714
- issuedAt: now,
715
- source: subscription.source,
716
- toPosition,
717
- wakeId,
718
- ...subscription.queueId === void 0 ? {} : { queueId: subscription.queueId }
719
- });
720
- }
721
819
  var PublisherRuntimeActor = class extends PeriodicActor {
722
820
  async runPass(context) {
723
821
  await this.params.tick(context.signal);
@@ -732,20 +830,24 @@ var TimingZod = z2.object({
732
830
  maxSubscriptions: z2.number().int().min(1).max(1024).default(64),
733
831
  requestTimeoutMs: z2.number().int().min(100).max(6e4).default(1e4)
734
832
  });
735
- var DeliveryResponseError = class extends Error {
736
- retryAfterMs;
737
- status;
738
- constructor(status, retryAfterMs) {
739
- super("Publisher admission failed");
740
- this.status = status;
741
- this.retryAfterMs = retryAfterMs;
742
- }
743
- get retryable() {
744
- return this.status === 408 || this.status === 429 || this.status >= 500;
745
- }
746
- };
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
+ }
747
844
  async function createPersistentEventPublisherRuntime(options) {
748
- const timing = TimingZod.parse(options);
845
+ const {
846
+ bestEffort,
847
+ deliveryMode,
848
+ deliveryNamespace,
849
+ timing
850
+ } = runtimeSettings(options);
749
851
  if (options.sources.length < 1 || options.sources.length > 16)
750
852
  throw new Error("Publisher requires between one and sixteen bounded sources");
751
853
  const clock = options.nowSeconds ?? (() => Math.floor(Date.now() / 1e3));
@@ -757,7 +859,12 @@ async function createPersistentEventPublisherRuntime(options) {
757
859
  const sourceReaders = new Map(options.sources.map((reader) => [publisherSourceKey(reader.source), reader]));
758
860
  if (sourceReaders.size !== options.sources.length)
759
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);
760
866
  const abort = new AbortController();
867
+ const seenThisProcess = /* @__PURE__ */ new Set();
761
868
  let running = false;
762
869
  let ready = false;
763
870
  let clockHealthy = true;
@@ -765,11 +872,16 @@ async function createPersistentEventPublisherRuntime(options) {
765
872
  let lastError;
766
873
  let sourcesHealthy = false;
767
874
  let stop;
768
- const state = options.state;
769
875
  const timed = (promise, signal) => boundedPublisherOperation(promise, signal, timing.requestTimeoutMs);
770
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");
771
882
  const key = subscription.subscriptionHash;
772
- 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);
773
885
  if (record === void 0) {
774
886
  makeEvent(subscription, subscription.fromPosition, subscription.fromPosition, now, "0".repeat(32));
775
887
  if (!Number.isSafeInteger(subscription.maxPositionsPerWake) || subscription.maxPositionsPerWake < 1 || subscription.maxPositionsPerWake > 4096) {
@@ -783,12 +895,26 @@ async function createPersistentEventPublisherRuntime(options) {
783
895
  nextAttemptMs: 0,
784
896
  state: "active"
785
897
  };
786
- state.set("deliveries", key, record);
898
+ state.set(deliveryNamespace, key, record);
787
899
  }
788
900
  assertDeliveryState(record, subscription);
789
901
  return record;
790
902
  }
791
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
+ }
792
918
  if (record.pending !== void 0) return { ...record, pending: record.pending };
793
919
  const subscription = record.subscription;
794
920
  const head = heads.get(publisherSourceKey(subscription.source));
@@ -800,43 +926,74 @@ async function createPersistentEventPublisherRuntime(options) {
800
926
  state.transaction(() => {
801
927
  const logicalKey = JSON.stringify([subscription.subscriptionHash, pending.fromPosition, pending.toPosition]);
802
928
  if (!state.insert("delivery-events", logicalKey, pending)) throw new Error("Publisher logical range already exists outside its contiguous cursor");
803
- state.set("deliveries", subscription.subscriptionHash, prepared);
929
+ state.set(deliveryNamespace, subscription.subscriptionHash, prepared);
804
930
  });
805
931
  return prepared;
806
932
  }
807
933
  async function stillActive(record, signal) {
808
934
  if (await timed(options.discovery.isActive(record.subscription, signal), signal)) return true;
809
- state.set("deliveries", record.subscription.subscriptionHash, {
935
+ state.set(deliveryNamespace, record.subscription.subscriptionHash, {
810
936
  ...record,
811
937
  pending: void 0,
812
938
  state: "cancelled"
813
939
  });
814
940
  return false;
815
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
+ }
816
964
  async function sendDelivery(record, signal, now) {
817
965
  const { pending, subscription } = record;
818
966
  signal.throwIfAborted();
819
967
  const signed = await signer.sign(pending, subscription.audience);
820
968
  signal.throwIfAborted();
821
- const attempt = state.get("delivery-attempts", pending.wakeId)?.count ?? 0;
822
- 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, {
823
976
  count: attempt + 1,
824
977
  startedAt: now,
825
978
  status: "in-flight"
826
979
  });
827
980
  const response = await options.transport.post(subscription.endpoint, signed.event, signal, signed.jwt);
828
- state.set("delivery-attempts", pending.wakeId, {
981
+ if (deliveryMode === "durable") state.set("delivery-attempts", pending.wakeId, {
829
982
  count: attempt + 1,
830
983
  startedAt: now,
831
984
  status: response.status
832
985
  });
833
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
+ }
834
991
  const receipt = z2.object({ receiptId: z2.string().min(1), wakeId: z2.literal(pending.wakeId) }).safeParse(response.body);
835
992
  if (!receipt.success) throw new Error("Publisher admission did not return a matching durable receipt");
836
993
  signal.throwIfAborted();
837
994
  state.transaction(() => {
838
995
  state.set("delivery-receipts", pending.wakeId, { receiptId: receipt.data.receiptId, receivedAt: now });
839
- state.set("deliveries", subscription.subscriptionHash, {
996
+ state.set(deliveryNamespace, subscription.subscriptionHash, {
840
997
  subscription,
841
998
  cursor: pending.toPosition,
842
999
  delivered: record.delivered + 1,
@@ -848,7 +1005,7 @@ async function createPersistentEventPublisherRuntime(options) {
848
1005
  }
849
1006
  async function deliver(subscription, signal, now, heads) {
850
1007
  let record = loadDelivery(subscription, now);
851
- if (record.state !== "active" || record.nextAttemptMs > now * 1e3) return;
1008
+ if (deliveryMode === "durable" && (record.state !== "active" || record.nextAttemptMs > now * 1e3)) return;
852
1009
  try {
853
1010
  if (!await stillActive(record, signal)) return;
854
1011
  options.transport.assertAllowed(subscription.endpoint);
@@ -856,17 +1013,16 @@ async function createPersistentEventPublisherRuntime(options) {
856
1013
  const prepared = prepareDelivery(record, now, heads);
857
1014
  if (prepared === void 0) return;
858
1015
  record = prepared;
1016
+ if (record.nextAttemptMs > now * 1e3) return;
859
1017
  if (!await stillActive(record, signal)) return;
860
1018
  await sendDelivery(prepared, signal, now);
861
1019
  } catch (error) {
862
1020
  if (signal.aborted) return;
863
- state.set("deliveries", subscription.subscriptionHash, {
864
- ...record,
865
- failures: record.failures + 1,
866
- state: error instanceof DeliveryResponseError && !error.retryable ? "blocked" : "active",
867
- 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))),
868
- lastError: "delivery-failed"
869
- });
1021
+ state.set(
1022
+ deliveryNamespace,
1023
+ subscription.subscriptionHash,
1024
+ failedDelivery(record, state.get(deliveryNamespace, subscription.subscriptionHash), deliveryMode, error, now)
1025
+ );
870
1026
  }
871
1027
  }
872
1028
  async function readSources(signal) {
@@ -896,13 +1052,13 @@ async function createPersistentEventPublisherRuntime(options) {
896
1052
  if (subscriptions.length > timing.maxSubscriptions) throw new Error("Publisher subscription limit exceeded");
897
1053
  const active = new Set(subscriptions.map((subscription) => subscription.subscriptionHash));
898
1054
  if (active.size !== subscriptions.length) throw new Error("Duplicate subscription identity");
899
- 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");
900
1056
  let available = true;
901
1057
  for (let offset = 0; offset < unavailable.length; offset += timing.concurrency) {
902
1058
  signal.throwIfAborted();
903
1059
  const results = await Promise.allSettled(unavailable.slice(offset, offset + timing.concurrency).map(async ({ key, value }) => {
904
1060
  const stillAuthorized = await timed(options.discovery.isActive(value.subscription, signal), signal);
905
- if (!stillAuthorized) state.set("deliveries", key, {
1061
+ if (!stillAuthorized) state.set(deliveryNamespace, key, {
906
1062
  ...value,
907
1063
  pending: void 0,
908
1064
  state: "cancelled"
@@ -960,10 +1116,11 @@ async function createPersistentEventPublisherRuntime(options) {
960
1116
  async start() {
961
1117
  if (stop !== void 0)
962
1118
  throw new Error("Publisher is stopped");
963
- if (!running) {
964
- await actor.start();
965
- running = true;
1119
+ if (running) {
1120
+ return;
966
1121
  }
1122
+ await actor.start();
1123
+ running = true;
967
1124
  },
968
1125
  async runOnce() {
969
1126
  if (stop !== void 0)
@@ -995,9 +1152,14 @@ async function createPersistentEventPublisherRuntime(options) {
995
1152
  throw new AggregateError(failures, "Publisher runtime shutdown failed");
996
1153
  })(),
997
1154
  status() {
998
- const deliveries = state.entries("deliveries").map((item) => item.value);
1155
+ const deliveries = state.entries(deliveryNamespace).map((item) => item.value);
999
1156
  return {
1000
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),
1001
1163
  ready: running && ready,
1002
1164
  clockHealthy,
1003
1165
  degraded: !ready || deliveries.some((item) => item.state !== "cancelled" && item.failures > 0),
@@ -1009,10 +1171,15 @@ async function createPersistentEventPublisherRuntime(options) {
1009
1171
  passes,
1010
1172
  ...lastError === void 0 ? {} : { lastError },
1011
1173
  ...options.discovery.status === void 0 ? {} : { handshake: options.discovery.status() },
1012
- sources: state.entries("source-heads").map((item) => ({ id: item.key, ...item.value })),
1013
- 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) => ({
1014
1176
  id: item.key,
1015
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
+ } : {},
1016
1183
  state: item.value.state,
1017
1184
  pending: item.value.pending !== void 0,
1018
1185
  failures: item.value.failures
@@ -1103,7 +1270,7 @@ async function openPublisherDataLake(options) {
1103
1270
  }
1104
1271
 
1105
1272
  // src/runtime/sqliteHandshakeStores.ts
1106
- import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
1273
+ import { isDeepStrictEqual as isDeepStrictEqual4 } from "node:util";
1107
1274
  import { endpointChallengeHash } from "@xyo-network/event-kit-protocol";
1108
1275
  import {
1109
1276
  Base64Url32BytesZod as Base64Url32BytesZod2,
@@ -1334,7 +1501,7 @@ function requireTime(value) {
1334
1501
  if (!Number.isSafeInteger(value) || value < 0) throw new Error("Publisher clock is invalid");
1335
1502
  }
1336
1503
  function samePublicationIntent(left, right) {
1337
- 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);
1338
1505
  }
1339
1506
  function publicationKey(intent) {
1340
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)) {
@@ -1342,11 +1509,12 @@ function publicationKey(intent) {
1342
1509
  }
1343
1510
  return JSON.stringify([intent.chainId, intent.source, intent.operationId]);
1344
1511
  }
1345
- 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)}`;
1346
1514
  const replay = {
1347
- load: async () => state.get("replay", "checkpoint"),
1515
+ load: async () => state.get("replay", replayKey),
1348
1516
  async save(checkpoint) {
1349
- state.set("replay", "checkpoint", checkpoint);
1517
+ state.set("replay", replayKey, checkpoint);
1350
1518
  }
1351
1519
  };
1352
1520
  const intents = {
@@ -1384,7 +1552,7 @@ function createSqlitePublisherHandshakeStores(state) {
1384
1552
  const key = `${record.publisher}:${record.keyId}`;
1385
1553
  const prior = state.get("encryption-keys", key);
1386
1554
  if (prior !== void 0) {
1387
- if (!isDeepStrictEqual2(prior, record)) throw new Error("Encryption key collision");
1555
+ if (!isDeepStrictEqual4(prior, record)) throw new Error("Encryption key collision");
1388
1556
  return "duplicate";
1389
1557
  }
1390
1558
  state.set("encryption-keys", key, record);
@@ -1404,7 +1572,7 @@ function createSqlitePublisherHandshakeStores(state) {
1404
1572
  const priorNonce = state.get("challenge-nonces", challenge.nonce);
1405
1573
  if (priorNonce !== void 0 && priorNonce !== hash) return { kind: "collision" };
1406
1574
  const prior = current(hash);
1407
- 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" };
1408
1576
  const record = {
1409
1577
  challenge,
1410
1578
  challengeHash: hash,
@@ -1457,7 +1625,7 @@ function createSqlitePublisherHandshakeStores(state) {
1457
1625
  for (const phase of phases) {
1458
1626
  const record = state.get(`publication:${phase}`, key);
1459
1627
  if (record === void 0) continue;
1460
- 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)) {
1461
1629
  throw new Error("Publication evidence collision");
1462
1630
  }
1463
1631
  latest = record;
@@ -1471,12 +1639,12 @@ function createSqlitePublisherHandshakeStores(state) {
1471
1639
  state.transaction(() => {
1472
1640
  const signed = state.get("publication:signed", key);
1473
1641
  if (signed === void 0 && record.phase !== "signed") throw new Error("Publication must bind signed evidence first");
1474
- 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))) {
1475
1643
  throw new Error("Publication intent or evidence collision");
1476
1644
  }
1477
1645
  const namespace = `publication:${record.phase}`;
1478
1646
  const prior = state.get(namespace, key);
1479
- 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");
1480
1648
  if (prior === void 0) state.set(namespace, key, record);
1481
1649
  });
1482
1650
  }
@@ -1512,9 +1680,7 @@ function replaceJsonValue(_key, candidate) {
1512
1680
  // src/runtime/sqlitePublisherState.ts
1513
1681
  function decode(value) {
1514
1682
  return JSON.parse(value, (_key, candidate) => {
1515
- if (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))
1516
- return BigInt(candidate.$eventKitBigInt);
1517
- return candidate;
1683
+ 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;
1518
1684
  });
1519
1685
  }
1520
1686
  function processExists(pid) {