@capxul/sdk 0.1.0-alpha.3 → 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 +51 -0
- package/README.md +1 -1
- package/dist/{client-BI863O0y.d.ts → client-ChfXWMzO.d.ts} +244 -77
- package/dist/{client-CwcnMbGv.d.cts → client-DW_fW9DC.d.cts} +244 -77
- package/dist/client.cjs +982 -118
- package/dist/client.d.cts +2 -2
- package/dist/client.d.ts +2 -2
- package/dist/client.js +982 -118
- package/dist/index.cjs +1079 -224
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1079 -225
- package/dist/{types-BE3tVIOI.d.ts → types-CYvLP5pP.d.ts} +22 -3
- package/dist/{types-z4VLz9rS.d.cts → types-X02RbQ2u.d.cts} +22 -3
- package/dist/webhooks.d.cts +1 -1
- package/dist/webhooks.d.ts +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -68,12 +68,441 @@ function fromConvexError(error) {
|
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
// ../observability/src/try-catch.ts
|
|
72
|
+
async function tryCatch(promise) {
|
|
73
|
+
try {
|
|
74
|
+
return [null, await promise];
|
|
75
|
+
} catch (e) {
|
|
76
|
+
return [e instanceof Error ? e : new Error(String(e)), null];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ../observability/src/debug-log.ts
|
|
81
|
+
function isDevelopmentBuild() {
|
|
82
|
+
if (typeof process === "undefined") {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
return process.env?.NODE_ENV === "development" || process.env?.NODE_ENV === "test";
|
|
86
|
+
}
|
|
87
|
+
function debugLog(line) {
|
|
88
|
+
if (!isDevelopmentBuild()) return;
|
|
89
|
+
if (typeof globalThis !== "undefined" && "window" in globalThis && typeof console?.info === "function") {
|
|
90
|
+
console.info(line);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (typeof process !== "undefined" && typeof process.stderr?.write === "function") {
|
|
94
|
+
process.stderr.write(`${line}
|
|
95
|
+
`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function formatDebugValue(value) {
|
|
99
|
+
if (value === void 0 || value === "") return "";
|
|
100
|
+
if (typeof value === "string") return value;
|
|
101
|
+
try {
|
|
102
|
+
return JSON.stringify(value);
|
|
103
|
+
} catch {
|
|
104
|
+
return String(value);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function track(...args) {
|
|
108
|
+
const [name, props] = args;
|
|
109
|
+
debugLog(`[TRACK] ${name} ${formatDebugValue(props ?? "")}`);
|
|
110
|
+
}
|
|
111
|
+
function formatDebugValue2(value) {
|
|
112
|
+
if (value === void 0 || value === "") return "";
|
|
113
|
+
if (typeof value === "string") return value;
|
|
114
|
+
try {
|
|
115
|
+
return JSON.stringify(value);
|
|
116
|
+
} catch {
|
|
117
|
+
return String(value);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function identify(userId, traits) {
|
|
121
|
+
debugLog(`[IDENTIFY] ${userId} ${formatDebugValue2(traits ?? "")}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ../platform-kernel/src/ids.ts
|
|
125
|
+
function makePrefixedIdConstructor(prefix, fieldName) {
|
|
126
|
+
const re = new RegExp(`^${prefix}_[A-Za-z0-9_\\-]+$`);
|
|
127
|
+
return (raw) => {
|
|
128
|
+
if (typeof raw !== "string" || !re.test(raw)) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
`Invalid ${fieldName}: expected string matching ^${prefix}_[A-Za-z0-9_\\-]+$, got ${String(raw)}`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return raw;
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
var toAccountId = makePrefixedIdConstructor(
|
|
137
|
+
"acct",
|
|
138
|
+
"accountId"
|
|
139
|
+
);
|
|
140
|
+
var toOrganizationId = makePrefixedIdConstructor("org", "organizationId");
|
|
141
|
+
var toMemberId = makePrefixedIdConstructor(
|
|
142
|
+
"mem",
|
|
143
|
+
"memberId"
|
|
144
|
+
);
|
|
145
|
+
var toSafeId = makePrefixedIdConstructor(
|
|
146
|
+
"safe",
|
|
147
|
+
"safeId"
|
|
148
|
+
);
|
|
149
|
+
var toTreasuryId = makePrefixedIdConstructor(
|
|
150
|
+
"try",
|
|
151
|
+
"treasuryId"
|
|
152
|
+
);
|
|
153
|
+
var toApiKeyId = makePrefixedIdConstructor(
|
|
154
|
+
"ak",
|
|
155
|
+
"apiKeyId"
|
|
156
|
+
);
|
|
157
|
+
var toOperationId = makePrefixedIdConstructor(
|
|
158
|
+
"op",
|
|
159
|
+
"operationId"
|
|
160
|
+
);
|
|
161
|
+
var toCorrelationId = makePrefixedIdConstructor("ctx", "correlationId");
|
|
162
|
+
var toKycProfileId = makePrefixedIdConstructor(
|
|
163
|
+
"kyc",
|
|
164
|
+
"kycProfileId"
|
|
165
|
+
);
|
|
166
|
+
var toKybProfileId = makePrefixedIdConstructor(
|
|
167
|
+
"kyb",
|
|
168
|
+
"kybProfileId"
|
|
169
|
+
);
|
|
170
|
+
var toExternalAccountId = makePrefixedIdConstructor("ext", "externalAccountId");
|
|
171
|
+
var toPaymentId = makePrefixedIdConstructor(
|
|
172
|
+
"pay",
|
|
173
|
+
"paymentId"
|
|
174
|
+
);
|
|
175
|
+
var toWithdrawalId = makePrefixedIdConstructor(
|
|
176
|
+
"wd",
|
|
177
|
+
"withdrawalId"
|
|
178
|
+
);
|
|
179
|
+
var toWebhookEndpointId = makePrefixedIdConstructor("we", "webhookEndpointId");
|
|
180
|
+
var toWebhookEventId = makePrefixedIdConstructor("evt", "webhookEventId");
|
|
181
|
+
var toSubAccountId = makePrefixedIdConstructor(
|
|
182
|
+
"sub",
|
|
183
|
+
"subAccountId"
|
|
184
|
+
);
|
|
185
|
+
var toVirtualAccountId = makePrefixedIdConstructor("va", "virtualAccountId");
|
|
186
|
+
var toVirtualCardId = makePrefixedIdConstructor(
|
|
187
|
+
"vc",
|
|
188
|
+
"virtualCardId"
|
|
189
|
+
);
|
|
190
|
+
var toTransferId = makePrefixedIdConstructor(
|
|
191
|
+
"txfr",
|
|
192
|
+
"transferId"
|
|
193
|
+
);
|
|
194
|
+
var toDocumentId = makePrefixedIdConstructor(
|
|
195
|
+
"doc",
|
|
196
|
+
"documentId"
|
|
197
|
+
);
|
|
198
|
+
var toBalanceLedgerEntryId = makePrefixedIdConstructor("bal", "balanceLedgerEntryId");
|
|
199
|
+
|
|
200
|
+
// ../platform-kernel/src/value-objects.ts
|
|
201
|
+
var PHONE_NUMBER_RE = /^\+[1-9]\d{1,14}$/;
|
|
202
|
+
function toPhoneNumber(raw) {
|
|
203
|
+
if (typeof raw !== "string" || !PHONE_NUMBER_RE.test(raw)) {
|
|
204
|
+
throw new Error(
|
|
205
|
+
`Invalid phoneNumber: expected E.164 string matching ^\\+[1-9]\\d{1,14}$, got ${String(raw)}`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
return raw;
|
|
209
|
+
}
|
|
210
|
+
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
211
|
+
function toEmail(raw) {
|
|
212
|
+
if (typeof raw !== "string") {
|
|
213
|
+
throw new Error(
|
|
214
|
+
`Invalid email: expected string, got ${String(raw)}`
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
const lowered = raw.trim().toLowerCase();
|
|
218
|
+
if (!EMAIL_RE.test(lowered)) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`Invalid email: expected local@domain.tld, got ${String(raw)}`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
return lowered;
|
|
224
|
+
}
|
|
225
|
+
var USERNAME_RE = /^[a-z][a-z0-9_-]{2,29}$/;
|
|
226
|
+
function toUsername(raw) {
|
|
227
|
+
if (typeof raw !== "string") {
|
|
228
|
+
throw new Error(
|
|
229
|
+
`Invalid username: expected string, got ${String(raw)}`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
const lowered = raw.toLowerCase();
|
|
233
|
+
if (!USERNAME_RE.test(lowered)) {
|
|
234
|
+
throw new Error(
|
|
235
|
+
`Invalid username: expected 3-30 chars, letter-first, [a-z0-9_-], got ${String(raw)}`
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
return lowered;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/core/external-accounts.ts
|
|
242
|
+
function brandExternalAccount(raw) {
|
|
243
|
+
return {
|
|
244
|
+
...raw,
|
|
245
|
+
id: toExternalAccountId(raw.id),
|
|
246
|
+
operation: {
|
|
247
|
+
id: toOperationId(raw.operation.id),
|
|
248
|
+
status: raw.operation.status,
|
|
249
|
+
correlationId: toCorrelationId(raw.operation.correlationId)
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function createExternalAccountsClient(config = {}) {
|
|
254
|
+
return {
|
|
255
|
+
retrieve: async (externalAccountId) => {
|
|
256
|
+
if (!config.data) {
|
|
257
|
+
return stub(
|
|
258
|
+
"externalAccounts.retrieve"
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
const [err, raw] = await tryCatch(
|
|
262
|
+
config.data.query(api.externalAccounts.queries.retrievePersonal, {
|
|
263
|
+
externalAccountId
|
|
264
|
+
})
|
|
265
|
+
);
|
|
266
|
+
if (err) {
|
|
267
|
+
return [
|
|
268
|
+
fromConvexError(err),
|
|
269
|
+
null
|
|
270
|
+
];
|
|
271
|
+
}
|
|
272
|
+
if (!raw) {
|
|
273
|
+
return [
|
|
274
|
+
new CapxulError({
|
|
275
|
+
code: "NOT_FOUND",
|
|
276
|
+
message: `external_account ${externalAccountId} not found`
|
|
277
|
+
}),
|
|
278
|
+
null
|
|
279
|
+
];
|
|
280
|
+
}
|
|
281
|
+
return [null, brandExternalAccount(raw)];
|
|
282
|
+
},
|
|
283
|
+
remove: async (externalAccountId) => {
|
|
284
|
+
if (!config.data) {
|
|
285
|
+
return stub("externalAccounts.remove");
|
|
286
|
+
}
|
|
287
|
+
const [err] = await tryCatch(
|
|
288
|
+
config.data.mutation(api.externalAccounts.mutations.removePersonal, {
|
|
289
|
+
externalAccountId
|
|
290
|
+
})
|
|
291
|
+
);
|
|
292
|
+
if (err) {
|
|
293
|
+
return [
|
|
294
|
+
fromConvexError(err),
|
|
295
|
+
null
|
|
296
|
+
];
|
|
297
|
+
}
|
|
298
|
+
return [null, void 0];
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
71
303
|
// src/core/accounts.ts
|
|
304
|
+
function createAccountExternalAccountsClient(config) {
|
|
305
|
+
return {
|
|
306
|
+
create: async (input) => {
|
|
307
|
+
if (!config.data) {
|
|
308
|
+
return stub(
|
|
309
|
+
"accounts.externalAccounts.create"
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
const [err, raw] = await tryCatch(
|
|
313
|
+
config.data.mutation(
|
|
314
|
+
api.externalAccounts.mutations.createPersonal,
|
|
315
|
+
{
|
|
316
|
+
kind: input.kind,
|
|
317
|
+
label: input.label,
|
|
318
|
+
address: input.address,
|
|
319
|
+
iban: input.iban,
|
|
320
|
+
bic: input.bic,
|
|
321
|
+
accountHolder: input.accountHolder,
|
|
322
|
+
network: input.network,
|
|
323
|
+
panToken: input.panToken,
|
|
324
|
+
last4: input.last4
|
|
325
|
+
}
|
|
326
|
+
)
|
|
327
|
+
);
|
|
328
|
+
if (err) {
|
|
329
|
+
return [
|
|
330
|
+
fromConvexError(err),
|
|
331
|
+
null
|
|
332
|
+
];
|
|
333
|
+
}
|
|
334
|
+
if (!raw) {
|
|
335
|
+
return [
|
|
336
|
+
new CapxulError({
|
|
337
|
+
code: "NOT_FOUND",
|
|
338
|
+
message: "external_account creation returned no resource"
|
|
339
|
+
}),
|
|
340
|
+
null
|
|
341
|
+
];
|
|
342
|
+
}
|
|
343
|
+
return [
|
|
344
|
+
null,
|
|
345
|
+
brandExternalAccount(
|
|
346
|
+
raw
|
|
347
|
+
)
|
|
348
|
+
];
|
|
349
|
+
},
|
|
350
|
+
list: async (input) => {
|
|
351
|
+
if (!config.data) {
|
|
352
|
+
return stub(
|
|
353
|
+
"accounts.externalAccounts.list"
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
const [err, result] = await tryCatch(
|
|
357
|
+
config.data.query(api.externalAccounts.queries.listPersonal, {
|
|
358
|
+
limit: input.limit,
|
|
359
|
+
cursor: input.cursor
|
|
360
|
+
})
|
|
361
|
+
);
|
|
362
|
+
if (err) {
|
|
363
|
+
return [fromConvexError(err), null];
|
|
364
|
+
}
|
|
365
|
+
const branded = result.data.map(
|
|
366
|
+
(row) => brandExternalAccount(
|
|
367
|
+
row
|
|
368
|
+
)
|
|
369
|
+
);
|
|
370
|
+
return [
|
|
371
|
+
null,
|
|
372
|
+
{
|
|
373
|
+
object: "list",
|
|
374
|
+
data: branded,
|
|
375
|
+
page: result.page
|
|
376
|
+
}
|
|
377
|
+
];
|
|
378
|
+
},
|
|
379
|
+
retrieve: async (externalAccountId) => {
|
|
380
|
+
if (!config.data) {
|
|
381
|
+
return stub(
|
|
382
|
+
"accounts.externalAccounts.retrieve"
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
const [err, raw] = await tryCatch(
|
|
386
|
+
config.data.query(api.externalAccounts.queries.retrievePersonal, {
|
|
387
|
+
externalAccountId
|
|
388
|
+
})
|
|
389
|
+
);
|
|
390
|
+
if (err) {
|
|
391
|
+
return [
|
|
392
|
+
fromConvexError(err),
|
|
393
|
+
null
|
|
394
|
+
];
|
|
395
|
+
}
|
|
396
|
+
if (!raw) {
|
|
397
|
+
return [
|
|
398
|
+
new CapxulError({
|
|
399
|
+
code: "NOT_FOUND",
|
|
400
|
+
message: `external_account ${externalAccountId} not found`
|
|
401
|
+
}),
|
|
402
|
+
null
|
|
403
|
+
];
|
|
404
|
+
}
|
|
405
|
+
return [
|
|
406
|
+
null,
|
|
407
|
+
brandExternalAccount(
|
|
408
|
+
raw
|
|
409
|
+
)
|
|
410
|
+
];
|
|
411
|
+
},
|
|
412
|
+
remove: async (externalAccountId) => {
|
|
413
|
+
if (!config.data) {
|
|
414
|
+
return stub("accounts.externalAccounts.remove");
|
|
415
|
+
}
|
|
416
|
+
const [err] = await tryCatch(
|
|
417
|
+
config.data.mutation(api.externalAccounts.mutations.removePersonal, {
|
|
418
|
+
externalAccountId
|
|
419
|
+
})
|
|
420
|
+
);
|
|
421
|
+
if (err) {
|
|
422
|
+
return [
|
|
423
|
+
fromConvexError(err),
|
|
424
|
+
null
|
|
425
|
+
];
|
|
426
|
+
}
|
|
427
|
+
return [null, void 0];
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
}
|
|
72
431
|
function createAccountsClient(config = {}) {
|
|
73
432
|
return {
|
|
74
|
-
retrieve: async () =>
|
|
433
|
+
retrieve: async (accountId) => {
|
|
434
|
+
if (!config.data) {
|
|
435
|
+
return stub("accounts.retrieve");
|
|
436
|
+
}
|
|
437
|
+
try {
|
|
438
|
+
const account = await config.data.query(
|
|
439
|
+
api.openfort.queries.getMyAccount,
|
|
440
|
+
{}
|
|
441
|
+
);
|
|
442
|
+
if (account.id !== accountId) {
|
|
443
|
+
return [
|
|
444
|
+
new CapxulError({
|
|
445
|
+
code: "PERMISSION_DENIED",
|
|
446
|
+
message: "accounts.retrieve currently supports the authenticated caller's own account only.",
|
|
447
|
+
details: {
|
|
448
|
+
requestedAccountId: accountId,
|
|
449
|
+
authenticatedAccountId: account.id
|
|
450
|
+
}
|
|
451
|
+
}),
|
|
452
|
+
null
|
|
453
|
+
];
|
|
454
|
+
}
|
|
455
|
+
return [null, account];
|
|
456
|
+
} catch (cause) {
|
|
457
|
+
return [fromConvexError(cause), null];
|
|
458
|
+
}
|
|
459
|
+
},
|
|
75
460
|
lookup: async () => stub("accounts.lookup"),
|
|
76
|
-
update: async () =>
|
|
461
|
+
update: async (input) => {
|
|
462
|
+
if (!config.data) {
|
|
463
|
+
return stub("accounts.update");
|
|
464
|
+
}
|
|
465
|
+
if (input.countryCode !== void 0) {
|
|
466
|
+
return [
|
|
467
|
+
new CapxulError({
|
|
468
|
+
code: "INVALID_INPUT",
|
|
469
|
+
message: "accounts.update does not support countryCode yet \u2014 backend mutation only patches displayName and username.",
|
|
470
|
+
details: { field: "countryCode" }
|
|
471
|
+
}),
|
|
472
|
+
null
|
|
473
|
+
];
|
|
474
|
+
}
|
|
475
|
+
try {
|
|
476
|
+
const current = await config.data.query(
|
|
477
|
+
api.openfort.queries.getMyAccount,
|
|
478
|
+
{}
|
|
479
|
+
);
|
|
480
|
+
if (current.id !== input.accountId) {
|
|
481
|
+
return [
|
|
482
|
+
new CapxulError({
|
|
483
|
+
code: "PERMISSION_DENIED",
|
|
484
|
+
message: "accounts.update currently supports the authenticated caller's own account only.",
|
|
485
|
+
details: {
|
|
486
|
+
requestedAccountId: input.accountId,
|
|
487
|
+
authenticatedAccountId: current.id
|
|
488
|
+
}
|
|
489
|
+
}),
|
|
490
|
+
null
|
|
491
|
+
];
|
|
492
|
+
}
|
|
493
|
+
await config.data.mutation(api.openfort.mutations.updateProfile, {
|
|
494
|
+
displayName: input.name,
|
|
495
|
+
username: input.username
|
|
496
|
+
});
|
|
497
|
+
const updated = await config.data.query(
|
|
498
|
+
api.openfort.queries.getMyAccount,
|
|
499
|
+
{}
|
|
500
|
+
);
|
|
501
|
+
return [null, updated];
|
|
502
|
+
} catch (cause) {
|
|
503
|
+
return [fromConvexError(cause), null];
|
|
504
|
+
}
|
|
505
|
+
},
|
|
77
506
|
provisionPersonal: async (input) => {
|
|
78
507
|
if (!config.data) {
|
|
79
508
|
return stub(
|
|
@@ -153,18 +582,7 @@ function createAccountsClient(config = {}) {
|
|
|
153
582
|
create: async () => stub("accounts.kycProfiles.create"),
|
|
154
583
|
retrieve: async () => stub("accounts.kycProfiles.retrieve")
|
|
155
584
|
},
|
|
156
|
-
externalAccounts:
|
|
157
|
-
create: async () => stub(
|
|
158
|
-
"accounts.externalAccounts.create"
|
|
159
|
-
),
|
|
160
|
-
list: async () => stub(
|
|
161
|
-
"accounts.externalAccounts.list"
|
|
162
|
-
),
|
|
163
|
-
retrieve: async () => stub(
|
|
164
|
-
"accounts.externalAccounts.retrieve"
|
|
165
|
-
),
|
|
166
|
-
remove: async () => stub("accounts.externalAccounts.remove")
|
|
167
|
-
},
|
|
585
|
+
externalAccounts: createAccountExternalAccountsClient(config),
|
|
168
586
|
subAccounts: {
|
|
169
587
|
create: async () => stub("accounts.subAccounts.create"),
|
|
170
588
|
list: async () => stub("accounts.subAccounts.list"),
|
|
@@ -260,7 +678,29 @@ var Errors = {
|
|
|
260
678
|
{ details }
|
|
261
679
|
),
|
|
262
680
|
emailDeliveryFailed: (detail) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`),
|
|
263
|
-
internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`)
|
|
681
|
+
internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`),
|
|
682
|
+
/**
|
|
683
|
+
* Verification gate. Surfaced when a request hits a verification
|
|
684
|
+
* boundary the actor cannot cross under their current state. Two
|
|
685
|
+
* variants share this code:
|
|
686
|
+
*
|
|
687
|
+
* - Rail gate (Withdrawals v1 W2, #465): the resolved
|
|
688
|
+
* `external_account.kind` routes to a withdrawal rail (e.g.
|
|
689
|
+
* `fiat_offramp`, `card_payout`) that is not yet supported. Carries
|
|
690
|
+
* `details.rail` + `details.currentKind`.
|
|
691
|
+
* - KYC tier gate (legacy / future): the actor's KYC tier is below
|
|
692
|
+
* the required tier. Carries `details.requiredTier`.
|
|
693
|
+
*
|
|
694
|
+
* Code is shared because both expose the same UX shape ("you cannot
|
|
695
|
+
* proceed until verification advances"); the `details.*` keys
|
|
696
|
+
* differentiate the route.
|
|
697
|
+
*/
|
|
698
|
+
verificationRequired: (details) => {
|
|
699
|
+
const message = "rail" in details ? `Withdrawal rail "${details.rail}" (kind=${details.currentKind}) is not yet supported.` : `Verification tier ${details.requiredTier} is required.`;
|
|
700
|
+
return new CapxulError2("VERIFICATION_REQUIRED", message, {
|
|
701
|
+
details: { ...details }
|
|
702
|
+
});
|
|
703
|
+
}
|
|
264
704
|
};
|
|
265
705
|
|
|
266
706
|
// ../config/src/safe.ts
|
|
@@ -672,22 +1112,42 @@ function createOrgDocumentsClient() {
|
|
|
672
1112
|
};
|
|
673
1113
|
}
|
|
674
1114
|
|
|
675
|
-
// src/core/external-accounts.ts
|
|
676
|
-
function createExternalAccountsClient() {
|
|
677
|
-
return {
|
|
678
|
-
retrieve: async () => stub("externalAccounts.retrieve"),
|
|
679
|
-
remove: async () => stub("externalAccounts.remove")
|
|
680
|
-
};
|
|
681
|
-
}
|
|
682
|
-
|
|
683
1115
|
// src/core/me.ts
|
|
684
1116
|
function createMeClient(config = {}) {
|
|
685
1117
|
return {
|
|
686
1118
|
get: async () => {
|
|
687
1119
|
if (!config.data) {
|
|
688
|
-
return stub("me.get");
|
|
1120
|
+
return stub("me.get");
|
|
1121
|
+
}
|
|
1122
|
+
try {
|
|
1123
|
+
const account = await config.data.query(
|
|
1124
|
+
api.openfort.queries.getMyAccount,
|
|
1125
|
+
{}
|
|
1126
|
+
);
|
|
1127
|
+
return [null, account];
|
|
1128
|
+
} catch (cause) {
|
|
1129
|
+
return [fromConvexError(cause), null];
|
|
1130
|
+
}
|
|
1131
|
+
},
|
|
1132
|
+
update: async (input) => {
|
|
1133
|
+
if (!config.data) {
|
|
1134
|
+
return stub("me.update");
|
|
1135
|
+
}
|
|
1136
|
+
if (input.countryCode !== void 0) {
|
|
1137
|
+
return [
|
|
1138
|
+
new CapxulError({
|
|
1139
|
+
code: "INVALID_INPUT",
|
|
1140
|
+
message: "me.update does not support countryCode yet \u2014 backend mutation only patches displayName and username.",
|
|
1141
|
+
details: { field: "countryCode" }
|
|
1142
|
+
}),
|
|
1143
|
+
null
|
|
1144
|
+
];
|
|
689
1145
|
}
|
|
690
1146
|
try {
|
|
1147
|
+
await config.data.mutation(api.openfort.mutations.updateProfile, {
|
|
1148
|
+
displayName: input.name,
|
|
1149
|
+
username: input.username
|
|
1150
|
+
});
|
|
691
1151
|
const account = await config.data.query(
|
|
692
1152
|
api.openfort.queries.getMyAccount,
|
|
693
1153
|
{}
|
|
@@ -696,8 +1156,7 @@ function createMeClient(config = {}) {
|
|
|
696
1156
|
} catch (cause) {
|
|
697
1157
|
return [fromConvexError(cause), null];
|
|
698
1158
|
}
|
|
699
|
-
}
|
|
700
|
-
update: async () => stub("me.update")
|
|
1159
|
+
}
|
|
701
1160
|
};
|
|
702
1161
|
}
|
|
703
1162
|
|
|
@@ -1100,24 +1559,378 @@ function createOrgTransfersClient() {
|
|
|
1100
1559
|
cancel: async () => stub("organizations.transfers.cancel")
|
|
1101
1560
|
};
|
|
1102
1561
|
}
|
|
1103
|
-
|
|
1104
|
-
// src/core/withdrawals.ts
|
|
1105
|
-
function createWithdrawalsClient() {
|
|
1562
|
+
function createWithdrawalsClient(config = {}) {
|
|
1106
1563
|
return {
|
|
1107
|
-
create: async () =>
|
|
1108
|
-
|
|
1109
|
-
|
|
1564
|
+
create: async (input) => {
|
|
1565
|
+
if (!config.data) {
|
|
1566
|
+
return stub("withdrawals.create");
|
|
1567
|
+
}
|
|
1568
|
+
const [createErr, createdRaw] = await tryCatch(
|
|
1569
|
+
config.data.mutation(api.withdrawals.mutations.create, {
|
|
1570
|
+
amount: input.amount,
|
|
1571
|
+
destination: {
|
|
1572
|
+
externalAccountId: input.destination.externalAccountId
|
|
1573
|
+
},
|
|
1574
|
+
source: input.source,
|
|
1575
|
+
reference: input.reference,
|
|
1576
|
+
idempotencyKey: input.idempotencyKey
|
|
1577
|
+
})
|
|
1578
|
+
);
|
|
1579
|
+
if (createErr) {
|
|
1580
|
+
return [mapCreateError2(fromConvexError(createErr)), null];
|
|
1581
|
+
}
|
|
1582
|
+
const created = createdRaw;
|
|
1583
|
+
if (!created) {
|
|
1584
|
+
return [
|
|
1585
|
+
new CapxulError({
|
|
1586
|
+
code: "NETWORK_ERROR",
|
|
1587
|
+
message: "withdrawals.create returned no withdrawal resource"
|
|
1588
|
+
}),
|
|
1589
|
+
null
|
|
1590
|
+
];
|
|
1591
|
+
}
|
|
1592
|
+
if (created.status !== "processing" || created.operation.status !== "processing") {
|
|
1593
|
+
return [null, created];
|
|
1594
|
+
}
|
|
1595
|
+
if (!config.signer || !config.signing) {
|
|
1596
|
+
return [null, created];
|
|
1597
|
+
}
|
|
1598
|
+
const [signerErr, currentSigner] = await tryCatch(
|
|
1599
|
+
config.data.query(api.safe.queries.getMySignerAddress, {})
|
|
1600
|
+
);
|
|
1601
|
+
if (signerErr) {
|
|
1602
|
+
return await handleSubmissionFailure(
|
|
1603
|
+
{ data: config.data },
|
|
1604
|
+
created.id,
|
|
1605
|
+
mapCreateError2(fromConvexError(signerErr))
|
|
1606
|
+
);
|
|
1607
|
+
}
|
|
1608
|
+
if (!currentSigner?.address) {
|
|
1609
|
+
return await handleSubmissionFailure(
|
|
1610
|
+
{ data: config.data },
|
|
1611
|
+
created.id,
|
|
1612
|
+
new CapxulError({
|
|
1613
|
+
code: "PERMISSION_DENIED",
|
|
1614
|
+
message: "No signer is registered for the authenticated account.",
|
|
1615
|
+
details: { withdrawalId: created.id }
|
|
1616
|
+
})
|
|
1617
|
+
);
|
|
1618
|
+
}
|
|
1619
|
+
if (getAddress(currentSigner.address) !== getAddress(config.signer.address)) {
|
|
1620
|
+
return await handleSubmissionFailure(
|
|
1621
|
+
{ data: config.data },
|
|
1622
|
+
created.id,
|
|
1623
|
+
new CapxulError({
|
|
1624
|
+
code: "PERMISSION_DENIED",
|
|
1625
|
+
message: "Configured signer does not match the authenticated account signer.",
|
|
1626
|
+
details: {
|
|
1627
|
+
withdrawalId: created.id,
|
|
1628
|
+
expectedSignerAddress: currentSigner.address,
|
|
1629
|
+
actualSignerAddress: config.signer.address
|
|
1630
|
+
}
|
|
1631
|
+
})
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
const [prepErr, submission] = await tryCatch(
|
|
1635
|
+
config.data.query(api.withdrawals.queries.prepareSubmission, {
|
|
1636
|
+
withdrawalId: created.id
|
|
1637
|
+
})
|
|
1638
|
+
);
|
|
1639
|
+
if (prepErr) {
|
|
1640
|
+
return await handleSubmissionFailure(
|
|
1641
|
+
{ data: config.data },
|
|
1642
|
+
created.id,
|
|
1643
|
+
mapCreateError2(fromConvexError(prepErr))
|
|
1644
|
+
);
|
|
1645
|
+
}
|
|
1646
|
+
const destinationAddress = submission?.destinationAddress;
|
|
1647
|
+
if (!submission || !destinationAddress) {
|
|
1648
|
+
return await handleSubmissionFailure(
|
|
1649
|
+
{ data: config.data },
|
|
1650
|
+
created.id,
|
|
1651
|
+
new CapxulError({
|
|
1652
|
+
code: "NETWORK_ERROR",
|
|
1653
|
+
message: "withdrawals.prepareSubmission returned no destination.",
|
|
1654
|
+
details: { withdrawalId: created.id }
|
|
1655
|
+
})
|
|
1656
|
+
);
|
|
1657
|
+
}
|
|
1658
|
+
const [transferErr, transferOk] = await tryCatch(
|
|
1659
|
+
transferAsOwner(
|
|
1660
|
+
{
|
|
1661
|
+
signer: config.signer,
|
|
1662
|
+
signing: config.signing
|
|
1663
|
+
},
|
|
1664
|
+
{
|
|
1665
|
+
tokenAddress: resolvePaymentTokenAddress(submission.amount.currency),
|
|
1666
|
+
recipientAddress: destinationAddress,
|
|
1667
|
+
amount: toTokenUnits(submission.amount.value, 6)
|
|
1668
|
+
}
|
|
1669
|
+
)
|
|
1670
|
+
);
|
|
1671
|
+
if (transferErr) {
|
|
1672
|
+
return await handleSubmissionFailure(
|
|
1673
|
+
{ data: config.data },
|
|
1674
|
+
created.id,
|
|
1675
|
+
mapCreateError2(fromConvexError(transferErr))
|
|
1676
|
+
);
|
|
1677
|
+
}
|
|
1678
|
+
if (!transferOk.success) {
|
|
1679
|
+
return await handleSubmissionFailure(
|
|
1680
|
+
{ data: config.data },
|
|
1681
|
+
created.id,
|
|
1682
|
+
new CapxulError({
|
|
1683
|
+
code: "NETWORK_ERROR",
|
|
1684
|
+
message: "Bundler submission did not succeed.",
|
|
1685
|
+
details: {
|
|
1686
|
+
withdrawalId: created.id,
|
|
1687
|
+
txHash: transferOk.txHash,
|
|
1688
|
+
userOpHash: transferOk.userOpHash
|
|
1689
|
+
}
|
|
1690
|
+
})
|
|
1691
|
+
);
|
|
1692
|
+
}
|
|
1693
|
+
const [recordErr] = await tryCatch(
|
|
1694
|
+
config.data.mutation(api.withdrawals.mutations.recordSubmitted, {
|
|
1695
|
+
withdrawalId: created.id,
|
|
1696
|
+
txHash: transferOk.txHash,
|
|
1697
|
+
userOpHash: transferOk.userOpHash
|
|
1698
|
+
})
|
|
1699
|
+
);
|
|
1700
|
+
if (recordErr) {
|
|
1701
|
+
return [
|
|
1702
|
+
new CapxulError({
|
|
1703
|
+
code: "NETWORK_ERROR",
|
|
1704
|
+
message: "Withdrawal was submitted on-chain, but backend submission tracking failed.",
|
|
1705
|
+
cause: recordErr,
|
|
1706
|
+
details: {
|
|
1707
|
+
withdrawalId: created.id,
|
|
1708
|
+
txHash: transferOk.txHash,
|
|
1709
|
+
userOpHash: transferOk.userOpHash
|
|
1710
|
+
}
|
|
1711
|
+
}),
|
|
1712
|
+
null
|
|
1713
|
+
];
|
|
1714
|
+
}
|
|
1715
|
+
return [null, created];
|
|
1716
|
+
},
|
|
1717
|
+
retrieve: async (withdrawalId) => {
|
|
1718
|
+
if (!config.data) {
|
|
1719
|
+
return stub("withdrawals.retrieve");
|
|
1720
|
+
}
|
|
1721
|
+
const [err, raw] = await tryCatch(
|
|
1722
|
+
config.data.query(api.withdrawals.queries.retrieve, { withdrawalId })
|
|
1723
|
+
);
|
|
1724
|
+
if (err) {
|
|
1725
|
+
return [fromConvexError(err), null];
|
|
1726
|
+
}
|
|
1727
|
+
const withdrawal = raw;
|
|
1728
|
+
if (!withdrawal) {
|
|
1729
|
+
return [
|
|
1730
|
+
new CapxulError({
|
|
1731
|
+
code: "NOT_FOUND",
|
|
1732
|
+
message: `withdrawal ${withdrawalId} not found`
|
|
1733
|
+
}),
|
|
1734
|
+
null
|
|
1735
|
+
];
|
|
1736
|
+
}
|
|
1737
|
+
return [null, withdrawal];
|
|
1738
|
+
},
|
|
1739
|
+
list: async (input) => {
|
|
1740
|
+
if (!config.data) {
|
|
1741
|
+
return stub("withdrawals.list");
|
|
1742
|
+
}
|
|
1743
|
+
const [err, raw] = await tryCatch(
|
|
1744
|
+
config.data.query(api.withdrawals.queries.list, {
|
|
1745
|
+
limit: input?.limit,
|
|
1746
|
+
cursor: input?.cursor
|
|
1747
|
+
})
|
|
1748
|
+
);
|
|
1749
|
+
if (err) {
|
|
1750
|
+
return [fromConvexError(err), null];
|
|
1751
|
+
}
|
|
1752
|
+
return [null, raw];
|
|
1753
|
+
},
|
|
1754
|
+
recordCompleted: async (input) => {
|
|
1755
|
+
if (!config.data) {
|
|
1756
|
+
return stub(
|
|
1757
|
+
"withdrawals.recordCompleted"
|
|
1758
|
+
);
|
|
1759
|
+
}
|
|
1760
|
+
const [err] = await tryCatch(
|
|
1761
|
+
config.data.mutation(api.withdrawals.mutations.recordCompleted, {
|
|
1762
|
+
withdrawalId: input.withdrawalId,
|
|
1763
|
+
txHash: input.txHash
|
|
1764
|
+
})
|
|
1765
|
+
);
|
|
1766
|
+
if (err) {
|
|
1767
|
+
return [
|
|
1768
|
+
mapRecordCompletedError(fromConvexError(err)),
|
|
1769
|
+
null
|
|
1770
|
+
];
|
|
1771
|
+
}
|
|
1772
|
+
return [null, null];
|
|
1773
|
+
}
|
|
1110
1774
|
};
|
|
1111
1775
|
}
|
|
1112
|
-
function createOrgWithdrawalsClient() {
|
|
1776
|
+
function createOrgWithdrawalsClient(config = {}) {
|
|
1113
1777
|
return {
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1778
|
+
/**
|
|
1779
|
+
* Org-scope create (Withdrawals v1 W2, #465).
|
|
1780
|
+
*
|
|
1781
|
+
* D6 — returns the `processing` row only. No `transferAsOwner`
|
|
1782
|
+
* tail, no `recordSubmitted` call. Org Safe + Zodiac submission
|
|
1783
|
+
* orchestration ships in W3+.
|
|
1784
|
+
*/
|
|
1785
|
+
create: async (input) => {
|
|
1786
|
+
if (!config.data) {
|
|
1787
|
+
return stub(
|
|
1788
|
+
"organizations.withdrawals.create"
|
|
1789
|
+
);
|
|
1790
|
+
}
|
|
1791
|
+
const [err, raw] = await tryCatch(
|
|
1792
|
+
config.data.mutation(api.withdrawals.mutations.createOrg, {
|
|
1793
|
+
organizationId: input.organizationId,
|
|
1794
|
+
amount: input.amount,
|
|
1795
|
+
destination: {
|
|
1796
|
+
externalAccountId: input.destination.externalAccountId
|
|
1797
|
+
},
|
|
1798
|
+
source: input.source,
|
|
1799
|
+
reference: input.reference,
|
|
1800
|
+
idempotencyKey: input.idempotencyKey
|
|
1801
|
+
})
|
|
1802
|
+
);
|
|
1803
|
+
if (err) {
|
|
1804
|
+
return [mapCreateError2(fromConvexError(err)), null];
|
|
1805
|
+
}
|
|
1806
|
+
const created = raw;
|
|
1807
|
+
if (!created) {
|
|
1808
|
+
return [
|
|
1809
|
+
new CapxulError({
|
|
1810
|
+
code: "NETWORK_ERROR",
|
|
1811
|
+
message: "organizations.withdrawals.create returned no withdrawal resource"
|
|
1812
|
+
}),
|
|
1813
|
+
null
|
|
1814
|
+
];
|
|
1815
|
+
}
|
|
1816
|
+
return [null, created];
|
|
1817
|
+
},
|
|
1818
|
+
retrieve: async (input) => {
|
|
1819
|
+
if (!config.data) {
|
|
1820
|
+
return stub(
|
|
1821
|
+
"organizations.withdrawals.retrieve"
|
|
1822
|
+
);
|
|
1823
|
+
}
|
|
1824
|
+
const [err, raw] = await tryCatch(
|
|
1825
|
+
config.data.query(api.withdrawals.queries.retrieve, {
|
|
1826
|
+
withdrawalId: input.withdrawalId
|
|
1827
|
+
})
|
|
1828
|
+
);
|
|
1829
|
+
if (err) {
|
|
1830
|
+
return [fromConvexError(err), null];
|
|
1831
|
+
}
|
|
1832
|
+
const withdrawal = raw;
|
|
1833
|
+
if (!withdrawal) {
|
|
1834
|
+
return [
|
|
1835
|
+
new CapxulError({
|
|
1836
|
+
code: "NOT_FOUND",
|
|
1837
|
+
message: `withdrawal ${input.withdrawalId} not found`
|
|
1838
|
+
}),
|
|
1839
|
+
null
|
|
1840
|
+
];
|
|
1841
|
+
}
|
|
1842
|
+
const ownerCheck = withdrawal.owner;
|
|
1843
|
+
if (ownerCheck?.type !== "organization" || ownerCheck.id !== input.organizationId) {
|
|
1844
|
+
return [
|
|
1845
|
+
new CapxulError({
|
|
1846
|
+
code: "NOT_FOUND",
|
|
1847
|
+
message: `withdrawal ${input.withdrawalId} does not belong to organization ${input.organizationId}`
|
|
1848
|
+
}),
|
|
1849
|
+
null
|
|
1850
|
+
];
|
|
1851
|
+
}
|
|
1852
|
+
return [null, withdrawal];
|
|
1853
|
+
},
|
|
1854
|
+
list: async (input) => {
|
|
1855
|
+
if (!config.data) {
|
|
1856
|
+
return stub(
|
|
1857
|
+
"organizations.withdrawals.list"
|
|
1858
|
+
);
|
|
1859
|
+
}
|
|
1860
|
+
const [err, raw] = await tryCatch(
|
|
1861
|
+
config.data.query(api.withdrawals.queries.listOrg, {
|
|
1862
|
+
organizationId: input.organizationId,
|
|
1863
|
+
limit: input.limit,
|
|
1864
|
+
cursor: input.cursor
|
|
1865
|
+
})
|
|
1866
|
+
);
|
|
1867
|
+
if (err) {
|
|
1868
|
+
return [fromConvexError(err), null];
|
|
1869
|
+
}
|
|
1870
|
+
return [null, raw];
|
|
1871
|
+
}
|
|
1119
1872
|
};
|
|
1120
1873
|
}
|
|
1874
|
+
async function handleSubmissionFailure(config, withdrawalId, error) {
|
|
1875
|
+
await bestEffortMarkFailed2(config, withdrawalId, error);
|
|
1876
|
+
return [error, null];
|
|
1877
|
+
}
|
|
1878
|
+
async function bestEffortMarkFailed2(config, withdrawalId, error) {
|
|
1879
|
+
await tryCatch(
|
|
1880
|
+
config.data.mutation(api.withdrawals.mutations.markFailed, {
|
|
1881
|
+
withdrawalId,
|
|
1882
|
+
errorCode: error.code,
|
|
1883
|
+
errorMessage: error.message
|
|
1884
|
+
})
|
|
1885
|
+
);
|
|
1886
|
+
}
|
|
1887
|
+
function mapCreateError2(error) {
|
|
1888
|
+
switch (error.code) {
|
|
1889
|
+
case "NOT_AUTHENTICATED":
|
|
1890
|
+
case "PERMISSION_DENIED":
|
|
1891
|
+
case "INVALID_INPUT":
|
|
1892
|
+
case "INSUFFICIENT_BALANCE":
|
|
1893
|
+
case "IDEMPOTENCY_CONFLICT":
|
|
1894
|
+
case "KYC_REQUIRED":
|
|
1895
|
+
case "POLICY_DENIED":
|
|
1896
|
+
case "RATE_LIMITED":
|
|
1897
|
+
case "NETWORK_ERROR":
|
|
1898
|
+
case "NOT_FOUND":
|
|
1899
|
+
case "VERIFICATION_REQUIRED":
|
|
1900
|
+
return error;
|
|
1901
|
+
default:
|
|
1902
|
+
return new CapxulError({
|
|
1903
|
+
code: "NETWORK_ERROR",
|
|
1904
|
+
message: error.message,
|
|
1905
|
+
cause: error,
|
|
1906
|
+
details: error.details,
|
|
1907
|
+
operationId: error.operationId,
|
|
1908
|
+
correlationId: error.correlationId,
|
|
1909
|
+
retryable: error.retryable
|
|
1910
|
+
});
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
function mapRecordCompletedError(error) {
|
|
1914
|
+
switch (error.code) {
|
|
1915
|
+
case "NOT_AUTHENTICATED":
|
|
1916
|
+
case "PERMISSION_DENIED":
|
|
1917
|
+
case "INVALID_INPUT":
|
|
1918
|
+
case "NOT_FOUND":
|
|
1919
|
+
case "NETWORK_ERROR":
|
|
1920
|
+
case "INTERNAL_ERROR":
|
|
1921
|
+
return error;
|
|
1922
|
+
default:
|
|
1923
|
+
return new CapxulError({
|
|
1924
|
+
code: "NETWORK_ERROR",
|
|
1925
|
+
message: error.message,
|
|
1926
|
+
cause: error,
|
|
1927
|
+
details: error.details,
|
|
1928
|
+
operationId: error.operationId,
|
|
1929
|
+
correlationId: error.correlationId,
|
|
1930
|
+
retryable: error.retryable
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1121
1934
|
|
|
1122
1935
|
// src/core/webhook-endpoints.ts
|
|
1123
1936
|
function createWebhookEndpointsClient() {
|
|
@@ -1138,8 +1951,138 @@ function createWebhookEventsClient() {
|
|
|
1138
1951
|
list: async () => stub("webhookEvents.list")
|
|
1139
1952
|
};
|
|
1140
1953
|
}
|
|
1141
|
-
|
|
1142
|
-
// src/core/organizations.ts
|
|
1954
|
+
|
|
1955
|
+
// src/core/organizations.ts
|
|
1956
|
+
function createOrgExternalAccountsClient(config) {
|
|
1957
|
+
return {
|
|
1958
|
+
create: async (input) => {
|
|
1959
|
+
if (!config.data) {
|
|
1960
|
+
return stub(
|
|
1961
|
+
"organizations.externalAccounts.create"
|
|
1962
|
+
);
|
|
1963
|
+
}
|
|
1964
|
+
const [err, raw] = await tryCatch(
|
|
1965
|
+
config.data.mutation(api.externalAccounts.mutations.createOrg, {
|
|
1966
|
+
organizationId: input.organizationId,
|
|
1967
|
+
kind: input.kind,
|
|
1968
|
+
label: input.label,
|
|
1969
|
+
address: input.address,
|
|
1970
|
+
iban: input.iban,
|
|
1971
|
+
bic: input.bic,
|
|
1972
|
+
accountHolder: input.accountHolder,
|
|
1973
|
+
network: input.network,
|
|
1974
|
+
panToken: input.panToken,
|
|
1975
|
+
last4: input.last4
|
|
1976
|
+
})
|
|
1977
|
+
);
|
|
1978
|
+
if (err) {
|
|
1979
|
+
return [
|
|
1980
|
+
fromConvexError(err),
|
|
1981
|
+
null
|
|
1982
|
+
];
|
|
1983
|
+
}
|
|
1984
|
+
if (!raw) {
|
|
1985
|
+
return [
|
|
1986
|
+
new CapxulError({
|
|
1987
|
+
code: "NOT_FOUND",
|
|
1988
|
+
message: "external_account creation returned no resource"
|
|
1989
|
+
}),
|
|
1990
|
+
null
|
|
1991
|
+
];
|
|
1992
|
+
}
|
|
1993
|
+
return [
|
|
1994
|
+
null,
|
|
1995
|
+
brandExternalAccount(
|
|
1996
|
+
raw
|
|
1997
|
+
)
|
|
1998
|
+
];
|
|
1999
|
+
},
|
|
2000
|
+
list: async (input) => {
|
|
2001
|
+
if (!config.data) {
|
|
2002
|
+
return stub(
|
|
2003
|
+
"organizations.externalAccounts.list"
|
|
2004
|
+
);
|
|
2005
|
+
}
|
|
2006
|
+
const [err, result] = await tryCatch(
|
|
2007
|
+
config.data.query(api.externalAccounts.queries.listOrg, {
|
|
2008
|
+
organizationId: input.organizationId,
|
|
2009
|
+
limit: input.limit,
|
|
2010
|
+
cursor: input.cursor
|
|
2011
|
+
})
|
|
2012
|
+
);
|
|
2013
|
+
if (err) {
|
|
2014
|
+
return [fromConvexError(err), null];
|
|
2015
|
+
}
|
|
2016
|
+
const branded = result.data.map(
|
|
2017
|
+
(row) => brandExternalAccount(
|
|
2018
|
+
row
|
|
2019
|
+
)
|
|
2020
|
+
);
|
|
2021
|
+
return [
|
|
2022
|
+
null,
|
|
2023
|
+
{
|
|
2024
|
+
object: "list",
|
|
2025
|
+
data: branded,
|
|
2026
|
+
page: result.page
|
|
2027
|
+
}
|
|
2028
|
+
];
|
|
2029
|
+
},
|
|
2030
|
+
retrieve: async (input) => {
|
|
2031
|
+
if (!config.data) {
|
|
2032
|
+
return stub(
|
|
2033
|
+
"organizations.externalAccounts.retrieve"
|
|
2034
|
+
);
|
|
2035
|
+
}
|
|
2036
|
+
const [err, raw] = await tryCatch(
|
|
2037
|
+
config.data.query(api.externalAccounts.queries.retrieveOrg, {
|
|
2038
|
+
organizationId: input.organizationId,
|
|
2039
|
+
externalAccountId: input.externalAccountId
|
|
2040
|
+
})
|
|
2041
|
+
);
|
|
2042
|
+
if (err) {
|
|
2043
|
+
return [
|
|
2044
|
+
fromConvexError(err),
|
|
2045
|
+
null
|
|
2046
|
+
];
|
|
2047
|
+
}
|
|
2048
|
+
if (!raw) {
|
|
2049
|
+
return [
|
|
2050
|
+
new CapxulError({
|
|
2051
|
+
code: "NOT_FOUND",
|
|
2052
|
+
message: `external_account ${input.externalAccountId} not found`
|
|
2053
|
+
}),
|
|
2054
|
+
null
|
|
2055
|
+
];
|
|
2056
|
+
}
|
|
2057
|
+
return [
|
|
2058
|
+
null,
|
|
2059
|
+
brandExternalAccount(
|
|
2060
|
+
raw
|
|
2061
|
+
)
|
|
2062
|
+
];
|
|
2063
|
+
},
|
|
2064
|
+
remove: async (input) => {
|
|
2065
|
+
if (!config.data) {
|
|
2066
|
+
return stub(
|
|
2067
|
+
"organizations.externalAccounts.remove"
|
|
2068
|
+
);
|
|
2069
|
+
}
|
|
2070
|
+
const [err] = await tryCatch(
|
|
2071
|
+
config.data.mutation(api.externalAccounts.mutations.removeOrg, {
|
|
2072
|
+
organizationId: input.organizationId,
|
|
2073
|
+
externalAccountId: input.externalAccountId
|
|
2074
|
+
})
|
|
2075
|
+
);
|
|
2076
|
+
if (err) {
|
|
2077
|
+
return [
|
|
2078
|
+
fromConvexError(err),
|
|
2079
|
+
null
|
|
2080
|
+
];
|
|
2081
|
+
}
|
|
2082
|
+
return [null, void 0];
|
|
2083
|
+
}
|
|
2084
|
+
};
|
|
2085
|
+
}
|
|
1143
2086
|
function createOrganizationsClient(config = {}) {
|
|
1144
2087
|
return {
|
|
1145
2088
|
create: async () => stub("organizations.create"),
|
|
@@ -1195,18 +2138,7 @@ function createOrganizationsClient(config = {}) {
|
|
|
1195
2138
|
retrieve: async () => stub("organizations.subAccounts.retrieve"),
|
|
1196
2139
|
remove: async () => stub("organizations.subAccounts.remove")
|
|
1197
2140
|
},
|
|
1198
|
-
externalAccounts:
|
|
1199
|
-
create: async () => stub(
|
|
1200
|
-
"organizations.externalAccounts.create"
|
|
1201
|
-
),
|
|
1202
|
-
list: async () => stub(
|
|
1203
|
-
"organizations.externalAccounts.list"
|
|
1204
|
-
),
|
|
1205
|
-
retrieve: async () => stub(
|
|
1206
|
-
"organizations.externalAccounts.retrieve"
|
|
1207
|
-
),
|
|
1208
|
-
remove: async () => stub("organizations.externalAccounts.remove")
|
|
1209
|
-
},
|
|
2141
|
+
externalAccounts: createOrgExternalAccountsClient(config),
|
|
1210
2142
|
balanceLedger: {
|
|
1211
2143
|
list: async () => stub(
|
|
1212
2144
|
"organizations.balanceLedger.list"
|
|
@@ -1217,7 +2149,7 @@ function createOrganizationsClient(config = {}) {
|
|
|
1217
2149
|
},
|
|
1218
2150
|
payments: createOrgPaymentsClient(),
|
|
1219
2151
|
transfers: createOrgTransfersClient(),
|
|
1220
|
-
withdrawals: createOrgWithdrawalsClient(),
|
|
2152
|
+
withdrawals: createOrgWithdrawalsClient(config),
|
|
1221
2153
|
documents: createOrgDocumentsClient(),
|
|
1222
2154
|
webhookEndpoints: createWebhookEndpointsClient(),
|
|
1223
2155
|
webhookEvents: createWebhookEventsClient()
|
|
@@ -1232,6 +2164,96 @@ function createSubAccountsClient() {
|
|
|
1232
2164
|
};
|
|
1233
2165
|
}
|
|
1234
2166
|
|
|
2167
|
+
// src/core/token-transfers.ts
|
|
2168
|
+
var toTokenTransferId = (raw) => {
|
|
2169
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
2170
|
+
throw new Error(
|
|
2171
|
+
`Invalid tokenTransferId: expected non-empty string, got ${String(raw)}`
|
|
2172
|
+
);
|
|
2173
|
+
}
|
|
2174
|
+
return raw;
|
|
2175
|
+
};
|
|
2176
|
+
function brandRow(row) {
|
|
2177
|
+
return {
|
|
2178
|
+
...row,
|
|
2179
|
+
id: toTokenTransferId(row.id)
|
|
2180
|
+
};
|
|
2181
|
+
}
|
|
2182
|
+
function createTokenTransfersClient(config = {}) {
|
|
2183
|
+
return {
|
|
2184
|
+
list: async (input) => {
|
|
2185
|
+
if (!config.data) {
|
|
2186
|
+
return stub("tokenTransfers.list");
|
|
2187
|
+
}
|
|
2188
|
+
try {
|
|
2189
|
+
const raw = await config.data.query(
|
|
2190
|
+
api.tokenTransfers.queries.list,
|
|
2191
|
+
{
|
|
2192
|
+
limit: input?.limit,
|
|
2193
|
+
cursor: input?.cursor,
|
|
2194
|
+
direction: input?.direction
|
|
2195
|
+
}
|
|
2196
|
+
);
|
|
2197
|
+
if (!raw) {
|
|
2198
|
+
return [
|
|
2199
|
+
new CapxulError({
|
|
2200
|
+
code: "NOT_AUTHENTICATED",
|
|
2201
|
+
message: "tokenTransfers.list requires an authenticated session."
|
|
2202
|
+
}),
|
|
2203
|
+
null
|
|
2204
|
+
];
|
|
2205
|
+
}
|
|
2206
|
+
return [
|
|
2207
|
+
null,
|
|
2208
|
+
{
|
|
2209
|
+
object: "list",
|
|
2210
|
+
data: raw.items.map(brandRow),
|
|
2211
|
+
page: {
|
|
2212
|
+
hasMore: raw.hasMore,
|
|
2213
|
+
nextCursor: raw.nextCursor
|
|
2214
|
+
},
|
|
2215
|
+
displayCurrency: raw.displayCurrency
|
|
2216
|
+
}
|
|
2217
|
+
];
|
|
2218
|
+
} catch (cause) {
|
|
2219
|
+
return [fromConvexError(cause), null];
|
|
2220
|
+
}
|
|
2221
|
+
},
|
|
2222
|
+
retrieve: async (input) => {
|
|
2223
|
+
if (!config.data) {
|
|
2224
|
+
return stub("tokenTransfers.retrieve");
|
|
2225
|
+
}
|
|
2226
|
+
try {
|
|
2227
|
+
const raw = await config.data.query(
|
|
2228
|
+
api.tokenTransfers.queries.getByTxLogIndex,
|
|
2229
|
+
{
|
|
2230
|
+
txHash: input.txHash,
|
|
2231
|
+
logIndex: input.logIndex,
|
|
2232
|
+
chainId: input.chainId
|
|
2233
|
+
}
|
|
2234
|
+
);
|
|
2235
|
+
if (!raw) {
|
|
2236
|
+
return [
|
|
2237
|
+
new CapxulError({
|
|
2238
|
+
code: "NOT_FOUND",
|
|
2239
|
+
message: `tokenTransfer (txHash=${input.txHash}, logIndex=${input.logIndex}) not found or not visible to caller.`,
|
|
2240
|
+
details: {
|
|
2241
|
+
txHash: input.txHash,
|
|
2242
|
+
logIndex: input.logIndex,
|
|
2243
|
+
chainId: input.chainId
|
|
2244
|
+
}
|
|
2245
|
+
}),
|
|
2246
|
+
null
|
|
2247
|
+
];
|
|
2248
|
+
}
|
|
2249
|
+
return [null, brandRow(raw)];
|
|
2250
|
+
} catch (cause) {
|
|
2251
|
+
return [fromConvexError(cause), null];
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
};
|
|
2255
|
+
}
|
|
2256
|
+
|
|
1235
2257
|
// src/core/virtual-accounts.ts
|
|
1236
2258
|
function createVirtualAccountsClient() {
|
|
1237
2259
|
return {
|
|
@@ -1253,59 +2275,6 @@ function createVirtualCardsClient() {
|
|
|
1253
2275
|
cancel: async () => stub("virtualCards.cancel")
|
|
1254
2276
|
};
|
|
1255
2277
|
}
|
|
1256
|
-
|
|
1257
|
-
// ../observability/src/try-catch.ts
|
|
1258
|
-
async function tryCatch(promise) {
|
|
1259
|
-
try {
|
|
1260
|
-
return [null, await promise];
|
|
1261
|
-
} catch (e) {
|
|
1262
|
-
return [e instanceof Error ? e : new Error(String(e)), null];
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
|
-
// ../observability/src/debug-log.ts
|
|
1267
|
-
function isDevelopmentBuild() {
|
|
1268
|
-
if (typeof process === "undefined") {
|
|
1269
|
-
return false;
|
|
1270
|
-
}
|
|
1271
|
-
return process.env?.NODE_ENV === "development" || process.env?.NODE_ENV === "test";
|
|
1272
|
-
}
|
|
1273
|
-
function debugLog(line) {
|
|
1274
|
-
if (!isDevelopmentBuild()) return;
|
|
1275
|
-
if (typeof globalThis !== "undefined" && "window" in globalThis && typeof console?.info === "function") {
|
|
1276
|
-
console.info(line);
|
|
1277
|
-
return;
|
|
1278
|
-
}
|
|
1279
|
-
if (typeof process !== "undefined" && typeof process.stderr?.write === "function") {
|
|
1280
|
-
process.stderr.write(`${line}
|
|
1281
|
-
`);
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
function formatDebugValue(value) {
|
|
1285
|
-
if (value === void 0 || value === "") return "";
|
|
1286
|
-
if (typeof value === "string") return value;
|
|
1287
|
-
try {
|
|
1288
|
-
return JSON.stringify(value);
|
|
1289
|
-
} catch {
|
|
1290
|
-
return String(value);
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
function track(...args) {
|
|
1294
|
-
const [name, props] = args;
|
|
1295
|
-
debugLog(`[TRACK] ${name} ${formatDebugValue(props ?? "")}`);
|
|
1296
|
-
}
|
|
1297
|
-
function formatDebugValue2(value) {
|
|
1298
|
-
if (value === void 0 || value === "") return "";
|
|
1299
|
-
if (typeof value === "string") return value;
|
|
1300
|
-
try {
|
|
1301
|
-
return JSON.stringify(value);
|
|
1302
|
-
} catch {
|
|
1303
|
-
return String(value);
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
function identify(userId, traits) {
|
|
1307
|
-
debugLog(`[IDENTIFY] ${userId} ${formatDebugValue2(traits ?? "")}`);
|
|
1308
|
-
}
|
|
1309
2278
|
function createAuthFlowMachine(client) {
|
|
1310
2279
|
return setup({
|
|
1311
2280
|
types: {},
|
|
@@ -1581,122 +2550,6 @@ function emailDomain(email) {
|
|
|
1581
2550
|
const domain = email.split("@")[1]?.trim().toLowerCase();
|
|
1582
2551
|
return domain || "unknown";
|
|
1583
2552
|
}
|
|
1584
|
-
|
|
1585
|
-
// ../platform-kernel/src/ids.ts
|
|
1586
|
-
function makePrefixedIdConstructor(prefix, fieldName) {
|
|
1587
|
-
const re = new RegExp(`^${prefix}_[A-Za-z0-9_\\-]+$`);
|
|
1588
|
-
return (raw) => {
|
|
1589
|
-
if (typeof raw !== "string" || !re.test(raw)) {
|
|
1590
|
-
throw new Error(
|
|
1591
|
-
`Invalid ${fieldName}: expected string matching ^${prefix}_[A-Za-z0-9_\\-]+$, got ${String(raw)}`
|
|
1592
|
-
);
|
|
1593
|
-
}
|
|
1594
|
-
return raw;
|
|
1595
|
-
};
|
|
1596
|
-
}
|
|
1597
|
-
var toAccountId = makePrefixedIdConstructor(
|
|
1598
|
-
"acct",
|
|
1599
|
-
"accountId"
|
|
1600
|
-
);
|
|
1601
|
-
var toOrganizationId = makePrefixedIdConstructor("org", "organizationId");
|
|
1602
|
-
var toMemberId = makePrefixedIdConstructor(
|
|
1603
|
-
"mem",
|
|
1604
|
-
"memberId"
|
|
1605
|
-
);
|
|
1606
|
-
var toSafeId = makePrefixedIdConstructor(
|
|
1607
|
-
"safe",
|
|
1608
|
-
"safeId"
|
|
1609
|
-
);
|
|
1610
|
-
var toTreasuryId = makePrefixedIdConstructor(
|
|
1611
|
-
"try",
|
|
1612
|
-
"treasuryId"
|
|
1613
|
-
);
|
|
1614
|
-
var toApiKeyId = makePrefixedIdConstructor(
|
|
1615
|
-
"ak",
|
|
1616
|
-
"apiKeyId"
|
|
1617
|
-
);
|
|
1618
|
-
var toOperationId = makePrefixedIdConstructor(
|
|
1619
|
-
"op",
|
|
1620
|
-
"operationId"
|
|
1621
|
-
);
|
|
1622
|
-
var toKycProfileId = makePrefixedIdConstructor(
|
|
1623
|
-
"kyc",
|
|
1624
|
-
"kycProfileId"
|
|
1625
|
-
);
|
|
1626
|
-
var toKybProfileId = makePrefixedIdConstructor(
|
|
1627
|
-
"kyb",
|
|
1628
|
-
"kybProfileId"
|
|
1629
|
-
);
|
|
1630
|
-
var toExternalAccountId = makePrefixedIdConstructor("ext", "externalAccountId");
|
|
1631
|
-
var toPaymentId = makePrefixedIdConstructor(
|
|
1632
|
-
"pay",
|
|
1633
|
-
"paymentId"
|
|
1634
|
-
);
|
|
1635
|
-
var toWithdrawalId = makePrefixedIdConstructor(
|
|
1636
|
-
"wd",
|
|
1637
|
-
"withdrawalId"
|
|
1638
|
-
);
|
|
1639
|
-
var toWebhookEndpointId = makePrefixedIdConstructor("we", "webhookEndpointId");
|
|
1640
|
-
var toWebhookEventId = makePrefixedIdConstructor("evt", "webhookEventId");
|
|
1641
|
-
var toSubAccountId = makePrefixedIdConstructor(
|
|
1642
|
-
"sub",
|
|
1643
|
-
"subAccountId"
|
|
1644
|
-
);
|
|
1645
|
-
var toVirtualAccountId = makePrefixedIdConstructor("va", "virtualAccountId");
|
|
1646
|
-
var toVirtualCardId = makePrefixedIdConstructor(
|
|
1647
|
-
"vc",
|
|
1648
|
-
"virtualCardId"
|
|
1649
|
-
);
|
|
1650
|
-
var toTransferId = makePrefixedIdConstructor(
|
|
1651
|
-
"txfr",
|
|
1652
|
-
"transferId"
|
|
1653
|
-
);
|
|
1654
|
-
var toDocumentId = makePrefixedIdConstructor(
|
|
1655
|
-
"doc",
|
|
1656
|
-
"documentId"
|
|
1657
|
-
);
|
|
1658
|
-
var toBalanceLedgerEntryId = makePrefixedIdConstructor("bal", "balanceLedgerEntryId");
|
|
1659
|
-
|
|
1660
|
-
// ../platform-kernel/src/value-objects.ts
|
|
1661
|
-
var PHONE_NUMBER_RE = /^\+[1-9]\d{1,14}$/;
|
|
1662
|
-
function toPhoneNumber(raw) {
|
|
1663
|
-
if (typeof raw !== "string" || !PHONE_NUMBER_RE.test(raw)) {
|
|
1664
|
-
throw new Error(
|
|
1665
|
-
`Invalid phoneNumber: expected E.164 string matching ^\\+[1-9]\\d{1,14}$, got ${String(raw)}`
|
|
1666
|
-
);
|
|
1667
|
-
}
|
|
1668
|
-
return raw;
|
|
1669
|
-
}
|
|
1670
|
-
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1671
|
-
function toEmail(raw) {
|
|
1672
|
-
if (typeof raw !== "string") {
|
|
1673
|
-
throw new Error(
|
|
1674
|
-
`Invalid email: expected string, got ${String(raw)}`
|
|
1675
|
-
);
|
|
1676
|
-
}
|
|
1677
|
-
const lowered = raw.trim().toLowerCase();
|
|
1678
|
-
if (!EMAIL_RE.test(lowered)) {
|
|
1679
|
-
throw new Error(
|
|
1680
|
-
`Invalid email: expected local@domain.tld, got ${String(raw)}`
|
|
1681
|
-
);
|
|
1682
|
-
}
|
|
1683
|
-
return lowered;
|
|
1684
|
-
}
|
|
1685
|
-
var USERNAME_RE = /^[a-z][a-z0-9_-]{2,29}$/;
|
|
1686
|
-
function toUsername(raw) {
|
|
1687
|
-
if (typeof raw !== "string") {
|
|
1688
|
-
throw new Error(
|
|
1689
|
-
`Invalid username: expected string, got ${String(raw)}`
|
|
1690
|
-
);
|
|
1691
|
-
}
|
|
1692
|
-
const lowered = raw.toLowerCase();
|
|
1693
|
-
if (!USERNAME_RE.test(lowered)) {
|
|
1694
|
-
throw new Error(
|
|
1695
|
-
`Invalid username: expected 3-30 chars, letter-first, [a-z0-9_-], got ${String(raw)}`
|
|
1696
|
-
);
|
|
1697
|
-
}
|
|
1698
|
-
return lowered;
|
|
1699
|
-
}
|
|
1700
2553
|
function createProvisioningMachine(client) {
|
|
1701
2554
|
return setup({
|
|
1702
2555
|
types: {},
|
|
@@ -2083,12 +2936,13 @@ function createCapxulClient(config = {}) {
|
|
|
2083
2936
|
organizations: createOrganizationsClient(config),
|
|
2084
2937
|
payments: createPaymentsClient(config),
|
|
2085
2938
|
transfers: createTransfersClient(),
|
|
2086
|
-
|
|
2939
|
+
tokenTransfers: createTokenTransfersClient(config),
|
|
2940
|
+
withdrawals: createWithdrawalsClient(config),
|
|
2087
2941
|
documents: createDocumentsClient(),
|
|
2088
2942
|
subAccounts: createSubAccountsClient(),
|
|
2089
2943
|
virtualAccounts: createVirtualAccountsClient(),
|
|
2090
2944
|
virtualCards: createVirtualCardsClient(),
|
|
2091
|
-
externalAccounts: createExternalAccountsClient(),
|
|
2945
|
+
externalAccounts: createExternalAccountsClient(config),
|
|
2092
2946
|
operations: createOperationsClient(config),
|
|
2093
2947
|
webhookEndpoints: createWebhookEndpointsClient(),
|
|
2094
2948
|
webhookEvents: createWebhookEventsClient(),
|
|
@@ -2201,4 +3055,4 @@ function isWebhookEvent(value) {
|
|
|
2201
3055
|
return typeof candidate.id === "string" && typeof candidate.type === "string" && typeof candidate.createdAt === "string" && (candidate.operationId === void 0 || typeof candidate.operationId === "string") && (candidate.correlationId === void 0 || typeof candidate.correlationId === "string") && !!candidate.data && typeof candidate.data === "object" && !Array.isArray(candidate.data);
|
|
2202
3056
|
}
|
|
2203
3057
|
|
|
2204
|
-
export { CapxulError, createAuthFlowMachine, createCapxulClient, createLocalSigner, createOnboardingFlowMachine, createProvisioningMachine as createProvisioningFlowMachine, makeHttpTransport, matchAction, matchError, matchStatus, toAccountId, toApiKeyId, toBalanceLedgerEntryId, toDocumentId, toEmail, toExternalAccountId, toKybProfileId, toKycProfileId, toMemberId, toOperationId, toOrganizationId, toPaymentId, toPhoneNumber, toSafeId, toSubAccountId, toTransferId, toTreasuryId, toUsername, toVirtualAccountId, toVirtualCardId, toWebhookEndpointId, toWebhookEventId, toWithdrawalId, tryCatch, verifyWebhook };
|
|
3058
|
+
export { CapxulError, createAuthFlowMachine, createCapxulClient, createLocalSigner, createOnboardingFlowMachine, createProvisioningMachine as createProvisioningFlowMachine, makeHttpTransport, matchAction, matchError, matchStatus, toAccountId, toApiKeyId, toBalanceLedgerEntryId, toDocumentId, toEmail, toExternalAccountId, toKybProfileId, toKycProfileId, toMemberId, toOperationId, toOrganizationId, toPaymentId, toPhoneNumber, toSafeId, toSubAccountId, toTokenTransferId, toTransferId, toTreasuryId, toUsername, toVirtualAccountId, toVirtualCardId, toWebhookEndpointId, toWebhookEventId, toWithdrawalId, tryCatch, verifyWebhook };
|