@shuttle.technology/authorization 0.0.0-stage → 0.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/CHANGELOG.md +191 -0
- package/README.md +1124 -2
- package/dist/accountLists.d.ts +28 -0
- package/dist/accountLists.js +27 -0
- package/dist/aesGcmSiv.d.ts +14 -0
- package/dist/aesGcmSiv.js +160 -0
- package/dist/auth.d.ts +68 -0
- package/dist/auth.js +189 -0
- package/dist/balances.d.ts +31 -0
- package/dist/balances.js +44 -0
- package/dist/canonicalize.d.ts +19 -0
- package/dist/canonicalize.js +107 -0
- package/dist/client.d.ts +152 -0
- package/dist/client.js +781 -0
- package/dist/clientTypes.d.ts +56 -0
- package/dist/clientTypes.js +5 -0
- package/dist/configuration.d.ts +48 -0
- package/dist/configuration.js +72 -0
- package/dist/consentDebit.d.ts +36 -0
- package/dist/consentDebit.js +95 -0
- package/dist/credentials.d.ts +116 -0
- package/dist/credentials.js +315 -0
- package/dist/debitPermissionWire.d.ts +90 -0
- package/dist/debitPermissionWire.js +118 -0
- package/dist/debitPermissions.d.ts +39 -0
- package/dist/debitPermissions.js +117 -0
- package/dist/deviceKey.d.ts +13 -0
- package/dist/deviceKey.js +28 -0
- package/dist/deviceRecovery.d.ts +40 -0
- package/dist/deviceRecovery.js +143 -0
- package/dist/deviceShare.d.ts +9 -0
- package/dist/deviceShare.js +24 -0
- package/dist/deviceShareEnvelope.d.ts +13 -0
- package/dist/deviceShareEnvelope.js +139 -0
- package/dist/deviceShares.d.ts +91 -0
- package/dist/deviceShares.js +204 -0
- package/dist/errors.d.ts +280 -0
- package/dist/errors.js +85 -0
- package/dist/linkedChecks.d.ts +17 -0
- package/dist/linkedChecks.js +31 -0
- package/dist/linkedRecovery.d.ts +40 -0
- package/dist/linkedRecovery.js +49 -0
- package/dist/linkedRegistrations.d.ts +11 -0
- package/dist/linkedRegistrations.js +109 -0
- package/dist/linkedSendDisplay.d.ts +73 -0
- package/dist/linkedSendDisplay.js +84 -0
- package/dist/linkedWalletSignals.d.ts +22 -0
- package/dist/linkedWalletSignals.js +101 -0
- package/dist/linkedWallets.d.ts +401 -0
- package/dist/linkedWallets.js +436 -0
- package/dist/me.d.ts +27 -0
- package/dist/me.js +30 -0
- package/dist/mediatedOperation.d.ts +27 -0
- package/dist/mediatedOperation.js +68 -0
- package/dist/mobile.d.ts +64 -0
- package/dist/mobile.js +69 -0
- package/dist/modeC.d.ts +131 -0
- package/dist/modeC.js +248 -0
- package/dist/modeCLinked.d.ts +207 -0
- package/dist/modeCLinked.js +270 -0
- package/dist/pages.d.ts +22 -0
- package/dist/pages.js +45 -0
- package/dist/poll.d.ts +15 -0
- package/dist/poll.js +76 -0
- package/dist/redirects.d.ts +45 -0
- package/dist/redirects.js +108 -0
- package/dist/requestGrant.d.ts +169 -0
- package/dist/requestGrant.js +243 -0
- package/dist/sends.d.ts +89 -0
- package/dist/sends.js +127 -0
- package/dist/serviceGrants.d.ts +52 -0
- package/dist/serviceGrants.js +46 -0
- package/dist/slice4.d.ts +187 -0
- package/dist/slice4.js +299 -0
- package/dist/streams.d.ts +212 -0
- package/dist/streams.js +282 -0
- package/dist/trackedWallets.d.ts +53 -0
- package/dist/trackedWallets.js +104 -0
- package/dist/transferStatus.d.ts +45 -0
- package/dist/transferStatus.js +43 -0
- package/dist/walletCreateGrant.d.ts +62 -0
- package/dist/walletCreateGrant.js +89 -0
- package/dist/walletDisplayParts.d.ts +17 -0
- package/dist/walletDisplayParts.js +47 -0
- package/dist/walletEvents.d.ts +43 -0
- package/dist/walletEvents.js +97 -0
- package/dist/walletLimits.d.ts +65 -0
- package/dist/walletLimits.js +102 -0
- package/dist/walletOperations.d.ts +209 -0
- package/dist/walletOperations.js +317 -0
- package/dist/webRenewal.d.ts +92 -0
- package/dist/webRenewal.js +179 -0
- package/package.json +65 -3
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// The device shares this SDK keeps (owner decision of 28 October 2026): for
|
|
2
|
+
// each wallet whose device key the SDK made, at creation or at a recovery, the
|
|
3
|
+
// non-extractable device key and the wallet's device share sealed to it, in
|
|
4
|
+
// the origin's storage. A browser that holds no usable share of a wallet,
|
|
5
|
+
// after a change of domain, cleared site data, another browser or device, or
|
|
6
|
+
// a recovery on another device that replaced its share, recovers it with one
|
|
7
|
+
// passkey confirmation (deviceRecovery.ts) and then sends as before. The
|
|
8
|
+
// provider names the device key of the wallet's latest creation or recovery;
|
|
9
|
+
// a kept share sealed to another key is stale. The tabs of one browser take
|
|
10
|
+
// turns under one Web Lock per wallet, so two of them never recover the same
|
|
11
|
+
// wallet at once. The plaintext share never leaves resealDeviceShare.
|
|
12
|
+
import { ClientFlowError } from "./auth.js";
|
|
13
|
+
import { generateDeviceKey } from "./deviceKey.js";
|
|
14
|
+
import { resealDeviceShare } from "./deviceShare.js";
|
|
15
|
+
const unavailable = () => new ClientFlowError("device_share_unavailable");
|
|
16
|
+
const DATABASE = "streams-authorization-device-shares";
|
|
17
|
+
const STORE = "shares";
|
|
18
|
+
function pending(record) {
|
|
19
|
+
return record !== undefined && record.deviceShare === undefined && typeof record.pendingOperationId === "string" && typeof record.executeKey === "string" && typeof record.publicKeyHex === "string";
|
|
20
|
+
}
|
|
21
|
+
function usable(record) {
|
|
22
|
+
return record !== undefined && record.pendingOperationId === undefined && record.deviceShare !== null && typeof record.deviceShare === "object";
|
|
23
|
+
}
|
|
24
|
+
/** A usable share is current when the provider names no device key, or names the one it is sealed to. */
|
|
25
|
+
function current(record, serverKey) {
|
|
26
|
+
return usable(record) && (serverKey === undefined || record.publicKeyHex === serverKey);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* indexedDBDeviceShareStore keeps the records in the origin's IndexedDB, under
|
|
30
|
+
* namespace (one per client configuration), so the tabs of this origin share
|
|
31
|
+
* them and the key handles stay non-extractable.
|
|
32
|
+
*/
|
|
33
|
+
export function indexedDBDeviceShareStore(namespace, factory = globalThis.indexedDB) {
|
|
34
|
+
if (!factory || typeof factory.open !== "function")
|
|
35
|
+
throw unavailable();
|
|
36
|
+
const key = (walletId) => `${namespace}|${walletId}`;
|
|
37
|
+
const run = (mode, work) => new Promise((resolve, reject) => {
|
|
38
|
+
const opened = factory.open(DATABASE, 1);
|
|
39
|
+
opened.onupgradeneeded = () => { opened.result.createObjectStore(STORE); };
|
|
40
|
+
opened.onerror = () => reject(unavailable());
|
|
41
|
+
opened.onsuccess = () => {
|
|
42
|
+
const database = opened.result;
|
|
43
|
+
try {
|
|
44
|
+
const transaction = database.transaction(STORE, mode);
|
|
45
|
+
const request = work(transaction.objectStore(STORE));
|
|
46
|
+
transaction.oncomplete = () => { database.close(); resolve(request.result); };
|
|
47
|
+
transaction.onerror = transaction.onabort = () => { database.close(); reject(unavailable()); };
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
database.close();
|
|
51
|
+
reject(unavailable());
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
async load(walletId) {
|
|
57
|
+
const value = await run("readonly", (store) => store.get(key(walletId)));
|
|
58
|
+
if (value === undefined)
|
|
59
|
+
return undefined;
|
|
60
|
+
const record = value;
|
|
61
|
+
if (record.walletId !== walletId || !(record.privateKey instanceof CryptoKey) || (!usable(record) && !pending(record)))
|
|
62
|
+
throw unavailable();
|
|
63
|
+
return record;
|
|
64
|
+
},
|
|
65
|
+
async save(record) {
|
|
66
|
+
const kept = { walletId: record.walletId, privateKey: record.privateKey };
|
|
67
|
+
if (record.publicKeyHex !== undefined)
|
|
68
|
+
kept.publicKeyHex = record.publicKeyHex;
|
|
69
|
+
if (record.deviceShare !== undefined)
|
|
70
|
+
kept.deviceShare = { ...record.deviceShare };
|
|
71
|
+
if (record.pendingOperationId !== undefined)
|
|
72
|
+
Object.assign(kept, { pendingOperationId: record.pendingOperationId, executeKey: record.executeKey });
|
|
73
|
+
if (record.pendingLinkedWalletId !== undefined)
|
|
74
|
+
kept.pendingLinkedWalletId = record.pendingLinkedWalletId;
|
|
75
|
+
await run("readwrite", (store) => store.put(kept, key(record.walletId)));
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** The store a web client keeps its shares in when the app supplies none: the origin's IndexedDB, where there is one. */
|
|
80
|
+
export function defaultDeviceShareStore(channel, namespace) {
|
|
81
|
+
if (channel !== "web" || typeof globalThis.indexedDB?.open !== "function")
|
|
82
|
+
return undefined;
|
|
83
|
+
return indexedDBDeviceShareStore(namespace);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* DeviceShareKeeper is one client's keeper: the store, the lock its tabs take
|
|
87
|
+
* turns under, the device keys of the creations it made, and the recovery the
|
|
88
|
+
* client runs and resumes (deviceRecovery.ts).
|
|
89
|
+
*/
|
|
90
|
+
export class DeviceShareKeeper {
|
|
91
|
+
#store;
|
|
92
|
+
#crypto;
|
|
93
|
+
#lock;
|
|
94
|
+
#lockPrefix;
|
|
95
|
+
#recover;
|
|
96
|
+
#resume;
|
|
97
|
+
/** Device keys of creations, by the creation's idempotency key, so a repeat sends the same request. */
|
|
98
|
+
#creationKeys = new Map();
|
|
99
|
+
/** The creation's device key, by operation, until its result is kept. */
|
|
100
|
+
#creations = new Map();
|
|
101
|
+
/** Recoveries under way in this client, by wallet. */
|
|
102
|
+
#flights = new Map();
|
|
103
|
+
constructor(input) {
|
|
104
|
+
this.#store = input.store;
|
|
105
|
+
this.#crypto = input.crypto;
|
|
106
|
+
this.#lock = input.lock;
|
|
107
|
+
this.#lockPrefix = input.lockPrefix;
|
|
108
|
+
this.#recover = input.recover;
|
|
109
|
+
this.#resume = input.resume;
|
|
110
|
+
}
|
|
111
|
+
#required() {
|
|
112
|
+
if (!this.#store)
|
|
113
|
+
throw unavailable();
|
|
114
|
+
return this.#store;
|
|
115
|
+
}
|
|
116
|
+
/** The device key a creation without the app's own key is sealed to; the same one for every call with the same idempotency key, also at once. */
|
|
117
|
+
creationKey(idempotencyKey) {
|
|
118
|
+
this.#required();
|
|
119
|
+
let made = this.#creationKeys.get(idempotencyKey);
|
|
120
|
+
if (!made) {
|
|
121
|
+
made = generateDeviceKey(this.#crypto.subtle);
|
|
122
|
+
this.#creationKeys.set(idempotencyKey, made);
|
|
123
|
+
made.catch(() => this.#creationKeys.delete(idempotencyKey));
|
|
124
|
+
}
|
|
125
|
+
return made;
|
|
126
|
+
}
|
|
127
|
+
/** holdCreation remembers which device key the admitted creation's share will be sealed to. */
|
|
128
|
+
holdCreation(operationId, device) { this.#creations.set(operationId, device); }
|
|
129
|
+
/**
|
|
130
|
+
* keepCreated keeps the share of a creation this keeper made the key for,
|
|
131
|
+
* once its result is read, under the wallet's lock. It never replaces a
|
|
132
|
+
* record of the wallet: one kept already is the creation's own, read before,
|
|
133
|
+
* or a later recovery's, pending or done, which a late read of the
|
|
134
|
+
* creation's result must not undo.
|
|
135
|
+
*/
|
|
136
|
+
async keepCreated(operationId, result) {
|
|
137
|
+
const device = this.#creations.get(operationId);
|
|
138
|
+
if (!device || result.kind !== "wallet_created" || !result.wallet || !result.deviceShare)
|
|
139
|
+
return;
|
|
140
|
+
const store = this.#required(), walletId = result.wallet.walletId, deviceShare = result.deviceShare;
|
|
141
|
+
await this.#lock(this.#lockName(walletId), async () => {
|
|
142
|
+
if ((await store.load(walletId)) === undefined)
|
|
143
|
+
await store.save({ walletId, privateKey: device.privateKey, publicKeyHex: device.publicKeyHex, deviceShare });
|
|
144
|
+
});
|
|
145
|
+
this.#creations.delete(operationId);
|
|
146
|
+
}
|
|
147
|
+
#lockName(walletId) { return `${this.#lockPrefix}:device-recovery:${walletId}`; }
|
|
148
|
+
async has(wallet, serverKey) {
|
|
149
|
+
const record = await this.#required().load(wallet.externalWalletId);
|
|
150
|
+
return usable(record) && current(record, await serverKey());
|
|
151
|
+
}
|
|
152
|
+
ensure(wallet, serverKey, options = {}) {
|
|
153
|
+
const store = this.#required();
|
|
154
|
+
const walletId = wallet.externalWalletId;
|
|
155
|
+
const force = options.force === true;
|
|
156
|
+
const running = force ? undefined : this.#flights.get(walletId);
|
|
157
|
+
if (running)
|
|
158
|
+
return running;
|
|
159
|
+
const flight = (async () => {
|
|
160
|
+
if (!force && current(await store.load(walletId), await serverKey()))
|
|
161
|
+
return false;
|
|
162
|
+
// Under the tabs' lock the store and the provider's key are read again:
|
|
163
|
+
// a tab that waited finds the share the other one recovered, or the
|
|
164
|
+
// recovery it left pending, and recovers nothing new.
|
|
165
|
+
return this.#lock(this.#lockName(walletId), async () => {
|
|
166
|
+
let record = await store.load(walletId);
|
|
167
|
+
let resumed = false;
|
|
168
|
+
if (pending(record)) {
|
|
169
|
+
record = await this.#resume(wallet, record, options);
|
|
170
|
+
if (record) {
|
|
171
|
+
await store.save(record);
|
|
172
|
+
resumed = true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// A share kept, or resumed just now, is used only while the provider
|
|
176
|
+
// names its key: a recovery on another device since replaced it.
|
|
177
|
+
if ((resumed || !force) && current(record, await serverKey()))
|
|
178
|
+
return resumed;
|
|
179
|
+
// One recovery, and one more when another device recovered meanwhile;
|
|
180
|
+
// then the call gives up rather than chase the other device.
|
|
181
|
+
for (let attempt = resumed ? 1 : 0; attempt < 2; attempt++) {
|
|
182
|
+
const recovered = await this.#recover(wallet, options, (kept) => store.save(kept));
|
|
183
|
+
if (recovered.walletId !== walletId)
|
|
184
|
+
throw new ClientFlowError("invalid_server_response");
|
|
185
|
+
await store.save(recovered);
|
|
186
|
+
if (current(recovered, await serverKey()))
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
throw new ClientFlowError("device_recovery_failed");
|
|
190
|
+
});
|
|
191
|
+
})();
|
|
192
|
+
if (!force) {
|
|
193
|
+
this.#flights.set(walletId, flight);
|
|
194
|
+
void flight.then(() => this.#flights.delete(walletId), () => this.#flights.delete(walletId));
|
|
195
|
+
}
|
|
196
|
+
return flight;
|
|
197
|
+
}
|
|
198
|
+
async sealedFor(wallet, transportPublicKey) {
|
|
199
|
+
const record = await this.#required().load(wallet.externalWalletId);
|
|
200
|
+
if (!usable(record))
|
|
201
|
+
throw unavailable();
|
|
202
|
+
return resealDeviceShare(record.privateKey, record.deviceShare, transportPublicKey, this.#crypto);
|
|
203
|
+
}
|
|
204
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The provider's error codes: every code of the ErrorCode enum of the
|
|
3
|
+
* committed OpenAPI document, each with the HTTP status, `retryable` and
|
|
4
|
+
* `next_action` it is always answered with. An answer that disagrees with this
|
|
5
|
+
* table is refused as `invalid_error_response`, so a code an application
|
|
6
|
+
* switches on is always one of these.
|
|
7
|
+
*/
|
|
8
|
+
import type { NextAction } from "./auth.ts";
|
|
9
|
+
export interface AuthorizationErrorContract {
|
|
10
|
+
readonly status: number;
|
|
11
|
+
readonly retryable: boolean;
|
|
12
|
+
readonly nextAction: NextAction;
|
|
13
|
+
}
|
|
14
|
+
declare const contracts: {
|
|
15
|
+
readonly INVALID_REQUEST: {
|
|
16
|
+
readonly status: 400;
|
|
17
|
+
readonly retryable: false;
|
|
18
|
+
readonly nextAction: "none";
|
|
19
|
+
};
|
|
20
|
+
readonly REGISTRATION_SCOPE_SELECTION_REQUIRED: {
|
|
21
|
+
readonly status: 409;
|
|
22
|
+
readonly retryable: false;
|
|
23
|
+
readonly nextAction: "none";
|
|
24
|
+
};
|
|
25
|
+
readonly CURSOR_INVALID: {
|
|
26
|
+
readonly status: 400;
|
|
27
|
+
readonly retryable: false;
|
|
28
|
+
readonly nextAction: "none";
|
|
29
|
+
};
|
|
30
|
+
readonly UNSUPPORTED_VERSION: {
|
|
31
|
+
readonly status: 400;
|
|
32
|
+
readonly retryable: false;
|
|
33
|
+
readonly nextAction: "none";
|
|
34
|
+
};
|
|
35
|
+
readonly UNAUTHORIZED: {
|
|
36
|
+
readonly status: 401;
|
|
37
|
+
readonly retryable: false;
|
|
38
|
+
readonly nextAction: "reauthenticate";
|
|
39
|
+
};
|
|
40
|
+
readonly SCOPE_DENIED: {
|
|
41
|
+
readonly status: 403;
|
|
42
|
+
readonly retryable: false;
|
|
43
|
+
readonly nextAction: "none";
|
|
44
|
+
};
|
|
45
|
+
readonly ORIGIN_NOT_ALLOWED: {
|
|
46
|
+
readonly status: 403;
|
|
47
|
+
readonly retryable: false;
|
|
48
|
+
readonly nextAction: "none";
|
|
49
|
+
};
|
|
50
|
+
readonly LIMIT_PER_TRANSFER_EXCEEDED: {
|
|
51
|
+
readonly status: 403;
|
|
52
|
+
readonly retryable: false;
|
|
53
|
+
readonly nextAction: "none";
|
|
54
|
+
};
|
|
55
|
+
readonly LIMIT_DAILY_EXCEEDED: {
|
|
56
|
+
readonly status: 403;
|
|
57
|
+
readonly retryable: false;
|
|
58
|
+
readonly nextAction: "none";
|
|
59
|
+
};
|
|
60
|
+
readonly RECIPIENT_NOT_ALLOWED: {
|
|
61
|
+
readonly status: 403;
|
|
62
|
+
readonly retryable: false;
|
|
63
|
+
readonly nextAction: "none";
|
|
64
|
+
};
|
|
65
|
+
readonly CONFIRMATION_METHOD_UNAVAILABLE: {
|
|
66
|
+
readonly status: 403;
|
|
67
|
+
readonly retryable: false;
|
|
68
|
+
readonly nextAction: "use_passkey";
|
|
69
|
+
};
|
|
70
|
+
readonly USER_PROOF_REQUIRED: {
|
|
71
|
+
readonly status: 403;
|
|
72
|
+
readonly retryable: false;
|
|
73
|
+
readonly nextAction: "sign_user_request";
|
|
74
|
+
};
|
|
75
|
+
readonly USER_PROOF_INVALID: {
|
|
76
|
+
readonly status: 403;
|
|
77
|
+
readonly retryable: false;
|
|
78
|
+
readonly nextAction: "sign_user_request";
|
|
79
|
+
};
|
|
80
|
+
readonly REQUEST_GRANT_INVALID: {
|
|
81
|
+
readonly status: 403;
|
|
82
|
+
readonly retryable: false;
|
|
83
|
+
readonly nextAction: "use_passkey";
|
|
84
|
+
};
|
|
85
|
+
readonly DEBIT_PERMISSION_INVALID: {
|
|
86
|
+
readonly status: 403;
|
|
87
|
+
readonly retryable: false;
|
|
88
|
+
readonly nextAction: "none";
|
|
89
|
+
};
|
|
90
|
+
readonly NOT_FOUND: {
|
|
91
|
+
readonly status: 404;
|
|
92
|
+
readonly retryable: false;
|
|
93
|
+
readonly nextAction: "none";
|
|
94
|
+
};
|
|
95
|
+
readonly IDENTITY_NOT_REGISTERED: {
|
|
96
|
+
readonly status: 409;
|
|
97
|
+
readonly retryable: false;
|
|
98
|
+
readonly nextAction: "start_signup";
|
|
99
|
+
};
|
|
100
|
+
readonly IDENTITY_ALREADY_REGISTERED: {
|
|
101
|
+
readonly status: 409;
|
|
102
|
+
readonly retryable: false;
|
|
103
|
+
readonly nextAction: "start_login";
|
|
104
|
+
};
|
|
105
|
+
readonly IDENTITY_ALREADY_LINKED: {
|
|
106
|
+
readonly status: 409;
|
|
107
|
+
readonly retryable: false;
|
|
108
|
+
readonly nextAction: "none";
|
|
109
|
+
};
|
|
110
|
+
readonly LAST_FACTOR: {
|
|
111
|
+
readonly status: 409;
|
|
112
|
+
readonly retryable: false;
|
|
113
|
+
readonly nextAction: "none";
|
|
114
|
+
};
|
|
115
|
+
readonly CONFIRMATION_REPLAYED: {
|
|
116
|
+
readonly status: 409;
|
|
117
|
+
readonly retryable: false;
|
|
118
|
+
readonly nextAction: "none";
|
|
119
|
+
};
|
|
120
|
+
readonly CANNOT_CANCEL: {
|
|
121
|
+
readonly status: 409;
|
|
122
|
+
readonly retryable: false;
|
|
123
|
+
readonly nextAction: "none";
|
|
124
|
+
};
|
|
125
|
+
readonly VERSION_CONFLICT: {
|
|
126
|
+
readonly status: 409;
|
|
127
|
+
readonly retryable: false;
|
|
128
|
+
readonly nextAction: "refresh_resource";
|
|
129
|
+
};
|
|
130
|
+
readonly AUTHORIZATION_STALE: {
|
|
131
|
+
readonly status: 409;
|
|
132
|
+
readonly retryable: false;
|
|
133
|
+
readonly nextAction: "refresh_resource";
|
|
134
|
+
};
|
|
135
|
+
readonly IDEMPOTENCY_CONFLICT: {
|
|
136
|
+
readonly status: 409;
|
|
137
|
+
readonly retryable: false;
|
|
138
|
+
readonly nextAction: "lookup_operation";
|
|
139
|
+
};
|
|
140
|
+
readonly USER_PROOF_REPLAYED: {
|
|
141
|
+
readonly status: 409;
|
|
142
|
+
readonly retryable: false;
|
|
143
|
+
readonly nextAction: "lookup_operation";
|
|
144
|
+
};
|
|
145
|
+
readonly OPERATION_NOT_AUTHORIZED: {
|
|
146
|
+
readonly status: 409;
|
|
147
|
+
readonly retryable: false;
|
|
148
|
+
readonly nextAction: "use_passkey";
|
|
149
|
+
};
|
|
150
|
+
readonly RESULT_NOT_READY: {
|
|
151
|
+
readonly status: 409;
|
|
152
|
+
readonly retryable: true;
|
|
153
|
+
readonly nextAction: "wait_for_result";
|
|
154
|
+
};
|
|
155
|
+
readonly TRANSFER_IN_FLIGHT: {
|
|
156
|
+
readonly status: 409;
|
|
157
|
+
readonly retryable: true;
|
|
158
|
+
readonly nextAction: "retry_same_request";
|
|
159
|
+
};
|
|
160
|
+
readonly ATTEMPT_EXPIRED: {
|
|
161
|
+
readonly status: 410;
|
|
162
|
+
readonly retryable: false;
|
|
163
|
+
readonly nextAction: "reauthenticate";
|
|
164
|
+
};
|
|
165
|
+
readonly OPERATION_EXPIRED: {
|
|
166
|
+
readonly status: 410;
|
|
167
|
+
readonly retryable: false;
|
|
168
|
+
readonly nextAction: "none";
|
|
169
|
+
};
|
|
170
|
+
readonly RESULT_EXPIRED: {
|
|
171
|
+
readonly status: 410;
|
|
172
|
+
readonly retryable: false;
|
|
173
|
+
readonly nextAction: "none";
|
|
174
|
+
};
|
|
175
|
+
readonly RESYNC_REQUIRED: {
|
|
176
|
+
readonly status: 410;
|
|
177
|
+
readonly retryable: false;
|
|
178
|
+
readonly nextAction: "resync_streams";
|
|
179
|
+
};
|
|
180
|
+
readonly USER_PROOF_EXPIRED: {
|
|
181
|
+
readonly status: 410;
|
|
182
|
+
readonly retryable: false;
|
|
183
|
+
readonly nextAction: "sign_user_request";
|
|
184
|
+
};
|
|
185
|
+
readonly CAPABILITY_UNAVAILABLE: {
|
|
186
|
+
readonly status: 422;
|
|
187
|
+
readonly retryable: false;
|
|
188
|
+
readonly nextAction: "none";
|
|
189
|
+
};
|
|
190
|
+
readonly RECOVERY_UNAVAILABLE: {
|
|
191
|
+
readonly status: 422;
|
|
192
|
+
readonly retryable: false;
|
|
193
|
+
readonly nextAction: "none";
|
|
194
|
+
};
|
|
195
|
+
readonly PAYLOAD_NOT_SUPPORTED: {
|
|
196
|
+
readonly status: 422;
|
|
197
|
+
readonly retryable: false;
|
|
198
|
+
readonly nextAction: "none";
|
|
199
|
+
};
|
|
200
|
+
readonly RATE_LIMITED: {
|
|
201
|
+
readonly status: 429;
|
|
202
|
+
readonly retryable: true;
|
|
203
|
+
readonly nextAction: "retry_same_request";
|
|
204
|
+
};
|
|
205
|
+
readonly DEPENDENCY_UNAVAILABLE: {
|
|
206
|
+
readonly status: 503;
|
|
207
|
+
readonly retryable: true;
|
|
208
|
+
readonly nextAction: "retry_same_request";
|
|
209
|
+
};
|
|
210
|
+
readonly NETWORK_FEE_TOO_HIGH: {
|
|
211
|
+
readonly status: 503;
|
|
212
|
+
readonly retryable: true;
|
|
213
|
+
readonly nextAction: "retry_same_request";
|
|
214
|
+
};
|
|
215
|
+
readonly INSUFFICIENT_BALANCE: {
|
|
216
|
+
readonly status: 422;
|
|
217
|
+
readonly retryable: false;
|
|
218
|
+
readonly nextAction: "none";
|
|
219
|
+
};
|
|
220
|
+
readonly WALLET_POLICY_DENIED: {
|
|
221
|
+
readonly status: 403;
|
|
222
|
+
readonly retryable: false;
|
|
223
|
+
readonly nextAction: "none";
|
|
224
|
+
};
|
|
225
|
+
readonly TENANT_AUTH_UNAVAILABLE: {
|
|
226
|
+
readonly status: 503;
|
|
227
|
+
readonly retryable: true;
|
|
228
|
+
readonly nextAction: "retry_same_request";
|
|
229
|
+
};
|
|
230
|
+
readonly AUDIT_UNAVAILABLE: {
|
|
231
|
+
readonly status: 503;
|
|
232
|
+
readonly retryable: true;
|
|
233
|
+
readonly nextAction: "retry_same_request";
|
|
234
|
+
};
|
|
235
|
+
readonly ADAPTER_INTEGRITY_CONFLICT: {
|
|
236
|
+
readonly status: 503;
|
|
237
|
+
readonly retryable: false;
|
|
238
|
+
readonly nextAction: "none";
|
|
239
|
+
};
|
|
240
|
+
readonly INTERNAL_ERROR: {
|
|
241
|
+
readonly status: 500;
|
|
242
|
+
readonly retryable: true;
|
|
243
|
+
readonly nextAction: "retry_same_request";
|
|
244
|
+
};
|
|
245
|
+
};
|
|
246
|
+
/** One code of the provider's ErrorCode enum. */
|
|
247
|
+
export type AuthorizationErrorCode = keyof typeof contracts;
|
|
248
|
+
/** Every code, with the status, `retryable` and `nextAction` it is answered with. */
|
|
249
|
+
export declare const AuthorizationErrorContracts: Readonly<Record<AuthorizationErrorCode, AuthorizationErrorContract>>;
|
|
250
|
+
/** Every code of the provider's ErrorCode enum. */
|
|
251
|
+
export declare const AuthorizationErrorCodes: readonly AuthorizationErrorCode[];
|
|
252
|
+
export declare function isAuthorizationErrorCode(value: unknown): value is AuthorizationErrorCode;
|
|
253
|
+
/**
|
|
254
|
+
* The refusals of a Mode C transfer that the person's app signed and the
|
|
255
|
+
* service forwarded, besides the answers every request can get. The proof:
|
|
256
|
+
* `USER_PROOF_REQUIRED`, `USER_PROOF_INVALID`, `USER_PROOF_EXPIRED`, and
|
|
257
|
+
* `USER_PROOF_REPLAYED` or `IDEMPOTENCY_CONFLICT` for a reused one. The grant:
|
|
258
|
+
* `REQUEST_GRANT_INVALID` when it is not this service's, has ended or was
|
|
259
|
+
* enrolled on other bounds. The amount and the recipient, against the grant's
|
|
260
|
+
* bounds and the wallet's limits: `LIMIT_PER_TRANSFER_EXCEEDED`,
|
|
261
|
+
* `LIMIT_DAILY_EXCEEDED`, `RECIPIENT_NOT_ALLOWED`. The fee:
|
|
262
|
+
* `NETWORK_FEE_TOO_HIGH`. The balance, short of the amount and the fee's
|
|
263
|
+
* ceiling: `INSUFFICIENT_BALANCE`. The wallet system's own signing policy:
|
|
264
|
+
* `WALLET_POLICY_DENIED`. A refused transfer moved nothing.
|
|
265
|
+
*/
|
|
266
|
+
export declare const ModeCRefusals: readonly ["USER_PROOF_REQUIRED", "USER_PROOF_INVALID", "USER_PROOF_EXPIRED", "USER_PROOF_REPLAYED", "IDEMPOTENCY_CONFLICT", "REQUEST_GRANT_INVALID", "LIMIT_PER_TRANSFER_EXCEEDED", "LIMIT_DAILY_EXCEEDED", "RECIPIENT_NOT_ALLOWED", "NETWORK_FEE_TOO_HIGH", "INSUFFICIENT_BALANCE", "WALLET_POLICY_DENIED"];
|
|
267
|
+
export type ModeCRefusal = (typeof ModeCRefusals)[number];
|
|
268
|
+
export declare const isModeCRefusal: (code: unknown) => code is ModeCRefusal;
|
|
269
|
+
/**
|
|
270
|
+
* The status of a write that possibly committed: it keeps its code and answers retryable false with
|
|
271
|
+
* lookup_operation and the operation to look up, at 500 for a code that is 500 and otherwise at 503
|
|
272
|
+
* (the server's error encoder).
|
|
273
|
+
*/
|
|
274
|
+
export declare const possibleCommitStatus: (code: AuthorizationErrorCode) => number;
|
|
275
|
+
/**
|
|
276
|
+
* The codes the server names an operation with while keeping their own tuple: its WithOperationID call
|
|
277
|
+
* sites and the platform-custody result read. Every other operation_id is a possible commit.
|
|
278
|
+
*/
|
|
279
|
+
export declare const operationIdCodes: ReadonlySet<string>;
|
|
280
|
+
export {};
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const contracts = {
|
|
2
|
+
INVALID_REQUEST: { status: 400, retryable: false, nextAction: "none" },
|
|
3
|
+
REGISTRATION_SCOPE_SELECTION_REQUIRED: { status: 409, retryable: false, nextAction: "none" },
|
|
4
|
+
CURSOR_INVALID: { status: 400, retryable: false, nextAction: "none" },
|
|
5
|
+
UNSUPPORTED_VERSION: { status: 400, retryable: false, nextAction: "none" },
|
|
6
|
+
UNAUTHORIZED: { status: 401, retryable: false, nextAction: "reauthenticate" },
|
|
7
|
+
SCOPE_DENIED: { status: 403, retryable: false, nextAction: "none" },
|
|
8
|
+
ORIGIN_NOT_ALLOWED: { status: 403, retryable: false, nextAction: "none" },
|
|
9
|
+
LIMIT_PER_TRANSFER_EXCEEDED: { status: 403, retryable: false, nextAction: "none" },
|
|
10
|
+
LIMIT_DAILY_EXCEEDED: { status: 403, retryable: false, nextAction: "none" },
|
|
11
|
+
RECIPIENT_NOT_ALLOWED: { status: 403, retryable: false, nextAction: "none" },
|
|
12
|
+
CONFIRMATION_METHOD_UNAVAILABLE: { status: 403, retryable: false, nextAction: "use_passkey" },
|
|
13
|
+
USER_PROOF_REQUIRED: { status: 403, retryable: false, nextAction: "sign_user_request" },
|
|
14
|
+
USER_PROOF_INVALID: { status: 403, retryable: false, nextAction: "sign_user_request" },
|
|
15
|
+
REQUEST_GRANT_INVALID: { status: 403, retryable: false, nextAction: "use_passkey" },
|
|
16
|
+
DEBIT_PERMISSION_INVALID: { status: 403, retryable: false, nextAction: "none" },
|
|
17
|
+
NOT_FOUND: { status: 404, retryable: false, nextAction: "none" },
|
|
18
|
+
IDENTITY_NOT_REGISTERED: { status: 409, retryable: false, nextAction: "start_signup" },
|
|
19
|
+
IDENTITY_ALREADY_REGISTERED: { status: 409, retryable: false, nextAction: "start_login" },
|
|
20
|
+
IDENTITY_ALREADY_LINKED: { status: 409, retryable: false, nextAction: "none" },
|
|
21
|
+
LAST_FACTOR: { status: 409, retryable: false, nextAction: "none" },
|
|
22
|
+
CONFIRMATION_REPLAYED: { status: 409, retryable: false, nextAction: "none" },
|
|
23
|
+
CANNOT_CANCEL: { status: 409, retryable: false, nextAction: "none" },
|
|
24
|
+
VERSION_CONFLICT: { status: 409, retryable: false, nextAction: "refresh_resource" },
|
|
25
|
+
AUTHORIZATION_STALE: { status: 409, retryable: false, nextAction: "refresh_resource" },
|
|
26
|
+
IDEMPOTENCY_CONFLICT: { status: 409, retryable: false, nextAction: "lookup_operation" },
|
|
27
|
+
USER_PROOF_REPLAYED: { status: 409, retryable: false, nextAction: "lookup_operation" },
|
|
28
|
+
OPERATION_NOT_AUTHORIZED: { status: 409, retryable: false, nextAction: "use_passkey" },
|
|
29
|
+
RESULT_NOT_READY: { status: 409, retryable: true, nextAction: "wait_for_result" },
|
|
30
|
+
TRANSFER_IN_FLIGHT: { status: 409, retryable: true, nextAction: "retry_same_request" },
|
|
31
|
+
ATTEMPT_EXPIRED: { status: 410, retryable: false, nextAction: "reauthenticate" },
|
|
32
|
+
OPERATION_EXPIRED: { status: 410, retryable: false, nextAction: "none" },
|
|
33
|
+
RESULT_EXPIRED: { status: 410, retryable: false, nextAction: "none" },
|
|
34
|
+
RESYNC_REQUIRED: { status: 410, retryable: false, nextAction: "resync_streams" },
|
|
35
|
+
USER_PROOF_EXPIRED: { status: 410, retryable: false, nextAction: "sign_user_request" },
|
|
36
|
+
CAPABILITY_UNAVAILABLE: { status: 422, retryable: false, nextAction: "none" },
|
|
37
|
+
RECOVERY_UNAVAILABLE: { status: 422, retryable: false, nextAction: "none" },
|
|
38
|
+
PAYLOAD_NOT_SUPPORTED: { status: 422, retryable: false, nextAction: "none" },
|
|
39
|
+
RATE_LIMITED: { status: 429, retryable: true, nextAction: "retry_same_request" },
|
|
40
|
+
DEPENDENCY_UNAVAILABLE: { status: 503, retryable: true, nextAction: "retry_same_request" },
|
|
41
|
+
NETWORK_FEE_TOO_HIGH: { status: 503, retryable: true, nextAction: "retry_same_request" },
|
|
42
|
+
INSUFFICIENT_BALANCE: { status: 422, retryable: false, nextAction: "none" },
|
|
43
|
+
WALLET_POLICY_DENIED: { status: 403, retryable: false, nextAction: "none" },
|
|
44
|
+
TENANT_AUTH_UNAVAILABLE: { status: 503, retryable: true, nextAction: "retry_same_request" },
|
|
45
|
+
AUDIT_UNAVAILABLE: { status: 503, retryable: true, nextAction: "retry_same_request" },
|
|
46
|
+
ADAPTER_INTEGRITY_CONFLICT: { status: 503, retryable: false, nextAction: "none" },
|
|
47
|
+
INTERNAL_ERROR: { status: 500, retryable: true, nextAction: "retry_same_request" },
|
|
48
|
+
};
|
|
49
|
+
/** Every code, with the status, `retryable` and `nextAction` it is answered with. */
|
|
50
|
+
export const AuthorizationErrorContracts = Object.freeze(contracts);
|
|
51
|
+
/** Every code of the provider's ErrorCode enum. */
|
|
52
|
+
export const AuthorizationErrorCodes = Object.freeze(Object.keys(contracts));
|
|
53
|
+
export function isAuthorizationErrorCode(value) {
|
|
54
|
+
return typeof value === "string" && Object.prototype.hasOwnProperty.call(contracts, value);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The refusals of a Mode C transfer that the person's app signed and the
|
|
58
|
+
* service forwarded, besides the answers every request can get. The proof:
|
|
59
|
+
* `USER_PROOF_REQUIRED`, `USER_PROOF_INVALID`, `USER_PROOF_EXPIRED`, and
|
|
60
|
+
* `USER_PROOF_REPLAYED` or `IDEMPOTENCY_CONFLICT` for a reused one. The grant:
|
|
61
|
+
* `REQUEST_GRANT_INVALID` when it is not this service's, has ended or was
|
|
62
|
+
* enrolled on other bounds. The amount and the recipient, against the grant's
|
|
63
|
+
* bounds and the wallet's limits: `LIMIT_PER_TRANSFER_EXCEEDED`,
|
|
64
|
+
* `LIMIT_DAILY_EXCEEDED`, `RECIPIENT_NOT_ALLOWED`. The fee:
|
|
65
|
+
* `NETWORK_FEE_TOO_HIGH`. The balance, short of the amount and the fee's
|
|
66
|
+
* ceiling: `INSUFFICIENT_BALANCE`. The wallet system's own signing policy:
|
|
67
|
+
* `WALLET_POLICY_DENIED`. A refused transfer moved nothing.
|
|
68
|
+
*/
|
|
69
|
+
export const ModeCRefusals = Object.freeze([
|
|
70
|
+
"USER_PROOF_REQUIRED", "USER_PROOF_INVALID", "USER_PROOF_EXPIRED", "USER_PROOF_REPLAYED", "IDEMPOTENCY_CONFLICT",
|
|
71
|
+
"REQUEST_GRANT_INVALID", "LIMIT_PER_TRANSFER_EXCEEDED", "LIMIT_DAILY_EXCEEDED", "RECIPIENT_NOT_ALLOWED", "NETWORK_FEE_TOO_HIGH", "INSUFFICIENT_BALANCE", "WALLET_POLICY_DENIED",
|
|
72
|
+
]);
|
|
73
|
+
export const isModeCRefusal = (code) => ModeCRefusals.includes(code);
|
|
74
|
+
/**
|
|
75
|
+
* The status of a write that possibly committed: it keeps its code and answers retryable false with
|
|
76
|
+
* lookup_operation and the operation to look up, at 500 for a code that is 500 and otherwise at 503
|
|
77
|
+
* (the server's error encoder).
|
|
78
|
+
*/
|
|
79
|
+
export const possibleCommitStatus = (code) => AuthorizationErrorContracts[code].status === 500 ? 500 : 503;
|
|
80
|
+
/**
|
|
81
|
+
* The codes the server names an operation with while keeping their own tuple: its WithOperationID call
|
|
82
|
+
* sites and the platform-custody result read. Every other operation_id is a possible commit.
|
|
83
|
+
*/
|
|
84
|
+
export const operationIdCodes = new Set(["AUTHORIZATION_STALE", "USER_PROOF_REQUIRED", "IDEMPOTENCY_CONFLICT", "RESULT_NOT_READY", "OPERATION_EXPIRED", "RESULT_EXPIRED",
|
|
85
|
+
"INVALID_REQUEST", "CAPABILITY_UNAVAILABLE", "ADAPTER_INTEGRITY_CONFLICT", "DEPENDENCY_UNAVAILABLE"]);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ResultRecipient } from './slice4.ts';
|
|
2
|
+
export declare function fault(): never;
|
|
3
|
+
export declare function requestID(v: unknown): void;
|
|
4
|
+
export declare function requestUUID(v: unknown): void;
|
|
5
|
+
export declare function token(v: unknown): void;
|
|
6
|
+
export declare function uuid(v: unknown): v is string;
|
|
7
|
+
export declare function safeRevision(v: unknown): v is string;
|
|
8
|
+
export declare function text(v: unknown, max: number): v is string;
|
|
9
|
+
export declare function hex(v: unknown): v is string;
|
|
10
|
+
export declare function b64(v: unknown): v is string;
|
|
11
|
+
export declare function digest(v: unknown): v is string;
|
|
12
|
+
export declare function publicKey(a: unknown, v: unknown): boolean;
|
|
13
|
+
export declare function network(v: unknown): v is string;
|
|
14
|
+
export declare function assets(v: unknown): v is string[];
|
|
15
|
+
export declare function activityTypes(v: unknown): v is string[];
|
|
16
|
+
export declare function recipient(v: unknown): ResultRecipient;
|
|
17
|
+
export declare function https(v: unknown): boolean;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// The small checks the linked-wallet modules share: identifiers, revisions,
|
|
2
|
+
// networks, assets and the error a request or an answer fails with. They
|
|
3
|
+
// moved out of linkedWallets.ts when that file reached the repository line
|
|
4
|
+
// target.
|
|
5
|
+
import { ClientFlowError, canonicalDecimal, opaqueId, record } from './auth.js';
|
|
6
|
+
import { MaxStreamsActivityTypes } from './streams.js';
|
|
7
|
+
export function fault() { throw new ClientFlowError('invalid_server_response'); }
|
|
8
|
+
export function requestID(v) { if (!opaqueId(v))
|
|
9
|
+
throw new ClientFlowError('invalid_linked_wallet_request'); }
|
|
10
|
+
export function requestUUID(v) { if (!uuid(v))
|
|
11
|
+
throw new ClientFlowError('invalid_linked_wallet_request'); }
|
|
12
|
+
export function token(v) { if (!text(v, 8192))
|
|
13
|
+
throw new ClientFlowError('invalid_linked_wallet_request'); }
|
|
14
|
+
export function uuid(v) { return typeof v === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(v); }
|
|
15
|
+
export function safeRevision(v) { return canonicalDecimal(v) && v.length <= 16 && v !== '0' && BigInt(v) <= BigInt(Number.MAX_SAFE_INTEGER); }
|
|
16
|
+
export function text(v, max) { return typeof v === 'string' && v.length > 0 && new TextEncoder().encode(v).length <= max && v.trim() === v && !/[\x00-\x1f\x7f]/.test(v); }
|
|
17
|
+
export function hex(v) { return typeof v === 'string' && v.length > 0 && v.length <= 2048 && /^([0-9a-f]{2})+$/.test(v); }
|
|
18
|
+
export function b64(v) { return typeof v === 'string' && /^[A-Za-z0-9_-]+$/.test(v); }
|
|
19
|
+
export function digest(v) { return typeof v === 'string' && /^[0-9a-f]{64}$/.test(v); }
|
|
20
|
+
export function publicKey(a, v) { return typeof v === 'string' && (a === 'ed25519' ? /^[a-f0-9]{64}$/.test(v) : a === 'secp256k1' && /^(02|03)[a-f0-9]{64}$|^04[a-f0-9]{128}$/.test(v)); }
|
|
21
|
+
export function network(v) { return typeof v === 'string' && /^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}$/.test(v); }
|
|
22
|
+
export function assets(v) { return Array.isArray(v) && v.length <= 32 && new Set(v).size === v.length && v.every(a => typeof a === 'string' && /^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}\/[-a-z0-9]{3,8}:[-.%a-zA-Z0-9]{1,128}(\/[-.%a-zA-Z0-9]{1,78})?$/.test(a)); }
|
|
23
|
+
export function activityTypes(v) { return Array.isArray(v) && v.length <= MaxStreamsActivityTypes && new Set(v).size === v.length && v.every(t => typeof t === 'string' && t.length <= 64 && /^[a-z][a-z0-9]*(?:[._][a-z0-9]+)*$/.test(t)); }
|
|
24
|
+
export function recipient(v) { const r = record(v, ['type', 'id']); if (!['account', 'service'].includes(r.type) || !opaqueId(r.id))
|
|
25
|
+
fault(); return { type: r.type, id: r.id }; }
|
|
26
|
+
export function https(v) { try {
|
|
27
|
+
return typeof v === 'string' && new URL(v).protocol === 'https:';
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
} }
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type DisplayCommon } from "./walletDisplayParts.ts";
|
|
2
|
+
import type { LinkedWalletIdentity } from "./linkedSendDisplay.ts";
|
|
3
|
+
import type { DeviceSharePackage } from "./walletOperations.ts";
|
|
4
|
+
/** The create body of a recovery: the account's own linked wallet and the new device key its share is sealed to. */
|
|
5
|
+
export type LinkedRecoverCreate = {
|
|
6
|
+
owner_type: "account";
|
|
7
|
+
owner_id: string;
|
|
8
|
+
kind: "wallet.recover";
|
|
9
|
+
linked_wallet_id: string;
|
|
10
|
+
payload: {
|
|
11
|
+
recipient_public_key: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* LinkedRecoverDisplay is what the person confirms: the wallet by its address
|
|
16
|
+
* and network, and the device key its share goes to. The summary is there from
|
|
17
|
+
* the admission on, since the recovery is frozen whole when it is admitted.
|
|
18
|
+
*/
|
|
19
|
+
export type LinkedRecoverDisplay = DisplayCommon & LinkedWalletIdentity & {
|
|
20
|
+
kind: "wallet.recover";
|
|
21
|
+
network: string;
|
|
22
|
+
address: string;
|
|
23
|
+
recipientPublicKey: string;
|
|
24
|
+
preparedSummary?: string;
|
|
25
|
+
};
|
|
26
|
+
export declare function linkedRecoverCreate(ownerId: string, linkedWalletId: string, recipientPublicKey: string): LinkedRecoverCreate;
|
|
27
|
+
/** validLinkedRecoverCreate checks the members a recovery adds to the common ones validateUserWalletCreate checked. */
|
|
28
|
+
export declare function validLinkedRecoverCreate(common: Record<string, unknown>): boolean;
|
|
29
|
+
export declare function parseLinkedRecoverDisplay(value: unknown): LinkedRecoverDisplay;
|
|
30
|
+
/** The released result of a recovery: the wallet it is of and its device share, sealed to the new device key. */
|
|
31
|
+
export declare function parseRecoveredShareResult(requestId: unknown, value: unknown, expectedOperationId: string): {
|
|
32
|
+
requestId: string;
|
|
33
|
+
result: {
|
|
34
|
+
operationId: string;
|
|
35
|
+
kind: "device_share_recovered";
|
|
36
|
+
receiptId: string;
|
|
37
|
+
linkedWalletId: string;
|
|
38
|
+
deviceShare: DeviceSharePackage;
|
|
39
|
+
};
|
|
40
|
+
};
|