keri-ts 2.1.0 → 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/README.md +30 -2
- package/esm/keri/npm/src/acdc/authoring.js +195 -0
- package/esm/keri/npm/src/acdc/custody.js +164 -0
- package/esm/keri/npm/src/acdc/dhking.js +244 -32
- package/esm/keri/npm/src/acdc/regbasing.js +10 -0
- package/esm/keri/npm/src/acdc/salting.js +16 -1
- package/esm/keri/npm/src/acdc/scheming.js +93 -54
- package/esm/keri/npm/src/acdc/unblinding.js +118 -95
- package/esm/keri/npm/src/app/bootstrap/authenticating.js +4 -6
- package/esm/keri/npm/src/app/bootstrap/oobi-resolving.js +41 -0
- package/esm/keri/npm/src/app/bootstrap/oobiery.js +4 -6
- package/esm/keri/npm/src/app/credentials/credential-revoking.js +1 -0
- package/esm/keri/npm/src/app/credentials/credential-workflows.js +2 -0
- package/esm/keri/npm/src/app/credentials/registry-settling.js +44 -0
- package/esm/keri/npm/src/app/credentials/root-replicating.js +68 -60
- package/esm/keri/npm/src/app/credentials/root-replicator.js +198 -0
- package/esm/keri/npm/src/app/credentials/tel-completion.js +36 -0
- package/esm/keri/npm/src/app/delivery/forwarding.js +16 -3
- package/esm/keri/npm/src/app/exchanges/exchanging.js +14 -4
- package/esm/keri/npm/src/app/identity/challenging.js +50 -0
- package/esm/keri/npm/src/app/identity/delegating.js +77 -77
- package/esm/keri/npm/src/app/identity/delegation-approval.js +333 -0
- package/esm/keri/npm/src/app/identity/event-completion.js +154 -0
- package/esm/keri/npm/src/app/identity/habbing.js +129 -6
- package/esm/keri/npm/src/app/identity/keeping.js +77 -4
- package/esm/keri/npm/src/app/ipex/group-ipex.js +327 -0
- package/esm/keri/npm/src/app/ipex/group-issuance.js +902 -0
- package/esm/keri/npm/src/app/ipex/ipex-admitting.js +120 -0
- package/esm/keri/npm/src/app/ipex/ipex-conversation.js +197 -0
- package/esm/keri/npm/src/app/ipex/ipex-discloser.js +591 -0
- package/esm/keri/npm/src/app/ipex/ipex-granting.js +93 -0
- package/esm/keri/npm/src/app/ipex/ipex-grouping.js +34 -11
- package/esm/keri/npm/src/app/ipex/issuance.js +888 -0
- package/esm/keri/npm/src/app/ipex/pending-presentations.js +46 -0
- package/esm/keri/npm/src/app/ipex/presentation-files.js +200 -0
- package/esm/keri/npm/src/app/ipex/presenting.js +401 -0
- package/esm/keri/npm/src/app/ipex/receive-issuance.js +86 -0
- package/esm/keri/npm/src/app/ipex/unblinding-workflow.js +660 -0
- package/esm/keri/npm/src/app/mailbox/ipex-arrival.js +103 -0
- package/esm/keri/npm/src/app/mailbox/kel-publisher.js +322 -0
- package/esm/keri/npm/src/app/mailbox/kel-publishing.js +65 -0
- package/esm/keri/npm/src/app/mailbox/kel-submission.js +24 -0
- package/esm/keri/npm/src/app/mailbox/mailbox-admin.js +208 -0
- package/esm/keri/npm/src/app/mailbox/mailbox-poller.js +212 -47
- package/esm/keri/npm/src/app/mailbox/mailboxing.js +10 -5
- package/esm/keri/npm/src/app/multisig/counseling.js +482 -0
- package/esm/keri/npm/src/app/multisig/group-members.js +18 -0
- package/esm/keri/npm/src/app/multisig/multisig-workflows.js +94 -139
- package/esm/keri/npm/src/app/multisig/proposals.js +281 -0
- package/esm/keri/npm/src/app/multisig/proposing.js +122 -0
- package/esm/keri/npm/src/app/notifications/signaling.js +5 -5
- package/esm/keri/npm/src/app/protocol/parsering.js +26 -1
- package/esm/keri/npm/src/app/protocol/protocol-processing.js +9 -3
- package/esm/keri/npm/src/app/queries/query-workflows.js +98 -0
- package/esm/keri/npm/src/app/runtime/agent-runtime.js +106 -55
- package/esm/keri/npm/src/app/runtime/cue-runtime.js +3 -6
- package/esm/keri/npm/src/app/runtime/delivery-outbox.js +87 -0
- package/esm/keri/npm/src/app/runtime/doing.js +17 -0
- package/esm/keri/npm/src/app/runtime/ipex-auxiliaries.js +207 -0
- package/esm/keri/npm/src/app/runtime/operation-owners.js +151 -0
- package/esm/keri/npm/src/app/runtime/retry-backoff.js +93 -0
- package/esm/keri/npm/src/app/runtime/workflow-contract.js +80 -0
- package/esm/keri/npm/src/app/transport/http-client.js +25 -6
- package/esm/keri/npm/src/app/witness/http-endpoints.js +4 -4
- package/esm/keri/npm/src/cli/common/event-completion.js +49 -0
- package/esm/keri/npm/src/cli/common/runtime-workers.js +23 -0
- package/esm/keri/npm/src/cli/credentials/ipex.js +157 -190
- package/esm/keri/npm/src/cli/credentials/vc.js +53 -72
- package/esm/keri/npm/src/cli/endpoints/did.js +12 -3
- package/esm/keri/npm/src/cli/endpoints/oobi.js +10 -13
- package/esm/keri/npm/src/cli/identity/challenge.js +8 -34
- package/esm/keri/npm/src/cli/identity/delegate.js +81 -258
- package/esm/keri/npm/src/cli/identity/incept.js +15 -32
- package/esm/keri/npm/src/cli/identity/interact.js +5 -17
- package/esm/keri/npm/src/cli/identity/rotate.js +8 -36
- package/esm/keri/npm/src/cli/index.js +3 -1
- package/esm/keri/npm/src/cli/messaging/group-join.js +79 -0
- package/esm/keri/npm/src/cli/messaging/group-wait.js +88 -0
- package/esm/keri/npm/src/cli/messaging/multisig.js +163 -284
- package/esm/keri/npm/src/cli/messaging/root-copy-lines.js +31 -0
- package/esm/keri/npm/src/core/kel/eventing.js +66 -10
- package/esm/keri/npm/src/core/mailbox/kram-policy.js +39 -6
- package/esm/keri/npm/src/core/mailbox/kraming.js +2 -0
- package/esm/keri/npm/src/core/records.js +23 -0
- package/esm/keri/npm/src/core/routing.js +13 -0
- package/esm/keri/npm/src/db/basing.js +34 -2
- package/esm/keri/npm/src/db/core/indexeddb-persistence.js +221 -0
- package/esm/keri/npm/src/db/indexeddb.js +98 -0
- package/esm/keri/npm/src/db/keeping.js +11 -2
- package/esm/keri/npm/src/db/store-lock.js +150 -0
- package/esm/keri/npm/src/npm/db-indexeddb.browser.js +2 -0
- package/esm/keri/npm/src/npm/db-indexeddb.js +3 -0
- package/esm/keri/npm/src/npm/db.browser.js +2 -0
- package/esm/keri/npm/src/npm/index.browser.js +9 -0
- package/esm/keri/npm/src/npm/runtime-web.browser.js +2 -0
- package/esm/keri/npm/src/npm/runtime-web.js +3 -0
- package/esm/keri/npm/src/npm/runtime.browser.js +2 -0
- package/esm/keri/npm/src/runtime/index.js +51 -4
- package/esm/keri/npm/src/runtime/web/output-barrier.js +47 -0
- package/esm/keri/npm/src/runtime/web/persistent-storage.js +37 -0
- package/esm/keri/npm/src/runtime/web.js +10 -0
- package/esm/keri/npm/src/vdr/tel-publication.js +16 -0
- package/esm/keri/npm/src/version.js +2 -2
- package/package.json +15 -2
- package/types/acdc/authoring.d.ts +54 -0
- package/types/acdc/authoring.d.ts.map +1 -0
- package/types/acdc/custody.d.ts +77 -0
- package/types/acdc/custody.d.ts.map +1 -0
- package/types/acdc/dhking.d.ts +94 -3
- package/types/acdc/dhking.d.ts.map +1 -1
- package/types/acdc/regbasing.d.ts +4 -0
- package/types/acdc/regbasing.d.ts.map +1 -1
- package/types/acdc/salting.d.ts +6 -1
- package/types/acdc/salting.d.ts.map +1 -1
- package/types/acdc/scheming.d.ts +2 -0
- package/types/acdc/scheming.d.ts.map +1 -1
- package/types/acdc/unblinding.d.ts +58 -19
- package/types/acdc/unblinding.d.ts.map +1 -1
- package/types/app/bootstrap/authenticating.d.ts +3 -1
- package/types/app/bootstrap/authenticating.d.ts.map +1 -1
- package/types/app/bootstrap/oobi-resolving.d.ts +92 -0
- package/types/app/bootstrap/oobi-resolving.d.ts.map +1 -0
- package/types/app/bootstrap/oobiery.d.ts +3 -1
- package/types/app/bootstrap/oobiery.d.ts.map +1 -1
- package/types/app/credentials/credential-revoking.d.ts +3 -0
- package/types/app/credentials/credential-revoking.d.ts.map +1 -1
- package/types/app/credentials/credential-workflows.d.ts +5 -0
- package/types/app/credentials/credential-workflows.d.ts.map +1 -1
- package/types/app/credentials/registry-settling.d.ts +85 -0
- package/types/app/credentials/registry-settling.d.ts.map +1 -0
- package/types/app/credentials/root-replicating.d.ts +56 -44
- package/types/app/credentials/root-replicating.d.ts.map +1 -1
- package/types/app/credentials/root-replicator.d.ts +105 -0
- package/types/app/credentials/root-replicator.d.ts.map +1 -0
- package/types/app/credentials/tel-completion.d.ts +55 -0
- package/types/app/credentials/tel-completion.d.ts.map +1 -0
- package/types/app/delivery/forwarding.d.ts +6 -0
- package/types/app/delivery/forwarding.d.ts.map +1 -1
- package/types/app/exchanges/exchanging.d.ts.map +1 -1
- package/types/app/identity/challenging.d.ts +54 -0
- package/types/app/identity/challenging.d.ts.map +1 -1
- package/types/app/identity/delegating.d.ts +21 -4
- package/types/app/identity/delegating.d.ts.map +1 -1
- package/types/app/identity/delegation-approval.d.ts +228 -0
- package/types/app/identity/delegation-approval.d.ts.map +1 -0
- package/types/app/identity/event-completion.d.ts +151 -0
- package/types/app/identity/event-completion.d.ts.map +1 -0
- package/types/app/identity/habbing.d.ts +67 -2
- package/types/app/identity/habbing.d.ts.map +1 -1
- package/types/app/identity/keeping.d.ts +49 -3
- package/types/app/identity/keeping.d.ts.map +1 -1
- package/types/app/ipex/group-ipex.d.ts +247 -0
- package/types/app/ipex/group-ipex.d.ts.map +1 -0
- package/types/app/ipex/group-issuance.d.ts +281 -0
- package/types/app/ipex/group-issuance.d.ts.map +1 -0
- package/types/app/ipex/ipex-admitting.d.ts +47 -0
- package/types/app/ipex/ipex-admitting.d.ts.map +1 -0
- package/types/app/ipex/ipex-conversation.d.ts +195 -0
- package/types/app/ipex/ipex-conversation.d.ts.map +1 -0
- package/types/app/ipex/ipex-discloser.d.ts +156 -0
- package/types/app/ipex/ipex-discloser.d.ts.map +1 -0
- package/types/app/ipex/ipex-granting.d.ts +145 -0
- package/types/app/ipex/ipex-granting.d.ts.map +1 -0
- package/types/app/ipex/ipex-grouping.d.ts +18 -0
- package/types/app/ipex/ipex-grouping.d.ts.map +1 -1
- package/types/app/ipex/issuance.d.ts +443 -0
- package/types/app/ipex/issuance.d.ts.map +1 -0
- package/types/app/ipex/pending-presentations.d.ts +25 -0
- package/types/app/ipex/pending-presentations.d.ts.map +1 -0
- package/types/app/ipex/presentation-files.d.ts +47 -0
- package/types/app/ipex/presentation-files.d.ts.map +1 -0
- package/types/app/ipex/presenting.d.ts +298 -0
- package/types/app/ipex/presenting.d.ts.map +1 -0
- package/types/app/ipex/receive-issuance.d.ts +99 -0
- package/types/app/ipex/receive-issuance.d.ts.map +1 -0
- package/types/app/ipex/unblinding-workflow.d.ts +255 -0
- package/types/app/ipex/unblinding-workflow.d.ts.map +1 -0
- package/types/app/mailbox/ipex-arrival.d.ts +104 -0
- package/types/app/mailbox/ipex-arrival.d.ts.map +1 -0
- package/types/app/mailbox/kel-publisher.d.ts +141 -0
- package/types/app/mailbox/kel-publisher.d.ts.map +1 -0
- package/types/app/mailbox/kel-publishing.d.ts +70 -0
- package/types/app/mailbox/kel-publishing.d.ts.map +1 -0
- package/types/app/mailbox/kel-submission.d.ts +18 -0
- package/types/app/mailbox/kel-submission.d.ts.map +1 -0
- package/types/app/mailbox/mailbox-admin.d.ts +113 -0
- package/types/app/mailbox/mailbox-admin.d.ts.map +1 -0
- package/types/app/mailbox/mailbox-poller.d.ts +92 -3
- package/types/app/mailbox/mailbox-poller.d.ts.map +1 -1
- package/types/app/mailbox/mailboxing.d.ts +3 -0
- package/types/app/mailbox/mailboxing.d.ts.map +1 -1
- package/types/app/multisig/counseling.d.ts +196 -0
- package/types/app/multisig/counseling.d.ts.map +1 -0
- package/types/app/multisig/group-members.d.ts +11 -0
- package/types/app/multisig/group-members.d.ts.map +1 -1
- package/types/app/multisig/grouping.d.ts +39 -20
- package/types/app/multisig/grouping.d.ts.map +1 -1
- package/types/app/multisig/multisig-workflows.d.ts +91 -22
- package/types/app/multisig/multisig-workflows.d.ts.map +1 -1
- package/types/app/multisig/proposals.d.ts +129 -0
- package/types/app/multisig/proposals.d.ts.map +1 -0
- package/types/app/multisig/proposing.d.ts +125 -0
- package/types/app/multisig/proposing.d.ts.map +1 -0
- package/types/app/notifications/signaling.d.ts +3 -1
- package/types/app/notifications/signaling.d.ts.map +1 -1
- package/types/app/protocol/parsering.d.ts +13 -0
- package/types/app/protocol/parsering.d.ts.map +1 -1
- package/types/app/protocol/protocol-processing.d.ts +7 -1
- package/types/app/protocol/protocol-processing.d.ts.map +1 -1
- package/types/app/queries/query-workflows.d.ts +98 -0
- package/types/app/queries/query-workflows.d.ts.map +1 -0
- package/types/app/runtime/agent-runtime.d.ts +72 -25
- package/types/app/runtime/agent-runtime.d.ts.map +1 -1
- package/types/app/runtime/cue-runtime.d.ts +2 -1
- package/types/app/runtime/cue-runtime.d.ts.map +1 -1
- package/types/app/runtime/delivery-outbox.d.ts +68 -0
- package/types/app/runtime/delivery-outbox.d.ts.map +1 -0
- package/types/app/runtime/doing.d.ts +27 -0
- package/types/app/runtime/doing.d.ts.map +1 -0
- package/types/app/runtime/ipex-auxiliaries.d.ts +74 -0
- package/types/app/runtime/ipex-auxiliaries.d.ts.map +1 -0
- package/types/app/runtime/operation-owners.d.ts +137 -0
- package/types/app/runtime/operation-owners.d.ts.map +1 -0
- package/types/app/runtime/retry-backoff.d.ts +49 -0
- package/types/app/runtime/retry-backoff.d.ts.map +1 -0
- package/types/app/runtime/workflow-contract.d.ts +79 -0
- package/types/app/runtime/workflow-contract.d.ts.map +1 -0
- package/types/app/transport/http-client.d.ts.map +1 -1
- package/types/cli/common/event-completion.d.ts +26 -0
- package/types/cli/common/event-completion.d.ts.map +1 -0
- package/types/cli/common/runtime-workers.d.ts +23 -0
- package/types/cli/common/runtime-workers.d.ts.map +1 -0
- package/types/cli/credentials/ipex.d.ts.map +1 -1
- package/types/cli/credentials/vc.d.ts +1 -1
- package/types/cli/credentials/vc.d.ts.map +1 -1
- package/types/cli/endpoints/did.d.ts.map +1 -1
- package/types/cli/endpoints/oobi.d.ts +3 -1
- package/types/cli/endpoints/oobi.d.ts.map +1 -1
- package/types/cli/identity/challenge.d.ts +1 -1
- package/types/cli/identity/challenge.d.ts.map +1 -1
- package/types/cli/identity/delegate.d.ts +14 -1
- package/types/cli/identity/delegate.d.ts.map +1 -1
- package/types/cli/identity/incept.d.ts.map +1 -1
- package/types/cli/identity/interact.d.ts.map +1 -1
- package/types/cli/identity/rotate.d.ts.map +1 -1
- package/types/cli/index.d.ts +3 -1
- package/types/cli/index.d.ts.map +1 -1
- package/types/cli/messaging/group-join.d.ts +53 -0
- package/types/cli/messaging/group-join.d.ts.map +1 -0
- package/types/cli/messaging/group-wait.d.ts +34 -0
- package/types/cli/messaging/group-wait.d.ts.map +1 -0
- package/types/cli/messaging/multisig.d.ts +28 -5
- package/types/cli/messaging/multisig.d.ts.map +1 -1
- package/types/cli/messaging/root-copy-lines.d.ts +39 -0
- package/types/cli/messaging/root-copy-lines.d.ts.map +1 -0
- package/types/core/cues.d.ts +11 -0
- package/types/core/cues.d.ts.map +1 -1
- package/types/core/kel/eventing.d.ts.map +1 -1
- package/types/core/mailbox/kram-policy.d.ts +14 -0
- package/types/core/mailbox/kram-policy.d.ts.map +1 -1
- package/types/core/mailbox/kraming.d.ts +1 -1
- package/types/core/mailbox/kraming.d.ts.map +1 -1
- package/types/core/records.d.ts +61 -0
- package/types/core/records.d.ts.map +1 -1
- package/types/core/routing.d.ts +6 -0
- package/types/core/routing.d.ts.map +1 -1
- package/types/db/basing.d.ts +8 -2
- package/types/db/basing.d.ts.map +1 -1
- package/types/db/core/indexeddb-persistence.d.ts +59 -0
- package/types/db/core/indexeddb-persistence.d.ts.map +1 -0
- package/types/db/indexeddb.d.ts +37 -0
- package/types/db/indexeddb.d.ts.map +1 -0
- package/types/db/keeping.d.ts +5 -2
- package/types/db/keeping.d.ts.map +1 -1
- package/types/db/store-lock.d.ts +72 -0
- package/types/db/store-lock.d.ts.map +1 -0
- package/types/npm/db-indexeddb.d.ts +4 -0
- package/types/npm/db-indexeddb.d.ts.map +1 -0
- package/types/npm/runtime-web.d.ts +4 -0
- package/types/npm/runtime-web.d.ts.map +1 -0
- package/types/runtime/index.d.ts +41 -6
- package/types/runtime/index.d.ts.map +1 -1
- package/types/runtime/web/output-barrier.d.ts +30 -0
- package/types/runtime/web/output-barrier.d.ts.map +1 -0
- package/types/runtime/web/persistent-storage.d.ts +36 -0
- package/types/runtime/web/persistent-storage.d.ts.map +1 -0
- package/types/runtime/web.d.ts +11 -0
- package/types/runtime/web.d.ts.map +1 -0
- package/types/vdr/tel-publication.d.ts +6 -0
- package/types/vdr/tel-publication.d.ts.map +1 -1
- package/types/version.d.ts +2 -2
- package/esm/keri/npm/src/cli/common/tel-completion.js +0 -24
- package/types/cli/common/tel-completion.d.ts +0 -8
- package/types/cli/common/tel-completion.d.ts.map +0 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { DEFAULT_RETRY_POLICY, RetryBackoff } from "./retry-backoff.js";
|
|
2
|
+
/** Flatten an error and its `cause` chain to one line of text, so a reader sees the transport's own reason. */
|
|
3
|
+
export function describeFailure(error) {
|
|
4
|
+
const parts = [];
|
|
5
|
+
for (let e = error, depth = 0; e !== undefined && e !== null && depth < 8; depth++) {
|
|
6
|
+
parts.push(e instanceof Error ? (e.message || e.name) : String(e));
|
|
7
|
+
e = e instanceof Error ? e.cause : undefined;
|
|
8
|
+
}
|
|
9
|
+
return parts.join(" <- ");
|
|
10
|
+
}
|
|
11
|
+
/** The queue and its retry state for one live operation. */
|
|
12
|
+
export class DeliveryOutbox {
|
|
13
|
+
constructor(policy = DEFAULT_RETRY_POLICY) {
|
|
14
|
+
Object.defineProperty(this, "queue", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
configurable: true,
|
|
17
|
+
writable: true,
|
|
18
|
+
value: []
|
|
19
|
+
});
|
|
20
|
+
Object.defineProperty(this, "backoff", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
value: void 0
|
|
25
|
+
});
|
|
26
|
+
/** Destinations the transport reported for everything delivered so far. */
|
|
27
|
+
Object.defineProperty(this, "delivered", {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
configurable: true,
|
|
30
|
+
writable: true,
|
|
31
|
+
value: []
|
|
32
|
+
});
|
|
33
|
+
this.backoff = new RetryBackoff(policy);
|
|
34
|
+
}
|
|
35
|
+
/** Messages still waiting. */
|
|
36
|
+
get length() {
|
|
37
|
+
return this.queue.length;
|
|
38
|
+
}
|
|
39
|
+
/** Consecutive delivery failures of the head message. */
|
|
40
|
+
get attempts() {
|
|
41
|
+
return this.backoff.attempts;
|
|
42
|
+
}
|
|
43
|
+
/** The latest delivery error, or null. */
|
|
44
|
+
get lastError() {
|
|
45
|
+
return this.backoff.last;
|
|
46
|
+
}
|
|
47
|
+
/** The retry state of the head message, or null while nothing is failing. */
|
|
48
|
+
get failure() {
|
|
49
|
+
return this.backoff.attempts === 0
|
|
50
|
+
? null
|
|
51
|
+
: {
|
|
52
|
+
attempts: this.backoff.attempts,
|
|
53
|
+
reason: describeFailure(this.backoff.last),
|
|
54
|
+
retryAt: new Date(this.backoff.next).toISOString(),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** Queue a message behind the others. */
|
|
58
|
+
enqueue(message) {
|
|
59
|
+
this.queue.push(message);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Deliver the queued messages in order. A failure keeps the message and schedules a retry; it throws only when
|
|
63
|
+
* `boundMs` is given and failures have lasted that long.
|
|
64
|
+
*
|
|
65
|
+
* @returns True when the queue is empty, false while a message waits for its retry.
|
|
66
|
+
*/
|
|
67
|
+
*drain(transport, clock, boundMs) {
|
|
68
|
+
while (this.queue.length) {
|
|
69
|
+
const now = clock.now();
|
|
70
|
+
if (!this.backoff.due(now))
|
|
71
|
+
return false;
|
|
72
|
+
try {
|
|
73
|
+
const { src, dest, msg, topic } = this.queue[0];
|
|
74
|
+
this.delivered.push(...yield* transport.deliver({ src, dest, msg: typeof msg === "function" ? msg() : msg, topic }));
|
|
75
|
+
this.queue.shift();
|
|
76
|
+
this.backoff.succeeded();
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
this.backoff.failed(now, error);
|
|
80
|
+
if (boundMs !== undefined && this.backoff.exhausted(now, boundMs))
|
|
81
|
+
throw error;
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { waitOnClock } from "./workflow-contract.js";
|
|
2
|
+
/** Milliseconds between passes of the protocol-facing background loops: KERIpy's Doist cycle, as the escrow loop uses. */
|
|
3
|
+
export const PROTOCOL_TOCK_MS = 31.25;
|
|
4
|
+
/** Tocks between full sweeps of a loop that has one: 1920 tocks of 31.25 ms is a minute. */
|
|
5
|
+
export const PROTOCOL_SWEEP_TOCKS = 1920;
|
|
6
|
+
/** The production pacing of the protocol-facing background loops. */
|
|
7
|
+
export const PROTOCOL_PACING = Object.freeze({
|
|
8
|
+
tockMs: PROTOCOL_TOCK_MS,
|
|
9
|
+
sweepTocks: PROTOCOL_SWEEP_TOCKS,
|
|
10
|
+
});
|
|
11
|
+
/** Run `pass` forever: pass 0, wait `tockMs` on `clock`, pass 1, and so on. A thrown error ends the doer. */
|
|
12
|
+
export function* doEvery(clock, tockMs, pass) {
|
|
13
|
+
for (let tock = 0;; tock++) {
|
|
14
|
+
yield* pass(tock);
|
|
15
|
+
yield* waitOnClock(clock, tockMs);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { Dhke } from "../../acdc/dhking.js";
|
|
2
|
+
import { Unblind } from "../../acdc/unblinding.js";
|
|
3
|
+
import { ValidationError } from "../../core/errors.js";
|
|
4
|
+
import { isLocalGroupHab } from "../multisig/group-members.js";
|
|
5
|
+
/** Per-identifier DH and Unblind owners behind one query receiver and one reply route each. */
|
|
6
|
+
export class IpexAuxiliaries {
|
|
7
|
+
/**
|
|
8
|
+
* @param exc Exchanger shared by every owner.
|
|
9
|
+
* @param registry Public registry observer shared by every owner.
|
|
10
|
+
* @param rvy Reply verifier whose router the owners' routes are registered on.
|
|
11
|
+
* @param now Receiver clock shared by every owner.
|
|
12
|
+
* @param kram Whether KRAM is enabled; with it off, `approve` of either owner throws.
|
|
13
|
+
*/
|
|
14
|
+
constructor(exc, registry, rvy, now,
|
|
15
|
+
/** Whether the runtime's KRAM is on; DH and Unblind approval need the rows only KRAM writes. */
|
|
16
|
+
kram) {
|
|
17
|
+
Object.defineProperty(this, "exc", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: exc
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(this, "registry", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: registry
|
|
28
|
+
});
|
|
29
|
+
Object.defineProperty(this, "rvy", {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
configurable: true,
|
|
32
|
+
writable: true,
|
|
33
|
+
value: rvy
|
|
34
|
+
});
|
|
35
|
+
Object.defineProperty(this, "now", {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
configurable: true,
|
|
38
|
+
writable: true,
|
|
39
|
+
value: now
|
|
40
|
+
});
|
|
41
|
+
Object.defineProperty(this, "kram", {
|
|
42
|
+
enumerable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
writable: true,
|
|
45
|
+
value: kram
|
|
46
|
+
});
|
|
47
|
+
/** Unused: `claims` decides which queries this receiver takes. */
|
|
48
|
+
Object.defineProperty(this, "resource", {
|
|
49
|
+
enumerable: true,
|
|
50
|
+
configurable: true,
|
|
51
|
+
writable: true,
|
|
52
|
+
value: "/ipex"
|
|
53
|
+
});
|
|
54
|
+
Object.defineProperty(this, "dhkes", {
|
|
55
|
+
enumerable: true,
|
|
56
|
+
configurable: true,
|
|
57
|
+
writable: true,
|
|
58
|
+
value: new Map()
|
|
59
|
+
});
|
|
60
|
+
Object.defineProperty(this, "unblinds", {
|
|
61
|
+
enumerable: true,
|
|
62
|
+
configurable: true,
|
|
63
|
+
writable: true,
|
|
64
|
+
value: new Map()
|
|
65
|
+
});
|
|
66
|
+
Object.defineProperty(this, "restored", {
|
|
67
|
+
enumerable: true,
|
|
68
|
+
configurable: true,
|
|
69
|
+
writable: true,
|
|
70
|
+
value: false
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/** Every live DH owner, for hosts draining their completion cues. */
|
|
74
|
+
dhkeOwners() {
|
|
75
|
+
return this.dhkes.values();
|
|
76
|
+
}
|
|
77
|
+
/** Every live Unblind owner, for hosts delivering the replies they owe. */
|
|
78
|
+
unblindOwners() {
|
|
79
|
+
if (!this.restored) {
|
|
80
|
+
// A request persisted before a restart is owed its reply by an owner nothing has created yet.
|
|
81
|
+
this.restored = true;
|
|
82
|
+
const owed = new Set([...this.exc.hby.db.heldu.getTopItemIter()].map(([[pre]]) => pre));
|
|
83
|
+
for (const pre of owed) {
|
|
84
|
+
const hab = this.exc.hby.habs.get(pre);
|
|
85
|
+
if (!hab)
|
|
86
|
+
continue;
|
|
87
|
+
try {
|
|
88
|
+
this.unblind(hab);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (!(error instanceof ValidationError))
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return this.unblinds.values();
|
|
97
|
+
}
|
|
98
|
+
/** Register the DH and Unblind reply routes once; owners are looked up on each reply. */
|
|
99
|
+
registerReplyRoutes(rtr) {
|
|
100
|
+
rtr.addRoute(Dhke.Route, { processReply: (args) => this.reply("dhke", args) });
|
|
101
|
+
rtr.addRoute(Unblind.Route, { processReply: (args) => this.reply("unblind", args) });
|
|
102
|
+
}
|
|
103
|
+
/** The DH owner for `hab`, created on first use and recreated when `hab` is no longer the owner's object. */
|
|
104
|
+
dhke(hab) {
|
|
105
|
+
const current = this.dhkes.get(hab.pre);
|
|
106
|
+
if (current?.hab === hab)
|
|
107
|
+
return current;
|
|
108
|
+
const owner = new Dhke({
|
|
109
|
+
hab,
|
|
110
|
+
exc: this.exc,
|
|
111
|
+
registryStore: this.registry.store,
|
|
112
|
+
rvy: this.rvy,
|
|
113
|
+
now: this.now,
|
|
114
|
+
kram: this.kram,
|
|
115
|
+
});
|
|
116
|
+
current?.close();
|
|
117
|
+
this.dhkes.set(hab.pre, owner);
|
|
118
|
+
return owner;
|
|
119
|
+
}
|
|
120
|
+
/** The Unblind owner for `hab`, created on first use and recreated when `hab` is no longer the owner's object. */
|
|
121
|
+
unblind(hab) {
|
|
122
|
+
const current = this.unblinds.get(hab.pre);
|
|
123
|
+
if (current?.hab === hab)
|
|
124
|
+
return current;
|
|
125
|
+
const owner = new Unblind({ hab, exc: this.exc, registry: this.registry, rvy: this.rvy, kram: this.kram });
|
|
126
|
+
current?.close();
|
|
127
|
+
this.unblinds.set(hab.pre, owner);
|
|
128
|
+
return owner;
|
|
129
|
+
}
|
|
130
|
+
/** Close and unregister both owners of `pre`; process-local approvals and retries are cleared. */
|
|
131
|
+
release(pre) {
|
|
132
|
+
this.restored = false;
|
|
133
|
+
for (const owners of [this.dhkes, this.unblinds]) {
|
|
134
|
+
owners.get(pre)?.close();
|
|
135
|
+
owners.delete(pre);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** Close every owner; the runtime calls this on explicit close and scope exit. */
|
|
139
|
+
close() {
|
|
140
|
+
for (const owner of [...this.dhkes.values(), ...this.unblinds.values()])
|
|
141
|
+
owner.close();
|
|
142
|
+
this.dhkes.clear();
|
|
143
|
+
this.unblinds.clear();
|
|
144
|
+
}
|
|
145
|
+
claims(serder) {
|
|
146
|
+
return this.select(serder) !== null;
|
|
147
|
+
}
|
|
148
|
+
processQuery(envelope) {
|
|
149
|
+
return this.select(envelope.serder)?.processQuery(envelope) ?? { kind: "drop", reason: "missingProcessor" };
|
|
150
|
+
}
|
|
151
|
+
reply(kind, args) {
|
|
152
|
+
const owner = this.claimant(kind, args.serder.pre, args.serder.sad?.a?.x);
|
|
153
|
+
if (!owner)
|
|
154
|
+
throw new ValidationError(`No resource is registered to handle route ${args.route}`);
|
|
155
|
+
return owner.processReply(args);
|
|
156
|
+
}
|
|
157
|
+
select(serder) {
|
|
158
|
+
const kind = serder.route === Dhke.Route ? "dhke" : serder.route === Unblind.Route ? "unblind" : null;
|
|
159
|
+
return kind && this.claimant(kind, serder.pre, serder.sad?.q?.x);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* The owner of the one local identifier party to the transaction, created now if needed; absent or
|
|
163
|
+
* ambiguous means unowned. Parties come from live owners (approved operations) and from the store.
|
|
164
|
+
*/
|
|
165
|
+
claimant(kind, sender, xid) {
|
|
166
|
+
if (!sender || typeof xid !== "string")
|
|
167
|
+
return null;
|
|
168
|
+
const owners = kind === "dhke" ? this.dhkes : this.unblinds;
|
|
169
|
+
const habs = this.exc.hby.habs;
|
|
170
|
+
const parties = new Set();
|
|
171
|
+
for (const [pre, owner] of owners)
|
|
172
|
+
if (owner.inTransaction(sender, xid))
|
|
173
|
+
parties.add(pre);
|
|
174
|
+
if (kind === "dhke") {
|
|
175
|
+
for (const recipient of Dhke.recipients(this.exc.hby.db, sender, xid)) {
|
|
176
|
+
if (habs.has(recipient))
|
|
177
|
+
parties.add(recipient);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
for (const pre of habs.keys()) {
|
|
182
|
+
if (Unblind.recipients(this.exc.hby.db, pre, xid).includes(sender))
|
|
183
|
+
parties.add(pre);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const group = habs.get(sender);
|
|
187
|
+
if (group && isLocalGroupHab(this.exc.hby, group))
|
|
188
|
+
parties.add(sender);
|
|
189
|
+
if (parties.size !== 1)
|
|
190
|
+
return null;
|
|
191
|
+
const [pre] = parties;
|
|
192
|
+
const hab = habs.get(pre);
|
|
193
|
+
if (!hab) {
|
|
194
|
+
this.release(pre);
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
return kind === "dhke" ? this.dhke(hab) : this.unblind(hab);
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
// A group without a local signing member cannot own the route.
|
|
202
|
+
if (error instanceof ValidationError)
|
|
203
|
+
return null;
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { BaserDerivedUpdateStore } from "../../acdc/custody.js";
|
|
2
|
+
import { RootReplicator } from "../credentials/root-replicator.js";
|
|
3
|
+
import { GroupIpexOperations } from "../ipex/group-ipex.js";
|
|
4
|
+
import { IssuanceOperations, posterDelivery } from "../ipex/issuance.js";
|
|
5
|
+
import { PresentationOperations } from "../ipex/presenting.js";
|
|
6
|
+
import { UnblindingOperations } from "../ipex/unblinding-workflow.js";
|
|
7
|
+
import { doEvery } from "./doing.js";
|
|
8
|
+
/** Milliseconds between owner passes; the cadence of the CLI drivers the loop replaced. */
|
|
9
|
+
export const OWNER_TOCK_MS = 50;
|
|
10
|
+
/** Tocks between the replicator's full rotation checks: 1200 tocks of 50 ms is a minute. */
|
|
11
|
+
export const OWNER_SWEEP_TOCKS = 1200;
|
|
12
|
+
/** The production pacing of the owner loop. */
|
|
13
|
+
export const OWNER_PACING = Object.freeze({
|
|
14
|
+
tockMs: OWNER_TOCK_MS,
|
|
15
|
+
sweepTocks: OWNER_SWEEP_TOCKS,
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* Failures of the owner loop itself. A failure of one operation (a delivery that throws, an unreadable stored
|
|
19
|
+
* Grant) ends that operation as `failed` and is never recorded here; this records only what escaped an owner
|
|
20
|
+
* pass, which the loop survives so that the other owners keep running. `faults` counts them and `last` keeps
|
|
21
|
+
* the latest. Nothing clears it: a host reads it and decides.
|
|
22
|
+
*/
|
|
23
|
+
export class OwnerHealth {
|
|
24
|
+
constructor() {
|
|
25
|
+
Object.defineProperty(this, "faults", {
|
|
26
|
+
enumerable: true,
|
|
27
|
+
configurable: true,
|
|
28
|
+
writable: true,
|
|
29
|
+
value: 0
|
|
30
|
+
});
|
|
31
|
+
Object.defineProperty(this, "last", {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
configurable: true,
|
|
34
|
+
writable: true,
|
|
35
|
+
value: null
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Construct the owners over their in-memory tables; the derivation store defaults to the registry database. */
|
|
40
|
+
export function composeOwners(parts, stores = {}) {
|
|
41
|
+
const { hby, acdc, exc, protocol, reactor, postman, receiptor, ipex, clock, registryRoot, mux, counselor } = parts;
|
|
42
|
+
const changes = stores.changes ?? parts.changes, delivery = posterDelivery(postman);
|
|
43
|
+
const issuance = {
|
|
44
|
+
hby,
|
|
45
|
+
rgy: acdc,
|
|
46
|
+
exc,
|
|
47
|
+
receiptor,
|
|
48
|
+
dhkes: ipex,
|
|
49
|
+
reactor,
|
|
50
|
+
protocol,
|
|
51
|
+
delivery,
|
|
52
|
+
derived: stores.derived ?? new BaserDerivedUpdateStore(acdc.store.baser),
|
|
53
|
+
clock,
|
|
54
|
+
changes,
|
|
55
|
+
retry: parts.retry,
|
|
56
|
+
queryResendMs: parts.queryResendMs,
|
|
57
|
+
group: { mux, registry: { hby, protocol, postman, counselor, registryStore: acdc.store } },
|
|
58
|
+
};
|
|
59
|
+
const operations = {
|
|
60
|
+
issuance: new IssuanceOperations(issuance),
|
|
61
|
+
groupIpex: new GroupIpexOperations({
|
|
62
|
+
hby,
|
|
63
|
+
rgy: acdc,
|
|
64
|
+
exc,
|
|
65
|
+
protocol,
|
|
66
|
+
postman,
|
|
67
|
+
clock,
|
|
68
|
+
registry: { hby, protocol, postman, counselor, registryStore: acdc.store },
|
|
69
|
+
changes,
|
|
70
|
+
}),
|
|
71
|
+
presentation: new PresentationOperations({
|
|
72
|
+
hby,
|
|
73
|
+
rgy: acdc,
|
|
74
|
+
exc,
|
|
75
|
+
receiptor,
|
|
76
|
+
reactor,
|
|
77
|
+
protocol,
|
|
78
|
+
delivery,
|
|
79
|
+
clock,
|
|
80
|
+
derived: issuance.derived,
|
|
81
|
+
views: stores.views,
|
|
82
|
+
retry: parts.retry,
|
|
83
|
+
changes,
|
|
84
|
+
}),
|
|
85
|
+
health: new OwnerHealth(),
|
|
86
|
+
unblinding: new UnblindingOperations({
|
|
87
|
+
hby,
|
|
88
|
+
exc,
|
|
89
|
+
unblinds: ipex,
|
|
90
|
+
group: { mux, protocol },
|
|
91
|
+
delivery,
|
|
92
|
+
clock,
|
|
93
|
+
retry: parts.retry,
|
|
94
|
+
changes,
|
|
95
|
+
}),
|
|
96
|
+
};
|
|
97
|
+
const rootReplicator = registryRoot ? new RootReplicator({ root: registryRoot, postman, clock, changes }) : null;
|
|
98
|
+
return { operations, rootReplicator };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Run every owner on one fiber until halted, as a fixed-tock doer (`./doing.ts`): each tock runs an owner that
|
|
102
|
+
* has live work, then the replicator (after prompting it with the live DH owners' `replicated` cues), and then
|
|
103
|
+
* the loop waits one tock. Work begun between tocks is seen on the next one; nothing wakes the loop early.
|
|
104
|
+
*
|
|
105
|
+
* Idle cost, per tock: four constant-time table checks, one in-memory walk of the Unblind owners for an owed
|
|
106
|
+
* reply, one drain of each DH owner's cue deck, and the replicator's emptiness checks. The replicator's
|
|
107
|
+
* rotation check (the establishment SAID of each local group, one keyed read per local identifier) runs on
|
|
108
|
+
* tock 0 and every `pacing.sweepTocks`. No owner reads a protocol store. Only the first pass, once, scans the
|
|
109
|
+
* pending exchange escrow for resumable Grants.
|
|
110
|
+
*
|
|
111
|
+
* A failure that escapes an owner pass is recorded in `operations.health` and the loop keeps running, so
|
|
112
|
+
* one owner's fault never stops the others. The owners run one after another, so a delivery that does not return holds the others up until it fails or
|
|
113
|
+
* its Grant deadline passes; operations already bound each delivery by that deadline.
|
|
114
|
+
*/
|
|
115
|
+
export function* operationOwnersDo(owners, pacing = OWNER_PACING) {
|
|
116
|
+
const { operations, rootReplicator, ipex, clock } = owners, { health } = operations;
|
|
117
|
+
const guarded = function* (owner, pass) {
|
|
118
|
+
try {
|
|
119
|
+
yield* pass();
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
health.faults++;
|
|
123
|
+
health.last = {
|
|
124
|
+
owner,
|
|
125
|
+
error: error instanceof Error ? error.message : String(error),
|
|
126
|
+
at: new Date(clock.now()).toISOString(),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
return yield* doEvery(clock, pacing.tockMs, function* (tock) {
|
|
131
|
+
if (operations.issuance.active())
|
|
132
|
+
yield* guarded("issuance", () => operations.issuance.processOnce());
|
|
133
|
+
if (operations.groupIpex.active())
|
|
134
|
+
yield* guarded("groupIpex", () => operations.groupIpex.processOnce());
|
|
135
|
+
if (operations.presentation.active())
|
|
136
|
+
yield* guarded("presentation", () => operations.presentation.processOnce());
|
|
137
|
+
if (operations.unblinding.active())
|
|
138
|
+
yield* guarded("unblinding", () => operations.unblinding.processOnce());
|
|
139
|
+
if (rootReplicator) {
|
|
140
|
+
yield* guarded("replicator", function* () {
|
|
141
|
+
for (const dh of ipex.dhkeOwners()) {
|
|
142
|
+
while (dh.replicated.length)
|
|
143
|
+
rootReplicator.replicate(dh.replicated.pull());
|
|
144
|
+
}
|
|
145
|
+
if (tock % pacing.sweepTocks === 0)
|
|
146
|
+
rootReplicator.sweep();
|
|
147
|
+
yield* rootReplicator.processDue();
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry state for a transient delivery failure inside a live operation.
|
|
3
|
+
*
|
|
4
|
+
* A member that is briefly offline is normal, so an owner keeps the unsent work, waits with a doubling delay and
|
|
5
|
+
* tries again on a later pass instead of ending the operation at the first error. The owner decides when retrying
|
|
6
|
+
* stops: an operation with a deadline ends at the deadline, and one without ends once failures have lasted the
|
|
7
|
+
* bound it states.
|
|
8
|
+
*/
|
|
9
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
10
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
11
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
12
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
13
|
+
};
|
|
14
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
15
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
16
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
17
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
18
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
19
|
+
};
|
|
20
|
+
var _RetryBackoff_next;
|
|
21
|
+
/** Delay after the first failure; it doubles per consecutive failure up to {@link RETRY_MAX_MS}. */
|
|
22
|
+
export const RETRY_BASE_MS = 1_000;
|
|
23
|
+
/** Longest delay between attempts. */
|
|
24
|
+
export const RETRY_MAX_MS = 30_000;
|
|
25
|
+
/** How long failures may last before an owner with no deadline of its own gives up. */
|
|
26
|
+
export const RETRY_BOUND_MS = 600_000;
|
|
27
|
+
/** The production delays of delivery retries. */
|
|
28
|
+
export const DEFAULT_RETRY_POLICY = Object.freeze({
|
|
29
|
+
baseMs: RETRY_BASE_MS,
|
|
30
|
+
maxMs: RETRY_MAX_MS,
|
|
31
|
+
boundMs: RETRY_BOUND_MS,
|
|
32
|
+
});
|
|
33
|
+
/** Consecutive-failure counter with a next-attempt time; memory only. */
|
|
34
|
+
export class RetryBackoff {
|
|
35
|
+
constructor(policy = DEFAULT_RETRY_POLICY) {
|
|
36
|
+
Object.defineProperty(this, "policy", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
configurable: true,
|
|
39
|
+
writable: true,
|
|
40
|
+
value: policy
|
|
41
|
+
});
|
|
42
|
+
/** Consecutive failures since the last success. */
|
|
43
|
+
Object.defineProperty(this, "attempts", {
|
|
44
|
+
enumerable: true,
|
|
45
|
+
configurable: true,
|
|
46
|
+
writable: true,
|
|
47
|
+
value: 0
|
|
48
|
+
});
|
|
49
|
+
/** Time of the first failure of the current run (host clock, ms); 0 when none. */
|
|
50
|
+
Object.defineProperty(this, "since", {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
configurable: true,
|
|
53
|
+
writable: true,
|
|
54
|
+
value: 0
|
|
55
|
+
});
|
|
56
|
+
/** Error of the latest failure. */
|
|
57
|
+
Object.defineProperty(this, "last", {
|
|
58
|
+
enumerable: true,
|
|
59
|
+
configurable: true,
|
|
60
|
+
writable: true,
|
|
61
|
+
value: null
|
|
62
|
+
});
|
|
63
|
+
_RetryBackoff_next.set(this, 0);
|
|
64
|
+
}
|
|
65
|
+
/** Host-clock time (ms) of the next attempt. */
|
|
66
|
+
get next() {
|
|
67
|
+
return __classPrivateFieldGet(this, _RetryBackoff_next, "f");
|
|
68
|
+
}
|
|
69
|
+
/** Whether an attempt may be made at `now`. */
|
|
70
|
+
due(now) {
|
|
71
|
+
return now >= __classPrivateFieldGet(this, _RetryBackoff_next, "f");
|
|
72
|
+
}
|
|
73
|
+
/** Record a failure at `now` and schedule the next attempt. */
|
|
74
|
+
failed(now, error) {
|
|
75
|
+
if (this.attempts === 0)
|
|
76
|
+
this.since = now;
|
|
77
|
+
this.attempts++;
|
|
78
|
+
this.last = error;
|
|
79
|
+
__classPrivateFieldSet(this, _RetryBackoff_next, now + Math.min(this.policy.baseMs * 2 ** (this.attempts - 1), this.policy.maxMs), "f");
|
|
80
|
+
}
|
|
81
|
+
/** Whether failures have lasted at least `boundMs` by `now`. */
|
|
82
|
+
exhausted(now, boundMs) {
|
|
83
|
+
return this.attempts > 0 && now - this.since >= boundMs;
|
|
84
|
+
}
|
|
85
|
+
/** Forget failures after a success. */
|
|
86
|
+
succeeded() {
|
|
87
|
+
this.attempts = 0;
|
|
88
|
+
this.since = 0;
|
|
89
|
+
this.last = null;
|
|
90
|
+
__classPrivateFieldSet(this, _RetryBackoff_next, 0, "f");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
_RetryBackoff_next = new WeakMap();
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared pieces of the library workflow contract.
|
|
3
|
+
*
|
|
4
|
+
* Library workflows follow one shape: `begin*` is an explicit operation that
|
|
5
|
+
* returns after local commit, `*Status` is a pure synchronous read, and
|
|
6
|
+
* `waitFor*` only observes. A wait never starts runtime workers: a host that
|
|
7
|
+
* already runs a runtime must not get a second poller and escrow loop. One-shot
|
|
8
|
+
* hosts such as the CLI run the runtime around the wait themselves
|
|
9
|
+
* (`cli/common/runtime-workers.ts`). Remote and time-driven outcomes, including
|
|
10
|
+
* an expired wait, are returned values; only caller-contract, invariant and
|
|
11
|
+
* infrastructure failures throw.
|
|
12
|
+
*/
|
|
13
|
+
import { action, sleep } from "effection";
|
|
14
|
+
import { systemClock } from "./runtime-clock.js";
|
|
15
|
+
import { runtimeTurn } from "./runtime-turn.js";
|
|
16
|
+
/** In-memory sink that records changes in order. */
|
|
17
|
+
export class MemoryChangeSink {
|
|
18
|
+
constructor() {
|
|
19
|
+
Object.defineProperty(this, "changes", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
configurable: true,
|
|
22
|
+
writable: true,
|
|
23
|
+
value: []
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
push(change) {
|
|
27
|
+
this.changes.push({ kind: change.kind, id: change.id });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Back a sink with the host's transient `Signaler`. Publishing needs no polling:
|
|
32
|
+
* `push` is a synchronous append that collapses repeats of one workflow into a
|
|
33
|
+
* single signal (collapse key `kind:id`). Delivery is pull-based, though: the
|
|
34
|
+
* Signaler has no subscriber hook, so the consumer must read `signaler.list()`.
|
|
35
|
+
* A closed or failing Signaler drops the change instead of throwing: a hint must
|
|
36
|
+
* never break the workflow that published it.
|
|
37
|
+
*/
|
|
38
|
+
export function signalerChangeSink(signaler) {
|
|
39
|
+
return {
|
|
40
|
+
push: ({ kind, id }) => {
|
|
41
|
+
try {
|
|
42
|
+
signaler.push({ id }, `/workflow/${kind}`, { ckey: `${kind}:${id}` });
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Dropped on purpose; consumers re-read status and tolerate a missed hint.
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Observe-only wait: poll `done`, yielding to the scheduler between checks,
|
|
52
|
+
* until it holds or the deadline passes. Starts no worker and has no effects of
|
|
53
|
+
* its own.
|
|
54
|
+
*
|
|
55
|
+
* @param done Synchronous status read; checked before the first yield.
|
|
56
|
+
* @param pollMs Elapsed milliseconds between checks. Omitted: yield one scheduler turn.
|
|
57
|
+
* @returns True when `done` held, false when `timeoutMs` elapsed first.
|
|
58
|
+
*/
|
|
59
|
+
export function* observeUntil(done, { timeoutMs, clock = systemClock, pollMs }) {
|
|
60
|
+
const deadline = clock.now() + timeoutMs;
|
|
61
|
+
while (!done()) {
|
|
62
|
+
const remaining = deadline - clock.now();
|
|
63
|
+
if (remaining <= 0)
|
|
64
|
+
return false;
|
|
65
|
+
if (pollMs === undefined)
|
|
66
|
+
yield* runtimeTurn();
|
|
67
|
+
else
|
|
68
|
+
yield* sleep(Math.min(pollMs, remaining));
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Wait `milliseconds` on the host's clock, so a fake clock drives it and cancellation releases the timer.
|
|
74
|
+
*/
|
|
75
|
+
export function* waitOnClock(clock, milliseconds) {
|
|
76
|
+
yield* action((resolve) => {
|
|
77
|
+
const timer = clock.setTimeout(resolve, milliseconds);
|
|
78
|
+
return () => clock.clearTimeout(timer);
|
|
79
|
+
});
|
|
80
|
+
}
|