@xyo-network/event-kit-protocol 1.0.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/neutral/admissionCore.d.ts +9 -1
  2. package/dist/neutral/admissionCore.d.ts.map +1 -1
  3. package/dist/neutral/admissionTypes.d.ts +28 -13
  4. package/dist/neutral/admissionTypes.d.ts.map +1 -1
  5. package/dist/neutral/admitAndEnqueue.d.ts.map +1 -1
  6. package/dist/neutral/bestEffortAdmission.d.ts +113 -0
  7. package/dist/neutral/bestEffortAdmission.d.ts.map +1 -0
  8. package/dist/neutral/grantEvidence.d.ts +3 -3
  9. package/dist/neutral/grantEvidence.d.ts.map +1 -1
  10. package/dist/neutral/handshakeConstruction.d.ts +5 -1
  11. package/dist/neutral/handshakeConstruction.d.ts.map +1 -1
  12. package/dist/neutral/index.d.ts +4 -2
  13. package/dist/neutral/index.d.ts.map +1 -1
  14. package/dist/neutral/index.mjs +568 -313
  15. package/dist/neutral/index.mjs.map +4 -4
  16. package/dist/neutral/memoryWakeQueue.d.ts +2 -3
  17. package/dist/neutral/memoryWakeQueue.d.ts.map +1 -1
  18. package/dist/neutral/publisherSubscription.d.ts.map +1 -1
  19. package/dist/neutral/staleWakeLeaseError.d.ts +7 -0
  20. package/dist/neutral/staleWakeLeaseError.d.ts.map +1 -0
  21. package/dist/neutral/statementGraphWakeGrantAuthorizer.d.ts.map +1 -1
  22. package/dist/neutral/types.d.ts +4 -0
  23. package/dist/neutral/types.d.ts.map +1 -1
  24. package/dist/neutral/verifiedAdmissionWake.d.ts +3 -1
  25. package/dist/neutral/verifiedAdmissionWake.d.ts.map +1 -1
  26. package/dist/neutral/verifyWakeRequest.d.ts +10 -1
  27. package/dist/neutral/verifyWakeRequest.d.ts.map +1 -1
  28. package/dist/neutral/wakeQueue.d.ts +16 -4
  29. package/dist/neutral/wakeQueue.d.ts.map +1 -1
  30. package/dist/neutral/wakeSubscriptionEncoding.d.ts.map +1 -1
  31. package/package.json +5 -5
@@ -12,7 +12,7 @@ async function admitVerifiedWake(input) {
12
12
  if (!isNonnegativeSafeInteger(input.expectedHead)) {
13
13
  return reject("grant-view-invalid", ["expectedHead must be a nonnegative safe integer"]);
14
14
  }
15
- const authorization = await authorize(input);
15
+ const authorization = await authorizeVerifiedWake(input);
16
16
  return authorization.ok ? await commitAuthorizedWake(input.inbox, attempt, authorization.grant, wake) : authorization.failure;
17
17
  }
18
18
  async function resolvePriorAttempt(inbox, attempt, wake) {
@@ -79,7 +79,7 @@ function createAttempt(verified) {
79
79
  ok: true
80
80
  };
81
81
  }
