keri-ts 0.6.0 → 0.7.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/esm/cesr/mod.js +1 -0
- package/esm/cesr/src/core/parser-frame-parser.js +3 -0
- package/esm/cesr/src/primitives/matter.js +7 -0
- package/esm/cesr/src/tables/counter.tables.generated.js +1 -5
- package/esm/cesr/src/tables/hard-code-tables.js +39 -0
- package/esm/cesr/src/tables/indexer.tables.generated.js +1 -9
- package/esm/cesr/src/tables/matter.tables.generated.js +1 -14
- package/esm/cesr/src/version.js +7 -4
- package/esm/keri/src/app/agent-runtime.js +11 -5
- package/esm/keri/src/app/authenticating.js +15 -4
- package/esm/keri/src/app/cli/delegate.js +28 -0
- package/esm/keri/src/app/cli/notifications.js +4 -0
- package/esm/keri/src/app/delegating.js +21 -0
- package/esm/keri/src/app/forwarding.js +59 -13
- package/esm/keri/src/app/habbing.js +1 -0
- package/esm/keri/src/app/httping.js +12 -7
- package/esm/keri/src/app/keeping.js +3 -3
- package/esm/keri/src/app/mailbox-sse.js +11 -9
- package/esm/keri/src/app/notifying.js +11 -0
- package/esm/keri/src/app/oobiery.js +15 -4
- package/esm/keri/src/app/query-transport.js +3 -0
- package/esm/keri/src/app/runtime-services.js +22 -0
- package/esm/keri/src/app/version.js +7 -4
- package/esm/keri/src/core/eventing.js +40 -0
- package/esm/keri/src/core/kever.js +9 -0
- package/esm/keri/src/core/protocol-eventing.js +15 -0
- package/esm/keri/src/db/noting.js +4 -0
- package/esm/keri/src/runtime/index.js +1 -0
- package/package.json +2 -2
- package/types/cesr/mod.d.ts +1 -0
- package/types/cesr/mod.d.ts.map +1 -1
- package/types/cesr/src/core/parser-frame-parser.d.ts.map +1 -1
- package/types/cesr/src/primitives/matter.d.ts.map +1 -1
- package/types/cesr/src/tables/counter.tables.generated.d.ts +1 -1
- package/types/cesr/src/tables/counter.tables.generated.d.ts.map +1 -1
- package/types/cesr/src/tables/hard-code-tables.d.ts +15 -0
- package/types/cesr/src/tables/hard-code-tables.d.ts.map +1 -0
- package/types/cesr/src/tables/indexer.tables.generated.d.ts +1 -1
- package/types/cesr/src/tables/indexer.tables.generated.d.ts.map +1 -1
- package/types/cesr/src/tables/matter.tables.generated.d.ts +1 -1
- package/types/cesr/src/tables/matter.tables.generated.d.ts.map +1 -1
- package/types/cesr/src/version.d.ts +7 -4
- package/types/cesr/src/version.d.ts.map +1 -1
- package/types/keri/src/app/agent-runtime.d.ts +5 -1
- package/types/keri/src/app/agent-runtime.d.ts.map +1 -1
- package/types/keri/src/app/authenticating.d.ts +7 -1
- package/types/keri/src/app/authenticating.d.ts.map +1 -1
- package/types/keri/src/app/cli/delegate.d.ts +14 -0
- package/types/keri/src/app/cli/delegate.d.ts.map +1 -1
- package/types/keri/src/app/cli/notifications.d.ts +10 -0
- package/types/keri/src/app/cli/notifications.d.ts.map +1 -1
- package/types/keri/src/app/delegating.d.ts +23 -0
- package/types/keri/src/app/delegating.d.ts.map +1 -1
- package/types/keri/src/app/forwarding.d.ts +39 -9
- package/types/keri/src/app/forwarding.d.ts.map +1 -1
- package/types/keri/src/app/habbing.d.ts.map +1 -1
- package/types/keri/src/app/httping.d.ts +10 -4
- package/types/keri/src/app/httping.d.ts.map +1 -1
- package/types/keri/src/app/keeping.d.ts +2 -2
- package/types/keri/src/app/mailbox-sse.d.ts +8 -4
- package/types/keri/src/app/mailbox-sse.d.ts.map +1 -1
- package/types/keri/src/app/notifying.d.ts +23 -0
- package/types/keri/src/app/notifying.d.ts.map +1 -1
- package/types/keri/src/app/oobiery.d.ts +7 -3
- package/types/keri/src/app/oobiery.d.ts.map +1 -1
- package/types/keri/src/app/query-transport.d.ts.map +1 -1
- package/types/keri/src/app/runtime-services.d.ts +26 -0
- package/types/keri/src/app/runtime-services.d.ts.map +1 -0
- package/types/keri/src/app/version.d.ts +7 -4
- package/types/keri/src/app/version.d.ts.map +1 -1
- package/types/keri/src/core/eventing.d.ts +30 -0
- package/types/keri/src/core/eventing.d.ts.map +1 -1
- package/types/keri/src/core/kever.d.ts.map +1 -1
- package/types/keri/src/core/protocol-eventing.d.ts +8 -0
- package/types/keri/src/core/protocol-eventing.d.ts.map +1 -1
- package/types/keri/src/db/noting.d.ts +2 -0
- package/types/keri/src/db/noting.d.ts.map +1 -1
- package/types/keri/src/runtime/index.d.ts +1 -0
- package/types/keri/src/runtime/index.d.ts.map +1 -1
|
@@ -25,6 +25,7 @@ import { closeResponseBody, fetchResponseHandle, fetchResponseHandleOrNull } fro
|
|
|
25
25
|
import { parseMailboxSse, readMailboxSseBody } from "./mailbox-sse.js";
|
|
26
26
|
import { directDeliveryEndpoints, firstSortedEndpoint, getOutboxer, mailboxDeliveryEndpoints, mailboxPollEndpoints, mailboxTopicKey, } from "./mailboxing.js";
|
|
27
27
|
import { Organizer } from "./organizing.js";
|
|
28
|
+
import { defaultRuntimeServices } from "./runtime-services.js";
|
|
28
29
|
import { runtimeTurn } from "./runtime-turn.js";
|
|
29
30
|
/**
|
|
30
31
|
* Mailbox-first postman for outbound EXN transport.
|
|
@@ -37,7 +38,7 @@ import { runtimeTurn } from "./runtime-turn.js";
|
|
|
37
38
|
* - persist sender-side mailbox retry state per mailbox endpoint
|
|
38
39
|
*/
|
|
39
40
|
export class Poster {
|
|
40
|
-
constructor(hby,
|
|
41
|
+
constructor(hby, options = {}) {
|
|
41
42
|
Object.defineProperty(this, "hby", {
|
|
42
43
|
enumerable: true,
|
|
43
44
|
configurable: true,
|
|
@@ -62,10 +63,18 @@ export class Poster {
|
|
|
62
63
|
writable: true,
|
|
63
64
|
value: void 0
|
|
64
65
|
});
|
|
66
|
+
Object.defineProperty(this, "services", {
|
|
67
|
+
enumerable: true,
|
|
68
|
+
configurable: true,
|
|
69
|
+
writable: true,
|
|
70
|
+
value: void 0
|
|
71
|
+
});
|
|
72
|
+
const { mailboxer, outboxer, services = defaultRuntimeServices } = options;
|
|
65
73
|
this.hby = hby;
|
|
66
74
|
this.mailboxer = mailboxer ?? null;
|
|
67
75
|
this.outboxer = outboxer ?? getOutboxer(hby);
|
|
68
76
|
this.organizer = new Organizer(hby);
|
|
77
|
+
this.services = services;
|
|
69
78
|
}
|
|
70
79
|
/**
|
|
71
80
|
* Resolve one CLI or user recipient input into the actual destination prefix.
|
|
@@ -164,7 +173,7 @@ export class Poster {
|
|
|
164
173
|
const directEndpoints = directDeliveryEndpoints(hab, recipient);
|
|
165
174
|
if (directEndpoints.length > 0) {
|
|
166
175
|
for (const endpoint of directEndpoints) {
|
|
167
|
-
yield* postCesrMessage(endpoint.url, message, this.hby.cesrBodyMode, endpoint.eid);
|
|
176
|
+
yield* postCesrMessage(endpoint.url, message, this.hby.cesrBodyMode, endpoint.eid, this.services);
|
|
168
177
|
deliveries.push(endpoint.url);
|
|
169
178
|
}
|
|
170
179
|
return { serder, deliveries, queued };
|
|
@@ -241,7 +250,7 @@ export class Poster {
|
|
|
241
250
|
const directEndpoints = directDeliveryEndpoints(hab, recipient);
|
|
242
251
|
if (directEndpoints.length > 0) {
|
|
243
252
|
for (const endpoint of directEndpoints) {
|
|
244
|
-
yield* postCesrMessage(endpoint.url, args.message, this.hby.cesrBodyMode, endpoint.eid);
|
|
253
|
+
yield* postCesrMessage(endpoint.url, args.message, this.hby.cesrBodyMode, endpoint.eid, this.services);
|
|
245
254
|
deliveries.push(endpoint.url);
|
|
246
255
|
}
|
|
247
256
|
return { deliveries, queued };
|
|
@@ -329,7 +338,7 @@ export class Poster {
|
|
|
329
338
|
topic,
|
|
330
339
|
message,
|
|
331
340
|
});
|
|
332
|
-
yield* postCesrMessage(endpoint.url, concatBytes(introduce(hab, endpoint.eid), forwarded), this.hby.cesrBodyMode, endpoint.eid);
|
|
341
|
+
yield* postCesrMessage(endpoint.url, concatBytes(introduce(hab, endpoint.eid), forwarded), this.hby.cesrBodyMode, endpoint.eid, this.services);
|
|
333
342
|
}
|
|
334
343
|
/** Require provider mailbox storage for local mailbox-target delivery. */
|
|
335
344
|
requireMailboxer() {
|
|
@@ -418,6 +427,9 @@ Object.defineProperty(ForwardHandler, "resource", {
|
|
|
418
427
|
value: "/fwd"
|
|
419
428
|
});
|
|
420
429
|
function hostCanStoreForwardRecipient(hby, recipient, recipientKever, hostAid) {
|
|
430
|
+
// A local host may store forwarded traffic only when it is an authorized
|
|
431
|
+
// witness/mailbox/agent for the recipient. This keeps mailbox storage from
|
|
432
|
+
// becoming a generic unauthenticated relay.
|
|
421
433
|
if (recipientKever.wits.includes(hostAid)) {
|
|
422
434
|
return true;
|
|
423
435
|
}
|
|
@@ -430,6 +442,8 @@ function hostCanStoreForwardRecipient(hby, recipient, recipientKever, hostAid) {
|
|
|
430
442
|
return false;
|
|
431
443
|
}
|
|
432
444
|
function hasLocalStoreForwardHost(hby, recipient, recipientKever) {
|
|
445
|
+
// Check all local prefixes because a multi-role runtime can host a mailbox
|
|
446
|
+
// AID that is distinct from the controller currently processing the EXN.
|
|
433
447
|
for (const hostAid of hby.db.prefixes) {
|
|
434
448
|
if (hostCanStoreForwardRecipient(hby, recipient, recipientKever, hostAid)) {
|
|
435
449
|
return true;
|
|
@@ -446,7 +460,7 @@ function hasLocalStoreForwardHost(hby, recipient, recipientKever) {
|
|
|
446
460
|
* - ingests retrieved payloads back through the shared `Reactor`
|
|
447
461
|
*/
|
|
448
462
|
export class MailboxPoller {
|
|
449
|
-
constructor(hby, mailboxDirector,
|
|
463
|
+
constructor(hby, mailboxDirector, options = {}) {
|
|
450
464
|
Object.defineProperty(this, "hby", {
|
|
451
465
|
enumerable: true,
|
|
452
466
|
configurable: true,
|
|
@@ -465,15 +479,30 @@ export class MailboxPoller {
|
|
|
465
479
|
writable: true,
|
|
466
480
|
value: void 0
|
|
467
481
|
});
|
|
482
|
+
Object.defineProperty(this, "services", {
|
|
483
|
+
enumerable: true,
|
|
484
|
+
configurable: true,
|
|
485
|
+
writable: true,
|
|
486
|
+
value: void 0
|
|
487
|
+
});
|
|
488
|
+
Object.defineProperty(this, "pollTransport", {
|
|
489
|
+
enumerable: true,
|
|
490
|
+
configurable: true,
|
|
491
|
+
writable: true,
|
|
492
|
+
value: void 0
|
|
493
|
+
});
|
|
468
494
|
Object.defineProperty(this, "localCursors", {
|
|
469
495
|
enumerable: true,
|
|
470
496
|
configurable: true,
|
|
471
497
|
writable: true,
|
|
472
498
|
value: new Map()
|
|
473
499
|
});
|
|
500
|
+
const { timeouts, services = defaultRuntimeServices, pollTransport = defaultMailboxPollTransport, } = options;
|
|
474
501
|
this.hby = hby;
|
|
475
502
|
this.mailboxDirector = mailboxDirector;
|
|
476
503
|
this.timeoutPolicy = normalizeMailboxPollingTimeoutPolicy(timeouts);
|
|
504
|
+
this.services = services;
|
|
505
|
+
this.pollTransport = pollTransport;
|
|
477
506
|
}
|
|
478
507
|
/** Configure mailbox polling state for future expansion; currently a no-op. */
|
|
479
508
|
configure(_args = {}) {
|
|
@@ -498,7 +527,7 @@ export class MailboxPoller {
|
|
|
498
527
|
return [];
|
|
499
528
|
}
|
|
500
529
|
const batches = [];
|
|
501
|
-
const deadline =
|
|
530
|
+
const deadline = this.services.clock.now() + positiveTimeoutMs(budgetMs, this.timeoutPolicy.commandLocalBudgetMs);
|
|
502
531
|
for (const pre of this.hby.prefixes) {
|
|
503
532
|
const local = this.readLocalMailbox(pre, topics);
|
|
504
533
|
if (local.length > 0) {
|
|
@@ -508,7 +537,7 @@ export class MailboxPoller {
|
|
|
508
537
|
for (const hab of this.hby.habs.values()) {
|
|
509
538
|
const remoteEndpoints = mailboxPollEndpoints(this.hby, hab);
|
|
510
539
|
for (const endpoint of remoteEndpoints) {
|
|
511
|
-
const remainingBudgetMs = deadline -
|
|
540
|
+
const remainingBudgetMs = deadline - this.services.clock.now();
|
|
512
541
|
if (remainingBudgetMs <= 0) {
|
|
513
542
|
return batches;
|
|
514
543
|
}
|
|
@@ -588,7 +617,14 @@ export class MailboxPoller {
|
|
|
588
617
|
*/
|
|
589
618
|
*pollRemoteEndpointOnce(hab, endpoint, budgetMs) {
|
|
590
619
|
const cursor = this.mailboxDirector.remoteQueryCursor(hab.pre, endpoint.eid);
|
|
591
|
-
const messages = yield*
|
|
620
|
+
const messages = yield* this.pollTransport.poll({
|
|
621
|
+
hab,
|
|
622
|
+
endpoint,
|
|
623
|
+
topics: cursor,
|
|
624
|
+
bodyMode: this.hby.cesrBodyMode,
|
|
625
|
+
timeouts: mailboxFetchTimeoutPolicy(this.timeoutPolicy, budgetMs),
|
|
626
|
+
services: this.services,
|
|
627
|
+
});
|
|
592
628
|
if (messages.length === 0) {
|
|
593
629
|
return null;
|
|
594
630
|
}
|
|
@@ -766,6 +802,8 @@ function extractForwardedMessage(serder, attachments) {
|
|
|
766
802
|
if (!evt || typeof evt !== "object" || Array.isArray(evt)) {
|
|
767
803
|
return null;
|
|
768
804
|
}
|
|
805
|
+
// Rebuild the embedded event from SAD plus only the attachment material
|
|
806
|
+
// pathed to `/e/evt`; other pathed groups belong to the outer `/fwd` message.
|
|
769
807
|
const embedded = new SerderKERI({ sad: evt });
|
|
770
808
|
const messageParts = [embedded.raw];
|
|
771
809
|
for (const attachment of attachments) {
|
|
@@ -794,15 +832,20 @@ function forwardedAttachmentForPath(raw, path) {
|
|
|
794
832
|
}
|
|
795
833
|
return raw.slice(offset + pather.fullSize);
|
|
796
834
|
}
|
|
835
|
+
const defaultMailboxPollTransport = {
|
|
836
|
+
*poll(args) {
|
|
837
|
+
return yield* fetchMailboxMessages(args.hab, args.endpoint.eid, args.endpoint.url, args.topics, args.bodyMode, args.timeouts, args.services);
|
|
838
|
+
},
|
|
839
|
+
};
|
|
797
840
|
/**
|
|
798
841
|
* Query one remote mailbox endpoint and parse any returned mailbox SSE events.
|
|
799
842
|
*
|
|
800
843
|
* The query cursor values are "next wanted" ordinals, matching `mbx`
|
|
801
844
|
* semantics.
|
|
802
845
|
*/
|
|
803
|
-
function* fetchMailboxMessages(hab, src, url, topics, bodyMode, timeouts) {
|
|
846
|
+
function* fetchMailboxMessages(hab, src, url, topics, bodyMode, timeouts, services = defaultRuntimeServices) {
|
|
804
847
|
const query = hab.query(hab.pre, src, { topics }, "mbx");
|
|
805
|
-
const handle = yield* fetchMailboxQueryResponse(url, query, bodyMode, src, timeouts.requestOpenTimeoutMs);
|
|
848
|
+
const handle = yield* fetchMailboxQueryResponse(url, query, bodyMode, src, timeouts.requestOpenTimeoutMs, services);
|
|
806
849
|
if (!handle) {
|
|
807
850
|
return [];
|
|
808
851
|
}
|
|
@@ -814,6 +857,7 @@ function* fetchMailboxMessages(hab, src, url, topics, bodyMode, timeouts) {
|
|
|
814
857
|
const text = yield* readMailboxSseBody(response, controller, {
|
|
815
858
|
idleTimeoutMs: timeouts.readIdleTimeoutMs,
|
|
816
859
|
maxDurationMs: timeouts.maxReadDurationMs,
|
|
860
|
+
services,
|
|
817
861
|
});
|
|
818
862
|
return parseMailboxSse(text);
|
|
819
863
|
}
|
|
@@ -827,7 +871,7 @@ function* fetchMailboxMessages(hab, src, url, topics, bodyMode, timeouts) {
|
|
|
827
871
|
*
|
|
828
872
|
* Message parsing and SSE body policy remain with `fetchMailboxMessages(...)`.
|
|
829
873
|
*/
|
|
830
|
-
function* fetchMailboxQueryResponse(url, query, bodyMode, destination, timeoutMs) {
|
|
874
|
+
function* fetchMailboxQueryResponse(url, query, bodyMode, destination, timeoutMs, services = defaultRuntimeServices) {
|
|
831
875
|
const request = buildCesrRequest(query, {
|
|
832
876
|
bodyMode,
|
|
833
877
|
destination,
|
|
@@ -836,7 +880,7 @@ function* fetchMailboxQueryResponse(url, query, bodyMode, destination, timeoutMs
|
|
|
836
880
|
method: "POST",
|
|
837
881
|
headers: request.headers,
|
|
838
882
|
body: request.body,
|
|
839
|
-
}, { timeoutMs });
|
|
883
|
+
}, { timeoutMs, services });
|
|
840
884
|
}
|
|
841
885
|
function normalizeMailboxPollingTimeoutPolicy(overrides) {
|
|
842
886
|
const defaults = MailboxPoller.DefaultTimeoutPolicy;
|
|
@@ -870,7 +914,7 @@ function positiveTimeoutMs(value, fallback) {
|
|
|
870
914
|
* Body-mode policy:
|
|
871
915
|
* - send the whole provided payload in the request body
|
|
872
916
|
*/
|
|
873
|
-
export function* postCesrMessage(url, body, bodyMode, destination) {
|
|
917
|
+
export function* postCesrMessage(url, body, bodyMode, destination, services = defaultRuntimeServices) {
|
|
874
918
|
const requests = bodyMode === "header" ? splitCesrStream(body) : [body];
|
|
875
919
|
for (const currentBody of requests) {
|
|
876
920
|
const request = buildCesrRequest(currentBody, {
|
|
@@ -881,6 +925,8 @@ export function* postCesrMessage(url, body, bodyMode, destination) {
|
|
|
881
925
|
method: "POST",
|
|
882
926
|
headers: request.headers,
|
|
883
927
|
body: request.body,
|
|
928
|
+
}, {
|
|
929
|
+
services,
|
|
884
930
|
});
|
|
885
931
|
yield* closeResponseBody(response);
|
|
886
932
|
if (!response.ok) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { action } from "effection";
|
|
2
|
+
import { defaultRuntimeServices } from "./runtime-services.js";
|
|
2
3
|
/**
|
|
3
4
|
* Fetch one HTTP response under Effection cancellation control.
|
|
4
5
|
*
|
|
@@ -8,8 +9,9 @@ import { action } from "effection";
|
|
|
8
9
|
* - hand the caller both the `Response` and the controller when body policy
|
|
9
10
|
* needs later abort access, such as mailbox SSE long-poll reads
|
|
10
11
|
*/
|
|
11
|
-
export function* fetchResponseHandle(url, init = {}) {
|
|
12
|
-
const
|
|
12
|
+
export function* fetchResponseHandle(url, init = {}, options = {}) {
|
|
13
|
+
const { services = defaultRuntimeServices } = options;
|
|
14
|
+
const handle = yield* fetchResponseHandleInternal(url, init, { services });
|
|
13
15
|
if (!handle) {
|
|
14
16
|
throw new Error("HTTP response handle unexpectedly resolved to null.");
|
|
15
17
|
}
|
|
@@ -21,10 +23,12 @@ export function* fetchResponseHandle(url, init = {}) {
|
|
|
21
23
|
* This is for callers such as mailbox polling that treat request timeout as a
|
|
22
24
|
* normal "no response yet" outcome instead of as an exception.
|
|
23
25
|
*/
|
|
24
|
-
export function* fetchResponseHandleOrNull(url, init = {},
|
|
26
|
+
export function* fetchResponseHandleOrNull(url, init = {}, options = {}) {
|
|
27
|
+
const { timeoutMs, services = defaultRuntimeServices } = options;
|
|
25
28
|
return yield* fetchResponseHandleInternal(url, init, {
|
|
26
29
|
timeoutMs,
|
|
27
30
|
nullOnAbort: true,
|
|
31
|
+
services,
|
|
28
32
|
});
|
|
29
33
|
}
|
|
30
34
|
/**
|
|
@@ -43,21 +47,22 @@ export function* closeResponseBody(response) {
|
|
|
43
47
|
return () => { };
|
|
44
48
|
});
|
|
45
49
|
}
|
|
46
|
-
function* fetchResponseHandleInternal(url, init,
|
|
50
|
+
function* fetchResponseHandleInternal(url, init, options = {}) {
|
|
51
|
+
const { timeoutMs, nullOnAbort = false, services = defaultRuntimeServices, } = options;
|
|
47
52
|
return yield* action((resolve, reject) => {
|
|
48
53
|
const controller = new AbortController();
|
|
49
54
|
let settled = false;
|
|
50
55
|
let timedOut = false;
|
|
51
|
-
const timeoutId = timeoutMs === undefined ? undefined : setTimeout(() => {
|
|
56
|
+
const timeoutId = timeoutMs === undefined ? undefined : services.clock.setTimeout(() => {
|
|
52
57
|
timedOut = true;
|
|
53
58
|
controller.abort();
|
|
54
59
|
}, timeoutMs);
|
|
55
60
|
const clearTimer = () => {
|
|
56
61
|
if (timeoutId !== undefined) {
|
|
57
|
-
clearTimeout(timeoutId);
|
|
62
|
+
services.clock.clearTimeout(timeoutId);
|
|
58
63
|
}
|
|
59
64
|
};
|
|
60
|
-
fetch(url, { ...init, signal: controller.signal }).then((response) => {
|
|
65
|
+
services.http.fetch(url, { ...init, signal: controller.signal }).then((response) => {
|
|
61
66
|
settled = true;
|
|
62
67
|
clearTimer();
|
|
63
68
|
resolve({ response, controller });
|
|
@@ -551,9 +551,9 @@ export class Manager {
|
|
|
551
551
|
ridx: lot.ridx,
|
|
552
552
|
kidx: lot.kidx + offset,
|
|
553
553
|
transferable: verfer.transferable,
|
|
554
|
-
//
|
|
555
|
-
//
|
|
556
|
-
temp:
|
|
554
|
+
// Temp keepers create temp-derived keys, so path re-derivation must
|
|
555
|
+
// mirror the active keeper mode. Durable keepers remain production-cost.
|
|
556
|
+
temp: this.ks.temp,
|
|
557
557
|
})[0];
|
|
558
558
|
if (!signer || signer.verfer.qb64 !== pub) {
|
|
559
559
|
throw new Error(`Derived signer mismatch for pre=${pre} ri=${lot.ridx} kidx=${lot.kidx + offset}.`);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { action } from "effection";
|
|
2
|
+
import { defaultRuntimeServices } from "./runtime-services.js";
|
|
2
3
|
/** Shared encoder for mailbox SSE parsing. */
|
|
3
4
|
const textEncoder = new TextEncoder();
|
|
4
5
|
const DEFAULT_READ_IDLE_TIMEOUT_MS = 500;
|
|
@@ -10,7 +11,8 @@ const DEFAULT_MAX_READ_DURATION_MS = 30_000;
|
|
|
10
11
|
* callers use an explicit read budget and idle timeout instead of waiting for
|
|
11
12
|
* `response.text()` to resolve.
|
|
12
13
|
*/
|
|
13
|
-
export function* readMailboxSseBody(response, controller,
|
|
14
|
+
export function* readMailboxSseBody(response, controller, options = {}) {
|
|
15
|
+
const { idleTimeoutMs = DEFAULT_READ_IDLE_TIMEOUT_MS, maxDurationMs = DEFAULT_MAX_READ_DURATION_MS, services = defaultRuntimeServices, } = options;
|
|
14
16
|
const body = response.body;
|
|
15
17
|
if (!body) {
|
|
16
18
|
return "";
|
|
@@ -18,14 +20,14 @@ export function* readMailboxSseBody(response, controller, { idleTimeoutMs = DEFA
|
|
|
18
20
|
const reader = body.getReader();
|
|
19
21
|
const decoder = new TextDecoder();
|
|
20
22
|
let text = "";
|
|
21
|
-
const deadline =
|
|
23
|
+
const deadline = services.clock.now() + maxDurationMs;
|
|
22
24
|
const timedOut = Symbol("timedOut");
|
|
23
25
|
try {
|
|
24
|
-
while (
|
|
25
|
-
const remaining = Math.max(1, Math.min(idleTimeoutMs, deadline -
|
|
26
|
+
while (services.clock.now() < deadline) {
|
|
27
|
+
const remaining = Math.max(1, Math.min(idleTimeoutMs, deadline - services.clock.now()));
|
|
26
28
|
const next = yield* action((resolve, reject) => {
|
|
27
29
|
let settled = false;
|
|
28
|
-
const timeoutId = setTimeout(() => {
|
|
30
|
+
const timeoutId = services.clock.setTimeout(() => {
|
|
29
31
|
settled = true;
|
|
30
32
|
resolve(timedOut);
|
|
31
33
|
}, remaining);
|
|
@@ -34,23 +36,23 @@ export function* readMailboxSseBody(response, controller, { idleTimeoutMs = DEFA
|
|
|
34
36
|
return;
|
|
35
37
|
}
|
|
36
38
|
settled = true;
|
|
37
|
-
clearTimeout(timeoutId);
|
|
39
|
+
services.clock.clearTimeout(timeoutId);
|
|
38
40
|
resolve(result);
|
|
39
41
|
}).catch((error) => {
|
|
40
42
|
if (settled) {
|
|
41
43
|
return;
|
|
42
44
|
}
|
|
43
45
|
settled = true;
|
|
44
|
-
clearTimeout(timeoutId);
|
|
46
|
+
services.clock.clearTimeout(timeoutId);
|
|
45
47
|
reject(error);
|
|
46
48
|
});
|
|
47
49
|
return () => {
|
|
48
|
-
clearTimeout(timeoutId);
|
|
50
|
+
services.clock.clearTimeout(timeoutId);
|
|
49
51
|
};
|
|
50
52
|
});
|
|
51
53
|
if (next === timedOut) {
|
|
52
54
|
if (parseMailboxSse(text).length > 0
|
|
53
|
-
||
|
|
55
|
+
|| services.clock.now() + idleTimeoutMs >= deadline) {
|
|
54
56
|
controller.abort();
|
|
55
57
|
break;
|
|
56
58
|
}
|
|
@@ -14,6 +14,7 @@ export function notice(attrs, options = {}) {
|
|
|
14
14
|
},
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
|
+
/** Derive sidecar-open options from the owning `Habery` path/layout settings. */
|
|
17
18
|
export function noterOptionsForHabery(hby, options = {}) {
|
|
18
19
|
return {
|
|
19
20
|
name: hby.name,
|
|
@@ -26,11 +27,16 @@ export function noterOptionsForHabery(hby, options = {}) {
|
|
|
26
27
|
...options,
|
|
27
28
|
};
|
|
28
29
|
}
|
|
30
|
+
/** Open the notification sidecar that belongs to one `Habery`. */
|
|
29
31
|
export function* openNoterForHabery(hby, options = {}) {
|
|
30
32
|
return yield* createNoter(noterOptionsForHabery(hby, options));
|
|
31
33
|
}
|
|
32
34
|
/**
|
|
33
35
|
* Signed durable controller notifications with transient `/notification` pings.
|
|
36
|
+
*
|
|
37
|
+
* The detached signature is verified on every read/mutation path so operators
|
|
38
|
+
* can trust CLI output even though notification payloads are intentionally
|
|
39
|
+
* application-specific maps.
|
|
34
40
|
*/
|
|
35
41
|
export class Notifier {
|
|
36
42
|
constructor(hby, { noter, signaler, }) {
|
|
@@ -56,6 +62,7 @@ export class Notifier {
|
|
|
56
62
|
this.noter = noter;
|
|
57
63
|
this.signaler = signaler ?? new Signaler();
|
|
58
64
|
}
|
|
65
|
+
/** Add one unread signed notice and emit a transient add signal. */
|
|
59
66
|
add(attrs) {
|
|
60
67
|
const signator = this.requireSignator();
|
|
61
68
|
const note = notice(attrs);
|
|
@@ -66,12 +73,15 @@ export class Notifier {
|
|
|
66
73
|
this.emitSignal("add", note);
|
|
67
74
|
return true;
|
|
68
75
|
}
|
|
76
|
+
/** List verified notices in durable datetime order. */
|
|
69
77
|
list(start = 0, limit = 25) {
|
|
70
78
|
return this.noter.listNotices(start, limit).map(([note, cigar]) => this.requireVerifiedNotice(note, cigar));
|
|
71
79
|
}
|
|
80
|
+
/** Count durable notices without verifying each payload. */
|
|
72
81
|
count() {
|
|
73
82
|
return this.noter.countNotices();
|
|
74
83
|
}
|
|
84
|
+
/** Mark one notice read and re-sign its updated pad. */
|
|
75
85
|
markRead(rid) {
|
|
76
86
|
const [note] = this.requireNoticePair(rid);
|
|
77
87
|
if (note.read) {
|
|
@@ -86,6 +96,7 @@ export class Notifier {
|
|
|
86
96
|
this.emitSignal("mar", note);
|
|
87
97
|
return true;
|
|
88
98
|
}
|
|
99
|
+
/** Remove one verified notice and emit a transient remove signal. */
|
|
89
100
|
remove(rid) {
|
|
90
101
|
const [note] = this.requireNoticePair(rid);
|
|
91
102
|
if (!this.noter.removeNotice(rid)) {
|
|
@@ -8,6 +8,7 @@ import { Roles } from "../core/roles.js";
|
|
|
8
8
|
import { acceptReplyDecision, unverifiedReplyDecision } from "../core/routing.js";
|
|
9
9
|
import { closeResponseBody, fetchResponseHandle } from "./httping.js";
|
|
10
10
|
import { persistResolvedContact } from "./organizing.js";
|
|
11
|
+
import { defaultRuntimeServices } from "./runtime-services.js";
|
|
11
12
|
import { runtimeTurn } from "./runtime-turn.js";
|
|
12
13
|
export const OOBI_REQUEST_ROUTE = "/oobis";
|
|
13
14
|
/**
|
|
@@ -24,7 +25,7 @@ export const OOBI_REQUEST_ROUTE = "/oobis";
|
|
|
24
25
|
* leak back to the `AgentRuntime` root
|
|
25
26
|
*/
|
|
26
27
|
export class Oobiery {
|
|
27
|
-
constructor(hby, reactor,
|
|
28
|
+
constructor(hby, reactor, options = {}) {
|
|
28
29
|
Object.defineProperty(this, "hby", {
|
|
29
30
|
enumerable: true,
|
|
30
31
|
configurable: true,
|
|
@@ -43,9 +44,17 @@ export class Oobiery {
|
|
|
43
44
|
writable: true,
|
|
44
45
|
value: void 0
|
|
45
46
|
});
|
|
47
|
+
Object.defineProperty(this, "services", {
|
|
48
|
+
enumerable: true,
|
|
49
|
+
configurable: true,
|
|
50
|
+
writable: true,
|
|
51
|
+
value: void 0
|
|
52
|
+
});
|
|
53
|
+
const { cues, services = defaultRuntimeServices } = options;
|
|
46
54
|
this.hby = hby;
|
|
47
55
|
this.reactor = reactor;
|
|
48
56
|
this.cues = cues ?? new Deck();
|
|
57
|
+
this.services = services;
|
|
49
58
|
}
|
|
50
59
|
/** Register `/introduce` on the shared reply router. */
|
|
51
60
|
registerReplyRoutes(router = this.reactor.router) {
|
|
@@ -149,7 +158,7 @@ export class Oobiery {
|
|
|
149
158
|
said: meta.said ?? null,
|
|
150
159
|
};
|
|
151
160
|
this.pinQueueStore(kind, url, queuedRecord);
|
|
152
|
-
const response = yield* fetchOobiResponse(url);
|
|
161
|
+
const response = yield* fetchOobiResponse(url, this.services);
|
|
153
162
|
if (!response.ok) {
|
|
154
163
|
yield* closeResponseBody(response);
|
|
155
164
|
this.remQueueStore(kind, url);
|
|
@@ -532,8 +541,10 @@ export { OOBI_MAILBOX_TOPIC };
|
|
|
532
541
|
* This is the real promise-adaptation boundary for remote OOBI retrieval. The
|
|
533
542
|
* surrounding runtime stays operation-native.
|
|
534
543
|
*/
|
|
535
|
-
function* fetchOobiResponse(url) {
|
|
536
|
-
const { response } = yield* fetchResponseHandle(url
|
|
544
|
+
function* fetchOobiResponse(url, services = defaultRuntimeServices) {
|
|
545
|
+
const { response } = yield* fetchResponseHandle(url, {}, {
|
|
546
|
+
services,
|
|
547
|
+
});
|
|
537
548
|
return response;
|
|
538
549
|
}
|
|
539
550
|
/**
|
|
@@ -62,6 +62,8 @@ function* postQueryMessage(runtime, url, body, bodyMode, destination) {
|
|
|
62
62
|
method: "POST",
|
|
63
63
|
headers: request.headers,
|
|
64
64
|
body: request.body,
|
|
65
|
+
}, {
|
|
66
|
+
services: runtime.services,
|
|
65
67
|
});
|
|
66
68
|
if (!response.ok) {
|
|
67
69
|
throw new ValidationError(`Query delivery to ${url} failed with HTTP ${response.status}.`);
|
|
@@ -72,6 +74,7 @@ function* postQueryMessage(runtime, url, body, bodyMode, destination) {
|
|
|
72
74
|
const text = yield* readMailboxSseBody(response, controller, {
|
|
73
75
|
idleTimeoutMs: 500,
|
|
74
76
|
maxDurationMs: 5_000,
|
|
77
|
+
services: runtime.services,
|
|
75
78
|
});
|
|
76
79
|
for (const message of parseMailboxSse(text)) {
|
|
77
80
|
if (message.msg.length > 0) {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Production clock backed by the JavaScript runtime. */
|
|
2
|
+
export const defaultRuntimeClock = Object.freeze({
|
|
3
|
+
now: () => Date.now(),
|
|
4
|
+
setTimeout: (callback, ms) => setTimeout(callback, ms),
|
|
5
|
+
clearTimeout: (timer) => clearTimeout(timer),
|
|
6
|
+
});
|
|
7
|
+
/** Production HTTP client backed by global `fetch`. */
|
|
8
|
+
export const defaultRuntimeHttpClient = Object.freeze({
|
|
9
|
+
fetch: (url, init) => fetch(url, init),
|
|
10
|
+
});
|
|
11
|
+
/** Production runtime services. */
|
|
12
|
+
export const defaultRuntimeServices = Object.freeze({
|
|
13
|
+
clock: defaultRuntimeClock,
|
|
14
|
+
http: defaultRuntimeHttpClient,
|
|
15
|
+
});
|
|
16
|
+
/** Resolve partial runtime service overrides against production defaults. */
|
|
17
|
+
export function resolveRuntimeServices(services = {}) {
|
|
18
|
+
return Object.freeze({
|
|
19
|
+
clock: services.clock ?? defaultRuntimeClock,
|
|
20
|
+
http: services.http ?? defaultRuntimeHttpClient,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Template used by scripts/generate_versions.ts for package version modules.
|
|
3
|
+
*
|
|
4
|
+
* The generator token-replaces the string placeholders below, then formats and
|
|
5
|
+
* compares the rendered output during version checks. Do not edit generated
|
|
6
|
+
* `src/version.ts` files by hand; edit this template instead.
|
|
4
7
|
*/
|
|
5
8
|
/** Package semantic version copied from the owning package manifest. */
|
|
6
|
-
export const PACKAGE_VERSION = "0.
|
|
9
|
+
export const PACKAGE_VERSION = "0.7.0";
|
|
7
10
|
/** Optional build metadata stamp injected by release/CI workflows. */
|
|
8
|
-
export const BUILD_METADATA = "build.
|
|
11
|
+
export const BUILD_METADATA = "build.12.6aeea4790568f27066636c48ec39d2f098e3c26d";
|
|
9
12
|
/** User-facing version string with build metadata appended when present. */
|
|
10
13
|
export const DISPLAY_VERSION = BUILD_METADATA
|
|
11
14
|
? `${PACKAGE_VERSION}+${BUILD_METADATA}`
|
|
@@ -172,6 +172,10 @@ export class Kevery {
|
|
|
172
172
|
else if (sn !== null && !this.db.fullyWitnessed(kever.serder)) {
|
|
173
173
|
return escrowQuery("notFullyWitnessed");
|
|
174
174
|
}
|
|
175
|
+
// Reopened validators may have accepted a delegated event before its
|
|
176
|
+
// `.aess` source-seal hint was learned. Repair that hint before replay
|
|
177
|
+
// so downstream receivers get the same delegation attachment KERIpy
|
|
178
|
+
// would clone from persisted delegating-event state.
|
|
175
179
|
this.repairReplaySourceSeal(kever);
|
|
176
180
|
const msgs = [...this.db.clonePreIter(pre, fn)];
|
|
177
181
|
if (kever.delpre) {
|
|
@@ -557,6 +561,9 @@ export class Kevery {
|
|
|
557
561
|
return this.makeEscrowDecision("duplicitous", init, `Likely duplicitous inception for ${pre}; existing SAID=${kever.said}, got ${said}.`);
|
|
558
562
|
}
|
|
559
563
|
const duplicateSaid = this.db.kels.getLast(pre, sn);
|
|
564
|
+
// A duplicate accepted event may still carry late source-seal or receipt
|
|
565
|
+
// attachments. Preserve those through the duplicate path instead of
|
|
566
|
+
// rejecting the whole replay as already-seen.
|
|
560
567
|
if (duplicateSaid && duplicateSaid === said && sn <= kever.sn) {
|
|
561
568
|
return this.buildDuplicateDecision(kever, init);
|
|
562
569
|
}
|
|
@@ -929,6 +936,9 @@ export class Kevery {
|
|
|
929
936
|
emitAcceptanceCues(kever, serder, local) {
|
|
930
937
|
const habord = this.db.getHab(kever.pre);
|
|
931
938
|
if (habord?.mid) {
|
|
939
|
+
// Group habitats can be learned through remote traffic after reopen.
|
|
940
|
+
// Keep the live group-prefix indexes synchronized with durable habitat
|
|
941
|
+
// records before protected-party cue policy runs.
|
|
932
942
|
this.db.prefixes.add(kever.pre);
|
|
933
943
|
this.db.groups.add(kever.pre);
|
|
934
944
|
}
|
|
@@ -1941,6 +1951,13 @@ export class Kevery {
|
|
|
1941
1951
|
duplicate: "sameSaid",
|
|
1942
1952
|
};
|
|
1943
1953
|
}
|
|
1954
|
+
/**
|
|
1955
|
+
* Decide whether a duplicate event's attached source seal is safe to store.
|
|
1956
|
+
*
|
|
1957
|
+
* Duplicate replay is intentionally narrow: only an existing delegated
|
|
1958
|
+
* establishment event may learn a late source seal, and the seal must name
|
|
1959
|
+
* the exact delegator event already known to anchor this delegate event.
|
|
1960
|
+
*/
|
|
1944
1961
|
validDuplicateSourceSeal(kever, serder, sourceSeal) {
|
|
1945
1962
|
const pre = serder.pre;
|
|
1946
1963
|
const said = serder.said;
|
|
@@ -1956,6 +1973,14 @@ export class Kevery {
|
|
|
1956
1973
|
return anchoring?.snh === sourceSeal.s.numh
|
|
1957
1974
|
&& anchoring.said === sourceSeal.d.qb64;
|
|
1958
1975
|
}
|
|
1976
|
+
/**
|
|
1977
|
+
* Backfill `.aess` for an accepted delegated establishment before replay.
|
|
1978
|
+
*
|
|
1979
|
+
* KERIpy clones delegation attachments from durable state during replay.
|
|
1980
|
+
* `keri-ts` keeps replay construction decoupled, so this helper repairs the
|
|
1981
|
+
* accepted source-seal index from the delegator's sealing event when the
|
|
1982
|
+
* event body was accepted before the hint was persisted.
|
|
1983
|
+
*/
|
|
1959
1984
|
repairReplaySourceSeal(kever) {
|
|
1960
1985
|
const serder = kever.serder;
|
|
1961
1986
|
const pre = serder.pre;
|
|
@@ -1981,6 +2006,14 @@ export class Kevery {
|
|
|
1981
2006
|
new Diger({ qb64: anchoring.said }),
|
|
1982
2007
|
]);
|
|
1983
2008
|
}
|
|
2009
|
+
/**
|
|
2010
|
+
* Repair accepted delegated events after a delegator event is accepted.
|
|
2011
|
+
*
|
|
2012
|
+
* When the newly accepted event contains delegation anchors, any already
|
|
2013
|
+
* stored delegate establishment it names can learn the corresponding
|
|
2014
|
+
* source-seal hint immediately. This keeps later duplicate/replay handling
|
|
2015
|
+
* deterministic without rerunning full event acceptance.
|
|
2016
|
+
*/
|
|
1984
2017
|
repairAnchoredDelegationSourceSeals(serder) {
|
|
1985
2018
|
const delpre = serder.pre;
|
|
1986
2019
|
const said = serder.said;
|
|
@@ -2011,6 +2044,13 @@ export class Kevery {
|
|
|
2011
2044
|
}
|
|
2012
2045
|
}
|
|
2013
2046
|
}
|
|
2047
|
+
/**
|
|
2048
|
+
* Repair source-seal hints for escrowed delegated establishment events.
|
|
2049
|
+
*
|
|
2050
|
+
* Escrow reprocessing may encounter a delegate event after the approving
|
|
2051
|
+
* delegator event was already accepted. This helper converts that discovered
|
|
2052
|
+
* anchor into the same `.aess` hint carried by live source-seal attachments.
|
|
2053
|
+
*/
|
|
2014
2054
|
repairEscrowDelegationSourceSeal(serder) {
|
|
2015
2055
|
const pre = serder.pre;
|
|
2016
2056
|
const said = serder.said;
|
|
@@ -1037,6 +1037,9 @@ export class Kever {
|
|
|
1037
1037
|
// for escrow. Misfit checks come first so locally protected events do not
|
|
1038
1038
|
// leak into a more permissive partial-signature or partial-delegation
|
|
1039
1039
|
// class.
|
|
1040
|
+
// A local delegator reviewing someone else's delegated establishment is a
|
|
1041
|
+
// protected-party case, but it must enter `delegables` until the local
|
|
1042
|
+
// approval interaction/rotation is actually attached as a source seal.
|
|
1040
1043
|
const localDelegatorApprovalCandidate = (input.serder.ilk === Ilks.dip || input.serder.ilk === Ilks.drt)
|
|
1041
1044
|
&& this.locallyDelegated(delpre)
|
|
1042
1045
|
&& !this.locallyOwned();
|
|
@@ -1158,6 +1161,9 @@ export class Kever {
|
|
|
1158
1161
|
// A local delegator without an attached approval seal is not a generic
|
|
1159
1162
|
// partial-delegation case. It is specifically waiting for local
|
|
1160
1163
|
// out-of-band approval to be attached and reprocessed.
|
|
1164
|
+
// Local approval is represented by a real, already accepted delegator
|
|
1165
|
+
// event. The attached source seal is only authoritative when it resolves
|
|
1166
|
+
// to that accepted event and that event's anchors name this delegate event.
|
|
1161
1167
|
const sourceSealApprovesEvent = input.sourceSeal
|
|
1162
1168
|
? this.lookupAcceptedDelegatingEvent(delpre, input.sourceSeal, input.serder) !== null
|
|
1163
1169
|
: false;
|
|
@@ -1403,6 +1409,9 @@ export class Kever {
|
|
|
1403
1409
|
if (!candidate || !candidate.said || candidate.sn === null) {
|
|
1404
1410
|
return null;
|
|
1405
1411
|
}
|
|
1412
|
+
// Accepted first-seen state is the primary signal, but restored KELs may
|
|
1413
|
+
// have authoritative sequence membership before first-seen metadata has
|
|
1414
|
+
// been reconstructed. Accept either durable signal for approval lookup.
|
|
1406
1415
|
const accepted = !!this.db.fons.get(dgKey(delpre, candidate.said))
|
|
1407
1416
|
|| this.db.kels.getLast(delpre, candidate.sn) === candidate.said;
|
|
1408
1417
|
if (!accepted)
|