@capxul/sdk 0.1.0-alpha.4 → 0.1.0-alpha.8
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 +21 -0
- package/README.md +87 -5
- package/dist/{client-CcCtq5XO.d.ts → client-ByzDfG98.d.ts} +77 -34
- package/dist/{client-CXRKtbfn.d.cts → client-DDAVWtzJ.d.cts} +77 -34
- package/dist/client.cjs +894 -302
- package/dist/client.d.cts +2 -3
- package/dist/client.d.ts +2 -3
- package/dist/client.js +894 -302
- package/dist/index.cjs +1000 -418
- package/dist/index.d.cts +9 -10
- package/dist/index.d.ts +9 -10
- package/dist/index.js +1000 -418
- package/dist/types-PM4AQRLP.d.ts +1136 -0
- package/dist/types-hfcOE7Oi.d.cts +1136 -0
- package/dist/webhooks.d.cts +1 -2
- package/dist/webhooks.d.ts +1 -2
- package/package.json +5 -5
- package/dist/types-75UokagF.d.ts +0 -202
- package/dist/types-DVWojoy4.d.cts +0 -202
package/dist/index.cjs
CHANGED
|
@@ -70,7 +70,366 @@ function fromConvexError(error) {
|
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
// ../observability/src/try-catch.ts
|
|
74
|
+
async function tryCatch(promise) {
|
|
75
|
+
try {
|
|
76
|
+
return [null, await promise];
|
|
77
|
+
} catch (e) {
|
|
78
|
+
return [e instanceof Error ? e : new Error(String(e)), null];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ../observability/src/debug-log.ts
|
|
83
|
+
function isDevelopmentBuild() {
|
|
84
|
+
if (typeof process === "undefined") {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return process.env?.NODE_ENV === "development" || process.env?.NODE_ENV === "test";
|
|
88
|
+
}
|
|
89
|
+
function debugLog(line) {
|
|
90
|
+
if (!isDevelopmentBuild()) return;
|
|
91
|
+
if (typeof globalThis !== "undefined" && "window" in globalThis && typeof console?.info === "function") {
|
|
92
|
+
console.info(line);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (typeof process !== "undefined" && typeof process.stderr?.write === "function") {
|
|
96
|
+
process.stderr.write(`${line}
|
|
97
|
+
`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function formatDebugValue(value) {
|
|
101
|
+
if (value === void 0 || value === "") return "";
|
|
102
|
+
if (typeof value === "string") return value;
|
|
103
|
+
try {
|
|
104
|
+
return JSON.stringify(value);
|
|
105
|
+
} catch {
|
|
106
|
+
return String(value);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function track(...args) {
|
|
110
|
+
const [name, props] = args;
|
|
111
|
+
debugLog(`[TRACK] ${name} ${formatDebugValue(props ?? "")}`);
|
|
112
|
+
}
|
|
113
|
+
function formatDebugValue2(value) {
|
|
114
|
+
if (value === void 0 || value === "") return "";
|
|
115
|
+
if (typeof value === "string") return value;
|
|
116
|
+
try {
|
|
117
|
+
return JSON.stringify(value);
|
|
118
|
+
} catch {
|
|
119
|
+
return String(value);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function identify(userId, traits) {
|
|
123
|
+
debugLog(`[IDENTIFY] ${userId} ${formatDebugValue2(traits ?? "")}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ../platform-kernel/src/ids.ts
|
|
127
|
+
function makePrefixedIdConstructor(prefix, fieldName) {
|
|
128
|
+
const re = new RegExp(`^${prefix}_[A-Za-z0-9_\\-]+$`);
|
|
129
|
+
return (raw) => {
|
|
130
|
+
if (typeof raw !== "string" || !re.test(raw)) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
`Invalid ${fieldName}: expected string matching ^${prefix}_[A-Za-z0-9_\\-]+$, got ${String(raw)}`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
return raw;
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
var toAccountId = makePrefixedIdConstructor(
|
|
139
|
+
"acct",
|
|
140
|
+
"accountId"
|
|
141
|
+
);
|
|
142
|
+
var toOrganizationId = makePrefixedIdConstructor("org", "organizationId");
|
|
143
|
+
var toMemberId = makePrefixedIdConstructor(
|
|
144
|
+
"mem",
|
|
145
|
+
"memberId"
|
|
146
|
+
);
|
|
147
|
+
var toSafeId = makePrefixedIdConstructor(
|
|
148
|
+
"safe",
|
|
149
|
+
"safeId"
|
|
150
|
+
);
|
|
151
|
+
var toTreasuryId = makePrefixedIdConstructor(
|
|
152
|
+
"try",
|
|
153
|
+
"treasuryId"
|
|
154
|
+
);
|
|
155
|
+
var toApiKeyId = makePrefixedIdConstructor(
|
|
156
|
+
"ak",
|
|
157
|
+
"apiKeyId"
|
|
158
|
+
);
|
|
159
|
+
var toOperationId = makePrefixedIdConstructor(
|
|
160
|
+
"op",
|
|
161
|
+
"operationId"
|
|
162
|
+
);
|
|
163
|
+
var toCorrelationId = makePrefixedIdConstructor("ctx", "correlationId");
|
|
164
|
+
var toKycProfileId = makePrefixedIdConstructor(
|
|
165
|
+
"kyc",
|
|
166
|
+
"kycProfileId"
|
|
167
|
+
);
|
|
168
|
+
var toKybProfileId = makePrefixedIdConstructor(
|
|
169
|
+
"kyb",
|
|
170
|
+
"kybProfileId"
|
|
171
|
+
);
|
|
172
|
+
var toExternalAccountId = makePrefixedIdConstructor("ext", "externalAccountId");
|
|
173
|
+
var toPaymentId = makePrefixedIdConstructor(
|
|
174
|
+
"pay",
|
|
175
|
+
"paymentId"
|
|
176
|
+
);
|
|
177
|
+
var toWithdrawalId = makePrefixedIdConstructor(
|
|
178
|
+
"wd",
|
|
179
|
+
"withdrawalId"
|
|
180
|
+
);
|
|
181
|
+
var toWebhookEndpointId = makePrefixedIdConstructor("we", "webhookEndpointId");
|
|
182
|
+
var toWebhookEventId = makePrefixedIdConstructor("evt", "webhookEventId");
|
|
183
|
+
var toSubAccountId = makePrefixedIdConstructor(
|
|
184
|
+
"sub",
|
|
185
|
+
"subAccountId"
|
|
186
|
+
);
|
|
187
|
+
var toVirtualAccountId = makePrefixedIdConstructor("va", "virtualAccountId");
|
|
188
|
+
var toVirtualCardId = makePrefixedIdConstructor(
|
|
189
|
+
"vc",
|
|
190
|
+
"virtualCardId"
|
|
191
|
+
);
|
|
192
|
+
var toTransferId = makePrefixedIdConstructor(
|
|
193
|
+
"txfr",
|
|
194
|
+
"transferId"
|
|
195
|
+
);
|
|
196
|
+
var toDocumentId = makePrefixedIdConstructor(
|
|
197
|
+
"doc",
|
|
198
|
+
"documentId"
|
|
199
|
+
);
|
|
200
|
+
var toBalanceLedgerEntryId = makePrefixedIdConstructor("bal", "balanceLedgerEntryId");
|
|
201
|
+
|
|
202
|
+
// ../platform-kernel/src/value-objects.ts
|
|
203
|
+
var PHONE_NUMBER_RE = /^\+[1-9]\d{1,14}$/;
|
|
204
|
+
function toPhoneNumber(raw) {
|
|
205
|
+
if (typeof raw !== "string" || !PHONE_NUMBER_RE.test(raw)) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`Invalid phoneNumber: expected E.164 string matching ^\\+[1-9]\\d{1,14}$, got ${String(raw)}`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
return raw;
|
|
211
|
+
}
|
|
212
|
+
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
213
|
+
function toEmail(raw) {
|
|
214
|
+
if (typeof raw !== "string") {
|
|
215
|
+
throw new Error(
|
|
216
|
+
`Invalid email: expected string, got ${String(raw)}`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
const lowered = raw.trim().toLowerCase();
|
|
220
|
+
if (!EMAIL_RE.test(lowered)) {
|
|
221
|
+
throw new Error(
|
|
222
|
+
`Invalid email: expected local@domain.tld, got ${String(raw)}`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
return lowered;
|
|
226
|
+
}
|
|
227
|
+
var USERNAME_RE = /^[a-z][a-z0-9_-]{2,29}$/;
|
|
228
|
+
function toUsername(raw) {
|
|
229
|
+
if (typeof raw !== "string") {
|
|
230
|
+
throw new Error(
|
|
231
|
+
`Invalid username: expected string, got ${String(raw)}`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
const lowered = raw.toLowerCase();
|
|
235
|
+
if (!USERNAME_RE.test(lowered)) {
|
|
236
|
+
throw new Error(
|
|
237
|
+
`Invalid username: expected 3-30 chars, letter-first, [a-z0-9_-], got ${String(raw)}`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
return lowered;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/core/external-accounts.ts
|
|
244
|
+
function brandExternalAccount(raw) {
|
|
245
|
+
return {
|
|
246
|
+
...raw,
|
|
247
|
+
id: toExternalAccountId(raw.id),
|
|
248
|
+
operation: {
|
|
249
|
+
id: toOperationId(raw.operation.id),
|
|
250
|
+
status: raw.operation.status,
|
|
251
|
+
correlationId: toCorrelationId(raw.operation.correlationId)
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function createExternalAccountsClient(config = {}) {
|
|
256
|
+
return {
|
|
257
|
+
retrieve: async (externalAccountId) => {
|
|
258
|
+
if (!config.data) {
|
|
259
|
+
return stub(
|
|
260
|
+
"externalAccounts.retrieve"
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
const [err, raw] = await tryCatch(
|
|
264
|
+
config.data.query(api.externalAccounts.queries.retrievePersonal, {
|
|
265
|
+
externalAccountId
|
|
266
|
+
})
|
|
267
|
+
);
|
|
268
|
+
if (err) {
|
|
269
|
+
return [
|
|
270
|
+
fromConvexError(err),
|
|
271
|
+
null
|
|
272
|
+
];
|
|
273
|
+
}
|
|
274
|
+
if (!raw) {
|
|
275
|
+
return [
|
|
276
|
+
new CapxulError({
|
|
277
|
+
code: "NOT_FOUND",
|
|
278
|
+
message: `external_account ${externalAccountId} not found`
|
|
279
|
+
}),
|
|
280
|
+
null
|
|
281
|
+
];
|
|
282
|
+
}
|
|
283
|
+
return [null, brandExternalAccount(raw)];
|
|
284
|
+
},
|
|
285
|
+
remove: async (externalAccountId) => {
|
|
286
|
+
if (!config.data) {
|
|
287
|
+
return stub("externalAccounts.remove");
|
|
288
|
+
}
|
|
289
|
+
const [err] = await tryCatch(
|
|
290
|
+
config.data.mutation(api.externalAccounts.mutations.removePersonal, {
|
|
291
|
+
externalAccountId
|
|
292
|
+
})
|
|
293
|
+
);
|
|
294
|
+
if (err) {
|
|
295
|
+
return [
|
|
296
|
+
fromConvexError(err),
|
|
297
|
+
null
|
|
298
|
+
];
|
|
299
|
+
}
|
|
300
|
+
return [null, void 0];
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
73
305
|
// src/core/accounts.ts
|
|
306
|
+
function createAccountExternalAccountsClient(config) {
|
|
307
|
+
return {
|
|
308
|
+
create: async (input) => {
|
|
309
|
+
if (!config.data) {
|
|
310
|
+
return stub(
|
|
311
|
+
"accounts.externalAccounts.create"
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
const [err, raw] = await tryCatch(
|
|
315
|
+
config.data.mutation(
|
|
316
|
+
api.externalAccounts.mutations.createPersonal,
|
|
317
|
+
{
|
|
318
|
+
kind: input.kind,
|
|
319
|
+
label: input.label,
|
|
320
|
+
address: input.address,
|
|
321
|
+
iban: input.iban,
|
|
322
|
+
bic: input.bic,
|
|
323
|
+
accountHolder: input.accountHolder,
|
|
324
|
+
network: input.network,
|
|
325
|
+
panToken: input.panToken,
|
|
326
|
+
last4: input.last4
|
|
327
|
+
}
|
|
328
|
+
)
|
|
329
|
+
);
|
|
330
|
+
if (err) {
|
|
331
|
+
return [
|
|
332
|
+
fromConvexError(err),
|
|
333
|
+
null
|
|
334
|
+
];
|
|
335
|
+
}
|
|
336
|
+
if (!raw) {
|
|
337
|
+
return [
|
|
338
|
+
new CapxulError({
|
|
339
|
+
code: "NOT_FOUND",
|
|
340
|
+
message: "external_account creation returned no resource"
|
|
341
|
+
}),
|
|
342
|
+
null
|
|
343
|
+
];
|
|
344
|
+
}
|
|
345
|
+
return [
|
|
346
|
+
null,
|
|
347
|
+
brandExternalAccount(
|
|
348
|
+
raw
|
|
349
|
+
)
|
|
350
|
+
];
|
|
351
|
+
},
|
|
352
|
+
list: async (input) => {
|
|
353
|
+
if (!config.data) {
|
|
354
|
+
return stub(
|
|
355
|
+
"accounts.externalAccounts.list"
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
const [err, result] = await tryCatch(
|
|
359
|
+
config.data.query(api.externalAccounts.queries.listPersonal, {
|
|
360
|
+
limit: input.limit,
|
|
361
|
+
cursor: input.cursor
|
|
362
|
+
})
|
|
363
|
+
);
|
|
364
|
+
if (err) {
|
|
365
|
+
return [fromConvexError(err), null];
|
|
366
|
+
}
|
|
367
|
+
const branded = result.data.map(
|
|
368
|
+
(row) => brandExternalAccount(
|
|
369
|
+
row
|
|
370
|
+
)
|
|
371
|
+
);
|
|
372
|
+
return [
|
|
373
|
+
null,
|
|
374
|
+
{
|
|
375
|
+
object: "list",
|
|
376
|
+
data: branded,
|
|
377
|
+
page: result.page
|
|
378
|
+
}
|
|
379
|
+
];
|
|
380
|
+
},
|
|
381
|
+
retrieve: async (externalAccountId) => {
|
|
382
|
+
if (!config.data) {
|
|
383
|
+
return stub(
|
|
384
|
+
"accounts.externalAccounts.retrieve"
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
const [err, raw] = await tryCatch(
|
|
388
|
+
config.data.query(api.externalAccounts.queries.retrievePersonal, {
|
|
389
|
+
externalAccountId
|
|
390
|
+
})
|
|
391
|
+
);
|
|
392
|
+
if (err) {
|
|
393
|
+
return [
|
|
394
|
+
fromConvexError(err),
|
|
395
|
+
null
|
|
396
|
+
];
|
|
397
|
+
}
|
|
398
|
+
if (!raw) {
|
|
399
|
+
return [
|
|
400
|
+
new CapxulError({
|
|
401
|
+
code: "NOT_FOUND",
|
|
402
|
+
message: `external_account ${externalAccountId} not found`
|
|
403
|
+
}),
|
|
404
|
+
null
|
|
405
|
+
];
|
|
406
|
+
}
|
|
407
|
+
return [
|
|
408
|
+
null,
|
|
409
|
+
brandExternalAccount(
|
|
410
|
+
raw
|
|
411
|
+
)
|
|
412
|
+
];
|
|
413
|
+
},
|
|
414
|
+
remove: async (externalAccountId) => {
|
|
415
|
+
if (!config.data) {
|
|
416
|
+
return stub("accounts.externalAccounts.remove");
|
|
417
|
+
}
|
|
418
|
+
const [err] = await tryCatch(
|
|
419
|
+
config.data.mutation(api.externalAccounts.mutations.removePersonal, {
|
|
420
|
+
externalAccountId
|
|
421
|
+
})
|
|
422
|
+
);
|
|
423
|
+
if (err) {
|
|
424
|
+
return [
|
|
425
|
+
fromConvexError(err),
|
|
426
|
+
null
|
|
427
|
+
];
|
|
428
|
+
}
|
|
429
|
+
return [null, void 0];
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
}
|
|
74
433
|
function createAccountsClient(config = {}) {
|
|
75
434
|
return {
|
|
76
435
|
retrieve: async (accountId) => {
|
|
@@ -225,18 +584,7 @@ function createAccountsClient(config = {}) {
|
|
|
225
584
|
create: async () => stub("accounts.kycProfiles.create"),
|
|
226
585
|
retrieve: async () => stub("accounts.kycProfiles.retrieve")
|
|
227
586
|
},
|
|
228
|
-
externalAccounts:
|
|
229
|
-
create: async () => stub(
|
|
230
|
-
"accounts.externalAccounts.create"
|
|
231
|
-
),
|
|
232
|
-
list: async () => stub(
|
|
233
|
-
"accounts.externalAccounts.list"
|
|
234
|
-
),
|
|
235
|
-
retrieve: async () => stub(
|
|
236
|
-
"accounts.externalAccounts.retrieve"
|
|
237
|
-
),
|
|
238
|
-
remove: async () => stub("accounts.externalAccounts.remove")
|
|
239
|
-
},
|
|
587
|
+
externalAccounts: createAccountExternalAccountsClient(config),
|
|
240
588
|
subAccounts: {
|
|
241
589
|
create: async () => stub("accounts.subAccounts.create"),
|
|
242
590
|
list: async () => stub("accounts.subAccounts.list"),
|
|
@@ -331,8 +679,35 @@ var Errors = {
|
|
|
331
679
|
"Idempotency key was already used for a different request",
|
|
332
680
|
{ details }
|
|
333
681
|
),
|
|
334
|
-
emailDeliveryFailed: (detail) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}
|
|
335
|
-
|
|
682
|
+
emailDeliveryFailed: (detail, details) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`, {
|
|
683
|
+
details
|
|
684
|
+
}),
|
|
685
|
+
rateLimited: (details) => new CapxulError2("RATE_LIMITED", "Request was rate limited", {
|
|
686
|
+
details: { ...details }
|
|
687
|
+
}),
|
|
688
|
+
internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`),
|
|
689
|
+
/**
|
|
690
|
+
* Verification gate. Surfaced when a request hits a verification
|
|
691
|
+
* boundary the actor cannot cross under their current state. Two
|
|
692
|
+
* variants share this code:
|
|
693
|
+
*
|
|
694
|
+
* - Rail gate (Withdrawals v1 W2, #465): the resolved
|
|
695
|
+
* `external_account.kind` routes to a withdrawal rail (e.g.
|
|
696
|
+
* `fiat_offramp`, `card_payout`) that is not yet supported. Carries
|
|
697
|
+
* `details.rail` + `details.currentKind`.
|
|
698
|
+
* - KYC tier gate (legacy / future): the actor's KYC tier is below
|
|
699
|
+
* the required tier. Carries `details.requiredTier`.
|
|
700
|
+
*
|
|
701
|
+
* Code is shared because both expose the same UX shape ("you cannot
|
|
702
|
+
* proceed until verification advances"); the `details.*` keys
|
|
703
|
+
* differentiate the route.
|
|
704
|
+
*/
|
|
705
|
+
verificationRequired: (details) => {
|
|
706
|
+
const message = "rail" in details ? `Withdrawal rail "${details.rail}" (kind=${details.currentKind}) is not yet supported.` : `Verification tier ${details.requiredTier} is required.`;
|
|
707
|
+
return new CapxulError2("VERIFICATION_REQUIRED", message, {
|
|
708
|
+
details: { ...details }
|
|
709
|
+
});
|
|
710
|
+
}
|
|
336
711
|
};
|
|
337
712
|
|
|
338
713
|
// ../config/src/safe.ts
|
|
@@ -382,13 +757,13 @@ function createLifecycle(initial) {
|
|
|
382
757
|
}
|
|
383
758
|
function makeBuildTimeUrlsTransport(config) {
|
|
384
759
|
if (!config.authBaseUrl || config.authBaseUrl.trim().length === 0) {
|
|
385
|
-
throw
|
|
760
|
+
throw invalidConfigError(
|
|
386
761
|
"authBaseUrl",
|
|
387
762
|
"build-time-urls transport requires a non-empty authBaseUrl."
|
|
388
763
|
);
|
|
389
764
|
}
|
|
390
765
|
if (!config.convexUrl || config.convexUrl.trim().length === 0) {
|
|
391
|
-
throw
|
|
766
|
+
throw invalidConfigError(
|
|
392
767
|
"convexUrl",
|
|
393
768
|
"build-time-urls transport requires a non-empty convexUrl."
|
|
394
769
|
);
|
|
@@ -402,6 +777,7 @@ function makeBuildTimeUrlsTransport(config) {
|
|
|
402
777
|
return {
|
|
403
778
|
authBaseUrl,
|
|
404
779
|
convexUrl,
|
|
780
|
+
ensureRuntime: async () => runtime,
|
|
405
781
|
fetch: (path, init) => fetchImpl(resolveUrl(authBaseUrl, path), init),
|
|
406
782
|
getState: lifecycle.getState,
|
|
407
783
|
subscribe: lifecycle.subscribe,
|
|
@@ -417,14 +793,15 @@ function makeBuildTimeUrlsTransport(config) {
|
|
|
417
793
|
};
|
|
418
794
|
}
|
|
419
795
|
function makePublishableKeyTransport(config) {
|
|
420
|
-
|
|
421
|
-
|
|
796
|
+
const publishableKey = config.publishableKey?.trim();
|
|
797
|
+
if (!publishableKey) {
|
|
798
|
+
throw invalidConfigError(
|
|
422
799
|
"publishableKey",
|
|
423
800
|
"publishable-key transport requires a non-empty publishableKey."
|
|
424
801
|
);
|
|
425
802
|
}
|
|
426
803
|
const fetchImpl = config.fetchImpl ?? globalThis.fetch;
|
|
427
|
-
const bootstrapUrl =
|
|
804
|
+
const bootstrapUrl = normalizeBootstrapUrl(
|
|
428
805
|
config.bootstrapUrl ?? `${CAPXUL_API_BASE_URL}/v1/client/bootstrap`
|
|
429
806
|
);
|
|
430
807
|
let authBaseUrl = "";
|
|
@@ -439,23 +816,20 @@ function makePublishableKeyTransport(config) {
|
|
|
439
816
|
const response = await fetchImpl(bootstrapUrl, {
|
|
440
817
|
method: "POST",
|
|
441
818
|
headers: { "content-type": "application/json" },
|
|
442
|
-
body: JSON.stringify({ publishableKey
|
|
819
|
+
body: JSON.stringify({ publishableKey })
|
|
443
820
|
});
|
|
444
821
|
if (!response.ok) {
|
|
445
|
-
throw
|
|
446
|
-
"publishableKey",
|
|
447
|
-
`${bootstrapUrl} failed with HTTP ${response.status}.`
|
|
448
|
-
);
|
|
822
|
+
throw await bootstrapResponseError(response, bootstrapUrl);
|
|
449
823
|
}
|
|
450
|
-
const body = await response
|
|
824
|
+
const body = await readBootstrapSuccessBody(response);
|
|
451
825
|
if (typeof body.authBaseUrl !== "string" || body.authBaseUrl.trim().length === 0) {
|
|
452
|
-
throw
|
|
826
|
+
throw bootstrapContractError(
|
|
453
827
|
"authBaseUrl",
|
|
454
828
|
"/v1/client/bootstrap returned no authBaseUrl."
|
|
455
829
|
);
|
|
456
830
|
}
|
|
457
831
|
if (typeof body.convexUrl !== "string" || body.convexUrl.trim().length === 0) {
|
|
458
|
-
throw
|
|
832
|
+
throw bootstrapContractError(
|
|
459
833
|
"convexUrl",
|
|
460
834
|
"/v1/client/bootstrap returned no convexUrl."
|
|
461
835
|
);
|
|
@@ -467,16 +841,13 @@ function makePublishableKeyTransport(config) {
|
|
|
467
841
|
return runtime;
|
|
468
842
|
})();
|
|
469
843
|
bootstrapPromise = attempt.catch((err) => {
|
|
844
|
+
const error = normalizeBootstrapThrownError(err);
|
|
470
845
|
bootstrapPromise = null;
|
|
471
846
|
lifecycle.setState({
|
|
472
847
|
status: "error",
|
|
473
|
-
error
|
|
474
|
-
code: "UNKNOWN",
|
|
475
|
-
message: "Bootstrap failed without a typed CapxulError.",
|
|
476
|
-
cause: err
|
|
477
|
-
})
|
|
848
|
+
error
|
|
478
849
|
});
|
|
479
|
-
throw
|
|
850
|
+
throw error;
|
|
480
851
|
});
|
|
481
852
|
return await bootstrapPromise;
|
|
482
853
|
}
|
|
@@ -487,6 +858,7 @@ function makePublishableKeyTransport(config) {
|
|
|
487
858
|
get convexUrl() {
|
|
488
859
|
return convexUrl;
|
|
489
860
|
},
|
|
861
|
+
ensureRuntime: ensureBootstrap,
|
|
490
862
|
fetch: async (path, init) => {
|
|
491
863
|
const resolved = await ensureBootstrap();
|
|
492
864
|
return await fetchImpl(resolveUrl(resolved.authBaseUrl, path), init);
|
|
@@ -497,7 +869,7 @@ function makePublishableKeyTransport(config) {
|
|
|
497
869
|
markAuthenticated: ({ dataClient: nextDataClient }) => {
|
|
498
870
|
const current = lifecycle.getState();
|
|
499
871
|
if (current.status !== "ready" && current.status !== "authenticated") {
|
|
500
|
-
throw
|
|
872
|
+
throw internalTransportError(
|
|
501
873
|
`markAuthenticated() called from status="${current.status}". Expected "ready" or "authenticated".`
|
|
502
874
|
);
|
|
503
875
|
}
|
|
@@ -519,16 +891,166 @@ function makePublishableKeyTransport(config) {
|
|
|
519
891
|
function stripTrailingSlash(url) {
|
|
520
892
|
return url.replace(/\/+$/, "");
|
|
521
893
|
}
|
|
894
|
+
function normalizeBootstrapUrl(url) {
|
|
895
|
+
const normalized = stripTrailingSlash(url.trim());
|
|
896
|
+
if (!isAbsoluteHttpUrl(normalized)) {
|
|
897
|
+
throw invalidConfigError(
|
|
898
|
+
"bootstrapUrl",
|
|
899
|
+
"publishable-key transport requires an absolute http(s) bootstrapUrl."
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
return normalized;
|
|
903
|
+
}
|
|
904
|
+
function isAbsoluteHttpUrl(url) {
|
|
905
|
+
try {
|
|
906
|
+
const parsed = new URL(url);
|
|
907
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
908
|
+
} catch {
|
|
909
|
+
return false;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
522
912
|
function resolveUrl(authBaseUrl, path) {
|
|
523
913
|
if (path.startsWith("http://") || path.startsWith("https://")) {
|
|
524
914
|
return path;
|
|
525
915
|
}
|
|
526
916
|
return `${authBaseUrl}${path}`;
|
|
527
917
|
}
|
|
528
|
-
function assertNever(value) {
|
|
529
|
-
throw
|
|
530
|
-
`Unhandled BrowserCapxulConfig.mode: ${String(value.mode)}.`
|
|
531
|
-
);
|
|
918
|
+
function assertNever(value) {
|
|
919
|
+
throw internalTransportError(
|
|
920
|
+
`Unhandled BrowserCapxulConfig.mode: ${String(value.mode)}.`
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
function invalidConfigError(field, reason) {
|
|
924
|
+
return new CapxulError({
|
|
925
|
+
code: "INVALID_INPUT",
|
|
926
|
+
message: `Invalid ${field}: ${reason}`,
|
|
927
|
+
details: { source: "sdk-config", field, reason }
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
function bootstrapContractError(field, message) {
|
|
931
|
+
return new CapxulError({
|
|
932
|
+
code: "INVALID_INPUT",
|
|
933
|
+
message,
|
|
934
|
+
details: {
|
|
935
|
+
source: "backend-bootstrap",
|
|
936
|
+
phase: "publishable-key-bootstrap",
|
|
937
|
+
field,
|
|
938
|
+
reason: message
|
|
939
|
+
}
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
function internalTransportError(reason) {
|
|
943
|
+
return new CapxulError({
|
|
944
|
+
code: "INTERNAL_ERROR",
|
|
945
|
+
message: `Internal error: ${reason}`,
|
|
946
|
+
details: { source: "sdk-transport", reason }
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
async function bootstrapResponseError(response, bootstrapUrl) {
|
|
950
|
+
const envelope = await readBootstrapErrorEnvelope(response);
|
|
951
|
+
const wireCode = readNonEmptyString(envelope?.error?.code);
|
|
952
|
+
const normalized = normalizeBootstrapErrorCode(wireCode);
|
|
953
|
+
const message = readNonEmptyString(envelope?.error?.message) ?? `${bootstrapUrl} failed with HTTP ${response.status}.`;
|
|
954
|
+
const backendDetails = readRecord(envelope?.error?.details);
|
|
955
|
+
return new CapxulError({
|
|
956
|
+
code: normalized.code,
|
|
957
|
+
message,
|
|
958
|
+
details: {
|
|
959
|
+
...backendDetails,
|
|
960
|
+
source: "backend-bootstrap",
|
|
961
|
+
phase: "publishable-key-bootstrap",
|
|
962
|
+
httpStatus: response.status,
|
|
963
|
+
...normalized.wireCode ? { wireCode: normalized.wireCode } : {}
|
|
964
|
+
},
|
|
965
|
+
operationId: readNonEmptyString(envelope?.error?.operationId),
|
|
966
|
+
correlationId: readNonEmptyString(envelope?.error?.correlationId),
|
|
967
|
+
retryable: typeof envelope?.error?.retryable === "boolean" ? envelope.error.retryable : void 0
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
async function readBootstrapErrorEnvelope(response) {
|
|
971
|
+
try {
|
|
972
|
+
const parsed = await response.json();
|
|
973
|
+
return typeof parsed === "object" && parsed !== null ? parsed : null;
|
|
974
|
+
} catch {
|
|
975
|
+
return null;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
async function readBootstrapSuccessBody(response) {
|
|
979
|
+
try {
|
|
980
|
+
const parsed = await response.json();
|
|
981
|
+
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
982
|
+
} catch {
|
|
983
|
+
throw bootstrapContractError(
|
|
984
|
+
"body",
|
|
985
|
+
"/v1/client/bootstrap returned invalid JSON."
|
|
986
|
+
);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
function normalizeBootstrapThrownError(error) {
|
|
990
|
+
if (error instanceof CapxulError) return error;
|
|
991
|
+
return new CapxulError({
|
|
992
|
+
code: "NETWORK_ERROR",
|
|
993
|
+
message: "Publishable-key bootstrap network failure.",
|
|
994
|
+
cause: error,
|
|
995
|
+
details: {
|
|
996
|
+
source: "bootstrap-network",
|
|
997
|
+
phase: "publishable-key-bootstrap"
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
function normalizeBootstrapErrorCode(wireCode) {
|
|
1002
|
+
if (wireCode === "INTERNAL_SERVER_ERROR") {
|
|
1003
|
+
return { code: "INTERNAL_ERROR", wireCode };
|
|
1004
|
+
}
|
|
1005
|
+
if (wireCode && isCapxulErrorCode(wireCode)) {
|
|
1006
|
+
return { code: wireCode };
|
|
1007
|
+
}
|
|
1008
|
+
return wireCode ? { code: "UNKNOWN", wireCode } : { code: "UNKNOWN" };
|
|
1009
|
+
}
|
|
1010
|
+
var CAPXUL_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
1011
|
+
"NOT_AUTHENTICATED",
|
|
1012
|
+
"EMAIL_DELIVERY_FAILED",
|
|
1013
|
+
"PROFILE_NOT_FOUND",
|
|
1014
|
+
"SMART_ACCOUNT_MISSING",
|
|
1015
|
+
"PLAYER_NOT_FOUND",
|
|
1016
|
+
"ACCOUNT_NOT_FOUND",
|
|
1017
|
+
"PROVIDER_ERROR",
|
|
1018
|
+
"INVALID_INPUT",
|
|
1019
|
+
"ENV_MISSING",
|
|
1020
|
+
"NOT_IMPLEMENTED",
|
|
1021
|
+
"VERIFICATION_REQUIRED",
|
|
1022
|
+
"INSUFFICIENT_BALANCE",
|
|
1023
|
+
"INVALID_RECIPIENT",
|
|
1024
|
+
"TRANSACTION_FAILED",
|
|
1025
|
+
"RATE_LIMITED",
|
|
1026
|
+
"NETWORK_ERROR",
|
|
1027
|
+
"UNKNOWN",
|
|
1028
|
+
"PERMISSION_DENIED",
|
|
1029
|
+
"API_KEY_INVALID",
|
|
1030
|
+
"API_KEY_EXPIRED",
|
|
1031
|
+
"IDEMPOTENCY_CONFLICT",
|
|
1032
|
+
"NOT_FOUND",
|
|
1033
|
+
"OPERATION_CANCELED",
|
|
1034
|
+
"OPERATION_TIMEOUT",
|
|
1035
|
+
"ACTION_REQUIRED",
|
|
1036
|
+
"KYC_REQUIRED",
|
|
1037
|
+
"POLICY_DENIED",
|
|
1038
|
+
"SAFE_NOT_READY",
|
|
1039
|
+
"PROVIDER_UNAVAILABLE",
|
|
1040
|
+
"PROVIDER_REJECTED",
|
|
1041
|
+
"RECONCILIATION_FAILED",
|
|
1042
|
+
"INTERNAL_ERROR",
|
|
1043
|
+
"QUOTE_EXPIRED",
|
|
1044
|
+
"QUOTE_NOT_FOUND"
|
|
1045
|
+
]);
|
|
1046
|
+
function isCapxulErrorCode(value) {
|
|
1047
|
+
return CAPXUL_ERROR_CODES.has(value);
|
|
1048
|
+
}
|
|
1049
|
+
function readRecord(value) {
|
|
1050
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
1051
|
+
}
|
|
1052
|
+
function readNonEmptyString(value) {
|
|
1053
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
532
1054
|
}
|
|
533
1055
|
|
|
534
1056
|
// src/core/auth.ts
|
|
@@ -584,13 +1106,16 @@ function createAuthClient(config = {}) {
|
|
|
584
1106
|
email: signIn.user.email,
|
|
585
1107
|
token: signIn.token,
|
|
586
1108
|
convexJwt,
|
|
587
|
-
expiresAt: new Date(
|
|
1109
|
+
expiresAt: new Date(
|
|
1110
|
+
Date.now() + 30 * 24 * 60 * 60 * 1e3
|
|
1111
|
+
).toISOString()
|
|
588
1112
|
};
|
|
589
1113
|
sessionStore.set(session);
|
|
590
1114
|
if (config.auth?.createDataClient) {
|
|
591
1115
|
try {
|
|
592
1116
|
dataClient = await config.auth.createDataClient(session);
|
|
593
1117
|
mutableConfig(config).data = dataClient;
|
|
1118
|
+
transport.markAuthenticated({ dataClient });
|
|
594
1119
|
} catch (cause) {
|
|
595
1120
|
return [
|
|
596
1121
|
new CapxulError({
|
|
@@ -609,6 +1134,8 @@ function createAuthClient(config = {}) {
|
|
|
609
1134
|
sessionStore.clear();
|
|
610
1135
|
dataClient = null;
|
|
611
1136
|
mutableConfig(config).data = void 0;
|
|
1137
|
+
const transport = getTransport();
|
|
1138
|
+
transport?.clearAuth();
|
|
612
1139
|
return [null, void 0];
|
|
613
1140
|
},
|
|
614
1141
|
serviceTokenMint: async () => stub("auth.serviceTokenMint"),
|
|
@@ -662,16 +1189,19 @@ async function postBetterAuth(transport, path, body, code, signal) {
|
|
|
662
1189
|
body: JSON.stringify(body),
|
|
663
1190
|
signal
|
|
664
1191
|
});
|
|
1192
|
+
const text = await response.text();
|
|
665
1193
|
if (!response.ok) {
|
|
1194
|
+
const parsedError = parseBetterAuthError(text);
|
|
666
1195
|
return [
|
|
667
1196
|
new CapxulError({
|
|
668
|
-
code,
|
|
669
|
-
message: `BetterAuth ${path} failed with HTTP ${response.status}
|
|
1197
|
+
code: parsedError.code ?? code,
|
|
1198
|
+
message: parsedError.message ?? `BetterAuth ${path} failed with HTTP ${response.status}.`,
|
|
1199
|
+
details: parsedError.details,
|
|
1200
|
+
retryable: parsedError.retryable
|
|
670
1201
|
}),
|
|
671
1202
|
null
|
|
672
1203
|
];
|
|
673
1204
|
}
|
|
674
|
-
const text = await response.text();
|
|
675
1205
|
return [null, text ? JSON.parse(text) : void 0];
|
|
676
1206
|
} catch (cause) {
|
|
677
1207
|
return [
|
|
@@ -684,6 +1214,36 @@ async function postBetterAuth(transport, path, body, code, signal) {
|
|
|
684
1214
|
];
|
|
685
1215
|
}
|
|
686
1216
|
}
|
|
1217
|
+
function parseBetterAuthError(text) {
|
|
1218
|
+
if (!text.trim()) {
|
|
1219
|
+
return {};
|
|
1220
|
+
}
|
|
1221
|
+
try {
|
|
1222
|
+
const body = JSON.parse(text);
|
|
1223
|
+
if (!body || typeof body !== "object") {
|
|
1224
|
+
return {};
|
|
1225
|
+
}
|
|
1226
|
+
const record = body;
|
|
1227
|
+
const nested = record.error && typeof record.error === "object" ? record.error : record;
|
|
1228
|
+
const code = typeof nested.code === "string" ? nested.code : void 0;
|
|
1229
|
+
const message = typeof nested.message === "string" ? nested.message : void 0;
|
|
1230
|
+
const details = nested.details && typeof nested.details === "object" ? nested.details : void 0;
|
|
1231
|
+
const correlationId = typeof nested.correlationId === "string" ? nested.correlationId : void 0;
|
|
1232
|
+
const retryable = typeof nested.retryable === "boolean" ? nested.retryable : void 0;
|
|
1233
|
+
return {
|
|
1234
|
+
code: isCapxulErrorCode2(code) ? code : void 0,
|
|
1235
|
+
message,
|
|
1236
|
+
details,
|
|
1237
|
+
correlationId,
|
|
1238
|
+
retryable
|
|
1239
|
+
};
|
|
1240
|
+
} catch {
|
|
1241
|
+
return {};
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
function isCapxulErrorCode2(code) {
|
|
1245
|
+
return code === "NOT_AUTHENTICATED" || code === "EMAIL_DELIVERY_FAILED" || code === "INVALID_INPUT" || code === "RATE_LIMITED" || code === "NETWORK_ERROR" || code === "API_KEY_INVALID" || code === "API_KEY_EXPIRED";
|
|
1246
|
+
}
|
|
687
1247
|
async function exchangeConvexToken(transport, config, token, signal) {
|
|
688
1248
|
const path = config.auth?.convexTokenUrl ?? "/convex/token";
|
|
689
1249
|
try {
|
|
@@ -744,14 +1304,6 @@ function createOrgDocumentsClient() {
|
|
|
744
1304
|
};
|
|
745
1305
|
}
|
|
746
1306
|
|
|
747
|
-
// src/core/external-accounts.ts
|
|
748
|
-
function createExternalAccountsClient() {
|
|
749
|
-
return {
|
|
750
|
-
retrieve: async () => stub("externalAccounts.retrieve"),
|
|
751
|
-
remove: async () => stub("externalAccounts.remove")
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
|
|
755
1307
|
// src/core/me.ts
|
|
756
1308
|
function createMeClient(config = {}) {
|
|
757
1309
|
return {
|
|
@@ -1199,67 +1751,68 @@ function createOrgTransfersClient() {
|
|
|
1199
1751
|
cancel: async () => stub("organizations.transfers.cancel")
|
|
1200
1752
|
};
|
|
1201
1753
|
}
|
|
1202
|
-
var EVM_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
1203
1754
|
function createWithdrawalsClient(config = {}) {
|
|
1204
1755
|
return {
|
|
1205
1756
|
create: async (input) => {
|
|
1206
1757
|
if (!config.data) {
|
|
1207
1758
|
return stub("withdrawals.create");
|
|
1208
1759
|
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
details: { field: "destination.externalAccountId" }
|
|
1248
|
-
});
|
|
1249
|
-
}
|
|
1250
|
-
const currentSigner = await config.data.query(
|
|
1251
|
-
api.safe.queries.getMySignerAddress,
|
|
1252
|
-
{}
|
|
1760
|
+
const [createErr, createdRaw] = await tryCatch(
|
|
1761
|
+
config.data.mutation(api.withdrawals.mutations.create, {
|
|
1762
|
+
amount: input.amount,
|
|
1763
|
+
destination: {
|
|
1764
|
+
externalAccountId: input.destination.externalAccountId
|
|
1765
|
+
},
|
|
1766
|
+
source: input.source,
|
|
1767
|
+
reference: input.reference,
|
|
1768
|
+
idempotencyKey: input.idempotencyKey
|
|
1769
|
+
})
|
|
1770
|
+
);
|
|
1771
|
+
if (createErr) {
|
|
1772
|
+
return [mapCreateError2(fromConvexError(createErr)), null];
|
|
1773
|
+
}
|
|
1774
|
+
const created = createdRaw;
|
|
1775
|
+
if (!created) {
|
|
1776
|
+
return [
|
|
1777
|
+
new CapxulError({
|
|
1778
|
+
code: "NETWORK_ERROR",
|
|
1779
|
+
message: "withdrawals.create returned no withdrawal resource"
|
|
1780
|
+
}),
|
|
1781
|
+
null
|
|
1782
|
+
];
|
|
1783
|
+
}
|
|
1784
|
+
if (created.status !== "processing" || created.operation.status !== "processing") {
|
|
1785
|
+
return [null, created];
|
|
1786
|
+
}
|
|
1787
|
+
if (!config.signer || !config.signing) {
|
|
1788
|
+
return [null, created];
|
|
1789
|
+
}
|
|
1790
|
+
const [signerErr, currentSigner] = await tryCatch(
|
|
1791
|
+
config.data.query(api.safe.queries.getMySignerAddress, {})
|
|
1792
|
+
);
|
|
1793
|
+
if (signerErr) {
|
|
1794
|
+
return await handleSubmissionFailure(
|
|
1795
|
+
{ data: config.data },
|
|
1796
|
+
created.id,
|
|
1797
|
+
mapCreateError2(fromConvexError(signerErr))
|
|
1253
1798
|
);
|
|
1254
|
-
|
|
1255
|
-
|
|
1799
|
+
}
|
|
1800
|
+
if (!currentSigner?.address) {
|
|
1801
|
+
return await handleSubmissionFailure(
|
|
1802
|
+
{ data: config.data },
|
|
1803
|
+
created.id,
|
|
1804
|
+
new CapxulError({
|
|
1256
1805
|
code: "PERMISSION_DENIED",
|
|
1257
1806
|
message: "No signer is registered for the authenticated account.",
|
|
1258
1807
|
details: { withdrawalId: created.id }
|
|
1259
|
-
})
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1808
|
+
})
|
|
1809
|
+
);
|
|
1810
|
+
}
|
|
1811
|
+
if (viem.getAddress(currentSigner.address) !== viem.getAddress(config.signer.address)) {
|
|
1812
|
+
return await handleSubmissionFailure(
|
|
1813
|
+
{ data: config.data },
|
|
1814
|
+
created.id,
|
|
1815
|
+
new CapxulError({
|
|
1263
1816
|
code: "PERMISSION_DENIED",
|
|
1264
1817
|
message: "Configured signer does not match the authenticated account signer.",
|
|
1265
1818
|
details: {
|
|
@@ -1267,170 +1820,228 @@ function createWithdrawalsClient(config = {}) {
|
|
|
1267
1820
|
expectedSignerAddress: currentSigner.address,
|
|
1268
1821
|
actualSignerAddress: config.signer.address
|
|
1269
1822
|
}
|
|
1270
|
-
})
|
|
1271
|
-
}
|
|
1272
|
-
const submission = await config.data.query(
|
|
1273
|
-
api.withdrawals.queries.prepareSubmission,
|
|
1274
|
-
{ withdrawalId: created.id }
|
|
1823
|
+
})
|
|
1275
1824
|
);
|
|
1276
|
-
|
|
1277
|
-
|
|
1825
|
+
}
|
|
1826
|
+
const [prepErr, submission] = await tryCatch(
|
|
1827
|
+
config.data.query(api.withdrawals.queries.prepareSubmission, {
|
|
1828
|
+
withdrawalId: created.id
|
|
1829
|
+
})
|
|
1830
|
+
);
|
|
1831
|
+
if (prepErr) {
|
|
1832
|
+
return await handleSubmissionFailure(
|
|
1833
|
+
{ data: config.data },
|
|
1834
|
+
created.id,
|
|
1835
|
+
mapCreateError2(fromConvexError(prepErr))
|
|
1836
|
+
);
|
|
1837
|
+
}
|
|
1838
|
+
const destinationAddress = submission?.destinationAddress;
|
|
1839
|
+
if (!submission || !destinationAddress) {
|
|
1840
|
+
return await handleSubmissionFailure(
|
|
1841
|
+
{ data: config.data },
|
|
1842
|
+
created.id,
|
|
1843
|
+
new CapxulError({
|
|
1278
1844
|
code: "NETWORK_ERROR",
|
|
1279
1845
|
message: "withdrawals.prepareSubmission returned no destination.",
|
|
1280
1846
|
details: { withdrawalId: created.id }
|
|
1281
|
-
})
|
|
1282
|
-
|
|
1283
|
-
|
|
1847
|
+
})
|
|
1848
|
+
);
|
|
1849
|
+
}
|
|
1850
|
+
const [transferErr, transferOk] = await tryCatch(
|
|
1851
|
+
transferAsOwner(
|
|
1284
1852
|
{
|
|
1285
1853
|
signer: config.signer,
|
|
1286
1854
|
signing: config.signing
|
|
1287
1855
|
},
|
|
1288
1856
|
{
|
|
1289
1857
|
tokenAddress: resolvePaymentTokenAddress(submission.amount.currency),
|
|
1290
|
-
recipientAddress:
|
|
1858
|
+
recipientAddress: destinationAddress,
|
|
1291
1859
|
amount: toTokenUnits(submission.amount.value, 6)
|
|
1292
1860
|
}
|
|
1861
|
+
)
|
|
1862
|
+
);
|
|
1863
|
+
if (transferErr) {
|
|
1864
|
+
return await handleSubmissionFailure(
|
|
1865
|
+
{ data: config.data },
|
|
1866
|
+
created.id,
|
|
1867
|
+
mapCreateError2(fromConvexError(transferErr))
|
|
1293
1868
|
);
|
|
1294
|
-
|
|
1295
|
-
|
|
1869
|
+
}
|
|
1870
|
+
if (!transferOk.success) {
|
|
1871
|
+
return await handleSubmissionFailure(
|
|
1872
|
+
{ data: config.data },
|
|
1873
|
+
created.id,
|
|
1874
|
+
new CapxulError({
|
|
1296
1875
|
code: "NETWORK_ERROR",
|
|
1297
1876
|
message: "Bundler submission did not succeed.",
|
|
1298
1877
|
details: {
|
|
1299
1878
|
withdrawalId: created.id,
|
|
1300
|
-
txHash:
|
|
1301
|
-
userOpHash:
|
|
1879
|
+
txHash: transferOk.txHash,
|
|
1880
|
+
userOpHash: transferOk.userOpHash
|
|
1302
1881
|
}
|
|
1303
|
-
})
|
|
1304
|
-
}
|
|
1305
|
-
submitted = {
|
|
1306
|
-
txHash: transfer.txHash,
|
|
1307
|
-
userOpHash: transfer.userOpHash
|
|
1308
|
-
};
|
|
1309
|
-
await config.data.mutation(
|
|
1310
|
-
api.withdrawals.mutations.recordSubmitted,
|
|
1311
|
-
{
|
|
1312
|
-
withdrawalId: created.id,
|
|
1313
|
-
txHash: transfer.txHash,
|
|
1314
|
-
userOpHash: transfer.userOpHash
|
|
1315
|
-
}
|
|
1882
|
+
})
|
|
1316
1883
|
);
|
|
1317
|
-
return [null, created];
|
|
1318
|
-
} catch (cause) {
|
|
1319
|
-
const error = mapCreateError2(fromConvexError(cause));
|
|
1320
|
-
if (created?.id && created.status === "processing" && !submitted) {
|
|
1321
|
-
await bestEffortMarkFailed2({ data: config.data }, created.id, error);
|
|
1322
|
-
}
|
|
1323
|
-
if (submitted && created?.id) {
|
|
1324
|
-
return [
|
|
1325
|
-
new CapxulError({
|
|
1326
|
-
code: "NETWORK_ERROR",
|
|
1327
|
-
message: "Withdrawal was submitted on-chain, but backend submission tracking failed.",
|
|
1328
|
-
cause,
|
|
1329
|
-
details: {
|
|
1330
|
-
withdrawalId: created.id,
|
|
1331
|
-
txHash: submitted.txHash,
|
|
1332
|
-
userOpHash: submitted.userOpHash
|
|
1333
|
-
}
|
|
1334
|
-
}),
|
|
1335
|
-
null
|
|
1336
|
-
];
|
|
1337
|
-
}
|
|
1338
|
-
return [error, null];
|
|
1339
1884
|
}
|
|
1885
|
+
const [recordErr] = await tryCatch(
|
|
1886
|
+
config.data.mutation(api.withdrawals.mutations.recordSubmitted, {
|
|
1887
|
+
withdrawalId: created.id,
|
|
1888
|
+
txHash: transferOk.txHash,
|
|
1889
|
+
userOpHash: transferOk.userOpHash
|
|
1890
|
+
})
|
|
1891
|
+
);
|
|
1892
|
+
if (recordErr) {
|
|
1893
|
+
return [
|
|
1894
|
+
new CapxulError({
|
|
1895
|
+
code: "NETWORK_ERROR",
|
|
1896
|
+
message: "Withdrawal was submitted on-chain, but backend submission tracking failed.",
|
|
1897
|
+
cause: recordErr,
|
|
1898
|
+
details: {
|
|
1899
|
+
withdrawalId: created.id,
|
|
1900
|
+
txHash: transferOk.txHash,
|
|
1901
|
+
userOpHash: transferOk.userOpHash
|
|
1902
|
+
}
|
|
1903
|
+
}),
|
|
1904
|
+
null
|
|
1905
|
+
];
|
|
1906
|
+
}
|
|
1907
|
+
return [null, created];
|
|
1340
1908
|
},
|
|
1341
1909
|
retrieve: async (withdrawalId) => {
|
|
1342
1910
|
if (!config.data) {
|
|
1343
1911
|
return stub("withdrawals.retrieve");
|
|
1344
1912
|
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
);
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
code: "NOT_FOUND",
|
|
1354
|
-
message: `withdrawal ${withdrawalId} not found`
|
|
1355
|
-
}),
|
|
1356
|
-
null
|
|
1357
|
-
];
|
|
1358
|
-
}
|
|
1359
|
-
return [null, withdrawal];
|
|
1360
|
-
} catch (cause) {
|
|
1913
|
+
const [err, raw] = await tryCatch(
|
|
1914
|
+
config.data.query(api.withdrawals.queries.retrieve, { withdrawalId })
|
|
1915
|
+
);
|
|
1916
|
+
if (err) {
|
|
1917
|
+
return [fromConvexError(err), null];
|
|
1918
|
+
}
|
|
1919
|
+
const withdrawal = raw;
|
|
1920
|
+
if (!withdrawal) {
|
|
1361
1921
|
return [
|
|
1362
|
-
|
|
1922
|
+
new CapxulError({
|
|
1923
|
+
code: "NOT_FOUND",
|
|
1924
|
+
message: `withdrawal ${withdrawalId} not found`
|
|
1925
|
+
}),
|
|
1363
1926
|
null
|
|
1364
1927
|
];
|
|
1365
1928
|
}
|
|
1929
|
+
return [null, withdrawal];
|
|
1366
1930
|
},
|
|
1367
1931
|
list: async (input) => {
|
|
1368
1932
|
if (!config.data) {
|
|
1369
1933
|
return stub("withdrawals.list");
|
|
1370
1934
|
}
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1935
|
+
const [err, raw] = await tryCatch(
|
|
1936
|
+
config.data.query(api.withdrawals.queries.list, {
|
|
1937
|
+
limit: input?.limit,
|
|
1938
|
+
cursor: input?.cursor
|
|
1939
|
+
})
|
|
1940
|
+
);
|
|
1941
|
+
if (err) {
|
|
1942
|
+
return [fromConvexError(err), null];
|
|
1943
|
+
}
|
|
1944
|
+
return [null, raw];
|
|
1945
|
+
},
|
|
1946
|
+
recordCompleted: async (input) => {
|
|
1947
|
+
if (!config.data) {
|
|
1948
|
+
return stub(
|
|
1949
|
+
"withdrawals.recordCompleted"
|
|
1378
1950
|
);
|
|
1379
|
-
|
|
1380
|
-
|
|
1951
|
+
}
|
|
1952
|
+
const [err] = await tryCatch(
|
|
1953
|
+
config.data.mutation(api.withdrawals.mutations.recordCompleted, {
|
|
1954
|
+
withdrawalId: input.withdrawalId,
|
|
1955
|
+
txHash: input.txHash
|
|
1956
|
+
})
|
|
1957
|
+
);
|
|
1958
|
+
if (err) {
|
|
1381
1959
|
return [
|
|
1382
|
-
fromConvexError(
|
|
1960
|
+
mapRecordCompletedError(fromConvexError(err)),
|
|
1383
1961
|
null
|
|
1384
1962
|
];
|
|
1385
1963
|
}
|
|
1964
|
+
return [null, null];
|
|
1386
1965
|
}
|
|
1387
1966
|
};
|
|
1388
1967
|
}
|
|
1389
1968
|
function createOrgWithdrawalsClient(config = {}) {
|
|
1390
1969
|
return {
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1970
|
+
/**
|
|
1971
|
+
* Org-scope create (Withdrawals v1 W2, #465).
|
|
1972
|
+
*
|
|
1973
|
+
* D6 — returns the `processing` row only. No `transferAsOwner`
|
|
1974
|
+
* tail, no `recordSubmitted` call. Org Safe + Zodiac submission
|
|
1975
|
+
* orchestration ships in W3+.
|
|
1976
|
+
*/
|
|
1977
|
+
create: async (input) => {
|
|
1978
|
+
if (!config.data) {
|
|
1979
|
+
return stub(
|
|
1980
|
+
"organizations.withdrawals.create"
|
|
1981
|
+
);
|
|
1982
|
+
}
|
|
1983
|
+
const [err, raw] = await tryCatch(
|
|
1984
|
+
config.data.mutation(api.withdrawals.mutations.createOrg, {
|
|
1985
|
+
organizationId: input.organizationId,
|
|
1986
|
+
amount: input.amount,
|
|
1987
|
+
destination: {
|
|
1988
|
+
externalAccountId: input.destination.externalAccountId
|
|
1989
|
+
},
|
|
1990
|
+
source: input.source,
|
|
1991
|
+
reference: input.reference,
|
|
1992
|
+
idempotencyKey: input.idempotencyKey
|
|
1993
|
+
})
|
|
1994
|
+
);
|
|
1995
|
+
if (err) {
|
|
1996
|
+
return [mapCreateError2(fromConvexError(err)), null];
|
|
1997
|
+
}
|
|
1998
|
+
const created = raw;
|
|
1999
|
+
if (!created) {
|
|
2000
|
+
return [
|
|
2001
|
+
new CapxulError({
|
|
2002
|
+
code: "NETWORK_ERROR",
|
|
2003
|
+
message: "organizations.withdrawals.create returned no withdrawal resource"
|
|
2004
|
+
}),
|
|
2005
|
+
null
|
|
2006
|
+
];
|
|
2007
|
+
}
|
|
2008
|
+
return [null, created];
|
|
2009
|
+
},
|
|
1397
2010
|
retrieve: async (input) => {
|
|
1398
2011
|
if (!config.data) {
|
|
1399
2012
|
return stub(
|
|
1400
2013
|
"organizations.withdrawals.retrieve"
|
|
1401
2014
|
);
|
|
1402
2015
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
}),
|
|
1414
|
-
null
|
|
1415
|
-
];
|
|
1416
|
-
}
|
|
1417
|
-
const ownerCheck = withdrawal.owner;
|
|
1418
|
-
if (ownerCheck?.type !== "organization" || ownerCheck.id !== input.organizationId) {
|
|
1419
|
-
return [
|
|
1420
|
-
new CapxulError({
|
|
1421
|
-
code: "NOT_FOUND",
|
|
1422
|
-
message: `withdrawal ${input.withdrawalId} does not belong to organization ${input.organizationId}`
|
|
1423
|
-
}),
|
|
1424
|
-
null
|
|
1425
|
-
];
|
|
1426
|
-
}
|
|
1427
|
-
return [null, withdrawal];
|
|
1428
|
-
} catch (cause) {
|
|
2016
|
+
const [err, raw] = await tryCatch(
|
|
2017
|
+
config.data.query(api.withdrawals.queries.retrieve, {
|
|
2018
|
+
withdrawalId: input.withdrawalId
|
|
2019
|
+
})
|
|
2020
|
+
);
|
|
2021
|
+
if (err) {
|
|
2022
|
+
return [fromConvexError(err), null];
|
|
2023
|
+
}
|
|
2024
|
+
const withdrawal = raw;
|
|
2025
|
+
if (!withdrawal) {
|
|
1429
2026
|
return [
|
|
1430
|
-
|
|
2027
|
+
new CapxulError({
|
|
2028
|
+
code: "NOT_FOUND",
|
|
2029
|
+
message: `withdrawal ${input.withdrawalId} not found`
|
|
2030
|
+
}),
|
|
2031
|
+
null
|
|
2032
|
+
];
|
|
2033
|
+
}
|
|
2034
|
+
const ownerCheck = withdrawal.owner;
|
|
2035
|
+
if (ownerCheck?.type !== "organization" || ownerCheck.id !== input.organizationId) {
|
|
2036
|
+
return [
|
|
2037
|
+
new CapxulError({
|
|
2038
|
+
code: "NOT_FOUND",
|
|
2039
|
+
message: `withdrawal ${input.withdrawalId} does not belong to organization ${input.organizationId}`
|
|
2040
|
+
}),
|
|
1431
2041
|
null
|
|
1432
2042
|
];
|
|
1433
2043
|
}
|
|
2044
|
+
return [null, withdrawal];
|
|
1434
2045
|
},
|
|
1435
2046
|
list: async (input) => {
|
|
1436
2047
|
if (!config.data) {
|
|
@@ -1438,46 +2049,67 @@ function createOrgWithdrawalsClient(config = {}) {
|
|
|
1438
2049
|
"organizations.withdrawals.list"
|
|
1439
2050
|
);
|
|
1440
2051
|
}
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
);
|
|
1450
|
-
return [null, result];
|
|
1451
|
-
} catch (cause) {
|
|
1452
|
-
return [
|
|
1453
|
-
fromConvexError(cause),
|
|
1454
|
-
null
|
|
1455
|
-
];
|
|
2052
|
+
const [err, raw] = await tryCatch(
|
|
2053
|
+
config.data.query(api.withdrawals.queries.listOrg, {
|
|
2054
|
+
organizationId: input.organizationId,
|
|
2055
|
+
limit: input.limit,
|
|
2056
|
+
cursor: input.cursor
|
|
2057
|
+
})
|
|
2058
|
+
);
|
|
2059
|
+
if (err) {
|
|
2060
|
+
return [fromConvexError(err), null];
|
|
1456
2061
|
}
|
|
2062
|
+
return [null, raw];
|
|
1457
2063
|
}
|
|
1458
2064
|
};
|
|
1459
2065
|
}
|
|
2066
|
+
async function handleSubmissionFailure(config, withdrawalId, error) {
|
|
2067
|
+
await bestEffortMarkFailed2(config, withdrawalId, error);
|
|
2068
|
+
return [error, null];
|
|
2069
|
+
}
|
|
1460
2070
|
async function bestEffortMarkFailed2(config, withdrawalId, error) {
|
|
1461
|
-
|
|
1462
|
-
|
|
2071
|
+
await tryCatch(
|
|
2072
|
+
config.data.mutation(api.withdrawals.mutations.markFailed, {
|
|
1463
2073
|
withdrawalId,
|
|
1464
2074
|
errorCode: error.code,
|
|
1465
2075
|
errorMessage: error.message
|
|
1466
|
-
})
|
|
1467
|
-
|
|
2076
|
+
})
|
|
2077
|
+
);
|
|
2078
|
+
}
|
|
2079
|
+
function mapCreateError2(error) {
|
|
2080
|
+
switch (error.code) {
|
|
2081
|
+
case "NOT_AUTHENTICATED":
|
|
2082
|
+
case "PERMISSION_DENIED":
|
|
2083
|
+
case "INVALID_INPUT":
|
|
2084
|
+
case "INSUFFICIENT_BALANCE":
|
|
2085
|
+
case "IDEMPOTENCY_CONFLICT":
|
|
2086
|
+
case "KYC_REQUIRED":
|
|
2087
|
+
case "POLICY_DENIED":
|
|
2088
|
+
case "RATE_LIMITED":
|
|
2089
|
+
case "NETWORK_ERROR":
|
|
2090
|
+
case "NOT_FOUND":
|
|
2091
|
+
case "VERIFICATION_REQUIRED":
|
|
2092
|
+
return error;
|
|
2093
|
+
default:
|
|
2094
|
+
return new CapxulError({
|
|
2095
|
+
code: "NETWORK_ERROR",
|
|
2096
|
+
message: error.message,
|
|
2097
|
+
cause: error,
|
|
2098
|
+
details: error.details,
|
|
2099
|
+
operationId: error.operationId,
|
|
2100
|
+
correlationId: error.correlationId,
|
|
2101
|
+
retryable: error.retryable
|
|
2102
|
+
});
|
|
1468
2103
|
}
|
|
1469
2104
|
}
|
|
1470
|
-
function
|
|
2105
|
+
function mapRecordCompletedError(error) {
|
|
1471
2106
|
switch (error.code) {
|
|
1472
2107
|
case "NOT_AUTHENTICATED":
|
|
1473
2108
|
case "PERMISSION_DENIED":
|
|
1474
2109
|
case "INVALID_INPUT":
|
|
1475
|
-
case "
|
|
1476
|
-
case "IDEMPOTENCY_CONFLICT":
|
|
1477
|
-
case "KYC_REQUIRED":
|
|
1478
|
-
case "POLICY_DENIED":
|
|
1479
|
-
case "RATE_LIMITED":
|
|
2110
|
+
case "NOT_FOUND":
|
|
1480
2111
|
case "NETWORK_ERROR":
|
|
2112
|
+
case "INTERNAL_ERROR":
|
|
1481
2113
|
return error;
|
|
1482
2114
|
default:
|
|
1483
2115
|
return new CapxulError({
|
|
@@ -1513,6 +2145,136 @@ function createWebhookEventsClient() {
|
|
|
1513
2145
|
}
|
|
1514
2146
|
|
|
1515
2147
|
// src/core/organizations.ts
|
|
2148
|
+
function createOrgExternalAccountsClient(config) {
|
|
2149
|
+
return {
|
|
2150
|
+
create: async (input) => {
|
|
2151
|
+
if (!config.data) {
|
|
2152
|
+
return stub(
|
|
2153
|
+
"organizations.externalAccounts.create"
|
|
2154
|
+
);
|
|
2155
|
+
}
|
|
2156
|
+
const [err, raw] = await tryCatch(
|
|
2157
|
+
config.data.mutation(api.externalAccounts.mutations.createOrg, {
|
|
2158
|
+
organizationId: input.organizationId,
|
|
2159
|
+
kind: input.kind,
|
|
2160
|
+
label: input.label,
|
|
2161
|
+
address: input.address,
|
|
2162
|
+
iban: input.iban,
|
|
2163
|
+
bic: input.bic,
|
|
2164
|
+
accountHolder: input.accountHolder,
|
|
2165
|
+
network: input.network,
|
|
2166
|
+
panToken: input.panToken,
|
|
2167
|
+
last4: input.last4
|
|
2168
|
+
})
|
|
2169
|
+
);
|
|
2170
|
+
if (err) {
|
|
2171
|
+
return [
|
|
2172
|
+
fromConvexError(err),
|
|
2173
|
+
null
|
|
2174
|
+
];
|
|
2175
|
+
}
|
|
2176
|
+
if (!raw) {
|
|
2177
|
+
return [
|
|
2178
|
+
new CapxulError({
|
|
2179
|
+
code: "NOT_FOUND",
|
|
2180
|
+
message: "external_account creation returned no resource"
|
|
2181
|
+
}),
|
|
2182
|
+
null
|
|
2183
|
+
];
|
|
2184
|
+
}
|
|
2185
|
+
return [
|
|
2186
|
+
null,
|
|
2187
|
+
brandExternalAccount(
|
|
2188
|
+
raw
|
|
2189
|
+
)
|
|
2190
|
+
];
|
|
2191
|
+
},
|
|
2192
|
+
list: async (input) => {
|
|
2193
|
+
if (!config.data) {
|
|
2194
|
+
return stub(
|
|
2195
|
+
"organizations.externalAccounts.list"
|
|
2196
|
+
);
|
|
2197
|
+
}
|
|
2198
|
+
const [err, result] = await tryCatch(
|
|
2199
|
+
config.data.query(api.externalAccounts.queries.listOrg, {
|
|
2200
|
+
organizationId: input.organizationId,
|
|
2201
|
+
limit: input.limit,
|
|
2202
|
+
cursor: input.cursor
|
|
2203
|
+
})
|
|
2204
|
+
);
|
|
2205
|
+
if (err) {
|
|
2206
|
+
return [fromConvexError(err), null];
|
|
2207
|
+
}
|
|
2208
|
+
const branded = result.data.map(
|
|
2209
|
+
(row) => brandExternalAccount(
|
|
2210
|
+
row
|
|
2211
|
+
)
|
|
2212
|
+
);
|
|
2213
|
+
return [
|
|
2214
|
+
null,
|
|
2215
|
+
{
|
|
2216
|
+
object: "list",
|
|
2217
|
+
data: branded,
|
|
2218
|
+
page: result.page
|
|
2219
|
+
}
|
|
2220
|
+
];
|
|
2221
|
+
},
|
|
2222
|
+
retrieve: async (input) => {
|
|
2223
|
+
if (!config.data) {
|
|
2224
|
+
return stub(
|
|
2225
|
+
"organizations.externalAccounts.retrieve"
|
|
2226
|
+
);
|
|
2227
|
+
}
|
|
2228
|
+
const [err, raw] = await tryCatch(
|
|
2229
|
+
config.data.query(api.externalAccounts.queries.retrieveOrg, {
|
|
2230
|
+
organizationId: input.organizationId,
|
|
2231
|
+
externalAccountId: input.externalAccountId
|
|
2232
|
+
})
|
|
2233
|
+
);
|
|
2234
|
+
if (err) {
|
|
2235
|
+
return [
|
|
2236
|
+
fromConvexError(err),
|
|
2237
|
+
null
|
|
2238
|
+
];
|
|
2239
|
+
}
|
|
2240
|
+
if (!raw) {
|
|
2241
|
+
return [
|
|
2242
|
+
new CapxulError({
|
|
2243
|
+
code: "NOT_FOUND",
|
|
2244
|
+
message: `external_account ${input.externalAccountId} not found`
|
|
2245
|
+
}),
|
|
2246
|
+
null
|
|
2247
|
+
];
|
|
2248
|
+
}
|
|
2249
|
+
return [
|
|
2250
|
+
null,
|
|
2251
|
+
brandExternalAccount(
|
|
2252
|
+
raw
|
|
2253
|
+
)
|
|
2254
|
+
];
|
|
2255
|
+
},
|
|
2256
|
+
remove: async (input) => {
|
|
2257
|
+
if (!config.data) {
|
|
2258
|
+
return stub(
|
|
2259
|
+
"organizations.externalAccounts.remove"
|
|
2260
|
+
);
|
|
2261
|
+
}
|
|
2262
|
+
const [err] = await tryCatch(
|
|
2263
|
+
config.data.mutation(api.externalAccounts.mutations.removeOrg, {
|
|
2264
|
+
organizationId: input.organizationId,
|
|
2265
|
+
externalAccountId: input.externalAccountId
|
|
2266
|
+
})
|
|
2267
|
+
);
|
|
2268
|
+
if (err) {
|
|
2269
|
+
return [
|
|
2270
|
+
fromConvexError(err),
|
|
2271
|
+
null
|
|
2272
|
+
];
|
|
2273
|
+
}
|
|
2274
|
+
return [null, void 0];
|
|
2275
|
+
}
|
|
2276
|
+
};
|
|
2277
|
+
}
|
|
1516
2278
|
function createOrganizationsClient(config = {}) {
|
|
1517
2279
|
return {
|
|
1518
2280
|
create: async () => stub("organizations.create"),
|
|
@@ -1568,18 +2330,7 @@ function createOrganizationsClient(config = {}) {
|
|
|
1568
2330
|
retrieve: async () => stub("organizations.subAccounts.retrieve"),
|
|
1569
2331
|
remove: async () => stub("organizations.subAccounts.remove")
|
|
1570
2332
|
},
|
|
1571
|
-
externalAccounts:
|
|
1572
|
-
create: async () => stub(
|
|
1573
|
-
"organizations.externalAccounts.create"
|
|
1574
|
-
),
|
|
1575
|
-
list: async () => stub(
|
|
1576
|
-
"organizations.externalAccounts.list"
|
|
1577
|
-
),
|
|
1578
|
-
retrieve: async () => stub(
|
|
1579
|
-
"organizations.externalAccounts.retrieve"
|
|
1580
|
-
),
|
|
1581
|
-
remove: async () => stub("organizations.externalAccounts.remove")
|
|
1582
|
-
},
|
|
2333
|
+
externalAccounts: createOrgExternalAccountsClient(config),
|
|
1583
2334
|
balanceLedger: {
|
|
1584
2335
|
list: async () => stub(
|
|
1585
2336
|
"organizations.balanceLedger.list"
|
|
@@ -1716,59 +2467,6 @@ function createVirtualCardsClient() {
|
|
|
1716
2467
|
cancel: async () => stub("virtualCards.cancel")
|
|
1717
2468
|
};
|
|
1718
2469
|
}
|
|
1719
|
-
|
|
1720
|
-
// ../observability/src/try-catch.ts
|
|
1721
|
-
async function tryCatch(promise) {
|
|
1722
|
-
try {
|
|
1723
|
-
return [null, await promise];
|
|
1724
|
-
} catch (e) {
|
|
1725
|
-
return [e instanceof Error ? e : new Error(String(e)), null];
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
|
-
// ../observability/src/debug-log.ts
|
|
1730
|
-
function isDevelopmentBuild() {
|
|
1731
|
-
if (typeof process === "undefined") {
|
|
1732
|
-
return false;
|
|
1733
|
-
}
|
|
1734
|
-
return process.env?.NODE_ENV === "development" || process.env?.NODE_ENV === "test";
|
|
1735
|
-
}
|
|
1736
|
-
function debugLog(line) {
|
|
1737
|
-
if (!isDevelopmentBuild()) return;
|
|
1738
|
-
if (typeof globalThis !== "undefined" && "window" in globalThis && typeof console?.info === "function") {
|
|
1739
|
-
console.info(line);
|
|
1740
|
-
return;
|
|
1741
|
-
}
|
|
1742
|
-
if (typeof process !== "undefined" && typeof process.stderr?.write === "function") {
|
|
1743
|
-
process.stderr.write(`${line}
|
|
1744
|
-
`);
|
|
1745
|
-
}
|
|
1746
|
-
}
|
|
1747
|
-
function formatDebugValue(value) {
|
|
1748
|
-
if (value === void 0 || value === "") return "";
|
|
1749
|
-
if (typeof value === "string") return value;
|
|
1750
|
-
try {
|
|
1751
|
-
return JSON.stringify(value);
|
|
1752
|
-
} catch {
|
|
1753
|
-
return String(value);
|
|
1754
|
-
}
|
|
1755
|
-
}
|
|
1756
|
-
function track(...args) {
|
|
1757
|
-
const [name, props] = args;
|
|
1758
|
-
debugLog(`[TRACK] ${name} ${formatDebugValue(props ?? "")}`);
|
|
1759
|
-
}
|
|
1760
|
-
function formatDebugValue2(value) {
|
|
1761
|
-
if (value === void 0 || value === "") return "";
|
|
1762
|
-
if (typeof value === "string") return value;
|
|
1763
|
-
try {
|
|
1764
|
-
return JSON.stringify(value);
|
|
1765
|
-
} catch {
|
|
1766
|
-
return String(value);
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
function identify(userId, traits) {
|
|
1770
|
-
debugLog(`[IDENTIFY] ${userId} ${formatDebugValue2(traits ?? "")}`);
|
|
1771
|
-
}
|
|
1772
2470
|
function createAuthFlowMachine(client) {
|
|
1773
2471
|
return xstate.setup({
|
|
1774
2472
|
types: {},
|
|
@@ -2044,122 +2742,6 @@ function emailDomain(email) {
|
|
|
2044
2742
|
const domain = email.split("@")[1]?.trim().toLowerCase();
|
|
2045
2743
|
return domain || "unknown";
|
|
2046
2744
|
}
|
|
2047
|
-
|
|
2048
|
-
// ../platform-kernel/src/ids.ts
|
|
2049
|
-
function makePrefixedIdConstructor(prefix, fieldName) {
|
|
2050
|
-
const re = new RegExp(`^${prefix}_[A-Za-z0-9_\\-]+$`);
|
|
2051
|
-
return (raw) => {
|
|
2052
|
-
if (typeof raw !== "string" || !re.test(raw)) {
|
|
2053
|
-
throw new Error(
|
|
2054
|
-
`Invalid ${fieldName}: expected string matching ^${prefix}_[A-Za-z0-9_\\-]+$, got ${String(raw)}`
|
|
2055
|
-
);
|
|
2056
|
-
}
|
|
2057
|
-
return raw;
|
|
2058
|
-
};
|
|
2059
|
-
}
|
|
2060
|
-
var toAccountId = makePrefixedIdConstructor(
|
|
2061
|
-
"acct",
|
|
2062
|
-
"accountId"
|
|
2063
|
-
);
|
|
2064
|
-
var toOrganizationId = makePrefixedIdConstructor("org", "organizationId");
|
|
2065
|
-
var toMemberId = makePrefixedIdConstructor(
|
|
2066
|
-
"mem",
|
|
2067
|
-
"memberId"
|
|
2068
|
-
);
|
|
2069
|
-
var toSafeId = makePrefixedIdConstructor(
|
|
2070
|
-
"safe",
|
|
2071
|
-
"safeId"
|
|
2072
|
-
);
|
|
2073
|
-
var toTreasuryId = makePrefixedIdConstructor(
|
|
2074
|
-
"try",
|
|
2075
|
-
"treasuryId"
|
|
2076
|
-
);
|
|
2077
|
-
var toApiKeyId = makePrefixedIdConstructor(
|
|
2078
|
-
"ak",
|
|
2079
|
-
"apiKeyId"
|
|
2080
|
-
);
|
|
2081
|
-
var toOperationId = makePrefixedIdConstructor(
|
|
2082
|
-
"op",
|
|
2083
|
-
"operationId"
|
|
2084
|
-
);
|
|
2085
|
-
var toKycProfileId = makePrefixedIdConstructor(
|
|
2086
|
-
"kyc",
|
|
2087
|
-
"kycProfileId"
|
|
2088
|
-
);
|
|
2089
|
-
var toKybProfileId = makePrefixedIdConstructor(
|
|
2090
|
-
"kyb",
|
|
2091
|
-
"kybProfileId"
|
|
2092
|
-
);
|
|
2093
|
-
var toExternalAccountId = makePrefixedIdConstructor("ext", "externalAccountId");
|
|
2094
|
-
var toPaymentId = makePrefixedIdConstructor(
|
|
2095
|
-
"pay",
|
|
2096
|
-
"paymentId"
|
|
2097
|
-
);
|
|
2098
|
-
var toWithdrawalId = makePrefixedIdConstructor(
|
|
2099
|
-
"wd",
|
|
2100
|
-
"withdrawalId"
|
|
2101
|
-
);
|
|
2102
|
-
var toWebhookEndpointId = makePrefixedIdConstructor("we", "webhookEndpointId");
|
|
2103
|
-
var toWebhookEventId = makePrefixedIdConstructor("evt", "webhookEventId");
|
|
2104
|
-
var toSubAccountId = makePrefixedIdConstructor(
|
|
2105
|
-
"sub",
|
|
2106
|
-
"subAccountId"
|
|
2107
|
-
);
|
|
2108
|
-
var toVirtualAccountId = makePrefixedIdConstructor("va", "virtualAccountId");
|
|
2109
|
-
var toVirtualCardId = makePrefixedIdConstructor(
|
|
2110
|
-
"vc",
|
|
2111
|
-
"virtualCardId"
|
|
2112
|
-
);
|
|
2113
|
-
var toTransferId = makePrefixedIdConstructor(
|
|
2114
|
-
"txfr",
|
|
2115
|
-
"transferId"
|
|
2116
|
-
);
|
|
2117
|
-
var toDocumentId = makePrefixedIdConstructor(
|
|
2118
|
-
"doc",
|
|
2119
|
-
"documentId"
|
|
2120
|
-
);
|
|
2121
|
-
var toBalanceLedgerEntryId = makePrefixedIdConstructor("bal", "balanceLedgerEntryId");
|
|
2122
|
-
|
|
2123
|
-
// ../platform-kernel/src/value-objects.ts
|
|
2124
|
-
var PHONE_NUMBER_RE = /^\+[1-9]\d{1,14}$/;
|
|
2125
|
-
function toPhoneNumber(raw) {
|
|
2126
|
-
if (typeof raw !== "string" || !PHONE_NUMBER_RE.test(raw)) {
|
|
2127
|
-
throw new Error(
|
|
2128
|
-
`Invalid phoneNumber: expected E.164 string matching ^\\+[1-9]\\d{1,14}$, got ${String(raw)}`
|
|
2129
|
-
);
|
|
2130
|
-
}
|
|
2131
|
-
return raw;
|
|
2132
|
-
}
|
|
2133
|
-
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2134
|
-
function toEmail(raw) {
|
|
2135
|
-
if (typeof raw !== "string") {
|
|
2136
|
-
throw new Error(
|
|
2137
|
-
`Invalid email: expected string, got ${String(raw)}`
|
|
2138
|
-
);
|
|
2139
|
-
}
|
|
2140
|
-
const lowered = raw.trim().toLowerCase();
|
|
2141
|
-
if (!EMAIL_RE.test(lowered)) {
|
|
2142
|
-
throw new Error(
|
|
2143
|
-
`Invalid email: expected local@domain.tld, got ${String(raw)}`
|
|
2144
|
-
);
|
|
2145
|
-
}
|
|
2146
|
-
return lowered;
|
|
2147
|
-
}
|
|
2148
|
-
var USERNAME_RE = /^[a-z][a-z0-9_-]{2,29}$/;
|
|
2149
|
-
function toUsername(raw) {
|
|
2150
|
-
if (typeof raw !== "string") {
|
|
2151
|
-
throw new Error(
|
|
2152
|
-
`Invalid username: expected string, got ${String(raw)}`
|
|
2153
|
-
);
|
|
2154
|
-
}
|
|
2155
|
-
const lowered = raw.toLowerCase();
|
|
2156
|
-
if (!USERNAME_RE.test(lowered)) {
|
|
2157
|
-
throw new Error(
|
|
2158
|
-
`Invalid username: expected 3-30 chars, letter-first, [a-z0-9_-], got ${String(raw)}`
|
|
2159
|
-
);
|
|
2160
|
-
}
|
|
2161
|
-
return lowered;
|
|
2162
|
-
}
|
|
2163
2745
|
function createProvisioningMachine(client) {
|
|
2164
2746
|
return xstate.setup({
|
|
2165
2747
|
types: {},
|
|
@@ -2552,7 +3134,7 @@ function createCapxulClient(config = {}) {
|
|
|
2552
3134
|
subAccounts: createSubAccountsClient(),
|
|
2553
3135
|
virtualAccounts: createVirtualAccountsClient(),
|
|
2554
3136
|
virtualCards: createVirtualCardsClient(),
|
|
2555
|
-
externalAccounts: createExternalAccountsClient(),
|
|
3137
|
+
externalAccounts: createExternalAccountsClient(config),
|
|
2556
3138
|
operations: createOperationsClient(config),
|
|
2557
3139
|
webhookEndpoints: createWebhookEndpointsClient(),
|
|
2558
3140
|
webhookEvents: createWebhookEventsClient(),
|