@shuttle.technology/authorization 0.1.0 → 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.
Files changed (52) hide show
  1. package/CHANGELOG.md +191 -0
  2. package/README.md +1082 -666
  3. package/dist/accountLists.d.ts +28 -0
  4. package/dist/accountLists.js +27 -0
  5. package/dist/auth.d.ts +11 -11
  6. package/dist/auth.js +28 -54
  7. package/dist/client.d.ts +62 -174
  8. package/dist/client.js +381 -354
  9. package/dist/clientTypes.d.ts +56 -0
  10. package/dist/clientTypes.js +5 -0
  11. package/dist/configuration.d.ts +48 -0
  12. package/dist/configuration.js +72 -0
  13. package/dist/consentDebit.d.ts +36 -0
  14. package/dist/consentDebit.js +95 -0
  15. package/dist/credentials.d.ts +116 -0
  16. package/dist/credentials.js +315 -0
  17. package/dist/debitPermissionWire.d.ts +90 -0
  18. package/dist/debitPermissionWire.js +118 -0
  19. package/dist/debitPermissions.d.ts +39 -0
  20. package/dist/debitPermissions.js +117 -0
  21. package/dist/deviceRecovery.d.ts +40 -0
  22. package/dist/deviceRecovery.js +143 -0
  23. package/dist/deviceShares.d.ts +91 -0
  24. package/dist/deviceShares.js +204 -0
  25. package/dist/errors.d.ts +280 -0
  26. package/dist/errors.js +85 -0
  27. package/dist/linkedRecovery.d.ts +40 -0
  28. package/dist/linkedRecovery.js +49 -0
  29. package/dist/linkedRegistrations.d.ts +11 -0
  30. package/dist/linkedRegistrations.js +109 -0
  31. package/dist/linkedWallets.d.ts +46 -18
  32. package/dist/linkedWallets.js +85 -120
  33. package/dist/me.d.ts +27 -0
  34. package/dist/me.js +30 -0
  35. package/dist/mobile.d.ts +64 -0
  36. package/dist/mobile.js +69 -0
  37. package/dist/redirects.d.ts +45 -0
  38. package/dist/redirects.js +108 -0
  39. package/dist/serviceGrants.d.ts +52 -0
  40. package/dist/serviceGrants.js +46 -0
  41. package/dist/slice4.d.ts +20 -6
  42. package/dist/slice4.js +22 -8
  43. package/dist/streams.js +1 -1
  44. package/dist/walletCreateGrant.d.ts +62 -0
  45. package/dist/walletCreateGrant.js +89 -0
  46. package/dist/walletDisplayParts.d.ts +2 -0
  47. package/dist/walletDisplayParts.js +4 -0
  48. package/dist/walletOperations.d.ts +23 -6
  49. package/dist/walletOperations.js +31 -22
  50. package/dist/webRenewal.d.ts +92 -0
  51. package/dist/webRenewal.js +179 -0
  52. package/package.json +25 -23
