@capxul/sdk 0.1.0-alpha.6 → 0.1.0-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -8
- package/README.md +86 -4
- package/dist/{client-ChfXWMzO.d.ts → client-ByzDfG98.d.ts} +12 -9
- package/dist/{client-DW_fW9DC.d.cts → client-DDAVWtzJ.d.cts} +12 -9
- package/dist/client.cjs +216 -26
- package/dist/client.d.cts +2 -3
- package/dist/client.d.ts +2 -3
- package/dist/client.js +216 -26
- package/dist/index.cjs +216 -26
- package/dist/index.d.cts +9 -10
- package/dist/index.d.ts +9 -10
- package/dist/index.js +216 -26
- package/dist/types-PM4AQRLP.d.ts +1136 -0
- package/dist/types-hfcOE7Oi.d.cts +1136 -0
- package/dist/webhooks.d.cts +1 -2
- package/dist/webhooks.d.ts +1 -2
- package/package.json +6 -6
- package/dist/types-CYvLP5pP.d.ts +0 -213
- package/dist/types-X02RbQ2u.d.cts +0 -213
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @capxul/sdk
|
|
2
2
|
|
|
3
|
-
## 0.1.0-alpha.
|
|
3
|
+
## 0.1.0-alpha.8
|
|
4
4
|
|
|
5
5
|
### Minor Changes
|
|
6
6
|
|
|
@@ -21,13 +21,6 @@
|
|
|
21
21
|
`capxul.withdrawals.create({ destination: { kind, externalAccountId } })`
|
|
22
22
|
call sites. Pass only `externalAccountId`.
|
|
23
23
|
|
|
24
|
-
## 0.1.0-alpha.5
|
|
25
|
-
|
|
26
|
-
### Patch Changes
|
|
27
|
-
|
|
28
|
-
- Version sync with `@capxul/sdk-react@0.1.0-alpha.5`; no headless SDK runtime
|
|
29
|
-
changes.
|
|
30
|
-
|
|
31
24
|
## 0.1.0-alpha.4
|
|
32
25
|
|
|
33
26
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -19,9 +19,9 @@ pnpm add @capxul/sdk @capxul/sdk-react
|
|
|
19
19
|
React-flavored entry point lives in `@capxul/sdk-react`. Pick:
|
|
20
20
|
|
|
21
21
|
- **Browser apps (recommended).** Install both. Use
|
|
22
|
-
`<CapxulProvider
|
|
23
|
-
and the `useMe` / `useCapxulStatus` hooks.
|
|
24
|
-
README for the lazy-DX example.
|
|
22
|
+
`<CapxulProvider config={{ mode: "publishable-key", publishableKey }}>`
|
|
23
|
+
from `@capxul/sdk-react` and the `useMe` / `useCapxulStatus` hooks.
|
|
24
|
+
See that package's README for the lazy-DX example.
|
|
25
25
|
- **Server / CLI / scripts.** Install only `@capxul/sdk`. Build a
|
|
26
26
|
`CapxulClient` directly with your own auth and Convex adapters
|
|
27
27
|
(see "Server-side construction" below).
|
|
@@ -76,11 +76,93 @@ runtime URLs lazily — no need to ship secrets to the client:
|
|
|
76
76
|
```tsx
|
|
77
77
|
import { CapxulProvider, useMe, useCapxulStatus } from "@capxul/sdk-react";
|
|
78
78
|
|
|
79
|
-
<CapxulProvider
|
|
79
|
+
<CapxulProvider
|
|
80
|
+
config={{
|
|
81
|
+
mode: "publishable-key",
|
|
82
|
+
publishableKey: process.env.NEXT_PUBLIC_CAPXUL_PUBLISHABLE_KEY!,
|
|
83
|
+
}}
|
|
84
|
+
>
|
|
80
85
|
<App />
|
|
81
86
|
</CapxulProvider>;
|
|
82
87
|
```
|
|
83
88
|
|
|
89
|
+
## Publishable-key transport
|
|
90
|
+
|
|
91
|
+
The browser transport accepts a publishable key, then lazily resolves
|
|
92
|
+
runtime URLs by POSTing to `/v1/client/bootstrap` on the first auth or
|
|
93
|
+
`ensureRuntime()` call. The successful backend response is intentionally
|
|
94
|
+
small:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{ "authBaseUrl": "https://<deployment>.convex.site/api/auth", "convexUrl": "https://<deployment>.convex.cloud" }
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The resolved runtime is cached for the lifetime of the transport. Concurrent
|
|
101
|
+
callers share one bootstrap request, successful resolutions are reused, and
|
|
102
|
+
failed bootstrap attempts reset so the next call can retry. Tests and
|
|
103
|
+
non-default deployments can inject both `fetchImpl` and an absolute
|
|
104
|
+
`bootstrapUrl`:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { makeHttpTransport, CapxulError } from "@capxul/sdk";
|
|
108
|
+
|
|
109
|
+
const transport = makeHttpTransport({
|
|
110
|
+
mode: "publishable-key",
|
|
111
|
+
publishableKey: process.env.NEXT_PUBLIC_CAPXUL_PUBLISHABLE_KEY!,
|
|
112
|
+
bootstrapUrl: "https://api.capxul.com/v1/client/bootstrap",
|
|
113
|
+
fetchImpl: fetch,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
await transport.ensureRuntime();
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Local setup errors and backend bootstrap errors both use `CapxulError`, but
|
|
120
|
+
carry different `details.source` values:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
try {
|
|
124
|
+
await transport.ensureRuntime();
|
|
125
|
+
} catch (error) {
|
|
126
|
+
if (error instanceof CapxulError) {
|
|
127
|
+
if (error.details?.source === "sdk-config") {
|
|
128
|
+
// Missing or malformed local config, such as publishableKey or bootstrapUrl.
|
|
129
|
+
}
|
|
130
|
+
if (error.details?.source === "backend-bootstrap") {
|
|
131
|
+
// Sanitized backend refusal, such as NOT_AUTHENTICATED or PERMISSION_DENIED.
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Runtime proof status:
|
|
138
|
+
|
|
139
|
+
| Surface | Source support | Runtime proof | Status |
|
|
140
|
+
|---|---|---|---|
|
|
141
|
+
| SDK transport | `makeHttpTransport({ mode: "publishable-key", publishableKey })` | `packages/sdk/tests/unit/transport.test.ts` proves config validation, singleflight bootstrap, retry after failure, lifecycle transitions, and sanitized backend errors. | Proven with mocked fetch |
|
|
142
|
+
| React provider | `CapxulProvider config={{ mode: "publishable-key", ... }}` | `packages/sdk-react/ops/proof/react-headless.test.tsx` proves bootstrap before a real `useMe()` read through the provider and lazy Convex data client. | Proven with mocked fetch + headless React |
|
|
143
|
+
| Reference CLI | `bootstrap probe --mock --json` | `apps/reference-cli/scripts/agent-driver.ts` phase 0 and the direct CLI command prove provider/bootstrap/auth ordering and sanitized output. | Proven locally; live endpoint remains manual-key gated |
|
|
144
|
+
|
|
145
|
+
Copy-paste local replication:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
corepack pnpm --filter @capxul/sdk check-types
|
|
149
|
+
corepack pnpm --filter @capxul/sdk build
|
|
150
|
+
corepack pnpm --filter @capxul/sdk-react check-types
|
|
151
|
+
corepack pnpm --filter @capxul/sdk-react build
|
|
152
|
+
corepack pnpm --filter @capxul/reference-cli check-types
|
|
153
|
+
corepack pnpm --filter @capxul/reference-cli build
|
|
154
|
+
node apps/reference-cli/dist/cli.js bootstrap probe --mock --json
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Run the SDK and React SDK builds before the reference CLI typecheck in a
|
|
158
|
+
fresh checkout; the CLI depends on their generated declaration outputs.
|
|
159
|
+
|
|
160
|
+
Expected sanitized pass signal:
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{"command":"bootstrap.probe","ok":true,"mode":"publishable-key","status":"ready","bootstrapRequests":1,"authRequests":1,"keyLengthClass":"provided","mocked":true}
|
|
164
|
+
```
|
|
165
|
+
|
|
84
166
|
## Public surface (alpha)
|
|
85
167
|
|
|
86
168
|
```ts
|
|
@@ -2,8 +2,7 @@ import { Account as Account$1 } from 'viem';
|
|
|
2
2
|
import { AnyStateMachine } from 'xstate';
|
|
3
3
|
import { A as AccountId, S as SafeId, c as KycProfileId, b as ExternalAccountId, f as SubAccountId, d as OrganizationId, a as ApiKeyId, X as TimestampIso, D as DocumentId, O as OperationId, P as PaymentId, g as TransferId, k as WithdrawalId, W as WebhookEndpointId, j as WebhookEventId, M as MemberId, K as KybProfileId, V as VirtualAccountId, i as VirtualCardId } from './next-action-DkrwXYay.js';
|
|
4
4
|
import { d as CapxulResult, C as CapxulError } from './errors-QHD5Tlok.js';
|
|
5
|
-
import { A as Account, U as UserIdentifier, a as AccountLookupResult, S as Safe, i as KycProfile, E as ExternalAccount, L as List, v as SubAccount, B as BalanceLedgerEntry, b as ApiKey, D as Document, O as Operation, l as OperationStatus, k as Money, C as CreatePaymentResult, o as Payment, F as TransferEndpoint, f as CreateTransferResult, y as Transfer, g as CreateWithdrawalResult, _ as Withdrawal, Y as WebhookEndpoint, Z as WebhookEvent, n as Organization, J as Treasury, M as Member, K as KybProfile, V as VirtualAccount, Q as VirtualCard } from './types-
|
|
6
|
-
import * as types from '@repo/api-contract/gen/types';
|
|
5
|
+
import { A as Account, U as UserIdentifier, a as AccountLookupResult, S as Safe, i as KycProfile, E as ExternalAccount, L as List, v as SubAccount, B as BalanceLedgerEntry, b as ApiKey, a0 as CreateApiKeyRequest, a1 as CreateDocumentRequest, D as Document, O as Operation, l as OperationStatus, k as Money, C as CreatePaymentResult, o as Payment, F as TransferEndpoint, f as CreateTransferResult, y as Transfer, g as CreateWithdrawalResult, _ as Withdrawal, a2 as CreateWebhookEndpointRequest, Y as WebhookEndpoint, Z as WebhookEvent, n as Organization, J as Treasury, M as Member, K as KybProfile, a3 as CreateVirtualAccountRequest, V as VirtualAccount, a4 as CreateVirtualCardRequest, Q as VirtualCard } from './types-PM4AQRLP.js';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Accounts domain — individual user accounts per sdk-surface.md §1a.
|
|
@@ -111,7 +110,7 @@ type AccountsClient = {
|
|
|
111
110
|
* on `create` only; subsequent reads omit it.
|
|
112
111
|
*/
|
|
113
112
|
|
|
114
|
-
type ApiKeysCreateInput =
|
|
113
|
+
type ApiKeysCreateInput = CreateApiKeyRequest & {
|
|
115
114
|
readonly organizationId: OrganizationId;
|
|
116
115
|
};
|
|
117
116
|
type ApiKeysRetrieveInput = {
|
|
@@ -236,7 +235,7 @@ type AuthClient = {
|
|
|
236
235
|
* bank_statement | tax_form`.
|
|
237
236
|
*/
|
|
238
237
|
|
|
239
|
-
type DocumentsCreateInput =
|
|
238
|
+
type DocumentsCreateInput = CreateDocumentRequest;
|
|
240
239
|
type DocumentsListInput = {
|
|
241
240
|
readonly limit?: number;
|
|
242
241
|
readonly cursor?: string;
|
|
@@ -589,7 +588,7 @@ type OrgWithdrawalsClient = {
|
|
|
589
588
|
* variant at call time and require `organizationId` explicitly.
|
|
590
589
|
*/
|
|
591
590
|
|
|
592
|
-
type WebhookEndpointsCreateInput =
|
|
591
|
+
type WebhookEndpointsCreateInput = CreateWebhookEndpointRequest & {
|
|
593
592
|
readonly organizationId: OrganizationId;
|
|
594
593
|
};
|
|
595
594
|
type WebhookEndpointsRetrieveInput = {
|
|
@@ -911,6 +910,7 @@ type TransportRuntime = {
|
|
|
911
910
|
*/
|
|
912
911
|
type HttpTransport = {
|
|
913
912
|
readonly fetch: (path: string, init?: RequestInit) => Promise<Response>;
|
|
913
|
+
readonly ensureRuntime: () => Promise<TransportRuntime>;
|
|
914
914
|
readonly authBaseUrl: string;
|
|
915
915
|
readonly convexUrl: string;
|
|
916
916
|
readonly getState: () => TransportState;
|
|
@@ -924,8 +924,11 @@ type HttpTransport = {
|
|
|
924
924
|
/**
|
|
925
925
|
* Build an `HttpTransport` from a `BrowserCapxulConfig`.
|
|
926
926
|
*
|
|
927
|
-
* Throws `
|
|
928
|
-
*
|
|
927
|
+
* Throws `CapxulError<"INVALID_INPUT">` with
|
|
928
|
+
* `details.source === "sdk-config"` when local transport config is
|
|
929
|
+
* malformed. Backend bootstrap failures also throw `CapxulError`, but
|
|
930
|
+
* carry `details.source === "backend-bootstrap"` so consumers can
|
|
931
|
+
* distinguish setup mistakes from server-side bootstrap refusals.
|
|
929
932
|
*/
|
|
930
933
|
declare function makeHttpTransport(config: BrowserCapxulConfig): HttpTransport;
|
|
931
934
|
|
|
@@ -1055,7 +1058,7 @@ type TokenTransfersClient = {
|
|
|
1055
1058
|
* in the create payload rather than nesting the route under the owner.
|
|
1056
1059
|
*/
|
|
1057
1060
|
|
|
1058
|
-
type VirtualAccountsCreateInput =
|
|
1061
|
+
type VirtualAccountsCreateInput = CreateVirtualAccountRequest;
|
|
1059
1062
|
type VirtualAccountsListInput = {
|
|
1060
1063
|
readonly limit?: number;
|
|
1061
1064
|
readonly cursor?: string;
|
|
@@ -1080,7 +1083,7 @@ type VirtualAccountsClient = {
|
|
|
1080
1083
|
* Uses Pattern C (ownerKind in body).
|
|
1081
1084
|
*/
|
|
1082
1085
|
|
|
1083
|
-
type VirtualCardsCreateInput =
|
|
1086
|
+
type VirtualCardsCreateInput = CreateVirtualCardRequest;
|
|
1084
1087
|
type VirtualCardsListInput = {
|
|
1085
1088
|
readonly limit?: number;
|
|
1086
1089
|
readonly cursor?: string;
|
|
@@ -2,8 +2,7 @@ import { Account as Account$1 } from 'viem';
|
|
|
2
2
|
import { AnyStateMachine } from 'xstate';
|
|
3
3
|
import { A as AccountId, S as SafeId, c as KycProfileId, b as ExternalAccountId, f as SubAccountId, d as OrganizationId, a as ApiKeyId, X as TimestampIso, D as DocumentId, O as OperationId, P as PaymentId, g as TransferId, k as WithdrawalId, W as WebhookEndpointId, j as WebhookEventId, M as MemberId, K as KybProfileId, V as VirtualAccountId, i as VirtualCardId } from './next-action-DkrwXYay.cjs';
|
|
4
4
|
import { d as CapxulResult, C as CapxulError } from './errors-GgKrSUKp.cjs';
|
|
5
|
-
import { A as Account, U as UserIdentifier, a as AccountLookupResult, S as Safe, i as KycProfile, E as ExternalAccount, L as List, v as SubAccount, B as BalanceLedgerEntry, b as ApiKey, D as Document, O as Operation, l as OperationStatus, k as Money, C as CreatePaymentResult, o as Payment, F as TransferEndpoint, f as CreateTransferResult, y as Transfer, g as CreateWithdrawalResult, _ as Withdrawal, Y as WebhookEndpoint, Z as WebhookEvent, n as Organization, J as Treasury, M as Member, K as KybProfile, V as VirtualAccount, Q as VirtualCard } from './types-
|
|
6
|
-
import * as types from '@repo/api-contract/gen/types';
|
|
5
|
+
import { A as Account, U as UserIdentifier, a as AccountLookupResult, S as Safe, i as KycProfile, E as ExternalAccount, L as List, v as SubAccount, B as BalanceLedgerEntry, b as ApiKey, a0 as CreateApiKeyRequest, a1 as CreateDocumentRequest, D as Document, O as Operation, l as OperationStatus, k as Money, C as CreatePaymentResult, o as Payment, F as TransferEndpoint, f as CreateTransferResult, y as Transfer, g as CreateWithdrawalResult, _ as Withdrawal, a2 as CreateWebhookEndpointRequest, Y as WebhookEndpoint, Z as WebhookEvent, n as Organization, J as Treasury, M as Member, K as KybProfile, a3 as CreateVirtualAccountRequest, V as VirtualAccount, a4 as CreateVirtualCardRequest, Q as VirtualCard } from './types-hfcOE7Oi.cjs';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Accounts domain — individual user accounts per sdk-surface.md §1a.
|
|
@@ -111,7 +110,7 @@ type AccountsClient = {
|
|
|
111
110
|
* on `create` only; subsequent reads omit it.
|
|
112
111
|
*/
|
|
113
112
|
|
|
114
|
-
type ApiKeysCreateInput =
|
|
113
|
+
type ApiKeysCreateInput = CreateApiKeyRequest & {
|
|
115
114
|
readonly organizationId: OrganizationId;
|
|
116
115
|
};
|
|
117
116
|
type ApiKeysRetrieveInput = {
|
|
@@ -236,7 +235,7 @@ type AuthClient = {
|
|
|
236
235
|
* bank_statement | tax_form`.
|
|
237
236
|
*/
|
|
238
237
|
|
|
239
|
-
type DocumentsCreateInput =
|
|
238
|
+
type DocumentsCreateInput = CreateDocumentRequest;
|
|
240
239
|
type DocumentsListInput = {
|
|
241
240
|
readonly limit?: number;
|
|
242
241
|
readonly cursor?: string;
|
|
@@ -589,7 +588,7 @@ type OrgWithdrawalsClient = {
|
|
|
589
588
|
* variant at call time and require `organizationId` explicitly.
|
|
590
589
|
*/
|
|
591
590
|
|
|
592
|
-
type WebhookEndpointsCreateInput =
|
|
591
|
+
type WebhookEndpointsCreateInput = CreateWebhookEndpointRequest & {
|
|
593
592
|
readonly organizationId: OrganizationId;
|
|
594
593
|
};
|
|
595
594
|
type WebhookEndpointsRetrieveInput = {
|
|
@@ -911,6 +910,7 @@ type TransportRuntime = {
|
|
|
911
910
|
*/
|
|
912
911
|
type HttpTransport = {
|
|
913
912
|
readonly fetch: (path: string, init?: RequestInit) => Promise<Response>;
|
|
913
|
+
readonly ensureRuntime: () => Promise<TransportRuntime>;
|
|
914
914
|
readonly authBaseUrl: string;
|
|
915
915
|
readonly convexUrl: string;
|
|
916
916
|
readonly getState: () => TransportState;
|
|
@@ -924,8 +924,11 @@ type HttpTransport = {
|
|
|
924
924
|
/**
|
|
925
925
|
* Build an `HttpTransport` from a `BrowserCapxulConfig`.
|
|
926
926
|
*
|
|
927
|
-
* Throws `
|
|
928
|
-
*
|
|
927
|
+
* Throws `CapxulError<"INVALID_INPUT">` with
|
|
928
|
+
* `details.source === "sdk-config"` when local transport config is
|
|
929
|
+
* malformed. Backend bootstrap failures also throw `CapxulError`, but
|
|
930
|
+
* carry `details.source === "backend-bootstrap"` so consumers can
|
|
931
|
+
* distinguish setup mistakes from server-side bootstrap refusals.
|
|
929
932
|
*/
|
|
930
933
|
declare function makeHttpTransport(config: BrowserCapxulConfig): HttpTransport;
|
|
931
934
|
|
|
@@ -1055,7 +1058,7 @@ type TokenTransfersClient = {
|
|
|
1055
1058
|
* in the create payload rather than nesting the route under the owner.
|
|
1056
1059
|
*/
|
|
1057
1060
|
|
|
1058
|
-
type VirtualAccountsCreateInput =
|
|
1061
|
+
type VirtualAccountsCreateInput = CreateVirtualAccountRequest;
|
|
1059
1062
|
type VirtualAccountsListInput = {
|
|
1060
1063
|
readonly limit?: number;
|
|
1061
1064
|
readonly cursor?: string;
|
|
@@ -1080,7 +1083,7 @@ type VirtualAccountsClient = {
|
|
|
1080
1083
|
* Uses Pattern C (ownerKind in body).
|
|
1081
1084
|
*/
|
|
1082
1085
|
|
|
1083
|
-
type VirtualCardsCreateInput =
|
|
1086
|
+
type VirtualCardsCreateInput = CreateVirtualCardRequest;
|
|
1084
1087
|
type VirtualCardsListInput = {
|
|
1085
1088
|
readonly limit?: number;
|
|
1086
1089
|
readonly cursor?: string;
|
package/dist/client.cjs
CHANGED
|
@@ -579,7 +579,12 @@ var Errors = {
|
|
|
579
579
|
"Idempotency key was already used for a different request",
|
|
580
580
|
{ details }
|
|
581
581
|
),
|
|
582
|
-
emailDeliveryFailed: (detail) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}
|
|
582
|
+
emailDeliveryFailed: (detail, details) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`, {
|
|
583
|
+
details
|
|
584
|
+
}),
|
|
585
|
+
rateLimited: (details) => new CapxulError2("RATE_LIMITED", "Request was rate limited", {
|
|
586
|
+
details: { ...details }
|
|
587
|
+
}),
|
|
583
588
|
internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`),
|
|
584
589
|
/**
|
|
585
590
|
* Verification gate. Surfaced when a request hits a verification
|
|
@@ -652,13 +657,13 @@ function createLifecycle(initial) {
|
|
|
652
657
|
}
|
|
653
658
|
function makeBuildTimeUrlsTransport(config) {
|
|
654
659
|
if (!config.authBaseUrl || config.authBaseUrl.trim().length === 0) {
|
|
655
|
-
throw
|
|
660
|
+
throw invalidConfigError(
|
|
656
661
|
"authBaseUrl",
|
|
657
662
|
"build-time-urls transport requires a non-empty authBaseUrl."
|
|
658
663
|
);
|
|
659
664
|
}
|
|
660
665
|
if (!config.convexUrl || config.convexUrl.trim().length === 0) {
|
|
661
|
-
throw
|
|
666
|
+
throw invalidConfigError(
|
|
662
667
|
"convexUrl",
|
|
663
668
|
"build-time-urls transport requires a non-empty convexUrl."
|
|
664
669
|
);
|
|
@@ -672,6 +677,7 @@ function makeBuildTimeUrlsTransport(config) {
|
|
|
672
677
|
return {
|
|
673
678
|
authBaseUrl,
|
|
674
679
|
convexUrl,
|
|
680
|
+
ensureRuntime: async () => runtime,
|
|
675
681
|
fetch: (path, init) => fetchImpl(resolveUrl(authBaseUrl, path), init),
|
|
676
682
|
getState: lifecycle.getState,
|
|
677
683
|
subscribe: lifecycle.subscribe,
|
|
@@ -687,14 +693,15 @@ function makeBuildTimeUrlsTransport(config) {
|
|
|
687
693
|
};
|
|
688
694
|
}
|
|
689
695
|
function makePublishableKeyTransport(config) {
|
|
690
|
-
|
|
691
|
-
|
|
696
|
+
const publishableKey = config.publishableKey?.trim();
|
|
697
|
+
if (!publishableKey) {
|
|
698
|
+
throw invalidConfigError(
|
|
692
699
|
"publishableKey",
|
|
693
700
|
"publishable-key transport requires a non-empty publishableKey."
|
|
694
701
|
);
|
|
695
702
|
}
|
|
696
703
|
const fetchImpl = config.fetchImpl ?? globalThis.fetch;
|
|
697
|
-
const bootstrapUrl =
|
|
704
|
+
const bootstrapUrl = normalizeBootstrapUrl(
|
|
698
705
|
config.bootstrapUrl ?? `${CAPXUL_API_BASE_URL}/v1/client/bootstrap`
|
|
699
706
|
);
|
|
700
707
|
let authBaseUrl = "";
|
|
@@ -709,23 +716,20 @@ function makePublishableKeyTransport(config) {
|
|
|
709
716
|
const response = await fetchImpl(bootstrapUrl, {
|
|
710
717
|
method: "POST",
|
|
711
718
|
headers: { "content-type": "application/json" },
|
|
712
|
-
body: JSON.stringify({ publishableKey
|
|
719
|
+
body: JSON.stringify({ publishableKey })
|
|
713
720
|
});
|
|
714
721
|
if (!response.ok) {
|
|
715
|
-
throw
|
|
716
|
-
"publishableKey",
|
|
717
|
-
`${bootstrapUrl} failed with HTTP ${response.status}.`
|
|
718
|
-
);
|
|
722
|
+
throw await bootstrapResponseError(response, bootstrapUrl);
|
|
719
723
|
}
|
|
720
|
-
const body = await response
|
|
724
|
+
const body = await readBootstrapSuccessBody(response);
|
|
721
725
|
if (typeof body.authBaseUrl !== "string" || body.authBaseUrl.trim().length === 0) {
|
|
722
|
-
throw
|
|
726
|
+
throw bootstrapContractError(
|
|
723
727
|
"authBaseUrl",
|
|
724
728
|
"/v1/client/bootstrap returned no authBaseUrl."
|
|
725
729
|
);
|
|
726
730
|
}
|
|
727
731
|
if (typeof body.convexUrl !== "string" || body.convexUrl.trim().length === 0) {
|
|
728
|
-
throw
|
|
732
|
+
throw bootstrapContractError(
|
|
729
733
|
"convexUrl",
|
|
730
734
|
"/v1/client/bootstrap returned no convexUrl."
|
|
731
735
|
);
|
|
@@ -737,16 +741,13 @@ function makePublishableKeyTransport(config) {
|
|
|
737
741
|
return runtime;
|
|
738
742
|
})();
|
|
739
743
|
bootstrapPromise = attempt.catch((err) => {
|
|
744
|
+
const error = normalizeBootstrapThrownError(err);
|
|
740
745
|
bootstrapPromise = null;
|
|
741
746
|
lifecycle.setState({
|
|
742
747
|
status: "error",
|
|
743
|
-
error
|
|
744
|
-
code: "UNKNOWN",
|
|
745
|
-
message: "Bootstrap failed without a typed CapxulError.",
|
|
746
|
-
cause: err
|
|
747
|
-
})
|
|
748
|
+
error
|
|
748
749
|
});
|
|
749
|
-
throw
|
|
750
|
+
throw error;
|
|
750
751
|
});
|
|
751
752
|
return await bootstrapPromise;
|
|
752
753
|
}
|
|
@@ -757,6 +758,7 @@ function makePublishableKeyTransport(config) {
|
|
|
757
758
|
get convexUrl() {
|
|
758
759
|
return convexUrl;
|
|
759
760
|
},
|
|
761
|
+
ensureRuntime: ensureBootstrap,
|
|
760
762
|
fetch: async (path, init) => {
|
|
761
763
|
const resolved = await ensureBootstrap();
|
|
762
764
|
return await fetchImpl(resolveUrl(resolved.authBaseUrl, path), init);
|
|
@@ -767,7 +769,7 @@ function makePublishableKeyTransport(config) {
|
|
|
767
769
|
markAuthenticated: ({ dataClient: nextDataClient }) => {
|
|
768
770
|
const current = lifecycle.getState();
|
|
769
771
|
if (current.status !== "ready" && current.status !== "authenticated") {
|
|
770
|
-
throw
|
|
772
|
+
throw internalTransportError(
|
|
771
773
|
`markAuthenticated() called from status="${current.status}". Expected "ready" or "authenticated".`
|
|
772
774
|
);
|
|
773
775
|
}
|
|
@@ -789,6 +791,24 @@ function makePublishableKeyTransport(config) {
|
|
|
789
791
|
function stripTrailingSlash(url) {
|
|
790
792
|
return url.replace(/\/+$/, "");
|
|
791
793
|
}
|
|
794
|
+
function normalizeBootstrapUrl(url) {
|
|
795
|
+
const normalized = stripTrailingSlash(url.trim());
|
|
796
|
+
if (!isAbsoluteHttpUrl(normalized)) {
|
|
797
|
+
throw invalidConfigError(
|
|
798
|
+
"bootstrapUrl",
|
|
799
|
+
"publishable-key transport requires an absolute http(s) bootstrapUrl."
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
return normalized;
|
|
803
|
+
}
|
|
804
|
+
function isAbsoluteHttpUrl(url) {
|
|
805
|
+
try {
|
|
806
|
+
const parsed = new URL(url);
|
|
807
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
808
|
+
} catch {
|
|
809
|
+
return false;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
792
812
|
function resolveUrl(authBaseUrl, path) {
|
|
793
813
|
if (path.startsWith("http://") || path.startsWith("https://")) {
|
|
794
814
|
return path;
|
|
@@ -796,10 +816,142 @@ function resolveUrl(authBaseUrl, path) {
|
|
|
796
816
|
return `${authBaseUrl}${path}`;
|
|
797
817
|
}
|
|
798
818
|
function assertNever(value) {
|
|
799
|
-
throw
|
|
819
|
+
throw internalTransportError(
|
|
800
820
|
`Unhandled BrowserCapxulConfig.mode: ${String(value.mode)}.`
|
|
801
821
|
);
|
|
802
822
|
}
|
|
823
|
+
function invalidConfigError(field, reason) {
|
|
824
|
+
return new CapxulError({
|
|
825
|
+
code: "INVALID_INPUT",
|
|
826
|
+
message: `Invalid ${field}: ${reason}`,
|
|
827
|
+
details: { source: "sdk-config", field, reason }
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
function bootstrapContractError(field, message) {
|
|
831
|
+
return new CapxulError({
|
|
832
|
+
code: "INVALID_INPUT",
|
|
833
|
+
message,
|
|
834
|
+
details: {
|
|
835
|
+
source: "backend-bootstrap",
|
|
836
|
+
phase: "publishable-key-bootstrap",
|
|
837
|
+
field,
|
|
838
|
+
reason: message
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
function internalTransportError(reason) {
|
|
843
|
+
return new CapxulError({
|
|
844
|
+
code: "INTERNAL_ERROR",
|
|
845
|
+
message: `Internal error: ${reason}`,
|
|
846
|
+
details: { source: "sdk-transport", reason }
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
async function bootstrapResponseError(response, bootstrapUrl) {
|
|
850
|
+
const envelope = await readBootstrapErrorEnvelope(response);
|
|
851
|
+
const wireCode = readNonEmptyString(envelope?.error?.code);
|
|
852
|
+
const normalized = normalizeBootstrapErrorCode(wireCode);
|
|
853
|
+
const message = readNonEmptyString(envelope?.error?.message) ?? `${bootstrapUrl} failed with HTTP ${response.status}.`;
|
|
854
|
+
const backendDetails = readRecord(envelope?.error?.details);
|
|
855
|
+
return new CapxulError({
|
|
856
|
+
code: normalized.code,
|
|
857
|
+
message,
|
|
858
|
+
details: {
|
|
859
|
+
...backendDetails,
|
|
860
|
+
source: "backend-bootstrap",
|
|
861
|
+
phase: "publishable-key-bootstrap",
|
|
862
|
+
httpStatus: response.status,
|
|
863
|
+
...normalized.wireCode ? { wireCode: normalized.wireCode } : {}
|
|
864
|
+
},
|
|
865
|
+
operationId: readNonEmptyString(envelope?.error?.operationId),
|
|
866
|
+
correlationId: readNonEmptyString(envelope?.error?.correlationId),
|
|
867
|
+
retryable: typeof envelope?.error?.retryable === "boolean" ? envelope.error.retryable : void 0
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
async function readBootstrapErrorEnvelope(response) {
|
|
871
|
+
try {
|
|
872
|
+
const parsed = await response.json();
|
|
873
|
+
return typeof parsed === "object" && parsed !== null ? parsed : null;
|
|
874
|
+
} catch {
|
|
875
|
+
return null;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
async function readBootstrapSuccessBody(response) {
|
|
879
|
+
try {
|
|
880
|
+
const parsed = await response.json();
|
|
881
|
+
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
882
|
+
} catch {
|
|
883
|
+
throw bootstrapContractError(
|
|
884
|
+
"body",
|
|
885
|
+
"/v1/client/bootstrap returned invalid JSON."
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
function normalizeBootstrapThrownError(error) {
|
|
890
|
+
if (error instanceof CapxulError) return error;
|
|
891
|
+
return new CapxulError({
|
|
892
|
+
code: "NETWORK_ERROR",
|
|
893
|
+
message: "Publishable-key bootstrap network failure.",
|
|
894
|
+
cause: error,
|
|
895
|
+
details: {
|
|
896
|
+
source: "bootstrap-network",
|
|
897
|
+
phase: "publishable-key-bootstrap"
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
function normalizeBootstrapErrorCode(wireCode) {
|
|
902
|
+
if (wireCode === "INTERNAL_SERVER_ERROR") {
|
|
903
|
+
return { code: "INTERNAL_ERROR", wireCode };
|
|
904
|
+
}
|
|
905
|
+
if (wireCode && isCapxulErrorCode(wireCode)) {
|
|
906
|
+
return { code: wireCode };
|
|
907
|
+
}
|
|
908
|
+
return wireCode ? { code: "UNKNOWN", wireCode } : { code: "UNKNOWN" };
|
|
909
|
+
}
|
|
910
|
+
var CAPXUL_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
911
|
+
"NOT_AUTHENTICATED",
|
|
912
|
+
"EMAIL_DELIVERY_FAILED",
|
|
913
|
+
"PROFILE_NOT_FOUND",
|
|
914
|
+
"SMART_ACCOUNT_MISSING",
|
|
915
|
+
"PLAYER_NOT_FOUND",
|
|
916
|
+
"ACCOUNT_NOT_FOUND",
|
|
917
|
+
"PROVIDER_ERROR",
|
|
918
|
+
"INVALID_INPUT",
|
|
919
|
+
"ENV_MISSING",
|
|
920
|
+
"NOT_IMPLEMENTED",
|
|
921
|
+
"VERIFICATION_REQUIRED",
|
|
922
|
+
"INSUFFICIENT_BALANCE",
|
|
923
|
+
"INVALID_RECIPIENT",
|
|
924
|
+
"TRANSACTION_FAILED",
|
|
925
|
+
"RATE_LIMITED",
|
|
926
|
+
"NETWORK_ERROR",
|
|
927
|
+
"UNKNOWN",
|
|
928
|
+
"PERMISSION_DENIED",
|
|
929
|
+
"API_KEY_INVALID",
|
|
930
|
+
"API_KEY_EXPIRED",
|
|
931
|
+
"IDEMPOTENCY_CONFLICT",
|
|
932
|
+
"NOT_FOUND",
|
|
933
|
+
"OPERATION_CANCELED",
|
|
934
|
+
"OPERATION_TIMEOUT",
|
|
935
|
+
"ACTION_REQUIRED",
|
|
936
|
+
"KYC_REQUIRED",
|
|
937
|
+
"POLICY_DENIED",
|
|
938
|
+
"SAFE_NOT_READY",
|
|
939
|
+
"PROVIDER_UNAVAILABLE",
|
|
940
|
+
"PROVIDER_REJECTED",
|
|
941
|
+
"RECONCILIATION_FAILED",
|
|
942
|
+
"INTERNAL_ERROR",
|
|
943
|
+
"QUOTE_EXPIRED",
|
|
944
|
+
"QUOTE_NOT_FOUND"
|
|
945
|
+
]);
|
|
946
|
+
function isCapxulErrorCode(value) {
|
|
947
|
+
return CAPXUL_ERROR_CODES.has(value);
|
|
948
|
+
}
|
|
949
|
+
function readRecord(value) {
|
|
950
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
951
|
+
}
|
|
952
|
+
function readNonEmptyString(value) {
|
|
953
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
954
|
+
}
|
|
803
955
|
|
|
804
956
|
// src/core/auth.ts
|
|
805
957
|
function createAuthClient(config = {}) {
|
|
@@ -854,13 +1006,16 @@ function createAuthClient(config = {}) {
|
|
|
854
1006
|
email: signIn.user.email,
|
|
855
1007
|
token: signIn.token,
|
|
856
1008
|
convexJwt,
|
|
857
|
-
expiresAt: new Date(
|
|
1009
|
+
expiresAt: new Date(
|
|
1010
|
+
Date.now() + 30 * 24 * 60 * 60 * 1e3
|
|
1011
|
+
).toISOString()
|
|
858
1012
|
};
|
|
859
1013
|
sessionStore.set(session);
|
|
860
1014
|
if (config.auth?.createDataClient) {
|
|
861
1015
|
try {
|
|
862
1016
|
dataClient = await config.auth.createDataClient(session);
|
|
863
1017
|
mutableConfig(config).data = dataClient;
|
|
1018
|
+
transport.markAuthenticated({ dataClient });
|
|
864
1019
|
} catch (cause) {
|
|
865
1020
|
return [
|
|
866
1021
|
new CapxulError({
|
|
@@ -879,6 +1034,8 @@ function createAuthClient(config = {}) {
|
|
|
879
1034
|
sessionStore.clear();
|
|
880
1035
|
dataClient = null;
|
|
881
1036
|
mutableConfig(config).data = void 0;
|
|
1037
|
+
const transport = getTransport();
|
|
1038
|
+
transport?.clearAuth();
|
|
882
1039
|
return [null, void 0];
|
|
883
1040
|
},
|
|
884
1041
|
serviceTokenMint: async () => stub("auth.serviceTokenMint"),
|
|
@@ -932,16 +1089,19 @@ async function postBetterAuth(transport, path, body, code, signal) {
|
|
|
932
1089
|
body: JSON.stringify(body),
|
|
933
1090
|
signal
|
|
934
1091
|
});
|
|
1092
|
+
const text = await response.text();
|
|
935
1093
|
if (!response.ok) {
|
|
1094
|
+
const parsedError = parseBetterAuthError(text);
|
|
936
1095
|
return [
|
|
937
1096
|
new CapxulError({
|
|
938
|
-
code,
|
|
939
|
-
message: `BetterAuth ${path} failed with HTTP ${response.status}
|
|
1097
|
+
code: parsedError.code ?? code,
|
|
1098
|
+
message: parsedError.message ?? `BetterAuth ${path} failed with HTTP ${response.status}.`,
|
|
1099
|
+
details: parsedError.details,
|
|
1100
|
+
retryable: parsedError.retryable
|
|
940
1101
|
}),
|
|
941
1102
|
null
|
|
942
1103
|
];
|
|
943
1104
|
}
|
|
944
|
-
const text = await response.text();
|
|
945
1105
|
return [null, text ? JSON.parse(text) : void 0];
|
|
946
1106
|
} catch (cause) {
|
|
947
1107
|
return [
|
|
@@ -954,6 +1114,36 @@ async function postBetterAuth(transport, path, body, code, signal) {
|
|
|
954
1114
|
];
|
|
955
1115
|
}
|
|
956
1116
|
}
|
|
1117
|
+
function parseBetterAuthError(text) {
|
|
1118
|
+
if (!text.trim()) {
|
|
1119
|
+
return {};
|
|
1120
|
+
}
|
|
1121
|
+
try {
|
|
1122
|
+
const body = JSON.parse(text);
|
|
1123
|
+
if (!body || typeof body !== "object") {
|
|
1124
|
+
return {};
|
|
1125
|
+
}
|
|
1126
|
+
const record = body;
|
|
1127
|
+
const nested = record.error && typeof record.error === "object" ? record.error : record;
|
|
1128
|
+
const code = typeof nested.code === "string" ? nested.code : void 0;
|
|
1129
|
+
const message = typeof nested.message === "string" ? nested.message : void 0;
|
|
1130
|
+
const details = nested.details && typeof nested.details === "object" ? nested.details : void 0;
|
|
1131
|
+
const correlationId = typeof nested.correlationId === "string" ? nested.correlationId : void 0;
|
|
1132
|
+
const retryable = typeof nested.retryable === "boolean" ? nested.retryable : void 0;
|
|
1133
|
+
return {
|
|
1134
|
+
code: isCapxulErrorCode2(code) ? code : void 0,
|
|
1135
|
+
message,
|
|
1136
|
+
details,
|
|
1137
|
+
correlationId,
|
|
1138
|
+
retryable
|
|
1139
|
+
};
|
|
1140
|
+
} catch {
|
|
1141
|
+
return {};
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
function isCapxulErrorCode2(code) {
|
|
1145
|
+
return code === "NOT_AUTHENTICATED" || code === "EMAIL_DELIVERY_FAILED" || code === "INVALID_INPUT" || code === "RATE_LIMITED" || code === "NETWORK_ERROR" || code === "API_KEY_INVALID" || code === "API_KEY_EXPIRED";
|
|
1146
|
+
}
|
|
957
1147
|
async function exchangeConvexToken(transport, config, token, signal) {
|
|
958
1148
|
const path = config.auth?.convexTokenUrl ?? "/convex/token";
|
|
959
1149
|
try {
|
package/dist/client.d.cts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import 'viem';
|
|
2
2
|
import 'xstate';
|
|
3
|
-
export { f as CapxulAuthConfig, C as CapxulClient, g as CapxulConfig, h as CapxulDataClient, i as CapxulFlowFactories, j as CapxulSigningConfig, K as createCapxulClient } from './client-
|
|
3
|
+
export { f as CapxulAuthConfig, C as CapxulClient, g as CapxulConfig, h as CapxulDataClient, i as CapxulFlowFactories, j as CapxulSigningConfig, K as createCapxulClient } from './client-DDAVWtzJ.cjs';
|
|
4
4
|
import './next-action-DkrwXYay.cjs';
|
|
5
5
|
import './errors-GgKrSUKp.cjs';
|
|
6
|
-
import './types-
|
|
7
|
-
import '@repo/api-contract/gen/types';
|
|
6
|
+
import './types-hfcOE7Oi.cjs';
|
package/dist/client.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import 'viem';
|
|
2
2
|
import 'xstate';
|
|
3
|
-
export { f as CapxulAuthConfig, C as CapxulClient, g as CapxulConfig, h as CapxulDataClient, i as CapxulFlowFactories, j as CapxulSigningConfig, K as createCapxulClient } from './client-
|
|
3
|
+
export { f as CapxulAuthConfig, C as CapxulClient, g as CapxulConfig, h as CapxulDataClient, i as CapxulFlowFactories, j as CapxulSigningConfig, K as createCapxulClient } from './client-ByzDfG98.js';
|
|
4
4
|
import './next-action-DkrwXYay.js';
|
|
5
5
|
import './errors-QHD5Tlok.js';
|
|
6
|
-
import './types-
|
|
7
|
-
import '@repo/api-contract/gen/types';
|
|
6
|
+
import './types-PM4AQRLP.js';
|