@capxul/sdk-react 1.0.0-alpha.12 → 1.0.0-alpha.14
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/dist/index.d.mts +110 -75
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +205 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
package/dist/index.mjs
CHANGED
|
@@ -3,8 +3,6 @@ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, use
|
|
|
3
3
|
import { QueryClient, QueryClientProvider, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
4
|
import { captureExceptionSync, createCapxulClient, isSettingUpLifecycle } from "@capxul/sdk";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
-
import { Errors, isCapxulError } from "@capxul/errors";
|
|
7
|
-
import "@capxul/types";
|
|
8
6
|
//#region src/internal/capxul-bootstrap-context.tsx
|
|
9
7
|
const CapxulBootstrapContext = createContext(null);
|
|
10
8
|
function CapxulBootstrapProvider({ value, children }) {
|
|
@@ -149,6 +147,159 @@ function CapxulProvider(props) {
|
|
|
149
147
|
});
|
|
150
148
|
}
|
|
151
149
|
//#endregion
|
|
150
|
+
//#region ../errors/src/errors.ts
|
|
151
|
+
const CAPXUL_ERROR_CODES = [
|
|
152
|
+
"NOT_AUTHENTICATED",
|
|
153
|
+
"EMAIL_DELIVERY_FAILED",
|
|
154
|
+
"PROFILE_NOT_FOUND",
|
|
155
|
+
"SMART_ACCOUNT_MISSING",
|
|
156
|
+
"PLAYER_NOT_FOUND",
|
|
157
|
+
"ACCOUNT_NOT_FOUND",
|
|
158
|
+
"PROVIDER_ERROR",
|
|
159
|
+
"INVALID_INPUT",
|
|
160
|
+
"ENV_MISSING",
|
|
161
|
+
"NOT_IMPLEMENTED",
|
|
162
|
+
"VERIFICATION_REQUIRED",
|
|
163
|
+
"INSUFFICIENT_BALANCE",
|
|
164
|
+
"INVALID_RECIPIENT",
|
|
165
|
+
"ROLE_PERMISSION_DENIED",
|
|
166
|
+
"TRANSACTION_FAILED",
|
|
167
|
+
"RATE_LIMITED",
|
|
168
|
+
"NETWORK_ERROR",
|
|
169
|
+
"UNKNOWN",
|
|
170
|
+
"OTP_EXPIRED",
|
|
171
|
+
"SIGNER_REJECTED",
|
|
172
|
+
"CANCELLED",
|
|
173
|
+
"WRONG_STATE"
|
|
174
|
+
];
|
|
175
|
+
var CapxulError$1 = class extends Error {
|
|
176
|
+
code;
|
|
177
|
+
details;
|
|
178
|
+
correlationId;
|
|
179
|
+
layer;
|
|
180
|
+
constructor(code, message, options = {}) {
|
|
181
|
+
super(message, "cause" in options ? { cause: options.cause } : void 0);
|
|
182
|
+
this.name = "CapxulError";
|
|
183
|
+
this.code = code;
|
|
184
|
+
if (options.details !== void 0) this.details = options.details;
|
|
185
|
+
if (options.correlationId !== void 0) this.correlationId = options.correlationId;
|
|
186
|
+
if (options.layer !== void 0) this.layer = options.layer;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
function isCapxulError(value) {
|
|
190
|
+
return value instanceof CapxulError$1;
|
|
191
|
+
}
|
|
192
|
+
const Errors = {
|
|
193
|
+
notAuthenticated: (message, opts) => new CapxulError$1("NOT_AUTHENTICATED", message ?? "Not authenticated", opts?.failure_mode ? { details: { failure_mode: opts.failure_mode } } : void 0),
|
|
194
|
+
emailDeliveryFailed: (detail) => new CapxulError$1("EMAIL_DELIVERY_FAILED", "Failed to send email", { details: { detail } }),
|
|
195
|
+
profileNotFound: (authUserId) => new CapxulError$1("PROFILE_NOT_FOUND", `Profile not found for user ${authUserId}`, { details: { authUserId } }),
|
|
196
|
+
smartAccountMissing: (authUserId) => new CapxulError$1("SMART_ACCOUNT_MISSING", "Smart account not provisioned", { details: { authUserId } }),
|
|
197
|
+
playerNotFound: (playerId) => new CapxulError$1("PLAYER_NOT_FOUND", playerId ? `Openfort player ${playerId} not found` : "Openfort player not found", playerId === void 0 ? void 0 : { details: { playerId } }),
|
|
198
|
+
accountNotFound: (accountId) => new CapxulError$1("ACCOUNT_NOT_FOUND", accountId ? `Openfort account ${accountId} not found` : "Openfort account not found", accountId === void 0 ? void 0 : { details: { accountId } }),
|
|
199
|
+
providerError: (provider, operation, cause, opts) => {
|
|
200
|
+
const details = {
|
|
201
|
+
provider,
|
|
202
|
+
operation
|
|
203
|
+
};
|
|
204
|
+
if (opts?.failure_mode) details.failure_mode = opts.failure_mode;
|
|
205
|
+
return new CapxulError$1("PROVIDER_ERROR", `Provider error: ${provider} ${operation}`, {
|
|
206
|
+
cause,
|
|
207
|
+
details
|
|
208
|
+
});
|
|
209
|
+
},
|
|
210
|
+
invalidInput: (field, reason) => new CapxulError$1("INVALID_INPUT", `Invalid ${field}: ${reason}`, { details: {
|
|
211
|
+
field,
|
|
212
|
+
reason
|
|
213
|
+
} }),
|
|
214
|
+
envMissing: (name) => new CapxulError$1("ENV_MISSING", `Environment variable ${name} not configured`, { details: { name } }),
|
|
215
|
+
notImplemented: (domain, method) => new CapxulError$1("NOT_IMPLEMENTED", `${domain}.${method} is not yet implemented. This feature is planned for a future release.`, { details: {
|
|
216
|
+
domain,
|
|
217
|
+
method
|
|
218
|
+
} }),
|
|
219
|
+
/**
|
|
220
|
+
* Sibling factory to {@link Errors.providerError} for the per-state timeout
|
|
221
|
+
* path in flows (initially ProvisioningFlow's mint_openfort / deploy_safe /
|
|
222
|
+
* register_indexer `after:` timers). Same `PROVIDER_ERROR` code as
|
|
223
|
+
* `providerError`, plus a `details.reason: "timeout"` discriminator so
|
|
224
|
+
* downstream observers can distinguish failure modes without parsing the
|
|
225
|
+
* message string. The redacted message names the timeout budget; the
|
|
226
|
+
* native `cause` carries the same information for `reportError` fidelity.
|
|
227
|
+
*/
|
|
228
|
+
providerTimeout: (provider, operation, timeoutMs) => new CapxulError$1("PROVIDER_ERROR", `Provider error: ${provider} ${operation} (timeout exceeded ${timeoutMs}ms)`, {
|
|
229
|
+
details: {
|
|
230
|
+
provider,
|
|
231
|
+
operation,
|
|
232
|
+
reason: "timeout"
|
|
233
|
+
},
|
|
234
|
+
cause: /* @__PURE__ */ new Error(`timeout: ${operation} exceeded ${timeoutMs}ms`)
|
|
235
|
+
}),
|
|
236
|
+
verificationRequired: (details) => {
|
|
237
|
+
return new CapxulError$1("VERIFICATION_REQUIRED", "rail" in details ? `Verification is required before ${details.rail} can use ${details.currentKind}.` : `Verification tier ${details.requiredTier} is required.`, { details });
|
|
238
|
+
},
|
|
239
|
+
insufficientBalance: (asset, available, required) => new CapxulError$1("INSUFFICIENT_BALANCE", `Insufficient ${asset} balance`, { details: {
|
|
240
|
+
asset,
|
|
241
|
+
available,
|
|
242
|
+
required
|
|
243
|
+
} }),
|
|
244
|
+
invalidRecipient: (reason) => new CapxulError$1("INVALID_RECIPIENT", `Invalid recipient: ${reason}`, { details: { reason } }),
|
|
245
|
+
/**
|
|
246
|
+
* The org Zodiac Roles modifier REFUSED the spend on-chain (G4 · #547): the
|
|
247
|
+
* member's role condition (per-tx cap, per-day allowance, allowed recipient,
|
|
248
|
+
* or membership) was violated, so `execTransactionWithRole` reverted. This is
|
|
249
|
+
* a PERMISSION denial — explicitly NOT an `INSUFFICIENT_BALANCE` (the treasury
|
|
250
|
+
* held the funds; the role's authority is what bound). `reason` discriminates
|
|
251
|
+
* the violated condition (`over_cap` / `daily_cap` / `not_member` /
|
|
252
|
+
* `disallowed_recipient` / `condition_violation`); leak-safe — no on-chain
|
|
253
|
+
* identifiers ever enter the details.
|
|
254
|
+
*/
|
|
255
|
+
rolePermissionDenied: (details) => new CapxulError$1("ROLE_PERMISSION_DENIED", `Org role denied this spend on-chain (${details.reason}).`, { details: details.operation === void 0 ? { reason: details.reason } : {
|
|
256
|
+
reason: details.reason,
|
|
257
|
+
operation: details.operation
|
|
258
|
+
} }),
|
|
259
|
+
/**
|
|
260
|
+
* A transaction (or sponsored UserOp) failed. `details.reason` discriminates
|
|
261
|
+
* the failure mode for callers that must distinguish a CONFIRMED on-chain
|
|
262
|
+
* revert (`"onchain_revert"` — the op executed and reverted, e.g. a Zodiac
|
|
263
|
+
* Roles condition violation) from an inconclusive infra failure. A confirmed
|
|
264
|
+
* revert is the ONLY mode the org spend port may map to a roles denial.
|
|
265
|
+
*/
|
|
266
|
+
transactionFailed: (operation, cause, extra) => new CapxulError$1("TRANSACTION_FAILED", `Transaction failed: ${operation}`, {
|
|
267
|
+
cause,
|
|
268
|
+
details: extra?.reason === void 0 ? { operation } : {
|
|
269
|
+
operation,
|
|
270
|
+
reason: extra.reason
|
|
271
|
+
}
|
|
272
|
+
}),
|
|
273
|
+
rateLimited: (details) => new CapxulError$1("RATE_LIMITED", "Rate limit exceeded", details === void 0 ? void 0 : { details: { ...details } }),
|
|
274
|
+
networkError: (operation, cause) => new CapxulError$1("NETWORK_ERROR", `Network error during ${operation}`, {
|
|
275
|
+
cause,
|
|
276
|
+
details: { operation }
|
|
277
|
+
}),
|
|
278
|
+
unknown: (cause) => new CapxulError$1("UNKNOWN", "Unknown error", { cause }),
|
|
279
|
+
otpExpired: (details) => new CapxulError$1("OTP_EXPIRED", "Verification code has expired. Request a new one.", details === void 0 ? void 0 : { details: { ...details } }),
|
|
280
|
+
signerRejected: (details) => new CapxulError$1("SIGNER_REJECTED", "Signer rejected the request.", {
|
|
281
|
+
cause: details.cause,
|
|
282
|
+
details: details.reason === void 0 ? { source: details.source } : {
|
|
283
|
+
source: details.source,
|
|
284
|
+
reason: details.reason
|
|
285
|
+
}
|
|
286
|
+
}),
|
|
287
|
+
cancelled: (details) => new CapxulError$1("CANCELLED", "Operation was cancelled.", details === void 0 ? void 0 : { details: { ...details } }),
|
|
288
|
+
/**
|
|
289
|
+
* Method called from a flow state where its precondition fails (TA16). The
|
|
290
|
+
* SDK's method API short-circuits with this error before driving the
|
|
291
|
+
* internal state machine. `currentState` is the Effect-machine snapshot
|
|
292
|
+
* tag (stringified — substrate is `@effect/experimental/Machine` per
|
|
293
|
+
* `docs/canon/decisions/state-machine-substrate.md`); `validStates`
|
|
294
|
+
* enumerates the states the method accepts.
|
|
295
|
+
*/
|
|
296
|
+
wrongState: (details) => new CapxulError$1("WRONG_STATE", `${details.method} called from state '${details.currentState}'; valid states: ${details.validStates.join(", ")}`, { details: {
|
|
297
|
+
...details,
|
|
298
|
+
validStates: [...details.validStates]
|
|
299
|
+
} })
|
|
300
|
+
};
|
|
301
|
+
new Set(CAPXUL_ERROR_CODES);
|
|
302
|
+
//#endregion
|
|
152
303
|
//#region src/internal/require-bootstrapped-client.ts
|
|
153
304
|
/**
|
|
154
305
|
* Narrow the bootstrap-nullable client to a ready client inside a query /
|
|
@@ -659,12 +810,29 @@ async function invalidateMoneyState(queryClient, input) {
|
|
|
659
810
|
await Promise.all(invalidations);
|
|
660
811
|
}
|
|
661
812
|
//#endregion
|
|
813
|
+
//#region src/internal/reject-unresolved-actor.ts
|
|
814
|
+
/**
|
|
815
|
+
* Guard for mutation hooks whose variables carry an optional `actor` field
|
|
816
|
+
* (`useCapxulPay`, `useCapxulPayout`, `useCapxulAddDestination`,
|
|
817
|
+
* `useCapxulRemoveDestination`). Mirrors `requireActorScope` on the query path:
|
|
818
|
+
* an EXPLICITLY-passed `actor: undefined` (the `capxulOrgScope(notYetLoadedOrgId)`
|
|
819
|
+
* footgun) must fail loudly rather than silently fall back to the personal
|
|
820
|
+
* scope. Omitting the `actor` key entirely keeps the personal-scope default.
|
|
821
|
+
*
|
|
822
|
+
* Throws `CapxulError` code `INVALID_INPUT` with `details.field === "actor"`;
|
|
823
|
+
* called inside an async mutationFn it surfaces as `mutation.error: CapxulError`.
|
|
824
|
+
*/
|
|
825
|
+
function rejectUnresolvedActor(variables, operation) {
|
|
826
|
+
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`);
|
|
827
|
+
}
|
|
828
|
+
//#endregion
|
|
662
829
|
//#region src/hooks/use-capxul-money.ts
|
|
663
830
|
function useCapxulPay() {
|
|
664
831
|
const client = useCapxulClientOrNull();
|
|
665
832
|
const queryClient = useQueryClient();
|
|
666
833
|
return useMutation({
|
|
667
834
|
mutationFn: async (input) => {
|
|
835
|
+
rejectUnresolvedActor(input, "payments.pay");
|
|
668
836
|
const bootstrappedClient = requireBootstrappedClient(client, "payments.pay");
|
|
669
837
|
return unwrapCapxulResult(await bootstrappedClient.payments.pay(input), bootstrappedClient._internal.telemetry, "mutation");
|
|
670
838
|
},
|
|
@@ -682,6 +850,7 @@ function useCapxulPayout() {
|
|
|
682
850
|
const queryClient = useQueryClient();
|
|
683
851
|
return useMutation({
|
|
684
852
|
mutationFn: async (input) => {
|
|
853
|
+
rejectUnresolvedActor(input, "payments.payout");
|
|
685
854
|
const bootstrappedClient = requireBootstrappedClient(client, "payments.payout");
|
|
686
855
|
return unwrapCapxulResult(await bootstrappedClient.payments.payout(input), bootstrappedClient._internal.telemetry, "mutation");
|
|
687
856
|
},
|
|
@@ -923,6 +1092,7 @@ function useCapxulAddDestination() {
|
|
|
923
1092
|
const queryClient = useQueryClient();
|
|
924
1093
|
return useMutation({
|
|
925
1094
|
mutationFn: async (input) => {
|
|
1095
|
+
rejectUnresolvedActor(input, "destinations.add");
|
|
926
1096
|
const bootstrappedClient = requireBootstrappedClient(client, "destinations.add");
|
|
927
1097
|
return unwrapCapxulResult(await bootstrappedClient.destinations.add(input), bootstrappedClient._internal.telemetry, "mutation");
|
|
928
1098
|
},
|
|
@@ -937,6 +1107,7 @@ function useCapxulRemoveDestination() {
|
|
|
937
1107
|
const queryClient = useQueryClient();
|
|
938
1108
|
return useMutation({
|
|
939
1109
|
mutationFn: async (input) => {
|
|
1110
|
+
rejectUnresolvedActor(input, "destinations.remove");
|
|
940
1111
|
const bootstrappedClient = requireBootstrappedClient(client, "destinations.remove");
|
|
941
1112
|
return unwrapCapxulResult(await bootstrappedClient.destinations.remove(input), bootstrappedClient._internal.telemetry, "mutation");
|
|
942
1113
|
},
|
|
@@ -1344,6 +1515,38 @@ function toQuerySlotState(query) {
|
|
|
1344
1515
|
};
|
|
1345
1516
|
}
|
|
1346
1517
|
//#endregion
|
|
1518
|
+
//#region ../types/src/index.ts
|
|
1519
|
+
const SUPPORTED_CURRENCIES = [
|
|
1520
|
+
{
|
|
1521
|
+
code: "USD",
|
|
1522
|
+
symbol: "$",
|
|
1523
|
+
name: "US Dollar"
|
|
1524
|
+
},
|
|
1525
|
+
{
|
|
1526
|
+
code: "NGN",
|
|
1527
|
+
symbol: "NGN",
|
|
1528
|
+
name: "Nigerian Naira"
|
|
1529
|
+
},
|
|
1530
|
+
{
|
|
1531
|
+
code: "GHS",
|
|
1532
|
+
symbol: "GHS",
|
|
1533
|
+
name: "Ghanaian Cedi"
|
|
1534
|
+
},
|
|
1535
|
+
{
|
|
1536
|
+
code: "KES",
|
|
1537
|
+
symbol: "KSh",
|
|
1538
|
+
name: "Kenyan Shilling"
|
|
1539
|
+
},
|
|
1540
|
+
{
|
|
1541
|
+
code: "UGX",
|
|
1542
|
+
symbol: "USh",
|
|
1543
|
+
name: "Ugandan Shilling"
|
|
1544
|
+
}
|
|
1545
|
+
];
|
|
1546
|
+
SUPPORTED_CURRENCIES.map((currency) => currency.code);
|
|
1547
|
+
Object.fromEntries(SUPPORTED_CURRENCIES.map((currency) => [currency.code, currency.symbol]));
|
|
1548
|
+
Math.floor(Number.MAX_SAFE_INTEGER / 1e3);
|
|
1549
|
+
//#endregion
|
|
1347
1550
|
//#region src/headless/money/SendMoney.tsx
|
|
1348
1551
|
function SendMoney({ initialValue = null, slots, onSent }) {
|
|
1349
1552
|
const pay = useCapxulPay();
|