@@ -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,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
+ };
@@ -0,0 +1,49 @@
1
+ // The wire of a linked wallet's device-share recovery (wallet.recover naming
2
+ // linked_wallet_id): the closed create body, the display the person confirms
3
+ // with the passkey, and the result, the device share sealed to the new device
4
+ // key. deviceRecovery.ts runs it; walletOperations.ts reads it among the
5
+ // other wallet operations.
6
+ import { ClientFlowError, opaqueId, record } from "./auth.js";
7
+ import { bindingRevision, displayCommon, lowerHexBytes, lowerUUID, summaryOf, walletRecipient } from "./walletDisplayParts.js";
8
+ const compressedP256Key = /^(02|03)[0-9a-f]{64}$/;
9
+ const linkedNetwork = /^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}$/;
10
+ export function linkedRecoverCreate(ownerId, linkedWalletId, recipientPublicKey) {
11
+ return { kind: "wallet.recover", owner_type: "account", owner_id: ownerId, linked_wallet_id: linkedWalletId, payload: { recipient_public_key: recipientPublicKey } };
12
+ }
13
+ /** validLinkedRecoverCreate checks the members a recovery adds to the common ones validateUserWalletCreate checked. */
14
+ export function validLinkedRecoverCreate(common) {
15
+ if (common.kind !== "wallet.recover" || common.wallet_id !== undefined || !lowerUUID(common.linked_wallet_id))
16
+ return false;
17
+ const payload = common.payload;
18
+ return payload !== null && typeof payload === "object" && !Array.isArray(payload) && Object.keys(payload).length === 1 &&
19
+ typeof payload.recipient_public_key === "string" && compressedP256Key.test(payload.recipient_public_key);
20
+ }
21
+ export function parseLinkedRecoverDisplay(value) {
22
+ const display = record(value, ["kind", "owner_id", "requester_id", "result_recipient", "binding_source", "connection_id", "linked_wallet_id", "binding_revision", "network", "address", "recipient_public_key"], ["prepared_summary"]);
23
+ if (display.kind !== "wallet.recover" || display.binding_source !== "linked_wallet" || !lowerUUID(display.connection_id) || !lowerUUID(display.linked_wallet_id) ||
24
+ !bindingRevision(display.binding_revision) || typeof display.network !== "string" || !linkedNetwork.test(display.network) ||
25
+ typeof display.address !== "string" || !walletRecipient.test(display.address) || typeof display.recipient_public_key !== "string" || !compressedP256Key.test(display.recipient_public_key)) {
26
+ throw new ClientFlowError("invalid_server_response");
27
+ }
28
+ return {
29
+ ...displayCommon(display), kind: "wallet.recover", connectionId: display.connection_id, linkedWalletId: display.linked_wallet_id, bindingRevision: display.binding_revision,
30
+ network: display.network, address: display.address, recipientPublicKey: display.recipient_public_key, ...summaryOf(display.prepared_summary),
31
+ };
32
+ }
33
+ /** The released result of a recovery: the wallet it is of and its device share, sealed to the new device key. */
34
+ export function parseRecoveredShareResult(requestId, value, expectedOperationId) {
35
+ const result = record(value, ["kind", "operation_id", "receipt_id", "linked_wallet_id", "device_share"]);
36
+ const share = record(result.device_share, ["ciphertext", "nonce", "ephemeral_public_key", "encryption_algorithm", "encryption_context"]);
37
+ if (!opaqueId(requestId) || result.kind !== "device_share_recovered" || result.operation_id !== expectedOperationId || !lowerUUID(result.receipt_id) || !lowerUUID(result.linked_wallet_id) ||
38
+ !lowerHexBytes(share.ciphertext) || !lowerHexBytes(share.nonce) || !lowerHexBytes(share.ephemeral_public_key) ||
39
+ typeof share.encryption_algorithm !== "string" || !/^[a-z0-9_-]{1,128}$/.test(share.encryption_algorithm) || !lowerHexBytes(share.encryption_context)) {
40
+ throw new ClientFlowError("invalid_server_response");
41
+ }
42
+ return {
43
+ requestId,
44
+ result: {
45
+ operationId: expectedOperationId, kind: "device_share_recovered", receiptId: result.receipt_id, linkedWalletId: result.linked_wallet_id,
46
+ deviceShare: { ciphertext: share.ciphertext, nonce: share.nonce, ephemeralPublicKey: share.ephemeral_public_key, encryptionAlgorithm: share.encryption_algorithm, encryptionContext: share.encryption_context },
47
+ },
48
+ };
49
+ }
@@ -0,0 +1,11 @@
1
+ import type { LinkedEventsOptions, LinkedRegistrationCreate, LinkedRegistrationListOptions, LinkedRegistrationOperation, LinkedRegistrationResult, LinkedStreamsRegistrationList } from './linkedWallets.ts';
2
+ export declare function validateLinkedRegistrationCreate(value: LinkedRegistrationCreate, ownerId: string): LinkedRegistrationCreate;
3
+ export declare function parseLinkedRegistrationOperationEnvelope(payload: unknown): {
4
+ requestId: string;
5
+ operation: LinkedRegistrationOperation;
6
+ };
7
+ export declare function parseLinkedRegistrationOperation(payload: unknown): LinkedRegistrationOperation;
8
+ export declare function parseLinkedRegistrationResult(payload: unknown, operationId: string): LinkedRegistrationResult;
9
+ export declare function linkedRegistrationsPath(linkedWalletId: string, options?: LinkedRegistrationListOptions): string;
10
+ export declare function linkedEventsPath(linkedWalletId: string, options?: LinkedEventsOptions): string;
11
+ export declare function parseLinkedStreamsRegistrationList(payload: unknown, linkedWalletId: string): LinkedStreamsRegistrationList;
@@ -0,0 +1,109 @@
1
+ // The linked-wallet registrations for tracking, as Authorization frames them:
2
+ // the closed create body, the operation and its result, and the paths and
3
+ // pages of a linked wallet's registrations and events. linkedWallets.ts calls
4
+ // them and re-exports them.
5
+ import { ClientFlowError, canonicalDecimal, deadlineAfter, integer, isNextAction, opaqueId, record, rfc3339UTC } from './auth.js';
6
+ import { appendEventTypes, MaxStreamsCursorLength, MaxStreamsPageLimit } from './streams.js';
7
+ import { activityTypes, assets, digest, fault, https, network, recipient, requestUUID, safeRevision, text, uuid } from './linkedChecks.js';
8
+ export function validateLinkedRegistrationCreate(value, ownerId) {
9
+ const r = record(value, ['kind', 'owner_type', 'owner_id', 'linked_wallet_id', 'payload']);
10
+ const p = record(r.payload, ['network'], ['assets', 'activity_types']);
11
+ if (r.kind !== 'streams.register' || r.owner_type !== 'account' || !opaqueId(r.owner_id) || r.owner_id !== ownerId || !uuid(r.linked_wallet_id) || !network(p.network) || (p.assets !== undefined && !assets(p.assets)) || (p.activity_types !== undefined && !activityTypes(p.activity_types)))
12
+ throw new ClientFlowError('invalid_operation_create');
13
+ return value;
14
+ }
15
+ export function parseLinkedRegistrationOperationEnvelope(payload) {
16
+ const r = record(payload, ['request_id', 'operation']);
17
+ if (!opaqueId(r.request_id))
18
+ fault();
19
+ return { requestId: r.request_id, operation: parseLinkedRegistrationOperation(r.operation) };
20
+ }
21
+ export function parseLinkedRegistrationOperation(payload) {
22
+ const r = record(payload, ['id', 'version', 'kind', 'state', 'effect_state', 'authorization_basis', 'owner_type', 'owner_id', 'requester_id', 'binding_source', 'connection_id', 'linked_wallet_id', 'binding_revision', 'request_fingerprint', 'operation_fingerprint', 'display', 'created_at', 'expires_at', 'next_action'], ['confirmation_location']);
23
+ if (!opaqueId(r.id) || !canonicalDecimal(r.version) || r.kind !== 'streams.register' || r.owner_type !== 'account' || !opaqueId(r.owner_id) || !opaqueId(r.requester_id) || r.binding_source !== 'linked_wallet' || !uuid(r.connection_id) || !uuid(r.linked_wallet_id) || !safeRevision(r.binding_revision) || !digest(r.request_fingerprint) || !digest(r.operation_fingerprint) || !rfc3339UTC(r.created_at) || !rfc3339UTC(r.expires_at) || !deadlineAfter(r.created_at, r.expires_at) || !isNextAction(r.next_action) || !['awaiting_confirmation', 'authorized', 'dispatching', 'outcome_unknown', 'succeeded', 'failed', 'denied', 'canceled', 'expired'].includes(r.state) || !['no_effect', 'possible', 'known'].includes(r.effect_state) || !['', 'user_confirmed', 'session_authorized'].includes(r.authorization_basis) || (r.confirmation_location !== undefined && !https(r.confirmation_location)))
24
+ fault();
25
+ const purged = typeof r.display === 'object' && r.display !== null && 'material_purged' in r.display;
26
+ const d = record(r.display, ['kind', 'owner_id', 'requester_id', 'result_recipient', ...(purged ? ['material_purged'] : ['binding_source', 'connection_id', 'linked_wallet_id', 'binding_revision', 'assets', 'network', 'address', 'streams_mapping_id', 'streams_mapping_version', 'target_customer_id', 'target_project_id', 'target_environment_id'])]);
27
+ if (d.kind !== r.kind || d.owner_id !== r.owner_id || d.requester_id !== r.requester_id)
28
+ fault();
29
+ const common = { kind: 'streams.register', ownerId: r.owner_id, requesterId: r.requester_id, resultRecipient: recipient(d.result_recipient) };
30
+ let display;
31
+ if (purged) {
32
+ if (d.material_purged !== true)
33
+ fault();
34
+ display = { ...common, materialPurged: true };
35
+ }
36
+ else {
37
+ if (d.binding_source !== r.binding_source || d.connection_id !== r.connection_id || d.linked_wallet_id !== r.linked_wallet_id || d.binding_revision !== r.binding_revision || !assets(d.assets) || !network(d.network) || !text(d.address, 256) || !safeRevision(d.streams_mapping_version) || ![d.streams_mapping_id, d.target_customer_id, d.target_project_id, d.target_environment_id].every(opaqueId))
38
+ fault();
39
+ display = { ...common, bindingSource: 'linked_wallet', connectionId: r.connection_id, linkedWalletId: r.linked_wallet_id, bindingRevision: r.binding_revision, assets: Object.freeze([...d.assets]), network: d.network, address: d.address, streamsMappingId: d.streams_mapping_id, streamsMappingVersion: d.streams_mapping_version, targetCustomerId: d.target_customer_id, targetProjectId: d.target_project_id, targetEnvironmentId: d.target_environment_id };
40
+ }
41
+ const operation = { id: r.id, version: r.version, kind: 'streams.register', state: r.state, effectState: r.effect_state, authorizationBasis: r.authorization_basis, ownerType: 'account', ownerId: r.owner_id, requesterId: r.requester_id, bindingSource: 'linked_wallet', connectionId: r.connection_id, linkedWalletId: r.linked_wallet_id, bindingRevision: r.binding_revision, requestFingerprint: r.request_fingerprint, operationFingerprint: r.operation_fingerprint, display, createdAt: r.created_at, expiresAt: r.expires_at, nextAction: r.next_action };
42
+ if (r.confirmation_location !== undefined)
43
+ operation.confirmationLocation = r.confirmation_location;
44
+ return operation;
45
+ }
46
+ export function parseLinkedRegistrationResult(payload, operationId) {
47
+ const r = record(payload, ['request_id', 'result']);
48
+ const v = record(r.result, ['kind', 'operation_id', 'binding_source', 'connection_id', 'linked_wallet_id', 'binding_revision', 'source_registration_id', 'registration_digest', 'receipt_id']);
49
+ if (!opaqueId(r.request_id) || !opaqueId(v.operation_id) || v.operation_id !== operationId || v.kind !== 'streams_registration' || v.binding_source !== 'linked_wallet' || !uuid(v.connection_id) || !uuid(v.linked_wallet_id) || !safeRevision(v.binding_revision) || !text(v.source_registration_id, 128) || !text(v.receipt_id, 128) || !digest(v.registration_digest))
50
+ fault();
51
+ return { requestId: r.request_id, result: { kind: 'streams_registration', operationId: v.operation_id, bindingSource: 'linked_wallet', connectionId: v.connection_id, linkedWalletId: v.linked_wallet_id, bindingRevision: v.binding_revision, sourceRegistrationId: v.source_registration_id, registrationDigest: v.registration_digest, receiptId: v.receipt_id } };
52
+ }
53
+ export function linkedRegistrationsPath(linkedWalletId, options = {}) {
54
+ requestUUID(linkedWalletId);
55
+ const declared = record(options, [], ['afterRegistrationId', 'limit', 'signal']);
56
+ const query = new URLSearchParams();
57
+ if (declared.afterRegistrationId !== undefined) {
58
+ requestUUID(declared.afterRegistrationId);
59
+ query.set('after_registration_id', declared.afterRegistrationId);
60
+ }
61
+ readLimit(query, declared.limit);
62
+ return linkedReadPath(linkedWalletId, 'registrations', query);
63
+ }
64
+ export function linkedEventsPath(linkedWalletId, options = {}) {
65
+ requestUUID(linkedWalletId);
66
+ const declared = record(options, [], ['registrationId', 'cursor', 'limit', 'eventTypes', 'signal']);
67
+ const query = new URLSearchParams();
68
+ if (declared.registrationId !== undefined) {
69
+ requestUUID(declared.registrationId);
70
+ query.set('registration_id', declared.registrationId);
71
+ }
72
+ if (declared.cursor !== undefined) {
73
+ if (typeof declared.cursor !== 'string' || declared.cursor.length < 1 || declared.cursor.length > MaxStreamsCursorLength)
74
+ throw new ClientFlowError('invalid_streams_request');
75
+ query.set('cursor', declared.cursor);
76
+ }
77
+ readLimit(query, declared.limit);
78
+ appendEventTypes(query, declared.eventTypes);
79
+ return linkedReadPath(linkedWalletId, 'events', query);
80
+ }
81
+ export function parseLinkedStreamsRegistrationList(payload, linkedWalletId) {
82
+ const root = record(payload, ['items', 'has_more'], ['next_after_registration_id']);
83
+ if (!Array.isArray(root.items) || root.items.length > MaxStreamsPageLimit || typeof root.has_more !== 'boolean' || root.has_more !== Object.hasOwn(root, 'next_after_registration_id'))
84
+ fault();
85
+ const items = root.items.map(item => parseLinkedStreamsRegistration(item, linkedWalletId));
86
+ if (items.some((item, index) => index > 0 && items[index - 1].registrationId >= item.registrationId))
87
+ fault();
88
+ if (root.has_more && (!uuid(root.next_after_registration_id) || items.length === 0 || root.next_after_registration_id !== items.at(-1).registrationId))
89
+ fault();
90
+ const page = { items: Object.freeze(items), hasMore: root.has_more };
91
+ return Object.freeze(root.has_more ? { ...page, nextAfterRegistrationId: root.next_after_registration_id } : page);
92
+ }
93
+ function parseLinkedStreamsRegistration(payload, linkedWalletId) {
94
+ const r = record(payload, ['registration_id', 'binding_source', 'connection_id', 'linked_wallet_id', 'binding_revision', 'source_registration_id', 'network', 'address', 'assets', 'status', 'effective_from', 'revision', 'source_version']);
95
+ if (!uuid(r.registration_id) || r.binding_source !== 'linked_wallet' || !uuid(r.connection_id) || !uuid(r.linked_wallet_id) || r.linked_wallet_id !== linkedWalletId || !safeRevision(r.binding_revision) || !opaqueId(r.source_registration_id) || !network(r.network) || typeof r.address !== 'string' || !/^[A-Za-z0-9_:-]{1,256}$/.test(r.address) || !assets(r.assets) || !['pending', 'active'].includes(r.status) || typeof r.effective_from !== 'string' || new TextEncoder().encode(r.effective_from).length < 1 || new TextEncoder().encode(r.effective_from).length > 256 || !integer(r.revision) || r.revision < 1 || typeof r.source_version !== 'string' || !/^[0-9]+\.[0-9]+$/.test(r.source_version))
96
+ fault();
97
+ return Object.freeze({ registrationId: r.registration_id, bindingSource: 'linked_wallet', connectionId: r.connection_id, linkedWalletId: r.linked_wallet_id, bindingRevision: r.binding_revision, sourceRegistrationId: r.source_registration_id, network: r.network, address: r.address, assets: Object.freeze([...r.assets]), status: r.status, effectiveFrom: r.effective_from, revision: r.revision, sourceVersion: r.source_version });
98
+ }
99
+ function linkedReadPath(id, kind, query) {
100
+ const path = `/v1/linked-wallets/${id}/${kind}`;
101
+ return query.size === 0 ? path : `${path}?${query.toString()}`;
102
+ }
103
+ function readLimit(query, value) {
104
+ if (value === undefined)
105
+ return;
106
+ if (!integer(value) || value < 1 || value > MaxStreamsPageLimit)
107
+ throw new ClientFlowError('invalid_streams_request');
108
+ query.set('limit', String(value));
109
+ }