82
- async function authorize(input) {
82
+ async function authorizeVerifiedWake(input) {
83
83
  const { route } = input.verified;
84
84
  let decision;
85
85
  try {
@@ -133,6 +133,7 @@ function validateGrantEvidence(evidence, verified, atBlock) {
133
133
  if (!isRecord(evidence)) return reject("grant-view-invalid", ["enabled grant decision omitted evidence"]);
134
134
  const requiredFields = [
135
135
  "assertedAtBlock",
136
+ "authorizationKind",
136
137
  "authorizationChainId",
137
138
  "filterHash",
138
139
  "sourceHash",
@@ -145,6 +146,9 @@ function validateGrantEvidence(evidence, verified, atBlock) {
145
146
  if (requiredFields.some((field) => !Object.hasOwn(evidence, field))) {
146
147
  return reject("grant-view-invalid", ["enabled grant evidence requires own canonical fields"]);
147
148
  }
149
+ if (evidence.authorizationKind !== "activated-subscription") {
150
+ return reject("grant-view-invalid", ["enabled authorization requires finalized subscription activation"]);
151
+ }
148
152
  if (typeof evidence.id !== "string" || evidence.id.length === 0) {
149
153
  return reject("grant-view-invalid", ["enabled grant evidence requires a non-empty id"]);
150
154
  }
@@ -180,6 +184,7 @@ function validateReceipt(receipt, attempt) {
180
184
  function snapshotGrantEvidence(evidence) {
181
185
  return Object.freeze({
182
186
  assertedAtBlock: evidence.assertedAtBlock,
187
+ authorizationKind: evidence.authorizationKind,
183
188
  authorizationChainId: evidence.authorizationChainId,
184
189
  sourceHash: evidence.sourceHash,
185
190
  filterHash: evidence.filterHash,
@@ -266,6 +271,7 @@ async function parseAdmittedWake(value) {
266
271
  // src/admitAndEnqueue.ts
267
272
  async function admitAndEnqueue(input) {
268
273
  const { verified } = input;
274
+ if (verified.route.deliveryMode !== "durable") throw new TypeError("Durable admission requires explicit durable delivery mode");
269
275
  const wake = AdmittedWakeZod.parse({
270
276
  audience: verified.audience,
271
277
  deploymentId: verified.deploymentId,
@@ -281,6 +287,53 @@ async function admitAndEnqueue(input) {
281
287
  return await admitVerifiedWake({ ...input, wake });
282
288
  }
283
289
 
290
+ // src/bestEffortAdmission.ts
291
+ import {
292
+ HashZod as HashZod3,
293
+ NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod3,
294
+ NormalizedAddressZod as NormalizedAddressZod3,
295
+ WakeIdZod as WakeIdZod2,
296
+ WakeJwtAudienceZod as WakeJwtAudienceZod3,
297
+ WakeJwtIdZod as WakeJwtIdZod2
298
+ } from "@xyo-network/event-kit-schemas";
299
+ import { z as z3 } from "zod";
300
+
301
+ // src/verifyWakeRequest.ts
302
+ import {
303
+ ChainIdZod,
304
+ DeploymentIdZod as DeploymentIdZod2,
305
+ HashZod as HashZod2,
306
+ MAX_POSITIONS_PER_SUBSCRIPTION_WAKE,
307
+ NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod2,
308
+ NormalizedAddressZod as NormalizedAddressZod2,
309
+ PositiveJsonSafeIntegerZod,
310
+ SOURCE_WAKE_FILTER_PROFILE,
311
+ validWakeDeliveryTerms,
312
+ WakeDeliveryTermsFields,
313
+ WakeEnvelopeZod as WakeEnvelopeZod2,
314
+ WakeJwtAudienceZod as WakeJwtAudienceZod2,
315
+ WakeJwtHeaderZod,
316
+ WakeJwtPayloadZod,
317
+ WakeJwtSchema,
318
+ WakeSourceZod as WakeSourceZod2
319
+ } from "@xyo-network/event-kit-schemas";
320
+ import {
321
+ Account,
322
+ PayloadBuilder as PayloadBuilder3
323
+ } from "@xyo-network/sdk";
324
+ import { z as z2 } from "zod";
325
+
326
+ // src/sourceWakeIdentity.ts
327
+ import {
328
+ SourceWakeSubscriptionFilterZod,
329
+ WakeSourceSchema,
330
+ WakeSourceZod
331
+ } from "@xyo-network/event-kit-schemas";
332
+ import {
333
+ asSchema,
334
+ PayloadBuilder as PayloadBuilder2
335
+ } from "@xyo-network/sdk";
336
+
284
337
  // src/canonicalJson.ts
285
338
  var textEncoder = new TextEncoder();
286
339
  function hasUnpairedSurrogate(value) {
@@ -330,6 +383,379 @@ function canonicalJsonBytes(value) {
330
383
  return textEncoder.encode(canonicalize(value));
331
384
  }
332
385
 
386
+ // src/sourceWakeIdentity.ts
387
+ async function wakeSourceHash(source) {
388
+ return await PayloadBuilder2.dataHash({ schema: WakeSourceSchema, ...WakeSourceZod.parse(source) });
389
+ }
390
+ function wakeSourcesEqual(left, right) {
391
+ return canonicalJson(WakeSourceZod.parse(left)) === canonicalJson(WakeSourceZod.parse(right));
392
+ }
393
+ async function sourceWakeFilterHash(filter) {
394
+ const parsed = SourceWakeSubscriptionFilterZod.parse(filter);
395
+ return await PayloadBuilder2.dataHash({ ...parsed, schema: asSchema(parsed.schema, true) });
396
+ }
397
+
398
+ // src/verifyWakeRequest.ts
399
+ var EVENT_KIT_MAX_JWT_LIFETIME_SECONDS = 300;
400
+ var EVENT_KIT_MAX_FUTURE_IAT_SECONDS = 60;
401
+ var WakeAdmissionRouteZod = z2.strictObject({
402
+ ...WakeDeliveryTermsFields,
403
+ audience: WakeJwtAudienceZod2,
404
+ authorizationChainId: ChainIdZod,
405
+ controller: NormalizedAddressZod2,
406
+ deploymentId: DeploymentIdZod2,
407
+ queueId: z2.string().min(1).max(255).optional(),
408
+ source: WakeSourceZod2,
409
+ filterHash: HashZod2,
410
+ subscriptionHash: HashZod2,
411
+ fromPosition: NonnegativeJsonSafeIntegerZod2,
412
+ maxPositionsPerWake: PositiveJsonSafeIntegerZod.max(MAX_POSITIONS_PER_SUBSCRIPTION_WAKE),
413
+ maxGrantLagBlocks: NonnegativeJsonSafeIntegerZod2
414
+ }).refine((route) => validWakeDeliveryTerms(route) && (route.deliveryMode !== "best-effort" || route.maxPositionsPerWake === 1), "Invalid explicit delivery terms");
415
+ function verifyEventBinding(event, route, nowSeconds) {
416
+ if (event.authorizationChainId !== route.authorizationChainId) {
417
+ return { ok: false, reason: "event-authorization-chain-mismatch" };
418
+ }
419
+ if (!wakeSourcesEqual(event.source, route.source)) return { ok: false, reason: "event-source-mismatch" };
420
+ if (event.filterHash !== route.filterHash) return { ok: false, reason: "event-filter-mismatch" };
421
+ if (event.deploymentId !== route.deploymentId || event.queueId !== route.queueId) {
422
+ return { ok: false, reason: "event-target-mismatch" };
423
+ }
424
+ if (event.deliveryMode !== route.deliveryMode) return { ok: false, reason: "event-mode-mismatch" };
425
+ if (route.deliveryMode === "best-effort") {
426
+ if (event.issuedAt > nowSeconds + EVENT_KIT_MAX_FUTURE_IAT_SECONDS) return { ok: false, reason: "hint-future-issued-at" };
427
+ if (route.bestEffort === void 0 || nowSeconds > event.issuedAt + route.bestEffort.maxHintAgeSeconds) return { ok: false, reason: "hint-expired" };
428
+ }
429
+ if (event.fromPosition < route.fromPosition) return { ok: false, reason: "range-before-floor" };
430
+ if (event.toPosition - event.fromPosition >= route.maxPositionsPerWake) return { ok: false, reason: "range-too-large" };
431
+ }
432
+ async function verifyWakeRequest(input) {
433
+ const routeResult = WakeAdmissionRouteZod.safeParse(input.route);
434
+ if (!routeResult.success || !NonnegativeJsonSafeIntegerZod2.safeParse(input.nowSeconds).success) {
435
+ return { ok: false, reason: "configuration-invalid" };
436
+ }
437
+ const eventResult = WakeEnvelopeZod2.safeParse(input.body);
438
+ if (!eventResult.success) return malformedWake(input.body, eventResult.error.issues);
439
+ const route = routeResult.data;
440
+ if (route.filterHash !== await sourceWakeFilterHash({
441
+ schema: SOURCE_WAKE_FILTER_PROFILE,
442
+ deliveryMode: route.deliveryMode,
443
+ ...route.bestEffort === void 0 ? {} : { bestEffort: route.bestEffort },
444
+ source: route.source,
445
+ fromPosition: route.fromPosition,
446
+ maxPositionsPerWake: route.maxPositionsPerWake
447
+ })) return { ok: false, reason: "configuration-invalid" };
448
+ const event = Object.freeze({ ...eventResult.data, source: Object.freeze(eventResult.data.source) });
449
+ const mismatch = verifyEventBinding(event, route, input.nowSeconds);
450
+ if (mismatch !== void 0) return mismatch;
451
+ const jwt = await verifyJwtProfile(input.jwt, route.audience, input.nowSeconds);
452
+ if (!jwt.ok) return jwt;
453
+ let eventHash;
454
+ try {
455
+ eventHash = await PayloadBuilder3.dataHash(event);
456
+ } catch {
457
+ return { ok: false, reason: "event-hash-failed" };
458
+ }
459
+ if (eventHash !== jwt.jwt.eventHash) return { ok: false, reason: "event-hash-mismatch" };
460
+ const sourceHash = await wakeSourceHash(route.source);
461
+ return Object.freeze({
462
+ ok: true,
463
+ verified: Object.freeze({
464
+ audience: route.audience,
465
+ deploymentId: event.deploymentId,
466
+ event,
467
+ eventHash,
468
+ expiresAt: jwt.jwt.expiresAt,
469
+ fromPosition: event.fromPosition,
470
+ issuedAt: event.issuedAt,
471
+ jwtId: jwt.jwt.jwtId,
472
+ publisher: jwt.jwt.publisher,
473
+ ...event.queueId === void 0 ? {} : { queueId: event.queueId },
474
+ route: Object.freeze({
475
+ ...route,
476
+ source: Object.freeze(route.source),
477
+ sourceHash,
478
+ ...route.bestEffort === void 0 ? {} : { bestEffort: Object.freeze(route.bestEffort) }
479
+ }),
480
+ toPosition: event.toPosition,
481
+ tokenIssuedAt: jwt.jwt.issuedAt,
482
+ wakeId: event.wakeId
483
+ })
484
+ });
485
+ }
486
+ async function verifyJwtProfile(token, audience, nowSeconds) {
487
+ let verified;
488
+ try {
489
+ verified = await Account.verifyJwt(token, { audience, now: nowSeconds });
490
+ } catch (error) {
491
+ return reject2("jwt-malformed", [errorMessage2(error)]);
492
+ }
493
+ if (!verified.ok) {
494
+ return Object.hasOwn(verified.payload ?? {}, "nbf") ? reject2("jwt-nbf-not-allowed") : reject2(classifySdkJwtFailure(verified.reasons), verified.reasons);
495
+ }
496
+ const wireResult = parseStrictJwtWire(verified.header, verified.payload, audience);
497
+ if (!wireResult.ok) return wireResult;
498
+ const { header: strictHeader, payload: strictPayload } = wireResult;
499
+ if (strictHeader.kid !== strictPayload.iss) return reject2("jwt-identity-invalid");
500
+ if (strictPayload.exp <= nowSeconds) return reject2("jwt-expired");
501
+ if (strictPayload.exp - strictPayload.iat > EVENT_KIT_MAX_JWT_LIFETIME_SECONDS) return reject2("jwt-lifetime-invalid");
502
+ if (strictPayload.iat - nowSeconds > EVENT_KIT_MAX_FUTURE_IAT_SECONDS) return reject2("jwt-future-iat");
503
+ const jwt = Object.freeze({
504
+ eventHash: strictPayload.eventHash,
505
+ expiresAt: strictPayload.exp,
506
+ issuedAt: strictPayload.iat,
507
+ jwtId: strictPayload.jti,
508
+ publisher: strictPayload.iss
509
+ });
510
+ return Object.freeze({ jwt, ok: true });
511
+ }
512
+ function parseStrictJwtWire(header, payload, audience) {
513
+ const headerResult = WakeJwtHeaderZod.safeParse(header);
514
+ if (!headerResult.success) {
515
+ return NormalizedAddressZod2.safeParse(header.kid).success ? reject2("jwt-header-invalid", zodDetails(headerResult.error.issues)) : reject2("jwt-identity-invalid");
516
+ }
517
+ if (Object.hasOwn(payload, "nbf")) return reject2("jwt-nbf-not-allowed");
518
+ if (!NormalizedAddressZod2.safeParse(payload.iss).success) return reject2("jwt-identity-invalid");
519
+ if (payload.schema !== WakeJwtSchema) return reject2("jwt-schema-mismatch");
520
+ if (payload.aud !== audience) return reject2("jwt-audience-mismatch");
521
+ if (!isNonnegativeSafeInteger2(payload.iat) || !isNonnegativeSafeInteger2(payload.exp) || payload.exp <= payload.iat) {
522
+ return reject2("jwt-lifetime-invalid");
523
+ }
524
+ const payloadResult = WakeJwtPayloadZod.safeParse(payload);
525
+ if (!payloadResult.success) return reject2("jwt-claims-invalid", zodDetails(payloadResult.error.issues));
526
+ return Object.freeze({
527
+ header: headerResult.data,
528
+ ok: true,
529
+ payload: payloadResult.data
530
+ });
531
+ }
532
+ function classifySdkJwtFailure(reasons) {
533
+ if (reasons.some((reason) => reason.startsWith("audience mismatch:"))) return "jwt-audience-mismatch";
534
+ if (reasons.some((reason) => reason.startsWith("expired:"))) return "jwt-expired";
535
+ if (reasons.some((reason) => reason.startsWith("unsupported alg:") || reason.startsWith("unsupported typ:"))) {
536
+ return "jwt-header-invalid";
537
+ }
538
+ return reasons.some((reason) => reason.startsWith("invalid public key:") || reason.startsWith("public key does not derive to kid:") || reason === "header.kid does not match payload.iss") ? "jwt-identity-invalid" : "jwt-invalid";
539
+ }
540
+ function malformedWake(body, issues) {
541
+ const details = zodDetails(issues);
542
+ if (hasInvertedSafeRange(body)) return reject2("range-invalid", details);
543
+ return reject2(isRecord2(body) && Object.hasOwn(body, "wakeId") && issues.some((issue) => issue.path[0] === "wakeId") ? "wake-id-invalid" : "event-malformed", details);
544
+ }
545
+ function hasInvertedSafeRange(value) {
546
+ if (!isRecord2(value)) return false;
547
+ const { fromPosition, toPosition } = value;
548
+ return typeof fromPosition === "number" && typeof toPosition === "number" && isNonnegativeSafeInteger2(fromPosition) && isNonnegativeSafeInteger2(toPosition) && fromPosition > toPosition;
549
+ }
550
+ function isNonnegativeSafeInteger2(value) {
551
+ return Number.isSafeInteger(value) && value >= 0;
552
+ }
553
+ function isRecord2(value) {
554
+ return typeof value === "object" && value !== null && !Array.isArray(value);
555
+ }
556
+ function zodDetails(issues) {
557
+ return issues.map((issue) => {
558
+ const path = issue.path.map(String).join(".") || "<root>";
559
+ return `${path}: ${issue.message}`;
560
+ });
561
+ }
562
+ function reject2(reason, details) {
563
+ if (details === void 0) return Object.freeze({ ok: false, reason });
564
+ return Object.freeze({
565
+ details: Object.freeze([...details]),
566
+ ok: false,
567
+ reason
568
+ });
569
+ }
570
+ function errorMessage2(error) {
571
+ return error instanceof Error ? error.message : String(error);
572
+ }
573
+
574
+ // src/bestEffortAdmission.ts
575
+ function hintRetention(input) {
576
+ const { verified, nowSeconds } = input;
577
+ const policy = verified.route.bestEffort;
578
+ if (verified.route.deliveryMode !== "best-effort" || verified.event.deliveryMode !== "best-effort" || policy === void 0 || verified.fromPosition !== verified.toPosition || !Number.isSafeInteger(nowSeconds) || nowSeconds < 0 || !Number.isSafeInteger(input.expectedHead) || input.expectedHead < 0)
579
+ return { ok: false, reason: "configuration-invalid" };
580
+ if (verified.issuedAt > nowSeconds + EVENT_KIT_MAX_FUTURE_IAT_SECONDS) return { ok: false, reason: "hint-future-issued-at" };
581
+ const bodyDeadline = verified.issuedAt + policy.maxHintAgeSeconds;
582
+ const retainWakeUntil = bodyDeadline + EVENT_KIT_REPLAY_RETENTION_SKEW_SECONDS;
583
+ const retainJwtUntil = verified.expiresAt + EVENT_KIT_REPLAY_RETENTION_SKEW_SECONDS;
584
+ if (!Number.isSafeInteger(retainWakeUntil) || !Number.isSafeInteger(retainJwtUntil)) return { ok: false, reason: "configuration-invalid" };
585
+ return nowSeconds > bodyDeadline ? { ok: false, reason: "hint-expired" } : {
586
+ ok: true,
587
+ retainJwtUntil,
588
+ retainWakeUntil
589
+ };
590
+ }
591
+ async function admitBestEffortHint(input) {
592
+ const retention = hintRetention(input);
593
+ if (!retention.ok) return retention;
594
+ const { retainJwtUntil, retainWakeUntil } = retention;
595
+ const { verified, nowSeconds } = input;
596
+ const authorization = await authorizeVerifiedWake(input);
597
+ if (!authorization.ok) return authorization.failure;
598
+ const wake = AdmittedWakeZod.parse({
599
+ audience: verified.audience,
600
+ deploymentId: verified.deploymentId,
601
+ event: verified.event,
602
+ eventHash: verified.eventHash,
603
+ fromPosition: verified.fromPosition,
604
+ toPosition: verified.toPosition,
605
+ jti: verified.jwtId,
606
+ publisher: verified.publisher,
607
+ wakeId: verified.wakeId,
608
+ ...verified.queueId === void 0 ? {} : { queueId: verified.queueId }
609
+ });
610
+ const attempt = {
611
+ audience: verified.audience,
612
+ eventHash: verified.eventHash,
613
+ jwtId: verified.jwtId,
614
+ publisher: verified.publisher,
615
+ retainJwtUntil,
616
+ retainWakeUntil,
617
+ wakeId: verified.wakeId
618
+ };
619
+ try {
620
+ const committed = await input.inbox.commitAuthorized({
621
+ attempt,
622
+ grant: authorization.grant,
623
+ wake,
624
+ nowSeconds
625
+ });
626
+ if (committed.kind !== "accepted" && committed.kind !== "duplicate") return { ok: false, reason: committed.kind };
627
+ const acknowledgment = committed.acknowledgment;
628
+ return acknowledgmentMatches(acknowledgment, attempt, nowSeconds) ? {
629
+ ok: true,
630
+ disposition: committed.kind,
631
+ acknowledgment,
632
+ wake
633
+ } : { ok: false, reason: "inbox-unavailable" };
634
+ } catch {
635
+ return { ok: false, reason: "inbox-unavailable" };
636
+ }
637
+ }
638
+ function acknowledgmentMatches(value, attempt, now) {
639
+ return BestEffortAdmissionAcknowledgmentZod.safeParse(value).success && value.eventHash === attempt.eventHash && value.wakeId === attempt.wakeId && value.acceptedAt <= now && value.expiresAt === attempt.retainWakeUntil;
640
+ }
641
+ var BestEffortAdmissionAcknowledgmentZod = z3.strictObject({
642
+ admissionId: z3.string().min(1).max(4096),
643
+ eventHash: HashZod3,
644
+ wakeId: WakeIdZod2,
645
+ acceptedAt: NonnegativeJsonSafeIntegerZod3,
646
+ expiresAt: NonnegativeJsonSafeIntegerZod3
647
+ }).refine((value) => value.expiresAt > value.acceptedAt, "Acknowledgment expiry must follow admission");
648
+ var JwtSecurityRecordZod = z3.strictObject({
649
+ audience: WakeJwtAudienceZod3,
650
+ publisher: NormalizedAddressZod3,
651
+ jwtId: WakeJwtIdZod2,
652
+ wakeId: WakeIdZod2,
653
+ eventHash: HashZod3,
654
+ expiresAt: NonnegativeJsonSafeIntegerZod3
655
+ });
656
+ var WakeSecurityRecordZod = z3.strictObject({
657
+ audience: WakeJwtAudienceZod3,
658
+ publisher: NormalizedAddressZod3,
659
+ wakeId: WakeIdZod2,
660
+ eventHash: HashZod3,
661
+ acknowledgment: BestEffortAdmissionAcknowledgmentZod
662
+ }).refine(
663
+ (value) => value.wakeId === value.acknowledgment.wakeId && value.eventHash === value.acknowledgment.eventHash,
664
+ "Security identity must match acknowledgment"
665
+ );
666
+ var BestEffortSecurityStateZod = z3.strictObject({
667
+ highestNowSeconds: NonnegativeJsonSafeIntegerZod3,
668
+ jwtRecords: z3.array(JwtSecurityRecordZod).max(65536),
669
+ wakeRecords: z3.array(WakeSecurityRecordZod).max(65536)
670
+ }).superRefine((value, context) => {
671
+ const jwtKeys = value.jwtRecords.map((record) => JSON.stringify([record.audience, record.publisher, record.jwtId]));
672
+ if (value.wakeRecords.some((record) => record.acknowledgment.acceptedAt > value.highestNowSeconds))
673
+ context.addIssue({ code: "custom", message: "Acknowledgment cannot precede stored admission clock" });
674
+ const wakeKeys = value.wakeRecords.map((record) => JSON.stringify([record.audience, record.publisher, record.wakeId]));
675
+ const jwtIdentities = new Set(jwtKeys);
676
+ const wakeIdentities = new Set(wakeKeys);
677
+ if (jwtIdentities.size !== jwtKeys.length || wakeIdentities.size !== wakeKeys.length)
678
+ context.addIssue({ code: "custom", message: "Persisted security identities must be unique" });
679
+ });
680
+ function parseBestEffortSecurityState(value) {
681
+ return BestEffortSecurityStateZod.parse(value);
682
+ }
683
+ var BestEffortAdmissionAttemptZod = z3.strictObject({
684
+ audience: WakeJwtAudienceZod3,
685
+ eventHash: HashZod3,
686
+ jwtId: WakeJwtIdZod2,
687
+ publisher: NormalizedAddressZod3,
688
+ retainJwtUntil: NonnegativeJsonSafeIntegerZod3,
689
+ retainWakeUntil: NonnegativeJsonSafeIntegerZod3,
690
+ wakeId: WakeIdZod2
691
+ });
692
+ function createBestEffortSecurityState() {
693
+ return {
694
+ highestNowSeconds: 0,
695
+ jwtRecords: [],
696
+ wakeRecords: []
697
+ };
698
+ }
699
+ function securityConflict(attempt, priorJwt, priorWake, jwtCount, wakeCount, maxRecords) {
700
+ if (priorJwt !== void 0 && (priorJwt.eventHash !== attempt.eventHash || priorJwt.wakeId !== attempt.wakeId))
701
+ return "jwt-id-collision";
702
+ if (priorWake !== void 0 && priorWake.eventHash !== attempt.eventHash)
703
+ return "wake-id-collision";
704
+ if (priorJwt === void 0 && jwtCount >= maxRecords || priorWake === void 0 && wakeCount >= maxRecords)
705
+ return "capacity-exceeded";
706
+ }
707
+ function validateTransaction(input, maxRecords) {
708
+ const attempt = BestEffortAdmissionAttemptZod.parse(input.attempt);
709
+ const wake = AdmittedWakeZod.parse(input.wake);
710
+ const nowSeconds = NonnegativeJsonSafeIntegerZod3.parse(input.nowSeconds);
711
+ if (attempt.audience !== wake.audience || attempt.publisher !== wake.publisher || attempt.jwtId !== wake.jti || attempt.wakeId !== wake.wakeId || attempt.eventHash !== wake.eventHash || wake.event.deliveryMode !== "best-effort" || attempt.retainJwtUntil <= nowSeconds || attempt.retainWakeUntil <= nowSeconds)
712
+ throw new TypeError("Best Effort transaction attempt does not match admitted hint");
713
+ if (!Number.isSafeInteger(maxRecords) || maxRecords < 1 || maxRecords > 65536) throw new TypeError("Invalid finite security capacity");
714
+ return { attempt, nowSeconds };
715
+ }
716
+ function commitBestEffortSecurity(state, input, maxRecords) {
717
+ state = parseBestEffortSecurityState(state);
718
+ const { attempt, nowSeconds } = validateTransaction(input, maxRecords);
719
+ if (nowSeconds < state.highestNowSeconds) return { state, result: { kind: "clock-regressed" } };
720
+ const jwtRecords = state.jwtRecords.filter((record) => record.expiresAt > nowSeconds).map((record) => ({ ...record }));
721
+ const wakeRecords = state.wakeRecords.filter((record) => record.acknowledgment.expiresAt > nowSeconds);
722
+ const priorJwt = jwtRecords.find((record) => record.audience === attempt.audience && record.publisher === attempt.publisher && record.jwtId === attempt.jwtId);
723
+ const priorWake = wakeRecords.find((record) => record.audience === attempt.audience && record.publisher === attempt.publisher && record.wakeId === attempt.wakeId);
724
+ const conflict = securityConflict(attempt, priorJwt, priorWake, jwtRecords.length, wakeRecords.length, maxRecords);
725
+ if (conflict !== void 0) return { state, result: { kind: conflict } };
726
+ const acknowledgment = priorWake?.acknowledgment ?? {
727
+ admissionId: JSON.stringify([attempt.audience, attempt.publisher, attempt.wakeId, attempt.eventHash]),
728
+ eventHash: attempt.eventHash,
729
+ wakeId: attempt.wakeId,
730
+ acceptedAt: nowSeconds,
731
+ expiresAt: attempt.retainWakeUntil
732
+ };
733
+ if (priorJwt !== void 0) priorJwt.expiresAt = Math.max(priorJwt.expiresAt, attempt.retainJwtUntil);
734
+ if (priorJwt === void 0) jwtRecords.push({
735
+ audience: attempt.audience,
736
+ publisher: attempt.publisher,
737
+ jwtId: attempt.jwtId,
738
+ wakeId: attempt.wakeId,
739
+ eventHash: attempt.eventHash,
740
+ expiresAt: attempt.retainJwtUntil
741
+ });
742
+ if (priorWake === void 0) wakeRecords.push({
743
+ audience: attempt.audience,
744
+ publisher: attempt.publisher,
745
+ wakeId: attempt.wakeId,
746
+ eventHash: attempt.eventHash,
747
+ acknowledgment
748
+ });
749
+ return {
750
+ state: {
751
+ highestNowSeconds: nowSeconds,
752
+ jwtRecords,
753
+ wakeRecords
754
+ },
755
+ result: { kind: priorWake === void 0 ? "accepted" : "duplicate", acknowledgment }
756
+ };
757
+ }
758
+
333
759
  // src/endpointChallenge.ts
334
760
  import {
335
761
  Base64Url32BytesZod,
@@ -343,7 +769,7 @@ import {
343
769
  generateHandshakeId,
344
770
  MAX_ENDPOINT_CHALLENGE_LIFETIME_SECONDS
345
771
  } from "@xyo-network/event-kit-schemas";
346
- import { PayloadBuilder as PayloadBuilder2 } from "@xyo-network/sdk";
772
+ import { PayloadBuilder as PayloadBuilder4 } from "@xyo-network/sdk";
347
773
  function hashBytes(hash) {
348
774
  return Uint8Array.from({ length: 32 }, (_, index) => Number.parseInt(hash.slice(index * 2, index * 2 + 2), 16));
349
775
  }
@@ -370,7 +796,7 @@ function equalProof(left, right) {
370
796
  }
371
797
  async function endpointChallengeHash(challenge) {
372
798
  const parsed = EndpointChallengeZod.parse(challenge);
373
- return await PayloadBuilder2.dataHash(parsed);
799
+ return await PayloadBuilder4.dataHash(parsed);
374
800
  }
375
801
  function createEndpointChallenge(input) {
376
802
  const lifetimeSeconds = input.lifetimeSeconds ?? MAX_ENDPOINT_CHALLENGE_LIFETIME_SECONDS;
@@ -383,7 +809,7 @@ function createEndpointChallenge(input) {
383
809
  if (input.issuedAt > Number.MAX_SAFE_INTEGER - lifetimeSeconds) {
384
810
  throw new RangeError("Challenge expiry exceeds the JSON safe-integer range");
385
811
  }
386
- const builder = new PayloadBuilder2({ schema: EndpointChallengeSchema });
812
+ const builder = new PayloadBuilder4({ schema: EndpointChallengeSchema });
387
813
  return EndpointChallengeZod.parse(builder.fields({
388
814
  challengeId: generateHandshakeId(),
389
815
  chainId: input.chainId,
@@ -402,7 +828,7 @@ function createEndpointChallenge(input) {
402
828
  }
403
829
  async function createEndpointChallengeResponse(challenge, secret) {
404
830
  const challengeHash = await endpointChallengeHash(challenge);
405
- const builder = new PayloadBuilder2({ schema: EndpointChallengeResponseSchema });
831
+ const builder = new PayloadBuilder4({ schema: EndpointChallengeResponseSchema });
406
832
  return EndpointChallengeResponseZod.parse(builder.fields({
407
833
  challenge: challengeHash,
408
834
  proof: await hmacProof(secret, challengeHash)
@@ -437,7 +863,7 @@ import {
437
863
  EndpointChallengeZod as EndpointChallengeZod2,
438
864
  generateGrantId,
439
865
  generateHandshakeId as generateHandshakeId2,
440
- HashZod as HashZod3,
866
+ HashZod as HashZod5,
441
867
  MAX_SUBSCRIPTION_PLAINTEXT_BYTES as MAX_SUBSCRIPTION_PLAINTEXT_BYTES2,
442
868
  PublisherEncryptionKeyRevocationSchema,
443
869
  PublisherEncryptionKeyRevocationZod,
@@ -448,7 +874,7 @@ import {
448
874
  PublisherServiceSchema,
449
875
  PublisherServiceZod,
450
876
  SOURCE_WAKE_EVENT_KIND,
451
- SOURCE_WAKE_FILTER_PROFILE,
877
+ SOURCE_WAKE_FILTER_PROFILE as SOURCE_WAKE_FILTER_PROFILE2,
452
878
  WAKE_SUBSCRIPTION_HPKE_PROFILE as WAKE_SUBSCRIPTION_HPKE_PROFILE3,
453
879
  WAKE_WALLET_JWT_DELIVERY_PROFILE,
454
880
  WakeGrantObjectSchema,
@@ -465,7 +891,7 @@ import {
465
891
  WakeSubscriptionRevocationSchema,
466
892
  WakeSubscriptionRevocationZod
467
893
  } from "@xyo-network/event-kit-schemas";
468
- import { PayloadBuilder as PayloadBuilder3 } from "@xyo-network/sdk";
894
+ import { PayloadBuilder as PayloadBuilder5 } from "@xyo-network/sdk";
469
895
 
470
896
  // src/wakeSubscriptionHpke.ts
471
897
  import {
@@ -480,8 +906,8 @@ import {
480
906
  canonicalBase64UrlToBytes as canonicalBase64UrlToBytes2,
481
907
  EncryptedWakeSubscriptionZod as EncryptedWakeSubscriptionZod2,
482
908
  HandshakeIdZod,
483
- HashZod as HashZod2,
484
- NormalizedAddressZod as NormalizedAddressZod3,
909
+ HashZod as HashZod4,
910
+ NormalizedAddressZod as NormalizedAddressZod5,
485
911
  WAKE_SUBSCRIPTION_HPKE_PROFILE as WAKE_SUBSCRIPTION_HPKE_PROFILE2,
486
912
  WakeSubscriptionCleartextZod as WakeSubscriptionCleartextZod2
487
913
  } from "@xyo-network/event-kit-schemas";
@@ -490,7 +916,7 @@ import {
490
916
  import {
491
917
  EncryptedWakeSubscriptionZod,
492
918
  MAX_SUBSCRIPTION_PLAINTEXT_BYTES,
493
- NormalizedAddressZod as NormalizedAddressZod2,
919
+ NormalizedAddressZod as NormalizedAddressZod4,
494
920
  WAKE_SUBSCRIPTION_HPKE_PROFILE,
495
921
  WakeSubscriptionCleartextZod,
496
922
  WakeSubscriptionPlaintextZod
@@ -507,7 +933,7 @@ function cleartextFromEncrypted(subscription) {
507
933
  return cleartext;
508
934
  }
509
935
  function wakeSubscriptionAadBytes(subscription, subscriber) {
510
- const parsedSubscriber = NormalizedAddressZod2.parse(subscriber);
936
+ const parsedSubscriber = NormalizedAddressZod4.parse(subscriber);
511
937
  const parsedSubscription = "encryption" in subscription ? cleartextFromEncrypted(EncryptedWakeSubscriptionZod.parse(subscription)) : WakeSubscriptionCleartextZod.parse(subscription);
512
938
  return canonicalJsonBytes({
513
939
  schema: parsedSubscription.schema,
@@ -522,6 +948,7 @@ function wakeSubscriptionAadBytes(subscription, subscriber) {
522
948
  revision: parsedSubscription.revision,
523
949
  supersedes: parsedSubscription.supersedes ?? null,
524
950
  eventKind: parsedSubscription.eventKind,
951
+ deliveryMode: parsedSubscription.deliveryMode,
525
952
  expiresAtBlock: parsedSubscription.expiresAtBlock,
526
953
  profile: WAKE_SUBSCRIPTION_HPKE_PROFILE
527
954
  });
@@ -571,14 +998,14 @@ var WakeSubscriptionDecryptionError = class extends Error {
571
998
  };
572
999
  function parsePublicKey(value) {
573
1000
  return {
574
- encryptionKey: HashZod2.parse(value.encryptionKey),
1001
+ encryptionKey: HashZod4.parse(value.encryptionKey),
575
1002
  keyId: HandshakeIdZod.parse(value.keyId),
576
1003
  publicKey: Base64Url32BytesZod2.parse(value.publicKey)
577
1004
  };
578
1005
  }
579
1006
  function parsePrivateKey(value) {
580
1007
  return {
581
- encryptionKey: HashZod2.parse(value.encryptionKey),
1008
+ encryptionKey: HashZod4.parse(value.encryptionKey),
582
1009
  keyId: HandshakeIdZod.parse(value.keyId),
583
1010
  privateKey: Base64Url32BytesZod2.parse(value.privateKey)
584
1011
  };
@@ -602,7 +1029,7 @@ async function generateWakeSubscriptionEncryptionKeyPair() {
602
1029
  }
603
1030
  async function encryptWakeSubscription(input) {
604
1031
  const subscription = WakeSubscriptionCleartextZod2.parse(input.subscription);
605
- const subscriber = NormalizedAddressZod3.parse(input.subscriber);
1032
+ const subscriber = NormalizedAddressZod5.parse(input.subscriber);
606
1033
  const publisherKey = parsePublicKey(input.publisherKey);
607
1034
  if (subscription.encryptionKey !== publisherKey.encryptionKey || subscription.keyId !== publisherKey.keyId) {
608
1035
  throw new TypeError("Subscription key reference does not match the supplied publisher key");
@@ -625,7 +1052,7 @@ async function encryptWakeSubscription(input) {
625
1052
  async function decryptWakeSubscription(input) {
626
1053
  try {
627
1054
  const subscription = EncryptedWakeSubscriptionZod2.parse(input.subscription);
628
- const subscriber = NormalizedAddressZod3.parse(input.subscriber);
1055
+ const subscriber = NormalizedAddressZod5.parse(input.subscriber);
629
1056
  const publisherKey = parsePrivateKey(input.publisherKey);
630
1057
  if (subscription.encryptionKey !== publisherKey.encryptionKey || subscription.keyId !== publisherKey.keyId) throw new TypeError("Key reference mismatch");
631
1058
  const recipientKey = await suite.kem.deserializePrivateKey(decoded32(publisherKey.privateKey));
@@ -668,13 +1095,13 @@ function parseHandshakePublicObject(value) {
668
1095
  throw new TypeError("Expected a strict Event Kit handshake public object");
669
1096
  }
670
1097
  async function handshakeObjectHash(object) {
671
- return HashZod3.parse(await PayloadBuilder3.dataHash(parseHandshakePublicObject(object)));
1098
+ return HashZod5.parse(await PayloadBuilder5.dataHash(parseHandshakePublicObject(object)));
672
1099
  }
673
1100
  async function withHandshakeObjectHash(body) {
674
1101
  return Object.freeze({ body, objectHash: await handshakeObjectHash(body) });
675
1102
  }
676
1103
  function createPublisherEncryptionKey(input) {
677
- const builder = new PayloadBuilder3({ schema: PublisherEncryptionKeySchema });
1104
+ const builder = new PayloadBuilder5({ schema: PublisherEncryptionKeySchema });
678
1105
  return PublisherEncryptionKeyZod.parse(builder.fields({
679
1106
  keyId: input.keyId ?? generateHandshakeId2(),
680
1107
  profile: WAKE_SUBSCRIPTION_HPKE_PROFILE3,
@@ -706,7 +1133,7 @@ async function generatePublisherEncryptionKey(input) {
706
1133
  });
707
1134
  }
708
1135
  function createPublisherService(input) {
709
- const builder = new PayloadBuilder3({ schema: PublisherServiceSchema });
1136
+ const builder = new PayloadBuilder5({ schema: PublisherServiceSchema });
710
1137
  return PublisherServiceZod.parse(builder.fields({
711
1138
  authorizationChainId: input.authorizationChainId,
712
1139
  serviceId: input.serviceId ?? generateHandshakeId2(),
@@ -716,9 +1143,11 @@ function createPublisherService(input) {
716
1143
  ...input.expiresAtBlock === void 0 ? {} : { expiresAtBlock: input.expiresAtBlock },
717
1144
  events: input.sources.map((source) => ({
718
1145
  kind: SOURCE_WAKE_EVENT_KIND,
719
- filterProfile: SOURCE_WAKE_FILTER_PROFILE,
1146
+ filterProfile: SOURCE_WAKE_FILTER_PROFILE2,
720
1147
  source
721
1148
  })),
1149
+ deliveryMode: input.deliveryMode,
1150
+ ...input.bestEffort === void 0 ? {} : { bestEffort: input.bestEffort },
722
1151
  deliveryProfile: WAKE_WALLET_JWT_DELIVERY_PROFILE,
723
1152
  challengeProfile: ENDPOINT_CHALLENGE_HMAC_SHA256_PROFILE,
724
1153
  encryptionProfile: WAKE_SUBSCRIPTION_HPKE_PROFILE3,
@@ -733,7 +1162,7 @@ function createPublisherService(input) {
733
1162
  }).build());
734
1163
  }
735
1164
  function createWakeGrant(input) {
736
- const builder = new PayloadBuilder3({ schema: WakeGrantObjectSchema });
1165
+ const builder = new PayloadBuilder5({ schema: WakeGrantObjectSchema });
737
1166
  return WakeGrantObjectZod.parse(builder.fields({
738
1167
  grantId: input.grantId ?? generateGrantId(),
739
1168
  deploymentId: input.deploymentId,
@@ -748,14 +1177,16 @@ function createWakeSubscriptionPlaintext(input) {
748
1177
  globalThis.crypto.getRandomValues(secretBytes);
749
1178
  }
750
1179
  const challengeSecret = input.challengeSecret ?? bytesToCanonicalBase64Url3(secretBytes);
751
- const builder = new PayloadBuilder3({ schema: WakeSubscriptionPlaintextSchema });
1180
+ const builder = new PayloadBuilder5({ schema: WakeSubscriptionPlaintextSchema });
752
1181
  return WakeSubscriptionPlaintextZod2.parse(builder.fields({
753
1182
  endpoint: input.endpoint,
754
1183
  audience: input.audience,
755
1184
  deploymentId: input.deploymentId,
756
1185
  queueId: input.queueId ?? null,
757
1186
  filter: {
758
- schema: SOURCE_WAKE_FILTER_PROFILE,
1187
+ schema: SOURCE_WAKE_FILTER_PROFILE2,
1188
+ deliveryMode: input.deliveryMode,
1189
+ ...input.bestEffort === void 0 ? {} : { bestEffort: input.bestEffort },
759
1190
  source: input.source,
760
1191
  fromPosition: input.fromPosition,
761
1192
  maxPositionsPerWake: input.maxPositionsPerWake
@@ -767,7 +1198,7 @@ function createWakeSubscriptionPlaintext(input) {
767
1198
  }).build());
768
1199
  }
769
1200
  async function createEncryptedWakeSubscription(input) {
770
- const builder = new PayloadBuilder3({ schema: EncryptedWakeSubscriptionSchema });
1201
+ const builder = new PayloadBuilder5({ schema: EncryptedWakeSubscriptionSchema });
771
1202
  const cleartext = WakeSubscriptionCleartextZod3.parse(builder.fields({
772
1203
  subscriptionId: input.subscriptionId ?? generateHandshakeId2(),
773
1204
  revision: input.revision ?? 1,
@@ -779,6 +1210,7 @@ async function createEncryptedWakeSubscription(input) {
779
1210
  keyId: input.publisherKey.keyId,
780
1211
  grant: input.grant,
781
1212
  eventKind: SOURCE_WAKE_EVENT_KIND,
1213
+ deliveryMode: input.plaintext.filter.deliveryMode,
782
1214
  expiresAtBlock: input.expiresAtBlock
783
1215
  }).build());
784
1216
  return await encryptWakeSubscription({
@@ -789,7 +1221,7 @@ async function createEncryptedWakeSubscription(input) {
789
1221
  });
790
1222
  }
791
1223
  function createWakeSubscriptionAcceptance(input) {
792
- const builder = new PayloadBuilder3({ schema: WakeSubscriptionAcceptanceSchema });
1224
+ const builder = new PayloadBuilder5({ schema: WakeSubscriptionAcceptanceSchema });
793
1225
  return WakeSubscriptionAcceptanceZod.parse(builder.fields({
794
1226
  challenge: input.challenge,
795
1227
  encryptionKey: input.encryptionKey,
@@ -800,45 +1232,45 @@ function createWakeSubscriptionAcceptance(input) {
800
1232
  }).build());
801
1233
  }
802
1234
  function createPublisherServiceRevocation(service) {
803
- const builder = new PayloadBuilder3({ schema: PublisherServiceRevocationSchema });
1235
+ const builder = new PayloadBuilder5({ schema: PublisherServiceRevocationSchema });
804
1236
  return PublisherServiceRevocationZod.parse(builder.fields({ service }).build());
805
1237
  }
806
1238
  function createPublisherEncryptionKeyRevocation(key) {
807
- const builder = new PayloadBuilder3({ schema: PublisherEncryptionKeyRevocationSchema });
1239
+ const builder = new PayloadBuilder5({ schema: PublisherEncryptionKeyRevocationSchema });
808
1240
  return PublisherEncryptionKeyRevocationZod.parse(builder.fields({ key }).build());
809
1241
  }
810
1242
  function createWakeGrantRevocation(grant) {
811
- const builder = new PayloadBuilder3({ schema: WakeGrantRevocationSchema });
1243
+ const builder = new PayloadBuilder5({ schema: WakeGrantRevocationSchema });
812
1244
  return WakeGrantRevocationZod.parse(builder.fields({ grant }).build());
813
1245
  }
814
1246
  function createWakeSubscriptionRevocation(subscription) {
815
- const builder = new PayloadBuilder3({ schema: WakeSubscriptionRevocationSchema });
1247
+ const builder = new PayloadBuilder5({ schema: WakeSubscriptionRevocationSchema });
816
1248
  return WakeSubscriptionRevocationZod.parse(builder.fields({ subscription }).build());
817
1249
  }
818
1250
  function createWakeSubscriptionAcceptanceRevocation(acceptance) {
819
- const builder = new PayloadBuilder3({ schema: WakeSubscriptionAcceptanceRevocationSchema });
1251
+ const builder = new PayloadBuilder5({ schema: WakeSubscriptionAcceptanceRevocationSchema });
820
1252
  return WakeSubscriptionAcceptanceRevocationZod.parse(builder.fields({ acceptance }).build());
821
1253
  }
822
1254
 
823
1255
  // src/lifecycleSelection.ts
824
- function isNonnegativeSafeInteger2(value) {
1256
+ function isNonnegativeSafeInteger3(value) {
825
1257
  return Number.isSafeInteger(value) && value >= 0;
826
1258
  }
827
1259
  function isVisible(claimedAtBlock, finalizedHead) {
828
- return isNonnegativeSafeInteger2(claimedAtBlock) && claimedAtBlock <= finalizedHead;
1260
+ return isNonnegativeSafeInteger3(claimedAtBlock) && claimedAtBlock <= finalizedHead;
829
1261
  }
830
1262
  function isRevoked(revokedAtBlock, finalizedHead) {
831
1263
  return revokedAtBlock !== void 0 && revokedAtBlock <= finalizedHead;
832
1264
  }
833
1265
  function isUnrevokedEvidence(claimedAtBlock, revokedAtBlock, finalizedHead) {
834
- return isVisible(claimedAtBlock, finalizedHead) && (revokedAtBlock === void 0 || isNonnegativeSafeInteger2(revokedAtBlock) && revokedAtBlock >= claimedAtBlock) && !isRevoked(revokedAtBlock, finalizedHead);
1266
+ return isVisible(claimedAtBlock, finalizedHead) && (revokedAtBlock === void 0 || isNonnegativeSafeInteger3(revokedAtBlock) && revokedAtBlock >= claimedAtBlock) && !isRevoked(revokedAtBlock, finalizedHead);
835
1267
  }
836
1268
  function isInsideExclusiveWindow(finalizedHead, validFromBlock, expiresAtBlock) {
837
- return isNonnegativeSafeInteger2(validFromBlock) && finalizedHead >= validFromBlock && (expiresAtBlock === void 0 || isNonnegativeSafeInteger2(expiresAtBlock) && expiresAtBlock > validFromBlock && finalizedHead < expiresAtBlock);
1269
+ return isNonnegativeSafeInteger3(validFromBlock) && finalizedHead >= validFromBlock && (expiresAtBlock === void 0 || isNonnegativeSafeInteger3(expiresAtBlock) && expiresAtBlock > validFromBlock && finalizedHead < expiresAtBlock);
838
1270
  }
839
1271
  function isRevisionShapeValid(revision) {
840
- if (!Number.isSafeInteger(revision.revision) || revision.revision < 1 || !isNonnegativeSafeInteger2(revision.claimedAtBlock)) return false;
841
- if (revision.revokedAtBlock !== void 0 && (!isNonnegativeSafeInteger2(revision.revokedAtBlock) || revision.revokedAtBlock < revision.claimedAtBlock)) return false;
1272
+ if (!Number.isSafeInteger(revision.revision) || revision.revision < 1 || !isNonnegativeSafeInteger3(revision.claimedAtBlock)) return false;
1273
+ if (revision.revokedAtBlock !== void 0 && (!isNonnegativeSafeInteger3(revision.revokedAtBlock) || revision.revokedAtBlock < revision.claimedAtBlock)) return false;
842
1274
  return revision.revision === 1 ? revision.supersedes === void 0 : revision.supersedes !== void 0;
843
1275
  }
844
1276
  function resolveLinearChain(revisions, finalizedHead) {
@@ -933,7 +1365,7 @@ function acceptedSubscription(subscription, input) {
933
1365
  const acceptances = acceptanceFor(subscription, input);
934
1366
  if (acceptances.length > 1) return "ambiguous";
935
1367
  const acceptance = acceptances[0];
936
- if (acceptance === void 0 || !isNonnegativeSafeInteger2(acceptance.acceptanceBlock)) return void 0;
1368
+ if (acceptance === void 0 || !isNonnegativeSafeInteger3(acceptance.acceptanceBlock)) return void 0;
937
1369
  if (!Number.isSafeInteger(subscription.expiresAtBlock) || subscription.expiresAtBlock < 1) return void 0;
938
1370
  if (acceptance.acceptanceBlock <= subscription.claimedAtBlock || acceptance.acceptanceBlock >= Number.MAX_SAFE_INTEGER) return void 0;
939
1371
  const activeFromBlock = acceptance.acceptanceBlock + 1;
@@ -1161,11 +1593,24 @@ function wakeKey(attempt) {
1161
1593
  return JSON.stringify([attempt.audience, attempt.publisher, attempt.wakeId]);
1162
1594
  }
1163
1595
 
1596
+ // src/staleWakeLeaseError.ts
1597
+ var StaleWakeLeaseError = class extends Error {
1598
+ code = "stale-wake-lease";
1599
+ name = "StaleWakeLeaseError";
1600
+ constructor(message = "Wake lease is no longer current") {
1601
+ super(message);
1602
+ }
1603
+ };
1604
+
1164
1605
  // src/memoryWakeQueue.ts
1165
1606
  function createMemoryWakeQueue() {
1166
1607
  const pending = [];
1167
1608
  const inflight = /* @__PURE__ */ new Map();
1168
1609
  let sequence = 0;
1610
+ function restore(entry) {
1611
+ pending.push(entry);
1612
+ pending.sort((left, right) => left.sequence - right.sequence);
1613
+ }
1169
1614
  return {
1170
1615
  async append(wake) {
1171
1616
  pending.push({ sequence: sequence += 1, wake });
@@ -1173,26 +1618,35 @@ function createMemoryWakeQueue() {
1173
1618
  async claim() {
1174
1619
  const entry = pending.shift();
1175
1620
  if (entry === void 0) return void 0;
1176
- inflight.set(entry.sequence, entry);
1621
+ const token = /* @__PURE__ */ Symbol("wake-lease");
1622
+ let completed2;
1623
+ inflight.set(entry.sequence, { entry, token });
1624
+ const finish = (operation) => {
1625
+ if (completed2 === operation) return;
1626
+ if (completed2 !== void 0 || inflight.get(entry.sequence)?.token !== token) {
1627
+ throw new StaleWakeLeaseError();
1628
+ }
1629
+ inflight.delete(entry.sequence);
1630
+ if (operation === "release") restore(entry);
1631
+ completed2 = operation;
1632
+ };
1177
1633
  return {
1178
1634
  wake: entry.wake,
1179
1635
  async ack() {
1180
- inflight.delete(entry.sequence);
1636
+ finish("ack");
1181
1637
  },
1182
1638
  async release() {
1183
- if (!inflight.delete(entry.sequence)) return;
1184
- pending.push(entry);
1185
- pending.sort((left, right) => left.sequence - right.sequence);
1639
+ finish("release");
1186
1640
  }
1187
1641
  };
1188
1642
  },
1189
1643
  inflightCount: async () => inflight.size,
1190
1644
  pendingCount: async () => pending.length,
1191
- async recoverInflight() {
1645
+ async recoverInflight(options) {
1646
+ if (options?.consumersStopped !== true) throw new TypeError("Recovery requires all consumers to be stopped");
1192
1647
  const recovered = inflight.size;
1193
- for (const entry of inflight.values()) pending.push(entry);
1648
+ for (const { entry } of inflight.values()) restore(entry);
1194
1649
  inflight.clear();
1195
- pending.sort((left, right) => left.sequence - right.sequence);
1196
1650
  return recovered;
1197
1651
  }
1198
1652
  };
@@ -1200,43 +1654,20 @@ function createMemoryWakeQueue() {
1200
1654
 
1201
1655
  // src/publisherSubscription.ts
1202
1656
  import {
1203
- ChainIdZod,
1657
+ ChainIdZod as ChainIdZod2,
1204
1658
  EncryptedWakeSubscriptionZod as EncryptedWakeSubscriptionZod4,
1205
1659
  EndpointChallengeZod as EndpointChallengeZod3,
1206
- HashZod as HashZod4,
1660
+ HashZod as HashZod6,
1207
1661
  hasMatchingHandshakeProfile,
1208
- NormalizedAddressZod as NormalizedAddressZod4,
1662
+ NormalizedAddressZod as NormalizedAddressZod6,
1209
1663
  PublisherEncryptionKeyZod as PublisherEncryptionKeyZod2,
1210
1664
  PublisherServiceZod as PublisherServiceZod2,
1211
1665
  WakeGrantObjectZod as WakeGrantObjectZod2
1212
1666
  } from "@xyo-network/event-kit-schemas";
1213
-
1214
- // src/sourceWakeIdentity.ts
1215
- import {
1216
- SourceWakeSubscriptionFilterZod,
1217
- WakeSourceSchema,
1218
- WakeSourceZod
1219
- } from "@xyo-network/event-kit-schemas";
1220
- import {
1221
- asSchema,
1222
- PayloadBuilder as PayloadBuilder4
1223
- } from "@xyo-network/sdk";
1224
- async function wakeSourceHash(source) {
1225
- return await PayloadBuilder4.dataHash({ schema: WakeSourceSchema, ...WakeSourceZod.parse(source) });
1226
- }
1227
- function wakeSourcesEqual(left, right) {
1228
- return canonicalJson(WakeSourceZod.parse(left)) === canonicalJson(WakeSourceZod.parse(right));
1229
- }
1230
- async function sourceWakeFilterHash(filter) {
1231
- const parsed = SourceWakeSubscriptionFilterZod.parse(filter);
1232
- return await PayloadBuilder4.dataHash({ ...parsed, schema: asSchema(parsed.schema, true) });
1233
- }
1234
-
1235
- // src/publisherSubscription.ts
1236
1667
  function rejectPublisherSubscription(reason) {
1237
1668
  return Object.freeze({ ok: false, reason });
1238
1669
  }
1239
- function isNonnegativeSafeInteger3(value) {
1670
+ function isNonnegativeSafeInteger4(value) {
1240
1671
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
1241
1672
  }
1242
1673
  function isActiveAt(value, block) {
@@ -1253,7 +1684,7 @@ function hasExactQueueScope(grant, plaintext) {
1253
1684
  return plaintext.deploymentId === grant.deploymentId && plaintext.queueId === grantQueue;
1254
1685
  }
1255
1686
  function hasExactSubscriptionBinding(candidate, subscription) {
1256
- return subscription.chainId === candidate.chainId && subscription.publisher === candidate.publisher && subscription.service === candidate.service.objectHash && subscription.encryptionKey === candidate.key.objectHash && subscription.keyId === candidate.key.body.keyId && subscription.grant === candidate.grant.objectHash;
1687
+ return subscription.deliveryMode === candidate.service.body.deliveryMode && subscription.chainId === candidate.chainId && subscription.publisher === candidate.publisher && subscription.service === candidate.service.objectHash && subscription.encryptionKey === candidate.key.objectHash && subscription.keyId === candidate.key.body.keyId && subscription.grant === candidate.grant.objectHash;
1257
1688
  }
1258
1689
  function hasExactKeyBinding(service, key, subscription) {
1259
1690
  return service.encryptionKeys.includes(key.objectHash) && service.preferredEncryptionKey === key.objectHash && key.body.keyId === subscription.keyId && key.objectHash === subscription.encryptionKey;
@@ -1264,22 +1695,22 @@ function expiryWithinServicePolicy(subscription, service, tupleBlock) {
1264
1695
  function parsePublisherSubscriptionCandidate(candidate) {
1265
1696
  try {
1266
1697
  return {
1267
- chainId: ChainIdZod.parse(candidate.chainId),
1698
+ chainId: ChainIdZod2.parse(candidate.chainId),
1268
1699
  grant: WakeGrantObjectZod2.parse(candidate.grant.body),
1269
1700
  key: PublisherEncryptionKeyZod2.parse(candidate.key.body),
1270
- publisher: NormalizedAddressZod4.parse(candidate.publisher),
1701
+ publisher: NormalizedAddressZod6.parse(candidate.publisher),
1271
1702
  service: PublisherServiceZod2.parse(candidate.service.body),
1272
- subscriber: NormalizedAddressZod4.parse(candidate.subscriber),
1703
+ subscriber: NormalizedAddressZod6.parse(candidate.subscriber),
1273
1704
  subscription: EncryptedWakeSubscriptionZod4.parse(candidate.subscription.body),
1274
- tupleObjectHashes: candidate.tupleObjectHashes.map((hash) => HashZod4.parse(hash)),
1275
- tupleSubject: NormalizedAddressZod4.parse(candidate.tupleSubject)
1705
+ tupleObjectHashes: candidate.tupleObjectHashes.map((hash) => HashZod6.parse(hash)),
1706
+ tupleSubject: NormalizedAddressZod6.parse(candidate.tupleSubject)
1276
1707
  };
1277
1708
  } catch {
1278
1709
  return void 0;
1279
1710
  }
1280
1711
  }
1281
1712
  function tupleBindingIsValid(candidate, parsed) {
1282
- return isNonnegativeSafeInteger3(candidate.tupleBlock) && isNonnegativeSafeInteger3(candidate.finalizedHead) && candidate.finalizedHead >= candidate.tupleBlock && parsed.tupleSubject === parsed.publisher && occursExactlyOnce(parsed.tupleObjectHashes, candidate.service.objectHash) && occursExactlyOnce(parsed.tupleObjectHashes, candidate.grant.objectHash) && occursExactlyOnce(parsed.tupleObjectHashes, candidate.subscription.objectHash);
1713
+ return isNonnegativeSafeInteger4(candidate.tupleBlock) && isNonnegativeSafeInteger4(candidate.finalizedHead) && candidate.finalizedHead >= candidate.tupleBlock && parsed.tupleSubject === parsed.publisher && occursExactlyOnce(parsed.tupleObjectHashes, candidate.service.objectHash) && occursExactlyOnce(parsed.tupleObjectHashes, candidate.grant.objectHash) && occursExactlyOnce(parsed.tupleObjectHashes, candidate.subscription.objectHash);
1283
1714
  }
1284
1715
  async function bodyHashesAreValid(candidate, parsed) {
1285
1716
  const canonicalHashes = await Promise.all([
@@ -1397,10 +1828,10 @@ async function issuePublisherEndpointChallenge(input) {
1397
1828
  }
1398
1829
  }
1399
1830
  async function beginPublisherEndpointChallengeAttempt(input) {
1400
- if (!isNonnegativeSafeInteger3(input.nowSeconds)) return { kind: "store-invalid" };
1831
+ if (!isNonnegativeSafeInteger4(input.nowSeconds)) return { kind: "store-invalid" };
1401
1832
  try {
1402
1833
  const result = await input.store.beginAttempt({
1403
- challengeHash: HashZod4.parse(input.challengeHash),
1834
+ challengeHash: HashZod6.parse(input.challengeHash),
1404
1835
  nowSeconds: input.nowSeconds
1405
1836
  });
1406
1837
  if (!["already-attempted", "expired", "missing", "started", "verified"].includes(result.kind)) {
@@ -1409,7 +1840,7 @@ async function beginPublisherEndpointChallengeAttempt(input) {
1409
1840
  if (result.kind !== "missing" && !await challengeRecordMatches(
1410
1841
  result.record,
1411
1842
  result.record.challenge,
1412
- HashZod4.parse(input.challengeHash)
1843
+ HashZod6.parse(input.challengeHash)
1413
1844
  )) return { kind: "store-invalid" };
1414
1845
  if ((result.kind === "started" || result.kind === "already-attempted") && result.record.state !== "attempted") return { kind: "store-invalid" };
1415
1846
  return result.kind === "verified" && result.record.state !== "verified" ? { kind: "store-invalid" } : result;
@@ -1435,7 +1866,7 @@ async function completePublisherEndpointChallenge(input) {
1435
1866
  if (!endpointChallengeMatchesPublisherContext(input.challenge, input.subscription.context)) {
1436
1867
  return completeFailure("challenge-context-invalid");
1437
1868
  }
1438
- if (!isNonnegativeSafeInteger3(input.nowSeconds)) return completeFailure("challenge-expired");
1869
+ if (!isNonnegativeSafeInteger4(input.nowSeconds)) return completeFailure("challenge-expired");
1439
1870
  const insideDecisionWindow = input.challenge.issuedAt <= input.nowSeconds && input.nowSeconds < input.challenge.expiresAt;
1440
1871
  const proofInput = {
1441
1872
  challenge: input.challenge,
@@ -1485,33 +1916,33 @@ async function completePublisherEndpointChallenge(input) {
1485
1916
  }
1486
1917
  function filterWithinServicePolicy(service, plaintext) {
1487
1918
  const filter = plaintext.filter;
1488
- return filter.maxPositionsPerWake <= service.limits.maxPositionsPerWake && service.events.some((event) => wakeSourcesEqual(event.source, filter.source));
1919
+ return service.deliveryMode === filter.deliveryMode && JSON.stringify(service.bestEffort) === JSON.stringify(filter.bestEffort) && filter.maxPositionsPerWake <= service.limits.maxPositionsPerWake && service.events.some((event) => wakeSourcesEqual(event.source, filter.source));
1489
1920
  }
1490
1921
 
1491
1922
  // src/sourceWakeActivation.ts
1492
1923
  import {
1493
- ChainIdZod as ChainIdZod2,
1494
- DeploymentIdZod as DeploymentIdZod2,
1495
- HashZod as HashZod5,
1496
- NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod2,
1497
- NormalizedAddressZod as NormalizedAddressZod5,
1498
- PositiveJsonSafeIntegerZod
1924
+ ChainIdZod as ChainIdZod3,
1925
+ DeploymentIdZod as DeploymentIdZod3,
1926
+ HashZod as HashZod7,
1927
+ NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod4,
1928
+ NormalizedAddressZod as NormalizedAddressZod7,
1929
+ PositiveJsonSafeIntegerZod as PositiveJsonSafeIntegerZod2
1499
1930
  } from "@xyo-network/event-kit-schemas";
1500
- import { z as z2 } from "zod";
1501
- var SourceWakeSubscriptionActivationZod = z2.strictObject({
1502
- atBlock: NonnegativeJsonSafeIntegerZod2,
1503
- activeFromBlock: NonnegativeJsonSafeIntegerZod2,
1504
- expiresAtBlock: PositiveJsonSafeIntegerZod,
1505
- authorizationChainId: ChainIdZod2,
1506
- controller: NormalizedAddressZod5,
1507
- publisher: NormalizedAddressZod5,
1508
- deploymentId: DeploymentIdZod2,
1509
- queueId: z2.string().min(1).max(255).optional(),
1510
- sourceHash: HashZod5,
1511
- filterHash: HashZod5,
1512
- subscriptionHash: HashZod5,
1513
- serviceHash: HashZod5,
1514
- grantHash: HashZod5
1931
+ import { z as z4 } from "zod";
1932
+ var SourceWakeSubscriptionActivationZod = z4.strictObject({
1933
+ atBlock: NonnegativeJsonSafeIntegerZod4,
1934
+ activeFromBlock: NonnegativeJsonSafeIntegerZod4,
1935
+ expiresAtBlock: PositiveJsonSafeIntegerZod2,
1936
+ authorizationChainId: ChainIdZod3,
1937
+ controller: NormalizedAddressZod7,
1938
+ publisher: NormalizedAddressZod7,
1939
+ deploymentId: DeploymentIdZod3,
1940
+ queueId: z4.string().min(1).max(255).optional(),
1941
+ sourceHash: HashZod7,
1942
+ filterHash: HashZod7,
1943
+ subscriptionHash: HashZod7,
1944
+ serviceHash: HashZod7,
1945
+ grantHash: HashZod7
1515
1946
  }).refine(
1516
1947
  (value) => value.activeFromBlock <= value.atBlock && value.atBlock < value.expiresAtBlock,
1517
1948
  "Subscription must be active at the exact finalized authorization snapshot"
@@ -1519,17 +1950,17 @@ var SourceWakeSubscriptionActivationZod = z2.strictObject({
1519
1950
 
1520
1951
  // src/statementGraphWakeGrantAuthorizer.ts
1521
1952
  import {
1522
- ChainIdZod as ChainIdZod3,
1523
- WakeSourceZod as WakeSourceZod2
1953
+ ChainIdZod as ChainIdZod4,
1954
+ WakeSourceZod as WakeSourceZod3
1524
1955
  } from "@xyo-network/event-kit-schemas";
1525
1956
 
1526
1957
  // src/grantEvidence.ts
1527
1958
  import {
1528
1959
  asWakeGrantObject,
1529
1960
  asWakeGrantRevocation,
1530
- DeploymentIdZod as DeploymentIdZod3,
1531
- HashZod as HashZod6,
1532
- NormalizedAddressZod as NormalizedAddressZod6,
1961
+ DeploymentIdZod as DeploymentIdZod4,
1962
+ HashZod as HashZod8,
1963
+ NormalizedAddressZod as NormalizedAddressZod8,
1533
1964
  WakeGrantObjectSchema as WakeGrantObjectSchema3,
1534
1965
  WakeGrantRevocationSchema as WakeGrantRevocationSchema3
1535
1966
  } from "@xyo-network/event-kit-schemas";
@@ -1585,7 +2016,7 @@ async function readRevocations(viewer, controller, subject, atBlock) {
1585
2016
  for (const claim of claims) {
1586
2017
  if (!isLiveClaim(claim, controller, subject, WakeGrantRevocationSchema3, atBlock)) continue;
1587
2018
  const object = await viewer.object(claim.objectHash);
1588
- if (object?.assurance !== "resolved" || object.declaredSchema !== WakeGrantRevocationSchema3 || object.objectHash !== claim.objectHash || !isRecord2(object.params) || Object.hasOwn(object.params, "schema")) continue;
2019
+ if (object?.assurance !== "resolved" || object.declaredSchema !== WakeGrantRevocationSchema3 || object.objectHash !== claim.objectHash || !isRecord3(object.params) || Object.hasOwn(object.params, "schema")) continue;
1589
2020
  const revocation = asWakeGrantRevocation({ ...object.params, schema: object.declaredSchema });
1590
2021
  if (revocation !== void 0) revoked.add(revocation.grant);
1591
2022
  }
@@ -1604,10 +2035,6 @@ async function resolveEvidence(viewer, claim, request, atBlock, revokedGrantHash
1604
2035
  if (!grant.permissions.includes("enqueue")) return void 0;
1605
2036
  return Object.freeze({
1606
2037
  assertedAtBlock: claim.firstBlock,
1607
- authorizationChainId: request.authorizationChainId,
1608
- sourceHash: request.sourceHash,
1609
- filterHash: request.filterHash,
1610
- subscriptionHash: request.subscriptionHash,
1611
2038
  controller: request.controller,
1612
2039
  deploymentId: grant.deploymentId,
1613
2040
  id: claim.firstEventHash,
@@ -1617,55 +2044,55 @@ async function resolveEvidence(viewer, claim, request, atBlock, revokedGrantHash
1617
2044
  });
1618
2045
  }
1619
2046
  function isLiveClaim(claim, controller, subject, declaredObjectSchema, atBlock) {
1620
- return claim.source === controller && claim.subject === subject && claim.declaredObjectSchema === declaredObjectSchema && isNonnegativeSafeInteger4(claim.firstBlock) && claim.firstBlock <= atBlock && isNonnegativeSafeInteger4(claim.lastBlock) && claim.lastBlock >= claim.firstBlock && HashZod6.safeParse(claim.firstEventHash).success && HashZod6.safeParse(claim.objectHash).success;
2047
+ return claim.source === controller && claim.subject === subject && claim.declaredObjectSchema === declaredObjectSchema && isNonnegativeSafeInteger5(claim.firstBlock) && claim.firstBlock <= atBlock && isNonnegativeSafeInteger5(claim.lastBlock) && claim.lastBlock >= claim.firstBlock && HashZod8.safeParse(claim.firstEventHash).success && HashZod8.safeParse(claim.objectHash).success;
1621
2048
  }
1622
2049
  function parseExactGrantObject(object, expectedObjectHash) {
1623
2050
  if (object?.assurance !== "resolved") return void 0;
1624
2051
  if (object.declaredSchema !== WakeGrantObjectSchema3 || object.objectHash !== expectedObjectHash) return void 0;
1625
- if (!isRecord2(object.params) || Object.hasOwn(object.params, "schema")) return void 0;
2052
+ if (!isRecord3(object.params) || Object.hasOwn(object.params, "schema")) return void 0;
1626
2053
  return asWakeGrantObject({
1627
2054
  ...object.params,
1628
2055
  schema: object.declaredSchema
1629
2056
  });
1630
2057
  }
1631
2058
  function assertValidRequest(request) {
1632
- if (request.exactGrantHash !== void 0 && !HashZod6.safeParse(request.exactGrantHash).success) {
2059
+ if (request.exactGrantHash !== void 0 && !HashZod8.safeParse(request.exactGrantHash).success) {
1633
2060
  throw new TypeError("Exact grant hash must be canonical lowercase hexadecimal");
1634
2061
  }
1635
- if (!NormalizedAddressZod6.safeParse(request.controller).success) {
2062
+ if (!NormalizedAddressZod8.safeParse(request.controller).success) {
1636
2063
  throw new TypeError("Grant controller must be a normalized address");
1637
2064
  }
1638
- if (!NormalizedAddressZod6.safeParse(request.publisher).success) {
2065
+ if (!NormalizedAddressZod8.safeParse(request.publisher).success) {
1639
2066
  throw new TypeError("Grant publisher must be a normalized address");
1640
2067
  }
1641
- if (!DeploymentIdZod3.safeParse(request.deploymentId).success) {
2068
+ if (!DeploymentIdZod4.safeParse(request.deploymentId).success) {
1642
2069
  throw new TypeError("Grant deploymentId must contain 1 to 255 characters");
1643
2070
  }
1644
2071
  if (request.queueId !== void 0 && (request.queueId.length === 0 || request.queueId.length > 255)) {
1645
2072
  throw new TypeError("Grant queueId must contain 1 to 255 characters when configured");
1646
2073
  }
1647
- if (!isNonnegativeSafeInteger4(request.expectedHead)) {
2074
+ if (!isNonnegativeSafeInteger5(request.expectedHead)) {
1648
2075
  throw new RangeError("Grant expectedHead must be a nonnegative safe integer");
1649
2076
  }
1650
2077
  }
1651
2078
  async function readHead(viewer) {
1652
2079
  const head = await viewer.head();
1653
- if (!isNonnegativeSafeInteger4(head)) {
2080
+ if (!isNonnegativeSafeInteger5(head)) {
1654
2081
  throw new RangeError("Statement Graph head must be a nonnegative safe integer");
1655
2082
  }
1656
2083
  return head;
1657
2084
  }
1658
- function isNonnegativeSafeInteger4(value) {
2085
+ function isNonnegativeSafeInteger5(value) {
1659
2086
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
1660
2087
  }
1661
- function isRecord2(value) {
2088
+ function isRecord3(value) {
1662
2089
  return typeof value === "object" && value !== null && !Array.isArray(value);
1663
2090
  }
1664
2091
 
1665
2092
  // src/statementGraphWakeGrantAuthorizer.ts
1666
2093
  function createStatementGraphWakeGrantAuthorizer(options) {
1667
- const authorizationChainId = ChainIdZod3.parse(options.authorizationChainId);
1668
- const source = WakeSourceZod2.parse(options.source);
2094
+ const authorizationChainId = ChainIdZod4.parse(options.authorizationChainId);
2095
+ const source = WakeSourceZod3.parse(options.source);
1669
2096
  const grants = createDeploymentGrantAuthorizer({ viewer: options.viewer });
1670
2097
  return Object.freeze({
1671
2098
  async authorizePublisher(request) {
@@ -1687,6 +2114,7 @@ function createStatementGraphWakeGrantAuthorizer(options) {
1687
2114
  ...decision,
1688
2115
  grant: Object.freeze({
1689
2116
  ...decision.grant,
2117
+ authorizationKind: "activated-subscription",
1690
2118
  authorizationChainId,
1691
2119
  sourceHash,
1692
2120
  filterHash: activation.filterHash,
@@ -1697,190 +2125,6 @@ function createStatementGraphWakeGrantAuthorizer(options) {
1697
2125
  });
1698
2126
  }
1699
2127
 
1700
- // src/verifyWakeRequest.ts
1701
- import {
1702
- ChainIdZod as ChainIdZod4,
1703
- DeploymentIdZod as DeploymentIdZod4,
1704
- HashZod as HashZod7,
1705
- MAX_POSITIONS_PER_SUBSCRIPTION_WAKE,
1706
- NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod3,
1707
- NormalizedAddressZod as NormalizedAddressZod7,
1708
- PositiveJsonSafeIntegerZod as PositiveJsonSafeIntegerZod2,
1709
- SOURCE_WAKE_FILTER_PROFILE as SOURCE_WAKE_FILTER_PROFILE2,
1710
- WakeEnvelopeZod as WakeEnvelopeZod2,
1711
- WakeJwtAudienceZod as WakeJwtAudienceZod2,
1712
- WakeJwtHeaderZod,
1713
- WakeJwtPayloadZod,
1714
- WakeJwtSchema,
1715
- WakeSourceZod as WakeSourceZod3
1716
- } from "@xyo-network/event-kit-schemas";
1717
- import {
1718
- Account,
1719
- PayloadBuilder as PayloadBuilder5
1720
- } from "@xyo-network/sdk";
1721
- import { z as z3 } from "zod";
1722
- var EVENT_KIT_MAX_JWT_LIFETIME_SECONDS = 300;
1723
- var EVENT_KIT_MAX_FUTURE_IAT_SECONDS = 60;
1724
- var WakeAdmissionRouteZod = z3.strictObject({
1725
- audience: WakeJwtAudienceZod2,
1726
- authorizationChainId: ChainIdZod4,
1727
- controller: NormalizedAddressZod7,
1728
- deploymentId: DeploymentIdZod4,
1729
- queueId: z3.string().min(1).max(255).optional(),
1730
- source: WakeSourceZod3,
1731
- filterHash: HashZod7,
1732
- subscriptionHash: HashZod7,
1733
- fromPosition: NonnegativeJsonSafeIntegerZod3,
1734
- maxPositionsPerWake: PositiveJsonSafeIntegerZod2.max(MAX_POSITIONS_PER_SUBSCRIPTION_WAKE),
1735
- maxGrantLagBlocks: NonnegativeJsonSafeIntegerZod3
1736
- });
1737
- async function verifyWakeRequest(input) {
1738
- const routeResult = WakeAdmissionRouteZod.safeParse(input.route);
1739
- if (!routeResult.success || !NonnegativeJsonSafeIntegerZod3.safeParse(input.nowSeconds).success) {
1740
- return { ok: false, reason: "configuration-invalid" };
1741
- }
1742
- const eventResult = WakeEnvelopeZod2.safeParse(input.body);
1743
- if (!eventResult.success) return malformedWake(input.body, eventResult.error.issues);
1744
- const route = routeResult.data;
1745
- if (route.filterHash !== await sourceWakeFilterHash({
1746
- schema: SOURCE_WAKE_FILTER_PROFILE2,
1747
- source: route.source,
1748
- fromPosition: route.fromPosition,
1749
- maxPositionsPerWake: route.maxPositionsPerWake
1750
- })) return { ok: false, reason: "configuration-invalid" };
1751
- const event = Object.freeze({ ...eventResult.data, source: Object.freeze(eventResult.data.source) });
1752
- if (event.authorizationChainId !== route.authorizationChainId) {
1753
- return { ok: false, reason: "event-authorization-chain-mismatch" };
1754
- }
1755
- if (!wakeSourcesEqual(event.source, route.source)) return { ok: false, reason: "event-source-mismatch" };
1756
- if (event.filterHash !== route.filterHash) return { ok: false, reason: "event-filter-mismatch" };
1757
- if (event.deploymentId !== route.deploymentId || event.queueId !== route.queueId) {
1758
- return { ok: false, reason: "event-target-mismatch" };
1759
- }
1760
- if (event.fromPosition < route.fromPosition) return { ok: false, reason: "range-before-floor" };
1761
- if (event.toPosition - event.fromPosition >= route.maxPositionsPerWake) return { ok: false, reason: "range-too-large" };
1762
- const jwt = await verifyJwtProfile(input.jwt, route.audience, input.nowSeconds);
1763
- if (!jwt.ok) return jwt;
1764
- let eventHash;
1765
- try {
1766
- eventHash = await PayloadBuilder5.dataHash(event);
1767
- } catch {
1768
- return { ok: false, reason: "event-hash-failed" };
1769
- }
1770
- if (eventHash !== jwt.jwt.eventHash) return { ok: false, reason: "event-hash-mismatch" };
1771
- const sourceHash = await wakeSourceHash(route.source);
1772
- return Object.freeze({
1773
- ok: true,
1774
- verified: Object.freeze({
1775
- audience: route.audience,
1776
- deploymentId: event.deploymentId,
1777
- event,
1778
- eventHash,
1779
- expiresAt: jwt.jwt.expiresAt,
1780
- fromPosition: event.fromPosition,
1781
- issuedAt: event.issuedAt,
1782
- jwtId: jwt.jwt.jwtId,
1783
- publisher: jwt.jwt.publisher,
1784
- ...event.queueId === void 0 ? {} : { queueId: event.queueId },
1785
- route: Object.freeze({
1786
- ...route,
1787
- source: Object.freeze(route.source),
1788
- sourceHash
1789
- }),
1790
- toPosition: event.toPosition,
1791
- tokenIssuedAt: jwt.jwt.issuedAt,
1792
- wakeId: event.wakeId
1793
- })
1794
- });
1795
- }
1796
- async function verifyJwtProfile(token, audience, nowSeconds) {
1797
- let verified;
1798
- try {
1799
- verified = await Account.verifyJwt(token, { audience, now: nowSeconds });
1800
- } catch (error) {
1801
- return reject2("jwt-malformed", [errorMessage2(error)]);
1802
- }
1803
- if (!verified.ok) {
1804
- return Object.hasOwn(verified.payload ?? {}, "nbf") ? reject2("jwt-nbf-not-allowed") : reject2(classifySdkJwtFailure(verified.reasons), verified.reasons);
1805
- }
1806
- const wireResult = parseStrictJwtWire(verified.header, verified.payload, audience);
1807
- if (!wireResult.ok) return wireResult;
1808
- const { header: strictHeader, payload: strictPayload } = wireResult;
1809
- if (strictHeader.kid !== strictPayload.iss) return reject2("jwt-identity-invalid");
1810
- if (strictPayload.exp <= nowSeconds) return reject2("jwt-expired");
1811
- if (strictPayload.exp - strictPayload.iat > EVENT_KIT_MAX_JWT_LIFETIME_SECONDS) return reject2("jwt-lifetime-invalid");
1812
- if (strictPayload.iat - nowSeconds > EVENT_KIT_MAX_FUTURE_IAT_SECONDS) return reject2("jwt-future-iat");
1813
- const jwt = Object.freeze({
1814
- eventHash: strictPayload.eventHash,
1815
- expiresAt: strictPayload.exp,
1816
- issuedAt: strictPayload.iat,
1817
- jwtId: strictPayload.jti,
1818
- publisher: strictPayload.iss
1819
- });
1820
- return Object.freeze({ jwt, ok: true });
1821
- }
1822
- function parseStrictJwtWire(header, payload, audience) {
1823
- const headerResult = WakeJwtHeaderZod.safeParse(header);
1824
- if (!headerResult.success) {
1825
- return NormalizedAddressZod7.safeParse(header.kid).success ? reject2("jwt-header-invalid", zodDetails(headerResult.error.issues)) : reject2("jwt-identity-invalid");
1826
- }
1827
- if (Object.hasOwn(payload, "nbf")) return reject2("jwt-nbf-not-allowed");
1828
- if (!NormalizedAddressZod7.safeParse(payload.iss).success) return reject2("jwt-identity-invalid");
1829
- if (payload.schema !== WakeJwtSchema) return reject2("jwt-schema-mismatch");
1830
- if (payload.aud !== audience) return reject2("jwt-audience-mismatch");
1831
- if (!isNonnegativeSafeInteger5(payload.iat) || !isNonnegativeSafeInteger5(payload.exp) || payload.exp <= payload.iat) {
1832
- return reject2("jwt-lifetime-invalid");
1833
- }
1834
- const payloadResult = WakeJwtPayloadZod.safeParse(payload);
1835
- if (!payloadResult.success) return reject2("jwt-claims-invalid", zodDetails(payloadResult.error.issues));
1836
- return Object.freeze({
1837
- header: headerResult.data,
1838
- ok: true,
1839
- payload: payloadResult.data
1840
- });
1841
- }
1842
- function classifySdkJwtFailure(reasons) {
1843
- if (reasons.some((reason) => reason.startsWith("audience mismatch:"))) return "jwt-audience-mismatch";
1844
- if (reasons.some((reason) => reason.startsWith("expired:"))) return "jwt-expired";
1845
- if (reasons.some((reason) => reason.startsWith("unsupported alg:") || reason.startsWith("unsupported typ:"))) {
1846
- return "jwt-header-invalid";
1847
- }
1848
- return reasons.some((reason) => reason.startsWith("invalid public key:") || reason.startsWith("public key does not derive to kid:") || reason === "header.kid does not match payload.iss") ? "jwt-identity-invalid" : "jwt-invalid";
1849
- }
1850
- function malformedWake(body, issues) {
1851
- const details = zodDetails(issues);
1852
- if (hasInvertedSafeRange(body)) return reject2("range-invalid", details);
1853
- return reject2(isRecord3(body) && Object.hasOwn(body, "wakeId") && issues.some((issue) => issue.path[0] === "wakeId") ? "wake-id-invalid" : "event-malformed", details);
1854
- }
1855
- function hasInvertedSafeRange(value) {
1856
- if (!isRecord3(value)) return false;
1857
- const { fromPosition, toPosition } = value;
1858
- return typeof fromPosition === "number" && typeof toPosition === "number" && isNonnegativeSafeInteger5(fromPosition) && isNonnegativeSafeInteger5(toPosition) && fromPosition > toPosition;
1859
- }
1860
- function isNonnegativeSafeInteger5(value) {
1861
- return Number.isSafeInteger(value) && value >= 0;
1862
- }
1863
- function isRecord3(value) {
1864
- return typeof value === "object" && value !== null && !Array.isArray(value);
1865
- }
1866
- function zodDetails(issues) {
1867
- return issues.map((issue) => {
1868
- const path = issue.path.map(String).join(".") || "<root>";
1869
- return `${path}: ${issue.message}`;
1870
- });
1871
- }
1872
- function reject2(reason, details) {
1873
- if (details === void 0) return Object.freeze({ ok: false, reason });
1874
- return Object.freeze({
1875
- details: Object.freeze([...details]),
1876
- ok: false,
1877
- reason
1878
- });
1879
- }
1880
- function errorMessage2(error) {
1881
- return error instanceof Error ? error.message : String(error);
1882
- }
1883
-
1884
2128
  // src/wakeQueue.ts
1885
2129
  async function drainQueue(queue, handler) {
1886
2130
  let handled = 0;
@@ -1890,7 +2134,11 @@ async function drainQueue(queue, handler) {
1890
2134
  try {
1891
2135
  await handler(leased.wake);
1892
2136
  } catch (error) {
1893
- await leased.release();
2137
+ try {
2138
+ await leased.release();
2139
+ } catch (releaseError) {
2140
+ throw new AggregateError([error, releaseError], "Wake handler and lease release both failed", { cause: releaseError });
2141
+ }
1894
2142
  throw error;
1895
2143
  }
1896
2144
  await leased.ack();
@@ -1899,17 +2147,23 @@ async function drainQueue(queue, handler) {
1899
2147
  }
1900
2148
  export {
1901
2149
  AdmittedWakeZod,
2150
+ BestEffortAdmissionAcknowledgmentZod,
2151
+ BestEffortSecurityStateZod,
1902
2152
  EVENT_KIT_MAX_FUTURE_IAT_SECONDS,
1903
2153
  EVENT_KIT_MAX_JWT_LIFETIME_SECONDS,
1904
2154
  EVENT_KIT_REPLAY_RETENTION_SKEW_SECONDS,
1905
2155
  SourceWakeSubscriptionActivationZod,
2156
+ StaleWakeLeaseError,
1906
2157
  WakeAdmissionRouteZod,
1907
2158
  WakeSubscriptionDecryptionError,
1908
2159
  admitAndEnqueue,
2160
+ admitBestEffortHint,
1909
2161
  beginPublisherEndpointChallengeAttempt,
1910
2162
  canonicalJson,
1911
2163
  canonicalJsonBytes,
2164
+ commitBestEffortSecurity,
1912
2165
  completePublisherEndpointChallenge,
2166
+ createBestEffortSecurityState,
1913
2167
  createEncryptedWakeSubscription,
1914
2168
  createEndpointChallenge,
1915
2169
  createEndpointChallengeResponse,
@@ -1937,6 +2191,7 @@ export {
1937
2191
  handshakeObjectHash,
1938
2192
  issuePublisherEndpointChallenge,
1939
2193
  parseAdmittedWake,
2194
+ parseBestEffortSecurityState,
1940
2195
  resolvePreferredEncryptionKey,
1941
2196
  selectPublisherService,
1942
2197
  selectSubscriptionActivation,