signify-ts 0.3.0-rc2 → 0.4.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/keri/app/aiding.d.ts +20 -4
- package/dist/keri/app/aiding.js +27 -2
- package/dist/keri/app/clienting.d.ts +2 -0
- package/dist/keri/app/clienting.js +1 -1
- package/dist/keri/app/contacting.d.ts +9 -14
- package/dist/keri/app/contacting.js +2 -2
- package/dist/keri/app/controller.js +2 -2
- package/dist/keri/app/coring.d.ts +119 -25
- package/dist/keri/app/coring.js +40 -13
- package/dist/keri/app/credentialing.d.ts +10 -10
- package/dist/keri/app/didwebing.d.ts +146 -0
- package/dist/keri/app/didwebing.js +359 -0
- package/dist/keri/app/escrowing.d.ts +4 -2
- package/dist/keri/app/escrowing.js +1 -1
- package/dist/keri/app/exchanging.d.ts +238 -7
- package/dist/keri/app/exchanging.js +102 -3
- package/dist/keri/app/grouping.d.ts +10 -8
- package/dist/keri/app/grouping.js +4 -4
- package/dist/keri/app/signaling.d.ts +50 -0
- package/dist/keri/app/signaling.js +52 -0
- package/dist/keri/app/w3cing.d.ts +137 -0
- package/dist/keri/app/w3cing.js +337 -0
- package/dist/keri/core/eventing.d.ts +2 -2
- package/dist/keri/core/eventing.js +7 -18
- package/dist/keri/core/keeping.d.ts +111 -8
- package/dist/keri/core/keeping.js +133 -54
- package/dist/keri/core/keyState.d.ts +84 -70
- package/dist/keri/core/keyState.js +1 -1
- package/dist/keri/core/manager.d.ts +1 -1
- package/dist/keri/core/manager.js +3 -3
- package/dist/keri/core/number.d.ts +1 -1
- package/dist/keri/core/number.js +7 -4
- package/dist/keri/core/salter.d.ts +2 -9
- package/dist/keri/core/salter.js +2 -9
- package/dist/types/keria-api-schema.d.ts +889 -35
- package/dist/types/keria-api-schema.js +34 -2
- package/package.json +3 -5
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { SignifyClient } from './clienting.ts';
|
|
2
|
+
import { SignedReplyEnvelope } from './signaling.ts';
|
|
3
|
+
/**
|
|
4
|
+
* KERI reply route used inside signed did:webs publication requests.
|
|
5
|
+
*
|
|
6
|
+
* Live delivery comes through `client.signals().stream()`. Durable fallback and
|
|
7
|
+
* request retrieval stay under `client.didwebs()`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DWS_SIGNING_ROUTE = "/didwebs/signing/request";
|
|
10
|
+
export declare const DWS_ACTION_CREATE_REGISTRY = "create_registry";
|
|
11
|
+
export declare const DWS_ACTION_ISSUE_DESIGNATED_ALIAS = "issue_designated_alias";
|
|
12
|
+
export declare const DWS_DEDUPE_IN_FLIGHT = "in_flight";
|
|
13
|
+
export declare const DWS_DEDUPE_SUBMITTED = "submitted";
|
|
14
|
+
export declare const DWS_DEDUPE_COMPLETE = "complete";
|
|
15
|
+
export declare const DWS_DEDUPE_FAILED = "failed";
|
|
16
|
+
export declare const DWS_DEDUPE_REJECTED = "rejected";
|
|
17
|
+
export declare const DWS_DEDUPE_TERMINAL_RETENTION_MS: number;
|
|
18
|
+
export declare const DWS_DEDUPE_IN_FLIGHT_RETENTION_MS: number;
|
|
19
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
20
|
+
export type DidWebsRequestDedupeStatus = typeof DWS_DEDUPE_IN_FLIGHT | typeof DWS_DEDUPE_SUBMITTED | typeof DWS_DEDUPE_COMPLETE | typeof DWS_DEDUPE_FAILED | typeof DWS_DEDUPE_REJECTED;
|
|
21
|
+
export interface DidWebsRequestDedupeRecord {
|
|
22
|
+
id: string;
|
|
23
|
+
aid: string;
|
|
24
|
+
action: string;
|
|
25
|
+
status: DidWebsRequestDedupeStatus;
|
|
26
|
+
updated: string;
|
|
27
|
+
error?: string | null;
|
|
28
|
+
}
|
|
29
|
+
export interface DidWebsRequestDedupeStore {
|
|
30
|
+
get(id: string): MaybePromise<DidWebsRequestDedupeRecord | undefined>;
|
|
31
|
+
put(record: DidWebsRequestDedupeRecord): MaybePromise<void>;
|
|
32
|
+
delete(id: string): MaybePromise<void>;
|
|
33
|
+
purgeTerminal(maxAgeMs?: number, now?: string): MaybePromise<number>;
|
|
34
|
+
}
|
|
35
|
+
export declare class MemoryDidWebsRequestDedupeStore implements DidWebsRequestDedupeStore {
|
|
36
|
+
records: Map<string, DidWebsRequestDedupeRecord>;
|
|
37
|
+
get(id: string): DidWebsRequestDedupeRecord | undefined;
|
|
38
|
+
put(record: DidWebsRequestDedupeRecord): void;
|
|
39
|
+
delete(id: string): void;
|
|
40
|
+
purgeTerminal(maxAgeMs?: number, now?: string): number;
|
|
41
|
+
}
|
|
42
|
+
export declare class IndexedDbDidWebsRequestDedupeStore implements DidWebsRequestDedupeStore {
|
|
43
|
+
private readonly factory;
|
|
44
|
+
private readonly dbName;
|
|
45
|
+
private readonly storeName;
|
|
46
|
+
private dbPromise?;
|
|
47
|
+
constructor(factory?: IDBFactory, dbName?: string, storeName?: string);
|
|
48
|
+
get(id: string): Promise<DidWebsRequestDedupeRecord | undefined>;
|
|
49
|
+
put(record: DidWebsRequestDedupeRecord): Promise<void>;
|
|
50
|
+
delete(id: string): Promise<void>;
|
|
51
|
+
purgeTerminal(maxAgeMs?: number, now?: string): Promise<number>;
|
|
52
|
+
private request;
|
|
53
|
+
private open;
|
|
54
|
+
}
|
|
55
|
+
export declare function defaultDidWebsRequestDedupeStore(): DidWebsRequestDedupeStore;
|
|
56
|
+
export type DidWebsRequestSource = 'sse' | 'polling';
|
|
57
|
+
export type DidWebsAutoApproveOutcome = 'submitted' | 'skipped' | 'complete' | 'failed' | 'rejected';
|
|
58
|
+
export interface DidWebsAutoApproveResult {
|
|
59
|
+
outcome: DidWebsAutoApproveOutcome;
|
|
60
|
+
requestId?: string;
|
|
61
|
+
source?: DidWebsRequestSource;
|
|
62
|
+
record?: DidWebsRequestDedupeRecord;
|
|
63
|
+
error?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Durable did:webs publication request prepared by KERIA for edge signing.
|
|
67
|
+
*/
|
|
68
|
+
export interface DidWebsSigningRequest {
|
|
69
|
+
d: string;
|
|
70
|
+
type: string;
|
|
71
|
+
action: string;
|
|
72
|
+
agent: string;
|
|
73
|
+
aid: string;
|
|
74
|
+
name: string;
|
|
75
|
+
did: string;
|
|
76
|
+
registryName: string;
|
|
77
|
+
registryId?: string;
|
|
78
|
+
schema: string;
|
|
79
|
+
credentialData: Record<string, unknown>;
|
|
80
|
+
rules: Record<string, unknown>;
|
|
81
|
+
didJsonUrl: string;
|
|
82
|
+
keriCesrUrl: string;
|
|
83
|
+
dt: string;
|
|
84
|
+
state?: string;
|
|
85
|
+
lastSignaled?: string | null;
|
|
86
|
+
error?: string | null;
|
|
87
|
+
envelope?: SignedReplyEnvelope;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* did:webs publication request helper.
|
|
91
|
+
*
|
|
92
|
+
* `DidWebs` owns only did:webs-specific polling and approval behavior. Generic
|
|
93
|
+
* SSE transport and signed KERI `rpy` envelope verification live on
|
|
94
|
+
* `client.signals()` so future KERIA workflows can reuse the same signaling
|
|
95
|
+
* channel without importing did:webs.
|
|
96
|
+
*/
|
|
97
|
+
export declare class DidWebs {
|
|
98
|
+
client: SignifyClient;
|
|
99
|
+
constructor(client: SignifyClient);
|
|
100
|
+
/**
|
|
101
|
+
* List durable did:webs publication requests from KERIA.
|
|
102
|
+
*
|
|
103
|
+
* This is the recovery path when a client was not connected to
|
|
104
|
+
* `/signals/stream` or missed a transient SSE event.
|
|
105
|
+
*/
|
|
106
|
+
requests(aid?: string, includeComplete?: boolean): Promise<DidWebsSigningRequest[]>;
|
|
107
|
+
/**
|
|
108
|
+
* Fetch one durable did:webs publication request by request SAID.
|
|
109
|
+
*/
|
|
110
|
+
request(requestId: string): Promise<DidWebsSigningRequest>;
|
|
111
|
+
/**
|
|
112
|
+
* Perform the edge-signed action requested by KERIA.
|
|
113
|
+
*
|
|
114
|
+
* The caller should verify `request.envelope` with
|
|
115
|
+
* `client.signals().verifyReplyEnvelope(..., { route:
|
|
116
|
+
* DIDWEBS_SIGNING_ROUTE })` before auto-approving a live SSE request.
|
|
117
|
+
* Polling fallback can trust normal authenticated KERIA transport but still
|
|
118
|
+
* uses the same request shape.
|
|
119
|
+
*/
|
|
120
|
+
approve(request: DidWebsSigningRequest): Promise<import("./credentialing.ts").IssueCredentialResult | import("./credentialing.ts").RegistryResult>;
|
|
121
|
+
}
|
|
122
|
+
export interface DidWebsAutoApproverOptions {
|
|
123
|
+
store?: DidWebsRequestDedupeStore;
|
|
124
|
+
now?: () => string;
|
|
125
|
+
terminalRetentionMs?: number;
|
|
126
|
+
inFlightRetentionMs?: number;
|
|
127
|
+
}
|
|
128
|
+
export declare class DidWebsAutoApprover {
|
|
129
|
+
client: SignifyClient;
|
|
130
|
+
didwebs: DidWebs;
|
|
131
|
+
store: DidWebsRequestDedupeStore;
|
|
132
|
+
now: () => string;
|
|
133
|
+
terminalRetentionMs: number;
|
|
134
|
+
inFlightRetentionMs: number;
|
|
135
|
+
private activeApprovals;
|
|
136
|
+
constructor(client: SignifyClient, options?: DidWebsAutoApproverOptions);
|
|
137
|
+
handleEnvelope(envelope: SignedReplyEnvelope): Promise<DidWebsAutoApproveResult>;
|
|
138
|
+
handleRequest(request: DidWebsSigningRequest | undefined, source?: DidWebsRequestSource): Promise<DidWebsAutoApproveResult>;
|
|
139
|
+
private handleRequestOnce;
|
|
140
|
+
pollOnce(aid?: string): Promise<DidWebsAutoApproveResult[]>;
|
|
141
|
+
reconcile(aid?: string): Promise<DidWebsRequestDedupeRecord[]>;
|
|
142
|
+
private shouldSkip;
|
|
143
|
+
private localOwnershipError;
|
|
144
|
+
private putRecord;
|
|
145
|
+
}
|
|
146
|
+
export {};
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KERI reply route used inside signed did:webs publication requests.
|
|
3
|
+
*
|
|
4
|
+
* Live delivery comes through `client.signals().stream()`. Durable fallback and
|
|
5
|
+
* request retrieval stay under `client.didwebs()`.
|
|
6
|
+
*/
|
|
7
|
+
export const DWS_SIGNING_ROUTE = '/didwebs/signing/request';
|
|
8
|
+
export const DWS_ACTION_CREATE_REGISTRY = 'create_registry';
|
|
9
|
+
export const DWS_ACTION_ISSUE_DESIGNATED_ALIAS = 'issue_designated_alias';
|
|
10
|
+
export const DWS_DEDUPE_IN_FLIGHT = 'in_flight';
|
|
11
|
+
export const DWS_DEDUPE_SUBMITTED = 'submitted';
|
|
12
|
+
export const DWS_DEDUPE_COMPLETE = 'complete';
|
|
13
|
+
export const DWS_DEDUPE_FAILED = 'failed';
|
|
14
|
+
export const DWS_DEDUPE_REJECTED = 'rejected';
|
|
15
|
+
export const DWS_DEDUPE_TERMINAL_RETENTION_MS = 10 * 60 * 1000;
|
|
16
|
+
export const DWS_DEDUPE_IN_FLIGHT_RETENTION_MS = 10 * 60 * 1000;
|
|
17
|
+
const DWS_DEDUPE_DB_NAME = 'signify-didwebs';
|
|
18
|
+
const DWS_DEDUPE_STORE_NAME = 'requestDedupe';
|
|
19
|
+
const DWS_TERMINAL_DEDUPE_STATUSES = new Set([
|
|
20
|
+
DWS_DEDUPE_COMPLETE,
|
|
21
|
+
DWS_DEDUPE_FAILED,
|
|
22
|
+
DWS_DEDUPE_REJECTED,
|
|
23
|
+
]);
|
|
24
|
+
export class MemoryDidWebsRequestDedupeStore {
|
|
25
|
+
constructor() {
|
|
26
|
+
this.records = new Map();
|
|
27
|
+
}
|
|
28
|
+
get(id) {
|
|
29
|
+
return this.records.get(id);
|
|
30
|
+
}
|
|
31
|
+
put(record) {
|
|
32
|
+
this.records.set(record.id, record);
|
|
33
|
+
}
|
|
34
|
+
delete(id) {
|
|
35
|
+
this.records.delete(id);
|
|
36
|
+
}
|
|
37
|
+
purgeTerminal(maxAgeMs = DWS_DEDUPE_TERMINAL_RETENTION_MS, now = new Date().toISOString()) {
|
|
38
|
+
let deleted = 0;
|
|
39
|
+
for (const record of this.records.values()) {
|
|
40
|
+
if (shouldPurgeTerminalRecord(record, maxAgeMs, now)) {
|
|
41
|
+
this.records.delete(record.id);
|
|
42
|
+
deleted += 1;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return deleted;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function shouldPurgeTerminalRecord(record, maxAgeMs, now) {
|
|
49
|
+
if (!DWS_TERMINAL_DEDUPE_STATUSES.has(record.status)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return recordAgeMs(record, now) >= maxAgeMs;
|
|
53
|
+
}
|
|
54
|
+
function recordAgeMs(record, now) {
|
|
55
|
+
const updated = Date.parse(record.updated);
|
|
56
|
+
const current = Date.parse(now);
|
|
57
|
+
if (Number.isNaN(updated) || Number.isNaN(current)) {
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
return current - updated;
|
|
61
|
+
}
|
|
62
|
+
export class IndexedDbDidWebsRequestDedupeStore {
|
|
63
|
+
constructor(factory = globalThis.indexedDB, dbName = DWS_DEDUPE_DB_NAME, storeName = DWS_DEDUPE_STORE_NAME) {
|
|
64
|
+
this.factory = factory;
|
|
65
|
+
this.dbName = dbName;
|
|
66
|
+
this.storeName = storeName;
|
|
67
|
+
if (factory === undefined) {
|
|
68
|
+
throw new Error('IndexedDB is unavailable');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async get(id) {
|
|
72
|
+
return await this.request('readonly', (store) => store.get(id));
|
|
73
|
+
}
|
|
74
|
+
async put(record) {
|
|
75
|
+
await this.request('readwrite', (store) => store.put(record));
|
|
76
|
+
}
|
|
77
|
+
async delete(id) {
|
|
78
|
+
await this.request('readwrite', (store) => store.delete(id));
|
|
79
|
+
}
|
|
80
|
+
async purgeTerminal(maxAgeMs = DWS_DEDUPE_TERMINAL_RETENTION_MS, now = new Date().toISOString()) {
|
|
81
|
+
const db = await this.open();
|
|
82
|
+
return await new Promise((resolve, reject) => {
|
|
83
|
+
let deleted = 0;
|
|
84
|
+
const transaction = db.transaction(this.storeName, 'readwrite');
|
|
85
|
+
const store = transaction.objectStore(this.storeName);
|
|
86
|
+
const request = store.getAll();
|
|
87
|
+
request.onsuccess = () => {
|
|
88
|
+
for (const record of request.result) {
|
|
89
|
+
if (shouldPurgeTerminalRecord(record, maxAgeMs, now)) {
|
|
90
|
+
store.delete(record.id);
|
|
91
|
+
deleted += 1;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
request.onerror = () => reject(request.error);
|
|
96
|
+
transaction.oncomplete = () => resolve(deleted);
|
|
97
|
+
transaction.onerror = () => reject(transaction.error);
|
|
98
|
+
transaction.onabort = () => reject(transaction.error);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
async request(mode, create) {
|
|
102
|
+
const db = await this.open();
|
|
103
|
+
return await new Promise((resolve, reject) => {
|
|
104
|
+
const transaction = db.transaction(this.storeName, mode);
|
|
105
|
+
const request = create(transaction.objectStore(this.storeName));
|
|
106
|
+
request.onsuccess = () => resolve(request.result);
|
|
107
|
+
request.onerror = () => reject(request.error);
|
|
108
|
+
transaction.onerror = () => reject(transaction.error);
|
|
109
|
+
transaction.onabort = () => reject(transaction.error);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
async open() {
|
|
113
|
+
if (this.dbPromise === undefined) {
|
|
114
|
+
this.dbPromise = new Promise((resolve, reject) => {
|
|
115
|
+
const request = this.factory.open(this.dbName, 1);
|
|
116
|
+
request.onupgradeneeded = () => {
|
|
117
|
+
const db = request.result;
|
|
118
|
+
if (!db.objectStoreNames.contains(this.storeName)) {
|
|
119
|
+
db.createObjectStore(this.storeName, { keyPath: 'id' });
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
request.onsuccess = () => resolve(request.result);
|
|
123
|
+
request.onerror = () => reject(request.error);
|
|
124
|
+
request.onblocked = () => reject(new Error('IndexedDB did:webs dedupe store is blocked'));
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return await this.dbPromise;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
export function defaultDidWebsRequestDedupeStore() {
|
|
131
|
+
if (typeof globalThis.indexedDB !== 'undefined') {
|
|
132
|
+
return new IndexedDbDidWebsRequestDedupeStore(globalThis.indexedDB);
|
|
133
|
+
}
|
|
134
|
+
return new MemoryDidWebsRequestDedupeStore();
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* did:webs publication request helper.
|
|
138
|
+
*
|
|
139
|
+
* `DidWebs` owns only did:webs-specific polling and approval behavior. Generic
|
|
140
|
+
* SSE transport and signed KERI `rpy` envelope verification live on
|
|
141
|
+
* `client.signals()` so future KERIA workflows can reuse the same signaling
|
|
142
|
+
* channel without importing did:webs.
|
|
143
|
+
*/
|
|
144
|
+
export class DidWebs {
|
|
145
|
+
constructor(client) {
|
|
146
|
+
this.client = client;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* List durable did:webs publication requests from KERIA.
|
|
150
|
+
*
|
|
151
|
+
* This is the recovery path when a client was not connected to
|
|
152
|
+
* `/signals/stream` or missed a transient SSE event.
|
|
153
|
+
*/
|
|
154
|
+
async requests(aid, includeComplete = false) {
|
|
155
|
+
const params = new URLSearchParams();
|
|
156
|
+
if (aid !== undefined) {
|
|
157
|
+
params.set('aid', aid);
|
|
158
|
+
}
|
|
159
|
+
if (includeComplete) {
|
|
160
|
+
params.set('includeComplete', 'true');
|
|
161
|
+
}
|
|
162
|
+
const query = params.size > 0 ? `?${params.toString()}` : '';
|
|
163
|
+
const res = await this.client.fetch(`/didwebs/signing/requests${query}`, 'GET', null);
|
|
164
|
+
const body = await res.json();
|
|
165
|
+
return body.requests;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Fetch one durable did:webs publication request by request SAID.
|
|
169
|
+
*/
|
|
170
|
+
async request(requestId) {
|
|
171
|
+
const res = await this.client.fetch(`/didwebs/signing/requests/${requestId}`, 'GET', null);
|
|
172
|
+
return await res.json();
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Perform the edge-signed action requested by KERIA.
|
|
176
|
+
*
|
|
177
|
+
* The caller should verify `request.envelope` with
|
|
178
|
+
* `client.signals().verifyReplyEnvelope(..., { route:
|
|
179
|
+
* DIDWEBS_SIGNING_ROUTE })` before auto-approving a live SSE request.
|
|
180
|
+
* Polling fallback can trust normal authenticated KERIA transport but still
|
|
181
|
+
* uses the same request shape.
|
|
182
|
+
*/
|
|
183
|
+
async approve(request) {
|
|
184
|
+
if (request.action === DWS_ACTION_CREATE_REGISTRY) {
|
|
185
|
+
return await this.client.registries().create({
|
|
186
|
+
name: request.name,
|
|
187
|
+
registryName: request.registryName,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
if (request.action === DWS_ACTION_ISSUE_DESIGNATED_ALIAS) {
|
|
191
|
+
if (request.registryId === undefined) {
|
|
192
|
+
throw new Error('did:webs designated-alias request is missing registryId');
|
|
193
|
+
}
|
|
194
|
+
return await this.client.credentials().issue(request.name, {
|
|
195
|
+
ri: request.registryId,
|
|
196
|
+
s: request.schema,
|
|
197
|
+
a: request.credentialData,
|
|
198
|
+
r: request.rules,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
throw new Error(`unsupported did:webs signing request action ${request.action}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
export class DidWebsAutoApprover {
|
|
205
|
+
constructor(client, options = {}) {
|
|
206
|
+
var _a, _b, _c, _d;
|
|
207
|
+
this.activeApprovals = new Map();
|
|
208
|
+
this.client = client;
|
|
209
|
+
this.didwebs = new DidWebs(client);
|
|
210
|
+
this.store = (_a = options.store) !== null && _a !== void 0 ? _a : defaultDidWebsRequestDedupeStore();
|
|
211
|
+
this.now = (_b = options.now) !== null && _b !== void 0 ? _b : (() => new Date().toISOString());
|
|
212
|
+
this.terminalRetentionMs =
|
|
213
|
+
(_c = options.terminalRetentionMs) !== null && _c !== void 0 ? _c : DWS_DEDUPE_TERMINAL_RETENTION_MS;
|
|
214
|
+
this.inFlightRetentionMs =
|
|
215
|
+
(_d = options.inFlightRetentionMs) !== null && _d !== void 0 ? _d : DWS_DEDUPE_IN_FLIGHT_RETENTION_MS;
|
|
216
|
+
}
|
|
217
|
+
async handleEnvelope(envelope) {
|
|
218
|
+
const verified = this.client.signals().verifyReplyEnvelope(envelope, {
|
|
219
|
+
route: DWS_SIGNING_ROUTE,
|
|
220
|
+
});
|
|
221
|
+
if (!verified) {
|
|
222
|
+
return {
|
|
223
|
+
outcome: 'rejected',
|
|
224
|
+
error: 'did:webs signing request envelope failed verification',
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
return await this.handleRequest(envelope.rpy.a, 'sse');
|
|
228
|
+
}
|
|
229
|
+
async handleRequest(request, source = 'polling') {
|
|
230
|
+
if ((request === null || request === void 0 ? void 0 : request.d) === undefined || request.d === '') {
|
|
231
|
+
return {
|
|
232
|
+
outcome: 'rejected',
|
|
233
|
+
source,
|
|
234
|
+
error: 'did:webs signing request is missing request SAID',
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
const active = this.activeApprovals.get(request.d);
|
|
238
|
+
if (active !== undefined) {
|
|
239
|
+
return await active;
|
|
240
|
+
}
|
|
241
|
+
const approval = this.handleRequestOnce(request, source);
|
|
242
|
+
this.activeApprovals.set(request.d, approval);
|
|
243
|
+
try {
|
|
244
|
+
return await approval;
|
|
245
|
+
}
|
|
246
|
+
finally {
|
|
247
|
+
this.activeApprovals.delete(request.d);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async handleRequestOnce(request, source) {
|
|
251
|
+
const now = this.now();
|
|
252
|
+
await this.store.purgeTerminal(this.terminalRetentionMs, now);
|
|
253
|
+
const existing = await this.store.get(request.d);
|
|
254
|
+
if (this.shouldSkip(existing, now)) {
|
|
255
|
+
return {
|
|
256
|
+
outcome: 'skipped',
|
|
257
|
+
requestId: request.d,
|
|
258
|
+
source,
|
|
259
|
+
record: existing,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
await this.putRecord(request, DWS_DEDUPE_IN_FLIGHT);
|
|
263
|
+
const ownershipError = await this.localOwnershipError(request);
|
|
264
|
+
if (ownershipError !== undefined) {
|
|
265
|
+
const record = await this.putRecord(request, DWS_DEDUPE_REJECTED, ownershipError);
|
|
266
|
+
return {
|
|
267
|
+
outcome: 'rejected',
|
|
268
|
+
requestId: request.d,
|
|
269
|
+
source,
|
|
270
|
+
record,
|
|
271
|
+
error: ownershipError,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
await this.didwebs.approve(request);
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
279
|
+
const record = await this.putRecord(request, DWS_DEDUPE_FAILED, message);
|
|
280
|
+
return {
|
|
281
|
+
outcome: 'failed',
|
|
282
|
+
requestId: request.d,
|
|
283
|
+
source,
|
|
284
|
+
record,
|
|
285
|
+
error: message,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
const record = await this.putRecord(request, DWS_DEDUPE_SUBMITTED);
|
|
289
|
+
return {
|
|
290
|
+
outcome: 'submitted',
|
|
291
|
+
requestId: request.d,
|
|
292
|
+
source,
|
|
293
|
+
record,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
async pollOnce(aid) {
|
|
297
|
+
const requests = await this.didwebs.requests(aid);
|
|
298
|
+
const results = [];
|
|
299
|
+
for (const request of requests) {
|
|
300
|
+
results.push(await this.handleRequest(request, 'polling'));
|
|
301
|
+
}
|
|
302
|
+
return results;
|
|
303
|
+
}
|
|
304
|
+
async reconcile(aid) {
|
|
305
|
+
var _a;
|
|
306
|
+
await this.store.purgeTerminal(this.terminalRetentionMs, this.now());
|
|
307
|
+
const requests = await this.didwebs.requests(aid, true);
|
|
308
|
+
const records = [];
|
|
309
|
+
for (const request of requests) {
|
|
310
|
+
if (request.state === DWS_DEDUPE_COMPLETE) {
|
|
311
|
+
records.push(await this.putRecord(request, DWS_DEDUPE_COMPLETE));
|
|
312
|
+
}
|
|
313
|
+
else if (request.state === DWS_DEDUPE_FAILED) {
|
|
314
|
+
records.push(await this.putRecord(request, DWS_DEDUPE_FAILED, (_a = request.error) !== null && _a !== void 0 ? _a : 'KERIA reported request failure'));
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return records;
|
|
318
|
+
}
|
|
319
|
+
shouldSkip(record, now) {
|
|
320
|
+
if ((record === null || record === void 0 ? void 0 : record.status) === DWS_DEDUPE_IN_FLIGHT &&
|
|
321
|
+
recordAgeMs(record, now) >= this.inFlightRetentionMs) {
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
return (record !== undefined &&
|
|
325
|
+
[
|
|
326
|
+
DWS_DEDUPE_IN_FLIGHT,
|
|
327
|
+
DWS_DEDUPE_SUBMITTED,
|
|
328
|
+
DWS_DEDUPE_COMPLETE,
|
|
329
|
+
DWS_DEDUPE_FAILED,
|
|
330
|
+
DWS_DEDUPE_REJECTED,
|
|
331
|
+
].includes(record.status));
|
|
332
|
+
}
|
|
333
|
+
async localOwnershipError(request) {
|
|
334
|
+
let hab;
|
|
335
|
+
try {
|
|
336
|
+
hab = await this.client.identifiers().get(request.name);
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
340
|
+
return `did:webs request ${request.d} targets ${request.aid}, but local identifier ${request.name} is unavailable: ${message}`;
|
|
341
|
+
}
|
|
342
|
+
if (hab.prefix !== request.aid) {
|
|
343
|
+
return `did:webs request ${request.d} targets ${request.aid}, but local identifier ${request.name} is ${hab.prefix}`;
|
|
344
|
+
}
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
async putRecord(request, status, error = null) {
|
|
348
|
+
const record = {
|
|
349
|
+
id: request.d,
|
|
350
|
+
aid: request.aid,
|
|
351
|
+
action: request.action,
|
|
352
|
+
status,
|
|
353
|
+
updated: this.now(),
|
|
354
|
+
error,
|
|
355
|
+
};
|
|
356
|
+
await this.store.put(record);
|
|
357
|
+
return record;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { SignifyClient } from './clienting.ts';
|
|
2
|
+
import { components } from '../../types/keria-api-schema.ts';
|
|
3
|
+
export type Rpy = components['schemas']['Rpy'];
|
|
2
4
|
/**
|
|
3
5
|
* Escrows
|
|
4
6
|
*/
|
|
@@ -13,7 +15,7 @@ export declare class Escrows {
|
|
|
13
15
|
* List replay messages
|
|
14
16
|
* @async
|
|
15
17
|
* @param {string} [route] Optional route in the replay message
|
|
16
|
-
* @returns {Promise<
|
|
18
|
+
* @returns {Promise<Rpy[]>} A promise to the list of replay messages
|
|
17
19
|
*/
|
|
18
|
-
listReply(route?: string): Promise<
|
|
20
|
+
listReply(route?: string): Promise<Rpy[]>;
|
|
19
21
|
}
|
|
@@ -13,7 +13,7 @@ export class Escrows {
|
|
|
13
13
|
* List replay messages
|
|
14
14
|
* @async
|
|
15
15
|
* @param {string} [route] Optional route in the replay message
|
|
16
|
-
* @returns {Promise<
|
|
16
|
+
* @returns {Promise<Rpy[]>} A promise to the list of replay messages
|
|
17
17
|
*/
|
|
18
18
|
async listReply(route) {
|
|
19
19
|
const params = new URLSearchParams();
|