@xyo-network/event-kit-node 0.1.1 → 0.1.2

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 (36) hide show
  1. package/README.md +23 -3
  2. package/dist/node/handshake.mjs +122 -249
  3. package/dist/node/handshake.mjs.map +2 -2
  4. package/dist/node/handshakeClients.d.ts +7 -4
  5. package/dist/node/handshakeClients.d.ts.map +1 -1
  6. package/dist/node/handshakeMutationIntent.d.ts +118 -34
  7. package/dist/node/handshakeMutationIntent.d.ts.map +1 -1
  8. package/dist/node/index.d.ts +1 -0
  9. package/dist/node/index.d.ts.map +1 -1
  10. package/dist/node/index.mjs +1582 -291
  11. package/dist/node/index.mjs.map +4 -4
  12. package/dist/node/runtime/abort.d.ts +6 -0
  13. package/dist/node/runtime/abort.d.ts.map +1 -0
  14. package/dist/node/runtime/boundedFetch.d.ts +20 -0
  15. package/dist/node/runtime/boundedFetch.d.ts.map +1 -0
  16. package/dist/node/runtime/handshakeDiscovery.d.ts +16 -0
  17. package/dist/node/runtime/handshakeDiscovery.d.ts.map +1 -0
  18. package/dist/node/runtime/httpsTransport.d.ts +24 -0
  19. package/dist/node/runtime/httpsTransport.d.ts.map +1 -0
  20. package/dist/node/runtime/publisherRuntime.d.ts +84 -0
  21. package/dist/node/runtime/publisherRuntime.d.ts.map +1 -0
  22. package/dist/node/runtime/restGateway.d.ts +36 -0
  23. package/dist/node/runtime/restGateway.d.ts.map +1 -0
  24. package/dist/node/runtime/sourceSigner.d.ts +41 -0
  25. package/dist/node/runtime/sourceSigner.d.ts.map +1 -0
  26. package/dist/node/runtime/sources.d.ts +35 -0
  27. package/dist/node/runtime/sources.d.ts.map +1 -0
  28. package/dist/node/runtime/sqliteHandshakeStores.d.ts +15 -0
  29. package/dist/node/runtime/sqliteHandshakeStores.d.ts.map +1 -0
  30. package/dist/node/runtime/sqlitePublisherState.d.ts +21 -0
  31. package/dist/node/runtime/sqlitePublisherState.d.ts.map +1 -0
  32. package/dist/node/runtime.d.ts +10 -0
  33. package/dist/node/runtime.d.ts.map +1 -0
  34. package/dist/node/runtime.mjs +1640 -0
  35. package/dist/node/runtime.mjs.map +7 -0
  36. package/package.json +16 -11
