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