@capxul/sdk 0.1.0-alpha.4 → 0.1.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.cjs CHANGED
@@ -69,7 +69,268 @@ function fromConvexError(error) {
69
69
  });
70
70
  }
71
71
 
72
+ // ../observability/src/try-catch.ts
73
+ async function tryCatch(promise) {
74
+ try {
75
+ return [null, await promise];
76
+ } catch (e) {
77
+ return [e instanceof Error ? e : new Error(String(e)), null];
78
+ }
79
+ }
80
+
81
+ // ../observability/src/debug-log.ts
82
+ function isDevelopmentBuild() {
83
+ if (typeof process === "undefined") {
84
+ return false;
85
+ }
86
+ return process.env?.NODE_ENV === "development" || process.env?.NODE_ENV === "test";
87
+ }
88
+ function debugLog(line) {
89
+ if (!isDevelopmentBuild()) return;
90
+ if (typeof globalThis !== "undefined" && "window" in globalThis && typeof console?.info === "function") {
91
+ console.info(line);
92
+ return;
93
+ }
94
+ if (typeof process !== "undefined" && typeof process.stderr?.write === "function") {
95
+ process.stderr.write(`${line}
96
+ `);
97
+ }
98
+ }
99
+ function formatDebugValue(value) {
100
+ if (value === void 0 || value === "") return "";
101
+ if (typeof value === "string") return value;
102
+ try {
103
+ return JSON.stringify(value);
104
+ } catch {
105
+ return String(value);
106
+ }
107
+ }
108
+ function track(...args) {
109
+ const [name, props] = args;
110
+ debugLog(`[TRACK] ${name} ${formatDebugValue(props ?? "")}`);
111
+ }
112
+ function formatDebugValue2(value) {
113
+ if (value === void 0 || value === "") return "";
114
+ if (typeof value === "string") return value;
115
+ try {
116
+ return JSON.stringify(value);
117
+ } catch {
118
+ return String(value);
119
+ }
120
+ }
121
+ function identify(userId, traits) {
122
+ debugLog(`[IDENTIFY] ${userId} ${formatDebugValue2(traits ?? "")}`);
123
+ }
124
+
125
+ // ../platform-kernel/src/ids.ts
126
+ function makePrefixedIdConstructor(prefix, fieldName) {
127
+ const re = new RegExp(`^${prefix}_[A-Za-z0-9_\\-]+$`);
128
+ return (raw) => {
129
+ if (typeof raw !== "string" || !re.test(raw)) {
130
+ throw new Error(
131
+ `Invalid ${fieldName}: expected string matching ^${prefix}_[A-Za-z0-9_\\-]+$, got ${String(raw)}`
132
+ );
133
+ }
134
+ return raw;
135
+ };
136
+ }
137
+ var toOperationId = makePrefixedIdConstructor(
138
+ "op",
139
+ "operationId"
140
+ );
141
+ var toCorrelationId = makePrefixedIdConstructor("ctx", "correlationId");
142
+ var toExternalAccountId = makePrefixedIdConstructor("ext", "externalAccountId");
143
+
144
+ // src/core/external-accounts.ts
145
+ function brandExternalAccount(raw) {
146
+ return {
147
+ ...raw,
148
+ id: toExternalAccountId(raw.id),
149
+ operation: {
150
+ id: toOperationId(raw.operation.id),
151
+ status: raw.operation.status,
152
+ correlationId: toCorrelationId(raw.operation.correlationId)
153
+ }
154
+ };
155
+ }
156
+ function createExternalAccountsClient(config = {}) {
157
+ return {
158
+ retrieve: async (externalAccountId) => {
159
+ if (!config.data) {
160
+ return stub(
161
+ "externalAccounts.retrieve"
162
+ );
163
+ }
164
+ const [err, raw] = await tryCatch(
165
+ config.data.query(api.externalAccounts.queries.retrievePersonal, {
166
+ externalAccountId
167
+ })
168
+ );
169
+ if (err) {
170
+ return [
171
+ fromConvexError(err),
172
+ null
173
+ ];
174
+ }
175
+ if (!raw) {
176
+ return [
177
+ new CapxulError({
178
+ code: "NOT_FOUND",
179
+ message: `external_account ${externalAccountId} not found`
180
+ }),
181
+ null
182
+ ];
183
+ }
184
+ return [null, brandExternalAccount(raw)];
185
+ },
186
+ remove: async (externalAccountId) => {
187
+ if (!config.data) {
188
+ return stub("externalAccounts.remove");
189
+ }
190
+ const [err] = await tryCatch(
191
+ config.data.mutation(api.externalAccounts.mutations.removePersonal, {
192
+ externalAccountId
193
+ })
194
+ );
195
+ if (err) {
196
+ return [
197
+ fromConvexError(err),
198
+ null
199
+ ];
200
+ }
201
+ return [null, void 0];
202
+ }
203
+ };
204
+ }
205
+
72
206
  // src/core/accounts.ts
