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