@@ -793,6 +793,7 @@ function requireTime(value) {
793
793
  // src/handshakeClients.ts
794
794
  import {
795
795
  beginPublisherEndpointChallengeAttempt,
796
+ canonicalJson,
796
797
  completePublisherEndpointChallenge,
797
798
  createEncryptedWakeSubscription,
798
799
  createPublisherEncryptionKeyRevocation,
@@ -841,10 +842,7 @@ function createSynchronizedEventKitHandshakeReader(options) {
841
842
  function assertIdentity() {
842
843
  const checkpoint = options.replay.snapshot();
843
844
  if (checkpoint.chainId !== chainId || checkpoint.network !== options.session.network || checkpoint.publisher !== publisher) {
844
- throw new EventKitHandshakeClientError(
845
- "identity-mismatch",
846
- "Finalized replay identity does not match the pinned Event Kit network and publisher"
847
- );
845
+ throw new EventKitHandshakeClientError("identity-mismatch", "Finalized replay identity does not match the pinned Event Kit network and publisher");
848
846
  }
849
847
  }
850
848
  return Object.freeze({
@@ -897,10 +895,7 @@ function createEventKitHandshakePublicationPort(transport) {
897
895
  const raw = String(await transport.signer.address());
898
896
  const normalized = NormalizedAddressZod3.parse(normalizeAddress(raw));
899
897
  if (raw !== normalized) {
900
- throw new EventKitHandshakeClientError(
901
- "identity-mismatch",
902
- "XL1 publication signer address is not canonical lowercase hexadecimal without 0x"
903
- );
898
+ throw new EventKitHandshakeClientError("identity-mismatch", "XL1 publication signer address is not canonical lowercase hexadecimal without 0x");
904
899
  }
905
900
  return normalized;
906
901
  }
@@ -916,10 +911,7 @@ function nextBlock(head) {
916
911
  function samplePublisherClock(nowSeconds) {
917
912
  const sampled = nowSeconds();
918
913
  if (!Number.isSafeInteger(sampled) || sampled < 0) {
919
- throw new EventKitHandshakeClientError(
920
- "challenge-rejected",
921
- "Publisher clock must return a nonnegative JSON-safe Unix second"
922
- );
914
+ throw new EventKitHandshakeClientError("challenge-rejected", "Publisher clock must return a nonnegative JSON-safe Unix second");
923
915
  }
924
916
  return sampled;
925
917
  }
@@ -942,37 +934,25 @@ function serviceQuery(reader, snapshot, serviceId) {
942
934
  }
943
935
  function requireService(discovery) {
944
936
  if (!discovery.ok) {
945
- throw new EventKitHandshakeClientError(
946
- "service-unavailable",
947
- `Publisher service is unavailable: ${discovery.reason}`
948
- );
937
+ throw new EventKitHandshakeClientError("service-unavailable", `Publisher service is unavailable: ${discovery.reason}`);
949
938
  }
950
939
  return discovery;
951
940
  }
952
941
  function requireExpectedService(discovery, expected) {
953
942
  const expectedHash = HashZod4.parse(expected);
954
943
  if (discovery.service.objectHash !== expectedHash) {
955
- throw new EventKitHandshakeClientError(
956
- "service-mismatch",
957
- "Currently selected publisher service does not match the expected object hash"
958
- );
944
+ throw new EventKitHandshakeClientError("service-mismatch", "Currently selected publisher service does not match the expected object hash");
959
945
  }
960
946
  return expectedHash;
961
947
  }
962
948
  function assertPublicationIdentity(publication, reader) {
963
949
  if (publication.chainId !== reader.chainId) {
964
- throw new EventKitHandshakeClientError(
965
- "identity-mismatch",
966
- "Publication transport chain ID does not match the synchronized handshake reader"
967
- );
950
+ throw new EventKitHandshakeClientError("identity-mismatch", "Publication transport chain ID does not match the synchronized handshake reader");
968
951
  }
969
952
  }
970
953
  async function assertPublicationSource(publication, expected) {
971
954
  if (await publication.source() !== expected) {
972
- throw new EventKitHandshakeClientError(
973
- "identity-mismatch",
974
- "Publication signer does not match the required handshake Claim source"
975
- );
955
+ throw new EventKitHandshakeClientError("identity-mismatch", "Publication signer does not match the required handshake Claim source");
976
956
  }
977
957
  }
978
958
  function intentLookup(reader, kind, operationId, source) {
@@ -985,15 +965,13 @@ function intentLookup(reader, kind, operationId, source) {
985
965
  };
986
966
  }
987
967
  function mutationIntentError(error) {
988
- return error instanceof HandshakeMutationIntentConflictError ? new EventKitHandshakeClientError("mutation-intent-conflict", error.message) : new EventKitHandshakeClientError(
989
- "mutation-intent-unavailable",
990
- "Handshake mutation intent storage is unavailable or invalid"
991
- );
968
+ return error instanceof HandshakeMutationIntentConflictError ? new EventKitHandshakeClientError("mutation-intent-conflict", error.message) : new EventKitHandshakeClientError("mutation-intent-unavailable", "Handshake mutation intent storage is unavailable or invalid");
992
969
  }
993
970
  async function loadMutationIntent(store, reader, kind, operationId, source, requestFingerprint) {
994
971
  try {
995
972
  const existing = await store.get(intentLookup(reader, kind, operationId, source));
996
- if (existing === void 0) return void 0;
973
+ if (existing === void 0)
974
+ return void 0;
997
975
  if (existing.kind !== kind || existing.requestFingerprint !== requestFingerprint) {
998
976
  throw new HandshakeMutationIntentConflictError();
999
977
  }
@@ -1012,7 +990,8 @@ async function persistMutationIntent(store, intent) {
1012
990
  async function publisherServiceRequestFingerprint(input) {
1013
991
  return await handshakeMutationRequestFingerprint({
1014
992
  expiresAtBlock: input.expiresAtBlock ?? null,
1015
- maxBlocksPerWake: input.maxBlocksPerWake,
993
+ maxPositionsPerWake: input.maxPositionsPerWake,
994
+ sources: input.sources,
1016
995
  maxSubscriptionLifetimeBlocks: input.maxSubscriptionLifetimeBlocks,
1017
996
  validFromBlock: input.validFromBlock ?? null
1018
997
  });
@@ -1021,7 +1000,7 @@ async function publisherRotationRequestFingerprint(input) {
1021
1000
  return await handshakeMutationRequestFingerprint({
1022
1001
  expiresAtBlock: input.expiresAtBlock ?? null,
1023
1002
  keyExpiresAtBlock: input.keyExpiresAtBlock ?? null,
1024
- maxBlocksPerWake: input.maxBlocksPerWake ?? null,
1003
+ maxPositionsPerWake: input.maxPositionsPerWake ?? null,
1025
1004
  maxSubscriptionLifetimeBlocks: input.maxSubscriptionLifetimeBlocks ?? null,
1026
1005
  service: HashZod4.parse(input.service),
1027
1006
  validFromBlock: input.validFromBlock ?? null
@@ -1035,7 +1014,9 @@ async function subscriberSubscriptionRequestFingerprint(input) {
1035
1014
  endpoint: input.endpoint,
1036
1015
  expiresAtBlock: input.expiresAtBlock,
1037
1016
  grantId: input.grantId ?? null,
1038
- maxBlocksPerWake: input.maxBlocksPerWake ?? null,
1017
+ source: input.source,
1018
+ fromPosition: input.fromPosition,
1019
+ maxPositionsPerWake: input.maxPositionsPerWake ?? null,
1039
1020
  queueId: input.queueId ?? null,
1040
1021
  revision: input.revision ?? null,
1041
1022
  subscriptionId: input.subscriptionId ?? null,
@@ -1078,10 +1059,7 @@ function protocolCandidate(reader, discovery, candidate, keys) {
1078
1059
  };
1079
1060
  }
1080
1061
  function validationError(reason) {
1081
- return new EventKitHandshakeClientError(
1082
- "subscription-invalid",
1083
- `Publisher subscription candidate failed validation: ${reason}`
1084
- );
1062
+ return new EventKitHandshakeClientError("subscription-invalid", `Publisher subscription candidate failed validation: ${reason}`);
1085
1063
  }
1086
1064
  function challengeError(result) {
1087
1065
  const reason = result.ok ? "unknown" : result.reason;
@@ -1106,7 +1084,8 @@ function createPublisherHandshakeClient(options) {
1106
1084
  }
1107
1085
  async function resolveCandidate(selector) {
1108
1086
  const found = await candidateDiscovery(selector);
1109
- if (!found.ok) throw new EventKitHandshakeClientError("service-unavailable", `Publisher service is unavailable: ${found.reason}`);
1087
+ if (!found.ok)
1088
+ throw new EventKitHandshakeClientError("service-unavailable", `Publisher service is unavailable: ${found.reason}`);
1110
1089
  const subscriber = NormalizedAddressZod3.parse(selector.subscriber);
1111
1090
  const subscription = selector.subscription === void 0 ? void 0 : HashZod4.parse(selector.subscription);
1112
1091
  const candidates = found.candidates.filter((candidate2) => candidate2.subscriber === subscriber && (subscription === void 0 || candidate2.tuple.subscription.objectHash === subscription));
@@ -1117,10 +1096,9 @@ function createPublisherHandshakeClient(options) {
1117
1096
  throw new EventKitHandshakeClientError("candidate-ambiguous", "More than one finalized acceptance candidate matched");
1118
1097
  }
1119
1098
  const candidate = candidates[0];
1120
- const validated = await validateAndDecryptPublisherSubscription(
1121
- protocolCandidate(options.reader, found, candidate, options.keys)
1122
- );
1123
- if (!validated.ok) throw validationError(validated.reason);
1099
+ const validated = await validateAndDecryptPublisherSubscription(protocolCandidate(options.reader, found, candidate, options.keys));
1100
+ if (!validated.ok)
1101
+ throw validationError(validated.reason);
1124
1102
  return { candidate, validated };
1125
1103
  }
1126
1104
  async function candidateForChallenge(challenge) {
@@ -1129,10 +1107,7 @@ function createPublisherHandshakeClient(options) {
1129
1107
  subscription: challenge.subscription
1130
1108
  });
1131
1109
  if (!endpointChallengeMatchesPublisherContext(challenge, resolved.validated.context)) {
1132
- throw new EventKitHandshakeClientError(
1133
- "challenge-context-invalid",
1134
- "Endpoint challenge does not match the current finalized subscription candidate"
1135
- );
1110
+ throw new EventKitHandshakeClientError("challenge-context-invalid", "Endpoint challenge does not match the current finalized subscription candidate");
1136
1111
  }
1137
1112
  return resolved;
1138
1113
  }
@@ -1222,12 +1197,7 @@ function createPublisherHandshakeClient(options) {
1222
1197
  operationId: intent.operationId,
1223
1198
  response: intent.response
1224
1199
  });
1225
- const reconciliation = await reconcileAcceptance(
1226
- acceptanceHash,
1227
- publication,
1228
- intent.challenge.subscriber,
1229
- intent.challenge.subscription
1230
- );
1200
+ const reconciliation = await reconcileAcceptance(acceptanceHash, publication, intent.challenge.subscriber, intent.challenge.subscription);
1231
1201
  return Object.freeze({
1232
1202
  acceptance: Object.freeze({ body: intent.acceptance, objectHash: acceptanceHash }),
1233
1203
  challenge: intent.operationId,
@@ -1248,10 +1218,7 @@ function createPublisherHandshakeClient(options) {
1248
1218
  publisher: options.reader.publisher
1249
1219
  });
1250
1220
  } catch {
1251
- throw new EventKitHandshakeClientError(
1252
- "key-custody-failed",
1253
- "Publisher encryption private key was not durably retained; publication was not attempted"
1254
- );
1221
+ throw new EventKitHandshakeClientError("key-custody-failed", "Publisher encryption private key was not durably retained; publication was not attempted");
1255
1222
  }
1256
1223
  const [keyHash, serviceHash] = await Promise.all([
1257
1224
  handshakeObjectHash2(intent.encryptionKey),
@@ -1274,19 +1241,19 @@ function createPublisherHandshakeClient(options) {
1274
1241
  const client = {
1275
1242
  async acceptanceCandidates(selector = {}) {
1276
1243
  const found = await candidateDiscovery(selector);
1277
- if (!found.ok || selector.subscription === void 0) return found;
1244
+ if (!found.ok || selector.subscription === void 0)
1245
+ return found;
1278
1246
  const subscription = HashZod4.parse(selector.subscription);
1279
1247
  return Object.freeze({
1280
1248
  ...found,
1281
- candidates: found.candidates.filter(
1282
- (candidate) => candidate.tuple.subscription.objectHash === subscription
1283
- )
1249
+ candidates: found.candidates.filter((candidate) => candidate.tuple.subscription.objectHash === subscription)
1284
1250
  });
1285
1251
  },
1286
1252
  async beginChallenge(input) {
1287
1253
  const challengeHash = HashZod4.parse(input.challengeHash);
1288
1254
  const record = await options.challenges.get(challengeHash);
1289
- if (record === void 0) return { kind: "missing" };
1255
+ if (record === void 0)
1256
+ return { kind: "missing" };
1290
1257
  const resolved = await candidateForChallenge(record.challenge);
1291
1258
  const begun = await beginPublisherEndpointChallengeAttempt({
1292
1259
  challengeHash,
@@ -1321,10 +1288,7 @@ function createPublisherHandshakeClient(options) {
1321
1288
  throw new EventKitHandshakeClientError("challenge-not-found", "Endpoint challenge was not found");
1322
1289
  }
1323
1290
  if (record.challenge.subscriber !== subscriber || record.challenge.subscription !== subscription) {
1324
- throw new EventKitHandshakeClientError(
1325
- "challenge-context-invalid",
1326
- "Stored endpoint challenge does not match the caller expected subscriber and subscription"
1327
- );
1291
+ throw new EventKitHandshakeClientError("challenge-context-invalid", "Stored endpoint challenge does not match the caller expected subscriber and subscription");
1328
1292
  }
1329
1293
  const responseHash = await handshakeObjectHash2(input.response);
1330
1294
  const requestFingerprint = await exactMutationRequestFingerprint({
@@ -1333,20 +1297,10 @@ function createPublisherHandshakeClient(options) {
1333
1297
  subscriber,
1334
1298
  subscription
1335
1299
  });
1336
- const existing = await loadMutationIntent(
1337
- options.intents,
1338
- options.reader,
1339
- "publisher-subscription-accept",
1340
- challengeHash,
1341
- options.reader.publisher,
1342
- requestFingerprint
1343
- );
1300
+ const existing = await loadMutationIntent(options.intents, options.reader, "publisher-subscription-accept", challengeHash, options.reader.publisher, requestFingerprint);
1344
1301
  if (existing !== void 0) {
1345
1302
  if (record.state !== "verified" || record.responseHash !== responseHash) {
1346
- throw new EventKitHandshakeClientError(
1347
- "mutation-intent-conflict",
1348
- "Acceptance intent does not match the durable verified challenge decision"
1349
- );
1303
+ throw new EventKitHandshakeClientError("mutation-intent-conflict", "Acceptance intent does not match the durable verified challenge decision");
1350
1304
  }
1351
1305
  return await publishAcceptanceIntent(existing, "resumed");
1352
1306
  }
@@ -1358,36 +1312,22 @@ function createPublisherHandshakeClient(options) {
1358
1312
  store: options.challenges,
1359
1313
  subscription: resolved.validated
1360
1314
  });
1361
- if (!completed.ok) throw challengeError(completed);
1362
- const concurrentlyPersisted = await loadMutationIntent(
1363
- options.intents,
1364
- options.reader,
1365
- "publisher-subscription-accept",
1366
- challengeHash,
1367
- options.reader.publisher,
1368
- requestFingerprint
1369
- );
1370
- if (concurrentlyPersisted !== void 0) return await publishAcceptanceIntent(concurrentlyPersisted, "resumed");
1315
+ if (!completed.ok)
1316
+ throw challengeError(completed);
1317
+ const concurrentlyPersisted = await loadMutationIntent(options.intents, options.reader, "publisher-subscription-accept", challengeHash, options.reader.publisher, requestFingerprint);
1318
+ if (concurrentlyPersisted !== void 0)
1319
+ return await publishAcceptanceIntent(concurrentlyPersisted, "resumed");
1371
1320
  let current;
1372
1321
  try {
1373
1322
  current = await candidateForChallenge(record.challenge);
1374
1323
  } catch (error) {
1375
- const recovered = await loadMutationIntent(
1376
- options.intents,
1377
- options.reader,
1378
- "publisher-subscription-accept",
1379
- challengeHash,
1380
- options.reader.publisher,
1381
- requestFingerprint
1382
- );
1383
- if (recovered !== void 0) return await publishAcceptanceIntent(recovered, "resumed");
1324
+ const recovered = await loadMutationIntent(options.intents, options.reader, "publisher-subscription-accept", challengeHash, options.reader.publisher, requestFingerprint);
1325
+ if (recovered !== void 0)
1326
+ return await publishAcceptanceIntent(recovered, "resumed");
1384
1327
  throw error;
1385
1328
  }
1386
1329
  if (!endpointChallengeMatchesPublisherContext(record.challenge, current.validated.context)) {
1387
- throw new EventKitHandshakeClientError(
1388
- "challenge-context-invalid",
1389
- "Endpoint challenge candidate changed before acceptance publication"
1390
- );
1330
+ throw new EventKitHandshakeClientError("challenge-context-invalid", "Endpoint challenge candidate changed before acceptance publication");
1391
1331
  }
1392
1332
  const resolvedIntent = await persistMutationIntent(options.intents, {
1393
1333
  acceptance: completed.acceptance,
@@ -1429,42 +1369,35 @@ function createPublisherHandshakeClient(options) {
1429
1369
  async publishService(input) {
1430
1370
  await assertPublicationSource(options.publication, options.reader.publisher);
1431
1371
  if (serviceId !== void 0) {
1432
- throw new EventKitHandshakeClientError(
1433
- "service-root-requires-fresh-id",
1434
- "Root service publication requires an unconfigured client so a fresh serviceId is generated"
1435
- );
1372
+ throw new EventKitHandshakeClientError("service-root-requires-fresh-id", "Root service publication requires an unconfigured client so a fresh serviceId is generated");
1436
1373
  }
1437
1374
  const operationId = HandshakeIdZod3.parse(input.operationId);
1438
1375
  const requestFingerprint = await publisherServiceRequestFingerprint(input);
1439
- const priorIntent = await loadMutationIntent(
1440
- options.intents,
1441
- options.reader,
1442
- "publisher-service-publish",
1443
- operationId,
1444
- options.reader.publisher,
1445
- requestFingerprint
1446
- );
1447
- if (priorIntent !== void 0) return await publishPublisherServiceIntent(priorIntent, "resumed");
1376
+ const priorIntent = await loadMutationIntent(options.intents, options.reader, "publisher-service-publish", operationId, options.reader.publisher, requestFingerprint);
1377
+ if (priorIntent !== void 0)
1378
+ return await publishPublisherServiceIntent(priorIntent, "resumed");
1448
1379
  const current = await snapshot();
1449
1380
  const existing = await discovery(current);
1450
1381
  if (existing.ok) {
1451
- throw new EventKitHandshakeClientError(
1452
- "service-already-exists",
1453
- "An active publisher service already exists; rotate its key to publish a successor"
1454
- );
1382
+ throw new EventKitHandshakeClientError("service-already-exists", "An active publisher service already exists; rotate its key to publish a successor");
1455
1383
  }
1456
1384
  if (existing.reason !== "service-not-found") {
1457
1385
  throw new EventKitHandshakeClientError("service-unavailable", `Cannot declare service: ${existing.reason}`);
1458
1386
  }
1459
1387
  const validFromBlock = input.validFromBlock ?? nextBlock(current.finalizedHead);
1460
1388
  const generated = await generatePublisherEncryptionKey({ validFromBlock });
1461
- const service = createPublisherService({
1389
+ const serviceInput = {
1462
1390
  encryptionKeys: [generated.descriptorHash],
1463
1391
  ...input.expiresAtBlock === void 0 ? {} : { expiresAtBlock: input.expiresAtBlock },
1464
- maxBlocksPerWake: input.maxBlocksPerWake,
1465
1392
  maxSubscriptionLifetimeBlocks: input.maxSubscriptionLifetimeBlocks,
1466
1393
  preferredEncryptionKey: generated.descriptorHash,
1467
1394
  validFromBlock
1395
+ };
1396
+ const service = createPublisherService({
1397
+ ...serviceInput,
1398
+ authorizationChainId: options.reader.chainId,
1399
+ sources: input.sources,
1400
+ maxPositionsPerWake: input.maxPositionsPerWake
1468
1401
  });
1469
1402
  const resolvedIntent = await persistMutationIntent(options.intents, {
1470
1403
  chainId: options.reader.chainId,
@@ -1484,23 +1417,13 @@ function createPublisherHandshakeClient(options) {
1484
1417
  await assertPublicationSource(options.publication, options.reader.publisher);
1485
1418
  const keyHash = HashZod4.parse(key);
1486
1419
  const requestFingerprint = await exactMutationRequestFingerprint({ key: keyHash });
1487
- let resolvedIntent = await loadMutationIntent(
1488
- options.intents,
1489
- options.reader,
1490
- "publisher-encryption-key-retire",
1491
- keyHash,
1492
- options.reader.publisher,
1493
- requestFingerprint
1494
- );
1420
+ let resolvedIntent = await loadMutationIntent(options.intents, options.reader, "publisher-encryption-key-retire", keyHash, options.reader.publisher, requestFingerprint);
1495
1421
  let intentDisposition = "resumed";
1496
1422
  if (resolvedIntent === void 0) {
1497
1423
  const current = await snapshot();
1498
1424
  const selected = requireService(await discovery(current));
1499
1425
  if (selected.key.objectHash !== keyHash || !selected.service.body.encryptionKeys.includes(keyHash) || selected.service.body.preferredEncryptionKey !== keyHash) {
1500
- throw new EventKitHandshakeClientError(
1501
- "key-mismatch",
1502
- "Encryption key does not match the exact current preferred descriptor evidence"
1503
- );
1426
+ throw new EventKitHandshakeClientError("key-mismatch", "Encryption key does not match the exact current preferred descriptor evidence");
1504
1427
  }
1505
1428
  const persisted = await persistMutationIntent(options.intents, {
1506
1429
  chainId: options.reader.chainId,
@@ -1532,14 +1455,7 @@ function createPublisherHandshakeClient(options) {
1532
1455
  await assertPublicationSource(options.publication, options.reader.publisher);
1533
1456
  const service = HashZod4.parse(expectedService);
1534
1457
  const requestFingerprint = await exactMutationRequestFingerprint({ service });
1535
- let resolvedIntent = await loadMutationIntent(
1536
- options.intents,
1537
- options.reader,
1538
- "publisher-service-retire",
1539
- service,
1540
- options.reader.publisher,
1541
- requestFingerprint
1542
- );
1458
+ let resolvedIntent = await loadMutationIntent(options.intents, options.reader, "publisher-service-retire", service, options.reader.publisher, requestFingerprint);
1543
1459
  let intentDisposition = "resumed";
1544
1460
  if (resolvedIntent === void 0) {
1545
1461
  const current = await snapshot();
@@ -1575,15 +1491,9 @@ function createPublisherHandshakeClient(options) {
1575
1491
  await assertPublicationSource(options.publication, options.reader.publisher);
1576
1492
  const operationId = HandshakeIdZod3.parse(input.operationId);
1577
1493
  const requestFingerprint = await publisherRotationRequestFingerprint(input);
1578
- const priorIntent = await loadMutationIntent(
1579
- options.intents,
1580
- options.reader,
1581
- "publisher-service-rotate-key",
1582
- operationId,
1583
- options.reader.publisher,
1584
- requestFingerprint
1585
- );
1586
- if (priorIntent !== void 0) return await publishPublisherServiceIntent(priorIntent, "resumed");
1494
+ const priorIntent = await loadMutationIntent(options.intents, options.reader, "publisher-service-rotate-key", operationId, options.reader.publisher, requestFingerprint);
1495
+ if (priorIntent !== void 0)
1496
+ return await publishPublisherServiceIntent(priorIntent, "resumed");
1587
1497
  const current = await snapshot();
1588
1498
  const selected = requireService(await discovery(current));
1589
1499
  requireExpectedService(selected, input.service);
@@ -1592,16 +1502,21 @@ function createPublisherHandshakeClient(options) {
1592
1502
  ...input.keyExpiresAtBlock === void 0 ? {} : { expiresAtBlock: input.keyExpiresAtBlock },
1593
1503
  validFromBlock
1594
1504
  });
1595
- const service = createPublisherService({
1505
+ const serviceInput = {
1596
1506
  encryptionKeys: [generated.descriptorHash],
1597
1507
  ...input.expiresAtBlock === void 0 ? selected.service.body.expiresAtBlock === void 0 ? {} : { expiresAtBlock: selected.service.body.expiresAtBlock } : { expiresAtBlock: input.expiresAtBlock },
1598
- maxBlocksPerWake: input.maxBlocksPerWake ?? selected.service.body.limits.maxBlocksPerWake,
1599
1508
  maxSubscriptionLifetimeBlocks: input.maxSubscriptionLifetimeBlocks ?? selected.service.body.limits.maxSubscriptionLifetimeBlocks,
1600
1509
  preferredEncryptionKey: generated.descriptorHash,
1601
1510
  revision: selected.service.body.revision + 1,
1602
1511
  serviceId: selected.service.body.serviceId,
1603
1512
  supersedes: HashZod4.parse(selected.service.objectHash),
1604
1513
  validFromBlock
1514
+ };
1515
+ const service = createPublisherService({
1516
+ ...serviceInput,
1517
+ authorizationChainId: options.reader.chainId,
1518
+ sources: selected.service.body.events.map((event) => event.source),
1519
+ maxPositionsPerWake: input.maxPositionsPerWake ?? selected.service.body.limits.maxPositionsPerWake
1605
1520
  });
1606
1521
  const resolvedIntent = await persistMutationIntent(options.intents, {
1607
1522
  chainId: options.reader.chainId,
@@ -1645,9 +1560,7 @@ function createSubscriberHandshakeClient(options) {
1645
1560
  return readySnapshot(await options.reader.sync());
1646
1561
  }
1647
1562
  async function serviceAt(current) {
1648
- return requireService(
1649
- await current.readModel.discoverPublisherService(serviceQuery(options.reader, current, serviceId))
1650
- );
1563
+ return requireService(await current.readModel.discoverPublisherService(serviceQuery(options.reader, current, serviceId)));
1651
1564
  }
1652
1565
  async function lookup(subscriptionId) {
1653
1566
  const current = await options.reader.sync();
@@ -1664,29 +1577,21 @@ function createSubscriberHandshakeClient(options) {
1664
1577
  const revision = input.revision ?? 1;
1665
1578
  if (revision === 1) {
1666
1579
  if (input.supersedes !== void 0) {
1667
- throw new EventKitHandshakeClientError(
1668
- "subscription-invalid",
1669
- "Subscription revision 1 must not declare supersedes"
1670
- );
1580
+ throw new EventKitHandshakeClientError("subscription-invalid", "Subscription revision 1 must not declare supersedes");
1671
1581
  }
1672
- if (input.subscriptionId === void 0) return;
1582
+ if (input.subscriptionId === void 0)
1583
+ return;
1673
1584
  const view2 = await lookupAt(current, input.subscriptionId);
1674
1585
  if (!view2.ok) {
1675
1586
  throw new EventKitHandshakeClientError("service-unavailable", `Cannot validate subscription identity: ${view2.reason}`);
1676
1587
  }
1677
1588
  if (view2.subscriptions.some((item) => item.subscriptionId === input.subscriptionId)) {
1678
- throw new EventKitHandshakeClientError(
1679
- "subscription-invalid",
1680
- "Subscription revision 1 would duplicate an existing finalized stable identity"
1681
- );
1589
+ throw new EventKitHandshakeClientError("subscription-invalid", "Subscription revision 1 would duplicate an existing finalized stable identity");
1682
1590
  }
1683
1591
  return;
1684
1592
  }
1685
1593
  if (input.subscriptionId === void 0 || input.supersedes === void 0) {
1686
- throw new EventKitHandshakeClientError(
1687
- "subscription-invalid",
1688
- "A replacement subscription requires subscriptionId and supersedes"
1689
- );
1594
+ throw new EventKitHandshakeClientError("subscription-invalid", "A replacement subscription requires subscriptionId and supersedes");
1690
1595
  }
1691
1596
  const view = await lookupAt(current, input.subscriptionId);
1692
1597
  if (!view.ok) {
@@ -1694,24 +1599,15 @@ function createSubscriberHandshakeClient(options) {
1694
1599
  }
1695
1600
  const lifecycles = view.subscriptions.filter((item) => item.subscriptionId === input.subscriptionId);
1696
1601
  if (lifecycles.length !== 1) {
1697
- throw new EventKitHandshakeClientError(
1698
- "subscription-invalid",
1699
- "Replacement subscription does not resolve one finalized stable identity"
1700
- );
1602
+ throw new EventKitHandshakeClientError("subscription-invalid", "Replacement subscription does not resolve one finalized stable identity");
1701
1603
  }
1702
1604
  if (lifecycles[0].state === "rejected") {
1703
- throw new EventKitHandshakeClientError(
1704
- "subscription-invalid",
1705
- "Replacement subscription cannot extend a rejected finalized revision chain"
1706
- );
1605
+ throw new EventKitHandshakeClientError("subscription-invalid", "Replacement subscription cannot extend a rejected finalized revision chain");
1707
1606
  }
1708
1607
  const highestRevision = Math.max(...lifecycles[0].tuples.map((tuple) => tuple.subscription.body.revision));
1709
1608
  const latest = lifecycles[0].tuples.filter((tuple) => tuple.subscription.body.revision === highestRevision);
1710
1609
  if (latest.length !== 1 || revision !== highestRevision + 1 || latest[0].subscription.objectHash !== input.supersedes) {
1711
- throw new EventKitHandshakeClientError(
1712
- "subscription-invalid",
1713
- "Replacement subscription revision and supersedes must extend the exact finalized chain tip"
1714
- );
1610
+ throw new EventKitHandshakeClientError("subscription-invalid", "Replacement subscription revision and supersedes must extend the exact finalized chain tip");
1715
1611
  }
1716
1612
  }
1717
1613
  async function publishSubscriberSubscriptionIntent(intent, intentDisposition) {
@@ -1740,23 +1636,13 @@ function createSubscriberHandshakeClient(options) {
1740
1636
  await assertPublicationSource(options.publication, subscriber);
1741
1637
  const grantHash = HashZod4.parse(grant);
1742
1638
  const requestFingerprint = await exactMutationRequestFingerprint({ grant: grantHash });
1743
- let resolvedIntent = await loadMutationIntent(
1744
- options.intents,
1745
- options.reader,
1746
- "subscriber-grant-revoke",
1747
- grantHash,
1748
- subscriber,
1749
- requestFingerprint
1750
- );
1639
+ let resolvedIntent = await loadMutationIntent(options.intents, options.reader, "subscriber-grant-revoke", grantHash, subscriber, requestFingerprint);
1751
1640
  let intentDisposition = "resumed";
1752
1641
  if (resolvedIntent === void 0) {
1753
1642
  const current = await snapshot();
1754
1643
  const view = await current.readModel.subscriber({ ...serviceQuery(options.reader, current, serviceId), subscriber });
1755
1644
  if (!view.ok || !tupleHashes(view).grants.has(grantHash)) {
1756
- throw new EventKitHandshakeClientError(
1757
- "subscription-not-found",
1758
- "Grant is not present in this subscriber finalization view"
1759
- );
1645
+ throw new EventKitHandshakeClientError("subscription-not-found", "Grant is not present in this subscriber finalization view");
1760
1646
  }
1761
1647
  const persisted = await persistMutationIntent(options.intents, {
1762
1648
  chainId: options.reader.chainId,
@@ -1789,23 +1675,13 @@ function createSubscriberHandshakeClient(options) {
1789
1675
  await assertPublicationSource(options.publication, subscriber);
1790
1676
  const subscriptionHash = HashZod4.parse(subscription);
1791
1677
  const requestFingerprint = await exactMutationRequestFingerprint({ subscription: subscriptionHash });
1792
- let resolvedIntent = await loadMutationIntent(
1793
- options.intents,
1794
- options.reader,
1795
- "subscriber-subscription-revoke",
1796
- subscriptionHash,
1797
- subscriber,
1798
- requestFingerprint
1799
- );
1678
+ let resolvedIntent = await loadMutationIntent(options.intents, options.reader, "subscriber-subscription-revoke", subscriptionHash, subscriber, requestFingerprint);
1800
1679
  let intentDisposition = "resumed";
1801
1680
  if (resolvedIntent === void 0) {
1802
1681
  const current = await snapshot();
1803
1682
  const view = await current.readModel.subscriber({ ...serviceQuery(options.reader, current, serviceId), subscriber });
1804
1683
  if (!view.ok || !tupleHashes(view).subscriptions.has(subscriptionHash)) {
1805
- throw new EventKitHandshakeClientError(
1806
- "subscription-not-found",
1807
- "Subscription is not present in this subscriber finalization view"
1808
- );
1684
+ throw new EventKitHandshakeClientError("subscription-not-found", "Subscription is not present in this subscriber finalization view");
1809
1685
  }
1810
1686
  const persisted = await persistMutationIntent(options.intents, {
1811
1687
  chainId: options.reader.chainId,
@@ -1842,19 +1718,18 @@ function createSubscriberHandshakeClient(options) {
1842
1718
  const subscriptionHash = HashZod4.parse(subscription);
1843
1719
  const current = await options.reader.sync();
1844
1720
  const view = await lookupAt(current);
1845
- if (!view.ok) return view;
1721
+ if (!view.ok)
1722
+ return view;
1846
1723
  const matches = view.subscriptions.flatMap((lifecycle) => lifecycle.tuples.filter((tuple) => tuple.subscription.objectHash === subscriptionHash).map((tuple) => ({ lifecycle, tuple })));
1847
1724
  if (matches.length > 1) {
1848
- throw new EventKitHandshakeClientError(
1849
- "subscription-invalid",
1850
- "Exact subscription hash appears more than once in the subscriber view"
1851
- );
1725
+ throw new EventKitHandshakeClientError("subscription-invalid", "Exact subscription hash appears more than once in the subscriber view");
1852
1726
  }
1853
- if (matches.length === 0) return Object.freeze({
1854
- found: false,
1855
- lookup: view,
1856
- ok: true
1857
- });
1727
+ if (matches.length === 0)
1728
+ return Object.freeze({
1729
+ found: false,
1730
+ lookup: view,
1731
+ ok: true
1732
+ });
1858
1733
  return Object.freeze({
1859
1734
  found: true,
1860
1735
  lifecycle: matches[0].lifecycle,
@@ -1867,36 +1742,22 @@ function createSubscriberHandshakeClient(options) {
1867
1742
  await assertPublicationSource(options.publication, subscriber);
1868
1743
  const operationId = HandshakeIdZod3.parse(input.operationId);
1869
1744
  const requestFingerprint = await subscriberSubscriptionRequestFingerprint(input);
1870
- const priorIntent = await loadMutationIntent(
1871
- options.intents,
1872
- options.reader,
1873
- "subscriber-subscription-subscribe",
1874
- operationId,
1875
- subscriber,
1876
- requestFingerprint
1877
- );
1878
- if (priorIntent !== void 0) return await publishSubscriberSubscriptionIntent(priorIntent, "resumed");
1745
+ const priorIntent = await loadMutationIntent(options.intents, options.reader, "subscriber-subscription-subscribe", operationId, subscriber, requestFingerprint);
1746
+ if (priorIntent !== void 0)
1747
+ return await publishSubscriberSubscriptionIntent(priorIntent, "resumed");
1879
1748
  const current = await snapshot();
1880
1749
  const selected = await serviceAt(current);
1881
1750
  if (input.expiresAtBlock - current.finalizedHead <= 3) {
1882
- throw new EventKitHandshakeClientError(
1883
- "subscription-invalid",
1884
- "Requested subscription expiry must leave finalized blocks for tuple publication, acceptance, and activation"
1885
- );
1751
+ throw new EventKitHandshakeClientError("subscription-invalid", "Requested subscription expiry must leave finalized blocks for tuple publication, acceptance, and activation");
1886
1752
  }
1887
1753
  await validateRevision(current, input);
1888
- const maxBlocksPerWake = input.maxBlocksPerWake ?? selected.service.body.limits.maxBlocksPerWake;
1889
- if (maxBlocksPerWake > selected.service.body.limits.maxBlocksPerWake) {
1890
- throw new EventKitHandshakeClientError(
1891
- "subscription-invalid",
1892
- "Requested maxBlocksPerWake exceeds the selected publisher service limit"
1893
- );
1754
+ const maximum = selected.service.body.limits.maxPositionsPerWake;
1755
+ const maxPositionsPerWake = input.maxPositionsPerWake ?? maximum;
1756
+ if (maxPositionsPerWake > maximum) {
1757
+ throw new EventKitHandshakeClientError("subscription-invalid", "Requested maxPositionsPerWake exceeds the selected publisher service limit");
1894
1758
  }
1895
1759
  if (input.expiresAtBlock > current.finalizedHead + selected.service.body.limits.maxSubscriptionLifetimeBlocks) {
1896
- throw new EventKitHandshakeClientError(
1897
- "subscription-invalid",
1898
- "Requested subscription expiry exceeds the selected publisher service lifetime limit"
1899
- );
1760
+ throw new EventKitHandshakeClientError("subscription-invalid", "Requested subscription expiry exceeds the selected publisher service lifetime limit");
1900
1761
  }
1901
1762
  const grant = createWakeGrant({
1902
1763
  deploymentId: input.deploymentId,
@@ -1905,19 +1766,11 @@ function createSubscriberHandshakeClient(options) {
1905
1766
  ...input.queueId === void 0 || input.queueId === null ? {} : { queueId: input.queueId }
1906
1767
  });
1907
1768
  const grantHash = await handshakeObjectHash2(grant);
1908
- const plaintext = createWakeSubscriptionPlaintext({
1909
- audience: input.audience,
1910
- challengeSecret: input.challengeSecret,
1911
- deploymentId: input.deploymentId,
1912
- endpoint: input.endpoint,
1913
- maxBlocksPerWake,
1914
- queueId: input.queueId
1915
- });
1916
- const subscription = await createEncryptedWakeSubscription({
1769
+ const plaintext = subscriberPlaintext(selected.service.body, input, maxPositionsPerWake);
1770
+ const subscriptionInput = {
1917
1771
  chainId: options.reader.chainId,
1918
1772
  expiresAtBlock: input.expiresAtBlock,
1919
1773
  grant: grantHash,
1920
- plaintext,
1921
1774
  publisher: options.reader.publisher,
1922
1775
  publisherKey: {
1923
1776
  encryptionKey: HashZod4.parse(selected.key.objectHash),
@@ -1929,7 +1782,8 @@ function createSubscriberHandshakeClient(options) {
1929
1782
  subscriber,
1930
1783
  ...input.subscriptionId === void 0 ? {} : { subscriptionId: input.subscriptionId },
1931
1784
  ...input.supersedes === void 0 ? {} : { supersedes: input.supersedes }
1932
- });
1785
+ };
1786
+ const subscription = await createEncryptedWakeSubscription({ ...subscriptionInput, plaintext });
1933
1787
  const resolvedIntent = await persistMutationIntent(options.intents, {
1934
1788
  chainId: options.reader.chainId,
1935
1789
  encryptionKey: selected.key.body,
@@ -1953,6 +1807,25 @@ function createSubscriberHandshakeClient(options) {
1953
1807
  };
1954
1808
  return Object.freeze(client);
1955
1809
  }
1810
+ function subscriberPlaintext(service, input, maxPositionsPerWake) {
1811
+ const plaintextInput = {
1812
+ audience: input.audience,
1813
+ challengeSecret: input.challengeSecret,
1814
+ deploymentId: input.deploymentId,
1815
+ endpoint: input.endpoint,
1816
+ queueId: input.queueId
1817
+ };
1818
+ const source = input.source;
1819
+ if (!service.events.some((event) => canonicalJson(event.source) === canonicalJson(source))) {
1820
+ throw new Error("Requested source is not advertised by the selected service");
1821
+ }
1822
+ return createWakeSubscriptionPlaintext({
1823
+ ...plaintextInput,
1824
+ source,
1825
+ fromPosition: input.fromPosition,
1826
+ maxPositionsPerWake
1827
+ });
1828
+ }
1956
1829
  export {
1957
1830
  EventKitHandshakeClientError,
1958
1831
  HandshakeMutationIntentConflictError,