@capxul/sdk 0.1.0-alpha.9 → 0.2.0-alpha.4

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.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { componentsGeneric, anyApi } from 'convex/server';
2
- import { getAddress, parseUnits, createPublicClient, http, encodeFunctionData } from 'viem';
3
2
  import { toSafeSmartAccount } from 'permissionless/accounts';
3
+ import { getAddress, encodeFunctionData, encodePacked, keccak256, getContractAddress, parseUnits, createPublicClient, http } from 'viem';
4
4
  import { entryPoint07Address, createPaymasterClient, createBundlerClient } from 'viem/account-abstraction';
5
5
  import { baseSepolia } from 'viem/chains';
6
+ import { ConvexHttpClient } from 'convex/browser';
6
7
  import { setup, fromPromise, assign } from 'xstate';
7
- import { privateKeyToAccount } from 'viem/accounts';
8
+ import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts';
8
9
 
9
10
  // src/_generated/api.js
10
11
  var api = anyApi;
@@ -108,17 +109,271 @@ function track(...args) {
108
109
  const [name, props] = args;
109
110
  debugLog(`[TRACK] ${name} ${formatDebugValue(props ?? "")}`);
110
111
  }
111
- function formatDebugValue2(value) {
112
- if (value === void 0 || value === "") return "";
113
- if (typeof value === "string") return value;
112
+
113
+ // ../config/src/chain.ts
114
+ var CAPXUL_PAYMENTS_ADDRESS = "0xe740ea65521edc9bef0ea75d30b5334711db99f4";
115
+ var TEST_USDC_ADDRESS = "0xf09fcbb6df9f8f918e58f9c8ab15a76ade862b77";
116
+ var CAPXUL_API_BASE_URL = "https://elated-oyster-269.convex.site";
117
+
118
+ // ../config/src/timing.ts
119
+ var FLOW_INVOKE_TIMEOUT_MS = 3e4;
120
+ var WEBHOOK_FRESHNESS_WINDOW_MS = 5 * 60 * 1e3;
121
+
122
+ // ../config/src/errors.ts
123
+ var CapxulError2 = class extends Error {
124
+ code;
125
+ details;
126
+ correlationId;
127
+ layer;
128
+ constructor(code, message, options) {
129
+ super(message, options?.cause ? { cause: options.cause } : void 0);
130
+ this.code = code;
131
+ this.details = options?.details;
132
+ this.correlationId = options?.correlationId;
133
+ this.layer = options?.layer;
134
+ }
135
+ };
136
+ var Errors = {
137
+ notAuthenticated: () => new CapxulError2("NOT_AUTHENTICATED", "Not authenticated"),
138
+ profileNotFound: (authUserId) => new CapxulError2("PROFILE_NOT_FOUND", `Profile not found for user ${authUserId}`),
139
+ smartAccountMissing: (authUserId) => new CapxulError2("SMART_ACCOUNT_MISSING", `Smart account not provisioned for user ${authUserId}`),
140
+ envMissing: (name) => new CapxulError2("ENV_MISSING", `Environment variable ${name} not configured`),
141
+ openfortApi: (operation, cause) => new CapxulError2(
142
+ "PROVIDER_ERROR",
143
+ `Openfort ${operation} failed: ${cause instanceof Error ? cause.message : String(cause)}`,
144
+ { cause, details: { provider: "openfort", operation } }
145
+ ),
146
+ shieldApi: (status, detail) => new CapxulError2(
147
+ "PROVIDER_ERROR",
148
+ `Shield API error (${status}): ${detail}`,
149
+ { details: { provider: "shield", status } }
150
+ ),
151
+ providerError: (provider, operation, cause) => (
152
+ // Public `message` is redacted to a fixed shape so provider-side
153
+ // exception text never leaks to the client. The original `cause`
154
+ // is preserved on `Error.cause` for server-side debugging via
155
+ // observability sinks (Sentry, console traces).
156
+ new CapxulError2(
157
+ "PROVIDER_ERROR",
158
+ `Provider error: ${provider} ${operation}`,
159
+ { cause, details: { provider, operation } }
160
+ )
161
+ ),
162
+ invalidInput: (field, reason) => new CapxulError2(
163
+ "INVALID_INPUT",
164
+ `Invalid ${field}: ${reason}`,
165
+ { details: { field, reason } }
166
+ ),
167
+ playerNotFound: (playerId) => new CapxulError2(
168
+ "PLAYER_NOT_FOUND",
169
+ playerId ? `Openfort player ${playerId} not found` : "Openfort player not found"
170
+ ),
171
+ accountNotFound: (accountId) => new CapxulError2(
172
+ "ACCOUNT_NOT_FOUND",
173
+ accountId ? `Openfort account ${accountId} not found` : "Openfort smart account not found"
174
+ ),
175
+ invalidRecipient: (reason) => new CapxulError2("INVALID_RECIPIENT", reason),
176
+ permissionDenied: (reason = "Permission denied") => new CapxulError2("PERMISSION_DENIED", reason),
177
+ notFound: (resource, id) => new CapxulError2(
178
+ "NOT_FOUND",
179
+ id ? `${resource} ${id} not found` : `${resource} not found`
180
+ ),
181
+ idempotencyConflict: (details) => new CapxulError2(
182
+ "IDEMPOTENCY_CONFLICT",
183
+ "Idempotency key was already used for a different request",
184
+ { details }
185
+ ),
186
+ emailDeliveryFailed: (detail, details) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`, {
187
+ details
188
+ }),
189
+ rateLimited: (details) => new CapxulError2("RATE_LIMITED", "Request was rate limited", {
190
+ details: { ...details }
191
+ }),
192
+ internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`),
193
+ /**
194
+ * Verification gate. Surfaced when a request hits a verification
195
+ * boundary the actor cannot cross under their current state. Two
196
+ * variants share this code:
197
+ *
198
+ * - Rail gate (Withdrawals v1 W2, #465): the resolved
199
+ * `external_account.kind` routes to a withdrawal rail (e.g.
200
+ * `fiat_offramp`, `card_payout`) that is not yet supported. Carries
201
+ * `details.rail` + `details.currentKind`.
202
+ * - KYC tier gate (legacy / future): the actor's KYC tier is below
203
+ * the required tier. Carries `details.requiredTier`.
204
+ *
205
+ * Code is shared because both expose the same UX shape ("you cannot
206
+ * proceed until verification advances"); the `details.*` keys
207
+ * differentiate the route.
208
+ */
209
+ verificationRequired: (details) => {
210
+ const message = "rail" in details ? `Withdrawal rail "${details.rail}" (kind=${details.currentKind}) is not yet supported.` : `Verification tier ${details.requiredTier} is required.`;
211
+ return new CapxulError2("VERIFICATION_REQUIRED", message, {
212
+ details: { ...details }
213
+ });
214
+ }
215
+ };
216
+
217
+ // ../config/src/safe.ts
218
+ var SAFE_PROXY_FACTORY = "0x4e1dcf7ad4e460cfd30791ccc4f9c8a4f820ec67";
219
+ var SAFE_L2_SINGLETON = "0x29fcb43b46531bca003ddc8fcb67ffe91900c762";
220
+ var SAFE_MODULE_SETUP = "0x2dd68b007b46fbe91b9a7c3eda5a7a1063cb5b47";
221
+ var SAFE_4337_MODULE = "0x75cf11467937ce3f2f357ce24ffc3dbf8fd5c226";
222
+ var MULTI_SEND = "0x38869bf66a61cf6bdb996a6ae40d5853fd43b526";
223
+
224
+ // ../config/src/org-roles.ts
225
+ function roleKeyFromLabel(label) {
226
+ const bytes = new TextEncoder().encode(label);
227
+ const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
228
+ return "0x" + hex.padEnd(64, "0");
229
+ }
230
+ roleKeyFromLabel("OWNER");
231
+ roleKeyFromLabel("FINANCE_MANAGER");
232
+ roleKeyFromLabel("PAYMENTS_OPERATOR");
233
+ var defaultSafeDeriveConfig = {
234
+ safeProxyFactory: SAFE_PROXY_FACTORY,
235
+ safeL2Singleton: SAFE_L2_SINGLETON,
236
+ safeModuleSetup: SAFE_MODULE_SETUP,
237
+ safe4337Module: SAFE_4337_MODULE,
238
+ multiSend: MULTI_SEND
239
+ };
240
+ var SAFE_PROXY_CREATION_CODE = "0x608060405234801561001057600080fd5b506040516101e63803806101e68339818101604052602081101561003357600080fd5b8101908080519060200190929190505050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156100ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806101c46022913960400191505060405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505060ab806101196000396000f3fe608060405273ffffffffffffffffffffffffffffffffffffffff600054167fa619486e0000000000000000000000000000000000000000000000000000000060003514156050578060005260206000f35b3660008037600080366000845af43d6000803e60008114156070573d6000fd5b3d6000f3fea264697066735822122003d1488ee65e08fa41e58e888a9865554c535f2c77126a82cb4c0f917f31441364736f6c63430007060033496e76616c69642073696e676c65746f6e20616464726573732070726f7669646564";
241
+ var enableModulesAbi = [
242
+ {
243
+ type: "function",
244
+ name: "enableModules",
245
+ inputs: [{ type: "address[]", name: "modules" }],
246
+ outputs: [],
247
+ stateMutability: "nonpayable"
248
+ }
249
+ ];
250
+ var multiSendAbi = [
251
+ {
252
+ type: "function",
253
+ name: "multiSend",
254
+ inputs: [{ type: "bytes", name: "transactions" }],
255
+ outputs: [],
256
+ stateMutability: "payable"
257
+ }
258
+ ];
259
+ var setupAbi = [
260
+ {
261
+ type: "function",
262
+ name: "setup",
263
+ inputs: [
264
+ { type: "address[]", name: "owners" },
265
+ { type: "uint256", name: "threshold" },
266
+ { type: "address", name: "to" },
267
+ { type: "bytes", name: "data" },
268
+ { type: "address", name: "fallbackHandler" },
269
+ { type: "address", name: "paymentToken" },
270
+ { type: "uint256", name: "payment" },
271
+ { type: "address", name: "paymentReceiver" }
272
+ ],
273
+ outputs: [],
274
+ stateMutability: "nonpayable"
275
+ }
276
+ ];
277
+ function encodeInternalTransaction(tx) {
278
+ const encoded = encodePacked(
279
+ ["uint8", "address", "uint256", "uint256", "bytes"],
280
+ [
281
+ tx.operation,
282
+ tx.to,
283
+ tx.value,
284
+ BigInt(tx.data.slice(2).length / 2),
285
+ tx.data
286
+ ]
287
+ );
288
+ return encoded.slice(2);
289
+ }
290
+ function computeSaltNonce(ownerAddress) {
291
+ return BigInt(`0x${ownerAddress.toLowerCase().slice(2).padEnd(64, "0")}`);
292
+ }
293
+ function deriveSafeAddress(signerAddress, config = defaultSafeDeriveConfig) {
294
+ const saltNonce = computeSaltNonce(signerAddress);
295
+ const enableModulesData = encodeFunctionData({
296
+ abi: enableModulesAbi,
297
+ functionName: "enableModules",
298
+ args: [[config.safe4337Module]]
299
+ });
300
+ const innerTx = encodeInternalTransaction({
301
+ operation: 1,
302
+ to: config.safeModuleSetup,
303
+ value: 0n,
304
+ data: enableModulesData
305
+ });
306
+ const multiSendCallData = encodeFunctionData({
307
+ abi: multiSendAbi,
308
+ functionName: "multiSend",
309
+ args: [`0x${innerTx}`]
310
+ });
311
+ const initializer = encodeFunctionData({
312
+ abi: setupAbi,
313
+ functionName: "setup",
314
+ args: [
315
+ [signerAddress],
316
+ 1n,
317
+ config.multiSend,
318
+ multiSendCallData,
319
+ config.safe4337Module,
320
+ "0x0000000000000000000000000000000000000000",
321
+ 0n,
322
+ "0x0000000000000000000000000000000000000000"
323
+ ]
324
+ });
325
+ const deploymentCode = encodePacked(
326
+ ["bytes", "uint256"],
327
+ [SAFE_PROXY_CREATION_CODE, BigInt(config.safeL2Singleton)]
328
+ );
329
+ const salt = keccak256(
330
+ encodePacked(
331
+ ["bytes32", "uint256"],
332
+ [keccak256(encodePacked(["bytes"], [initializer])), saltNonce]
333
+ )
334
+ );
335
+ return getContractAddress({
336
+ from: config.safeProxyFactory,
337
+ salt,
338
+ bytecode: deploymentCode,
339
+ opcode: "CREATE2"
340
+ });
341
+ }
342
+
343
+ // src/internal/safe/account.ts
344
+ async function buildSafeAccount(signer, chain) {
114
345
  try {
115
- return JSON.stringify(value);
116
- } catch {
117
- return String(value);
346
+ const publicClient = createPublicClient({
347
+ chain: baseSepolia,
348
+ transport: http(chain.rpcUrl)
349
+ });
350
+ return await toSafeSmartAccount({
351
+ client: publicClient,
352
+ entryPoint: { address: entryPoint07Address, version: "0.7" },
353
+ version: "1.4.1",
354
+ owners: [signer],
355
+ saltNonce: computeSaltNonce2(signer.address),
356
+ safeSingletonAddress: SAFE_L2_SINGLETON,
357
+ safeProxyFactoryAddress: SAFE_PROXY_FACTORY,
358
+ safeModuleSetupAddress: SAFE_MODULE_SETUP,
359
+ safe4337ModuleAddress: SAFE_4337_MODULE,
360
+ safeModules: [],
361
+ setupTransactions: []
362
+ });
363
+ } catch (cause) {
364
+ throw new CapxulError({
365
+ code: "NETWORK_ERROR",
366
+ message: cause instanceof Error ? cause.message : String(cause),
367
+ cause,
368
+ details: { chainId: chain.chainId }
369
+ });
118
370
  }
119
371
  }
