@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/client.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 = 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
 
10
11
  // src/_generated/api.js
@@ -109,17 +110,270 @@ function track(...args) {
109
110
  const [name, props] = args;
110
111
  debugLog(`[TRACK] ${name} ${formatDebugValue(props ?? "")}`);
111
112
  }
112
- function formatDebugValue2(value) {
113
- if (value === void 0 || value === "") return "";
114
- if (typeof value === "string") return value;
113
+
114
+ // ../config/src/chain.ts
115
+ var CAPXUL_PAYMENTS_ADDRESS = "0xe740ea65521edc9bef0ea75d30b5334711db99f4";
116
+ var TEST_USDC_ADDRESS = "0xf09fcbb6df9f8f918e58f9c8ab15a76ade862b77";
117
+ var CAPXUL_API_BASE_URL = "https://elated-oyster-269.convex.site";
118
+
119
+ // ../config/src/timing.ts
120
+ var FLOW_INVOKE_TIMEOUT_MS = 3e4;
121
+
122
+ // ../config/src/errors.ts
123
+ var CapxulError2 = class extends Error {
124
+ code;
125
+ details;
126
+ correlationId;
127
+ layer;
128
+ constructor(code, message, options) {
129
+ super(message, options?.cause ? { cause: options.cause } : void 0);
130
+ this.code = code;
131
+ this.details = options?.details;
132
+ this.correlationId = options?.correlationId;
133
+ this.layer = options?.layer;
134
+ }
135
+ };
136
+ var Errors = {
137
+ notAuthenticated: () => new CapxulError2("NOT_AUTHENTICATED", "Not authenticated"),
138
+ profileNotFound: (authUserId) => new CapxulError2("PROFILE_NOT_FOUND", `Profile not found for user ${authUserId}`),
139
+ smartAccountMissing: (authUserId) => new CapxulError2("SMART_ACCOUNT_MISSING", `Smart account not provisioned for user ${authUserId}`),
140
+ envMissing: (name) => new CapxulError2("ENV_MISSING", `Environment variable ${name} not configured`),
141
+ openfortApi: (operation, cause) => new CapxulError2(
142
+ "PROVIDER_ERROR",
143
+ `Openfort ${operation} failed: ${cause instanceof Error ? cause.message : String(cause)}`,
144
+ { cause, details: { provider: "openfort", operation } }
145
+ ),
146
+ shieldApi: (status, detail) => new CapxulError2(
147
+ "PROVIDER_ERROR",
148
+ `Shield API error (${status}): ${detail}`,
149
+ { details: { provider: "shield", status } }
150
+ ),
151
+ providerError: (provider, operation, cause) => (
152
+ // Public `message` is redacted to a fixed shape so provider-side
153
+ // exception text never leaks to the client. The original `cause`
154
+ // is preserved on `Error.cause` for server-side debugging via
155
+ // observability sinks (Sentry, console traces).
156
+ new CapxulError2(
157
+ "PROVIDER_ERROR",
158
+ `Provider error: ${provider} ${operation}`,
159
+ { cause, details: { provider, operation } }
160
+ )
161
+ ),
162
+ invalidInput: (field, reason) => new CapxulError2(
163
+ "INVALID_INPUT",
164
+ `Invalid ${field}: ${reason}`,
165
+ { details: { field, reason } }
166
+ ),
167
+ playerNotFound: (playerId) => new CapxulError2(
168
+ "PLAYER_NOT_FOUND",
169
+ playerId ? `Openfort player ${playerId} not found` : "Openfort player not found"
170
+ ),
171
+ accountNotFound: (accountId) => new CapxulError2(
172
+ "ACCOUNT_NOT_FOUND",
173
+ accountId ? `Openfort account ${accountId} not found` : "Openfort smart account not found"
174
+ ),
175
+ invalidRecipient: (reason) => new CapxulError2("INVALID_RECIPIENT", reason),
176
+ permissionDenied: (reason = "Permission denied") => new CapxulError2("PERMISSION_DENIED", reason),
177
+ notFound: (resource, id) => new CapxulError2(
178
+ "NOT_FOUND",
179
+ id ? `${resource} ${id} not found` : `${resource} not found`
180
+ ),
181
+ idempotencyConflict: (details) => new CapxulError2(
182
+ "IDEMPOTENCY_CONFLICT",
183
+ "Idempotency key was already used for a different request",
184
+ { details }
185
+ ),
186
+ emailDeliveryFailed: (detail, details) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`, {
187
+ details
188
+ }),
189
+ rateLimited: (details) => new CapxulError2("RATE_LIMITED", "Request was rate limited", {
190
+ details: { ...details }
191
+ }),
192
+ internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`),
193
+ /**
194
+ * Verification gate. Surfaced when a request hits a verification
195
+ * boundary the actor cannot cross under their current state. Two
196
+ * variants share this code:
197
+ *
198
+ * - Rail gate (Withdrawals v1 W2, #465): the resolved
199
+ * `external_account.kind` routes to a withdrawal rail (e.g.
200
+ * `fiat_offramp`, `card_payout`) that is not yet supported. Carries
201
+ * `details.rail` + `details.currentKind`.
202
+ * - KYC tier gate (legacy / future): the actor's KYC tier is below
203
+ * the required tier. Carries `details.requiredTier`.
204
+ *
205
+ * Code is shared because both expose the same UX shape ("you cannot
206
+ * proceed until verification advances"); the `details.*` keys
207
+ * differentiate the route.
208
+ */
209
+ verificationRequired: (details) => {
210
+ const message = "rail" in details ? `Withdrawal rail "${details.rail}" (kind=${details.currentKind}) is not yet supported.` : `Verification tier ${details.requiredTier} is required.`;
211
+ return new CapxulError2("VERIFICATION_REQUIRED", message, {
212
+ details: { ...details }
213
+ });
214
+ }
215
+ };
216
+
217
+ // ../config/src/safe.ts
218
+ var SAFE_PROXY_FACTORY = "0x4e1dcf7ad4e460cfd30791ccc4f9c8a4f820ec67";
219
+ var SAFE_L2_SINGLETON = "0x29fcb43b46531bca003ddc8fcb67ffe91900c762";
220
+ var SAFE_MODULE_SETUP = "0x2dd68b007b46fbe91b9a7c3eda5a7a1063cb5b47";
221
+ var SAFE_4337_MODULE = "0x75cf11467937ce3f2f357ce24ffc3dbf8fd5c226";
222
+ var MULTI_SEND = "0x38869bf66a61cf6bdb996a6ae40d5853fd43b526";
223
+
224
+ // ../config/src/org-roles.ts
225
+ function roleKeyFromLabel(label) {
226
+ const bytes = new TextEncoder().encode(label);
227
+ const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
228
+ return "0x" + hex.padEnd(64, "0");
229
+ }
230
+ roleKeyFromLabel("OWNER");
231
+ roleKeyFromLabel("FINANCE_MANAGER");
232
+ roleKeyFromLabel("PAYMENTS_OPERATOR");
233
+ var defaultSafeDeriveConfig = {
234
+ safeProxyFactory: SAFE_PROXY_FACTORY,
235
+ safeL2Singleton: SAFE_L2_SINGLETON,
236
+ safeModuleSetup: SAFE_MODULE_SETUP,
237
+ safe4337Module: SAFE_4337_MODULE,
238
+ multiSend: MULTI_SEND
239
+ };
240
+ var SAFE_PROXY_CREATION_CODE = "0x608060405234801561001057600080fd5b506040516101e63803806101e68339818101604052602081101561003357600080fd5b8101908080519060200190929190505050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156100ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806101c46022913960400191505060405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505060ab806101196000396000f3fe608060405273ffffffffffffffffffffffffffffffffffffffff600054167fa619486e0000000000000000000000000000000000000000000000000000000060003514156050578060005260206000f35b3660008037600080366000845af43d6000803e60008114156070573d6000fd5b3d6000f3fea264697066735822122003d1488ee65e08fa41e58e888a9865554c535f2c77126a82cb4c0f917f31441364736f6c63430007060033496e76616c69642073696e676c65746f6e20616464726573732070726f7669646564";
241
+ var enableModulesAbi = [
242
+ {
243
+ type: "function",
244
+ name: "enableModules",
245
+ inputs: [{ type: "address[]", name: "modules" }],
246
+ outputs: [],
247
+ stateMutability: "nonpayable"
248
+ }
249
+ ];
250
+ var multiSendAbi = [
251
+ {
252
+ type: "function",
253
+ name: "multiSend",
254
+ inputs: [{ type: "bytes", name: "transactions" }],
255
+ outputs: [],
256
+ stateMutability: "payable"
257
+ }
258
+ ];
259
+ var setupAbi = [
260
+ {
261
+ type: "function",
262
+ name: "setup",
263
+ inputs: [
264
+ { type: "address[]", name: "owners" },
265
+ { type: "uint256", name: "threshold" },
266
+ { type: "address", name: "to" },
267
+ { type: "bytes", name: "data" },
268
+ { type: "address", name: "fallbackHandler" },
269
+ { type: "address", name: "paymentToken" },
270
+ { type: "uint256", name: "payment" },
271
+ { type: "address", name: "paymentReceiver" }
272
+ ],
273
+ outputs: [],
274
+ stateMutability: "nonpayable"
275
+ }
276
+ ];
277
+ function encodeInternalTransaction(tx) {
278
+ const encoded = viem.encodePacked(
279
+ ["uint8", "address", "uint256", "uint256", "bytes"],
280
+ [
281
+ tx.operation,
282
+ tx.to,
283
+ tx.value,
284
+ BigInt(tx.data.slice(2).length / 2),
285
+ tx.data
286
+ ]
287
+ );
288
+ return encoded.slice(2);
289
+ }
290
+ function computeSaltNonce(ownerAddress) {
291
+ return BigInt(`0x${ownerAddress.toLowerCase().slice(2).padEnd(64, "0")}`);
292
+ }
293
+ function deriveSafeAddress(signerAddress, config = defaultSafeDeriveConfig) {
294
+ const saltNonce = computeSaltNonce(signerAddress);
295
+ const enableModulesData = viem.encodeFunctionData({
296
+ abi: enableModulesAbi,
297
+ functionName: "enableModules",
298
+ args: [[config.safe4337Module]]
299
+ });
300
+ const innerTx = encodeInternalTransaction({
301
+ operation: 1,
302
+ to: config.safeModuleSetup,
303
+ value: 0n,
304
+ data: enableModulesData
305
+ });
306
+ const multiSendCallData = viem.encodeFunctionData({
307
+ abi: multiSendAbi,
308
+ functionName: "multiSend",
309
+ args: [`0x${innerTx}`]
310
+ });
311
+ const initializer = viem.encodeFunctionData({
312
+ abi: setupAbi,
313
+ functionName: "setup",
314
+ args: [
315
+ [signerAddress],
316
+ 1n,
317
+ config.multiSend,
318
+ multiSendCallData,
319
+ config.safe4337Module,
320
+ "0x0000000000000000000000000000000000000000",
321
+ 0n,
322
+ "0x0000000000000000000000000000000000000000"
323
+ ]
324
+ });
325
+ const deploymentCode = viem.encodePacked(
326
+ ["bytes", "uint256"],
327
+ [SAFE_PROXY_CREATION_CODE, BigInt(config.safeL2Singleton)]
328
+ );
329
+ const salt = viem.keccak256(
330
+ viem.encodePacked(
331
+ ["bytes32", "uint256"],
332
+ [viem.keccak256(viem.encodePacked(["bytes"], [initializer])), saltNonce]
333
+ )
334
+ );
335
+ return viem.getContractAddress({
336
+ from: config.safeProxyFactory,
337
+ salt,
338
+ bytecode: deploymentCode,
339
+ opcode: "CREATE2"
340
+ });
341
+ }
342
+
343
+ // src/internal/safe/account.ts
344
+ async function buildSafeAccount(signer, chain) {
115
345
  try {
116
- return JSON.stringify(value);
117
- } catch {
118
- return String(value);
346
+ const publicClient = viem.createPublicClient({
347
+ chain: chains.baseSepolia,
348
+ transport: viem.http(chain.rpcUrl)
349
+ });
350
+ return await accounts.toSafeSmartAccount({
351
+ client: publicClient,
352
+ entryPoint: { address: accountAbstraction.entryPoint07Address, version: "0.7" },
353
+ version: "1.4.1",
354
+ owners: [signer],
355
+ saltNonce: computeSaltNonce2(signer.address),
356
+ safeSingletonAddress: SAFE_L2_SINGLETON,
357
+ safeProxyFactoryAddress: SAFE_PROXY_FACTORY,
358
+ safeModuleSetupAddress: SAFE_MODULE_SETUP,
359
+ safe4337ModuleAddress: SAFE_4337_MODULE,
360
+ safeModules: [],
361
+ setupTransactions: []
362
+ });
363
+ } catch (cause) {
364
+ throw new CapxulError({
365
+ code: "NETWORK_ERROR",
366
+ message: cause instanceof Error ? cause.message : String(cause),
367
+ cause,
368
+ details: { chainId: chain.chainId }
369
+ });
119
370
  }
120
371
  }
121
- function identify(userId, traits) {
122
- debugLog(`[IDENTIFY] ${userId} ${formatDebugValue2(traits ?? "")}`);
372
+ function computeSaltNonce2(ownerAddress) {
373
+ return BigInt(`0x${ownerAddress.toLowerCase().slice(2).padEnd(64, "0")}`);
374
+ }
375
+ function deriveSafeAddress2(signerAddress) {
376
+ return deriveSafeAddress(signerAddress, defaultSafeDeriveConfig);
123
377
  }
124
378
 
125
379
  // ../platform-kernel/src/ids.ts
@@ -134,12 +388,24 @@ function makePrefixedIdConstructor(prefix, fieldName) {
134
388
  return raw;
135
389
  };
136
390
  }
391
+ var toSafeId = makePrefixedIdConstructor(
392
+ "safe",
393
+ "safeId"
394
+ );
395
+ var toTreasuryId = makePrefixedIdConstructor(
396
+ "try",
397
+ "treasuryId"
398
+ );
137
399
  var toOperationId = makePrefixedIdConstructor(
138
400
  "op",
139
401
  "operationId"
140
402
  );
141
403
  var toCorrelationId = makePrefixedIdConstructor("ctx", "correlationId");
142
404
  var toExternalAccountId = makePrefixedIdConstructor("ext", "externalAccountId");
405
+ var toSubAccountId = makePrefixedIdConstructor(
406
+ "sub",
407
+ "subAccountId"
408
+ );
143
409
 
144
410
  // src/core/external-accounts.ts
