@capxul/sdk 4.1.4 → 4.2.0-rc.10
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/dist/{signer-C0hZ6Kiy.d.mts → OAuthBearerAuthClient-C-ip-z8M.d.mts} +30 -6
- package/dist/{clock-C2AUlq1V.mjs → OAuthBearerAuthClient-IB2W1Fzj.mjs} +630 -52
- package/dist/capxul-source.json +11 -0
- package/dist/index.d.mts +65 -51
- package/dist/index.mjs +144 -3505
- package/dist/node/index.d.mts +2 -3
- package/dist/node/index.mjs +1 -2
- package/dist/{create-capxul-client-B3ET402J.mjs → production-BndbwuVx.mjs} +7717 -2966
- package/dist/{observation-Bhdq9-l6.d.mts → production-Du4Vc7YD.d.mts} +248 -34
- package/dist/testing/index.d.mts +82 -2
- package/dist/testing/index.mjs +101 -4
- package/package.json +5 -5
- package/dist/OAuthBearerAuthClient-BHvDR8_0.mjs +0 -211
- package/dist/OAuthBearerAuthClient-D-DLYlKj.d.mts +0 -19
|
@@ -1,39 +1,275 @@
|
|
|
1
|
-
import { Context, Data, Effect, Layer, Result } from "effect";
|
|
2
1
|
import { concat, encodeAbiParameters, encodeFunctionData, encodePacked, getContractAddress, keccak256, padHex, stringToHex, toBytes, toEventSelector, toFunctionSelector, toFunctionSignature } from "viem";
|
|
2
|
+
import { Context, Data, Effect, Layer, Result } from "effect";
|
|
3
3
|
//#region ../errors/src/secret-material.ts
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
const CREDENTIAL_FIELDS = /* @__PURE__ */ new Set([
|
|
5
|
+
"password",
|
|
6
|
+
"passwd",
|
|
7
|
+
"pwd",
|
|
8
|
+
"pass",
|
|
9
|
+
"passphrase",
|
|
10
|
+
"currentpassword",
|
|
11
|
+
"newpassword",
|
|
12
|
+
"confirmpassword",
|
|
13
|
+
"passwordconfirmation",
|
|
14
|
+
"passwordhash",
|
|
15
|
+
"hashedpassword",
|
|
16
|
+
"pin",
|
|
17
|
+
"privatekey",
|
|
18
|
+
"walletprivatekey",
|
|
19
|
+
"secretkey",
|
|
20
|
+
"signingkey",
|
|
21
|
+
"signingsecret",
|
|
22
|
+
"seed",
|
|
23
|
+
"seedphrase",
|
|
24
|
+
"mnemonic",
|
|
25
|
+
"recoveryphrase",
|
|
26
|
+
"recovery",
|
|
27
|
+
"recoverycode",
|
|
28
|
+
"recoverycodes",
|
|
29
|
+
"keyshare",
|
|
30
|
+
"signingmaterial",
|
|
31
|
+
"recoverymaterial",
|
|
32
|
+
"privatekeybytes",
|
|
33
|
+
"signingprivatekey",
|
|
34
|
+
"encryptionkey",
|
|
35
|
+
"decryptionkey",
|
|
36
|
+
"authtoken",
|
|
37
|
+
"apitoken",
|
|
38
|
+
"bearertoken",
|
|
39
|
+
"oauthtoken",
|
|
40
|
+
"csrftoken",
|
|
41
|
+
"clientassertion",
|
|
42
|
+
"token",
|
|
43
|
+
"accesstoken",
|
|
44
|
+
"refreshtoken",
|
|
45
|
+
"idtoken",
|
|
46
|
+
"sessiontoken",
|
|
47
|
+
"session",
|
|
48
|
+
"sessionkey",
|
|
49
|
+
"sessionsecret",
|
|
50
|
+
"authsessionid",
|
|
51
|
+
"sessioncredential",
|
|
52
|
+
"sessioncredentials",
|
|
53
|
+
"sessioncookie",
|
|
54
|
+
"auth",
|
|
55
|
+
"authentication",
|
|
56
|
+
"authorization",
|
|
57
|
+
"proxyauthorization",
|
|
58
|
+
"cookie",
|
|
59
|
+
"cookies",
|
|
60
|
+
"setcookie",
|
|
61
|
+
"apikey",
|
|
62
|
+
"xapikey",
|
|
63
|
+
"xauthtoken",
|
|
64
|
+
"clientsecret",
|
|
65
|
+
"secret",
|
|
66
|
+
"secrets",
|
|
67
|
+
"credential",
|
|
68
|
+
"credentials",
|
|
69
|
+
"signature",
|
|
70
|
+
"otp",
|
|
71
|
+
"otpcode",
|
|
72
|
+
"totpsecret",
|
|
73
|
+
"onetimecode",
|
|
74
|
+
"onetimepassword",
|
|
75
|
+
"verificationcode",
|
|
76
|
+
"authcode",
|
|
77
|
+
"authorizationcode",
|
|
78
|
+
"oauthcode",
|
|
79
|
+
"codeverifier"
|
|
80
|
+
]);
|
|
81
|
+
/** Match credential fields by meaning, without hiding token addresses or replay IDs. */
|
|
82
|
+
function isCredentialField(name, context) {
|
|
83
|
+
const normalized = name.toLowerCase().replace(/[^a-z0-9]/gu, "");
|
|
84
|
+
return CREDENTIAL_FIELDS.has(normalized) || /(?:password(?:hash)?|passwd|passphrase|privatekey|signingkey|apikey|clientsecret|accesstoken|refreshtoken|sessiontoken)$/u.test(normalized) || context === "authentication" && (normalized === "code" || normalized === "sessionid") || context === "header" && /(?:token|secret|apikey|privatekey|signingkey|password|authorization|cookie|session)$/u.test(normalized);
|
|
85
|
+
}
|
|
86
|
+
function decodeUrlComponent(text) {
|
|
87
|
+
try {
|
|
88
|
+
return decodeURIComponent(text);
|
|
89
|
+
} catch {
|
|
90
|
+
return text;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Preserve URL diagnostics while removing credentials from their declared components. */
|
|
94
|
+
function redactUrlSecrets(value, source) {
|
|
95
|
+
try {
|
|
96
|
+
const absolute = /^[a-z][a-z0-9+.-]*:/iu.test(value);
|
|
97
|
+
const protocolRelative = value.startsWith("//");
|
|
98
|
+
const url = new URL(value, "https://redaction.invalid");
|
|
99
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") return maskText(value);
|
|
100
|
+
let changed = url.username.length > 0 || url.password.length > 0;
|
|
101
|
+
const maskParams = (params) => new URLSearchParams(Array.from(params, ([name, entry]) => {
|
|
102
|
+
const safeName = maskText(name);
|
|
103
|
+
const safeEntry = isCredentialField(name) || /^(?:code|draft|__posthog)$/iu.test(name) ? "[REDACTED]" : maskText(entry);
|
|
104
|
+
changed ||= safeName !== name || safeEntry !== entry;
|
|
105
|
+
return [safeName, safeEntry];
|
|
106
|
+
})).toString();
|
|
107
|
+
const provider = source !== void 0 || /(?:^|\.)(?:alchemy\.com|alchemyapi\.io|infura\.io)$/iu.test(url.hostname);
|
|
108
|
+
const segments = (absolute || protocolRelative ? url.pathname : value.split(/[?#]/u)[0] ?? "").split("/");
|
|
109
|
+
const path = segments.map((segment, index) => {
|
|
110
|
+
const decoded = decodeUrlComponent(segment);
|
|
111
|
+
const masked = provider && /^v[23]$/u.test(decodeUrlComponent(segments[index - 1] ?? "")) && decoded.length > 0 ? "[REDACTED]" : maskText(decoded);
|
|
112
|
+
changed ||= masked !== decoded;
|
|
113
|
+
return masked === decoded ? segment : encodeURIComponent(masked);
|
|
114
|
+
}).join("/");
|
|
115
|
+
const query = maskParams(url.searchParams);
|
|
116
|
+
const fragment = url.hash.slice(1);
|
|
117
|
+
const decodedFragment = decodeUrlComponent(fragment);
|
|
118
|
+
const maskedFragment = maskText(decodedFragment);
|
|
119
|
+
changed ||= maskedFragment !== decodedFragment;
|
|
120
|
+
const fragmentQuery = fragment.indexOf("?");
|
|
121
|
+
const fragmentPath = fragment.slice(0, fragmentQuery);
|
|
122
|
+
const maskedFragmentPath = maskText(decodeUrlComponent(fragmentPath));
|
|
123
|
+
changed ||= maskedFragmentPath !== decodeUrlComponent(fragmentPath);
|
|
124
|
+
const hash = fragmentQuery >= 0 ? `${maskedFragmentPath === decodeUrlComponent(fragmentPath) ? fragmentPath : encodeURIComponent(maskedFragmentPath)}?${maskParams(new URLSearchParams(fragment.slice(fragmentQuery + 1)))}` : fragment.includes("=") ? maskParams(new URLSearchParams(fragment)) : maskedFragment === decodedFragment ? fragment : encodeURIComponent(maskedFragment);
|
|
125
|
+
const origin = absolute ? url.origin : protocolRelative ? `//${url.host}` : "";
|
|
126
|
+
if (!changed) return value;
|
|
127
|
+
return `${origin}${path}${query ? `?${query}` : ""}${hash ? `#${hash}` : ""}`;
|
|
128
|
+
} catch {
|
|
129
|
+
return maskText(value.replace(/(https?:\/\/)[^/\s@]+@/giu, "$1[REDACTED]@").replace(/([?&#])([^=&#]+)=([^&#]*)/gu, (whole, separator, name) => isCredentialField(decodeUrlComponent(name)) || /^(?:code|draft|__posthog)$/iu.test(decodeUrlComponent(name)) ? `${separator}${name}=[REDACTED]` : whole));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const JSON_FIELD = /("(?:[^"\\]|\\.)*")(\s*:\s*)("(?:[^"\\]|\\.)*"|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)/gu;
|
|
133
|
+
const PROSE_FIELD = /(?=(\b([A-Za-z][A-Za-z0-9_-]*(?:[ \t]+[A-Za-z][A-Za-z0-9_-]*){0,3})\s*[:=]\s*((?:Bearer|Basic)[ \t]+[^\s,;"']+|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|[^\s,;&"']+)))/gu;
|
|
134
|
+
const EMBEDDED_URL = /(https?:\/\/[^\s"<>]*)/giu;
|
|
135
|
+
function isSecretLabel(name) {
|
|
136
|
+
return isCredentialField(name) || /^request[ _-]?body$/iu.test(name);
|
|
137
|
+
}
|
|
138
|
+
const JSON_ENCODING_MAX = 8;
|
|
139
|
+
const JSON_NESTING_MAX = 64;
|
|
140
|
+
const JSON_KEY = /("(?:[^"\\]|\\.)*")\s*:/gu;
|
|
141
|
+
const JSON_CONTAINER_START = /(?:\{\s*["}]|\[\s*[[\]{}"0-9tfn-])/uy;
|
|
142
|
+
function hasSecretJsonKey(value) {
|
|
143
|
+
for (const match of value.matchAll(JSON_KEY)) try {
|
|
144
|
+
if (match[1] !== void 0 && isSecretLabel(JSON.parse(match[1]))) return true;
|
|
145
|
+
} catch {}
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
/** Project a complete fragment once. Never retry all of its nested substrings. */
|
|
149
|
+
function projectJsonFragment(value, encodingDepth, overDepth) {
|
|
150
|
+
if (overDepth || encodingDepth >= JSON_ENCODING_MAX) return "[REDACTED]";
|
|
151
|
+
let parsed;
|
|
152
|
+
try {
|
|
153
|
+
parsed = JSON.parse(value);
|
|
154
|
+
} catch {
|
|
155
|
+
return hasSecretJsonKey(value) ? "[REDACTED]" : value;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
let changed = false;
|
|
159
|
+
const masked = JSON.stringify(parsed, (name, nested) => {
|
|
160
|
+
if (isSecretLabel(name)) {
|
|
161
|
+
changed ||= nested !== "[REDACTED]";
|
|
162
|
+
return "[REDACTED]";
|
|
163
|
+
}
|
|
164
|
+
if (typeof nested === "string") {
|
|
165
|
+
const projected = maskJsonFragments(nested, encodingDepth + 1);
|
|
166
|
+
changed ||= projected !== nested;
|
|
167
|
+
return projected;
|
|
168
|
+
}
|
|
169
|
+
return nested;
|
|
170
|
+
});
|
|
171
|
+
return changed ? masked : value;
|
|
172
|
+
} catch {
|
|
173
|
+
return "[REDACTED]";
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/** Scan balanced object/array fragments once, respecting JSON quotes and escapes. */
|
|
177
|
+
function maskJsonFragments(value, encodingDepth = 0) {
|
|
178
|
+
if (value.trimStart().startsWith("\"")) try {
|
|
179
|
+
const decoded = JSON.parse(value);
|
|
180
|
+
if (typeof decoded === "string") {
|
|
181
|
+
if (encodingDepth >= JSON_ENCODING_MAX) return "\"[REDACTED]\"";
|
|
182
|
+
const masked = maskJsonFragments(decoded, encodingDepth + 1);
|
|
183
|
+
return masked === decoded ? value : JSON.stringify(masked);
|
|
184
|
+
}
|
|
185
|
+
} catch {}
|
|
186
|
+
let output = "";
|
|
187
|
+
let cursor = 0;
|
|
188
|
+
let start = -1;
|
|
189
|
+
let depth = 0;
|
|
190
|
+
let overDepth = false;
|
|
191
|
+
let quoted = false;
|
|
192
|
+
let escaped = false;
|
|
193
|
+
for (let index = 0; index < value.length; index++) {
|
|
194
|
+
const character = value[index];
|
|
195
|
+
if (start < 0) {
|
|
196
|
+
JSON_CONTAINER_START.lastIndex = index;
|
|
197
|
+
if (!JSON_CONTAINER_START.test(value)) continue;
|
|
198
|
+
start = index;
|
|
199
|
+
depth = 1;
|
|
200
|
+
overDepth = false;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (quoted) {
|
|
204
|
+
if (escaped) escaped = false;
|
|
205
|
+
else if (character === "\\") escaped = true;
|
|
206
|
+
else if (character === "\"") quoted = false;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (character === "\"") quoted = true;
|
|
210
|
+
else if (character === "{" || character === "[") {
|
|
211
|
+
depth++;
|
|
212
|
+
overDepth ||= depth > JSON_NESTING_MAX;
|
|
213
|
+
} else if (character === "}" || character === "]") {
|
|
214
|
+
depth--;
|
|
215
|
+
if (depth !== 0) continue;
|
|
216
|
+
output += value.slice(cursor, start) + projectJsonFragment(value.slice(start, index + 1), encodingDepth, overDepth);
|
|
217
|
+
cursor = index + 1;
|
|
218
|
+
start = -1;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (start >= 0) {
|
|
222
|
+
const fragment = value.slice(start);
|
|
223
|
+
output += value.slice(cursor, start) + (overDepth || hasSecretJsonKey(fragment) ? "[REDACTED]" : fragment);
|
|
224
|
+
cursor = value.length;
|
|
225
|
+
}
|
|
226
|
+
return output + value.slice(cursor);
|
|
227
|
+
}
|
|
228
|
+
function maskLabelledText(value) {
|
|
229
|
+
const jsonMasked = maskJsonFragments(value).replace(JSON_FIELD, (whole, quotedName, separator) => {
|
|
230
|
+
try {
|
|
231
|
+
return isSecretLabel(JSON.parse(quotedName)) ? `${quotedName}${separator}"[REDACTED]"` : whole;
|
|
232
|
+
} catch {
|
|
233
|
+
return whole;
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
let output = "";
|
|
237
|
+
let cursor = 0;
|
|
238
|
+
for (const match of jsonMasked.matchAll(PROSE_FIELD)) {
|
|
239
|
+
const [, whole, label, entry] = match;
|
|
240
|
+
if (whole === void 0 || label === void 0 || entry === void 0 || match.index < cursor || /^Bearer[ \t]+/iu.test(entry)) continue;
|
|
241
|
+
const words = label.split(/[ \t]+/u);
|
|
242
|
+
for (let index = 0; index < words.length; index++) {
|
|
243
|
+
const name = words.slice(index).join(" ");
|
|
244
|
+
if (isSecretLabel(name)) {
|
|
245
|
+
const prefix = words.slice(0, index).join(" ");
|
|
246
|
+
output += jsonMasked.slice(cursor, match.index) + `${prefix ? `${prefix} ` : ""}${name}=[REDACTED]`;
|
|
247
|
+
cursor = match.index + whole.length;
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return output + jsonMasked.slice(cursor);
|
|
253
|
+
}
|
|
254
|
+
const TOKEN_MASKS = [
|
|
255
|
+
[/\bBearer\s+(?!(?:realm|error|error_description|scope|authorization_uri|resource|claims)\s*=)[^\s,;"']+/giu, "Bearer [REDACTED]"],
|
|
14
256
|
[/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/gu, "[REDACTED]"],
|
|
15
257
|
[/(^|[^a-fA-F0-9])(?<!0[xX])[a-fA-F0-9]{64}(?=$|[^a-fA-F0-9])/gu, "$1[REDACTED]"],
|
|
16
|
-
[/(?:(?:sk|pk|rk)_(?:live|test)|(?:phc|phx|ghp|gho|ghu|ghs|ghr|whsec)_|github_pat_|xox[baprs]-|AKIA[0-9A-Z]{16}|AIza)[A-Za-z0-9_-]*/giu, "[REDACTED]"]
|
|
17
|
-
[/(https?:\/\/[^\s"']+?\/v[0-9]\/)[A-Za-z0-9_-]{16,}/giu, "$1[REDACTED]"],
|
|
18
|
-
[/\b(api[ _-]?key|access[ _-]?token|token|secret|password|passphrase|private[ _-]?key|authorization|cookie|signature|request[ _-]?body|credential|otp|one[ _-]?time[ _-]?(?:password|code)|verification[ _-]?code)\b\s*[:=]\s*(?:"[^"]*"|'[^']*'|[^\s,;&]+)/giu, "$1=[REDACTED]"]
|
|
258
|
+
[/(?:(?:sk|pk|rk)_(?:live|test)|(?:phc|phx|ghp|gho|ghu|ghs|ghr|whsec)_|github_pat_|xox[baprs]-|AKIA[0-9A-Z]{16}|AIza)[A-Za-z0-9_-]*/giu, "[REDACTED]"]
|
|
19
259
|
];
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
260
|
+
/** URL components and malformed URLs use this non-recursive masking layer. */
|
|
261
|
+
function maskText(value) {
|
|
262
|
+
let text = maskLabelledText(value);
|
|
263
|
+
for (const [pattern, replacement] of TOKEN_MASKS) text = text.replace(pattern, replacement);
|
|
264
|
+
return text;
|
|
265
|
+
}
|
|
266
|
+
/** Reject only when the same semantic sanitizer removes credential material. */
|
|
25
267
|
function containsSensitiveMaterial(value) {
|
|
26
|
-
return
|
|
268
|
+
return redactSecrets(value) !== value;
|
|
27
269
|
}
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
* lines, provider messages, PostHog rows) but must not carry live credentials.
|
|
32
|
-
*/
|
|
33
|
-
function redactSecrets(value) {
|
|
34
|
-
let text = value;
|
|
35
|
-
for (const [pattern, replacement] of MASKS) text = text.replace(pattern, replacement);
|
|
36
|
-
return text;
|
|
270
|
+
/** Mask credential fields and embedded URLs while preserving surrounding evidence. */
|
|
271
|
+
function redactSecrets(value, source) {
|
|
272
|
+
return maskLabelledText(value).split(EMBEDDED_URL).map((part, index) => index % 2 === 1 ? redactUrlSecrets(part, source).replaceAll("%5BREDACTED%5D", "[REDACTED]") : maskText(part)).join("");
|
|
37
273
|
}
|
|
38
274
|
//#endregion
|
|
39
275
|
//#region ../errors/src/chain-cause.ts
|
|
@@ -605,7 +841,29 @@ function decodeConvexError(err) {
|
|
|
605
841
|
/** Canonical handle syntax shared by validation and branded constructors. */
|
|
606
842
|
const HANDLE_RE = /^[a-z0-9_-]{3,30}$/;
|
|
607
843
|
//#endregion
|
|
844
|
+
//#region ../config/src/handle.ts
|
|
845
|
+
const RESERVED_HANDLES = /* @__PURE__ */ new Set([
|
|
846
|
+
"admin",
|
|
847
|
+
"api",
|
|
848
|
+
"capxul",
|
|
849
|
+
"help",
|
|
850
|
+
"root",
|
|
851
|
+
"support",
|
|
852
|
+
"system",
|
|
853
|
+
"www"
|
|
854
|
+
]);
|
|
855
|
+
function normalizeHandle(raw) {
|
|
856
|
+
return raw.trim().toLowerCase().replace(/^@/, "");
|
|
857
|
+
}
|
|
858
|
+
function validateHandle(raw) {
|
|
859
|
+
const handle = normalizeHandle(raw);
|
|
860
|
+
if (!HANDLE_RE.test(handle)) throw Errors.invalidInput("handle", "must be 3-30 characters of a-z, 0-9, underscore, or hyphen");
|
|
861
|
+
if (RESERVED_HANDLES.has(handle)) throw Errors.invalidInput("handle", "handle is reserved");
|
|
862
|
+
return handle;
|
|
863
|
+
}
|
|
864
|
+
//#endregion
|
|
608
865
|
//#region ../types/src/index.ts
|
|
866
|
+
const ASSET_ID_RE = /^eip155:([1-9][0-9]*)\/erc20:(0x[0-9a-f]{40})$/;
|
|
609
867
|
const EVM_ADDRESS_RE = /^0x[0-9a-f]{40}$/i;
|
|
610
868
|
const BYTES32_RE = /^0x[0-9a-f]{64}$/i;
|
|
611
869
|
const ZERO_BYTES32 = `0x${"0".repeat(64)}`;
|
|
@@ -644,6 +902,7 @@ const COUNTRY_CODE_RE = /^[A-Z]{2}$/;
|
|
|
644
902
|
const ACCOUNT_ID_RE = /^account_[0-9A-Za-z]+$/;
|
|
645
903
|
const PARTY_ID_RE = /^party_[0-9A-Za-z]+$/;
|
|
646
904
|
const APP_ID_RE = /^app_[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
|
|
905
|
+
const TX_HASH_RE = /^0x[0-9a-f]{64}$/i;
|
|
647
906
|
const WEI_RE = /^[0-9]+$/;
|
|
648
907
|
Math.floor(Number.MAX_SAFE_INTEGER / 1e3);
|
|
649
908
|
function toAddress(raw) {
|
|
@@ -699,6 +958,10 @@ function toPublishableKey(raw) {
|
|
|
699
958
|
if (typeof raw !== "string" || !PUBLISHABLE_KEY_PATTERN.test(raw)) throw Errors.invalidInput("publishableKey", invalidValueReason("must match cap_pk_(test|live) plus 32 Crockford base32 chars", raw));
|
|
700
959
|
return raw;
|
|
701
960
|
}
|
|
961
|
+
function toTxHash(raw) {
|
|
962
|
+
if (typeof raw !== "string" || !TX_HASH_RE.test(raw)) throw Errors.invalidInput("txHash", invalidValueReason("must be 0x + 64 hex chars", raw));
|
|
963
|
+
return raw.toLowerCase();
|
|
964
|
+
}
|
|
702
965
|
function toEpochMs(raw) {
|
|
703
966
|
assertSafeNonNegativeInteger(raw, "epochMs");
|
|
704
967
|
return raw;
|
|
@@ -707,6 +970,17 @@ function toEpochSeconds(raw) {
|
|
|
707
970
|
assertSafeNonNegativeInteger(raw, "epochSeconds");
|
|
708
971
|
return raw;
|
|
709
972
|
}
|
|
973
|
+
/** Validate an already canonical identity without changing signed input. */
|
|
974
|
+
function toAssetId(raw) {
|
|
975
|
+
const match = typeof raw === "string" ? ASSET_ID_RE.exec(raw) : null;
|
|
976
|
+
if (match === null || match[0] !== raw || !Number.isSafeInteger(Number(match[1]))) throw Errors.invalidInput("assetId", "must be a canonical ERC-20 CAIP-19 identifier");
|
|
977
|
+
if (match[2] === "0x0000000000000000000000000000000000000000") throw Errors.invalidInput("assetId", "must identify a nonzero token contract");
|
|
978
|
+
return raw;
|
|
979
|
+
}
|
|
980
|
+
/** Build an identity from verified chain facts. Normalize the token address. */
|
|
981
|
+
function assetIdFor(chainId, tokenAddress) {
|
|
982
|
+
return toAssetId(`eip155:${toChainId(chainId)}/erc20:${toAddress(tokenAddress)}`);
|
|
983
|
+
}
|
|
710
984
|
function toChainId(raw) {
|
|
711
985
|
if (typeof raw !== "number" || !Number.isSafeInteger(raw) || raw <= 0) throw Errors.invalidInput("chainId", invalidValueReason("must be a positive safe integer", raw));
|
|
712
986
|
return raw;
|
|
@@ -769,31 +1043,121 @@ function invalidValueReason(prefix, raw) {
|
|
|
769
1043
|
return `${prefix}: ${String(raw)}`;
|
|
770
1044
|
}
|
|
771
1045
|
//#endregion
|
|
772
|
-
//#region ../config/src/handle.ts
|
|
773
|
-
const RESERVED_HANDLES = /* @__PURE__ */ new Set([
|
|
774
|
-
"admin",
|
|
775
|
-
"api",
|
|
776
|
-
"capxul",
|
|
777
|
-
"help",
|
|
778
|
-
"root",
|
|
779
|
-
"support",
|
|
780
|
-
"system",
|
|
781
|
-
"www"
|
|
782
|
-
]);
|
|
783
|
-
function normalizeHandle(raw) {
|
|
784
|
-
return raw.trim().toLowerCase().replace(/^@/, "");
|
|
785
|
-
}
|
|
786
|
-
function validateHandle(raw) {
|
|
787
|
-
const handle = normalizeHandle(raw);
|
|
788
|
-
if (!HANDLE_RE.test(handle)) throw Errors.invalidInput("handle", "must be 3-30 characters of a-z, 0-9, underscore, or hyphen");
|
|
789
|
-
if (RESERVED_HANDLES.has(handle)) throw Errors.invalidInput("handle", "handle is reserved");
|
|
790
|
-
return handle;
|
|
791
|
-
}
|
|
792
|
-
//#endregion
|
|
793
1046
|
//#region ../config/src/tokens.ts
|
|
794
1047
|
/** `TestUSDC` ("USDX") — Base Sepolia, 6 decimals, open `mint`. (Canon §1.) */
|
|
795
1048
|
const USDX_ADDRESS_BASE_SEPOLIA = "0xf09fcbb6df9f8f918e58f9c8ab15a76ade862b77";
|
|
796
|
-
|
|
1049
|
+
const USDX_DEPLOYMENT_START_BLOCK = 39860173;
|
|
1050
|
+
/**
|
|
1051
|
+
* The ERC-20 `symbol()` the deployed `TestUSDC` contract returns
|
|
1052
|
+
* (`packages/contracts/src/TestUSDC.sol:10`). It travels with the address and
|
|
1053
|
+
* the decimals so a holdings row can never carry one token's address with
|
|
1054
|
+
* another token's display symbol. This is the display symbol, not the
|
|
1055
|
+
* currency: see `USDX_CURRENCY` below.
|
|
1056
|
+
*/
|
|
1057
|
+
const USDX_SYMBOL = "USDX";
|
|
1058
|
+
const SYNTHETIC_TEST_ASSET_SOURCE = "synthetic-test-liquidity";
|
|
1059
|
+
const USDX_ASSET = {
|
|
1060
|
+
assetId: assetIdFor(84532, USDX_ADDRESS_BASE_SEPOLIA),
|
|
1061
|
+
chainId: toChainId(84532),
|
|
1062
|
+
symbol: USDX_SYMBOL,
|
|
1063
|
+
peg: toCurrencyCode("USD"),
|
|
1064
|
+
tokenAddress: USDX_ADDRESS_BASE_SEPOLIA,
|
|
1065
|
+
deploymentStartBlock: USDX_DEPLOYMENT_START_BLOCK,
|
|
1066
|
+
decimals: 6,
|
|
1067
|
+
source: "settlement"
|
|
1068
|
+
};
|
|
1069
|
+
/**
|
|
1070
|
+
* The settlement tier: rows the exact-transfer path and the Movement scanner
|
|
1071
|
+
* admit, each carrying the legacy `currency` projection those readers expect.
|
|
1072
|
+
* Adding an owned test asset here would put candidate liquidity on the
|
|
1073
|
+
* money-write path, so M03's rows go to the candidate tier below instead.
|
|
1074
|
+
*/
|
|
1075
|
+
const CONFIGURED_MONEY_ASSETS = [{
|
|
1076
|
+
...USDX_ASSET,
|
|
1077
|
+
currency: USDX_ASSET.peg
|
|
1078
|
+
}];
|
|
1079
|
+
/**
|
|
1080
|
+
* The candidate tier: five owned test assets, every field read back from Base
|
|
1081
|
+
* Sepolia after the deployment that created it (chain 84532, blocks
|
|
1082
|
+
* 46,465,978–46,465,982). Real contracts, authored liquidity, no settlement
|
|
1083
|
+
* admission — so rollback is removing consumers, never relabelling a
|
|
1084
|
+
* deployed address or deleting its history.
|
|
1085
|
+
*
|
|
1086
|
+
* `symbol` is the display string the owned contract returns. A
|
|
1087
|
+
* provider-supported USDC would be a different contract at a different
|
|
1088
|
+
* address returning the same symbol, which is why the resolvers below key on
|
|
1089
|
+
* `assetId`, or on chain plus address, and never on a symbol.
|
|
1090
|
+
*/
|
|
1091
|
+
const SYNTHETIC_TEST_ASSETS = [
|
|
1092
|
+
{
|
|
1093
|
+
assetId: assetIdFor(84532, "0xb3d8566fb90f7df939f4bc08a09b19047117f549"),
|
|
1094
|
+
chainId: toChainId(84532),
|
|
1095
|
+
tokenAddress: "0xb3d8566fb90f7df939f4bc08a09b19047117f549",
|
|
1096
|
+
symbol: "USDC",
|
|
1097
|
+
decimals: 6,
|
|
1098
|
+
peg: toCurrencyCode("USD"),
|
|
1099
|
+
deploymentStartBlock: 46465978,
|
|
1100
|
+
deploymentTransaction: "0xa7fb8bb9d1fd1cc5fe2fc42fa4cdccace7742efae8ea99f091a283e5709343de",
|
|
1101
|
+
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1102
|
+
},
|
|
1103
|
+
{
|
|
1104
|
+
assetId: assetIdFor(84532, "0x7ddcfb6aaffb9908c4c2db308b784c269eec8a01"),
|
|
1105
|
+
chainId: toChainId(84532),
|
|
1106
|
+
tokenAddress: "0x7ddcfb6aaffb9908c4c2db308b784c269eec8a01",
|
|
1107
|
+
symbol: "USDT",
|
|
1108
|
+
decimals: 6,
|
|
1109
|
+
peg: toCurrencyCode("USD"),
|
|
1110
|
+
deploymentStartBlock: 46465979,
|
|
1111
|
+
deploymentTransaction: "0xa17ad76fe8b24cdf1fbcff245c3eb022444710cf9fa7b5ee59818be654f4ae10",
|
|
1112
|
+
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1113
|
+
},
|
|
1114
|
+
{
|
|
1115
|
+
assetId: assetIdFor(84532, "0x1d93d525f73453fe18eebf044e8a3954bfe3721e"),
|
|
1116
|
+
chainId: toChainId(84532),
|
|
1117
|
+
tokenAddress: "0x1d93d525f73453fe18eebf044e8a3954bfe3721e",
|
|
1118
|
+
symbol: "WETH",
|
|
1119
|
+
decimals: 18,
|
|
1120
|
+
peg: null,
|
|
1121
|
+
deploymentStartBlock: 46465980,
|
|
1122
|
+
deploymentTransaction: "0xcb9b2d024686fac93ec1243ccf02d3567fd531da46176115660cba2ba644b09d",
|
|
1123
|
+
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1124
|
+
},
|
|
1125
|
+
{
|
|
1126
|
+
assetId: assetIdFor(84532, "0x8fe52c70aa9f7b6d74d8bb26eca33d87413cb624"),
|
|
1127
|
+
chainId: toChainId(84532),
|
|
1128
|
+
tokenAddress: "0x8fe52c70aa9f7b6d74d8bb26eca33d87413cb624",
|
|
1129
|
+
symbol: "WBTC",
|
|
1130
|
+
decimals: 8,
|
|
1131
|
+
peg: null,
|
|
1132
|
+
deploymentStartBlock: 46465981,
|
|
1133
|
+
deploymentTransaction: "0x728ba7b430ffd141f83d0ed5ce429d050049d7b097c936fd11e9c572d1243854",
|
|
1134
|
+
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
assetId: assetIdFor(84532, "0x5865fe9787ac214feb5facaebaec06969ac0a9a3"),
|
|
1138
|
+
chainId: toChainId(84532),
|
|
1139
|
+
tokenAddress: "0x5865fe9787ac214feb5facaebaec06969ac0a9a3",
|
|
1140
|
+
symbol: "cNGN",
|
|
1141
|
+
decimals: 6,
|
|
1142
|
+
peg: toCurrencyCode("NGN"),
|
|
1143
|
+
deploymentStartBlock: 46465982,
|
|
1144
|
+
deploymentTransaction: "0xcedf254bcef57fc64189261a5ab1161bc4fde370e470933f627c4d8ddb5a2e26",
|
|
1145
|
+
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1146
|
+
}
|
|
1147
|
+
];
|
|
1148
|
+
toChainId(84532);
|
|
1149
|
+
/**
|
|
1150
|
+
* Every known asset in ONE identity space. The tiers above declare admission;
|
|
1151
|
+
* this is the only list an identity resolves against, so an asset is never
|
|
1152
|
+
* two different things depending on which list a caller happened to read.
|
|
1153
|
+
*/
|
|
1154
|
+
const MONEY_ASSET_REGISTRY = [...CONFIGURED_MONEY_ASSETS, ...SYNTHETIC_TEST_ASSETS];
|
|
1155
|
+
CONFIGURED_MONEY_ASSETS.map((asset) => asset.tokenAddress.toLowerCase());
|
|
1156
|
+
/** The registry row for `assetId` across every tier, or `null`. */
|
|
1157
|
+
function configuredMoneyAssetById(assetId) {
|
|
1158
|
+
const canonical = toAssetId(assetId);
|
|
1159
|
+
return MONEY_ASSET_REGISTRY.find((asset) => asset.assetId === canonical) ?? null;
|
|
1160
|
+
}
|
|
797
1161
|
//#endregion
|
|
798
1162
|
//#region ../config/src/org-payments.ts
|
|
799
1163
|
/** Zodiac MultiSendCallOnly module on Base Sepolia. */
|
|
@@ -1682,6 +2046,12 @@ const USDX_BASE_SEPOLIA_TOKEN = {
|
|
|
1682
2046
|
USDX_BASE_SEPOLIA_TOKEN.chainId;
|
|
1683
2047
|
USDX_BASE_SEPOLIA_TOKEN.chainId;
|
|
1684
2048
|
10n ** BigInt(6);
|
|
2049
|
+
/**
|
|
2050
|
+
* The bootstrap manage role. It administers people and roles, cannot spend,
|
|
2051
|
+
* and is not a signer of the Organization Safe; "Owner" implied the latter,
|
|
2052
|
+
* so the label is Admin (ruled 2026-09-06).
|
|
2053
|
+
*/
|
|
2054
|
+
const ADMIN_ROLE_LABEL = "Admin";
|
|
1685
2055
|
function normalizeOrgRoleLabel(label) {
|
|
1686
2056
|
const normalized = label.trim().replace(/\s+/g, " ");
|
|
1687
2057
|
if (normalized.length === 0) throw Errors.invalidInput("role.label", "must be a non-empty role label");
|
|
@@ -1762,6 +2132,26 @@ function errorKind(error) {
|
|
|
1762
2132
|
var ClockError = class extends Data.TaggedError("ClockError") {};
|
|
1763
2133
|
var ClockPortTag = class extends Context.Service()("@capxul/sdk/ports/ClockPort") {};
|
|
1764
2134
|
//#endregion
|
|
2135
|
+
//#region src/adapters/clock/SystemClockAdapter.ts
|
|
2136
|
+
var SystemClockAdapter = class {
|
|
2137
|
+
now = Effect.try({
|
|
2138
|
+
try: () => toEpochMs(Date.now()),
|
|
2139
|
+
catch: (cause) => new ClockError({
|
|
2140
|
+
operation: "now",
|
|
2141
|
+
cause
|
|
2142
|
+
})
|
|
2143
|
+
});
|
|
2144
|
+
sleep(duration) {
|
|
2145
|
+
return Effect.callback((resume) => {
|
|
2146
|
+
const timeout = setTimeout(() => resume(Effect.void), duration);
|
|
2147
|
+
return Effect.sync(() => clearTimeout(timeout));
|
|
2148
|
+
});
|
|
2149
|
+
}
|
|
2150
|
+
};
|
|
2151
|
+
function SystemClockLayer() {
|
|
2152
|
+
return Layer.succeed(ClockPortTag, new SystemClockAdapter());
|
|
2153
|
+
}
|
|
2154
|
+
//#endregion
|
|
1765
2155
|
//#region src/ports/auth-cache.ts
|
|
1766
2156
|
var AuthCacheError = class extends Data.TaggedError("AuthCacheError") {};
|
|
1767
2157
|
var AuthCachePortTag = class extends Context.Service()("@capxul/sdk/ports/AuthCachePort") {};
|
|
@@ -1890,4 +2280,192 @@ async function readClockNow(clock) {
|
|
|
1890
2280
|
}
|
|
1891
2281
|
}
|
|
1892
2282
|
//#endregion
|
|
1893
|
-
|
|
2283
|
+
//#region src/adapters/auth-client/OAuthBearerAuthClient.ts
|
|
2284
|
+
const PROVIDER = "agent-exchange";
|
|
2285
|
+
/** Re-exchange this far ahead of expiry, matching the JWT cache eviction margin. */
|
|
2286
|
+
const REFRESH_MARGIN_MS = 3e4;
|
|
2287
|
+
/**
|
|
2288
|
+
* `AuthSession.token` is a Better Auth session handle. This flow has none, and
|
|
2289
|
+
* the access token must never be copied into it, so the slot holds a placeholder.
|
|
2290
|
+
*/
|
|
2291
|
+
const OAUTH_SESSION_TOKEN = toSessionToken("oauth-bearer");
|
|
2292
|
+
var OAuthBearerAuthClient = class {
|
|
2293
|
+
#exchangeUrl;
|
|
2294
|
+
#exchangeSecret;
|
|
2295
|
+
#accessToken;
|
|
2296
|
+
#fetchImpl;
|
|
2297
|
+
#clock;
|
|
2298
|
+
#cached = null;
|
|
2299
|
+
constructor(input) {
|
|
2300
|
+
this.#exchangeUrl = input.exchangeUrl;
|
|
2301
|
+
this.#exchangeSecret = input.exchangeSecret;
|
|
2302
|
+
this.#accessToken = input.accessToken;
|
|
2303
|
+
this.#fetchImpl = input.fetch ?? fetch;
|
|
2304
|
+
this.#clock = input.clock ?? new SystemClockAdapter();
|
|
2305
|
+
}
|
|
2306
|
+
async canSendOtp() {
|
|
2307
|
+
return refuse("canSendOtp");
|
|
2308
|
+
}
|
|
2309
|
+
async sendOtp() {
|
|
2310
|
+
return refuse("sendOtp");
|
|
2311
|
+
}
|
|
2312
|
+
async verifyOtp() {
|
|
2313
|
+
return refuse("verifyOtp");
|
|
2314
|
+
}
|
|
2315
|
+
/**
|
|
2316
|
+
* The authorization server owns the token's lifetime, so revoking it there is
|
|
2317
|
+
* the sign-out. Reporting success here would claim a token was dropped.
|
|
2318
|
+
*/
|
|
2319
|
+
async signOut() {
|
|
2320
|
+
return refuse("signOut");
|
|
2321
|
+
}
|
|
2322
|
+
/**
|
|
2323
|
+
* The exchanged identity. Unlike the Better Auth adapters this never answers
|
|
2324
|
+
* `null`: the consumer holds a usable access token or a refused one, and a
|
|
2325
|
+
* refused one is a NOT_AUTHENTICATED failure, not the absence of a session.
|
|
2326
|
+
*/
|
|
2327
|
+
async getSession(options) {
|
|
2328
|
+
const exchange = await this.#exchange("getSession", options);
|
|
2329
|
+
if (!exchange.ok) return exchange;
|
|
2330
|
+
return {
|
|
2331
|
+
ok: true,
|
|
2332
|
+
value: {
|
|
2333
|
+
authUserId: toAuthUserId(exchange.value.authUserId),
|
|
2334
|
+
email: toEmail(exchange.value.email),
|
|
2335
|
+
token: OAUTH_SESSION_TOKEN,
|
|
2336
|
+
expiresAt: toEpochMs(exchange.value.expiresAt)
|
|
2337
|
+
}
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2340
|
+
async getConvexJwt(options) {
|
|
2341
|
+
const exchange = await this.#exchange("getConvexJwt", options);
|
|
2342
|
+
if (!exchange.ok) return exchange;
|
|
2343
|
+
return {
|
|
2344
|
+
ok: true,
|
|
2345
|
+
value: {
|
|
2346
|
+
token: toJwtToken(exchange.value.token),
|
|
2347
|
+
expEpochSeconds: toEpochSeconds(Math.floor(exchange.value.expiresAt / 1e3))
|
|
2348
|
+
}
|
|
2349
|
+
};
|
|
2350
|
+
}
|
|
2351
|
+
/**
|
|
2352
|
+
* The cached exchange while it is fresh, otherwise a new one. A failed
|
|
2353
|
+
* exchange never replaces the cache, so a stale token is never served.
|
|
2354
|
+
*
|
|
2355
|
+
* lazy: no in-flight de-duplication — two concurrent reads past the margin
|
|
2356
|
+
* each mint a JWT. Minting is idempotent, so the cost is one extra request.
|
|
2357
|
+
*/
|
|
2358
|
+
async #exchange(operation, options) {
|
|
2359
|
+
if (options?.signal?.aborted) return {
|
|
2360
|
+
ok: false,
|
|
2361
|
+
error: Errors.cancelled({ operation })
|
|
2362
|
+
};
|
|
2363
|
+
const cached = this.#cached;
|
|
2364
|
+
if (cached !== null && options?.forceRefresh !== true) {
|
|
2365
|
+
const now = await readClockNow(this.#clock);
|
|
2366
|
+
if (!now.ok) return now;
|
|
2367
|
+
if (now.value < cached.expiresAt - REFRESH_MARGIN_MS) return {
|
|
2368
|
+
ok: true,
|
|
2369
|
+
value: cached
|
|
2370
|
+
};
|
|
2371
|
+
}
|
|
2372
|
+
return this.#postExchange(operation, options?.signal);
|
|
2373
|
+
}
|
|
2374
|
+
async #postExchange(operation, signal) {
|
|
2375
|
+
let res;
|
|
2376
|
+
try {
|
|
2377
|
+
res = await this.#fetchImpl(this.#exchangeUrl, {
|
|
2378
|
+
method: "POST",
|
|
2379
|
+
headers: {
|
|
2380
|
+
"content-type": "application/json",
|
|
2381
|
+
authorization: `Bearer ${this.#exchangeSecret}`
|
|
2382
|
+
},
|
|
2383
|
+
body: JSON.stringify({ accessToken: this.#accessToken }),
|
|
2384
|
+
...signal === void 0 ? {} : { signal }
|
|
2385
|
+
});
|
|
2386
|
+
} catch (cause) {
|
|
2387
|
+
return {
|
|
2388
|
+
ok: false,
|
|
2389
|
+
error: signal?.aborted === true || isAbortError(cause) ? Errors.cancelled({ operation }) : Errors.networkError(operation, cause)
|
|
2390
|
+
};
|
|
2391
|
+
}
|
|
2392
|
+
if (signal?.aborted === true) return {
|
|
2393
|
+
ok: false,
|
|
2394
|
+
error: Errors.cancelled({ operation })
|
|
2395
|
+
};
|
|
2396
|
+
const body = await res.json().catch(() => null);
|
|
2397
|
+
if (!res.ok) return {
|
|
2398
|
+
ok: false,
|
|
2399
|
+
error: exchangeRefused(operation, res.status, readErrorCode(body))
|
|
2400
|
+
};
|
|
2401
|
+
const exchange = readExchange(body);
|
|
2402
|
+
if (exchange === null) return {
|
|
2403
|
+
ok: false,
|
|
2404
|
+
error: Errors.providerError(PROVIDER, operation, /* @__PURE__ */ new Error("unexpected body"), { httpStatus: res.status })
|
|
2405
|
+
};
|
|
2406
|
+
this.#cached = exchange;
|
|
2407
|
+
return {
|
|
2408
|
+
ok: true,
|
|
2409
|
+
value: exchange
|
|
2410
|
+
};
|
|
2411
|
+
}
|
|
2412
|
+
};
|
|
2413
|
+
/**
|
|
2414
|
+
* `AuthClientPort` over the backend agent-token exchange. Build one per OAuth
|
|
2415
|
+
* access token and hand it to `createCapxulClient({ authClient })`.
|
|
2416
|
+
*/
|
|
2417
|
+
function oauthBearerAuthClient(input) {
|
|
2418
|
+
return authClientPortFromPromiseAdapter(new OAuthBearerAuthClient(input));
|
|
2419
|
+
}
|
|
2420
|
+
/** The OTP verbs belong to the email flow; `validStates` names it for the caller. */
|
|
2421
|
+
function refuse(method) {
|
|
2422
|
+
return {
|
|
2423
|
+
ok: false,
|
|
2424
|
+
error: Errors.wrongState({
|
|
2425
|
+
method,
|
|
2426
|
+
currentState: "oauth-bearer",
|
|
2427
|
+
validStates: ["email-otp"]
|
|
2428
|
+
})
|
|
2429
|
+
};
|
|
2430
|
+
}
|
|
2431
|
+
/**
|
|
2432
|
+
* The error catalog has no FORBIDDEN code, so 403 (`insufficient_scope`) and
|
|
2433
|
+
* 503 (`exchange_unconfigured`) both land on PROVIDER_ERROR. `httpStatus` and
|
|
2434
|
+
* `details.reason` carry which one the backend reported. Only 401 becomes
|
|
2435
|
+
* NOT_AUTHENTICATED, because only 401 is answered by a new access token.
|
|
2436
|
+
*/
|
|
2437
|
+
function exchangeRefused(operation, status, reason) {
|
|
2438
|
+
if (status === 401) return Errors.notAuthenticated("The exchange refused this OAuth access token");
|
|
2439
|
+
return Errors.providerError(PROVIDER, operation, /* @__PURE__ */ new Error(`HTTP ${status}`), {
|
|
2440
|
+
httpStatus: status,
|
|
2441
|
+
...reason === void 0 ? {} : { details: { reason } }
|
|
2442
|
+
});
|
|
2443
|
+
}
|
|
2444
|
+
/** An abort raised by something other than the caller's own signal. */
|
|
2445
|
+
function isAbortError(cause) {
|
|
2446
|
+
return readField(cause, "name") === "AbortError";
|
|
2447
|
+
}
|
|
2448
|
+
function readErrorCode(body) {
|
|
2449
|
+
const error = readField(body, "error");
|
|
2450
|
+
return typeof error === "string" ? error : void 0;
|
|
2451
|
+
}
|
|
2452
|
+
function readField(value, key) {
|
|
2453
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
2454
|
+
return value[key];
|
|
2455
|
+
}
|
|
2456
|
+
function readExchange(body) {
|
|
2457
|
+
if (typeof body !== "object" || body === null) return null;
|
|
2458
|
+
const { token, expiresAt, authUserId, email } = body;
|
|
2459
|
+
if (typeof token !== "string" || token.length === 0) return null;
|
|
2460
|
+
if (typeof expiresAt !== "number" || !Number.isFinite(expiresAt)) return null;
|
|
2461
|
+
if (typeof authUserId !== "string" || authUserId.length === 0) return null;
|
|
2462
|
+
if (typeof email !== "string" || email.length === 0) return null;
|
|
2463
|
+
return {
|
|
2464
|
+
token,
|
|
2465
|
+
expiresAt,
|
|
2466
|
+
authUserId,
|
|
2467
|
+
email
|
|
2468
|
+
};
|
|
2469
|
+
}
|
|
2470
|
+
//#endregion
|
|
2471
|
+
export { toRoleKey as $, currencySymbolFor as A, toCurrencyCode as B, ASSET_ID_RE as C, redactSecrets as Ct, WEI_RE as D, SUPPORTED_CURRENCY_CODES as E, toAssetId as F, toHandle as G, toEmail as H, toAuthUserId as I, toOrgId as J, toJwtToken as K, toBudgetId as L, toAddress as M, toAllowedOrigin as N, ZERO_BYTES32 as O, toAppId as P, toPublishableKey as Q, toChainId as R, APP_ID_RE as S, isCredentialField as St, EVM_ADDRESS_RE as T, toEpochMs as U, toDurationMs as V, toEpochSeconds as W, toPayrollGroupId as X, toPartyId as Y, toPayrollRunId as Z, normalizeBindingEmail as _, failureFingerprint as _t, parseAuthSession as a, decodeConvexError as at, configuredMoneyAssetById as b, revertSummaryText as bt, AuthCachePortTag as c, EXPECTED_OPERATION_OUTCOMES as ct, authClientPortFromPromiseAdapter as d, CHAIN_UPSTREAMS as dt, toSessionToken as et, AuthClientError as f, FAILURE_MODES as ft, CAPXUL_PAYMENTS_V2_ADDRESS as g, decodeChainCause as gt, orgRoleKeyForLabel as h, chainEvidenceLabel as ht, BrowserAuthCacheAdapter as i, HANDLE_RE as it, toAccountId as j, assetIdFor as k, SystemClockLayer as l, Errors as lt, ADMIN_ROLE_LABEL as m, chainCauseProperties as mt, readClockNow as n, toTxHash as nt, parseCachedJwt as o, CAPXUL_ERROR_CODES as ot, AuthClientPortTag as p, boundedResponseHeaders as pt, toKycTier as q, InMemoryAuthCacheAdapter as r, validateHandle as rt, AuthCacheError as s, CapxulError as st, oauthBearerAuthClient as t, toTesterKind as tt, ClockPortTag as u, isCapxulError as ut, BASE_SEPOLIA_CHAIN_ID as v, isChainUpstream as vt, BYTES32_RE as w, redactUrlSecrets as wt, ACCOUNT_ID_RE as x, containsSensitiveMaterial as xt, deriveCapxulSafeAddress as y, isFailureMode as yt, toCountryCode as z };
|