@capxul/sdk-react 0.1.0-alpha.4 → 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 +34 -0
- package/README.md +21 -1
- package/dist/index.cjs +169 -11
- package/dist/index.d.cts +54 -8
- package/dist/index.d.ts +54 -8
- package/dist/index.js +170 -12
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# @capxul/sdk-react
|
|
2
2
|
|
|
3
|
+
## 0.1.0-alpha.8
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3af04a9: Public `<CapxulProvider>` now wires authenticated Convex reads end-to-end (#487, closes #484).
|
|
8
|
+
|
|
9
|
+
The `build-time-urls` arm of `BrowserCapxulConfig` now causes the provider to internally instantiate a `ConvexReactClient`, wrap it as a `CapxulDataClient`, and feed it as `config.data` into `createCapxulClient`. The existing `auth.createDataClient` callback is wired to the same singleton so a `verifyOtp` call refreshes the data client's auth header without churning the WebSocket. The provider also wraps `client.auth.signOut` so the data client survives sign-out — post-signout reads return typed backend `NOT_AUTHENTICATED` instead of the SDK's `NOT_IMPLEMENTED` stub (closes the #474 friction as a side-effect).
|
|
10
|
+
|
|
11
|
+
New optional `sessionStore?: AuthSessionStore` prop lets browser apps opt into `localStorage`-backed persistence and Node consumers (CLIs, e2e harnesses) opt into file-backed persistence. Defaults to in-memory.
|
|
12
|
+
|
|
13
|
+
This closes the architectural gap that made the React hook surface (`useMe`, `useAccount`, `useSafe`, ...) impossible to exercise end-to-end through the public provider — previously `config.data` was never populated, so every authenticated read short-circuited to `NOT_IMPLEMENTED`. The Ink reference CLI (`apps/reference-cli/`) is rebuilt on top of the public provider as proof: 17/17 agent-driver assertions pass against live alpha-3 Convex, including a dev-OTP authenticated round-trip that mints a real session via `AUTH_DEV_OTP=00000` and exercises three distinct hook end-states (`NOT_AUTHENTICATED`, `PROFILE_NOT_FOUND`, post-signout `NOT_AUTHENTICATED`).
|
|
14
|
+
|
|
15
|
+
- 57203a4: Withdrawals v1 W2 (#465) — public surface tightening + org-scope create
|
|
16
|
+
- `WithdrawalsCreateInput.destination` no longer accepts `kind`. The
|
|
17
|
+
backend now resolves the `external_account` row by FK and infers
|
|
18
|
+
the kind + rail server-side. Anything that doesn't route to
|
|
19
|
+
`chain_wallet` (or is chain_wallet but non-EVM in slice 1) returns
|
|
20
|
+
`VERIFICATION_REQUIRED` with `details.rail` + `details.currentKind`.
|
|
21
|
+
- `organizations.withdrawals.create` is now a real mutation (no
|
|
22
|
+
longer a `NOT_IMPLEMENTED` stub). Returns the `processing` row
|
|
23
|
+
only — Safe + Zodiac submission orchestration ships in W3+.
|
|
24
|
+
- `Errors.verificationRequired({ rail, currentKind })` factory
|
|
25
|
+
added; the `VERIFICATION_REQUIRED` code now broadens to cover
|
|
26
|
+
both KYC tier gates and unsupported withdrawal rails.
|
|
27
|
+
|
|
28
|
+
**Migration:** Remove `destination.kind` from any
|
|
29
|
+
`capxul.withdrawals.create({ destination: { kind, externalAccountId } })`
|
|
30
|
+
call sites. Pass only `externalAccountId`.
|
|
31
|
+
|
|
32
|
+
### Patch Changes
|
|
33
|
+
|
|
34
|
+
- Updated dependencies [57203a4]
|
|
35
|
+
- @capxul/sdk@0.1.0-alpha.8
|
|
36
|
+
|
|
3
37
|
## 0.1.0-alpha.4
|
|
4
38
|
|
|
5
39
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -40,7 +40,12 @@ import { CapxulProvider } from "@capxul/sdk-react";
|
|
|
40
40
|
|
|
41
41
|
export default function RootLayout({ children }) {
|
|
42
42
|
return (
|
|
43
|
-
<CapxulProvider
|
|
43
|
+
<CapxulProvider
|
|
44
|
+
config={{
|
|
45
|
+
mode: "publishable-key",
|
|
46
|
+
publishableKey: process.env.NEXT_PUBLIC_CAPXUL_PUBLISHABLE_KEY!,
|
|
47
|
+
}}
|
|
48
|
+
>
|
|
44
49
|
{children}
|
|
45
50
|
</CapxulProvider>
|
|
46
51
|
);
|
|
@@ -119,6 +124,21 @@ subscription against the transport singleton — only the components
|
|
|
119
124
|
that actually call it re-render when state changes. The provider
|
|
120
125
|
itself never re-renders.
|
|
121
126
|
|
|
127
|
+
## Publishable-key proof status
|
|
128
|
+
|
|
129
|
+
The publishable-key path is runtime-proven where the repo can run it
|
|
130
|
+
without secrets:
|
|
131
|
+
|
|
132
|
+
| Surface | Runtime proof | Expected safe signal |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| Provider + `useMe()` | `corepack pnpm --filter @capxul/sdk-react check-types` plus the headless proof tests under `packages/sdk-react/ops/proof` | provider reaches `useCapxulStatus().status === "ready"` after one bootstrap request |
|
|
135
|
+
| Reference CLI mock | `corepack pnpm --filter @capxul/reference-cli build && node apps/reference-cli/dist/cli.js bootstrap probe --mock --json` | `ok:true`, `mode:"publishable-key"`, `bootstrapRequests:1`, `authRequests:1`, `keyLengthClass:"provided"` |
|
|
136
|
+
| Reference CLI live | same command without `--mock`, with `CAPXUL_REF_PUBLISHABLE_KEY` and optional `CAPXUL_REF_BOOTSTRAP_URL` set locally | success only when the key/origin/runtime are valid; otherwise sanitized SDK error JSON |
|
|
137
|
+
|
|
138
|
+
Do not paste or commit publishable keys, session tokens, Convex JWTs,
|
|
139
|
+
cookies, or provider payloads. Proof output reports only
|
|
140
|
+
`keyLengthClass`.
|
|
141
|
+
|
|
122
142
|
## Hooks catalogue
|
|
123
143
|
|
|
124
144
|
| Surface | Hook |
|
package/dist/index.cjs
CHANGED
|
@@ -5,6 +5,7 @@ var react = require('react');
|
|
|
5
5
|
var sdk = require('@capxul/sdk');
|
|
6
6
|
var reactQuery = require('@tanstack/react-query');
|
|
7
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
var react$2 = require('convex/react');
|
|
8
9
|
var errors = require('@capxul/sdk/errors');
|
|
9
10
|
var react$1 = require('@xstate/react');
|
|
10
11
|
var viem = require('viem');
|
|
@@ -109,8 +110,35 @@ var Errors = {
|
|
|
109
110
|
"Idempotency key was already used for a different request",
|
|
110
111
|
{ details }
|
|
111
112
|
),
|
|
112
|
-
emailDeliveryFailed: (detail) => new CapxulError("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}
|
|
113
|
-
|
|
113
|
+
emailDeliveryFailed: (detail, details) => new CapxulError("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`, {
|
|
114
|
+
details
|
|
115
|
+
}),
|
|
116
|
+
rateLimited: (details) => new CapxulError("RATE_LIMITED", "Request was rate limited", {
|
|
117
|
+
details: { ...details }
|
|
118
|
+
}),
|
|
119
|
+
internalError: (reason) => new CapxulError("INTERNAL_ERROR", `Internal error: ${reason}`),
|
|
120
|
+
/**
|
|
121
|
+
* Verification gate. Surfaced when a request hits a verification
|
|
122
|
+
* boundary the actor cannot cross under their current state. Two
|
|
123
|
+
* variants share this code:
|
|
124
|
+
*
|
|
125
|
+
* - Rail gate (Withdrawals v1 W2, #465): the resolved
|
|
126
|
+
* `external_account.kind` routes to a withdrawal rail (e.g.
|
|
127
|
+
* `fiat_offramp`, `card_payout`) that is not yet supported. Carries
|
|
128
|
+
* `details.rail` + `details.currentKind`.
|
|
129
|
+
* - KYC tier gate (legacy / future): the actor's KYC tier is below
|
|
130
|
+
* the required tier. Carries `details.requiredTier`.
|
|
131
|
+
*
|
|
132
|
+
* Code is shared because both expose the same UX shape ("you cannot
|
|
133
|
+
* proceed until verification advances"); the `details.*` keys
|
|
134
|
+
* differentiate the route.
|
|
135
|
+
*/
|
|
136
|
+
verificationRequired: (details) => {
|
|
137
|
+
const message = "rail" in details ? `Withdrawal rail "${details.rail}" (kind=${details.currentKind}) is not yet supported.` : `Verification tier ${details.requiredTier} is required.`;
|
|
138
|
+
return new CapxulError("VERIFICATION_REQUIRED", message, {
|
|
139
|
+
details: { ...details }
|
|
140
|
+
});
|
|
141
|
+
}
|
|
114
142
|
};
|
|
115
143
|
|
|
116
144
|
// ../config/src/org-roles.ts
|
|
@@ -172,12 +200,87 @@ function assertModeRequiredFields(input) {
|
|
|
172
200
|
}
|
|
173
201
|
}
|
|
174
202
|
}
|
|
203
|
+
function createReactDataClient(convexUrl, sessionStore) {
|
|
204
|
+
const client = new react$2.ConvexReactClient(convexUrl);
|
|
205
|
+
const refreshAuth = () => {
|
|
206
|
+
const session = sessionStore.get();
|
|
207
|
+
const jwt = session?.convexJwt;
|
|
208
|
+
if (jwt) {
|
|
209
|
+
client.setAuth(() => Promise.resolve(jwt));
|
|
210
|
+
} else {
|
|
211
|
+
client.clearAuth();
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
refreshAuth();
|
|
215
|
+
return {
|
|
216
|
+
query: (name, args) => client.query(name, args),
|
|
217
|
+
mutation: (name, args) => client.mutation(name, args),
|
|
218
|
+
action: (name, args) => client.action(name, args),
|
|
219
|
+
refreshAuth,
|
|
220
|
+
close: () => {
|
|
221
|
+
void client.close();
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function createLazyReactDataClient(transport, sessionStore, createClient = createReactDataClient) {
|
|
226
|
+
let client = null;
|
|
227
|
+
let initializeClient = null;
|
|
228
|
+
let closed = false;
|
|
229
|
+
function closedError() {
|
|
230
|
+
return new Error("Capxul React data client is closed");
|
|
231
|
+
}
|
|
232
|
+
async function getClient() {
|
|
233
|
+
if (closed) throw closedError();
|
|
234
|
+
if (client) return client;
|
|
235
|
+
initializeClient ??= (async () => {
|
|
236
|
+
const runtime = await transport.ensureRuntime();
|
|
237
|
+
if (closed) throw closedError();
|
|
238
|
+
const nextClient = createClient(runtime.convexUrl, sessionStore);
|
|
239
|
+
if (closed) {
|
|
240
|
+
nextClient.close();
|
|
241
|
+
throw closedError();
|
|
242
|
+
}
|
|
243
|
+
client = nextClient;
|
|
244
|
+
return nextClient;
|
|
245
|
+
})().catch((error) => {
|
|
246
|
+
if (!closed) {
|
|
247
|
+
initializeClient = null;
|
|
248
|
+
}
|
|
249
|
+
throw error;
|
|
250
|
+
});
|
|
251
|
+
return initializeClient;
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
query: async (name, args) => (await getClient()).query(name, args),
|
|
255
|
+
mutation: async (name, args) => (await getClient()).mutation(name, args),
|
|
256
|
+
action: async (name, args) => (await getClient()).action?.(name, args),
|
|
257
|
+
refreshAuth: () => {
|
|
258
|
+
client?.refreshAuth();
|
|
259
|
+
},
|
|
260
|
+
close: () => {
|
|
261
|
+
closed = true;
|
|
262
|
+
client?.close();
|
|
263
|
+
client = null;
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
}
|
|
175
267
|
function CapxulProvider({
|
|
176
268
|
config,
|
|
269
|
+
sessionStore,
|
|
177
270
|
queryClient,
|
|
178
271
|
children
|
|
179
272
|
}) {
|
|
180
|
-
const
|
|
273
|
+
const defaultSessionStore = react.useMemo(() => createMemorySessionStore(), []);
|
|
274
|
+
const effectiveSessionStore = sessionStore ?? defaultSessionStore;
|
|
275
|
+
const wiring = react.useMemo(
|
|
276
|
+
() => buildWiring(config, effectiveSessionStore),
|
|
277
|
+
[config, effectiveSessionStore]
|
|
278
|
+
);
|
|
279
|
+
react.useEffect(() => {
|
|
280
|
+
return () => {
|
|
281
|
+
wiring.dataClient?.close();
|
|
282
|
+
};
|
|
283
|
+
}, [wiring]);
|
|
181
284
|
const defaultClient = react.useMemo(
|
|
182
285
|
() => new reactQuery.QueryClient({
|
|
183
286
|
defaultOptions: { queries: { staleTime: 3e4 } }
|
|
@@ -187,16 +290,58 @@ function CapxulProvider({
|
|
|
187
290
|
const effectiveClient = queryClient ?? defaultClient;
|
|
188
291
|
return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: effectiveClient, children: /* @__PURE__ */ jsxRuntime.jsx(CapxulTransportProvider, { transport: wiring.transport, children: /* @__PURE__ */ jsxRuntime.jsx(CapxulClientProvider, { client: wiring.client, children }) }) });
|
|
189
292
|
}
|
|
190
|
-
function buildWiring(config) {
|
|
293
|
+
function buildWiring(config, sessionStore) {
|
|
191
294
|
const validated = createCapxulConfig(config);
|
|
192
295
|
const transport = sdk.makeHttpTransport(validated);
|
|
296
|
+
const dataClient = validated.mode === "build-time-urls" ? createReactDataClient(validated.convexUrl, sessionStore) : createLazyReactDataClient(transport, sessionStore);
|
|
193
297
|
const sdkConfig = {
|
|
194
298
|
_transport: transport,
|
|
195
|
-
publishableKey: validated.mode === "publishable-key" ? validated.publishableKey : void 0
|
|
299
|
+
publishableKey: validated.mode === "publishable-key" ? validated.publishableKey : void 0,
|
|
300
|
+
data: dataClient ?? void 0,
|
|
301
|
+
auth: validated.mode === "build-time-urls" || validated.mode === "publishable-key" ? {
|
|
302
|
+
// The auth client builds the BetterAuth root URL from this
|
|
303
|
+
// value. Convex's `.cloud` URL is the wrong host (BetterAuth
|
|
304
|
+
// is mounted on the `.site` URL), but the build-time-urls
|
|
305
|
+
// transport already encodes the correct `authBaseUrl` via
|
|
306
|
+
// its discriminated union. We pass `convexUrl` here only so
|
|
307
|
+
// `core/auth.ts`'s `createTransportProvider` short-circuits
|
|
308
|
+
// to the externally-injected `_transport` cache slot.
|
|
309
|
+
baseUrl: transport.authBaseUrl,
|
|
310
|
+
sessionStore,
|
|
311
|
+
createDataClient: async (_session) => {
|
|
312
|
+
dataClient.refreshAuth();
|
|
313
|
+
return dataClient;
|
|
314
|
+
}
|
|
315
|
+
} : void 0
|
|
196
316
|
};
|
|
317
|
+
const client = sdk.createCapxulClient(sdkConfig);
|
|
318
|
+
if (dataClient) {
|
|
319
|
+
const originalSignOut = client.auth.signOut;
|
|
320
|
+
Object.assign(client.auth, {
|
|
321
|
+
signOut: async () => {
|
|
322
|
+
const result = await originalSignOut();
|
|
323
|
+
dataClient.refreshAuth();
|
|
324
|
+
sdkConfig.data = dataClient;
|
|
325
|
+
return result;
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}
|
|
197
329
|
return {
|
|
198
|
-
client
|
|
199
|
-
transport
|
|
330
|
+
client,
|
|
331
|
+
transport,
|
|
332
|
+
dataClient
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
function createMemorySessionStore() {
|
|
336
|
+
let current = null;
|
|
337
|
+
return {
|
|
338
|
+
get: () => current,
|
|
339
|
+
set: (session) => {
|
|
340
|
+
current = session;
|
|
341
|
+
},
|
|
342
|
+
clear: () => {
|
|
343
|
+
current = null;
|
|
344
|
+
}
|
|
200
345
|
};
|
|
201
346
|
}
|
|
202
347
|
function notImplementedQuery(hookName) {
|
|
@@ -318,8 +463,15 @@ function useKycProfile(_accountId) {
|
|
|
318
463
|
function useKybProfile(_organizationId) {
|
|
319
464
|
return notImplementedQuery("useKybProfile");
|
|
320
465
|
}
|
|
321
|
-
function useExternalAccount(
|
|
322
|
-
|
|
466
|
+
function useExternalAccount(args) {
|
|
467
|
+
const capxul = useCapxul();
|
|
468
|
+
return useSdkQuery(
|
|
469
|
+
() => args.ownerKind === "account" ? capxul.externalAccounts.retrieve(args.externalAccountId) : capxul.organizations.externalAccounts.retrieve({
|
|
470
|
+
organizationId: args.ownerId,
|
|
471
|
+
externalAccountId: args.externalAccountId
|
|
472
|
+
}),
|
|
473
|
+
[capxul, args.ownerKind, args.ownerId, args.externalAccountId]
|
|
474
|
+
);
|
|
323
475
|
}
|
|
324
476
|
function useSubAccount(_subAccountId) {
|
|
325
477
|
return notImplementedQuery("useSubAccount");
|
|
@@ -377,8 +529,14 @@ function useOrganizations() {
|
|
|
377
529
|
function useMembers(_organizationId) {
|
|
378
530
|
return notImplementedQuery("useMembers");
|
|
379
531
|
}
|
|
380
|
-
function useExternalAccounts(
|
|
381
|
-
|
|
532
|
+
function useExternalAccounts(args) {
|
|
533
|
+
const capxul = useCapxul();
|
|
534
|
+
return useSdkQuery(
|
|
535
|
+
() => args.ownerKind === "account" ? capxul.accounts.externalAccounts.list({ accountId: args.ownerId }) : capxul.organizations.externalAccounts.list({
|
|
536
|
+
organizationId: args.ownerId
|
|
537
|
+
}),
|
|
538
|
+
[capxul, args.ownerKind, args.ownerId]
|
|
539
|
+
);
|
|
382
540
|
}
|
|
383
541
|
function useSubAccounts(_args) {
|
|
384
542
|
return notImplementedQuery("useSubAccounts");
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { HttpTransport, TransportState, BrowserCapxulConfig, AccountId, OrganizationId, ApiKeyId, BalanceLedgerEntryId, ExternalAccountId, MemberId, Account, ApiKey, BalanceLedgerEntry, DocumentId, Document, ExternalAccount, KybProfile, KycProfile, CapxulError as CapxulError$1, Member, OperationId, Operation, Organization, PaymentId, Payment, SafeId, Safe, SubAccountId, SubAccount, TokenTransfer, TransferId, Transfer, Treasury, VirtualAccountId, VirtualAccount, VirtualCardId, VirtualCard, WebhookEndpointId, WebhookEndpoint, WebhookEventId, WebhookEvent, WithdrawalId, Withdrawal, List, TokenTransfersListInput, TokenTransfersListPage, LocalPrivateKeySignerProvider } from '@capxul/sdk';
|
|
2
|
+
import { HttpTransport, TransportState, BrowserCapxulConfig, AuthSessionStore, AccountId, OrganizationId, ApiKeyId, BalanceLedgerEntryId, ExternalAccountId, MemberId, Account, ApiKey, BalanceLedgerEntry, DocumentId, Document, ExternalAccount, KybProfile, KycProfile, CapxulError as CapxulError$1, Member, OperationId, Operation, Organization, PaymentId, Payment, SafeId, Safe, SubAccountId, SubAccount, TokenTransfer, TransferId, Transfer, Treasury, VirtualAccountId, VirtualAccount, VirtualCardId, VirtualCard, WebhookEndpointId, WebhookEndpoint, WebhookEventId, WebhookEvent, WithdrawalId, Withdrawal, List, TokenTransfersListInput, TokenTransfersListPage, LocalPrivateKeySignerProvider } from '@capxul/sdk';
|
|
3
3
|
export { AuthFlowContext, AuthFlowEvent, BrowserCapxulConfig, OnboardingFlowContext, OnboardingFlowEvent, ProvisioningFlowContext, ProvisioningFlowEvent } from '@capxul/sdk';
|
|
4
4
|
import { QueryClient, UseQueryResult } from '@tanstack/react-query';
|
|
5
5
|
import { CapxulClient } from '@capxul/sdk/client';
|
|
@@ -58,16 +58,30 @@ declare function useCapxulStatus(): TransportState;
|
|
|
58
58
|
* `CapxulProvider` — public React provider for `@capxul/sdk-react`.
|
|
59
59
|
*
|
|
60
60
|
* Single-input contract: accepts ONLY `{ config: BrowserCapxulConfig,
|
|
61
|
-
* queryClient?, children }`. The browser config is the
|
|
62
|
-
* discriminated union from `@capxul/sdk` — `apiKey`,
|
|
63
|
-
* and other server-only fields are rejected at
|
|
64
|
-
* runtime via `createCapxulConfig`'s allow-list
|
|
61
|
+
* sessionStore?, queryClient?, children }`. The browser config is the
|
|
62
|
+
* secret-safe discriminated union from `@capxul/sdk` — `apiKey`,
|
|
63
|
+
* `data`, `signer`, and other server-only fields are rejected at
|
|
64
|
+
* compile-time AND at runtime via `createCapxulConfig`'s allow-list
|
|
65
|
+
* validator.
|
|
65
66
|
*
|
|
66
67
|
* The provider builds an `HttpTransport` synchronously and injects it
|
|
67
68
|
* into `createCapxulClient` via the internal `_transport` slot. The
|
|
68
69
|
* transport is exposed through `CapxulTransportContext` so
|
|
69
70
|
* `useCapxulStatus()` can subscribe to its lifecycle state machine.
|
|
70
71
|
*
|
|
72
|
+
* The provider also builds a `ConvexReactClient`-backed data client
|
|
73
|
+
* for the `build-time-urls` arm and feeds it as `config.data` so the
|
|
74
|
+
* SDK domain methods (`me.get`, `accounts.retrieve`, ...) and the
|
|
75
|
+
* React hooks built on top can run authenticated reads against live
|
|
76
|
+
* Convex. The auth lifecycle threads through the optional
|
|
77
|
+
* `sessionStore` prop: `verifyOtp` writes a session to the store
|
|
78
|
+
* (which the SDK auth client persists), and the provider's data
|
|
79
|
+
* client re-reads the JWT on demand via `refreshAuth()`. Without a
|
|
80
|
+
* session store the provider falls back to an in-memory one, which
|
|
81
|
+
* is fine for browser apps that hold the page until next reload but
|
|
82
|
+
* inadequate for CLI / Node consumers that need cross-process
|
|
83
|
+
* persistence — those pass a file-backed store via the prop.
|
|
84
|
+
*
|
|
71
85
|
* Tests and the e2e harness need the server-augmented `CapxulConfig`
|
|
72
86
|
* shape (with `data`, `signer`, `signing`). Those callers use
|
|
73
87
|
* `CapxulTestProvider` from `@capxul/sdk-react/proof` instead.
|
|
@@ -88,6 +102,17 @@ type CapxulProviderProps = {
|
|
|
88
102
|
* via `createCapxulConfig`'s allow-list validator.
|
|
89
103
|
*/
|
|
90
104
|
readonly config: BrowserCapxulConfig;
|
|
105
|
+
/**
|
|
106
|
+
* Auth session persistence adapter. The SDK auth client writes
|
|
107
|
+
* sessions here on `verifyOtp` and clears them on `signOut`. The
|
|
108
|
+
* provider's data client re-reads the JWT from this store on every
|
|
109
|
+
* `refreshAuth` call so reads carry the right auth header.
|
|
110
|
+
*
|
|
111
|
+
* Defaults to an in-memory store scoped to this provider. Pass a
|
|
112
|
+
* file-backed (Node CLI) or `localStorage`-backed (browser) store
|
|
113
|
+
* to persist sessions across process or page lifetimes.
|
|
114
|
+
*/
|
|
115
|
+
readonly sessionStore?: AuthSessionStore;
|
|
91
116
|
/**
|
|
92
117
|
* Optional TanStack Query `QueryClient`. Pass your app's existing
|
|
93
118
|
* client to share the cache across the SDK hooks and the host
|
|
@@ -97,7 +122,7 @@ type CapxulProviderProps = {
|
|
|
97
122
|
readonly queryClient?: QueryClient;
|
|
98
123
|
readonly children: ReactNode;
|
|
99
124
|
};
|
|
100
|
-
declare function CapxulProvider({ config, queryClient, children, }: CapxulProviderProps): ReactNode;
|
|
125
|
+
declare function CapxulProvider({ config, sessionStore, queryClient, children, }: CapxulProviderProps): ReactNode;
|
|
101
126
|
|
|
102
127
|
/**
|
|
103
128
|
* `QueryResult<T>` — the canonical three-state return shape for every
|
|
@@ -232,7 +257,18 @@ declare function useTreasury(_organizationId: OrganizationId): QueryResult<Treas
|
|
|
232
257
|
declare function useApiKey(_args: UseApiKeyArgs): QueryResult<ApiKey>;
|
|
233
258
|
declare function useKycProfile(_accountId: AccountId): QueryResult<KycProfile>;
|
|
234
259
|
declare function useKybProfile(_organizationId: OrganizationId): QueryResult<KybProfile>;
|
|
235
|
-
|
|
260
|
+
/**
|
|
261
|
+
* Withdrawals v1 W1 (#464) — wired through the SDK.
|
|
262
|
+
*
|
|
263
|
+
* Branches on `args.ownerKind` so `account` scope reads via the
|
|
264
|
+
* top-level `capxul.externalAccounts.retrieve(id)` (visibility-gated
|
|
265
|
+
* server-side by the caller's accountId), and `organization` scope
|
|
266
|
+
* reads via `capxul.organizations.externalAccounts.retrieve({ ... })`
|
|
267
|
+
* which adds an org-scope check for cross-org isolation. Both routes
|
|
268
|
+
* resolve to the same Convex query handler — the SDK ergonomics
|
|
269
|
+
* differ but the wire shape is identical.
|
|
270
|
+
*/
|
|
271
|
+
declare function useExternalAccount(args: UseExternalAccountArgs): QueryResult<ExternalAccount>;
|
|
236
272
|
declare function useSubAccount(_subAccountId: SubAccountId): QueryResult<SubAccount>;
|
|
237
273
|
declare function useVirtualAccount(_virtualAccountId: VirtualAccountId): QueryResult<VirtualAccount>;
|
|
238
274
|
declare function useVirtualCard(_virtualCardId: VirtualCardId): QueryResult<VirtualCard>;
|
|
@@ -344,7 +380,17 @@ type OrgDocumentsFilters = OrgScopedFilters & {
|
|
|
344
380
|
*/
|
|
345
381
|
declare function useOrganizations(): QueryResult<List<Organization>>;
|
|
346
382
|
declare function useMembers(_organizationId: OrganizationId): QueryResult<List<Member>>;
|
|
347
|
-
|
|
383
|
+
/**
|
|
384
|
+
* Withdrawals v1 W1 (#464) — wired through the SDK.
|
|
385
|
+
*
|
|
386
|
+
* Personal scope reads through `capxul.accounts.externalAccounts.list({
|
|
387
|
+
* accountId })` (Pattern A nested namespace); org scope reads through
|
|
388
|
+
* `capxul.organizations.externalAccounts.list({ organizationId })`.
|
|
389
|
+
* Both resolve to the same Convex query handler — the SDK ergonomics
|
|
390
|
+
* differ. Backend filters out `revoked` rows but keeps
|
|
391
|
+
* `pending_verification` rows visible (D5).
|
|
392
|
+
*/
|
|
393
|
+
declare function useExternalAccounts(args: OwnerRef): QueryResult<List<ExternalAccount>>;
|
|
348
394
|
declare function useSubAccounts(_args: OwnerRef): QueryResult<List<SubAccount>>;
|
|
349
395
|
declare function useVirtualAccounts(_filters?: VirtualAccountsFilters): QueryResult<List<VirtualAccount>>;
|
|
350
396
|
declare function useVirtualCards(_filters?: VirtualCardsFilters): QueryResult<List<VirtualCard>>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { HttpTransport, TransportState, BrowserCapxulConfig, AccountId, OrganizationId, ApiKeyId, BalanceLedgerEntryId, ExternalAccountId, MemberId, Account, ApiKey, BalanceLedgerEntry, DocumentId, Document, ExternalAccount, KybProfile, KycProfile, CapxulError as CapxulError$1, Member, OperationId, Operation, Organization, PaymentId, Payment, SafeId, Safe, SubAccountId, SubAccount, TokenTransfer, TransferId, Transfer, Treasury, VirtualAccountId, VirtualAccount, VirtualCardId, VirtualCard, WebhookEndpointId, WebhookEndpoint, WebhookEventId, WebhookEvent, WithdrawalId, Withdrawal, List, TokenTransfersListInput, TokenTransfersListPage, LocalPrivateKeySignerProvider } from '@capxul/sdk';
|
|
2
|
+
import { HttpTransport, TransportState, BrowserCapxulConfig, AuthSessionStore, AccountId, OrganizationId, ApiKeyId, BalanceLedgerEntryId, ExternalAccountId, MemberId, Account, ApiKey, BalanceLedgerEntry, DocumentId, Document, ExternalAccount, KybProfile, KycProfile, CapxulError as CapxulError$1, Member, OperationId, Operation, Organization, PaymentId, Payment, SafeId, Safe, SubAccountId, SubAccount, TokenTransfer, TransferId, Transfer, Treasury, VirtualAccountId, VirtualAccount, VirtualCardId, VirtualCard, WebhookEndpointId, WebhookEndpoint, WebhookEventId, WebhookEvent, WithdrawalId, Withdrawal, List, TokenTransfersListInput, TokenTransfersListPage, LocalPrivateKeySignerProvider } from '@capxul/sdk';
|
|
3
3
|
export { AuthFlowContext, AuthFlowEvent, BrowserCapxulConfig, OnboardingFlowContext, OnboardingFlowEvent, ProvisioningFlowContext, ProvisioningFlowEvent } from '@capxul/sdk';
|
|
4
4
|
import { QueryClient, UseQueryResult } from '@tanstack/react-query';
|
|
5
5
|
import { CapxulClient } from '@capxul/sdk/client';
|
|
@@ -58,16 +58,30 @@ declare function useCapxulStatus(): TransportState;
|
|
|
58
58
|
* `CapxulProvider` — public React provider for `@capxul/sdk-react`.
|
|
59
59
|
*
|
|
60
60
|
* Single-input contract: accepts ONLY `{ config: BrowserCapxulConfig,
|
|
61
|
-
* queryClient?, children }`. The browser config is the
|
|
62
|
-
* discriminated union from `@capxul/sdk` — `apiKey`,
|
|
63
|
-
* and other server-only fields are rejected at
|
|
64
|
-
* runtime via `createCapxulConfig`'s allow-list
|
|
61
|
+
* sessionStore?, queryClient?, children }`. The browser config is the
|
|
62
|
+
* secret-safe discriminated union from `@capxul/sdk` — `apiKey`,
|
|
63
|
+
* `data`, `signer`, and other server-only fields are rejected at
|
|
64
|
+
* compile-time AND at runtime via `createCapxulConfig`'s allow-list
|
|
65
|
+
* validator.
|
|
65
66
|
*
|
|
66
67
|
* The provider builds an `HttpTransport` synchronously and injects it
|
|
67
68
|
* into `createCapxulClient` via the internal `_transport` slot. The
|
|
68
69
|
* transport is exposed through `CapxulTransportContext` so
|
|
69
70
|
* `useCapxulStatus()` can subscribe to its lifecycle state machine.
|
|
70
71
|
*
|
|
72
|
+
* The provider also builds a `ConvexReactClient`-backed data client
|
|
73
|
+
* for the `build-time-urls` arm and feeds it as `config.data` so the
|
|
74
|
+
* SDK domain methods (`me.get`, `accounts.retrieve`, ...) and the
|
|
75
|
+
* React hooks built on top can run authenticated reads against live
|
|
76
|
+
* Convex. The auth lifecycle threads through the optional
|
|
77
|
+
* `sessionStore` prop: `verifyOtp` writes a session to the store
|
|
78
|
+
* (which the SDK auth client persists), and the provider's data
|
|
79
|
+
* client re-reads the JWT on demand via `refreshAuth()`. Without a
|
|
80
|
+
* session store the provider falls back to an in-memory one, which
|
|
81
|
+
* is fine for browser apps that hold the page until next reload but
|
|
82
|
+
* inadequate for CLI / Node consumers that need cross-process
|
|
83
|
+
* persistence — those pass a file-backed store via the prop.
|
|
84
|
+
*
|
|
71
85
|
* Tests and the e2e harness need the server-augmented `CapxulConfig`
|
|
72
86
|
* shape (with `data`, `signer`, `signing`). Those callers use
|
|
73
87
|
* `CapxulTestProvider` from `@capxul/sdk-react/proof` instead.
|
|
@@ -88,6 +102,17 @@ type CapxulProviderProps = {
|
|
|
88
102
|
* via `createCapxulConfig`'s allow-list validator.
|
|
89
103
|
*/
|
|
90
104
|
readonly config: BrowserCapxulConfig;
|
|
105
|
+
/**
|
|
106
|
+
* Auth session persistence adapter. The SDK auth client writes
|
|
107
|
+
* sessions here on `verifyOtp` and clears them on `signOut`. The
|
|
108
|
+
* provider's data client re-reads the JWT from this store on every
|
|
109
|
+
* `refreshAuth` call so reads carry the right auth header.
|
|
110
|
+
*
|
|
111
|
+
* Defaults to an in-memory store scoped to this provider. Pass a
|
|
112
|
+
* file-backed (Node CLI) or `localStorage`-backed (browser) store
|
|
113
|
+
* to persist sessions across process or page lifetimes.
|
|
114
|
+
*/
|
|
115
|
+
readonly sessionStore?: AuthSessionStore;
|
|
91
116
|
/**
|
|
92
117
|
* Optional TanStack Query `QueryClient`. Pass your app's existing
|
|
93
118
|
* client to share the cache across the SDK hooks and the host
|
|
@@ -97,7 +122,7 @@ type CapxulProviderProps = {
|
|
|
97
122
|
readonly queryClient?: QueryClient;
|
|
98
123
|
readonly children: ReactNode;
|
|
99
124
|
};
|
|
100
|
-
declare function CapxulProvider({ config, queryClient, children, }: CapxulProviderProps): ReactNode;
|
|
125
|
+
declare function CapxulProvider({ config, sessionStore, queryClient, children, }: CapxulProviderProps): ReactNode;
|
|
101
126
|
|
|
102
127
|
/**
|
|
103
128
|
* `QueryResult<T>` — the canonical three-state return shape for every
|
|
@@ -232,7 +257,18 @@ declare function useTreasury(_organizationId: OrganizationId): QueryResult<Treas
|
|
|
232
257
|
declare function useApiKey(_args: UseApiKeyArgs): QueryResult<ApiKey>;
|
|
233
258
|
declare function useKycProfile(_accountId: AccountId): QueryResult<KycProfile>;
|
|
234
259
|
declare function useKybProfile(_organizationId: OrganizationId): QueryResult<KybProfile>;
|
|
235
|
-
|
|
260
|
+
/**
|
|
261
|
+
* Withdrawals v1 W1 (#464) — wired through the SDK.
|
|
262
|
+
*
|
|
263
|
+
* Branches on `args.ownerKind` so `account` scope reads via the
|
|
264
|
+
* top-level `capxul.externalAccounts.retrieve(id)` (visibility-gated
|
|
265
|
+
* server-side by the caller's accountId), and `organization` scope
|
|
266
|
+
* reads via `capxul.organizations.externalAccounts.retrieve({ ... })`
|
|
267
|
+
* which adds an org-scope check for cross-org isolation. Both routes
|
|
268
|
+
* resolve to the same Convex query handler — the SDK ergonomics
|
|
269
|
+
* differ but the wire shape is identical.
|
|
270
|
+
*/
|
|
271
|
+
declare function useExternalAccount(args: UseExternalAccountArgs): QueryResult<ExternalAccount>;
|
|
236
272
|
declare function useSubAccount(_subAccountId: SubAccountId): QueryResult<SubAccount>;
|
|
237
273
|
declare function useVirtualAccount(_virtualAccountId: VirtualAccountId): QueryResult<VirtualAccount>;
|
|
238
274
|
declare function useVirtualCard(_virtualCardId: VirtualCardId): QueryResult<VirtualCard>;
|
|
@@ -344,7 +380,17 @@ type OrgDocumentsFilters = OrgScopedFilters & {
|
|
|
344
380
|
*/
|
|
345
381
|
declare function useOrganizations(): QueryResult<List<Organization>>;
|
|
346
382
|
declare function useMembers(_organizationId: OrganizationId): QueryResult<List<Member>>;
|
|
347
|
-
|
|
383
|
+
/**
|
|
384
|
+
* Withdrawals v1 W1 (#464) — wired through the SDK.
|
|
385
|
+
*
|
|
386
|
+
* Personal scope reads through `capxul.accounts.externalAccounts.list({
|
|
387
|
+
* accountId })` (Pattern A nested namespace); org scope reads through
|
|
388
|
+
* `capxul.organizations.externalAccounts.list({ organizationId })`.
|
|
389
|
+
* Both resolve to the same Convex query handler — the SDK ergonomics
|
|
390
|
+
* differ. Backend filters out `revoked` rows but keeps
|
|
391
|
+
* `pending_verification` rows visible (D5).
|
|
392
|
+
*/
|
|
393
|
+
declare function useExternalAccounts(args: OwnerRef): QueryResult<List<ExternalAccount>>;
|
|
348
394
|
declare function useSubAccounts(_args: OwnerRef): QueryResult<List<SubAccount>>;
|
|
349
395
|
declare function useVirtualAccounts(_filters?: VirtualAccountsFilters): QueryResult<List<VirtualAccount>>;
|
|
350
396
|
declare function useVirtualCards(_filters?: VirtualCardsFilters): QueryResult<List<VirtualCard>>;
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { createContext, useContext, useSyncExternalStore, useMemo,
|
|
2
|
+
import { createContext, useContext, useSyncExternalStore, useMemo, useEffect, useState } from 'react';
|
|
3
3
|
import { makeHttpTransport, createCapxulClient, CapxulError as CapxulError$1 } from '@capxul/sdk';
|
|
4
4
|
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
|
|
5
5
|
import { jsx } from 'react/jsx-runtime';
|
|
6
|
+
import { ConvexReactClient } from 'convex/react';
|
|
6
7
|
import { CapxulError as CapxulError$2 } from '@capxul/sdk/errors';
|
|
7
8
|
import { useActor } from '@xstate/react';
|
|
8
9
|
import { getAddress } from 'viem';
|
|
@@ -107,8 +108,35 @@ var Errors = {
|
|
|
107
108
|
"Idempotency key was already used for a different request",
|
|
108
109
|
{ details }
|
|
109
110
|
),
|
|
110
|
-
emailDeliveryFailed: (detail) => new CapxulError("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}
|
|
111
|
-
|
|
111
|
+
emailDeliveryFailed: (detail, details) => new CapxulError("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`, {
|
|
112
|
+
details
|
|
113
|
+
}),
|
|
114
|
+
rateLimited: (details) => new CapxulError("RATE_LIMITED", "Request was rate limited", {
|
|
115
|
+
details: { ...details }
|
|
116
|
+
}),
|
|
117
|
+
internalError: (reason) => new CapxulError("INTERNAL_ERROR", `Internal error: ${reason}`),
|
|
118
|
+
/**
|
|
119
|
+
* Verification gate. Surfaced when a request hits a verification
|
|
120
|
+
* boundary the actor cannot cross under their current state. Two
|
|
121
|
+
* variants share this code:
|
|
122
|
+
*
|
|
123
|
+
* - Rail gate (Withdrawals v1 W2, #465): the resolved
|
|
124
|
+
* `external_account.kind` routes to a withdrawal rail (e.g.
|
|
125
|
+
* `fiat_offramp`, `card_payout`) that is not yet supported. Carries
|
|
126
|
+
* `details.rail` + `details.currentKind`.
|
|
127
|
+
* - KYC tier gate (legacy / future): the actor's KYC tier is below
|
|
128
|
+
* the required tier. Carries `details.requiredTier`.
|
|
129
|
+
*
|
|
130
|
+
* Code is shared because both expose the same UX shape ("you cannot
|
|
131
|
+
* proceed until verification advances"); the `details.*` keys
|
|
132
|
+
* differentiate the route.
|
|
133
|
+
*/
|
|
134
|
+
verificationRequired: (details) => {
|
|
135
|
+
const message = "rail" in details ? `Withdrawal rail "${details.rail}" (kind=${details.currentKind}) is not yet supported.` : `Verification tier ${details.requiredTier} is required.`;
|
|
136
|
+
return new CapxulError("VERIFICATION_REQUIRED", message, {
|
|
137
|
+
details: { ...details }
|
|
138
|
+
});
|
|
139
|
+
}
|
|
112
140
|
};
|
|
113
141
|
|
|
114
142
|
// ../config/src/org-roles.ts
|
|
@@ -170,12 +198,87 @@ function assertModeRequiredFields(input) {
|
|
|
170
198
|
}
|
|
171
199
|
}
|
|
172
200
|
}
|
|
201
|
+
function createReactDataClient(convexUrl, sessionStore) {
|
|
202
|
+
const client = new ConvexReactClient(convexUrl);
|
|
203
|
+
const refreshAuth = () => {
|
|
204
|
+
const session = sessionStore.get();
|
|
205
|
+
const jwt = session?.convexJwt;
|
|
206
|
+
if (jwt) {
|
|
207
|
+
client.setAuth(() => Promise.resolve(jwt));
|
|
208
|
+
} else {
|
|
209
|
+
client.clearAuth();
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
refreshAuth();
|
|
213
|
+
return {
|
|
214
|
+
query: (name, args) => client.query(name, args),
|
|
215
|
+
mutation: (name, args) => client.mutation(name, args),
|
|
216
|
+
action: (name, args) => client.action(name, args),
|
|
217
|
+
refreshAuth,
|
|
218
|
+
close: () => {
|
|
219
|
+
void client.close();
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function createLazyReactDataClient(transport, sessionStore, createClient = createReactDataClient) {
|
|
224
|
+
let client = null;
|
|
225
|
+
let initializeClient = null;
|
|
226
|
+
let closed = false;
|
|
227
|
+
function closedError() {
|
|
228
|
+
return new Error("Capxul React data client is closed");
|
|
229
|
+
}
|
|
230
|
+
async function getClient() {
|
|
231
|
+
if (closed) throw closedError();
|
|
232
|
+
if (client) return client;
|
|
233
|
+
initializeClient ??= (async () => {
|
|
234
|
+
const runtime = await transport.ensureRuntime();
|
|
235
|
+
if (closed) throw closedError();
|
|
236
|
+
const nextClient = createClient(runtime.convexUrl, sessionStore);
|
|
237
|
+
if (closed) {
|
|
238
|
+
nextClient.close();
|
|
239
|
+
throw closedError();
|
|
240
|
+
}
|
|
241
|
+
client = nextClient;
|
|
242
|
+
return nextClient;
|
|
243
|
+
})().catch((error) => {
|
|
244
|
+
if (!closed) {
|
|
245
|
+
initializeClient = null;
|
|
246
|
+
}
|
|
247
|
+
throw error;
|
|
248
|
+
});
|
|
249
|
+
return initializeClient;
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
query: async (name, args) => (await getClient()).query(name, args),
|
|
253
|
+
mutation: async (name, args) => (await getClient()).mutation(name, args),
|
|
254
|
+
action: async (name, args) => (await getClient()).action?.(name, args),
|
|
255
|
+
refreshAuth: () => {
|
|
256
|
+
client?.refreshAuth();
|
|
257
|
+
},
|
|
258
|
+
close: () => {
|
|
259
|
+
closed = true;
|
|
260
|
+
client?.close();
|
|
261
|
+
client = null;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
173
265
|
function CapxulProvider({
|
|
174
266
|
config,
|
|
267
|
+
sessionStore,
|
|
175
268
|
queryClient,
|
|
176
269
|
children
|
|
177
270
|
}) {
|
|
178
|
-
const
|
|
271
|
+
const defaultSessionStore = useMemo(() => createMemorySessionStore(), []);
|
|
272
|
+
const effectiveSessionStore = sessionStore ?? defaultSessionStore;
|
|
273
|
+
const wiring = useMemo(
|
|
274
|
+
() => buildWiring(config, effectiveSessionStore),
|
|
275
|
+
[config, effectiveSessionStore]
|
|
276
|
+
);
|
|
277
|
+
useEffect(() => {
|
|
278
|
+
return () => {
|
|
279
|
+
wiring.dataClient?.close();
|
|
280
|
+
};
|
|
281
|
+
}, [wiring]);
|
|
179
282
|
const defaultClient = useMemo(
|
|
180
283
|
() => new QueryClient({
|
|
181
284
|
defaultOptions: { queries: { staleTime: 3e4 } }
|
|
@@ -185,16 +288,58 @@ function CapxulProvider({
|
|
|
185
288
|
const effectiveClient = queryClient ?? defaultClient;
|
|
186
289
|
return /* @__PURE__ */ jsx(QueryClientProvider, { client: effectiveClient, children: /* @__PURE__ */ jsx(CapxulTransportProvider, { transport: wiring.transport, children: /* @__PURE__ */ jsx(CapxulClientProvider, { client: wiring.client, children }) }) });
|
|
187
290
|
}
|
|
188
|
-
function buildWiring(config) {
|
|
291
|
+
function buildWiring(config, sessionStore) {
|
|
189
292
|
const validated = createCapxulConfig(config);
|
|
190
293
|
const transport = makeHttpTransport(validated);
|
|
294
|
+
const dataClient = validated.mode === "build-time-urls" ? createReactDataClient(validated.convexUrl, sessionStore) : createLazyReactDataClient(transport, sessionStore);
|
|
191
295
|
const sdkConfig = {
|
|
192
296
|
_transport: transport,
|
|
193
|
-
publishableKey: validated.mode === "publishable-key" ? validated.publishableKey : void 0
|
|
297
|
+
publishableKey: validated.mode === "publishable-key" ? validated.publishableKey : void 0,
|
|
298
|
+
data: dataClient ?? void 0,
|
|
299
|
+
auth: validated.mode === "build-time-urls" || validated.mode === "publishable-key" ? {
|
|
300
|
+
// The auth client builds the BetterAuth root URL from this
|
|
301
|
+
// value. Convex's `.cloud` URL is the wrong host (BetterAuth
|
|
302
|
+
// is mounted on the `.site` URL), but the build-time-urls
|
|
303
|
+
// transport already encodes the correct `authBaseUrl` via
|
|
304
|
+
// its discriminated union. We pass `convexUrl` here only so
|
|
305
|
+
// `core/auth.ts`'s `createTransportProvider` short-circuits
|
|
306
|
+
// to the externally-injected `_transport` cache slot.
|
|
307
|
+
baseUrl: transport.authBaseUrl,
|
|
308
|
+
sessionStore,
|
|
309
|
+
createDataClient: async (_session) => {
|
|
310
|
+
dataClient.refreshAuth();
|
|
311
|
+
return dataClient;
|
|
312
|
+
}
|
|
313
|
+
} : void 0
|
|
194
314
|
};
|
|
315
|
+
const client = createCapxulClient(sdkConfig);
|
|
316
|
+
if (dataClient) {
|
|
317
|
+
const originalSignOut = client.auth.signOut;
|
|
318
|
+
Object.assign(client.auth, {
|
|
319
|
+
signOut: async () => {
|
|
320
|
+
const result = await originalSignOut();
|
|
321
|
+
dataClient.refreshAuth();
|
|
322
|
+
sdkConfig.data = dataClient;
|
|
323
|
+
return result;
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
}
|
|
195
327
|
return {
|
|
196
|
-
client
|
|
197
|
-
transport
|
|
328
|
+
client,
|
|
329
|
+
transport,
|
|
330
|
+
dataClient
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
function createMemorySessionStore() {
|
|
334
|
+
let current = null;
|
|
335
|
+
return {
|
|
336
|
+
get: () => current,
|
|
337
|
+
set: (session) => {
|
|
338
|
+
current = session;
|
|
339
|
+
},
|
|
340
|
+
clear: () => {
|
|
341
|
+
current = null;
|
|
342
|
+
}
|
|
198
343
|
};
|
|
199
344
|
}
|
|
200
345
|
function notImplementedQuery(hookName) {
|
|
@@ -316,8 +461,15 @@ function useKycProfile(_accountId) {
|
|
|
316
461
|
function useKybProfile(_organizationId) {
|
|
317
462
|
return notImplementedQuery("useKybProfile");
|
|
318
463
|
}
|
|
319
|
-
function useExternalAccount(
|
|
320
|
-
|
|
464
|
+
function useExternalAccount(args) {
|
|
465
|
+
const capxul = useCapxul();
|
|
466
|
+
return useSdkQuery(
|
|
467
|
+
() => args.ownerKind === "account" ? capxul.externalAccounts.retrieve(args.externalAccountId) : capxul.organizations.externalAccounts.retrieve({
|
|
468
|
+
organizationId: args.ownerId,
|
|
469
|
+
externalAccountId: args.externalAccountId
|
|
470
|
+
}),
|
|
471
|
+
[capxul, args.ownerKind, args.ownerId, args.externalAccountId]
|
|
472
|
+
);
|
|
321
473
|
}
|
|
322
474
|
function useSubAccount(_subAccountId) {
|
|
323
475
|
return notImplementedQuery("useSubAccount");
|
|
@@ -375,8 +527,14 @@ function useOrganizations() {
|
|
|
375
527
|
function useMembers(_organizationId) {
|
|
376
528
|
return notImplementedQuery("useMembers");
|
|
377
529
|
}
|
|
378
|
-
function useExternalAccounts(
|
|
379
|
-
|
|
530
|
+
function useExternalAccounts(args) {
|
|
531
|
+
const capxul = useCapxul();
|
|
532
|
+
return useSdkQuery(
|
|
533
|
+
() => args.ownerKind === "account" ? capxul.accounts.externalAccounts.list({ accountId: args.ownerId }) : capxul.organizations.externalAccounts.list({
|
|
534
|
+
organizationId: args.ownerId
|
|
535
|
+
}),
|
|
536
|
+
[capxul, args.ownerKind, args.ownerId]
|
|
537
|
+
);
|
|
380
538
|
}
|
|
381
539
|
function useSubAccounts(_args) {
|
|
382
540
|
return notImplementedQuery("useSubAccounts");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capxul/sdk-react",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.8",
|
|
4
4
|
"description": "React provider + hooks for the @capxul/sdk headless client.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@capxul/sdk": "0.1.0-alpha.
|
|
49
|
+
"@capxul/sdk": "0.1.0-alpha.8"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"@tanstack/react-query": "^5",
|