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