120
- function identify(userId, traits) {
121
- debugLog(`[IDENTIFY] ${userId} ${formatDebugValue2(traits ?? "")}`);
372
+ function computeSaltNonce2(ownerAddress) {
373
+ return BigInt(`0x${ownerAddress.toLowerCase().slice(2).padEnd(64, "0")}`);
374
+ }
375
+ function deriveSafeAddress2(signerAddress) {
376
+ return deriveSafeAddress(signerAddress, defaultSafeDeriveConfig);
122
377
  }
123
378
 
124
379
  // ../platform-kernel/src/ids.ts
@@ -139,7 +394,7 @@ var toAccountId = makePrefixedIdConstructor(
139
394
  );
140
395
  var toOrganizationId = makePrefixedIdConstructor("org", "organizationId");
141
396
  var toMemberId = makePrefixedIdConstructor(
142
- "mem",
397
+ "mb",
143
398
  "memberId"
144
399
  );
145
400
  var toSafeId = makePrefixedIdConstructor(
@@ -253,13 +508,13 @@ function brandExternalAccount(raw) {
253
508
  function createExternalAccountsClient(config = {}) {
254
509
  return {
255
510
  retrieve: async (externalAccountId) => {
256
- if (!config.data) {
511
+ if (!config._data) {
257
512
  return stub(
258
513
  "externalAccounts.retrieve"
259
514
  );
260
515
  }
261
516
  const [err, raw] = await tryCatch(
262
- config.data.query(api.externalAccounts.queries.retrievePersonal, {
517
+ config._data.query(api.externalAccounts.queries.retrievePersonal, {
263
518
  externalAccountId
264
519
  })
265
520
  );
@@ -281,11 +536,11 @@ function createExternalAccountsClient(config = {}) {
281
536
  return [null, brandExternalAccount(raw)];
282
537
  },
283
538
  remove: async (externalAccountId) => {
284
- if (!config.data) {
539
+ if (!config._data) {
285
540
  return stub("externalAccounts.remove");
286
541
  }
287
542
  const [err] = await tryCatch(
288
- config.data.mutation(api.externalAccounts.mutations.removePersonal, {
543
+ config._data.mutation(api.externalAccounts.mutations.removePersonal, {
289
544
  externalAccountId
290
545
  })
291
546
  );
@@ -300,17 +555,203 @@ function createExternalAccountsClient(config = {}) {
300
555
  };
301
556
  }
302
557
 
558
+ // src/core/sub-accounts.ts
559
+ function malformedWireError(reason, raw) {
560
+ return new CapxulError({
561
+ code: "PROVIDER_ERROR",
562
+ message: `convex brandSubAccount failed: ${reason}`,
563
+ details: {
564
+ provider: "convex",
565
+ operation: "brandSubAccount",
566
+ reason,
567
+ // PII discipline (`.claude/rules/posthog.md`): user-authored
568
+ // strings on the wire (`name`, `purpose`) are customer-confidential
569
+ // — sub-account names like "Q3 Acquisition Reserve" or
570
+ // "Vendor ABC payments" must not flow into telemetry. We emit a
571
+ // structural keys-only sample via a strict ALLOWLIST so any future
572
+ // wire-shape addition (e.g. `recipientEmail`, `tags`) is dropped
573
+ // by construction rather than leaked through a denylist gap.
574
+ sample: safeSampleShape(raw)
575
+ }
576
+ });
577
+ }
578
+ function safeSampleShape(raw) {
579
+ if (raw === null || typeof raw !== "object") {
580
+ return { type: typeof raw };
581
+ }
582
+ const r = raw;
583
+ const balance = r.balance;
584
+ return {
585
+ object: typeof r.object === "string" ? r.object : typeof r.object,
586
+ idPresent: typeof r.id === "string" && r.id.length > 0,
587
+ // First 4 chars only — enough to distinguish "sub_" prefixed IDs
588
+ // from accidental other resource IDs without leaking the full ID.
589
+ idPrefix: typeof r.id === "string" ? r.id.slice(0, 4) : void 0,
590
+ parentKind: r.parent !== null && typeof r.parent === "object" ? r.parent.kind : typeof r.parent,
591
+ status: r.status,
592
+ hasName: typeof r.name === "string",
593
+ hasPurpose: r.purpose !== void 0,
594
+ balanceShape: balance !== null && typeof balance === "object" ? Object.keys(balance).sort() : typeof balance,
595
+ createdAtType: typeof r.createdAt,
596
+ updatedAtType: typeof r.updatedAt
597
+ };
598
+ }
599
+ function isMoneyShape(v) {
600
+ if (typeof v !== "object" || v === null) return false;
601
+ const m = v;
602
+ return typeof m.value === "string" && typeof m.currency === "string";
603
+ }
604
+ function isParentShape(v) {
605
+ if (typeof v !== "object" || v === null) return false;
606
+ const p = v;
607
+ return (p.kind === "account" || p.kind === "organization") && typeof p.id === "string";
608
+ }
609
+ function isFiniteNonNegativeInteger(v) {
610
+ return typeof v === "number" && Number.isFinite(v) && Number.isInteger(v) && v >= 0;
611
+ }
612
+ function validateWireSubAccount(raw) {
613
+ if (typeof raw !== "object" || raw === null) {
614
+ return { ok: false, reason: "not an object" };
615
+ }
616
+ const r = raw;
617
+ if (r.object !== "sub_account") {
618
+ return { ok: false, reason: `object must be "sub_account" (got ${String(r.object)})` };
619
+ }
620
+ if (typeof r.id !== "string" || r.id.length === 0) {
621
+ return { ok: false, reason: "id must be a non-empty string" };
622
+ }
623
+ if (!isParentShape(r.parent)) {
624
+ return { ok: false, reason: "parent must be { kind: 'account' | 'organization', id: string }" };
625
+ }
626
+ if (typeof r.name !== "string") {
627
+ return { ok: false, reason: "name must be a string" };
628
+ }
629
+ if (r.purpose !== void 0 && typeof r.purpose !== "string") {
630
+ return { ok: false, reason: "purpose must be a string when present" };
631
+ }
632
+ if (r.status !== "active" && r.status !== "archived") {
633
+ return { ok: false, reason: `status must be "active" | "archived" (got ${String(r.status)})` };
634
+ }
635
+ if (!isMoneyShape(r.balance)) {
636
+ return { ok: false, reason: "balance must be { value: string, currency: string }" };
637
+ }
638
+ if (!isFiniteNonNegativeInteger(r.createdAt)) {
639
+ return { ok: false, reason: "createdAt must be a finite non-negative integer (epoch ms)" };
640
+ }
641
+ if (r.updatedAt !== void 0 && !isFiniteNonNegativeInteger(r.updatedAt)) {
642
+ return { ok: false, reason: "updatedAt must be a finite non-negative integer when present" };
643
+ }
644
+ return { ok: true, value: r };
645
+ }
646
+ function brandSubAccount(raw) {
647
+ const result = validateWireSubAccount(raw);
648
+ if (!result.ok) {
649
+ throw malformedWireError(result.reason, raw);
650
+ }
651
+ const wire = result.value;
652
+ return {
653
+ object: wire.object,
654
+ id: toSubAccountId(wire.id),
655
+ parent: wire.parent,
656
+ name: wire.name,
657
+ ...wire.purpose !== void 0 ? { purpose: wire.purpose } : {},
658
+ status: wire.status,
659
+ balance: wire.balance,
660
+ createdAt: new Date(wire.createdAt).toISOString()
661
+ };
662
+ }
663
+ function tryBrandSubAccount(raw) {
664
+ try {
665
+ return [null, brandSubAccount(raw)];
666
+ } catch (err) {
667
+ if (err instanceof CapxulError && err.code === "PROVIDER_ERROR") {
668
+ return [err, null];
669
+ }
670
+ return [
671
+ malformedWireError(
672
+ err instanceof Error ? err.message : String(err),
673
+ raw
674
+ ),
675
+ null
676
+ ];
677
+ }
678
+ }
679
+ function createSubAccountsClient(config = {}) {
680
+ return {
681
+ retrieve: async (subAccountId) => {
682
+ if (!config._data) {
683
+ return stub("subAccounts.retrieve");
684
+ }
685
+ const [err, raw] = await tryCatch(
686
+ config._data.query(api.subAccounts.queries.retrieve, {
687
+ subAccountId
688
+ })
689
+ );
690
+ if (err) {
691
+ return [
692
+ fromConvexError(err),
693
+ null
694
+ ];
695
+ }
696
+ if (!raw) {
697
+ return [
698
+ new CapxulError({
699
+ code: "NOT_FOUND",
700
+ message: `sub_account ${subAccountId} not found`
701
+ }),
702
+ null
703
+ ];
704
+ }
705
+ const [brandErr, branded] = tryBrandSubAccount(raw);
706
+ if (brandErr) {
707
+ return [brandErr, null];
708
+ }
709
+ return [null, branded];
710
+ },
711
+ remove: async (subAccountId) => {
712
+ if (!config._data) {
713
+ return stub("subAccounts.remove");
714
+ }
715
+ const [err, raw] = await tryCatch(
716
+ config._data.mutation(api.subAccounts.mutations.archive, {
717
+ subAccountId
718
+ })
719
+ );
720
+ if (err) {
721
+ return [
722
+ fromConvexError(err),
723
+ null
724
+ ];
725
+ }
726
+ if (!raw) {
727
+ return [
728
+ new CapxulError({
729
+ code: "NOT_FOUND",
730
+ message: `sub_account ${subAccountId} not found`
731
+ }),
732
+ null
733
+ ];
734
+ }
735
+ const [brandErr, branded] = tryBrandSubAccount(raw);
736
+ if (brandErr) {
737
+ return [brandErr, null];
738
+ }
739
+ return [null, branded];
740
+ }
741
+ };
742
+ }
743
+
303
744
  // src/core/accounts.ts
304
745
  function createAccountExternalAccountsClient(config) {
305
746
  return {
306
747
  create: async (input) => {
307
- if (!config.data) {
748
+ if (!config._data) {
308
749
  return stub(
309
750
  "accounts.externalAccounts.create"
310
751
  );
311
752
  }
312
753
  const [err, raw] = await tryCatch(
313
- config.data.mutation(
754
+ config._data.mutation(
314
755
  api.externalAccounts.mutations.createPersonal,
315
756
  {
316
757
  kind: input.kind,
@@ -348,13 +789,13 @@ function createAccountExternalAccountsClient(config) {
348
789
  ];
349
790
  },
350
791
  list: async (input) => {
351
- if (!config.data) {
792
+ if (!config._data) {
352
793
  return stub(
353
794
  "accounts.externalAccounts.list"
354
795
  );
355
796
  }
356
797
  const [err, result] = await tryCatch(
357
- config.data.query(api.externalAccounts.queries.listPersonal, {
798
+ config._data.query(api.externalAccounts.queries.listPersonal, {
358
799
  limit: input.limit,
359
800
  cursor: input.cursor
360
801
  })
@@ -377,13 +818,13 @@ function createAccountExternalAccountsClient(config) {
377
818
  ];
378
819
  },
379
820
  retrieve: async (externalAccountId) => {
380
- if (!config.data) {
821
+ if (!config._data) {
381
822
  return stub(
382
823
  "accounts.externalAccounts.retrieve"
383
824
  );
384
825
  }
385
826
  const [err, raw] = await tryCatch(
386
- config.data.query(api.externalAccounts.queries.retrievePersonal, {
827
+ config._data.query(api.externalAccounts.queries.retrievePersonal, {
387
828
  externalAccountId
388
829
  })
389
830
  );
@@ -410,11 +851,11 @@ function createAccountExternalAccountsClient(config) {
410
851
  ];
411
852
  },
412
853
  remove: async (externalAccountId) => {
413
- if (!config.data) {
854
+ if (!config._data) {
414
855
  return stub("accounts.externalAccounts.remove");
415
856
  }
416
857
  const [err] = await tryCatch(
417
- config.data.mutation(api.externalAccounts.mutations.removePersonal, {
858
+ config._data.mutation(api.externalAccounts.mutations.removePersonal, {
418
859
  externalAccountId
419
860
  })
420
861
  );
@@ -428,14 +869,162 @@ function createAccountExternalAccountsClient(config) {
428
869
  }
429
870
  };
430
871
  }
431
- function createAccountsClient(config = {}) {
872
+ function createAccountSubAccountsClient(config) {
432
873
  return {
433
- retrieve: async (accountId) => {
434
- if (!config.data) {
435
- return stub("accounts.retrieve");
436
- }
874
+ create: async (input) => {
875
+ if (!config._data) {
876
+ return stub(
877
+ "accounts.subAccounts.create"
878
+ );
879
+ }
880
+ const [err, raw] = await tryCatch(
881
+ config._data.mutation(api.subAccounts.mutations.create, {
882
+ parent: { kind: "account", id: input.accountId },
883
+ name: input.name,
884
+ purpose: input.purpose
885
+ })
886
+ );
887
+ if (err) {
888
+ return [
889
+ fromConvexError(err),
890
+ null
891
+ ];
892
+ }
893
+ if (!raw) {
894
+ return [
895
+ new CapxulError({
896
+ code: "NOT_FOUND",
897
+ message: "sub_account creation returned no resource"
898
+ }),
899
+ null
900
+ ];
901
+ }
902
+ const [brandErr, branded] = tryBrandSubAccount(raw);
903
+ if (brandErr) {
904
+ return [
905
+ brandErr,
906
+ null
907
+ ];
908
+ }
909
+ return [null, branded];
910
+ },
911
+ list: async (input) => {
912
+ if (!config._data) {
913
+ return stub(
914
+ "accounts.subAccounts.list"
915
+ );
916
+ }
917
+ const [err, rows] = await tryCatch(
918
+ config._data.query(api.subAccounts.queries.listByAccount, {
919
+ accountId: input.accountId
920
+ })
921
+ );
922
+ if (err) {
923
+ return [
924
+ fromConvexError(err),
925
+ null
926
+ ];
927
+ }
928
+ const branded = [];
929
+ for (const row of rows) {
930
+ const [brandErr, value] = tryBrandSubAccount(row);
931
+ if (brandErr) {
932
+ return [
933
+ brandErr,
934
+ null
935
+ ];
936
+ }
937
+ branded.push(value);
938
+ }
939
+ return [
940
+ null,
941
+ {
942
+ object: "list",
943
+ data: branded,
944
+ page: { hasMore: false }
945
+ }
946
+ ];
947
+ },
948
+ retrieve: async (subAccountId) => {
949
+ if (!config._data) {
950
+ return stub(
951
+ "accounts.subAccounts.retrieve"
952
+ );
953
+ }
954
+ const [err, raw] = await tryCatch(
955
+ config._data.query(api.subAccounts.queries.retrieve, {
956
+ subAccountId
957
+ })
958
+ );
959
+ if (err) {
960
+ return [
961
+ fromConvexError(err),
962
+ null
963
+ ];
964
+ }
965
+ if (!raw) {
966
+ return [
967
+ new CapxulError({
968
+ code: "NOT_FOUND",
969
+ message: `sub_account ${subAccountId} not found`
970
+ }),
971
+ null
972
+ ];
973
+ }
974
+ const [brandErr, branded] = tryBrandSubAccount(raw);
975
+ if (brandErr) {
976
+ return [
977
+ brandErr,
978
+ null
979
+ ];
980
+ }
981
+ return [null, branded];
982
+ },
983
+ remove: async (subAccountId) => {
984
+ if (!config._data) {
985
+ return stub(
986
+ "accounts.subAccounts.remove"
987
+ );
988
+ }
989
+ const [err, raw] = await tryCatch(
990
+ config._data.mutation(api.subAccounts.mutations.archive, {
991
+ subAccountId
992
+ })
993
+ );
994
+ if (err) {
995
+ return [
996
+ fromConvexError(err),
997
+ null
998
+ ];
999
+ }
1000
+ if (!raw) {
1001
+ return [
1002
+ new CapxulError({
1003
+ code: "NOT_FOUND",
1004
+ message: `sub_account ${subAccountId} not found`
1005
+ }),
1006
+ null
1007
+ ];
1008
+ }
1009
+ const [brandErr, branded] = tryBrandSubAccount(raw);
1010
+ if (brandErr) {
1011
+ return [
1012
+ brandErr,
1013
+ null
1014
+ ];
1015
+ }
1016
+ return [null, branded];
1017
+ }
1018
+ };
1019
+ }
1020
+ function createAccountsClient(config = {}) {
1021
+ return {
1022
+ retrieve: async (accountId) => {
1023
+ if (!config._data) {
1024
+ return stub("accounts.retrieve");
1025
+ }
437
1026
  try {
438
- const account = await config.data.query(
1027
+ const account = await config._data.query(
439
1028
  api.openfort.queries.getMyAccount,
440
1029
  {}
441
1030
  );
@@ -459,7 +1048,7 @@ function createAccountsClient(config = {}) {
459
1048
  },
460
1049
  lookup: async () => stub("accounts.lookup"),
461
1050
  update: async (input) => {
462
- if (!config.data) {
1051
+ if (!config._data) {
463
1052
  return stub("accounts.update");
464
1053
  }
465
1054
  if (input.countryCode !== void 0) {
@@ -473,7 +1062,7 @@ function createAccountsClient(config = {}) {
473
1062
  ];
474
1063
  }
475
1064
  try {
476
- const current = await config.data.query(
1065
+ const current = await config._data.query(
477
1066
  api.openfort.queries.getMyAccount,
478
1067
  {}
479
1068
  );
@@ -490,11 +1079,11 @@ function createAccountsClient(config = {}) {
490
1079
  null
491
1080
  ];
492
1081
  }
493
- await config.data.mutation(api.openfort.mutations.updateProfile, {
1082
+ await config._data.mutation(api.openfort.mutations.updateProfile, {
494
1083
  displayName: input.name,
495
1084
  username: input.username
496
1085
  });
497
- const updated = await config.data.query(
1086
+ const updated = await config._data.query(
498
1087
  api.openfort.queries.getMyAccount,
499
1088
  {}
500
1089
  );
@@ -504,7 +1093,7 @@ function createAccountsClient(config = {}) {
504
1093
  }
505
1094
  },
506
1095
  provisionPersonal: async (input) => {
507
- if (!config.data) {
1096
+ if (!config._data) {
508
1097
  return stub(
509
1098
  "accounts.provisionPersonal"
510
1099
  );
@@ -519,17 +1108,17 @@ function createAccountsClient(config = {}) {
519
1108
  ];
520
1109
  }
521
1110
  try {
522
- await config.data.mutation(
1111
+ await config._data.mutation(
523
1112
  api.safe.mutations.provisionLocalPersonalAccount,
524
1113
  {
525
1114
  displayName: input.displayName,
526
1115
  username: input.username,
527
1116
  countryCode: input.countryCode,
528
1117
  eoaAddress: input.signerProvider.signerAddress,
529
- safeAddress: input.signerProvider.safeAddress
1118
+ safeAddress: deriveSafeAddress2(input.signerProvider.signerAddress)
530
1119
  }
531
1120
  );
532
- const account = await config.data.query(
1121
+ const account = await config._data.query(
533
1122
  api.openfort.queries.getMyAccount,
534
1123
  {}
535
1124
  );
@@ -552,11 +1141,11 @@ function createAccountsClient(config = {}) {
552
1141
  },
553
1142
  safes: {
554
1143
  retrieve: async (safeId) => {
555
- if (!config.data) {
1144
+ if (!config._data) {
556
1145
  return stub("accounts.safes.retrieve");
557
1146
  }
558
1147
  try {
559
- const safe = await config.data.query(
1148
+ const safe = await config._data.query(
560
1149
  api.safe.queries.retrieveAccountSafe,
561
1150
  { safeId }
562
1151
  );
@@ -583,19 +1172,50 @@ function createAccountsClient(config = {}) {
583
1172
  retrieve: async () => stub("accounts.kycProfiles.retrieve")
584
1173
  },
585
1174
  externalAccounts: createAccountExternalAccountsClient(config),
586
- subAccounts: {
587
- create: async () => stub("accounts.subAccounts.create"),
588
- list: async () => stub("accounts.subAccounts.list"),
589
- retrieve: async () => stub("accounts.subAccounts.retrieve"),
590
- remove: async () => stub("accounts.subAccounts.remove")
591
- },
1175
+ subAccounts: createAccountSubAccountsClient(config),
592
1176
  balanceLedger: {
593
- list: async () => stub(
594
- "accounts.balanceLedger.list"
595
- ),
596
- retrieve: async () => stub(
597
- "accounts.balanceLedger.retrieve"
598
- )
1177
+ list: async (input) => {
1178
+ if (!config._data) {
1179
+ return stub(
1180
+ "accounts.balanceLedger.list"
1181
+ );
1182
+ }
1183
+ try {
1184
+ const accountId = input.accountId.replace(/^acct_/, "");
1185
+ const page = await config._data.query(
1186
+ api.balanceLedger.queries.listForAccount,
1187
+ { accountId, limit: input.limit, cursor: input.cursor }
1188
+ );
1189
+ return [null, page];
1190
+ } catch (cause) {
1191
+ return [fromConvexError(cause), null];
1192
+ }
1193
+ },
1194
+ retrieve: async (entryId) => {
1195
+ if (!config._data) {
1196
+ return stub(
1197
+ "accounts.balanceLedger.retrieve"
1198
+ );
1199
+ }
1200
+ try {
1201
+ const entry = await config._data.query(
1202
+ api.balanceLedger.queries.retrieve,
1203
+ { entryId }
1204
+ );
1205
+ if (!entry) {
1206
+ return [
1207
+ new CapxulError({
1208
+ code: "NOT_FOUND",
1209
+ message: `balance_ledger_entry ${entryId} not found`
1210
+ }),
1211
+ null
1212
+ ];
1213
+ }
1214
+ return [null, entry];
1215
+ } catch (cause) {
1216
+ return [fromConvexError(cause), null];
1217
+ }
1218
+ }
599
1219
  }
600
1220
  };
601
1221
  }
@@ -609,120 +1229,11 @@ function createApiKeysClient() {
609
1229
  revoke: async () => stub("apiKeys.revoke")
610
1230
  };
611
1231
  }
612
-
613
- // ../config/src/chain.ts
614
- var CAPXUL_PAYMENTS_ADDRESS = "0xe740ea65521edc9bef0ea75d30b5334711db99f4";
615
- var TEST_USDC_ADDRESS = "0xf09fcbb6df9f8f918e58f9c8ab15a76ade862b77";
616
- var CAPXUL_API_BASE_URL = "https://elated-oyster-269.convex.site";
617
-
618
- // ../config/src/timing.ts
619
- var FLOW_INVOKE_TIMEOUT_MS = 3e4;
620
- var WEBHOOK_FRESHNESS_WINDOW_MS = 5 * 60 * 1e3;
621
-
622
- // ../config/src/errors.ts
623
- var CapxulError2 = class extends Error {
624
- code;
625
- details;
626
- correlationId;
627
- layer;
628
- constructor(code, message, options) {
629
- super(message, options?.cause ? { cause: options.cause } : void 0);
630
- this.code = code;
631
- this.details = options?.details;
632
- this.correlationId = options?.correlationId;
633
- this.layer = options?.layer;
634
- }
635
- };
636
- var Errors = {
637
- notAuthenticated: () => new CapxulError2("NOT_AUTHENTICATED", "Not authenticated"),
638
- profileNotFound: (authUserId) => new CapxulError2("PROFILE_NOT_FOUND", `Profile not found for user ${authUserId}`),
639
- smartAccountMissing: (authUserId) => new CapxulError2("SMART_ACCOUNT_MISSING", `Smart account not provisioned for user ${authUserId}`),
640
- envMissing: (name) => new CapxulError2("ENV_MISSING", `Environment variable ${name} not configured`),
641
- openfortApi: (operation, cause) => new CapxulError2(
642
- "PROVIDER_ERROR",
643
- `Openfort ${operation} failed: ${cause instanceof Error ? cause.message : String(cause)}`,
644
- { cause, details: { provider: "openfort", operation } }
645
- ),
646
- shieldApi: (status, detail) => new CapxulError2(
647
- "PROVIDER_ERROR",
648
- `Shield API error (${status}): ${detail}`,
649
- { details: { provider: "shield", status } }
650
- ),
651
- providerError: (provider, operation, cause) => new CapxulError2(
652
- "PROVIDER_ERROR",
653
- `${provider} ${operation} failed: ${cause instanceof Error ? cause.message : String(cause)}`,
654
- { cause, details: { provider, operation } }
655
- ),
656
- invalidInput: (field, reason) => new CapxulError2(
657
- "INVALID_INPUT",
658
- `Invalid ${field}: ${reason}`,
659
- { details: { field, reason } }
660
- ),
661
- playerNotFound: (playerId) => new CapxulError2(
662
- "PLAYER_NOT_FOUND",
663
- playerId ? `Openfort player ${playerId} not found` : "Openfort player not found"
664
- ),
665
- accountNotFound: (accountId) => new CapxulError2(
666
- "ACCOUNT_NOT_FOUND",
667
- accountId ? `Openfort account ${accountId} not found` : "Openfort smart account not found"
668
- ),
669
- invalidRecipient: (reason) => new CapxulError2("INVALID_RECIPIENT", reason),
670
- permissionDenied: (reason = "Permission denied") => new CapxulError2("PERMISSION_DENIED", reason),
671
- notFound: (resource, id) => new CapxulError2(
672
- "NOT_FOUND",
673
- id ? `${resource} ${id} not found` : `${resource} not found`
674
- ),
675
- idempotencyConflict: (details) => new CapxulError2(
676
- "IDEMPOTENCY_CONFLICT",
677
- "Idempotency key was already used for a different request",
678
- { details }
679
- ),
680
- emailDeliveryFailed: (detail, details) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`, {
681
- details
682
- }),
683
- rateLimited: (details) => new CapxulError2("RATE_LIMITED", "Request was rate limited", {
684
- details: { ...details }
685
- }),
686
- internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`),
687
- /**
688
- * Verification gate. Surfaced when a request hits a verification
689
- * boundary the actor cannot cross under their current state. Two
690
- * variants share this code:
691
- *
692
- * - Rail gate (Withdrawals v1 W2, #465): the resolved
693
- * `external_account.kind` routes to a withdrawal rail (e.g.
694
- * `fiat_offramp`, `card_payout`) that is not yet supported. Carries
695
- * `details.rail` + `details.currentKind`.
696
- * - KYC tier gate (legacy / future): the actor's KYC tier is below
697
- * the required tier. Carries `details.requiredTier`.
698
- *
699
- * Code is shared because both expose the same UX shape ("you cannot
700
- * proceed until verification advances"); the `details.*` keys
701
- * differentiate the route.
702
- */
703
- verificationRequired: (details) => {
704
- const message = "rail" in details ? `Withdrawal rail "${details.rail}" (kind=${details.currentKind}) is not yet supported.` : `Verification tier ${details.requiredTier} is required.`;
705
- return new CapxulError2("VERIFICATION_REQUIRED", message, {
706
- details: { ...details }
707
- });
708
- }
709
- };
710
-
711
- // ../config/src/safe.ts
712
- var SAFE_PROXY_FACTORY = "0x4e1dcf7ad4e460cfd30791ccc4f9c8a4f820ec67";
713
- var SAFE_L2_SINGLETON = "0x29fcb43b46531bca003ddc8fcb67ffe91900c762";
714
- var SAFE_MODULE_SETUP = "0x2dd68b007b46fbe91b9a7c3eda5a7a1063cb5b47";
715
- var SAFE_4337_MODULE = "0x75cf11467937ce3f2f357ce24ffc3dbf8fd5c226";
716
-
717
- // ../config/src/org-roles.ts
718
- function roleKeyFromLabel(label) {
719
- const bytes = new TextEncoder().encode(label);
720
- const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
721
- return "0x" + hex.padEnd(64, "0");
1232
+ function createDefaultDataClient(convexUrl, jwt) {
1233
+ const client = new ConvexHttpClient(convexUrl);
1234
+ client.setAuth(jwt);
1235
+ return client;
722
1236
  }
723
- roleKeyFromLabel("OWNER");
724
- roleKeyFromLabel("FINANCE_MANAGER");
725
- roleKeyFromLabel("TEAM_LEAD");
726
1237
 
727
1238
  // src/transport.ts
728
1239
  function makeHttpTransport(config) {
@@ -1053,7 +1564,7 @@ function readNonEmptyString(value) {
1053
1564
 
1054
1565
  // src/core/auth.ts
1055
1566
  function createAuthClient(config = {}) {
1056
- let dataClient = config.data ?? null;
1567
+ let dataClient = config._data ?? null;
1057
1568
  const sessionStore = config.auth?.sessionStore ?? createMemorySessionStore();
1058
1569
  const getTransport = createTransportProvider(config);
1059
1570
  return {
@@ -1109,11 +1620,20 @@ function createAuthClient(config = {}) {
1109
1620
  ).toISOString()
1110
1621
  };
1111
1622
  sessionStore.set(session);
1112
- if (config.auth?.createDataClient) {
1623
+ if (!dataClient) {
1113
1624
  try {
1114
- dataClient = await config.auth.createDataClient(session);
1115
- mutableConfig(config).data = dataClient;
1116
- transport.markAuthenticated({ dataClient });
1625
+ const convexUrl = transport.convexUrl;
1626
+ if (!convexUrl || !session.convexJwt) {
1627
+ return [
1628
+ new CapxulError({
1629
+ code: "NETWORK_ERROR",
1630
+ message: "Cannot create data client: missing convex URL or JWT."
1631
+ }),
1632
+ null
1633
+ ];
1634
+ }
1635
+ dataClient = createDefaultDataClient(convexUrl, session.convexJwt);
1636
+ mutableConfig(config)._data = dataClient;
1117
1637
  } catch (cause) {
1118
1638
  return [
1119
1639
  new CapxulError({
@@ -1124,7 +1644,15 @@ function createAuthClient(config = {}) {
1124
1644
  null
1125
1645
  ];
1126
1646
  }
1647
+ } else {
1648
+ const injected = dataClient;
1649
+ if (typeof injected.refreshAuth === "function") {
1650
+ injected.refreshAuth();
1651
+ } else if (typeof injected.setAuth === "function" && session.convexJwt) {
1652
+ injected.setAuth(session.convexJwt);
1653
+ }
1127
1654
  }
1655
+ transport.markAuthenticated({ dataClient });
1128
1656
  if (!dataClient) {
1129
1657
  return [
1130
1658
  new CapxulError({
@@ -1152,7 +1680,7 @@ function createAuthClient(config = {}) {
1152
1680
  },
1153
1681
  completeBootstrap: async (input) => {
1154
1682
  const session = sessionStore.get();
1155
- const data = dataClient ?? config.data;
1683
+ const data = dataClient ?? config._data;
1156
1684
  if (!session || !data) {
1157
1685
  return [
1158
1686
  new CapxulError({
@@ -1162,23 +1690,38 @@ function createAuthClient(config = {}) {
1162
1690
  null
1163
1691
  ];
1164
1692
  }
1165
- if (input.signerProvider.kind !== "local-private-key") {
1693
+ const signerAddress = config.signer?.address;
1694
+ if (!signerAddress) {
1166
1695
  return [
1167
1696
  new CapxulError({
1168
1697
  code: "INVALID_INPUT",
1169
- message: "completeBootstrap currently supports local-private-key signer providers only."
1698
+ message: "completeBootstrap requires a signer to be configured on the client."
1170
1699
  }),
1171
1700
  null
1172
1701
  ];
1173
1702
  }
1174
1703
  try {
1175
- const result = await data.mutation(api.authBootstrap.completeBootstrap, {
1704
+ const safeAddress = deriveSafeAddress2(signerAddress);
1705
+ if (!data.action) {
1706
+ return [
1707
+ new CapxulError({
1708
+ code: "INVALID_INPUT",
1709
+ message: "completeBootstrap requires a data client that can execute Convex actions."
1710
+ }),
1711
+ null
1712
+ ];
1713
+ }
1714
+ const result = await data.action(api.authBootstrap.completeBootstrap, {
1176
1715
  bootstrapToken: input.bootstrapToken,
1177
1716
  sessionToken: session.token,
1178
1717
  username: input.username,
1179
1718
  displayName: input.displayName,
1180
1719
  countryCode: input.countryCode,
1181
- signerProvider: input.signerProvider
1720
+ signerProvider: {
1721
+ kind: "local-private-key",
1722
+ signerAddress,
1723
+ safeAddress
1724
+ }
1182
1725
  });
1183
1726
  return [null, { kind: "authenticated", session, ...result }];
1184
1727
  } catch (cause) {
@@ -1192,7 +1735,7 @@ function createAuthClient(config = {}) {
1192
1735
  signOut: async () => {
1193
1736
  sessionStore.clear();
1194
1737
  dataClient = null;
1195
- mutableConfig(config).data = void 0;
1738
+ mutableConfig(config)._data = void 0;
1196
1739
  const transport = getTransport();
1197
1740
  transport?.clearAuth();
1198
1741
  return [null, void 0];
@@ -1263,6 +1806,9 @@ async function postBetterAuth(transport, path, body, code, signal) {
1263
1806
  }
1264
1807
  return [null, text ? JSON.parse(text) : void 0];
1265
1808
  } catch (cause) {
1809
+ if (cause instanceof CapxulError) {
1810
+ return [cause, null];
1811
+ }
1266
1812
  return [
1267
1813
  new CapxulError({
1268
1814
  code: "NETWORK_ERROR",
@@ -1301,7 +1847,7 @@ function parseBetterAuthError(text) {
1301
1847
  }
1302
1848
  }
1303
1849
  function isCapxulErrorCode2(code) {
1304
- return code === "NOT_AUTHENTICATED" || code === "EMAIL_DELIVERY_FAILED" || code === "INVALID_INPUT" || code === "RATE_LIMITED" || code === "NETWORK_ERROR" || code === "API_KEY_INVALID" || code === "API_KEY_EXPIRED";
1850
+ return code === "NOT_AUTHENTICATED" || code === "EMAIL_DELIVERY_FAILED" || code === "INVALID_INPUT" || code === "RATE_LIMITED" || code === "NETWORK_ERROR" || code === "API_KEY_INVALID" || code === "API_KEY_EXPIRED" || code === "INTERNAL_ERROR" || code === "ENV_MISSING" || code === "UNKNOWN" || code === "PROVIDER_ERROR";
1305
1851
  }
1306
1852
  async function exchangeConvexToken(transport, config, token, signal) {
1307
1853
  const path = config.auth?.convexTokenUrl ?? "/convex/token";
@@ -1331,6 +1877,9 @@ async function exchangeConvexToken(transport, config, token, signal) {
1331
1877
  }
1332
1878
  return [null, body.token];
1333
1879
  } catch (cause) {
1880
+ if (cause instanceof CapxulError) {
1881
+ return [cause, null];
1882
+ }
1334
1883
  return [
1335
1884
  new CapxulError({
1336
1885
  code: "NETWORK_ERROR",
@@ -1345,16 +1894,227 @@ function mutableConfig(config) {
1345
1894
  return config;
1346
1895
  }
1347
1896
 
1897
+ // src/core/auth-service.ts
1898
+ var AuthService = class {
1899
+ authClient;
1900
+ sessionStore;
1901
+ config;
1902
+ constructor(config = {}) {
1903
+ this.config = config;
1904
+ this.sessionStore = config.auth?.sessionStore ?? createMemorySessionStore2();
1905
+ this.authClient = createAuthClient({
1906
+ ...config,
1907
+ auth: { ...config.auth, sessionStore: this.sessionStore }
1908
+ });
1909
+ }
1910
+ async sendOtp(email, options) {
1911
+ const [err] = await this.authClient.sendOtp({ email }, options);
1912
+ if (err) throw err;
1913
+ }
1914
+ async verifyOtp(email, otp, options) {
1915
+ const [err, result] = await this.authClient.verifyOtp(
1916
+ { email, otp },
1917
+ options
1918
+ );
1919
+ if (err) throw err;
1920
+ return result;
1921
+ }
1922
+ async completeBootstrap(params, signer) {
1923
+ if (signer) {
1924
+ const tempClient = createAuthClient({
1925
+ ...this.config,
1926
+ signer,
1927
+ auth: { ...this.config.auth, sessionStore: this.sessionStore }
1928
+ });
1929
+ const [err2, result2] = await tempClient.completeBootstrap(params);
1930
+ if (err2) throw err2;
1931
+ return result2;
1932
+ }
1933
+ const [err, result] = await this.authClient.completeBootstrap(params);
1934
+ if (err) throw err;
1935
+ return result;
1936
+ }
1937
+ /**
1938
+ * Clears the persisted session and, when a transport was pre-injected,
1939
+ * drops the cached auth header.
1940
+ *
1941
+ * **Transport safety note:** `clearAuth()` is only invoked when
1942
+ * `config._transport` was supplied at construction (e.g. by the React
1943
+ * provider). If `AuthService` is instantiated directly in a Node/CLI
1944
+ * context without an injected transport, the transport-side auth cache
1945
+ * is the caller's responsibility.
1946
+ */
1947
+ async signOut() {
1948
+ if (!this.config._transport) {
1949
+ const [err] = await this.authClient.signOut();
1950
+ if (err) throw err;
1951
+ mutableConfig2(this.config)._data = void 0;
1952
+ return;
1953
+ }
1954
+ this.sessionStore.clear();
1955
+ this.config._transport.clearAuth();
1956
+ }
1957
+ async getSession() {
1958
+ const [err, session] = await this.authClient.getSession();
1959
+ if (err) throw err;
1960
+ return session;
1961
+ }
1962
+ };
1963
+ function mutableConfig2(config) {
1964
+ return config;
1965
+ }
1966
+ function createMemorySessionStore2() {
1967
+ let current = null;
1968
+ return {
1969
+ get: () => current,
1970
+ set: (session) => {
1971
+ current = session;
1972
+ },
1973
+ clear: () => {
1974
+ current = null;
1975
+ }
1976
+ };
1977
+ }
1978
+
1348
1979
  // src/core/documents.ts
1349
- function createDocumentsClient() {
1980
+ function requireInvoiceHash(row) {
1981
+ if (!row.invoiceHash) {
1982
+ throw new CapxulError({
1983
+ code: "INVALID_INPUT",
1984
+ message: `invoice document ${row.documentId ?? row._id} is missing its canonical invoiceHash`
1985
+ });
1986
+ }
1987
+ return row.invoiceHash;
1988
+ }
1989
+ function mapInvoiceRow(row) {
1990
+ const status = row.status === "cancelled" ? "canceled" : row.status === "pending" ? "open" : row.status === "overdue" ? "expired" : row.status;
1991
+ return {
1992
+ object: "document",
1993
+ id: row.documentId ?? row._id,
1994
+ type: "invoice",
1995
+ owner: {
1996
+ kind: row.scope === "org" ? "organization" : "account",
1997
+ id: row.orgId ?? row.payeeEmail ?? row.payeeLabel
1998
+ },
1999
+ recipient: { email: row.payerEmail },
2000
+ amount: {
2001
+ value: row.amount,
2002
+ currency: row.currency
2003
+ },
2004
+ reference: row.note,
2005
+ lineItems: row.items,
2006
+ invoiceHash: requireInvoiceHash(row),
2007
+ dueAt: row.dueDate,
2008
+ status,
2009
+ createdAt: new Date(row.createdAt).toISOString()
2010
+ };
2011
+ }
2012
+ function mapDocumentError(cause) {
2013
+ return fromConvexError(cause);
2014
+ }
2015
+ function createDocumentsClient(config = {}) {
1350
2016
  return {
1351
- create: async () => stub("documents.create"),
1352
- retrieve: async () => stub("documents.retrieve"),
1353
- list: async () => stub("documents.list"),
1354
- cancel: async () => stub("documents.cancel")
2017
+ create: async (input) => {
2018
+ if (!config._data) return stub("documents.create");
2019
+ if (input.type !== "invoice") {
2020
+ return [
2021
+ new CapxulError({
2022
+ code: "INVALID_INPUT",
2023
+ message: "documents.create currently supports personal invoice documents only."
2024
+ }),
2025
+ null
2026
+ ];
2027
+ }
2028
+ if (!("email" in input.recipient)) {
2029
+ return [
2030
+ new CapxulError({
2031
+ code: "INVALID_INPUT",
2032
+ message: "personal invoice documents currently require an email recipient."
2033
+ }),
2034
+ null
2035
+ ];
2036
+ }
2037
+ try {
2038
+ const created = await config._data.mutation(
2039
+ api.paymentRecords.mutations.createInvoice,
2040
+ {
2041
+ scope: "personal",
2042
+ payerEmail: input.recipient.email,
2043
+ amount: input.amount.value,
2044
+ currency: input.amount.currency,
2045
+ dueDate: input.dueAt ?? (/* @__PURE__ */ new Date()).toISOString(),
2046
+ note: input.reference,
2047
+ items: input.lineItems
2048
+ }
2049
+ );
2050
+ const row = await config._data.query(
2051
+ api.paymentRecords.queries.getInvoiceByDocumentId,
2052
+ { documentId: created.documentId }
2053
+ );
2054
+ if (!row) throw new Error("created invoice was not readable");
2055
+ return [null, mapInvoiceRow(row)];
2056
+ } catch (cause) {
2057
+ return [mapDocumentError(cause), null];
2058
+ }
2059
+ },
2060
+ retrieve: async (documentId) => {
2061
+ if (!config._data)
2062
+ return stub("documents.retrieve");
2063
+ try {
2064
+ const row = await config._data.query(
2065
+ api.paymentRecords.queries.getInvoiceByDocumentId,
2066
+ { documentId }
2067
+ );
2068
+ if (!row) {
2069
+ return [
2070
+ new CapxulError({
2071
+ code: "NOT_FOUND",
2072
+ message: `document ${documentId} not found`
2073
+ }),
2074
+ null
2075
+ ];
2076
+ }
2077
+ return [null, mapInvoiceRow(row)];
2078
+ } catch (cause) {
2079
+ return [mapDocumentError(cause), null];
2080
+ }
2081
+ },
2082
+ list: async (input = {}) => {
2083
+ if (!config._data)
2084
+ return stub("documents.list");
2085
+ try {
2086
+ if (input.type && input.type !== "invoice") {
2087
+ return [null, { object: "list", data: [], page: { hasMore: false } }];
2088
+ }
2089
+ const rows = await config._data.query(
2090
+ api.paymentRecords.queries.listMyInvoices,
2091
+ {
2092
+ limit: input.limit,
2093
+ cursor: input.cursor
2094
+ }
2095
+ );
2096
+ const page = rows;
2097
+ const data = page.data.map((row) => mapInvoiceRow(row));
2098
+ return [null, { object: "list", data, page: page.page }];
2099
+ } catch (cause) {
2100
+ return [mapDocumentError(cause), null];
2101
+ }
2102
+ },
2103
+ cancel: async (documentId) => {
2104
+ if (!config._data) return stub("documents.cancel");
2105
+ try {
2106
+ const row = await config._data.mutation(
2107
+ api.paymentRecords.mutations.cancelInvoice,
2108
+ { documentId }
2109
+ );
2110
+ return [null, mapInvoiceRow(row)];
2111
+ } catch (cause) {
2112
+ return [mapDocumentError(cause), null];
2113
+ }
2114
+ }
1355
2115
  };
1356
2116
  }
1357
- function createOrgDocumentsClient() {
2117
+ function createOrgDocumentsClient(_config = {}) {
1358
2118
  return {
1359
2119
  create: async () => stub("organizations.documents.create"),
1360
2120
  retrieve: async () => stub("organizations.documents.retrieve"),
@@ -1367,11 +2127,11 @@ function createOrgDocumentsClient() {
1367
2127
  function createMeClient(config = {}) {
1368
2128
  return {
1369
2129
  get: async () => {
1370
- if (!config.data) {
2130
+ if (!config._data) {
1371
2131
  return stub("me.get");
1372
2132
  }
1373
2133
  try {
1374
- const account = await config.data.query(
2134
+ const account = await config._data.query(
1375
2135
  api.openfort.queries.getMyAccount,
1376
2136
  {}
1377
2137
  );
@@ -1381,7 +2141,7 @@ function createMeClient(config = {}) {
1381
2141
  }
1382
2142
  },
1383
2143
  update: async (input) => {
1384
- if (!config.data) {
2144
+ if (!config._data) {
1385
2145
  return stub("me.update");
1386
2146
  }
1387
2147
  if (input.countryCode !== void 0) {
@@ -1395,11 +2155,11 @@ function createMeClient(config = {}) {
1395
2155
  ];
1396
2156
  }
1397
2157
  try {
1398
- await config.data.mutation(api.openfort.mutations.updateProfile, {
2158
+ await config._data.mutation(api.openfort.mutations.updateProfile, {
1399
2159
  displayName: input.name,
1400
2160
  username: input.username
1401
2161
  });
1402
- const account = await config.data.query(
2162
+ const account = await config._data.query(
1403
2163
  api.openfort.queries.getMyAccount,
1404
2164
  {}
1405
2165
  );
@@ -1414,11 +2174,11 @@ function createMeClient(config = {}) {
1414
2174
  // src/core/operations.ts
1415
2175
  function createOperationsClient(config = {}) {
1416
2176
  const retrieve = async (operationId) => {
1417
- if (!config.data) {
2177
+ if (!config._data) {
1418
2178
  return stub("operations.retrieve");
1419
2179
  }
1420
2180
  try {
1421
- const operation = await config.data.query(api.operations.queries.retrieve, {
2181
+ const operation = await config._data.query(api.operations.queries.retrieve, {
1422
2182
  operationId
1423
2183
  });
1424
2184
  if (!operation) {
@@ -1435,7 +2195,7 @@ function createOperationsClient(config = {}) {
1435
2195
  return {
1436
2196
  retrieve,
1437
2197
  wait: async (operationId, input = {}) => {
1438
- if (!config.data) {
2198
+ if (!config._data) {
1439
2199
  return stub("operations.wait");
1440
2200
  }
1441
2201
  const until = new Set(
@@ -1470,49 +2230,22 @@ function toTokenUnits(value, decimals = 6) {
1470
2230
  return parseUnits(value, decimals);
1471
2231
  }
1472
2232
 
1473
- // src/internal/payment-token.ts
1474
- function resolvePaymentTokenAddress(currency) {
2233
+ // src/core/token-registry.ts
2234
+ function resolvePaymentToken(currency) {
1475
2235
  const normalized = currency.trim().toUpperCase();
1476
2236
  if (normalized === "USD" || normalized === "USDC") {
1477
- return TEST_USDC_ADDRESS.toLowerCase();
2237
+ return {
2238
+ address: TEST_USDC_ADDRESS.toLowerCase(),
2239
+ decimals: 6,
2240
+ symbol: "USDC"
2241
+ };
1478
2242
  }
1479
2243
  throw new CapxulError({
1480
- code: "NETWORK_ERROR",
1481
- message: `Currency ${currency} is not configured for on-chain payment submission.`,
2244
+ code: "NOT_IMPLEMENTED",
2245
+ message: `Currency ${currency} is not yet supported by the token registry.`,
1482
2246
  details: { currency: normalized }
1483
2247
  });
1484
2248
  }
1485
- async function buildSafeAccount(signer, chain) {
1486
- try {
1487
- const publicClient = createPublicClient({
1488
- chain: baseSepolia,
1489
- transport: http(chain.rpcUrl)
1490
- });
1491
- return await toSafeSmartAccount({
1492
- client: publicClient,
1493
- entryPoint: { address: entryPoint07Address, version: "0.7" },
1494
- version: "1.4.1",
1495
- owners: [signer],
1496
- saltNonce: computeSaltNonce(signer.address),
1497
- safeSingletonAddress: SAFE_L2_SINGLETON,
1498
- safeProxyFactoryAddress: SAFE_PROXY_FACTORY,
1499
- safeModuleSetupAddress: SAFE_MODULE_SETUP,
1500
- safe4337ModuleAddress: SAFE_4337_MODULE,
1501
- safeModules: [],
1502
- setupTransactions: []
1503
- });
1504
- } catch (cause) {
1505
- throw new CapxulError({
1506
- code: "NETWORK_ERROR",
1507
- message: cause instanceof Error ? cause.message : String(cause),
1508
- cause,
1509
- details: { chainId: chain.chainId }
1510
- });
1511
- }
1512
- }
1513
- function computeSaltNonce(ownerAddress) {
1514
- return BigInt(`0x${ownerAddress.toLowerCase().slice(2).padEnd(64, "0")}`);
1515
- }
1516
2249
  function createCapxulBundler(config) {
1517
2250
  const paymaster = createPaymasterClient({
1518
2251
  transport: http(config.rpcUrl)
@@ -1619,13 +2352,13 @@ async function transferAsOwner(config, params) {
1619
2352
  function createPaymentsClient(config = {}) {
1620
2353
  return {
1621
2354
  create: async (input) => {
1622
- if (!config.data || !config.signer || !config.signing) {
2355
+ if (!config._data || !config.signer || !config.signing) {
1623
2356
  return stub("payments.create");
1624
2357
  }
1625
2358
  let created = null;
1626
2359
  let submitted = null;
1627
2360
  try {
1628
- created = await config.data.mutation(api.payments.mutations.create, {
2361
+ created = await config._data.mutation(api.payments.mutations.create, {
1629
2362
  to: input.to,
1630
2363
  amount: input.amount,
1631
2364
  reference: input.reference,
@@ -1633,15 +2366,21 @@ function createPaymentsClient(config = {}) {
1633
2366
  source: input.source
1634
2367
  });
1635
2368
  if (!created) {
1636
- return [new CapxulError({
1637
- code: "NETWORK_ERROR",
1638
- message: "payments.create returned no payment resource"
1639
- }), null];
2369
+ return [
2370
+ new CapxulError({
2371
+ code: "NETWORK_ERROR",
2372
+ message: "payments.create returned no payment resource"
2373
+ }),
2374
+ null
2375
+ ];
1640
2376
  }
1641
2377
  if (created.status !== "processing" || created.operation.status !== "processing") {
1642
2378
  return [null, created];
1643
2379
  }
1644
- const currentSigner = await config.data.query(api.safe.queries.getMySignerAddress, {});
2380
+ const currentSigner = await config._data.query(
2381
+ api.safe.queries.getMySignerAddress,
2382
+ {}
2383
+ );
1645
2384
  if (!currentSigner?.address) {
1646
2385
  throw new CapxulError({
1647
2386
  code: "PERMISSION_DENIED",
@@ -1660,9 +2399,12 @@ function createPaymentsClient(config = {}) {
1660
2399
  }
1661
2400
  });
1662
2401
  }
1663
- const submission = await config.data.query(api.payments.queries.prepareSubmission, {
1664
- paymentId: created.id
1665
- });
2402
+ const submission = await config._data.query(
2403
+ api.payments.queries.prepareSubmission,
2404
+ {
2405
+ paymentId: created.id
2406
+ }
2407
+ );
1666
2408
  if (!submission?.recipientAddress) {
1667
2409
  throw new CapxulError({
1668
2410
  code: "NETWORK_ERROR",
@@ -1670,15 +2412,16 @@ function createPaymentsClient(config = {}) {
1670
2412
  details: { paymentId: created.id }
1671
2413
  });
1672
2414
  }
2415
+ const token = resolvePaymentToken(submission.amount.currency);
1673
2416
  const transfer = await transferAsOwner(
1674
2417
  {
1675
2418
  signer: config.signer,
1676
2419
  signing: config.signing
1677
2420
  },
1678
2421
  {
1679
- tokenAddress: resolvePaymentTokenAddress(submission.amount.currency),
2422
+ tokenAddress: token.address,
1680
2423
  recipientAddress: submission.recipientAddress,
1681
- amount: toTokenUnits(submission.amount.value, 6)
2424
+ amount: toTokenUnits(submission.amount.value, token.decimals)
1682
2425
  }
1683
2426
  );
1684
2427
  if (!transfer.success) {
@@ -1696,7 +2439,7 @@ function createPaymentsClient(config = {}) {
1696
2439
  txHash: transfer.txHash,
1697
2440
  userOpHash: transfer.userOpHash
1698
2441
  };
1699
- await config.data.mutation(api.payments.mutations.recordSubmitted, {
2442
+ await config._data.mutation(api.payments.mutations.recordSubmitted, {
1700
2443
  paymentId: created.id,
1701
2444
  txHash: transfer.txHash,
1702
2445
  userOpHash: transfer.userOpHash,
@@ -1706,43 +2449,65 @@ function createPaymentsClient(config = {}) {
1706
2449
  } catch (cause) {
1707
2450
  const error = mapCreateError(fromConvexError(cause));
1708
2451
  if (created?.id && created.status === "processing" && !submitted) {
1709
- await bestEffortMarkFailed({ data: config.data }, created.id, error);
2452
+ await bestEffortMarkFailed({ _data: config._data }, created.id, error);
1710
2453
  }
1711
2454
  if (submitted && created?.id) {
1712
- return [new CapxulError({
1713
- code: "NETWORK_ERROR",
1714
- message: "Payment was submitted on-chain, but backend submission tracking failed.",
1715
- cause,
1716
- details: {
1717
- paymentId: created.id,
1718
- txHash: submitted.txHash,
1719
- userOpHash: submitted.userOpHash
1720
- }
1721
- }), null];
2455
+ return [
2456
+ new CapxulError({
2457
+ code: "NETWORK_ERROR",
2458
+ message: "Payment was submitted on-chain, but backend submission tracking failed.",
2459
+ cause,
2460
+ details: {
2461
+ paymentId: created.id,
2462
+ txHash: submitted.txHash,
2463
+ userOpHash: submitted.userOpHash
2464
+ }
2465
+ }),
2466
+ null
2467
+ ];
1722
2468
  }
1723
2469
  return [error, null];
1724
2470
  }
1725
2471
  },
1726
2472
  retrieve: async (paymentId) => {
1727
- if (!config.data) {
2473
+ if (!config._data) {
1728
2474
  return stub("payments.retrieve");
1729
2475
  }
1730
2476
  try {
1731
- const payment = await config.data.query(api.payments.queries.retrieve, {
1732
- paymentId
1733
- });
2477
+ const payment = await config._data.query(
2478
+ api.payments.queries.retrieve,
2479
+ {
2480
+ paymentId
2481
+ }
2482
+ );
1734
2483
  if (!payment) {
1735
- return [new CapxulError({
1736
- code: "NOT_FOUND",
1737
- message: `payment ${paymentId} not found`
1738
- }), null];
2484
+ return [
2485
+ new CapxulError({
2486
+ code: "NOT_FOUND",
2487
+ message: `payment ${paymentId} not found`
2488
+ }),
2489
+ null
2490
+ ];
1739
2491
  }
1740
2492
  return [null, payment];
1741
2493
  } catch (cause) {
1742
2494
  return [fromConvexError(cause), null];
1743
2495
  }
1744
2496
  },
1745
- list: async () => stub("payments.list")
2497
+ list: async (input) => {
2498
+ if (!config._data) {
2499
+ return stub("payments.list");
2500
+ }
2501
+ try {
2502
+ const page = await config._data.query(api.payments.queries.list, {
2503
+ limit: input?.limit,
2504
+ cursor: input?.cursor
2505
+ });
2506
+ return [null, page];
2507
+ } catch (cause) {
2508
+ return [fromConvexError(cause), null];
2509
+ }
2510
+ }
1746
2511
  };
1747
2512
  }
1748
2513
  function createOrgPaymentsClient() {
@@ -1756,7 +2521,7 @@ function createOrgPaymentsClient() {
1756
2521
  }
1757
2522
  async function bestEffortMarkFailed(config, paymentId, error) {
1758
2523
  try {
1759
- await config.data.mutation(api.payments.mutations.markFailed, {
2524
+ await config._data.mutation(api.payments.mutations.markFailed, {
1760
2525
  paymentId,
1761
2526
  errorCode: error.code,
1762
2527
  errorMessage: error.message,
@@ -1813,11 +2578,11 @@ function createOrgTransfersClient() {
1813
2578
  function createWithdrawalsClient(config = {}) {
1814
2579
  return {
1815
2580
  create: async (input) => {
1816
- if (!config.data) {
2581
+ if (!config._data) {
1817
2582
  return stub("withdrawals.create");
1818
2583
  }
1819
2584
  const [createErr, createdRaw] = await tryCatch(
1820
- config.data.mutation(api.withdrawals.mutations.create, {
2585
+ config._data.mutation(api.withdrawals.mutations.create, {
1821
2586
  amount: input.amount,
1822
2587
  destination: {
1823
2588
  externalAccountId: input.destination.externalAccountId
@@ -1847,18 +2612,18 @@ function createWithdrawalsClient(config = {}) {
1847
2612
  return [null, created];
1848
2613
  }
1849
2614
  const [signerErr, currentSigner] = await tryCatch(
1850
- config.data.query(api.safe.queries.getMySignerAddress, {})
2615
+ config._data.query(api.safe.queries.getMySignerAddress, {})
1851
2616
  );
1852
2617
  if (signerErr) {
1853
2618
  return await handleSubmissionFailure(
1854
- { data: config.data },
2619
+ { _data: config._data },
1855
2620
  created.id,
1856
2621
  mapCreateError2(fromConvexError(signerErr))
1857
2622
  );
1858
2623
  }
1859
2624
  if (!currentSigner?.address) {
1860
2625
  return await handleSubmissionFailure(
1861
- { data: config.data },
2626
+ { _data: config._data },
1862
2627
  created.id,
1863
2628
  new CapxulError({
1864
2629
  code: "PERMISSION_DENIED",
@@ -1869,7 +2634,7 @@ function createWithdrawalsClient(config = {}) {
1869
2634
  }
1870
2635
  if (getAddress(currentSigner.address) !== getAddress(config.signer.address)) {
1871
2636
  return await handleSubmissionFailure(
1872
- { data: config.data },
2637
+ { _data: config._data },
1873
2638
  created.id,
1874
2639
  new CapxulError({
1875
2640
  code: "PERMISSION_DENIED",
@@ -1883,13 +2648,13 @@ function createWithdrawalsClient(config = {}) {
1883
2648
  );
1884
2649
  }
1885
2650
  const [prepErr, submission] = await tryCatch(
1886
- config.data.query(api.withdrawals.queries.prepareSubmission, {
2651
+ config._data.query(api.withdrawals.queries.prepareSubmission, {
1887
2652
  withdrawalId: created.id
1888
2653
  })
1889
2654
  );
1890
2655
  if (prepErr) {
1891
2656
  return await handleSubmissionFailure(
1892
- { data: config.data },
2657
+ { _data: config._data },
1893
2658
  created.id,
1894
2659
  mapCreateError2(fromConvexError(prepErr))
1895
2660
  );
@@ -1897,7 +2662,7 @@ function createWithdrawalsClient(config = {}) {
1897
2662
  const destinationAddress = submission?.destinationAddress;
1898
2663
  if (!submission || !destinationAddress) {
1899
2664
  return await handleSubmissionFailure(
1900
- { data: config.data },
2665
+ { _data: config._data },
1901
2666
  created.id,
1902
2667
  new CapxulError({
1903
2668
  code: "NETWORK_ERROR",
@@ -1906,6 +2671,7 @@ function createWithdrawalsClient(config = {}) {
1906
2671
  })
1907
2672
  );
1908
2673
  }
2674
+ const token = resolvePaymentToken(submission.amount.currency);
1909
2675
  const [transferErr, transferOk] = await tryCatch(
1910
2676
  transferAsOwner(
1911
2677
  {
@@ -1913,22 +2679,22 @@ function createWithdrawalsClient(config = {}) {
1913
2679
  signing: config.signing
1914
2680
  },
1915
2681
  {
1916
- tokenAddress: resolvePaymentTokenAddress(submission.amount.currency),
2682
+ tokenAddress: token.address,
1917
2683
  recipientAddress: destinationAddress,
1918
- amount: toTokenUnits(submission.amount.value, 6)
2684
+ amount: toTokenUnits(submission.amount.value, token.decimals)
1919
2685
  }
1920
2686
  )
1921
2687
  );
1922
2688
  if (transferErr) {
1923
2689
  return await handleSubmissionFailure(
1924
- { data: config.data },
2690
+ { _data: config._data },
1925
2691
  created.id,
1926
2692
  mapCreateError2(fromConvexError(transferErr))
1927
2693
  );
1928
2694
  }
1929
2695
  if (!transferOk.success) {
1930
2696
  return await handleSubmissionFailure(
1931
- { data: config.data },
2697
+ { _data: config._data },
1932
2698
  created.id,
1933
2699
  new CapxulError({
1934
2700
  code: "NETWORK_ERROR",
@@ -1942,7 +2708,7 @@ function createWithdrawalsClient(config = {}) {
1942
2708
  );
1943
2709
  }
1944
2710
  const [recordErr] = await tryCatch(
1945
- config.data.mutation(api.withdrawals.mutations.recordSubmitted, {
2711
+ config._data.mutation(api.withdrawals.mutations.recordSubmitted, {
1946
2712
  withdrawalId: created.id,
1947
2713
  txHash: transferOk.txHash,
1948
2714
  userOpHash: transferOk.userOpHash
@@ -1966,11 +2732,11 @@ function createWithdrawalsClient(config = {}) {
1966
2732
  return [null, created];
1967
2733
  },
1968
2734
  retrieve: async (withdrawalId) => {
1969
- if (!config.data) {
2735
+ if (!config._data) {
1970
2736
  return stub("withdrawals.retrieve");
1971
2737
  }
1972
2738
  const [err, raw] = await tryCatch(
1973
- config.data.query(api.withdrawals.queries.retrieve, { withdrawalId })
2739
+ config._data.query(api.withdrawals.queries.retrieve, { withdrawalId })
1974
2740
  );
1975
2741
  if (err) {
1976
2742
  return [fromConvexError(err), null];
@@ -1988,11 +2754,11 @@ function createWithdrawalsClient(config = {}) {
1988
2754
  return [null, withdrawal];
1989
2755
  },
1990
2756
  list: async (input) => {
1991
- if (!config.data) {
2757
+ if (!config._data) {
1992
2758
  return stub("withdrawals.list");
1993
2759
  }
1994
2760
  const [err, raw] = await tryCatch(
1995
- config.data.query(api.withdrawals.queries.list, {
2761
+ config._data.query(api.withdrawals.queries.list, {
1996
2762
  limit: input?.limit,
1997
2763
  cursor: input?.cursor
1998
2764
  })
@@ -2003,13 +2769,13 @@ function createWithdrawalsClient(config = {}) {
2003
2769
  return [null, raw];
2004
2770
  },
2005
2771
  recordCompleted: async (input) => {
2006
- if (!config.data) {
2772
+ if (!config._data) {
2007
2773
  return stub(
2008
2774
  "withdrawals.recordCompleted"
2009
2775
  );
2010
2776
  }
2011
2777
  const [err] = await tryCatch(
2012
- config.data.mutation(api.withdrawals.mutations.recordCompleted, {
2778
+ config._data.mutation(api.withdrawals.mutations.recordCompleted, {
2013
2779
  withdrawalId: input.withdrawalId,
2014
2780
  txHash: input.txHash
2015
2781
  })
@@ -2034,13 +2800,13 @@ function createOrgWithdrawalsClient(config = {}) {
2034
2800
  * orchestration ships in W3+.
2035
2801
  */
2036
2802
  create: async (input) => {
2037
- if (!config.data) {
2803
+ if (!config._data) {
2038
2804
  return stub(
2039
2805
  "organizations.withdrawals.create"
2040
2806
  );
2041
2807
  }
2042
2808
  const [err, raw] = await tryCatch(
2043
- config.data.mutation(api.withdrawals.mutations.createOrg, {
2809
+ config._data.mutation(api.withdrawals.mutations.createOrg, {
2044
2810
  organizationId: input.organizationId,
2045
2811
  amount: input.amount,
2046
2812
  destination: {
@@ -2067,13 +2833,13 @@ function createOrgWithdrawalsClient(config = {}) {
2067
2833
  return [null, created];
2068
2834
  },
2069
2835
  retrieve: async (input) => {
2070
- if (!config.data) {
2836
+ if (!config._data) {
2071
2837
  return stub(
2072
2838
  "organizations.withdrawals.retrieve"
2073
2839
  );
2074
2840
  }
2075
2841
  const [err, raw] = await tryCatch(
2076
- config.data.query(api.withdrawals.queries.retrieve, {
2842
+ config._data.query(api.withdrawals.queries.retrieve, {
2077
2843
  withdrawalId: input.withdrawalId
2078
2844
  })
2079
2845
  );
@@ -2103,13 +2869,13 @@ function createOrgWithdrawalsClient(config = {}) {
2103
2869
  return [null, withdrawal];
2104
2870
  },
2105
2871
  list: async (input) => {
2106
- if (!config.data) {
2872
+ if (!config._data) {
2107
2873
  return stub(
2108
2874
  "organizations.withdrawals.list"
2109
2875
  );
2110
2876
  }
2111
2877
  const [err, raw] = await tryCatch(
2112
- config.data.query(api.withdrawals.queries.listOrg, {
2878
+ config._data.query(api.withdrawals.queries.listOrg, {
2113
2879
  organizationId: input.organizationId,
2114
2880
  limit: input.limit,
2115
2881
  cursor: input.cursor
@@ -2128,7 +2894,7 @@ async function handleSubmissionFailure(config, withdrawalId, error) {
2128
2894
  }
2129
2895
  async function bestEffortMarkFailed2(config, withdrawalId, error) {
2130
2896
  await tryCatch(
2131
- config.data.mutation(api.withdrawals.mutations.markFailed, {
2897
+ config._data.mutation(api.withdrawals.mutations.markFailed, {
2132
2898
  withdrawalId,
2133
2899
  errorCode: error.code,
2134
2900
  errorMessage: error.message
@@ -2207,13 +2973,13 @@ function createWebhookEventsClient() {
2207
2973
  function createOrgExternalAccountsClient(config) {
2208
2974
  return {
2209
2975
  create: async (input) => {
2210
- if (!config.data) {
2976
+ if (!config._data) {
2211
2977
  return stub(
2212
2978
  "organizations.externalAccounts.create"
2213
2979
  );
2214
2980
  }
2215
2981
  const [err, raw] = await tryCatch(
2216
- config.data.mutation(api.externalAccounts.mutations.createOrg, {
2982
+ config._data.mutation(api.externalAccounts.mutations.createOrg, {
2217
2983
  organizationId: input.organizationId,
2218
2984
  kind: input.kind,
2219
2985
  label: input.label,
@@ -2227,10 +2993,7 @@ function createOrgExternalAccountsClient(config) {
2227
2993
  })
2228
2994
  );
2229
2995
  if (err) {
2230
- return [
2231
- fromConvexError(err),
2232
- null
2233
- ];
2996
+ return [fromConvexError(err), null];
2234
2997
  }
2235
2998
  if (!raw) {
2236
2999
  return [
@@ -2241,21 +3004,16 @@ function createOrgExternalAccountsClient(config) {
2241
3004
  null
2242
3005
  ];
2243
3006
  }
2244
- return [
2245
- null,
2246
- brandExternalAccount(
2247
- raw
2248
- )
2249
- ];
3007
+ return [null, brandExternalAccount(raw)];
2250
3008
  },
2251
3009
  list: async (input) => {
2252
- if (!config.data) {
3010
+ if (!config._data) {
2253
3011
  return stub(
2254
3012
  "organizations.externalAccounts.list"
2255
3013
  );
2256
3014
  }
2257
3015
  const [err, result] = await tryCatch(
2258
- config.data.query(api.externalAccounts.queries.listOrg, {
3016
+ config._data.query(api.externalAccounts.queries.listOrg, {
2259
3017
  organizationId: input.organizationId,
2260
3018
  limit: input.limit,
2261
3019
  cursor: input.cursor
@@ -2265,9 +3023,7 @@ function createOrgExternalAccountsClient(config) {
2265
3023
  return [fromConvexError(err), null];
2266
3024
  }
2267
3025
  const branded = result.data.map(
2268
- (row) => brandExternalAccount(
2269
- row
2270
- )
3026
+ (row) => brandExternalAccount(row)
2271
3027
  );
2272
3028
  return [
2273
3029
  null,
@@ -2279,17 +3035,66 @@ function createOrgExternalAccountsClient(config) {
2279
3035
  ];
2280
3036
  },
2281
3037
  retrieve: async (input) => {
2282
- if (!config.data) {
3038
+ if (!config._data) {
2283
3039
  return stub(
2284
3040
  "organizations.externalAccounts.retrieve"
2285
3041
  );
2286
3042
  }
2287
3043
  const [err, raw] = await tryCatch(
2288
- config.data.query(api.externalAccounts.queries.retrieveOrg, {
3044
+ config._data.query(api.externalAccounts.queries.retrieveOrg, {
3045
+ organizationId: input.organizationId,
3046
+ externalAccountId: input.externalAccountId
3047
+ })
3048
+ );
3049
+ if (err) {
3050
+ return [fromConvexError(err), null];
3051
+ }
3052
+ if (!raw) {
3053
+ return [
3054
+ new CapxulError({
3055
+ code: "NOT_FOUND",
3056
+ message: `external_account ${input.externalAccountId} not found`
3057
+ }),
3058
+ null
3059
+ ];
3060
+ }
3061
+ return [null, brandExternalAccount(raw)];
3062
+ },
3063
+ remove: async (input) => {
3064
+ if (!config._data) {
3065
+ return stub("organizations.externalAccounts.remove");
3066
+ }
3067
+ const [err] = await tryCatch(
3068
+ config._data.mutation(api.externalAccounts.mutations.removeOrg, {
2289
3069
  organizationId: input.organizationId,
2290
3070
  externalAccountId: input.externalAccountId
2291
3071
  })
2292
3072
  );
3073
+ if (err) {
3074
+ return [fromConvexError(err), null];
3075
+ }
3076
+ return [null, void 0];
3077
+ }
3078
+ };
3079
+ }
3080
+ function createOrgSubAccountsClient(config) {
3081
+ return {
3082
+ create: async (input) => {
3083
+ if (!config._data) {
3084
+ return stub(
3085
+ "organizations.subAccounts.create"
3086
+ );
3087
+ }
3088
+ const [err, raw] = await tryCatch(
3089
+ config._data.mutation(api.subAccounts.mutations.create, {
3090
+ parent: {
3091
+ kind: "organization",
3092
+ id: input.organizationId
3093
+ },
3094
+ name: input.name,
3095
+ purpose: input.purpose
3096
+ })
3097
+ );
2293
3098
  if (err) {
2294
3099
  return [
2295
3100
  fromConvexError(err),
@@ -2300,28 +3105,60 @@ function createOrgExternalAccountsClient(config) {
2300
3105
  return [
2301
3106
  new CapxulError({
2302
3107
  code: "NOT_FOUND",
2303
- message: `external_account ${input.externalAccountId} not found`
3108
+ message: "sub_account creation returned no resource"
2304
3109
  }),
2305
3110
  null
2306
3111
  ];
2307
3112
  }
3113
+ const [brandErr, branded] = tryBrandSubAccount(raw);
3114
+ if (brandErr) {
3115
+ return [brandErr, null];
3116
+ }
3117
+ return [null, branded];
3118
+ },
3119
+ list: async (input) => {
3120
+ if (!config._data) {
3121
+ return stub(
3122
+ "organizations.subAccounts.list"
3123
+ );
3124
+ }
3125
+ const [err, rows] = await tryCatch(
3126
+ config._data.query(api.subAccounts.queries.listByOrganization, {
3127
+ organizationId: input.organizationId
3128
+ })
3129
+ );
3130
+ if (err) {
3131
+ return [
3132
+ fromConvexError(err),
3133
+ null
3134
+ ];
3135
+ }
3136
+ const branded = [];
3137
+ for (const row of rows) {
3138
+ const [brandErr, value] = tryBrandSubAccount(row);
3139
+ if (brandErr) {
3140
+ return [brandErr, null];
3141
+ }
3142
+ branded.push(value);
3143
+ }
2308
3144
  return [
2309
3145
  null,
2310
- brandExternalAccount(
2311
- raw
2312
- )
3146
+ {
3147
+ object: "list",
3148
+ data: branded,
3149
+ page: { hasMore: false }
3150
+ }
2313
3151
  ];
2314
3152
  },
2315
- remove: async (input) => {
2316
- if (!config.data) {
3153
+ retrieve: async (input) => {
3154
+ if (!config._data) {
2317
3155
  return stub(
2318
- "organizations.externalAccounts.remove"
3156
+ "organizations.subAccounts.retrieve"
2319
3157
  );
2320
3158
  }
2321
- const [err] = await tryCatch(
2322
- config.data.mutation(api.externalAccounts.mutations.removeOrg, {
2323
- organizationId: input.organizationId,
2324
- externalAccountId: input.externalAccountId
3159
+ const [err, raw] = await tryCatch(
3160
+ config._data.query(api.subAccounts.queries.retrieve, {
3161
+ subAccountId: input.subAccountId
2325
3162
  })
2326
3163
  );
2327
3164
  if (err) {
@@ -2330,822 +3167,557 @@ function createOrgExternalAccountsClient(config) {
2330
3167
  null
2331
3168
  ];
2332
3169
  }
2333
- return [null, void 0];
2334
- }
2335
- };
3170
+ if (!raw) {
3171
+ return [
3172
+ new CapxulError({
3173
+ code: "NOT_FOUND",
3174
+ message: `sub_account ${input.subAccountId} not found`
3175
+ }),
3176
+ null
3177
+ ];
3178
+ }
3179
+ const [brandErr, branded] = tryBrandSubAccount(raw);
3180
+ if (brandErr) {
3181
+ return [
3182
+ brandErr,
3183
+ null
3184
+ ];
3185
+ }
3186
+ return [null, branded];
3187
+ },
3188
+ remove: async (input) => {
3189
+ if (!config._data) {
3190
+ return stub(
3191
+ "organizations.subAccounts.remove"
3192
+ );
3193
+ }
3194
+ const [err, raw] = await tryCatch(
3195
+ config._data.mutation(api.subAccounts.mutations.archive, {
3196
+ subAccountId: input.subAccountId
3197
+ })
3198
+ );
3199
+ if (err) {
3200
+ return [
3201
+ fromConvexError(err),
3202
+ null
3203
+ ];
3204
+ }
3205
+ if (!raw) {
3206
+ return [
3207
+ new CapxulError({
3208
+ code: "NOT_FOUND",
3209
+ message: `sub_account ${input.subAccountId} not found`
3210
+ }),
3211
+ null
3212
+ ];
3213
+ }
3214
+ const [brandErr, branded] = tryBrandSubAccount(raw);
3215
+ if (brandErr) {
3216
+ return [
3217
+ brandErr,
3218
+ null
3219
+ ];
3220
+ }
3221
+ return [null, branded];
3222
+ }
3223
+ };
2336
3224
  }
2337
3225
  function createOrganizationsClient(config = {}) {
2338
3226
  return {
2339
- create: async () => stub("organizations.create"),
2340
- retrieve: async () => stub("organizations.retrieve"),
2341
- list: async () => stub("organizations.list"),
2342
- update: async () => stub("organizations.update"),
2343
- safes: {
2344
- retrieve: async (input) => {
2345
- if (!config.data) {
2346
- return stub("organizations.safes.retrieve");
2347
- }
2348
- try {
2349
- const safe = await config.data.query(
2350
- api.safe.queries.retrieveOrganizationSafe,
2351
- input
2352
- );
2353
- if (!safe) {
2354
- return [
2355
- new CapxulError({
2356
- code: "NOT_FOUND",
2357
- message: `safe ${input.safeId} not found`
2358
- }),
2359
- null
2360
- ];
2361
- }
2362
- return [null, safe];
2363
- } catch (cause) {
3227
+ create: async (input) => {
3228
+ if (!config._data) {
3229
+ return stub("organizations.create");
3230
+ }
3231
+ if (input.country !== void 0) {
3232
+ return [
3233
+ new CapxulError({
3234
+ code: "INVALID_INPUT",
3235
+ message: "organizations.create does not support country yet \u2014 backend mutation only accepts name.",
3236
+ details: { field: "country" }
3237
+ }),
3238
+ null
3239
+ ];
3240
+ }
3241
+ try {
3242
+ const orgId = await config._data.mutation(api.org.mutations.create, {
3243
+ name: input.name
3244
+ });
3245
+ const org = await config._data.query(api.org.queries.retrieve, {
3246
+ orgId
3247
+ });
3248
+ if (!org) {
2364
3249
  return [
2365
- fromConvexError(cause),
3250
+ new CapxulError({
3251
+ code: "NETWORK_ERROR",
3252
+ message: "organization created but could not be retrieved"
3253
+ }),
2366
3254
  null
2367
3255
  ];
2368
3256
  }
3257
+ return [null, org];
3258
+ } catch (cause) {
3259
+ return [fromConvexError(cause), null];
2369
3260
  }
2370
3261
  },
2371
- treasury: {
2372
- retrieve: async () => stub("organizations.treasury.retrieve")
2373
- },
2374
- members: {
2375
- list: async () => stub("organizations.members.list"),
2376
- retrieve: async () => stub("organizations.members.retrieve"),
2377
- invite: async () => stub("organizations.members.invite"),
2378
- updateRole: async () => stub("organizations.members.updateRole"),
2379
- remove: async () => stub("organizations.members.remove")
2380
- },
2381
- apiKeys: createApiKeysClient(),
2382
- kybProfile: {
2383
- start: async () => stub("organizations.kybProfile.start"),
2384
- retrieve: async () => stub("organizations.kybProfile.retrieve")
2385
- },
2386
- subAccounts: {
2387
- create: async () => stub("organizations.subAccounts.create"),
2388
- list: async () => stub("organizations.subAccounts.list"),
2389
- retrieve: async () => stub("organizations.subAccounts.retrieve"),
2390
- remove: async () => stub("organizations.subAccounts.remove")
2391
- },
2392
- externalAccounts: createOrgExternalAccountsClient(config),
2393
- balanceLedger: {
2394
- list: async () => stub(
2395
- "organizations.balanceLedger.list"
2396
- ),
2397
- retrieve: async () => stub(
2398
- "organizations.balanceLedger.retrieve"
2399
- )
2400
- },
2401
- payments: createOrgPaymentsClient(),
2402
- transfers: createOrgTransfersClient(),
2403
- withdrawals: createOrgWithdrawalsClient(config),
2404
- documents: createOrgDocumentsClient(),
2405
- webhookEndpoints: createWebhookEndpointsClient(),
2406
- webhookEvents: createWebhookEventsClient()
2407
- };
2408
- }
2409
-
2410
- // src/core/sub-accounts.ts
2411
- function createSubAccountsClient() {
2412
- return {
2413
- retrieve: async () => stub("subAccounts.retrieve"),
2414
- remove: async () => stub("subAccounts.remove")
2415
- };
2416
- }
2417
-
2418
- // src/core/token-transfers.ts
2419
- var toTokenTransferId = (raw) => {
2420
- if (typeof raw !== "string" || raw.length === 0) {
2421
- throw new Error(
2422
- `Invalid tokenTransferId: expected non-empty string, got ${String(raw)}`
2423
- );
2424
- }
2425
- return raw;
2426
- };
2427
- function brandRow(row) {
2428
- return {
2429
- ...row,
2430
- id: toTokenTransferId(row.id)
2431
- };
2432
- }
2433
- function createTokenTransfersClient(config = {}) {
2434
- return {
2435
- list: async (input) => {
2436
- if (!config.data) {
2437
- return stub("tokenTransfers.list");
3262
+ retrieve: async (organizationId) => {
3263
+ if (!config._data) {
3264
+ return stub("organizations.retrieve");
2438
3265
  }
2439
3266
  try {
2440
- const raw = await config.data.query(
2441
- api.tokenTransfers.queries.list,
2442
- {
2443
- limit: input?.limit,
2444
- cursor: input?.cursor,
2445
- direction: input?.direction
2446
- }
2447
- );
2448
- if (!raw) {
3267
+ const orgId = organizationId.replace(/^org_/, "");
3268
+ const org = await config._data.query(api.org.queries.retrieve, {
3269
+ orgId
3270
+ });
3271
+ if (!org) {
2449
3272
  return [
2450
3273
  new CapxulError({
2451
- code: "NOT_AUTHENTICATED",
2452
- message: "tokenTransfers.list requires an authenticated session."
3274
+ code: "NOT_FOUND",
3275
+ message: `organization ${organizationId} not found`
2453
3276
  }),
2454
3277
  null
2455
3278
  ];
2456
3279
  }
2457
- return [
2458
- null,
2459
- {
2460
- object: "list",
2461
- data: raw.items.map(brandRow),
2462
- page: {
2463
- hasMore: raw.hasMore,
2464
- nextCursor: raw.nextCursor
2465
- },
2466
- displayCurrency: raw.displayCurrency
2467
- }
2468
- ];
3280
+ return [null, org];
2469
3281
  } catch (cause) {
2470
3282
  return [fromConvexError(cause), null];
2471
3283
  }
2472
3284
  },
2473
- retrieve: async (input) => {
2474
- if (!config.data) {
2475
- return stub("tokenTransfers.retrieve");
3285
+ list: async (input) => {
3286
+ if (!config._data) {
3287
+ return stub("organizations.list");
2476
3288
  }
2477
3289
  try {
2478
- const raw = await config.data.query(
2479
- api.tokenTransfers.queries.getByTxLogIndex,
2480
- {
2481
- txHash: input.txHash,
2482
- logIndex: input.logIndex,
2483
- chainId: input.chainId
3290
+ const page = await config._data.query(api.org.queries.list, {
3291
+ limit: input?.limit,
3292
+ cursor: input?.cursor
3293
+ });
3294
+ const result = {
3295
+ object: "list",
3296
+ data: page.data,
3297
+ page: {
3298
+ hasMore: page.hasMore,
3299
+ cursor: page.nextCursor
2484
3300
  }
2485
- );
2486
- if (!raw) {
3301
+ };
3302
+ return [null, result];
3303
+ } catch (cause) {
3304
+ return [fromConvexError(cause), null];
3305
+ }
3306
+ },
3307
+ update: async (input) => {
3308
+ if (!config._data) {
3309
+ return stub("organizations.update");
3310
+ }
3311
+ try {
3312
+ const orgId = input.organizationId.replace(/^org_/, "");
3313
+ const org = await config._data.mutation(api.org.mutations.update, {
3314
+ orgId,
3315
+ name: input.name
3316
+ });
3317
+ if (!org) {
2487
3318
  return [
2488
3319
  new CapxulError({
2489
3320
  code: "NOT_FOUND",
2490
- message: `tokenTransfer (txHash=${input.txHash}, logIndex=${input.logIndex}) not found or not visible to caller.`,
2491
- details: {
2492
- txHash: input.txHash,
2493
- logIndex: input.logIndex,
2494
- chainId: input.chainId
2495
- }
3321
+ message: `organization ${input.organizationId} not found`
2496
3322
  }),
2497
3323
  null
2498
3324
  ];
2499
3325
  }
2500
- return [null, brandRow(raw)];
3326
+ return [null, org];
2501
3327
  } catch (cause) {
2502
3328
  return [fromConvexError(cause), null];
2503
3329
  }
2504
- }
2505
- };
2506
- }
2507
-
2508
- // src/core/virtual-accounts.ts
2509
- function createVirtualAccountsClient() {
2510
- return {
2511
- create: async () => stub("virtualAccounts.create"),
2512
- retrieve: async () => stub("virtualAccounts.retrieve"),
2513
- list: async () => stub("virtualAccounts.list"),
2514
- remove: async () => stub("virtualAccounts.remove")
2515
- };
2516
- }
2517
-
2518
- // src/core/virtual-cards.ts
2519
- function createVirtualCardsClient() {
2520
- return {
2521
- create: async () => stub("virtualCards.create"),
2522
- retrieve: async () => stub("virtualCards.retrieve"),
2523
- list: async () => stub("virtualCards.list"),
2524
- freeze: async () => stub("virtualCards.freeze"),
2525
- unfreeze: async () => stub("virtualCards.unfreeze"),
2526
- cancel: async () => stub("virtualCards.cancel")
2527
- };
2528
- }
2529
- function createAuthFlowMachine(client) {
2530
- return setup({
2531
- types: {},
2532
- actors: {
2533
- // XState v5's `fromPromise` injects an `AbortSignal` that aborts
2534
- // when the actor is stopped (parent transition fires, machine is
2535
- // disposed, etc.). Plumbing it through `client.auth.sendOtp` /
2536
- // `client.auth.verifyOtp` makes the in-flight HTTP request
2537
- // cancellable: stale responses can't race a state machine
2538
- // that's already moved on. See PR #406 S5.
2539
- sendOtp: fromPromise(async ({ input, signal }) => {
2540
- const [error] = await client.auth.sendOtp(
2541
- { email: input.email },
2542
- { signal }
2543
- );
2544
- if (error) throw error;
2545
- }),
2546
- verifyOtp: fromPromise(
2547
- async ({ input, signal }) => {
2548
- const [error, result] = await client.auth.verifyOtp(
2549
- {
2550
- email: input.email,
2551
- otp: input.code
2552
- },
2553
- { signal }
2554
- );
2555
- if (error) throw error;
2556
- if (result.kind === "bootstrap_required") {
2557
- throw new CapxulError({
2558
- code: "ACTION_REQUIRED",
2559
- message: "OTP verified but auth bootstrap is required. Use createAuthBootstrapFlowMachine for product sign-up.",
2560
- details: { reason: result.reason }
2561
- });
2562
- }
2563
- return result.session;
2564
- }
2565
- ),
2566
- signOut: fromPromise(async () => {
2567
- const [error] = await client.auth.signOut();
2568
- if (error) throw error;
2569
- })
2570
3330
  },
2571
- actions: {
2572
- trackOtpRequested: ({ context }) => {
2573
- if (!context.email) return;
2574
- track("auth_otp_requested", {
2575
- email_domain: emailDomain(context.email)
2576
- });
2577
- },
2578
- trackOtpFailed: ({ event }) => {
2579
- const error = errorFromEvent(event);
2580
- track("auth_failed", {
2581
- auth_type: "email_otp",
2582
- reason: error.code
2583
- });
2584
- },
2585
- trackTimeoutFailed: () => {
2586
- track("auth_failed", {
2587
- auth_type: "email_otp",
2588
- reason: "timeout"
2589
- });
2590
- },
2591
- trackVerified: () => {
2592
- track("auth_verified", { auth_type: "email_otp" });
2593
- },
2594
- identifyAndTrack: ({ context }) => {
2595
- if (!context.session) return;
2596
- identify(context.session.authUserId, {
2597
- email_domain: emailDomain(context.session.email)
2598
- });
2599
- track("auth_identified", {
2600
- email_domain: emailDomain(context.session.email)
2601
- });
2602
- },
2603
- trackSignedOut: () => {
2604
- track("auth_signed_out");
2605
- }
2606
- }
2607
- }).createMachine({
2608
- id: "auth",
2609
- initial: "idle",
2610
- context: { email: null, session: null, error: null },
2611
- states: {
2612
- idle: {
2613
- on: {
2614
- REQUEST_OTP: {
2615
- target: "sending_otp",
2616
- actions: assign({
2617
- email: ({ event }) => event.email,
2618
- error: () => null
2619
- })
2620
- }
2621
- }
2622
- },
2623
- sending_otp: {
2624
- invoke: {
2625
- src: "sendOtp",
2626
- input: ({ context }) => ({ email: requireEmail(context) }),
2627
- onDone: {
2628
- target: "otp_requested",
2629
- actions: ["trackOtpRequested"]
2630
- },
2631
- onError: {
2632
- target: "error",
2633
- actions: [
2634
- assign({ error: ({ event }) => errorFromEvent(event) }),
2635
- "trackOtpFailed"
2636
- ]
2637
- }
2638
- },
2639
- after: {
2640
- [FLOW_INVOKE_TIMEOUT_MS]: {
2641
- target: "error",
2642
- actions: [
2643
- assign({
2644
- error: () => timeoutError("sending_otp")
2645
- }),
2646
- "trackTimeoutFailed"
2647
- ]
2648
- }
2649
- }
2650
- },
2651
- otp_requested: {
2652
- on: {
2653
- VERIFY: { target: "verifying" },
2654
- RESET: {
2655
- target: "idle",
2656
- actions: assign({ email: () => null, error: () => null })
2657
- }
3331
+ safes: {
3332
+ retrieve: async (input) => {
3333
+ if (!config._data) {
3334
+ return stub("organizations.safes.retrieve");
2658
3335
  }
2659
- },
2660
- verifying: {
2661
- invoke: {
2662
- src: "verifyOtp",
2663
- input: ({ context, event }) => ({
2664
- email: requireEmail(context),
2665
- code: requireCodeFromEvent(event)
2666
- }),
2667
- onDone: {
2668
- target: "authenticated",
2669
- actions: [
2670
- // Scrub the duplicate `context.email` (input value
2671
- // captured during sendOtp) since the verified
2672
- // `session.email` is now the canonical source
2673
- // post-authentication. The session's email is
2674
- // intentionally retained — it's the auth result, not
2675
- // lingering input. See PR #406 S2.
2676
- assign({
2677
- session: ({ event }) => event.output,
2678
- email: () => null
2679
- }),
2680
- "trackVerified",
2681
- "identifyAndTrack"
2682
- ]
2683
- },
2684
- onError: {
2685
- target: "error",
2686
- actions: [
2687
- assign({ error: ({ event }) => errorFromEvent(event) }),
2688
- "trackOtpFailed"
2689
- ]
2690
- }
2691
- },
2692
- after: {
2693
- [FLOW_INVOKE_TIMEOUT_MS]: {
2694
- target: "error",
2695
- actions: [
2696
- assign({
2697
- error: () => timeoutError("verifying")
3336
+ try {
3337
+ const safe = await config._data.query(
3338
+ api.safe.queries.retrieveOrganizationSafe,
3339
+ input
3340
+ );
3341
+ if (!safe) {
3342
+ return [
3343
+ new CapxulError({
3344
+ code: "NOT_FOUND",
3345
+ message: `safe ${input.safeId} not found`
2698
3346
  }),
2699
- "trackTimeoutFailed"
2700
- ]
3347
+ null
3348
+ ];
2701
3349
  }
3350
+ return [null, safe];
3351
+ } catch (cause) {
3352
+ return [
3353
+ fromConvexError(cause),
3354
+ null
3355
+ ];
2702
3356
  }
2703
- },
2704
- authenticated: {
2705
- on: {
2706
- SIGN_OUT: { target: "signing_out" }
3357
+ }
3358
+ },
3359
+ treasury: {
3360
+ retrieve: async (organizationId) => {
3361
+ if (!config._data) {
3362
+ return stub(
3363
+ "organizations.treasury.retrieve"
3364
+ );
2707
3365
  }
2708
- },
2709
- signing_out: {
2710
- invoke: {
2711
- src: "signOut",
2712
- onDone: {
2713
- target: "idle",
2714
- actions: [
2715
- assign({
2716
- session: () => null,
2717
- email: () => null,
2718
- error: () => null
3366
+ try {
3367
+ const orgId = organizationId.replace(/^org_/, "");
3368
+ const raw = await config._data.query(
3369
+ api.safe.queries.getOrgTreasuryBalance,
3370
+ { orgId }
3371
+ );
3372
+ if (!raw) {
3373
+ return [
3374
+ new CapxulError({
3375
+ code: "NOT_FOUND",
3376
+ message: `treasury for organization ${organizationId} not found`
2719
3377
  }),
2720
- "trackSignedOut"
2721
- ]
2722
- },
2723
- onError: {
2724
- target: "error",
2725
- actions: assign({ error: ({ event }) => errorFromEvent(event) })
2726
- }
2727
- }
2728
- },
2729
- error: {
2730
- on: {
2731
- RESET: {
2732
- target: "idle",
2733
- actions: assign({ error: () => null })
3378
+ null
3379
+ ];
2734
3380
  }
3381
+ const treasury = {
3382
+ object: "treasury",
3383
+ id: toTreasuryId(`try_${orgId}`),
3384
+ organizationId,
3385
+ status: "active",
3386
+ safeId: toSafeId(
3387
+ `safe_${raw.safeAddress.replace(/^0x/, "").toLowerCase()}`
3388
+ ),
3389
+ totalBalance: { value: "0", currency: "USD" },
3390
+ positions: raw.tokens.map((t) => ({
3391
+ symbol: t.symbol,
3392
+ contractAddress: t.tokenAddress,
3393
+ amount: t.balance
3394
+ })),
3395
+ asOf: raw.lastUpdatedAt ? new Date(raw.lastUpdatedAt).toISOString() : (/* @__PURE__ */ new Date()).toISOString()
3396
+ };
3397
+ return [null, treasury];
3398
+ } catch (cause) {
3399
+ return [
3400
+ fromConvexError(cause),
3401
+ null
3402
+ ];
2735
3403
  }
2736
3404
  }
2737
- }
2738
- });
2739
- }
2740
- function requireEmail(context) {
2741
- if (!context.email) {
2742
- throw Errors.invalidInput(
2743
- "email",
2744
- "Auth flow advanced without an email captured in context."
2745
- );
2746
- }
2747
- return context.email;
2748
- }
2749
- function requireCodeFromEvent(event) {
2750
- if (event.type !== "VERIFY") {
2751
- throw Errors.invalidInput(
2752
- "code",
2753
- `Auth flow's verifying state requires a VERIFY event, got ${event.type}.`
2754
- );
2755
- }
2756
- return event.code;
2757
- }
2758
- function errorFromEvent(event) {
2759
- const cause = typeof event === "object" && event !== null && "error" in event ? event.error : event;
2760
- if (cause instanceof CapxulError) {
2761
- if (!EMAIL_MATCH_PATTERN.test(cause.message)) return cause;
2762
- return new CapxulError({
2763
- code: cause.code,
2764
- message: redactEmail(cause.message),
2765
- cause,
2766
- details: cause.details,
2767
- operationId: cause.operationId,
2768
- correlationId: cause.correlationId,
2769
- retryable: cause.retryable
2770
- });
2771
- }
2772
- if (cause instanceof CapxulError2) {
2773
- if (!EMAIL_MATCH_PATTERN.test(cause.message)) return cause;
2774
- return new CapxulError2(cause.code, redactEmail(cause.message), {
2775
- cause,
2776
- details: cause.details,
2777
- correlationId: cause.correlationId,
2778
- layer: cause.layer
2779
- });
2780
- }
2781
- return Errors.providerError("auth", "flow", redactCauseEmail(cause));
2782
- }
2783
- var EMAIL_MATCH_PATTERN = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/;
2784
- var EMAIL_REDACT_PATTERN = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;
2785
- function redactEmail(message) {
2786
- return message.replace(EMAIL_REDACT_PATTERN, "<redacted>");
2787
- }
2788
- function redactCauseEmail(cause) {
2789
- if (cause instanceof Error) {
2790
- if (!EMAIL_MATCH_PATTERN.test(cause.message)) return cause;
2791
- const redacted = new Error(redactEmail(cause.message));
2792
- redacted.cause = cause;
2793
- return redacted;
2794
- }
2795
- if (typeof cause === "string") {
2796
- return redactEmail(cause);
2797
- }
2798
- return cause;
2799
- }
2800
- function timeoutError(state) {
2801
- return Errors.providerError(
2802
- "auth",
2803
- "flow",
2804
- new Error(`timeout: ${state} exceeded ${FLOW_INVOKE_TIMEOUT_MS}ms`)
2805
- );
2806
- }
2807
- function emailDomain(email) {
2808
- const domain = email.split("@")[1]?.trim().toLowerCase();
2809
- return domain || "unknown";
2810
- }
2811
- var initialContext = {
2812
- email: null,
2813
- code: null,
2814
- username: null,
2815
- signerProvider: null,
2816
- bootstrapToken: null,
2817
- bootstrapReason: null,
2818
- session: null,
2819
- account: null,
2820
- safe: null,
2821
- error: null
2822
- };
2823
- function createAuthBootstrapFlowMachine(client) {
2824
- return setup({
2825
- types: {},
2826
- actors: {
2827
- sendOtp: fromPromise(async ({ input, signal }) => {
2828
- const [error] = await client.auth.sendOtp(
2829
- { email: input.email },
2830
- { signal }
2831
- );
2832
- if (error) throw error;
2833
- }),
2834
- verifyOtp: fromPromise(
2835
- async ({ input, signal }) => {
2836
- const [error, result] = await client.auth.verifyOtp(
2837
- { email: input.email, otp: input.code },
2838
- { signal }
2839
- );
2840
- if (error) throw error;
2841
- return result;
2842
- }
2843
- ),
2844
- completeBootstrap: fromPromise(async ({ input }) => {
2845
- const [error, result] = await client.auth.completeBootstrap(input);
2846
- if (error) throw error;
2847
- return result;
2848
- }),
2849
- signOut: fromPromise(async () => {
2850
- const [error] = await client.auth.signOut();
2851
- if (error) throw error;
2852
- })
2853
3405
  },
2854
- actions: {
2855
- trackOtpRequested: ({ context }) => {
2856
- if (!context.email) return;
2857
- track("auth_otp_requested", {
2858
- email_domain: emailDomain2(context.email)
2859
- });
2860
- },
2861
- trackFailed: ({ event }) => {
2862
- track("auth_failed", {
2863
- auth_type: "email_otp",
2864
- reason: errorFromEvent2(event).code
2865
- });
2866
- },
2867
- trackTimeoutFailed: () => {
2868
- track("auth_failed", {
2869
- auth_type: "email_otp",
2870
- reason: "timeout"
2871
- });
2872
- },
2873
- trackVerified: () => {
2874
- track("auth_verified", { auth_type: "email_otp" });
2875
- },
2876
- trackBootstrapRequired: ({ context }) => {
2877
- track("auth_verified", {
2878
- auth_type: "email_otp",
2879
- auth_mode: context.bootstrapReason ?? "bootstrap_required"
2880
- });
2881
- },
2882
- identifyAndTrack: ({ context }) => {
2883
- if (!context.session) return;
2884
- identify(context.session.authUserId, {
2885
- email_domain: emailDomain2(context.session.email)
2886
- });
2887
- track("auth_identified", {
2888
- email_domain: emailDomain2(context.session.email)
2889
- });
3406
+ members: {
3407
+ list: async (input) => {
3408
+ if (!config._data?.action) {
3409
+ return stub("organizations.members.list");
3410
+ }
3411
+ try {
3412
+ const orgId = input.organizationId.replace(/^org_/, "");
3413
+ const page = await config._data.action(api.org.actions.membersList, {
3414
+ organizationId: orgId,
3415
+ status: input.status,
3416
+ limit: input.limit,
3417
+ cursor: input.cursor
3418
+ });
3419
+ return [null, page];
3420
+ } catch (cause) {
3421
+ return [fromConvexError(cause), null];
3422
+ }
2890
3423
  },
2891
- trackSignedOut: () => {
2892
- track("auth_signed_out");
2893
- }
2894
- }
2895
- }).createMachine({
2896
- id: "authBootstrap",
2897
- initial: "email",
2898
- context: initialContext,
2899
- states: {
2900
- email: {
2901
- on: {
2902
- ENTER_EMAIL: {
2903
- actions: assign({
2904
- email: ({ event }) => event.email,
2905
- error: () => null
2906
- })
2907
- },
2908
- REQUEST_OTP: { target: "sending_otp" }
3424
+ retrieve: async (input) => {
3425
+ if (!config._data?.action) {
3426
+ return stub("organizations.members.retrieve");
3427
+ }
3428
+ try {
3429
+ const orgId = input.organizationId.replace(/^org_/, "");
3430
+ const memberId = input.memberId.replace(/^mb_/, "");
3431
+ const member = await config._data.action(
3432
+ api.org.actions.retrieveMember,
3433
+ {
3434
+ organizationId: orgId,
3435
+ memberId
3436
+ }
3437
+ );
3438
+ return [null, member];
3439
+ } catch (cause) {
3440
+ return [fromConvexError(cause), null];
2909
3441
  }
2910
3442
  },
2911
- sending_otp: {
2912
- invoke: {
2913
- src: "sendOtp",
2914
- input: ({ context }) => ({ email: requireEmail2(context) }),
2915
- onDone: { target: "otp_requested", actions: "trackOtpRequested" },
2916
- onError: {
2917
- target: "otp_requested",
2918
- actions: [
2919
- assign({ error: ({ event }) => errorFromEvent2(event) }),
2920
- "trackFailed"
2921
- ]
2922
- }
2923
- },
2924
- after: {
2925
- [FLOW_INVOKE_TIMEOUT_MS]: {
2926
- target: "otp_requested",
2927
- actions: [
2928
- assign({ error: () => timeoutError2("sending_otp") }),
2929
- "trackTimeoutFailed"
2930
- ]
2931
- }
3443
+ invite: async (input) => {
3444
+ if (!config._data?.action) {
3445
+ return stub(
3446
+ "organizations.members.invite"
3447
+ );
3448
+ }
3449
+ try {
3450
+ const orgId = input.organizationId.replace(/^org_/, "");
3451
+ const result = await config._data.action(
3452
+ api.org.actions.inviteMember,
3453
+ {
3454
+ organizationId: orgId,
3455
+ email: input.email,
3456
+ role: input.role
3457
+ }
3458
+ );
3459
+ return [null, result];
3460
+ } catch (cause) {
3461
+ return [fromConvexError(cause), null];
2932
3462
  }
2933
3463
  },
2934
- otp_requested: {
2935
- on: {
2936
- ENTER_OTP: {
2937
- actions: assign({
2938
- code: ({ event }) => event.code,
2939
- error: () => null
2940
- })
2941
- },
2942
- VERIFY_OTP: { target: "verifying_otp" },
2943
- BACK: { target: "email" },
2944
- RESET: { target: "email", actions: assign(() => initialContext) }
3464
+ accept: async (input) => {
3465
+ if (!config._data?.action) {
3466
+ return stub("organizations.members.accept");
3467
+ }
3468
+ try {
3469
+ const member = await config._data.action(
3470
+ api.org.actions.acceptInvitation,
3471
+ { token: input.token }
3472
+ );
3473
+ return [null, member];
3474
+ } catch (cause) {
3475
+ return [fromConvexError(cause), null];
2945
3476
  }
2946
3477
  },
2947
- verifying_otp: {
2948
- invoke: {
2949
- src: "verifyOtp",
2950
- input: ({ context }) => ({
2951
- email: requireEmail2(context),
2952
- code: requireCode(context)
2953
- }),
2954
- onDone: [
2955
- {
2956
- guard: ({ event }) => event.output.kind === "existing_member",
2957
- target: "authenticated",
2958
- actions: [
2959
- assign({
2960
- session: ({ event }) => event.output.session,
2961
- account: ({ event }) => event.output.kind === "existing_member" ? event.output.account : null,
2962
- username: ({ event }) => event.output.kind === "existing_member" ? event.output.username : null,
2963
- safe: ({ event }) => event.output.kind === "existing_member" ? event.output.safe : null,
2964
- email: () => null,
2965
- error: () => null
2966
- }),
2967
- "trackVerified",
2968
- "identifyAndTrack"
2969
- ]
2970
- },
3478
+ updateRole: async (input) => {
3479
+ if (!config._data?.action) {
3480
+ return stub("organizations.members.updateRole");
3481
+ }
3482
+ try {
3483
+ const orgId = input.organizationId.replace(/^org_/, "");
3484
+ const memberId = input.memberId.replace(/^mb_/, "");
3485
+ const member = await config._data.action(
3486
+ api.org.actions.updateMemberRole,
2971
3487
  {
2972
- target: "bootstrap_required",
2973
- actions: [
2974
- assign({
2975
- session: ({ event }) => event.output.session,
2976
- bootstrapToken: ({ event }) => event.output.kind === "bootstrap_required" ? event.output.bootstrapToken : null,
2977
- bootstrapReason: ({ event }) => event.output.kind === "bootstrap_required" ? event.output.reason : null,
2978
- username: ({ event }) => event.output.kind === "bootstrap_required" ? event.output.username ?? null : null,
2979
- email: ({ event }) => event.output.kind === "bootstrap_required" ? event.output.email : null,
2980
- error: () => null
2981
- }),
2982
- "trackVerified",
2983
- "trackBootstrapRequired"
2984
- ]
3488
+ organizationId: orgId,
3489
+ memberId,
3490
+ role: input.role
2985
3491
  }
2986
- ],
2987
- onError: {
2988
- target: "otp_requested",
2989
- actions: [
2990
- assign({ error: ({ event }) => errorFromEvent2(event) }),
2991
- "trackFailed"
2992
- ]
2993
- }
2994
- },
2995
- after: {
2996
- [FLOW_INVOKE_TIMEOUT_MS]: {
2997
- target: "otp_requested",
2998
- actions: [
2999
- assign({ error: () => timeoutError2("verifying_otp") }),
3000
- "trackTimeoutFailed"
3001
- ]
3002
- }
3492
+ );
3493
+ return [null, member];
3494
+ } catch (cause) {
3495
+ return [fromConvexError(cause), null];
3003
3496
  }
3004
3497
  },
3005
- bootstrap_required: {
3006
- on: {
3007
- ENTER_USERNAME: {
3008
- actions: assign({
3009
- username: ({ event }) => event.username,
3010
- error: () => null
3011
- })
3012
- },
3013
- ENTER_SIGNER_PROVIDER: {
3014
- actions: assign({
3015
- signerProvider: ({ event }) => event.signerProvider,
3016
- error: () => null
3017
- })
3018
- },
3019
- COMPLETE_BOOTSTRAP: { target: "completing_bootstrap" },
3020
- BACK: { target: "otp_requested" },
3021
- RESET: { target: "email", actions: assign(() => initialContext) }
3498
+ revoke: async (input) => {
3499
+ if (!config._data?.action) {
3500
+ return stub("organizations.members.revoke");
3022
3501
  }
3023
- },
3024
- completing_bootstrap: {
3025
- invoke: {
3026
- src: "completeBootstrap",
3027
- input: ({ context }) => ({
3028
- bootstrapToken: requireBootstrapToken(context),
3029
- username: requireUsername(context),
3030
- signerProvider: requireSignerProvider(context)
3031
- }),
3032
- onDone: {
3033
- target: "authenticated",
3034
- actions: [
3035
- assign({
3036
- session: ({ event }) => event.output.session,
3037
- account: ({ event }) => event.output.account,
3038
- username: ({ event }) => event.output.username,
3039
- safe: ({ event }) => event.output.safe,
3040
- bootstrapToken: () => null,
3041
- bootstrapReason: () => null,
3042
- signerProvider: () => null,
3043
- email: () => null,
3044
- error: () => null
3045
- }),
3046
- "identifyAndTrack"
3047
- ]
3048
- },
3049
- onError: {
3050
- target: "bootstrap_required",
3051
- actions: [
3052
- assign({ error: ({ event }) => errorFromEvent2(event) }),
3053
- "trackFailed"
3054
- ]
3055
- }
3056
- },
3057
- after: {
3058
- [FLOW_INVOKE_TIMEOUT_MS]: {
3059
- target: "bootstrap_required",
3060
- actions: [
3061
- assign({ error: () => timeoutError2("completing_bootstrap") }),
3062
- "trackTimeoutFailed"
3063
- ]
3064
- }
3502
+ try {
3503
+ const orgId = input.organizationId.replace(/^org_/, "");
3504
+ const memberId = input.memberId.replace(/^mb_/, "");
3505
+ const member = await config._data.action(
3506
+ api.org.actions.revokeMember,
3507
+ {
3508
+ organizationId: orgId,
3509
+ memberId
3510
+ }
3511
+ );
3512
+ return [null, member];
3513
+ } catch (cause) {
3514
+ return [fromConvexError(cause), null];
3065
3515
  }
3066
3516
  },
3067
- authenticated: {
3068
- on: {
3069
- SIGN_OUT: { target: "signing_out" }
3517
+ remove: async (input) => {
3518
+ if (!config._data?.action) {
3519
+ return stub("organizations.members.remove");
3520
+ }
3521
+ try {
3522
+ const orgId = input.organizationId.replace(/^org_/, "");
3523
+ const memberId = input.memberId.replace(/^mb_/, "");
3524
+ await config._data.action(api.org.actions.removeMember, {
3525
+ organizationId: orgId,
3526
+ memberId
3527
+ });
3528
+ return [null, void 0];
3529
+ } catch (cause) {
3530
+ return [fromConvexError(cause), null];
3070
3531
  }
3071
3532
  },
3072
- signing_out: {
3073
- invoke: {
3074
- src: "signOut",
3075
- onDone: {
3076
- target: "email",
3077
- actions: [
3078
- assign(() => initialContext),
3079
- "trackSignedOut"
3080
- ]
3081
- },
3082
- onError: {
3083
- target: "error",
3084
- actions: assign({ error: ({ event }) => errorFromEvent2(event) })
3085
- }
3533
+ resend: async (input) => {
3534
+ if (!config._data?.action) {
3535
+ return stub(
3536
+ "organizations.members.resend"
3537
+ );
3538
+ }
3539
+ try {
3540
+ const orgId = input.organizationId.replace(/^org_/, "");
3541
+ const memberId = input.memberId.replace(/^mb_/, "");
3542
+ const result = await config._data.action(
3543
+ api.org.actions.resendInvitation,
3544
+ {
3545
+ organizationId: orgId,
3546
+ memberId
3547
+ }
3548
+ );
3549
+ return [null, result];
3550
+ } catch (cause) {
3551
+ return [fromConvexError(cause), null];
3552
+ }
3553
+ }
3554
+ },
3555
+ apiKeys: createApiKeysClient(),
3556
+ subAccounts: createOrgSubAccountsClient(config),
3557
+ externalAccounts: createOrgExternalAccountsClient(config),
3558
+ balanceLedger: {
3559
+ list: async (input) => {
3560
+ if (!config._data) {
3561
+ return stub(
3562
+ "organizations.balanceLedger.list"
3563
+ );
3564
+ }
3565
+ try {
3566
+ const orgId = input.organizationId.replace(/^org_/, "");
3567
+ const page = await config._data.query(
3568
+ api.balanceLedger.queries.listForOrg,
3569
+ { orgId, limit: input.limit, cursor: input.cursor }
3570
+ );
3571
+ return [null, page];
3572
+ } catch (cause) {
3573
+ return [fromConvexError(cause), null];
3086
3574
  }
3087
3575
  },
3088
- error: {
3089
- on: {
3090
- RESET: { target: "email", actions: assign(() => initialContext) }
3576
+ retrieve: async (input) => {
3577
+ if (!config._data) {
3578
+ return stub(
3579
+ "organizations.balanceLedger.retrieve"
3580
+ );
3581
+ }
3582
+ try {
3583
+ const entry = await config._data.query(
3584
+ api.balanceLedger.queries.retrieve,
3585
+ { entryId: input.entryId }
3586
+ );
3587
+ if (!entry) {
3588
+ return [
3589
+ new CapxulError({
3590
+ code: "NOT_FOUND",
3591
+ message: `balance_ledger_entry ${input.entryId} not found`
3592
+ }),
3593
+ null
3594
+ ];
3595
+ }
3596
+ return [null, entry];
3597
+ } catch (cause) {
3598
+ return [fromConvexError(cause), null];
3091
3599
  }
3092
3600
  }
3093
- }
3094
- });
3095
- }
3096
- function requireEmail2(context) {
3097
- if (!context.email) {
3098
- throw Errors.invalidInput("email", "Auth bootstrap requires an email.");
3099
- }
3100
- return context.email;
3101
- }
3102
- function requireCode(context) {
3103
- if (!context.code) {
3104
- throw Errors.invalidInput("code", "Auth bootstrap requires an OTP code.");
3105
- }
3106
- return context.code;
3107
- }
3108
- function requireBootstrapToken(context) {
3109
- if (!context.bootstrapToken) {
3110
- throw Errors.invalidInput(
3111
- "bootstrapToken",
3112
- "Auth bootstrap requires a continuation token."
3113
- );
3114
- }
3115
- return context.bootstrapToken;
3116
- }
3117
- function requireUsername(context) {
3118
- if (!context.username) {
3119
- throw Errors.invalidInput("username", "Auth bootstrap requires a username.");
3120
- }
3121
- return context.username;
3601
+ },
3602
+ payments: createOrgPaymentsClient(),
3603
+ transfers: createOrgTransfersClient(),
3604
+ withdrawals: createOrgWithdrawalsClient(config),
3605
+ documents: createOrgDocumentsClient(config),
3606
+ webhookEndpoints: createWebhookEndpointsClient(),
3607
+ webhookEvents: createWebhookEventsClient()
3608
+ };
3122
3609
  }
3123
- function requireSignerProvider(context) {
3124
- if (!context.signerProvider) {
3125
- throw Errors.invalidInput(
3126
- "signerProvider",
3127
- "Auth bootstrap requires a signer provider."
3610
+
3611
+ // src/core/token-transfers.ts
3612
+ var toTokenTransferId = (raw) => {
3613
+ if (typeof raw !== "string" || raw.length === 0) {
3614
+ throw new Error(
3615
+ `Invalid tokenTransferId: expected non-empty string, got ${String(raw)}`
3128
3616
  );
3129
3617
  }
3130
- return context.signerProvider;
3618
+ return raw;
3619
+ };
3620
+ function brandRow(row) {
3621
+ return {
3622
+ ...row,
3623
+ id: toTokenTransferId(row.id)
3624
+ };
3131
3625
  }
3132
- function errorFromEvent2(event) {
3133
- const cause = typeof event === "object" && event !== null && "error" in event ? event.error : event;
3134
- if (cause instanceof CapxulError || cause instanceof CapxulError2) {
3135
- return cause;
3136
- }
3137
- return Errors.providerError("auth", "bootstrap", cause);
3626
+ function createTokenTransfersClient(config = {}) {
3627
+ return {
3628
+ list: async (input) => {
3629
+ if (!config._data) {
3630
+ return stub("tokenTransfers.list");
3631
+ }
3632
+ try {
3633
+ const raw = await config._data.query(
3634
+ api.tokenTransfers.queries.list,
3635
+ {
3636
+ limit: input?.limit,
3637
+ cursor: input?.cursor,
3638
+ direction: input?.direction
3639
+ }
3640
+ );
3641
+ if (!raw) {
3642
+ return [
3643
+ new CapxulError({
3644
+ code: "NOT_AUTHENTICATED",
3645
+ message: "tokenTransfers.list requires an authenticated session."
3646
+ }),
3647
+ null
3648
+ ];
3649
+ }
3650
+ return [
3651
+ null,
3652
+ {
3653
+ object: "list",
3654
+ data: raw.items.map(brandRow),
3655
+ page: {
3656
+ hasMore: raw.hasMore,
3657
+ nextCursor: raw.nextCursor
3658
+ },
3659
+ displayCurrency: raw.displayCurrency
3660
+ }
3661
+ ];
3662
+ } catch (cause) {
3663
+ return [fromConvexError(cause), null];
3664
+ }
3665
+ },
3666
+ retrieve: async (input) => {
3667
+ if (!config._data) {
3668
+ return stub("tokenTransfers.retrieve");
3669
+ }
3670
+ try {
3671
+ const raw = await config._data.query(
3672
+ api.tokenTransfers.queries.getByTxLogIndex,
3673
+ {
3674
+ txHash: input.txHash,
3675
+ logIndex: input.logIndex,
3676
+ chainId: input.chainId
3677
+ }
3678
+ );
3679
+ if (!raw) {
3680
+ return [
3681
+ new CapxulError({
3682
+ code: "NOT_FOUND",
3683
+ message: `tokenTransfer (txHash=${input.txHash}, logIndex=${input.logIndex}) not found or not visible to caller.`,
3684
+ details: {
3685
+ txHash: input.txHash,
3686
+ logIndex: input.logIndex,
3687
+ chainId: input.chainId
3688
+ }
3689
+ }),
3690
+ null
3691
+ ];
3692
+ }
3693
+ return [null, brandRow(raw)];
3694
+ } catch (cause) {
3695
+ return [fromConvexError(cause), null];
3696
+ }
3697
+ }
3698
+ };
3138
3699
  }
3139
- function timeoutError2(state) {
3140
- return Errors.providerError(
3141
- "auth",
3142
- "bootstrap",
3143
- new Error(`timeout: ${state} exceeded ${FLOW_INVOKE_TIMEOUT_MS}ms`)
3144
- );
3700
+
3701
+ // src/core/virtual-accounts.ts
3702
+ function createVirtualAccountsClient() {
3703
+ return {
3704
+ create: async () => stub("virtualAccounts.create"),
3705
+ retrieve: async () => stub("virtualAccounts.retrieve"),
3706
+ list: async () => stub("virtualAccounts.list"),
3707
+ remove: async () => stub("virtualAccounts.remove")
3708
+ };
3145
3709
  }
3146
- function emailDomain2(email) {
3147
- const domain = email.split("@")[1]?.trim().toLowerCase();
3148
- return domain || "unknown";
3710
+
3711
+ // src/core/virtual-cards.ts
3712
+ function createVirtualCardsClient() {
3713
+ return {
3714
+ create: async () => stub("virtualCards.create"),
3715
+ retrieve: async () => stub("virtualCards.retrieve"),
3716
+ list: async () => stub("virtualCards.list"),
3717
+ freeze: async () => stub("virtualCards.freeze"),
3718
+ unfreeze: async () => stub("virtualCards.unfreeze"),
3719
+ cancel: async () => stub("virtualCards.cancel")
3720
+ };
3149
3721
  }
3150
3722
  function createProvisioningMachine(client) {
3151
3723
  return setup({
@@ -3174,7 +3746,7 @@ function createProvisioningMachine(client) {
3174
3746
  const provider = context.input?.signerProvider;
3175
3747
  if (!provider) return;
3176
3748
  track("provisioning_safe_created", {
3177
- safe_address: provider.safeAddress
3749
+ safe_address: deriveSafeAddress2(provider.signerAddress)
3178
3750
  });
3179
3751
  }
3180
3752
  }
@@ -3219,13 +3791,13 @@ function createProvisioningMachine(client) {
3219
3791
  },
3220
3792
  onError: {
3221
3793
  target: "error",
3222
- actions: assign({ error: ({ event }) => errorFromEvent3(event) })
3794
+ actions: assign({ error: ({ event }) => errorFromEvent(event) })
3223
3795
  }
3224
3796
  },
3225
3797
  after: {
3226
3798
  [FLOW_INVOKE_TIMEOUT_MS]: {
3227
3799
  target: "error",
3228
- actions: assign({ error: () => timeoutError3() })
3800
+ actions: assign({ error: () => timeoutError() })
3229
3801
  }
3230
3802
  }
3231
3803
  },
@@ -3243,7 +3815,7 @@ function createProvisioningMachine(client) {
3243
3815
  * this payload on its `onDone` transition and branches via guards
3244
3816
  * on `event.output.error`.
3245
3817
  */
3246
- output: ({ context }) => context.error ? { error: context.error } : context.account ? { account: context.account } : { error: timeoutError3() }
3818
+ output: ({ context }) => context.error ? { error: context.error } : context.account ? { account: context.account } : { error: timeoutError() }
3247
3819
  });
3248
3820
  }
3249
3821
  function requireProvisionInput(context) {
@@ -3255,13 +3827,13 @@ function requireProvisionInput(context) {
3255
3827
  }
3256
3828
  return context.input;
3257
3829
  }
3258
- function errorFromEvent3(event) {
3830
+ function errorFromEvent(event) {
3259
3831
  const cause = typeof event === "object" && event !== null && "error" in event ? event.error : event;
3260
3832
  if (cause instanceof CapxulError) return cause;
3261
3833
  if (cause instanceof CapxulError2) return cause;
3262
3834
  return Errors.providerError("provisioning", "flow", cause);
3263
3835
  }
3264
- function timeoutError3() {
3836
+ function timeoutError() {
3265
3837
  return Errors.providerError(
3266
3838
  "provisioning",
3267
3839
  "flow",
@@ -3354,7 +3926,7 @@ function createOnboardingFlowMachine(client) {
3354
3926
  error: ({ event }) => extractChildErrorOrFallback(event)
3355
3927
  }),
3356
3928
  assignChildThrown: assign({
3357
- error: ({ event }) => errorFromEvent4(event)
3929
+ error: ({ event }) => errorFromEvent2(event)
3358
3930
  }),
3359
3931
  assignAccountFromChild: assign({
3360
3932
  account: ({ event }) => extractChildAccountOrNull(event)
@@ -3516,7 +4088,7 @@ function extractChildAccountOrNull(event) {
3516
4088
  if (output && "account" in output && output.account) return output.account;
3517
4089
  return null;
3518
4090
  }
3519
- function errorFromEvent4(event) {
4091
+ function errorFromEvent2(event) {
3520
4092
  const cause = typeof event === "object" && event !== null && "error" in event ? event.error : event;
3521
4093
  if (cause instanceof CapxulError) return cause;
3522
4094
  if (cause instanceof CapxulError2) return cause;
@@ -3527,7 +4099,7 @@ function errorFromEvent4(event) {
3527
4099
  function createCapxulClient(config = {}) {
3528
4100
  const clientWithoutFlows = {
3529
4101
  id: crypto.randomUUID(),
3530
- auth: createAuthClient(config),
4102
+ auth: new AuthService(config),
3531
4103
  me: createMeClient(config),
3532
4104
  accounts: createAccountsClient(config),
3533
4105
  organizations: createOrganizationsClient(config),
@@ -3535,8 +4107,8 @@ function createCapxulClient(config = {}) {
3535
4107
  transfers: createTransfersClient(),
3536
4108
  tokenTransfers: createTokenTransfersClient(config),
3537
4109
  withdrawals: createWithdrawalsClient(config),
3538
- documents: createDocumentsClient(),
3539
- subAccounts: createSubAccountsClient(),
4110
+ documents: createDocumentsClient(config),
4111
+ subAccounts: createSubAccountsClient(config),
3540
4112
  virtualAccounts: createVirtualAccountsClient(),
3541
4113
  virtualCards: createVirtualCardsClient(),
3542
4114
  externalAccounts: createExternalAccountsClient(config),
@@ -3547,8 +4119,6 @@ function createCapxulClient(config = {}) {
3547
4119
  };
3548
4120
  const client = clientWithoutFlows;
3549
4121
  client.flows = {
3550
- auth: () => createAuthFlowMachine(client),
3551
- authBootstrap: () => createAuthBootstrapFlowMachine(client),
3552
4122
  onboarding: () => createOnboardingFlowMachine(client),
3553
4123
  provisioning: () => createProvisioningMachine(client)
3554
4124
  };
@@ -3652,5 +4222,13 @@ function isWebhookEvent(value) {
3652
4222
  const candidate = value;
3653
4223
  return typeof candidate.id === "string" && typeof candidate.type === "string" && typeof candidate.createdAt === "string" && (candidate.operationId === void 0 || typeof candidate.operationId === "string") && (candidate.correlationId === void 0 || typeof candidate.correlationId === "string") && !!candidate.data && typeof candidate.data === "object" && !Array.isArray(candidate.data);
3654
4224
  }
4225
+ var SignerProvisioner = class {
4226
+ provision() {
4227
+ const privateKey = generatePrivateKey();
4228
+ const signer = privateKeyToAccount(privateKey);
4229
+ const safeAddress = deriveSafeAddress2(signer.address);
4230
+ return { signer, safeAddress };
4231
+ }
4232
+ };
3655
4233
 
3656
- export { CapxulError, createAuthBootstrapFlowMachine, createAuthFlowMachine, createCapxulClient, createLocalSigner, createOnboardingFlowMachine, createProvisioningMachine as createProvisioningFlowMachine, makeHttpTransport, matchAction, matchError, matchStatus, toAccountId, toApiKeyId, toBalanceLedgerEntryId, toDocumentId, toEmail, toExternalAccountId, toKybProfileId, toKycProfileId, toMemberId, toOperationId, toOrganizationId, toPaymentId, toPhoneNumber, toSafeId, toSubAccountId, toTokenTransferId, toTransferId, toTreasuryId, toUsername, toVirtualAccountId, toVirtualCardId, toWebhookEndpointId, toWebhookEventId, toWithdrawalId, tryCatch, verifyWebhook };
4234
+ export { AuthService, CapxulError, SignerProvisioner, createCapxulClient, createLocalSigner, createOnboardingFlowMachine, createProvisioningMachine as createProvisioningFlowMachine, makeHttpTransport, matchAction, matchError, matchStatus, resolvePaymentToken, toAccountId, toApiKeyId, toBalanceLedgerEntryId, toDocumentId, toEmail, toExternalAccountId, toKybProfileId, toKycProfileId, toMemberId, toOperationId, toOrganizationId, toPaymentId, toPhoneNumber, toSafeId, toSubAccountId, toTokenTransferId, toTransferId, toTreasuryId, toUsername, toVirtualAccountId, toVirtualCardId, toWebhookEndpointId, toWebhookEventId, toWithdrawalId, tryCatch, verifyWebhook };