@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.
@@ -1349,6 +1349,8 @@ async function persistMutationIntent(store, intent) {
1349
1349
  }
1350
1350
  async function publisherServiceRequestFingerprint(input) {
1351
1351
  return await handshakeMutationRequestFingerprint({
1352
+ deliveryMode: input.deliveryMode,
1353
+ bestEffort: input.bestEffort ?? null,
1352
1354
  expiresAtBlock: input.expiresAtBlock ?? null,
1353
1355
  maxPositionsPerWake: input.maxPositionsPerWake,
1354
1356
  sources: input.sources,
@@ -1368,6 +1370,8 @@ async function publisherRotationRequestFingerprint(input) {
1368
1370
  }
1369
1371
  async function subscriberSubscriptionRequestFingerprint(input) {
1370
1372
  return await handshakeMutationRequestFingerprint({
1373
+ deliveryMode: input.deliveryMode,
1374
+ bestEffort: input.bestEffort ?? null,
1371
1375
  audience: input.audience,
1372
1376
  challengeSecret: input.challengeSecret,
1373
1377
  deploymentId: input.deploymentId,
@@ -1754,6 +1758,8 @@ function createPublisherHandshakeClient(options) {
1754
1758
  validFromBlock
1755
1759
  };
1756
1760
  const service = createPublisherService({
1761
+ deliveryMode: input.deliveryMode,
1762
+ ...input.bestEffort === void 0 ? {} : { bestEffort: input.bestEffort },
1757
1763
  ...serviceInput,
1758
1764
  authorizationChainId: options.reader.chainId,
1759
1765
  sources: input.sources,
@@ -1873,6 +1879,8 @@ function createPublisherHandshakeClient(options) {
1873
1879
  validFromBlock
1874
1880
  };
1875
1881
  const service = createPublisherService({
1882
+ deliveryMode: selected.service.body.deliveryMode,
1883
+ ...selected.service.body.bestEffort === void 0 ? {} : { bestEffort: selected.service.body.bestEffort },
1876
1884
  ...serviceInput,
1877
1885
  authorizationChainId: options.reader.chainId,
1878
1886
  sources: selected.service.body.events.map((event) => event.source),
@@ -2166,6 +2174,8 @@ function createSubscriberHandshakeClient(options) {
2166
2174
  return Object.freeze(client);
2167
2175
  }
2168
2176
  function subscriberPlaintext(service, input, maxPositionsPerWake) {
2177
+ if (input.deliveryMode !== service.deliveryMode || canonicalJson(input.bestEffort ?? null) !== canonicalJson(service.bestEffort ?? null))
2178
+ throw new Error("Requested delivery terms do not match exact selected service");
2169
2179
  const plaintextInput = {
2170
2180
  audience: input.audience,
2171
2181
  challengeSecret: input.challengeSecret,
@@ -2178,6 +2188,8 @@ function subscriberPlaintext(service, input, maxPositionsPerWake) {
2178
2188
  throw new Error("Requested source is not advertised by the selected service");
2179
2189
  }
2180
2190
  return createWakeSubscriptionPlaintext({
2191
+ deliveryMode: input.deliveryMode,
2192
+ ...input.bestEffort === void 0 ? {} : { bestEffort: input.bestEffort },
2181
2193
  ...plaintextInput,
2182
2194
  source,
2183
2195
  fromPosition: input.fromPosition,
@@ -2317,6 +2329,7 @@ function createBoundedPublisherEthersTransport(options = {}) {
2317
2329
  }
2318
2330
 
2319
2331
  // src/runtime/handshakeDiscovery.ts
2332
+ import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
2320
2333
  import { decryptWakeSubscription, sourceWakeFilterHash } from "@xyo-network/event-kit-protocol";
2321
2334
  import { EndpointChallengeResponseZod as EndpointChallengeResponseZod2 } from "@xyo-network/event-kit-schemas";
2322
2335
  function createPublisherHandshakeDiscovery(options) {
@@ -2332,6 +2345,9 @@ function createPublisherHandshakeDiscovery(options) {
2332
2345
  rejectedSubscriptions: 0,
2333
2346
  challengeFailures: 0
2334
2347
  };
2348
+ function selectedTuple(tuple) {
2349
+ 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));
2350
+ }
2335
2351
  async function activeView(signal) {
2336
2352
  signal.throwIfAborted();
2337
2353
  viewing ??= (async () => {
@@ -2349,7 +2365,7 @@ function createPublisherHandshakeDiscovery(options) {
2349
2365
  ...status,
2350
2366
  finalizedHead: view.finalizedHead,
2351
2367
  indexedThrough: view.indexedThrough,
2352
- pendingSubscriptions: view.subscriptions.filter((item) => item.state === "pending").length,
2368
+ pendingSubscriptions: view.subscriptions.filter((item) => item.state === "pending" && item.tuples.some(selectedTuple)).length,
2353
2369
  rejectedSubscriptions: view.rejections.length
2354
2370
  };
2355
2371
  return view;
@@ -2414,7 +2430,7 @@ function createPublisherHandshakeDiscovery(options) {
2414
2430
  if (!found.ok)
2415
2431
  throw new Error("Publisher acceptance candidates are unavailable");
2416
2432
  const offset = options.state.get("challenge-rate", "offset") ?? 0;
2417
- const candidates = found.candidates;
2433
+ const candidates = found.candidates.filter((candidate) => selectedTuple(candidate.tuple));
2418
2434
  let attempted = 0;
2419
2435
  for (let index = 0; index < Math.min(candidates.length, 128) && attempted < 4; index += 1) {
2420
2436
  const candidate = candidates[(offset + index) % candidates.length];
@@ -2430,30 +2446,40 @@ function createPublisherHandshakeDiscovery(options) {
2430
2446
  await acceptCandidate(candidate.subscriber, candidate.tuple.subscription.objectHash, signal);
2431
2447
  }
2432
2448
  }
2449
+ async function loadScopedKeys(view, active) {
2450
+ const scopedKeys = active.flatMap((item) => item.tuples.filter((tuple) => tuple.subscription.objectHash === item.selectedSubscription).map((tuple) => ({
2451
+ encryptionKey: tuple.subscription.body.encryptionKey,
2452
+ keyId: tuple.subscription.body.keyId,
2453
+ publisher: options.publisher
2454
+ })));
2455
+ if (view.hasCurrentAdvertisement && (options.serviceHash === void 0 || view.service.objectHash === options.serviceHash) && (options.deliveryMode === void 0 || view.service.body.deliveryMode === options.deliveryMode)) {
2456
+ scopedKeys.push({
2457
+ encryptionKey: view.key.objectHash,
2458
+ keyId: view.key.body.keyId,
2459
+ publisher: options.publisher
2460
+ });
2461
+ }
2462
+ const loadedKeys = await Promise.all(scopedKeys.map(async (key) => await options.keys.getPrivateKey(key)));
2463
+ status = {
2464
+ ...status,
2465
+ keyLoaded: scopedKeys.length > 0 && loadedKeys.every((key) => key !== void 0),
2466
+ activeKeyIds: [...new Set(scopedKeys.map((key) => key.keyId))]
2467
+ };
2468
+ }
2433
2469
  return {
2434
2470
  async discover(signal) {
2435
2471
  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");
2472
+ const active = view.subscriptions.filter((item) => item.state === "active" && item.tuples.some((tuple) => tuple.subscription.objectHash === item.selectedSubscription && selectedTuple(tuple)));
2473
+ await loadScopedKeys(view, active);
2449
2474
  if (active.length > (options.maxSubscriptions ?? 64))
2450
2475
  throw new Error("Publisher active subscription limit exceeded");
2476
+ const subscriptions = [];
2451
2477
  for (const lifecycle of active) {
2452
2478
  if (lifecycle.state !== "active" || lifecycle.activeFromBlock === void 0)
2453
2479
  continue;
2454
2480
  try {
2455
2481
  const tuple = lifecycle.tuples.find((item) => item.subscription.objectHash === lifecycle.selectedSubscription);
2456
- if (tuple === void 0)
2482
+ if (tuple === void 0 || !selectedTuple(tuple))
2457
2483
  throw new Error("Active publisher subscription has no selected tuple");
2458
2484
  const encrypted = tuple.subscription.body;
2459
2485
  if (encrypted.chainId !== options.authorizationChainId)
@@ -2473,6 +2499,9 @@ function createPublisherHandshakeDiscovery(options) {
2473
2499
  options.transport.assertAllowed(plaintext.endpoint);
2474
2500
  subscriptions.push({
2475
2501
  subscriptionHash: tuple.subscription.objectHash,
2502
+ serviceHash: tuple.service.objectHash,
2503
+ deliveryMode: plaintext.filter.deliveryMode,
2504
+ ...plaintext.filter.bestEffort === void 0 ? {} : { bestEffort: plaintext.filter.bestEffort },
2476
2505
  authorizationChainId: options.authorizationChainId,
2477
2506
  controller: tuple.subscriber,
2478
2507
  audience: plaintext.audience,
@@ -2492,7 +2521,7 @@ function createPublisherHandshakeDiscovery(options) {
2492
2521
  },
2493
2522
  async isActive(subscription, signal) {
2494
2523
  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));
2524
+ 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
2525
  },
2497
2526
  async acceptPending(signal) {
2498
2527
  if (accepting === void 0) {
@@ -2818,18 +2847,128 @@ function createPublisherHttpsTransport(input) {
2818
2847
  }
2819
2848
 
2820
2849
  // src/runtime/publisherRuntime.ts
2821
- import { randomBytes as randomBytes6 } from "node:crypto";
2822
- import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
2850
+ import { isDeepStrictEqual as isDeepStrictEqual4 } from "node:util";
2823
2851
  import { createMemoryLocator, PeriodicActor } from "@ariestools/actor";
2824
2852
  import { creatable } from "@ariestools/sdk";
2825
- import { WakeEnvelopeSchema, WakeEnvelopeZod as WakeEnvelopeZod2 } from "@xyo-network/event-kit-schemas";
2853
+ import { BestEffortAdmissionAcknowledgmentZod } from "@xyo-network/event-kit-protocol";
2854
+ import { BestEffortDeliveryPolicyZod, WakeDeliveryModeZod } from "@xyo-network/event-kit-schemas";
2826
2855
  import { z as z3 } from "zod";
2827
2856
 
2828
- // src/runtime/sourceSigner.ts
2857
+ // src/runtime/publisherDeliveryState.ts
2829
2858
  import { randomBytes as randomBytes5 } from "node:crypto";
2859
+ import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
2860
+ import { WakeEnvelopeSchema, WakeEnvelopeZod } from "@xyo-network/event-kit-schemas";
2861
+ function assertDeliveryState(record, subscription) {
2862
+ if (!isDeepStrictEqual3(record.subscription, subscription))
2863
+ throw new Error("Subscription revision changed without a new identity");
2864
+ if (!Number.isSafeInteger(record.cursor) || record.cursor < subscription.fromPosition - 1 || [record.delivered, record.failures, record.nextAttemptMs].some((value) => !(Number.isSafeInteger(value) && value >= 0)) || [record.attempts, record.superseded, record.abandoned, record.totalAttempts, record.lastHintAt, record.lastNotifiedAt, record.lastHintTarget].some((value) => value !== void 0 && !(Number.isSafeInteger(value) && value >= 0)) || !["active", "blocked", "cancelled"].includes(record.state))
2865
+ throw new Error("Persisted publisher delivery state is invalid");
2866
+ if (record.pending === void 0) return;
2867
+ const pending = WakeEnvelopeZod.parse(record.pending);
2868
+ if (subscription.deliveryMode === "durable" && pending.fromPosition !== record.cursor + 1 || pending.deliveryMode !== subscription.deliveryMode || pending.toPosition - pending.fromPosition + 1 > subscription.maxPositionsPerWake || pending.authorizationChainId !== subscription.authorizationChainId || pending.deploymentId !== subscription.deploymentId || pending.queueId !== subscription.queueId || pending.filterHash !== subscription.filterHash || !isDeepStrictEqual3(pending.source, subscription.source))
2869
+ throw new Error("Persisted publisher outbox binding is invalid");
2870
+ }
2871
+ function makeEvent(subscription, fromPosition, toPosition, now, wakeId = randomBytes5(16).toString("hex")) {
2872
+ return WakeEnvelopeZod.parse({
2873
+ schema: WakeEnvelopeSchema,
2874
+ authorizationChainId: subscription.authorizationChainId,
2875
+ deliveryMode: subscription.deliveryMode,
2876
+ deploymentId: subscription.deploymentId,
2877
+ filterHash: subscription.filterHash,
2878
+ fromPosition,
2879
+ issuedAt: now,
2880
+ source: subscription.source,
2881
+ toPosition,
2882
+ wakeId,
2883
+ ...subscription.queueId === void 0 ? {} : { queueId: subscription.queueId }
2884
+ });
2885
+ }
2886
+ var DeliveryResponseError = class extends Error {
2887
+ retryAfterMs;
2888
+ status;
2889
+ constructor(status, retryAfterMs) {
2890
+ super("Publisher admission failed");
2891
+ this.status = status;
2892
+ this.retryAfterMs = retryAfterMs;
2893
+ }
2894
+ get retryable() {
2895
+ return this.status === 408 || this.status === 429 || this.status >= 500;
2896
+ }
2897
+ };
2898
+ function prepareBestEffortDelivery(options) {
2899
+ const {
2900
+ bestEffort,
2901
+ first,
2902
+ heads,
2903
+ now,
2904
+ record,
2905
+ state
2906
+ } = options;
2907
+ const subscription = record.subscription;
2908
+ const head = heads.get(publisherSourceKey(subscription.source));
2909
+ const prior = record.pending;
2910
+ const expired = isHintExpired(record, now, bestEffort);
2911
+ if (head === void 0 || head instanceof Error || head.position < subscription.fromPosition) {
2912
+ if (prior !== void 0 && !expired) return { ...record, pending: prior };
2913
+ if (prior !== void 0) state.set("best-effort-deliveries", subscription.subscriptionHash, {
2914
+ ...record,
2915
+ pending: void 0,
2916
+ abandoned: (record.abandoned ?? 0) + 1
2917
+ });
2918
+ return;
2919
+ }
2920
+ const refresh = refreshDue(record, now, bestEffort);
2921
+ if (freshHintRequired(record, head.position, first, refresh, expired)) {
2922
+ const pending = makeEvent(subscription, head.position, head.position, now);
2923
+ const prepared = {
2924
+ ...record,
2925
+ pending,
2926
+ attempts: 0,
2927
+ lastHintAt: now,
2928
+ lastHintTarget: head.position,
2929
+ superseded: (record.superseded ?? 0) + (prior !== void 0 && !expired ? 1 : 0),
2930
+ abandoned: (record.abandoned ?? 0) + (expired ? 1 : 0),
2931
+ nextAttemptMs: 0,
2932
+ state: "active"
2933
+ };
2934
+ state.set("best-effort-deliveries", subscription.subscriptionHash, prepared);
2935
+ return prepared;
2936
+ }
2937
+ if (expired) state.set("best-effort-deliveries", subscription.subscriptionHash, {
2938
+ ...record,
2939
+ pending: void 0,
2940
+ abandoned: (record.abandoned ?? 0) + 1
2941
+ });
2942
+ return prior === void 0 || expired ? void 0 : { ...record, pending: prior };
2943
+ }
2944
+ function isHintExpired(record, now, policy) {
2945
+ return record.pending !== void 0 && (now > record.pending.issuedAt + policy.maxHintAgeSeconds || (record.attempts ?? 0) >= policy.maxAttempts);
2946
+ }
2947
+ function refreshDue(record, now, policy) {
2948
+ return now - (record.lastHintAt ?? record.lastNotifiedAt ?? 0) >= policy.refreshIntervalSeconds;
2949
+ }
2950
+ function freshHintRequired(record, head, first, refresh, expired) {
2951
+ return first || head > (record.pending?.toPosition ?? record.lastHintTarget ?? record.cursor) || refresh && (record.pending === void 0 || expired);
2952
+ }
2953
+ function failedDelivery(record, current, mode, error, now) {
2954
+ const bestEffort = mode === "best-effort";
2955
+ const wait = error instanceof DeliveryResponseError && error.retryAfterMs !== void 0 ? error.retryAfterMs : Math.min(3e5, 1e3 * 2 ** Math.min(record.failures, 8));
2956
+ return {
2957
+ ...record,
2958
+ failures: record.failures + 1,
2959
+ totalAttempts: bestEffort ? current?.totalAttempts ?? record.totalAttempts ?? 0 : record.totalAttempts,
2960
+ attempts: bestEffort ? current?.attempts ?? record.attempts ?? 0 : record.attempts,
2961
+ state: !bestEffort && error instanceof DeliveryResponseError && !error.retryable ? "blocked" : "active",
2962
+ nextAttemptMs: now * 1e3 + Math.min(36e5, wait),
2963
+ lastError: "delivery-failed"
2964
+ };
2965
+ }
2966
+
2967
+ // src/runtime/sourceSigner.ts
2968
+ import { randomBytes as randomBytes6 } from "node:crypto";
2830
2969
  import {
2831
2970
  NormalizedAddressZod as NormalizedAddressZod4,
2832
- WakeEnvelopeZod,
2971
+ WakeEnvelopeZod as WakeEnvelopeZod2,
2833
2972
  WakeJwtSchema
2834
2973
  } from "@xyo-network/event-kit-schemas";
2835
2974
  import { PayloadBuilder as PayloadBuilder2 } from "@xyo-network/sdk";
@@ -2839,14 +2978,14 @@ function createAccountSourceWakeSigner(options) {
2839
2978
  throw new Error("Publisher signing account mismatch");
2840
2979
  return {
2841
2980
  async sign(input, audience) {
2842
- const event = WakeEnvelopeZod.parse(input);
2981
+ const event = WakeEnvelopeZod2.parse(input);
2843
2982
  const eventHash = await PayloadBuilder2.dataHash(event);
2844
2983
  const now = options.nowSeconds?.() ?? Math.floor(Date.now() / 1e3);
2845
2984
  if (!Number.isSafeInteger(now) || now < 0)
2846
2985
  throw new Error("Publisher signing clock is invalid");
2847
2986
  const { token: jwt } = await options.account.signJwt({
2848
2987
  audience,
2849
- claims: { eventHash, jti: randomBytes5(16).toString("hex") },
2988
+ claims: { eventHash, jti: randomBytes6(16).toString("hex") },
2850
2989
  now,
2851
2990
  schema: WakeJwtSchema,
2852
2991
  ttl: 60
@@ -2865,30 +3004,6 @@ function createAccountSourceWakeSigner(options) {
2865
3004
  function activationReady(status) {
2866
3005
  return (status?.keyLoaded ?? true) && (status?.pendingSubscriptions ?? 0) === 0;
2867
3006
  }
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
3007
  var PublisherRuntimeActor = class extends PeriodicActor {
2893
3008
  async runPass(context) {
2894
3009
  await this.params.tick(context.signal);
@@ -2903,20 +3018,24 @@ var TimingZod = z3.object({
2903
3018
  maxSubscriptions: z3.number().int().min(1).max(1024).default(64),
2904
3019
  requestTimeoutMs: z3.number().int().min(100).max(6e4).default(1e4)
2905
3020
  });
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
- };
3021
+ function runtimeSettings(options) {
3022
+ const deliveryMode = WakeDeliveryModeZod.parse(options.deliveryMode);
3023
+ const bestEffort = options.bestEffort === void 0 ? void 0 : BestEffortDeliveryPolicyZod.parse(options.bestEffort);
3024
+ if (deliveryMode === "best-effort" !== (bestEffort !== void 0)) throw new TypeError("Explicit delivery mode requires its exact policy");
3025
+ return {
3026
+ bestEffort,
3027
+ deliveryMode,
3028
+ deliveryNamespace: deliveryMode === "best-effort" ? "best-effort-deliveries" : "deliveries",
3029
+ timing: TimingZod.parse(options)
3030
+ };
3031
+ }
2918
3032
  async function createPersistentEventPublisherRuntime(options) {
2919
- const timing = TimingZod.parse(options);
3033
+ const {
3034
+ bestEffort,
3035
+ deliveryMode,
3036
+ deliveryNamespace,
3037
+ timing
3038
+ } = runtimeSettings(options);
2920
3039
  if (options.sources.length < 1 || options.sources.length > 16)
2921
3040
  throw new Error("Publisher requires between one and sixteen bounded sources");
2922
3041
  const clock = options.nowSeconds ?? (() => Math.floor(Date.now() / 1e3));
@@ -2928,7 +3047,12 @@ async function createPersistentEventPublisherRuntime(options) {
2928
3047
  const sourceReaders = new Map(options.sources.map((reader) => [publisherSourceKey(reader.source), reader]));
2929
3048
  if (sourceReaders.size !== options.sources.length)
2930
3049
  throw new Error("Duplicate publisher source identity");
3050
+ const state = options.state;
3051
+ const existingMode = state.get("metadata", "runtime-delivery-mode");
3052
+ if (existingMode !== void 0 && existingMode !== deliveryMode) throw new Error("Publisher state delivery mode mismatch; preserve custody and use an explicitly qualified state binding");
3053
+ state.set("metadata", "runtime-delivery-mode", deliveryMode);
2931
3054
  const abort = new AbortController();
3055
+ const seenThisProcess = /* @__PURE__ */ new Set();
2932
3056
  let running = false;
2933
3057
  let ready = false;
2934
3058
  let clockHealthy = true;
@@ -2936,11 +3060,16 @@ async function createPersistentEventPublisherRuntime(options) {
2936
3060
  let lastError;
2937
3061
  let sourcesHealthy = false;
2938
3062
  let stop;
2939
- const state = options.state;
2940
3063
  const timed = (promise, signal) => boundedPublisherOperation(promise, signal, timing.requestTimeoutMs);
2941
3064
  function loadDelivery(subscription, now) {
3065
+ if (subscription.deliveryMode !== deliveryMode || !isDeepStrictEqual4(subscription.bestEffort, bestEffort))
3066
+ throw new Error("Subscription delivery mode or policy disagrees with runtime");
3067
+ if (deliveryMode === "best-effort" && (subscription.maxPositionsPerWake !== 1 || subscription.serviceHash === void 0)) throw new Error("Best Effort requires point range");
3068
+ if (options.serviceHash !== void 0 && subscription.serviceHash !== options.serviceHash)
3069
+ throw new Error("Subscription does not match the configured exact service descriptor");
2942
3070
  const key = subscription.subscriptionHash;
2943
- let record = state.get("deliveries", key);
3071
+ 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");
3072
+ let record = state.get(deliveryNamespace, key);
2944
3073
  if (record === void 0) {
2945
3074
  makeEvent(subscription, subscription.fromPosition, subscription.fromPosition, now, "0".repeat(32));
2946
3075
  if (!Number.isSafeInteger(subscription.maxPositionsPerWake) || subscription.maxPositionsPerWake < 1 || subscription.maxPositionsPerWake > 4096) {
@@ -2954,12 +3083,26 @@ async function createPersistentEventPublisherRuntime(options) {
2954
3083
  nextAttemptMs: 0,
2955
3084
  state: "active"
2956
3085
  };
2957
- state.set("deliveries", key, record);
3086
+ state.set(deliveryNamespace, key, record);
2958
3087
  }
2959
3088
  assertDeliveryState(record, subscription);
2960
3089
  return record;
2961
3090
  }
2962
3091
  function prepareDelivery(record, now, heads) {
3092
+ if (deliveryMode === "best-effort" && bestEffort !== void 0) {
3093
+ const first = !seenThisProcess.has(record.subscription.subscriptionHash);
3094
+ const head2 = heads.get(publisherSourceKey(record.subscription.source));
3095
+ if (head2 !== void 0 && !(head2 instanceof Error) && head2.position >= record.subscription.fromPosition)
3096
+ seenThisProcess.add(record.subscription.subscriptionHash);
3097
+ return prepareBestEffortDelivery({
3098
+ bestEffort,
3099
+ first,
3100
+ heads,
3101
+ now,
3102
+ record,
3103
+ state
3104
+ });
3105
+ }
2963
3106
  if (record.pending !== void 0) return { ...record, pending: record.pending };
2964
3107
  const subscription = record.subscription;
2965
3108
  const head = heads.get(publisherSourceKey(subscription.source));
@@ -2971,43 +3114,74 @@ async function createPersistentEventPublisherRuntime(options) {
2971
3114
  state.transaction(() => {
2972
3115
  const logicalKey = JSON.stringify([subscription.subscriptionHash, pending.fromPosition, pending.toPosition]);
2973
3116
  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);
3117
+ state.set(deliveryNamespace, subscription.subscriptionHash, prepared);
2975
3118
  });
2976
3119
  return prepared;
2977
3120
  }
2978
3121
  async function stillActive(record, signal) {
2979
3122
  if (await timed(options.discovery.isActive(record.subscription, signal), signal)) return true;
2980
- state.set("deliveries", record.subscription.subscriptionHash, {
3123
+ state.set(deliveryNamespace, record.subscription.subscriptionHash, {
2981
3124
  ...record,
2982
3125
  pending: void 0,
2983
3126
  state: "cancelled"
2984
3127
  });
2985
3128
  return false;
2986
3129
  }
3130
+ function acknowledgeBestEffort(record, body, eventHash, signal, now) {
3131
+ const { pending, subscription } = record;
3132
+ const acknowledgment = BestEffortAdmissionAcknowledgmentZod.safeParse(body);
3133
+ 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)
3134
+ throw new Error("Publisher did not receive a matching finite acknowledgment");
3135
+ signal.throwIfAborted();
3136
+ state.set(deliveryNamespace, subscription.subscriptionHash, {
3137
+ subscription,
3138
+ cursor: pending.toPosition,
3139
+ delivered: record.delivered + 1,
3140
+ failures: 0,
3141
+ attempts: 0,
3142
+ superseded: record.superseded ?? 0,
3143
+ abandoned: record.abandoned ?? 0,
3144
+ totalAttempts: (record.totalAttempts ?? 0) + 1,
3145
+ lastNotifiedAt: now,
3146
+ lastHintAt: pending.issuedAt,
3147
+ lastHintTarget: pending.toPosition,
3148
+ nextAttemptMs: 0,
3149
+ state: "active"
3150
+ });
3151
+ }
2987
3152
  async function sendDelivery(record, signal, now) {
2988
3153
  const { pending, subscription } = record;
2989
3154
  signal.throwIfAborted();
2990
3155
  const signed = await signer.sign(pending, subscription.audience);
2991
3156
  signal.throwIfAborted();
2992
- const attempt = state.get("delivery-attempts", pending.wakeId)?.count ?? 0;
2993
- state.set("delivery-attempts", pending.wakeId, {
3157
+ const attempt = deliveryMode === "best-effort" ? record.attempts ?? 0 : state.get("delivery-attempts", pending.wakeId)?.count ?? 0;
3158
+ if (deliveryMode === "best-effort") state.set(deliveryNamespace, subscription.subscriptionHash, {
3159
+ ...record,
3160
+ attempts: attempt + 1,
3161
+ totalAttempts: (record.totalAttempts ?? 0) + 1
3162
+ });
3163
+ else state.set("delivery-attempts", pending.wakeId, {
2994
3164
  count: attempt + 1,
2995
3165
  startedAt: now,
2996
3166
  status: "in-flight"
2997
3167
  });
2998
3168
  const response = await options.transport.post(subscription.endpoint, signed.event, signal, signed.jwt);
2999
- state.set("delivery-attempts", pending.wakeId, {
3169
+ if (deliveryMode === "durable") state.set("delivery-attempts", pending.wakeId, {
3000
3170
  count: attempt + 1,
3001
3171
  startedAt: now,
3002
3172
  status: response.status
3003
3173
  });
3004
3174
  if (response.status !== 200 && response.status !== 202) throw new DeliveryResponseError(response.status, response.retryAfterMs);
3175
+ if (deliveryMode === "best-effort") {
3176
+ acknowledgeBestEffort(record, response.body, signed.eventHash, signal, now);
3177
+ return;
3178
+ }
3005
3179
  const receipt = z3.object({ receiptId: z3.string().min(1), wakeId: z3.literal(pending.wakeId) }).safeParse(response.body);
3006
3180
  if (!receipt.success) throw new Error("Publisher admission did not return a matching durable receipt");
3007
3181
  signal.throwIfAborted();
3008
3182
  state.transaction(() => {
3009
3183
  state.set("delivery-receipts", pending.wakeId, { receiptId: receipt.data.receiptId, receivedAt: now });
3010
- state.set("deliveries", subscription.subscriptionHash, {
3184
+ state.set(deliveryNamespace, subscription.subscriptionHash, {
3011
3185
  subscription,
3012
3186
  cursor: pending.toPosition,
3013
3187
  delivered: record.delivered + 1,
@@ -3019,7 +3193,7 @@ async function createPersistentEventPublisherRuntime(options) {
3019
3193
  }
3020
3194
  async function deliver(subscription, signal, now, heads) {
3021
3195
  let record = loadDelivery(subscription, now);
3022
- if (record.state !== "active" || record.nextAttemptMs > now * 1e3) return;
3196
+ if (deliveryMode === "durable" && (record.state !== "active" || record.nextAttemptMs > now * 1e3)) return;
3023
3197
  try {
3024
3198
  if (!await stillActive(record, signal)) return;
3025
3199
  options.transport.assertAllowed(subscription.endpoint);
@@ -3027,17 +3201,16 @@ async function createPersistentEventPublisherRuntime(options) {
3027
3201
  const prepared = prepareDelivery(record, now, heads);
3028
3202
  if (prepared === void 0) return;
3029
3203
  record = prepared;
3204
+ if (record.nextAttemptMs > now * 1e3) return;
3030
3205
  if (!await stillActive(record, signal)) return;
3031
3206
  await sendDelivery(prepared, signal, now);
3032
3207
  } catch (error) {
3033
3208
  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
- });
3209
+ state.set(
3210
+ deliveryNamespace,
3211
+ subscription.subscriptionHash,
3212
+ failedDelivery(record, state.get(deliveryNamespace, subscription.subscriptionHash), deliveryMode, error, now)
3213
+ );
3041
3214
  }
3042
3215
  }
3043
3216
  async function readSources(signal) {
@@ -3067,13 +3240,13 @@ async function createPersistentEventPublisherRuntime(options) {
3067
3240
  if (subscriptions.length > timing.maxSubscriptions) throw new Error("Publisher subscription limit exceeded");
3068
3241
  const active = new Set(subscriptions.map((subscription) => subscription.subscriptionHash));
3069
3242
  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");
3243
+ const unavailable = state.entries(deliveryNamespace).filter((item) => !active.has(item.key) && item.value.state !== "cancelled");
3071
3244
  let available = true;
3072
3245
  for (let offset = 0; offset < unavailable.length; offset += timing.concurrency) {
3073
3246
  signal.throwIfAborted();
3074
3247
  const results = await Promise.allSettled(unavailable.slice(offset, offset + timing.concurrency).map(async ({ key, value }) => {
3075
3248
  const stillAuthorized = await timed(options.discovery.isActive(value.subscription, signal), signal);
3076
- if (!stillAuthorized) state.set("deliveries", key, {
3249
+ if (!stillAuthorized) state.set(deliveryNamespace, key, {
3077
3250
  ...value,
3078
3251
  pending: void 0,
3079
3252
  state: "cancelled"
@@ -3167,9 +3340,14 @@ async function createPersistentEventPublisherRuntime(options) {
3167
3340
  throw new AggregateError(failures, "Publisher runtime shutdown failed");
3168
3341
  })(),
3169
3342
  status() {
3170
- const deliveries = state.entries("deliveries").map((item) => item.value);
3343
+ const deliveries = state.entries(deliveryNamespace).map((item) => item.value);
3171
3344
  return {
3172
3345
  running,
3346
+ deliveryMode,
3347
+ ...bestEffort === void 0 ? {} : { bestEffort: Object.freeze({ ...bestEffort }) },
3348
+ superseded: deliveries.reduce((sum, item) => sum + (item.superseded ?? 0), 0),
3349
+ abandoned: deliveries.reduce((sum, item) => sum + (item.abandoned ?? 0), 0),
3350
+ totalAttempts: deliveries.reduce((sum, item) => sum + (item.totalAttempts ?? 0), 0),
3173
3351
  ready: running && ready,
3174
3352
  clockHealthy,
3175
3353
  degraded: !ready || deliveries.some((item) => item.state !== "cancelled" && item.failures > 0),
@@ -3181,10 +3359,15 @@ async function createPersistentEventPublisherRuntime(options) {
3181
3359
  passes,
3182
3360
  ...lastError === void 0 ? {} : { lastError },
3183
3361
  ...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) => ({
3362
+ sources: state.entries("source-heads").filter((item) => sourceReaders.has(item.key)).map((item) => ({ id: item.key, ...item.value })),
3363
+ subscriptions: state.entries(deliveryNamespace).map((item) => ({
3186
3364
  id: item.key,
3187
3365
  cursor: item.value.cursor,
3366
+ ...deliveryMode === "best-effort" ? {
3367
+ ...item.value.cursor < item.value.subscription.fromPosition ? {} : { lastAdmissionTarget: item.value.cursor },
3368
+ attempts: item.value.attempts ?? 0,
3369
+ lastHintAt: item.value.lastHintAt
3370
+ } : {},
3188
3371
  state: item.value.state,
3189
3372
  pending: item.value.pending !== void 0,
3190
3373
  failures: item.value.failures
@@ -3275,7 +3458,7 @@ async function openPublisherDataLake(options) {
3275
3458
  }
3276
3459
 
3277
3460
  // src/runtime/sqliteHandshakeStores.ts
3278
- import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
3461
+ import { isDeepStrictEqual as isDeepStrictEqual5 } from "node:util";
3279
3462
  import { endpointChallengeHash as endpointChallengeHash2 } from "@xyo-network/event-kit-protocol";
3280
3463
  import {
3281
3464
  Base64Url32BytesZod as Base64Url32BytesZod3,
@@ -3296,7 +3479,7 @@ function requireTime2(value) {
3296
3479
  if (!Number.isSafeInteger(value) || value < 0) throw new Error("Publisher clock is invalid");
3297
3480
  }
3298
3481
  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);
3482
+ return left.chainId === right.chainId && left.source === right.source && left.operationId === right.operationId && isDeepStrictEqual5(left.eventHashes, right.eventHashes) && isDeepStrictEqual5(left.attachmentHashes, right.attachmentHashes);
3300
3483
  }
3301
3484
  function publicationKey(intent) {
3302
3485
  if (!/^[0-9a-f]{40}$/u.test(intent.chainId) || !/^[0-9a-f]{40}$/u.test(intent.source) || intent.operationId.length === 0 || intent.operationId.length > 256 || [...intent.eventHashes, ...intent.attachmentHashes].some((hash) => !HashZod5.safeParse(hash).success)) {
@@ -3304,11 +3487,12 @@ function publicationKey(intent) {
3304
3487
  }
3305
3488
  return JSON.stringify([intent.chainId, intent.source, intent.operationId]);
3306
3489
  }
3307
- function createSqlitePublisherHandshakeStores(state) {
3490
+ function createSqlitePublisherHandshakeStores(state, options = {}) {
3491
+ const replayKey = options.replayNamespace === void 0 ? "checkpoint" : `checkpoint:${z4.string().min(1).max(128).regex(/^[A-Za-z0-9_-]+$/u).parse(options.replayNamespace)}`;
3308
3492
  const replay = {
3309
- load: async () => state.get("replay", "checkpoint"),
3493
+ load: async () => state.get("replay", replayKey),
3310
3494
  async save(checkpoint) {
3311
- state.set("replay", "checkpoint", checkpoint);
3495
+ state.set("replay", replayKey, checkpoint);
3312
3496
  }
3313
3497
  };
3314
3498
  const intents = {
@@ -3346,7 +3530,7 @@ function createSqlitePublisherHandshakeStores(state) {
3346
3530
  const key = `${record.publisher}:${record.keyId}`;
3347
3531
  const prior = state.get("encryption-keys", key);
3348
3532
  if (prior !== void 0) {
3349
- if (!isDeepStrictEqual3(prior, record)) throw new Error("Encryption key collision");
3533
+ if (!isDeepStrictEqual5(prior, record)) throw new Error("Encryption key collision");
3350
3534
  return "duplicate";
3351
3535
  }
3352
3536
  state.set("encryption-keys", key, record);
@@ -3366,7 +3550,7 @@ function createSqlitePublisherHandshakeStores(state) {
3366
3550
  const priorNonce = state.get("challenge-nonces", challenge.nonce);
3367
3551
  if (priorNonce !== void 0 && priorNonce !== hash) return { kind: "collision" };
3368
3552
  const prior = current(hash);
3369
- if (prior !== void 0) return isDeepStrictEqual3(prior.challenge, challenge) ? { kind: "duplicate", record: prior } : { kind: "collision" };
3553
+ if (prior !== void 0) return isDeepStrictEqual5(prior.challenge, challenge) ? { kind: "duplicate", record: prior } : { kind: "collision" };
3370
3554
  const record = {
3371
3555
  challenge,
3372
3556
  challengeHash: hash,
@@ -3419,7 +3603,7 @@ function createSqlitePublisherHandshakeStores(state) {
3419
3603
  for (const phase of phases) {
3420
3604
  const record = state.get(`publication:${phase}`, key);
3421
3605
  if (record === void 0) continue;
3422
- if (record.phase !== phase || !samePublicationIntent(record, intent) || !isDeepStrictEqual3(record.evidence, signed.evidence)) {
3606
+ if (record.phase !== phase || !samePublicationIntent(record, intent) || !isDeepStrictEqual5(record.evidence, signed.evidence)) {
3423
3607
  throw new Error("Publication evidence collision");
3424
3608
  }
3425
3609
  latest = record;
@@ -3433,12 +3617,12 @@ function createSqlitePublisherHandshakeStores(state) {
3433
3617
  state.transaction(() => {
3434
3618
  const signed = state.get("publication:signed", key);
3435
3619
  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))) {
3620
+ if (signed !== void 0 && (!samePublicationIntent(signed, record) || !isDeepStrictEqual5(signed.evidence, record.evidence))) {
3437
3621
  throw new Error("Publication intent or evidence collision");
3438
3622
  }
3439
3623
  const namespace = `publication:${record.phase}`;
3440
3624
  const prior = state.get(namespace, key);
3441
- if (prior !== void 0 && !isDeepStrictEqual3(prior, record)) throw new Error("Publication phase collision");
3625
+ if (prior !== void 0 && !isDeepStrictEqual5(prior, record)) throw new Error("Publication phase collision");
3442
3626
  if (prior === void 0) state.set(namespace, key, record);
3443
3627
  });
3444
3628
  }