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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.cjs CHANGED
@@ -72,9 +72,79 @@ function fromConvexError(error) {
72
72
  // src/core/accounts.ts
73
73
  function createAccountsClient(config = {}) {
74
74
  return {
75
- retrieve: async () => stub("accounts.retrieve"),
75
+ retrieve: async (accountId) => {
76
+ if (!config.data) {
77
+ return stub("accounts.retrieve");
78
+ }
79
+ try {
80
+ const account = await config.data.query(
81
+ api.openfort.queries.getMyAccount,
82
+ {}
83
+ );
84
+ if (account.id !== accountId) {
85
+ return [
86
+ new CapxulError({
87
+ code: "PERMISSION_DENIED",
88
+ message: "accounts.retrieve currently supports the authenticated caller's own account only.",
89
+ details: {
90
+ requestedAccountId: accountId,
91
+ authenticatedAccountId: account.id
92
+ }
93
+ }),
94
+ null
95
+ ];
96
+ }
97
+ return [null, account];
98
+ } catch (cause) {
99
+ return [fromConvexError(cause), null];
100
+ }
101
+ },
76
102
  lookup: async () => stub("accounts.lookup"),
77
- update: async () => stub("accounts.update"),
103
+ update: async (input) => {
104
+ if (!config.data) {
105
+ return stub("accounts.update");
106
+ }
107
+ if (input.countryCode !== void 0) {
108
+ return [
109
+ new CapxulError({
110
+ code: "INVALID_INPUT",
111
+ message: "accounts.update does not support countryCode yet \u2014 backend mutation only patches displayName and username.",
112
+ details: { field: "countryCode" }
113
+ }),
114
+ null
115
+ ];
116
+ }
117
+ try {
118
+ const current = await config.data.query(
119
+ api.openfort.queries.getMyAccount,
120
+ {}
121
+ );
122
+ if (current.id !== input.accountId) {
123
+ return [
124
+ new CapxulError({
125
+ code: "PERMISSION_DENIED",
126
+ message: "accounts.update currently supports the authenticated caller's own account only.",
127
+ details: {
128
+ requestedAccountId: input.accountId,
129
+ authenticatedAccountId: current.id
130
+ }
131
+ }),
132
+ null
133
+ ];
134
+ }
135
+ await config.data.mutation(api.openfort.mutations.updateProfile, {
136
+ displayName: input.name,
137
+ username: input.username
138
+ });
139
+ const updated = await config.data.query(
140
+ api.openfort.queries.getMyAccount,
141
+ {}
142
+ );
143
+ return [null, updated];
144
+ } catch (cause) {
145
+ return [fromConvexError(cause), null];
146
+ }
147
+ },
78
148
  provisionPersonal: async (input) => {
79
149
  if (!config.data) {
80
150
  return stub(
@@ -697,7 +767,34 @@ function createMeClient(config = {}) {
697
767
  return [fromConvexError(cause), null];
698
768
  }
699
769
  },
700
- update: async () => stub("me.update")
770
+ update: async (input) => {
771
+ if (!config.data) {
772
+ return stub("me.update");
773
+ }
774
+ if (input.countryCode !== void 0) {
775
+ return [
776
+ new CapxulError({
777
+ code: "INVALID_INPUT",
778
+ message: "me.update does not support countryCode yet \u2014 backend mutation only patches displayName and username.",
779
+ details: { field: "countryCode" }
780
+ }),
781
+ null
782
+ ];
783
+ }
784
+ try {
785
+ await config.data.mutation(api.openfort.mutations.updateProfile, {
786
+ displayName: input.name,
787
+ username: input.username
788
+ });
789
+ const account = await config.data.query(
790
+ api.openfort.queries.getMyAccount,
791
+ {}
792
+ );
793
+ return [null, account];
794
+ } catch (cause) {
795
+ return [fromConvexError(cause), null];
796
+ }
797
+ }
701
798
  };
702
799
  }
703
800
 
@@ -1100,24 +1197,298 @@ function createOrgTransfersClient() {
1100
1197
  cancel: async () => stub("organizations.transfers.cancel")
1101
1198
  };
1102
1199
  }
1103
-
1104
- // src/core/withdrawals.ts
1105
- function createWithdrawalsClient() {
1200
+ var EVM_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
1201
+ function createWithdrawalsClient(config = {}) {
1106
1202
  return {
1107
- create: async () => stub("withdrawals.create"),
1108
- retrieve: async () => stub("withdrawals.retrieve"),
1109
- list: async () => stub("withdrawals.list")
1203
+ create: async (input) => {
1204
+ if (!config.data) {
1205
+ return stub("withdrawals.create");
1206
+ }
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
+ {}
1251
+ );
1252
+ if (!currentSigner?.address) {
1253
+ throw new CapxulError({
1254
+ code: "PERMISSION_DENIED",
1255
+ message: "No signer is registered for the authenticated account.",
1256
+ details: { withdrawalId: created.id }
1257
+ });
1258
+ }
1259
+ if (viem.getAddress(currentSigner.address) !== viem.getAddress(config.signer.address)) {
1260
+ throw new CapxulError({
1261
+ code: "PERMISSION_DENIED",
1262
+ message: "Configured signer does not match the authenticated account signer.",
1263
+ details: {
1264
+ withdrawalId: created.id,
1265
+ expectedSignerAddress: currentSigner.address,
1266
+ actualSignerAddress: config.signer.address
1267
+ }
1268
+ });
1269
+ }
1270
+ const submission = await config.data.query(
1271
+ api.withdrawals.queries.prepareSubmission,
1272
+ { withdrawalId: created.id }
1273
+ );
1274
+ if (!submission?.externalAccountId) {
1275
+ throw new CapxulError({
1276
+ code: "NETWORK_ERROR",
1277
+ message: "withdrawals.prepareSubmission returned no destination.",
1278
+ details: { withdrawalId: created.id }
1279
+ });
1280
+ }
1281
+ const transfer = await transferAsOwner(
1282
+ {
1283
+ signer: config.signer,
1284
+ signing: config.signing
1285
+ },
1286
+ {
1287
+ tokenAddress: resolvePaymentTokenAddress(submission.amount.currency),
1288
+ recipientAddress: submission.externalAccountId,
1289
+ amount: toTokenUnits(submission.amount.value, 6)
1290
+ }
1291
+ );
1292
+ if (!transfer.success) {
1293
+ throw new CapxulError({
1294
+ code: "NETWORK_ERROR",
1295
+ message: "Bundler submission did not succeed.",
1296
+ details: {
1297
+ withdrawalId: created.id,
1298
+ txHash: transfer.txHash,
1299
+ userOpHash: transfer.userOpHash
1300
+ }
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
+ }
1314
+ );
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
+ }
1338
+ },
1339
+ retrieve: async (withdrawalId) => {
1340
+ if (!config.data) {
1341
+ return stub("withdrawals.retrieve");
1342
+ }
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) {
1359
+ return [
1360
+ fromConvexError(cause),
1361
+ null
1362
+ ];
1363
+ }
1364
+ },
1365
+ list: async (input) => {
1366
+ if (!config.data) {
1367
+ return stub("withdrawals.list");
1368
+ }
1369
+ try {
1370
+ const result = await config.data.query(
1371
+ api.withdrawals.queries.list,
1372
+ {
1373
+ limit: input?.limit,
1374
+ cursor: input?.cursor
1375
+ }
1376
+ );
1377
+ return [null, result];
1378
+ } catch (cause) {
1379
+ return [
1380
+ fromConvexError(cause),
1381
+ null
1382
+ ];
1383
+ }
1384
+ }
1110
1385
  };
1111
1386
  }
1112
- function createOrgWithdrawalsClient() {
1387
+ function createOrgWithdrawalsClient(config = {}) {
1113
1388
  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.
1114
1392
  create: async () => stub(
1115
1393
  "organizations.withdrawals.create"
1116
1394
  ),
1117
- retrieve: async () => stub("organizations.withdrawals.retrieve"),
1118
- list: async () => stub("organizations.withdrawals.list")
1395
+ retrieve: async (input) => {
1396
+ if (!config.data) {
1397
+ return stub(
1398
+ "organizations.withdrawals.retrieve"
1399
+ );
1400
+ }
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) {
1427
+ return [
1428
+ fromConvexError(cause),
1429
+ null
1430
+ ];
1431
+ }
1432
+ },
1433
+ list: async (input) => {
1434
+ if (!config.data) {
1435
+ return stub(
1436
+ "organizations.withdrawals.list"
1437
+ );
1438
+ }
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
+ ];
1454
+ }
1455
+ }
1119
1456
  };
