@capxul/sdk-react 1.0.0-alpha.9 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/dist/controllers-BU11km12.mjs +647 -0
- package/dist/controllers-BU11km12.mjs.map +1 -0
- package/dist/controllers-DuHYSiw1.d.mts +207 -0
- package/dist/controllers-DuHYSiw1.d.mts.map +1 -0
- package/dist/index.d.mts +156 -175
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +420 -395
- package/dist/index.mjs.map +1 -1
- package/dist/testing/index.d.mts +21 -0
- package/dist/testing/index.d.mts.map +1 -0
- package/dist/testing/index.mjs +24 -0
- package/dist/testing/index.mjs.map +1 -0
- package/package.json +10 -11
package/dist/index.mjs
CHANGED
|
@@ -1,152 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { jsx } from "react/jsx-runtime";
|
|
6
|
-
//#region src/internal/capxul-bootstrap-context.tsx
|
|
7
|
-
const CapxulBootstrapContext = createContext(null);
|
|
8
|
-
function CapxulBootstrapProvider({ value, children }) {
|
|
9
|
-
return /* @__PURE__ */ jsx(CapxulBootstrapContext.Provider, {
|
|
10
|
-
value,
|
|
11
|
-
children
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
function useCapxul() {
|
|
15
|
-
const state = useContext(CapxulBootstrapContext);
|
|
16
|
-
if (state === null) throw new Error("useCapxul must be used within <CapxulProvider>");
|
|
17
|
-
return state;
|
|
18
|
-
}
|
|
19
|
-
//#endregion
|
|
20
|
-
//#region src/internal/capxul-client-context.tsx
|
|
21
|
-
const MISSING_CAPXUL_CLIENT_PROVIDER = Symbol("MISSING_CAPXUL_CLIENT_PROVIDER");
|
|
22
|
-
const CapxulClientContext = createContext(MISSING_CAPXUL_CLIENT_PROVIDER);
|
|
23
|
-
function CapxulClientProvider({ client, children }) {
|
|
24
|
-
return /* @__PURE__ */ jsx(CapxulClientContext.Provider, {
|
|
25
|
-
value: client,
|
|
26
|
-
children
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
function useCapxulClient() {
|
|
30
|
-
const client = useCapxulClientOrNull();
|
|
31
|
-
if (client === null) throw new Error("useCapxulClient called before <CapxulProvider> bootstrap resolved");
|
|
32
|
-
return client;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Returns the client, or `null` while `<CapxulProvider>` is still bootstrapping.
|
|
36
|
-
* Data hooks use this so they can sit in `isPending` (disabled query) until the
|
|
37
|
-
* client resolves, rather than throwing during bootstrap.
|
|
38
|
-
*/
|
|
39
|
-
function useCapxulClientOrNull() {
|
|
40
|
-
const client = useContext(CapxulClientContext);
|
|
41
|
-
if (client === MISSING_CAPXUL_CLIENT_PROVIDER) throw new Error("useCapxulClient must be used within <CapxulProvider>");
|
|
42
|
-
return client;
|
|
43
|
-
}
|
|
44
|
-
//#endregion
|
|
45
|
-
//#region src/provider.tsx
|
|
46
|
-
function makeDefaultQueryClient() {
|
|
47
|
-
return new QueryClient({ defaultOptions: {
|
|
48
|
-
queries: {
|
|
49
|
-
retry: 2,
|
|
50
|
-
staleTime: 3e4
|
|
51
|
-
},
|
|
52
|
-
mutations: { retry: 0 }
|
|
53
|
-
} });
|
|
54
|
-
}
|
|
55
|
-
function isCapxulQueryKey(queryKey) {
|
|
56
|
-
return queryKey[0] === "capxul";
|
|
57
|
-
}
|
|
58
|
-
function clearClientScopedQueries(queryClient, ownsQueryClient) {
|
|
59
|
-
if (ownsQueryClient) {
|
|
60
|
-
queryClient.clear();
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
queryClient.removeQueries({ predicate: (query) => isCapxulQueryKey(query.queryKey) });
|
|
64
|
-
}
|
|
65
|
-
function CapxulProvider(props) {
|
|
66
|
-
const { publishableKey, client: injectedClient, requirement, signer, queryClient, children } = props;
|
|
67
|
-
const [resolvedQueryClient] = useState(() => queryClient ?? makeDefaultQueryClient());
|
|
68
|
-
const [ownsQueryClient] = useState(() => queryClient === void 0);
|
|
69
|
-
const [client, setClient] = useState(injectedClient ?? null);
|
|
70
|
-
const previousClientRef = useRef(injectedClient ?? null);
|
|
71
|
-
const [status, setStatus] = useState(injectedClient === void 0 ? "bootstrapping" : "ready");
|
|
72
|
-
const [error, setError] = useState(null);
|
|
73
|
-
const [attempt, setAttempt] = useState(0);
|
|
74
|
-
const retry = useCallback(() => {
|
|
75
|
-
setAttempt((n) => n + 1);
|
|
76
|
-
}, []);
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
if (publishableKey === void 0) return;
|
|
79
|
-
let cancelled = false;
|
|
80
|
-
let created = null;
|
|
81
|
-
setStatus("bootstrapping");
|
|
82
|
-
setError(null);
|
|
83
|
-
setClient(null);
|
|
84
|
-
(async () => {
|
|
85
|
-
const result = await createCapxulClient({
|
|
86
|
-
publishableKey,
|
|
87
|
-
...requirement === void 0 ? {} : { requirement },
|
|
88
|
-
...signer === void 0 ? {} : { signer }
|
|
89
|
-
});
|
|
90
|
-
if (cancelled) {
|
|
91
|
-
if (result.ok) await result.value._internal.close?.();
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
if (result.ok) {
|
|
95
|
-
created = result.value;
|
|
96
|
-
setClient(result.value);
|
|
97
|
-
setStatus("ready");
|
|
98
|
-
} else {
|
|
99
|
-
setError(result.error);
|
|
100
|
-
setStatus("error");
|
|
101
|
-
}
|
|
102
|
-
})();
|
|
103
|
-
return () => {
|
|
104
|
-
cancelled = true;
|
|
105
|
-
created?._internal.close?.();
|
|
106
|
-
};
|
|
107
|
-
}, [
|
|
108
|
-
publishableKey,
|
|
109
|
-
requirement,
|
|
110
|
-
signer,
|
|
111
|
-
attempt
|
|
112
|
-
]);
|
|
113
|
-
useEffect(() => {
|
|
114
|
-
if (injectedClient === void 0) return;
|
|
115
|
-
setClient(injectedClient);
|
|
116
|
-
setStatus("ready");
|
|
117
|
-
setError(null);
|
|
118
|
-
}, [injectedClient]);
|
|
119
|
-
useEffect(() => {
|
|
120
|
-
const previous = previousClientRef.current;
|
|
121
|
-
if (previous !== null && previous !== client) clearClientScopedQueries(resolvedQueryClient, ownsQueryClient);
|
|
122
|
-
previousClientRef.current = client;
|
|
123
|
-
}, [
|
|
124
|
-
client,
|
|
125
|
-
ownsQueryClient,
|
|
126
|
-
resolvedQueryClient
|
|
127
|
-
]);
|
|
128
|
-
const bootstrapState = useMemo(() => ({
|
|
129
|
-
status,
|
|
130
|
-
error,
|
|
131
|
-
retry
|
|
132
|
-
}), [
|
|
133
|
-
status,
|
|
134
|
-
error,
|
|
135
|
-
retry
|
|
136
|
-
]);
|
|
137
|
-
if (publishableKey === void 0 === (injectedClient === void 0)) throw new Error("CapxulProvider requires exactly one of `publishableKey` or `client`");
|
|
138
|
-
return /* @__PURE__ */ jsx(QueryClientProvider, {
|
|
139
|
-
client: resolvedQueryClient,
|
|
140
|
-
children: /* @__PURE__ */ jsx(CapxulBootstrapProvider, {
|
|
141
|
-
value: bootstrapState,
|
|
142
|
-
children: /* @__PURE__ */ jsx(CapxulClientProvider, {
|
|
143
|
-
client,
|
|
144
|
-
children
|
|
145
|
-
})
|
|
146
|
-
})
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
//#endregion
|
|
2
|
+
import { a as useCapxulAuth, c as useCapxulIdentityOrNull, d as capxulKeys, f as useCapxulClientOrNull, i as entered, l as useCapxulSend, n as CapxulOnboardingController, o as useCapxulDestination, p as useCapxul, r as CapxulProvider, s as useCapxulIdentity, t as CapxulAuthenticationController, u as useCapxulTransitions } from "./controllers-BU11km12.mjs";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
150
5
|
//#region ../errors/src/errors.ts
|
|
151
6
|
const CAPXUL_ERROR_CODES = [
|
|
152
7
|
"NOT_AUTHENTICATED",
|
|
@@ -170,7 +25,11 @@ const CAPXUL_ERROR_CODES = [
|
|
|
170
25
|
"OTP_EXPIRED",
|
|
171
26
|
"SIGNER_REJECTED",
|
|
172
27
|
"CANCELLED",
|
|
173
|
-
"WRONG_STATE"
|
|
28
|
+
"WRONG_STATE",
|
|
29
|
+
"STALE_EPOCH",
|
|
30
|
+
"SUPERSEDED",
|
|
31
|
+
"WORK_DIED",
|
|
32
|
+
"ACTOR_STOPPED"
|
|
174
33
|
];
|
|
175
34
|
var CapxulError = class extends Error {
|
|
176
35
|
code;
|
|
@@ -215,8 +74,7 @@ const Errors = {
|
|
|
215
74
|
} }),
|
|
216
75
|
/**
|
|
217
76
|
* Sibling factory to {@link Errors.providerError} for the per-state timeout
|
|
218
|
-
* path in flows
|
|
219
|
-
* register_indexer `after:` timers). Same `PROVIDER_ERROR` code as
|
|
77
|
+
* path in flows. Same `PROVIDER_ERROR` code as
|
|
220
78
|
* `providerError`, plus a `details.reason: "timeout"` discriminator so
|
|
221
79
|
* downstream observers can distinguish failure modes without parsing the
|
|
222
80
|
* message string. The redacted message names the timeout budget; the
|
|
@@ -286,8 +144,8 @@ const Errors = {
|
|
|
286
144
|
* Method called from a flow state where its precondition fails (TA16). The
|
|
287
145
|
* SDK's method API short-circuits with this error before driving the
|
|
288
146
|
* internal state machine. `currentState` is the Effect-machine snapshot
|
|
289
|
-
* tag (stringified
|
|
290
|
-
* `
|
|
147
|
+
* tag (stringified from the SDK's actor-shell snapshot; see
|
|
148
|
+
* `packages/errors/CONTEXT.md`); `validStates`
|
|
291
149
|
* enumerates the states the method accepts.
|
|
292
150
|
*/
|
|
293
151
|
wrongState: (details) => new CapxulError("WRONG_STATE", `${details.method} called from state '${details.currentState}'; valid states: ${details.validStates.join(", ")}`, { details: {
|
|
@@ -313,162 +171,32 @@ function requireBootstrappedClient(client, method) {
|
|
|
313
171
|
return client;
|
|
314
172
|
}
|
|
315
173
|
//#endregion
|
|
316
|
-
//#region src/internal/reactivity-keys.ts
|
|
317
|
-
const capxulKeys = {
|
|
318
|
-
session: ["capxul", "session"],
|
|
319
|
-
profile: ["capxul", "profile"],
|
|
320
|
-
account: ["capxul", "account"],
|
|
321
|
-
accountLifecycle: ["capxul", "accountLifecycle"],
|
|
322
|
-
provisioning: ["capxul", "provisioning"],
|
|
323
|
-
binding: ["capxul", "binding"],
|
|
324
|
-
accountBalance: ["capxul", "accountBalance"],
|
|
325
|
-
subAccounts: (accountId) => [
|
|
326
|
-
"capxul",
|
|
327
|
-
"subAccounts",
|
|
328
|
-
accountId ?? "pending"
|
|
329
|
-
],
|
|
330
|
-
orgs: ["capxul", "orgs"],
|
|
331
|
-
org: (orgId) => [
|
|
332
|
-
"capxul",
|
|
333
|
-
"org",
|
|
334
|
-
orgId ?? "pending"
|
|
335
|
-
],
|
|
336
|
-
orgMembers: (orgId) => [
|
|
337
|
-
"capxul",
|
|
338
|
-
"org",
|
|
339
|
-
orgId ?? "pending",
|
|
340
|
-
"members"
|
|
341
|
-
],
|
|
342
|
-
orgRoles: (orgId) => [
|
|
343
|
-
"capxul",
|
|
344
|
-
"org",
|
|
345
|
-
orgId ?? "pending",
|
|
346
|
-
"roles"
|
|
347
|
-
],
|
|
348
|
-
orgTreasury: (orgId) => [
|
|
349
|
-
"capxul",
|
|
350
|
-
"org",
|
|
351
|
-
orgId ?? "pending",
|
|
352
|
-
"treasury"
|
|
353
|
-
]
|
|
354
|
-
};
|
|
355
|
-
//#endregion
|
|
356
174
|
//#region src/internal/unwrap-capxul-result.ts
|
|
357
175
|
/**
|
|
358
|
-
* Unwrap a `CapxulResult` for TanStack query/mutation functions
|
|
359
|
-
*
|
|
176
|
+
* Unwrap a `CapxulResult` for TanStack query/mutation functions and throw into
|
|
177
|
+
* React Query's error path.
|
|
360
178
|
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
179
|
+
* Failure observation belongs to the core SDK method boundary. The legacy
|
|
180
|
+
* telemetry/operation arguments remain temporarily source-compatible with the
|
|
181
|
+
* existing hook call sites, but are deliberately ignored so React cannot
|
|
182
|
+
* report the same logical failure a second time.
|
|
365
183
|
*/
|
|
366
|
-
function unwrapCapxulResult(result,
|
|
184
|
+
function unwrapCapxulResult(result, _legacyTelemetry, _legacyOperation = "query") {
|
|
367
185
|
if (result.ok) return result.value;
|
|
368
|
-
if (telemetry) try {
|
|
369
|
-
captureExceptionSync(telemetry, result.error, {
|
|
370
|
-
layer: "react-query",
|
|
371
|
-
operation
|
|
372
|
-
});
|
|
373
|
-
} catch {}
|
|
374
186
|
throw result.error;
|
|
375
187
|
}
|
|
376
188
|
//#endregion
|
|
377
|
-
//#region src/hooks/use-capxul-session.ts
|
|
378
|
-
function useCapxulSession() {
|
|
379
|
-
const client = useCapxulClientOrNull();
|
|
380
|
-
return useQuery({
|
|
381
|
-
queryKey: capxulKeys.session,
|
|
382
|
-
queryFn: async () => unwrapCapxulResult(await requireBootstrappedClient(client, "auth.getSession").auth.getSession(), client._internal.telemetry),
|
|
383
|
-
enabled: client !== null
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
//#endregion
|
|
387
189
|
//#region src/hooks/use-capxul-profile.ts
|
|
388
190
|
function useCapxulProfile() {
|
|
389
191
|
const client = useCapxulClientOrNull();
|
|
192
|
+
const identity = useCapxulIdentityOrNull();
|
|
390
193
|
return useQuery({
|
|
391
194
|
queryKey: capxulKeys.profile,
|
|
392
195
|
queryFn: async () => unwrapCapxulResult(await requireBootstrappedClient(client, "identity.loadCurrent").identity.loadCurrent(), client._internal.telemetry),
|
|
393
|
-
enabled: client !== null
|
|
196
|
+
enabled: client !== null && identity?.phase === "authenticated"
|
|
394
197
|
});
|
|
395
198
|
}
|
|
396
199
|
//#endregion
|
|
397
|
-
//#region src/internal/is-vitest-runtime.ts
|
|
398
|
-
/** True under Vitest — disables hook polling intervals that fight fake timers. */
|
|
399
|
-
function isVitestRuntime() {
|
|
400
|
-
return typeof process !== "undefined" && process.env["VITEST"] === "true";
|
|
401
|
-
}
|
|
402
|
-
//#endregion
|
|
403
|
-
//#region src/internal/invalidate-auth-boundary.ts
|
|
404
|
-
/** Background refetch after verifyOtp — avoids hard reset cancel errors in UI. */
|
|
405
|
-
async function invalidateAuthBoundary(queryClient) {
|
|
406
|
-
await Promise.all([
|
|
407
|
-
queryClient.invalidateQueries({ queryKey: capxulKeys.session }),
|
|
408
|
-
queryClient.invalidateQueries({ queryKey: capxulKeys.profile }),
|
|
409
|
-
queryClient.invalidateQueries({ queryKey: capxulKeys.accountLifecycle }),
|
|
410
|
-
queryClient.invalidateQueries({ queryKey: capxulKeys.accountBalance })
|
|
411
|
-
]);
|
|
412
|
-
}
|
|
413
|
-
/** Hard reset after signOut — drop cached authenticated rows immediately. */
|
|
414
|
-
async function resetAuthBoundary(queryClient) {
|
|
415
|
-
await queryClient.cancelQueries({ queryKey: capxulKeys.accountBalance });
|
|
416
|
-
await Promise.all([
|
|
417
|
-
queryClient.resetQueries({ queryKey: capxulKeys.session }),
|
|
418
|
-
queryClient.resetQueries({ queryKey: capxulKeys.profile }),
|
|
419
|
-
queryClient.resetQueries({ queryKey: capxulKeys.accountLifecycle }),
|
|
420
|
-
queryClient.resetQueries({ queryKey: capxulKeys.accountBalance })
|
|
421
|
-
]);
|
|
422
|
-
}
|
|
423
|
-
//#endregion
|
|
424
|
-
//#region src/hooks/use-capxul-account-lifecycle.ts
|
|
425
|
-
const LOADING_LIFECYCLE = { status: "loading" };
|
|
426
|
-
function useCapxulAccountLifecycle() {
|
|
427
|
-
const client = useCapxulClientOrNull();
|
|
428
|
-
const queryClient = useQueryClient();
|
|
429
|
-
const query = useQuery({
|
|
430
|
-
queryKey: capxulKeys.accountLifecycle,
|
|
431
|
-
queryFn: async () => {
|
|
432
|
-
const bootstrappedClient = requireBootstrappedClient(client, "account.getLifecycle");
|
|
433
|
-
return unwrapCapxulResult(await bootstrappedClient.account.getLifecycle(), bootstrappedClient._internal.telemetry);
|
|
434
|
-
},
|
|
435
|
-
enabled: client !== null,
|
|
436
|
-
refetchInterval: (q) => {
|
|
437
|
-
if (isVitestRuntime()) return false;
|
|
438
|
-
const data = q.state.data;
|
|
439
|
-
if (data === void 0) return false;
|
|
440
|
-
if (data.status === "loading" || isSettingUpLifecycle(data)) return 2e3;
|
|
441
|
-
return false;
|
|
442
|
-
}
|
|
443
|
-
});
|
|
444
|
-
const retryMutation = useMutation({
|
|
445
|
-
mutationFn: async () => {
|
|
446
|
-
const bootstrappedClient = requireBootstrappedClient(client, "account.retrySetup");
|
|
447
|
-
return unwrapCapxulResult(await bootstrappedClient.account.retrySetup(), bootstrappedClient._internal.telemetry, "mutation");
|
|
448
|
-
},
|
|
449
|
-
onSuccess: async () => {
|
|
450
|
-
await invalidateAuthBoundary(queryClient);
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
const lifecycle = query.data ?? LOADING_LIFECYCLE;
|
|
454
|
-
const failedError = lifecycle.status === "failed" ? lifecycle.error : null;
|
|
455
|
-
const queryError = query.isError ? query.error : null;
|
|
456
|
-
return {
|
|
457
|
-
lifecycle: queryError !== null && lifecycle.status === "loading" ? {
|
|
458
|
-
status: "failed",
|
|
459
|
-
at: "connecting",
|
|
460
|
-
error: queryError
|
|
461
|
-
} : lifecycle,
|
|
462
|
-
isSettingUp: isSettingUpLifecycle(lifecycle),
|
|
463
|
-
error: failedError ?? queryError,
|
|
464
|
-
isLoading: query.isLoading,
|
|
465
|
-
isFetching: query.isFetching,
|
|
466
|
-
isError: query.isError,
|
|
467
|
-
retry: retryMutation.mutateAsync,
|
|
468
|
-
isRetrying: retryMutation.isPending
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
//#endregion
|
|
472
200
|
//#region src/hooks/use-capxul-account-balance.ts
|
|
473
201
|
function useCapxulAccountBalance(options) {
|
|
474
202
|
const client = useCapxulClientOrNull();
|
|
@@ -497,46 +225,6 @@ function useCapxulAccountFund() {
|
|
|
497
225
|
});
|
|
498
226
|
}
|
|
499
227
|
//#endregion
|
|
500
|
-
//#region src/hooks/use-capxul-sign-in.ts
|
|
501
|
-
function useCapxulSignIn() {
|
|
502
|
-
const client = useCapxulClientOrNull();
|
|
503
|
-
return useMutation({ mutationFn: async (input) => {
|
|
504
|
-
const bootstrappedClient = requireBootstrappedClient(client, "auth.signIn");
|
|
505
|
-
return unwrapCapxulResult(await bootstrappedClient.auth.signIn(input), bootstrappedClient._internal.telemetry, "mutation");
|
|
506
|
-
} });
|
|
507
|
-
}
|
|
508
|
-
//#endregion
|
|
509
|
-
//#region src/hooks/use-capxul-verify-otp.ts
|
|
510
|
-
function useCapxulVerifyOtp() {
|
|
511
|
-
const client = useCapxulClientOrNull();
|
|
512
|
-
const queryClient = useQueryClient();
|
|
513
|
-
return useMutation({
|
|
514
|
-
mutationFn: async (input) => {
|
|
515
|
-
const bootstrappedClient = requireBootstrappedClient(client, "auth.verifyOtp");
|
|
516
|
-
return unwrapCapxulResult(await bootstrappedClient.auth.verifyOtp(input), bootstrappedClient._internal.telemetry, "mutation");
|
|
517
|
-
},
|
|
518
|
-
onSuccess: async () => {
|
|
519
|
-
await invalidateAuthBoundary(queryClient);
|
|
520
|
-
}
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
//#endregion
|
|
524
|
-
//#region src/hooks/use-capxul-sign-out.ts
|
|
525
|
-
function useCapxulSignOut() {
|
|
526
|
-
const client = useCapxulClientOrNull();
|
|
527
|
-
const queryClient = useQueryClient();
|
|
528
|
-
return useMutation({
|
|
529
|
-
onMutate: async () => {
|
|
530
|
-
await queryClient.cancelQueries({ queryKey: capxulKeys.accountBalance });
|
|
531
|
-
},
|
|
532
|
-
mutationFn: async () => {
|
|
533
|
-
const bootstrappedClient = requireBootstrappedClient(client, "auth.signOut");
|
|
534
|
-
return unwrapCapxulResult(await bootstrappedClient.auth.signOut(), bootstrappedClient._internal.telemetry, "mutation");
|
|
535
|
-
},
|
|
536
|
-
onSuccess: () => resetAuthBoundary(queryClient)
|
|
537
|
-
});
|
|
538
|
-
}
|
|
539
|
-
//#endregion
|
|
540
228
|
//#region src/hooks/use-capxul-sub-accounts.ts
|
|
541
229
|
function useCapxulSubAccountsList(accountId, options) {
|
|
542
230
|
const client = useCapxulClientOrNull();
|
|
@@ -611,65 +299,160 @@ function useCapxulTransfer() {
|
|
|
611
299
|
});
|
|
612
300
|
}
|
|
613
301
|
//#endregion
|
|
614
|
-
//#region src/
|
|
615
|
-
function
|
|
302
|
+
//#region src/internal/invalidate-money-state.ts
|
|
303
|
+
async function invalidateMoneyState(queryClient, input) {
|
|
304
|
+
const invalidations = [queryClient.invalidateQueries({ queryKey: capxulKeys.payments })];
|
|
305
|
+
if (input.payment !== void 0) invalidations.push(queryClient.invalidateQueries({ queryKey: capxulKeys.payment(input.payment.id) }));
|
|
306
|
+
if (input.actor.kind === "account" || input.actor.kind === "personal") invalidations.push(queryClient.invalidateQueries({ queryKey: capxulKeys.accountBalance }));
|
|
307
|
+
else {
|
|
308
|
+
const orgId = input.actor.kind === "org" ? input.actor.orgId : input.actor.organizationId;
|
|
309
|
+
invalidations.push(queryClient.invalidateQueries({ queryKey: capxulKeys.orgTreasury(orgId) }));
|
|
310
|
+
}
|
|
311
|
+
await Promise.all(invalidations);
|
|
312
|
+
}
|
|
313
|
+
//#endregion
|
|
314
|
+
//#region src/internal/reject-unresolved-actor.ts
|
|
315
|
+
/**
|
|
316
|
+
* Guard for mutation hooks whose variables carry an optional `actor` field
|
|
317
|
+
* (`useCapxulPay`, `useCapxulPayout`, `useCapxulAddDestination`,
|
|
318
|
+
* `useCapxulRemoveDestination`). Mirrors `requireActorScope` on the query path:
|
|
319
|
+
* an EXPLICITLY-passed `actor: undefined` (the `capxulOrgScope(notYetLoadedOrgId)`
|
|
320
|
+
* footgun) must fail loudly rather than silently fall back to the personal
|
|
321
|
+
* scope. Omitting the `actor` key entirely keeps the personal-scope default.
|
|
322
|
+
*
|
|
323
|
+
* Throws `CapxulError` code `INVALID_INPUT` with `details.field === "actor"`;
|
|
324
|
+
* called inside an async mutationFn it surfaces as `mutation.error: CapxulError`.
|
|
325
|
+
*/
|
|
326
|
+
function rejectUnresolvedActor(variables, operation) {
|
|
327
|
+
if (Object.hasOwn(variables, "actor") && variables.actor === void 0) throw Errors.invalidInput("actor", `explicitly provided but undefined for ${operation} — actor scope not yet resolved; omit actor for the personal scope`);
|
|
328
|
+
}
|
|
329
|
+
//#endregion
|
|
330
|
+
//#region src/hooks/use-capxul-money.ts
|
|
331
|
+
function useCapxulPay() {
|
|
332
|
+
const client = useCapxulClientOrNull();
|
|
333
|
+
const queryClient = useQueryClient();
|
|
334
|
+
return useMutation({
|
|
335
|
+
mutationFn: async (input) => {
|
|
336
|
+
rejectUnresolvedActor(input, "payments.pay");
|
|
337
|
+
const bootstrappedClient = requireBootstrappedClient(client, "payments.pay");
|
|
338
|
+
return unwrapCapxulResult(await bootstrappedClient.payments.pay(input), bootstrappedClient._internal.telemetry, "mutation");
|
|
339
|
+
},
|
|
340
|
+
onSuccess: async (payment) => {
|
|
341
|
+
await invalidateMoneyState(queryClient, {
|
|
342
|
+
actor: { kind: "personal" },
|
|
343
|
+
payment
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
function useCapxulPayout() {
|
|
349
|
+
const client = useCapxulClientOrNull();
|
|
350
|
+
const queryClient = useQueryClient();
|
|
351
|
+
return useMutation({
|
|
352
|
+
mutationFn: async (input) => {
|
|
353
|
+
rejectUnresolvedActor(input, "payments.payout");
|
|
354
|
+
const bootstrappedClient = requireBootstrappedClient(client, "payments.payout");
|
|
355
|
+
return unwrapCapxulResult(await bootstrappedClient.payments.payout(input), bootstrappedClient._internal.telemetry, "mutation");
|
|
356
|
+
},
|
|
357
|
+
onSuccess: async (payment, variables) => {
|
|
358
|
+
await invalidateMoneyState(queryClient, {
|
|
359
|
+
actor: variables.actor ?? { kind: "personal" },
|
|
360
|
+
payment
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
function useCapxulWithdraw() {
|
|
366
|
+
const client = useCapxulClientOrNull();
|
|
367
|
+
const queryClient = useQueryClient();
|
|
368
|
+
return useMutation({
|
|
369
|
+
mutationFn: async (input) => {
|
|
370
|
+
const bootstrappedClient = requireBootstrappedClient(client, "payments.withdraw");
|
|
371
|
+
return unwrapCapxulResult(await bootstrappedClient.payments.withdraw(input), bootstrappedClient._internal.telemetry, "mutation");
|
|
372
|
+
},
|
|
373
|
+
onSuccess: async (payment) => {
|
|
374
|
+
await invalidateMoneyState(queryClient, {
|
|
375
|
+
actor: { kind: "personal" },
|
|
376
|
+
payment
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
function useCapxulPayments(options) {
|
|
616
382
|
const client = useCapxulClientOrNull();
|
|
617
383
|
return useQuery({
|
|
618
|
-
queryKey: capxulKeys.
|
|
384
|
+
queryKey: capxulKeys.payments,
|
|
619
385
|
queryFn: async () => {
|
|
620
|
-
const bootstrappedClient = requireBootstrappedClient(client, "
|
|
621
|
-
return unwrapCapxulResult(await bootstrappedClient.
|
|
386
|
+
const bootstrappedClient = requireBootstrappedClient(client, "payments.list");
|
|
387
|
+
return unwrapCapxulResult(await bootstrappedClient.payments.list(), bootstrappedClient._internal.telemetry);
|
|
622
388
|
},
|
|
623
389
|
enabled: client !== null && (options?.enabled ?? true)
|
|
624
390
|
});
|
|
625
391
|
}
|
|
392
|
+
function useCapxulPayment(paymentId, options) {
|
|
393
|
+
const client = useCapxulClientOrNull();
|
|
394
|
+
return useQuery({
|
|
395
|
+
queryKey: capxulKeys.payment(paymentId),
|
|
396
|
+
queryFn: async () => {
|
|
397
|
+
if (paymentId === void 0) throw Errors.invalidInput("paymentId", "required for payments.get");
|
|
398
|
+
const bootstrappedClient = requireBootstrappedClient(client, "payments.get");
|
|
399
|
+
return unwrapCapxulResult(await bootstrappedClient.payments.get(paymentId), bootstrappedClient._internal.telemetry);
|
|
400
|
+
},
|
|
401
|
+
enabled: client !== null && paymentId !== void 0 && (options?.enabled ?? true)
|
|
402
|
+
});
|
|
403
|
+
}
|
|
626
404
|
//#endregion
|
|
627
|
-
//#region src/hooks/use-capxul-
|
|
628
|
-
function
|
|
629
|
-
const client =
|
|
405
|
+
//#region src/hooks/use-capxul-orgs.ts
|
|
406
|
+
function useCapxulOrgs(options) {
|
|
407
|
+
const client = useCapxulClientOrNull();
|
|
630
408
|
return useQuery({
|
|
631
|
-
queryKey: capxulKeys.
|
|
409
|
+
queryKey: capxulKeys.orgs,
|
|
632
410
|
queryFn: async () => {
|
|
633
|
-
|
|
634
|
-
return unwrapCapxulResult(await
|
|
411
|
+
const bootstrappedClient = requireBootstrappedClient(client, "orgs");
|
|
412
|
+
return unwrapCapxulResult(await bootstrappedClient.orgs(), bootstrappedClient._internal.telemetry);
|
|
635
413
|
},
|
|
636
|
-
enabled: (options?.enabled ?? true)
|
|
414
|
+
enabled: client !== null && (options?.enabled ?? true)
|
|
637
415
|
});
|
|
638
416
|
}
|
|
639
417
|
//#endregion
|
|
640
418
|
//#region src/hooks/use-capxul-org-members.ts
|
|
641
419
|
function useCapxulOrgMembers(orgId, options) {
|
|
642
|
-
const client =
|
|
420
|
+
const client = useCapxulClientOrNull();
|
|
421
|
+
const enabled = options?.enabled ?? true;
|
|
643
422
|
return useQuery({
|
|
644
423
|
queryKey: capxulKeys.orgMembers(orgId),
|
|
645
424
|
queryFn: async () => {
|
|
646
425
|
if (orgId === void 0) throw Errors.invalidInput("orgId", "required for useCapxulOrgMembers");
|
|
647
|
-
|
|
426
|
+
const bootstrappedClient = requireBootstrappedClient(client, "org.members");
|
|
427
|
+
return unwrapCapxulResult(await bootstrappedClient.org(orgId).members(), bootstrappedClient._internal.telemetry);
|
|
648
428
|
},
|
|
649
|
-
enabled:
|
|
429
|
+
enabled: client !== null && enabled && orgId !== void 0
|
|
650
430
|
});
|
|
651
431
|
}
|
|
652
432
|
//#endregion
|
|
653
433
|
//#region src/hooks/use-capxul-org-roles.ts
|
|
654
434
|
function useCapxulOrgRoles(orgId, options) {
|
|
655
|
-
const client =
|
|
435
|
+
const client = useCapxulClientOrNull();
|
|
436
|
+
const enabled = options?.enabled ?? true;
|
|
656
437
|
return useQuery({
|
|
657
438
|
queryKey: capxulKeys.orgRoles(orgId),
|
|
658
439
|
queryFn: async () => {
|
|
659
440
|
if (orgId === void 0) throw Errors.invalidInput("orgId", "required for useCapxulOrgRoles");
|
|
660
|
-
|
|
441
|
+
const bootstrappedClient = requireBootstrappedClient(client, "org.roles");
|
|
442
|
+
return unwrapCapxulResult(await bootstrappedClient.org(orgId).roles(), bootstrappedClient._internal.telemetry);
|
|
661
443
|
},
|
|
662
|
-
enabled:
|
|
444
|
+
enabled: client !== null && enabled && orgId !== void 0
|
|
663
445
|
});
|
|
664
446
|
}
|
|
665
447
|
//#endregion
|
|
666
448
|
//#region src/hooks/use-capxul-org-deploy-roles.ts
|
|
667
449
|
function useCapxulOrgDeployRoles() {
|
|
668
|
-
const client =
|
|
450
|
+
const client = useCapxulClientOrNull();
|
|
669
451
|
const queryClient = useQueryClient();
|
|
670
452
|
return useMutation({
|
|
671
453
|
mutationFn: async (orgId) => {
|
|
672
|
-
|
|
454
|
+
const bootstrappedClient = requireBootstrappedClient(client, "org.deployRoles");
|
|
455
|
+
return unwrapCapxulResult(await bootstrappedClient.org(orgId).deployRoles(), bootstrappedClient._internal.telemetry, "mutation");
|
|
673
456
|
},
|
|
674
457
|
onSuccess: async (_roles, orgId) => {
|
|
675
458
|
await queryClient.invalidateQueries({ queryKey: capxulKeys.orgRoles(orgId) });
|
|
@@ -681,65 +464,43 @@ function useCapxulOrgDeployRoles() {
|
|
|
681
464
|
//#endregion
|
|
682
465
|
//#region src/hooks/use-capxul-org-treasury.ts
|
|
683
466
|
function useCapxulOrgTreasury(orgId, options) {
|
|
684
|
-
const client =
|
|
467
|
+
const client = useCapxulClientOrNull();
|
|
468
|
+
const enabled = options?.enabled ?? true;
|
|
685
469
|
return useQuery({
|
|
686
470
|
queryKey: capxulKeys.orgTreasury(orgId),
|
|
687
471
|
queryFn: async () => {
|
|
688
472
|
if (orgId === void 0) throw Errors.invalidInput("orgId", "required for useCapxulOrgTreasury");
|
|
689
|
-
|
|
473
|
+
const bootstrappedClient = requireBootstrappedClient(client, "org.treasury");
|
|
474
|
+
return unwrapCapxulResult(await bootstrappedClient.org(orgId).treasury(), bootstrappedClient._internal.telemetry);
|
|
690
475
|
},
|
|
691
|
-
enabled:
|
|
476
|
+
enabled: client !== null && enabled && orgId !== void 0
|
|
692
477
|
});
|
|
693
478
|
}
|
|
694
479
|
//#endregion
|
|
695
480
|
//#region src/hooks/use-capxul-create-org.ts
|
|
696
481
|
function useCapxulCreateOrg() {
|
|
697
|
-
const client =
|
|
482
|
+
const client = useCapxulClientOrNull();
|
|
698
483
|
const queryClient = useQueryClient();
|
|
699
484
|
return useMutation({
|
|
700
|
-
mutationFn: async (input) =>
|
|
485
|
+
mutationFn: async (input) => {
|
|
486
|
+
const bootstrappedClient = requireBootstrappedClient(client, "createOrg");
|
|
487
|
+
return unwrapCapxulResult(await bootstrappedClient.createOrg(input), bootstrappedClient._internal.telemetry, "mutation");
|
|
488
|
+
},
|
|
701
489
|
onSuccess: async () => {
|
|
702
490
|
await queryClient.invalidateQueries({ queryKey: capxulKeys.orgs });
|
|
703
491
|
}
|
|
704
492
|
});
|
|
705
493
|
}
|
|
706
494
|
//#endregion
|
|
707
|
-
//#region src/hooks/use-capxul-complete-personal-onboarding.ts
|
|
708
|
-
function useCapxulCompletePersonalOnboarding() {
|
|
709
|
-
const client = useCapxulClient();
|
|
710
|
-
const queryClient = useQueryClient();
|
|
711
|
-
return useMutation({
|
|
712
|
-
mutationFn: async (input) => unwrapCapxulResult(await client.onboarding.completePersonal(input), client._internal.telemetry, "mutation"),
|
|
713
|
-
onSuccess: async () => {
|
|
714
|
-
await Promise.all([queryClient.invalidateQueries({ queryKey: capxulKeys.profile }), queryClient.invalidateQueries({ queryKey: capxulKeys.accountLifecycle })]);
|
|
715
|
-
}
|
|
716
|
-
});
|
|
717
|
-
}
|
|
718
|
-
//#endregion
|
|
719
|
-
//#region src/hooks/use-capxul-complete-organization-onboarding.ts
|
|
720
|
-
function useCapxulCompleteOrganizationOnboarding() {
|
|
721
|
-
const client = useCapxulClient();
|
|
722
|
-
const queryClient = useQueryClient();
|
|
723
|
-
return useMutation({
|
|
724
|
-
mutationFn: async (input) => unwrapCapxulResult(await client.onboarding.completeOrganization(input), client._internal.telemetry, "mutation"),
|
|
725
|
-
onSuccess: async () => {
|
|
726
|
-
await Promise.all([
|
|
727
|
-
queryClient.invalidateQueries({ queryKey: capxulKeys.profile }),
|
|
728
|
-
queryClient.invalidateQueries({ queryKey: capxulKeys.accountLifecycle }),
|
|
729
|
-
queryClient.invalidateQueries({ queryKey: capxulKeys.orgs })
|
|
730
|
-
]);
|
|
731
|
-
}
|
|
732
|
-
});
|
|
733
|
-
}
|
|
734
|
-
//#endregion
|
|
735
495
|
//#region src/hooks/use-capxul-invite-member.ts
|
|
736
496
|
function useCapxulInviteMember(orgId) {
|
|
737
|
-
const client =
|
|
497
|
+
const client = useCapxulClientOrNull();
|
|
738
498
|
const queryClient = useQueryClient();
|
|
739
499
|
return useMutation({
|
|
740
500
|
mutationFn: async (input) => {
|
|
741
501
|
if (orgId === void 0) throw Errors.invalidInput("orgId", "required for useCapxulInviteMember");
|
|
742
|
-
|
|
502
|
+
const bootstrappedClient = requireBootstrappedClient(client, "org.invite");
|
|
503
|
+
return unwrapCapxulResult(await bootstrappedClient.org(orgId).invite(input), bootstrappedClient._internal.telemetry, "mutation");
|
|
743
504
|
},
|
|
744
505
|
onSuccess: async () => {
|
|
745
506
|
if (orgId === void 0) return;
|
|
@@ -750,12 +511,13 @@ function useCapxulInviteMember(orgId) {
|
|
|
750
511
|
//#endregion
|
|
751
512
|
//#region src/hooks/use-capxul-remove-member.ts
|
|
752
513
|
function useCapxulRemoveMember(orgId) {
|
|
753
|
-
const client =
|
|
514
|
+
const client = useCapxulClientOrNull();
|
|
754
515
|
const queryClient = useQueryClient();
|
|
755
516
|
return useMutation({
|
|
756
517
|
mutationFn: async (input) => {
|
|
757
518
|
if (orgId === void 0) throw Errors.invalidInput("orgId", "required for useCapxulRemoveMember");
|
|
758
|
-
|
|
519
|
+
const bootstrappedClient = requireBootstrappedClient(client, "org.removeMember");
|
|
520
|
+
return unwrapCapxulResult(await bootstrappedClient.org(orgId).removeMember(input), bootstrappedClient._internal.telemetry, "mutation");
|
|
759
521
|
},
|
|
760
522
|
onSuccess: async () => {
|
|
761
523
|
if (orgId === void 0) return;
|
|
@@ -766,12 +528,13 @@ function useCapxulRemoveMember(orgId) {
|
|
|
766
528
|
//#endregion
|
|
767
529
|
//#region src/hooks/use-capxul-assign-role.ts
|
|
768
530
|
function useCapxulAssignRole(orgId) {
|
|
769
|
-
const client =
|
|
531
|
+
const client = useCapxulClientOrNull();
|
|
770
532
|
const queryClient = useQueryClient();
|
|
771
533
|
return useMutation({
|
|
772
534
|
mutationFn: async (input) => {
|
|
773
535
|
if (orgId === void 0) throw Errors.invalidInput("orgId", "required for useCapxulAssignRole");
|
|
774
|
-
|
|
536
|
+
const bootstrappedClient = requireBootstrappedClient(client, "org.assignRole");
|
|
537
|
+
return unwrapCapxulResult(await bootstrappedClient.org(orgId).assignRole(input), bootstrappedClient._internal.telemetry, "mutation");
|
|
775
538
|
},
|
|
776
539
|
onSuccess: async () => {
|
|
777
540
|
if (orgId === void 0) return;
|
|
@@ -780,11 +543,273 @@ function useCapxulAssignRole(orgId) {
|
|
|
780
543
|
});
|
|
781
544
|
}
|
|
782
545
|
//#endregion
|
|
783
|
-
//#region src/
|
|
784
|
-
|
|
785
|
-
|
|
546
|
+
//#region src/headless/journey/journey-state.ts
|
|
547
|
+
const JOURNEY_KEY = "capxul.onboarding.journey.v1";
|
|
548
|
+
const VALIDATED_OWNER_KEY = "capxul.onboarding.validated-owner.v1";
|
|
549
|
+
const JOURNEY_VERSION = 1;
|
|
550
|
+
const MAX_DRAFT_TEXT_LENGTH = 200;
|
|
551
|
+
const MAX_ID_LENGTH = 160;
|
|
552
|
+
const MAX_STORED_JOURNEY_LENGTH = 8192;
|
|
553
|
+
const MAX_PAYOUT_DRAFT_ENTRIES = 10;
|
|
554
|
+
/**
|
|
555
|
+
* The owner whose stored journey may currently be attributed to observations.
|
|
556
|
+
* Persisted in sessionStorage rather than module memory so `currentOnboarding-
|
|
557
|
+
* JourneyId` is a pure function of durable state — attribution is the same
|
|
558
|
+
* regardless of the order of renders, reloads, or telemetry emits. Established
|
|
559
|
+
* only when an authenticated owner is (journey start, or an owner-matched load)
|
|
560
|
+
* and cleared at every auth boundary or on discard.
|
|
561
|
+
*/
|
|
562
|
+
function markOnboardingOwnerValidated(ownerId) {
|
|
563
|
+
try {
|
|
564
|
+
sessionStorage.setItem(VALIDATED_OWNER_KEY, ownerId);
|
|
565
|
+
} catch {}
|
|
566
|
+
}
|
|
567
|
+
function clearValidatedOnboardingOwner() {
|
|
568
|
+
try {
|
|
569
|
+
sessionStorage.removeItem(VALIDATED_OWNER_KEY);
|
|
570
|
+
} catch {}
|
|
571
|
+
}
|
|
572
|
+
function validatedOnboardingOwner() {
|
|
573
|
+
try {
|
|
574
|
+
return sessionStorage.getItem(VALIDATED_OWNER_KEY);
|
|
575
|
+
} catch {
|
|
576
|
+
return null;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
function startOnboardingJourney(input) {
|
|
580
|
+
const journey = {
|
|
581
|
+
version: JOURNEY_VERSION,
|
|
582
|
+
ownerId: input.ownerId,
|
|
583
|
+
journeyId: `journey_${createRandomId()}`,
|
|
584
|
+
intent: input.intent,
|
|
585
|
+
entryPoint: input.entryPoint,
|
|
586
|
+
step: input.step ?? "profile",
|
|
587
|
+
...input.origin === void 0 ? {} : { origin: input.origin }
|
|
588
|
+
};
|
|
589
|
+
markOnboardingOwnerValidated(journey.ownerId);
|
|
590
|
+
saveOnboardingJourney(journey);
|
|
591
|
+
return journey;
|
|
592
|
+
}
|
|
593
|
+
function loadOnboardingJourney(expectedOwnerId) {
|
|
594
|
+
try {
|
|
595
|
+
const raw = sessionStorage.getItem(JOURNEY_KEY);
|
|
596
|
+
if (raw === null) return null;
|
|
597
|
+
if (raw.length > MAX_STORED_JOURNEY_LENGTH) return discardInvalidJourney();
|
|
598
|
+
const value = JSON.parse(raw);
|
|
599
|
+
if (!isValidJourney(value)) return discardInvalidJourney();
|
|
600
|
+
if (expectedOwnerId !== void 0 && value.ownerId !== expectedOwnerId) return discardInvalidJourney();
|
|
601
|
+
if (expectedOwnerId !== void 0) markOnboardingOwnerValidated(expectedOwnerId);
|
|
602
|
+
return value;
|
|
603
|
+
} catch {
|
|
604
|
+
return discardInvalidJourney();
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
function saveOnboardingJourney(journey) {
|
|
608
|
+
if (!isValidJourney(journey)) {
|
|
609
|
+
discardInvalidJourney();
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
try {
|
|
613
|
+
sessionStorage.setItem(JOURNEY_KEY, JSON.stringify(journey));
|
|
614
|
+
} catch {}
|
|
615
|
+
}
|
|
616
|
+
function currentOnboardingJourneyId() {
|
|
617
|
+
const journey = loadOnboardingJourney();
|
|
618
|
+
const owner = validatedOnboardingOwner();
|
|
619
|
+
return journey !== null && owner !== null && journey.ownerId === owner ? journey.journeyId : void 0;
|
|
620
|
+
}
|
|
621
|
+
/** Stop observation attribution while no authenticated owner is established. */
|
|
622
|
+
function invalidateOnboardingJourneyObservation() {
|
|
623
|
+
clearValidatedOnboardingOwner();
|
|
624
|
+
}
|
|
625
|
+
/** Where the journey stands — the pure position apps map to their routes. */
|
|
626
|
+
function onboardingJourneyPosition(journey) {
|
|
627
|
+
const proof = journey.origin?.kind === "proof";
|
|
628
|
+
if (journey.organizationId !== void 0) return {
|
|
629
|
+
intent: journey.intent,
|
|
630
|
+
step: "provisioning",
|
|
631
|
+
organizationId: journey.organizationId,
|
|
632
|
+
proof
|
|
633
|
+
};
|
|
634
|
+
return {
|
|
635
|
+
intent: journey.intent,
|
|
636
|
+
step: journey.step,
|
|
637
|
+
proof
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* The interruption-recovery decision (behavior map §4): a journey that does
|
|
642
|
+
* not match the ready destination the app just rendered must resume; a
|
|
643
|
+
* matching one needs no recovery. Returns the journey to resume, or null.
|
|
644
|
+
*/
|
|
645
|
+
function activeOnboardingRecovery(ownerId, renderedReadyDestination) {
|
|
646
|
+
const journey = loadOnboardingJourney(ownerId);
|
|
647
|
+
if (journey === null) return null;
|
|
648
|
+
return (renderedReadyDestination?.kind === "personal" ? journey.intent === "personal" && journey.step === "provisioning" && journey.organizationId === void 0 : renderedReadyDestination?.kind === "organization" && journey.intent === "organization" && journey.step === "provisioning" && journey.organizationId === renderedReadyDestination.organizationId) ? null : journey;
|
|
649
|
+
}
|
|
650
|
+
function acknowledgeOnboardingDestination(destination, ownerId) {
|
|
651
|
+
const journey = loadOnboardingJourney(ownerId);
|
|
652
|
+
if (journey === null) return false;
|
|
653
|
+
if (!(destination.kind === "personal" ? journey.intent === "personal" && journey.step === "provisioning" : journey.intent === "organization" && journey.step === "provisioning" && journey.organizationId === destination.organizationId)) return false;
|
|
654
|
+
clearOnboardingJourney();
|
|
655
|
+
return true;
|
|
656
|
+
}
|
|
657
|
+
function clearOnboardingJourney() {
|
|
658
|
+
clearValidatedOnboardingOwner();
|
|
659
|
+
try {
|
|
660
|
+
sessionStorage.removeItem(JOURNEY_KEY);
|
|
661
|
+
} catch {}
|
|
662
|
+
}
|
|
663
|
+
function isValidJourney(value) {
|
|
664
|
+
if (!isRecord(value) || value.version !== JOURNEY_VERSION) return false;
|
|
665
|
+
if (!hasOnlyKeys(value, [
|
|
666
|
+
"version",
|
|
667
|
+
"ownerId",
|
|
668
|
+
"journeyId",
|
|
669
|
+
"intent",
|
|
670
|
+
"entryPoint",
|
|
671
|
+
"origin",
|
|
672
|
+
"step",
|
|
673
|
+
"profile",
|
|
674
|
+
"organization",
|
|
675
|
+
"stableHandle",
|
|
676
|
+
"organizationId"
|
|
677
|
+
])) return false;
|
|
678
|
+
if (!isBoundedId(value.ownerId)) return false;
|
|
679
|
+
if (!isBoundedId(value.journeyId) || !value.journeyId.startsWith("journey_")) return false;
|
|
680
|
+
if (value.intent !== "personal" && value.intent !== "organization") return false;
|
|
681
|
+
if (value.entryPoint !== "signup" && value.entryPoint !== "dashboard") return false;
|
|
682
|
+
if (value.step !== "profile" && value.step !== "organization" && value.step !== "provisioning") return false;
|
|
683
|
+
if (value.origin !== void 0 && !isOrigin(value.origin)) return false;
|
|
684
|
+
if (value.profile !== void 0 && !isProfileDraft(value.profile)) return false;
|
|
685
|
+
if (value.organization !== void 0 && !isOrganizationDraft(value.organization)) return false;
|
|
686
|
+
if (value.stableHandle !== void 0 && !isDraftText(value.stableHandle)) return false;
|
|
687
|
+
if (value.organizationId !== void 0 && !isBoundedId(value.organizationId)) return false;
|
|
688
|
+
if (value.organization !== void 0 && value.stableHandle !== value.organization.handle) return false;
|
|
689
|
+
if (value.stableHandle !== void 0 && value.organization === void 0) return false;
|
|
690
|
+
if (value.intent === "personal" && (value.step === "organization" || value.organization !== void 0 || value.stableHandle !== void 0)) return false;
|
|
691
|
+
if (value.step === "organization" && value.intent !== "organization") return false;
|
|
692
|
+
if (value.organizationId !== void 0 && value.intent !== "organization") return false;
|
|
693
|
+
if (value.organizationId !== void 0 && value.step !== "provisioning") return false;
|
|
694
|
+
return true;
|
|
695
|
+
}
|
|
696
|
+
function isOrigin(value) {
|
|
697
|
+
if (!isRecord(value)) return false;
|
|
698
|
+
if (value.kind === "personal") return hasOnlyKeys(value, ["kind"]);
|
|
699
|
+
if (value.kind === "organization") return hasOnlyKeys(value, ["kind", "organizationId"]) && isBoundedId(value.organizationId);
|
|
700
|
+
return value.kind === "proof" && hasOnlyKeys(value, ["kind", "organizationId"]) && (value.organizationId === void 0 || isBoundedId(value.organizationId));
|
|
701
|
+
}
|
|
702
|
+
function isProfileDraft(value) {
|
|
703
|
+
if (!isRecord(value) || !hasOnlyKeys(value, [
|
|
704
|
+
"displayName",
|
|
705
|
+
"country",
|
|
706
|
+
"withdrawalAddress",
|
|
707
|
+
"username",
|
|
708
|
+
"payoutAddresses"
|
|
709
|
+
]) || !isDraftText(value.displayName) || !isDraftText(value.country) || !isDraftText(value.withdrawalAddress)) return false;
|
|
710
|
+
if (value.username !== void 0 && !isDraftText(value.username)) return false;
|
|
711
|
+
if (value.payoutAddresses !== void 0 && !isPayoutDraftList(value.payoutAddresses)) return false;
|
|
712
|
+
return true;
|
|
713
|
+
}
|
|
714
|
+
function isPayoutDraftList(value) {
|
|
715
|
+
return Array.isArray(value) && value.length <= MAX_PAYOUT_DRAFT_ENTRIES && value.every((entry) => isRecord(entry) && hasOnlyKeys(entry, ["chain", "address"]) && (entry.chain === "evm" || entry.chain === "solana" || entry.chain === "starknet") && isDraftText(entry.address));
|
|
716
|
+
}
|
|
717
|
+
function isOrganizationDraft(value) {
|
|
718
|
+
if (!isRecord(value) || !hasOnlyKeys(value, [
|
|
719
|
+
"name",
|
|
720
|
+
"handle",
|
|
721
|
+
"country",
|
|
722
|
+
"bio",
|
|
723
|
+
"size"
|
|
724
|
+
]) || !isDraftText(value.name) || !isDraftText(value.handle) || !isDraftText(value.country)) return false;
|
|
725
|
+
if (value.bio !== void 0 && !isDraftText(value.bio)) return false;
|
|
726
|
+
if (value.size !== void 0 && !isDraftText(value.size)) return false;
|
|
727
|
+
return true;
|
|
728
|
+
}
|
|
729
|
+
function isDraftText(value) {
|
|
730
|
+
return typeof value === "string" && value.length <= MAX_DRAFT_TEXT_LENGTH;
|
|
731
|
+
}
|
|
732
|
+
function isBoundedId(value) {
|
|
733
|
+
return typeof value === "string" && value.length > 0 && value.length <= MAX_ID_LENGTH;
|
|
734
|
+
}
|
|
735
|
+
function discardInvalidJourney() {
|
|
736
|
+
clearValidatedOnboardingOwner();
|
|
737
|
+
try {
|
|
738
|
+
sessionStorage.removeItem(JOURNEY_KEY);
|
|
739
|
+
} catch {}
|
|
740
|
+
return null;
|
|
741
|
+
}
|
|
742
|
+
function createRandomId() {
|
|
743
|
+
if (typeof globalThis.crypto?.randomUUID === "function") return globalThis.crypto.randomUUID();
|
|
744
|
+
return Math.random().toString(36).slice(2);
|
|
745
|
+
}
|
|
746
|
+
function isRecord(value) {
|
|
747
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
748
|
+
}
|
|
749
|
+
function hasOnlyKeys(value, allowed) {
|
|
750
|
+
return Object.keys(value).every((key) => allowed.includes(key));
|
|
751
|
+
}
|
|
752
|
+
//#endregion
|
|
753
|
+
//#region src/internal/use-debounced-value.ts
|
|
754
|
+
/** The value as of `delayMs` after its last change (initial value immediate). */
|
|
755
|
+
function useDebouncedValue(value, delayMs) {
|
|
756
|
+
const [debounced, setDebounced] = useState(value);
|
|
757
|
+
useEffect(() => {
|
|
758
|
+
const timer = setTimeout(() => setDebounced(value), delayMs);
|
|
759
|
+
return () => clearTimeout(timer);
|
|
760
|
+
}, [value, delayMs]);
|
|
761
|
+
return debounced;
|
|
762
|
+
}
|
|
763
|
+
//#endregion
|
|
764
|
+
//#region src/headless/onboarding/use-capxul-username-availability.ts
|
|
765
|
+
/**
|
|
766
|
+
* #1062: debounced availability probe for the username the user is typing.
|
|
767
|
+
* Disabled until the candidate reaches the 3-character floor; a malformed or
|
|
768
|
+
* reserved candidate surfaces as the query's error (INVALID_INPUT with the
|
|
769
|
+
* boundary's message), not as `available: false` — only a username someone
|
|
770
|
+
* else owns is "taken".
|
|
771
|
+
*/
|
|
772
|
+
function useCapxulUsernameAvailability(username, options) {
|
|
773
|
+
const client = useCapxulClientOrNull();
|
|
774
|
+
const candidate = useDebouncedValue(username.trim(), options?.debounceMs ?? 300);
|
|
775
|
+
return useQuery({
|
|
776
|
+
queryKey: capxulKeys.usernameAvailability(candidate),
|
|
777
|
+
queryFn: async () => {
|
|
778
|
+
const bootstrappedClient = requireBootstrappedClient(client, "identity.usernameAvailable");
|
|
779
|
+
return unwrapCapxulResult(await bootstrappedClient.identity.usernameAvailable(candidate), bootstrappedClient._internal.telemetry);
|
|
780
|
+
},
|
|
781
|
+
enabled: client !== null && candidate.length >= 3 && (options?.enabled ?? true)
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
//#endregion
|
|
785
|
+
//#region src/headless/media/use-capxul-image-upload.ts
|
|
786
|
+
/**
|
|
787
|
+
* #1061 / ADR-0014: the whole upload→record sequence as one mutation —
|
|
788
|
+
* upload the blob, then bind the returned storage id to the profile image or
|
|
789
|
+
* the org logo. The url is the resolved serving URL (re-read from queries,
|
|
790
|
+
* never persisted by consumers).
|
|
791
|
+
*/
|
|
792
|
+
function useCapxulImageUpload() {
|
|
793
|
+
const client = useCapxulClientOrNull();
|
|
794
|
+
const queryClient = useQueryClient();
|
|
795
|
+
return useMutation({
|
|
796
|
+
mutationFn: async ({ blob, target }) => {
|
|
797
|
+
const bootstrappedClient = requireBootstrappedClient(client, "media.uploadImage");
|
|
798
|
+
const media = bootstrappedClient.media;
|
|
799
|
+
const telemetry = bootstrappedClient._internal.telemetry;
|
|
800
|
+
const uploaded = unwrapCapxulResult(await media.uploadImage(blob), telemetry);
|
|
801
|
+
if (target.kind === "profile") return { url: unwrapCapxulResult(await media.setProfileImage({ storageId: uploaded.storageId }), telemetry).imageUrl };
|
|
802
|
+
return { url: unwrapCapxulResult(await media.setOrgLogo({
|
|
803
|
+
orgId: target.orgId,
|
|
804
|
+
storageId: uploaded.storageId
|
|
805
|
+
}), telemetry).logoUrl };
|
|
806
|
+
},
|
|
807
|
+
onSuccess: async (_value, input) => {
|
|
808
|
+
await queryClient.invalidateQueries({ queryKey: input.target.kind === "profile" ? capxulKeys.profile : capxulKeys.orgs });
|
|
809
|
+
}
|
|
810
|
+
});
|
|
786
811
|
}
|
|
787
812
|
//#endregion
|
|
788
|
-
export { CapxulProvider, useCapxul, useCapxulAccountBalance, useCapxulAccountFund,
|
|
813
|
+
export { CapxulAuthenticationController, CapxulOnboardingController, CapxulProvider, acknowledgeOnboardingDestination, activeOnboardingRecovery, clearOnboardingJourney, currentOnboardingJourneyId, entered, invalidateOnboardingJourneyObservation, loadOnboardingJourney, onboardingJourneyPosition, saveOnboardingJourney, startOnboardingJourney, useCapxul, useCapxulAccountBalance, useCapxulAccountFund, useCapxulAssignRole, useCapxulAuth, useCapxulClientOrNull, useCapxulCreateOrg, useCapxulDestination, useCapxulIdentity, useCapxulImageUpload, useCapxulInviteMember, useCapxulOrgDeployRoles, useCapxulOrgMembers, useCapxulOrgRoles, useCapxulOrgTreasury, useCapxulOrgs, useCapxulPay, useCapxulPayment, useCapxulPayments, useCapxulPayout, useCapxulProfile, useCapxulRemoveMember, useCapxulSend, useCapxulSubAccountCreate, useCapxulSubAccountDelete, useCapxulSubAccountRename, useCapxulSubAccountsList, useCapxulTransfer, useCapxulTransitions, useCapxulUsernameAvailability, useCapxulWithdraw };
|
|
789
814
|
|
|
790
815
|
//# sourceMappingURL=index.mjs.map
|