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