1120
1457
  }
1458
+ async function bestEffortMarkFailed2(config, withdrawalId, error) {
1459
+ try {
1460
+ await config.data.mutation(api.withdrawals.mutations.markFailed, {
1461
+ withdrawalId,
1462
+ errorCode: error.code,
1463
+ errorMessage: error.message
1464
+ });
1465
+ } catch {
1466
+ }
1467
+ }
1468
+ function mapCreateError2(error) {
1469
+ switch (error.code) {
1470
+ case "NOT_AUTHENTICATED":
1471
+ case "PERMISSION_DENIED":
1472
+ case "INVALID_INPUT":
1473
+ case "INSUFFICIENT_BALANCE":
1474
+ case "IDEMPOTENCY_CONFLICT":
1475
+ case "KYC_REQUIRED":
1476
+ case "POLICY_DENIED":
1477
+ case "RATE_LIMITED":
1478
+ case "NETWORK_ERROR":
1479
+ return error;
1480
+ default:
1481
+ return new CapxulError({
1482
+ code: "NETWORK_ERROR",
1483
+ message: error.message,
1484
+ cause: error,
1485
+ details: error.details,
1486
+ operationId: error.operationId,
1487
+ correlationId: error.correlationId,
1488
+ retryable: error.retryable
1489
+ });
1490
+ }
1491
+ }
1121
1492
 
