@xkova/sdk 0.1.0-alpha.5 → 0.1.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -0
- package/dist/abi.d.ts +40 -0
- package/dist/abi.js +25 -0
- package/dist/generated/types.d.ts +386 -3
- package/dist/member-bff.d.ts +53 -4
- package/dist/member-bff.js +63 -6
- package/dist/member-resources.d.ts +17 -1
- package/dist/member-resources.js +7 -6
- package/dist/resources/account-holders.d.ts +107 -1
- package/dist/resources/account-holders.js +36 -7
- package/dist/resources/branding.d.ts +10 -0
- package/dist/resources/branding.js +2 -0
- package/dist/resources/escrows.d.ts +3 -0
- package/dist/resources/payment-requests.d.ts +4 -0
- package/dist/resources/tenant.d.ts +15 -0
- package/dist/resources/tenant.js +6 -1
- package/dist/resources/tokenization.d.ts +15 -0
- package/dist/resources/wallets.d.ts +2 -0
- package/examples/postgres-checkpoint-store.mjs +116 -0
- package/examples/postgres-checkpoints.sql +9 -0
- package/llms.txt +1 -1
- package/package.json +3 -2
package/dist/member-bff.d.ts
CHANGED
|
@@ -34,6 +34,14 @@ export interface XKOVAMemberSessionMaterial extends XKOVAMemberContext {
|
|
|
34
34
|
/** Confidential refresh credential, BFF storage only. */
|
|
35
35
|
readonly refreshToken: string;
|
|
36
36
|
}
|
|
37
|
+
export interface XKOVAExternalMemberEnrollment {
|
|
38
|
+
readonly accountHolderId: string;
|
|
39
|
+
readonly applicationId: string;
|
|
40
|
+
readonly publishableKey: string;
|
|
41
|
+
readonly applicationConfigVersion: number;
|
|
42
|
+
readonly providerNamespace: string;
|
|
43
|
+
readonly created: boolean;
|
|
44
|
+
}
|
|
37
45
|
export interface XKOVABrowserMemberProjection {
|
|
38
46
|
readonly signedIn: true;
|
|
39
47
|
readonly accountHolderId: string;
|
|
@@ -49,8 +57,12 @@ export interface StoredXKOVAMemberSession<TApplicationData = unknown> extends XK
|
|
|
49
57
|
}
|
|
50
58
|
/**
|
|
51
59
|
* `runExclusive` must serialize refresh and logout for the same reference
|
|
52
|
-
* across all BFF instances. Use a
|
|
53
|
-
* a process-local mutex cannot coordinate multiple instances.
|
|
60
|
+
* across all BFF instances. Use a distributed or session-level database lock;
|
|
61
|
+
* a process-local mutex cannot coordinate multiple instances. This lock is not
|
|
62
|
+
* a surrounding rollback transaction: every awaited put/replace/delete must be
|
|
63
|
+
* durable before resolving and must survive a later error from work(). In
|
|
64
|
+
* particular, a pending refresh-attempt checkpoint cannot roll back after the
|
|
65
|
+
* provider/API has acted. replace is exact-existing-only, never an upsert.
|
|
54
66
|
*/
|
|
55
67
|
export interface XKOVAMemberSessionStore<TApplicationData = unknown> {
|
|
56
68
|
put(reference: string, session: StoredXKOVAMemberSession<TApplicationData>): Promise<void>;
|
|
@@ -116,7 +128,7 @@ export type XKOVAMemberAuthorizationTransaction<TApplicationData = unknown> = {
|
|
|
116
128
|
readonly sessionReference: string;
|
|
117
129
|
readonly intent: XKOVAAccountCenterIntent;
|
|
118
130
|
readonly actionAuthorization?: {
|
|
119
|
-
readonly method: 'hosted_passkey';
|
|
131
|
+
readonly method: 'hosted_passkey' | 'xkova_webauthn';
|
|
120
132
|
} | {
|
|
121
133
|
readonly method: 'external_oidc';
|
|
122
134
|
readonly actionToken: string;
|
|
@@ -169,6 +181,11 @@ export type XKOVAMemberAuthorizationTransaction<TApplicationData = unknown> = {
|
|
|
169
181
|
* every BFF instance. `replace` and `delete` must target that exact state. A
|
|
170
182
|
* production store must encrypt the transaction because it can briefly hold a
|
|
171
183
|
* confidential provider proof while XKOVA session exchange is retried.
|
|
184
|
+
* Every awaited write must commit independently of a later work() exception.
|
|
185
|
+
* Never wrap the entire callback in a rollback transaction: that can erase the
|
|
186
|
+
* staged proof after its one-time provider code has already been consumed.
|
|
187
|
+
* On uncertain write acknowledgement, reconcile the same exact record; never
|
|
188
|
+
* re-exchange the provider code or invent a replacement operation identity.
|
|
172
189
|
*/
|
|
173
190
|
export interface XKOVAMemberAuthorizationTransactionStore<TApplicationData = unknown> {
|
|
174
191
|
put(stateHash: string, transaction: XKOVAMemberAuthorizationTransaction<TApplicationData>): Promise<void>;
|
|
@@ -198,6 +215,10 @@ export type XKOVAMemberProofExchangeTransaction<TApplicationData = unknown> = {
|
|
|
198
215
|
* Durable transaction store for a confidential proof exchange that does not
|
|
199
216
|
* use the browser callback helper. Pending records contain a provider proof
|
|
200
217
|
* and customer application data, so production adapters must encrypt them.
|
|
218
|
+
* Serialize the exact reference across replicas without a surrounding rollback
|
|
219
|
+
* transaction. Awaited put/replace/delete checkpoints must remain committed if
|
|
220
|
+
* the later exchange fails. Reconcile an uncertain acknowledgement against the
|
|
221
|
+
* same reference and attempt identity before retrying.
|
|
201
222
|
*/
|
|
202
223
|
export interface XKOVAMemberProofExchangeTransactionStore<TApplicationData = unknown> {
|
|
203
224
|
put(exchangeHash: string, transaction: XKOVAMemberProofExchangeTransaction<TApplicationData>): Promise<void>;
|
|
@@ -239,7 +260,7 @@ export interface XKOVAAccountCenterHandoffCompletion {
|
|
|
239
260
|
readonly applicationReturnUri?: string;
|
|
240
261
|
}
|
|
241
262
|
export type XKOVAMemberActionAuthorizationStart = {
|
|
242
|
-
readonly authorizationMethod: 'hosted_passkey';
|
|
263
|
+
readonly authorizationMethod: 'hosted_passkey' | 'xkova_webauthn';
|
|
243
264
|
readonly authorizationUrl: string;
|
|
244
265
|
readonly state: string;
|
|
245
266
|
readonly expiresAt: string;
|
|
@@ -321,6 +342,18 @@ export declare class XKOVAMemberBffClient {
|
|
|
321
342
|
exchangeProof(proofToken: string, options: {
|
|
322
343
|
readonly attemptId: string;
|
|
323
344
|
}): Promise<XKOVAMemberSessionMaterial>;
|
|
345
|
+
/**
|
|
346
|
+
* BFF-only membership provisioning after validating the provider identity or
|
|
347
|
+
* resolving it from a trusted directory. This creates no login session or
|
|
348
|
+
* wallet and does not substitute for genuine provider-proof verification.
|
|
349
|
+
* Save one idempotencyKey before the first attempt and retain the same subject,
|
|
350
|
+
* selector and observed configuration version during response-loss recovery.
|
|
351
|
+
*/
|
|
352
|
+
enrollExternalMember(input: {
|
|
353
|
+
readonly externalSub: string;
|
|
354
|
+
readonly applicationConfigVersion: number;
|
|
355
|
+
readonly idempotencyKey: string;
|
|
356
|
+
}): Promise<XKOVAExternalMemberEnrollment>;
|
|
324
357
|
refresh(refreshToken: string, options: {
|
|
325
358
|
readonly attemptId: string;
|
|
326
359
|
}): Promise<XKOVAMemberSessionMaterial>;
|
|
@@ -426,6 +459,17 @@ export declare class XKOVAMemberBffClient {
|
|
|
426
459
|
readonly transactionStore: XKOVAMemberAuthorizationTransactionStore<TApplicationData>;
|
|
427
460
|
readonly sessionStore: XKOVAMemberSessionStore<TApplicationData>;
|
|
428
461
|
readonly exchangeProviderCode: (input: XKOVAMemberProviderCodeExchangeInput) => XKOVAMemberProviderCodeExchangeResult | Promise<XKOVAMemberProviderCodeExchangeResult>;
|
|
462
|
+
/**
|
|
463
|
+
* Optional OIDC enrollment, invoked only after the validated provider proof
|
|
464
|
+
* is durably staged. Validate/extract its exact subject on your backend and
|
|
465
|
+
* call enrollExternalMember with this stable idempotency key. Do not perform
|
|
466
|
+
* enrollment RPC inside exchangeProviderCode: failure there can otherwise
|
|
467
|
+
* require reusing an already-consumed provider authorization code.
|
|
468
|
+
*/
|
|
469
|
+
readonly ensureMemberEnrollment?: (input: {
|
|
470
|
+
readonly proofToken: string;
|
|
471
|
+
readonly idempotencyKey: string;
|
|
472
|
+
}) => Promise<XKOVAExternalMemberEnrollment>;
|
|
429
473
|
readonly applicationData: TApplicationData;
|
|
430
474
|
readonly applicationSessionSeconds?: number;
|
|
431
475
|
}): Promise<EstablishedXKOVAMemberSession<TApplicationData>>;
|
|
@@ -455,6 +499,11 @@ export declare class XKOVAMemberBffClient {
|
|
|
455
499
|
resolveResources<TApplicationData>(input: {
|
|
456
500
|
readonly reference: string;
|
|
457
501
|
readonly sessionStore: XKOVAMemberSessionStore<TApplicationData>;
|
|
502
|
+
/**
|
|
503
|
+
* Defaults true. Set false only when the bound resource request executes
|
|
504
|
+
* immediately and therefore performs the same exact-session validation.
|
|
505
|
+
*/
|
|
506
|
+
readonly validateWithXkova?: boolean;
|
|
458
507
|
}): Promise<{
|
|
459
508
|
readonly session: StoredXKOVAMemberSession<TApplicationData>;
|
|
460
509
|
readonly resources: XKOVAMemberResources;
|
package/dist/member-bff.js
CHANGED
|
@@ -79,6 +79,48 @@ export class XKOVAMemberBffClient {
|
|
|
79
79
|
proof_token: proofToken,
|
|
80
80
|
}), 'proof exchange');
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* BFF-only membership provisioning after validating the provider identity or
|
|
84
|
+
* resolving it from a trusted directory. This creates no login session or
|
|
85
|
+
* wallet and does not substitute for genuine provider-proof verification.
|
|
86
|
+
* Save one idempotencyKey before the first attempt and retain the same subject,
|
|
87
|
+
* selector and observed configuration version during response-loss recovery.
|
|
88
|
+
*/
|
|
89
|
+
async enrollExternalMember(input) {
|
|
90
|
+
if (typeof input.externalSub !== 'string' ||
|
|
91
|
+
input.externalSub.length === 0 ||
|
|
92
|
+
input.externalSub.length > 512 ||
|
|
93
|
+
/\u0000/u.test(input.externalSub) ||
|
|
94
|
+
!/\S/u.test(input.externalSub))
|
|
95
|
+
throw new TypeError('externalSub must be an exact nonempty provider subject of at most 512 characters');
|
|
96
|
+
assertPositiveInteger(input.applicationConfigVersion, 'applicationConfigVersion');
|
|
97
|
+
if (!/^[A-Za-z0-9._:-]{1,255}$/u.test(input.idempotencyKey))
|
|
98
|
+
throw new TypeError('idempotencyKey must be a retained opaque operation key of at most 255 characters');
|
|
99
|
+
const value = await this.recoverablePostJson('/member-auth/enrollments', {
|
|
100
|
+
publishable_key: this.options.publishableKey,
|
|
101
|
+
application_config_version: input.applicationConfigVersion,
|
|
102
|
+
external_sub: input.externalSub,
|
|
103
|
+
}, undefined, input.idempotencyKey);
|
|
104
|
+
const result = requireObject(value, 'external member enrollment');
|
|
105
|
+
const applicationId = requireString(result, 'application_id', 'external member enrollment');
|
|
106
|
+
assertUuid(applicationId, 'enrollment application id');
|
|
107
|
+
const publishableKey = requireString(result, 'publishable_key', 'external member enrollment');
|
|
108
|
+
const applicationConfigVersion = requirePositiveInteger(result, 'application_config_version', 'external member enrollment');
|
|
109
|
+
const providerNamespace = requireString(result, 'provider_namespace', 'external member enrollment');
|
|
110
|
+
if (publishableKey !== this.options.publishableKey ||
|
|
111
|
+
applicationConfigVersion !== input.applicationConfigVersion ||
|
|
112
|
+
!providerNamespace.startsWith('oidc:') ||
|
|
113
|
+
typeof result.created !== 'boolean')
|
|
114
|
+
throw new Error('XKOVA external member enrollment returned a different application or configuration');
|
|
115
|
+
return {
|
|
116
|
+
accountHolderId: requireString(result, 'account_holder_id', 'external member enrollment'),
|
|
117
|
+
applicationId,
|
|
118
|
+
publishableKey,
|
|
119
|
+
applicationConfigVersion,
|
|
120
|
+
providerNamespace,
|
|
121
|
+
created: result.created,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
82
124
|
async refresh(refreshToken, options) {
|
|
83
125
|
if (refreshToken.length < 20) {
|
|
84
126
|
throw new TypeError('member refresh token is malformed');
|
|
@@ -536,15 +578,15 @@ export class XKOVAMemberBffClient {
|
|
|
536
578
|
const object = requireObject(value, 'member action authorization start');
|
|
537
579
|
const method = requireString(object, 'authorization_method', 'member action authorization start');
|
|
538
580
|
const { expiresAt, expiresAtMillis } = boundedAuthorizationExpiry(object, provisionalExpiresAt, 'member action authorization start');
|
|
539
|
-
if (method === 'hosted_passkey') {
|
|
581
|
+
if (method === 'hosted_passkey' || method === 'xkova_webauthn') {
|
|
540
582
|
const authorizationUrl = normalizeHostedAuthorizationUrl(requireString(object, 'authorization_url', 'member action authorization start'));
|
|
541
583
|
await input.transactionStore.replace(stateHash, {
|
|
542
584
|
...transaction,
|
|
543
|
-
actionAuthorization: { method
|
|
585
|
+
actionAuthorization: { method },
|
|
544
586
|
expiresAt: expiresAtMillis,
|
|
545
587
|
});
|
|
546
588
|
return {
|
|
547
|
-
authorizationMethod:
|
|
589
|
+
authorizationMethod: method,
|
|
548
590
|
authorizationUrl,
|
|
549
591
|
state,
|
|
550
592
|
expiresAt,
|
|
@@ -606,7 +648,7 @@ export class XKOVAMemberBffClient {
|
|
|
606
648
|
if (initial?.completionMode === 'account_center_result_code' &&
|
|
607
649
|
initial.status === 'pending' &&
|
|
608
650
|
initial.intent === 'action' &&
|
|
609
|
-
initial.actionAuthorization?.method
|
|
651
|
+
['hosted_passkey', 'xkova_webauthn'].includes(initial.actionAuthorization?.method ?? '')) {
|
|
610
652
|
const completion = await this.completeAccountCenterHandoff(input);
|
|
611
653
|
if (!completion.signingExchangeCode || !completion.signingUrl) {
|
|
612
654
|
throw new Error('XKOVA Hosted action completion omitted its signing projection');
|
|
@@ -759,6 +801,10 @@ export class XKOVAMemberBffClient {
|
|
|
759
801
|
if (transaction.completionMode !== 'provider_proof') {
|
|
760
802
|
throw new Error('member authorization transaction is not a provider-proof flow');
|
|
761
803
|
}
|
|
804
|
+
if (input.ensureMemberEnrollment &&
|
|
805
|
+
transaction.status === 'pending' &&
|
|
806
|
+
transaction.proofKind !== 'oidc')
|
|
807
|
+
throw new Error('external member enrollment is available only for an OIDC proof flow');
|
|
762
808
|
if (!sameCallback(callback, new URL(transaction.redirectUri))) {
|
|
763
809
|
throw new Error('member authorization callback does not match its redirect URI');
|
|
764
810
|
}
|
|
@@ -821,6 +867,14 @@ export class XKOVAMemberBffClient {
|
|
|
821
867
|
},
|
|
822
868
|
});
|
|
823
869
|
}
|
|
870
|
+
const enrollment = input.ensureMemberEnrollment
|
|
871
|
+
? await input.ensureMemberEnrollment({
|
|
872
|
+
proofToken,
|
|
873
|
+
idempotencyKey: `enroll:${transaction.exchangeAttemptId}`,
|
|
874
|
+
})
|
|
875
|
+
: undefined;
|
|
876
|
+
if (enrollment && enrollment.publishableKey !== this.options.publishableKey)
|
|
877
|
+
throw new Error('member enrollment does not belong to this application');
|
|
824
878
|
const established = await this.persistSessionWithAttempt({
|
|
825
879
|
proofToken,
|
|
826
880
|
attemptId: transaction.exchangeAttemptId,
|
|
@@ -828,6 +882,7 @@ export class XKOVAMemberBffClient {
|
|
|
828
882
|
sessionStore: input.sessionStore,
|
|
829
883
|
applicationData,
|
|
830
884
|
applicationExpiresAt,
|
|
885
|
+
...(enrollment !== undefined && { expectedAccountHolderId: enrollment.accountHolderId }),
|
|
831
886
|
});
|
|
832
887
|
await input.transactionStore.replace(stateHash, {
|
|
833
888
|
status: 'completed',
|
|
@@ -1014,6 +1069,9 @@ export class XKOVAMemberBffClient {
|
|
|
1014
1069
|
const material = await this.exchangeProof(input.proofToken, {
|
|
1015
1070
|
attemptId: input.attemptId,
|
|
1016
1071
|
});
|
|
1072
|
+
if (input.expectedAccountHolderId !== undefined &&
|
|
1073
|
+
material.accountHolderId !== input.expectedAccountHolderId)
|
|
1074
|
+
throw new Error('XKOVA proof exchange returned a different member from the enrolled identity');
|
|
1017
1075
|
return this.persistSessionMaterial({
|
|
1018
1076
|
material,
|
|
1019
1077
|
reference: input.reference,
|
|
@@ -1064,8 +1122,7 @@ export class XKOVAMemberBffClient {
|
|
|
1064
1122
|
...(idempotencyKey !== undefined && { idempotencyKey }),
|
|
1065
1123
|
});
|
|
1066
1124
|
}
|
|
1067
|
-
async recoverablePostJson(path, body, sessionId) {
|
|
1068
|
-
const idempotencyKey = generateIdempotencyKey();
|
|
1125
|
+
async recoverablePostJson(path, body, sessionId, idempotencyKey = generateIdempotencyKey()) {
|
|
1069
1126
|
try {
|
|
1070
1127
|
return await this.postJson(path, body, sessionId, idempotencyKey);
|
|
1071
1128
|
}
|
|
@@ -52,6 +52,9 @@ export declare class XKOVAMemberResources {
|
|
|
52
52
|
credit: {
|
|
53
53
|
amount: string;
|
|
54
54
|
token_deployment_id: string;
|
|
55
|
+
token_address: string;
|
|
56
|
+
token_symbol: string;
|
|
57
|
+
chain: import("./generated/types.js").components["schemas"]["ChainDescriptor"];
|
|
55
58
|
status: "pending" | "submitted" | "settled" | "requires_review";
|
|
56
59
|
transaction_hash: string | null;
|
|
57
60
|
} | null;
|
|
@@ -64,12 +67,15 @@ export declare class XKOVAMemberResources {
|
|
|
64
67
|
credit: {
|
|
65
68
|
amount: string;
|
|
66
69
|
token_deployment_id: string;
|
|
70
|
+
token_address: string;
|
|
71
|
+
token_symbol: string;
|
|
72
|
+
chain: import("./generated/types.js").components["schemas"]["ChainDescriptor"];
|
|
67
73
|
status: "pending" | "submitted" | "settled" | "requires_review";
|
|
68
74
|
transaction_hash: string | null;
|
|
69
75
|
} | null;
|
|
70
76
|
}>;
|
|
71
77
|
actionReadiness: (control?: XKOVARequestControl) => Promise<{
|
|
72
|
-
authorization_method: "hosted_passkey" | "external_oidc";
|
|
78
|
+
authorization_method: "hosted_passkey" | "external_oidc" | "xkova_webauthn";
|
|
73
79
|
setup_status: "passkey_required" | "recovery_acknowledgement_required" | "recovery_required" | "ready" | "externally_managed";
|
|
74
80
|
action_authorization_enabled: boolean;
|
|
75
81
|
requires_fresh_proof: true;
|
|
@@ -103,10 +109,20 @@ export declare class XKOVAMemberResources {
|
|
|
103
109
|
}>;
|
|
104
110
|
balance: (control?: XKOVARequestControl) => Promise<{
|
|
105
111
|
balances: import("./generated/types.js").components["schemas"]["AccountHolderSelfBalanceEntry"][];
|
|
112
|
+
data_freshness: import("./generated/types.js").components["schemas"]["DataFreshness"][];
|
|
106
113
|
}>;
|
|
107
114
|
wallets: (control?: XKOVARequestControl) => Promise<{
|
|
108
115
|
data: import("./generated/types.js").components["schemas"]["Wallet"][];
|
|
109
116
|
}>;
|
|
117
|
+
walletAuth: (control?: XKOVARequestControl) => Promise<{
|
|
118
|
+
strategy: "auth_endpoint";
|
|
119
|
+
payload: string;
|
|
120
|
+
client_id: string;
|
|
121
|
+
project_id: string;
|
|
122
|
+
external_user_id: string;
|
|
123
|
+
expires_at: string;
|
|
124
|
+
expected_address?: string;
|
|
125
|
+
} & Record<string, never>>;
|
|
110
126
|
walletReadiness: (control?: XKOVARequestControl) => Promise<{
|
|
111
127
|
status: "not_registered" | "provisioning" | "ready";
|
|
112
128
|
wallets: import("./generated/types.js").components["schemas"]["Wallet"][];
|
package/dist/member-resources.js
CHANGED
|
@@ -31,7 +31,7 @@ export class XKOVAMemberResources {
|
|
|
31
31
|
return this.client.wallets;
|
|
32
32
|
}
|
|
33
33
|
self = {
|
|
34
|
-
profile: () => this.client.accountHolders.
|
|
34
|
+
profile: () => this.client.accountHolders.selfProfile(this.context),
|
|
35
35
|
productEnrollment: (product, control) => this.client.accountHolders.selfProductEnrollment(this.context, product, control),
|
|
36
36
|
acceptProductEnrollment: (product, body, opts) => this.client.accountHolders.acceptSelfProductEnrollment(this.context, product, body, opts),
|
|
37
37
|
actionReadiness: (control) => this.client.accountHolders.selfActionReadiness({ ...this.context, accountHolderId: this.accountHolderId }, control),
|
|
@@ -39,6 +39,7 @@ export class XKOVAMemberResources {
|
|
|
39
39
|
verification: (control) => this.client.accountHolders.selfVerification(this.context, control),
|
|
40
40
|
balance: (control) => this.client.accountHolders.selfBalance(this.context, control),
|
|
41
41
|
wallets: (control) => this.client.accountHolders.listSelfWallets(this.context, control),
|
|
42
|
+
walletAuth: (control) => this.client.accountHolders.selfWalletAuth(this.context, control),
|
|
42
43
|
walletReadiness: (control) => this.client.accountHolders.selfWalletReadiness(this.context, control),
|
|
43
44
|
ensureWalletProvisioning: (opts) => this.client.accountHolders.ensureSelfWalletProvisioning(this.context, opts),
|
|
44
45
|
bankBalance: (control) => this.client.accountHolders.selfBankBalance(this.context, control),
|
|
@@ -50,10 +51,10 @@ export class XKOVAMemberResources {
|
|
|
50
51
|
requestDataExport: (opts) => this.client.accountHolders.requestSelfDataExport(this.context, opts),
|
|
51
52
|
dataExport: (id, control) => this.client.accountHolders.selfDataExport(this.context, id, control),
|
|
52
53
|
transactions: (query, control) => this.client.accountHolders.listSelfTransactions(this.context, query, control),
|
|
53
|
-
avatar: () => this.client.accountHolders.
|
|
54
|
-
uploadAvatar: (image, control) => this.client.accountHolders.
|
|
55
|
-
deleteAvatar: (opts) => this.client.accountHolders.
|
|
56
|
-
confirmAvatar: (body, opts) => this.client.accountHolders.
|
|
57
|
-
presignAvatarUpload: (body, opts) => this.client.accountHolders.
|
|
54
|
+
avatar: () => this.client.accountHolders.selfAvatarUrl(this.context),
|
|
55
|
+
uploadAvatar: (image, control) => this.client.accountHolders.uploadSelfAvatar(this.context, image, control),
|
|
56
|
+
deleteAvatar: (opts) => this.client.accountHolders.deleteSelfAvatar(this.context, opts),
|
|
57
|
+
confirmAvatar: (body, opts) => this.client.accountHolders.confirmSelfAvatar(this.context, body, opts),
|
|
58
|
+
presignAvatarUpload: (body, opts) => this.client.accountHolders.presignSelfAvatarUpload(this.context, body, opts),
|
|
58
59
|
};
|
|
59
60
|
}
|
|
@@ -100,6 +100,32 @@ export declare class AccountHoldersResource extends Resource {
|
|
|
100
100
|
next_cursor?: string | null;
|
|
101
101
|
has_more: boolean;
|
|
102
102
|
}>;
|
|
103
|
+
selfProfile(context: XKOVAMemberSessionContext, control?: XKOVARequestControl): Promise<{
|
|
104
|
+
id: import("../generated/types.js").components["schemas"]["AccountHolderId"];
|
|
105
|
+
tenant_id?: string;
|
|
106
|
+
state: "active" | "held" | "closed" | "closed_uncooperative" | "provisioning_failed";
|
|
107
|
+
state_reason?: string | null;
|
|
108
|
+
state_changed_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
|
|
109
|
+
tier: "tier_1" | "tier_2";
|
|
110
|
+
tier_2_uplift_status?: "not_requested" | "pending" | "passed" | "failed";
|
|
111
|
+
first_use_completed: boolean;
|
|
112
|
+
cip_payload?: {
|
|
113
|
+
name_hash?: string;
|
|
114
|
+
dob_hash?: string;
|
|
115
|
+
id_hash?: string;
|
|
116
|
+
address_hash?: string;
|
|
117
|
+
} | null;
|
|
118
|
+
cip_accepted_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
|
|
119
|
+
merchant_role_state?: "none" | "pending_review" | "approved" | "rejected" | "revoked";
|
|
120
|
+
auto_save_contacts?: boolean;
|
|
121
|
+
created_at: import("../generated/types.js").components["schemas"]["TimestampUtc"];
|
|
122
|
+
updated_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"];
|
|
123
|
+
email?: string | null;
|
|
124
|
+
first_name?: string | null;
|
|
125
|
+
last_name?: string | null;
|
|
126
|
+
phone?: string | null;
|
|
127
|
+
avatar_url?: string | null;
|
|
128
|
+
}>;
|
|
103
129
|
selfVerification(context: XKOVAMemberSessionContext, control?: XKOVARequestControl): Promise<{
|
|
104
130
|
required: boolean;
|
|
105
131
|
status: string;
|
|
@@ -126,6 +152,9 @@ export declare class AccountHoldersResource extends Resource {
|
|
|
126
152
|
credit: {
|
|
127
153
|
amount: string;
|
|
128
154
|
token_deployment_id: string;
|
|
155
|
+
token_address: string;
|
|
156
|
+
token_symbol: string;
|
|
157
|
+
chain: import("../generated/types.js").components["schemas"]["ChainDescriptor"];
|
|
129
158
|
status: "pending" | "submitted" | "settled" | "requires_review";
|
|
130
159
|
transaction_hash: string | null;
|
|
131
160
|
} | null;
|
|
@@ -138,16 +167,29 @@ export declare class AccountHoldersResource extends Resource {
|
|
|
138
167
|
credit: {
|
|
139
168
|
amount: string;
|
|
140
169
|
token_deployment_id: string;
|
|
170
|
+
token_address: string;
|
|
171
|
+
token_symbol: string;
|
|
172
|
+
chain: import("../generated/types.js").components["schemas"]["ChainDescriptor"];
|
|
141
173
|
status: "pending" | "submitted" | "settled" | "requires_review";
|
|
142
174
|
transaction_hash: string | null;
|
|
143
175
|
} | null;
|
|
144
176
|
}>;
|
|
145
177
|
selfBalance(context: XKOVAMemberSessionContext, control?: XKOVARequestControl): Promise<{
|
|
146
178
|
balances: import("../generated/types.js").components["schemas"]["AccountHolderSelfBalanceEntry"][];
|
|
179
|
+
data_freshness: import("../generated/types.js").components["schemas"]["DataFreshness"][];
|
|
147
180
|
}>;
|
|
148
181
|
listSelfWallets(context: XKOVAMemberSessionContext, control?: XKOVARequestControl): Promise<{
|
|
149
182
|
data: import("../generated/types.js").components["schemas"]["Wallet"][];
|
|
150
183
|
}>;
|
|
184
|
+
selfWalletAuth(context: XKOVAMemberSessionContext, control?: XKOVARequestControl): Promise<{
|
|
185
|
+
strategy: "auth_endpoint";
|
|
186
|
+
payload: string;
|
|
187
|
+
client_id: string;
|
|
188
|
+
project_id: string;
|
|
189
|
+
external_user_id: string;
|
|
190
|
+
expires_at: string;
|
|
191
|
+
expected_address?: string;
|
|
192
|
+
} & Record<string, never>>;
|
|
151
193
|
selfWalletReadiness(context: XKOVAMemberSessionContext, control?: XKOVARequestControl): Promise<{
|
|
152
194
|
status: "not_registered" | "provisioning" | "ready";
|
|
153
195
|
wallets: import("../generated/types.js").components["schemas"]["Wallet"][];
|
|
@@ -173,7 +215,7 @@ export declare class AccountHoldersResource extends Resource {
|
|
|
173
215
|
selfActionReadiness(context: XKOVAMemberSessionContext & {
|
|
174
216
|
accountHolderId: string;
|
|
175
217
|
}, control?: XKOVARequestControl): Promise<{
|
|
176
|
-
authorization_method: "hosted_passkey" | "external_oidc";
|
|
218
|
+
authorization_method: "hosted_passkey" | "external_oidc" | "xkova_webauthn";
|
|
177
219
|
setup_status: "passkey_required" | "recovery_acknowledgement_required" | "recovery_required" | "ready" | "externally_managed";
|
|
178
220
|
action_authorization_enabled: boolean;
|
|
179
221
|
requires_fresh_proof: true;
|
|
@@ -253,6 +295,70 @@ export declare class AccountHoldersResource extends Resource {
|
|
|
253
295
|
uploadAvatar(id: string, image: Blob, control?: XKOVARequestControl): Promise<{
|
|
254
296
|
url: string | null;
|
|
255
297
|
}>;
|
|
298
|
+
uploadSelfAvatar(context: XKOVAMemberSessionContext, image: Blob, control?: XKOVARequestControl): Promise<{
|
|
299
|
+
url: string | null;
|
|
300
|
+
}>;
|
|
301
|
+
private uploadAvatarImage;
|
|
302
|
+
selfAvatarUrl(context: XKOVAMemberSessionContext): Promise<{
|
|
303
|
+
url: string | null;
|
|
304
|
+
}>;
|
|
305
|
+
deleteSelfAvatar(context: XKOVAMemberSessionContext, opts?: XKOVAMutationOptions): Promise<{
|
|
306
|
+
id: import("../generated/types.js").components["schemas"]["AccountHolderId"];
|
|
307
|
+
tenant_id?: string;
|
|
308
|
+
state: "active" | "held" | "closed" | "closed_uncooperative" | "provisioning_failed";
|
|
309
|
+
state_reason?: string | null;
|
|
310
|
+
state_changed_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
|
|
311
|
+
tier: "tier_1" | "tier_2";
|
|
312
|
+
tier_2_uplift_status?: "not_requested" | "pending" | "passed" | "failed";
|
|
313
|
+
first_use_completed: boolean;
|
|
314
|
+
cip_payload?: {
|
|
315
|
+
name_hash?: string;
|
|
316
|
+
dob_hash?: string;
|
|
317
|
+
id_hash?: string;
|
|
318
|
+
address_hash?: string;
|
|
319
|
+
} | null;
|
|
320
|
+
cip_accepted_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
|
|
321
|
+
merchant_role_state?: "none" | "pending_review" | "approved" | "rejected" | "revoked";
|
|
322
|
+
auto_save_contacts?: boolean;
|
|
323
|
+
created_at: import("../generated/types.js").components["schemas"]["TimestampUtc"];
|
|
324
|
+
updated_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"];
|
|
325
|
+
email?: string | null;
|
|
326
|
+
first_name?: string | null;
|
|
327
|
+
last_name?: string | null;
|
|
328
|
+
phone?: string | null;
|
|
329
|
+
avatar_url?: string | null;
|
|
330
|
+
}>;
|
|
331
|
+
confirmSelfAvatar(context: XKOVAMemberSessionContext, body: ReqBody<'confirmAccountHolderSelfAvatar'>, opts?: XKOVAMutationOptions): Promise<{
|
|
332
|
+
id: import("../generated/types.js").components["schemas"]["AccountHolderId"];
|
|
333
|
+
tenant_id?: string;
|
|
334
|
+
state: "active" | "held" | "closed" | "closed_uncooperative" | "provisioning_failed";
|
|
335
|
+
state_reason?: string | null;
|
|
336
|
+
state_changed_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
|
|
337
|
+
tier: "tier_1" | "tier_2";
|
|
338
|
+
tier_2_uplift_status?: "not_requested" | "pending" | "passed" | "failed";
|
|
339
|
+
first_use_completed: boolean;
|
|
340
|
+
cip_payload?: {
|
|
341
|
+
name_hash?: string;
|
|
342
|
+
dob_hash?: string;
|
|
343
|
+
id_hash?: string;
|
|
344
|
+
address_hash?: string;
|
|
345
|
+
} | null;
|
|
346
|
+
cip_accepted_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
|
|
347
|
+
merchant_role_state?: "none" | "pending_review" | "approved" | "rejected" | "revoked";
|
|
348
|
+
auto_save_contacts?: boolean;
|
|
349
|
+
created_at: import("../generated/types.js").components["schemas"]["TimestampUtc"];
|
|
350
|
+
updated_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"];
|
|
351
|
+
email?: string | null;
|
|
352
|
+
first_name?: string | null;
|
|
353
|
+
last_name?: string | null;
|
|
354
|
+
phone?: string | null;
|
|
355
|
+
avatar_url?: string | null;
|
|
356
|
+
}>;
|
|
357
|
+
presignSelfAvatarUpload(context: XKOVAMemberSessionContext, body: ReqBody<'presignAccountHolderSelfAvatarUpload'>, opts?: XKOVAMutationOptions): Promise<{
|
|
358
|
+
upload_url: string;
|
|
359
|
+
object_key: string;
|
|
360
|
+
expires_at: import("../generated/types.js").components["schemas"]["TimestampUtc"];
|
|
361
|
+
}>;
|
|
256
362
|
avatarUrl(id: string): Promise<{
|
|
257
363
|
url: string | null;
|
|
258
364
|
}>;
|
|
@@ -28,6 +28,9 @@ export class AccountHoldersResource extends Resource {
|
|
|
28
28
|
history(id, query = {}) {
|
|
29
29
|
return this.getJson(`/account-holders/${this.enc(id)}/history`, query);
|
|
30
30
|
}
|
|
31
|
+
selfProfile(context, control) {
|
|
32
|
+
return this.getJson('/account-holders/self', undefined, control, memberSessionHeaders(context));
|
|
33
|
+
}
|
|
31
34
|
selfVerification(context, control) {
|
|
32
35
|
return this.getJson('/account-holders/self/verification', undefined, control, memberSessionHeaders(context));
|
|
33
36
|
}
|
|
@@ -43,6 +46,9 @@ export class AccountHoldersResource extends Resource {
|
|
|
43
46
|
listSelfWallets(context, control) {
|
|
44
47
|
return this.getJson('/account-holders/self/wallets', undefined, control, memberSessionHeaders(context));
|
|
45
48
|
}
|
|
49
|
+
selfWalletAuth(context, control) {
|
|
50
|
+
return this.postJson('/account-holders/self/wallet-auth', undefined, control, undefined, memberSessionHeaders(context));
|
|
51
|
+
}
|
|
46
52
|
selfWalletReadiness(context, control) {
|
|
47
53
|
return this.getJson('/account-holders/self/wallet-readiness', undefined, control, memberSessionHeaders(context));
|
|
48
54
|
}
|
|
@@ -64,7 +70,7 @@ export class AccountHoldersResource extends Resource {
|
|
|
64
70
|
}
|
|
65
71
|
const readiness = status.action_readiness;
|
|
66
72
|
if (!readiness ||
|
|
67
|
-
!['hosted_passkey', 'external_oidc'].includes(readiness.authorization_method) ||
|
|
73
|
+
!['hosted_passkey', 'external_oidc', 'xkova_webauthn'].includes(readiness.authorization_method) ||
|
|
68
74
|
![
|
|
69
75
|
'passkey_required',
|
|
70
76
|
'recovery_acknowledgement_required',
|
|
@@ -109,15 +115,26 @@ export class AccountHoldersResource extends Resource {
|
|
|
109
115
|
* member context and cookies never follow the presigned URL or a redirect.
|
|
110
116
|
*/
|
|
111
117
|
async uploadAvatar(id, image, control = {}) {
|
|
118
|
+
return this.uploadAvatarImage(image, control, {
|
|
119
|
+
presign: (body) => this.presignAvatarUpload(id, body, control),
|
|
120
|
+
confirm: (object_key) => this.confirmAvatar(id, { object_key }, control),
|
|
121
|
+
read: () => this.avatarUrl(id),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
async uploadSelfAvatar(context, image, control = {}) {
|
|
125
|
+
return this.uploadAvatarImage(image, control, {
|
|
126
|
+
presign: (body) => this.presignSelfAvatarUpload(context, body, control),
|
|
127
|
+
confirm: (object_key) => this.confirmSelfAvatar(context, { object_key }, control),
|
|
128
|
+
read: () => this.selfAvatarUrl(context),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
async uploadAvatarImage(image, control, operations) {
|
|
112
132
|
if (!['image/png', 'image/jpeg', 'image/webp', 'image/gif'].includes(image.type) ||
|
|
113
133
|
image.size === 0 ||
|
|
114
134
|
image.size > 5 * 1024 * 1024) {
|
|
115
135
|
throw new TypeError('Avatar must be a PNG, JPEG, WebP, or GIF image of at most 5 MiB');
|
|
116
136
|
}
|
|
117
|
-
const upload = await
|
|
118
|
-
content_type: image.type,
|
|
119
|
-
content_length: image.size,
|
|
120
|
-
}, control);
|
|
137
|
+
const upload = await operations.presign({ content_type: image.type, content_length: image.size });
|
|
121
138
|
const target = new URL(upload.upload_url);
|
|
122
139
|
if (target.username ||
|
|
123
140
|
target.password ||
|
|
@@ -151,8 +168,20 @@ export class AccountHoldersResource extends Resource {
|
|
|
151
168
|
}
|
|
152
169
|
if (!response.ok)
|
|
153
170
|
throw new XKOVAUploadError('Object storage rejected the avatar upload', response.status);
|
|
154
|
-
await
|
|
155
|
-
return
|
|
171
|
+
await operations.confirm(upload.object_key);
|
|
172
|
+
return operations.read();
|
|
173
|
+
}
|
|
174
|
+
selfAvatarUrl(context) {
|
|
175
|
+
return this.getJson('/account-holders/self/avatar', undefined, {}, memberSessionHeaders(context));
|
|
176
|
+
}
|
|
177
|
+
deleteSelfAvatar(context, opts) {
|
|
178
|
+
return this.deleteJson('/account-holders/self/avatar', opts, undefined, memberSessionHeaders(context));
|
|
179
|
+
}
|
|
180
|
+
confirmSelfAvatar(context, body, opts) {
|
|
181
|
+
return this.postJson('/account-holders/self/avatar/confirm', body, opts, undefined, memberSessionHeaders(context));
|
|
182
|
+
}
|
|
183
|
+
presignSelfAvatarUpload(context, body, opts) {
|
|
184
|
+
return this.postJson('/account-holders/self/avatar/upload-url', body, opts, undefined, memberSessionHeaders(context));
|
|
156
185
|
}
|
|
157
186
|
avatarUrl(id) {
|
|
158
187
|
return this.getJson(`/account-holders/${this.enc(id)}/avatar`);
|
|
@@ -2,6 +2,8 @@ import { Resource, type ReqBody, type Query } from './base.js';
|
|
|
2
2
|
import type { XKOVAMutationOptions } from '../client.js';
|
|
3
3
|
export declare class BrandingResource extends Resource {
|
|
4
4
|
get(): Promise<{
|
|
5
|
+
logo_upload_limits: import("../generated/types.js").components["schemas"]["LogoUploadLimits"];
|
|
6
|
+
has_logo_override: boolean;
|
|
5
7
|
resolved: {
|
|
6
8
|
brand_name: string;
|
|
7
9
|
legal_name: string;
|
|
@@ -28,6 +30,8 @@ export declare class BrandingResource extends Resource {
|
|
|
28
30
|
theme: import("../generated/types.js").components["schemas"]["TenantBrandingTheme"];
|
|
29
31
|
}>;
|
|
30
32
|
upsert(body: ReqBody<'upsertBranding'>, opts?: XKOVAMutationOptions): Promise<{
|
|
33
|
+
logo_upload_limits: import("../generated/types.js").components["schemas"]["LogoUploadLimits"];
|
|
34
|
+
has_logo_override: boolean;
|
|
31
35
|
resolved: {
|
|
32
36
|
brand_name: string;
|
|
33
37
|
legal_name: string;
|
|
@@ -94,12 +98,16 @@ export declare class BrandingResource extends Resource {
|
|
|
94
98
|
last_synced_at: string;
|
|
95
99
|
created_at: string;
|
|
96
100
|
}>;
|
|
101
|
+
/** Upload bytes to the returned URL, then confirm its opaque key as the same tenant. */
|
|
97
102
|
logoUploadUrl(body: ReqBody<'brandingLogoUploadUrl'>, opts?: XKOVAMutationOptions): Promise<{
|
|
98
103
|
upload_url: string;
|
|
99
104
|
object_key: string;
|
|
100
105
|
expires_at: string;
|
|
101
106
|
}>;
|
|
107
|
+
/** Publish a newly issued upload. Saved logo URLs or keys are not upload keys. */
|
|
102
108
|
confirmLogo(body: ReqBody<'confirmBrandingLogo'>, opts?: XKOVAMutationOptions): Promise<{
|
|
109
|
+
logo_upload_limits: import("../generated/types.js").components["schemas"]["LogoUploadLimits"];
|
|
110
|
+
has_logo_override: boolean;
|
|
103
111
|
resolved: {
|
|
104
112
|
brand_name: string;
|
|
105
113
|
legal_name: string;
|
|
@@ -126,6 +134,8 @@ export declare class BrandingResource extends Resource {
|
|
|
126
134
|
theme: import("../generated/types.js").components["schemas"]["TenantBrandingTheme"];
|
|
127
135
|
}>;
|
|
128
136
|
deleteLogo(opts?: XKOVAMutationOptions): Promise<{
|
|
137
|
+
logo_upload_limits: import("../generated/types.js").components["schemas"]["LogoUploadLimits"];
|
|
138
|
+
has_logo_override: boolean;
|
|
129
139
|
resolved: {
|
|
130
140
|
brand_name: string;
|
|
131
141
|
legal_name: string;
|
|
@@ -22,9 +22,11 @@ export class BrandingResource extends Resource {
|
|
|
22
22
|
verifyEmailDomain(id, opts) {
|
|
23
23
|
return this.postJson(`/branding/email-domains/${this.enc(id)}/verify`, undefined, opts);
|
|
24
24
|
}
|
|
25
|
+
/** Upload bytes to the returned URL, then confirm its opaque key as the same tenant. */
|
|
25
26
|
logoUploadUrl(body, opts) {
|
|
26
27
|
return this.postJson('/branding/logo/upload-url', body, opts);
|
|
27
28
|
}
|
|
29
|
+
/** Publish a newly issued upload. Saved logo URLs or keys are not upload keys. */
|
|
28
30
|
confirmLogo(body, opts) {
|
|
29
31
|
return this.postJson('/branding/logo', body, opts);
|
|
30
32
|
}
|
|
@@ -7,6 +7,7 @@ export declare class EscrowsResource extends Resource {
|
|
|
7
7
|
has_more: boolean;
|
|
8
8
|
}>;
|
|
9
9
|
create(body: ReqBody<'createEscrow'>, opts?: XKOVAMutationOptions): Promise<{
|
|
10
|
+
notification_status?: "queued" | "sent" | "failed" | "cancelled";
|
|
10
11
|
escrow: import("../generated/types.js").components["schemas"]["Escrow"];
|
|
11
12
|
claim_token: string;
|
|
12
13
|
claim_url: string;
|
|
@@ -15,6 +16,7 @@ export declare class EscrowsResource extends Resource {
|
|
|
15
16
|
signing_expires_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
|
|
16
17
|
}>;
|
|
17
18
|
retrieve(id: string, query?: Query<'getEscrow'>): Promise<{
|
|
19
|
+
notification_status?: "queued" | "sent" | "failed" | "cancelled";
|
|
18
20
|
id: import("../generated/types.js").components["schemas"]["EscrowId"];
|
|
19
21
|
chain: import("../generated/types.js").components["schemas"]["ChainDescriptor"];
|
|
20
22
|
fingerprint: string;
|
|
@@ -28,6 +30,7 @@ export declare class EscrowsResource extends Resource {
|
|
|
28
30
|
on_chain_payment_id?: string | null;
|
|
29
31
|
create_tx_hash?: string | null;
|
|
30
32
|
release_tx_hash?: string | null;
|
|
33
|
+
data_freshness?: import("../generated/types.js").components["schemas"]["DataFreshness"][];
|
|
31
34
|
fee_accruals?: import("../generated/types.js").components["schemas"]["EscrowFeeAccrual"][];
|
|
32
35
|
net_amount?: import("../generated/types.js").components["schemas"]["Money"] | null;
|
|
33
36
|
claimed_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
|