@xkova/sdk 0.1.0-alpha.1
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/LICENSE +202 -0
- package/README.md +453 -0
- package/dist/abi.d.ts +10288 -0
- package/dist/abi.js +6645 -0
- package/dist/client.d.ts +80 -0
- package/dist/client.js +251 -0
- package/dist/cursor.d.ts +21 -0
- package/dist/cursor.js +28 -0
- package/dist/errors.d.ts +31 -0
- package/dist/errors.js +63 -0
- package/dist/generated/types.d.ts +13371 -0
- package/dist/generated/types.js +3 -0
- package/dist/idempotency.d.ts +25 -0
- package/dist/idempotency.js +46 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +8 -0
- package/dist/member-action-intents.d.ts +72 -0
- package/dist/member-action-intents.js +181 -0
- package/dist/member-bff.d.ts +535 -0
- package/dist/member-bff.js +1594 -0
- package/dist/member-resources.d.ts +203 -0
- package/dist/member-resources.js +56 -0
- package/dist/request-signing.d.ts +23 -0
- package/dist/request-signing.js +112 -0
- package/dist/resources/account-holders.d.ts +295 -0
- package/dist/resources/account-holders.js +166 -0
- package/dist/resources/base.d.ts +47 -0
- package/dist/resources/base.js +25 -0
- package/dist/resources/branding.d.ts +154 -0
- package/dist/resources/branding.js +34 -0
- package/dist/resources/chains.d.ts +6 -0
- package/dist/resources/chains.js +6 -0
- package/dist/resources/contacts.d.ts +55 -0
- package/dist/resources/contacts.js +22 -0
- package/dist/resources/escrows.d.ts +45 -0
- package/dist/resources/escrows.js +18 -0
- package/dist/resources/fee-schedules.d.ts +40 -0
- package/dist/resources/fee-schedules.js +15 -0
- package/dist/resources/index.d.ts +21 -0
- package/dist/resources/index.js +21 -0
- package/dist/resources/integration-gateways.d.ts +114 -0
- package/dist/resources/integration-gateways.js +27 -0
- package/dist/resources/onboarding.d.ts +18 -0
- package/dist/resources/onboarding.js +9 -0
- package/dist/resources/payment-requests.d.ts +115 -0
- package/dist/resources/payment-requests.js +27 -0
- package/dist/resources/payments.d.ts +182 -0
- package/dist/resources/payments.js +33 -0
- package/dist/resources/ramps.d.ts +163 -0
- package/dist/resources/ramps.js +44 -0
- package/dist/resources/rwa.d.ts +397 -0
- package/dist/resources/rwa.js +150 -0
- package/dist/resources/tenant.d.ts +136 -0
- package/dist/resources/tenant.js +42 -0
- package/dist/resources/tokenization.d.ts +394 -0
- package/dist/resources/tokenization.js +126 -0
- package/dist/resources/transactions.d.ts +8 -0
- package/dist/resources/transactions.js +6 -0
- package/dist/resources/wallets.d.ts +56 -0
- package/dist/resources/wallets.js +21 -0
- package/dist/resources/webhooks.d.ts +115 -0
- package/dist/resources/webhooks.js +43 -0
- package/dist/resources/workflows.d.ts +44 -0
- package/dist/resources/workflows.js +70 -0
- package/dist/resources/workspace-tokens.d.ts +6 -0
- package/dist/resources/workspace-tokens.js +6 -0
- package/dist/resources/workspaces.d.ts +25 -0
- package/dist/resources/workspaces.js +12 -0
- package/dist/retry.d.ts +21 -0
- package/dist/retry.js +44 -0
- package/dist/rwa-verify.d.ts +50 -0
- package/dist/rwa-verify.js +159 -0
- package/dist/webhook.d.ts +24 -0
- package/dist/webhook.js +56 -0
- package/llms.txt +84 -0
- package/package.json +62 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type XKOVARequestSigner } from './request-signing.js';
|
|
2
|
+
import { AccountHoldersResource } from './resources/account-holders.js';
|
|
3
|
+
import { BrandingResource } from './resources/branding.js';
|
|
4
|
+
import { ChainsResource } from './resources/chains.js';
|
|
5
|
+
import { ContactsResource } from './resources/contacts.js';
|
|
6
|
+
import { EscrowsResource } from './resources/escrows.js';
|
|
7
|
+
import { FeeSchedulesResource } from './resources/fee-schedules.js';
|
|
8
|
+
import { IntegrationGatewaysResource } from './resources/integration-gateways.js';
|
|
9
|
+
import { OnboardingResource } from './resources/onboarding.js';
|
|
10
|
+
import { PaymentRequestsResource } from './resources/payment-requests.js';
|
|
11
|
+
import { PaymentsResource } from './resources/payments.js';
|
|
12
|
+
import { RampsResource } from './resources/ramps.js';
|
|
13
|
+
import { RwaResource } from './resources/rwa.js';
|
|
14
|
+
import { TenantResource } from './resources/tenant.js';
|
|
15
|
+
import { TokenizationResource } from './resources/tokenization.js';
|
|
16
|
+
import { TransactionsResource } from './resources/transactions.js';
|
|
17
|
+
import { WalletsResource } from './resources/wallets.js';
|
|
18
|
+
import { WebhooksResource } from './resources/webhooks.js';
|
|
19
|
+
import { WorkflowClient } from './resources/workflows.js';
|
|
20
|
+
import { WorkspaceTokensResource } from './resources/workspace-tokens.js';
|
|
21
|
+
import { WorkspacesResource } from './resources/workspaces.js';
|
|
22
|
+
type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
23
|
+
export type XKOVAHttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
24
|
+
export interface XKOVAClientOptions {
|
|
25
|
+
/** API origin or versioned base URL, such as https://sandbox-api.xkova.com. */
|
|
26
|
+
baseUrl: string;
|
|
27
|
+
/** Tenant API key. Keep this credential in a trusted server process. */
|
|
28
|
+
apiKey: string;
|
|
29
|
+
/** Default request deadline in milliseconds. Omit for no SDK deadline. */
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
/** Defaults to global fetch. Override for tests or custom transport. */
|
|
32
|
+
fetchImpl?: FetchLike;
|
|
33
|
+
/**
|
|
34
|
+
* Customer-owned asymmetric signer for mutation requests. Required for
|
|
35
|
+
* production workspace keys and optional for sandbox workspace keys.
|
|
36
|
+
*/
|
|
37
|
+
requestSigner?: XKOVARequestSigner;
|
|
38
|
+
}
|
|
39
|
+
export interface XKOVARequestControl {
|
|
40
|
+
/** Cancels this request or workflow poll. */
|
|
41
|
+
signal?: AbortSignal;
|
|
42
|
+
}
|
|
43
|
+
export interface XKOVAMutationOptions extends XKOVARequestControl {
|
|
44
|
+
/** Reuse this key when retrying the same logical write. */
|
|
45
|
+
idempotencyKey?: string;
|
|
46
|
+
}
|
|
47
|
+
export declare class XKOVAClient {
|
|
48
|
+
private readonly config;
|
|
49
|
+
private readonly memberContext?;
|
|
50
|
+
private readonly apiBaseUrl;
|
|
51
|
+
private readonly fetchImpl;
|
|
52
|
+
private constructor();
|
|
53
|
+
private readonly namespaces;
|
|
54
|
+
get accountHolders(): AccountHoldersResource;
|
|
55
|
+
get branding(): BrandingResource;
|
|
56
|
+
get chains(): ChainsResource;
|
|
57
|
+
get contacts(): ContactsResource;
|
|
58
|
+
get escrows(): EscrowsResource;
|
|
59
|
+
get feeSchedules(): FeeSchedulesResource;
|
|
60
|
+
get integrationGateways(): IntegrationGatewaysResource;
|
|
61
|
+
get onboarding(): OnboardingResource;
|
|
62
|
+
get paymentRequests(): PaymentRequestsResource;
|
|
63
|
+
get payments(): PaymentsResource;
|
|
64
|
+
get ramps(): RampsResource;
|
|
65
|
+
get rwa(): RwaResource;
|
|
66
|
+
get tenant(): TenantResource;
|
|
67
|
+
get tokenization(): TokenizationResource;
|
|
68
|
+
get transactions(): TransactionsResource;
|
|
69
|
+
get wallets(): WalletsResource;
|
|
70
|
+
get webhooks(): WebhooksResource;
|
|
71
|
+
get workflows(): WorkflowClient;
|
|
72
|
+
get workspaceTokens(): WorkspaceTokensResource;
|
|
73
|
+
get workspaces(): WorkspacesResource;
|
|
74
|
+
static forTenant(options: XKOVAClientOptions): XKOVAClient;
|
|
75
|
+
private prepareRequest;
|
|
76
|
+
private buildUrl;
|
|
77
|
+
private buildHeaders;
|
|
78
|
+
private requestSignal;
|
|
79
|
+
}
|
|
80
|
+
export {};
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { XKOVAApiError } from './errors.js';
|
|
2
|
+
import { generateIdempotencyKey } from './idempotency.js';
|
|
3
|
+
import { signXKOVARequest, validateXKOVARequestSigner, xkovaContentDigest, } from './request-signing.js';
|
|
4
|
+
import { AccountHoldersResource, } from './resources/account-holders.js';
|
|
5
|
+
import { BrandingResource } from './resources/branding.js';
|
|
6
|
+
import { ChainsResource } from './resources/chains.js';
|
|
7
|
+
import { ContactsResource } from './resources/contacts.js';
|
|
8
|
+
import { EscrowsResource } from './resources/escrows.js';
|
|
9
|
+
import { FeeSchedulesResource } from './resources/fee-schedules.js';
|
|
10
|
+
import { IntegrationGatewaysResource } from './resources/integration-gateways.js';
|
|
11
|
+
import { OnboardingResource } from './resources/onboarding.js';
|
|
12
|
+
import { PaymentRequestsResource } from './resources/payment-requests.js';
|
|
13
|
+
import { PaymentsResource } from './resources/payments.js';
|
|
14
|
+
import { RampsResource } from './resources/ramps.js';
|
|
15
|
+
import { RwaResource } from './resources/rwa.js';
|
|
16
|
+
import { TenantResource } from './resources/tenant.js';
|
|
17
|
+
import { TokenizationResource } from './resources/tokenization.js';
|
|
18
|
+
import { TransactionsResource } from './resources/transactions.js';
|
|
19
|
+
import { WalletsResource } from './resources/wallets.js';
|
|
20
|
+
import { WebhooksResource } from './resources/webhooks.js';
|
|
21
|
+
import { WorkflowClient } from './resources/workflows.js';
|
|
22
|
+
import { WorkspaceTokensResource } from './resources/workspace-tokens.js';
|
|
23
|
+
import { WorkspacesResource } from './resources/workspaces.js';
|
|
24
|
+
export class XKOVAClient {
|
|
25
|
+
config;
|
|
26
|
+
memberContext;
|
|
27
|
+
apiBaseUrl;
|
|
28
|
+
fetchImpl;
|
|
29
|
+
constructor(config, memberContext) {
|
|
30
|
+
this.config = config;
|
|
31
|
+
this.memberContext = memberContext;
|
|
32
|
+
if (config.timeoutMs !== undefined &&
|
|
33
|
+
(!Number.isFinite(config.timeoutMs) || config.timeoutMs <= 0)) {
|
|
34
|
+
throw new Error('XKOVAClient timeoutMs must be a positive finite number');
|
|
35
|
+
}
|
|
36
|
+
if (config.requestSigner !== undefined) {
|
|
37
|
+
validateXKOVARequestSigner(config.requestSigner);
|
|
38
|
+
}
|
|
39
|
+
this.apiBaseUrl = normalizeApiBaseUrl(config.baseUrl);
|
|
40
|
+
this.fetchImpl = config.fetchImpl ?? fetch;
|
|
41
|
+
}
|
|
42
|
+
namespaces = {};
|
|
43
|
+
get accountHolders() {
|
|
44
|
+
return (this.namespaces.accountHolders ??= new AccountHoldersResource(this, this.fetchImpl));
|
|
45
|
+
}
|
|
46
|
+
get branding() {
|
|
47
|
+
return (this.namespaces.branding ??= new BrandingResource(this));
|
|
48
|
+
}
|
|
49
|
+
get chains() {
|
|
50
|
+
return (this.namespaces.chains ??= new ChainsResource(this));
|
|
51
|
+
}
|
|
52
|
+
get contacts() {
|
|
53
|
+
return (this.namespaces.contacts ??= new ContactsResource(this));
|
|
54
|
+
}
|
|
55
|
+
get escrows() {
|
|
56
|
+
return (this.namespaces.escrows ??= new EscrowsResource(this));
|
|
57
|
+
}
|
|
58
|
+
get feeSchedules() {
|
|
59
|
+
return (this.namespaces.feeSchedules ??= new FeeSchedulesResource(this));
|
|
60
|
+
}
|
|
61
|
+
get integrationGateways() {
|
|
62
|
+
return (this.namespaces.integrationGateways ??= new IntegrationGatewaysResource(this));
|
|
63
|
+
}
|
|
64
|
+
get onboarding() {
|
|
65
|
+
return (this.namespaces.onboarding ??= new OnboardingResource(this));
|
|
66
|
+
}
|
|
67
|
+
get paymentRequests() {
|
|
68
|
+
return (this.namespaces.paymentRequests ??= new PaymentRequestsResource(this));
|
|
69
|
+
}
|
|
70
|
+
get payments() {
|
|
71
|
+
return (this.namespaces.payments ??= new PaymentsResource(this));
|
|
72
|
+
}
|
|
73
|
+
get ramps() {
|
|
74
|
+
return (this.namespaces.ramps ??= new RampsResource(this));
|
|
75
|
+
}
|
|
76
|
+
get rwa() {
|
|
77
|
+
return (this.namespaces.rwa ??= new RwaResource(this));
|
|
78
|
+
}
|
|
79
|
+
get tenant() {
|
|
80
|
+
return (this.namespaces.tenant ??= new TenantResource(this));
|
|
81
|
+
}
|
|
82
|
+
get tokenization() {
|
|
83
|
+
return (this.namespaces.tokenization ??= new TokenizationResource(this));
|
|
84
|
+
}
|
|
85
|
+
get transactions() {
|
|
86
|
+
return (this.namespaces.transactions ??= new TransactionsResource(this));
|
|
87
|
+
}
|
|
88
|
+
get wallets() {
|
|
89
|
+
return (this.namespaces.wallets ??= new WalletsResource(this));
|
|
90
|
+
}
|
|
91
|
+
get webhooks() {
|
|
92
|
+
return (this.namespaces.webhooks ??= new WebhooksResource(this));
|
|
93
|
+
}
|
|
94
|
+
get workflows() {
|
|
95
|
+
return (this.namespaces.workflows ??= new WorkflowClient(this));
|
|
96
|
+
}
|
|
97
|
+
get workspaceTokens() {
|
|
98
|
+
return (this.namespaces.workspaceTokens ??= new WorkspaceTokensResource(this));
|
|
99
|
+
}
|
|
100
|
+
get workspaces() {
|
|
101
|
+
return (this.namespaces.workspaces ??= new WorkspacesResource(this));
|
|
102
|
+
}
|
|
103
|
+
static forTenant(options) {
|
|
104
|
+
return new XKOVAClient(options);
|
|
105
|
+
}
|
|
106
|
+
/** @internal Created by the BFF helper after resolving its confidential session store. */
|
|
107
|
+
static forMemberSession(options, context) {
|
|
108
|
+
return new XKOVAClient(options, Object.freeze({ ...context }));
|
|
109
|
+
}
|
|
110
|
+
/** @internal Resource transport. Use typed namespaces instead. */
|
|
111
|
+
async requestJson(options) {
|
|
112
|
+
const prepared = await this.prepareRequest(options);
|
|
113
|
+
const signal = this.requestSignal(options.signal);
|
|
114
|
+
const response = await this.fetchImpl(prepared.url, {
|
|
115
|
+
method: options.method,
|
|
116
|
+
headers: prepared.headers,
|
|
117
|
+
cache: 'no-store',
|
|
118
|
+
redirect: 'error',
|
|
119
|
+
...(signal !== undefined && { signal }),
|
|
120
|
+
...(prepared.body !== undefined && { body: prepared.body }),
|
|
121
|
+
});
|
|
122
|
+
if (!response.ok)
|
|
123
|
+
throw await XKOVAApiError.fromResponse(response);
|
|
124
|
+
if (response.status === 204)
|
|
125
|
+
return undefined;
|
|
126
|
+
const text = await response.text();
|
|
127
|
+
if (text.length === 0)
|
|
128
|
+
return undefined;
|
|
129
|
+
return JSON.parse(text);
|
|
130
|
+
}
|
|
131
|
+
/** @internal Resource transport. Use typed namespaces instead. */
|
|
132
|
+
async requestText(options) {
|
|
133
|
+
const prepared = await this.prepareRequest({
|
|
134
|
+
...options,
|
|
135
|
+
accept: options.accept ?? 'text/plain, */*',
|
|
136
|
+
});
|
|
137
|
+
const signal = this.requestSignal(options.signal);
|
|
138
|
+
const response = await this.fetchImpl(prepared.url, {
|
|
139
|
+
method: options.method,
|
|
140
|
+
headers: prepared.headers,
|
|
141
|
+
cache: 'no-store',
|
|
142
|
+
redirect: 'error',
|
|
143
|
+
...(signal !== undefined && { signal }),
|
|
144
|
+
...(prepared.body !== undefined && { body: prepared.body }),
|
|
145
|
+
});
|
|
146
|
+
if (!response.ok)
|
|
147
|
+
throw await XKOVAApiError.fromResponse(response);
|
|
148
|
+
return response.text();
|
|
149
|
+
}
|
|
150
|
+
async prepareRequest(options) {
|
|
151
|
+
const mutation = isMutationMethod(options.method);
|
|
152
|
+
const signer = this.config.requestSigner;
|
|
153
|
+
if (mutation && this.config.apiKey.startsWith('xkv_live_') && signer === undefined) {
|
|
154
|
+
throw new Error('XKOVA production mutations require a configured asymmetric request signer');
|
|
155
|
+
}
|
|
156
|
+
const url = this.buildUrl(options);
|
|
157
|
+
const serializedBody = options.body === undefined ? undefined : JSON.stringify(options.body);
|
|
158
|
+
if (options.body !== undefined && serializedBody === undefined) {
|
|
159
|
+
throw new Error('XKOVA request body is not JSON serializable');
|
|
160
|
+
}
|
|
161
|
+
const bodyBytes = serializedBody === undefined ? undefined : new TextEncoder().encode(serializedBody);
|
|
162
|
+
const headers = this.buildHeaders(options);
|
|
163
|
+
if (mutation && signer !== undefined) {
|
|
164
|
+
const signatureBody = bodyBytes ?? new Uint8Array();
|
|
165
|
+
if (bodyBytes !== undefined || headers.has('content-digest')) {
|
|
166
|
+
headers.set('Content-Digest', xkovaContentDigest(signatureBody));
|
|
167
|
+
}
|
|
168
|
+
const fields = await signXKOVARequest({
|
|
169
|
+
method: options.method,
|
|
170
|
+
url,
|
|
171
|
+
headers,
|
|
172
|
+
body: signatureBody,
|
|
173
|
+
signer,
|
|
174
|
+
});
|
|
175
|
+
headers.set('Signature-Input', fields.signatureInput);
|
|
176
|
+
headers.set('Signature', fields.signature);
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
url: url.toString(),
|
|
180
|
+
headers,
|
|
181
|
+
...(serializedBody !== undefined && { body: serializedBody }),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
buildUrl(options) {
|
|
185
|
+
const url = new URL(`${this.apiBaseUrl}${options.path}`);
|
|
186
|
+
for (const [key, value] of Object.entries(options.query ?? {})) {
|
|
187
|
+
if (value === undefined || value === null)
|
|
188
|
+
continue;
|
|
189
|
+
if (Array.isArray(value)) {
|
|
190
|
+
if (value.length > 0)
|
|
191
|
+
url.searchParams.set(key, value.join(','));
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
url.searchParams.set(key, String(value));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return url;
|
|
198
|
+
}
|
|
199
|
+
buildHeaders(options) {
|
|
200
|
+
const headers = new Headers({
|
|
201
|
+
Accept: options.accept ?? 'application/json',
|
|
202
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
203
|
+
});
|
|
204
|
+
if (options.body !== undefined)
|
|
205
|
+
headers.set('Content-Type', 'application/json');
|
|
206
|
+
if (options.method !== 'GET') {
|
|
207
|
+
headers.set('Idempotency-Key', options.idempotencyKey ?? generateIdempotencyKey());
|
|
208
|
+
}
|
|
209
|
+
for (const [name, value] of Object.entries(options.headers ?? {})) {
|
|
210
|
+
headers.set(name, value);
|
|
211
|
+
}
|
|
212
|
+
if (this.memberContext) {
|
|
213
|
+
const authority = {
|
|
214
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
215
|
+
'X-Member-Session-Id': this.memberContext.memberSessionId,
|
|
216
|
+
'X-XKOVA-Publishable-Key': this.memberContext.memberAuthPublishableKey,
|
|
217
|
+
'X-Account-Holder-Id': this.memberContext.accountHolderId,
|
|
218
|
+
};
|
|
219
|
+
for (const [name, value] of Object.entries(authority)) {
|
|
220
|
+
if (headers.has(name) && headers.get(name) !== value) {
|
|
221
|
+
throw new TypeError('A member-bound SDK operation cannot replace its session authority');
|
|
222
|
+
}
|
|
223
|
+
headers.set(name, value);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return headers;
|
|
227
|
+
}
|
|
228
|
+
requestSignal(signal) {
|
|
229
|
+
const timeout = this.config.timeoutMs === undefined ? undefined : AbortSignal.timeout(this.config.timeoutMs);
|
|
230
|
+
if (signal && timeout)
|
|
231
|
+
return AbortSignal.any([signal, timeout]);
|
|
232
|
+
return signal ?? timeout;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function isMutationMethod(method) {
|
|
236
|
+
return method !== 'GET';
|
|
237
|
+
}
|
|
238
|
+
function normalizeApiBaseUrl(baseUrl) {
|
|
239
|
+
const url = new URL(baseUrl);
|
|
240
|
+
if (url.username || url.password || url.search || url.hash) {
|
|
241
|
+
throw new TypeError('XKOVA SDK baseUrl must be an API origin or /v1 base URL');
|
|
242
|
+
}
|
|
243
|
+
if (url.protocol !== 'https:' && !(url.protocol === 'http:' && url.hostname === 'localhost')) {
|
|
244
|
+
throw new TypeError('XKOVA SDK baseUrl must use HTTPS or localhost HTTP');
|
|
245
|
+
}
|
|
246
|
+
const pathname = url.pathname.replace(/\/+$/, '');
|
|
247
|
+
if (pathname !== '' && pathname !== '/v1') {
|
|
248
|
+
throw new TypeError('XKOVA SDK baseUrl must be an API origin or /v1 base URL');
|
|
249
|
+
}
|
|
250
|
+
return `${url.origin}/v1`;
|
|
251
|
+
}
|
package/dist/cursor.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursors are opaque. Pass each page's `next_cursor` back unchanged until it
|
|
3
|
+
* is null. Errors stop iteration; use `withRetry` in the page fetcher if needed.
|
|
4
|
+
*/
|
|
5
|
+
export interface CursorPage<T> {
|
|
6
|
+
items: ReadonlyArray<T>;
|
|
7
|
+
/** `null` when there's no more data; otherwise pass back as `cursor`. */
|
|
8
|
+
next_cursor: string | null;
|
|
9
|
+
}
|
|
10
|
+
export interface PaginateCursorOptions<T> {
|
|
11
|
+
/** Receives undefined on the first call, then the previous page's cursor. */
|
|
12
|
+
fetchPage: (cursor: string | undefined) => Promise<CursorPage<T>>;
|
|
13
|
+
/** Hard cap on pages walked (defense against runaway pagination). */
|
|
14
|
+
maxPages?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function paginateCursor<T>(opts: PaginateCursorOptions<T>): AsyncGenerator<T, void, void>;
|
|
17
|
+
/**
|
|
18
|
+
* Collect only bounded lists. Use `for await` on larger results to avoid
|
|
19
|
+
* holding every item in memory.
|
|
20
|
+
*/
|
|
21
|
+
export declare function collectCursor<T>(opts: PaginateCursorOptions<T>): Promise<T[]>;
|
package/dist/cursor.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursors are opaque. Pass each page's `next_cursor` back unchanged until it
|
|
3
|
+
* is null. Errors stop iteration; use `withRetry` in the page fetcher if needed.
|
|
4
|
+
*/
|
|
5
|
+
export async function* paginateCursor(opts) {
|
|
6
|
+
const max = opts.maxPages ?? Infinity;
|
|
7
|
+
let cursor = undefined;
|
|
8
|
+
let pages = 0;
|
|
9
|
+
while (pages < max) {
|
|
10
|
+
const page = await opts.fetchPage(cursor);
|
|
11
|
+
for (const item of page.items)
|
|
12
|
+
yield item;
|
|
13
|
+
pages += 1;
|
|
14
|
+
if (page.next_cursor === null)
|
|
15
|
+
return;
|
|
16
|
+
cursor = page.next_cursor;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Collect only bounded lists. Use `for await` on larger results to avoid
|
|
21
|
+
* holding every item in memory.
|
|
22
|
+
*/
|
|
23
|
+
export async function collectCursor(opts) {
|
|
24
|
+
const out = [];
|
|
25
|
+
for await (const item of paginateCursor(opts))
|
|
26
|
+
out.push(item);
|
|
27
|
+
return out;
|
|
28
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API error codes can grow between SDK releases, so `code` remains a string.
|
|
3
|
+
* Branch on it before narrowing `details` with a runtime guard. Retain the
|
|
4
|
+
* correlation ID when reporting a failure to support.
|
|
5
|
+
*/
|
|
6
|
+
export declare class XKOVAApiError extends Error {
|
|
7
|
+
readonly code: string;
|
|
8
|
+
readonly status: number;
|
|
9
|
+
readonly details: Record<string, unknown> | undefined;
|
|
10
|
+
readonly correlationId: string;
|
|
11
|
+
readonly retryAfter: string | undefined;
|
|
12
|
+
constructor(opts: {
|
|
13
|
+
code: string;
|
|
14
|
+
message: string;
|
|
15
|
+
status: number;
|
|
16
|
+
correlationId: string;
|
|
17
|
+
details?: Record<string, unknown>;
|
|
18
|
+
retryAfter?: string;
|
|
19
|
+
});
|
|
20
|
+
/**
|
|
21
|
+
* Rejects malformed responses with a plain Error. A proxy error page has no
|
|
22
|
+
* XKOVA error code, so callers must handle failures outside the API envelope.
|
|
23
|
+
*/
|
|
24
|
+
static fromResponse(res: Response): Promise<XKOVAApiError>;
|
|
25
|
+
}
|
|
26
|
+
/** Object-storage stage failure, separate from the API's error namespace. */
|
|
27
|
+
export declare class XKOVAUploadError extends Error {
|
|
28
|
+
readonly upstreamStatus?: number | undefined;
|
|
29
|
+
readonly stage = "avatar.object-storage";
|
|
30
|
+
constructor(message: string, upstreamStatus?: number | undefined);
|
|
31
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API error codes can grow between SDK releases, so `code` remains a string.
|
|
3
|
+
* Branch on it before narrowing `details` with a runtime guard. Retain the
|
|
4
|
+
* correlation ID when reporting a failure to support.
|
|
5
|
+
*/
|
|
6
|
+
export class XKOVAApiError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
status;
|
|
9
|
+
details;
|
|
10
|
+
correlationId;
|
|
11
|
+
retryAfter;
|
|
12
|
+
constructor(opts) {
|
|
13
|
+
super(opts.message);
|
|
14
|
+
this.name = 'XKOVAApiError';
|
|
15
|
+
this.code = opts.code;
|
|
16
|
+
this.status = opts.status;
|
|
17
|
+
this.details = opts.details;
|
|
18
|
+
this.correlationId = opts.correlationId;
|
|
19
|
+
this.retryAfter = opts.retryAfter;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Rejects malformed responses with a plain Error. A proxy error page has no
|
|
23
|
+
* XKOVA error code, so callers must handle failures outside the API envelope.
|
|
24
|
+
*/
|
|
25
|
+
static async fromResponse(res) {
|
|
26
|
+
let body;
|
|
27
|
+
try {
|
|
28
|
+
body = await res.json();
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
throw new Error(`XKOVAApiError.fromResponse: response body wasn't JSON (status ${res.status})`);
|
|
32
|
+
}
|
|
33
|
+
if (!isErrorEnvelope(body)) {
|
|
34
|
+
throw new Error(`XKOVAApiError.fromResponse: response body wasn't an XKOVA error envelope (status ${res.status})`);
|
|
35
|
+
}
|
|
36
|
+
return new XKOVAApiError({
|
|
37
|
+
code: body.code,
|
|
38
|
+
message: body.message,
|
|
39
|
+
status: res.status,
|
|
40
|
+
correlationId: body.correlation_id,
|
|
41
|
+
...(res.headers.has('retry-after') && { retryAfter: res.headers.get('retry-after') }),
|
|
42
|
+
...(body.details !== undefined && { details: body.details }),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Object-storage stage failure, separate from the API's error namespace. */
|
|
47
|
+
export class XKOVAUploadError extends Error {
|
|
48
|
+
upstreamStatus;
|
|
49
|
+
stage = 'avatar.object-storage';
|
|
50
|
+
constructor(message, upstreamStatus) {
|
|
51
|
+
super(message);
|
|
52
|
+
this.upstreamStatus = upstreamStatus;
|
|
53
|
+
this.name = 'XKOVAUploadError';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function isErrorEnvelope(v) {
|
|
57
|
+
if (typeof v !== 'object' || v === null)
|
|
58
|
+
return false;
|
|
59
|
+
const o = v;
|
|
60
|
+
return (typeof o['code'] === 'string' &&
|
|
61
|
+
typeof o['message'] === 'string' &&
|
|
62
|
+
typeof o['correlation_id'] === 'string');
|
|
63
|
+
}
|