1122
1493
  // src/core/webhook-endpoints.ts
1123
1494
  function createWebhookEndpointsClient() {
@@ -1217,7 +1588,7 @@ function createOrganizationsClient(config = {}) {
1217
1588
  },
1218
1589
  payments: createOrgPaymentsClient(),
1219
1590
  transfers: createOrgTransfersClient(),
1220
- withdrawals: createOrgWithdrawalsClient(),
1591
+ withdrawals: createOrgWithdrawalsClient(config),
1221
1592
  documents: createOrgDocumentsClient(),
1222
1593
  webhookEndpoints: createWebhookEndpointsClient(),
1223
1594
  webhookEvents: createWebhookEventsClient()
@@ -1232,6 +1603,96 @@ function createSubAccountsClient() {
1232
1603
  };
1233
1604
  }
1234
1605
 
1606
+ // src/core/token-transfers.ts
1607
+ var toTokenTransferId = (raw) => {
1608
+ if (typeof raw !== "string" || raw.length === 0) {
1609
+ throw new Error(
1610
+ `Invalid tokenTransferId: expected non-empty string, got ${String(raw)}`
1611
+ );
1612
+ }
1613
+ return raw;
1614
+ };
1615
+ function brandRow(row) {
1616
+ return {
1617
+ ...row,
1618
+ id: toTokenTransferId(row.id)
1619
+ };
1620
+ }
1621
+ function createTokenTransfersClient(config = {}) {
1622
+ return {
1623
+ list: async (input) => {
1624
+ if (!config.data) {
1625
+ return stub("tokenTransfers.list");
1626
+ }
1627
+ try {
1628
+ const raw = await config.data.query(
1629
+ api.tokenTransfers.queries.list,
1630
+ {
1631
+ limit: input?.limit,
1632
+ cursor: input?.cursor,
1633
+ direction: input?.direction
1634
+ }
1635
+ );
1636
+ if (!raw) {
1637
+ return [
1638
+ new CapxulError({
1639
+ code: "NOT_AUTHENTICATED",
1640
+ message: "tokenTransfers.list requires an authenticated session."
1641
+ }),
1642
+ null
1643
+ ];
1644
+ }
1645
+ return [
1646
+ null,
1647
+ {
1648
+ object: "list",
1649
+ data: raw.items.map(brandRow),
1650
+ page: {
1651
+ hasMore: raw.hasMore,
1652
+ nextCursor: raw.nextCursor
1653
+ },
1654
+ displayCurrency: raw.displayCurrency
1655
+ }
1656
+ ];
1657
+ } catch (cause) {
1658
+ return [fromConvexError(cause), null];
1659
+ }
1660
+ },
1661
+ retrieve: async (input) => {
1662
+ if (!config.data) {
1663
+ return stub("tokenTransfers.retrieve");
1664
+ }
1665
+ try {
1666
+ const raw = await config.data.query(
1667
+ api.tokenTransfers.queries.getByTxLogIndex,
1668
+ {
1669
+ txHash: input.txHash,
1670
+ logIndex: input.logIndex,
1671
+ chainId: input.chainId
1672
+ }
1673
+ );
1674
+ if (!raw) {
1675
+ return [
1676
+ new CapxulError({
1677
+ code: "NOT_FOUND",
1678
+ message: `tokenTransfer (txHash=${input.txHash}, logIndex=${input.logIndex}) not found or not visible to caller.`,
1679
+ details: {
1680
+ txHash: input.txHash,
1681
+ logIndex: input.logIndex,
1682
+ chainId: input.chainId
1683
+ }
1684
+ }),
1685
+ null
1686
+ ];
1687
+ }
1688
+ return [null, brandRow(raw)];
1689
+ } catch (cause) {
1690
+ return [fromConvexError(cause), null];
1691
+ }
1692
+ }
1693
+ };
1694
+ }
1695
+
1235
1696
  // src/core/virtual-accounts.ts
