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