145
411
  function brandExternalAccount(raw) {
@@ -156,13 +422,13 @@ function brandExternalAccount(raw) {
156
422
  function createExternalAccountsClient(config = {}) {
157
423
  return {
158
424
  retrieve: async (externalAccountId) => {
159
- if (!config.data) {
425
+ if (!config._data) {
160
426
  return stub(
161
427
  "externalAccounts.retrieve"
162
428
  );
163
429
  }
164
430
  const [err, raw] = await tryCatch(
165
- config.data.query(api.externalAccounts.queries.retrievePersonal, {
431
+ config._data.query(api.externalAccounts.queries.retrievePersonal, {
166
432
  externalAccountId
167
433
  })
168
434
  );
@@ -184,11 +450,11 @@ function createExternalAccountsClient(config = {}) {
184
450
  return [null, brandExternalAccount(raw)];
185
451
  },
186
452
  remove: async (externalAccountId) => {
187
- if (!config.data) {
453
+ if (!config._data) {
188
454
  return stub("externalAccounts.remove");
189
455
  }
190
456
  const [err] = await tryCatch(
191
- config.data.mutation(api.externalAccounts.mutations.removePersonal, {
457
+ config._data.mutation(api.externalAccounts.mutations.removePersonal, {
192
458
  externalAccountId
193
459
  })
194
460
  );
@@ -203,17 +469,203 @@ function createExternalAccountsClient(config = {}) {
203
469
  };
204
470
  }
205
471
 
472
+ // src/core/sub-accounts.ts
473
+ function malformedWireError(reason, raw) {
474
+ return new CapxulError({
475
+ code: "PROVIDER_ERROR",
476
+ message: `convex brandSubAccount failed: ${reason}`,
477
+ details: {
478
+ provider: "convex",
479
+ operation: "brandSubAccount",
480
+ reason,
481
+ // PII discipline (`.claude/rules/posthog.md`): user-authored
482
+ // strings on the wire (`name`, `purpose`) are customer-confidential
483
+ // — sub-account names like "Q3 Acquisition Reserve" or
484
+ // "Vendor ABC payments" must not flow into telemetry. We emit a
485
+ // structural keys-only sample via a strict ALLOWLIST so any future
486
+ // wire-shape addition (e.g. `recipientEmail`, `tags`) is dropped
487
+ // by construction rather than leaked through a denylist gap.
488
+ sample: safeSampleShape(raw)
489
+ }
490
+ });
491
+ }
492
+ function safeSampleShape(raw) {
493
+ if (raw === null || typeof raw !== "object") {
494
+ return { type: typeof raw };
495
+ }
496
+ const r = raw;
497
+ const balance = r.balance;
498
+ return {
499
+ object: typeof r.object === "string" ? r.object : typeof r.object,
500
+ idPresent: typeof r.id === "string" && r.id.length > 0,
501
+ // First 4 chars only — enough to distinguish "sub_" prefixed IDs
502
+ // from accidental other resource IDs without leaking the full ID.
503
+ idPrefix: typeof r.id === "string" ? r.id.slice(0, 4) : void 0,
504
+ parentKind: r.parent !== null && typeof r.parent === "object" ? r.parent.kind : typeof r.parent,
505
+ status: r.status,
506
+ hasName: typeof r.name === "string",
507
+ hasPurpose: r.purpose !== void 0,
508
+ balanceShape: balance !== null && typeof balance === "object" ? Object.keys(balance).sort() : typeof balance,
509
+ createdAtType: typeof r.createdAt,
510
+ updatedAtType: typeof r.updatedAt
511
+ };
512
+ }
513
+ function isMoneyShape(v) {
514
+ if (typeof v !== "object" || v === null) return false;
515
+ const m = v;
516
+ return typeof m.value === "string" && typeof m.currency === "string";
517
+ }
518
+ function isParentShape(v) {
519
+ if (typeof v !== "object" || v === null) return false;
520
+ const p = v;
521
+ return (p.kind === "account" || p.kind === "organization") && typeof p.id === "string";
522
+ }
523
+ function isFiniteNonNegativeInteger(v) {
524
+ return typeof v === "number" && Number.isFinite(v) && Number.isInteger(v) && v >= 0;
525
+ }
526
+ function validateWireSubAccount(raw) {
527
+ if (typeof raw !== "object" || raw === null) {
528
+ return { ok: false, reason: "not an object" };
529
+ }
530
+ const r = raw;
531
+ if (r.object !== "sub_account") {
532
+ return { ok: false, reason: `object must be "sub_account" (got ${String(r.object)})` };
533
+ }
534
+ if (typeof r.id !== "string" || r.id.length === 0) {
535
+ return { ok: false, reason: "id must be a non-empty string" };
536
+ }
537
+ if (!isParentShape(r.parent)) {
538
+ return { ok: false, reason: "parent must be { kind: 'account' | 'organization', id: string }" };
539
+ }
540
+ if (typeof r.name !== "string") {
541
+ return { ok: false, reason: "name must be a string" };
542
+ }
543
+ if (r.purpose !== void 0 && typeof r.purpose !== "string") {
544
+ return { ok: false, reason: "purpose must be a string when present" };
545
+ }
546
+ if (r.status !== "active" && r.status !== "archived") {
547
+ return { ok: false, reason: `status must be "active" | "archived" (got ${String(r.status)})` };
548
+ }
549
+ if (!isMoneyShape(r.balance)) {
550
+ return { ok: false, reason: "balance must be { value: string, currency: string }" };
551
+ }
552
+ if (!isFiniteNonNegativeInteger(r.createdAt)) {
553
+ return { ok: false, reason: "createdAt must be a finite non-negative integer (epoch ms)" };
554
+ }
555
+ if (r.updatedAt !== void 0 && !isFiniteNonNegativeInteger(r.updatedAt)) {
556
+ return { ok: false, reason: "updatedAt must be a finite non-negative integer when present" };
557
+ }
558
+ return { ok: true, value: r };
559
+ }
560
+ function brandSubAccount(raw) {
561
+ const result = validateWireSubAccount(raw);
562
+ if (!result.ok) {
563
+ throw malformedWireError(result.reason, raw);
564
+ }
565
+ const wire = result.value;
566
+ return {
567
+ object: wire.object,
568
+ id: toSubAccountId(wire.id),
569
+ parent: wire.parent,
570
+ name: wire.name,
571
+ ...wire.purpose !== void 0 ? { purpose: wire.purpose } : {},
572
+ status: wire.status,
573
+ balance: wire.balance,
574
+ createdAt: new Date(wire.createdAt).toISOString()
575
+ };
576
+ }
577
+ function tryBrandSubAccount(raw) {
578
+ try {
579
+ return [null, brandSubAccount(raw)];
580
+ } catch (err) {
581
+ if (err instanceof CapxulError && err.code === "PROVIDER_ERROR") {
582
+ return [err, null];
583
+ }
584
+ return [
585
+ malformedWireError(
586
+ err instanceof Error ? err.message : String(err),
587
+ raw
588
+ ),
589
+ null
590
+ ];
591
+ }
592
+ }
593
+ function createSubAccountsClient(config = {}) {
594
+ return {
595
+ retrieve: async (subAccountId) => {
596
+ if (!config._data) {
597
+ return stub("subAccounts.retrieve");
598
+ }
599
+ const [err, raw] = await tryCatch(
600
+ config._data.query(api.subAccounts.queries.retrieve, {
601
+ subAccountId
602
+ })
603
+ );
604
+ if (err) {
605
+ return [
606
+ fromConvexError(err),
607
+ null
608
+ ];
609
+ }
610
+ if (!raw) {
611
+ return [
612
+ new CapxulError({
613
+ code: "NOT_FOUND",
614
+ message: `sub_account ${subAccountId} not found`
615
+ }),
616
+ null
617
+ ];
618
+ }
619
+ const [brandErr, branded] = tryBrandSubAccount(raw);
620
+ if (brandErr) {
621
+ return [brandErr, null];
622
+ }
623
+ return [null, branded];
624
+ },
625
+ remove: async (subAccountId) => {
626
+ if (!config._data) {
627
+ return stub("subAccounts.remove");
628
+ }
629
+ const [err, raw] = await tryCatch(
630
+ config._data.mutation(api.subAccounts.mutations.archive, {
631
+ subAccountId
632
+ })
633
+ );
634
+ if (err) {
635
+ return [
636
+ fromConvexError(err),
637
+ null
638
+ ];
639
+ }
640
+ if (!raw) {
641
+ return [
642
+ new CapxulError({
643
+ code: "NOT_FOUND",
644
+ message: `sub_account ${subAccountId} not found`
645
+ }),
646
+ null
647
+ ];
648
+ }
649
+ const [brandErr, branded] = tryBrandSubAccount(raw);
650
+ if (brandErr) {
651
+ return [brandErr, null];
652
+ }
653
+ return [null, branded];
654
+ }
655
+ };
656
+ }
657
+
206
658
  // src/core/accounts.ts
207
659
  function createAccountExternalAccountsClient(config) {
208
660
  return {
209
661
  create: async (input) => {
210
- if (!config.data) {
662
+ if (!config._data) {
211
663
  return stub(
212
664
  "accounts.externalAccounts.create"
213
665
  );
214
666
  }
215
667
  const [err, raw] = await tryCatch(
216
- config.data.mutation(
668
+ config._data.mutation(
217
669
  api.externalAccounts.mutations.createPersonal,
218
670
  {
219
671
  kind: input.kind,
@@ -251,13 +703,13 @@ function createAccountExternalAccountsClient(config) {
251
703
  ];
252
704
  },
253
705
  list: async (input) => {
254
- if (!config.data) {
706
+ if (!config._data) {
255
707
  return stub(
256
708
  "accounts.externalAccounts.list"
257
709
  );
258
710
  }
259
711
  const [err, result] = await tryCatch(
260
- config.data.query(api.externalAccounts.queries.listPersonal, {
712
+ config._data.query(api.externalAccounts.queries.listPersonal, {
261
713
  limit: input.limit,
262
714
  cursor: input.cursor
263
715
  })
@@ -280,13 +732,13 @@ function createAccountExternalAccountsClient(config) {
280
732
  ];
281
733
  },
282
734
  retrieve: async (externalAccountId) => {
283
- if (!config.data) {
735
+ if (!config._data) {
284
736
  return stub(
285
737
  "accounts.externalAccounts.retrieve"
286
738
  );
287
739
  }
288
740
  const [err, raw] = await tryCatch(
289
- config.data.query(api.externalAccounts.queries.retrievePersonal, {
741
+ config._data.query(api.externalAccounts.queries.retrievePersonal, {
290
742
  externalAccountId
291
743
  })
292
744
  );
@@ -313,11 +765,11 @@ function createAccountExternalAccountsClient(config) {
313
765
  ];
314
766
  },
315
767
  remove: async (externalAccountId) => {
316
- if (!config.data) {
768
+ if (!config._data) {
317
769
  return stub("accounts.externalAccounts.remove");
318
770
  }
319
771
  const [err] = await tryCatch(
320
- config.data.mutation(api.externalAccounts.mutations.removePersonal, {
772
+ config._data.mutation(api.externalAccounts.mutations.removePersonal, {
321
773
  externalAccountId
322
774
  })
323
775
  );
@@ -331,14 +783,162 @@ function createAccountExternalAccountsClient(config) {
331
783
  }
332
784
  };
333
785
  }
786
+ function createAccountSubAccountsClient(config) {
787
+ return {
788
+ create: async (input) => {
789
+ if (!config._data) {
790
+ return stub(
791
+ "accounts.subAccounts.create"
792
+ );
793
+ }
794
+ const [err, raw] = await tryCatch(
795
+ config._data.mutation(api.subAccounts.mutations.create, {
796
+ parent: { kind: "account", id: input.accountId },
797
+ name: input.name,
798
+ purpose: input.purpose
799
+ })
800
+ );
801
+ if (err) {
802
+ return [
803
+ fromConvexError(err),
804
+ null
805
+ ];
806
+ }
807
+ if (!raw) {
808
+ return [
809
+ new CapxulError({
810
+ code: "NOT_FOUND",
811
+ message: "sub_account creation returned no resource"
812
+ }),
813
+ null
814
+ ];
815
+ }
816
+ const [brandErr, branded] = tryBrandSubAccount(raw);
817
+ if (brandErr) {
818
+ return [
819
+ brandErr,
820
+ null
821
+ ];
822
+ }
823
+ return [null, branded];
824
+ },
825
+ list: async (input) => {
826
+ if (!config._data) {
827
+ return stub(
828
+ "accounts.subAccounts.list"
829
+ );
830
+ }
831
+ const [err, rows] = await tryCatch(
832
+ config._data.query(api.subAccounts.queries.listByAccount, {
833
+ accountId: input.accountId
834
+ })
835
+ );
836
+ if (err) {
837
+ return [
838
+ fromConvexError(err),
839
+ null
840
+ ];
841
+ }
842
+ const branded = [];
843
+ for (const row of rows) {
844
+ const [brandErr, value] = tryBrandSubAccount(row);
845
+ if (brandErr) {
846
+ return [
847
+ brandErr,
848
+ null
849
+ ];
850
+ }
851
+ branded.push(value);
852
+ }
853
+ return [
854
+ null,
855
+ {
856
+ object: "list",
857
+ data: branded,
858
+ page: { hasMore: false }
859
+ }
860
+ ];
861
+ },
862
+ retrieve: async (subAccountId) => {
863
+ if (!config._data) {
864
+ return stub(
865
+ "accounts.subAccounts.retrieve"
866
+ );
867
+ }
868
+ const [err, raw] = await tryCatch(
869
+ config._data.query(api.subAccounts.queries.retrieve, {
870
+ subAccountId
871
+ })
872
+ );
873
+ if (err) {
874
+ return [
875
+ fromConvexError(err),
876
+ null
877
+ ];
878
+ }
879
+ if (!raw) {
880
+ return [
881
+ new CapxulError({
882
+ code: "NOT_FOUND",
883
+ message: `sub_account ${subAccountId} not found`
884
+ }),
885
+ null
886
+ ];
887
+ }
888
+ const [brandErr, branded] = tryBrandSubAccount(raw);
889
+ if (brandErr) {
890
+ return [
891
+ brandErr,
892
+ null
893
+ ];
894
+ }
895
+ return [null, branded];
896
+ },
897
+ remove: async (subAccountId) => {
898
+ if (!config._data) {
899
+ return stub(
900
+ "accounts.subAccounts.remove"
901
+ );
902
+ }
903
+ const [err, raw] = await tryCatch(
904
+ config._data.mutation(api.subAccounts.mutations.archive, {
905
+ subAccountId
906
+ })
907
+ );
908
+ if (err) {
909
+ return [
910
+ fromConvexError(err),
911
+ null
912
+ ];
913
+ }
914
+ if (!raw) {
915
+ return [
916
+ new CapxulError({
917
+ code: "NOT_FOUND",
918
+ message: `sub_account ${subAccountId} not found`
919
+ }),
920
+ null
921
+ ];
922
+ }
923
+ const [brandErr, branded] = tryBrandSubAccount(raw);
924
+ if (brandErr) {
925
+ return [
926
+ brandErr,
927
+ null
928
+ ];
929
+ }
930
+ return [null, branded];
931
+ }
932
+ };
933
+ }
334
934
  function createAccountsClient(config = {}) {
335
935
  return {
336
936
  retrieve: async (accountId) => {
337
- if (!config.data) {
937
+ if (!config._data) {
338
938
  return stub("accounts.retrieve");
339
939
  }
340
940
  try {
341
- const account = await config.data.query(
941
+ const account = await config._data.query(
342
942
  api.openfort.queries.getMyAccount,
343
943
  {}
344
944
  );
@@ -362,7 +962,7 @@ function createAccountsClient(config = {}) {
362
962
  },
363
963
  lookup: async () => stub("accounts.lookup"),
364
964
  update: async (input) => {
365
- if (!config.data) {
965
+ if (!config._data) {
366
966
  return stub("accounts.update");
367
967
  }
368
968
  if (input.countryCode !== void 0) {
@@ -376,7 +976,7 @@ function createAccountsClient(config = {}) {
376
976
  ];
377
977
  }
378
978
  try {
379
- const current = await config.data.query(
979
+ const current = await config._data.query(
380
980
  api.openfort.queries.getMyAccount,
381
981
  {}
382
982
  );
@@ -393,11 +993,11 @@ function createAccountsClient(config = {}) {
393
993
  null
394
994
  ];
395
995
  }
396
- await config.data.mutation(api.openfort.mutations.updateProfile, {
996
+ await config._data.mutation(api.openfort.mutations.updateProfile, {
397
997
  displayName: input.name,
398
998
  username: input.username
399
999
  });
400
- const updated = await config.data.query(
1000
+ const updated = await config._data.query(
401
1001
  api.openfort.queries.getMyAccount,
402
1002
  {}
403
1003
  );
@@ -407,7 +1007,7 @@ function createAccountsClient(config = {}) {
407
1007
  }
408
1008
  },
409
1009
  provisionPersonal: async (input) => {
410
- if (!config.data) {
1010
+ if (!config._data) {
411
1011
  return stub(
412
1012
  "accounts.provisionPersonal"
413
1013
  );
@@ -422,17 +1022,17 @@ function createAccountsClient(config = {}) {
422
1022
  ];
423
1023
  }
424
1024
  try {
425
- await config.data.mutation(
1025
+ await config._data.mutation(
426
1026
  api.safe.mutations.provisionLocalPersonalAccount,
427
1027
  {
428
1028
  displayName: input.displayName,
429
1029
  username: input.username,
430
1030
  countryCode: input.countryCode,
431
1031
  eoaAddress: input.signerProvider.signerAddress,
432
- safeAddress: input.signerProvider.safeAddress
1032
+ safeAddress: deriveSafeAddress2(input.signerProvider.signerAddress)
433
1033
  }
434
1034
  );
435
- const account = await config.data.query(
1035
+ const account = await config._data.query(
436
1036
  api.openfort.queries.getMyAccount,
437
1037
  {}
438
1038
  );
@@ -455,11 +1055,11 @@ function createAccountsClient(config = {}) {
455
1055
  },
456
1056
  safes: {
457
1057
  retrieve: async (safeId) => {
458
- if (!config.data) {
1058
+ if (!config._data) {
459
1059
  return stub("accounts.safes.retrieve");
460
1060
  }
461
1061
  try {
462
- const safe = await config.data.query(
1062
+ const safe = await config._data.query(
463
1063
  api.safe.queries.retrieveAccountSafe,
464
1064
  { safeId }
465
1065
  );
@@ -486,19 +1086,50 @@ function createAccountsClient(config = {}) {
486
1086
  retrieve: async () => stub("accounts.kycProfiles.retrieve")
487
1087
  },
488
1088
  externalAccounts: createAccountExternalAccountsClient(config),
489
- subAccounts: {
490
- create: async () => stub("accounts.subAccounts.create"),
491
- list: async () => stub("accounts.subAccounts.list"),
492
- retrieve: async () => stub("accounts.subAccounts.retrieve"),
493
- remove: async () => stub("accounts.subAccounts.remove")
494
- },
1089
+ subAccounts: createAccountSubAccountsClient(config),
495
1090
  balanceLedger: {
496
- list: async () => stub(
497
- "accounts.balanceLedger.list"
498
- ),
499
- retrieve: async () => stub(
500
- "accounts.balanceLedger.retrieve"
501
- )
1091
+ list: async (input) => {
1092
+ if (!config._data) {
1093
+ return stub(
1094
+ "accounts.balanceLedger.list"
1095
+ );
1096
+ }
1097
+ try {
1098
+ const accountId = input.accountId.replace(/^acct_/, "");
1099
+ const page = await config._data.query(
1100
+ api.balanceLedger.queries.listForAccount,
1101
+ { accountId, limit: input.limit, cursor: input.cursor }
1102
+ );
1103
+ return [null, page];
1104
+ } catch (cause) {
1105
+ return [fromConvexError(cause), null];
1106
+ }
1107
+ },
1108
+ retrieve: async (entryId) => {
1109
+ if (!config._data) {
1110
+ return stub(
1111
+ "accounts.balanceLedger.retrieve"
1112
+ );
1113
+ }
1114
+ try {
1115
+ const entry = await config._data.query(
1116
+ api.balanceLedger.queries.retrieve,
1117
+ { entryId }
1118
+ );
1119
+ if (!entry) {
1120
+ return [
1121
+ new CapxulError({
1122
+ code: "NOT_FOUND",
1123
+ message: `balance_ledger_entry ${entryId} not found`
1124
+ }),
1125
+ null
1126
+ ];
1127
+ }
1128
+ return [null, entry];
1129
+ } catch (cause) {
1130
+ return [fromConvexError(cause), null];
1131
+ }
1132
+ }
502
1133
  }
503
1134
  };
504
1135
  }
@@ -512,119 +1143,11 @@ function createApiKeysClient() {
512
1143
  revoke: async () => stub("apiKeys.revoke")
513
1144
  };
514
1145
  }
515
-
516
- // ../config/src/chain.ts
517
- var CAPXUL_PAYMENTS_ADDRESS = "0xe740ea65521edc9bef0ea75d30b5334711db99f4";
518
- var TEST_USDC_ADDRESS = "0xf09fcbb6df9f8f918e58f9c8ab15a76ade862b77";
519
- var CAPXUL_API_BASE_URL = "https://elated-oyster-269.convex.site";
520
-
521
- // ../config/src/timing.ts
522
- var FLOW_INVOKE_TIMEOUT_MS = 3e4;
523
-
524
- // ../config/src/errors.ts
525
- var CapxulError2 = class extends Error {
526
- code;
527
- details;
528
- correlationId;
529
- layer;
530
- constructor(code, message, options) {
531
- super(message, options?.cause ? { cause: options.cause } : void 0);
532
- this.code = code;
533
- this.details = options?.details;
534
- this.correlationId = options?.correlationId;
535
- this.layer = options?.layer;
536
- }
537
- };
538
- var Errors = {
539
- notAuthenticated: () => new CapxulError2("NOT_AUTHENTICATED", "Not authenticated"),
540
- profileNotFound: (authUserId) => new CapxulError2("PROFILE_NOT_FOUND", `Profile not found for user ${authUserId}`),
541
- smartAccountMissing: (authUserId) => new CapxulError2("SMART_ACCOUNT_MISSING", `Smart account not provisioned for user ${authUserId}`),
542
- envMissing: (name) => new CapxulError2("ENV_MISSING", `Environment variable ${name} not configured`),
543
- openfortApi: (operation, cause) => new CapxulError2(
544
- "PROVIDER_ERROR",
545
- `Openfort ${operation} failed: ${cause instanceof Error ? cause.message : String(cause)}`,
546
- { cause, details: { provider: "openfort", operation } }
547
- ),
548
- shieldApi: (status, detail) => new CapxulError2(
549
- "PROVIDER_ERROR",
550
- `Shield API error (${status}): ${detail}`,
551
- { details: { provider: "shield", status } }
552
- ),
553
- providerError: (provider, operation, cause) => new CapxulError2(
554
- "PROVIDER_ERROR",
555
- `${provider} ${operation} failed: ${cause instanceof Error ? cause.message : String(cause)}`,
556
- { cause, details: { provider, operation } }
557
- ),
558
- invalidInput: (field, reason) => new CapxulError2(
559
- "INVALID_INPUT",
560
- `Invalid ${field}: ${reason}`,
561
- { details: { field, reason } }
562
- ),
563
- playerNotFound: (playerId) => new CapxulError2(
564
- "PLAYER_NOT_FOUND",
565
- playerId ? `Openfort player ${playerId} not found` : "Openfort player not found"
566
- ),
567
- accountNotFound: (accountId) => new CapxulError2(
568
- "ACCOUNT_NOT_FOUND",
569
- accountId ? `Openfort account ${accountId} not found` : "Openfort smart account not found"
570
- ),
571
- invalidRecipient: (reason) => new CapxulError2("INVALID_RECIPIENT", reason),
572
- permissionDenied: (reason = "Permission denied") => new CapxulError2("PERMISSION_DENIED", reason),
573
- notFound: (resource, id) => new CapxulError2(
574
- "NOT_FOUND",
575
- id ? `${resource} ${id} not found` : `${resource} not found`
576
- ),
577
- idempotencyConflict: (details) => new CapxulError2(
578
- "IDEMPOTENCY_CONFLICT",
579
- "Idempotency key was already used for a different request",
580
- { details }
581
- ),
582
- emailDeliveryFailed: (detail, details) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`, {
583
- details
584
- }),
585
- rateLimited: (details) => new CapxulError2("RATE_LIMITED", "Request was rate limited", {
586
- details: { ...details }
587
- }),
588
- internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`),
589
- /**
590
- * Verification gate. Surfaced when a request hits a verification
591
- * boundary the actor cannot cross under their current state. Two
592
- * variants share this code:
593
- *
594
- * - Rail gate (Withdrawals v1 W2, #465): the resolved
595
- * `external_account.kind` routes to a withdrawal rail (e.g.
596
- * `fiat_offramp`, `card_payout`) that is not yet supported. Carries
597
- * `details.rail` + `details.currentKind`.
598
- * - KYC tier gate (legacy / future): the actor's KYC tier is below
599
- * the required tier. Carries `details.requiredTier`.
600
- *
601
- * Code is shared because both expose the same UX shape ("you cannot
602
- * proceed until verification advances"); the `details.*` keys
603
- * differentiate the route.
604
- */
605
- verificationRequired: (details) => {
606
- const message = "rail" in details ? `Withdrawal rail "${details.rail}" (kind=${details.currentKind}) is not yet supported.` : `Verification tier ${details.requiredTier} is required.`;
607
- return new CapxulError2("VERIFICATION_REQUIRED", message, {
608
- details: { ...details }
609
- });
610
- }
611
- };
612
-
613
- // ../config/src/safe.ts
614
- var SAFE_PROXY_FACTORY = "0x4e1dcf7ad4e460cfd30791ccc4f9c8a4f820ec67";
615
- var SAFE_L2_SINGLETON = "0x29fcb43b46531bca003ddc8fcb67ffe91900c762";
616
- var SAFE_MODULE_SETUP = "0x2dd68b007b46fbe91b9a7c3eda5a7a1063cb5b47";
617
- var SAFE_4337_MODULE = "0x75cf11467937ce3f2f357ce24ffc3dbf8fd5c226";
618
-
619
- // ../config/src/org-roles.ts
620
- function roleKeyFromLabel(label) {
621
- const bytes = new TextEncoder().encode(label);
622
- const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
623
- return "0x" + hex.padEnd(64, "0");
1146
+ function createDefaultDataClient(convexUrl, jwt) {
1147
+ const client = new browser.ConvexHttpClient(convexUrl);
1148
+ client.setAuth(jwt);
1149
+ return client;
624
1150
  }
625
- roleKeyFromLabel("OWNER");
626
- roleKeyFromLabel("FINANCE_MANAGER");
627
- roleKeyFromLabel("TEAM_LEAD");
628
1151
 
629
1152
  // src/transport.ts
630
1153
  function makeHttpTransport(config) {
@@ -955,7 +1478,7 @@ function readNonEmptyString(value) {
955
1478
 
956
1479
  // src/core/auth.ts
957
1480
  function createAuthClient(config = {}) {
958
- let dataClient = config.data ?? null;
1481
+ let dataClient = config._data ?? null;
959
1482
  const sessionStore = config.auth?.sessionStore ?? createMemorySessionStore();
960
1483
  const getTransport = createTransportProvider(config);
961
1484
  return {
@@ -1011,11 +1534,20 @@ function createAuthClient(config = {}) {
1011
1534
  ).toISOString()
1012
1535
  };
1013
1536
  sessionStore.set(session);
1014
- if (config.auth?.createDataClient) {
1537
+ if (!dataClient) {
1015
1538
  try {
1016
- dataClient = await config.auth.createDataClient(session);
1017
- mutableConfig(config).data = dataClient;
1018
- transport.markAuthenticated({ dataClient });
1539
+ const convexUrl = transport.convexUrl;
1540
+ if (!convexUrl || !session.convexJwt) {
1541
+ return [
1542
+ new CapxulError({
1543
+ code: "NETWORK_ERROR",
1544
+ message: "Cannot create data client: missing convex URL or JWT."
1545
+ }),
1546
+ null
1547
+ ];
1548
+ }
1549
+ dataClient = createDefaultDataClient(convexUrl, session.convexJwt);
1550
+ mutableConfig(config)._data = dataClient;
1019
1551
  } catch (cause) {
1020
1552
  return [
1021
1553
  new CapxulError({
@@ -1026,7 +1558,15 @@ function createAuthClient(config = {}) {
1026
1558
  null
1027
1559
  ];
1028
1560
  }
1561
+ } else {
1562
+ const injected = dataClient;
1563
+ if (typeof injected.refreshAuth === "function") {
1564
+ injected.refreshAuth();
1565
+ } else if (typeof injected.setAuth === "function" && session.convexJwt) {
1566
+ injected.setAuth(session.convexJwt);
1567
+ }
1029
1568
  }
1569
+ transport.markAuthenticated({ dataClient });
1030
1570
  if (!dataClient) {
1031
1571
  return [
1032
1572
  new CapxulError({
@@ -1054,7 +1594,7 @@ function createAuthClient(config = {}) {
1054
1594
  },
1055
1595
  completeBootstrap: async (input) => {
1056
1596
  const session = sessionStore.get();
1057
- const data = dataClient ?? config.data;
1597
+ const data = dataClient ?? config._data;
1058
1598
  if (!session || !data) {
1059
1599
  return [
1060
1600
  new CapxulError({
@@ -1064,23 +1604,38 @@ function createAuthClient(config = {}) {
1064
1604
  null
1065
1605
  ];
1066
1606
  }
1067
- if (input.signerProvider.kind !== "local-private-key") {
1607
+ const signerAddress = config.signer?.address;
1608
+ if (!signerAddress) {
1068
1609
  return [
1069
1610
  new CapxulError({
1070
1611
  code: "INVALID_INPUT",
1071
- message: "completeBootstrap currently supports local-private-key signer providers only."
1612
+ message: "completeBootstrap requires a signer to be configured on the client."
1072
1613
  }),
1073
1614
  null
1074
1615
  ];
1075
1616
  }
1076
1617
  try {
1077
- const result = await data.mutation(api.authBootstrap.completeBootstrap, {
1618
+ const safeAddress = deriveSafeAddress2(signerAddress);
1619
+ if (!data.action) {
1620
+ return [
1621
+ new CapxulError({
1622
+ code: "INVALID_INPUT",
1623
+ message: "completeBootstrap requires a data client that can execute Convex actions."
1624
+ }),
1625
+ null
1626
+ ];
1627
+ }
1628
+ const result = await data.action(api.authBootstrap.completeBootstrap, {
1078
1629
  bootstrapToken: input.bootstrapToken,
1079
1630
  sessionToken: session.token,
1080
1631
  username: input.username,
1081
1632
  displayName: input.displayName,
1082
1633
  countryCode: input.countryCode,
1083
- signerProvider: input.signerProvider
1634
+ signerProvider: {
1635
+ kind: "local-private-key",
1636
+ signerAddress,
1637
+ safeAddress
1638
+ }
1084
1639
  });
1085
1640
  return [null, { kind: "authenticated", session, ...result }];
1086
1641
  } catch (cause) {
@@ -1094,7 +1649,7 @@ function createAuthClient(config = {}) {
1094
1649
  signOut: async () => {
1095
1650
  sessionStore.clear();
1096
1651
  dataClient = null;
1097
- mutableConfig(config).data = void 0;
1652
+ mutableConfig(config)._data = void 0;
1098
1653
  const transport = getTransport();
1099
1654
  transport?.clearAuth();
1100
1655
  return [null, void 0];
@@ -1165,6 +1720,9 @@ async function postBetterAuth(transport, path, body, code, signal) {
1165
1720
  }
1166
1721
  return [null, text ? JSON.parse(text) : void 0];
1167
1722
  } catch (cause) {
1723
+ if (cause instanceof CapxulError) {
1724
+ return [cause, null];
1725
+ }
1168
1726
  return [
1169
1727
  new CapxulError({
1170
1728
  code: "NETWORK_ERROR",
@@ -1203,7 +1761,7 @@ function parseBetterAuthError(text) {
1203
1761
  }
1204
1762
  }
1205
1763
  function isCapxulErrorCode2(code) {
1206
- 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";
1764
+ 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";
1207
1765
  }
1208
1766
  async function exchangeConvexToken(transport, config, token, signal) {
1209
1767
  const path = config.auth?.convexTokenUrl ?? "/convex/token";
@@ -1233,6 +1791,9 @@ async function exchangeConvexToken(transport, config, token, signal) {
1233
1791
  }
1234
1792
  return [null, body.token];
1235
1793
  } catch (cause) {
1794
+ if (cause instanceof CapxulError) {
1795
+ return [cause, null];
1796
+ }
1236
1797
  return [
1237
1798
  new CapxulError({
1238
1799
  code: "NETWORK_ERROR",
@@ -1247,16 +1808,227 @@ function mutableConfig(config) {
1247
1808
  return config;
1248
1809
  }
1249
1810
 
1250
- // src/core/documents.ts
1251
- function createDocumentsClient() {
1252
- return {
1253
- create: async () => stub("documents.create"),
1254
- retrieve: async () => stub("documents.retrieve"),
1255
- list: async () => stub("documents.list"),
1256
- cancel: async () => stub("documents.cancel")
1811
+ // src/core/auth-service.ts
1812
+ var AuthService = class {
1813
+ authClient;
1814
+ sessionStore;
1815
+ config;
1816
+ constructor(config = {}) {
1817
+ this.config = config;
1818
+ this.sessionStore = config.auth?.sessionStore ?? createMemorySessionStore2();
1819
+ this.authClient = createAuthClient({
1820
+ ...config,
1821
+ auth: { ...config.auth, sessionStore: this.sessionStore }
1822
+ });
1823
+ }
1824
+ async sendOtp(email, options) {
1825
+ const [err] = await this.authClient.sendOtp({ email }, options);
1826
+ if (err) throw err;
1827
+ }
1828
+ async verifyOtp(email, otp, options) {
1829
+ const [err, result] = await this.authClient.verifyOtp(
1830
+ { email, otp },
1831
+ options
1832
+ );
1833
+ if (err) throw err;
1834
+ return result;
1835
+ }
1836
+ async completeBootstrap(params, signer) {
1837
+ if (signer) {
1838
+ const tempClient = createAuthClient({
1839
+ ...this.config,
1840
+ signer,
1841
+ auth: { ...this.config.auth, sessionStore: this.sessionStore }
1842
+ });
1843
+ const [err2, result2] = await tempClient.completeBootstrap(params);
1844
+ if (err2) throw err2;
1845
+ return result2;
1846
+ }
1847
+ const [err, result] = await this.authClient.completeBootstrap(params);
1848
+ if (err) throw err;
1849
+ return result;
1850
+ }
1851
+ /**
1852
+ * Clears the persisted session and, when a transport was pre-injected,
1853
+ * drops the cached auth header.
1854
+ *
1855
+ * **Transport safety note:** `clearAuth()` is only invoked when
1856
+ * `config._transport` was supplied at construction (e.g. by the React
1857
+ * provider). If `AuthService` is instantiated directly in a Node/CLI
1858
+ * context without an injected transport, the transport-side auth cache
1859
+ * is the caller's responsibility.
1860
+ */
1861
+ async signOut() {
1862
+ if (!this.config._transport) {
1863
+ const [err] = await this.authClient.signOut();
1864
+ if (err) throw err;
1865
+ mutableConfig2(this.config)._data = void 0;
1866
+ return;
1867
+ }
1868
+ this.sessionStore.clear();
1869
+ this.config._transport.clearAuth();
1870
+ }
1871
+ async getSession() {
1872
+ const [err, session] = await this.authClient.getSession();
1873
+ if (err) throw err;
1874
+ return session;
1875
+ }
1876
+ };
1877
+ function mutableConfig2(config) {
1878
+ return config;
1879
+ }
1880
+ function createMemorySessionStore2() {
1881
+ let current = null;
1882
+ return {
1883
+ get: () => current,
1884
+ set: (session) => {
1885
+ current = session;
1886
+ },
1887
+ clear: () => {
1888
+ current = null;
1889
+ }
1890
+ };
1891
+ }
1892
+
1893
+ // src/core/documents.ts
1894
+ function requireInvoiceHash(row) {
1895
+ if (!row.invoiceHash) {
1896
+ throw new CapxulError({
1897
+ code: "INVALID_INPUT",
1898
+ message: `invoice document ${row.documentId ?? row._id} is missing its canonical invoiceHash`
1899
+ });
1900
+ }
1901
+ return row.invoiceHash;
1902
+ }
1903
+ function mapInvoiceRow(row) {
1904
+ const status = row.status === "cancelled" ? "canceled" : row.status === "pending" ? "open" : row.status === "overdue" ? "expired" : row.status;
1905
+ return {
1906
+ object: "document",
1907
+ id: row.documentId ?? row._id,
1908
+ type: "invoice",
1909
+ owner: {
1910
+ kind: row.scope === "org" ? "organization" : "account",
1911
+ id: row.orgId ?? row.payeeEmail ?? row.payeeLabel
1912
+ },
1913
+ recipient: { email: row.payerEmail },
1914
+ amount: {
1915
+ value: row.amount,
1916
+ currency: row.currency
1917
+ },
1918
+ reference: row.note,
1919
+ lineItems: row.items,
1920
+ invoiceHash: requireInvoiceHash(row),
1921
+ dueAt: row.dueDate,
1922
+ status,
1923
+ createdAt: new Date(row.createdAt).toISOString()
1924
+ };
1925
+ }
1926
+ function mapDocumentError(cause) {
1927
+ return fromConvexError(cause);
1928
+ }
1929
+ function createDocumentsClient(config = {}) {
1930
+ return {
1931
+ create: async (input) => {
1932
+ if (!config._data) return stub("documents.create");
1933
+ if (input.type !== "invoice") {
1934
+ return [
1935
+ new CapxulError({
1936
+ code: "INVALID_INPUT",
1937
+ message: "documents.create currently supports personal invoice documents only."
1938
+ }),
1939
+ null
1940
+ ];
1941
+ }
1942
+ if (!("email" in input.recipient)) {
1943
+ return [
1944
+ new CapxulError({
1945
+ code: "INVALID_INPUT",
1946
+ message: "personal invoice documents currently require an email recipient."
1947
+ }),
1948
+ null
1949
+ ];
1950
+ }
1951
+ try {
1952
+ const created = await config._data.mutation(
1953
+ api.paymentRecords.mutations.createInvoice,
1954
+ {
1955
+ scope: "personal",
1956
+ payerEmail: input.recipient.email,
1957
+ amount: input.amount.value,
1958
+ currency: input.amount.currency,
1959
+ dueDate: input.dueAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1960
+ note: input.reference,
1961
+ items: input.lineItems
1962
+ }
1963
+ );
1964
+ const row = await config._data.query(
1965
+ api.paymentRecords.queries.getInvoiceByDocumentId,
1966
+ { documentId: created.documentId }
1967
+ );
1968
+ if (!row) throw new Error("created invoice was not readable");
1969
+ return [null, mapInvoiceRow(row)];
1970
+ } catch (cause) {
1971
+ return [mapDocumentError(cause), null];
1972
+ }
1973
+ },
1974
+ retrieve: async (documentId) => {
1975
+ if (!config._data)
1976
+ return stub("documents.retrieve");
1977
+ try {
1978
+ const row = await config._data.query(
1979
+ api.paymentRecords.queries.getInvoiceByDocumentId,
1980
+ { documentId }
1981
+ );
1982
+ if (!row) {
1983
+ return [
1984
+ new CapxulError({
1985
+ code: "NOT_FOUND",
1986
+ message: `document ${documentId} not found`
1987
+ }),
1988
+ null
1989
+ ];
1990
+ }
1991
+ return [null, mapInvoiceRow(row)];
1992
+ } catch (cause) {
1993
+ return [mapDocumentError(cause), null];
1994
+ }
1995
+ },
1996
+ list: async (input = {}) => {
1997
+ if (!config._data)
1998
+ return stub("documents.list");
1999
+ try {
2000
+ if (input.type && input.type !== "invoice") {
2001
+ return [null, { object: "list", data: [], page: { hasMore: false } }];
2002
+ }
2003
+ const rows = await config._data.query(
2004
+ api.paymentRecords.queries.listMyInvoices,
2005
+ {
2006
+ limit: input.limit,
2007
+ cursor: input.cursor
2008
+ }
2009
+ );
2010
+ const page = rows;
2011
+ const data = page.data.map((row) => mapInvoiceRow(row));
2012
+ return [null, { object: "list", data, page: page.page }];
2013
+ } catch (cause) {
2014
+ return [mapDocumentError(cause), null];
2015
+ }
2016
+ },
2017
+ cancel: async (documentId) => {
2018
+ if (!config._data) return stub("documents.cancel");
2019
+ try {
2020
+ const row = await config._data.mutation(
2021
+ api.paymentRecords.mutations.cancelInvoice,
2022
+ { documentId }
2023
+ );
2024
+ return [null, mapInvoiceRow(row)];
2025
+ } catch (cause) {
2026
+ return [mapDocumentError(cause), null];
2027
+ }
2028
+ }
1257
2029
  };
1258
2030
  }
1259
- function createOrgDocumentsClient() {
2031
+ function createOrgDocumentsClient(_config = {}) {
1260
2032
  return {
1261
2033
  create: async () => stub("organizations.documents.create"),
1262
2034
  retrieve: async () => stub("organizations.documents.retrieve"),
@@ -1269,11 +2041,11 @@ function createOrgDocumentsClient() {
1269
2041
  function createMeClient(config = {}) {
1270
2042
  return {
1271
2043
  get: async () => {
1272
- if (!config.data) {
2044
+ if (!config._data) {
1273
2045
  return stub("me.get");
1274
2046
  }
1275
2047
  try {
1276
- const account = await config.data.query(
2048
+ const account = await config._data.query(
1277
2049
  api.openfort.queries.getMyAccount,
1278
2050
  {}
1279
2051
  );
@@ -1283,7 +2055,7 @@ function createMeClient(config = {}) {
1283
2055
  }
1284
2056
  },
1285
2057
  update: async (input) => {
1286
- if (!config.data) {
2058
+ if (!config._data) {
1287
2059
  return stub("me.update");
1288
2060
  }
1289
2061
  if (input.countryCode !== void 0) {
@@ -1297,11 +2069,11 @@ function createMeClient(config = {}) {
1297
2069
  ];
1298
2070
  }
1299
2071
  try {
1300
- await config.data.mutation(api.openfort.mutations.updateProfile, {
2072
+ await config._data.mutation(api.openfort.mutations.updateProfile, {
1301
2073
  displayName: input.name,
1302
2074
  username: input.username
1303
2075
  });
1304
- const account = await config.data.query(
2076
+ const account = await config._data.query(
1305
2077
  api.openfort.queries.getMyAccount,
1306
2078
  {}
1307
2079
  );
@@ -1316,11 +2088,11 @@ function createMeClient(config = {}) {
1316
2088
  // src/core/operations.ts
1317
2089
  function createOperationsClient(config = {}) {
1318
2090
  const retrieve = async (operationId) => {
1319
- if (!config.data) {
2091
+ if (!config._data) {
1320
2092
  return stub("operations.retrieve");
1321
2093
  }
1322
2094
  try {
1323
- const operation = await config.data.query(api.operations.queries.retrieve, {
2095
+ const operation = await config._data.query(api.operations.queries.retrieve, {
1324
2096
  operationId
1325
2097
  });
1326
2098
  if (!operation) {
@@ -1337,7 +2109,7 @@ function createOperationsClient(config = {}) {
1337
2109
  return {
1338
2110
  retrieve,
1339
2111
  wait: async (operationId, input = {}) => {
1340
- if (!config.data) {
2112
+ if (!config._data) {
1341
2113
  return stub("operations.wait");
1342
2114
  }
1343
2115
  const until = new Set(
@@ -1372,49 +2144,22 @@ function toTokenUnits(value, decimals = 6) {
1372
2144
  return viem.parseUnits(value, decimals);
1373
2145
  }
1374
2146
 
1375
- // src/internal/payment-token.ts
1376
- function resolvePaymentTokenAddress(currency) {
2147
+ // src/core/token-registry.ts
2148
+ function resolvePaymentToken(currency) {
1377
2149
  const normalized = currency.trim().toUpperCase();
1378
2150
  if (normalized === "USD" || normalized === "USDC") {
1379
- return TEST_USDC_ADDRESS.toLowerCase();
2151
+ return {
2152
+ address: TEST_USDC_ADDRESS.toLowerCase(),
2153
+ decimals: 6,
2154
+ symbol: "USDC"
2155
+ };
1380
2156
  }
1381
2157
  throw new CapxulError({
1382
- code: "NETWORK_ERROR",
1383
- message: `Currency ${currency} is not configured for on-chain payment submission.`,
2158
+ code: "NOT_IMPLEMENTED",
2159
+ message: `Currency ${currency} is not yet supported by the token registry.`,
1384
2160
  details: { currency: normalized }
1385
2161
  });
1386
2162
  }
1387
- async function buildSafeAccount(signer, chain) {
1388
- try {
1389
- const publicClient = viem.createPublicClient({
1390
- chain: chains.baseSepolia,
1391
- transport: viem.http(chain.rpcUrl)
1392
- });
1393
- return await accounts.toSafeSmartAccount({
1394
- client: publicClient,
1395
- entryPoint: { address: accountAbstraction.entryPoint07Address, version: "0.7" },
1396
- version: "1.4.1",
1397
- owners: [signer],
1398
- saltNonce: computeSaltNonce(signer.address),
1399
- safeSingletonAddress: SAFE_L2_SINGLETON,
1400
- safeProxyFactoryAddress: SAFE_PROXY_FACTORY,
1401
- safeModuleSetupAddress: SAFE_MODULE_SETUP,
1402
- safe4337ModuleAddress: SAFE_4337_MODULE,
1403
- safeModules: [],
1404
- setupTransactions: []
1405
- });
1406
- } catch (cause) {
1407
- throw new CapxulError({
1408
- code: "NETWORK_ERROR",
1409
- message: cause instanceof Error ? cause.message : String(cause),
1410
- cause,
1411
- details: { chainId: chain.chainId }
1412
- });
1413
- }
1414
- }
1415
- function computeSaltNonce(ownerAddress) {
1416
- return BigInt(`0x${ownerAddress.toLowerCase().slice(2).padEnd(64, "0")}`);
1417
- }
1418
2163
  function createCapxulBundler(config) {
1419
2164
  const paymaster = accountAbstraction.createPaymasterClient({
1420
2165
  transport: viem.http(config.rpcUrl)
@@ -1521,13 +2266,13 @@ async function transferAsOwner(config, params) {
1521
2266
  function createPaymentsClient(config = {}) {
1522
2267
  return {
1523
2268
  create: async (input) => {
1524
- if (!config.data || !config.signer || !config.signing) {
2269
+ if (!config._data || !config.signer || !config.signing) {
1525
2270
  return stub("payments.create");
1526
2271
  }
1527
2272
  let created = null;
1528
2273
  let submitted = null;
1529
2274
  try {
1530
- created = await config.data.mutation(api.payments.mutations.create, {
2275
+ created = await config._data.mutation(api.payments.mutations.create, {
1531
2276
  to: input.to,
1532
2277
  amount: input.amount,
1533
2278
  reference: input.reference,
@@ -1535,15 +2280,21 @@ function createPaymentsClient(config = {}) {
1535
2280
  source: input.source
1536
2281
  });
1537
2282
  if (!created) {
1538
- return [new CapxulError({
1539
- code: "NETWORK_ERROR",
1540
- message: "payments.create returned no payment resource"
1541
- }), null];
2283
+ return [
2284
+ new CapxulError({
2285
+ code: "NETWORK_ERROR",
2286
+ message: "payments.create returned no payment resource"
2287
+ }),
2288
+ null
2289
+ ];
1542
2290
  }
1543
2291
  if (created.status !== "processing" || created.operation.status !== "processing") {
1544
2292
  return [null, created];
1545
2293
  }
1546
- const currentSigner = await config.data.query(api.safe.queries.getMySignerAddress, {});
2294
+ const currentSigner = await config._data.query(
2295
+ api.safe.queries.getMySignerAddress,
2296
+ {}
2297
+ );
1547
2298
  if (!currentSigner?.address) {
1548
2299
  throw new CapxulError({
1549
2300
  code: "PERMISSION_DENIED",
@@ -1562,9 +2313,12 @@ function createPaymentsClient(config = {}) {
1562
2313
  }
1563
2314
  });
1564
2315
  }
1565
- const submission = await config.data.query(api.payments.queries.prepareSubmission, {
1566
- paymentId: created.id
1567
- });
2316
+ const submission = await config._data.query(
2317
+ api.payments.queries.prepareSubmission,
2318
+ {
2319
+ paymentId: created.id
2320
+ }
2321
+ );
1568
2322
  if (!submission?.recipientAddress) {
1569
2323
  throw new CapxulError({
1570
2324
  code: "NETWORK_ERROR",
@@ -1572,15 +2326,16 @@ function createPaymentsClient(config = {}) {
1572
2326
  details: { paymentId: created.id }
1573
2327
  });
1574
2328
  }
2329
+ const token = resolvePaymentToken(submission.amount.currency);
1575
2330
  const transfer = await transferAsOwner(
1576
2331
  {
1577
2332
  signer: config.signer,
1578
2333
  signing: config.signing
1579
2334
  },
1580
2335
  {
1581
- tokenAddress: resolvePaymentTokenAddress(submission.amount.currency),
2336
+ tokenAddress: token.address,
1582
2337
  recipientAddress: submission.recipientAddress,
1583
- amount: toTokenUnits(submission.amount.value, 6)
2338
+ amount: toTokenUnits(submission.amount.value, token.decimals)
1584
2339
  }
1585
2340
  );
1586
2341
  if (!transfer.success) {
@@ -1598,7 +2353,7 @@ function createPaymentsClient(config = {}) {
1598
2353
  txHash: transfer.txHash,
1599
2354
  userOpHash: transfer.userOpHash
1600
2355
  };
1601
- await config.data.mutation(api.payments.mutations.recordSubmitted, {
2356
+ await config._data.mutation(api.payments.mutations.recordSubmitted, {
1602
2357
  paymentId: created.id,
1603
2358
  txHash: transfer.txHash,
1604
2359
  userOpHash: transfer.userOpHash,
@@ -1608,43 +2363,65 @@ function createPaymentsClient(config = {}) {
1608
2363
  } catch (cause) {
1609
2364
  const error = mapCreateError(fromConvexError(cause));
1610
2365
  if (created?.id && created.status === "processing" && !submitted) {
1611
- await bestEffortMarkFailed({ data: config.data }, created.id, error);
2366
+ await bestEffortMarkFailed({ _data: config._data }, created.id, error);
1612
2367
  }
1613
2368
  if (submitted && created?.id) {
1614
- return [new CapxulError({
1615
- code: "NETWORK_ERROR",
1616
- message: "Payment was submitted on-chain, but backend submission tracking failed.",
1617
- cause,
1618
- details: {
1619
- paymentId: created.id,
1620
- txHash: submitted.txHash,
1621
- userOpHash: submitted.userOpHash
1622
- }
1623
- }), null];
2369
+ return [
2370
+ new CapxulError({
2371
+ code: "NETWORK_ERROR",
2372
+ message: "Payment was submitted on-chain, but backend submission tracking failed.",
2373
+ cause,
2374
+ details: {
2375
+ paymentId: created.id,
2376
+ txHash: submitted.txHash,
2377
+ userOpHash: submitted.userOpHash
2378
+ }
2379
+ }),
2380
+ null
2381
+ ];
1624
2382
  }
1625
2383
  return [error, null];
1626
2384
  }
1627
2385
  },
1628
2386
  retrieve: async (paymentId) => {
1629
- if (!config.data) {
2387
+ if (!config._data) {
1630
2388
  return stub("payments.retrieve");
1631
2389
  }
1632
2390
  try {
1633
- const payment = await config.data.query(api.payments.queries.retrieve, {
1634
- paymentId
1635
- });
2391
+ const payment = await config._data.query(
2392
+ api.payments.queries.retrieve,
2393
+ {
2394
+ paymentId
2395
+ }
2396
+ );
1636
2397
  if (!payment) {
1637
- return [new CapxulError({
1638
- code: "NOT_FOUND",
1639
- message: `payment ${paymentId} not found`
1640
- }), null];
2398
+ return [
2399
+ new CapxulError({
2400
+ code: "NOT_FOUND",
2401
+ message: `payment ${paymentId} not found`
2402
+ }),
2403
+ null
2404
+ ];
1641
2405
  }
1642
2406
  return [null, payment];
1643
2407
  } catch (cause) {
1644
2408
  return [fromConvexError(cause), null];
1645
2409
  }
1646
2410
  },
1647
- list: async () => stub("payments.list")
2411
+ list: async (input) => {
2412
+ if (!config._data) {
2413
+ return stub("payments.list");
2414
+ }
2415
+ try {
2416
+ const page = await config._data.query(api.payments.queries.list, {
2417
+ limit: input?.limit,
2418
+ cursor: input?.cursor
2419
+ });
2420
+ return [null, page];
2421
+ } catch (cause) {
2422
+ return [fromConvexError(cause), null];
2423
+ }
2424
+ }
1648
2425
  };
1649
2426
  }
1650
2427
  function createOrgPaymentsClient() {
@@ -1658,7 +2435,7 @@ function createOrgPaymentsClient() {
1658
2435
  }
1659
2436
  async function bestEffortMarkFailed(config, paymentId, error) {
1660
2437
  try {
1661
- await config.data.mutation(api.payments.mutations.markFailed, {
2438
+ await config._data.mutation(api.payments.mutations.markFailed, {
1662
2439
  paymentId,
1663
2440
  errorCode: error.code,
1664
2441
  errorMessage: error.message,
@@ -1715,11 +2492,11 @@ function createOrgTransfersClient() {
1715
2492
  function createWithdrawalsClient(config = {}) {
1716
2493
  return {
1717
2494
  create: async (input) => {
1718
- if (!config.data) {
2495
+ if (!config._data) {
1719
2496
  return stub("withdrawals.create");
1720
2497
  }
1721
2498
  const [createErr, createdRaw] = await tryCatch(
1722
- config.data.mutation(api.withdrawals.mutations.create, {
2499
+ config._data.mutation(api.withdrawals.mutations.create, {
1723
2500
  amount: input.amount,
1724
2501
  destination: {
1725
2502
  externalAccountId: input.destination.externalAccountId
@@ -1749,18 +2526,18 @@ function createWithdrawalsClient(config = {}) {
1749
2526
  return [null, created];
1750
2527
  }
1751
2528
  const [signerErr, currentSigner] = await tryCatch(
1752
- config.data.query(api.safe.queries.getMySignerAddress, {})
2529
+ config._data.query(api.safe.queries.getMySignerAddress, {})
1753
2530
  );
1754
2531
  if (signerErr) {
1755
2532
  return await handleSubmissionFailure(
1756
- { data: config.data },
2533
+ { _data: config._data },
1757
2534
  created.id,
1758
2535
  mapCreateError2(fromConvexError(signerErr))
1759
2536
  );
1760
2537
  }
1761
2538
  if (!currentSigner?.address) {
1762
2539
  return await handleSubmissionFailure(
1763
- { data: config.data },
2540
+ { _data: config._data },
1764
2541
  created.id,
1765
2542
  new CapxulError({
1766
2543
  code: "PERMISSION_DENIED",
@@ -1771,7 +2548,7 @@ function createWithdrawalsClient(config = {}) {
1771
2548
  }
1772
2549
  if (viem.getAddress(currentSigner.address) !== viem.getAddress(config.signer.address)) {
1773
2550
  return await handleSubmissionFailure(
1774
- { data: config.data },
2551
+ { _data: config._data },
1775
2552
  created.id,
1776
2553
  new CapxulError({
1777
2554
  code: "PERMISSION_DENIED",
@@ -1785,13 +2562,13 @@ function createWithdrawalsClient(config = {}) {
1785
2562
  );
1786
2563
  }
1787
2564
  const [prepErr, submission] = await tryCatch(
1788
- config.data.query(api.withdrawals.queries.prepareSubmission, {
2565
+ config._data.query(api.withdrawals.queries.prepareSubmission, {
1789
2566
  withdrawalId: created.id
1790
2567
  })
1791
2568
  );
1792
2569
  if (prepErr) {
1793
2570
  return await handleSubmissionFailure(
1794
- { data: config.data },
2571
+ { _data: config._data },
1795
2572
  created.id,
1796
2573
  mapCreateError2(fromConvexError(prepErr))
1797
2574
  );
@@ -1799,7 +2576,7 @@ function createWithdrawalsClient(config = {}) {
1799
2576
  const destinationAddress = submission?.destinationAddress;
1800
2577
  if (!submission || !destinationAddress) {
1801
2578
  return await handleSubmissionFailure(
1802
- { data: config.data },
2579
+ { _data: config._data },
1803
2580
  created.id,
1804
2581
  new CapxulError({
1805
2582
  code: "NETWORK_ERROR",
@@ -1808,6 +2585,7 @@ function createWithdrawalsClient(config = {}) {
1808
2585
  })
1809
2586
  );
1810
2587
  }
2588
+ const token = resolvePaymentToken(submission.amount.currency);
1811
2589
  const [transferErr, transferOk] = await tryCatch(
1812
2590
  transferAsOwner(
1813
2591
  {
@@ -1815,22 +2593,22 @@ function createWithdrawalsClient(config = {}) {
1815
2593
  signing: config.signing
1816
2594
  },
1817
2595
  {
1818
- tokenAddress: resolvePaymentTokenAddress(submission.amount.currency),
2596
+ tokenAddress: token.address,
1819
2597
  recipientAddress: destinationAddress,
1820
- amount: toTokenUnits(submission.amount.value, 6)
2598
+ amount: toTokenUnits(submission.amount.value, token.decimals)
1821
2599
  }
1822
2600
  )
1823
2601
  );
1824
2602
  if (transferErr) {
1825
2603
  return await handleSubmissionFailure(
1826
- { data: config.data },
2604
+ { _data: config._data },
1827
2605
  created.id,
1828
2606
  mapCreateError2(fromConvexError(transferErr))
1829
2607
  );
1830
2608
  }
1831
2609
  if (!transferOk.success) {
1832
2610
  return await handleSubmissionFailure(
1833
- { data: config.data },
2611
+ { _data: config._data },
1834
2612
  created.id,
1835
2613
  new CapxulError({
1836
2614
  code: "NETWORK_ERROR",
@@ -1844,7 +2622,7 @@ function createWithdrawalsClient(config = {}) {
1844
2622
  );
1845
2623
  }
1846
2624
  const [recordErr] = await tryCatch(
1847
- config.data.mutation(api.withdrawals.mutations.recordSubmitted, {
2625
+ config._data.mutation(api.withdrawals.mutations.recordSubmitted, {
1848
2626
  withdrawalId: created.id,
1849
2627
  txHash: transferOk.txHash,
1850
2628
  userOpHash: transferOk.userOpHash
@@ -1868,11 +2646,11 @@ function createWithdrawalsClient(config = {}) {
1868
2646
  return [null, created];
1869
2647
  },
1870
2648
  retrieve: async (withdrawalId) => {
1871
- if (!config.data) {
2649
+ if (!config._data) {
1872
2650
  return stub("withdrawals.retrieve");
1873
2651
  }
1874
2652
  const [err, raw] = await tryCatch(
1875
- config.data.query(api.withdrawals.queries.retrieve, { withdrawalId })
2653
+ config._data.query(api.withdrawals.queries.retrieve, { withdrawalId })
1876
2654
  );
1877
2655
  if (err) {
1878
2656
  return [fromConvexError(err), null];
@@ -1890,11 +2668,11 @@ function createWithdrawalsClient(config = {}) {
1890
2668
  return [null, withdrawal];
1891
2669
  },
1892
2670
  list: async (input) => {
1893
- if (!config.data) {
2671
+ if (!config._data) {
1894
2672
  return stub("withdrawals.list");
1895
2673
  }
1896
2674
  const [err, raw] = await tryCatch(
1897
- config.data.query(api.withdrawals.queries.list, {
2675
+ config._data.query(api.withdrawals.queries.list, {
1898
2676
  limit: input?.limit,
1899
2677
  cursor: input?.cursor
1900
2678
  })
@@ -1905,13 +2683,13 @@ function createWithdrawalsClient(config = {}) {
1905
2683
  return [null, raw];
1906
2684
  },
1907
2685
  recordCompleted: async (input) => {
1908
- if (!config.data) {
2686
+ if (!config._data) {
1909
2687
  return stub(
1910
2688
  "withdrawals.recordCompleted"
1911
2689
  );
1912
2690
  }
1913
2691
  const [err] = await tryCatch(
1914
- config.data.mutation(api.withdrawals.mutations.recordCompleted, {
2692
+ config._data.mutation(api.withdrawals.mutations.recordCompleted, {
1915
2693
  withdrawalId: input.withdrawalId,
1916
2694
  txHash: input.txHash
1917
2695
  })
@@ -1936,13 +2714,13 @@ function createOrgWithdrawalsClient(config = {}) {
1936
2714
  * orchestration ships in W3+.
1937
2715
  */
1938
2716
  create: async (input) => {
1939
- if (!config.data) {
2717
+ if (!config._data) {
1940
2718
  return stub(
1941
2719
  "organizations.withdrawals.create"
1942
2720
  );
1943
2721
  }
1944
2722
  const [err, raw] = await tryCatch(
1945
- config.data.mutation(api.withdrawals.mutations.createOrg, {
2723
+ config._data.mutation(api.withdrawals.mutations.createOrg, {
1946
2724
  organizationId: input.organizationId,
1947
2725
  amount: input.amount,
1948
2726
  destination: {
@@ -1969,13 +2747,13 @@ function createOrgWithdrawalsClient(config = {}) {
1969
2747
  return [null, created];
1970
2748
  },
1971
2749
  retrieve: async (input) => {
1972
- if (!config.data) {
2750
+ if (!config._data) {
1973
2751
  return stub(
1974
2752
  "organizations.withdrawals.retrieve"
1975
2753
  );
1976
2754
  }
1977
2755
  const [err, raw] = await tryCatch(
1978
- config.data.query(api.withdrawals.queries.retrieve, {
2756
+ config._data.query(api.withdrawals.queries.retrieve, {
1979
2757
  withdrawalId: input.withdrawalId
1980
2758
  })
1981
2759
  );
@@ -2005,13 +2783,13 @@ function createOrgWithdrawalsClient(config = {}) {
2005
2783
  return [null, withdrawal];
2006
2784
  },
2007
2785
  list: async (input) => {
2008
- if (!config.data) {
2786
+ if (!config._data) {
2009
2787
  return stub(
2010
2788
  "organizations.withdrawals.list"
2011
2789
  );
2012
2790
  }
2013
2791
  const [err, raw] = await tryCatch(
2014
- config.data.query(api.withdrawals.queries.listOrg, {
2792
+ config._data.query(api.withdrawals.queries.listOrg, {
2015
2793
  organizationId: input.organizationId,
2016
2794
  limit: input.limit,
2017
2795
  cursor: input.cursor
@@ -2030,7 +2808,7 @@ async function handleSubmissionFailure(config, withdrawalId, error) {
2030
2808
  }
2031
2809
  async function bestEffortMarkFailed2(config, withdrawalId, error) {
2032
2810
  await tryCatch(
2033
- config.data.mutation(api.withdrawals.mutations.markFailed, {
2811
+ config._data.mutation(api.withdrawals.mutations.markFailed, {
2034
2812
  withdrawalId,
2035
2813
  errorCode: error.code,
2036
2814
  errorMessage: error.message
@@ -2109,13 +2887,13 @@ function createWebhookEventsClient() {
2109
2887
  function createOrgExternalAccountsClient(config) {
2110
2888
  return {
2111
2889
  create: async (input) => {
2112
- if (!config.data) {
2890
+ if (!config._data) {
2113
2891
  return stub(
2114
2892
  "organizations.externalAccounts.create"
2115
2893
  );
2116
2894
  }
2117
2895
  const [err, raw] = await tryCatch(
2118
- config.data.mutation(api.externalAccounts.mutations.createOrg, {
2896
+ config._data.mutation(api.externalAccounts.mutations.createOrg, {
2119
2897
  organizationId: input.organizationId,
2120
2898
  kind: input.kind,
2121
2899
  label: input.label,
@@ -2129,10 +2907,7 @@ function createOrgExternalAccountsClient(config) {
2129
2907
  })
2130
2908
  );
2131
2909
  if (err) {
2132
- return [
2133
- fromConvexError(err),
2134
- null
2135
- ];
2910
+ return [fromConvexError(err), null];
2136
2911
  }
2137
2912
  if (!raw) {
2138
2913
  return [
@@ -2143,21 +2918,16 @@ function createOrgExternalAccountsClient(config) {
2143
2918
  null
2144
2919
  ];
2145
2920
  }
2146
- return [
2147
- null,
2148
- brandExternalAccount(
2149
- raw
2150
- )
2151
- ];
2921
+ return [null, brandExternalAccount(raw)];
2152
2922
  },
2153
2923
  list: async (input) => {
2154
- if (!config.data) {
2924
+ if (!config._data) {
2155
2925
  return stub(
2156
2926
  "organizations.externalAccounts.list"
2157
2927
  );
2158
2928
  }
2159
2929
  const [err, result] = await tryCatch(
2160
- config.data.query(api.externalAccounts.queries.listOrg, {
2930
+ config._data.query(api.externalAccounts.queries.listOrg, {
2161
2931
  organizationId: input.organizationId,
2162
2932
  limit: input.limit,
2163
2933
  cursor: input.cursor
@@ -2167,9 +2937,7 @@ function createOrgExternalAccountsClient(config) {
2167
2937
  return [fromConvexError(err), null];
2168
2938
  }
2169
2939
  const branded = result.data.map(
2170
- (row) => brandExternalAccount(
2171
- row
2172
- )
2940
+ (row) => brandExternalAccount(row)
2173
2941
  );
2174
2942
  return [
2175
2943
  null,
@@ -2181,17 +2949,66 @@ function createOrgExternalAccountsClient(config) {
2181
2949
  ];
2182
2950
  },
2183
2951
  retrieve: async (input) => {
2184
- if (!config.data) {
2952
+ if (!config._data) {
2185
2953
  return stub(
2186
2954
  "organizations.externalAccounts.retrieve"
2187
2955
  );
2188
2956
  }
2189
2957
  const [err, raw] = await tryCatch(
2190
- config.data.query(api.externalAccounts.queries.retrieveOrg, {
2958
+ config._data.query(api.externalAccounts.queries.retrieveOrg, {
2959
+ organizationId: input.organizationId,
2960
+ externalAccountId: input.externalAccountId
2961
+ })
2962
+ );
2963
+ if (err) {
2964
+ return [fromConvexError(err), null];
2965
+ }
2966
+ if (!raw) {
2967
+ return [
2968
+ new CapxulError({
2969
+ code: "NOT_FOUND",
2970
+ message: `external_account ${input.externalAccountId} not found`
2971
+ }),
2972
+ null
2973
+ ];
2974
+ }
2975
+ return [null, brandExternalAccount(raw)];
2976
+ },
2977
+ remove: async (input) => {
2978
+ if (!config._data) {
2979
+ return stub("organizations.externalAccounts.remove");
2980
+ }
2981
+ const [err] = await tryCatch(
2982
+ config._data.mutation(api.externalAccounts.mutations.removeOrg, {
2191
2983
  organizationId: input.organizationId,
2192
2984
  externalAccountId: input.externalAccountId
2193
2985
  })
2194
2986
  );
2987
+ if (err) {
2988
+ return [fromConvexError(err), null];
2989
+ }
2990
+ return [null, void 0];
2991
+ }
2992
+ };
2993
+ }
2994
+ function createOrgSubAccountsClient(config) {
2995
+ return {
2996
+ create: async (input) => {
2997
+ if (!config._data) {
2998
+ return stub(
2999
+ "organizations.subAccounts.create"
3000
+ );
3001
+ }
3002
+ const [err, raw] = await tryCatch(
3003
+ config._data.mutation(api.subAccounts.mutations.create, {
3004
+ parent: {
3005
+ kind: "organization",
3006
+ id: input.organizationId
3007
+ },
3008
+ name: input.name,
3009
+ purpose: input.purpose
3010
+ })
3011
+ );
2195
3012
  if (err) {
2196
3013
  return [
2197
3014
  fromConvexError(err),
@@ -2202,28 +3019,60 @@ function createOrgExternalAccountsClient(config) {
2202
3019
  return [
2203
3020
  new CapxulError({
2204
3021
  code: "NOT_FOUND",
2205
- message: `external_account ${input.externalAccountId} not found`
3022
+ message: "sub_account creation returned no resource"
2206
3023
  }),
2207
3024
  null
2208
3025
  ];
2209
3026
  }
3027
+ const [brandErr, branded] = tryBrandSubAccount(raw);
3028
+ if (brandErr) {
3029
+ return [brandErr, null];
3030
+ }
3031
+ return [null, branded];
3032
+ },
3033
+ list: async (input) => {
3034
+ if (!config._data) {
3035
+ return stub(
3036
+ "organizations.subAccounts.list"
3037
+ );
3038
+ }
3039
+ const [err, rows] = await tryCatch(
3040
+ config._data.query(api.subAccounts.queries.listByOrganization, {
3041
+ organizationId: input.organizationId
3042
+ })
3043
+ );
3044
+ if (err) {
3045
+ return [
3046
+ fromConvexError(err),
3047
+ null
3048
+ ];
3049
+ }
3050
+ const branded = [];
3051
+ for (const row of rows) {
3052
+ const [brandErr, value] = tryBrandSubAccount(row);
3053
+ if (brandErr) {
3054
+ return [brandErr, null];
3055
+ }
3056
+ branded.push(value);
3057
+ }
2210
3058
  return [
2211
3059
  null,
2212
- brandExternalAccount(
2213
- raw
2214
- )
3060
+ {
3061
+ object: "list",
3062
+ data: branded,
3063
+ page: { hasMore: false }
3064
+ }
2215
3065
  ];
2216
3066
  },
2217
- remove: async (input) => {
2218
- if (!config.data) {
3067
+ retrieve: async (input) => {
3068
+ if (!config._data) {
2219
3069
  return stub(
2220
- "organizations.externalAccounts.remove"
3070
+ "organizations.subAccounts.retrieve"
2221
3071
  );
2222
3072
  }
2223
- const [err] = await tryCatch(
2224
- config.data.mutation(api.externalAccounts.mutations.removeOrg, {
2225
- organizationId: input.organizationId,
2226
- externalAccountId: input.externalAccountId
3073
+ const [err, raw] = await tryCatch(
3074
+ config._data.query(api.subAccounts.queries.retrieve, {
3075
+ subAccountId: input.subAccountId
2227
3076
  })
2228
3077
  );
2229
3078
  if (err) {
@@ -2232,822 +3081,557 @@ function createOrgExternalAccountsClient(config) {
2232
3081
  null
2233
3082
  ];
2234
3083
  }
2235
- return [null, void 0];
2236
- }
2237
- };
2238
- }
2239
- function createOrganizationsClient(config = {}) {
2240
- return {
2241
- create: async () => stub("organizations.create"),
2242
- retrieve: async () => stub("organizations.retrieve"),
2243
- list: async () => stub("organizations.list"),
2244
- update: async () => stub("organizations.update"),
2245
- safes: {
2246
- retrieve: async (input) => {
2247
- if (!config.data) {
2248
- return stub("organizations.safes.retrieve");
2249
- }
2250
- try {
2251
- const safe = await config.data.query(
2252
- api.safe.queries.retrieveOrganizationSafe,
2253
- input
2254
- );
2255
- if (!safe) {
2256
- return [
2257
- new CapxulError({
2258
- code: "NOT_FOUND",
2259
- message: `safe ${input.safeId} not found`
2260
- }),
2261
- null
2262
- ];
2263
- }
2264
- return [null, safe];
2265
- } catch (cause) {
2266
- return [
2267
- fromConvexError(cause),
2268
- null
2269
- ];
2270
- }
3084
+ if (!raw) {
3085
+ return [
3086
+ new CapxulError({
3087
+ code: "NOT_FOUND",
3088
+ message: `sub_account ${input.subAccountId} not found`
3089
+ }),
3090
+ null
3091
+ ];
2271
3092
  }
3093
+ const [brandErr, branded] = tryBrandSubAccount(raw);
3094
+ if (brandErr) {
3095
+ return [
3096
+ brandErr,
3097
+ null
3098
+ ];
3099
+ }
3100
+ return [null, branded];
2272
3101
  },
2273
- treasury: {
2274
- retrieve: async () => stub("organizations.treasury.retrieve")
2275
- },
2276
- members: {
2277
- list: async () => stub("organizations.members.list"),
2278
- retrieve: async () => stub("organizations.members.retrieve"),
2279
- invite: async () => stub("organizations.members.invite"),
2280
- updateRole: async () => stub("organizations.members.updateRole"),
2281
- remove: async () => stub("organizations.members.remove")
2282
- },
2283
- apiKeys: createApiKeysClient(),
2284
- kybProfile: {
2285
- start: async () => stub("organizations.kybProfile.start"),
2286
- retrieve: async () => stub("organizations.kybProfile.retrieve")
2287
- },
2288
- subAccounts: {
2289
- create: async () => stub("organizations.subAccounts.create"),
2290
- list: async () => stub("organizations.subAccounts.list"),
2291
- retrieve: async () => stub("organizations.subAccounts.retrieve"),
2292
- remove: async () => stub("organizations.subAccounts.remove")
2293
- },
2294
- externalAccounts: createOrgExternalAccountsClient(config),
2295
- balanceLedger: {
2296
- list: async () => stub(
2297
- "organizations.balanceLedger.list"
2298
- ),
2299
- retrieve: async () => stub(
2300
- "organizations.balanceLedger.retrieve"
2301
- )
2302
- },
2303
- payments: createOrgPaymentsClient(),
2304
- transfers: createOrgTransfersClient(),
2305
- withdrawals: createOrgWithdrawalsClient(config),
2306
- documents: createOrgDocumentsClient(),
2307
- webhookEndpoints: createWebhookEndpointsClient(),
2308
- webhookEvents: createWebhookEventsClient()
2309
- };
2310
- }
2311
-
2312
- // src/core/sub-accounts.ts
2313
- function createSubAccountsClient() {
2314
- return {
2315
- retrieve: async () => stub("subAccounts.retrieve"),
2316
- remove: async () => stub("subAccounts.remove")
2317
- };
2318
- }
2319
-
2320
- // src/core/token-transfers.ts
2321
- var toTokenTransferId = (raw) => {
2322
- if (typeof raw !== "string" || raw.length === 0) {
2323
- throw new Error(
2324
- `Invalid tokenTransferId: expected non-empty string, got ${String(raw)}`
2325
- );
2326
- }
2327
- return raw;
2328
- };
2329
- function brandRow(row) {
2330
- return {
2331
- ...row,
2332
- id: toTokenTransferId(row.id)
3102
+ remove: async (input) => {
3103
+ if (!config._data) {
3104
+ return stub(
3105
+ "organizations.subAccounts.remove"
3106
+ );
3107
+ }
3108
+ const [err, raw] = await tryCatch(
3109
+ config._data.mutation(api.subAccounts.mutations.archive, {
3110
+ subAccountId: input.subAccountId
3111
+ })
3112
+ );
3113
+ if (err) {
3114
+ return [
3115
+ fromConvexError(err),
3116
+ null
3117
+ ];
3118
+ }
3119
+ if (!raw) {
3120
+ return [
3121
+ new CapxulError({
3122
+ code: "NOT_FOUND",
3123
+ message: `sub_account ${input.subAccountId} not found`
3124
+ }),
3125
+ null
3126
+ ];
3127
+ }
3128
+ const [brandErr, branded] = tryBrandSubAccount(raw);
3129
+ if (brandErr) {
3130
+ return [
3131
+ brandErr,
3132
+ null
3133
+ ];
3134
+ }
3135
+ return [null, branded];
3136
+ }
2333
3137
  };
2334
3138
  }
2335
- function createTokenTransfersClient(config = {}) {
3139
+ function createOrganizationsClient(config = {}) {
2336
3140
  return {
2337
- list: async (input) => {
2338
- if (!config.data) {
2339
- return stub("tokenTransfers.list");
3141
+ create: async (input) => {
3142
+ if (!config._data) {
3143
+ return stub("organizations.create");
3144
+ }
3145
+ if (input.country !== void 0) {
3146
+ return [
3147
+ new CapxulError({
3148
+ code: "INVALID_INPUT",
3149
+ message: "organizations.create does not support country yet \u2014 backend mutation only accepts name.",
3150
+ details: { field: "country" }
3151
+ }),
3152
+ null
3153
+ ];
2340
3154
  }
2341
3155
  try {
2342
- const raw = await config.data.query(
2343
- api.tokenTransfers.queries.list,
2344
- {
2345
- limit: input?.limit,
2346
- cursor: input?.cursor,
2347
- direction: input?.direction
2348
- }
2349
- );
2350
- if (!raw) {
3156
+ const orgId = await config._data.mutation(api.org.mutations.create, {
3157
+ name: input.name
3158
+ });
3159
+ const org = await config._data.query(api.org.queries.retrieve, {
3160
+ orgId
3161
+ });
3162
+ if (!org) {
2351
3163
  return [
2352
3164
  new CapxulError({
2353
- code: "NOT_AUTHENTICATED",
2354
- message: "tokenTransfers.list requires an authenticated session."
3165
+ code: "NETWORK_ERROR",
3166
+ message: "organization created but could not be retrieved"
2355
3167
  }),
2356
3168
  null
2357
3169
  ];
2358
3170
  }
2359
- return [
2360
- null,
2361
- {
2362
- object: "list",
2363
- data: raw.items.map(brandRow),
2364
- page: {
2365
- hasMore: raw.hasMore,
2366
- nextCursor: raw.nextCursor
2367
- },
2368
- displayCurrency: raw.displayCurrency
2369
- }
2370
- ];
3171
+ return [null, org];
2371
3172
  } catch (cause) {
2372
3173
  return [fromConvexError(cause), null];
2373
3174
  }
2374
3175
  },
2375
- retrieve: async (input) => {
2376
- if (!config.data) {
2377
- return stub("tokenTransfers.retrieve");
3176
+ retrieve: async (organizationId) => {
3177
+ if (!config._data) {
3178
+ return stub("organizations.retrieve");
2378
3179
  }
2379
3180
  try {
2380
- const raw = await config.data.query(
2381
- api.tokenTransfers.queries.getByTxLogIndex,
2382
- {
2383
- txHash: input.txHash,
2384
- logIndex: input.logIndex,
2385
- chainId: input.chainId
2386
- }
2387
- );
2388
- if (!raw) {
3181
+ const orgId = organizationId.replace(/^org_/, "");
3182
+ const org = await config._data.query(api.org.queries.retrieve, {
3183
+ orgId
3184
+ });
3185
+ if (!org) {
2389
3186
  return [
2390
3187
  new CapxulError({
2391
3188
  code: "NOT_FOUND",
2392
- message: `tokenTransfer (txHash=${input.txHash}, logIndex=${input.logIndex}) not found or not visible to caller.`,
2393
- details: {
2394
- txHash: input.txHash,
2395
- logIndex: input.logIndex,
2396
- chainId: input.chainId
2397
- }
3189
+ message: `organization ${organizationId} not found`
2398
3190
  }),
2399
3191
  null
2400
3192
  ];
2401
3193
  }
2402
- return [null, brandRow(raw)];
3194
+ return [null, org];
2403
3195
  } catch (cause) {
2404
3196
  return [fromConvexError(cause), null];
2405
3197
  }
2406
- }
2407
- };
2408
- }
2409
-
2410
- // src/core/virtual-accounts.ts
2411
- function createVirtualAccountsClient() {
2412
- return {
2413
- create: async () => stub("virtualAccounts.create"),
2414
- retrieve: async () => stub("virtualAccounts.retrieve"),
2415
- list: async () => stub("virtualAccounts.list"),
2416
- remove: async () => stub("virtualAccounts.remove")
2417
- };
2418
- }
2419
-
2420
- // src/core/virtual-cards.ts
2421
- function createVirtualCardsClient() {
2422
- return {
2423
- create: async () => stub("virtualCards.create"),
2424
- retrieve: async () => stub("virtualCards.retrieve"),
2425
- list: async () => stub("virtualCards.list"),
2426
- freeze: async () => stub("virtualCards.freeze"),
2427
- unfreeze: async () => stub("virtualCards.unfreeze"),
2428
- cancel: async () => stub("virtualCards.cancel")
2429
- };
2430
- }
2431
- function createAuthFlowMachine(client) {
2432
- return xstate.setup({
2433
- types: {},
2434
- actors: {
2435
- // XState v5's `fromPromise` injects an `AbortSignal` that aborts
2436
- // when the actor is stopped (parent transition fires, machine is
2437
- // disposed, etc.). Plumbing it through `client.auth.sendOtp` /
2438
- // `client.auth.verifyOtp` makes the in-flight HTTP request
2439
- // cancellable: stale responses can't race a state machine
2440
- // that's already moved on. See PR #406 S5.
2441
- sendOtp: xstate.fromPromise(async ({ input, signal }) => {
2442
- const [error] = await client.auth.sendOtp(
2443
- { email: input.email },
2444
- { signal }
2445
- );
2446
- if (error) throw error;
2447
- }),
2448
- verifyOtp: xstate.fromPromise(
2449
- async ({ input, signal }) => {
2450
- const [error, result] = await client.auth.verifyOtp(
2451
- {
2452
- email: input.email,
2453
- otp: input.code
2454
- },
2455
- { signal }
2456
- );
2457
- if (error) throw error;
2458
- if (result.kind === "bootstrap_required") {
2459
- throw new CapxulError({
2460
- code: "ACTION_REQUIRED",
2461
- message: "OTP verified but auth bootstrap is required. Use createAuthBootstrapFlowMachine for product sign-up.",
2462
- details: { reason: result.reason }
2463
- });
2464
- }
2465
- return result.session;
2466
- }
2467
- ),
2468
- signOut: xstate.fromPromise(async () => {
2469
- const [error] = await client.auth.signOut();
2470
- if (error) throw error;
2471
- })
2472
3198
  },
2473
- actions: {
2474
- trackOtpRequested: ({ context }) => {
2475
- if (!context.email) return;
2476
- track("auth_otp_requested", {
2477
- email_domain: emailDomain(context.email)
2478
- });
2479
- },
2480
- trackOtpFailed: ({ event }) => {
2481
- const error = errorFromEvent(event);
2482
- track("auth_failed", {
2483
- auth_type: "email_otp",
2484
- reason: error.code
2485
- });
2486
- },
2487
- trackTimeoutFailed: () => {
2488
- track("auth_failed", {
2489
- auth_type: "email_otp",
2490
- reason: "timeout"
2491
- });
2492
- },
2493
- trackVerified: () => {
2494
- track("auth_verified", { auth_type: "email_otp" });
2495
- },
2496
- identifyAndTrack: ({ context }) => {
2497
- if (!context.session) return;
2498
- identify(context.session.authUserId, {
2499
- email_domain: emailDomain(context.session.email)
2500
- });
2501
- track("auth_identified", {
2502
- email_domain: emailDomain(context.session.email)
2503
- });
2504
- },
2505
- trackSignedOut: () => {
2506
- track("auth_signed_out");
3199
+ list: async (input) => {
3200
+ if (!config._data) {
3201
+ return stub("organizations.list");
2507
3202
  }
2508
- }
2509
- }).createMachine({
2510
- id: "auth",
2511
- initial: "idle",
2512
- context: { email: null, session: null, error: null },
2513
- states: {
2514
- idle: {
2515
- on: {
2516
- REQUEST_OTP: {
2517
- target: "sending_otp",
2518
- actions: xstate.assign({
2519
- email: ({ event }) => event.email,
2520
- error: () => null
2521
- })
2522
- }
2523
- }
2524
- },
2525
- sending_otp: {
2526
- invoke: {
2527
- src: "sendOtp",
2528
- input: ({ context }) => ({ email: requireEmail(context) }),
2529
- onDone: {
2530
- target: "otp_requested",
2531
- actions: ["trackOtpRequested"]
2532
- },
2533
- onError: {
2534
- target: "error",
2535
- actions: [
2536
- xstate.assign({ error: ({ event }) => errorFromEvent(event) }),
2537
- "trackOtpFailed"
2538
- ]
2539
- }
2540
- },
2541
- after: {
2542
- [FLOW_INVOKE_TIMEOUT_MS]: {
2543
- target: "error",
2544
- actions: [
2545
- xstate.assign({
2546
- error: () => timeoutError("sending_otp")
2547
- }),
2548
- "trackTimeoutFailed"
2549
- ]
3203
+ try {
3204
+ const page = await config._data.query(api.org.queries.list, {
3205
+ limit: input?.limit,
3206
+ cursor: input?.cursor
3207
+ });
3208
+ const result = {
3209
+ object: "list",
3210
+ data: page.data,
3211
+ page: {
3212
+ hasMore: page.hasMore,
3213
+ cursor: page.nextCursor
2550
3214
  }
3215
+ };
3216
+ return [null, result];
3217
+ } catch (cause) {
3218
+ return [fromConvexError(cause), null];
3219
+ }
3220
+ },
3221
+ update: async (input) => {
3222
+ if (!config._data) {
3223
+ return stub("organizations.update");
3224
+ }
3225
+ try {
3226
+ const orgId = input.organizationId.replace(/^org_/, "");
3227
+ const org = await config._data.mutation(api.org.mutations.update, {
3228
+ orgId,
3229
+ name: input.name
3230
+ });
3231
+ if (!org) {
3232
+ return [
3233
+ new CapxulError({
3234
+ code: "NOT_FOUND",
3235
+ message: `organization ${input.organizationId} not found`
3236
+ }),
3237
+ null
3238
+ ];
2551
3239
  }
2552
- },
2553
- otp_requested: {
2554
- on: {
2555
- VERIFY: { target: "verifying" },
2556
- RESET: {
2557
- target: "idle",
2558
- actions: xstate.assign({ email: () => null, error: () => null })
2559
- }
3240
+ return [null, org];
3241
+ } catch (cause) {
3242
+ return [fromConvexError(cause), null];
3243
+ }
3244
+ },
3245
+ safes: {
3246
+ retrieve: async (input) => {
3247
+ if (!config._data) {
3248
+ return stub("organizations.safes.retrieve");
2560
3249
  }
2561
- },
2562
- verifying: {
2563
- invoke: {
2564
- src: "verifyOtp",
2565
- input: ({ context, event }) => ({
2566
- email: requireEmail(context),
2567
- code: requireCodeFromEvent(event)
2568
- }),
2569
- onDone: {
2570
- target: "authenticated",
2571
- actions: [
2572
- // Scrub the duplicate `context.email` (input value
2573
- // captured during sendOtp) since the verified
2574
- // `session.email` is now the canonical source
2575
- // post-authentication. The session's email is
2576
- // intentionally retained — it's the auth result, not
2577
- // lingering input. See PR #406 S2.
2578
- xstate.assign({
2579
- session: ({ event }) => event.output,
2580
- email: () => null
2581
- }),
2582
- "trackVerified",
2583
- "identifyAndTrack"
2584
- ]
2585
- },
2586
- onError: {
2587
- target: "error",
2588
- actions: [
2589
- xstate.assign({ error: ({ event }) => errorFromEvent(event) }),
2590
- "trackOtpFailed"
2591
- ]
2592
- }
2593
- },
2594
- after: {
2595
- [FLOW_INVOKE_TIMEOUT_MS]: {
2596
- target: "error",
2597
- actions: [
2598
- xstate.assign({
2599
- error: () => timeoutError("verifying")
3250
+ try {
3251
+ const safe = await config._data.query(
3252
+ api.safe.queries.retrieveOrganizationSafe,
3253
+ input
3254
+ );
3255
+ if (!safe) {
3256
+ return [
3257
+ new CapxulError({
3258
+ code: "NOT_FOUND",
3259
+ message: `safe ${input.safeId} not found`
2600
3260
  }),
2601
- "trackTimeoutFailed"
2602
- ]
3261
+ null
3262
+ ];
2603
3263
  }
3264
+ return [null, safe];
3265
+ } catch (cause) {
3266
+ return [
3267
+ fromConvexError(cause),
3268
+ null
3269
+ ];
2604
3270
  }
2605
- },
2606
- authenticated: {
2607
- on: {
2608
- SIGN_OUT: { target: "signing_out" }
3271
+ }
3272
+ },
3273
+ treasury: {
3274
+ retrieve: async (organizationId) => {
3275
+ if (!config._data) {
3276
+ return stub(
3277
+ "organizations.treasury.retrieve"
3278
+ );
2609
3279
  }
2610
- },
2611
- signing_out: {
2612
- invoke: {
2613
- src: "signOut",
2614
- onDone: {
2615
- target: "idle",
2616
- actions: [
2617
- xstate.assign({
2618
- session: () => null,
2619
- email: () => null,
2620
- error: () => null
3280
+ try {
3281
+ const orgId = organizationId.replace(/^org_/, "");
3282
+ const raw = await config._data.query(
3283
+ api.safe.queries.getOrgTreasuryBalance,
3284
+ { orgId }
3285
+ );
3286
+ if (!raw) {
3287
+ return [
3288
+ new CapxulError({
3289
+ code: "NOT_FOUND",
3290
+ message: `treasury for organization ${organizationId} not found`
2621
3291
  }),
2622
- "trackSignedOut"
2623
- ]
2624
- },
2625
- onError: {
2626
- target: "error",
2627
- actions: xstate.assign({ error: ({ event }) => errorFromEvent(event) })
2628
- }
2629
- }
2630
- },
2631
- error: {
2632
- on: {
2633
- RESET: {
2634
- target: "idle",
2635
- actions: xstate.assign({ error: () => null })
3292
+ null
3293
+ ];
2636
3294
  }
3295
+ const treasury = {
3296
+ object: "treasury",
3297
+ id: toTreasuryId(`try_${orgId}`),
3298
+ organizationId,
3299
+ status: "active",
3300
+ safeId: toSafeId(
3301
+ `safe_${raw.safeAddress.replace(/^0x/, "").toLowerCase()}`
3302
+ ),
3303
+ totalBalance: { value: "0", currency: "USD" },
3304
+ positions: raw.tokens.map((t) => ({
3305
+ symbol: t.symbol,
3306
+ contractAddress: t.tokenAddress,
3307
+ amount: t.balance
3308
+ })),
3309
+ asOf: raw.lastUpdatedAt ? new Date(raw.lastUpdatedAt).toISOString() : (/* @__PURE__ */ new Date()).toISOString()
3310
+ };
3311
+ return [null, treasury];
3312
+ } catch (cause) {
3313
+ return [
3314
+ fromConvexError(cause),
3315
+ null
3316
+ ];
2637
3317
  }
2638
3318
  }
2639
- }
2640
- });
2641
- }
2642
- function requireEmail(context) {
2643
- if (!context.email) {
2644
- throw Errors.invalidInput(
2645
- "email",
2646
- "Auth flow advanced without an email captured in context."
2647
- );
2648
- }
2649
- return context.email;
2650
- }
2651
- function requireCodeFromEvent(event) {
2652
- if (event.type !== "VERIFY") {
2653
- throw Errors.invalidInput(
2654
- "code",
2655
- `Auth flow's verifying state requires a VERIFY event, got ${event.type}.`
2656
- );
2657
- }
2658
- return event.code;
2659
- }
2660
- function errorFromEvent(event) {
2661
- const cause = typeof event === "object" && event !== null && "error" in event ? event.error : event;
2662
- if (cause instanceof CapxulError) {
2663
- if (!EMAIL_MATCH_PATTERN.test(cause.message)) return cause;
2664
- return new CapxulError({
2665
- code: cause.code,
2666
- message: redactEmail(cause.message),
2667
- cause,
2668
- details: cause.details,
2669
- operationId: cause.operationId,
2670
- correlationId: cause.correlationId,
2671
- retryable: cause.retryable
2672
- });
2673
- }
2674
- if (cause instanceof CapxulError2) {
2675
- if (!EMAIL_MATCH_PATTERN.test(cause.message)) return cause;
2676
- return new CapxulError2(cause.code, redactEmail(cause.message), {
2677
- cause,
2678
- details: cause.details,
2679
- correlationId: cause.correlationId,
2680
- layer: cause.layer
2681
- });
2682
- }
2683
- return Errors.providerError("auth", "flow", redactCauseEmail(cause));
2684
- }
2685
- var EMAIL_MATCH_PATTERN = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/;
2686
- var EMAIL_REDACT_PATTERN = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;
2687
- function redactEmail(message) {
2688
- return message.replace(EMAIL_REDACT_PATTERN, "<redacted>");
2689
- }
2690
- function redactCauseEmail(cause) {
2691
- if (cause instanceof Error) {
2692
- if (!EMAIL_MATCH_PATTERN.test(cause.message)) return cause;
2693
- const redacted = new Error(redactEmail(cause.message));
2694
- redacted.cause = cause;
2695
- return redacted;
2696
- }
2697
- if (typeof cause === "string") {
2698
- return redactEmail(cause);
2699
- }
2700
- return cause;
2701
- }
2702
- function timeoutError(state) {
2703
- return Errors.providerError(
2704
- "auth",
2705
- "flow",
2706
- new Error(`timeout: ${state} exceeded ${FLOW_INVOKE_TIMEOUT_MS}ms`)
2707
- );
2708
- }
2709
- function emailDomain(email) {
2710
- const domain = email.split("@")[1]?.trim().toLowerCase();
2711
- return domain || "unknown";
2712
- }
2713
- var initialContext = {
2714
- email: null,
2715
- code: null,
2716
- username: null,
2717
- signerProvider: null,
2718
- bootstrapToken: null,
2719
- bootstrapReason: null,
2720
- session: null,
2721
- account: null,
2722
- safe: null,
2723
- error: null
2724
- };
2725
- function createAuthBootstrapFlowMachine(client) {
2726
- return xstate.setup({
2727
- types: {},
2728
- actors: {
2729
- sendOtp: xstate.fromPromise(async ({ input, signal }) => {
2730
- const [error] = await client.auth.sendOtp(
2731
- { email: input.email },
2732
- { signal }
2733
- );
2734
- if (error) throw error;
2735
- }),
2736
- verifyOtp: xstate.fromPromise(
2737
- async ({ input, signal }) => {
2738
- const [error, result] = await client.auth.verifyOtp(
2739
- { email: input.email, otp: input.code },
2740
- { signal }
2741
- );
2742
- if (error) throw error;
2743
- return result;
2744
- }
2745
- ),
2746
- completeBootstrap: xstate.fromPromise(async ({ input }) => {
2747
- const [error, result] = await client.auth.completeBootstrap(input);
2748
- if (error) throw error;
2749
- return result;
2750
- }),
2751
- signOut: xstate.fromPromise(async () => {
2752
- const [error] = await client.auth.signOut();
2753
- if (error) throw error;
2754
- })
2755
3319
  },
2756
- actions: {
2757
- trackOtpRequested: ({ context }) => {
2758
- if (!context.email) return;
2759
- track("auth_otp_requested", {
2760
- email_domain: emailDomain2(context.email)
2761
- });
2762
- },
2763
- trackFailed: ({ event }) => {
2764
- track("auth_failed", {
2765
- auth_type: "email_otp",
2766
- reason: errorFromEvent2(event).code
2767
- });
2768
- },
2769
- trackTimeoutFailed: () => {
2770
- track("auth_failed", {
2771
- auth_type: "email_otp",
2772
- reason: "timeout"
2773
- });
2774
- },
2775
- trackVerified: () => {
2776
- track("auth_verified", { auth_type: "email_otp" });
2777
- },
2778
- trackBootstrapRequired: ({ context }) => {
2779
- track("auth_verified", {
2780
- auth_type: "email_otp",
2781
- auth_mode: context.bootstrapReason ?? "bootstrap_required"
2782
- });
2783
- },
2784
- identifyAndTrack: ({ context }) => {
2785
- if (!context.session) return;
2786
- identify(context.session.authUserId, {
2787
- email_domain: emailDomain2(context.session.email)
2788
- });
2789
- track("auth_identified", {
2790
- email_domain: emailDomain2(context.session.email)
2791
- });
2792
- },
2793
- trackSignedOut: () => {
2794
- track("auth_signed_out");
2795
- }
2796
- }
2797
- }).createMachine({
2798
- id: "authBootstrap",
2799
- initial: "email",
2800
- context: initialContext,
2801
- states: {
2802
- email: {
2803
- on: {
2804
- ENTER_EMAIL: {
2805
- actions: xstate.assign({
2806
- email: ({ event }) => event.email,
2807
- error: () => null
2808
- })
2809
- },
2810
- REQUEST_OTP: { target: "sending_otp" }
3320
+ members: {
3321
+ list: async (input) => {
3322
+ if (!config._data?.action) {
3323
+ return stub("organizations.members.list");
3324
+ }
3325
+ try {
3326
+ const orgId = input.organizationId.replace(/^org_/, "");
3327
+ const page = await config._data.action(api.org.actions.membersList, {
3328
+ organizationId: orgId,
3329
+ status: input.status,
3330
+ limit: input.limit,
3331
+ cursor: input.cursor
3332
+ });
3333
+ return [null, page];
3334
+ } catch (cause) {
3335
+ return [fromConvexError(cause), null];
2811
3336
  }
2812
3337
  },
2813
- sending_otp: {
2814
- invoke: {
2815
- src: "sendOtp",
2816
- input: ({ context }) => ({ email: requireEmail2(context) }),
2817
- onDone: { target: "otp_requested", actions: "trackOtpRequested" },
2818
- onError: {
2819
- target: "otp_requested",
2820
- actions: [
2821
- xstate.assign({ error: ({ event }) => errorFromEvent2(event) }),
2822
- "trackFailed"
2823
- ]
2824
- }
2825
- },
2826
- after: {
2827
- [FLOW_INVOKE_TIMEOUT_MS]: {
2828
- target: "otp_requested",
2829
- actions: [
2830
- xstate.assign({ error: () => timeoutError2("sending_otp") }),
2831
- "trackTimeoutFailed"
2832
- ]
2833
- }
3338
+ retrieve: async (input) => {
3339
+ if (!config._data?.action) {
3340
+ return stub("organizations.members.retrieve");
2834
3341
  }
2835
- },
2836
- otp_requested: {
2837
- on: {
2838
- ENTER_OTP: {
2839
- actions: xstate.assign({
2840
- code: ({ event }) => event.code,
2841
- error: () => null
2842
- })
2843
- },
2844
- VERIFY_OTP: { target: "verifying_otp" },
2845
- BACK: { target: "email" },
2846
- RESET: { target: "email", actions: xstate.assign(() => initialContext) }
3342
+ try {
3343
+ const orgId = input.organizationId.replace(/^org_/, "");
3344
+ const memberId = input.memberId.replace(/^mb_/, "");
3345
+ const member = await config._data.action(
3346
+ api.org.actions.retrieveMember,
3347
+ {
3348
+ organizationId: orgId,
3349
+ memberId
3350
+ }
3351
+ );
3352
+ return [null, member];
3353
+ } catch (cause) {
3354
+ return [fromConvexError(cause), null];
2847
3355
  }
2848
3356
  },
2849
- verifying_otp: {
2850
- invoke: {
2851
- src: "verifyOtp",
2852
- input: ({ context }) => ({
2853
- email: requireEmail2(context),
2854
- code: requireCode(context)
2855
- }),
2856
- onDone: [
2857
- {
2858
- guard: ({ event }) => event.output.kind === "existing_member",
2859
- target: "authenticated",
2860
- actions: [
2861
- xstate.assign({
2862
- session: ({ event }) => event.output.session,
2863
- account: ({ event }) => event.output.kind === "existing_member" ? event.output.account : null,
2864
- username: ({ event }) => event.output.kind === "existing_member" ? event.output.username : null,
2865
- safe: ({ event }) => event.output.kind === "existing_member" ? event.output.safe : null,
2866
- email: () => null,
2867
- error: () => null
2868
- }),
2869
- "trackVerified",
2870
- "identifyAndTrack"
2871
- ]
2872
- },
3357
+ invite: async (input) => {
3358
+ if (!config._data?.action) {
3359
+ return stub(
3360
+ "organizations.members.invite"
3361
+ );
3362
+ }
3363
+ try {
3364
+ const orgId = input.organizationId.replace(/^org_/, "");
3365
+ const result = await config._data.action(
3366
+ api.org.actions.inviteMember,
2873
3367
  {
2874
- target: "bootstrap_required",
2875
- actions: [
2876
- xstate.assign({
2877
- session: ({ event }) => event.output.session,
2878
- bootstrapToken: ({ event }) => event.output.kind === "bootstrap_required" ? event.output.bootstrapToken : null,
2879
- bootstrapReason: ({ event }) => event.output.kind === "bootstrap_required" ? event.output.reason : null,
2880
- username: ({ event }) => event.output.kind === "bootstrap_required" ? event.output.username ?? null : null,
2881
- email: ({ event }) => event.output.kind === "bootstrap_required" ? event.output.email : null,
2882
- error: () => null
2883
- }),
2884
- "trackVerified",
2885
- "trackBootstrapRequired"
2886
- ]
3368
+ organizationId: orgId,
3369
+ email: input.email,
3370
+ role: input.role
2887
3371
  }
2888
- ],
2889
- onError: {
2890
- target: "otp_requested",
2891
- actions: [
2892
- xstate.assign({ error: ({ event }) => errorFromEvent2(event) }),
2893
- "trackFailed"
2894
- ]
2895
- }
2896
- },
2897
- after: {
2898
- [FLOW_INVOKE_TIMEOUT_MS]: {
2899
- target: "otp_requested",
2900
- actions: [
2901
- xstate.assign({ error: () => timeoutError2("verifying_otp") }),
2902
- "trackTimeoutFailed"
2903
- ]
2904
- }
3372
+ );
3373
+ return [null, result];
3374
+ } catch (cause) {
3375
+ return [fromConvexError(cause), null];
2905
3376
  }
2906
3377
  },
2907
- bootstrap_required: {
2908
- on: {
2909
- ENTER_USERNAME: {
2910
- actions: xstate.assign({
2911
- username: ({ event }) => event.username,
2912
- error: () => null
2913
- })
2914
- },
2915
- ENTER_SIGNER_PROVIDER: {
2916
- actions: xstate.assign({
2917
- signerProvider: ({ event }) => event.signerProvider,
2918
- error: () => null
2919
- })
2920
- },
2921
- COMPLETE_BOOTSTRAP: { target: "completing_bootstrap" },
2922
- BACK: { target: "otp_requested" },
2923
- RESET: { target: "email", actions: xstate.assign(() => initialContext) }
3378
+ accept: async (input) => {
3379
+ if (!config._data?.action) {
3380
+ return stub("organizations.members.accept");
3381
+ }
3382
+ try {
3383
+ const member = await config._data.action(
3384
+ api.org.actions.acceptInvitation,
3385
+ { token: input.token }
3386
+ );
3387
+ return [null, member];
3388
+ } catch (cause) {
3389
+ return [fromConvexError(cause), null];
2924
3390
  }
2925
3391
  },
2926
- completing_bootstrap: {
2927
- invoke: {
2928
- src: "completeBootstrap",
2929
- input: ({ context }) => ({
2930
- bootstrapToken: requireBootstrapToken(context),
2931
- username: requireUsername(context),
2932
- signerProvider: requireSignerProvider(context)
2933
- }),
2934
- onDone: {
2935
- target: "authenticated",
2936
- actions: [
2937
- xstate.assign({
2938
- session: ({ event }) => event.output.session,
2939
- account: ({ event }) => event.output.account,
2940
- username: ({ event }) => event.output.username,
2941
- safe: ({ event }) => event.output.safe,
2942
- bootstrapToken: () => null,
2943
- bootstrapReason: () => null,
2944
- signerProvider: () => null,
2945
- email: () => null,
2946
- error: () => null
2947
- }),
2948
- "identifyAndTrack"
2949
- ]
2950
- },
2951
- onError: {
2952
- target: "bootstrap_required",
2953
- actions: [
2954
- xstate.assign({ error: ({ event }) => errorFromEvent2(event) }),
2955
- "trackFailed"
2956
- ]
2957
- }
2958
- },
2959
- after: {
2960
- [FLOW_INVOKE_TIMEOUT_MS]: {
2961
- target: "bootstrap_required",
2962
- actions: [
2963
- xstate.assign({ error: () => timeoutError2("completing_bootstrap") }),
2964
- "trackTimeoutFailed"
2965
- ]
2966
- }
3392
+ updateRole: async (input) => {
3393
+ if (!config._data?.action) {
3394
+ return stub("organizations.members.updateRole");
3395
+ }
3396
+ try {
3397
+ const orgId = input.organizationId.replace(/^org_/, "");
3398
+ const memberId = input.memberId.replace(/^mb_/, "");
3399
+ const member = await config._data.action(
3400
+ api.org.actions.updateMemberRole,
3401
+ {
3402
+ organizationId: orgId,
3403
+ memberId,
3404
+ role: input.role
3405
+ }
3406
+ );
3407
+ return [null, member];
3408
+ } catch (cause) {
3409
+ return [fromConvexError(cause), null];
2967
3410
  }
2968
3411
  },
2969
- authenticated: {
2970
- on: {
2971
- SIGN_OUT: { target: "signing_out" }
3412
+ revoke: async (input) => {
3413
+ if (!config._data?.action) {
3414
+ return stub("organizations.members.revoke");
3415
+ }
3416
+ try {
3417
+ const orgId = input.organizationId.replace(/^org_/, "");
3418
+ const memberId = input.memberId.replace(/^mb_/, "");
3419
+ const member = await config._data.action(
3420
+ api.org.actions.revokeMember,
3421
+ {
3422
+ organizationId: orgId,
3423
+ memberId
3424
+ }
3425
+ );
3426
+ return [null, member];
3427
+ } catch (cause) {
3428
+ return [fromConvexError(cause), null];
2972
3429
  }
2973
3430
  },
2974
- signing_out: {
2975
- invoke: {
2976
- src: "signOut",
2977
- onDone: {
2978
- target: "email",
2979
- actions: [
2980
- xstate.assign(() => initialContext),
2981
- "trackSignedOut"
2982
- ]
2983
- },
2984
- onError: {
2985
- target: "error",
2986
- actions: xstate.assign({ error: ({ event }) => errorFromEvent2(event) })
2987
- }
3431
+ remove: async (input) => {
3432
+ if (!config._data?.action) {
3433
+ return stub("organizations.members.remove");
3434
+ }
3435
+ try {
3436
+ const orgId = input.organizationId.replace(/^org_/, "");
3437
+ const memberId = input.memberId.replace(/^mb_/, "");
3438
+ await config._data.action(api.org.actions.removeMember, {
3439
+ organizationId: orgId,
3440
+ memberId
3441
+ });
3442
+ return [null, void 0];
3443
+ } catch (cause) {
3444
+ return [fromConvexError(cause), null];
2988
3445
  }
2989
3446
  },
2990
- error: {
2991
- on: {
2992
- RESET: { target: "email", actions: xstate.assign(() => initialContext) }
3447
+ resend: async (input) => {
3448
+ if (!config._data?.action) {
3449
+ return stub(
3450
+ "organizations.members.resend"
3451
+ );
3452
+ }
3453
+ try {
3454
+ const orgId = input.organizationId.replace(/^org_/, "");
3455
+ const memberId = input.memberId.replace(/^mb_/, "");
3456
+ const result = await config._data.action(
3457
+ api.org.actions.resendInvitation,
3458
+ {
3459
+ organizationId: orgId,
3460
+ memberId
3461
+ }
3462
+ );
3463
+ return [null, result];
3464
+ } catch (cause) {
3465
+ return [fromConvexError(cause), null];
2993
3466
  }
2994
3467
  }
2995
- }
2996
- });
2997
- }
2998
- function requireEmail2(context) {
2999
- if (!context.email) {
3000
- throw Errors.invalidInput("email", "Auth bootstrap requires an email.");
3001
- }
3002
- return context.email;
3003
- }
3004
- function requireCode(context) {
3005
- if (!context.code) {
3006
- throw Errors.invalidInput("code", "Auth bootstrap requires an OTP code.");
3007
- }
3008
- return context.code;
3009
- }
3010
- function requireBootstrapToken(context) {
3011
- if (!context.bootstrapToken) {
3012
- throw Errors.invalidInput(
3013
- "bootstrapToken",
3014
- "Auth bootstrap requires a continuation token."
3015
- );
3016
- }
3017
- return context.bootstrapToken;
3018
- }
3019
- function requireUsername(context) {
3020
- if (!context.username) {
3021
- throw Errors.invalidInput("username", "Auth bootstrap requires a username.");
3022
- }
3023
- return context.username;
3468
+ },
3469
+ apiKeys: createApiKeysClient(),
3470
+ subAccounts: createOrgSubAccountsClient(config),
3471
+ externalAccounts: createOrgExternalAccountsClient(config),
3472
+ balanceLedger: {
3473
+ list: async (input) => {
3474
+ if (!config._data) {
3475
+ return stub(
3476
+ "organizations.balanceLedger.list"
3477
+ );
3478
+ }
3479
+ try {
3480
+ const orgId = input.organizationId.replace(/^org_/, "");
3481
+ const page = await config._data.query(
3482
+ api.balanceLedger.queries.listForOrg,
3483
+ { orgId, limit: input.limit, cursor: input.cursor }
3484
+ );
3485
+ return [null, page];
3486
+ } catch (cause) {
3487
+ return [fromConvexError(cause), null];
3488
+ }
3489
+ },
3490
+ retrieve: async (input) => {
3491
+ if (!config._data) {
3492
+ return stub(
3493
+ "organizations.balanceLedger.retrieve"
3494
+ );
3495
+ }
3496
+ try {
3497
+ const entry = await config._data.query(
3498
+ api.balanceLedger.queries.retrieve,
3499
+ { entryId: input.entryId }
3500
+ );
3501
+ if (!entry) {
3502
+ return [
3503
+ new CapxulError({
3504
+ code: "NOT_FOUND",
3505
+ message: `balance_ledger_entry ${input.entryId} not found`
3506
+ }),
3507
+ null
3508
+ ];
3509
+ }
3510
+ return [null, entry];
3511
+ } catch (cause) {
3512
+ return [fromConvexError(cause), null];
3513
+ }
3514
+ }
3515
+ },
3516
+ payments: createOrgPaymentsClient(),
3517
+ transfers: createOrgTransfersClient(),
3518
+ withdrawals: createOrgWithdrawalsClient(config),
3519
+ documents: createOrgDocumentsClient(config),
3520
+ webhookEndpoints: createWebhookEndpointsClient(),
3521
+ webhookEvents: createWebhookEventsClient()
3522
+ };
3024
3523
  }
3025
- function requireSignerProvider(context) {
3026
- if (!context.signerProvider) {
3027
- throw Errors.invalidInput(
3028
- "signerProvider",
3029
- "Auth bootstrap requires a signer provider."
3524
+
3525
+ // src/core/token-transfers.ts
3526
+ var toTokenTransferId = (raw) => {
3527
+ if (typeof raw !== "string" || raw.length === 0) {
3528
+ throw new Error(
3529
+ `Invalid tokenTransferId: expected non-empty string, got ${String(raw)}`
3030
3530
  );
3031
3531
  }
3032
- return context.signerProvider;
3532
+ return raw;
3533
+ };
3534
+ function brandRow(row) {
3535
+ return {
3536
+ ...row,
3537
+ id: toTokenTransferId(row.id)
3538
+ };
3033
3539
  }
3034
- function errorFromEvent2(event) {
3035
- const cause = typeof event === "object" && event !== null && "error" in event ? event.error : event;
3036
- if (cause instanceof CapxulError || cause instanceof CapxulError2) {
3037
- return cause;
3038
- }
3039
- return Errors.providerError("auth", "bootstrap", cause);
3540
+ function createTokenTransfersClient(config = {}) {
3541
+ return {
3542
+ list: async (input) => {
3543
+ if (!config._data) {
3544
+ return stub("tokenTransfers.list");
3545
+ }
3546
+ try {
3547
+ const raw = await config._data.query(
3548
+ api.tokenTransfers.queries.list,
3549
+ {
3550
+ limit: input?.limit,
3551
+ cursor: input?.cursor,
3552
+ direction: input?.direction
3553
+ }
3554
+ );
3555
+ if (!raw) {
3556
+ return [
3557
+ new CapxulError({
3558
+ code: "NOT_AUTHENTICATED",
3559
+ message: "tokenTransfers.list requires an authenticated session."
3560
+ }),
3561
+ null
3562
+ ];
3563
+ }
3564
+ return [
3565
+ null,
3566
+ {
3567
+ object: "list",
3568
+ data: raw.items.map(brandRow),
3569
+ page: {
3570
+ hasMore: raw.hasMore,
3571
+ nextCursor: raw.nextCursor
3572
+ },
3573
+ displayCurrency: raw.displayCurrency
3574
+ }
3575
+ ];
3576
+ } catch (cause) {
3577
+ return [fromConvexError(cause), null];
3578
+ }
3579
+ },
3580
+ retrieve: async (input) => {
3581
+ if (!config._data) {
3582
+ return stub("tokenTransfers.retrieve");
3583
+ }
3584
+ try {
3585
+ const raw = await config._data.query(
3586
+ api.tokenTransfers.queries.getByTxLogIndex,
3587
+ {
3588
+ txHash: input.txHash,
3589
+ logIndex: input.logIndex,
3590
+ chainId: input.chainId
3591
+ }
3592
+ );
3593
+ if (!raw) {
3594
+ return [
3595
+ new CapxulError({
3596
+ code: "NOT_FOUND",
3597
+ message: `tokenTransfer (txHash=${input.txHash}, logIndex=${input.logIndex}) not found or not visible to caller.`,
3598
+ details: {
3599
+ txHash: input.txHash,
3600
+ logIndex: input.logIndex,
3601
+ chainId: input.chainId
3602
+ }
3603
+ }),
3604
+ null
3605
+ ];
3606
+ }
3607
+ return [null, brandRow(raw)];
3608
+ } catch (cause) {
3609
+ return [fromConvexError(cause), null];
3610
+ }
3611
+ }
3612
+ };
3040
3613
  }
3041
- function timeoutError2(state) {
3042
- return Errors.providerError(
3043
- "auth",
3044
- "bootstrap",
3045
- new Error(`timeout: ${state} exceeded ${FLOW_INVOKE_TIMEOUT_MS}ms`)
3046
- );
3614
+
3615
+ // src/core/virtual-accounts.ts
3616
+ function createVirtualAccountsClient() {
3617
+ return {
3618
+ create: async () => stub("virtualAccounts.create"),
3619
+ retrieve: async () => stub("virtualAccounts.retrieve"),
3620
+ list: async () => stub("virtualAccounts.list"),
3621
+ remove: async () => stub("virtualAccounts.remove")
3622
+ };
3047
3623
  }
3048
- function emailDomain2(email) {
3049
- const domain = email.split("@")[1]?.trim().toLowerCase();
3050
- return domain || "unknown";
3624
+
3625
+ // src/core/virtual-cards.ts
3626
+ function createVirtualCardsClient() {
3627
+ return {
3628
+ create: async () => stub("virtualCards.create"),
3629
+ retrieve: async () => stub("virtualCards.retrieve"),
3630
+ list: async () => stub("virtualCards.list"),
3631
+ freeze: async () => stub("virtualCards.freeze"),
3632
+ unfreeze: async () => stub("virtualCards.unfreeze"),
3633
+ cancel: async () => stub("virtualCards.cancel")
3634
+ };
3051
3635
  }
3052
3636
  function createProvisioningMachine(client) {
3053
3637
  return xstate.setup({
@@ -3076,7 +3660,7 @@ function createProvisioningMachine(client) {
3076
3660
  const provider = context.input?.signerProvider;
3077
3661
  if (!provider) return;
3078
3662
  track("provisioning_safe_created", {
3079
- safe_address: provider.safeAddress
3663
+ safe_address: deriveSafeAddress2(provider.signerAddress)
3080
3664
  });
3081
3665
  }
3082
3666
  }
@@ -3121,13 +3705,13 @@ function createProvisioningMachine(client) {
3121
3705
  },
3122
3706
  onError: {
3123
3707
  target: "error",
3124
- actions: xstate.assign({ error: ({ event }) => errorFromEvent3(event) })
3708
+ actions: xstate.assign({ error: ({ event }) => errorFromEvent(event) })
3125
3709
  }
3126
3710
  },
3127
3711
  after: {
3128
3712
  [FLOW_INVOKE_TIMEOUT_MS]: {
3129
3713
  target: "error",
3130
- actions: xstate.assign({ error: () => timeoutError3() })
3714
+ actions: xstate.assign({ error: () => timeoutError() })
3131
3715
  }
3132
3716
  }
3133
3717
  },
@@ -3145,7 +3729,7 @@ function createProvisioningMachine(client) {
3145
3729
  * this payload on its `onDone` transition and branches via guards
3146
3730
  * on `event.output.error`.
3147
3731
  */
3148
- output: ({ context }) => context.error ? { error: context.error } : context.account ? { account: context.account } : { error: timeoutError3() }
3732
+ output: ({ context }) => context.error ? { error: context.error } : context.account ? { account: context.account } : { error: timeoutError() }
3149
3733
  });
3150
3734
  }
3151
3735
  function requireProvisionInput(context) {
@@ -3157,13 +3741,13 @@ function requireProvisionInput(context) {
3157
3741
  }
3158
3742
  return context.input;
3159
3743
  }
3160
- function errorFromEvent3(event) {
3744
+ function errorFromEvent(event) {
3161
3745
  const cause = typeof event === "object" && event !== null && "error" in event ? event.error : event;
3162
3746
  if (cause instanceof CapxulError) return cause;
3163
3747
  if (cause instanceof CapxulError2) return cause;
3164
3748
  return Errors.providerError("provisioning", "flow", cause);
3165
3749
  }
3166
- function timeoutError3() {
3750
+ function timeoutError() {
3167
3751
  return Errors.providerError(
3168
3752
  "provisioning",
3169
3753
  "flow",
@@ -3256,7 +3840,7 @@ function createOnboardingFlowMachine(client) {
3256
3840
  error: ({ event }) => extractChildErrorOrFallback(event)
3257
3841
  }),
3258
3842
  assignChildThrown: xstate.assign({
3259
- error: ({ event }) => errorFromEvent4(event)
3843
+ error: ({ event }) => errorFromEvent2(event)
3260
3844
  }),
3261
3845
  assignAccountFromChild: xstate.assign({
3262
3846
  account: ({ event }) => extractChildAccountOrNull(event)
@@ -3418,7 +4002,7 @@ function extractChildAccountOrNull(event) {
3418
4002
  if (output && "account" in output && output.account) return output.account;
3419
4003
  return null;
3420
4004
  }
3421
- function errorFromEvent4(event) {
4005
+ function errorFromEvent2(event) {
3422
4006
  const cause = typeof event === "object" && event !== null && "error" in event ? event.error : event;
3423
4007
  if (cause instanceof CapxulError) return cause;
3424
4008
  if (cause instanceof CapxulError2) return cause;
@@ -3429,7 +4013,7 @@ function errorFromEvent4(event) {
3429
4013
  function createCapxulClient(config = {}) {
3430
4014
  const clientWithoutFlows = {
3431
4015
  id: crypto.randomUUID(),
3432
- auth: createAuthClient(config),
4016
+ auth: new AuthService(config),
3433
4017
  me: createMeClient(config),
3434
4018
  accounts: createAccountsClient(config),
3435
4019
  organizations: createOrganizationsClient(config),
@@ -3437,8 +4021,8 @@ function createCapxulClient(config = {}) {
3437
4021
  transfers: createTransfersClient(),
3438
4022
  tokenTransfers: createTokenTransfersClient(config),
3439
4023
  withdrawals: createWithdrawalsClient(config),
3440
- documents: createDocumentsClient(),
3441
- subAccounts: createSubAccountsClient(),
4024
+ documents: createDocumentsClient(config),
4025
+ subAccounts: createSubAccountsClient(config),
3442
4026
  virtualAccounts: createVirtualAccountsClient(),
3443
4027
  virtualCards: createVirtualCardsClient(),
3444
4028
  externalAccounts: createExternalAccountsClient(config),
@@ -3449,8 +4033,6 @@ function createCapxulClient(config = {}) {
3449
4033
  };
3450
4034
  const client = clientWithoutFlows;
3451
4035
  client.flows = {
3452
- auth: () => createAuthFlowMachine(client),
3453
- authBootstrap: () => createAuthBootstrapFlowMachine(client),
3454
4036
  onboarding: () => createOnboardingFlowMachine(client),
3455
4037
  provisioning: () => createProvisioningMachine(client)
3456
4038
  };