@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/CHANGELOG.md +28 -0
- package/README.md +1 -1
- package/dist/{client-CcCtq5XO.d.ts → client-ChfXWMzO.d.ts} +66 -26
- package/dist/{client-CXRKtbfn.d.cts → client-DW_fW9DC.d.cts} +66 -26
- package/dist/client.cjs +678 -276
- package/dist/client.d.cts +2 -2
- package/dist/client.d.ts +2 -2
- package/dist/client.js +678 -276
- package/dist/index.cjs +781 -389
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +781 -389
- package/dist/{types-75UokagF.d.ts → types-CYvLP5pP.d.ts} +13 -2
- package/dist/{types-DVWojoy4.d.cts → types-X02RbQ2u.d.cts} +13 -2
- package/dist/webhooks.d.cts +1 -1
- package/dist/webhooks.d.ts +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -70,7 +70,366 @@ function fromConvexError(error) {
|
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
// ../observability/src/try-catch.ts
|
|
74
|
+
async function tryCatch(promise) {
|
|
75
|
+
try {
|
|
76
|
+
return [null, await promise];
|
|
77
|
+
} catch (e) {
|
|
78
|
+
return [e instanceof Error ? e : new Error(String(e)), null];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ../observability/src/debug-log.ts
|
|
83
|
+
function isDevelopmentBuild() {
|
|
84
|
+
if (typeof process === "undefined") {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return process.env?.NODE_ENV === "development" || process.env?.NODE_ENV === "test";
|
|
88
|
+
}
|
|
89
|
+
function debugLog(line) {
|
|
90
|
+
if (!isDevelopmentBuild()) return;
|
|
91
|
+
if (typeof globalThis !== "undefined" && "window" in globalThis && typeof console?.info === "function") {
|
|
92
|
+
console.info(line);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (typeof process !== "undefined" && typeof process.stderr?.write === "function") {
|
|
96
|
+
process.stderr.write(`${line}
|
|
97
|
+
`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function formatDebugValue(value) {
|
|
101
|
+
if (value === void 0 || value === "") return "";
|
|
102
|
+
if (typeof value === "string") return value;
|
|
103
|
+
try {
|
|
104
|
+
return JSON.stringify(value);
|
|
105
|
+
} catch {
|
|
106
|
+
return String(value);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function track(...args) {
|
|
110
|
+
const [name, props] = args;
|
|
111
|
+
debugLog(`[TRACK] ${name} ${formatDebugValue(props ?? "")}`);
|
|
112
|
+
}
|
|
113
|
+
function formatDebugValue2(value) {
|
|
114
|
+
if (value === void 0 || value === "") return "";
|
|
115
|
+
if (typeof value === "string") return value;
|
|
116
|
+
try {
|
|
117
|
+
return JSON.stringify(value);
|
|
118
|
+
} catch {
|
|
119
|
+
return String(value);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function identify(userId, traits) {
|
|
123
|
+
debugLog(`[IDENTIFY] ${userId} ${formatDebugValue2(traits ?? "")}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ../platform-kernel/src/ids.ts
|
|
127
|
+
function makePrefixedIdConstructor(prefix, fieldName) {
|
|
128
|
+
const re = new RegExp(`^${prefix}_[A-Za-z0-9_\\-]+$`);
|
|
129
|
+
return (raw) => {
|
|
130
|
+
if (typeof raw !== "string" || !re.test(raw)) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
`Invalid ${fieldName}: expected string matching ^${prefix}_[A-Za-z0-9_\\-]+$, got ${String(raw)}`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
return raw;
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
var toAccountId = makePrefixedIdConstructor(
|
|
139
|
+
"acct",
|
|
140
|
+
"accountId"
|
|
141
|
+
);
|
|
142
|
+
var toOrganizationId = makePrefixedIdConstructor("org", "organizationId");
|
|
143
|
+
var toMemberId = makePrefixedIdConstructor(
|
|
144
|
+
"mem",
|
|
145
|
+
"memberId"
|
|
146
|
+
);
|
|
147
|
+
var toSafeId = makePrefixedIdConstructor(
|
|
148
|
+
"safe",
|
|
149
|
+
"safeId"
|
|
150
|
+
);
|
|
151
|
+
var toTreasuryId = makePrefixedIdConstructor(
|
|
152
|
+
"try",
|
|
153
|
+
"treasuryId"
|
|
154
|
+
);
|
|
155
|
+
var toApiKeyId = makePrefixedIdConstructor(
|
|
156
|
+
"ak",
|
|
157
|
+
"apiKeyId"
|
|
158
|
+
);
|
|
159
|
+
var toOperationId = makePrefixedIdConstructor(
|
|
160
|
+
"op",
|
|
161
|
+
"operationId"
|
|
162
|
+
);
|
|
163
|
+
var toCorrelationId = makePrefixedIdConstructor("ctx", "correlationId");
|
|
164
|
+
var toKycProfileId = makePrefixedIdConstructor(
|
|
165
|
+
"kyc",
|
|
166
|
+
"kycProfileId"
|
|
167
|
+
);
|
|
168
|
+
var toKybProfileId = makePrefixedIdConstructor(
|
|
169
|
+
"kyb",
|
|
170
|
+
"kybProfileId"
|
|
171
|
+
);
|
|
172
|
+
var toExternalAccountId = makePrefixedIdConstructor("ext", "externalAccountId");
|
|
173
|
+
var toPaymentId = makePrefixedIdConstructor(
|
|
174
|
+
"pay",
|
|
175
|
+
"paymentId"
|
|
176
|
+
);
|
|
177
|
+
var toWithdrawalId = makePrefixedIdConstructor(
|
|
178
|
+
"wd",
|
|
179
|
+
"withdrawalId"
|
|
180
|
+
);
|
|
181
|
+
var toWebhookEndpointId = makePrefixedIdConstructor("we", "webhookEndpointId");
|
|
182
|
+
var toWebhookEventId = makePrefixedIdConstructor("evt", "webhookEventId");
|
|
183
|
+
var toSubAccountId = makePrefixedIdConstructor(
|
|
184
|
+
"sub",
|
|
185
|
+
"subAccountId"
|
|
186
|
+
);
|
|
187
|
+
var toVirtualAccountId = makePrefixedIdConstructor("va", "virtualAccountId");
|
|
188
|
+
var toVirtualCardId = makePrefixedIdConstructor(
|
|
189
|
+
"vc",
|
|
190
|
+
"virtualCardId"
|
|
191
|
+
);
|
|
192
|
+
var toTransferId = makePrefixedIdConstructor(
|
|
193
|
+
"txfr",
|
|
194
|
+
"transferId"
|
|
195
|
+
);
|
|
196
|
+
var toDocumentId = makePrefixedIdConstructor(
|
|
197
|
+
"doc",
|
|
198
|
+
"documentId"
|
|
199
|
+
);
|
|
200
|
+
var toBalanceLedgerEntryId = makePrefixedIdConstructor("bal", "balanceLedgerEntryId");
|
|
201
|
+
|
|
202
|
+
// ../platform-kernel/src/value-objects.ts
|
|
203
|
+
var PHONE_NUMBER_RE = /^\+[1-9]\d{1,14}$/;
|
|
204
|
+
function toPhoneNumber(raw) {
|
|
205
|
+
if (typeof raw !== "string" || !PHONE_NUMBER_RE.test(raw)) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`Invalid phoneNumber: expected E.164 string matching ^\\+[1-9]\\d{1,14}$, got ${String(raw)}`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
return raw;
|
|
211
|
+
}
|
|
212
|
+
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
213
|
+
function toEmail(raw) {
|
|
214
|
+
if (typeof raw !== "string") {
|
|
215
|
+
throw new Error(
|
|
216
|
+
`Invalid email: expected string, got ${String(raw)}`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
const lowered = raw.trim().toLowerCase();
|
|
220
|
+
if (!EMAIL_RE.test(lowered)) {
|
|
221
|
+
throw new Error(
|
|
222
|
+
`Invalid email: expected local@domain.tld, got ${String(raw)}`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
return lowered;
|
|
226
|
+
}
|
|
227
|
+
var USERNAME_RE = /^[a-z][a-z0-9_-]{2,29}$/;
|
|
228
|
+
function toUsername(raw) {
|
|
229
|
+
if (typeof raw !== "string") {
|
|
230
|
+
throw new Error(
|
|
231
|
+
`Invalid username: expected string, got ${String(raw)}`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
const lowered = raw.toLowerCase();
|
|
235
|
+
if (!USERNAME_RE.test(lowered)) {
|
|
236
|
+
throw new Error(
|
|
237
|
+
`Invalid username: expected 3-30 chars, letter-first, [a-z0-9_-], got ${String(raw)}`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
return lowered;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/core/external-accounts.ts
|
|
244
|
+
function brandExternalAccount(raw) {
|
|
245
|
+
return {
|
|
246
|
+
...raw,
|
|
247
|
+
id: toExternalAccountId(raw.id),
|
|
248
|
+
operation: {
|
|
249
|
+
id: toOperationId(raw.operation.id),
|
|
250
|
+
status: raw.operation.status,
|
|
251
|
+
correlationId: toCorrelationId(raw.operation.correlationId)
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function createExternalAccountsClient(config = {}) {
|
|
256
|
+
return {
|
|
257
|
+
retrieve: async (externalAccountId) => {
|
|
258
|
+
if (!config.data) {
|
|
259
|
+
return stub(
|
|
260
|
+
"externalAccounts.retrieve"
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
const [err, raw] = await tryCatch(
|
|
264
|
+
config.data.query(api.externalAccounts.queries.retrievePersonal, {
|
|
265
|
+
externalAccountId
|
|
266
|
+
})
|
|
267
|
+
);
|
|
268
|
+
if (err) {
|
|
269
|
+
return [
|
|
270
|
+
fromConvexError(err),
|
|
271
|
+
null
|
|
272
|
+
];
|
|
273
|
+
}
|
|
274
|
+
if (!raw) {
|
|
275
|
+
return [
|
|
276
|
+
new CapxulError({
|
|
277
|
+
code: "NOT_FOUND",
|
|
278
|
+
message: `external_account ${externalAccountId} not found`
|
|
279
|
+
}),
|
|
280
|
+
null
|
|
281
|
+
];
|
|
282
|
+
}
|
|
283
|
+
return [null, brandExternalAccount(raw)];
|
|
284
|
+
},
|
|
285
|
+
remove: async (externalAccountId) => {
|
|
286
|
+
if (!config.data) {
|
|
287
|
+
return stub("externalAccounts.remove");
|
|
288
|
+
}
|
|
289
|
+
const [err] = await tryCatch(
|
|
290
|
+
config.data.mutation(api.externalAccounts.mutations.removePersonal, {
|
|
291
|
+
externalAccountId
|
|
292
|
+
})
|
|
293
|
+
);
|
|
294
|
+
if (err) {
|
|
295
|
+
return [
|
|
296
|
+
fromConvexError(err),
|
|
297
|
+
null
|
|
298
|
+
];
|
|
299
|
+
}
|
|
300
|
+
return [null, void 0];
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
73
305
|
// src/core/accounts.ts
|
|
306
|
+
function createAccountExternalAccountsClient(config) {
|
|
307
|
+
return {
|
|
308
|
+
create: async (input) => {
|
|
309
|
+
if (!config.data) {
|
|
310
|
+
return stub(
|
|
311
|
+
"accounts.externalAccounts.create"
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
const [err, raw] = await tryCatch(
|
|
315
|
+
config.data.mutation(
|
|
316
|
+
api.externalAccounts.mutations.createPersonal,
|
|
317
|
+
{
|
|
318
|
+
kind: input.kind,
|
|
319
|
+
label: input.label,
|
|
320
|
+
address: input.address,
|
|
321
|
+
iban: input.iban,
|
|
322
|
+
bic: input.bic,
|
|
323
|
+
accountHolder: input.accountHolder,
|
|
324
|
+
network: input.network,
|
|
325
|
+
panToken: input.panToken,
|
|
326
|
+
last4: input.last4
|
|
327
|
+
}
|
|
328
|
+
)
|
|
329
|
+
);
|
|
330
|
+
if (err) {
|
|
331
|
+
return [
|
|
332
|
+
fromConvexError(err),
|
|
333
|
+
null
|
|
334
|
+
];
|
|
335
|
+
}
|
|
336
|
+
if (!raw) {
|
|
337
|
+
return [
|
|
338
|
+
new CapxulError({
|
|
339
|
+
code: "NOT_FOUND",
|
|
340
|
+
message: "external_account creation returned no resource"
|
|
341
|
+
}),
|
|
342
|
+
null
|
|
343
|
+
];
|
|
344
|
+
}
|
|
345
|
+
return [
|
|
346
|
+
null,
|
|
347
|
+
brandExternalAccount(
|
|
348
|
+
raw
|
|
349
|
+
)
|
|
350
|
+
];
|
|
351
|
+
},
|
|
352
|
+
list: async (input) => {
|
|
353
|
+
if (!config.data) {
|
|
354
|
+
return stub(
|
|
355
|
+
"accounts.externalAccounts.list"
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
const [err, result] = await tryCatch(
|
|
359
|
+
config.data.query(api.externalAccounts.queries.listPersonal, {
|
|
360
|
+
limit: input.limit,
|
|
361
|
+
cursor: input.cursor
|
|
362
|
+
})
|
|
363
|
+
);
|
|
364
|
+
if (err) {
|
|
365
|
+
return [fromConvexError(err), null];
|
|
366
|
+
}
|
|
367
|
+
const branded = result.data.map(
|
|
368
|
+
(row) => brandExternalAccount(
|
|
369
|
+
row
|
|
370
|
+
)
|
|
371
|
+
);
|
|
372
|
+
return [
|
|
373
|
+
null,
|
|
374
|
+
{
|
|
375
|
+
object: "list",
|
|
376
|
+
data: branded,
|
|
377
|
+
page: result.page
|
|
378
|
+
}
|
|
379
|
+
];
|
|
380
|
+
},
|
|
381
|
+
retrieve: async (externalAccountId) => {
|
|
382
|
+
if (!config.data) {
|
|
383
|
+
return stub(
|
|
384
|
+
"accounts.externalAccounts.retrieve"
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
const [err, raw] = await tryCatch(
|
|
388
|
+
config.data.query(api.externalAccounts.queries.retrievePersonal, {
|
|
389
|
+
externalAccountId
|
|
390
|
+
})
|
|
391
|
+
);
|
|
392
|
+
if (err) {
|
|
393
|
+
return [
|
|
394
|
+
fromConvexError(err),
|
|
395
|
+
null
|
|
396
|
+
];
|
|
397
|
+
}
|
|
398
|
+
if (!raw) {
|
|
399
|
+
return [
|
|
400
|
+
new CapxulError({
|
|
401
|
+
code: "NOT_FOUND",
|
|
402
|
+
message: `external_account ${externalAccountId} not found`
|
|
403
|
+
}),
|
|
404
|
+
null
|
|
405
|
+
];
|
|
406
|
+
}
|
|
407
|
+
return [
|
|
408
|
+
null,
|
|
409
|
+
brandExternalAccount(
|
|
410
|
+
raw
|
|
411
|
+
)
|
|
412
|
+
];
|
|
413
|
+
},
|
|
414
|
+
remove: async (externalAccountId) => {
|
|
415
|
+
if (!config.data) {
|
|
416
|
+
return stub("accounts.externalAccounts.remove");
|
|
417
|
+
}
|
|
418
|
+
const [err] = await tryCatch(
|
|
419
|
+
config.data.mutation(api.externalAccounts.mutations.removePersonal, {
|
|
420
|
+
externalAccountId
|
|
421
|
+
})
|
|
422
|
+
);
|
|
423
|
+
if (err) {
|
|
424
|
+
return [
|
|
425
|
+
fromConvexError(err),
|
|
426
|
+
null
|
|
427
|
+
];
|
|
428
|
+
}
|
|
429
|
+
return [null, void 0];
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
}
|
|
74
433
|
function createAccountsClient(config = {}) {
|
|
75
434
|
return {
|
|
76
435
|
retrieve: async (accountId) => {
|
|
@@ -225,18 +584,7 @@ function createAccountsClient(config = {}) {
|
|
|
225
584
|
create: async () => stub("accounts.kycProfiles.create"),
|
|
226
585
|
retrieve: async () => stub("accounts.kycProfiles.retrieve")
|
|
227
586
|
},
|
|
228
|
-
externalAccounts:
|
|
229
|
-
create: async () => stub(
|
|
230
|
-
"accounts.externalAccounts.create"
|
|
231
|
-
),
|
|
232
|
-
list: async () => stub(
|
|
233
|
-
"accounts.externalAccounts.list"
|
|
234
|
-
),
|
|
235
|
-
retrieve: async () => stub(
|
|
236
|
-
"accounts.externalAccounts.retrieve"
|
|
237
|
-
),
|
|
238
|
-
remove: async () => stub("accounts.externalAccounts.remove")
|
|
239
|
-
},
|
|
587
|
+
externalAccounts: createAccountExternalAccountsClient(config),
|
|
240
588
|
subAccounts: {
|
|
241
589
|
create: async () => stub("accounts.subAccounts.create"),
|
|
242
590
|
list: async () => stub("accounts.subAccounts.list"),
|
|
@@ -332,7 +680,29 @@ var Errors = {
|
|
|
332
680
|
{ details }
|
|
333
681
|
),
|
|
334
682
|
emailDeliveryFailed: (detail) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`),
|
|
335
|
-
internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`)
|
|
683
|
+
internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`),
|
|
684
|
+
/**
|
|
685
|
+
* Verification gate. Surfaced when a request hits a verification
|
|
686
|
+
* boundary the actor cannot cross under their current state. Two
|
|
687
|
+
* variants share this code:
|
|
688
|
+
*
|
|
689
|
+
* - Rail gate (Withdrawals v1 W2, #465): the resolved
|
|
690
|
+
* `external_account.kind` routes to a withdrawal rail (e.g.
|
|
691
|
+
* `fiat_offramp`, `card_payout`) that is not yet supported. Carries
|
|
692
|
+
* `details.rail` + `details.currentKind`.
|
|
693
|
+
* - KYC tier gate (legacy / future): the actor's KYC tier is below
|
|
694
|
+
* the required tier. Carries `details.requiredTier`.
|
|
695
|
+
*
|
|
696
|
+
* Code is shared because both expose the same UX shape ("you cannot
|
|
697
|
+
* proceed until verification advances"); the `details.*` keys
|
|
698
|
+
* differentiate the route.
|
|
699
|
+
*/
|
|
700
|
+
verificationRequired: (details) => {
|
|
701
|
+
const message = "rail" in details ? `Withdrawal rail "${details.rail}" (kind=${details.currentKind}) is not yet supported.` : `Verification tier ${details.requiredTier} is required.`;
|
|
702
|
+
return new CapxulError2("VERIFICATION_REQUIRED", message, {
|
|
703
|
+
details: { ...details }
|
|
704
|
+
});
|
|
705
|
+
}
|
|
336
706
|
};
|
|
337
707
|
|
|
338
708
|
// ../config/src/safe.ts
|
|
@@ -744,14 +1114,6 @@ function createOrgDocumentsClient() {
|
|
|
744
1114
|
};
|
|
745
1115
|
}
|
|
746
1116
|
|
|
747
|
-
// src/core/external-accounts.ts
|
|
748
|
-
function createExternalAccountsClient() {
|
|
749
|
-
return {
|
|
750
|
-
retrieve: async () => stub("externalAccounts.retrieve"),
|
|
751
|
-
remove: async () => stub("externalAccounts.remove")
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
|
|
755
1117
|
// src/core/me.ts
|
|
756
1118
|
function createMeClient(config = {}) {
|
|
757
1119
|
return {
|
|
@@ -1199,67 +1561,68 @@ function createOrgTransfersClient() {
|
|
|
1199
1561
|
cancel: async () => stub("organizations.transfers.cancel")
|
|
1200
1562
|
};
|
|
1201
1563
|
}
|
|
1202
|
-
var EVM_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
1203
1564
|
function createWithdrawalsClient(config = {}) {
|
|
1204
1565
|
return {
|
|
1205
1566
|
create: async (input) => {
|
|
1206
1567
|
if (!config.data) {
|
|
1207
1568
|
return stub("withdrawals.create");
|
|
1208
1569
|
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
details: { field: "destination.externalAccountId" }
|
|
1248
|
-
});
|
|
1249
|
-
}
|
|
1250
|
-
const currentSigner = await config.data.query(
|
|
1251
|
-
api.safe.queries.getMySignerAddress,
|
|
1252
|
-
{}
|
|
1570
|
+
const [createErr, createdRaw] = await tryCatch(
|
|
1571
|
+
config.data.mutation(api.withdrawals.mutations.create, {
|
|
1572
|
+
amount: input.amount,
|
|
1573
|
+
destination: {
|
|
1574
|
+
externalAccountId: input.destination.externalAccountId
|
|
1575
|
+
},
|
|
1576
|
+
source: input.source,
|
|
1577
|
+
reference: input.reference,
|
|
1578
|
+
idempotencyKey: input.idempotencyKey
|
|
1579
|
+
})
|
|
1580
|
+
);
|
|
1581
|
+
if (createErr) {
|
|
1582
|
+
return [mapCreateError2(fromConvexError(createErr)), null];
|
|
1583
|
+
}
|
|
1584
|
+
const created = createdRaw;
|
|
1585
|
+
if (!created) {
|
|
1586
|
+
return [
|
|
1587
|
+
new CapxulError({
|
|
1588
|
+
code: "NETWORK_ERROR",
|
|
1589
|
+
message: "withdrawals.create returned no withdrawal resource"
|
|
1590
|
+
}),
|
|
1591
|
+
null
|
|
1592
|
+
];
|
|
1593
|
+
}
|
|
1594
|
+
if (created.status !== "processing" || created.operation.status !== "processing") {
|
|
1595
|
+
return [null, created];
|
|
1596
|
+
}
|
|
1597
|
+
if (!config.signer || !config.signing) {
|
|
1598
|
+
return [null, created];
|
|
1599
|
+
}
|
|
1600
|
+
const [signerErr, currentSigner] = await tryCatch(
|
|
1601
|
+
config.data.query(api.safe.queries.getMySignerAddress, {})
|
|
1602
|
+
);
|
|
1603
|
+
if (signerErr) {
|
|
1604
|
+
return await handleSubmissionFailure(
|
|
1605
|
+
{ data: config.data },
|
|
1606
|
+
created.id,
|
|
1607
|
+
mapCreateError2(fromConvexError(signerErr))
|
|
1253
1608
|
);
|
|
1254
|
-
|
|
1255
|
-
|
|
1609
|
+
}
|
|
1610
|
+
if (!currentSigner?.address) {
|
|
1611
|
+
return await handleSubmissionFailure(
|
|
1612
|
+
{ data: config.data },
|
|
1613
|
+
created.id,
|
|
1614
|
+
new CapxulError({
|
|
1256
1615
|
code: "PERMISSION_DENIED",
|
|
1257
1616
|
message: "No signer is registered for the authenticated account.",
|
|
1258
1617
|
details: { withdrawalId: created.id }
|
|
1259
|
-
})
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1618
|
+
})
|
|
1619
|
+
);
|
|
1620
|
+
}
|
|
1621
|
+
if (viem.getAddress(currentSigner.address) !== viem.getAddress(config.signer.address)) {
|
|
1622
|
+
return await handleSubmissionFailure(
|
|
1623
|
+
{ data: config.data },
|
|
1624
|
+
created.id,
|
|
1625
|
+
new CapxulError({
|
|
1263
1626
|
code: "PERMISSION_DENIED",
|
|
1264
1627
|
message: "Configured signer does not match the authenticated account signer.",
|
|
1265
1628
|
details: {
|
|
@@ -1267,170 +1630,228 @@ function createWithdrawalsClient(config = {}) {
|
|
|
1267
1630
|
expectedSignerAddress: currentSigner.address,
|
|
1268
1631
|
actualSignerAddress: config.signer.address
|
|
1269
1632
|
}
|
|
1270
|
-
})
|
|
1271
|
-
}
|
|
1272
|
-
const submission = await config.data.query(
|
|
1273
|
-
api.withdrawals.queries.prepareSubmission,
|
|
1274
|
-
{ withdrawalId: created.id }
|
|
1633
|
+
})
|
|
1275
1634
|
);
|
|
1276
|
-
|
|
1277
|
-
|
|
1635
|
+
}
|
|
1636
|
+
const [prepErr, submission] = await tryCatch(
|
|
1637
|
+
config.data.query(api.withdrawals.queries.prepareSubmission, {
|
|
1638
|
+
withdrawalId: created.id
|
|
1639
|
+
})
|
|
1640
|
+
);
|
|
1641
|
+
if (prepErr) {
|
|
1642
|
+
return await handleSubmissionFailure(
|
|
1643
|
+
{ data: config.data },
|
|
1644
|
+
created.id,
|
|
1645
|
+
mapCreateError2(fromConvexError(prepErr))
|
|
1646
|
+
);
|
|
1647
|
+
}
|
|
1648
|
+
const destinationAddress = submission?.destinationAddress;
|
|
1649
|
+
if (!submission || !destinationAddress) {
|
|
1650
|
+
return await handleSubmissionFailure(
|
|
1651
|
+
{ data: config.data },
|
|
1652
|
+
created.id,
|
|
1653
|
+
new CapxulError({
|
|
1278
1654
|
code: "NETWORK_ERROR",
|
|
1279
1655
|
message: "withdrawals.prepareSubmission returned no destination.",
|
|
1280
1656
|
details: { withdrawalId: created.id }
|
|
1281
|
-
})
|
|
1282
|
-
|
|
1283
|
-
|
|
1657
|
+
})
|
|
1658
|
+
);
|
|
1659
|
+
}
|
|
1660
|
+
const [transferErr, transferOk] = await tryCatch(
|
|
1661
|
+
transferAsOwner(
|
|
1284
1662
|
{
|
|
1285
1663
|
signer: config.signer,
|
|
1286
1664
|
signing: config.signing
|
|
1287
1665
|
},
|
|
1288
1666
|
{
|
|
1289
1667
|
tokenAddress: resolvePaymentTokenAddress(submission.amount.currency),
|
|
1290
|
-
recipientAddress:
|
|
1668
|
+
recipientAddress: destinationAddress,
|
|
1291
1669
|
amount: toTokenUnits(submission.amount.value, 6)
|
|
1292
1670
|
}
|
|
1671
|
+
)
|
|
1672
|
+
);
|
|
1673
|
+
if (transferErr) {
|
|
1674
|
+
return await handleSubmissionFailure(
|
|
1675
|
+
{ data: config.data },
|
|
1676
|
+
created.id,
|
|
1677
|
+
mapCreateError2(fromConvexError(transferErr))
|
|
1293
1678
|
);
|
|
1294
|
-
|
|
1295
|
-
|
|
1679
|
+
}
|
|
1680
|
+
if (!transferOk.success) {
|
|
1681
|
+
return await handleSubmissionFailure(
|
|
1682
|
+
{ data: config.data },
|
|
1683
|
+
created.id,
|
|
1684
|
+
new CapxulError({
|
|
1296
1685
|
code: "NETWORK_ERROR",
|
|
1297
1686
|
message: "Bundler submission did not succeed.",
|
|
1298
1687
|
details: {
|
|
1299
1688
|
withdrawalId: created.id,
|
|
1300
|
-
txHash:
|
|
1301
|
-
userOpHash:
|
|
1689
|
+
txHash: transferOk.txHash,
|
|
1690
|
+
userOpHash: transferOk.userOpHash
|
|
1302
1691
|
}
|
|
1303
|
-
})
|
|
1304
|
-
}
|
|
1305
|
-
submitted = {
|
|
1306
|
-
txHash: transfer.txHash,
|
|
1307
|
-
userOpHash: transfer.userOpHash
|
|
1308
|
-
};
|
|
1309
|
-
await config.data.mutation(
|
|
1310
|
-
api.withdrawals.mutations.recordSubmitted,
|
|
1311
|
-
{
|
|
1312
|
-
withdrawalId: created.id,
|
|
1313
|
-
txHash: transfer.txHash,
|
|
1314
|
-
userOpHash: transfer.userOpHash
|
|
1315
|
-
}
|
|
1692
|
+
})
|
|
1316
1693
|
);
|
|
1317
|
-
return [null, created];
|
|
1318
|
-
} catch (cause) {
|
|
1319
|
-
const error = mapCreateError2(fromConvexError(cause));
|
|
1320
|
-
if (created?.id && created.status === "processing" && !submitted) {
|
|
1321
|
-
await bestEffortMarkFailed2({ data: config.data }, created.id, error);
|
|
1322
|
-
}
|
|
1323
|
-
if (submitted && created?.id) {
|
|
1324
|
-
return [
|
|
1325
|
-
new CapxulError({
|
|
1326
|
-
code: "NETWORK_ERROR",
|
|
1327
|
-
message: "Withdrawal was submitted on-chain, but backend submission tracking failed.",
|
|
1328
|
-
cause,
|
|
1329
|
-
details: {
|
|
1330
|
-
withdrawalId: created.id,
|
|
1331
|
-
txHash: submitted.txHash,
|
|
1332
|
-
userOpHash: submitted.userOpHash
|
|
1333
|
-
}
|
|
1334
|
-
}),
|
|
1335
|
-
null
|
|
1336
|
-
];
|
|
1337
|
-
}
|
|
1338
|
-
return [error, null];
|
|
1339
1694
|
}
|
|
1695
|
+
const [recordErr] = await tryCatch(
|
|
1696
|
+
config.data.mutation(api.withdrawals.mutations.recordSubmitted, {
|
|
1697
|
+
withdrawalId: created.id,
|
|
1698
|
+
txHash: transferOk.txHash,
|
|
1699
|
+
userOpHash: transferOk.userOpHash
|
|
1700
|
+
})
|
|
1701
|
+
);
|
|
1702
|
+
if (recordErr) {
|
|
1703
|
+
return [
|
|
1704
|
+
new CapxulError({
|
|
1705
|
+
code: "NETWORK_ERROR",
|
|
1706
|
+
message: "Withdrawal was submitted on-chain, but backend submission tracking failed.",
|
|
1707
|
+
cause: recordErr,
|
|
1708
|
+
details: {
|
|
1709
|
+
withdrawalId: created.id,
|
|
1710
|
+
txHash: transferOk.txHash,
|
|
1711
|
+
userOpHash: transferOk.userOpHash
|
|
1712
|
+
}
|
|
1713
|
+
}),
|
|
1714
|
+
null
|
|
1715
|
+
];
|
|
1716
|
+
}
|
|
1717
|
+
return [null, created];
|
|
1340
1718
|
},
|
|
1341
1719
|
retrieve: async (withdrawalId) => {
|
|
1342
1720
|
if (!config.data) {
|
|
1343
1721
|
return stub("withdrawals.retrieve");
|
|
1344
1722
|
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
);
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
code: "NOT_FOUND",
|
|
1354
|
-
message: `withdrawal ${withdrawalId} not found`
|
|
1355
|
-
}),
|
|
1356
|
-
null
|
|
1357
|
-
];
|
|
1358
|
-
}
|
|
1359
|
-
return [null, withdrawal];
|
|
1360
|
-
} catch (cause) {
|
|
1723
|
+
const [err, raw] = await tryCatch(
|
|
1724
|
+
config.data.query(api.withdrawals.queries.retrieve, { withdrawalId })
|
|
1725
|
+
);
|
|
1726
|
+
if (err) {
|
|
1727
|
+
return [fromConvexError(err), null];
|
|
1728
|
+
}
|
|
1729
|
+
const withdrawal = raw;
|
|
1730
|
+
if (!withdrawal) {
|
|
1361
1731
|
return [
|
|
1362
|
-
|
|
1732
|
+
new CapxulError({
|
|
1733
|
+
code: "NOT_FOUND",
|
|
1734
|
+
message: `withdrawal ${withdrawalId} not found`
|
|
1735
|
+
}),
|
|
1363
1736
|
null
|
|
1364
1737
|
];
|
|
1365
1738
|
}
|
|
1739
|
+
return [null, withdrawal];
|
|
1366
1740
|
},
|
|
1367
1741
|
list: async (input) => {
|
|
1368
1742
|
if (!config.data) {
|
|
1369
1743
|
return stub("withdrawals.list");
|
|
1370
1744
|
}
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1745
|
+
const [err, raw] = await tryCatch(
|
|
1746
|
+
config.data.query(api.withdrawals.queries.list, {
|
|
1747
|
+
limit: input?.limit,
|
|
1748
|
+
cursor: input?.cursor
|
|
1749
|
+
})
|
|
1750
|
+
);
|
|
1751
|
+
if (err) {
|
|
1752
|
+
return [fromConvexError(err), null];
|
|
1753
|
+
}
|
|
1754
|
+
return [null, raw];
|
|
1755
|
+
},
|
|
1756
|
+
recordCompleted: async (input) => {
|
|
1757
|
+
if (!config.data) {
|
|
1758
|
+
return stub(
|
|
1759
|
+
"withdrawals.recordCompleted"
|
|
1378
1760
|
);
|
|
1379
|
-
|
|
1380
|
-
|
|
1761
|
+
}
|
|
1762
|
+
const [err] = await tryCatch(
|
|
1763
|
+
config.data.mutation(api.withdrawals.mutations.recordCompleted, {
|
|
1764
|
+
withdrawalId: input.withdrawalId,
|
|
1765
|
+
txHash: input.txHash
|
|
1766
|
+
})
|
|
1767
|
+
);
|
|
1768
|
+
if (err) {
|
|
1381
1769
|
return [
|
|
1382
|
-
fromConvexError(
|
|
1770
|
+
mapRecordCompletedError(fromConvexError(err)),
|
|
1383
1771
|
null
|
|
1384
1772
|
];
|
|
1385
1773
|
}
|
|
1774
|
+
return [null, null];
|
|
1386
1775
|
}
|
|
1387
1776
|
};
|
|
1388
1777
|
}
|
|
1389
1778
|
function createOrgWithdrawalsClient(config = {}) {
|
|
1390
1779
|
return {
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1780
|
+
/**
|
|
1781
|
+
* Org-scope create (Withdrawals v1 W2, #465).
|
|
1782
|
+
*
|
|
1783
|
+
* D6 — returns the `processing` row only. No `transferAsOwner`
|
|
1784
|
+
* tail, no `recordSubmitted` call. Org Safe + Zodiac submission
|
|
1785
|
+
* orchestration ships in W3+.
|
|
1786
|
+
*/
|
|
1787
|
+
create: async (input) => {
|
|
1788
|
+
if (!config.data) {
|
|
1789
|
+
return stub(
|
|
1790
|
+
"organizations.withdrawals.create"
|
|
1791
|
+
);
|
|
1792
|
+
}
|
|
1793
|
+
const [err, raw] = await tryCatch(
|
|
1794
|
+
config.data.mutation(api.withdrawals.mutations.createOrg, {
|
|
1795
|
+
organizationId: input.organizationId,
|
|
1796
|
+
amount: input.amount,
|
|
1797
|
+
destination: {
|
|
1798
|
+
externalAccountId: input.destination.externalAccountId
|
|
1799
|
+
},
|
|
1800
|
+
source: input.source,
|
|
1801
|
+
reference: input.reference,
|
|
1802
|
+
idempotencyKey: input.idempotencyKey
|
|
1803
|
+
})
|
|
1804
|
+
);
|
|
1805
|
+
if (err) {
|
|
1806
|
+
return [mapCreateError2(fromConvexError(err)), null];
|
|
1807
|
+
}
|
|
1808
|
+
const created = raw;
|
|
1809
|
+
if (!created) {
|
|
1810
|
+
return [
|
|
1811
|
+
new CapxulError({
|
|
1812
|
+
code: "NETWORK_ERROR",
|
|
1813
|
+
message: "organizations.withdrawals.create returned no withdrawal resource"
|
|
1814
|
+
}),
|
|
1815
|
+
null
|
|
1816
|
+
];
|
|
1817
|
+
}
|
|
1818
|
+
return [null, created];
|
|
1819
|
+
},
|
|
1397
1820
|
retrieve: async (input) => {
|
|
1398
1821
|
if (!config.data) {
|
|
1399
1822
|
return stub(
|
|
1400
1823
|
"organizations.withdrawals.retrieve"
|
|
1401
1824
|
);
|
|
1402
1825
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
}),
|
|
1414
|
-
null
|
|
1415
|
-
];
|
|
1416
|
-
}
|
|
1417
|
-
const ownerCheck = withdrawal.owner;
|
|
1418
|
-
if (ownerCheck?.type !== "organization" || ownerCheck.id !== input.organizationId) {
|
|
1419
|
-
return [
|
|
1420
|
-
new CapxulError({
|
|
1421
|
-
code: "NOT_FOUND",
|
|
1422
|
-
message: `withdrawal ${input.withdrawalId} does not belong to organization ${input.organizationId}`
|
|
1423
|
-
}),
|
|
1424
|
-
null
|
|
1425
|
-
];
|
|
1426
|
-
}
|
|
1427
|
-
return [null, withdrawal];
|
|
1428
|
-
} catch (cause) {
|
|
1826
|
+
const [err, raw] = await tryCatch(
|
|
1827
|
+
config.data.query(api.withdrawals.queries.retrieve, {
|
|
1828
|
+
withdrawalId: input.withdrawalId
|
|
1829
|
+
})
|
|
1830
|
+
);
|
|
1831
|
+
if (err) {
|
|
1832
|
+
return [fromConvexError(err), null];
|
|
1833
|
+
}
|
|
1834
|
+
const withdrawal = raw;
|
|
1835
|
+
if (!withdrawal) {
|
|
1429
1836
|
return [
|
|
1430
|
-
|
|
1837
|
+
new CapxulError({
|
|
1838
|
+
code: "NOT_FOUND",
|
|
1839
|
+
message: `withdrawal ${input.withdrawalId} not found`
|
|
1840
|
+
}),
|
|
1841
|
+
null
|
|
1842
|
+
];
|
|
1843
|
+
}
|
|
1844
|
+
const ownerCheck = withdrawal.owner;
|
|
1845
|
+
if (ownerCheck?.type !== "organization" || ownerCheck.id !== input.organizationId) {
|
|
1846
|
+
return [
|
|
1847
|
+
new CapxulError({
|
|
1848
|
+
code: "NOT_FOUND",
|
|
1849
|
+
message: `withdrawal ${input.withdrawalId} does not belong to organization ${input.organizationId}`
|
|
1850
|
+
}),
|
|
1431
1851
|
null
|
|
1432
1852
|
];
|
|
1433
1853
|
}
|
|
1854
|
+
return [null, withdrawal];
|
|
1434
1855
|
},
|
|
1435
1856
|
list: async (input) => {
|
|
1436
1857
|
if (!config.data) {
|
|
@@ -1438,46 +1859,67 @@ function createOrgWithdrawalsClient(config = {}) {
|
|
|
1438
1859
|
"organizations.withdrawals.list"
|
|
1439
1860
|
);
|
|
1440
1861
|
}
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
);
|
|
1450
|
-
return [null, result];
|
|
1451
|
-
} catch (cause) {
|
|
1452
|
-
return [
|
|
1453
|
-
fromConvexError(cause),
|
|
1454
|
-
null
|
|
1455
|
-
];
|
|
1862
|
+
const [err, raw] = await tryCatch(
|
|
1863
|
+
config.data.query(api.withdrawals.queries.listOrg, {
|
|
1864
|
+
organizationId: input.organizationId,
|
|
1865
|
+
limit: input.limit,
|
|
1866
|
+
cursor: input.cursor
|
|
1867
|
+
})
|
|
1868
|
+
);
|
|
1869
|
+
if (err) {
|
|
1870
|
+
return [fromConvexError(err), null];
|
|
1456
1871
|
}
|
|
1872
|
+
return [null, raw];
|
|
1457
1873
|
}
|
|
1458
1874
|
};
|
|
1459
1875
|
}
|
|
1876
|
+
async function handleSubmissionFailure(config, withdrawalId, error) {
|
|
1877
|
+
await bestEffortMarkFailed2(config, withdrawalId, error);
|
|
1878
|
+
return [error, null];
|
|
1879
|
+
}
|
|
1460
1880
|
async function bestEffortMarkFailed2(config, withdrawalId, error) {
|
|
1461
|
-
|
|
1462
|
-
|
|
1881
|
+
await tryCatch(
|
|
1882
|
+
config.data.mutation(api.withdrawals.mutations.markFailed, {
|
|
1463
1883
|
withdrawalId,
|
|
1464
1884
|
errorCode: error.code,
|
|
1465
1885
|
errorMessage: error.message
|
|
1466
|
-
})
|
|
1467
|
-
|
|
1886
|
+
})
|
|
1887
|
+
);
|
|
1888
|
+
}
|
|
1889
|
+
function mapCreateError2(error) {
|
|
1890
|
+
switch (error.code) {
|
|
1891
|
+
case "NOT_AUTHENTICATED":
|
|
1892
|
+
case "PERMISSION_DENIED":
|
|
1893
|
+
case "INVALID_INPUT":
|
|
1894
|
+
case "INSUFFICIENT_BALANCE":
|
|
1895
|
+
case "IDEMPOTENCY_CONFLICT":
|
|
1896
|
+
case "KYC_REQUIRED":
|
|
1897
|
+
case "POLICY_DENIED":
|
|
1898
|
+
case "RATE_LIMITED":
|
|
1899
|
+
case "NETWORK_ERROR":
|
|
1900
|
+
case "NOT_FOUND":
|
|
1901
|
+
case "VERIFICATION_REQUIRED":
|
|
1902
|
+
return error;
|
|
1903
|
+
default:
|
|
1904
|
+
return new CapxulError({
|
|
1905
|
+
code: "NETWORK_ERROR",
|
|
1906
|
+
message: error.message,
|
|
1907
|
+
cause: error,
|
|
1908
|
+
details: error.details,
|
|
1909
|
+
operationId: error.operationId,
|
|
1910
|
+
correlationId: error.correlationId,
|
|
1911
|
+
retryable: error.retryable
|
|
1912
|
+
});
|
|
1468
1913
|
}
|
|
1469
1914
|
}
|
|
1470
|
-
function
|
|
1915
|
+
function mapRecordCompletedError(error) {
|
|
1471
1916
|
switch (error.code) {
|
|
1472
1917
|
case "NOT_AUTHENTICATED":
|
|
1473
1918
|
case "PERMISSION_DENIED":
|
|
1474
1919
|
case "INVALID_INPUT":
|
|
1475
|
-
case "
|
|
1476
|
-
case "IDEMPOTENCY_CONFLICT":
|
|
1477
|
-
case "KYC_REQUIRED":
|
|
1478
|
-
case "POLICY_DENIED":
|
|
1479
|
-
case "RATE_LIMITED":
|
|
1920
|
+
case "NOT_FOUND":
|
|
1480
1921
|
case "NETWORK_ERROR":
|
|
1922
|
+
case "INTERNAL_ERROR":
|
|
1481
1923
|
return error;
|
|
1482
1924
|
default:
|
|
1483
1925
|
return new CapxulError({
|
|
@@ -1513,6 +1955,136 @@ function createWebhookEventsClient() {
|
|
|
1513
1955
|
}
|
|
1514
1956
|
|
|
1515
1957
|
// src/core/organizations.ts
|
|
1958
|
+
function createOrgExternalAccountsClient(config) {
|
|
1959
|
+
return {
|
|
1960
|
+
create: async (input) => {
|
|
1961
|
+
if (!config.data) {
|
|
1962
|
+
return stub(
|
|
1963
|
+
"organizations.externalAccounts.create"
|
|
1964
|
+
);
|
|
1965
|
+
}
|
|
1966
|
+
const [err, raw] = await tryCatch(
|
|
1967
|
+
config.data.mutation(api.externalAccounts.mutations.createOrg, {
|
|
1968
|
+
organizationId: input.organizationId,
|
|
1969
|
+
kind: input.kind,
|
|
1970
|
+
label: input.label,
|
|
1971
|
+
address: input.address,
|
|
1972
|
+
iban: input.iban,
|
|
1973
|
+
bic: input.bic,
|
|
1974
|
+
accountHolder: input.accountHolder,
|
|
1975
|
+
network: input.network,
|
|
1976
|
+
panToken: input.panToken,
|
|
1977
|
+
last4: input.last4
|
|
1978
|
+
})
|
|
1979
|
+
);
|
|
1980
|
+
if (err) {
|
|
1981
|
+
return [
|
|
1982
|
+
fromConvexError(err),
|
|
1983
|
+
null
|
|
1984
|
+
];
|
|
1985
|
+
}
|
|
1986
|
+
if (!raw) {
|
|
1987
|
+
return [
|
|
1988
|
+
new CapxulError({
|
|
1989
|
+
code: "NOT_FOUND",
|
|
1990
|
+
message: "external_account creation returned no resource"
|
|
1991
|
+
}),
|
|
1992
|
+
null
|
|
1993
|
+
];
|
|
1994
|
+
}
|
|
1995
|
+
return [
|
|
1996
|
+
null,
|
|
1997
|
+
brandExternalAccount(
|
|
1998
|
+
raw
|
|
1999
|
+
)
|
|
2000
|
+
];
|
|
2001
|
+
},
|
|
2002
|
+
list: async (input) => {
|
|
2003
|
+
if (!config.data) {
|
|
2004
|
+
return stub(
|
|
2005
|
+
"organizations.externalAccounts.list"
|
|
2006
|
+
);
|
|
2007
|
+
}
|
|
2008
|
+
const [err, result] = await tryCatch(
|
|
2009
|
+
config.data.query(api.externalAccounts.queries.listOrg, {
|
|
2010
|
+
organizationId: input.organizationId,
|
|
2011
|
+
limit: input.limit,
|
|
2012
|
+
cursor: input.cursor
|
|
2013
|
+
})
|
|
2014
|
+
);
|
|
2015
|
+
if (err) {
|
|
2016
|
+
return [fromConvexError(err), null];
|
|
2017
|
+
}
|
|
2018
|
+
const branded = result.data.map(
|
|
2019
|
+
(row) => brandExternalAccount(
|
|
2020
|
+
row
|
|
2021
|
+
)
|
|
2022
|
+
);
|
|
2023
|
+
return [
|
|
2024
|
+
null,
|
|
2025
|
+
{
|
|
2026
|
+
object: "list",
|
|
2027
|
+
data: branded,
|
|
2028
|
+
page: result.page
|
|
2029
|
+
}
|
|
2030
|
+
];
|
|
2031
|
+
},
|
|
2032
|
+
retrieve: async (input) => {
|
|
2033
|
+
if (!config.data) {
|
|
2034
|
+
return stub(
|
|
2035
|
+
"organizations.externalAccounts.retrieve"
|
|
2036
|
+
);
|
|
2037
|
+
}
|
|
2038
|
+
const [err, raw] = await tryCatch(
|
|
2039
|
+
config.data.query(api.externalAccounts.queries.retrieveOrg, {
|
|
2040
|
+
organizationId: input.organizationId,
|
|
2041
|
+
externalAccountId: input.externalAccountId
|
|
2042
|
+
})
|
|
2043
|
+
);
|
|
2044
|
+
if (err) {
|
|
2045
|
+
return [
|
|
2046
|
+
fromConvexError(err),
|
|
2047
|
+
null
|
|
2048
|
+
];
|
|
2049
|
+
}
|
|
2050
|
+
if (!raw) {
|
|
2051
|
+
return [
|
|
2052
|
+
new CapxulError({
|
|
2053
|
+
code: "NOT_FOUND",
|
|
2054
|
+
message: `external_account ${input.externalAccountId} not found`
|
|
2055
|
+
}),
|
|
2056
|
+
null
|
|
2057
|
+
];
|
|
2058
|
+
}
|
|
2059
|
+
return [
|
|
2060
|
+
null,
|
|
2061
|
+
brandExternalAccount(
|
|
2062
|
+
raw
|
|
2063
|
+
)
|
|
2064
|
+
];
|
|
2065
|
+
},
|
|
2066
|
+
remove: async (input) => {
|
|
2067
|
+
if (!config.data) {
|
|
2068
|
+
return stub(
|
|
2069
|
+
"organizations.externalAccounts.remove"
|
|
2070
|
+
);
|
|
2071
|
+
}
|
|
2072
|
+
const [err] = await tryCatch(
|
|
2073
|
+
config.data.mutation(api.externalAccounts.mutations.removeOrg, {
|
|
2074
|
+
organizationId: input.organizationId,
|
|
2075
|
+
externalAccountId: input.externalAccountId
|
|
2076
|
+
})
|
|
2077
|
+
);
|
|
2078
|
+
if (err) {
|
|
2079
|
+
return [
|
|
2080
|
+
fromConvexError(err),
|
|
2081
|
+
null
|
|
2082
|
+
];
|
|
2083
|
+
}
|
|
2084
|
+
return [null, void 0];
|
|
2085
|
+
}
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
1516
2088
|
function createOrganizationsClient(config = {}) {
|
|
1517
2089
|
return {
|
|
1518
2090
|
create: async () => stub("organizations.create"),
|
|
@@ -1568,18 +2140,7 @@ function createOrganizationsClient(config = {}) {
|
|
|
1568
2140
|
retrieve: async () => stub("organizations.subAccounts.retrieve"),
|
|
1569
2141
|
remove: async () => stub("organizations.subAccounts.remove")
|
|
1570
2142
|
},
|
|
1571
|
-
externalAccounts:
|
|
1572
|
-
create: async () => stub(
|
|
1573
|
-
"organizations.externalAccounts.create"
|
|
1574
|
-
),
|
|
1575
|
-
list: async () => stub(
|
|
1576
|
-
"organizations.externalAccounts.list"
|
|
1577
|
-
),
|
|
1578
|
-
retrieve: async () => stub(
|
|
1579
|
-
"organizations.externalAccounts.retrieve"
|
|
1580
|
-
),
|
|
1581
|
-
remove: async () => stub("organizations.externalAccounts.remove")
|
|
1582
|
-
},
|
|
2143
|
+
externalAccounts: createOrgExternalAccountsClient(config),
|
|
1583
2144
|
balanceLedger: {
|
|
1584
2145
|
list: async () => stub(
|
|
1585
2146
|
"organizations.balanceLedger.list"
|
|
@@ -1716,59 +2277,6 @@ function createVirtualCardsClient() {
|
|
|
1716
2277
|
cancel: async () => stub("virtualCards.cancel")
|
|
1717
2278
|
};
|
|
1718
2279
|
}
|
|
1719
|
-
|
|
1720
|
-
// ../observability/src/try-catch.ts
|
|
1721
|
-
async function tryCatch(promise) {
|
|
1722
|
-
try {
|
|
1723
|
-
return [null, await promise];
|
|
1724
|
-
} catch (e) {
|
|
1725
|
-
return [e instanceof Error ? e : new Error(String(e)), null];
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
|
-
// ../observability/src/debug-log.ts
|
|
1730
|
-
function isDevelopmentBuild() {
|
|
1731
|
-
if (typeof process === "undefined") {
|
|
1732
|
-
return false;
|
|
1733
|
-
}
|
|
1734
|
-
return process.env?.NODE_ENV === "development" || process.env?.NODE_ENV === "test";
|
|
1735
|
-
}
|
|
1736
|
-
function debugLog(line) {
|
|
1737
|
-
if (!isDevelopmentBuild()) return;
|
|
1738
|
-
if (typeof globalThis !== "undefined" && "window" in globalThis && typeof console?.info === "function") {
|
|
1739
|
-
console.info(line);
|
|
1740
|
-
return;
|
|
1741
|
-
}
|
|
1742
|
-
if (typeof process !== "undefined" && typeof process.stderr?.write === "function") {
|
|
1743
|
-
process.stderr.write(`${line}
|
|
1744
|
-
`);
|
|
1745
|
-
}
|
|
1746
|
-
}
|
|
1747
|
-
function formatDebugValue(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 track(...args) {
|
|
1757
|
-
const [name, props] = args;
|
|
1758
|
-
debugLog(`[TRACK] ${name} ${formatDebugValue(props ?? "")}`);
|
|
1759
|
-
}
|
|
1760
|
-
function formatDebugValue2(value) {
|
|
1761
|
-
if (value === void 0 || value === "") return "";
|
|
1762
|
-
if (typeof value === "string") return value;
|
|
1763
|
-
try {
|
|
1764
|
-
return JSON.stringify(value);
|
|
1765
|
-
} catch {
|
|
1766
|
-
return String(value);
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
function identify(userId, traits) {
|
|
1770
|
-
debugLog(`[IDENTIFY] ${userId} ${formatDebugValue2(traits ?? "")}`);
|
|
1771
|
-
}
|
|
1772
2280
|
function createAuthFlowMachine(client) {
|
|
1773
2281
|
return xstate.setup({
|
|
1774
2282
|
types: {},
|
|
@@ -2044,122 +2552,6 @@ function emailDomain(email) {
|
|
|
2044
2552
|
const domain = email.split("@")[1]?.trim().toLowerCase();
|
|
2045
2553
|
return domain || "unknown";
|
|
2046
2554
|
}
|
|
2047
|
-
|
|
2048
|
-
// ../platform-kernel/src/ids.ts
|
|
2049
|
-
function makePrefixedIdConstructor(prefix, fieldName) {
|
|
2050
|
-
const re = new RegExp(`^${prefix}_[A-Za-z0-9_\\-]+$`);
|
|
2051
|
-
return (raw) => {
|
|
2052
|
-
if (typeof raw !== "string" || !re.test(raw)) {
|
|
2053
|
-
throw new Error(
|
|
2054
|
-
`Invalid ${fieldName}: expected string matching ^${prefix}_[A-Za-z0-9_\\-]+$, got ${String(raw)}`
|
|
2055
|
-
);
|
|
2056
|
-
}
|
|
2057
|
-
return raw;
|
|
2058
|
-
};
|
|
2059
|
-
}
|
|
2060
|
-
var toAccountId = makePrefixedIdConstructor(
|
|
2061
|
-
"acct",
|
|
2062
|
-
"accountId"
|
|
2063
|
-
);
|
|
2064
|
-
var toOrganizationId = makePrefixedIdConstructor("org", "organizationId");
|
|
2065
|
-
var toMemberId = makePrefixedIdConstructor(
|
|
2066
|
-
"mem",
|
|
2067
|
-
"memberId"
|
|
2068
|
-
);
|
|
2069
|
-
var toSafeId = makePrefixedIdConstructor(
|
|
2070
|
-
"safe",
|
|
2071
|
-
"safeId"
|
|
2072
|
-
);
|
|
2073
|
-
var toTreasuryId = makePrefixedIdConstructor(
|
|
2074
|
-
"try",
|
|
2075
|
-
"treasuryId"
|
|
2076
|
-
);
|
|
2077
|
-
var toApiKeyId = makePrefixedIdConstructor(
|
|
2078
|
-
"ak",
|
|
2079
|
-
"apiKeyId"
|
|
2080
|
-
);
|
|
2081
|
-
var toOperationId = makePrefixedIdConstructor(
|
|
2082
|
-
"op",
|
|
2083
|
-
"operationId"
|
|
2084
|
-
);
|
|
2085
|
-
var toKycProfileId = makePrefixedIdConstructor(
|
|
2086
|
-
"kyc",
|
|
2087
|
-
"kycProfileId"
|
|
2088
|
-
);
|
|
2089
|
-
var toKybProfileId = makePrefixedIdConstructor(
|
|
2090
|
-
"kyb",
|
|
2091
|
-
"kybProfileId"
|
|
2092
|
-
);
|
|
2093
|
-
var toExternalAccountId = makePrefixedIdConstructor("ext", "externalAccountId");
|
|
2094
|
-
var toPaymentId = makePrefixedIdConstructor(
|
|
2095
|
-
"pay",
|
|
2096
|
-
"paymentId"
|
|
2097
|
-
);
|
|
2098
|
-
var toWithdrawalId = makePrefixedIdConstructor(
|
|
2099
|
-
"wd",
|
|
2100
|
-
"withdrawalId"
|
|
2101
|
-
);
|
|
2102
|
-
var toWebhookEndpointId = makePrefixedIdConstructor("we", "webhookEndpointId");
|
|
2103
|
-
var toWebhookEventId = makePrefixedIdConstructor("evt", "webhookEventId");
|
|
2104
|
-
var toSubAccountId = makePrefixedIdConstructor(
|
|
2105
|
-
"sub",
|
|
2106
|
-
"subAccountId"
|
|
2107
|
-
);
|
|
2108
|
-
var toVirtualAccountId = makePrefixedIdConstructor("va", "virtualAccountId");
|
|
2109
|
-
var toVirtualCardId = makePrefixedIdConstructor(
|
|
2110
|
-
"vc",
|
|
2111
|
-
"virtualCardId"
|
|
2112
|
-
);
|
|
2113
|
-
var toTransferId = makePrefixedIdConstructor(
|
|
2114
|
-
"txfr",
|
|
2115
|
-
"transferId"
|
|
2116
|
-
);
|
|
2117
|
-
var toDocumentId = makePrefixedIdConstructor(
|
|
2118
|
-
"doc",
|
|
2119
|
-
"documentId"
|
|
2120
|
-
);
|
|
2121
|
-
var toBalanceLedgerEntryId = makePrefixedIdConstructor("bal", "balanceLedgerEntryId");
|
|
2122
|
-
|
|
2123
|
-
// ../platform-kernel/src/value-objects.ts
|
|
2124
|
-
var PHONE_NUMBER_RE = /^\+[1-9]\d{1,14}$/;
|
|
2125
|
-
function toPhoneNumber(raw) {
|
|
2126
|
-
if (typeof raw !== "string" || !PHONE_NUMBER_RE.test(raw)) {
|
|
2127
|
-
throw new Error(
|
|
2128
|
-
`Invalid phoneNumber: expected E.164 string matching ^\\+[1-9]\\d{1,14}$, got ${String(raw)}`
|
|
2129
|
-
);
|
|
2130
|
-
}
|
|
2131
|
-
return raw;
|
|
2132
|
-
}
|
|
2133
|
-
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2134
|
-
function toEmail(raw) {
|
|
2135
|
-
if (typeof raw !== "string") {
|
|
2136
|
-
throw new Error(
|
|
2137
|
-
`Invalid email: expected string, got ${String(raw)}`
|
|
2138
|
-
);
|
|
2139
|
-
}
|
|
2140
|
-
const lowered = raw.trim().toLowerCase();
|
|
2141
|
-
if (!EMAIL_RE.test(lowered)) {
|
|
2142
|
-
throw new Error(
|
|
2143
|
-
`Invalid email: expected local@domain.tld, got ${String(raw)}`
|
|
2144
|
-
);
|
|
2145
|
-
}
|
|
2146
|
-
return lowered;
|
|
2147
|
-
}
|
|
2148
|
-
var USERNAME_RE = /^[a-z][a-z0-9_-]{2,29}$/;
|
|
2149
|
-
function toUsername(raw) {
|
|
2150
|
-
if (typeof raw !== "string") {
|
|
2151
|
-
throw new Error(
|
|
2152
|
-
`Invalid username: expected string, got ${String(raw)}`
|
|
2153
|
-
);
|
|
2154
|
-
}
|
|
2155
|
-
const lowered = raw.toLowerCase();
|
|
2156
|
-
if (!USERNAME_RE.test(lowered)) {
|
|
2157
|
-
throw new Error(
|
|
2158
|
-
`Invalid username: expected 3-30 chars, letter-first, [a-z0-9_-], got ${String(raw)}`
|
|
2159
|
-
);
|
|
2160
|
-
}
|
|
2161
|
-
return lowered;
|
|
2162
|
-
}
|
|
2163
2555
|
function createProvisioningMachine(client) {
|
|
2164
2556
|
return xstate.setup({
|
|
2165
2557
|
types: {},
|
|
@@ -2552,7 +2944,7 @@ function createCapxulClient(config = {}) {
|
|
|
2552
2944
|
subAccounts: createSubAccountsClient(),
|
|
2553
2945
|
virtualAccounts: createVirtualAccountsClient(),
|
|
2554
2946
|
virtualCards: createVirtualCardsClient(),
|
|
2555
|
-
externalAccounts: createExternalAccountsClient(),
|
|
2947
|
+
externalAccounts: createExternalAccountsClient(config),
|
|
2556
2948
|
operations: createOperationsClient(config),
|
|
2557
2949
|
webhookEndpoints: createWebhookEndpointsClient(),
|
|
2558
2950
|
webhookEvents: createWebhookEventsClient(),
|