1236
1697
  function createVirtualAccountsClient() {
1237
1698
  return {
@@ -1975,7 +2436,8 @@ function createCapxulClient(config = {}) {
1975
2436
  organizations: createOrganizationsClient(config),
1976
2437
  payments: createPaymentsClient(config),
1977
2438
  transfers: createTransfersClient(),
1978
- withdrawals: createWithdrawalsClient(),
2439
+ tokenTransfers: createTokenTransfersClient(config),
2440
+ withdrawals: createWithdrawalsClient(config),
1979
2441
  documents: createDocumentsClient(),
1980
2442
  subAccounts: createSubAccountsClient(),
1981
2443
  virtualAccounts: createVirtualAccountsClient(),
package/dist/client.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import 'viem';
2
2
  import 'xstate';
3
- export { f as CapxulAuthConfig, C as CapxulClient, g as CapxulConfig, h as CapxulDataClient, i as CapxulFlowFactories, j as CapxulSigningConfig, y as createCapxulClient } from './client-CwcnMbGv.cjs';
3
+ export { f as CapxulAuthConfig, C as CapxulClient, g as CapxulConfig, h as CapxulDataClient, i as CapxulFlowFactories, j as CapxulSigningConfig, K as createCapxulClient } from './client-CXRKtbfn.cjs';
4
4
  import './next-action-DkrwXYay.cjs';
5
5
  import './errors-GgKrSUKp.cjs';
6
- import './types-z4VLz9rS.cjs';
6
+ import './types-DVWojoy4.cjs';
7
7
  import '@repo/api-contract/gen/types';
package/dist/client.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import 'viem';
2
2
  import 'xstate';
3
- export { f as CapxulAuthConfig, C as CapxulClient, g as CapxulConfig, h as CapxulDataClient, i as CapxulFlowFactories, j as CapxulSigningConfig, y as createCapxulClient } from './client-BI863O0y.js';
3
+ export { f as CapxulAuthConfig, C as CapxulClient, g as CapxulConfig, h as CapxulDataClient, i as CapxulFlowFactories, j as CapxulSigningConfig, K as createCapxulClient } from './client-CcCtq5XO.js';
4
4
  import './next-action-DkrwXYay.js';
5
5
  import './errors-QHD5Tlok.js';
6
- import './types-BE3tVIOI.js';
6
+ import './types-75UokagF.js';
7
7
  import '@repo/api-contract/gen/types';