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

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/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @capxul/sdk
2
2
 
3
+ ## 0.1.0-alpha.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Re-export `tryCatch` from `@capxul/sdk`.
8
+
9
+ `tryCatch` is referenced in `packages/sdk/README.md` (live on npm at
10
+ `0.1.0-alpha.2`) and in `.claude/rules/sdk.md` as the canonical
11
+ async-error helper for SDK operations:
12
+
13
+ ```ts
14
+ import { tryCatch } from "@capxul/sdk";
15
+
16
+ const [err, payment] = await tryCatch(capxul.payments.create({ ... }));
17
+ ```
18
+
19
+ The helper lives at `@repo/observability/try-catch.ts` and is bundled
20
+ into the SDK dist via tsup `noExternal`, but it was never re-exported
21
+ from `packages/sdk/src/index.ts`. Consumers following the README example
22
+ hit `Module '"@capxul/sdk"' has no exported member 'tryCatch'.` at
23
+ import time.
24
+
25
+ Caught during alpha.2 spike-install verification (REVIEW.html, "What's
26
+ not proven" row 6). One-line public-export addition; no runtime change.
27
+
3
28
  ## 0.1.0-alpha.2
4
29
 
5
30
  ### Patch Changes
@@ -2,7 +2,7 @@ import { Account as Account$1 } from 'viem';
2
2
  import { AnyStateMachine } from 'xstate';
3
3
  import { A as AccountId, S as SafeId, c as KycProfileId, b as ExternalAccountId, f as SubAccountId, d as OrganizationId, a as ApiKeyId, X as TimestampIso, D as DocumentId, O as OperationId, P as PaymentId, g as TransferId, k as WithdrawalId, W as WebhookEndpointId, j as WebhookEventId, M as MemberId, K as KybProfileId, V as VirtualAccountId, i as VirtualCardId } from './next-action-DkrwXYay.js';
4
4
  import { d as CapxulResult, C as CapxulError } from './errors-QHD5Tlok.js';
5
- import { A as Account, U as UserIdentifier, a as AccountLookupResult, S as Safe, i as KycProfile, E as ExternalAccount, L as List, v as SubAccount, B as BalanceLedgerEntry, b as ApiKey, D as Document, O as Operation, l as OperationStatus, k as Money, C as CreatePaymentResult, o as Payment, F as TransferEndpoint, f as CreateTransferResult, y as Transfer, g as CreateWithdrawalResult, _ as Withdrawal, Y as WebhookEndpoint, Z as WebhookEvent, n as Organization, J as Treasury, M as Member, K as KybProfile, V as VirtualAccount, Q as VirtualCard } from './types-75UokagF.js';
5
+ import { A as Account, U as UserIdentifier, a as AccountLookupResult, S as Safe, i as KycProfile, E as ExternalAccount, L as List, v as SubAccount, B as BalanceLedgerEntry, b as ApiKey, D as Document, O as Operation, l as OperationStatus, k as Money, C as CreatePaymentResult, o as Payment, F as TransferEndpoint, f as CreateTransferResult, y as Transfer, g as CreateWithdrawalResult, _ as Withdrawal, Y as WebhookEndpoint, Z as WebhookEvent, n as Organization, J as Treasury, M as Member, K as KybProfile, V as VirtualAccount, Q as VirtualCard } from './types-BE3tVIOI.js';
6
6
  import * as types from '@repo/api-contract/gen/types';
7
7
 
8
8
  /**
@@ -473,35 +473,13 @@ type OrgTransfersClient = {
473
473
 
474
474
  /**
475
475
  * Withdrawals domain — funds exit Capxul via an `external_account` per
476
- * sdk-surface.md §1a + §4.52 + withdrawal-orchestration.mdx (slice 1
477
- * of Withdrawals v1, #440).
478
- *
479
- * Slice 1 NOTE — transitional input shape:
480
- * Until the `external_accounts` resource lands (slice 1.x or v2), the
481
- * destination input includes a `kind` field so the backend + SDK can
482
- * route to the chain_wallet rail without a real external_accounts
483
- * lookup. When the resource lands, `kind` becomes optional / ignored
484
- * and the kind is inferred from the resolved row.
485
- *
486
- * Slice 1 NOTE — chain_wallet rail integration:
487
- * For evm destinations, the `externalAccountId` is treated as the
488
- * destination address (must start with `0x`) so the chain_wallet rail
489
- * can call `transferAsOwner` directly. For solana / starknet the SDK
490
- * accepts the create call but does NOT execute the rail submission —
491
- * the row stays in `processing` until external_accounts lands. The
492
- * caller can transition it via `markFailed` if needed.
476
+ * sdk-surface.md §1a + §4.52.
493
477
  */
494
478
 
495
479
  type WithdrawalsCreateInput = {
496
480
  readonly amount: Money;
497
481
  readonly destination: {
498
482
  readonly externalAccountId: ExternalAccountId;
499
- /**
500
- * Slice 1 transitional field. Required until the
501
- * `external_accounts` resource lands; will become optional / ignored
502
- * when the resource is the source of truth for kind.
503
- */
504
- readonly kind: "evm" | "solana" | "starknet" | "bank" | "momo" | "card_payout";
505
483
  };
506
484
  readonly source?: {
507
485
  readonly subAccountId: SubAccountId;
@@ -2,7 +2,7 @@ import { Account as Account$1 } from 'viem';
2
2
  import { AnyStateMachine } from 'xstate';
3
3
  import { A as AccountId, S as SafeId, c as KycProfileId, b as ExternalAccountId, f as SubAccountId, d as OrganizationId, a as ApiKeyId, X as TimestampIso, D as DocumentId, O as OperationId, P as PaymentId, g as TransferId, k as WithdrawalId, W as WebhookEndpointId, j as WebhookEventId, M as MemberId, K as KybProfileId, V as VirtualAccountId, i as VirtualCardId } from './next-action-DkrwXYay.cjs';
4
4
  import { d as CapxulResult, C as CapxulError } from './errors-GgKrSUKp.cjs';
5
- import { A as Account, U as UserIdentifier, a as AccountLookupResult, S as Safe, i as KycProfile, E as ExternalAccount, L as List, v as SubAccount, B as BalanceLedgerEntry, b as ApiKey, D as Document, O as Operation, l as OperationStatus, k as Money, C as CreatePaymentResult, o as Payment, F as TransferEndpoint, f as CreateTransferResult, y as Transfer, g as CreateWithdrawalResult, _ as Withdrawal, Y as WebhookEndpoint, Z as WebhookEvent, n as Organization, J as Treasury, M as Member, K as KybProfile, V as VirtualAccount, Q as VirtualCard } from './types-DVWojoy4.cjs';
5
+ import { A as Account, U as UserIdentifier, a as AccountLookupResult, S as Safe, i as KycProfile, E as ExternalAccount, L as List, v as SubAccount, B as BalanceLedgerEntry, b as ApiKey, D as Document, O as Operation, l as OperationStatus, k as Money, C as CreatePaymentResult, o as Payment, F as TransferEndpoint, f as CreateTransferResult, y as Transfer, g as CreateWithdrawalResult, _ as Withdrawal, Y as WebhookEndpoint, Z as WebhookEvent, n as Organization, J as Treasury, M as Member, K as KybProfile, V as VirtualAccount, Q as VirtualCard } from './types-z4VLz9rS.cjs';
6
6
  import * as types from '@repo/api-contract/gen/types';
7
7
 
8
8
  /**
@@ -473,35 +473,13 @@ type OrgTransfersClient = {
473
473
 
474
474
  /**
475
475
  * Withdrawals domain — funds exit Capxul via an `external_account` per
476
- * sdk-surface.md §1a + §4.52 + withdrawal-orchestration.mdx (slice 1
477
- * of Withdrawals v1, #440).
478
- *
479
- * Slice 1 NOTE — transitional input shape:
480
- * Until the `external_accounts` resource lands (slice 1.x or v2), the
481
- * destination input includes a `kind` field so the backend + SDK can
482
- * route to the chain_wallet rail without a real external_accounts
483
- * lookup. When the resource lands, `kind` becomes optional / ignored
484
- * and the kind is inferred from the resolved row.
485
- *
486
- * Slice 1 NOTE — chain_wallet rail integration:
487
- * For evm destinations, the `externalAccountId` is treated as the
488
- * destination address (must start with `0x`) so the chain_wallet rail
489
- * can call `transferAsOwner` directly. For solana / starknet the SDK
490
- * accepts the create call but does NOT execute the rail submission —
491
- * the row stays in `processing` until external_accounts lands. The
492
- * caller can transition it via `markFailed` if needed.
476
+ * sdk-surface.md §1a + §4.52.
493
477
  */
494
478
 
495
479
  type WithdrawalsCreateInput = {
496
480
  readonly amount: Money;
497
481
  readonly destination: {
498
482
  readonly externalAccountId: ExternalAccountId;
499
- /**
500
- * Slice 1 transitional field. Required until the
501
- * `external_accounts` resource lands; will become optional / ignored
502
- * when the resource is the source of truth for kind.
503
- */
504
- readonly kind: "evm" | "solana" | "starknet" | "bank" | "momo" | "card_payout";
505
483
  };
506
484
  readonly source?: {
507
485
  readonly subAccountId: SubAccountId;
package/dist/client.cjs CHANGED
@@ -1100,298 +1100,24 @@ function createOrgTransfersClient() {
1100
1100
  cancel: async () => stub("organizations.transfers.cancel")
1101
1101
  };
1102
1102
  }
1103
- var EVM_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
1104
- function createWithdrawalsClient(config = {}) {
1103
+
1104
+ // src/core/withdrawals.ts
1105
+ function createWithdrawalsClient() {
1105
1106
  return {
1106
- create: async (input) => {
1107
- if (!config.data) {
1108
- return stub("withdrawals.create");
1109
- }
1110
- let created = null;
1111
- let submitted = null;
1112
- try {
1113
- created = await config.data.mutation(
1114
- api.withdrawals.mutations.create,
1115
- {
1116
- amount: input.amount,
1117
- destination: {
1118
- externalAccountId: input.destination.externalAccountId,
1119
- kind: input.destination.kind
1120
- },
1121
- source: input.source,
1122
- reference: input.reference,
1123
- idempotencyKey: input.idempotencyKey
1124
- }
1125
- );
1126
- if (!created) {
1127
- return [
1128
- new CapxulError({
1129
- code: "NETWORK_ERROR",
1130
- message: "withdrawals.create returned no withdrawal resource"
1131
- }),
1132
- null
1133
- ];
1134
- }
1135
- if (created.status !== "processing" || created.operation.status !== "processing") {
1136
- return [null, created];
1137
- }
1138
- if (input.destination.kind !== "evm") {
1139
- return [null, created];
1140
- }
1141
- if (!config.signer || !config.signing) {
1142
- return [null, created];
1143
- }
1144
- if (!EVM_ADDRESS_RE.test(input.destination.externalAccountId)) {
1145
- throw new CapxulError({
1146
- code: "INVALID_INPUT",
1147
- message: "destination.externalAccountId must be a 0x-prefixed EVM address while external_accounts resolution is pending (slice 1).",
1148
- details: { field: "destination.externalAccountId" }
1149
- });
1150
- }
1151
- const currentSigner = await config.data.query(
1152
- api.safe.queries.getMySignerAddress,
1153
- {}
1154
- );
1155
- if (!currentSigner?.address) {
1156
- throw new CapxulError({
1157
- code: "PERMISSION_DENIED",
1158
- message: "No signer is registered for the authenticated account.",
1159
- details: { withdrawalId: created.id }
1160
- });
1161
- }
1162
- if (viem.getAddress(currentSigner.address) !== viem.getAddress(config.signer.address)) {
1163
- throw new CapxulError({
1164
- code: "PERMISSION_DENIED",
1165
- message: "Configured signer does not match the authenticated account signer.",
1166
- details: {
1167
- withdrawalId: created.id,
1168
- expectedSignerAddress: currentSigner.address,
1169
- actualSignerAddress: config.signer.address
1170
- }
1171
- });
1172
- }
1173
- const submission = await config.data.query(
1174
- api.withdrawals.queries.prepareSubmission,
1175
- { withdrawalId: created.id }
1176
- );
1177
- if (!submission?.externalAccountId) {
1178
- throw new CapxulError({
1179
- code: "NETWORK_ERROR",
1180
- message: "withdrawals.prepareSubmission returned no destination.",
1181
- details: { withdrawalId: created.id }
1182
- });
1183
- }
1184
- const transfer = await transferAsOwner(
1185
- {
1186
- signer: config.signer,
1187
- signing: config.signing
1188
- },
1189
- {
1190
- tokenAddress: resolvePaymentTokenAddress(submission.amount.currency),
1191
- recipientAddress: submission.externalAccountId,
1192
- amount: toTokenUnits(submission.amount.value, 6)
1193
- }
1194
- );
1195
- if (!transfer.success) {
1196
- throw new CapxulError({
1197
- code: "NETWORK_ERROR",
1198
- message: "Bundler submission did not succeed.",
1199
- details: {
1200
- withdrawalId: created.id,
1201
- txHash: transfer.txHash,
1202
- userOpHash: transfer.userOpHash
1203
- }
1204
- });
1205
- }
1206
- submitted = {
1207
- txHash: transfer.txHash,
1208
- userOpHash: transfer.userOpHash
1209
- };
1210
- await config.data.mutation(
1211
- api.withdrawals.mutations.recordSubmitted,
1212
- {
1213
- withdrawalId: created.id,
1214
- txHash: transfer.txHash,
1215
- userOpHash: transfer.userOpHash
1216
- }
1217
- );
1218
- return [null, created];
1219
- } catch (cause) {
1220
- const error = mapCreateError2(fromConvexError(cause));
1221
- if (created?.id && created.status === "processing" && !submitted) {
1222
- await bestEffortMarkFailed2({ data: config.data }, created.id, error);
1223
- }
1224
- if (submitted && created?.id) {
1225
- return [
1226
- new CapxulError({
1227
- code: "NETWORK_ERROR",
1228
- message: "Withdrawal was submitted on-chain, but backend submission tracking failed.",
1229
- cause,
1230
- details: {
1231
- withdrawalId: created.id,
1232
- txHash: submitted.txHash,
1233
- userOpHash: submitted.userOpHash
1234
- }
1235
- }),
1236
- null
1237
- ];
1238
- }
1239
- return [error, null];
1240
- }
1241
- },
1242
- retrieve: async (withdrawalId) => {
1243
- if (!config.data) {
1244
- return stub("withdrawals.retrieve");
1245
- }
1246
- try {
1247
- const withdrawal = await config.data.query(
1248
- api.withdrawals.queries.retrieve,
1249
- { withdrawalId }
1250
- );
1251
- if (!withdrawal) {
1252
- return [
1253
- new CapxulError({
1254
- code: "NOT_FOUND",
1255
- message: `withdrawal ${withdrawalId} not found`
1256
- }),
1257
- null
1258
- ];
1259
- }
1260
- return [null, withdrawal];
1261
- } catch (cause) {
1262
- return [
1263
- fromConvexError(cause),
1264
- null
1265
- ];
1266
- }
1267
- },
1268
- list: async (input) => {
1269
- if (!config.data) {
1270
- return stub("withdrawals.list");
1271
- }
1272
- try {
1273
- const result = await config.data.query(
1274
- api.withdrawals.queries.list,
1275
- {
1276
- limit: input?.limit,
1277
- cursor: input?.cursor
1278
- }
1279
- );
1280
- return [null, result];
1281
- } catch (cause) {
1282
- return [
1283
- fromConvexError(cause),
1284
- null
1285
- ];
1286
- }
1287
- }
1107
+ create: async () => stub("withdrawals.create"),
1108
+ retrieve: async () => stub("withdrawals.retrieve"),
1109
+ list: async () => stub("withdrawals.list")
1288
1110
  };
1289
1111
  }
1290
- function createOrgWithdrawalsClient(config = {}) {
1112
+ function createOrgWithdrawalsClient() {
1291
1113
  return {
1292
- // Slice 1 ships personal-scope only end-to-end; org-scope create
1293
- // remains stubbed pending org-scoped backend mutation. List + retrieve
1294
- // are wired through the org-aware query.
1295
1114
  create: async () => stub(
1296
1115
  "organizations.withdrawals.create"
1297
1116
  ),
1298
- retrieve: async (input) => {
1299
- if (!config.data) {
1300
- return stub(
1301
- "organizations.withdrawals.retrieve"
1302
- );
1303
- }
1304
- try {
1305
- const withdrawal = await config.data.query(
1306
- api.withdrawals.queries.retrieve,
1307
- { withdrawalId: input.withdrawalId }
1308
- );
1309
- if (!withdrawal) {
1310
- return [
1311
- new CapxulError({
1312
- code: "NOT_FOUND",
1313
- message: `withdrawal ${input.withdrawalId} not found`
1314
- }),
1315
- null
1316
- ];
1317
- }
1318
- const ownerCheck = withdrawal.owner;
1319
- if (ownerCheck?.type !== "organization" || ownerCheck.id !== input.organizationId) {
1320
- return [
1321
- new CapxulError({
1322
- code: "NOT_FOUND",
1323
- message: `withdrawal ${input.withdrawalId} does not belong to organization ${input.organizationId}`
1324
- }),
1325
- null
1326
- ];
1327
- }
1328
- return [null, withdrawal];
1329
- } catch (cause) {
1330
- return [
1331
- fromConvexError(cause),
1332
- null
1333
- ];
1334
- }
1335
- },
1336
- list: async (input) => {
1337
- if (!config.data) {
1338
- return stub(
1339
- "organizations.withdrawals.list"
1340
- );
1341
- }
1342
- try {
1343
- const result = await config.data.query(
1344
- api.withdrawals.queries.listOrg,
1345
- {
1346
- organizationId: input.organizationId,
1347
- limit: input.limit,
1348
- cursor: input.cursor
1349
- }
1350
- );
1351
- return [null, result];
1352
- } catch (cause) {
1353
- return [
1354
- fromConvexError(cause),
1355
- null
1356
- ];
1357
- }
1358
- }
1117
+ retrieve: async () => stub("organizations.withdrawals.retrieve"),
1118
+ list: async () => stub("organizations.withdrawals.list")
1359
1119
  };
1360
1120
  }
1361
- async function bestEffortMarkFailed2(config, withdrawalId, error) {
1362
- try {
1363
- await config.data.mutation(api.withdrawals.mutations.markFailed, {
1364
- withdrawalId,
1365
- errorCode: error.code,
1366
- errorMessage: error.message
1367
- });
1368
- } catch {
1369
- }
1370
- }
1371
- function mapCreateError2(error) {
1372
- switch (error.code) {
1373
- case "NOT_AUTHENTICATED":
1374
- case "PERMISSION_DENIED":
1375
- case "INVALID_INPUT":
1376
- case "INSUFFICIENT_BALANCE":
1377
- case "IDEMPOTENCY_CONFLICT":
1378
- case "KYC_REQUIRED":
1379
- case "POLICY_DENIED":
1380
- case "RATE_LIMITED":
1381
- case "NETWORK_ERROR":
1382
- return error;
1383
- default:
1384
- return new CapxulError({
1385
- code: "NETWORK_ERROR",
1386
- message: error.message,
1387
- cause: error,
1388
- details: error.details,
1389
- operationId: error.operationId,
1390
- correlationId: error.correlationId,
1391
- retryable: error.retryable
1392
- });
1393
- }
1394
- }
1395
1121
 
1396
1122
  // src/core/webhook-endpoints.ts
1397
1123
  function createWebhookEndpointsClient() {
@@ -1491,7 +1217,7 @@ function createOrganizationsClient(config = {}) {
1491
1217
  },
1492
1218
  payments: createOrgPaymentsClient(),
1493
1219
  transfers: createOrgTransfersClient(),
1494
- withdrawals: createOrgWithdrawalsClient(config),
1220
+ withdrawals: createOrgWithdrawalsClient(),
1495
1221
  documents: createOrgDocumentsClient(),
1496
1222
  webhookEndpoints: createWebhookEndpointsClient(),
1497
1223
  webhookEvents: createWebhookEventsClient()
@@ -2249,7 +1975,7 @@ function createCapxulClient(config = {}) {
2249
1975
  organizations: createOrganizationsClient(config),
2250
1976
  payments: createPaymentsClient(config),
2251
1977
  transfers: createTransfersClient(),
2252
- withdrawals: createWithdrawalsClient(config),
1978
+ withdrawals: createWithdrawalsClient(),
2253
1979
  documents: createDocumentsClient(),
2254
1980
  subAccounts: createSubAccountsClient(),
2255
1981
  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-D-0KTn8D.cjs';
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';
4
4
  import './next-action-DkrwXYay.cjs';
5
5
  import './errors-GgKrSUKp.cjs';
6
- import './types-DVWojoy4.cjs';
6
+ import './types-z4VLz9rS.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-CweJSkaY.js';
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';
4
4
  import './next-action-DkrwXYay.js';
5
5
  import './errors-QHD5Tlok.js';
6
- import './types-75UokagF.js';
6
+ import './types-BE3tVIOI.js';
7
7
  import '@repo/api-contract/gen/types';