@xyo-network/event-kit-protocol 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.
@@ -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 {
@@ -266,6 +266,7 @@ async function parseAdmittedWake(value) {
266
266
  // src/admitAndEnqueue.ts
267
267
  async function admitAndEnqueue(input) {
268
268
  const { verified } = input;
269
+ if (verified.route.deliveryMode !== "durable") throw new TypeError("Durable admission requires explicit durable delivery mode");
269
270
  const wake = AdmittedWakeZod.parse({
270
271
  audience: verified.audience,
271
272
  deploymentId: verified.deploymentId,
@@ -281,6 +282,53 @@ async function admitAndEnqueue(input) {
281
282
  return await admitVerifiedWake({ ...input, wake });
282
283
  }
283
284
 
285
+ // src/bestEffortAdmission.ts
286
+ import {
287
+ HashZod as HashZod3,
288
+ NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod3,
289
+ NormalizedAddressZod as NormalizedAddressZod3,
290
+ WakeIdZod as WakeIdZod2,
291
+ WakeJwtAudienceZod as WakeJwtAudienceZod3,
292
+ WakeJwtIdZod as WakeJwtIdZod2
293
+ } from "@xyo-network/event-kit-schemas";
294
+ import { z as z3 } from "zod";
295
+
296
+ // src/verifyWakeRequest.ts
297
+ import {
298
+ ChainIdZod,
299
+ DeploymentIdZod as DeploymentIdZod2,
300
+ HashZod as HashZod2,
301
+ MAX_POSITIONS_PER_SUBSCRIPTION_WAKE,
302
+ NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod2,
303
+ NormalizedAddressZod as NormalizedAddressZod2,
304
+ PositiveJsonSafeIntegerZod,
305
+ SOURCE_WAKE_FILTER_PROFILE,
306
+ validWakeDeliveryTerms,
307
+ WakeDeliveryTermsFields,
308
+ WakeEnvelopeZod as WakeEnvelopeZod2,
309
+ WakeJwtAudienceZod as WakeJwtAudienceZod2,
310
+ WakeJwtHeaderZod,
311
+ WakeJwtPayloadZod,
312
+ WakeJwtSchema,
313
+ WakeSourceZod as WakeSourceZod2
314
+ } from "@xyo-network/event-kit-schemas";
315
+ import {
316
+ Account,
317
+ PayloadBuilder as PayloadBuilder3
318
+ } from "@xyo-network/sdk";
319
+ import { z as z2 } from "zod";
320
+
321
+ // src/sourceWakeIdentity.ts
322
+ import {
323
+ SourceWakeSubscriptionFilterZod,
324
+ WakeSourceSchema,
325
+ WakeSourceZod
326
+ } from "@xyo-network/event-kit-schemas";
327
+ import {
328
+ asSchema,
329
+ PayloadBuilder as PayloadBuilder2
330
+ } from "@xyo-network/sdk";
331
+
284
332
  // src/canonicalJson.ts
285
333
  var textEncoder = new TextEncoder();
286
334
  function hasUnpairedSurrogate(value) {
@@ -330,6 +378,379 @@ function canonicalJsonBytes(value) {
330
378
  return textEncoder.encode(canonicalize(value));
331
379
  }
332
380
 
381
+ // src/sourceWakeIdentity.ts
382
+ async function wakeSourceHash(source) {
383
+ return await PayloadBuilder2.dataHash({ schema: WakeSourceSchema, ...WakeSourceZod.parse(source) });
384
+ }
385
+ function wakeSourcesEqual(left, right) {
386
+ return canonicalJson(WakeSourceZod.parse(left)) === canonicalJson(WakeSourceZod.parse(right));
387
+ }
388
+ async function sourceWakeFilterHash(filter) {
389
+ const parsed = SourceWakeSubscriptionFilterZod.parse(filter);
390
+ return await PayloadBuilder2.dataHash({ ...parsed, schema: asSchema(parsed.schema, true) });
391
+ }
392
+
393
+ // src/verifyWakeRequest.ts
394
+ var EVENT_KIT_MAX_JWT_LIFETIME_SECONDS = 300;
395
+ var EVENT_KIT_MAX_FUTURE_IAT_SECONDS = 60;
396
+ var WakeAdmissionRouteZod = z2.strictObject({
397
+ ...WakeDeliveryTermsFields,
398
+ audience: WakeJwtAudienceZod2,
399
+ authorizationChainId: ChainIdZod,
400
+ controller: NormalizedAddressZod2,
401
+ deploymentId: DeploymentIdZod2,
402
+ queueId: z2.string().min(1).max(255).optional(),
403
+ source: WakeSourceZod2,
404
+ filterHash: HashZod2,
405
+ subscriptionHash: HashZod2,
406
+ fromPosition: NonnegativeJsonSafeIntegerZod2,
407
+ maxPositionsPerWake: PositiveJsonSafeIntegerZod.max(MAX_POSITIONS_PER_SUBSCRIPTION_WAKE),
408
+ maxGrantLagBlocks: NonnegativeJsonSafeIntegerZod2
409
+ }).refine((route) => validWakeDeliveryTerms(route) && (route.deliveryMode !== "best-effort" || route.maxPositionsPerWake === 1), "Invalid explicit delivery terms");
410
+ function verifyEventBinding(event, route, nowSeconds) {
411
+ if (event.authorizationChainId !== route.authorizationChainId) {
412
+ return { ok: false, reason: "event-authorization-chain-mismatch" };
413
+ }
414
+ if (!wakeSourcesEqual(event.source, route.source)) return { ok: false, reason: "event-source-mismatch" };
415
+ if (event.filterHash !== route.filterHash) return { ok: false, reason: "event-filter-mismatch" };
416
+ if (event.deploymentId !== route.deploymentId || event.queueId !== route.queueId) {
417
+ return { ok: false, reason: "event-target-mismatch" };
418
+ }
419
+ if (event.deliveryMode !== route.deliveryMode) return { ok: false, reason: "event-mode-mismatch" };
420
+ if (route.deliveryMode === "best-effort") {
421
+ if (event.issuedAt > nowSeconds + EVENT_KIT_MAX_FUTURE_IAT_SECONDS) return { ok: false, reason: "hint-future-issued-at" };
422
+ if (route.bestEffort === void 0 || nowSeconds > event.issuedAt + route.bestEffort.maxHintAgeSeconds) return { ok: false, reason: "hint-expired" };
423
+ }
424
+ if (event.fromPosition < route.fromPosition) return { ok: false, reason: "range-before-floor" };
425
+ if (event.toPosition - event.fromPosition >= route.maxPositionsPerWake) return { ok: false, reason: "range-too-large" };
426
+ }
427
+ async function verifyWakeRequest(input) {
428
+ const routeResult = WakeAdmissionRouteZod.safeParse(input.route);
429
+ if (!routeResult.success || !NonnegativeJsonSafeIntegerZod2.safeParse(input.nowSeconds).success) {
430
+ return { ok: false, reason: "configuration-invalid" };
431
+ }
432
+ const eventResult = WakeEnvelopeZod2.safeParse(input.body);
433
+ if (!eventResult.success) return malformedWake(input.body, eventResult.error.issues);
434
+ const route = routeResult.data;
435
+ if (route.filterHash !== await sourceWakeFilterHash({
436
+ schema: SOURCE_WAKE_FILTER_PROFILE,
437
+ deliveryMode: route.deliveryMode,
438
+ ...route.bestEffort === void 0 ? {} : { bestEffort: route.bestEffort },
439
+ source: route.source,
440
+ fromPosition: route.fromPosition,
441
+ maxPositionsPerWake: route.maxPositionsPerWake
442
+ })) return { ok: false, reason: "configuration-invalid" };
443
+ const event = Object.freeze({ ...eventResult.data, source: Object.freeze(eventResult.data.source) });
444
+ const mismatch = verifyEventBinding(event, route, input.nowSeconds);
445
+ if (mismatch !== void 0) return mismatch;
446
+ const jwt = await verifyJwtProfile(input.jwt, route.audience, input.nowSeconds);
447
+ if (!jwt.ok) return jwt;
448
+ let eventHash;
449
+ try {
450
+ eventHash = await PayloadBuilder3.dataHash(event);
451
+ } catch {
452
+ return { ok: false, reason: "event-hash-failed" };
453
+ }
454
+ if (eventHash !== jwt.jwt.eventHash) return { ok: false, reason: "event-hash-mismatch" };
455
+ const sourceHash = await wakeSourceHash(route.source);
456
+ return Object.freeze({
457
+ ok: true,
458
+ verified: Object.freeze({
459
+ audience: route.audience,
460
+ deploymentId: event.deploymentId,
461
+ event,
462
+ eventHash,
463
+ expiresAt: jwt.jwt.expiresAt,
464
+ fromPosition: event.fromPosition,
465
+ issuedAt: event.issuedAt,
466
+ jwtId: jwt.jwt.jwtId,
467
+ publisher: jwt.jwt.publisher,
468
+ ...event.queueId === void 0 ? {} : { queueId: event.queueId },
469
+ route: Object.freeze({
470
+ ...route,
471
+ source: Object.freeze(route.source),
472
+ sourceHash,
473
+ ...route.bestEffort === void 0 ? {} : { bestEffort: Object.freeze(route.bestEffort) }
474
+ }),
475
+ toPosition: event.toPosition,
476
+ tokenIssuedAt: jwt.jwt.issuedAt,
477
+ wakeId: event.wakeId
478
+ })
479
+ });
480
+ }
481
+ async function verifyJwtProfile(token, audience, nowSeconds) {
482
+ let verified;
483
+ try {
484
+ verified = await Account.verifyJwt(token, { audience, now: nowSeconds });
485
+ } catch (error) {
486
+ return reject2("jwt-malformed", [errorMessage2(error)]);
487
+ }
488
+ if (!verified.ok) {
489
+ return Object.hasOwn(verified.payload ?? {}, "nbf") ? reject2("jwt-nbf-not-allowed") : reject2(classifySdkJwtFailure(verified.reasons), verified.reasons);
490
+ }
491
+ const wireResult = parseStrictJwtWire(verified.header, verified.payload, audience);
492
+ if (!wireResult.ok) return wireResult;
493
+ const { header: strictHeader, payload: strictPayload } = wireResult;
494
+ if (strictHeader.kid !== strictPayload.iss) return reject2("jwt-identity-invalid");
495
+ if (strictPayload.exp <= nowSeconds) return reject2("jwt-expired");
496
+ if (strictPayload.exp - strictPayload.iat > EVENT_KIT_MAX_JWT_LIFETIME_SECONDS) return reject2("jwt-lifetime-invalid");
497
+ if (strictPayload.iat - nowSeconds > EVENT_KIT_MAX_FUTURE_IAT_SECONDS) return reject2("jwt-future-iat");
498
+ const jwt = Object.freeze({
499
+ eventHash: strictPayload.eventHash,
500
+ expiresAt: strictPayload.exp,
501
+ issuedAt: strictPayload.iat,
502
+ jwtId: strictPayload.jti,
503
+ publisher: strictPayload.iss
504
+ });
505
+ return Object.freeze({ jwt, ok: true });
506
+ }
507
+ function parseStrictJwtWire(header, payload, audience) {
508
+ const headerResult = WakeJwtHeaderZod.safeParse(header);
509
+ if (!headerResult.success) {
510
+ return NormalizedAddressZod2.safeParse(header.kid).success ? reject2("jwt-header-invalid", zodDetails(headerResult.error.issues)) : reject2("jwt-identity-invalid");
511
+ }
512
+ if (Object.hasOwn(payload, "nbf")) return reject2("jwt-nbf-not-allowed");
513
+ if (!NormalizedAddressZod2.safeParse(payload.iss).success) return reject2("jwt-identity-invalid");
514
+ if (payload.schema !== WakeJwtSchema) return reject2("jwt-schema-mismatch");
515
+ if (payload.aud !== audience) return reject2("jwt-audience-mismatch");
516
+ if (!isNonnegativeSafeInteger2(payload.iat) || !isNonnegativeSafeInteger2(payload.exp) || payload.exp <= payload.iat) {
517
+ return reject2("jwt-lifetime-invalid");
518
+ }
519
+ const payloadResult = WakeJwtPayloadZod.safeParse(payload);
520
+ if (!payloadResult.success) return reject2("jwt-claims-invalid", zodDetails(payloadResult.error.issues));
521
+ return Object.freeze({
522
+ header: headerResult.data,
523
+ ok: true,
524
+ payload: payloadResult.data
525
+ });
526
+ }
527
+ function classifySdkJwtFailure(reasons) {
528
+ if (reasons.some((reason) => reason.startsWith("audience mismatch:"))) return "jwt-audience-mismatch";
529
+ if (reasons.some((reason) => reason.startsWith("expired:"))) return "jwt-expired";
530
+ if (reasons.some((reason) => reason.startsWith("unsupported alg:") || reason.startsWith("unsupported typ:"))) {
531
+ return "jwt-header-invalid";
532
+ }
533
+ 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";
534
+ }
535
+ function malformedWake(body, issues) {
536
+ const details = zodDetails(issues);
537
+ if (hasInvertedSafeRange(body)) return reject2("range-invalid", details);
538
+ return reject2(isRecord2(body) && Object.hasOwn(body, "wakeId") && issues.some((issue) => issue.path[0] === "wakeId") ? "wake-id-invalid" : "event-malformed", details);
539
+ }
540
+ function hasInvertedSafeRange(value) {
541
+ if (!isRecord2(value)) return false;
542
+ const { fromPosition, toPosition } = value;
543
+ return typeof fromPosition === "number" && typeof toPosition === "number" && isNonnegativeSafeInteger2(fromPosition) && isNonnegativeSafeInteger2(toPosition) && fromPosition > toPosition;
544
+ }
545
+ function isNonnegativeSafeInteger2(value) {
546
+ return Number.isSafeInteger(value) && value >= 0;
547
+ }
548
+ function isRecord2(value) {
549
+ return typeof value === "object" && value !== null && !Array.isArray(value);
550
+ }
551
+ function zodDetails(issues) {
552
+ return issues.map((issue) => {
553
+ const path = issue.path.map(String).join(".") || "<root>";
554
+ return `${path}: ${issue.message}`;
555
+ });
556
+ }
557
+ function reject2(reason, details) {
558
+ if (details === void 0) return Object.freeze({ ok: false, reason });
559
+ return Object.freeze({
560
+ details: Object.freeze([...details]),
561
+ ok: false,
562
+ reason
563
+ });
564
+ }
565
+ function errorMessage2(error) {
566
+ return error instanceof Error ? error.message : String(error);
567
+ }
568
+
569
+ // src/bestEffortAdmission.ts
570
+ function hintRetention(input) {
571
+ const { verified, nowSeconds } = input;
572
+ const policy = verified.route.bestEffort;
573
+ 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)
574
+ return { ok: false, reason: "configuration-invalid" };
575
+ if (verified.issuedAt > nowSeconds + EVENT_KIT_MAX_FUTURE_IAT_SECONDS) return { ok: false, reason: "hint-future-issued-at" };
576
+ const bodyDeadline = verified.issuedAt + policy.maxHintAgeSeconds;
577
+ const retainWakeUntil = bodyDeadline + EVENT_KIT_REPLAY_RETENTION_SKEW_SECONDS;
578
+ const retainJwtUntil = verified.expiresAt + EVENT_KIT_REPLAY_RETENTION_SKEW_SECONDS;
579
+ if (!Number.isSafeInteger(retainWakeUntil) || !Number.isSafeInteger(retainJwtUntil)) return { ok: false, reason: "configuration-invalid" };
580
+ return nowSeconds > bodyDeadline ? { ok: false, reason: "hint-expired" } : {
581
+ ok: true,
582
+ retainJwtUntil,
583
+ retainWakeUntil
584
+ };
585
+ }
586
+ async function admitBestEffortHint(input) {
587
+ const retention = hintRetention(input);
588
+ if (!retention.ok) return retention;
589
+ const { retainJwtUntil, retainWakeUntil } = retention;
590
+ const { verified, nowSeconds } = input;
591
+ const authorization = await authorizeVerifiedWake(input);
592
+ if (!authorization.ok) return authorization.failure;
593
+ const wake = AdmittedWakeZod.parse({
594
+ audience: verified.audience,
595
+ deploymentId: verified.deploymentId,
596
+ event: verified.event,
597
+ eventHash: verified.eventHash,
598
+ fromPosition: verified.fromPosition,
599
+ toPosition: verified.toPosition,
600
+ jti: verified.jwtId,
601
+ publisher: verified.publisher,
602
+ wakeId: verified.wakeId,
603
+ ...verified.queueId === void 0 ? {} : { queueId: verified.queueId }
604
+ });
605
+ const attempt = {
606
+ audience: verified.audience,
607
+ eventHash: verified.eventHash,
608
+ jwtId: verified.jwtId,
609
+ publisher: verified.publisher,
610
+ retainJwtUntil,
611
+ retainWakeUntil,
612
+ wakeId: verified.wakeId
613
+ };
614
+ try {
615
+ const committed = await input.inbox.commitAuthorized({
616
+ attempt,
617
+ grant: authorization.grant,
618
+ wake,
619
+ nowSeconds
620
+ });
621
+ if (committed.kind !== "accepted" && committed.kind !== "duplicate") return { ok: false, reason: committed.kind };
622
+ const acknowledgment = committed.acknowledgment;
623
+ return acknowledgmentMatches(acknowledgment, attempt, nowSeconds) ? {
624
+ ok: true,
625
+ disposition: committed.kind,
626
+ acknowledgment,
627
+ wake
628
+ } : { ok: false, reason: "inbox-unavailable" };
629
+ } catch {
630
+ return { ok: false, reason: "inbox-unavailable" };
631
+ }
632
+ }
633
+ function acknowledgmentMatches(value, attempt, now) {
634
+ return BestEffortAdmissionAcknowledgmentZod.safeParse(value).success && value.eventHash === attempt.eventHash && value.wakeId === attempt.wakeId && value.acceptedAt <= now && value.expiresAt === attempt.retainWakeUntil;
635
+ }
636
+ var BestEffortAdmissionAcknowledgmentZod = z3.strictObject({
637
+ admissionId: z3.string().min(1).max(4096),
638
+ eventHash: HashZod3,
639
+ wakeId: WakeIdZod2,
640
+ acceptedAt: NonnegativeJsonSafeIntegerZod3,
641
+ expiresAt: NonnegativeJsonSafeIntegerZod3
642
+ }).refine((value) => value.expiresAt > value.acceptedAt, "Acknowledgment expiry must follow admission");
643
+ var JwtSecurityRecordZod = z3.strictObject({
644
+ audience: WakeJwtAudienceZod3,
645
+ publisher: NormalizedAddressZod3,
646
+ jwtId: WakeJwtIdZod2,
647
+ wakeId: WakeIdZod2,
648
+ eventHash: HashZod3,
649
+ expiresAt: NonnegativeJsonSafeIntegerZod3
650
+ });
651
+ var WakeSecurityRecordZod = z3.strictObject({
652
+ audience: WakeJwtAudienceZod3,
653
+ publisher: NormalizedAddressZod3,
654
+ wakeId: WakeIdZod2,
655
+ eventHash: HashZod3,
656
+ acknowledgment: BestEffortAdmissionAcknowledgmentZod
657
+ }).refine(
658
+ (value) => value.wakeId === value.acknowledgment.wakeId && value.eventHash === value.acknowledgment.eventHash,
659
+ "Security identity must match acknowledgment"
660
+ );
661
+ var BestEffortSecurityStateZod = z3.strictObject({
662
+ highestNowSeconds: NonnegativeJsonSafeIntegerZod3,
663
+ jwtRecords: z3.array(JwtSecurityRecordZod).max(65536),
664
+ wakeRecords: z3.array(WakeSecurityRecordZod).max(65536)
665
+ }).superRefine((value, context) => {
666
+ const jwtKeys = value.jwtRecords.map((record) => JSON.stringify([record.audience, record.publisher, record.jwtId]));
667
+ if (value.wakeRecords.some((record) => record.acknowledgment.acceptedAt > value.highestNowSeconds))
668
+ context.addIssue({ code: "custom", message: "Acknowledgment cannot precede stored admission clock" });
669
+ const wakeKeys = value.wakeRecords.map((record) => JSON.stringify([record.audience, record.publisher, record.wakeId]));
670
+ const jwtIdentities = new Set(jwtKeys);
671
+ const wakeIdentities = new Set(wakeKeys);
672
+ if (jwtIdentities.size !== jwtKeys.length || wakeIdentities.size !== wakeKeys.length)
673
+ context.addIssue({ code: "custom", message: "Persisted security identities must be unique" });
674
+ });
675
+ function parseBestEffortSecurityState(value) {
676
+ return BestEffortSecurityStateZod.parse(value);
677
+ }
678
+ var BestEffortAdmissionAttemptZod = z3.strictObject({
679
+ audience: WakeJwtAudienceZod3,
680
+ eventHash: HashZod3,
681
+ jwtId: WakeJwtIdZod2,
682
+ publisher: NormalizedAddressZod3,
683
+ retainJwtUntil: NonnegativeJsonSafeIntegerZod3,
684
+ retainWakeUntil: NonnegativeJsonSafeIntegerZod3,
685
+ wakeId: WakeIdZod2
686
+ });
687
+ function createBestEffortSecurityState() {
688
+ return {
689
+ highestNowSeconds: 0,
690
+ jwtRecords: [],
691
+ wakeRecords: []
692
+ };
693
+ }
694
+ function securityConflict(attempt, priorJwt, priorWake, jwtCount, wakeCount, maxRecords) {
695
+ if (priorJwt !== void 0 && (priorJwt.eventHash !== attempt.eventHash || priorJwt.wakeId !== attempt.wakeId))
696
+ return "jwt-id-collision";
697
+ if (priorWake !== void 0 && priorWake.eventHash !== attempt.eventHash)
698
+ return "wake-id-collision";
699
+ if (priorJwt === void 0 && jwtCount >= maxRecords || priorWake === void 0 && wakeCount >= maxRecords)
700
+ return "capacity-exceeded";
701
+ }
702
+ function validateTransaction(input, maxRecords) {
703
+ const attempt = BestEffortAdmissionAttemptZod.parse(input.attempt);
704
+ const wake = AdmittedWakeZod.parse(input.wake);
705
+ const nowSeconds = NonnegativeJsonSafeIntegerZod3.parse(input.nowSeconds);
706
+ 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)
707
+ throw new TypeError("Best Effort transaction attempt does not match admitted hint");
708
+ if (!Number.isSafeInteger(maxRecords) || maxRecords < 1 || maxRecords > 65536) throw new TypeError("Invalid finite security capacity");
709
+ return { attempt, nowSeconds };
710
+ }
711
+ function commitBestEffortSecurity(state, input, maxRecords) {
712
+ state = parseBestEffortSecurityState(state);
713
+ const { attempt, nowSeconds } = validateTransaction(input, maxRecords);
714
+ if (nowSeconds < state.highestNowSeconds) return { state, result: { kind: "clock-regressed" } };
715
+ const jwtRecords = state.jwtRecords.filter((record) => record.expiresAt > nowSeconds).map((record) => ({ ...record }));
716
+ const wakeRecords = state.wakeRecords.filter((record) => record.acknowledgment.expiresAt > nowSeconds);
717
+ const priorJwt = jwtRecords.find((record) => record.audience === attempt.audience && record.publisher === attempt.publisher && record.jwtId === attempt.jwtId);
718
+ const priorWake = wakeRecords.find((record) => record.audience === attempt.audience && record.publisher === attempt.publisher && record.wakeId === attempt.wakeId);
719
+ const conflict = securityConflict(attempt, priorJwt, priorWake, jwtRecords.length, wakeRecords.length, maxRecords);
720
+ if (conflict !== void 0) return { state, result: { kind: conflict } };
721
+ const acknowledgment = priorWake?.acknowledgment ?? {
722
+ admissionId: JSON.stringify([attempt.audience, attempt.publisher, attempt.wakeId, attempt.eventHash]),
723
+ eventHash: attempt.eventHash,
724
+ wakeId: attempt.wakeId,
725
+ acceptedAt: nowSeconds,
726
+ expiresAt: attempt.retainWakeUntil
727
+ };
728
+ if (priorJwt !== void 0) priorJwt.expiresAt = Math.max(priorJwt.expiresAt, attempt.retainJwtUntil);
729
+ if (priorJwt === void 0) jwtRecords.push({
730
+ audience: attempt.audience,
731
+ publisher: attempt.publisher,
732
+ jwtId: attempt.jwtId,
733
+ wakeId: attempt.wakeId,
734
+ eventHash: attempt.eventHash,
735
+ expiresAt: attempt.retainJwtUntil
736
+ });
737
+ if (priorWake === void 0) wakeRecords.push({
738
+ audience: attempt.audience,
739
+ publisher: attempt.publisher,
740
+ wakeId: attempt.wakeId,
741
+ eventHash: attempt.eventHash,
742
+ acknowledgment
743
+ });
744
+ return {
745
+ state: {
746
+ highestNowSeconds: nowSeconds,
747
+ jwtRecords,
748
+ wakeRecords
749
+ },
750
+ result: { kind: priorWake === void 0 ? "accepted" : "duplicate", acknowledgment }
751
+ };
752
+ }
753
+
333
754
  // src/endpointChallenge.ts
334
755
  import {
335
756
  Base64Url32BytesZod,
@@ -343,7 +764,7 @@ import {
343
764
  generateHandshakeId,
344
765
  MAX_ENDPOINT_CHALLENGE_LIFETIME_SECONDS
345
766
  } from "@xyo-network/event-kit-schemas";
346
- import { PayloadBuilder as PayloadBuilder2 } from "@xyo-network/sdk";
767
+ import { PayloadBuilder as PayloadBuilder4 } from "@xyo-network/sdk";
347
768
  function hashBytes(hash) {
348
769
  return Uint8Array.from({ length: 32 }, (_, index) => Number.parseInt(hash.slice(index * 2, index * 2 + 2), 16));
349
770
  }
@@ -370,7 +791,7 @@ function equalProof(left, right) {
370
791
  }
371
792
  async function endpointChallengeHash(challenge) {
372
793
  const parsed = EndpointChallengeZod.parse(challenge);
373
- return await PayloadBuilder2.dataHash(parsed);
794
+ return await PayloadBuilder4.dataHash(parsed);
374
795
  }
375
796
  function createEndpointChallenge(input) {
376
797
  const lifetimeSeconds = input.lifetimeSeconds ?? MAX_ENDPOINT_CHALLENGE_LIFETIME_SECONDS;
@@ -383,7 +804,7 @@ function createEndpointChallenge(input) {
383
804
  if (input.issuedAt > Number.MAX_SAFE_INTEGER - lifetimeSeconds) {
384
805
  throw new RangeError("Challenge expiry exceeds the JSON safe-integer range");
385
806
  }
386
- const builder = new PayloadBuilder2({ schema: EndpointChallengeSchema });
807
+ const builder = new PayloadBuilder4({ schema: EndpointChallengeSchema });
387
808
  return EndpointChallengeZod.parse(builder.fields({
388
809
  challengeId: generateHandshakeId(),
389
810
  chainId: input.chainId,
@@ -402,7 +823,7 @@ function createEndpointChallenge(input) {
402
823
  }
403
824
  async function createEndpointChallengeResponse(challenge, secret) {
404
825
  const challengeHash = await endpointChallengeHash(challenge);
405
- const builder = new PayloadBuilder2({ schema: EndpointChallengeResponseSchema });
826
+ const builder = new PayloadBuilder4({ schema: EndpointChallengeResponseSchema });
406
827
  return EndpointChallengeResponseZod.parse(builder.fields({
407
828
  challenge: challengeHash,
408
829
  proof: await hmacProof(secret, challengeHash)
@@ -437,7 +858,7 @@ import {
437
858
  EndpointChallengeZod as EndpointChallengeZod2,
438
859
  generateGrantId,
439
860
  generateHandshakeId as generateHandshakeId2,
440
- HashZod as HashZod3,
861
+ HashZod as HashZod5,
441
862
  MAX_SUBSCRIPTION_PLAINTEXT_BYTES as MAX_SUBSCRIPTION_PLAINTEXT_BYTES2,
442
863
  PublisherEncryptionKeyRevocationSchema,
443
864
  PublisherEncryptionKeyRevocationZod,
@@ -448,7 +869,7 @@ import {
448
869
  PublisherServiceSchema,
449
870
  PublisherServiceZod,
450
871
  SOURCE_WAKE_EVENT_KIND,
451
- SOURCE_WAKE_FILTER_PROFILE,
872
+ SOURCE_WAKE_FILTER_PROFILE as SOURCE_WAKE_FILTER_PROFILE2,
452
873
  WAKE_SUBSCRIPTION_HPKE_PROFILE as WAKE_SUBSCRIPTION_HPKE_PROFILE3,
453
874
  WAKE_WALLET_JWT_DELIVERY_PROFILE,
454
875
  WakeGrantObjectSchema,
@@ -465,7 +886,7 @@ import {
465
886
  WakeSubscriptionRevocationSchema,
466
887
  WakeSubscriptionRevocationZod
467
888
  } from "@xyo-network/event-kit-schemas";
468
- import { PayloadBuilder as PayloadBuilder3 } from "@xyo-network/sdk";
889
+ import { PayloadBuilder as PayloadBuilder5 } from "@xyo-network/sdk";
469
890
 
470
891
  // src/wakeSubscriptionHpke.ts
471
892
  import {
@@ -480,8 +901,8 @@ import {
480
901
  canonicalBase64UrlToBytes as canonicalBase64UrlToBytes2,
481
902
  EncryptedWakeSubscriptionZod as EncryptedWakeSubscriptionZod2,
482
903
  HandshakeIdZod,
483
- HashZod as HashZod2,
484
- NormalizedAddressZod as NormalizedAddressZod3,
904
+ HashZod as HashZod4,
905
+ NormalizedAddressZod as NormalizedAddressZod5,
485
906
  WAKE_SUBSCRIPTION_HPKE_PROFILE as WAKE_SUBSCRIPTION_HPKE_PROFILE2,
486
907
  WakeSubscriptionCleartextZod as WakeSubscriptionCleartextZod2
487
908
  } from "@xyo-network/event-kit-schemas";
@@ -490,7 +911,7 @@ import {
490
911
  import {
491
912
  EncryptedWakeSubscriptionZod,
492
913
  MAX_SUBSCRIPTION_PLAINTEXT_BYTES,
493
- NormalizedAddressZod as NormalizedAddressZod2,
914
+ NormalizedAddressZod as NormalizedAddressZod4,
494
915
  WAKE_SUBSCRIPTION_HPKE_PROFILE,
495
916
  WakeSubscriptionCleartextZod,
496
917
  WakeSubscriptionPlaintextZod
@@ -507,7 +928,7 @@ function cleartextFromEncrypted(subscription) {
507
928
  return cleartext;
508
929
  }
509
930
  function wakeSubscriptionAadBytes(subscription, subscriber) {
510
- const parsedSubscriber = NormalizedAddressZod2.parse(subscriber);
931
+ const parsedSubscriber = NormalizedAddressZod4.parse(subscriber);
511
932
  const parsedSubscription = "encryption" in subscription ? cleartextFromEncrypted(EncryptedWakeSubscriptionZod.parse(subscription)) : WakeSubscriptionCleartextZod.parse(subscription);
512
933
  return canonicalJsonBytes({
513
934
  schema: parsedSubscription.schema,
@@ -522,6 +943,7 @@ function wakeSubscriptionAadBytes(subscription, subscriber) {
522
943
  revision: parsedSubscription.revision,
523
944
  supersedes: parsedSubscription.supersedes ?? null,
524
945
  eventKind: parsedSubscription.eventKind,
946
+ deliveryMode: parsedSubscription.deliveryMode,
525
947
  expiresAtBlock: parsedSubscription.expiresAtBlock,
526
948
  profile: WAKE_SUBSCRIPTION_HPKE_PROFILE
527
949
  });
@@ -571,14 +993,14 @@ var WakeSubscriptionDecryptionError = class extends Error {
571
993
  };
572
994
  function parsePublicKey(value) {
573
995
  return {
574
- encryptionKey: HashZod2.parse(value.encryptionKey),
996
+ encryptionKey: HashZod4.parse(value.encryptionKey),
575
997
  keyId: HandshakeIdZod.parse(value.keyId),
576
998
  publicKey: Base64Url32BytesZod2.parse(value.publicKey)
577
999
  };
578
1000
  }
579
1001
  function parsePrivateKey(value) {
580
1002
  return {
581
- encryptionKey: HashZod2.parse(value.encryptionKey),
1003
+ encryptionKey: HashZod4.parse(value.encryptionKey),
582
1004
  keyId: HandshakeIdZod.parse(value.keyId),
583
1005
  privateKey: Base64Url32BytesZod2.parse(value.privateKey)
584
1006
  };
@@ -602,7 +1024,7 @@ async function generateWakeSubscriptionEncryptionKeyPair() {
602
1024
  }
603
1025
  async function encryptWakeSubscription(input) {
604
1026
  const subscription = WakeSubscriptionCleartextZod2.parse(input.subscription);
605
- const subscriber = NormalizedAddressZod3.parse(input.subscriber);
1027
+ const subscriber = NormalizedAddressZod5.parse(input.subscriber);
606
1028
  const publisherKey = parsePublicKey(input.publisherKey);
607
1029
  if (subscription.encryptionKey !== publisherKey.encryptionKey || subscription.keyId !== publisherKey.keyId) {
608
1030
  throw new TypeError("Subscription key reference does not match the supplied publisher key");
@@ -625,7 +1047,7 @@ async function encryptWakeSubscription(input) {
625
1047
  async function decryptWakeSubscription(input) {
626
1048
  try {
627
1049
  const subscription = EncryptedWakeSubscriptionZod2.parse(input.subscription);
628
- const subscriber = NormalizedAddressZod3.parse(input.subscriber);
1050
+ const subscriber = NormalizedAddressZod5.parse(input.subscriber);
629
1051
  const publisherKey = parsePrivateKey(input.publisherKey);
630
1052
  if (subscription.encryptionKey !== publisherKey.encryptionKey || subscription.keyId !== publisherKey.keyId) throw new TypeError("Key reference mismatch");
631
1053
  const recipientKey = await suite.kem.deserializePrivateKey(decoded32(publisherKey.privateKey));
@@ -668,13 +1090,13 @@ function parseHandshakePublicObject(value) {
668
1090
  throw new TypeError("Expected a strict Event Kit handshake public object");
669
1091
  }
670
1092
  async function handshakeObjectHash(object) {
671
- return HashZod3.parse(await PayloadBuilder3.dataHash(parseHandshakePublicObject(object)));
1093
+ return HashZod5.parse(await PayloadBuilder5.dataHash(parseHandshakePublicObject(object)));
672
1094
  }
673
1095
  async function withHandshakeObjectHash(body) {
674
1096
  return Object.freeze({ body, objectHash: await handshakeObjectHash(body) });
675
1097
  }
676
1098
  function createPublisherEncryptionKey(input) {
677
- const builder = new PayloadBuilder3({ schema: PublisherEncryptionKeySchema });
1099
+ const builder = new PayloadBuilder5({ schema: PublisherEncryptionKeySchema });
678
1100
  return PublisherEncryptionKeyZod.parse(builder.fields({
679
1101
  keyId: input.keyId ?? generateHandshakeId2(),
680
1102
  profile: WAKE_SUBSCRIPTION_HPKE_PROFILE3,
@@ -706,7 +1128,7 @@ async function generatePublisherEncryptionKey(input) {
706
1128
  });
707
1129
  }
708
1130
  function createPublisherService(input) {
709
- const builder = new PayloadBuilder3({ schema: PublisherServiceSchema });
1131
+ const builder = new PayloadBuilder5({ schema: PublisherServiceSchema });
710
1132
  return PublisherServiceZod.parse(builder.fields({
711
1133
  authorizationChainId: input.authorizationChainId,
712
1134
  serviceId: input.serviceId ?? generateHandshakeId2(),
@@ -716,9 +1138,11 @@ function createPublisherService(input) {
716
1138
  ...input.expiresAtBlock === void 0 ? {} : { expiresAtBlock: input.expiresAtBlock },
717
1139
  events: input.sources.map((source) => ({
718
1140
  kind: SOURCE_WAKE_EVENT_KIND,
719
- filterProfile: SOURCE_WAKE_FILTER_PROFILE,
1141
+ filterProfile: SOURCE_WAKE_FILTER_PROFILE2,
720
1142
  source
721
1143
  })),
1144
+ deliveryMode: input.deliveryMode,
1145
+ ...input.bestEffort === void 0 ? {} : { bestEffort: input.bestEffort },
722
1146
  deliveryProfile: WAKE_WALLET_JWT_DELIVERY_PROFILE,
723
1147
  challengeProfile: ENDPOINT_CHALLENGE_HMAC_SHA256_PROFILE,
724
1148
  encryptionProfile: WAKE_SUBSCRIPTION_HPKE_PROFILE3,
@@ -733,7 +1157,7 @@ function createPublisherService(input) {
733
1157
  }).build());
734
1158
  }
735
1159
  function createWakeGrant(input) {
736
- const builder = new PayloadBuilder3({ schema: WakeGrantObjectSchema });
1160
+ const builder = new PayloadBuilder5({ schema: WakeGrantObjectSchema });
737
1161
  return WakeGrantObjectZod.parse(builder.fields({
738
1162
  grantId: input.grantId ?? generateGrantId(),
739
1163
  deploymentId: input.deploymentId,
@@ -748,14 +1172,16 @@ function createWakeSubscriptionPlaintext(input) {
748
1172
  globalThis.crypto.getRandomValues(secretBytes);
749
1173
  }
750
1174
  const challengeSecret = input.challengeSecret ?? bytesToCanonicalBase64Url3(secretBytes);
751
- const builder = new PayloadBuilder3({ schema: WakeSubscriptionPlaintextSchema });
1175
+ const builder = new PayloadBuilder5({ schema: WakeSubscriptionPlaintextSchema });
752
1176
  return WakeSubscriptionPlaintextZod2.parse(builder.fields({
753
1177
  endpoint: input.endpoint,
754
1178
  audience: input.audience,
755
1179
  deploymentId: input.deploymentId,
756
1180
  queueId: input.queueId ?? null,
757
1181
  filter: {
758
- schema: SOURCE_WAKE_FILTER_PROFILE,
1182
+ schema: SOURCE_WAKE_FILTER_PROFILE2,
1183
+ deliveryMode: input.deliveryMode,
1184
+ ...input.bestEffort === void 0 ? {} : { bestEffort: input.bestEffort },
759
1185
  source: input.source,
760
1186
  fromPosition: input.fromPosition,
761
1187
  maxPositionsPerWake: input.maxPositionsPerWake
@@ -767,7 +1193,7 @@ function createWakeSubscriptionPlaintext(input) {
767
1193
  }).build());
768
1194
  }
769
1195
  async function createEncryptedWakeSubscription(input) {
770
- const builder = new PayloadBuilder3({ schema: EncryptedWakeSubscriptionSchema });
1196
+ const builder = new PayloadBuilder5({ schema: EncryptedWakeSubscriptionSchema });
771
1197
  const cleartext = WakeSubscriptionCleartextZod3.parse(builder.fields({
772
1198
  subscriptionId: input.subscriptionId ?? generateHandshakeId2(),
773
1199
  revision: input.revision ?? 1,
@@ -779,6 +1205,7 @@ async function createEncryptedWakeSubscription(input) {
779
1205
  keyId: input.publisherKey.keyId,
780
1206
  grant: input.grant,
781
1207
  eventKind: SOURCE_WAKE_EVENT_KIND,
1208
+ deliveryMode: input.plaintext.filter.deliveryMode,
782
1209
  expiresAtBlock: input.expiresAtBlock
783
1210
  }).build());
784
1211
  return await encryptWakeSubscription({
@@ -789,7 +1216,7 @@ async function createEncryptedWakeSubscription(input) {
789
1216
  });
790
1217
  }
791
1218
  function createWakeSubscriptionAcceptance(input) {
792
- const builder = new PayloadBuilder3({ schema: WakeSubscriptionAcceptanceSchema });
1219
+ const builder = new PayloadBuilder5({ schema: WakeSubscriptionAcceptanceSchema });
793
1220
  return WakeSubscriptionAcceptanceZod.parse(builder.fields({
794
1221
  challenge: input.challenge,
795
1222
  encryptionKey: input.encryptionKey,
@@ -800,45 +1227,45 @@ function createWakeSubscriptionAcceptance(input) {
800
1227
  }).build());
801
1228
  }
802
1229
  function createPublisherServiceRevocation(service) {
803
- const builder = new PayloadBuilder3({ schema: PublisherServiceRevocationSchema });
1230
+ const builder = new PayloadBuilder5({ schema: PublisherServiceRevocationSchema });
804
1231
  return PublisherServiceRevocationZod.parse(builder.fields({ service }).build());
805
1232
  }
806
1233
  function createPublisherEncryptionKeyRevocation(key) {
807
- const builder = new PayloadBuilder3({ schema: PublisherEncryptionKeyRevocationSchema });
1234
+ const builder = new PayloadBuilder5({ schema: PublisherEncryptionKeyRevocationSchema });
808
1235
  return PublisherEncryptionKeyRevocationZod.parse(builder.fields({ key }).build());
809
1236
  }
810
1237
  function createWakeGrantRevocation(grant) {
811
- const builder = new PayloadBuilder3({ schema: WakeGrantRevocationSchema });
1238
+ const builder = new PayloadBuilder5({ schema: WakeGrantRevocationSchema });
812
1239
  return WakeGrantRevocationZod.parse(builder.fields({ grant }).build());
813
1240
  }
814
1241
  function createWakeSubscriptionRevocation(subscription) {
815
- const builder = new PayloadBuilder3({ schema: WakeSubscriptionRevocationSchema });
1242
+ const builder = new PayloadBuilder5({ schema: WakeSubscriptionRevocationSchema });
816
1243
  return WakeSubscriptionRevocationZod.parse(builder.fields({ subscription }).build());
817
1244
  }
818
1245
  function createWakeSubscriptionAcceptanceRevocation(acceptance) {
819
- const builder = new PayloadBuilder3({ schema: WakeSubscriptionAcceptanceRevocationSchema });
1246
+ const builder = new PayloadBuilder5({ schema: WakeSubscriptionAcceptanceRevocationSchema });
820
1247
  return WakeSubscriptionAcceptanceRevocationZod.parse(builder.fields({ acceptance }).build());
821
1248
  }
822
1249
 
823
1250
  // src/lifecycleSelection.ts
824
- function isNonnegativeSafeInteger2(value) {
1251
+ function isNonnegativeSafeInteger3(value) {
825
1252
  return Number.isSafeInteger(value) && value >= 0;
826
1253
  }
827
1254
  function isVisible(claimedAtBlock, finalizedHead) {
828
- return isNonnegativeSafeInteger2(claimedAtBlock) && claimedAtBlock <= finalizedHead;
1255
+ return isNonnegativeSafeInteger3(claimedAtBlock) && claimedAtBlock <= finalizedHead;
829
1256
  }
830
1257
  function isRevoked(revokedAtBlock, finalizedHead) {
831
1258
  return revokedAtBlock !== void 0 && revokedAtBlock <= finalizedHead;
832
1259
  }
833
1260
  function isUnrevokedEvidence(claimedAtBlock, revokedAtBlock, finalizedHead) {
834
- return isVisible(claimedAtBlock, finalizedHead) && (revokedAtBlock === void 0 || isNonnegativeSafeInteger2(revokedAtBlock) && revokedAtBlock >= claimedAtBlock) && !isRevoked(revokedAtBlock, finalizedHead);
1261
+ return isVisible(claimedAtBlock, finalizedHead) && (revokedAtBlock === void 0 || isNonnegativeSafeInteger3(revokedAtBlock) && revokedAtBlock >= claimedAtBlock) && !isRevoked(revokedAtBlock, finalizedHead);
835
1262
  }
836
1263
  function isInsideExclusiveWindow(finalizedHead, validFromBlock, expiresAtBlock) {
837
- return isNonnegativeSafeInteger2(validFromBlock) && finalizedHead >= validFromBlock && (expiresAtBlock === void 0 || isNonnegativeSafeInteger2(expiresAtBlock) && expiresAtBlock > validFromBlock && finalizedHead < expiresAtBlock);
1264
+ return isNonnegativeSafeInteger3(validFromBlock) && finalizedHead >= validFromBlock && (expiresAtBlock === void 0 || isNonnegativeSafeInteger3(expiresAtBlock) && expiresAtBlock > validFromBlock && finalizedHead < expiresAtBlock);
838
1265
  }
839
1266
  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;
1267
+ if (!Number.isSafeInteger(revision.revision) || revision.revision < 1 || !isNonnegativeSafeInteger3(revision.claimedAtBlock)) return false;
1268
+ if (revision.revokedAtBlock !== void 0 && (!isNonnegativeSafeInteger3(revision.revokedAtBlock) || revision.revokedAtBlock < revision.claimedAtBlock)) return false;
842
1269
  return revision.revision === 1 ? revision.supersedes === void 0 : revision.supersedes !== void 0;
843
1270
  }
844
1271
  function resolveLinearChain(revisions, finalizedHead) {
@@ -933,7 +1360,7 @@ function acceptedSubscription(subscription, input) {
933
1360
  const acceptances = acceptanceFor(subscription, input);
934
1361
  if (acceptances.length > 1) return "ambiguous";
935
1362
  const acceptance = acceptances[0];
936
- if (acceptance === void 0 || !isNonnegativeSafeInteger2(acceptance.acceptanceBlock)) return void 0;
1363
+ if (acceptance === void 0 || !isNonnegativeSafeInteger3(acceptance.acceptanceBlock)) return void 0;
937
1364
  if (!Number.isSafeInteger(subscription.expiresAtBlock) || subscription.expiresAtBlock < 1) return void 0;
938
1365
  if (acceptance.acceptanceBlock <= subscription.claimedAtBlock || acceptance.acceptanceBlock >= Number.MAX_SAFE_INTEGER) return void 0;
939
1366
  const activeFromBlock = acceptance.acceptanceBlock + 1;
@@ -1200,43 +1627,20 @@ function createMemoryWakeQueue() {
1200
1627
 
1201
1628
  // src/publisherSubscription.ts
1202
1629
  import {
1203
- ChainIdZod,
1630
+ ChainIdZod as ChainIdZod2,
1204
1631
  EncryptedWakeSubscriptionZod as EncryptedWakeSubscriptionZod4,
1205
1632
  EndpointChallengeZod as EndpointChallengeZod3,
1206
- HashZod as HashZod4,
1633
+ HashZod as HashZod6,
1207
1634
  hasMatchingHandshakeProfile,
1208
- NormalizedAddressZod as NormalizedAddressZod4,
1635
+ NormalizedAddressZod as NormalizedAddressZod6,
1209
1636
  PublisherEncryptionKeyZod as PublisherEncryptionKeyZod2,
1210
1637
  PublisherServiceZod as PublisherServiceZod2,
1211
1638
  WakeGrantObjectZod as WakeGrantObjectZod2
1212
1639
  } 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
1640
  function rejectPublisherSubscription(reason) {
1237
1641
  return Object.freeze({ ok: false, reason });
1238
1642
  }
1239
- function isNonnegativeSafeInteger3(value) {
1643
+ function isNonnegativeSafeInteger4(value) {
1240
1644
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
1241
1645
  }
1242
1646
  function isActiveAt(value, block) {
@@ -1253,7 +1657,7 @@ function hasExactQueueScope(grant, plaintext) {
1253
1657
  return plaintext.deploymentId === grant.deploymentId && plaintext.queueId === grantQueue;
1254
1658
  }
1255
1659
  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;
1660
+ 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
1661
  }
1258
1662
  function hasExactKeyBinding(service, key, subscription) {
1259
1663
  return service.encryptionKeys.includes(key.objectHash) && service.preferredEncryptionKey === key.objectHash && key.body.keyId === subscription.keyId && key.objectHash === subscription.encryptionKey;
@@ -1264,22 +1668,22 @@ function expiryWithinServicePolicy(subscription, service, tupleBlock) {
1264
1668
  function parsePublisherSubscriptionCandidate(candidate) {
1265
1669
  try {
1266
1670
  return {
1267
- chainId: ChainIdZod.parse(candidate.chainId),
1671
+ chainId: ChainIdZod2.parse(candidate.chainId),
1268
1672
  grant: WakeGrantObjectZod2.parse(candidate.grant.body),
1269
1673
  key: PublisherEncryptionKeyZod2.parse(candidate.key.body),
1270
- publisher: NormalizedAddressZod4.parse(candidate.publisher),
1674
+ publisher: NormalizedAddressZod6.parse(candidate.publisher),
1271
1675
  service: PublisherServiceZod2.parse(candidate.service.body),
1272
- subscriber: NormalizedAddressZod4.parse(candidate.subscriber),
1676
+ subscriber: NormalizedAddressZod6.parse(candidate.subscriber),
1273
1677
  subscription: EncryptedWakeSubscriptionZod4.parse(candidate.subscription.body),
1274
- tupleObjectHashes: candidate.tupleObjectHashes.map((hash) => HashZod4.parse(hash)),
1275
- tupleSubject: NormalizedAddressZod4.parse(candidate.tupleSubject)
1678
+ tupleObjectHashes: candidate.tupleObjectHashes.map((hash) => HashZod6.parse(hash)),
1679
+ tupleSubject: NormalizedAddressZod6.parse(candidate.tupleSubject)
1276
1680
  };
1277
1681
  } catch {
1278
1682
  return void 0;
1279
1683
  }
1280
1684
  }
1281
1685
  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);
1686
+ 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
1687
  }
1284
1688
  async function bodyHashesAreValid(candidate, parsed) {
1285
1689
  const canonicalHashes = await Promise.all([
@@ -1397,10 +1801,10 @@ async function issuePublisherEndpointChallenge(input) {
1397
1801
  }
1398
1802
  }
1399
1803
  async function beginPublisherEndpointChallengeAttempt(input) {
1400
- if (!isNonnegativeSafeInteger3(input.nowSeconds)) return { kind: "store-invalid" };
1804
+ if (!isNonnegativeSafeInteger4(input.nowSeconds)) return { kind: "store-invalid" };
1401
1805
  try {
1402
1806
  const result = await input.store.beginAttempt({
1403
- challengeHash: HashZod4.parse(input.challengeHash),
1807
+ challengeHash: HashZod6.parse(input.challengeHash),
1404
1808
  nowSeconds: input.nowSeconds
1405
1809
  });
1406
1810
  if (!["already-attempted", "expired", "missing", "started", "verified"].includes(result.kind)) {
@@ -1409,7 +1813,7 @@ async function beginPublisherEndpointChallengeAttempt(input) {
1409
1813
  if (result.kind !== "missing" && !await challengeRecordMatches(
1410
1814
  result.record,
1411
1815
  result.record.challenge,
1412
- HashZod4.parse(input.challengeHash)
1816
+ HashZod6.parse(input.challengeHash)
1413
1817
  )) return { kind: "store-invalid" };
1414
1818
  if ((result.kind === "started" || result.kind === "already-attempted") && result.record.state !== "attempted") return { kind: "store-invalid" };
1415
1819
  return result.kind === "verified" && result.record.state !== "verified" ? { kind: "store-invalid" } : result;
@@ -1435,7 +1839,7 @@ async function completePublisherEndpointChallenge(input) {
1435
1839
  if (!endpointChallengeMatchesPublisherContext(input.challenge, input.subscription.context)) {
1436
1840
  return completeFailure("challenge-context-invalid");
1437
1841
  }
1438
- if (!isNonnegativeSafeInteger3(input.nowSeconds)) return completeFailure("challenge-expired");
1842
+ if (!isNonnegativeSafeInteger4(input.nowSeconds)) return completeFailure("challenge-expired");
1439
1843
  const insideDecisionWindow = input.challenge.issuedAt <= input.nowSeconds && input.nowSeconds < input.challenge.expiresAt;
1440
1844
  const proofInput = {
1441
1845
  challenge: input.challenge,
@@ -1485,33 +1889,33 @@ async function completePublisherEndpointChallenge(input) {
1485
1889
  }
1486
1890
  function filterWithinServicePolicy(service, plaintext) {
1487
1891
  const filter = plaintext.filter;
1488
- return filter.maxPositionsPerWake <= service.limits.maxPositionsPerWake && service.events.some((event) => wakeSourcesEqual(event.source, filter.source));
1892
+ 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
1893
  }
1490
1894
 
1491
1895
  // src/sourceWakeActivation.ts
1492
1896
  import {
1493
- ChainIdZod as ChainIdZod2,
1494
- DeploymentIdZod as DeploymentIdZod2,
1495
- HashZod as HashZod5,
1496
- NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod2,
1497
- NormalizedAddressZod as NormalizedAddressZod5,
1498
- PositiveJsonSafeIntegerZod
1897
+ ChainIdZod as ChainIdZod3,
1898
+ DeploymentIdZod as DeploymentIdZod3,
1899
+ HashZod as HashZod7,
1900
+ NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod4,
1901
+ NormalizedAddressZod as NormalizedAddressZod7,
1902
+ PositiveJsonSafeIntegerZod as PositiveJsonSafeIntegerZod2
1499
1903
  } 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
1904
+ import { z as z4 } from "zod";
1905
+ var SourceWakeSubscriptionActivationZod = z4.strictObject({
1906
+ atBlock: NonnegativeJsonSafeIntegerZod4,
1907
+ activeFromBlock: NonnegativeJsonSafeIntegerZod4,
1908
+ expiresAtBlock: PositiveJsonSafeIntegerZod2,
1909
+ authorizationChainId: ChainIdZod3,
1910
+ controller: NormalizedAddressZod7,
1911
+ publisher: NormalizedAddressZod7,
1912
+ deploymentId: DeploymentIdZod3,
1913
+ queueId: z4.string().min(1).max(255).optional(),
1914
+ sourceHash: HashZod7,
1915
+ filterHash: HashZod7,
1916
+ subscriptionHash: HashZod7,
1917
+ serviceHash: HashZod7,
1918
+ grantHash: HashZod7
1515
1919
  }).refine(
1516
1920
  (value) => value.activeFromBlock <= value.atBlock && value.atBlock < value.expiresAtBlock,
1517
1921
  "Subscription must be active at the exact finalized authorization snapshot"
@@ -1519,17 +1923,17 @@ var SourceWakeSubscriptionActivationZod = z2.strictObject({
1519
1923
 
1520
1924
  // src/statementGraphWakeGrantAuthorizer.ts
1521
1925
  import {
1522
- ChainIdZod as ChainIdZod3,
1523
- WakeSourceZod as WakeSourceZod2
1926
+ ChainIdZod as ChainIdZod4,
1927
+ WakeSourceZod as WakeSourceZod3
1524
1928
  } from "@xyo-network/event-kit-schemas";
1525
1929
 
1526
1930
  // src/grantEvidence.ts
1527
1931
  import {
1528
1932
  asWakeGrantObject,
1529
1933
  asWakeGrantRevocation,
1530
- DeploymentIdZod as DeploymentIdZod3,
1531
- HashZod as HashZod6,
1532
- NormalizedAddressZod as NormalizedAddressZod6,
1934
+ DeploymentIdZod as DeploymentIdZod4,
1935
+ HashZod as HashZod8,
1936
+ NormalizedAddressZod as NormalizedAddressZod8,
1533
1937
  WakeGrantObjectSchema as WakeGrantObjectSchema3,
1534
1938
  WakeGrantRevocationSchema as WakeGrantRevocationSchema3
1535
1939
  } from "@xyo-network/event-kit-schemas";
@@ -1585,7 +1989,7 @@ async function readRevocations(viewer, controller, subject, atBlock) {
1585
1989
  for (const claim of claims) {
1586
1990
  if (!isLiveClaim(claim, controller, subject, WakeGrantRevocationSchema3, atBlock)) continue;
1587
1991
  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;
1992
+ if (object?.assurance !== "resolved" || object.declaredSchema !== WakeGrantRevocationSchema3 || object.objectHash !== claim.objectHash || !isRecord3(object.params) || Object.hasOwn(object.params, "schema")) continue;
1589
1993
  const revocation = asWakeGrantRevocation({ ...object.params, schema: object.declaredSchema });
1590
1994
  if (revocation !== void 0) revoked.add(revocation.grant);
1591
1995
  }
@@ -1617,55 +2021,55 @@ async function resolveEvidence(viewer, claim, request, atBlock, revokedGrantHash
1617
2021
  });
1618
2022
  }
1619
2023
  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;
2024
+ 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
2025
  }
1622
2026
  function parseExactGrantObject(object, expectedObjectHash) {
1623
2027
  if (object?.assurance !== "resolved") return void 0;
1624
2028
  if (object.declaredSchema !== WakeGrantObjectSchema3 || object.objectHash !== expectedObjectHash) return void 0;
1625
- if (!isRecord2(object.params) || Object.hasOwn(object.params, "schema")) return void 0;
2029
+ if (!isRecord3(object.params) || Object.hasOwn(object.params, "schema")) return void 0;
1626
2030
  return asWakeGrantObject({
1627
2031
  ...object.params,
1628
2032
  schema: object.declaredSchema
1629
2033
  });
1630
2034
  }
1631
2035
  function assertValidRequest(request) {
1632
- if (request.exactGrantHash !== void 0 && !HashZod6.safeParse(request.exactGrantHash).success) {
2036
+ if (request.exactGrantHash !== void 0 && !HashZod8.safeParse(request.exactGrantHash).success) {
1633
2037
  throw new TypeError("Exact grant hash must be canonical lowercase hexadecimal");
1634
2038
  }
1635
- if (!NormalizedAddressZod6.safeParse(request.controller).success) {
2039
+ if (!NormalizedAddressZod8.safeParse(request.controller).success) {
1636
2040
  throw new TypeError("Grant controller must be a normalized address");
1637
2041
  }
1638
- if (!NormalizedAddressZod6.safeParse(request.publisher).success) {
2042
+ if (!NormalizedAddressZod8.safeParse(request.publisher).success) {
1639
2043
  throw new TypeError("Grant publisher must be a normalized address");
1640
2044
  }
1641
- if (!DeploymentIdZod3.safeParse(request.deploymentId).success) {
2045
+ if (!DeploymentIdZod4.safeParse(request.deploymentId).success) {
1642
2046
  throw new TypeError("Grant deploymentId must contain 1 to 255 characters");
1643
2047
  }
1644
2048
  if (request.queueId !== void 0 && (request.queueId.length === 0 || request.queueId.length > 255)) {
1645
2049
  throw new TypeError("Grant queueId must contain 1 to 255 characters when configured");
1646
2050
  }
1647
- if (!isNonnegativeSafeInteger4(request.expectedHead)) {
2051
+ if (!isNonnegativeSafeInteger5(request.expectedHead)) {
1648
2052
  throw new RangeError("Grant expectedHead must be a nonnegative safe integer");
1649
2053
  }
1650
2054
  }
1651
2055
  async function readHead(viewer) {
1652
2056
  const head = await viewer.head();
1653
- if (!isNonnegativeSafeInteger4(head)) {
2057
+ if (!isNonnegativeSafeInteger5(head)) {
1654
2058
  throw new RangeError("Statement Graph head must be a nonnegative safe integer");
1655
2059
  }
1656
2060
  return head;
1657
2061
  }
1658
- function isNonnegativeSafeInteger4(value) {
2062
+ function isNonnegativeSafeInteger5(value) {
1659
2063
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
1660
2064
  }
1661
- function isRecord2(value) {
2065
+ function isRecord3(value) {
1662
2066
  return typeof value === "object" && value !== null && !Array.isArray(value);
1663
2067
  }
1664
2068
 
1665
2069
  // src/statementGraphWakeGrantAuthorizer.ts
1666
2070
  function createStatementGraphWakeGrantAuthorizer(options) {
1667
- const authorizationChainId = ChainIdZod3.parse(options.authorizationChainId);
1668
- const source = WakeSourceZod2.parse(options.source);
2071
+ const authorizationChainId = ChainIdZod4.parse(options.authorizationChainId);
2072
+ const source = WakeSourceZod3.parse(options.source);
1669
2073
  const grants = createDeploymentGrantAuthorizer({ viewer: options.viewer });
1670
2074
  return Object.freeze({
1671
2075
  async authorizePublisher(request) {
@@ -1697,190 +2101,6 @@ function createStatementGraphWakeGrantAuthorizer(options) {
1697
2101
  });
1698
2102
  }
1699
2103
 
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
2104
  // src/wakeQueue.ts
1885
2105
  async function drainQueue(queue, handler) {
1886
2106
  let handled = 0;
@@ -1899,6 +2119,8 @@ async function drainQueue(queue, handler) {
1899
2119
  }
1900
2120
  export {
1901
2121
  AdmittedWakeZod,
2122
+ BestEffortAdmissionAcknowledgmentZod,
2123
+ BestEffortSecurityStateZod,
1902
2124
  EVENT_KIT_MAX_FUTURE_IAT_SECONDS,
1903
2125
  EVENT_KIT_MAX_JWT_LIFETIME_SECONDS,
1904
2126
  EVENT_KIT_REPLAY_RETENTION_SKEW_SECONDS,
@@ -1906,10 +2128,13 @@ export {
1906
2128
  WakeAdmissionRouteZod,
1907
2129
  WakeSubscriptionDecryptionError,
1908
2130
  admitAndEnqueue,
2131
+ admitBestEffortHint,
1909
2132
  beginPublisherEndpointChallengeAttempt,
1910
2133
  canonicalJson,
1911
2134
  canonicalJsonBytes,
2135
+ commitBestEffortSecurity,
1912
2136
  completePublisherEndpointChallenge,
2137
+ createBestEffortSecurityState,
1913
2138
  createEncryptedWakeSubscription,
1914
2139
  createEndpointChallenge,
1915
2140
  createEndpointChallengeResponse,
@@ -1937,6 +2162,7 @@ export {
1937
2162
  handshakeObjectHash,
1938
2163
  issuePublisherEndpointChallenge,
1939
2164
  parseAdmittedWake,
2165
+ parseBestEffortSecurityState,
1940
2166
  resolvePreferredEncryptionKey,
1941
2167
  selectPublisherService,
1942
2168
  selectSubscriptionActivation,