@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.cjs
CHANGED
|
@@ -70,12 +70,441 @@ 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
|
-
retrieve: async () =>
|
|
435
|
+
retrieve: async (accountId) => {
|
|
436
|
+
if (!config.data) {
|
|
437
|
+
return stub("accounts.retrieve");
|
|
438
|
+
}
|
|
439
|
+
try {
|
|
440
|
+
const account = await config.data.query(
|
|
441
|
+
api.openfort.queries.getMyAccount,
|
|
442
|
+
{}
|
|
443
|
+
);
|
|
444
|
+
if (account.id !== accountId) {
|
|
445
|
+
return [
|
|
446
|
+
new CapxulError({
|
|
447
|
+
code: "PERMISSION_DENIED",
|
|
448
|
+
message: "accounts.retrieve currently supports the authenticated caller's own account only.",
|
|
449
|
+
details: {
|
|
450
|
+
requestedAccountId: accountId,
|
|
451
|
+
authenticatedAccountId: account.id
|
|
452
|
+
}
|
|
453
|
+
}),
|
|
454
|
+
null
|
|
455
|
+
];
|
|
456
|
+
}
|
|
457
|
+
return [null, account];
|
|
458
|
+
} catch (cause) {
|
|
459
|
+
return [fromConvexError(cause), null];
|
|
460
|
+
}
|
|
461
|
+
},
|
|
77
462
|
lookup: async () => stub("accounts.lookup"),
|
|
78
|
-
update: async () =>
|
|
463
|
+
update: async (input) => {
|
|
464
|
+
if (!config.data) {
|
|
465
|
+
return stub("accounts.update");
|
|
466
|
+
}
|
|
467
|
+
if (input.countryCode !== void 0) {
|
|
468
|
+
return [
|
|
469
|
+
new CapxulError({
|
|
470
|
+
code: "INVALID_INPUT",
|
|
471
|
+
message: "accounts.update does not support countryCode yet \u2014 backend mutation only patches displayName and username.",
|
|
472
|
+
details: { field: "countryCode" }
|
|
473
|
+
}),
|
|
474
|
+
null
|
|
475
|
+
];
|
|
476
|
+
}
|
|
477
|
+
try {
|
|
478
|
+
const current = await config.data.query(
|
|
479
|
+
api.openfort.queries.getMyAccount,
|
|
480
|
+
{}
|
|
481
|
+
);
|
|
482
|
+
if (current.id !== input.accountId) {
|
|
483
|
+
return [
|
|
484
|
+
new CapxulError({
|
|
485
|
+
code: "PERMISSION_DENIED",
|
|
486
|
+
message: "accounts.update currently supports the authenticated caller's own account only.",
|
|
487
|
+
details: {
|
|
488
|
+
requestedAccountId: input.accountId,
|
|
489
|
+
authenticatedAccountId: current.id
|
|
490
|
+
}
|
|
491
|
+
}),
|
|
492
|
+
null
|
|
493
|
+
];
|
|
494
|
+
}
|
|
495
|
+
await config.data.mutation(api.openfort.mutations.updateProfile, {
|
|
496
|
+
displayName: input.name,
|
|
497
|
+
username: input.username
|
|
498
|
+
});
|
|
499
|
+
const updated = await config.data.query(
|
|
500
|
+
api.openfort.queries.getMyAccount,
|
|
501
|
+
{}
|
|
502
|
+
);
|
|
503
|
+
return [null, updated];
|
|
504
|
+
} catch (cause) {
|
|
505
|
+
return [fromConvexError(cause), null];
|
|
506
|
+
}
|
|
507
|
+
},
|
|
79
508
|
provisionPersonal: async (input) => {
|
|
80
509
|
if (!config.data) {
|
|
81
510
|
return stub(
|
|
@@ -155,18 +584,7 @@ function createAccountsClient(config = {}) {
|
|
|
155
584
|
create: async () => stub("accounts.kycProfiles.create"),
|
|
156
585
|
retrieve: async () => stub("accounts.kycProfiles.retrieve")
|
|
157
586
|
},
|
|
158
|
-
externalAccounts:
|
|
159
|
-
create: async () => stub(
|
|
160
|
-
"accounts.externalAccounts.create"
|
|
161
|
-
),
|
|
162
|
-
list: async () => stub(
|
|
163
|
-
"accounts.externalAccounts.list"
|
|
164
|
-
),
|
|
165
|
-
retrieve: async () => stub(
|
|
166
|
-
"accounts.externalAccounts.retrieve"
|
|
167
|
-
),
|
|
168
|
-
remove: async () => stub("accounts.externalAccounts.remove")
|
|
169
|
-
},
|
|
587
|
+
externalAccounts: createAccountExternalAccountsClient(config),
|
|
170
588
|
subAccounts: {
|
|
171
589
|
create: async () => stub("accounts.subAccounts.create"),
|
|
172
590
|
list: async () => stub("accounts.subAccounts.list"),
|
|
@@ -262,7 +680,29 @@ var Errors = {
|
|
|
262
680
|
{ details }
|
|
263
681
|
),
|
|
264
682
|
emailDeliveryFailed: (detail) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`),
|
|
265
|
-
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
|
+
}
|
|
266
706
|
};
|
|
267
707
|
|
|
268
708
|
// ../config/src/safe.ts
|
|
@@ -674,22 +1114,42 @@ function createOrgDocumentsClient() {
|
|
|
674
1114
|
};
|
|
675
1115
|
}
|
|
676
1116
|
|
|
677
|
-
// src/core/external-accounts.ts
|
|
678
|
-
function createExternalAccountsClient() {
|
|
679
|
-
return {
|
|
680
|
-
retrieve: async () => stub("externalAccounts.retrieve"),
|
|
681
|
-
remove: async () => stub("externalAccounts.remove")
|
|
682
|
-
};
|
|
683
|
-
}
|
|
684
|
-
|
|
685
1117
|
// src/core/me.ts
|
|
686
1118
|
function createMeClient(config = {}) {
|
|
687
1119
|
return {
|
|
688
1120
|
get: async () => {
|
|
689
1121
|
if (!config.data) {
|
|
690
|
-
return stub("me.get");
|
|
1122
|
+
return stub("me.get");
|
|
1123
|
+
}
|
|
1124
|
+
try {
|
|
1125
|
+
const account = await config.data.query(
|
|
1126
|
+
api.openfort.queries.getMyAccount,
|
|
1127
|
+
{}
|
|
1128
|
+
);
|
|
1129
|
+
return [null, account];
|
|
1130
|
+
} catch (cause) {
|
|
1131
|
+
return [fromConvexError(cause), null];
|
|
1132
|
+
}
|
|
1133
|
+
},
|
|
1134
|
+
update: async (input) => {
|
|
1135
|
+
if (!config.data) {
|
|
1136
|
+
return stub("me.update");
|
|
1137
|
+
}
|
|
1138
|
+
if (input.countryCode !== void 0) {
|
|
1139
|
+
return [
|
|
1140
|
+
new CapxulError({
|
|
1141
|
+
code: "INVALID_INPUT",
|
|
1142
|
+
message: "me.update does not support countryCode yet \u2014 backend mutation only patches displayName and username.",
|
|
1143
|
+
details: { field: "countryCode" }
|
|
1144
|
+
}),
|
|
1145
|
+
null
|
|
1146
|
+
];
|
|
691
1147
|
}
|
|
692
1148
|
try {
|
|
1149
|
+
await config.data.mutation(api.openfort.mutations.updateProfile, {
|
|
1150
|
+
displayName: input.name,
|
|
1151
|
+
username: input.username
|
|
1152
|
+
});
|
|
693
1153
|
const account = await config.data.query(
|
|
694
1154
|
api.openfort.queries.getMyAccount,
|
|
695
1155
|
{}
|
|
@@ -698,8 +1158,7 @@ function createMeClient(config = {}) {
|
|
|
698
1158
|
} catch (cause) {
|
|
699
1159
|
return [fromConvexError(cause), null];
|
|
700
1160
|
}
|
|
701
|
-
}
|
|
702
|
-
update: async () => stub("me.update")
|
|
1161
|
+
}
|
|
703
1162
|
};
|
|
704
1163
|
}
|
|
705
1164
|
|
|
@@ -1102,24 +1561,378 @@ function createOrgTransfersClient() {
|
|
|
1102
1561
|
cancel: async () => stub("organizations.transfers.cancel")
|
|
1103
1562
|
};
|
|
1104
1563
|
}
|
|
1105
|
-
|
|
1106
|
-
// src/core/withdrawals.ts
|
|
1107
|
-
function createWithdrawalsClient() {
|
|
1564
|
+
function createWithdrawalsClient(config = {}) {
|
|
1108
1565
|
return {
|
|
1109
|
-
create: async () =>
|
|
1110
|
-
|
|
1111
|
-
|
|
1566
|
+
create: async (input) => {
|
|
1567
|
+
if (!config.data) {
|
|
1568
|
+
return stub("withdrawals.create");
|
|
1569
|
+
}
|
|
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))
|
|
1608
|
+
);
|
|
1609
|
+
}
|
|
1610
|
+
if (!currentSigner?.address) {
|
|
1611
|
+
return await handleSubmissionFailure(
|
|
1612
|
+
{ data: config.data },
|
|
1613
|
+
created.id,
|
|
1614
|
+
new CapxulError({
|
|
1615
|
+
code: "PERMISSION_DENIED",
|
|
1616
|
+
message: "No signer is registered for the authenticated account.",
|
|
1617
|
+
details: { withdrawalId: created.id }
|
|
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({
|
|
1626
|
+
code: "PERMISSION_DENIED",
|
|
1627
|
+
message: "Configured signer does not match the authenticated account signer.",
|
|
1628
|
+
details: {
|
|
1629
|
+
withdrawalId: created.id,
|
|
1630
|
+
expectedSignerAddress: currentSigner.address,
|
|
1631
|
+
actualSignerAddress: config.signer.address
|
|
1632
|
+
}
|
|
1633
|
+
})
|
|
1634
|
+
);
|
|
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({
|
|
1654
|
+
code: "NETWORK_ERROR",
|
|
1655
|
+
message: "withdrawals.prepareSubmission returned no destination.",
|
|
1656
|
+
details: { withdrawalId: created.id }
|
|
1657
|
+
})
|
|
1658
|
+
);
|
|
1659
|
+
}
|
|
1660
|
+
const [transferErr, transferOk] = await tryCatch(
|
|
1661
|
+
transferAsOwner(
|
|
1662
|
+
{
|
|
1663
|
+
signer: config.signer,
|
|
1664
|
+
signing: config.signing
|
|
1665
|
+
},
|
|
1666
|
+
{
|
|
1667
|
+
tokenAddress: resolvePaymentTokenAddress(submission.amount.currency),
|
|
1668
|
+
recipientAddress: destinationAddress,
|
|
1669
|
+
amount: toTokenUnits(submission.amount.value, 6)
|
|
1670
|
+
}
|
|
1671
|
+
)
|
|
1672
|
+
);
|
|
1673
|
+
if (transferErr) {
|
|
1674
|
+
return await handleSubmissionFailure(
|
|
1675
|
+
{ data: config.data },
|
|
1676
|
+
created.id,
|
|
1677
|
+
mapCreateError2(fromConvexError(transferErr))
|
|
1678
|
+
);
|
|
1679
|
+
}
|
|
1680
|
+
if (!transferOk.success) {
|
|
1681
|
+
return await handleSubmissionFailure(
|
|
1682
|
+
{ data: config.data },
|
|
1683
|
+
created.id,
|
|
1684
|
+
new CapxulError({
|
|
1685
|
+
code: "NETWORK_ERROR",
|
|
1686
|
+
message: "Bundler submission did not succeed.",
|
|
1687
|
+
details: {
|
|
1688
|
+
withdrawalId: created.id,
|
|
1689
|
+
txHash: transferOk.txHash,
|
|
1690
|
+
userOpHash: transferOk.userOpHash
|
|
1691
|
+
}
|
|
1692
|
+
})
|
|
1693
|
+
);
|
|
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];
|
|
1718
|
+
},
|
|
1719
|
+
retrieve: async (withdrawalId) => {
|
|
1720
|
+
if (!config.data) {
|
|
1721
|
+
return stub("withdrawals.retrieve");
|
|
1722
|
+
}
|
|
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) {
|
|
1731
|
+
return [
|
|
1732
|
+
new CapxulError({
|
|
1733
|
+
code: "NOT_FOUND",
|
|
1734
|
+
message: `withdrawal ${withdrawalId} not found`
|
|
1735
|
+
}),
|
|
1736
|
+
null
|
|
1737
|
+
];
|
|
1738
|
+
}
|
|
1739
|
+
return [null, withdrawal];
|
|
1740
|
+
},
|
|
1741
|
+
list: async (input) => {
|
|
1742
|
+
if (!config.data) {
|
|
1743
|
+
return stub("withdrawals.list");
|
|
1744
|
+
}
|
|
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"
|
|
1760
|
+
);
|
|
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) {
|
|
1769
|
+
return [
|
|
1770
|
+
mapRecordCompletedError(fromConvexError(err)),
|
|
1771
|
+
null
|
|
1772
|
+
];
|
|
1773
|
+
}
|
|
1774
|
+
return [null, null];
|
|
1775
|
+
}
|
|
1112
1776
|
};
|
|
1113
1777
|
}
|
|
1114
|
-
function createOrgWithdrawalsClient() {
|
|
1778
|
+
function createOrgWithdrawalsClient(config = {}) {
|
|
1115
1779
|
return {
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
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
|
+
},
|
|
1820
|
+
retrieve: async (input) => {
|
|
1821
|
+
if (!config.data) {
|
|
1822
|
+
return stub(
|
|
1823
|
+
"organizations.withdrawals.retrieve"
|
|
1824
|
+
);
|
|
1825
|
+
}
|
|
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) {
|
|
1836
|
+
return [
|
|
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
|
+
}),
|
|
1851
|
+
null
|
|
1852
|
+
];
|
|
1853
|
+
}
|
|
1854
|
+
return [null, withdrawal];
|
|
1855
|
+
},
|
|
1856
|
+
list: async (input) => {
|
|
1857
|
+
if (!config.data) {
|
|
1858
|
+
return stub(
|
|
1859
|
+
"organizations.withdrawals.list"
|
|
1860
|
+
);
|
|
1861
|
+
}
|
|
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];
|
|
1871
|
+
}
|
|
1872
|
+
return [null, raw];
|
|
1873
|
+
}
|
|
1121
1874
|
};
|
|
1122
1875
|
}
|
|
1876
|
+
async function handleSubmissionFailure(config, withdrawalId, error) {
|
|
1877
|
+
await bestEffortMarkFailed2(config, withdrawalId, error);
|
|
1878
|
+
return [error, null];
|
|
1879
|
+
}
|
|
1880
|
+
async function bestEffortMarkFailed2(config, withdrawalId, error) {
|
|
1881
|
+
await tryCatch(
|
|
1882
|
+
config.data.mutation(api.withdrawals.mutations.markFailed, {
|
|
1883
|
+
withdrawalId,
|
|
1884
|
+
errorCode: error.code,
|
|
1885
|
+
errorMessage: error.message
|
|
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
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
function mapRecordCompletedError(error) {
|
|
1916
|
+
switch (error.code) {
|
|
1917
|
+
case "NOT_AUTHENTICATED":
|
|
1918
|
+
case "PERMISSION_DENIED":
|
|
1919
|
+
case "INVALID_INPUT":
|
|
1920
|
+
case "NOT_FOUND":
|
|
1921
|
+
case "NETWORK_ERROR":
|
|
1922
|
+
case "INTERNAL_ERROR":
|
|
1923
|
+
return error;
|
|
1924
|
+
default:
|
|
1925
|
+
return new CapxulError({
|
|
1926
|
+
code: "NETWORK_ERROR",
|
|
1927
|
+
message: error.message,
|
|
1928
|
+
cause: error,
|
|
1929
|
+
details: error.details,
|
|
1930
|
+
operationId: error.operationId,
|
|
1931
|
+
correlationId: error.correlationId,
|
|
1932
|
+
retryable: error.retryable
|
|
1933
|
+
});
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1123
1936
|
|
|
1124
1937
|
// src/core/webhook-endpoints.ts
|
|
1125
1938
|
function createWebhookEndpointsClient() {
|
|
@@ -1140,8 +1953,138 @@ function createWebhookEventsClient() {
|
|
|
1140
1953
|
list: async () => stub("webhookEvents.list")
|
|
1141
1954
|
};
|
|
1142
1955
|
}
|
|
1143
|
-
|
|
1144
|
-
// src/core/organizations.ts
|
|
1956
|
+
|
|
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
|
+
}
|
|
1145
2088
|
function createOrganizationsClient(config = {}) {
|
|
1146
2089
|
return {
|
|
1147
2090
|
create: async () => stub("organizations.create"),
|
|
@@ -1197,18 +2140,7 @@ function createOrganizationsClient(config = {}) {
|
|
|
1197
2140
|
retrieve: async () => stub("organizations.subAccounts.retrieve"),
|
|
1198
2141
|
remove: async () => stub("organizations.subAccounts.remove")
|
|
1199
2142
|
},
|
|
1200
|
-
externalAccounts:
|
|
1201
|
-
create: async () => stub(
|
|
1202
|
-
"organizations.externalAccounts.create"
|
|
1203
|
-
),
|
|
1204
|
-
list: async () => stub(
|
|
1205
|
-
"organizations.externalAccounts.list"
|
|
1206
|
-
),
|
|
1207
|
-
retrieve: async () => stub(
|
|
1208
|
-
"organizations.externalAccounts.retrieve"
|
|
1209
|
-
),
|
|
1210
|
-
remove: async () => stub("organizations.externalAccounts.remove")
|
|
1211
|
-
},
|
|
2143
|
+
externalAccounts: createOrgExternalAccountsClient(config),
|
|
1212
2144
|
balanceLedger: {
|
|
1213
2145
|
list: async () => stub(
|
|
1214
2146
|
"organizations.balanceLedger.list"
|
|
@@ -1219,7 +2151,7 @@ function createOrganizationsClient(config = {}) {
|
|
|
1219
2151
|
},
|
|
1220
2152
|
payments: createOrgPaymentsClient(),
|
|
1221
2153
|
transfers: createOrgTransfersClient(),
|
|
1222
|
-
withdrawals: createOrgWithdrawalsClient(),
|
|
2154
|
+
withdrawals: createOrgWithdrawalsClient(config),
|
|
1223
2155
|
documents: createOrgDocumentsClient(),
|
|
1224
2156
|
webhookEndpoints: createWebhookEndpointsClient(),
|
|
1225
2157
|
webhookEvents: createWebhookEventsClient()
|
|
@@ -1234,6 +2166,96 @@ function createSubAccountsClient() {
|
|
|
1234
2166
|
};
|
|
1235
2167
|
}
|
|
1236
2168
|
|
|
2169
|
+
// src/core/token-transfers.ts
|
|
2170
|
+
var toTokenTransferId = (raw) => {
|
|
2171
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
2172
|
+
throw new Error(
|
|
2173
|
+
`Invalid tokenTransferId: expected non-empty string, got ${String(raw)}`
|
|
2174
|
+
);
|
|
2175
|
+
}
|
|
2176
|
+
return raw;
|
|
2177
|
+
};
|
|
2178
|
+
function brandRow(row) {
|
|
2179
|
+
return {
|
|
2180
|
+
...row,
|
|
2181
|
+
id: toTokenTransferId(row.id)
|
|
2182
|
+
};
|
|
2183
|
+
}
|
|
2184
|
+
function createTokenTransfersClient(config = {}) {
|
|
2185
|
+
return {
|
|
2186
|
+
list: async (input) => {
|
|
2187
|
+
if (!config.data) {
|
|
2188
|
+
return stub("tokenTransfers.list");
|
|
2189
|
+
}
|
|
2190
|
+
try {
|
|
2191
|
+
const raw = await config.data.query(
|
|
2192
|
+
api.tokenTransfers.queries.list,
|
|
2193
|
+
{
|
|
2194
|
+
limit: input?.limit,
|
|
2195
|
+
cursor: input?.cursor,
|
|
2196
|
+
direction: input?.direction
|
|
2197
|
+
}
|
|
2198
|
+
);
|
|
2199
|
+
if (!raw) {
|
|
2200
|
+
return [
|
|
2201
|
+
new CapxulError({
|
|
2202
|
+
code: "NOT_AUTHENTICATED",
|
|
2203
|
+
message: "tokenTransfers.list requires an authenticated session."
|
|
2204
|
+
}),
|
|
2205
|
+
null
|
|
2206
|
+
];
|
|
2207
|
+
}
|
|
2208
|
+
return [
|
|
2209
|
+
null,
|
|
2210
|
+
{
|
|
2211
|
+
object: "list",
|
|
2212
|
+
data: raw.items.map(brandRow),
|
|
2213
|
+
page: {
|
|
2214
|
+
hasMore: raw.hasMore,
|
|
2215
|
+
nextCursor: raw.nextCursor
|
|
2216
|
+
},
|
|
2217
|
+
displayCurrency: raw.displayCurrency
|
|
2218
|
+
}
|
|
2219
|
+
];
|
|
2220
|
+
} catch (cause) {
|
|
2221
|
+
return [fromConvexError(cause), null];
|
|
2222
|
+
}
|
|
2223
|
+
},
|
|
2224
|
+
retrieve: async (input) => {
|
|
2225
|
+
if (!config.data) {
|
|
2226
|
+
return stub("tokenTransfers.retrieve");
|
|
2227
|
+
}
|
|
2228
|
+
try {
|
|
2229
|
+
const raw = await config.data.query(
|
|
2230
|
+
api.tokenTransfers.queries.getByTxLogIndex,
|
|
2231
|
+
{
|
|
2232
|
+
txHash: input.txHash,
|
|
2233
|
+
logIndex: input.logIndex,
|
|
2234
|
+
chainId: input.chainId
|
|
2235
|
+
}
|
|
2236
|
+
);
|
|
2237
|
+
if (!raw) {
|
|
2238
|
+
return [
|
|
2239
|
+
new CapxulError({
|
|
2240
|
+
code: "NOT_FOUND",
|
|
2241
|
+
message: `tokenTransfer (txHash=${input.txHash}, logIndex=${input.logIndex}) not found or not visible to caller.`,
|
|
2242
|
+
details: {
|
|
2243
|
+
txHash: input.txHash,
|
|
2244
|
+
logIndex: input.logIndex,
|
|
2245
|
+
chainId: input.chainId
|
|
2246
|
+
}
|
|
2247
|
+
}),
|
|
2248
|
+
null
|
|
2249
|
+
];
|
|
2250
|
+
}
|
|
2251
|
+
return [null, brandRow(raw)];
|
|
2252
|
+
} catch (cause) {
|
|
2253
|
+
return [fromConvexError(cause), null];
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
};
|
|
2257
|
+
}
|
|
2258
|
+
|
|
1237
2259
|
// src/core/virtual-accounts.ts
|
|
1238
2260
|
function createVirtualAccountsClient() {
|
|
1239
2261
|
return {
|
|
@@ -1255,59 +2277,6 @@ function createVirtualCardsClient() {
|
|
|
1255
2277
|
cancel: async () => stub("virtualCards.cancel")
|
|
1256
2278
|
};
|
|
1257
2279
|
}
|
|
1258
|
-
|
|
1259
|
-
// ../observability/src/try-catch.ts
|
|
1260
|
-
async function tryCatch(promise) {
|
|
1261
|
-
try {
|
|
1262
|
-
return [null, await promise];
|
|
1263
|
-
} catch (e) {
|
|
1264
|
-
return [e instanceof Error ? e : new Error(String(e)), null];
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
// ../observability/src/debug-log.ts
|
|
1269
|
-
function isDevelopmentBuild() {
|
|
1270
|
-
if (typeof process === "undefined") {
|
|
1271
|
-
return false;
|
|
1272
|
-
}
|
|
1273
|
-
return process.env?.NODE_ENV === "development" || process.env?.NODE_ENV === "test";
|
|
1274
|
-
}
|
|
1275
|
-
function debugLog(line) {
|
|
1276
|
-
if (!isDevelopmentBuild()) return;
|
|
1277
|
-
if (typeof globalThis !== "undefined" && "window" in globalThis && typeof console?.info === "function") {
|
|
1278
|
-
console.info(line);
|
|
1279
|
-
return;
|
|
1280
|
-
}
|
|
1281
|
-
if (typeof process !== "undefined" && typeof process.stderr?.write === "function") {
|
|
1282
|
-
process.stderr.write(`${line}
|
|
1283
|
-
`);
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
function formatDebugValue(value) {
|
|
1287
|
-
if (value === void 0 || value === "") return "";
|
|
1288
|
-
if (typeof value === "string") return value;
|
|
1289
|
-
try {
|
|
1290
|
-
return JSON.stringify(value);
|
|
1291
|
-
} catch {
|
|
1292
|
-
return String(value);
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
function track(...args) {
|
|
1296
|
-
const [name, props] = args;
|
|
1297
|
-
debugLog(`[TRACK] ${name} ${formatDebugValue(props ?? "")}`);
|
|
1298
|
-
}
|
|
1299
|
-
function formatDebugValue2(value) {
|
|
1300
|
-
if (value === void 0 || value === "") return "";
|
|
1301
|
-
if (typeof value === "string") return value;
|
|
1302
|
-
try {
|
|
1303
|
-
return JSON.stringify(value);
|
|
1304
|
-
} catch {
|
|
1305
|
-
return String(value);
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
|
-
function identify(userId, traits) {
|
|
1309
|
-
debugLog(`[IDENTIFY] ${userId} ${formatDebugValue2(traits ?? "")}`);
|
|
1310
|
-
}
|
|
1311
2280
|
function createAuthFlowMachine(client) {
|
|
1312
2281
|
return xstate.setup({
|
|
1313
2282
|
types: {},
|
|
@@ -1583,122 +2552,6 @@ function emailDomain(email) {
|
|
|
1583
2552
|
const domain = email.split("@")[1]?.trim().toLowerCase();
|
|
1584
2553
|
return domain || "unknown";
|
|
1585
2554
|
}
|
|
1586
|
-
|
|
1587
|
-
// ../platform-kernel/src/ids.ts
|
|
1588
|
-
function makePrefixedIdConstructor(prefix, fieldName) {
|
|
1589
|
-
const re = new RegExp(`^${prefix}_[A-Za-z0-9_\\-]+$`);
|
|
1590
|
-
return (raw) => {
|
|
1591
|
-
if (typeof raw !== "string" || !re.test(raw)) {
|
|
1592
|
-
throw new Error(
|
|
1593
|
-
`Invalid ${fieldName}: expected string matching ^${prefix}_[A-Za-z0-9_\\-]+$, got ${String(raw)}`
|
|
1594
|
-
);
|
|
1595
|
-
}
|
|
1596
|
-
return raw;
|
|
1597
|
-
};
|
|
1598
|
-
}
|
|
1599
|
-
var toAccountId = makePrefixedIdConstructor(
|
|
1600
|
-
"acct",
|
|
1601
|
-
"accountId"
|
|
1602
|
-
);
|
|
1603
|
-
var toOrganizationId = makePrefixedIdConstructor("org", "organizationId");
|
|
1604
|
-
var toMemberId = makePrefixedIdConstructor(
|
|
1605
|
-
"mem",
|
|
1606
|
-
"memberId"
|
|
1607
|
-
);
|
|
1608
|
-
var toSafeId = makePrefixedIdConstructor(
|
|
1609
|
-
"safe",
|
|
1610
|
-
"safeId"
|
|
1611
|
-
);
|
|
1612
|
-
var toTreasuryId = makePrefixedIdConstructor(
|
|
1613
|
-
"try",
|
|
1614
|
-
"treasuryId"
|
|
1615
|
-
);
|
|
1616
|
-
var toApiKeyId = makePrefixedIdConstructor(
|
|
1617
|
-
"ak",
|
|
1618
|
-
"apiKeyId"
|
|
1619
|
-
);
|
|
1620
|
-
var toOperationId = makePrefixedIdConstructor(
|
|
1621
|
-
"op",
|
|
1622
|
-
"operationId"
|
|
1623
|
-
);
|
|
1624
|
-
var toKycProfileId = makePrefixedIdConstructor(
|
|
1625
|
-
"kyc",
|
|
1626
|
-
"kycProfileId"
|
|
1627
|
-
);
|
|
1628
|
-
var toKybProfileId = makePrefixedIdConstructor(
|
|
1629
|
-
"kyb",
|
|
1630
|
-
"kybProfileId"
|
|
1631
|
-
);
|
|
1632
|
-
var toExternalAccountId = makePrefixedIdConstructor("ext", "externalAccountId");
|
|
1633
|
-
var toPaymentId = makePrefixedIdConstructor(
|
|
1634
|
-
"pay",
|
|
1635
|
-
"paymentId"
|
|
1636
|
-
);
|
|
1637
|
-
var toWithdrawalId = makePrefixedIdConstructor(
|
|
1638
|
-
"wd",
|
|
1639
|
-
"withdrawalId"
|
|
1640
|
-
);
|
|
1641
|
-
var toWebhookEndpointId = makePrefixedIdConstructor("we", "webhookEndpointId");
|
|
1642
|
-
var toWebhookEventId = makePrefixedIdConstructor("evt", "webhookEventId");
|
|
1643
|
-
var toSubAccountId = makePrefixedIdConstructor(
|
|
1644
|
-
"sub",
|
|
1645
|
-
"subAccountId"
|
|
1646
|
-
);
|
|
1647
|
-
var toVirtualAccountId = makePrefixedIdConstructor("va", "virtualAccountId");
|
|
1648
|
-
var toVirtualCardId = makePrefixedIdConstructor(
|
|
1649
|
-
"vc",
|
|
1650
|
-
"virtualCardId"
|
|
1651
|
-
);
|
|
1652
|
-
var toTransferId = makePrefixedIdConstructor(
|
|
1653
|
-
"txfr",
|
|
1654
|
-
"transferId"
|
|
1655
|
-
);
|
|
1656
|
-
var toDocumentId = makePrefixedIdConstructor(
|
|
1657
|
-
"doc",
|
|
1658
|
-
"documentId"
|
|
1659
|
-
);
|
|
1660
|
-
var toBalanceLedgerEntryId = makePrefixedIdConstructor("bal", "balanceLedgerEntryId");
|
|
1661
|
-
|
|
1662
|
-
// ../platform-kernel/src/value-objects.ts
|
|
1663
|
-
var PHONE_NUMBER_RE = /^\+[1-9]\d{1,14}$/;
|
|
1664
|
-
function toPhoneNumber(raw) {
|
|
1665
|
-
if (typeof raw !== "string" || !PHONE_NUMBER_RE.test(raw)) {
|
|
1666
|
-
throw new Error(
|
|
1667
|
-
`Invalid phoneNumber: expected E.164 string matching ^\\+[1-9]\\d{1,14}$, got ${String(raw)}`
|
|
1668
|
-
);
|
|
1669
|
-
}
|
|
1670
|
-
return raw;
|
|
1671
|
-
}
|
|
1672
|
-
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1673
|
-
function toEmail(raw) {
|
|
1674
|
-
if (typeof raw !== "string") {
|
|
1675
|
-
throw new Error(
|
|
1676
|
-
`Invalid email: expected string, got ${String(raw)}`
|
|
1677
|
-
);
|
|
1678
|
-
}
|
|
1679
|
-
const lowered = raw.trim().toLowerCase();
|
|
1680
|
-
if (!EMAIL_RE.test(lowered)) {
|
|
1681
|
-
throw new Error(
|
|
1682
|
-
`Invalid email: expected local@domain.tld, got ${String(raw)}`
|
|
1683
|
-
);
|
|
1684
|
-
}
|
|
1685
|
-
return lowered;
|
|
1686
|
-
}
|
|
1687
|
-
var USERNAME_RE = /^[a-z][a-z0-9_-]{2,29}$/;
|
|
1688
|
-
function toUsername(raw) {
|
|
1689
|
-
if (typeof raw !== "string") {
|
|
1690
|
-
throw new Error(
|
|
1691
|
-
`Invalid username: expected string, got ${String(raw)}`
|
|
1692
|
-
);
|
|
1693
|
-
}
|
|
1694
|
-
const lowered = raw.toLowerCase();
|
|
1695
|
-
if (!USERNAME_RE.test(lowered)) {
|
|
1696
|
-
throw new Error(
|
|
1697
|
-
`Invalid username: expected 3-30 chars, letter-first, [a-z0-9_-], got ${String(raw)}`
|
|
1698
|
-
);
|
|
1699
|
-
}
|
|
1700
|
-
return lowered;
|
|
1701
|
-
}
|
|
1702
2555
|
function createProvisioningMachine(client) {
|
|
1703
2556
|
return xstate.setup({
|
|
1704
2557
|
types: {},
|
|
@@ -2085,12 +2938,13 @@ function createCapxulClient(config = {}) {
|
|
|
2085
2938
|
organizations: createOrganizationsClient(config),
|
|
2086
2939
|
payments: createPaymentsClient(config),
|
|
2087
2940
|
transfers: createTransfersClient(),
|
|
2088
|
-
|
|
2941
|
+
tokenTransfers: createTokenTransfersClient(config),
|
|
2942
|
+
withdrawals: createWithdrawalsClient(config),
|
|
2089
2943
|
documents: createDocumentsClient(),
|
|
2090
2944
|
subAccounts: createSubAccountsClient(),
|
|
2091
2945
|
virtualAccounts: createVirtualAccountsClient(),
|
|
2092
2946
|
virtualCards: createVirtualCardsClient(),
|
|
2093
|
-
externalAccounts: createExternalAccountsClient(),
|
|
2947
|
+
externalAccounts: createExternalAccountsClient(config),
|
|
2094
2948
|
operations: createOperationsClient(config),
|
|
2095
2949
|
webhookEndpoints: createWebhookEndpointsClient(),
|
|
2096
2950
|
webhookEvents: createWebhookEventsClient(),
|
|
@@ -2228,6 +3082,7 @@ exports.toPaymentId = toPaymentId;
|
|
|
2228
3082
|
exports.toPhoneNumber = toPhoneNumber;
|
|
2229
3083
|
exports.toSafeId = toSafeId;
|
|
2230
3084
|
exports.toSubAccountId = toSubAccountId;
|
|
3085
|
+
exports.toTokenTransferId = toTokenTransferId;
|
|
2231
3086
|
exports.toTransferId = toTransferId;
|
|
2232
3087
|
exports.toTreasuryId = toTreasuryId;
|
|
2233
3088
|
exports.toUsername = toUsername;
|