207
+ function createAccountExternalAccountsClient(config) {
208
+ return {
209
+ create: async (input) => {
210
+ if (!config.data) {
211
+ return stub(
212
+ "accounts.externalAccounts.create"
213
+ );
214
+ }
215
+ const [err, raw] = await tryCatch(
216
+ config.data.mutation(
217
+ api.externalAccounts.mutations.createPersonal,
218
+ {
219
+ kind: input.kind,
220
+ label: input.label,
221
+ address: input.address,
222
+ iban: input.iban,
223
+ bic: input.bic,
224
+ accountHolder: input.accountHolder,
225
+ network: input.network,
226
+ panToken: input.panToken,
227
+ last4: input.last4
228
+ }
229
+ )
230
+ );
231
+ if (err) {
232
+ return [
233
+ fromConvexError(err),
234
+ null
235
+ ];
236
+ }
237
+ if (!raw) {
238
+ return [
239
+ new CapxulError({
240
+ code: "NOT_FOUND",
241
+ message: "external_account creation returned no resource"
242
+ }),
243
+ null
244
+ ];
245
+ }
246
+ return [
247
+ null,
248
+ brandExternalAccount(
249
+ raw
250
+ )
251
+ ];
252
+ },
253
+ list: async (input) => {
254
+ if (!config.data) {
255
+ return stub(
256
+ "accounts.externalAccounts.list"
257
+ );
258
+ }
259
+ const [err, result] = await tryCatch(
260
+ config.data.query(api.externalAccounts.queries.listPersonal, {
261
+ limit: input.limit,
262
+ cursor: input.cursor
263
+ })
264
+ );
265
+ if (err) {
266
+ return [fromConvexError(err), null];
267
+ }
268
+ const branded = result.data.map(
269
+ (row) => brandExternalAccount(
270
+ row
271
+ )
272
+ );
273
+ return [
274
+ null,
275
+ {
276
+ object: "list",
277
+ data: branded,
278
+ page: result.page
279
+ }
280
+ ];
281
+ },
282
+ retrieve: async (externalAccountId) => {
283
+ if (!config.data) {
284
+ return stub(
285
+ "accounts.externalAccounts.retrieve"
286
+ );
287
+ }
288
+ const [err, raw] = await tryCatch(
289
+ config.data.query(api.externalAccounts.queries.retrievePersonal, {
290
+ externalAccountId
291
+ })
292
+ );
293
+ if (err) {
294
+ return [
295
+ fromConvexError(err),
296
+ null
297
+ ];
298
+ }
299
+ if (!raw) {
300
+ return [
301
+ new CapxulError({
302
+ code: "NOT_FOUND",
303
+ message: `external_account ${externalAccountId} not found`
304
+ }),
305
+ null
306
+ ];
307
+ }
308
+ return [
309
+ null,
310
+ brandExternalAccount(
311
+ raw
312
+ )
313
+ ];
314
+ },
315
+ remove: async (externalAccountId) => {
316
+ if (!config.data) {
317
+ return stub("accounts.externalAccounts.remove");
318
+ }
319
+ const [err] = await tryCatch(
320
+ config.data.mutation(api.externalAccounts.mutations.removePersonal, {
321
+ externalAccountId
322
+ })
323
+ );
324
+ if (err) {
325
+ return [
326
+ fromConvexError(err),
327
+ null
328
+ ];
329
+ }
330
+ return [null, void 0];
331
+ }
332
+ };
333
+ }
73
334
  function createAccountsClient(config = {}) {
74
335
  return {
75
336
  retrieve: async (accountId) => {
@@ -224,18 +485,7 @@ function createAccountsClient(config = {}) {
224
485
  create: async () => stub("accounts.kycProfiles.create"),
225
486
  retrieve: async () => stub("accounts.kycProfiles.retrieve")
226
487
  },
227
- externalAccounts: {
228
- create: async () => stub(
229
- "accounts.externalAccounts.create"
230
- ),
231
- list: async () => stub(
232
- "accounts.externalAccounts.list"
233
- ),
234
- retrieve: async () => stub(
235
- "accounts.externalAccounts.retrieve"
236
- ),
237
- remove: async () => stub("accounts.externalAccounts.remove")
238
- },
488
+ externalAccounts: createAccountExternalAccountsClient(config),
239
489
  subAccounts: {
240
490
  create: async () => stub("accounts.subAccounts.create"),
241
491
  list: async () => stub("accounts.subAccounts.list"),
@@ -330,7 +580,29 @@ var Errors = {
330
580
  { details }
331
581
  ),
332
582
  emailDeliveryFailed: (detail) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`),
333
- internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`)
583
+ internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`),
584
+ /**
585
+ * Verification gate. Surfaced when a request hits a verification
586
+ * boundary the actor cannot cross under their current state. Two
587
+ * variants share this code:
588
+ *
589
+ * - Rail gate (Withdrawals v1 W2, #465): the resolved
590
+ * `external_account.kind` routes to a withdrawal rail (e.g.
591
+ * `fiat_offramp`, `card_payout`) that is not yet supported. Carries
592
+ * `details.rail` + `details.currentKind`.
593
+ * - KYC tier gate (legacy / future): the actor's KYC tier is below
594
+ * the required tier. Carries `details.requiredTier`.
595
+ *
596
+ * Code is shared because both expose the same UX shape ("you cannot
597
+ * proceed until verification advances"); the `details.*` keys
598
+ * differentiate the route.
599
+ */
600
+ verificationRequired: (details) => {
601
+ const message = "rail" in details ? `Withdrawal rail "${details.rail}" (kind=${details.currentKind}) is not yet supported.` : `Verification tier ${details.requiredTier} is required.`;
602
+ return new CapxulError2("VERIFICATION_REQUIRED", message, {
603
+ details: { ...details }
604
+ });
605
+ }
334
606
  };
335
607
 
336
608
  // ../config/src/safe.ts
@@ -742,14 +1014,6 @@ function createOrgDocumentsClient() {
742
1014
  };
743
1015
  }
744
1016
 
745
- // src/core/external-accounts.ts
746
- function createExternalAccountsClient() {
747
- return {
748
- retrieve: async () => stub("externalAccounts.retrieve"),
749
- remove: async () => stub("externalAccounts.remove")
750
- };
751
- }
752
-
753
1017
  // src/core/me.ts
754
1018
  function createMeClient(config = {}) {
755
1019
  return {
@@ -1197,67 +1461,68 @@ function createOrgTransfersClient() {
1197
1461
  cancel: async () => stub("organizations.transfers.cancel")
1198
1462
  };
1199
1463
  }
1200
- var EVM_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
1201
1464
  function createWithdrawalsClient(config = {}) {
1202
1465
  return {
1203
1466
  create: async (input) => {
1204
1467
  if (!config.data) {
1205
1468
  return stub("withdrawals.create");
1206
1469
  }
1207
- let created = null;
1208
- let submitted = null;
1209
- try {
1210
- created = await config.data.mutation(
1211
- api.withdrawals.mutations.create,
1212
- {
1213
- amount: input.amount,
1214
- destination: {
1215
- externalAccountId: input.destination.externalAccountId,
1216
- kind: input.destination.kind
1217
- },
1218
- source: input.source,
1219
- reference: input.reference,
1220
- idempotencyKey: input.idempotencyKey
1221
- }
1222
- );
1223
- if (!created) {
1224
- return [
1225
- new CapxulError({
1226
- code: "NETWORK_ERROR",
1227
- message: "withdrawals.create returned no withdrawal resource"
1228
- }),
1229
- null
1230
- ];
1231
- }
1232
- if (created.status !== "processing" || created.operation.status !== "processing") {
1233
- return [null, created];
1234
- }
1235
- if (input.destination.kind !== "evm") {
1236
- return [null, created];
1237
- }
1238
- if (!config.signer || !config.signing) {
1239
- return [null, created];
1240
- }
1241
- if (!EVM_ADDRESS_RE.test(input.destination.externalAccountId)) {
1242
- throw new CapxulError({
1243
- code: "INVALID_INPUT",
1244
- message: "destination.externalAccountId must be a 0x-prefixed EVM address while external_accounts resolution is pending (slice 1).",
1245
- details: { field: "destination.externalAccountId" }
1246
- });
1247
- }
1248
- const currentSigner = await config.data.query(
1249
- api.safe.queries.getMySignerAddress,
1250
- {}
1470
+ const [createErr, createdRaw] = await tryCatch(
1471
+ config.data.mutation(api.withdrawals.mutations.create, {
1472
+ amount: input.amount,
1473
+ destination: {
1474
+ externalAccountId: input.destination.externalAccountId
1475
+ },
1476
+ source: input.source,
1477
+ reference: input.reference,
1478
+ idempotencyKey: input.idempotencyKey
1479
+ })
1480
+ );
1481
+ if (createErr) {
1482
+ return [mapCreateError2(fromConvexError(createErr)), null];
1483
+ }
1484
+ const created = createdRaw;
1485
+ if (!created) {
1486
+ return [
1487
+ new CapxulError({
1488
+ code: "NETWORK_ERROR",
1489
+ message: "withdrawals.create returned no withdrawal resource"
1490
+ }),
1491
+ null
1492
+ ];
1493
+ }
1494
+ if (created.status !== "processing" || created.operation.status !== "processing") {
1495
+ return [null, created];
1496
+ }
1497
+ if (!config.signer || !config.signing) {
1498
+ return [null, created];
1499
+ }
1500
+ const [signerErr, currentSigner] = await tryCatch(
1501
+ config.data.query(api.safe.queries.getMySignerAddress, {})
1502
+ );
1503
+ if (signerErr) {
1504
+ return await handleSubmissionFailure(
1505
+ { data: config.data },
1506
+ created.id,
1507
+ mapCreateError2(fromConvexError(signerErr))
1251
1508
  );
1252
- if (!currentSigner?.address) {
1253
- throw new CapxulError({
1509
+ }
1510
+ if (!currentSigner?.address) {
1511
+ return await handleSubmissionFailure(
1512
+ { data: config.data },
1513
+ created.id,
1514
+ new CapxulError({
1254
1515
  code: "PERMISSION_DENIED",
1255
1516
  message: "No signer is registered for the authenticated account.",
1256
1517
  details: { withdrawalId: created.id }
1257
- });
1258
- }
1259
- if (viem.getAddress(currentSigner.address) !== viem.getAddress(config.signer.address)) {
1260
- throw new CapxulError({
1518
+ })
1519
+ );
1520
+ }
1521
+ if (viem.getAddress(currentSigner.address) !== viem.getAddress(config.signer.address)) {
1522
+ return await handleSubmissionFailure(
1523
+ { data: config.data },
1524
+ created.id,
1525
+ new CapxulError({
1261
1526
  code: "PERMISSION_DENIED",
1262
1527
  message: "Configured signer does not match the authenticated account signer.",
1263
1528
  details: {
@@ -1265,170 +1530,228 @@ function createWithdrawalsClient(config = {}) {
1265
1530
  expectedSignerAddress: currentSigner.address,
1266
1531
  actualSignerAddress: config.signer.address
1267
1532
  }
1268
- });
1269
- }
1270
- const submission = await config.data.query(
1271
- api.withdrawals.queries.prepareSubmission,
1272
- { withdrawalId: created.id }
1533
+ })
1273
1534
  );
1274
- if (!submission?.externalAccountId) {
1275
- throw new CapxulError({
1535
+ }
1536
+ const [prepErr, submission] = await tryCatch(
1537
+ config.data.query(api.withdrawals.queries.prepareSubmission, {
1538
+ withdrawalId: created.id
1539
+ })
1540
+ );
1541
+ if (prepErr) {
1542
+ return await handleSubmissionFailure(
1543
+ { data: config.data },
1544
+ created.id,
1545
+ mapCreateError2(fromConvexError(prepErr))
1546
+ );
1547
+ }
1548
+ const destinationAddress = submission?.destinationAddress;
1549
+ if (!submission || !destinationAddress) {
1550
+ return await handleSubmissionFailure(
1551
+ { data: config.data },
1552
+ created.id,
1553
+ new CapxulError({
1276
1554
  code: "NETWORK_ERROR",
1277
1555
  message: "withdrawals.prepareSubmission returned no destination.",
1278
1556
  details: { withdrawalId: created.id }
1279
- });
1280
- }
1281
- const transfer = await transferAsOwner(
1557
+ })
1558
+ );
1559
+ }
1560
+ const [transferErr, transferOk] = await tryCatch(
1561
+ transferAsOwner(
1282
1562
  {
1283
1563
  signer: config.signer,
1284
1564
  signing: config.signing
1285
1565
  },
1286
1566
  {
1287
1567
  tokenAddress: resolvePaymentTokenAddress(submission.amount.currency),
1288
- recipientAddress: submission.externalAccountId,
1568
+ recipientAddress: destinationAddress,
1289
1569
  amount: toTokenUnits(submission.amount.value, 6)
1290
1570
  }
1571
+ )
1572
+ );
1573
+ if (transferErr) {
1574
+ return await handleSubmissionFailure(
1575
+ { data: config.data },
1576
+ created.id,
1577
+ mapCreateError2(fromConvexError(transferErr))
1291
1578
  );
1292
- if (!transfer.success) {
1293
- throw new CapxulError({
1579
+ }
1580
+ if (!transferOk.success) {
1581
+ return await handleSubmissionFailure(
1582
+ { data: config.data },
1583
+ created.id,
1584
+ new CapxulError({
1294
1585
  code: "NETWORK_ERROR",
1295
1586
  message: "Bundler submission did not succeed.",
1296
1587
  details: {
1297
1588
  withdrawalId: created.id,
1298
- txHash: transfer.txHash,
1299
- userOpHash: transfer.userOpHash
1589
+ txHash: transferOk.txHash,
1590
+ userOpHash: transferOk.userOpHash
1300
1591
  }
1301
- });
1302
- }
1303
- submitted = {
1304
- txHash: transfer.txHash,
1305
- userOpHash: transfer.userOpHash
1306
- };
1307
- await config.data.mutation(
1308
- api.withdrawals.mutations.recordSubmitted,
1309
- {
1310
- withdrawalId: created.id,
1311
- txHash: transfer.txHash,
1312
- userOpHash: transfer.userOpHash
1313
- }
1592
+ })
1314
1593
  );
1315
- return [null, created];
1316
- } catch (cause) {
1317
- const error = mapCreateError2(fromConvexError(cause));
1318
- if (created?.id && created.status === "processing" && !submitted) {
1319
- await bestEffortMarkFailed2({ data: config.data }, created.id, error);
1320
- }
1321
- if (submitted && created?.id) {
1322
- return [
1323
- new CapxulError({
1324
- code: "NETWORK_ERROR",
1325
- message: "Withdrawal was submitted on-chain, but backend submission tracking failed.",
1326
- cause,
1327
- details: {
1328
- withdrawalId: created.id,
1329
- txHash: submitted.txHash,
1330
- userOpHash: submitted.userOpHash
1331
- }
1332
- }),
1333
- null
1334
- ];
1335
- }
1336
- return [error, null];
1337
1594
  }
1595
+ const [recordErr] = await tryCatch(
1596
+ config.data.mutation(api.withdrawals.mutations.recordSubmitted, {
1597
+ withdrawalId: created.id,
1598
+ txHash: transferOk.txHash,
1599
+ userOpHash: transferOk.userOpHash
1600
+ })
1601
+ );
1602
+ if (recordErr) {
1603
+ return [
1604
+ new CapxulError({
1605
+ code: "NETWORK_ERROR",
1606
+ message: "Withdrawal was submitted on-chain, but backend submission tracking failed.",
1607
+ cause: recordErr,
1608
+ details: {
1609
+ withdrawalId: created.id,
1610
+ txHash: transferOk.txHash,
1611
+ userOpHash: transferOk.userOpHash
1612
+ }
1613
+ }),
1614
+ null
1615
+ ];
1616
+ }
1617
+ return [null, created];
1338
1618
  },
1339
1619
  retrieve: async (withdrawalId) => {
1340
1620
  if (!config.data) {
1341
1621
  return stub("withdrawals.retrieve");
1342
1622
  }
1343
- try {
1344
- const withdrawal = await config.data.query(
1345
- api.withdrawals.queries.retrieve,
1346
- { withdrawalId }
1347
- );
1348
- if (!withdrawal) {
1349
- return [
1350
- new CapxulError({
1351
- code: "NOT_FOUND",
1352
- message: `withdrawal ${withdrawalId} not found`
1353
- }),
1354
- null
1355
- ];
1356
- }
1357
- return [null, withdrawal];
1358
- } catch (cause) {
1623
+ const [err, raw] = await tryCatch(
1624
+ config.data.query(api.withdrawals.queries.retrieve, { withdrawalId })
1625
+ );
1626
+ if (err) {
1627
+ return [fromConvexError(err), null];
1628
+ }
1629
+ const withdrawal = raw;
1630
+ if (!withdrawal) {
1359
1631
  return [
1360
- fromConvexError(cause),
1632
+ new CapxulError({
1633
+ code: "NOT_FOUND",
1634
+ message: `withdrawal ${withdrawalId} not found`
1635
+ }),
1361
1636
  null
1362
1637
  ];
1363
1638
  }
1639
+ return [null, withdrawal];
1364
1640
  },
1365
1641
  list: async (input) => {
1366
1642
  if (!config.data) {
1367
1643
  return stub("withdrawals.list");
1368
1644
  }
1369
- try {
1370
- const result = await config.data.query(
1371
- api.withdrawals.queries.list,
1372
- {
1373
- limit: input?.limit,
1374
- cursor: input?.cursor
1375
- }
1645
+ const [err, raw] = await tryCatch(
1646
+ config.data.query(api.withdrawals.queries.list, {
1647
+ limit: input?.limit,
1648
+ cursor: input?.cursor
1649
+ })
1650
+ );
1651
+ if (err) {
1652
+ return [fromConvexError(err), null];
1653
+ }
1654
+ return [null, raw];
1655
+ },
1656
+ recordCompleted: async (input) => {
1657
+ if (!config.data) {
1658
+ return stub(
1659
+ "withdrawals.recordCompleted"
1376
1660
  );
1377
- return [null, result];
1378
- } catch (cause) {
1661
+ }
1662
+ const [err] = await tryCatch(
1663
+ config.data.mutation(api.withdrawals.mutations.recordCompleted, {
1664
+ withdrawalId: input.withdrawalId,
1665
+ txHash: input.txHash
1666
+ })
1667
+ );
1668
+ if (err) {
1379
1669
  return [
1380
- fromConvexError(cause),
1670
+ mapRecordCompletedError(fromConvexError(err)),
1381
1671
  null
1382
1672
  ];
1383
1673
  }
1674
+ return [null, null];
1384
1675
  }
1385
1676
  };
1386
1677
  }
1387
1678
  function createOrgWithdrawalsClient(config = {}) {
1388
1679
  return {
1389
- // Slice 1 ships personal-scope only end-to-end; org-scope create
1390
- // remains stubbed pending org-scoped backend mutation. List + retrieve
1391
- // are wired through the org-aware query.
1392
- create: async () => stub(
1393
- "organizations.withdrawals.create"
1394
- ),
1680
+ /**
1681
+ * Org-scope create (Withdrawals v1 W2, #465).
1682
+ *
1683
+ * D6 returns the `processing` row only. No `transferAsOwner`
1684
+ * tail, no `recordSubmitted` call. Org Safe + Zodiac submission
1685
+ * orchestration ships in W3+.
1686
+ */
1687
+ create: async (input) => {
1688
+ if (!config.data) {
1689
+ return stub(
1690
+ "organizations.withdrawals.create"
1691
+ );
1692
+ }
1693
+ const [err, raw] = await tryCatch(
1694
+ config.data.mutation(api.withdrawals.mutations.createOrg, {
1695
+ organizationId: input.organizationId,
1696
+ amount: input.amount,
1697
+ destination: {
1698
+ externalAccountId: input.destination.externalAccountId
1699
+ },
1700
+ source: input.source,
1701
+ reference: input.reference,
1702
+ idempotencyKey: input.idempotencyKey
1703
+ })
1704
+ );
1705
+ if (err) {
1706
+ return [mapCreateError2(fromConvexError(err)), null];
1707
+ }
1708
+ const created = raw;
1709
+ if (!created) {
1710
+ return [
1711
+ new CapxulError({
1712
+ code: "NETWORK_ERROR",
1713
+ message: "organizations.withdrawals.create returned no withdrawal resource"
1714
+ }),
1715
+ null
1716
+ ];
1717
+ }
1718
+ return [null, created];
1719
+ },
1395
1720
  retrieve: async (input) => {
1396
1721
  if (!config.data) {
1397
1722
  return stub(
1398
1723
  "organizations.withdrawals.retrieve"
1399
1724
  );
1400
1725
  }
1401
- try {
1402
- const withdrawal = await config.data.query(
1403
- api.withdrawals.queries.retrieve,
1404
- { withdrawalId: input.withdrawalId }
1405
- );
1406
- if (!withdrawal) {
1407
- return [
1408
- new CapxulError({
1409
- code: "NOT_FOUND",
1410
- message: `withdrawal ${input.withdrawalId} not found`
1411
- }),
1412
- null
1413
- ];
1414
- }
1415
- const ownerCheck = withdrawal.owner;
1416
- if (ownerCheck?.type !== "organization" || ownerCheck.id !== input.organizationId) {
1417
- return [
1418
- new CapxulError({
1419
- code: "NOT_FOUND",
1420
- message: `withdrawal ${input.withdrawalId} does not belong to organization ${input.organizationId}`
1421
- }),
1422
- null
1423
- ];
1424
- }
1425
- return [null, withdrawal];
1426
- } catch (cause) {
1726
+ const [err, raw] = await tryCatch(
1727
+ config.data.query(api.withdrawals.queries.retrieve, {
1728
+ withdrawalId: input.withdrawalId
1729
+ })
1730
+ );
1731
+ if (err) {
1732
+ return [fromConvexError(err), null];
1733
+ }
1734
+ const withdrawal = raw;
1735
+ if (!withdrawal) {
1427
1736
  return [
1428
- fromConvexError(cause),
1737
+ new CapxulError({
1738
+ code: "NOT_FOUND",
1739
+ message: `withdrawal ${input.withdrawalId} not found`
1740
+ }),
1429
1741
  null
1430
1742
  ];
1431
1743
  }
1744
+ const ownerCheck = withdrawal.owner;
1745
+ if (ownerCheck?.type !== "organization" || ownerCheck.id !== input.organizationId) {
1746
+ return [
1747
+ new CapxulError({
1748
+ code: "NOT_FOUND",
1749
+ message: `withdrawal ${input.withdrawalId} does not belong to organization ${input.organizationId}`
1750
+ }),
1751
+ null
1752
+ ];
1753
+ }
1754
+ return [null, withdrawal];
1432
1755
  },
1433
1756
  list: async (input) => {
1434
1757
  if (!config.data) {
@@ -1436,34 +1759,32 @@ function createOrgWithdrawalsClient(config = {}) {
1436
1759
  "organizations.withdrawals.list"
1437
1760
  );
1438
1761
  }
1439
- try {
1440
- const result = await config.data.query(
1441
- api.withdrawals.queries.listOrg,
1442
- {
1443
- organizationId: input.organizationId,
1444
- limit: input.limit,
1445
- cursor: input.cursor
1446
- }
1447
- );
1448
- return [null, result];
1449
- } catch (cause) {
1450
- return [
1451
- fromConvexError(cause),
1452
- null
1453
- ];
1762
+ const [err, raw] = await tryCatch(
1763
+ config.data.query(api.withdrawals.queries.listOrg, {
1764
+ organizationId: input.organizationId,
1765
+ limit: input.limit,
1766
+ cursor: input.cursor
1767
+ })
1768
+ );
1769
+ if (err) {
1770
+ return [fromConvexError(err), null];
1454
1771
  }
1772
+ return [null, raw];
1455
1773
  }
1456
1774
  };
1457
1775
  }
1776
+ async function handleSubmissionFailure(config, withdrawalId, error) {
1777
+ await bestEffortMarkFailed2(config, withdrawalId, error);
1778
+ return [error, null];
1779
+ }
1458
1780
  async function bestEffortMarkFailed2(config, withdrawalId, error) {
1459
- try {
1460
- await config.data.mutation(api.withdrawals.mutations.markFailed, {
1781
+ await tryCatch(
1782
+ config.data.mutation(api.withdrawals.mutations.markFailed, {
1461
1783
  withdrawalId,
1462
1784
  errorCode: error.code,
1463
1785
  errorMessage: error.message
1464
- });
1465
- } catch {
1466
- }
1786
+ })
1787
+ );
1467
1788
  }
1468
1789
  function mapCreateError2(error) {
1469
1790
  switch (error.code) {
@@ -1476,6 +1797,29 @@ function mapCreateError2(error) {
1476
1797
  case "POLICY_DENIED":
1477
1798
  case "RATE_LIMITED":
1478
1799
  case "NETWORK_ERROR":
1800
+ case "NOT_FOUND":
1801
+ case "VERIFICATION_REQUIRED":
1802
+ return error;
1803
+ default:
1804
+ return new CapxulError({
1805
+ code: "NETWORK_ERROR",
1806
+ message: error.message,
1807
+ cause: error,
1808
+ details: error.details,
1809
+ operationId: error.operationId,
1810
+ correlationId: error.correlationId,
1811
+ retryable: error.retryable
1812
+ });
1813
+ }
1814
+ }
1815
+ function mapRecordCompletedError(error) {
1816
+ switch (error.code) {
1817
+ case "NOT_AUTHENTICATED":
1818
+ case "PERMISSION_DENIED":
1819
+ case "INVALID_INPUT":
1820
+ case "NOT_FOUND":
1821
+ case "NETWORK_ERROR":
1822
+ case "INTERNAL_ERROR":
1479
1823
  return error;
1480
1824
  default:
1481
1825
  return new CapxulError({
@@ -1511,6 +1855,136 @@ function createWebhookEventsClient() {
1511
1855
  }
1512
1856
 
1513
1857
  // src/core/organizations.ts
1858
+ function createOrgExternalAccountsClient(config) {
1859
+ return {
1860
+ create: async (input) => {
1861
+ if (!config.data) {
1862
+ return stub(
1863
+ "organizations.externalAccounts.create"
1864
+ );
1865
+ }
1866
+ const [err, raw] = await tryCatch(
1867
+ config.data.mutation(api.externalAccounts.mutations.createOrg, {
1868
+ organizationId: input.organizationId,
1869
+ kind: input.kind,
1870
+ label: input.label,
1871
+ address: input.address,
1872
+ iban: input.iban,
1873
+ bic: input.bic,
1874
+ accountHolder: input.accountHolder,
1875
+ network: input.network,
1876
+ panToken: input.panToken,
1877
+ last4: input.last4
1878
+ })
1879
+ );
1880
+ if (err) {
1881
+ return [
1882
+ fromConvexError(err),
1883
+ null
1884
+ ];
1885
+ }
1886
+ if (!raw) {
1887
+ return [
1888
+ new CapxulError({
1889
+ code: "NOT_FOUND",
1890
+ message: "external_account creation returned no resource"
1891
+ }),
1892
+ null
1893
+ ];
1894
+ }
1895
+ return [
1896
+ null,
1897
+ brandExternalAccount(
1898
+ raw
1899
+ )
1900
+ ];
1901
+ },
1902
+ list: async (input) => {
1903
+ if (!config.data) {
1904
+ return stub(
1905
+ "organizations.externalAccounts.list"
1906
+ );
1907
+ }
1908
+ const [err, result] = await tryCatch(
1909
+ config.data.query(api.externalAccounts.queries.listOrg, {
1910
+ organizationId: input.organizationId,
1911
+ limit: input.limit,
1912
+ cursor: input.cursor
1913
+ })
1914
+ );
1915
+ if (err) {
1916
+ return [fromConvexError(err), null];
1917
+ }
1918
+ const branded = result.data.map(
1919
+ (row) => brandExternalAccount(
1920
+ row
1921
+ )
1922
+ );
1923
+ return [
1924
+ null,
1925
+ {
1926
+ object: "list",
1927
+ data: branded,
1928
+ page: result.page
1929
+ }
1930
+ ];
1931
+ },
1932
+ retrieve: async (input) => {
1933
+ if (!config.data) {
1934
+ return stub(
1935
+ "organizations.externalAccounts.retrieve"
1936
+ );
1937
+ }
1938
+ const [err, raw] = await tryCatch(
1939
+ config.data.query(api.externalAccounts.queries.retrieveOrg, {
1940
+ organizationId: input.organizationId,
1941
+ externalAccountId: input.externalAccountId
1942
+ })
1943
+ );
1944
+ if (err) {
1945
+ return [
1946
+ fromConvexError(err),
1947
+ null
1948
+ ];
1949
+ }
1950
+ if (!raw) {
1951
+ return [
1952
+ new CapxulError({
1953
+ code: "NOT_FOUND",
1954
+ message: `external_account ${input.externalAccountId} not found`
1955
+ }),
1956
+ null
1957
+ ];
1958
+ }
1959
+ return [
1960
+ null,
1961
+ brandExternalAccount(
1962
+ raw
1963
+ )
1964
+ ];
1965
+ },
1966
+ remove: async (input) => {
1967
+ if (!config.data) {
1968
+ return stub(
1969
+ "organizations.externalAccounts.remove"
1970
+ );
1971
+ }
1972
+ const [err] = await tryCatch(
1973
+ config.data.mutation(api.externalAccounts.mutations.removeOrg, {
1974
+ organizationId: input.organizationId,
1975
+ externalAccountId: input.externalAccountId
1976
+ })
1977
+ );
1978
+ if (err) {
1979
+ return [
1980
+ fromConvexError(err),
1981
+ null
1982
+ ];
1983
+ }
1984
+ return [null, void 0];
1985
+ }
1986
+ };
1987
+ }
1514
1988
  function createOrganizationsClient(config = {}) {
1515
1989
  return {
1516
1990
  create: async () => stub("organizations.create"),
@@ -1566,18 +2040,7 @@ function createOrganizationsClient(config = {}) {
1566
2040
  retrieve: async () => stub("organizations.subAccounts.retrieve"),
1567
2041
  remove: async () => stub("organizations.subAccounts.remove")
1568
2042
  },
1569
- externalAccounts: {
1570
- create: async () => stub(
1571
- "organizations.externalAccounts.create"
1572
- ),
1573
- list: async () => stub(
1574
- "organizations.externalAccounts.list"
1575
- ),
1576
- retrieve: async () => stub(
1577
- "organizations.externalAccounts.retrieve"
1578
- ),
1579
- remove: async () => stub("organizations.externalAccounts.remove")
1580
- },
2043
+ externalAccounts: createOrgExternalAccountsClient(config),
1581
2044
  balanceLedger: {
1582
2045
  list: async () => stub(
1583
2046
  "organizations.balanceLedger.list"
@@ -1714,50 +2177,6 @@ function createVirtualCardsClient() {
1714
2177
  cancel: async () => stub("virtualCards.cancel")
1715
2178
  };
1716
2179
  }
1717
-
1718
- // ../observability/src/debug-log.ts
1719
- function isDevelopmentBuild() {
1720
- if (typeof process === "undefined") {
1721
- return false;
1722
- }
1723
- return process.env?.NODE_ENV === "development" || process.env?.NODE_ENV === "test";
1724
- }
1725
- function debugLog(line) {
1726
- if (!isDevelopmentBuild()) return;
1727
- if (typeof globalThis !== "undefined" && "window" in globalThis && typeof console?.info === "function") {
1728
- console.info(line);
1729
- return;
1730
- }
1731
- if (typeof process !== "undefined" && typeof process.stderr?.write === "function") {
1732
- process.stderr.write(`${line}
1733
- `);
1734
- }
1735
- }
1736
- function formatDebugValue(value) {
1737
- if (value === void 0 || value === "") return "";
1738
- if (typeof value === "string") return value;
1739
- try {
1740
- return JSON.stringify(value);
1741
- } catch {
1742
- return String(value);
1743
- }
1744
- }
1745
- function track(...args) {
1746
- const [name, props] = args;
1747
- debugLog(`[TRACK] ${name} ${formatDebugValue(props ?? "")}`);
1748
- }
1749
- function formatDebugValue2(value) {
1750
- if (value === void 0 || value === "") return "";
1751
- if (typeof value === "string") return value;
1752
- try {
1753
- return JSON.stringify(value);
1754
- } catch {
1755
- return String(value);
1756
- }
1757
- }
1758
- function identify(userId, traits) {
1759
- debugLog(`[IDENTIFY] ${userId} ${formatDebugValue2(traits ?? "")}`);
1760
- }
1761
2180
  function createAuthFlowMachine(client) {
1762
2181
  return xstate.setup({
1763
2182
  types: {},
@@ -2033,23 +2452,6 @@ function emailDomain(email) {
2033
2452
  const domain = email.split("@")[1]?.trim().toLowerCase();
2034
2453
  return domain || "unknown";
2035
2454
  }
2036
-
2037
- // ../platform-kernel/src/ids.ts
2038
- function makePrefixedIdConstructor(prefix, fieldName) {
2039
- const re = new RegExp(`^${prefix}_[A-Za-z0-9_\\-]+$`);
2040
- return (raw) => {
2041
- if (typeof raw !== "string" || !re.test(raw)) {
2042
- throw new Error(
2043
- `Invalid ${fieldName}: expected string matching ^${prefix}_[A-Za-z0-9_\\-]+$, got ${String(raw)}`
2044
- );
2045
- }
2046
- return raw;
2047
- };
2048
- }
2049
- var toOperationId = makePrefixedIdConstructor(
2050
- "op",
2051
- "operationId"
2052
- );
2053
2455
  function createProvisioningMachine(client) {
2054
2456
  return xstate.setup({
2055
2457
  types: {},
@@ -2442,7 +2844,7 @@ function createCapxulClient(config = {}) {
2442
2844
  subAccounts: createSubAccountsClient(),
2443
2845
  virtualAccounts: createVirtualAccountsClient(),
2444
2846
  virtualCards: createVirtualCardsClient(),
2445
- externalAccounts: createExternalAccountsClient(),
2847
+ externalAccounts: createExternalAccountsClient(config),
2446
2848
  operations: createOperationsClient(config),
2447
2849
  webhookEndpoints: createWebhookEndpointsClient(),
2448
2850
  webhookEvents: createWebhookEventsClient(),