hanzio 1.2.0 → 2.1.0
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/README.md +786 -239
- package/dist/api-wrapper/builders.d.ts +4 -3
- package/dist/api-wrapper/create-client.d.ts +1 -1
- package/dist/api-wrapper/describe.d.ts +16 -0
- package/dist/api-wrapper/endpoint.d.ts +18 -0
- package/dist/api-wrapper/errors.d.ts +39 -2
- package/dist/api-wrapper/index.d.ts +5 -0
- package/dist/api-wrapper/otel.d.ts +44 -0
- package/dist/api-wrapper/paginate.d.ts +12 -0
- package/dist/api-wrapper/redaction.d.ts +2 -0
- package/dist/api-wrapper/shared.d.ts +16 -2
- package/dist/api-wrapper/testing.d.ts +23 -0
- package/dist/api-wrapper/transport.d.ts +4 -5
- package/dist/api-wrapper/types.d.ts +103 -18
- package/dist/array/index.d.ts +50 -4
- package/dist/cache/createCache.d.ts +31 -0
- package/dist/cache/index.d.ts +19 -2
- package/dist/color/contrast.d.ts +40 -0
- package/dist/color/index.d.ts +2 -0
- package/dist/{math → color}/metricsSeriesColors.d.ts +14 -7
- package/dist/cool-console-log/index.d.ts +34 -5
- package/dist/crypto/encoding.d.ts +18 -0
- package/dist/crypto/hash.d.ts +33 -0
- package/dist/crypto/index.d.ts +4 -0
- package/dist/crypto/sealer.d.ts +25 -0
- package/dist/crypto/webhook.d.ts +20 -0
- package/dist/date/index.d.ts +46 -2
- package/dist/emitter/index.d.ts +21 -0
- package/dist/error/index.d.ts +12 -0
- package/dist/format/index.d.ts +102 -0
- package/dist/function/index.d.ts +60 -0
- package/dist/guard/index.d.ts +20 -0
- package/dist/i18n/index.d.ts +37 -0
- package/dist/index-0zvtcbzy.js +224 -0
- package/dist/index-8r2vdzjg.js +87 -0
- package/dist/index-hy7kbb99.js +157 -0
- package/dist/index-hyt6c56h.js +290 -0
- package/dist/{src/secrets/infisical.js → index-j1ybktbr.js} +500 -538
- package/dist/index-k9yx6bne.js +89 -0
- package/dist/index-kj1phgrx.js +169 -0
- package/dist/index-ktynw3vs.js +37 -0
- package/dist/index-mnkcfyx8.js +78 -0
- package/dist/{p-queue/index.js → index-pwt0dz63.js} +7 -11
- package/dist/index-rp5pqghz.js +157 -0
- package/dist/index-w882zry3.js +29 -0
- package/dist/index.d.ts +7 -7
- package/dist/jwt/index.d.ts +50 -2
- package/dist/math/index.d.ts +2 -2
- package/dist/math/linearRegressionTrend.d.ts +14 -1
- package/dist/math/scale.d.ts +8 -0
- package/dist/object/index.d.ts +19 -0
- package/dist/p-queue/index.d.ts +1 -1
- package/dist/promise/index.d.ts +97 -2
- package/dist/secrets/core.d.ts +7 -0
- package/dist/secrets/index.d.ts +3 -1
- package/dist/secrets/public-config.d.ts +7 -0
- package/dist/secrets/vite.d.ts +1 -0
- package/dist/secrets/worker.d.ts +1 -0
- package/dist/sitemap/index.d.ts +48 -6
- package/dist/src/api-wrapper/index.js +60 -1130
- package/dist/src/api-wrapper/testing.js +20 -0
- package/dist/{cool-console-log → src/cool-console-log}/index.js +80 -33
- package/dist/src/crypto/index.js +195 -0
- package/dist/src/format/index.js +211 -0
- package/dist/src/i18n/index.js +36 -0
- package/dist/src/index.js +739 -3145
- package/dist/src/jwt/index.js +138 -92
- package/dist/src/p-queue/index.js +9 -437
- package/dist/src/secrets/index.js +50 -1428
- package/dist/src/secrets/vite.js +10 -39
- package/dist/src/secrets/worker.js +14 -273
- package/dist/src/sitemap/index.js +183 -511
- package/dist/src/storage/index.js +119 -0
- package/dist/src/zod/index.js +340 -167
- package/dist/state/index.d.ts +21 -0
- package/dist/storage/index.d.ts +58 -0
- package/dist/string/index.d.ts +75 -2
- package/dist/typedswitch/index.d.ts +31 -24
- package/dist/types/index.d.ts +11 -0
- package/dist/url/index.d.ts +50 -1
- package/dist/zod/index.d.ts +32 -3
- package/dist/zod/introspect.d.ts +19 -0
- package/dist/zod/jsonToZodSchemaMapper.d.ts +3 -1
- package/dist/zod/zodMinMaxFilter.d.ts +22 -3
- package/dist/zod/zodToTypeString.d.ts +8 -3
- package/package.json +78 -7
- package/src/api-wrapper/actions-impl.ts +11 -72
- package/src/api-wrapper/builders.ts +32 -25
- package/src/api-wrapper/create-client.ts +171 -41
- package/src/api-wrapper/describe.ts +157 -0
- package/src/api-wrapper/endpoint.ts +26 -0
- package/src/api-wrapper/error-reporting.ts +7 -1
- package/src/api-wrapper/errors.ts +107 -2
- package/src/api-wrapper/headers.ts +11 -6
- package/src/api-wrapper/index.ts +5 -0
- package/src/api-wrapper/otel.ts +139 -0
- package/src/api-wrapper/paginate.ts +28 -0
- package/src/api-wrapper/redaction.ts +42 -0
- package/src/api-wrapper/shared.ts +21 -2
- package/src/api-wrapper/testing.ts +48 -0
- package/src/api-wrapper/transport.ts +41 -43
- package/src/api-wrapper/types.ts +185 -18
- package/src/array/index.ts +195 -42
- package/src/cache/createCache.ts +157 -0
- package/src/cache/index.ts +132 -147
- package/src/color/contrast.ts +163 -0
- package/src/color/index.ts +22 -0
- package/src/{math → color}/metricsSeriesColors.ts +47 -49
- package/src/cool-console-log/index.ts +128 -41
- package/src/crypto/encoding.ts +83 -0
- package/src/crypto/hash.ts +170 -0
- package/src/crypto/index.ts +26 -0
- package/src/crypto/sealer.ts +168 -0
- package/src/crypto/webhook.ts +86 -0
- package/src/date/index.ts +109 -34
- package/src/emitter/index.ts +82 -0
- package/src/error/index.ts +63 -0
- package/src/format/index.ts +389 -0
- package/src/function/index.ts +201 -0
- package/src/guard/index.ts +80 -0
- package/src/i18n/index.ts +125 -0
- package/src/index.ts +11 -7
- package/src/jwt/index.ts +199 -125
- package/src/math/index.ts +5 -10
- package/src/math/linearRegressionTrend.ts +33 -8
- package/src/math/scale.ts +17 -0
- package/src/object/index.ts +99 -0
- package/src/p-queue/index.ts +2 -0
- package/src/promise/index.ts +304 -9
- package/src/secrets/core.ts +72 -16
- package/src/secrets/index.ts +16 -4
- package/src/secrets/public-config.ts +47 -0
- package/src/secrets/vite.ts +2 -0
- package/src/secrets/worker.ts +2 -0
- package/src/sitemap/index.ts +293 -130
- package/src/state/index.ts +133 -81
- package/src/storage/index.ts +187 -0
- package/src/string/index.ts +261 -23
- package/src/typedswitch/index.ts +58 -38
- package/src/types/index.ts +14 -0
- package/src/url/index.ts +146 -1
- package/src/zod/index.ts +105 -9
- package/src/zod/introspect.ts +103 -0
- package/src/zod/jsonToZodSchemaMapper.ts +44 -21
- package/src/zod/zodMinMaxFilter.ts +37 -13
- package/src/zod/zodToTypeString.ts +184 -181
- package/dist/api-wrapper/index.js +0 -250
- package/dist/gzip/index.d.ts +0 -2
- package/dist/gzip/index.js +0 -16
- package/dist/index.js +0 -1823
- package/dist/jwt/index.js +0 -19
- package/dist/sitemap/index.js +0 -552
- package/dist/src/gzip/index.js +0 -16
- package/dist/src/secrets/loaders.js +0 -1529
- package/dist/zod/index.js +0 -210
package/dist/src/index.js
CHANGED
|
@@ -1,3 +1,89 @@
|
|
|
1
|
+
import {
|
|
2
|
+
chunkArray2,
|
|
3
|
+
findArrayDifferenceByKey2,
|
|
4
|
+
getUniqueValues2,
|
|
5
|
+
compact2,
|
|
6
|
+
getUniqueValuesByKey2,
|
|
7
|
+
groupBy2,
|
|
8
|
+
countBy2,
|
|
9
|
+
keyBy2,
|
|
10
|
+
partition2,
|
|
11
|
+
pickItemsInArray2,
|
|
12
|
+
toArray2,
|
|
13
|
+
sumBy2,
|
|
14
|
+
sortBy2,
|
|
15
|
+
range2
|
|
16
|
+
} from "../index-hy7kbb99.js";
|
|
17
|
+
import {
|
|
18
|
+
createCache2
|
|
19
|
+
} from "../index-k9yx6bne.js";
|
|
20
|
+
import {
|
|
21
|
+
ok2,
|
|
22
|
+
err2,
|
|
23
|
+
isSuccess2,
|
|
24
|
+
isFailure2,
|
|
25
|
+
unwrapResult2,
|
|
26
|
+
backgroundPromise2,
|
|
27
|
+
backgroundPromiseSync2,
|
|
28
|
+
promiseTimeout2,
|
|
29
|
+
tryCatch2,
|
|
30
|
+
PromiseTimeoutError2,
|
|
31
|
+
withTimeout2,
|
|
32
|
+
exponentialBackoff2,
|
|
33
|
+
retry2,
|
|
34
|
+
pMap2,
|
|
35
|
+
startTimer2,
|
|
36
|
+
timed2,
|
|
37
|
+
anySignal2,
|
|
38
|
+
timeoutSignal2,
|
|
39
|
+
raceAbort2,
|
|
40
|
+
createLatestGuard2
|
|
41
|
+
} from "../index-0zvtcbzy.js";
|
|
42
|
+
import {
|
|
43
|
+
InvariantError2,
|
|
44
|
+
getErrorMessage2,
|
|
45
|
+
toError2,
|
|
46
|
+
must2,
|
|
47
|
+
invariant2,
|
|
48
|
+
isRecord2,
|
|
49
|
+
isPlainObject2,
|
|
50
|
+
isDefined2,
|
|
51
|
+
isTruthy2,
|
|
52
|
+
isString2,
|
|
53
|
+
isNumber2,
|
|
54
|
+
isBoolean2,
|
|
55
|
+
isFunction2,
|
|
56
|
+
isOneOf2,
|
|
57
|
+
hasKey2,
|
|
58
|
+
isNonEmpty2,
|
|
59
|
+
nonEmpty2
|
|
60
|
+
} from "../index-8r2vdzjg.js";
|
|
61
|
+
import {
|
|
62
|
+
extractNumber2,
|
|
63
|
+
generateId2,
|
|
64
|
+
sha256Id2,
|
|
65
|
+
slugify2,
|
|
66
|
+
stableStringify2,
|
|
67
|
+
truncate2,
|
|
68
|
+
normalizeWhitespace2,
|
|
69
|
+
escapeRegExp2,
|
|
70
|
+
foldForSearch2,
|
|
71
|
+
matchesSearch2,
|
|
72
|
+
initials2,
|
|
73
|
+
safeJsonParse2
|
|
74
|
+
} from "../index-kj1phgrx.js";
|
|
75
|
+
import {
|
|
76
|
+
buildQueryString2,
|
|
77
|
+
replacePathParams2,
|
|
78
|
+
buildUrl2,
|
|
79
|
+
normalizeDomain2,
|
|
80
|
+
getDomainFaviconUrl2,
|
|
81
|
+
trimTrailingSlash2,
|
|
82
|
+
ensureLeadingSlash2,
|
|
83
|
+
buildMailto2,
|
|
84
|
+
parseRetryAfter2
|
|
85
|
+
} from "../index-mnkcfyx8.js";
|
|
86
|
+
|
|
1
87
|
// src/typedswitch/index.ts
|
|
2
88
|
function typedSwitch(value, keyOrCases, casesOrDefault, defaultCase) {
|
|
3
89
|
let key;
|
|
@@ -14,10 +100,10 @@ function typedSwitch(value, keyOrCases, casesOrDefault, defaultCase) {
|
|
|
14
100
|
}
|
|
15
101
|
discriminantKey = keyOrCases;
|
|
16
102
|
const discriminantValue = value[discriminantKey];
|
|
17
|
-
if (typeof discriminantValue !== "string") {
|
|
18
|
-
throw new Error(`Invalid discriminant value for key "${discriminantKey}": expected string, received ${typeof discriminantValue}`);
|
|
103
|
+
if (typeof discriminantValue !== "string" && typeof discriminantValue !== "number" && typeof discriminantValue !== "boolean") {
|
|
104
|
+
throw new Error(`Invalid discriminant value for key "${discriminantKey}": expected string, number or boolean, received ${typeof discriminantValue}`);
|
|
19
105
|
}
|
|
20
|
-
key = discriminantValue;
|
|
106
|
+
key = String(discriminantValue);
|
|
21
107
|
cases = casesOrDefault;
|
|
22
108
|
defaultHandler = defaultCase;
|
|
23
109
|
}
|
|
@@ -33,184 +119,8 @@ function typedSwitch(value, keyOrCases, casesOrDefault, defaultCase) {
|
|
|
33
119
|
const contextSuffix = discriminantKey ? ` (discriminant key: "${discriminantKey}")` : "";
|
|
34
120
|
throw new Error(`Unhandled case: ${key}${contextSuffix}.${availableCasesSuffix}`);
|
|
35
121
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (!Number.isInteger(chunkSize) || chunkSize <= 0) {
|
|
39
|
-
throw new Error("Chunk size must be a positive integer");
|
|
40
|
-
}
|
|
41
|
-
const chunks = [];
|
|
42
|
-
for (let index = 0;index < array.length; index += chunkSize) {
|
|
43
|
-
chunks.push(array.slice(index, index + chunkSize));
|
|
44
|
-
}
|
|
45
|
-
return chunks;
|
|
46
|
-
}
|
|
47
|
-
function findArrayDifferenceByKey(current, next, key) {
|
|
48
|
-
const currentByKey = new Map(current.map((item) => [String(item[key]), item]));
|
|
49
|
-
const itemsToDelete = [...current];
|
|
50
|
-
const newItems = [];
|
|
51
|
-
const upsertItems = [];
|
|
52
|
-
for (const item of next) {
|
|
53
|
-
const keyValue = String(item[key]);
|
|
54
|
-
if (!currentByKey.has(keyValue)) {
|
|
55
|
-
newItems.push(item);
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
upsertItems.push(item);
|
|
59
|
-
const deleteIndex = itemsToDelete.findIndex((currentItem) => String(currentItem[key]) === keyValue);
|
|
60
|
-
if (deleteIndex >= 0) {
|
|
61
|
-
itemsToDelete.splice(deleteIndex, 1);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return {
|
|
65
|
-
new: newItems,
|
|
66
|
-
upsert: upsertItems,
|
|
67
|
-
delete: itemsToDelete
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
function getUniqueValues(array) {
|
|
71
|
-
const seen = new Map;
|
|
72
|
-
for (const element of array) {
|
|
73
|
-
const key = JSON.stringify(element);
|
|
74
|
-
if (!seen.has(key)) {
|
|
75
|
-
seen.set(key, element);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return Array.from(seen.values());
|
|
79
|
-
}
|
|
80
|
-
function compact(array) {
|
|
81
|
-
return array.filter((item) => item != null);
|
|
82
|
-
}
|
|
83
|
-
function getUniqueValuesByKey(array, key) {
|
|
84
|
-
const seen = new Set;
|
|
85
|
-
const result = [];
|
|
86
|
-
for (const item of array) {
|
|
87
|
-
const value = item[key];
|
|
88
|
-
if (seen.has(value))
|
|
89
|
-
continue;
|
|
90
|
-
seen.add(value);
|
|
91
|
-
result.push(item);
|
|
92
|
-
}
|
|
93
|
-
return result;
|
|
94
|
-
}
|
|
95
|
-
function groupBy(array, keyOrFn) {
|
|
96
|
-
return array.reduce((result, item) => {
|
|
97
|
-
const groupKey = typeof keyOrFn === "function" ? keyOrFn(item) : item[keyOrFn];
|
|
98
|
-
result[groupKey] ??= [];
|
|
99
|
-
result[groupKey].push(item);
|
|
100
|
-
return result;
|
|
101
|
-
}, {});
|
|
102
|
-
}
|
|
103
|
-
function keyBy(array, keyOrFn) {
|
|
104
|
-
const result = {};
|
|
105
|
-
for (const item of array) {
|
|
106
|
-
const recordKey = typeof keyOrFn === "function" ? keyOrFn(item) : item[keyOrFn];
|
|
107
|
-
result[recordKey] = item;
|
|
108
|
-
}
|
|
109
|
-
return result;
|
|
110
|
-
}
|
|
111
|
-
function partition(array, predicate) {
|
|
112
|
-
const matched = [];
|
|
113
|
-
const unmatched = [];
|
|
114
|
-
array.forEach((item, index) => {
|
|
115
|
-
if (predicate(item, index)) {
|
|
116
|
-
matched.push(item);
|
|
117
|
-
} else {
|
|
118
|
-
unmatched.push(item);
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
return [matched, unmatched];
|
|
122
|
-
}
|
|
123
|
-
function pickItemsInArray(array, key, values) {
|
|
124
|
-
return array.filter((item) => values.includes(item[key]));
|
|
125
|
-
}
|
|
126
|
-
// src/string/index.ts
|
|
127
|
-
function extractNumber(value) {
|
|
128
|
-
if (!value?.trim())
|
|
129
|
-
return;
|
|
130
|
-
const cleanedValue = value.replace(/[^\d.,-]/g, "");
|
|
131
|
-
if (!cleanedValue)
|
|
132
|
-
return;
|
|
133
|
-
const parsedNumber = Number.parseFloat(cleanedValue);
|
|
134
|
-
return Number.isNaN(parsedNumber) ? undefined : parsedNumber;
|
|
135
|
-
}
|
|
136
|
-
function generateId(input) {
|
|
137
|
-
const data = normalizeIdInput(input);
|
|
138
|
-
let hash = 0;
|
|
139
|
-
for (let index = 0;index < data.length; index++) {
|
|
140
|
-
const char = data.charCodeAt(index);
|
|
141
|
-
hash = (hash << 5) - hash + char;
|
|
142
|
-
hash |= 0;
|
|
143
|
-
}
|
|
144
|
-
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
145
|
-
}
|
|
146
|
-
function slugify(value) {
|
|
147
|
-
const source = "àáâäæãåāăąçćčđďèéêëēėęěğǵḧîïíīįìłḿñńǹňôöòóœøōõṕŕřßśšşșťțûüùúūǘůűųẃẍÿýžźż·/_,:;";
|
|
148
|
-
const replacement = "aaaaaaaaaacccddeeeeeeeegghiiiiiilmnnnnooooooooprrsssssttuuuuuuuuuwxyyzzz------";
|
|
149
|
-
const specialCharacterPattern = new RegExp(source.split("").join("|"), "g");
|
|
150
|
-
return value.toString().toLowerCase().replace(/\s+/g, "-").replace(specialCharacterPattern, (character) => replacement.charAt(source.indexOf(character))).replace(/&/g, "-and-").replace(/[^\w-]+/g, "").replace(/--+/g, "-").replace(/^-+/, "").replace(/-+$/, "");
|
|
151
|
-
}
|
|
152
|
-
function normalizeIdInput(input) {
|
|
153
|
-
if (input === undefined)
|
|
154
|
-
return "undefined";
|
|
155
|
-
if (input === null)
|
|
156
|
-
return "null";
|
|
157
|
-
if (typeof input === "string") {
|
|
158
|
-
return decodeURIComponent(input).split("&").sort().join("&");
|
|
159
|
-
}
|
|
160
|
-
if (typeof input === "number" || typeof input === "boolean") {
|
|
161
|
-
return input.toString();
|
|
162
|
-
}
|
|
163
|
-
return JSON.stringify(sortJsonValue(input));
|
|
164
|
-
}
|
|
165
|
-
function sortJsonValue(value) {
|
|
166
|
-
if (value === undefined)
|
|
167
|
-
return null;
|
|
168
|
-
if (Array.isArray(value)) {
|
|
169
|
-
return value.map((item) => sortJsonValue(item));
|
|
170
|
-
}
|
|
171
|
-
if (typeof value === "object" && value !== null) {
|
|
172
|
-
return Object.fromEntries(Object.entries(value).filter(([, itemValue]) => itemValue !== undefined).sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey)).map(([key, itemValue]) => [key, sortJsonValue(itemValue)]));
|
|
173
|
-
}
|
|
174
|
-
return value;
|
|
175
|
-
}
|
|
176
|
-
// src/promise/index.ts
|
|
177
|
-
function isSuccess(result) {
|
|
178
|
-
return result.error === null;
|
|
179
|
-
}
|
|
180
|
-
function isFailure(result) {
|
|
181
|
-
return result.error !== null;
|
|
182
|
-
}
|
|
183
|
-
function unwrapResult(result) {
|
|
184
|
-
if (isSuccess(result))
|
|
185
|
-
return result.data;
|
|
186
|
-
throw result.error;
|
|
187
|
-
}
|
|
188
|
-
function backgroundPromise(promiseFactory, options = {}) {
|
|
189
|
-
const run = () => {
|
|
190
|
-
promiseFactory().then(() => options.onSuccess?.()).catch((error) => options.onError?.(error));
|
|
191
|
-
};
|
|
192
|
-
if (options.scheduler) {
|
|
193
|
-
options.scheduler(run);
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
run();
|
|
197
|
-
}
|
|
198
|
-
function backgroundPromiseSync(promiseFactory, options = {}) {
|
|
199
|
-
backgroundPromise(promiseFactory, {
|
|
200
|
-
...options,
|
|
201
|
-
scheduler: options.scheduler ?? queueMicrotask
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
function promiseTimeout(ms) {
|
|
205
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
206
|
-
}
|
|
207
|
-
async function tryCatch(promise) {
|
|
208
|
-
try {
|
|
209
|
-
const data = await promise;
|
|
210
|
-
return { data, error: null };
|
|
211
|
-
} catch (error) {
|
|
212
|
-
return { data: null, error };
|
|
213
|
-
}
|
|
122
|
+
function assertNever(value, message) {
|
|
123
|
+
throw new Error(message ?? `Unexpected value: ${String(value)}`);
|
|
214
124
|
}
|
|
215
125
|
// src/cache/index.ts
|
|
216
126
|
function cacheFunction(options) {
|
|
@@ -223,7 +133,8 @@ function cacheFunction(options) {
|
|
|
223
133
|
cacheKeyFn,
|
|
224
134
|
onBackgroundRefreshError,
|
|
225
135
|
store,
|
|
226
|
-
onStoreError
|
|
136
|
+
onStoreError,
|
|
137
|
+
maxEntries
|
|
227
138
|
} = options;
|
|
228
139
|
const externalStore = store;
|
|
229
140
|
const cache = new Map;
|
|
@@ -255,143 +166,116 @@ function cacheFunction(options) {
|
|
|
255
166
|
};
|
|
256
167
|
const buildKey = (args) => {
|
|
257
168
|
const keyArgs = cacheKeyFn ? cacheKeyFn(...args) : cacheKeyArgs ? cacheKeyArgs.map((i) => args[i]) : args;
|
|
258
|
-
return `${name}:${
|
|
169
|
+
return `${name}:${stableStringify2(keyArgs)}`;
|
|
259
170
|
};
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
171
|
+
const isFresh = (entry) => Date.now() - entry.timestamp < cacheTimeMs;
|
|
172
|
+
const servable = (entry) => entry && (refreshInBackground || isFresh(entry)) ? entry : undefined;
|
|
173
|
+
const touch = (key, entry) => {
|
|
174
|
+
cache.delete(key);
|
|
175
|
+
cache.set(key, entry);
|
|
176
|
+
if (maxEntries !== undefined && cache.size > maxEntries) {
|
|
177
|
+
cache.delete(cache.keys().next().value);
|
|
178
|
+
}
|
|
263
179
|
};
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
180
|
+
const guardStore = async (operation) => {
|
|
181
|
+
try {
|
|
182
|
+
return await operation();
|
|
183
|
+
} catch (error) {
|
|
184
|
+
onStoreError?.(error, { name });
|
|
185
|
+
return;
|
|
269
186
|
}
|
|
270
|
-
const promise = result.then((value) => {
|
|
271
|
-
saveEntry(key, value);
|
|
272
|
-
pendingPromises.delete(key);
|
|
273
|
-
return value;
|
|
274
|
-
}).catch((error) => {
|
|
275
|
-
pendingPromises.delete(key);
|
|
276
|
-
throw error;
|
|
277
|
-
});
|
|
278
|
-
pendingPromises.set(key, promise);
|
|
279
|
-
return promise;
|
|
280
187
|
};
|
|
281
|
-
const
|
|
188
|
+
const save = (key, value) => {
|
|
189
|
+
const entry = { value, timestamp: Date.now() };
|
|
190
|
+
if (externalStore) {
|
|
191
|
+
return guardStore(() => externalStore.set(key, entry, entryLifetimeMs));
|
|
192
|
+
}
|
|
193
|
+
touch(key, entry);
|
|
194
|
+
ensureCleanup();
|
|
195
|
+
};
|
|
196
|
+
const run = (key, args, stale) => {
|
|
282
197
|
const result = fn(...args);
|
|
283
198
|
if (!isPromiseLike(result)) {
|
|
284
|
-
|
|
285
|
-
return;
|
|
199
|
+
save(key, result);
|
|
200
|
+
return result;
|
|
286
201
|
}
|
|
287
|
-
const promise = result.then((value) => {
|
|
288
|
-
|
|
289
|
-
|
|
202
|
+
const promise = Promise.resolve(result).then(async (value) => {
|
|
203
|
+
if (pendingPromises.get(key) === promise) {
|
|
204
|
+
await save(key, value);
|
|
205
|
+
}
|
|
290
206
|
return value;
|
|
291
|
-
}
|
|
292
|
-
|
|
207
|
+
}, (error) => {
|
|
208
|
+
if (!stale)
|
|
209
|
+
throw error;
|
|
293
210
|
onBackgroundRefreshError?.(error, { name });
|
|
294
|
-
return
|
|
211
|
+
return stale.value;
|
|
212
|
+
}).finally(() => {
|
|
213
|
+
if (pendingPromises.get(key) === promise) {
|
|
214
|
+
pendingPromises.delete(key);
|
|
215
|
+
}
|
|
295
216
|
});
|
|
296
217
|
pendingPromises.set(key, promise);
|
|
218
|
+
return promise;
|
|
297
219
|
};
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
} catch (error) {
|
|
302
|
-
onStoreError?.(error, { name });
|
|
220
|
+
const serve = (key, args, entry) => {
|
|
221
|
+
if (!isFresh(entry) && !pendingPromises.has(key)) {
|
|
222
|
+
run(key, args, entry);
|
|
303
223
|
}
|
|
224
|
+
return entry.value;
|
|
304
225
|
};
|
|
305
|
-
const callWithStore = (cacheStore, args) => {
|
|
306
|
-
const key = buildKey(args);
|
|
226
|
+
const callWithStore = (cacheStore, key, args) => {
|
|
307
227
|
const pending = pendingPromises.get(key);
|
|
308
228
|
if (pending && !refreshInBackground) {
|
|
309
229
|
return pending;
|
|
310
230
|
}
|
|
311
231
|
return (async () => {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
onStoreError?.(error, { name });
|
|
232
|
+
const entry = await guardStore(() => cacheStore.get(key));
|
|
233
|
+
const cached = servable(entry);
|
|
234
|
+
if (cached) {
|
|
235
|
+
return serve(key, args, cached);
|
|
317
236
|
}
|
|
318
237
|
if (entry) {
|
|
319
|
-
|
|
320
|
-
if (isFresh) {
|
|
321
|
-
return entry.value;
|
|
322
|
-
}
|
|
323
|
-
if (refreshInBackground) {
|
|
324
|
-
if (!pendingPromises.has(key)) {
|
|
325
|
-
const staleValue = entry.value;
|
|
326
|
-
const refresh = fn(...args).then(async (value) => {
|
|
327
|
-
await saveToStore(cacheStore, key, value);
|
|
328
|
-
pendingPromises.delete(key);
|
|
329
|
-
return value;
|
|
330
|
-
}).catch((error) => {
|
|
331
|
-
pendingPromises.delete(key);
|
|
332
|
-
onBackgroundRefreshError?.(error, { name });
|
|
333
|
-
return staleValue;
|
|
334
|
-
});
|
|
335
|
-
pendingPromises.set(key, refresh);
|
|
336
|
-
}
|
|
337
|
-
return entry.value;
|
|
338
|
-
}
|
|
339
|
-
try {
|
|
340
|
-
await cacheStore.delete(key);
|
|
341
|
-
} catch (error) {
|
|
342
|
-
onStoreError?.(error, { name });
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
const existing = pendingPromises.get(key);
|
|
346
|
-
if (existing) {
|
|
347
|
-
return existing;
|
|
238
|
+
await guardStore(() => cacheStore.delete(key));
|
|
348
239
|
}
|
|
349
|
-
|
|
350
|
-
await saveToStore(cacheStore, key, value);
|
|
351
|
-
pendingPromises.delete(key);
|
|
352
|
-
return value;
|
|
353
|
-
}).catch((error) => {
|
|
354
|
-
pendingPromises.delete(key);
|
|
355
|
-
throw error;
|
|
356
|
-
});
|
|
357
|
-
pendingPromises.set(key, promise);
|
|
358
|
-
return promise;
|
|
240
|
+
return pendingPromises.get(key) ?? run(key, args);
|
|
359
241
|
})();
|
|
360
242
|
};
|
|
361
243
|
const wrappedFn = (...args) => {
|
|
244
|
+
const key = buildKey(args);
|
|
362
245
|
if (externalStore) {
|
|
363
|
-
return callWithStore(externalStore, args);
|
|
246
|
+
return callWithStore(externalStore, key, args);
|
|
364
247
|
}
|
|
365
|
-
const
|
|
366
|
-
const cached = cache.get(key);
|
|
248
|
+
const cached = servable(cache.get(key));
|
|
367
249
|
if (cached) {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
return cached.value;
|
|
371
|
-
}
|
|
372
|
-
if (refreshInBackground) {
|
|
373
|
-
if (!pendingPromises.has(key)) {
|
|
374
|
-
startBackgroundRefresh(key, args, cached.value);
|
|
375
|
-
}
|
|
376
|
-
return cached.value;
|
|
377
|
-
}
|
|
378
|
-
cache.delete(key);
|
|
379
|
-
}
|
|
380
|
-
const pending = pendingPromises.get(key);
|
|
381
|
-
if (pending) {
|
|
382
|
-
return pending;
|
|
250
|
+
touch(key, cached);
|
|
251
|
+
return serve(key, args, cached);
|
|
383
252
|
}
|
|
384
|
-
|
|
253
|
+
cache.delete(key);
|
|
254
|
+
return pendingPromises.get(key) ?? run(key, args);
|
|
385
255
|
};
|
|
386
256
|
wrappedFn.clearCache = () => {
|
|
387
257
|
cache.clear();
|
|
388
258
|
pendingPromises.clear();
|
|
389
259
|
stopCleanup();
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
260
|
+
const clearStore = externalStore?.clear?.bind(externalStore);
|
|
261
|
+
if (clearStore) {
|
|
262
|
+
guardStore(clearStore);
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
wrappedFn.invalidate = (...args) => {
|
|
266
|
+
const key = buildKey(args);
|
|
267
|
+
cache.delete(key);
|
|
268
|
+
pendingPromises.delete(key);
|
|
269
|
+
if (externalStore) {
|
|
270
|
+
guardStore(() => externalStore.delete(key));
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
wrappedFn.peek = (...args) => {
|
|
274
|
+
const key = buildKey(args);
|
|
275
|
+
if (externalStore) {
|
|
276
|
+
return guardStore(() => externalStore.get(key)).then((entry) => servable(entry)?.value);
|
|
394
277
|
}
|
|
278
|
+
return servable(cache.get(key))?.value;
|
|
395
279
|
};
|
|
396
280
|
return wrappedFn;
|
|
397
281
|
}
|
|
@@ -413,84 +297,54 @@ class SimpleStateMachine {
|
|
|
413
297
|
}
|
|
414
298
|
async getAvailableActions() {
|
|
415
299
|
const actionsMap = this.config.stateActionMap[this.getCurrentStatus()] || {};
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
continue;
|
|
420
|
-
const validationResult = await this.validateTransition(targetState, { action });
|
|
421
|
-
result[action] = validationResult;
|
|
422
|
-
}
|
|
423
|
-
return result;
|
|
300
|
+
const entries = Object.entries(actionsMap).filter((entry) => !!entry[1]);
|
|
301
|
+
const results = await Promise.all(entries.map(([action, targetState]) => this.validateTransition(targetState, { action })));
|
|
302
|
+
return Object.fromEntries(entries.map(([action], i) => [action, results[i]]));
|
|
424
303
|
}
|
|
425
304
|
async getPossibleTransitions() {
|
|
426
305
|
const possibleTransitions = this.config.stateTransitions[this.getCurrentStatus()] || [];
|
|
427
306
|
const actionsMap = this.config.stateActionMap[this.getCurrentStatus()] || {};
|
|
428
|
-
const
|
|
429
|
-
for (const targetState of possibleTransitions) {
|
|
307
|
+
const results = await Promise.all(possibleTransitions.map(async (targetState) => {
|
|
430
308
|
const actionsToTarget = Object.entries(actionsMap).filter(([, t]) => t === targetState);
|
|
431
309
|
if (actionsToTarget.length === 0) {
|
|
432
|
-
|
|
433
|
-
continue;
|
|
434
|
-
}
|
|
435
|
-
let lastError = true;
|
|
436
|
-
let anyOk = false;
|
|
437
|
-
for (const [actionKey] of actionsToTarget) {
|
|
438
|
-
const r = await this.validateTransition(targetState, {
|
|
439
|
-
action: actionKey
|
|
440
|
-
});
|
|
441
|
-
if (r === true) {
|
|
442
|
-
anyOk = true;
|
|
443
|
-
break;
|
|
444
|
-
}
|
|
445
|
-
lastError = r;
|
|
310
|
+
return this.validateTransition(targetState);
|
|
446
311
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
312
|
+
const rs = await Promise.all(actionsToTarget.map(([action]) => this.validateTransition(targetState, { action })));
|
|
313
|
+
return rs.includes(true) ? true : rs[rs.length - 1];
|
|
314
|
+
}));
|
|
315
|
+
return Object.fromEntries(possibleTransitions.map((targetState, i) => [targetState, results[i]]));
|
|
450
316
|
}
|
|
451
|
-
async
|
|
317
|
+
async canExecute(action) {
|
|
452
318
|
const actionsMap = this.config.stateActionMap[this.getCurrentStatus()];
|
|
453
319
|
if (!actionsMap) {
|
|
454
|
-
return {
|
|
455
|
-
success: false,
|
|
456
|
-
error: `No actions defined for state '${this.getCurrentStatus()}'`
|
|
457
|
-
};
|
|
320
|
+
return `No actions defined for state '${this.getCurrentStatus()}'`;
|
|
458
321
|
}
|
|
459
322
|
const targetState = actionsMap[action];
|
|
460
323
|
if (!targetState) {
|
|
461
|
-
return {
|
|
462
|
-
success: false,
|
|
463
|
-
error: `Action '${action}' not allowed in state '${this.getCurrentStatus()}'`
|
|
464
|
-
};
|
|
324
|
+
return `Action '${action}' not allowed in state '${this.getCurrentStatus()}'`;
|
|
465
325
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
success: false,
|
|
481
|
-
error: error instanceof Error ? error.message : String(error)
|
|
482
|
-
};
|
|
483
|
-
}
|
|
326
|
+
return this.validateTransition(targetState, { action });
|
|
327
|
+
}
|
|
328
|
+
async executeAction(action, data) {
|
|
329
|
+
const fromState = this.getCurrentStatus();
|
|
330
|
+
const validationResult = await this.canExecute(action);
|
|
331
|
+
if (validationResult !== true) {
|
|
332
|
+
return { success: false, error: validationResult };
|
|
333
|
+
}
|
|
334
|
+
const targetState = this.config.stateActionMap[fromState][action];
|
|
335
|
+
try {
|
|
336
|
+
const result = await this.config.actions?.[action]?.(this.item, data);
|
|
337
|
+
await this.config.onTransition?.(this.item, fromState, targetState, action);
|
|
338
|
+
if (this.config.applyTransition) {
|
|
339
|
+
this.item[this.stateKey] = targetState;
|
|
484
340
|
}
|
|
341
|
+
return { success: true, newState: targetState, result };
|
|
342
|
+
} catch (error) {
|
|
485
343
|
return {
|
|
486
|
-
success:
|
|
487
|
-
|
|
344
|
+
success: false,
|
|
345
|
+
error: error instanceof Error ? error.message : String(error)
|
|
488
346
|
};
|
|
489
347
|
}
|
|
490
|
-
return {
|
|
491
|
-
success: false,
|
|
492
|
-
error: validationResult
|
|
493
|
-
};
|
|
494
348
|
}
|
|
495
349
|
async validateTransition(toState, options) {
|
|
496
350
|
const action = options?.action;
|
|
@@ -508,7 +362,14 @@ class SimpleStateMachine {
|
|
|
508
362
|
}
|
|
509
363
|
const stateValidator = this.config.validators?.[toState];
|
|
510
364
|
if (stateValidator) {
|
|
511
|
-
|
|
365
|
+
const stateResult = await stateValidator(this.item, this.getCurrentStatus(), toState, action);
|
|
366
|
+
if (stateResult !== true) {
|
|
367
|
+
return stateResult;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
const actionValidator = action && this.config.actionValidators?.[action];
|
|
371
|
+
if (actionValidator) {
|
|
372
|
+
return await actionValidator(this.item, this.getCurrentStatus(), toState, action);
|
|
512
373
|
}
|
|
513
374
|
return true;
|
|
514
375
|
}
|
|
@@ -516,17 +377,23 @@ class SimpleStateMachine {
|
|
|
516
377
|
return Object.keys(this.config.stateTransitions);
|
|
517
378
|
}
|
|
518
379
|
getInitialState() {
|
|
519
|
-
return this.getStates()[0];
|
|
380
|
+
return this.config.initialState ?? this.getStates()[0];
|
|
520
381
|
}
|
|
521
382
|
}
|
|
383
|
+
function createStateMachine(config) {
|
|
384
|
+
return {
|
|
385
|
+
config,
|
|
386
|
+
for: (item, stateKey) => new SimpleStateMachine(config, item, stateKey)
|
|
387
|
+
};
|
|
388
|
+
}
|
|
522
389
|
// src/math/linearRegressionTrend.ts
|
|
523
|
-
function
|
|
390
|
+
function linearRegression(values) {
|
|
524
391
|
const n = values.length;
|
|
525
392
|
if (n < 2)
|
|
526
|
-
return
|
|
393
|
+
return null;
|
|
527
394
|
for (const v of values) {
|
|
528
395
|
if (!Number.isFinite(v))
|
|
529
|
-
return
|
|
396
|
+
return null;
|
|
530
397
|
}
|
|
531
398
|
let sumX = 0;
|
|
532
399
|
let sumY = 0;
|
|
@@ -542,24 +409,155 @@ function linearRegressionTrend(values) {
|
|
|
542
409
|
}
|
|
543
410
|
const denom = n * sumX2 - sumX * sumX;
|
|
544
411
|
if (denom === 0)
|
|
545
|
-
return
|
|
412
|
+
return null;
|
|
546
413
|
const slope = (n * sumXY - sumX * sumY) / denom;
|
|
547
414
|
const intercept = (sumY - slope * sumX) / n;
|
|
548
|
-
const
|
|
415
|
+
const mean = sumY / n;
|
|
416
|
+
const fitted = [];
|
|
417
|
+
let ssRes = 0;
|
|
418
|
+
let ssTot = 0;
|
|
549
419
|
for (let i = 0;i < n; i++) {
|
|
550
|
-
|
|
420
|
+
const y = values[i];
|
|
421
|
+
const f = intercept + slope * i;
|
|
422
|
+
fitted.push(f);
|
|
423
|
+
ssRes += (y - f) ** 2;
|
|
424
|
+
ssTot += (y - mean) ** 2;
|
|
551
425
|
}
|
|
552
|
-
return
|
|
426
|
+
return { slope, intercept, r2: ssTot === 0 ? 1 : 1 - ssRes / ssTot, fitted };
|
|
427
|
+
}
|
|
428
|
+
function linearRegressionTrend(values) {
|
|
429
|
+
return linearRegression(values)?.fitted ?? [];
|
|
430
|
+
}
|
|
431
|
+
// src/math/scale.ts
|
|
432
|
+
function clamp(value, min, max) {
|
|
433
|
+
return Math.min(max, Math.max(min, value));
|
|
434
|
+
}
|
|
435
|
+
function lerp(start, end, t) {
|
|
436
|
+
return start + (end - start) * t;
|
|
437
|
+
}
|
|
438
|
+
function normalize(value, min, max) {
|
|
439
|
+
if (max <= min)
|
|
440
|
+
return 0;
|
|
441
|
+
return clamp((value - min) / (max - min), 0, 1);
|
|
442
|
+
}
|
|
443
|
+
// src/color/contrast.ts
|
|
444
|
+
function colorFunctionArgs(color) {
|
|
445
|
+
const match = /^(rgb|hsl)a?\(([^()]*)\)$/i.exec(color.trim());
|
|
446
|
+
const args = match?.[2]?.trim().split(/[\s,/]+/);
|
|
447
|
+
if (!match || !args || args.length !== 3 && args.length !== 4) {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
return { name: match[1].toLowerCase(), args };
|
|
451
|
+
}
|
|
452
|
+
function parseChannel(raw, percentOf) {
|
|
453
|
+
const isPercent = raw.endsWith("%");
|
|
454
|
+
const text = isPercent ? raw.slice(0, -1) : raw;
|
|
455
|
+
const n = text === "" ? Number.NaN : Number(text);
|
|
456
|
+
if (!Number.isFinite(n))
|
|
457
|
+
return;
|
|
458
|
+
return isPercent ? n / 100 * percentOf : n;
|
|
459
|
+
}
|
|
460
|
+
function hslToRgb(h, s, l) {
|
|
461
|
+
const a = s * Math.min(l, 1 - l);
|
|
462
|
+
const f = (n) => {
|
|
463
|
+
const k = (n + h / 30) % 12;
|
|
464
|
+
return (l - a * Math.max(-1, Math.min(k - 3, 9 - k, 1))) * 255;
|
|
465
|
+
};
|
|
466
|
+
return [f(0), f(8), f(4)];
|
|
467
|
+
}
|
|
468
|
+
function parseColor(color) {
|
|
469
|
+
const trimmed = color.trim();
|
|
470
|
+
const hex = /^#([\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/i.exec(trimmed)?.[1];
|
|
471
|
+
if (hex) {
|
|
472
|
+
const full = hex.length <= 4 ? hex.replace(/./g, "$&$&") : hex;
|
|
473
|
+
const [r = 0, g = 0, b = 0, a = 255] = [0, 2, 4, 6].map((i) => i < full.length ? Number.parseInt(full.slice(i, i + 2), 16) : undefined);
|
|
474
|
+
return { r, g, b, a: a / 255 };
|
|
475
|
+
}
|
|
476
|
+
const fn = colorFunctionArgs(trimmed);
|
|
477
|
+
if (!fn)
|
|
478
|
+
return;
|
|
479
|
+
const [x = "", y = "", z = "", alpha = "1"] = fn.args;
|
|
480
|
+
const isRgb = fn.name === "rgb";
|
|
481
|
+
const c1 = parseChannel(isRgb ? x : x.replace(/deg$/i, ""), isRgb ? 255 : 360);
|
|
482
|
+
const c2 = parseChannel(y, isRgb ? 255 : 100);
|
|
483
|
+
const c3 = parseChannel(z, isRgb ? 255 : 100);
|
|
484
|
+
const a = parseChannel(alpha, 1);
|
|
485
|
+
if (c1 === undefined || c2 === undefined || c3 === undefined)
|
|
486
|
+
return;
|
|
487
|
+
if (a === undefined)
|
|
488
|
+
return;
|
|
489
|
+
const [r, g, b] = isRgb ? [c1, c2, c3] : hslToRgb((c1 % 360 + 360) % 360, clamp(c2 / 100, 0, 1), clamp(c3 / 100, 0, 1));
|
|
490
|
+
const channel = (v) => Math.round(clamp(v, 0, 255));
|
|
491
|
+
return { r: channel(r), g: channel(g), b: channel(b), a: clamp(a, 0, 1) };
|
|
492
|
+
}
|
|
493
|
+
function toRgba(color) {
|
|
494
|
+
if (typeof color !== "string")
|
|
495
|
+
return color;
|
|
496
|
+
const parsed = parseColor(color);
|
|
497
|
+
if (!parsed)
|
|
498
|
+
throw new TypeError(`Unsupported color: ${color}`);
|
|
499
|
+
return parsed;
|
|
500
|
+
}
|
|
501
|
+
function toHex(color, options = {}) {
|
|
502
|
+
const { r, g, b, a } = toRgba(color);
|
|
503
|
+
const channels = options.alpha ? [r, g, b, a * 255] : [r, g, b];
|
|
504
|
+
return `#${channels.map((v) => Math.round(clamp(v, 0, 255)).toString(16).padStart(2, "0")).join("")}`;
|
|
505
|
+
}
|
|
506
|
+
function toRgbString(color) {
|
|
507
|
+
const { r, g, b, a } = toRgba(color);
|
|
508
|
+
const [rr, gg, bb] = [r, g, b].map((v) => Math.round(clamp(v, 0, 255)));
|
|
509
|
+
return a < 1 ? `rgba(${rr}, ${gg}, ${bb}, ${a})` : `rgb(${rr}, ${gg}, ${bb})`;
|
|
510
|
+
}
|
|
511
|
+
function relativeLuminance(color) {
|
|
512
|
+
const { r, g, b } = toRgba(color);
|
|
513
|
+
const [lr, lg, lb] = [r, g, b].map((v) => {
|
|
514
|
+
const c = clamp(v, 0, 255) / 255;
|
|
515
|
+
return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
516
|
+
});
|
|
517
|
+
return 0.2126 * lr + 0.7152 * lg + 0.0722 * lb;
|
|
518
|
+
}
|
|
519
|
+
function contrastRatio(a, b) {
|
|
520
|
+
const la = relativeLuminance(a);
|
|
521
|
+
const lb = relativeLuminance(b);
|
|
522
|
+
return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
|
|
523
|
+
}
|
|
524
|
+
function contrastText(background, options = {}) {
|
|
525
|
+
const { light = "#ffffff", dark = "#000000" } = options;
|
|
526
|
+
return contrastRatio(background, light) >= contrastRatio(background, dark) ? light : dark;
|
|
527
|
+
}
|
|
528
|
+
function mix(a, b, weight) {
|
|
529
|
+
const t = clamp(weight, 0, 1);
|
|
530
|
+
const at = (x, y) => x + (y - x) * t;
|
|
531
|
+
return {
|
|
532
|
+
r: at(a.r, b.r),
|
|
533
|
+
g: at(a.g, b.g),
|
|
534
|
+
b: at(a.b, b.b),
|
|
535
|
+
a: at(a.a, b.a)
|
|
536
|
+
};
|
|
553
537
|
}
|
|
554
|
-
|
|
538
|
+
var hexOf = (color) => toHex(color, { alpha: color.a < 1 });
|
|
539
|
+
function mixColors(a, b, weight) {
|
|
540
|
+
return hexOf(mix(toRgba(a), toRgba(b), weight));
|
|
541
|
+
}
|
|
542
|
+
function shade(color, amount) {
|
|
543
|
+
const base = toRgba(color);
|
|
544
|
+
const v = amount < 0 ? 0 : 255;
|
|
545
|
+
return hexOf(mix(base, { r: v, g: v, b: v, a: base.a }, Math.abs(amount)));
|
|
546
|
+
}
|
|
547
|
+
// src/color/metricsSeriesColors.ts
|
|
555
548
|
var METRICS_SERIES_PALETTE_SIZE = 10;
|
|
556
|
-
function hashStringToColorIndex(str) {
|
|
549
|
+
function hashStringToColorIndex(str, paletteSize = METRICS_SERIES_PALETTE_SIZE) {
|
|
557
550
|
let hash = 2166136261;
|
|
558
551
|
for (let i = 0;i < str.length; i++) {
|
|
559
552
|
hash ^= str.charCodeAt(i);
|
|
560
553
|
hash = Math.imul(hash, 16777619);
|
|
561
554
|
}
|
|
562
|
-
return Math.abs(hash) %
|
|
555
|
+
return Math.abs(hash) % paletteSize;
|
|
556
|
+
}
|
|
557
|
+
function pickFromPalette(seed, palette) {
|
|
558
|
+
if (palette.length === 0)
|
|
559
|
+
throw new RangeError("palette is empty");
|
|
560
|
+
return palette[hashStringToColorIndex(seed, palette.length)];
|
|
563
561
|
}
|
|
564
562
|
function circularHueDistance(a, b, paletteSize) {
|
|
565
563
|
const diff = Math.abs(a - b);
|
|
@@ -586,21 +584,20 @@ function seriesColorKey(label, key) {
|
|
|
586
584
|
return fromLabel;
|
|
587
585
|
return key.trim() || key;
|
|
588
586
|
}
|
|
589
|
-
function resolveSeriesColorMap(keys) {
|
|
587
|
+
function resolveSeriesColorMap(keys, paletteSize = METRICS_SERIES_PALETTE_SIZE) {
|
|
590
588
|
const result = new Map;
|
|
591
589
|
const uniqueKeys = [...new Set(keys)];
|
|
592
590
|
if (uniqueKeys.length === 0)
|
|
593
591
|
return result;
|
|
594
|
-
const paletteSize = METRICS_SERIES_PALETTE_SIZE;
|
|
595
592
|
if (uniqueKeys.length === 1) {
|
|
596
593
|
const only = uniqueKeys[0];
|
|
597
|
-
result.set(only, hashStringToColorIndex(only));
|
|
594
|
+
result.set(only, hashStringToColorIndex(only, paletteSize));
|
|
598
595
|
return result;
|
|
599
596
|
}
|
|
600
597
|
const usedIndices = new Set;
|
|
601
598
|
const entries = uniqueKeys.map((key) => ({
|
|
602
599
|
key,
|
|
603
|
-
preferred: hashStringToColorIndex(key)
|
|
600
|
+
preferred: hashStringToColorIndex(key, paletteSize)
|
|
604
601
|
}));
|
|
605
602
|
for (const entry of entries) {
|
|
606
603
|
if (!usedIndices.has(entry.preferred)) {
|
|
@@ -622,35 +619,25 @@ function resolveSeriesColorMap(keys) {
|
|
|
622
619
|
}
|
|
623
620
|
return result;
|
|
624
621
|
}
|
|
625
|
-
function createResolveColorsByKeys(keys, getSeriesColor, getSeriesHoverColor) {
|
|
626
|
-
const colorMap = resolveSeriesColorMap(keys);
|
|
622
|
+
function createResolveColorsByKeys(keys, getSeriesColor, getSeriesHoverColor, paletteSize = METRICS_SERIES_PALETTE_SIZE) {
|
|
623
|
+
const colorMap = resolveSeriesColorMap(keys, paletteSize);
|
|
624
|
+
const indexOf = (key) => colorMap.get(key) ?? hashStringToColorIndex(key, paletteSize);
|
|
627
625
|
return {
|
|
628
|
-
getColor: (key) => getSeriesColor(
|
|
629
|
-
getHoverColor: (key) => getSeriesHoverColor(
|
|
626
|
+
getColor: (key) => getSeriesColor(indexOf(key)),
|
|
627
|
+
getHoverColor: (key) => getSeriesHoverColor(indexOf(key))
|
|
630
628
|
};
|
|
631
629
|
}
|
|
632
|
-
function clamp(value, min, max) {
|
|
633
|
-
return Math.min(max, Math.max(min, value));
|
|
634
|
-
}
|
|
635
630
|
function withAlpha(color, alpha) {
|
|
636
631
|
const trimmed = color.trim();
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
const g = Number.parseInt(normalized.slice(2, 4), 16);
|
|
643
|
-
const b = Number.parseInt(normalized.slice(4, 6), 16);
|
|
644
|
-
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
if (trimmed.startsWith("rgb(")) {
|
|
648
|
-
return trimmed.replace("rgb(", "rgba(").replace(")", `, ${alpha})`);
|
|
632
|
+
const rgba = parseColor(trimmed);
|
|
633
|
+
const fn = rgba && colorFunctionArgs(trimmed);
|
|
634
|
+
if (fn?.name === "hsl") {
|
|
635
|
+
const [h, s, l] = fn.args;
|
|
636
|
+
return `hsla(${h}, ${s}, ${l}, ${alpha})`;
|
|
649
637
|
}
|
|
650
|
-
if (
|
|
651
|
-
return
|
|
652
|
-
}
|
|
653
|
-
return trimmed;
|
|
638
|
+
if (rgba)
|
|
639
|
+
return `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${alpha})`;
|
|
640
|
+
return `color-mix(in srgb, ${trimmed} ${alpha * 100}%, transparent)`;
|
|
654
641
|
}
|
|
655
642
|
function getHeatmapCellStyle(params) {
|
|
656
643
|
const {
|
|
@@ -673,2833 +660,440 @@ function getHeatmapCellStyle(params) {
|
|
|
673
660
|
backgroundColor: withAlpha(neutralColor ?? baseColor, 0.32)
|
|
674
661
|
};
|
|
675
662
|
}
|
|
676
|
-
const normalized = clamp((value - min) / (max - min), 0, 1);
|
|
677
|
-
const alpha = minAlpha + (maxAlpha - minAlpha) * normalized;
|
|
678
663
|
return {
|
|
679
|
-
backgroundColor: withAlpha(baseColor,
|
|
664
|
+
backgroundColor: withAlpha(baseColor, lerp(minAlpha, maxAlpha, normalize(value, min, max)))
|
|
680
665
|
};
|
|
681
666
|
}
|
|
682
667
|
// src/date/index.ts
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
const prevQYear = qStart === 0 ? year - 1 : year;
|
|
699
|
-
return {
|
|
700
|
-
start: new Date(prevQYear, prevQStart, 1),
|
|
701
|
-
end: new Date(prevQYear, prevQStart + 3, 0)
|
|
702
|
-
};
|
|
703
|
-
}
|
|
704
|
-
case "YEARLY": {
|
|
705
|
-
return {
|
|
706
|
-
start: new Date(year - 1, 0, 1),
|
|
707
|
-
end: new Date(year - 1, 11, 31)
|
|
708
|
-
};
|
|
709
|
-
}
|
|
668
|
+
var MONTHS_PER_PERIOD = { MONTHLY: 1, QUARTERLY: 3, YEARLY: 12 };
|
|
669
|
+
function ymd(date, utc = false) {
|
|
670
|
+
return utc ? [date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()] : [date.getFullYear(), date.getMonth(), date.getDate()];
|
|
671
|
+
}
|
|
672
|
+
function startOfDay(y, m, d, utc = false) {
|
|
673
|
+
return utc ? new Date(Date.UTC(y, m, d)) : new Date(y, m, d);
|
|
674
|
+
}
|
|
675
|
+
function addPeriod(date, type, n, options = {}) {
|
|
676
|
+
const { utc = false } = options;
|
|
677
|
+
let [y, m, d] = ymd(date, utc);
|
|
678
|
+
if (type === "DAILY" || type === "WEEKLY") {
|
|
679
|
+
d += n * (type === "WEEKLY" ? 7 : 1);
|
|
680
|
+
} else {
|
|
681
|
+
m += n * MONTHS_PER_PERIOD[type];
|
|
682
|
+
d = Math.min(d, new Date(Date.UTC(y, m + 1, 0)).getUTCDate());
|
|
710
683
|
}
|
|
684
|
+
const out = new Date(date);
|
|
685
|
+
if (utc)
|
|
686
|
+
out.setUTCFullYear(y, m, d);
|
|
687
|
+
else
|
|
688
|
+
out.setFullYear(y, m, d);
|
|
689
|
+
return out;
|
|
690
|
+
}
|
|
691
|
+
function getPeriod(type, referenceDate = new Date, offset = 0, options = {}) {
|
|
692
|
+
const { utc = false, weekStartsOn = 1 } = options;
|
|
693
|
+
const [y, m, d] = ymd(referenceDate, utc);
|
|
694
|
+
const weekday = utc ? referenceDate.getUTCDay() : referenceDate.getDay();
|
|
695
|
+
const anchor = {
|
|
696
|
+
DAILY: () => startOfDay(y, m, d, utc),
|
|
697
|
+
WEEKLY: () => startOfDay(y, m, d - (weekday - weekStartsOn + 7) % 7, utc),
|
|
698
|
+
MONTHLY: () => startOfDay(y, m, 1, utc),
|
|
699
|
+
QUARTERLY: () => startOfDay(y, m - m % 3, 1, utc),
|
|
700
|
+
YEARLY: () => startOfDay(y, 0, 1, utc)
|
|
701
|
+
}[type]();
|
|
702
|
+
const [ey, em, ed] = ymd(addPeriod(anchor, type, offset + 1, { utc }), utc);
|
|
703
|
+
return {
|
|
704
|
+
start: addPeriod(anchor, type, offset, { utc }),
|
|
705
|
+
end: startOfDay(ey, em, ed - 1, utc)
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
function getPreviousReportPeriod(periodType, referenceDate = new Date) {
|
|
709
|
+
return getPeriod(periodType, referenceDate, -1);
|
|
711
710
|
}
|
|
712
|
-
function formatPeriodLabel(start, end,
|
|
711
|
+
function formatPeriodLabel(start, end, options = false) {
|
|
712
|
+
const {
|
|
713
|
+
includeDay = false,
|
|
714
|
+
locale = "en-GB",
|
|
715
|
+
separator = " — ",
|
|
716
|
+
collapse = false,
|
|
717
|
+
timeZone
|
|
718
|
+
} = typeof options === "boolean" ? { includeDay: options } : options;
|
|
713
719
|
const s = start instanceof Date ? start : new Date(start);
|
|
714
720
|
const e = end instanceof Date ? end : new Date(end);
|
|
715
|
-
const
|
|
721
|
+
const format = new Intl.DateTimeFormat(locale, {
|
|
716
722
|
year: "numeric",
|
|
717
723
|
month: "long",
|
|
718
|
-
...includeDay && { day: "numeric" }
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
}
|
|
722
|
-
// src/url/index.ts
|
|
723
|
-
function getDomainFaviconUrl(domain, size = 128) {
|
|
724
|
-
return `https://www.google.com/s2/favicons?domain=${encodeURIComponent(domain)}&sz=${size}`;
|
|
725
|
-
}
|
|
726
|
-
// src/zod/index.ts
|
|
727
|
-
import { z as z3 } from "zod";
|
|
728
|
-
|
|
729
|
-
// src/zod/jsonToZodSchemaMapper.ts
|
|
730
|
-
import { z } from "zod";
|
|
731
|
-
function jsonToZodSchemaMapper(jsonObject, zodSchema, config) {
|
|
732
|
-
const result = {};
|
|
733
|
-
const shape = zodSchema instanceof z.ZodObject ? zodSchema.shape : {};
|
|
734
|
-
for (const key of Object.keys(shape)) {
|
|
735
|
-
const fieldConfig = config[key];
|
|
736
|
-
let value = jsonObject[key];
|
|
737
|
-
if (fieldConfig) {
|
|
738
|
-
value = fieldConfig(jsonObject);
|
|
739
|
-
}
|
|
740
|
-
if (shape[key] instanceof z.ZodObject && value && typeof value === "object") {
|
|
741
|
-
result[key] = jsonToZodSchemaMapper(value, shape[key], config[key] || {});
|
|
742
|
-
} else {
|
|
743
|
-
result[key] = value;
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
return zodSchema.parse(result);
|
|
747
|
-
}
|
|
748
|
-
// src/zod/zodMinMaxFilter.ts
|
|
749
|
-
import { z as z2 } from "zod";
|
|
750
|
-
var zodMinMaxFilter = z2.object({
|
|
751
|
-
min: z2.number().optional(),
|
|
752
|
-
max: z2.number().optional()
|
|
753
|
-
});
|
|
754
|
-
function toGteLteFilter(filter) {
|
|
755
|
-
if (!filter)
|
|
756
|
-
return;
|
|
757
|
-
const hasMin = filter.min !== undefined;
|
|
758
|
-
const hasMax = filter.max !== undefined;
|
|
759
|
-
if (!hasMin && !hasMax)
|
|
760
|
-
return;
|
|
761
|
-
return {
|
|
762
|
-
...hasMin && { gte: filter.min },
|
|
763
|
-
...hasMax && { lte: filter.max }
|
|
764
|
-
};
|
|
765
|
-
}
|
|
766
|
-
// src/zod/zodToTypeString.ts
|
|
767
|
-
function getDefProp(def, prop) {
|
|
768
|
-
return def?.[prop];
|
|
724
|
+
...includeDay && { day: "numeric" },
|
|
725
|
+
timeZone
|
|
726
|
+
});
|
|
727
|
+
return collapse ? format.formatRange(s, e) : `${format.format(s)}${separator}${format.format(e)}`;
|
|
769
728
|
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
const def = schema._zod?.def ?? schema._def;
|
|
774
|
-
if (!def)
|
|
775
|
-
return "unknown";
|
|
776
|
-
const schemaType = getDefProp(def, "type") ?? getDefProp(def, "typeName");
|
|
777
|
-
switch (schemaType) {
|
|
778
|
-
case "object": {
|
|
779
|
-
const shape = getDefProp(def, "shape");
|
|
780
|
-
if (!shape || Object.keys(shape).length === 0)
|
|
781
|
-
return "{}";
|
|
782
|
-
const fields = Object.entries(shape).map(([key, value]) => {
|
|
783
|
-
const valueType = getDefProp(value._zod?.def ?? value._def, "type");
|
|
784
|
-
const isOptional = valueType === "optional";
|
|
785
|
-
const innerSchema = isOptional ? getDefProp(value._zod?.def ?? value._def, "innerType") ?? value : value;
|
|
786
|
-
const typeStr = zodToTypeString(innerSchema, indent + 1);
|
|
787
|
-
return `${innerPad}${key}${isOptional ? "?" : ""}: ${typeStr}`;
|
|
788
|
-
});
|
|
789
|
-
return `{
|
|
790
|
-
${fields.join(`
|
|
791
|
-
`)}
|
|
792
|
-
${pad}}`;
|
|
793
|
-
}
|
|
794
|
-
case "array": {
|
|
795
|
-
const itemType = getDefProp(def, "element") ?? getDefProp(def, "type");
|
|
796
|
-
return itemType ? `${zodToTypeString(itemType, indent)}[]` : "unknown[]";
|
|
797
|
-
}
|
|
798
|
-
case "string":
|
|
799
|
-
return "string";
|
|
800
|
-
case "number":
|
|
801
|
-
return "number";
|
|
802
|
-
case "boolean":
|
|
803
|
-
return "boolean";
|
|
804
|
-
case "date":
|
|
805
|
-
return "Date";
|
|
806
|
-
case "null":
|
|
807
|
-
return "null";
|
|
808
|
-
case "undefined":
|
|
809
|
-
return "undefined";
|
|
810
|
-
case "any":
|
|
811
|
-
return "any";
|
|
812
|
-
case "unknown":
|
|
813
|
-
return "unknown";
|
|
814
|
-
case "record": {
|
|
815
|
-
const valueType = getDefProp(def, "valueType");
|
|
816
|
-
return valueType ? `Record<string, ${zodToTypeString(valueType, indent)}>` : "Record<string, unknown>";
|
|
817
|
-
}
|
|
818
|
-
case "enum": {
|
|
819
|
-
const values = getDefProp(def, "values");
|
|
820
|
-
return values ? values.map((v) => `"${v}"`).join(" | ") : "string";
|
|
821
|
-
}
|
|
822
|
-
case "literal": {
|
|
823
|
-
const value = getDefProp(def, "value");
|
|
824
|
-
return typeof value === "string" ? `"${value}"` : String(value);
|
|
825
|
-
}
|
|
826
|
-
case "union": {
|
|
827
|
-
const options = getDefProp(def, "options");
|
|
828
|
-
return options ? options.map((o) => zodToTypeString(o, indent)).join(" | ") : "unknown";
|
|
829
|
-
}
|
|
830
|
-
case "optional": {
|
|
831
|
-
const inner = getDefProp(def, "innerType");
|
|
832
|
-
return inner ? `${zodToTypeString(inner, indent)} | undefined` : "unknown";
|
|
833
|
-
}
|
|
834
|
-
case "nullable": {
|
|
835
|
-
const inner = getDefProp(def, "innerType");
|
|
836
|
-
return inner ? `${zodToTypeString(inner, indent)} | null` : "unknown";
|
|
837
|
-
}
|
|
838
|
-
case "default": {
|
|
839
|
-
const inner = getDefProp(def, "innerType");
|
|
840
|
-
return inner ? zodToTypeString(inner, indent) : "unknown";
|
|
841
|
-
}
|
|
842
|
-
case "ZodObject":
|
|
843
|
-
case "ZodArray":
|
|
844
|
-
case "ZodString":
|
|
845
|
-
case "ZodNumber":
|
|
846
|
-
case "ZodBoolean":
|
|
847
|
-
case "ZodDate":
|
|
848
|
-
case "ZodNull":
|
|
849
|
-
case "ZodUndefined":
|
|
850
|
-
case "ZodAny":
|
|
851
|
-
case "ZodUnknown":
|
|
852
|
-
case "ZodRecord":
|
|
853
|
-
case "ZodEnum":
|
|
854
|
-
case "ZodLiteral":
|
|
855
|
-
case "ZodUnion":
|
|
856
|
-
case "ZodOptional":
|
|
857
|
-
case "ZodNullable":
|
|
858
|
-
case "ZodDefault":
|
|
859
|
-
return zodToTypeString(schema, indent);
|
|
860
|
-
default:
|
|
861
|
-
return "unknown";
|
|
862
|
-
}
|
|
729
|
+
// src/object/index.ts
|
|
730
|
+
function typedKeys(object) {
|
|
731
|
+
return Object.keys(object);
|
|
863
732
|
}
|
|
864
|
-
function
|
|
865
|
-
|
|
866
|
-
if (!def)
|
|
867
|
-
return null;
|
|
868
|
-
const schemaType = getDefProp(def, "type") ?? getDefProp(def, "typeName");
|
|
869
|
-
switch (schemaType) {
|
|
870
|
-
case "object": {
|
|
871
|
-
const shape = getDefProp(def, "shape");
|
|
872
|
-
if (!shape)
|
|
873
|
-
return {};
|
|
874
|
-
const result = {};
|
|
875
|
-
for (const [key, value] of Object.entries(shape)) {
|
|
876
|
-
const valueType = getDefProp(value._zod?.def ?? value._def, "type");
|
|
877
|
-
const isOptional = valueType === "optional";
|
|
878
|
-
if (!isOptional) {
|
|
879
|
-
result[key] = zodToExample(value);
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
return result;
|
|
883
|
-
}
|
|
884
|
-
case "array": {
|
|
885
|
-
const itemType = getDefProp(def, "element") ?? getDefProp(def, "type");
|
|
886
|
-
return itemType ? [zodToExample(itemType)] : [];
|
|
887
|
-
}
|
|
888
|
-
case "string":
|
|
889
|
-
return "";
|
|
890
|
-
case "number":
|
|
891
|
-
return 0;
|
|
892
|
-
case "boolean":
|
|
893
|
-
return false;
|
|
894
|
-
case "enum": {
|
|
895
|
-
const values = getDefProp(def, "values");
|
|
896
|
-
return values?.[0] ?? "";
|
|
897
|
-
}
|
|
898
|
-
case "literal":
|
|
899
|
-
return getDefProp(def, "value");
|
|
900
|
-
case "optional":
|
|
901
|
-
case "default": {
|
|
902
|
-
const inner = getDefProp(def, "innerType");
|
|
903
|
-
return inner ? zodToExample(inner) : null;
|
|
904
|
-
}
|
|
905
|
-
case "record":
|
|
906
|
-
return {};
|
|
907
|
-
default:
|
|
908
|
-
return null;
|
|
909
|
-
}
|
|
733
|
+
function typedEntries(object) {
|
|
734
|
+
return Object.entries(object);
|
|
910
735
|
}
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
function createZId(typeName) {
|
|
914
|
-
return z3.string().min(1, `${typeName} ID is required`).brand();
|
|
736
|
+
function typedFromEntries(entries) {
|
|
737
|
+
return Object.fromEntries(entries);
|
|
915
738
|
}
|
|
916
|
-
function
|
|
917
|
-
return
|
|
739
|
+
function recordFromKeys(keys, fn) {
|
|
740
|
+
return typedFromEntries(keys.map((key) => [key, fn(key)]));
|
|
918
741
|
}
|
|
919
|
-
function
|
|
920
|
-
|
|
921
|
-
return [];
|
|
922
|
-
if (Array.isArray(value))
|
|
923
|
-
return [...value];
|
|
924
|
-
return [value];
|
|
742
|
+
function mapValues(object, fn) {
|
|
743
|
+
return typedFromEntries(typedEntries(object).map(([key, value]) => [key, fn(value, key)]));
|
|
925
744
|
}
|
|
926
|
-
|
|
927
|
-
var CANONICAL_HEADER_NAMES = {
|
|
928
|
-
"content-type": "Content-Type",
|
|
929
|
-
authorization: "Authorization",
|
|
930
|
-
accept: "Accept",
|
|
931
|
-
"user-agent": "User-Agent",
|
|
932
|
-
"x-request-id": "X-Request-Id",
|
|
933
|
-
"x-correlation-id": "X-Correlation-Id"
|
|
934
|
-
};
|
|
935
|
-
function normalizeHeaders(headers) {
|
|
745
|
+
function pick(object, keys) {
|
|
936
746
|
const result = {};
|
|
937
|
-
const
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
const canonical = CANONICAL_HEADER_NAMES[lower] ?? key;
|
|
941
|
-
const existing = lowerToCanonical.get(lower);
|
|
942
|
-
if (existing && existing !== canonical)
|
|
943
|
-
delete result[existing];
|
|
944
|
-
lowerToCanonical.set(lower, canonical);
|
|
945
|
-
result[canonical] = value;
|
|
747
|
+
for (const key of keys) {
|
|
748
|
+
if (key in object)
|
|
749
|
+
result[key] = object[key];
|
|
946
750
|
}
|
|
947
751
|
return result;
|
|
948
752
|
}
|
|
949
|
-
function
|
|
950
|
-
const
|
|
951
|
-
const
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
return headers[canonical];
|
|
955
|
-
}
|
|
956
|
-
for (const [k, v] of Object.entries(headers)) {
|
|
957
|
-
if (k.toLowerCase() === lower)
|
|
958
|
-
return v;
|
|
959
|
-
}
|
|
960
|
-
return;
|
|
753
|
+
function omit(object, keys) {
|
|
754
|
+
const result = { ...object };
|
|
755
|
+
for (const key of keys)
|
|
756
|
+
delete result[key];
|
|
757
|
+
return result;
|
|
961
758
|
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
759
|
+
var UNSAFE_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
760
|
+
function toPath(path) {
|
|
761
|
+
if (typeof path !== "string")
|
|
762
|
+
return path;
|
|
763
|
+
return path === "" ? [] : path.split(".");
|
|
965
764
|
}
|
|
966
|
-
function
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
return JSON.stringify(body);
|
|
973
|
-
if (typeof body === "object" && (Object.getPrototypeOf(body) === Object.prototype || Object.getPrototypeOf(body) === null) && configError) {
|
|
974
|
-
throw configError("Request body is a plain object but Content-Type is not application/json");
|
|
765
|
+
function getAtPath(object, path) {
|
|
766
|
+
let current = object;
|
|
767
|
+
for (const key of toPath(path)) {
|
|
768
|
+
if (current === null || typeof current !== "object")
|
|
769
|
+
return;
|
|
770
|
+
current = current[key];
|
|
975
771
|
}
|
|
976
|
-
return
|
|
977
|
-
}
|
|
978
|
-
function getHeadersAsObject(headers) {
|
|
979
|
-
const result = {};
|
|
980
|
-
headers.forEach((value, key) => {
|
|
981
|
-
result[key] = value;
|
|
982
|
-
});
|
|
983
|
-
return result;
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
// src/api-wrapper/zod-issues.ts
|
|
987
|
-
import { ZodError } from "zod";
|
|
988
|
-
function formatZodIssues(error, rawData) {
|
|
989
|
-
return error.issues.map((issue) => {
|
|
990
|
-
const pathArray = [...issue.path];
|
|
991
|
-
const path = formatIssuePath(pathArray);
|
|
992
|
-
const value = getValueAtPath(rawData, pathArray);
|
|
993
|
-
const valuePreview = value === undefined ? undefined : previewValue(value);
|
|
994
|
-
const raw = issue;
|
|
995
|
-
const expected = typeof raw.expected === "string" ? raw.expected : undefined;
|
|
996
|
-
const received = typeof raw.received === "string" ? raw.received : value !== undefined ? typeofVerbose(value) : undefined;
|
|
997
|
-
const known = new Set([
|
|
998
|
-
"code",
|
|
999
|
-
"path",
|
|
1000
|
-
"message",
|
|
1001
|
-
"expected",
|
|
1002
|
-
"received",
|
|
1003
|
-
"fatal"
|
|
1004
|
-
]);
|
|
1005
|
-
const extra = {};
|
|
1006
|
-
for (const [k, v] of Object.entries(raw)) {
|
|
1007
|
-
if (!known.has(k) && v !== undefined)
|
|
1008
|
-
extra[k] = v;
|
|
1009
|
-
}
|
|
1010
|
-
return {
|
|
1011
|
-
path,
|
|
1012
|
-
pathArray,
|
|
1013
|
-
code: issue.code,
|
|
1014
|
-
message: issue.message,
|
|
1015
|
-
expected,
|
|
1016
|
-
received,
|
|
1017
|
-
value,
|
|
1018
|
-
valuePreview,
|
|
1019
|
-
extra: Object.keys(extra).length > 0 ? extra : undefined
|
|
1020
|
-
};
|
|
1021
|
-
});
|
|
772
|
+
return current;
|
|
1022
773
|
}
|
|
1023
|
-
function
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
const exp = issue.expected ? `expected ${issue.expected}` : "";
|
|
1031
|
-
const rec = issue.received ? `received ${issue.received}` : "";
|
|
1032
|
-
parts.push(` (${[exp, rec].filter(Boolean).join(", ")})`);
|
|
1033
|
-
}
|
|
1034
|
-
if (issue.valuePreview !== undefined) {
|
|
1035
|
-
parts.push(` value: ${issue.valuePreview}`);
|
|
1036
|
-
}
|
|
1037
|
-
if (issue.extra) {
|
|
1038
|
-
for (const [k, v] of Object.entries(issue.extra)) {
|
|
1039
|
-
parts.push(` ${k}: ${previewValue(v, 120)}`);
|
|
1040
|
-
}
|
|
774
|
+
function setAtPath(object, path, value) {
|
|
775
|
+
const keys = toPath(path);
|
|
776
|
+
if (keys.length === 0)
|
|
777
|
+
throw new Error("Path must not be empty");
|
|
778
|
+
for (const key of keys) {
|
|
779
|
+
if (UNSAFE_KEYS.has(String(key))) {
|
|
780
|
+
throw new Error(`Unsafe path segment: ${key}`);
|
|
1041
781
|
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
let out = "";
|
|
1051
|
-
for (const segment of pathArray) {
|
|
1052
|
-
if (typeof segment === "number") {
|
|
1053
|
-
out += `[${segment}]`;
|
|
1054
|
-
} else if (/^[A-Za-z_$][\w$]*$/.test(segment)) {
|
|
1055
|
-
out += out === "" ? segment : `.${segment}`;
|
|
1056
|
-
} else {
|
|
1057
|
-
out += `[${JSON.stringify(segment)}]`;
|
|
782
|
+
}
|
|
783
|
+
let current = object;
|
|
784
|
+
for (let index = 0;index < keys.length - 1; index++) {
|
|
785
|
+
const key = keys[index];
|
|
786
|
+
let next = current[key];
|
|
787
|
+
if (next === null || typeof next !== "object") {
|
|
788
|
+
next = /^\d+$/.test(String(keys[index + 1])) ? [] : {};
|
|
789
|
+
current[key] = next;
|
|
1058
790
|
}
|
|
791
|
+
current = next;
|
|
1059
792
|
}
|
|
1060
|
-
|
|
1061
|
-
}
|
|
1062
|
-
function getValueAtPath(data, pathArray) {
|
|
1063
|
-
let current = data;
|
|
1064
|
-
for (const segment of pathArray) {
|
|
1065
|
-
if (current === null || current === undefined)
|
|
1066
|
-
return;
|
|
1067
|
-
if (typeof current !== "object")
|
|
1068
|
-
return;
|
|
1069
|
-
current = current[segment];
|
|
1070
|
-
}
|
|
1071
|
-
return current;
|
|
1072
|
-
}
|
|
1073
|
-
function typeofVerbose(value) {
|
|
1074
|
-
if (value === null)
|
|
1075
|
-
return "null";
|
|
1076
|
-
if (Array.isArray(value))
|
|
1077
|
-
return "array";
|
|
1078
|
-
return typeof value;
|
|
1079
|
-
}
|
|
1080
|
-
function previewValue(value, maxLen = 200) {
|
|
1081
|
-
let str;
|
|
1082
|
-
try {
|
|
1083
|
-
if (value === undefined)
|
|
1084
|
-
str = "undefined";
|
|
1085
|
-
else if (typeof value === "string")
|
|
1086
|
-
str = JSON.stringify(value);
|
|
1087
|
-
else
|
|
1088
|
-
str = JSON.stringify(value);
|
|
1089
|
-
} catch {
|
|
1090
|
-
str = String(value);
|
|
1091
|
-
}
|
|
1092
|
-
if (str === undefined)
|
|
1093
|
-
str = String(value);
|
|
1094
|
-
if (str.length > maxLen)
|
|
1095
|
-
str = `${str.slice(0, maxLen)}…`;
|
|
1096
|
-
return str;
|
|
1097
|
-
}
|
|
1098
|
-
function makeJsonParseZodError(error, text) {
|
|
1099
|
-
const message = error instanceof Error ? `Response body was not valid JSON: ${error.message}` : "Response body was not valid JSON";
|
|
1100
|
-
const preview = text.length > 200 ? `${text.slice(0, 200)}…` : text;
|
|
1101
|
-
return new ZodError([
|
|
1102
|
-
{
|
|
1103
|
-
code: "custom",
|
|
1104
|
-
path: [],
|
|
1105
|
-
message: `${message} (body preview: ${JSON.stringify(preview)})`,
|
|
1106
|
-
input: text
|
|
1107
|
-
}
|
|
1108
|
-
]);
|
|
1109
|
-
}
|
|
1110
|
-
function tryParseJson(body) {
|
|
1111
|
-
if (!body)
|
|
1112
|
-
return;
|
|
1113
|
-
try {
|
|
1114
|
-
return JSON.parse(body);
|
|
1115
|
-
} catch {
|
|
1116
|
-
return;
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
// src/api-wrapper/errors.ts
|
|
1121
|
-
var MAX_BODY_PREVIEW = 500;
|
|
1122
|
-
|
|
1123
|
-
class ApiError extends Error {
|
|
1124
|
-
context;
|
|
1125
|
-
cause;
|
|
1126
|
-
constructor(message, context, options) {
|
|
1127
|
-
super(message);
|
|
1128
|
-
this.name = "ApiError";
|
|
1129
|
-
this.context = context;
|
|
1130
|
-
this.cause = options?.cause;
|
|
1131
|
-
}
|
|
1132
|
-
toJSON() {
|
|
1133
|
-
return {
|
|
1134
|
-
name: this.name,
|
|
1135
|
-
message: this.message,
|
|
1136
|
-
context: this.context
|
|
1137
|
-
};
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
class HttpResponseError extends ApiError {
|
|
1142
|
-
status;
|
|
1143
|
-
body;
|
|
1144
|
-
bodyJson;
|
|
1145
|
-
responseHeaders;
|
|
1146
|
-
requestId;
|
|
1147
|
-
constructor(status, body, responseHeaders, context) {
|
|
1148
|
-
const preview = body.length > MAX_BODY_PREVIEW ? `${body.slice(0, MAX_BODY_PREVIEW)}…` : body;
|
|
1149
|
-
super(`HTTP ${status} ${context.method} ${context.url} - ${preview}`, context);
|
|
1150
|
-
this.name = "HttpResponseError";
|
|
1151
|
-
this.status = status;
|
|
1152
|
-
this.body = body;
|
|
1153
|
-
this.responseHeaders = responseHeaders;
|
|
1154
|
-
this.requestId = getHeader(responseHeaders, "x-request-id") ?? getHeader(responseHeaders, "x-correlation-id");
|
|
1155
|
-
this.bodyJson = tryParseJson(body);
|
|
1156
|
-
}
|
|
1157
|
-
toJSON() {
|
|
1158
|
-
return {
|
|
1159
|
-
...super.toJSON(),
|
|
1160
|
-
status: this.status,
|
|
1161
|
-
body: this.body,
|
|
1162
|
-
bodyJson: this.bodyJson,
|
|
1163
|
-
requestId: this.requestId,
|
|
1164
|
-
responseHeaders: this.responseHeaders
|
|
1165
|
-
};
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
class ResponseValidationError extends ApiError {
|
|
1170
|
-
zodError;
|
|
1171
|
-
rawResponse;
|
|
1172
|
-
issues;
|
|
1173
|
-
validationIssues;
|
|
1174
|
-
constructor(zodError, rawResponse, context) {
|
|
1175
|
-
const issues = formatZodIssues(zodError, rawResponse);
|
|
1176
|
-
const summary = renderIssueSummary(issues);
|
|
1177
|
-
super(`Response validation failed (${context.method} ${context.url}):
|
|
1178
|
-
${summary}`, context, { cause: zodError });
|
|
1179
|
-
this.name = "ResponseValidationError";
|
|
1180
|
-
this.zodError = zodError;
|
|
1181
|
-
this.rawResponse = rawResponse;
|
|
1182
|
-
this.issues = issues;
|
|
1183
|
-
this.validationIssues = issues.map((i) => `${i.path}: ${i.message}`).join("; ");
|
|
1184
|
-
}
|
|
1185
|
-
toJSON() {
|
|
1186
|
-
return {
|
|
1187
|
-
...super.toJSON(),
|
|
1188
|
-
issues: this.issues,
|
|
1189
|
-
validationIssues: this.validationIssues,
|
|
1190
|
-
rawResponsePreview: previewValue(this.rawResponse, 1000)
|
|
1191
|
-
};
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
class RequestValidationError extends ApiError {
|
|
1196
|
-
zodError;
|
|
1197
|
-
target;
|
|
1198
|
-
rawInput;
|
|
1199
|
-
issues;
|
|
1200
|
-
validationIssues;
|
|
1201
|
-
constructor(zodError, target, rawInput, context) {
|
|
1202
|
-
const issues = formatZodIssues(zodError, rawInput);
|
|
1203
|
-
const summary = renderIssueSummary(issues);
|
|
1204
|
-
super(`Request ${target} validation failed (${context.method} ${context.url}):
|
|
1205
|
-
${summary}`, context, { cause: zodError });
|
|
1206
|
-
this.name = "RequestValidationError";
|
|
1207
|
-
this.zodError = zodError;
|
|
1208
|
-
this.target = target;
|
|
1209
|
-
this.rawInput = rawInput;
|
|
1210
|
-
this.issues = issues;
|
|
1211
|
-
this.validationIssues = issues.map((i) => `${i.path}: ${i.message}`).join("; ");
|
|
1212
|
-
}
|
|
1213
|
-
toJSON() {
|
|
1214
|
-
return {
|
|
1215
|
-
...super.toJSON(),
|
|
1216
|
-
target: this.target,
|
|
1217
|
-
issues: this.issues,
|
|
1218
|
-
validationIssues: this.validationIssues
|
|
1219
|
-
};
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
class RequestTimeoutError extends ApiError {
|
|
1224
|
-
timeoutMs;
|
|
1225
|
-
constructor(timeoutMs, context) {
|
|
1226
|
-
super(`Request timed out after ${timeoutMs}ms: ${context.method} ${context.url}`, context);
|
|
1227
|
-
this.name = "RequestTimeoutError";
|
|
1228
|
-
this.timeoutMs = timeoutMs;
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
class NetworkError extends ApiError {
|
|
1233
|
-
constructor(message, context, cause) {
|
|
1234
|
-
super(message, context, { cause });
|
|
1235
|
-
this.name = "NetworkError";
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
class RequestAbortedError extends ApiError {
|
|
1240
|
-
constructor(context, cause) {
|
|
1241
|
-
super(`Request aborted: ${context.method} ${context.url}`, context, {
|
|
1242
|
-
cause
|
|
1243
|
-
});
|
|
1244
|
-
this.name = "RequestAbortedError";
|
|
1245
|
-
}
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
class ConfigError extends ApiError {
|
|
1249
|
-
constructor(message, context) {
|
|
1250
|
-
super(message, context);
|
|
1251
|
-
this.name = "ConfigError";
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
class ActionError extends ApiError {
|
|
1256
|
-
constructor(message, context, cause) {
|
|
1257
|
-
super(message, context, { cause });
|
|
1258
|
-
this.name = "ActionError";
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
var isApiError = (e) => e instanceof ApiError;
|
|
1262
|
-
var isHttpResponseError = (e) => e instanceof HttpResponseError;
|
|
1263
|
-
var isResponseValidationError = (e) => e instanceof ResponseValidationError;
|
|
1264
|
-
var isRequestValidationError = (e) => e instanceof RequestValidationError;
|
|
1265
|
-
var isRequestTimeoutError = (e) => e instanceof RequestTimeoutError;
|
|
1266
|
-
var isNetworkError = (e) => e instanceof NetworkError;
|
|
1267
|
-
var isRequestAbortedError = (e) => e instanceof RequestAbortedError;
|
|
1268
|
-
var isConfigError = (e) => e instanceof ConfigError;
|
|
1269
|
-
var isActionError = (e) => e instanceof ActionError;
|
|
1270
|
-
function isNonRetryableApiError(error) {
|
|
1271
|
-
return error instanceof HttpResponseError || error instanceof ResponseValidationError || error instanceof RequestValidationError || error instanceof ConfigError || error instanceof RequestAbortedError;
|
|
1272
|
-
}
|
|
1273
|
-
// src/api-wrapper/builders.ts
|
|
1274
|
-
import { ZodError as ZodError2 } from "zod";
|
|
1275
|
-
function formatQueryString(queryParams, doNotEncode) {
|
|
1276
|
-
if (Object.keys(queryParams).length === 0)
|
|
1277
|
-
return "";
|
|
1278
|
-
return doNotEncode ? Object.entries(queryParams).map(([key, value]) => `${key}=${value}`).join("&") : new URLSearchParams(queryParams).toString();
|
|
1279
|
-
}
|
|
1280
|
-
function buildUrl(options) {
|
|
1281
|
-
const {
|
|
1282
|
-
endpoint,
|
|
1283
|
-
params,
|
|
1284
|
-
queryParams,
|
|
1285
|
-
baseApiUrls,
|
|
1286
|
-
defaultBaseApiUrl,
|
|
1287
|
-
url,
|
|
1288
|
-
doNotEncodeQueryParams,
|
|
1289
|
-
configError
|
|
1290
|
-
} = options;
|
|
1291
|
-
let finalUrl;
|
|
1292
|
-
if (url) {
|
|
1293
|
-
finalUrl = replacePathParams(url, params, configError);
|
|
1294
|
-
} else {
|
|
1295
|
-
const defaultBaseUrl = defaultBaseApiUrl ?? Object.keys(baseApiUrls)[0];
|
|
1296
|
-
if (!defaultBaseUrl) {
|
|
1297
|
-
throw configError("At least one base API URL is required");
|
|
1298
|
-
}
|
|
1299
|
-
const baseUrlKey = endpoint.baseApiUrl ?? defaultBaseUrl;
|
|
1300
|
-
const configBaseUrl = baseApiUrls[baseUrlKey];
|
|
1301
|
-
if (!configBaseUrl) {
|
|
1302
|
-
throw configError(`Unknown base API URL: ${baseUrlKey}`);
|
|
1303
|
-
}
|
|
1304
|
-
const baseUrl = typeof configBaseUrl === "function" ? configBaseUrl() : configBaseUrl;
|
|
1305
|
-
const path = typeof endpoint.path === "function" ? endpoint.path(baseUrl) : endpoint.path;
|
|
1306
|
-
finalUrl = `${baseUrl}${replacePathParams(path, params, configError)}`;
|
|
1307
|
-
}
|
|
1308
|
-
const queryString = formatQueryString(queryParams, doNotEncodeQueryParams);
|
|
1309
|
-
return {
|
|
1310
|
-
finalUrl,
|
|
1311
|
-
fullUrl: queryString ? `${finalUrl}?${queryString}` : finalUrl
|
|
1312
|
-
};
|
|
1313
|
-
}
|
|
1314
|
-
function buildPathParams(endpoint, params, ctx) {
|
|
1315
|
-
if (!endpoint.reqParamsSchema)
|
|
1316
|
-
return params;
|
|
1317
|
-
try {
|
|
1318
|
-
return endpoint.reqParamsSchema.parse(params);
|
|
1319
|
-
} catch (error) {
|
|
1320
|
-
if (error instanceof ZodError2) {
|
|
1321
|
-
throw new RequestValidationError(error, "params", params, ctx());
|
|
1322
|
-
}
|
|
1323
|
-
throw error;
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
function buildQueryParams(endpoint, query, ctx) {
|
|
1327
|
-
const defaultParams = endpoint.reqDefaultQueryParams ?? {};
|
|
1328
|
-
let parsedQuery;
|
|
1329
|
-
if (endpoint.reqQuerySchema) {
|
|
1330
|
-
try {
|
|
1331
|
-
parsedQuery = endpoint.reqQuerySchema.parse(query);
|
|
1332
|
-
} catch (error) {
|
|
1333
|
-
if (error instanceof ZodError2) {
|
|
1334
|
-
throw new RequestValidationError(error, "query", query, ctx());
|
|
1335
|
-
}
|
|
1336
|
-
throw error;
|
|
1337
|
-
}
|
|
1338
|
-
} else {
|
|
1339
|
-
parsedQuery = query ?? {};
|
|
1340
|
-
}
|
|
1341
|
-
const merged = { ...defaultParams, ...parsedQuery };
|
|
1342
|
-
return Object.fromEntries(Object.entries(merged).filter(([, value]) => value !== undefined).map(([key, value]) => [key, String(value)]));
|
|
1343
|
-
}
|
|
1344
|
-
function buildRequestBody(endpoint, body, ctx) {
|
|
1345
|
-
if (!endpoint.reqBodySchema)
|
|
1346
|
-
return body;
|
|
1347
|
-
let parsedBody;
|
|
1348
|
-
try {
|
|
1349
|
-
parsedBody = endpoint.reqBodySchema.parse(body);
|
|
1350
|
-
} catch (error) {
|
|
1351
|
-
if (error instanceof ZodError2) {
|
|
1352
|
-
throw new RequestValidationError(error, "body", body, ctx());
|
|
1353
|
-
}
|
|
1354
|
-
throw error;
|
|
1355
|
-
}
|
|
1356
|
-
if (endpoint.reqBodyFormat !== "form-data") {
|
|
1357
|
-
return parsedBody;
|
|
1358
|
-
}
|
|
1359
|
-
const formData = new FormData;
|
|
1360
|
-
for (const [key, value] of Object.entries(parsedBody)) {
|
|
1361
|
-
if (value == null)
|
|
1362
|
-
continue;
|
|
1363
|
-
formData.set(key, value instanceof Blob ? value : String(value));
|
|
1364
|
-
}
|
|
1365
|
-
return formData;
|
|
1366
|
-
}
|
|
1367
|
-
function buildHeaders(endpoint, headers, defaultHeaders, ctx) {
|
|
1368
|
-
const contentType = endpoint.reqBodyFormat === "json" ? "application/json" : undefined;
|
|
1369
|
-
let parsedHeaders;
|
|
1370
|
-
if (endpoint.reqHeadersSchema) {
|
|
1371
|
-
try {
|
|
1372
|
-
parsedHeaders = endpoint.reqHeadersSchema.parse(headers);
|
|
1373
|
-
} catch (error) {
|
|
1374
|
-
if (error instanceof ZodError2) {
|
|
1375
|
-
throw new RequestValidationError(error, "headers", headers, ctx());
|
|
1376
|
-
}
|
|
1377
|
-
throw error;
|
|
1378
|
-
}
|
|
1379
|
-
} else {
|
|
1380
|
-
parsedHeaders = headers ?? {};
|
|
1381
|
-
}
|
|
1382
|
-
return normalizeHeaders({
|
|
1383
|
-
...defaultHeaders,
|
|
1384
|
-
...contentType && { "Content-Type": contentType },
|
|
1385
|
-
...endpoint.defaultHeaders,
|
|
1386
|
-
...parsedHeaders
|
|
1387
|
-
});
|
|
1388
|
-
}
|
|
1389
|
-
function replacePathParams(path, params, configError) {
|
|
1390
|
-
return path.replace(/:(\w+)/g, (_, key) => {
|
|
1391
|
-
const value = params?.[key];
|
|
1392
|
-
if (value === undefined) {
|
|
1393
|
-
throw configError(`Missing required path parameter: ${key}`);
|
|
1394
|
-
}
|
|
1395
|
-
return encodeURIComponent(String(value));
|
|
1396
|
-
});
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
// src/api-wrapper/actions-impl.ts
|
|
1400
|
-
function createActionCache() {
|
|
1401
|
-
const store = new Map;
|
|
1402
|
-
const isExpired = (entry) => entry.expiresAt !== undefined && entry.expiresAt <= Date.now();
|
|
1403
|
-
const cache = async (key, fn, options) => {
|
|
1404
|
-
const existing = store.get(key);
|
|
1405
|
-
if (existing && !isExpired(existing)) {
|
|
1406
|
-
if (existing.inflight)
|
|
1407
|
-
return existing.inflight;
|
|
1408
|
-
if (existing.hasValue)
|
|
1409
|
-
return existing.value;
|
|
1410
|
-
}
|
|
1411
|
-
const promise = (async () => fn())();
|
|
1412
|
-
const entry = {
|
|
1413
|
-
hasValue: false,
|
|
1414
|
-
inflight: promise,
|
|
1415
|
-
expiresAt: options?.ttlMs !== undefined ? Date.now() + options.ttlMs : undefined
|
|
1416
|
-
};
|
|
1417
|
-
store.set(key, entry);
|
|
1418
|
-
try {
|
|
1419
|
-
const value = await promise;
|
|
1420
|
-
entry.value = value;
|
|
1421
|
-
entry.hasValue = true;
|
|
1422
|
-
entry.inflight = undefined;
|
|
1423
|
-
return value;
|
|
1424
|
-
} catch (error) {
|
|
1425
|
-
store.delete(key);
|
|
1426
|
-
throw error;
|
|
1427
|
-
}
|
|
1428
|
-
};
|
|
1429
|
-
cache.get = (key) => {
|
|
1430
|
-
const entry = store.get(key);
|
|
1431
|
-
if (!entry || !entry.hasValue)
|
|
1432
|
-
return;
|
|
1433
|
-
if (isExpired(entry)) {
|
|
1434
|
-
store.delete(key);
|
|
1435
|
-
return;
|
|
1436
|
-
}
|
|
1437
|
-
return entry.value;
|
|
1438
|
-
};
|
|
1439
|
-
cache.set = (key, value, options) => {
|
|
1440
|
-
store.set(key, {
|
|
1441
|
-
value,
|
|
1442
|
-
hasValue: true,
|
|
1443
|
-
expiresAt: options?.ttlMs !== undefined ? Date.now() + options.ttlMs : undefined
|
|
1444
|
-
});
|
|
1445
|
-
};
|
|
1446
|
-
cache.invalidate = (key) => {
|
|
1447
|
-
store.delete(key);
|
|
1448
|
-
};
|
|
1449
|
-
cache.clear = () => {
|
|
1450
|
-
store.clear();
|
|
1451
|
-
};
|
|
1452
|
-
return cache;
|
|
1453
|
-
}
|
|
1454
|
-
function makeDefineAction() {
|
|
1455
|
-
function defineAction(...args) {
|
|
1456
|
-
if (args.length === 0) {
|
|
1457
|
-
return (def) => ({
|
|
1458
|
-
...def,
|
|
1459
|
-
noRuntimeInput: false
|
|
1460
|
-
});
|
|
1461
|
-
}
|
|
1462
|
-
const def = args[0];
|
|
1463
|
-
return {
|
|
1464
|
-
...def,
|
|
1465
|
-
noRuntimeInput: true
|
|
1466
|
-
};
|
|
1467
|
-
}
|
|
1468
|
-
return defineAction;
|
|
1469
|
-
}
|
|
1470
|
-
var defineAction = makeDefineAction();
|
|
1471
|
-
|
|
1472
|
-
// src/api-wrapper/redaction.ts
|
|
1473
|
-
var SENSITIVE_HEADER_KEYS = new Set([
|
|
1474
|
-
"authorization",
|
|
1475
|
-
"cookie",
|
|
1476
|
-
"set-cookie",
|
|
1477
|
-
"x-api-key",
|
|
1478
|
-
"x-auth-token"
|
|
1479
|
-
]);
|
|
1480
|
-
var SENSITIVE_BODY_KEYS = new Set([
|
|
1481
|
-
"password",
|
|
1482
|
-
"token",
|
|
1483
|
-
"accesstoken",
|
|
1484
|
-
"access_token",
|
|
1485
|
-
"refreshtoken",
|
|
1486
|
-
"refresh_token",
|
|
1487
|
-
"secret",
|
|
1488
|
-
"client_secret",
|
|
1489
|
-
"clientsecret",
|
|
1490
|
-
"apikey",
|
|
1491
|
-
"api_key"
|
|
1492
|
-
]);
|
|
1493
|
-
function redactHeaders(headers) {
|
|
1494
|
-
if (!headers)
|
|
1495
|
-
return;
|
|
1496
|
-
const out = {};
|
|
1497
|
-
for (const [k, v] of Object.entries(headers)) {
|
|
1498
|
-
if (SENSITIVE_HEADER_KEYS.has(k.toLowerCase())) {
|
|
1499
|
-
out[k] = "[REDACTED]";
|
|
1500
|
-
} else {
|
|
1501
|
-
out[k] = v;
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
return out;
|
|
1505
|
-
}
|
|
1506
|
-
function redactBody(body) {
|
|
1507
|
-
if (body === null || body === undefined)
|
|
1508
|
-
return body;
|
|
1509
|
-
if (typeof body !== "object" || Array.isArray(body)) {
|
|
1510
|
-
return body;
|
|
1511
|
-
}
|
|
1512
|
-
const rec = body;
|
|
1513
|
-
const out = { ...rec };
|
|
1514
|
-
for (const key of Object.keys(out)) {
|
|
1515
|
-
if (SENSITIVE_BODY_KEYS.has(key.toLowerCase())) {
|
|
1516
|
-
out[key] = "[REDACTED]";
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
return out;
|
|
1520
|
-
}
|
|
1521
|
-
function defaultRedactContext(ctx) {
|
|
1522
|
-
return {
|
|
1523
|
-
...ctx,
|
|
1524
|
-
requestHeaders: redactHeaders(ctx.requestHeaders),
|
|
1525
|
-
requestBody: redactBody(ctx.requestBody),
|
|
1526
|
-
meta: ctx.meta
|
|
1527
|
-
};
|
|
1528
|
-
}
|
|
1529
|
-
|
|
1530
|
-
// src/api-wrapper/error-reporting.ts
|
|
1531
|
-
function applyErrorContextRedaction(ctx, config) {
|
|
1532
|
-
const base = defaultRedactContext(ctx);
|
|
1533
|
-
return config.redact ? config.redact(base) : base;
|
|
1534
|
-
}
|
|
1535
|
-
async function reportApiError(err, config) {
|
|
1536
|
-
err.context = applyErrorContextRedaction(err.context, config);
|
|
1537
|
-
try {
|
|
1538
|
-
await config.onError?.(err);
|
|
1539
|
-
} catch {}
|
|
1540
|
-
config.logger?.error?.(err);
|
|
1541
|
-
return err;
|
|
1542
|
-
}
|
|
1543
|
-
|
|
1544
|
-
// src/api-wrapper/size.ts
|
|
1545
|
-
function calculateSizeInMb(data) {
|
|
1546
|
-
let bytes;
|
|
1547
|
-
if (data === undefined || data === null) {
|
|
1548
|
-
bytes = 0;
|
|
1549
|
-
} else if (data instanceof Blob) {
|
|
1550
|
-
bytes = data.size;
|
|
1551
|
-
} else if (typeof FormData !== "undefined" && data instanceof FormData) {
|
|
1552
|
-
bytes = 0;
|
|
1553
|
-
data.forEach((value) => {
|
|
1554
|
-
if (value instanceof Blob)
|
|
1555
|
-
bytes += value.size;
|
|
1556
|
-
else
|
|
1557
|
-
bytes += new TextEncoder().encode(String(value)).length;
|
|
1558
|
-
});
|
|
1559
|
-
} else if (typeof data === "string") {
|
|
1560
|
-
bytes = new TextEncoder().encode(data).length;
|
|
1561
|
-
} else {
|
|
1562
|
-
bytes = new TextEncoder().encode(JSON.stringify(data) ?? "").length;
|
|
1563
|
-
}
|
|
1564
|
-
return bytes / (1024 * 1024);
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
|
-
// src/api-wrapper/transport.ts
|
|
1568
|
-
import { ZodError as ZodError3 } from "zod";
|
|
1569
|
-
async function validateResponse(schema, data, ctx) {
|
|
1570
|
-
try {
|
|
1571
|
-
return await schema.parseAsync(data);
|
|
1572
|
-
} catch (error) {
|
|
1573
|
-
if (error instanceof ZodError3) {
|
|
1574
|
-
throw new ResponseValidationError(error, data, ctx());
|
|
1575
|
-
}
|
|
1576
|
-
throw error;
|
|
1577
|
-
}
|
|
1578
|
-
}
|
|
1579
|
-
async function httpRequest(config, ctx) {
|
|
1580
|
-
const timeoutController = new AbortController;
|
|
1581
|
-
const timeout = config.timeoutMs === undefined ? undefined : setTimeout(() => timeoutController.abort(), config.timeoutMs);
|
|
1582
|
-
const signal = mergeSignals(timeoutController.signal, config.userSignal);
|
|
1583
|
-
try {
|
|
1584
|
-
return await config.fetchFn(config.fullUrl, {
|
|
1585
|
-
method: config.method,
|
|
1586
|
-
headers: config.headers,
|
|
1587
|
-
body: buildFetchBody(config.body, config.headers, (msg) => new ConfigError(msg, ctx())),
|
|
1588
|
-
signal
|
|
1589
|
-
});
|
|
1590
|
-
} catch (error) {
|
|
1591
|
-
if (isDomAbortError(error)) {
|
|
1592
|
-
if (config.userSignal?.aborted) {
|
|
1593
|
-
throw new RequestAbortedError(ctx(), error);
|
|
1594
|
-
}
|
|
1595
|
-
if (timeoutController.signal.aborted && config.timeoutMs !== undefined) {
|
|
1596
|
-
throw new RequestTimeoutError(config.timeoutMs, ctx());
|
|
1597
|
-
}
|
|
1598
|
-
throw new RequestAbortedError(ctx(), error);
|
|
1599
|
-
}
|
|
1600
|
-
if (error instanceof ConfigError)
|
|
1601
|
-
throw error;
|
|
1602
|
-
throw new NetworkError(error instanceof Error ? error.message : String(error), ctx(), error);
|
|
1603
|
-
} finally {
|
|
1604
|
-
if (timeout)
|
|
1605
|
-
clearTimeout(timeout);
|
|
1606
|
-
}
|
|
1607
|
-
}
|
|
1608
|
-
function isDomAbortError(error) {
|
|
1609
|
-
return typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
|
|
1610
|
-
}
|
|
1611
|
-
function mergeSignals(a, b) {
|
|
1612
|
-
if (!b)
|
|
1613
|
-
return a;
|
|
1614
|
-
const anyFn = AbortSignal.any;
|
|
1615
|
-
if (typeof anyFn === "function")
|
|
1616
|
-
return anyFn([a, b]);
|
|
1617
|
-
const controller = new AbortController;
|
|
1618
|
-
const onAbort = () => controller.abort();
|
|
1619
|
-
if (a.aborted)
|
|
1620
|
-
controller.abort();
|
|
1621
|
-
else
|
|
1622
|
-
a.addEventListener("abort", onAbort, { once: true });
|
|
1623
|
-
if (b.aborted)
|
|
1624
|
-
controller.abort();
|
|
1625
|
-
else
|
|
1626
|
-
b.addEventListener("abort", onAbort, { once: true });
|
|
1627
|
-
return controller.signal;
|
|
1628
|
-
}
|
|
1629
|
-
async function readErrorResponse(response) {
|
|
1630
|
-
const body = await response.text();
|
|
1631
|
-
return {
|
|
1632
|
-
body,
|
|
1633
|
-
headers: getHeadersAsObject(response.headers),
|
|
1634
|
-
status: response.status
|
|
1635
|
-
};
|
|
1636
|
-
}
|
|
1637
|
-
function getRetryDelay(params) {
|
|
1638
|
-
const maxDelay = params.maxRetryDelayMs ?? 30000;
|
|
1639
|
-
const { retryDelay, attempt, response } = params;
|
|
1640
|
-
if (response && (response.status === 429 || response.status === 503)) {
|
|
1641
|
-
const retryAfter = response.headers.get("retry-after");
|
|
1642
|
-
if (retryAfter) {
|
|
1643
|
-
const seconds = Number(retryAfter);
|
|
1644
|
-
if (Number.isFinite(seconds)) {
|
|
1645
|
-
return Math.min(Math.max(0, seconds * 1000), maxDelay);
|
|
1646
|
-
}
|
|
1647
|
-
const dateMs = Date.parse(retryAfter);
|
|
1648
|
-
if (!Number.isNaN(dateMs)) {
|
|
1649
|
-
return Math.min(Math.max(0, dateMs - Date.now()), maxDelay);
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
const delayMs = typeof retryDelay === "function" ? retryDelay(attempt) : retryDelay;
|
|
1654
|
-
return Math.min(delayMs, maxDelay);
|
|
1655
|
-
}
|
|
1656
|
-
function shouldAttemptRetryOnError(error) {
|
|
1657
|
-
return !isNonRetryableApiError(error);
|
|
1658
|
-
}
|
|
1659
|
-
function shouldAttemptRetryOnHttp(retryCtx) {
|
|
1660
|
-
const { error, response } = retryCtx;
|
|
1661
|
-
if (error)
|
|
1662
|
-
return shouldAttemptRetryOnError(error);
|
|
1663
|
-
return response ? response.status >= 500 || response.status === 429 : false;
|
|
1664
|
-
}
|
|
1665
|
-
async function makeRequestWithRetry(args) {
|
|
1666
|
-
const {
|
|
1667
|
-
config,
|
|
1668
|
-
maxRetries,
|
|
1669
|
-
retryDelay,
|
|
1670
|
-
maxRetryDelayMs,
|
|
1671
|
-
validateFn,
|
|
1672
|
-
shouldRetry,
|
|
1673
|
-
logger,
|
|
1674
|
-
ctx,
|
|
1675
|
-
endpointName,
|
|
1676
|
-
method,
|
|
1677
|
-
fullUrl,
|
|
1678
|
-
meta,
|
|
1679
|
-
onResponse,
|
|
1680
|
-
onRetry
|
|
1681
|
-
} = args;
|
|
1682
|
-
let retryCount = 0;
|
|
1683
|
-
while (true) {
|
|
1684
|
-
let response;
|
|
1685
|
-
try {
|
|
1686
|
-
logger?.debug?.("API request", config);
|
|
1687
|
-
response = await httpRequest(config, (over) => ctx({ attempt: retryCount, ...over }));
|
|
1688
|
-
await onResponse?.({
|
|
1689
|
-
endpoint: endpointName,
|
|
1690
|
-
method,
|
|
1691
|
-
url: fullUrl,
|
|
1692
|
-
response: response.clone(),
|
|
1693
|
-
attempt: retryCount,
|
|
1694
|
-
meta
|
|
1695
|
-
});
|
|
1696
|
-
if (response.status >= 400) {
|
|
1697
|
-
const retryCtx = {
|
|
1698
|
-
response,
|
|
1699
|
-
retryCount,
|
|
1700
|
-
maxRetries,
|
|
1701
|
-
endpoint: endpointName,
|
|
1702
|
-
method,
|
|
1703
|
-
url: fullUrl
|
|
1704
|
-
};
|
|
1705
|
-
if (retryCount < maxRetries && shouldRetry(retryCtx)) {
|
|
1706
|
-
const delayMs = getRetryDelay({
|
|
1707
|
-
retryDelay,
|
|
1708
|
-
attempt: retryCount,
|
|
1709
|
-
response,
|
|
1710
|
-
maxRetryDelayMs
|
|
1711
|
-
});
|
|
1712
|
-
await onRetry?.({
|
|
1713
|
-
...retryCtx,
|
|
1714
|
-
delayMs,
|
|
1715
|
-
nextAttempt: retryCount + 1
|
|
1716
|
-
});
|
|
1717
|
-
retryCount++;
|
|
1718
|
-
await delay(delayMs);
|
|
1719
|
-
continue;
|
|
1720
|
-
}
|
|
1721
|
-
const { body, headers: responseHeaders } = await readErrorResponse(response);
|
|
1722
|
-
throw new HttpResponseError(response.status, body, responseHeaders, ctx({ attempt: retryCount }));
|
|
1723
|
-
}
|
|
1724
|
-
return {
|
|
1725
|
-
validatedData: await validateFn(response, retryCount),
|
|
1726
|
-
retryCount,
|
|
1727
|
-
httpStatus: response.status
|
|
1728
|
-
};
|
|
1729
|
-
} catch (error) {
|
|
1730
|
-
if (isNonRetryableApiError(error)) {
|
|
1731
|
-
throw error;
|
|
1732
|
-
}
|
|
1733
|
-
const retryCtx = {
|
|
1734
|
-
error,
|
|
1735
|
-
retryCount,
|
|
1736
|
-
maxRetries,
|
|
1737
|
-
endpoint: endpointName,
|
|
1738
|
-
method,
|
|
1739
|
-
url: fullUrl
|
|
1740
|
-
};
|
|
1741
|
-
if (retryCount >= maxRetries || !shouldRetry(retryCtx)) {
|
|
1742
|
-
throw error;
|
|
1743
|
-
}
|
|
1744
|
-
const delayMs = getRetryDelay({
|
|
1745
|
-
retryDelay,
|
|
1746
|
-
attempt: retryCount,
|
|
1747
|
-
response: undefined,
|
|
1748
|
-
maxRetryDelayMs
|
|
1749
|
-
});
|
|
1750
|
-
await onRetry?.({
|
|
1751
|
-
...retryCtx,
|
|
1752
|
-
delayMs,
|
|
1753
|
-
nextAttempt: retryCount + 1
|
|
1754
|
-
});
|
|
1755
|
-
retryCount++;
|
|
1756
|
-
await delay(delayMs);
|
|
1757
|
-
}
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
async function validateAndTransformResponse(endpoint, response, ctx) {
|
|
1761
|
-
const responseHeaders = getHeadersAsObject(response.headers);
|
|
1762
|
-
const text = await response.text();
|
|
1763
|
-
const responseSizeBytes = new TextEncoder().encode(text).length;
|
|
1764
|
-
let responseData;
|
|
1765
|
-
if (hasJsonContentType(responseHeaders)) {
|
|
1766
|
-
try {
|
|
1767
|
-
responseData = text === "" ? undefined : JSON.parse(text);
|
|
1768
|
-
} catch (error) {
|
|
1769
|
-
throw new ResponseValidationError(makeJsonParseZodError(error, text), text, ctx());
|
|
1770
|
-
}
|
|
1771
|
-
} else {
|
|
1772
|
-
responseData = text;
|
|
1773
|
-
}
|
|
1774
|
-
const transformedData = endpoint.resFormatter ? endpoint.resFormatter(responseData, responseHeaders) : responseData;
|
|
1775
|
-
const data = await validateResponse(endpoint.resSchema, transformedData, ctx);
|
|
1776
|
-
return { data, responseSizeBytes };
|
|
1777
|
-
}
|
|
1778
|
-
function defaultShouldRetry(ctx) {
|
|
1779
|
-
if (ctx.error)
|
|
1780
|
-
return shouldAttemptRetryOnError(ctx.error);
|
|
1781
|
-
return shouldAttemptRetryOnHttp(ctx);
|
|
1782
|
-
}
|
|
1783
|
-
function delay(ms) {
|
|
1784
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1785
|
-
}
|
|
1786
|
-
|
|
1787
|
-
// src/api-wrapper/create-client.ts
|
|
1788
|
-
function isActionInvokeOptionsOnly(value) {
|
|
1789
|
-
if (value === null || value === undefined)
|
|
1790
|
-
return false;
|
|
1791
|
-
if (typeof value !== "object" || Array.isArray(value))
|
|
1792
|
-
return false;
|
|
1793
|
-
return Object.keys(value).every((k) => k === "signal" || k === "meta");
|
|
1794
|
-
}
|
|
1795
|
-
function resolveActionInvocation(action, actionName, first, second) {
|
|
1796
|
-
if (!action.noRuntimeInput) {
|
|
1797
|
-
return { input: first, options: second ?? {} };
|
|
1798
|
-
}
|
|
1799
|
-
if (first === undefined || first === null) {
|
|
1800
|
-
return { input: undefined, options: second ?? {} };
|
|
1801
|
-
}
|
|
1802
|
-
if (second !== undefined) {
|
|
1803
|
-
return { input: undefined, options: second };
|
|
1804
|
-
}
|
|
1805
|
-
if (isActionInvokeOptionsOnly(first)) {
|
|
1806
|
-
return { input: undefined, options: first };
|
|
1807
|
-
}
|
|
1808
|
-
throw new ConfigError(`Action "${actionName}" does not accept input; pass only optional { signal, meta }`, {
|
|
1809
|
-
endpoint: actionName,
|
|
1810
|
-
method: "GET",
|
|
1811
|
-
url: "",
|
|
1812
|
-
attempt: 0,
|
|
1813
|
-
maxRetries: 0,
|
|
1814
|
-
elapsedMs: 0
|
|
1815
|
-
});
|
|
1816
|
-
}
|
|
1817
|
-
function createApiClientImpl(apiConfig) {
|
|
1818
|
-
let actions = {};
|
|
1819
|
-
const cache = createActionCache();
|
|
1820
|
-
const invokeEndpoint = async (endpointKey, input = {}) => {
|
|
1821
|
-
const endpoint = apiConfig.endpoints[endpointKey];
|
|
1822
|
-
const rawInput = input;
|
|
1823
|
-
const endpointName = apiConfig.name ? `${apiConfig.name}.${String(endpointKey)}` : String(endpointKey);
|
|
1824
|
-
const startedAt = Date.now();
|
|
1825
|
-
const maxRetries = input.retries ?? apiConfig.retries ?? 3;
|
|
1826
|
-
const baseContext = {
|
|
1827
|
-
endpoint: endpointName,
|
|
1828
|
-
method: endpoint?.method ?? "GET",
|
|
1829
|
-
url: "",
|
|
1830
|
-
attempt: 0,
|
|
1831
|
-
maxRetries,
|
|
1832
|
-
elapsedMs: 0,
|
|
1833
|
-
meta: input.meta
|
|
1834
|
-
};
|
|
1835
|
-
const elapsed = () => Date.now() - startedAt;
|
|
1836
|
-
const ctx = (over = {}) => ({
|
|
1837
|
-
...baseContext,
|
|
1838
|
-
elapsedMs: elapsed(),
|
|
1839
|
-
...over
|
|
1840
|
-
});
|
|
1841
|
-
if (!endpoint) {
|
|
1842
|
-
throw await reportApiError(new ConfigError(`Unknown API endpoint: ${String(endpointKey)}`, ctx()), apiConfig);
|
|
1843
|
-
}
|
|
1844
|
-
baseContext.method = endpoint.method;
|
|
1845
|
-
let pathParams;
|
|
1846
|
-
let queryParams;
|
|
1847
|
-
let body;
|
|
1848
|
-
let headers;
|
|
1849
|
-
try {
|
|
1850
|
-
pathParams = buildPathParams(endpoint, rawInput.reqParams, ctx);
|
|
1851
|
-
queryParams = buildQueryParams(endpoint, rawInput.reqQuery, ctx);
|
|
1852
|
-
body = buildRequestBody(endpoint, rawInput.reqBody, ctx);
|
|
1853
|
-
headers = buildHeaders(endpoint, rawInput.reqHeaders, apiConfig.defaultHeaders ?? {}, ctx);
|
|
1854
|
-
} catch (e) {
|
|
1855
|
-
if (e instanceof ApiError)
|
|
1856
|
-
throw await reportApiError(e, apiConfig);
|
|
1857
|
-
throw e;
|
|
1858
|
-
}
|
|
1859
|
-
let fullUrl;
|
|
1860
|
-
try {
|
|
1861
|
-
const built = buildUrl({
|
|
1862
|
-
endpoint,
|
|
1863
|
-
params: pathParams,
|
|
1864
|
-
queryParams,
|
|
1865
|
-
baseApiUrls: apiConfig.baseApiUrls,
|
|
1866
|
-
defaultBaseApiUrl: apiConfig.defaultBaseApiUrl,
|
|
1867
|
-
url: input.url,
|
|
1868
|
-
doNotEncodeQueryParams: endpoint.doNotEncodeQueryParams,
|
|
1869
|
-
configError: (msg) => new ConfigError(msg, ctx())
|
|
1870
|
-
});
|
|
1871
|
-
fullUrl = built.fullUrl;
|
|
1872
|
-
} catch (e) {
|
|
1873
|
-
if (e instanceof ApiError)
|
|
1874
|
-
throw await reportApiError(e, apiConfig);
|
|
1875
|
-
throw e;
|
|
1876
|
-
}
|
|
1877
|
-
baseContext.url = fullUrl;
|
|
1878
|
-
baseContext.requestHeaders = headers;
|
|
1879
|
-
baseContext.requestBody = body;
|
|
1880
|
-
if (apiConfig.onRequest) {
|
|
1881
|
-
try {
|
|
1882
|
-
const result = await apiConfig.onRequest({
|
|
1883
|
-
endpoint: endpointName,
|
|
1884
|
-
method: endpoint.method,
|
|
1885
|
-
url: fullUrl,
|
|
1886
|
-
headers,
|
|
1887
|
-
body,
|
|
1888
|
-
meta: input.meta
|
|
1889
|
-
});
|
|
1890
|
-
if (result?.headers)
|
|
1891
|
-
headers = result.headers;
|
|
1892
|
-
if (result && "body" in result)
|
|
1893
|
-
body = result.body;
|
|
1894
|
-
} catch (e) {
|
|
1895
|
-
if (e instanceof ApiError)
|
|
1896
|
-
throw await reportApiError(e, apiConfig);
|
|
1897
|
-
throw e;
|
|
1898
|
-
}
|
|
1899
|
-
}
|
|
1900
|
-
const config = {
|
|
1901
|
-
method: endpoint.method,
|
|
1902
|
-
fullUrl,
|
|
1903
|
-
body,
|
|
1904
|
-
headers,
|
|
1905
|
-
timeoutMs: input.timeoutMs ?? apiConfig.timeoutMs,
|
|
1906
|
-
fetchFn: input.fetch ?? apiConfig.fetch ?? fetch,
|
|
1907
|
-
userSignal: input.signal
|
|
1908
|
-
};
|
|
1909
|
-
try {
|
|
1910
|
-
const result = await makeRequestWithRetry({
|
|
1911
|
-
config,
|
|
1912
|
-
maxRetries,
|
|
1913
|
-
retryDelay: apiConfig.retryDelayMs ?? 300,
|
|
1914
|
-
maxRetryDelayMs: apiConfig.maxRetryDelayMs,
|
|
1915
|
-
validateFn: (response, attempt) => validateAndTransformResponse(endpoint, response, (over) => ctx({ attempt, ...over })),
|
|
1916
|
-
shouldRetry: apiConfig.shouldRetry ?? defaultShouldRetry,
|
|
1917
|
-
logger: apiConfig.logger,
|
|
1918
|
-
ctx,
|
|
1919
|
-
endpointName,
|
|
1920
|
-
method: endpoint.method,
|
|
1921
|
-
fullUrl,
|
|
1922
|
-
meta: input.meta,
|
|
1923
|
-
onResponse: apiConfig.onResponse,
|
|
1924
|
-
onRetry: apiConfig.onRetry
|
|
1925
|
-
});
|
|
1926
|
-
const responseTimeMs = elapsed();
|
|
1927
|
-
const responseSizeMb = result.validatedData.responseSizeBytes / (1024 * 1024);
|
|
1928
|
-
const requestBodySizeMb = calculateSizeInMb(config.body);
|
|
1929
|
-
return {
|
|
1930
|
-
data: result.validatedData.data,
|
|
1931
|
-
requestBodySizeMb,
|
|
1932
|
-
responseSizeMb,
|
|
1933
|
-
responseTimeMs,
|
|
1934
|
-
httpStatus: result.httpStatus,
|
|
1935
|
-
retryCount: result.retryCount
|
|
1936
|
-
};
|
|
1937
|
-
} catch (error) {
|
|
1938
|
-
if (error instanceof ApiError)
|
|
1939
|
-
throw await reportApiError(error, apiConfig);
|
|
1940
|
-
throw error;
|
|
1941
|
-
}
|
|
1942
|
-
};
|
|
1943
|
-
async function invokeAction(name, rawInput, options = {}) {
|
|
1944
|
-
const action = actions[name];
|
|
1945
|
-
const inputForHandler = action.noRuntimeInput ? undefined : rawInput;
|
|
1946
|
-
const actionName = apiConfig.name ? `${apiConfig.name}.${name}` : name;
|
|
1947
|
-
const startedAt = Date.now();
|
|
1948
|
-
const baseContext = {
|
|
1949
|
-
endpoint: actionName,
|
|
1950
|
-
method: "GET",
|
|
1951
|
-
url: "",
|
|
1952
|
-
attempt: 0,
|
|
1953
|
-
maxRetries: 0,
|
|
1954
|
-
elapsedMs: 0,
|
|
1955
|
-
meta: options.meta,
|
|
1956
|
-
requestBody: action.noRuntimeInput ? undefined : rawInput
|
|
1957
|
-
};
|
|
1958
|
-
const ctx = (over = {}) => ({
|
|
1959
|
-
...baseContext,
|
|
1960
|
-
elapsedMs: Date.now() - startedAt,
|
|
1961
|
-
...over
|
|
1962
|
-
});
|
|
1963
|
-
apiConfig.logger?.debug?.("Action start", { action: actionName });
|
|
1964
|
-
try {
|
|
1965
|
-
const result = await action.handler({
|
|
1966
|
-
input: inputForHandler,
|
|
1967
|
-
api: proxy,
|
|
1968
|
-
signal: options.signal,
|
|
1969
|
-
meta: options.meta,
|
|
1970
|
-
logger: apiConfig.logger,
|
|
1971
|
-
cache
|
|
1972
|
-
});
|
|
1973
|
-
apiConfig.logger?.debug?.("Action end", {
|
|
1974
|
-
action: actionName,
|
|
1975
|
-
elapsedMs: Date.now() - startedAt
|
|
1976
|
-
});
|
|
1977
|
-
return result;
|
|
1978
|
-
} catch (error) {
|
|
1979
|
-
if (error instanceof ApiError) {
|
|
1980
|
-
throw await reportApiError(error, apiConfig);
|
|
1981
|
-
}
|
|
1982
|
-
const wrapped = new ActionError(`Action ${actionName} failed: ${error instanceof Error ? error.message : String(error)}`, ctx(), error);
|
|
1983
|
-
throw await reportApiError(wrapped, apiConfig);
|
|
1984
|
-
}
|
|
1985
|
-
}
|
|
1986
|
-
async function dispatchAction(name, first, second) {
|
|
1987
|
-
const action = actions[name];
|
|
1988
|
-
const { input, options } = resolveActionInvocation(action, name, first, second);
|
|
1989
|
-
return invokeAction(name, input, options);
|
|
1990
|
-
}
|
|
1991
|
-
const request = async (key, input, options) => {
|
|
1992
|
-
if (key in actions) {
|
|
1993
|
-
return dispatchAction(key, input, options);
|
|
1994
|
-
}
|
|
1995
|
-
return invokeEndpoint(key, input ?? {});
|
|
1996
|
-
};
|
|
1997
|
-
const client = { request, cache };
|
|
1998
|
-
const proxy = new Proxy(client, {
|
|
1999
|
-
get(target, prop, receiver) {
|
|
2000
|
-
if (prop in target || typeof prop !== "string") {
|
|
2001
|
-
return Reflect.get(target, prop, receiver);
|
|
2002
|
-
}
|
|
2003
|
-
if (prop in apiConfig.endpoints) {
|
|
2004
|
-
return (input) => invokeEndpoint(prop, input ?? {});
|
|
2005
|
-
}
|
|
2006
|
-
if (prop in actions) {
|
|
2007
|
-
return (first, second) => dispatchAction(prop, first, second);
|
|
2008
|
-
}
|
|
2009
|
-
return;
|
|
2010
|
-
}
|
|
2011
|
-
});
|
|
2012
|
-
const rawActions = apiConfig.actions;
|
|
2013
|
-
if (typeof rawActions === "function") {
|
|
2014
|
-
actions = rawActions({
|
|
2015
|
-
api: proxy,
|
|
2016
|
-
defineAction: makeDefineAction()
|
|
2017
|
-
});
|
|
2018
|
-
} else if (rawActions) {
|
|
2019
|
-
actions = rawActions;
|
|
2020
|
-
}
|
|
2021
|
-
const collisions = Object.keys(actions).filter((key) => (key in apiConfig.endpoints));
|
|
2022
|
-
if (collisions.length > 0) {
|
|
2023
|
-
throw new ConfigError(`Action name(s) collide with endpoint name(s): ${collisions.join(", ")}`, {
|
|
2024
|
-
endpoint: collisions[0],
|
|
2025
|
-
method: "GET",
|
|
2026
|
-
url: "",
|
|
2027
|
-
attempt: 0,
|
|
2028
|
-
maxRetries: 0,
|
|
2029
|
-
elapsedMs: 0
|
|
2030
|
-
});
|
|
2031
|
-
}
|
|
2032
|
-
return proxy;
|
|
2033
|
-
}
|
|
2034
|
-
var createApiClient = (apiConfig) => createApiClientImpl(apiConfig);
|
|
2035
|
-
// src/p-queue/index.ts
|
|
2036
|
-
class TimeoutError extends Error {
|
|
2037
|
-
constructor(message) {
|
|
2038
|
-
super(message);
|
|
2039
|
-
this.name = "TimeoutError";
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2042
|
-
|
|
2043
|
-
class PriorityQueue {
|
|
2044
|
-
#queue = [];
|
|
2045
|
-
enqueue(run, options = {}) {
|
|
2046
|
-
const element = {
|
|
2047
|
-
priority: options.priority ?? 0,
|
|
2048
|
-
id: options.id,
|
|
2049
|
-
run
|
|
2050
|
-
};
|
|
2051
|
-
const index = this.#queue.findIndex((item) => item.priority < element.priority);
|
|
2052
|
-
if (index === -1) {
|
|
2053
|
-
this.#queue.push(element);
|
|
2054
|
-
return;
|
|
2055
|
-
}
|
|
2056
|
-
this.#queue.splice(index, 0, element);
|
|
2057
|
-
}
|
|
2058
|
-
dequeue() {
|
|
2059
|
-
return this.#queue.shift()?.run;
|
|
2060
|
-
}
|
|
2061
|
-
filter(options) {
|
|
2062
|
-
return this.#queue.filter((item) => Object.entries(options).every(([key, value]) => item[key] === value)).map((item) => item.run);
|
|
2063
|
-
}
|
|
2064
|
-
setPriority(id, priority) {
|
|
2065
|
-
const index = this.#queue.findIndex((item) => item.id === id);
|
|
2066
|
-
if (index === -1) {
|
|
2067
|
-
throw new ReferenceError(`No promise function with the id "${id}" exists in the queue.`);
|
|
2068
|
-
}
|
|
2069
|
-
const [item] = this.#queue.splice(index, 1);
|
|
2070
|
-
this.enqueue(item.run, { ...item, priority });
|
|
2071
|
-
}
|
|
2072
|
-
remove(id) {
|
|
2073
|
-
const index = this.#queue.findIndex((item) => item.id === id);
|
|
2074
|
-
if (index !== -1) {
|
|
2075
|
-
this.#queue.splice(index, 1);
|
|
2076
|
-
}
|
|
2077
|
-
}
|
|
2078
|
-
get size() {
|
|
2079
|
-
return this.#queue.length;
|
|
2080
|
-
}
|
|
2081
|
-
}
|
|
2082
|
-
|
|
2083
|
-
class PQueue {
|
|
2084
|
-
#listeners = new Map;
|
|
2085
|
-
#queue;
|
|
2086
|
-
#queueClass;
|
|
2087
|
-
#pending = 0;
|
|
2088
|
-
#concurrency;
|
|
2089
|
-
#isPaused;
|
|
2090
|
-
#intervalCap;
|
|
2091
|
-
#interval;
|
|
2092
|
-
#carryoverIntervalCount;
|
|
2093
|
-
#strict;
|
|
2094
|
-
#intervalCount = 0;
|
|
2095
|
-
#intervalEnd = 0;
|
|
2096
|
-
#intervalTimer;
|
|
2097
|
-
#strictTimestamps = [];
|
|
2098
|
-
#rateLimited = false;
|
|
2099
|
-
#idCounter = 1n;
|
|
2100
|
-
#runningTasks = new Map;
|
|
2101
|
-
timeout;
|
|
2102
|
-
constructor(options = {}) {
|
|
2103
|
-
const {
|
|
2104
|
-
concurrency = Number.POSITIVE_INFINITY,
|
|
2105
|
-
autoStart = true,
|
|
2106
|
-
queueClass = PriorityQueue,
|
|
2107
|
-
intervalCap = Number.POSITIVE_INFINITY,
|
|
2108
|
-
interval = 0,
|
|
2109
|
-
carryoverIntervalCount,
|
|
2110
|
-
carryoverConcurrencyCount,
|
|
2111
|
-
strict = false,
|
|
2112
|
-
timeout
|
|
2113
|
-
} = options;
|
|
2114
|
-
if (!(typeof concurrency === "number" && concurrency >= 1)) {
|
|
2115
|
-
throw new TypeError("Expected `concurrency` to be a number from 1 and up");
|
|
2116
|
-
}
|
|
2117
|
-
if (!(typeof intervalCap === "number" && intervalCap >= 1)) {
|
|
2118
|
-
throw new TypeError("Expected `intervalCap` to be a number from 1 and up");
|
|
2119
|
-
}
|
|
2120
|
-
if (!(typeof interval === "number" && Number.isFinite(interval) && interval >= 0)) {
|
|
2121
|
-
throw new TypeError("Expected `interval` to be a finite number >= 0");
|
|
2122
|
-
}
|
|
2123
|
-
if (strict && interval === 0) {
|
|
2124
|
-
throw new TypeError("The `strict` option requires a non-zero `interval`");
|
|
2125
|
-
}
|
|
2126
|
-
if (strict && intervalCap === Number.POSITIVE_INFINITY) {
|
|
2127
|
-
throw new TypeError("The `strict` option requires a finite `intervalCap`");
|
|
2128
|
-
}
|
|
2129
|
-
if (timeout !== undefined && !(Number.isFinite(timeout) && timeout > 0)) {
|
|
2130
|
-
throw new TypeError("Expected `timeout` to be a positive finite number");
|
|
2131
|
-
}
|
|
2132
|
-
this.#queueClass = queueClass;
|
|
2133
|
-
this.#queue = new queueClass;
|
|
2134
|
-
this.#concurrency = concurrency;
|
|
2135
|
-
this.#isPaused = !autoStart;
|
|
2136
|
-
this.#intervalCap = intervalCap;
|
|
2137
|
-
this.#interval = interval;
|
|
2138
|
-
this.#carryoverIntervalCount = carryoverIntervalCount ?? carryoverConcurrencyCount ?? false;
|
|
2139
|
-
this.#strict = strict;
|
|
2140
|
-
this.timeout = timeout;
|
|
2141
|
-
}
|
|
2142
|
-
on(event, listener) {
|
|
2143
|
-
const listeners = this.#listeners.get(event) ?? new Set;
|
|
2144
|
-
listeners.add(listener);
|
|
2145
|
-
this.#listeners.set(event, listeners);
|
|
2146
|
-
return this;
|
|
2147
|
-
}
|
|
2148
|
-
off(event, listener) {
|
|
2149
|
-
this.#listeners.get(event)?.delete(listener);
|
|
2150
|
-
return this;
|
|
2151
|
-
}
|
|
2152
|
-
emit(event, ...arguments_) {
|
|
2153
|
-
const listeners = this.#listeners.get(event);
|
|
2154
|
-
if (!listeners || listeners.size === 0) {
|
|
2155
|
-
return false;
|
|
2156
|
-
}
|
|
2157
|
-
for (const listener of [...listeners]) {
|
|
2158
|
-
listener(...arguments_);
|
|
2159
|
-
}
|
|
2160
|
-
return true;
|
|
2161
|
-
}
|
|
2162
|
-
get concurrency() {
|
|
2163
|
-
return this.#concurrency;
|
|
2164
|
-
}
|
|
2165
|
-
set concurrency(newConcurrency) {
|
|
2166
|
-
if (!(typeof newConcurrency === "number" && newConcurrency >= 1)) {
|
|
2167
|
-
throw new TypeError("Expected `concurrency` to be a number from 1 and up");
|
|
2168
|
-
}
|
|
2169
|
-
this.#concurrency = newConcurrency;
|
|
2170
|
-
this.#processQueue();
|
|
2171
|
-
}
|
|
2172
|
-
get size() {
|
|
2173
|
-
return this.#queue.size;
|
|
2174
|
-
}
|
|
2175
|
-
sizeBy(options) {
|
|
2176
|
-
return this.#queue.filter(options).length;
|
|
2177
|
-
}
|
|
2178
|
-
get pending() {
|
|
2179
|
-
return this.#pending;
|
|
2180
|
-
}
|
|
2181
|
-
get isPaused() {
|
|
2182
|
-
return this.#isPaused;
|
|
2183
|
-
}
|
|
2184
|
-
get isRateLimited() {
|
|
2185
|
-
this.#updateRateLimitState();
|
|
2186
|
-
return this.#rateLimited;
|
|
2187
|
-
}
|
|
2188
|
-
get isSaturated() {
|
|
2189
|
-
return this.#pending >= this.#concurrency && this.#queue.size > 0 || this.isRateLimited && this.#queue.size > 0;
|
|
2190
|
-
}
|
|
2191
|
-
get runningTasks() {
|
|
2192
|
-
return [...this.#runningTasks.values()].map((task) => ({ ...task }));
|
|
2193
|
-
}
|
|
2194
|
-
setPriority(id, priority) {
|
|
2195
|
-
if (typeof priority !== "number" || !Number.isFinite(priority)) {
|
|
2196
|
-
throw new TypeError("Expected `priority` to be a finite number");
|
|
2197
|
-
}
|
|
2198
|
-
this.#queue.setPriority(id, priority);
|
|
2199
|
-
}
|
|
2200
|
-
async add(function_, options = {}) {
|
|
2201
|
-
const taskOptions = {
|
|
2202
|
-
timeout: this.timeout,
|
|
2203
|
-
...options,
|
|
2204
|
-
id: options.id ?? (this.#idCounter++).toString()
|
|
2205
|
-
};
|
|
2206
|
-
return new Promise((resolve, reject) => {
|
|
2207
|
-
const taskSymbol = Symbol(taskOptions.id);
|
|
2208
|
-
let didRun = false;
|
|
2209
|
-
let queuedAbortHandler;
|
|
2210
|
-
let runningAbortHandler;
|
|
2211
|
-
const cleanupQueuedAbort = () => {
|
|
2212
|
-
if (queuedAbortHandler) {
|
|
2213
|
-
taskOptions.signal?.removeEventListener("abort", queuedAbortHandler);
|
|
2214
|
-
queuedAbortHandler = undefined;
|
|
2215
|
-
}
|
|
2216
|
-
};
|
|
2217
|
-
const run = async () => {
|
|
2218
|
-
didRun = true;
|
|
2219
|
-
cleanupQueuedAbort();
|
|
2220
|
-
this.#pending++;
|
|
2221
|
-
this.#runningTasks.set(taskSymbol, {
|
|
2222
|
-
id: taskOptions.id,
|
|
2223
|
-
priority: taskOptions.priority ?? 0,
|
|
2224
|
-
startTime: Date.now(),
|
|
2225
|
-
timeout: taskOptions.timeout
|
|
2226
|
-
});
|
|
2227
|
-
try {
|
|
2228
|
-
taskOptions.signal?.throwIfAborted();
|
|
2229
|
-
const operation = Promise.resolve(function_({ signal: taskOptions.signal }));
|
|
2230
|
-
const result = await this.#withTimeoutAndAbort(operation, taskOptions, (listener) => {
|
|
2231
|
-
runningAbortHandler = listener;
|
|
2232
|
-
});
|
|
2233
|
-
resolve(result);
|
|
2234
|
-
this.emit("completed", result);
|
|
2235
|
-
} catch (error) {
|
|
2236
|
-
reject(error);
|
|
2237
|
-
this.emit("error", error);
|
|
2238
|
-
} finally {
|
|
2239
|
-
if (runningAbortHandler) {
|
|
2240
|
-
taskOptions.signal?.removeEventListener("abort", runningAbortHandler);
|
|
2241
|
-
}
|
|
2242
|
-
this.#runningTasks.delete(taskSymbol);
|
|
2243
|
-
queueMicrotask(() => {
|
|
2244
|
-
this.#next();
|
|
2245
|
-
});
|
|
2246
|
-
}
|
|
2247
|
-
};
|
|
2248
|
-
if (taskOptions.signal?.aborted) {
|
|
2249
|
-
reject(taskOptions.signal.reason);
|
|
2250
|
-
return;
|
|
2251
|
-
}
|
|
2252
|
-
if (taskOptions.signal) {
|
|
2253
|
-
queuedAbortHandler = () => {
|
|
2254
|
-
if (didRun) {
|
|
2255
|
-
return;
|
|
2256
|
-
}
|
|
2257
|
-
this.#queue.remove?.(taskOptions.id);
|
|
2258
|
-
reject(taskOptions.signal?.reason);
|
|
2259
|
-
this.emit("next");
|
|
2260
|
-
this.#tryToStartAnother();
|
|
2261
|
-
};
|
|
2262
|
-
taskOptions.signal.addEventListener("abort", queuedAbortHandler, {
|
|
2263
|
-
once: true
|
|
2264
|
-
});
|
|
2265
|
-
}
|
|
2266
|
-
this.#queue.enqueue(run, taskOptions);
|
|
2267
|
-
this.emit("add");
|
|
2268
|
-
this.#processQueue();
|
|
2269
|
-
});
|
|
2270
|
-
}
|
|
2271
|
-
async addAll(functions, options) {
|
|
2272
|
-
return Promise.all(functions.map(async (function_) => this.add(function_, options)));
|
|
2273
|
-
}
|
|
2274
|
-
start() {
|
|
2275
|
-
if (!this.#isPaused) {
|
|
2276
|
-
return this;
|
|
2277
|
-
}
|
|
2278
|
-
this.#isPaused = false;
|
|
2279
|
-
this.#processQueue();
|
|
2280
|
-
return this;
|
|
2281
|
-
}
|
|
2282
|
-
pause() {
|
|
2283
|
-
this.#isPaused = true;
|
|
2284
|
-
}
|
|
2285
|
-
clear() {
|
|
2286
|
-
this.#queue = new this.#queueClass;
|
|
2287
|
-
this.#clearIntervalTimer();
|
|
2288
|
-
this.#updateRateLimitState();
|
|
2289
|
-
this.emit("empty");
|
|
2290
|
-
if (this.#pending === 0) {
|
|
2291
|
-
this.emit("idle");
|
|
2292
|
-
}
|
|
2293
|
-
this.emit("next");
|
|
2294
|
-
}
|
|
2295
|
-
async onEmpty() {
|
|
2296
|
-
if (this.#queue.size === 0) {
|
|
2297
|
-
return;
|
|
2298
|
-
}
|
|
2299
|
-
await this.#onEvent("empty");
|
|
2300
|
-
}
|
|
2301
|
-
async onIdle() {
|
|
2302
|
-
if (this.#queue.size === 0 && this.#pending === 0) {
|
|
2303
|
-
return;
|
|
2304
|
-
}
|
|
2305
|
-
await this.#onEvent("idle");
|
|
2306
|
-
}
|
|
2307
|
-
async onPendingZero() {
|
|
2308
|
-
if (this.#pending === 0) {
|
|
2309
|
-
return;
|
|
2310
|
-
}
|
|
2311
|
-
await this.#onEvent("pendingZero");
|
|
2312
|
-
}
|
|
2313
|
-
async onRateLimit() {
|
|
2314
|
-
if (this.isRateLimited) {
|
|
2315
|
-
return;
|
|
2316
|
-
}
|
|
2317
|
-
await this.#onEvent("rateLimit");
|
|
2318
|
-
}
|
|
2319
|
-
async onRateLimitCleared() {
|
|
2320
|
-
if (!this.isRateLimited) {
|
|
2321
|
-
return;
|
|
2322
|
-
}
|
|
2323
|
-
await this.#onEvent("rateLimitCleared");
|
|
2324
|
-
}
|
|
2325
|
-
onError() {
|
|
2326
|
-
return new Promise((_resolve, reject) => {
|
|
2327
|
-
const listener = (error) => {
|
|
2328
|
-
this.off("error", listener);
|
|
2329
|
-
reject(error);
|
|
2330
|
-
};
|
|
2331
|
-
this.on("error", listener);
|
|
2332
|
-
});
|
|
2333
|
-
}
|
|
2334
|
-
async onSizeLessThan(limit) {
|
|
2335
|
-
if (this.#queue.size < limit) {
|
|
2336
|
-
return;
|
|
2337
|
-
}
|
|
2338
|
-
await this.#onEvent("next", () => this.#queue.size < limit);
|
|
2339
|
-
}
|
|
2340
|
-
async#onEvent(event, filter) {
|
|
2341
|
-
return new Promise((resolve) => {
|
|
2342
|
-
const listener = () => {
|
|
2343
|
-
if (filter && !filter()) {
|
|
2344
|
-
return;
|
|
2345
|
-
}
|
|
2346
|
-
this.off(event, listener);
|
|
2347
|
-
resolve();
|
|
2348
|
-
};
|
|
2349
|
-
this.on(event, listener);
|
|
2350
|
-
});
|
|
2351
|
-
}
|
|
2352
|
-
async#withTimeoutAndAbort(operation, options, setAbortListener) {
|
|
2353
|
-
const races = [operation];
|
|
2354
|
-
let timeoutId;
|
|
2355
|
-
if (options.timeout) {
|
|
2356
|
-
races.push(new Promise((_resolve, reject) => {
|
|
2357
|
-
timeoutId = setTimeout(() => {
|
|
2358
|
-
reject(new TimeoutError(`Task timed out after ${options.timeout}ms`));
|
|
2359
|
-
}, options.timeout);
|
|
2360
|
-
}));
|
|
2361
|
-
}
|
|
2362
|
-
if (options.signal) {
|
|
2363
|
-
races.push(new Promise((_resolve, reject) => {
|
|
2364
|
-
const listener = () => {
|
|
2365
|
-
reject(options.signal?.reason);
|
|
2366
|
-
};
|
|
2367
|
-
setAbortListener(listener);
|
|
2368
|
-
options.signal?.addEventListener("abort", listener, { once: true });
|
|
2369
|
-
}));
|
|
2370
|
-
}
|
|
2371
|
-
try {
|
|
2372
|
-
return await Promise.race(races);
|
|
2373
|
-
} finally {
|
|
2374
|
-
if (timeoutId) {
|
|
2375
|
-
clearTimeout(timeoutId);
|
|
2376
|
-
}
|
|
2377
|
-
}
|
|
2378
|
-
}
|
|
2379
|
-
#next() {
|
|
2380
|
-
this.#pending--;
|
|
2381
|
-
if (this.#pending === 0) {
|
|
2382
|
-
this.emit("pendingZero");
|
|
2383
|
-
}
|
|
2384
|
-
this.#processQueue();
|
|
2385
|
-
this.emit("next");
|
|
2386
|
-
}
|
|
2387
|
-
#processQueue() {
|
|
2388
|
-
while (this.#tryToStartAnother()) {}
|
|
2389
|
-
this.#updateRateLimitState();
|
|
2390
|
-
}
|
|
2391
|
-
#tryToStartAnother() {
|
|
2392
|
-
if (this.#queue.size === 0) {
|
|
2393
|
-
this.#clearIntervalTimer();
|
|
2394
|
-
this.emit("empty");
|
|
2395
|
-
if (this.#pending === 0) {
|
|
2396
|
-
this.emit("idle");
|
|
2397
|
-
}
|
|
2398
|
-
return false;
|
|
2399
|
-
}
|
|
2400
|
-
if (this.#isPaused || this.#pending >= this.#concurrency || !this.#doesIntervalAllowAnother()) {
|
|
2401
|
-
return false;
|
|
2402
|
-
}
|
|
2403
|
-
const job = this.#queue.dequeue();
|
|
2404
|
-
if (!job) {
|
|
2405
|
-
return false;
|
|
2406
|
-
}
|
|
2407
|
-
this.#consumeIntervalSlot();
|
|
2408
|
-
this.emit("active");
|
|
2409
|
-
job();
|
|
2410
|
-
return true;
|
|
2411
|
-
}
|
|
2412
|
-
#doesIntervalAllowAnother() {
|
|
2413
|
-
if (this.#intervalCap === Number.POSITIVE_INFINITY || this.#interval === 0) {
|
|
2414
|
-
return true;
|
|
2415
|
-
}
|
|
2416
|
-
const now = Date.now();
|
|
2417
|
-
if (this.#strict) {
|
|
2418
|
-
this.#strictTimestamps = this.#strictTimestamps.filter((timestamp) => now - timestamp < this.#interval);
|
|
2419
|
-
if (this.#strictTimestamps.length < this.#intervalCap) {
|
|
2420
|
-
return true;
|
|
2421
|
-
}
|
|
2422
|
-
this.#scheduleIntervalResume(this.#interval - (now - this.#strictTimestamps[0]));
|
|
2423
|
-
return false;
|
|
2424
|
-
}
|
|
2425
|
-
if (this.#intervalEnd === 0 || now >= this.#intervalEnd) {
|
|
2426
|
-
this.#intervalCount = this.#carryoverIntervalCount ? this.#pending : 0;
|
|
2427
|
-
this.#intervalEnd = now + this.#interval;
|
|
2428
|
-
}
|
|
2429
|
-
if (this.#intervalCount < this.#intervalCap) {
|
|
2430
|
-
return true;
|
|
2431
|
-
}
|
|
2432
|
-
this.#scheduleIntervalResume(this.#intervalEnd - now);
|
|
2433
|
-
return false;
|
|
2434
|
-
}
|
|
2435
|
-
#consumeIntervalSlot() {
|
|
2436
|
-
if (this.#intervalCap === Number.POSITIVE_INFINITY || this.#interval === 0) {
|
|
2437
|
-
return;
|
|
2438
|
-
}
|
|
2439
|
-
if (this.#strict) {
|
|
2440
|
-
this.#strictTimestamps.push(Date.now());
|
|
2441
|
-
return;
|
|
2442
|
-
}
|
|
2443
|
-
this.#intervalCount++;
|
|
2444
|
-
}
|
|
2445
|
-
#scheduleIntervalResume(delay) {
|
|
2446
|
-
if (this.#intervalTimer) {
|
|
2447
|
-
return;
|
|
2448
|
-
}
|
|
2449
|
-
this.#intervalTimer = setTimeout(() => {
|
|
2450
|
-
this.#intervalTimer = undefined;
|
|
2451
|
-
this.#processQueue();
|
|
2452
|
-
}, Math.max(0, delay));
|
|
2453
|
-
}
|
|
2454
|
-
#clearIntervalTimer() {
|
|
2455
|
-
if (this.#intervalTimer) {
|
|
2456
|
-
clearTimeout(this.#intervalTimer);
|
|
2457
|
-
this.#intervalTimer = undefined;
|
|
2458
|
-
}
|
|
2459
|
-
}
|
|
2460
|
-
#updateRateLimitState() {
|
|
2461
|
-
const previous = this.#rateLimited;
|
|
2462
|
-
const isRateLimited = this.#queue.size > 0 && this.#intervalCap !== Number.POSITIVE_INFINITY && this.#interval > 0 && !this.#doesIntervalAllowAnother();
|
|
2463
|
-
this.#rateLimited = isRateLimited;
|
|
2464
|
-
if (previous !== isRateLimited) {
|
|
2465
|
-
this.emit(isRateLimited ? "rateLimit" : "rateLimitCleared");
|
|
2466
|
-
}
|
|
2467
|
-
}
|
|
2468
|
-
}
|
|
2469
|
-
// src/cool-console-log/index.ts
|
|
2470
|
-
var terminalColors = {
|
|
2471
|
-
reset: "\x1B[0m",
|
|
2472
|
-
bold: "\x1B[1m",
|
|
2473
|
-
dim: "\x1B[2m",
|
|
2474
|
-
italic: "\x1B[3m",
|
|
2475
|
-
underline: "\x1B[4m",
|
|
2476
|
-
black: "\x1B[30m",
|
|
2477
|
-
red: "\x1B[31m",
|
|
2478
|
-
green: "\x1B[32m",
|
|
2479
|
-
yellow: "\x1B[33m",
|
|
2480
|
-
blue: "\x1B[34m",
|
|
2481
|
-
magenta: "\x1B[35m",
|
|
2482
|
-
cyan: "\x1B[36m",
|
|
2483
|
-
white: "\x1B[37m",
|
|
2484
|
-
gray: "\x1B[90m",
|
|
2485
|
-
brightRed: "\x1B[91m",
|
|
2486
|
-
brightGreen: "\x1B[92m",
|
|
2487
|
-
brightYellow: "\x1B[93m",
|
|
2488
|
-
brightBlue: "\x1B[94m",
|
|
2489
|
-
brightMagenta: "\x1B[95m",
|
|
2490
|
-
brightCyan: "\x1B[96m",
|
|
2491
|
-
brightWhite: "\x1B[97m",
|
|
2492
|
-
bgBlack: "\x1B[40m",
|
|
2493
|
-
bgRed: "\x1B[41m",
|
|
2494
|
-
bgGreen: "\x1B[42m",
|
|
2495
|
-
bgYellow: "\x1B[43m",
|
|
2496
|
-
bgBlue: "\x1B[44m",
|
|
2497
|
-
bgMagenta: "\x1B[45m",
|
|
2498
|
-
bgCyan: "\x1B[46m",
|
|
2499
|
-
bgWhite: "\x1B[47m"
|
|
2500
|
-
};
|
|
2501
|
-
var levelColors = {
|
|
2502
|
-
debug: { badge: terminalColors.gray, text: terminalColors.dim },
|
|
2503
|
-
info: { badge: terminalColors.cyan, text: terminalColors.reset },
|
|
2504
|
-
warn: { badge: terminalColors.yellow, text: terminalColors.yellow },
|
|
2505
|
-
error: { badge: terminalColors.red, text: terminalColors.brightRed }
|
|
2506
|
-
};
|
|
2507
|
-
function formatAttributes(attrs) {
|
|
2508
|
-
if (!attrs || Object.keys(attrs).length === 0)
|
|
2509
|
-
return "";
|
|
2510
|
-
const formatted = Object.entries(attrs).map(([k, v]) => `${terminalColors.dim}${k}=${terminalColors.reset}${v}`).join(" ");
|
|
2511
|
-
return ` ${formatted}`;
|
|
2512
|
-
}
|
|
2513
|
-
function createCoolLogger(options = {}) {
|
|
2514
|
-
const { colorsInDevOnly = true, onLog } = options;
|
|
2515
|
-
const isDev = true;
|
|
2516
|
-
const useColors = colorsInDevOnly ? isDev : true;
|
|
2517
|
-
const createLogMethod = (level) => (message, attributes) => {
|
|
2518
|
-
onLog?.(level, message, attributes);
|
|
2519
|
-
if (level === "debug" && !isDev)
|
|
2520
|
-
return;
|
|
2521
|
-
if (useColors) {
|
|
2522
|
-
const { badge, text } = levelColors[level];
|
|
2523
|
-
const label = level.toUpperCase().padEnd(5);
|
|
2524
|
-
const attrStr = formatAttributes(attributes);
|
|
2525
|
-
console.log(`${badge}[${label}]${terminalColors.reset} ${text}${message}${terminalColors.reset}${attrStr}`);
|
|
2526
|
-
} else {
|
|
2527
|
-
console[level](`[${level.toUpperCase()}]`, message, attributes ?? "");
|
|
2528
|
-
}
|
|
2529
|
-
};
|
|
2530
|
-
return {
|
|
2531
|
-
debug: createLogMethod("debug"),
|
|
2532
|
-
info: createLogMethod("info"),
|
|
2533
|
-
warn: createLogMethod("warn"),
|
|
2534
|
-
error: createLogMethod("error")
|
|
2535
|
-
};
|
|
2536
|
-
}
|
|
2537
|
-
function logOperationSummary(operationName, durationMs, success, options = {}) {
|
|
2538
|
-
const { devOnly = true, error } = options;
|
|
2539
|
-
const isDev = true;
|
|
2540
|
-
if (devOnly && !isDev)
|
|
2541
|
-
return;
|
|
2542
|
-
const c = terminalColors;
|
|
2543
|
-
const statusIcon = success ? `${c.brightGreen}✓${c.reset}` : `${c.brightRed}✗${c.reset}`;
|
|
2544
|
-
const statusColor = success ? c.green : c.red;
|
|
2545
|
-
const durationStr = `${c.dim}${durationMs.toFixed(1)}ms${c.reset}`;
|
|
2546
|
-
const errorMessage = error ? ` ${c.dim}·${c.reset} ${c.red}${error instanceof Error ? error.message : String(error)}${c.reset}` : "";
|
|
2547
|
-
console.log(`${statusColor}${c.bold}${operationName}${c.reset} ${statusIcon} ${c.dim}·${c.reset} ${durationStr}${errorMessage}`);
|
|
2548
|
-
}
|
|
2549
|
-
function colorize(text, color, ...modifiers) {
|
|
2550
|
-
const isDev = true;
|
|
2551
|
-
if (!isDev)
|
|
2552
|
-
return text;
|
|
2553
|
-
const colorCodes = [
|
|
2554
|
-
terminalColors[color],
|
|
2555
|
-
...modifiers.map((m) => terminalColors[m])
|
|
2556
|
-
].join("");
|
|
2557
|
-
return `${colorCodes}${text}${terminalColors.reset}`;
|
|
2558
|
-
}
|
|
2559
|
-
function logBanner(text, color = "cyan", options = {}) {
|
|
2560
|
-
const { devOnly = true } = options;
|
|
2561
|
-
const isDev = true;
|
|
2562
|
-
if (devOnly && !isDev) {
|
|
2563
|
-
console.log(`=== ${text} ===`);
|
|
2564
|
-
return;
|
|
2565
|
-
}
|
|
2566
|
-
const c = terminalColors;
|
|
2567
|
-
const padding = 2;
|
|
2568
|
-
const line = "═".repeat(text.length + padding * 2);
|
|
2569
|
-
console.log(`${c[color]}╔${line}╗${c.reset}`);
|
|
2570
|
-
console.log(`${c[color]}║${" ".repeat(padding)}${c.bold}${text}${c.reset}${c[color]}${" ".repeat(padding)}║${c.reset}`);
|
|
2571
|
-
console.log(`${c[color]}╚${line}╝${c.reset}`);
|
|
2572
|
-
}
|
|
2573
|
-
// src/jwt/index.ts
|
|
2574
|
-
var BYTE_CHUNK_SIZE = 32768;
|
|
2575
|
-
var textEncoder = new TextEncoder;
|
|
2576
|
-
var textDecoder = new TextDecoder;
|
|
2577
|
-
async function jwtSign(payload, secret) {
|
|
2578
|
-
const header = encodeJson({ alg: "HS256" });
|
|
2579
|
-
const body = encodeJson({
|
|
2580
|
-
...payload,
|
|
2581
|
-
iat: Math.floor(Date.now() / 1000)
|
|
2582
|
-
});
|
|
2583
|
-
const data = `${header}.${body}`;
|
|
2584
|
-
const signature = await sign(data, secret);
|
|
2585
|
-
return `${data}.${encodeBase64Url(signature)}`;
|
|
2586
|
-
}
|
|
2587
|
-
async function jwtVerify(token, secret) {
|
|
2588
|
-
try {
|
|
2589
|
-
const parts = token.split(".");
|
|
2590
|
-
if (parts.length !== 3)
|
|
2591
|
-
return null;
|
|
2592
|
-
const [encodedHeader, encodedPayload, encodedSignature] = parts;
|
|
2593
|
-
const header = decodeJson(encodedHeader);
|
|
2594
|
-
if (!isJwtHeader(header))
|
|
2595
|
-
return null;
|
|
2596
|
-
const data = `${encodedHeader}.${encodedPayload}`;
|
|
2597
|
-
const signature = decodeBase64Url(encodedSignature);
|
|
2598
|
-
const isValid = await verify(data, signature, secret);
|
|
2599
|
-
if (!isValid)
|
|
2600
|
-
return null;
|
|
2601
|
-
const payload = decodeJson(encodedPayload);
|
|
2602
|
-
if (!isJwtPayload(payload) || !isTimeValid(payload))
|
|
2603
|
-
return null;
|
|
2604
|
-
return payload;
|
|
2605
|
-
} catch (_error) {
|
|
2606
|
-
return null;
|
|
2607
|
-
}
|
|
2608
|
-
}
|
|
2609
|
-
async function sign(data, secret) {
|
|
2610
|
-
const key = await getKey(secret, ["sign"]);
|
|
2611
|
-
return await getSubtleCrypto().sign("HMAC", key, encodeText(data));
|
|
2612
|
-
}
|
|
2613
|
-
async function verify(data, signature, secret) {
|
|
2614
|
-
const key = await getKey(secret, ["verify"]);
|
|
2615
|
-
return await getSubtleCrypto().verify("HMAC", key, signature, encodeText(data));
|
|
2616
|
-
}
|
|
2617
|
-
async function getKey(secret, usages) {
|
|
2618
|
-
return await getSubtleCrypto().importKey("raw", encodeText(secret), {
|
|
2619
|
-
name: "HMAC",
|
|
2620
|
-
hash: "SHA-256"
|
|
2621
|
-
}, false, usages);
|
|
2622
|
-
}
|
|
2623
|
-
function encodeJson(value) {
|
|
2624
|
-
return encodeBase64Url(encodeText(JSON.stringify(value)));
|
|
2625
|
-
}
|
|
2626
|
-
function decodeJson(value) {
|
|
2627
|
-
return JSON.parse(textDecoder.decode(decodeBase64Url(value)));
|
|
2628
|
-
}
|
|
2629
|
-
function encodeBase64Url(value) {
|
|
2630
|
-
const bytes = value instanceof ArrayBuffer ? new Uint8Array(value) : value;
|
|
2631
|
-
return btoa(bytesToBinaryString(bytes)).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
|
2632
|
-
}
|
|
2633
|
-
function decodeBase64Url(value) {
|
|
2634
|
-
const base64 = value.replaceAll("-", "+").replaceAll("_", "/");
|
|
2635
|
-
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
|
|
2636
|
-
return binaryStringToBytes(atob(padded)).buffer;
|
|
2637
|
-
}
|
|
2638
|
-
function encodeText(value) {
|
|
2639
|
-
return toArrayBuffer(textEncoder.encode(value));
|
|
2640
|
-
}
|
|
2641
|
-
function toArrayBuffer(bytes) {
|
|
2642
|
-
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
2643
|
-
}
|
|
2644
|
-
function bytesToBinaryString(bytes) {
|
|
2645
|
-
let binary = "";
|
|
2646
|
-
for (let offset = 0;offset < bytes.length; offset += BYTE_CHUNK_SIZE) {
|
|
2647
|
-
binary += String.fromCharCode(...bytes.subarray(offset, offset + BYTE_CHUNK_SIZE));
|
|
2648
|
-
}
|
|
2649
|
-
return binary;
|
|
2650
|
-
}
|
|
2651
|
-
function binaryStringToBytes(binary) {
|
|
2652
|
-
const bytes = new Uint8Array(binary.length);
|
|
2653
|
-
for (let index = 0;index < binary.length; index += 1) {
|
|
2654
|
-
bytes[index] = binary.charCodeAt(index);
|
|
2655
|
-
}
|
|
2656
|
-
return bytes;
|
|
2657
|
-
}
|
|
2658
|
-
function getSubtleCrypto() {
|
|
2659
|
-
const subtle = globalThis.crypto?.subtle;
|
|
2660
|
-
if (!subtle) {
|
|
2661
|
-
throw new Error("Web Crypto is not available in this runtime");
|
|
2662
|
-
}
|
|
2663
|
-
return subtle;
|
|
2664
|
-
}
|
|
2665
|
-
function isJwtHeader(value) {
|
|
2666
|
-
return typeof value === "object" && value !== null && "alg" in value && value.alg === "HS256";
|
|
2667
|
-
}
|
|
2668
|
-
function isJwtPayload(value) {
|
|
2669
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2670
|
-
}
|
|
2671
|
-
function isTimeValid(payload) {
|
|
2672
|
-
const now = Math.floor(Date.now() / 1000);
|
|
2673
|
-
if (typeof payload.exp === "number" && now >= payload.exp)
|
|
2674
|
-
return false;
|
|
2675
|
-
if (typeof payload.nbf === "number" && now < payload.nbf)
|
|
2676
|
-
return false;
|
|
2677
|
-
return true;
|
|
793
|
+
current[keys.at(-1)] = value;
|
|
2678
794
|
}
|
|
2679
|
-
// src/
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
"/sitemap.xml",
|
|
2689
|
-
"/sitemap_index.xml",
|
|
2690
|
-
"/sitemap/",
|
|
2691
|
-
"/sitemaps.xml",
|
|
2692
|
-
"/sitemap/sitemap.xml"
|
|
2693
|
-
];
|
|
2694
|
-
const sitemaps = new Set;
|
|
2695
|
-
const requestOptions = {
|
|
2696
|
-
headers: {
|
|
2697
|
-
"User-Agent": "SitemapCrawler/1.0"
|
|
2698
|
-
},
|
|
2699
|
-
signal: createTimeoutController(5000).signal
|
|
2700
|
-
};
|
|
2701
|
-
try {
|
|
2702
|
-
const robotsTxt = await fetch(`${normalizedDomain}/robots.txt`, {
|
|
2703
|
-
...requestOptions
|
|
2704
|
-
});
|
|
2705
|
-
if (robotsTxt.ok) {
|
|
2706
|
-
const text = await robotsTxt.text();
|
|
2707
|
-
const sitemapMatches = text.match(/Sitemap: (.*)/gi);
|
|
2708
|
-
if (sitemapMatches) {
|
|
2709
|
-
sitemapMatches.forEach((match) => {
|
|
2710
|
-
const url = match.replace("Sitemap:", "").trim();
|
|
2711
|
-
sitemaps.add(url);
|
|
2712
|
-
});
|
|
2713
|
-
}
|
|
2714
|
-
if (sitemaps.size > 0) {
|
|
2715
|
-
return [...sitemaps];
|
|
2716
|
-
}
|
|
2717
|
-
}
|
|
2718
|
-
} catch (error) {
|
|
2719
|
-
console.warn("Could not fetch robots.txt:", error instanceof Error ? error.message : "Unknown error");
|
|
2720
|
-
}
|
|
2721
|
-
for (const location of commonLocations) {
|
|
2722
|
-
try {
|
|
2723
|
-
const response = await fetch(`${normalizedDomain}${location}`, {
|
|
2724
|
-
...requestOptions,
|
|
2725
|
-
method: "HEAD"
|
|
2726
|
-
});
|
|
2727
|
-
if (response.ok) {
|
|
2728
|
-
return [`${normalizedDomain}${location}`];
|
|
2729
|
-
}
|
|
2730
|
-
} catch (_error) {}
|
|
2731
|
-
}
|
|
2732
|
-
return [];
|
|
2733
|
-
};
|
|
2734
|
-
var getDomainSitemap = async (domain, options = {}) => {
|
|
2735
|
-
const {
|
|
2736
|
-
filterIndexes,
|
|
2737
|
-
concurrency = 10,
|
|
2738
|
-
maxDepth = 5,
|
|
2739
|
-
timeout = 1e4
|
|
2740
|
-
} = options;
|
|
2741
|
-
let normalizedDomain = domain.replace(/\/$/, "");
|
|
2742
|
-
if (!normalizedDomain.startsWith("http")) {
|
|
2743
|
-
normalizedDomain = `https://${normalizedDomain}`;
|
|
2744
|
-
}
|
|
2745
|
-
const sitemapUrls = await discoverSitemaps(normalizedDomain);
|
|
2746
|
-
if (sitemapUrls.length === 0) {
|
|
2747
|
-
throw new Error("No sitemaps found for the domain");
|
|
2748
|
-
}
|
|
2749
|
-
const queue = new PQueue({ concurrency });
|
|
2750
|
-
const result = {
|
|
2751
|
-
links: new Set,
|
|
2752
|
-
visited: new Set
|
|
2753
|
-
};
|
|
2754
|
-
const locRegex = /<loc>([^<]+)<\/loc>/g;
|
|
2755
|
-
const isXmlSitemap = (url) => url.endsWith(".xml") || url.toLowerCase().includes("sitemap");
|
|
2756
|
-
const processSitemap = async (sitemapUrl, depth = 0) => {
|
|
2757
|
-
if (depth > maxDepth || result.visited.has(sitemapUrl)) {
|
|
2758
|
-
return;
|
|
2759
|
-
}
|
|
2760
|
-
result.visited.add(sitemapUrl);
|
|
2761
|
-
try {
|
|
2762
|
-
const controller = createTimeoutController(timeout);
|
|
2763
|
-
const response = await fetch(sitemapUrl, {
|
|
2764
|
-
headers: {
|
|
2765
|
-
"User-Agent": "SitemapCrawler/1.0"
|
|
2766
|
-
},
|
|
2767
|
-
signal: controller.signal
|
|
2768
|
-
});
|
|
2769
|
-
if (!response.ok)
|
|
2770
|
-
throw new Error(`HTTP error! status: ${response.status}`);
|
|
2771
|
-
const data = await response.text();
|
|
2772
|
-
const matches = [...data.matchAll(locRegex)].map((match) => match[1].trim()).filter((link) => {
|
|
2773
|
-
if (filterIndexes && isXmlSitemap(link)) {
|
|
2774
|
-
return link.includes(filterIndexes);
|
|
2775
|
-
}
|
|
2776
|
-
return true;
|
|
2777
|
-
});
|
|
2778
|
-
for (const link of matches) {
|
|
2779
|
-
if (isXmlSitemap(link)) {
|
|
2780
|
-
queue.add(() => processSitemap(link, depth + 1));
|
|
2781
|
-
} else {
|
|
2782
|
-
result.links.add(link);
|
|
2783
|
-
}
|
|
2784
|
-
}
|
|
2785
|
-
} catch (error) {
|
|
2786
|
-
console.warn(`Failed to process sitemap ${sitemapUrl}:`, error instanceof Error ? error.message : "Unknown error");
|
|
2787
|
-
}
|
|
795
|
+
// src/function/index.ts
|
|
796
|
+
function debounce(fn, waitMs, options = {}) {
|
|
797
|
+
let timer;
|
|
798
|
+
let maxTimer;
|
|
799
|
+
let lastArgs;
|
|
800
|
+
const cancel = () => {
|
|
801
|
+
clearTimeout(timer);
|
|
802
|
+
clearTimeout(maxTimer);
|
|
803
|
+
timer = maxTimer = lastArgs = undefined;
|
|
2788
804
|
};
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
return [...result.links];
|
|
2792
|
-
};
|
|
2793
|
-
// src/secrets/errors.ts
|
|
2794
|
-
class SecretLoadError extends Error {
|
|
2795
|
-
code;
|
|
2796
|
-
operation;
|
|
2797
|
-
status;
|
|
2798
|
-
constructor(code, message, details = {}) {
|
|
2799
|
-
super(message);
|
|
2800
|
-
this.name = "SecretLoadError";
|
|
2801
|
-
this.code = code;
|
|
2802
|
-
this.operation = details.operation;
|
|
2803
|
-
this.status = details.status;
|
|
2804
|
-
}
|
|
2805
|
-
}
|
|
2806
|
-
|
|
2807
|
-
// src/secrets/deadline.ts
|
|
2808
|
-
async function withSecretDeadline(options, operation) {
|
|
2809
|
-
const timeoutMs = options.timeoutMs ?? 30000;
|
|
2810
|
-
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2147483647) {
|
|
2811
|
-
throw new SecretLoadError("CONFIGURATION", "Secret timeoutMs must be a positive finite timer duration.");
|
|
2812
|
-
}
|
|
2813
|
-
const controller = new AbortController;
|
|
2814
|
-
const cancel = () => controller.abort(new SecretLoadError("ABORTED", "Secret loading was cancelled."));
|
|
2815
|
-
if (options.signal?.aborted) {
|
|
805
|
+
const flush = () => {
|
|
806
|
+
const args = lastArgs;
|
|
2816
807
|
cancel();
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
}
|
|
2820
|
-
const timer = setTimeout(() => controller.abort(new SecretLoadError("TIMEOUT", `Secret loading timed out after ${timeoutMs}ms.`)), timeoutMs);
|
|
2821
|
-
let rejectOnAbort = () => {};
|
|
2822
|
-
try {
|
|
2823
|
-
const aborted = new Promise((_, reject) => {
|
|
2824
|
-
rejectOnAbort = () => reject(controller.signal.reason);
|
|
2825
|
-
controller.signal.addEventListener("abort", rejectOnAbort, { once: true });
|
|
2826
|
-
});
|
|
2827
|
-
if (controller.signal.aborted) {
|
|
2828
|
-
throw controller.signal.reason;
|
|
2829
|
-
}
|
|
2830
|
-
return await Promise.race([operation(controller.signal), aborted]);
|
|
2831
|
-
} finally {
|
|
2832
|
-
clearTimeout(timer);
|
|
2833
|
-
options.signal?.removeEventListener("abort", cancel);
|
|
2834
|
-
controller.signal.removeEventListener("abort", rejectOnAbort);
|
|
2835
|
-
}
|
|
2836
|
-
}
|
|
2837
|
-
|
|
2838
|
-
// src/secrets/environment.ts
|
|
2839
|
-
function getProcessEnvironment() {
|
|
2840
|
-
return typeof process === "undefined" ? undefined : process.env;
|
|
2841
|
-
}
|
|
2842
|
-
function resolveSecretEnvironment(option, env) {
|
|
2843
|
-
const explicit = typeof option === "function" ? option() : option;
|
|
2844
|
-
if (typeof explicit === "object" && explicit !== null) {
|
|
2845
|
-
if (typeof explicit.slug !== "string" || !/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(explicit.slug)) {
|
|
2846
|
-
throw new SecretLoadError("CONFIGURATION", "Custom secret environment requires a valid slug.");
|
|
2847
|
-
}
|
|
2848
|
-
return explicit.slug;
|
|
2849
|
-
}
|
|
2850
|
-
const selected = explicit ?? env?.SECRETS_ENV;
|
|
2851
|
-
if (selected !== undefined) {
|
|
2852
|
-
if (selected === "production" || selected === "prod") {
|
|
2853
|
-
return "prod";
|
|
2854
|
-
}
|
|
2855
|
-
if (selected === "development" || selected === "dev") {
|
|
2856
|
-
return "dev";
|
|
2857
|
-
}
|
|
2858
|
-
if (selected === "staging") {
|
|
2859
|
-
return "staging";
|
|
2860
|
-
}
|
|
2861
|
-
throw new SecretLoadError("CONFIGURATION", "Invalid secret environment. Use dev, staging, prod, or an explicit { slug } option.");
|
|
2862
|
-
}
|
|
2863
|
-
if (env?.NODE_ENV === "production" || env?.NODE_ENV === "prod") {
|
|
2864
|
-
return "prod";
|
|
2865
|
-
}
|
|
2866
|
-
if (env?.NODE_ENV === "staging") {
|
|
2867
|
-
return "staging";
|
|
2868
|
-
}
|
|
2869
|
-
return "dev";
|
|
2870
|
-
}
|
|
2871
|
-
|
|
2872
|
-
// src/secrets/core.ts
|
|
2873
|
-
var registryKey = Symbol.for("hanzio.secrets.environmentWrites.v1");
|
|
2874
|
-
var shared = globalThis;
|
|
2875
|
-
var environmentWrites = shared[registryKey] ?? new WeakMap;
|
|
2876
|
-
shared[registryKey] = environmentWrites;
|
|
2877
|
-
var defaultLogger = {
|
|
2878
|
-
info: (message, attrs) => console.log(`${message} environment=${attrs.environment} count=${attrs.count} source=${attrs.source} durationMs=${attrs.durationMs}`, { sources: attrs.sources }),
|
|
2879
|
-
error: (message, attrs) => console.error(message, attrs)
|
|
2880
|
-
};
|
|
2881
|
-
function readOverrides(keys, env) {
|
|
2882
|
-
if (!env) {
|
|
2883
|
-
return {};
|
|
2884
|
-
}
|
|
2885
|
-
const writes = environmentWrites.get(env);
|
|
2886
|
-
return Object.fromEntries(keys.flatMap((key) => {
|
|
2887
|
-
const value = Object.hasOwn(env, key) ? env[key] : undefined;
|
|
2888
|
-
if (writes?.has(key)) {
|
|
2889
|
-
if (writes.get(key) === value) {
|
|
2890
|
-
return [];
|
|
2891
|
-
}
|
|
2892
|
-
writes.delete(key);
|
|
2893
|
-
}
|
|
2894
|
-
return value ? [[key, value]] : [];
|
|
2895
|
-
}));
|
|
2896
|
-
}
|
|
2897
|
-
async function defineSecretSet(keys, options, environmentVariables) {
|
|
2898
|
-
const keySet = [...new Set(keys)];
|
|
2899
|
-
const environment = resolveSecretEnvironment(options.environment, environmentVariables);
|
|
2900
|
-
const logger = options.logger === false ? undefined : options.logger ?? defaultLogger;
|
|
2901
|
-
let cached;
|
|
2902
|
-
let parsed;
|
|
2903
|
-
let sources;
|
|
2904
|
-
let inFlight;
|
|
2905
|
-
const log = (level, message, attributes) => {
|
|
2906
|
-
try {
|
|
2907
|
-
logger?.[level](message, attributes);
|
|
2908
|
-
} catch {}
|
|
808
|
+
if (args)
|
|
809
|
+
fn(...args);
|
|
2909
810
|
};
|
|
2910
|
-
const
|
|
2911
|
-
const
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
try {
|
|
2917
|
-
loaded = await options.loader({
|
|
2918
|
-
keys: missing,
|
|
2919
|
-
environment,
|
|
2920
|
-
signal,
|
|
2921
|
-
timeoutMs: request.timeoutMs ?? options.timeoutMs ?? 30000,
|
|
2922
|
-
reportSource: (source) => {
|
|
2923
|
-
loaderSource = source;
|
|
2924
|
-
}
|
|
2925
|
-
});
|
|
2926
|
-
} catch (error) {
|
|
2927
|
-
if (error instanceof SecretLoadError) {
|
|
2928
|
-
throw error;
|
|
2929
|
-
}
|
|
2930
|
-
throw new SecretLoadError("LOADER", "Secret loader failed.");
|
|
2931
|
-
}
|
|
2932
|
-
}
|
|
2933
|
-
const overrides = readOverrides(keySet, environmentVariables);
|
|
2934
|
-
const next = Object.fromEntries(keySet.map((key) => {
|
|
2935
|
-
if (Object.hasOwn(overrides, key)) {
|
|
2936
|
-
return [key, overrides[key]];
|
|
2937
|
-
}
|
|
2938
|
-
if (Object.hasOwn(loaded, key)) {
|
|
2939
|
-
return [key, loaded[key]];
|
|
2940
|
-
}
|
|
2941
|
-
return [key, undefined];
|
|
2942
|
-
}));
|
|
2943
|
-
const missingKeys = keySet.filter((key) => typeof next[key] !== "string" || !next[key]);
|
|
2944
|
-
if (missingKeys.length) {
|
|
2945
|
-
throw new SecretLoadError("MISSING_SECRETS", `Missing secrets: ${missingKeys.join(", ")}`);
|
|
2946
|
-
}
|
|
2947
|
-
let nextParsed;
|
|
2948
|
-
try {
|
|
2949
|
-
nextParsed = options.schema ? await options.schema.parseAsync({ ...next }) : { ...next };
|
|
2950
|
-
nextParsed = structuredClone(nextParsed);
|
|
2951
|
-
} catch {
|
|
2952
|
-
throw new SecretLoadError("VALIDATION", "Secret schema validation failed. Check the configured schema; values are omitted.");
|
|
2953
|
-
}
|
|
2954
|
-
signal.throwIfAborted();
|
|
2955
|
-
const nextSources = Object.fromEntries(keySet.map((key) => [
|
|
2956
|
-
key,
|
|
2957
|
-
Object.hasOwn(overrides, key) ? "process.env" : loaderSource
|
|
2958
|
-
]));
|
|
2959
|
-
if (options.writeToProcessEnv !== false && environmentVariables) {
|
|
2960
|
-
let writes = environmentWrites.get(environmentVariables);
|
|
2961
|
-
if (!writes) {
|
|
2962
|
-
writes = new Map;
|
|
2963
|
-
environmentWrites.set(environmentVariables, writes);
|
|
2964
|
-
}
|
|
2965
|
-
for (const key of keySet) {
|
|
2966
|
-
if (Object.hasOwn(overrides, key)) {
|
|
2967
|
-
continue;
|
|
2968
|
-
}
|
|
2969
|
-
environmentVariables[key] = next[key];
|
|
2970
|
-
writes.set(key, next[key]);
|
|
2971
|
-
}
|
|
811
|
+
const debounced = (...args) => {
|
|
812
|
+
const idle = timer === undefined;
|
|
813
|
+
clearTimeout(timer);
|
|
814
|
+
timer = setTimeout(flush, waitMs);
|
|
815
|
+
if (options.maxWaitMs !== undefined && maxTimer === undefined) {
|
|
816
|
+
maxTimer = setTimeout(flush, options.maxWaitMs);
|
|
2972
817
|
}
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
818
|
+
if (idle && options.leading)
|
|
819
|
+
fn(...args);
|
|
820
|
+
else
|
|
821
|
+
lastArgs = args;
|
|
2976
822
|
};
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
const source = sources[key];
|
|
2991
|
-
sourceCounts.set(source, (sourceCounts.get(source) ?? 0) + 1);
|
|
2992
|
-
}
|
|
2993
|
-
const counts = Object.fromEntries(sourceCounts);
|
|
2994
|
-
log("info", "Loaded secrets", {
|
|
2995
|
-
environment,
|
|
2996
|
-
count: keySet.length,
|
|
2997
|
-
durationMs: Date.now() - start,
|
|
2998
|
-
source: Object.keys(counts).join(" + ") || "none",
|
|
2999
|
-
sources: counts
|
|
3000
|
-
});
|
|
3001
|
-
} catch (error) {
|
|
3002
|
-
const safe = error instanceof SecretLoadError ? error : new SecretLoadError("LOADER", "Secret loading failed.");
|
|
3003
|
-
log("error", "Secret loading failed", {
|
|
3004
|
-
environment,
|
|
3005
|
-
count: keySet.length,
|
|
3006
|
-
durationMs: Date.now() - start,
|
|
3007
|
-
code: safe.code
|
|
3008
|
-
});
|
|
3009
|
-
throw safe;
|
|
3010
|
-
}
|
|
3011
|
-
})().finally(() => {
|
|
3012
|
-
inFlight = undefined;
|
|
3013
|
-
});
|
|
3014
|
-
return inFlight;
|
|
823
|
+
return Object.assign(debounced, {
|
|
824
|
+
cancel,
|
|
825
|
+
flush,
|
|
826
|
+
pending: () => lastArgs !== undefined
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
function keyedDebounce(fn, waitMs) {
|
|
830
|
+
const byKey = new Map;
|
|
831
|
+
const each = (key, action) => {
|
|
832
|
+
const targets = key === undefined ? [...byKey.values()] : [byKey.get(key)];
|
|
833
|
+
for (const debounced of targets)
|
|
834
|
+
if (debounced)
|
|
835
|
+
action(debounced);
|
|
3015
836
|
};
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
837
|
+
return {
|
|
838
|
+
call(key, ...args) {
|
|
839
|
+
let debounced = byKey.get(key);
|
|
840
|
+
if (!debounced) {
|
|
841
|
+
debounced = debounce((...latest) => {
|
|
842
|
+
byKey.delete(key);
|
|
843
|
+
fn(key, ...latest);
|
|
844
|
+
}, waitMs);
|
|
845
|
+
byKey.set(key, debounced);
|
|
3020
846
|
}
|
|
3021
|
-
|
|
847
|
+
debounced(...args);
|
|
3022
848
|
},
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
849
|
+
cancel(key) {
|
|
850
|
+
each(key, (debounced) => debounced.cancel());
|
|
851
|
+
if (key === undefined)
|
|
852
|
+
byKey.clear();
|
|
853
|
+
else
|
|
854
|
+
byKey.delete(key);
|
|
855
|
+
},
|
|
856
|
+
flush(key) {
|
|
857
|
+
each(key, (debounced) => debounced.flush());
|
|
3029
858
|
}
|
|
3030
859
|
};
|
|
3031
|
-
await load();
|
|
3032
|
-
return secretSet;
|
|
3033
860
|
}
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
861
|
+
function throttle(fn, intervalMs, options = {}) {
|
|
862
|
+
const { trailing = true } = options;
|
|
863
|
+
let timer;
|
|
864
|
+
let trailingArgs;
|
|
865
|
+
const invoke = (args) => {
|
|
866
|
+
timer = setTimeout(() => {
|
|
867
|
+
timer = undefined;
|
|
868
|
+
const next = trailingArgs;
|
|
869
|
+
trailingArgs = undefined;
|
|
870
|
+
if (next)
|
|
871
|
+
invoke(next);
|
|
872
|
+
}, intervalMs);
|
|
873
|
+
fn(...args);
|
|
3047
874
|
};
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
`--domain=${options.siteUrl}`,
|
|
3059
|
-
"--silent"
|
|
3060
|
-
], {
|
|
3061
|
-
env,
|
|
3062
|
-
encoding: "utf8",
|
|
3063
|
-
shell: false,
|
|
3064
|
-
timeout: options.timeoutMs ?? 30000,
|
|
3065
|
-
signal: options.signal,
|
|
3066
|
-
killSignal: "SIGKILL",
|
|
3067
|
-
maxBuffer: 10485760,
|
|
3068
|
-
windowsHide: true
|
|
3069
|
-
}, (error, output) => {
|
|
3070
|
-
if (error) {
|
|
3071
|
-
reject(error);
|
|
3072
|
-
} else {
|
|
3073
|
-
resolve(output);
|
|
3074
|
-
}
|
|
3075
|
-
});
|
|
3076
|
-
child.stdin?.end();
|
|
3077
|
-
});
|
|
3078
|
-
return stdout.trim();
|
|
3079
|
-
} catch (error) {
|
|
3080
|
-
if (options.signal.aborted) {
|
|
3081
|
-
throw options.signal.reason;
|
|
3082
|
-
}
|
|
3083
|
-
const failure = error;
|
|
3084
|
-
if (failure?.code === "ENOENT") {
|
|
3085
|
-
throw new SecretLoadError("CLI", "Infisical CLI not found. Install infisical and ensure it is on PATH, or provide Infisical client credentials.");
|
|
3086
|
-
}
|
|
3087
|
-
if (failure?.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") {
|
|
3088
|
-
throw new SecretLoadError("CLI", "Infisical CLI output exceeded the 10 MiB limit.");
|
|
3089
|
-
}
|
|
3090
|
-
if (failure?.killed) {
|
|
3091
|
-
throw new SecretLoadError("TIMEOUT", `Infisical CLI timed out after ${(options.timeoutMs ?? 30000) / 1000} seconds or was terminated.`);
|
|
3092
|
-
}
|
|
3093
|
-
throw new SecretLoadError("CLI", `Infisical CLI session token failed. Run infisical login --domain=${options.siteUrl} and verify access to the configured organization, project, and environment.`);
|
|
3094
|
-
}
|
|
3095
|
-
}
|
|
3096
|
-
|
|
3097
|
-
// src/secrets/infisical-config.ts
|
|
3098
|
-
function validateInfisicalScope(options) {
|
|
3099
|
-
validateInfisicalOrganizationId(options.organizationId);
|
|
3100
|
-
let url;
|
|
3101
|
-
try {
|
|
3102
|
-
url = new URL(options.siteUrl);
|
|
3103
|
-
} catch {
|
|
3104
|
-
throw new SecretLoadError("CONFIGURATION", "Invalid Infisical siteUrl.");
|
|
3105
|
-
}
|
|
3106
|
-
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash) {
|
|
3107
|
-
throw new SecretLoadError("CONFIGURATION", "Infisical siteUrl must be an HTTP(S) URL without credentials, query, or fragment.");
|
|
3108
|
-
}
|
|
3109
|
-
if (!options.projectId.trim() || !options.environment.trim() || !(options.secretPath ?? "/").startsWith("/")) {
|
|
3110
|
-
throw new SecretLoadError("CONFIGURATION", "Infisical requires a projectId, environment, and absolute secretPath.");
|
|
3111
|
-
}
|
|
3112
|
-
}
|
|
3113
|
-
function validateInfisicalOrganizationId(organizationId) {
|
|
3114
|
-
if (typeof organizationId !== "string" || !organizationId.trim() || /\s/.test(organizationId)) {
|
|
3115
|
-
throw new SecretLoadError("CONFIGURATION", "Infisical organizationId is required and must be a nonempty ID without whitespace.");
|
|
3116
|
-
}
|
|
3117
|
-
}
|
|
3118
|
-
|
|
3119
|
-
// src/secrets/infisical-session.ts
|
|
3120
|
-
import { z as z4 } from "zod";
|
|
3121
|
-
var sessionClaimsSchema = z4.object({
|
|
3122
|
-
organizationId: z4.string().optional(),
|
|
3123
|
-
subOrganizationId: z4.string().optional(),
|
|
3124
|
-
exp: z4.number().finite().optional()
|
|
3125
|
-
});
|
|
3126
|
-
function parseInfisicalSessionToken(token) {
|
|
3127
|
-
let claims;
|
|
3128
|
-
try {
|
|
3129
|
-
if (!/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(token)) {
|
|
3130
|
-
throw new Error("Invalid token format");
|
|
3131
|
-
}
|
|
3132
|
-
const payload = token.split(".")[1];
|
|
3133
|
-
const json = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
|
|
3134
|
-
claims = sessionClaimsSchema.parse(JSON.parse(json));
|
|
3135
|
-
} catch {
|
|
3136
|
-
throw new SecretLoadError("AUTHENTICATION", "Infisical returned an invalid user session token. Run infisical login for the configured domain.");
|
|
3137
|
-
}
|
|
3138
|
-
if (claims.exp !== undefined && claims.exp <= Date.now() / 1000) {
|
|
3139
|
-
throw new SecretLoadError("AUTHENTICATION", "Infisical user session has expired. Run infisical login for the configured domain.");
|
|
3140
|
-
}
|
|
3141
|
-
return { organizationId: claims.subOrganizationId ?? claims.organizationId };
|
|
3142
|
-
}
|
|
3143
|
-
|
|
3144
|
-
// src/secrets/infisical.ts
|
|
3145
|
-
var DEFAULT_INFISICAL_SITE_URL = "https://eu.infisical.com";
|
|
3146
|
-
var defaultFetch = globalThis.fetch;
|
|
3147
|
-
var infisicalAuthResponseSchema = z5.object({ accessToken: z5.string() });
|
|
3148
|
-
var infisicalSecretSchema = z5.object({
|
|
3149
|
-
secretKey: z5.string(),
|
|
3150
|
-
secretValue: z5.string()
|
|
3151
|
-
});
|
|
3152
|
-
var infisicalSecretsResponseSchema = z5.object({
|
|
3153
|
-
secrets: z5.array(infisicalSecretSchema)
|
|
3154
|
-
});
|
|
3155
|
-
async function fetchInfisicalSecrets(options) {
|
|
3156
|
-
validateInfisicalScope(options);
|
|
3157
|
-
return withSecretDeadline(options, async (signal) => {
|
|
3158
|
-
let operation = "login";
|
|
3159
|
-
try {
|
|
3160
|
-
const infisical = createInfisicalApiClient(options.siteUrl);
|
|
3161
|
-
const { data: auth } = await infisical.request("login", {
|
|
3162
|
-
signal,
|
|
3163
|
-
reqBody: {
|
|
3164
|
-
clientId: options.clientId,
|
|
3165
|
-
clientSecret: options.clientSecret
|
|
3166
|
-
}
|
|
3167
|
-
});
|
|
3168
|
-
signal.throwIfAborted();
|
|
3169
|
-
operation = "listSecrets";
|
|
3170
|
-
return await listInfisicalSecrets(infisical, auth.accessToken, {
|
|
3171
|
-
...options,
|
|
3172
|
-
signal
|
|
3173
|
-
});
|
|
3174
|
-
} catch (error) {
|
|
3175
|
-
throwInfisicalHttpError(error, operation, signal);
|
|
875
|
+
const throttled = (...args) => {
|
|
876
|
+
if (timer === undefined)
|
|
877
|
+
invoke(args);
|
|
878
|
+
else if (trailing)
|
|
879
|
+
trailingArgs = args;
|
|
880
|
+
};
|
|
881
|
+
return Object.assign(throttled, {
|
|
882
|
+
cancel: () => {
|
|
883
|
+
clearTimeout(timer);
|
|
884
|
+
timer = trailingArgs = undefined;
|
|
3176
885
|
}
|
|
3177
886
|
});
|
|
3178
887
|
}
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
throw new SecretLoadError("CONFIGURATION", "Invalid Infisical auth mode.");
|
|
3187
|
-
}
|
|
3188
|
-
const useHttp = mode === "http" || mode === "auto" && Boolean(clientId && clientSecret);
|
|
3189
|
-
if (useHttp) {
|
|
3190
|
-
if (!clientId || !clientSecret) {
|
|
3191
|
-
throw new SecretLoadError("AUTHENTICATION", "Infisical HTTP authentication requires both client credentials.");
|
|
888
|
+
function once(fn) {
|
|
889
|
+
let called = false;
|
|
890
|
+
let result;
|
|
891
|
+
return (...args) => {
|
|
892
|
+
if (!called) {
|
|
893
|
+
called = true;
|
|
894
|
+
result = fn(...args);
|
|
3192
895
|
}
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
}
|
|
3196
|
-
options.reportSource?.(`Infisical CLI session (organization=${options.organizationId} project=${options.projectId} environment=${options.environment})`);
|
|
3197
|
-
return withSecretDeadline(options, (signal) => fetchInfisicalSessionSecrets({ ...options, signal }));
|
|
896
|
+
return result;
|
|
897
|
+
};
|
|
3198
898
|
}
|
|
3199
|
-
|
|
3200
|
-
const {
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
let
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
899
|
+
function createHeartbeat(options) {
|
|
900
|
+
const {
|
|
901
|
+
now = Date.now,
|
|
902
|
+
setTimeout: schedule = (callback, ms) => setTimeout(callback, ms),
|
|
903
|
+
clearTimeout: unschedule = (timer) => clearTimeout(timer)
|
|
904
|
+
} = options;
|
|
905
|
+
let active = false;
|
|
906
|
+
let lastSentAt = 0;
|
|
907
|
+
let idleTimer;
|
|
908
|
+
const stop = () => {
|
|
909
|
+
if (idleTimer !== undefined)
|
|
910
|
+
unschedule(idleTimer);
|
|
911
|
+
idleTimer = undefined;
|
|
912
|
+
if (!active)
|
|
913
|
+
return;
|
|
914
|
+
active = false;
|
|
915
|
+
options.onStop?.();
|
|
916
|
+
};
|
|
917
|
+
return {
|
|
918
|
+
activity() {
|
|
919
|
+
const time = now();
|
|
920
|
+
if (!active || time - lastSentAt >= options.intervalMs) {
|
|
921
|
+
active = true;
|
|
922
|
+
lastSentAt = time;
|
|
923
|
+
options.send();
|
|
3223
924
|
}
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
operation = "listSecrets";
|
|
3228
|
-
return await listInfisicalSecrets(infisical, token, options);
|
|
3229
|
-
} catch (error) {
|
|
3230
|
-
throwInfisicalHttpError(error, operation, signal);
|
|
3231
|
-
}
|
|
3232
|
-
}
|
|
3233
|
-
async function listInfisicalSecrets(infisical, token, options) {
|
|
3234
|
-
const { data } = await infisical.request("listSecrets", {
|
|
3235
|
-
signal: options.signal,
|
|
3236
|
-
reqHeaders: { Authorization: `Bearer ${token}` },
|
|
3237
|
-
reqQuery: {
|
|
3238
|
-
environment: options.environment,
|
|
3239
|
-
projectId: options.projectId,
|
|
3240
|
-
secretPath: options.secretPath ?? "/",
|
|
3241
|
-
viewSecretValue: true,
|
|
3242
|
-
expandSecretReferences: true,
|
|
3243
|
-
includeImports: true
|
|
3244
|
-
}
|
|
3245
|
-
});
|
|
3246
|
-
const wantedKeys = new Set(options.keys);
|
|
3247
|
-
return Object.fromEntries(data.secrets.filter((secret) => wantedKeys.has(secret.secretKey)).map((secret) => [secret.secretKey, secret.secretValue]));
|
|
3248
|
-
}
|
|
3249
|
-
function throwInfisicalHttpError(error, operation, signal) {
|
|
3250
|
-
if (signal.aborted) {
|
|
3251
|
-
throw signal.reason;
|
|
3252
|
-
}
|
|
3253
|
-
if (error instanceof SecretLoadError) {
|
|
3254
|
-
throw error;
|
|
3255
|
-
}
|
|
3256
|
-
const status = error instanceof HttpResponseError ? error.status : undefined;
|
|
3257
|
-
const guidance = operation === "selectOrganization" && (status === 401 || status === 403) ? " Run infisical login for the target organization and configured domain, then verify your access." : "";
|
|
3258
|
-
throw new SecretLoadError("HTTP", `Infisical HTTP ${operation} failed${status ? ` (status ${status})` : ""}.${guidance}`, { operation, status });
|
|
3259
|
-
}
|
|
3260
|
-
function createInfisicalApiClient(siteUrl) {
|
|
3261
|
-
return createApiClient({
|
|
3262
|
-
name: "infisical",
|
|
3263
|
-
baseApiUrls: {
|
|
3264
|
-
infisical: siteUrl
|
|
925
|
+
if (idleTimer !== undefined)
|
|
926
|
+
unschedule(idleTimer);
|
|
927
|
+
idleTimer = schedule(stop, options.idleMs);
|
|
3265
928
|
},
|
|
3266
|
-
|
|
3267
|
-
fetch: getFetch(),
|
|
3268
|
-
retries: 1,
|
|
3269
|
-
endpoints: {
|
|
3270
|
-
selectOrganization: {
|
|
3271
|
-
method: "POST",
|
|
3272
|
-
path: "/api/v3/auth/select-organization",
|
|
3273
|
-
reqHeadersSchema: z5.object({
|
|
3274
|
-
Authorization: z5.string(),
|
|
3275
|
-
"User-Agent": z5.string()
|
|
3276
|
-
}),
|
|
3277
|
-
reqBodySchema: z5.object({ organizationId: z5.string() }),
|
|
3278
|
-
resSchema: z5.object({
|
|
3279
|
-
token: z5.string().min(1),
|
|
3280
|
-
isMfaEnabled: z5.boolean().optional()
|
|
3281
|
-
}),
|
|
3282
|
-
reqBodyFormat: "json"
|
|
3283
|
-
},
|
|
3284
|
-
login: {
|
|
3285
|
-
method: "POST",
|
|
3286
|
-
path: "/api/v1/auth/universal-auth/login",
|
|
3287
|
-
reqBodySchema: z5.object({
|
|
3288
|
-
clientId: z5.string(),
|
|
3289
|
-
clientSecret: z5.string()
|
|
3290
|
-
}),
|
|
3291
|
-
resSchema: infisicalAuthResponseSchema,
|
|
3292
|
-
reqBodyFormat: "json"
|
|
3293
|
-
},
|
|
3294
|
-
listSecrets: {
|
|
3295
|
-
method: "GET",
|
|
3296
|
-
path: "/api/v4/secrets",
|
|
3297
|
-
reqHeadersSchema: z5.object({
|
|
3298
|
-
Authorization: z5.string()
|
|
3299
|
-
}),
|
|
3300
|
-
reqQuerySchema: z5.object({
|
|
3301
|
-
projectId: z5.string(),
|
|
3302
|
-
environment: z5.string(),
|
|
3303
|
-
secretPath: z5.string(),
|
|
3304
|
-
viewSecretValue: z5.boolean(),
|
|
3305
|
-
expandSecretReferences: z5.boolean(),
|
|
3306
|
-
includeImports: z5.boolean()
|
|
3307
|
-
}),
|
|
3308
|
-
resSchema: infisicalSecretsResponseSchema
|
|
3309
|
-
}
|
|
3310
|
-
}
|
|
3311
|
-
});
|
|
3312
|
-
}
|
|
3313
|
-
function getFetch() {
|
|
3314
|
-
const fetchFn = globalThis.fetch ?? defaultFetch;
|
|
3315
|
-
return (input, init) => {
|
|
3316
|
-
init?.signal?.throwIfAborted();
|
|
3317
|
-
return fetchFn(input, init);
|
|
929
|
+
stop
|
|
3318
930
|
};
|
|
3319
931
|
}
|
|
3320
|
-
// src/
|
|
3321
|
-
function
|
|
3322
|
-
const
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
}
|
|
3330
|
-
return out;
|
|
932
|
+
// src/emitter/index.ts
|
|
933
|
+
function createEmitter() {
|
|
934
|
+
const listeners = new Map;
|
|
935
|
+
const add = (event, listener, once) => {
|
|
936
|
+
const map = listeners.get(event) ?? new Map;
|
|
937
|
+
listeners.set(event, map);
|
|
938
|
+
map.set(listener, once);
|
|
939
|
+
return () => {
|
|
940
|
+
map.delete(listener);
|
|
941
|
+
};
|
|
3331
942
|
};
|
|
3332
|
-
return Object.assign(loader, { source: "Cloudflare Worker bindings" });
|
|
3333
|
-
}
|
|
3334
|
-
function workerBindingToString(value) {
|
|
3335
|
-
if (value === undefined || value === null) {
|
|
3336
|
-
return;
|
|
3337
|
-
}
|
|
3338
|
-
if (typeof value === "string") {
|
|
3339
|
-
return value === "" ? undefined : value;
|
|
3340
|
-
}
|
|
3341
|
-
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
3342
|
-
return String(value);
|
|
3343
|
-
}
|
|
3344
|
-
if (typeof value === "object") {
|
|
3345
|
-
return JSON.stringify(value);
|
|
3346
|
-
}
|
|
3347
|
-
return String(value);
|
|
3348
|
-
}
|
|
3349
|
-
|
|
3350
|
-
// src/secrets/loaders.ts
|
|
3351
|
-
function infisicalLoader(config) {
|
|
3352
|
-
validateInfisicalOrganizationId(config.organizationId);
|
|
3353
|
-
const loader = async (ctx) => fetchInfisicalSecrets({
|
|
3354
|
-
keys: ctx.keys,
|
|
3355
|
-
projectId: config.projectId,
|
|
3356
|
-
organizationId: config.organizationId,
|
|
3357
|
-
environment: ctx.environment,
|
|
3358
|
-
siteUrl: config.siteUrl ?? DEFAULT_INFISICAL_SITE_URL,
|
|
3359
|
-
clientId: config.clientId,
|
|
3360
|
-
clientSecret: config.clientSecret,
|
|
3361
|
-
secretPath: config.secretPath,
|
|
3362
|
-
timeoutMs: config.timeoutMs ?? ctx.timeoutMs,
|
|
3363
|
-
signal: ctx.signal
|
|
3364
|
-
});
|
|
3365
|
-
return Object.assign(loader, { source: "Infisical HTTP" });
|
|
3366
|
-
}
|
|
3367
|
-
async function processEnvLoader(_ctx) {
|
|
3368
|
-
return {};
|
|
3369
|
-
}
|
|
3370
|
-
Object.assign(processEnvLoader, { source: "process.env" });
|
|
3371
|
-
// src/secrets/vite.ts
|
|
3372
|
-
function getViteDefine(secretSet, options = {}) {
|
|
3373
|
-
const values = secretSet.secrets();
|
|
3374
|
-
const keys = options.publicKeys ?? Object.keys(values);
|
|
3375
|
-
if (!options.publicKeys && keys.some((key) => !key.startsWith("VITE_"))) {
|
|
3376
|
-
throw new SecretLoadError("CONFIGURATION", "Vite secret sets must contain only VITE_ keys, or provide an explicit publicKeys list.");
|
|
3377
|
-
}
|
|
3378
|
-
for (const key of keys) {
|
|
3379
|
-
if (!Object.hasOwn(values, key) || !/^[A-Za-z_$][\w$]*$/.test(key)) {
|
|
3380
|
-
throw new SecretLoadError("CONFIGURATION", "Vite publicKeys must reference configured keys with valid JavaScript identifiers.");
|
|
3381
|
-
}
|
|
3382
|
-
if (["MODE", "BASE_URL", "PROD", "DEV", "SSR"].includes(key)) {
|
|
3383
|
-
throw new SecretLoadError("CONFIGURATION", "Vite built-in environment constants cannot be overridden by secrets.");
|
|
3384
|
-
}
|
|
3385
|
-
}
|
|
3386
|
-
return Object.fromEntries(keys.map((key) => [`import.meta.env.${key}`, JSON.stringify(values[key])]));
|
|
3387
|
-
}
|
|
3388
|
-
function viteSecretSetPlugin(secretSet, options = {}) {
|
|
3389
943
|
return {
|
|
3390
|
-
|
|
3391
|
-
|
|
944
|
+
on: (event, listener) => add(event, listener, false),
|
|
945
|
+
once: (event, listener) => add(event, listener, true),
|
|
946
|
+
off(event, listener) {
|
|
947
|
+
listeners.get(event)?.delete(listener);
|
|
948
|
+
},
|
|
949
|
+
emit(event, ...[payload]) {
|
|
950
|
+
const map = listeners.get(event);
|
|
951
|
+
if (!map)
|
|
952
|
+
return;
|
|
953
|
+
const errors = [];
|
|
954
|
+
for (const [listener, once] of [...map]) {
|
|
955
|
+
if (!map.has(listener))
|
|
956
|
+
continue;
|
|
957
|
+
if (once)
|
|
958
|
+
map.delete(listener);
|
|
959
|
+
try {
|
|
960
|
+
listener(payload);
|
|
961
|
+
} catch (error) {
|
|
962
|
+
errors.push(error);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
if (errors.length === 1)
|
|
966
|
+
throw errors[0];
|
|
967
|
+
if (errors.length > 1) {
|
|
968
|
+
throw new AggregateError(errors, `${errors.length} listeners threw for "${String(event)}"`);
|
|
969
|
+
}
|
|
970
|
+
},
|
|
971
|
+
clear(event) {
|
|
972
|
+
if (event === undefined)
|
|
973
|
+
listeners.clear();
|
|
974
|
+
else
|
|
975
|
+
listeners.delete(event);
|
|
976
|
+
},
|
|
977
|
+
listenerCount: (event) => listeners.get(event)?.size ?? 0
|
|
3392
978
|
};
|
|
3393
979
|
}
|
|
3394
|
-
|
|
3395
|
-
// src/secrets/index.ts
|
|
3396
|
-
function getSecretEnvironment() {
|
|
3397
|
-
return resolveSecretEnvironment(undefined, getProcessEnvironment());
|
|
3398
|
-
}
|
|
3399
|
-
async function defineSecretSet2(keys, options) {
|
|
3400
|
-
let loader;
|
|
3401
|
-
if (!options.loader) {
|
|
3402
|
-
validateInfisicalOrganizationId(options.organizationId);
|
|
3403
|
-
loader = createDefaultInfisicalLoader(options);
|
|
3404
|
-
} else {
|
|
3405
|
-
loader = options.loader;
|
|
3406
|
-
}
|
|
3407
|
-
return defineSecretSet(keys, { ...options, loader }, getProcessEnvironment());
|
|
3408
|
-
}
|
|
3409
|
-
function createDefaultInfisicalLoader(options) {
|
|
3410
|
-
return (context) => loadInfisicalSecrets({
|
|
3411
|
-
...context,
|
|
3412
|
-
projectId: options.projectId,
|
|
3413
|
-
organizationId: options.organizationId,
|
|
3414
|
-
siteUrl: options.siteUrl ?? DEFAULT_INFISICAL_SITE_URL,
|
|
3415
|
-
secretPath: options.secretPath,
|
|
3416
|
-
auth: options.auth,
|
|
3417
|
-
clientIdEnvKey: options.clientIdEnvKey ?? "INFISICAL_CLIENT_ID",
|
|
3418
|
-
clientSecretEnvKey: options.clientSecretEnvKey ?? "INFISICAL_CLIENT_SECRET"
|
|
3419
|
-
});
|
|
3420
|
-
}
|
|
3421
980
|
export {
|
|
3422
|
-
|
|
3423
|
-
ApiError,
|
|
3424
|
-
ConfigError,
|
|
3425
|
-
DEFAULT_INFISICAL_SITE_URL,
|
|
3426
|
-
HttpResponseError,
|
|
981
|
+
InvariantError2 as InvariantError,
|
|
3427
982
|
METRICS_SERIES_PALETTE_SIZE,
|
|
3428
|
-
|
|
3429
|
-
PriorityQueue,
|
|
3430
|
-
RequestAbortedError,
|
|
3431
|
-
RequestTimeoutError,
|
|
3432
|
-
RequestValidationError,
|
|
3433
|
-
ResponseValidationError,
|
|
3434
|
-
SecretLoadError,
|
|
983
|
+
PromiseTimeoutError2 as PromiseTimeoutError,
|
|
3435
984
|
SimpleStateMachine,
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
985
|
+
addPeriod,
|
|
986
|
+
anySignal2 as anySignal,
|
|
987
|
+
assertNever,
|
|
988
|
+
backgroundPromise2 as backgroundPromise,
|
|
989
|
+
backgroundPromiseSync2 as backgroundPromiseSync,
|
|
990
|
+
buildMailto2 as buildMailto,
|
|
991
|
+
buildQueryString2 as buildQueryString,
|
|
992
|
+
buildUrl2 as buildUrl,
|
|
3439
993
|
cacheFunction,
|
|
3440
|
-
chunkArray,
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
994
|
+
chunkArray2 as chunkArray,
|
|
995
|
+
clamp,
|
|
996
|
+
compact2 as compact,
|
|
997
|
+
contrastRatio,
|
|
998
|
+
contrastText,
|
|
999
|
+
countBy2 as countBy,
|
|
1000
|
+
createCache2 as createCache,
|
|
1001
|
+
createEmitter,
|
|
1002
|
+
createHeartbeat,
|
|
1003
|
+
createLatestGuard2 as createLatestGuard,
|
|
3446
1004
|
createResolveColorsByKeys,
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
1005
|
+
createStateMachine,
|
|
1006
|
+
debounce,
|
|
1007
|
+
ensureLeadingSlash2 as ensureLeadingSlash,
|
|
1008
|
+
err2 as err,
|
|
1009
|
+
escapeRegExp2 as escapeRegExp,
|
|
1010
|
+
exponentialBackoff2 as exponentialBackoff,
|
|
1011
|
+
extractNumber2 as extractNumber,
|
|
1012
|
+
findArrayDifferenceByKey2 as findArrayDifferenceByKey,
|
|
1013
|
+
foldForSearch2 as foldForSearch,
|
|
3453
1014
|
formatPeriodLabel,
|
|
3454
|
-
generateId,
|
|
3455
|
-
|
|
3456
|
-
|
|
1015
|
+
generateId2 as generateId,
|
|
1016
|
+
getAtPath,
|
|
1017
|
+
getDomainFaviconUrl2 as getDomainFaviconUrl,
|
|
1018
|
+
getErrorMessage2 as getErrorMessage,
|
|
3457
1019
|
getHeatmapCellStyle,
|
|
1020
|
+
getPeriod,
|
|
3458
1021
|
getPreviousReportPeriod,
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
groupBy,
|
|
1022
|
+
getUniqueValues2 as getUniqueValues,
|
|
1023
|
+
getUniqueValuesByKey2 as getUniqueValuesByKey,
|
|
1024
|
+
groupBy2 as groupBy,
|
|
1025
|
+
hasKey2 as hasKey,
|
|
3464
1026
|
hashStringToColorIndex,
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
isFailure,
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
isSuccess,
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
1027
|
+
initials2 as initials,
|
|
1028
|
+
invariant2 as invariant,
|
|
1029
|
+
isBoolean2 as isBoolean,
|
|
1030
|
+
isDefined2 as isDefined,
|
|
1031
|
+
isFailure2 as isFailure,
|
|
1032
|
+
isFunction2 as isFunction,
|
|
1033
|
+
isNonEmpty2 as isNonEmpty,
|
|
1034
|
+
isNumber2 as isNumber,
|
|
1035
|
+
isOneOf2 as isOneOf,
|
|
1036
|
+
isPlainObject2 as isPlainObject,
|
|
1037
|
+
isRecord2 as isRecord,
|
|
1038
|
+
isString2 as isString,
|
|
1039
|
+
isSuccess2 as isSuccess,
|
|
1040
|
+
isTruthy2 as isTruthy,
|
|
1041
|
+
keyBy2 as keyBy,
|
|
1042
|
+
keyedDebounce,
|
|
1043
|
+
lerp,
|
|
1044
|
+
linearRegression,
|
|
3482
1045
|
linearRegressionTrend,
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
1046
|
+
mapValues,
|
|
1047
|
+
matchesSearch2 as matchesSearch,
|
|
1048
|
+
mixColors,
|
|
1049
|
+
must2 as must,
|
|
1050
|
+
nonEmpty2 as nonEmpty,
|
|
1051
|
+
normalize,
|
|
1052
|
+
normalizeDomain2 as normalizeDomain,
|
|
1053
|
+
normalizeWhitespace2 as normalizeWhitespace,
|
|
1054
|
+
ok2 as ok,
|
|
1055
|
+
omit,
|
|
1056
|
+
once,
|
|
1057
|
+
pMap2 as pMap,
|
|
1058
|
+
parseColor,
|
|
1059
|
+
parseRetryAfter2 as parseRetryAfter,
|
|
1060
|
+
partition2 as partition,
|
|
1061
|
+
pick,
|
|
1062
|
+
pickFromPalette,
|
|
1063
|
+
pickItemsInArray2 as pickItemsInArray,
|
|
1064
|
+
promiseTimeout2 as promiseTimeout,
|
|
1065
|
+
raceAbort2 as raceAbort,
|
|
1066
|
+
range2 as range,
|
|
1067
|
+
recordFromKeys,
|
|
1068
|
+
relativeLuminance,
|
|
1069
|
+
replacePathParams2 as replacePathParams,
|
|
3491
1070
|
resolveSeriesColorMap,
|
|
1071
|
+
retry2 as retry,
|
|
1072
|
+
safeJsonParse2 as safeJsonParse,
|
|
3492
1073
|
seriesColorKey,
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
1074
|
+
setAtPath,
|
|
1075
|
+
sha256Id2 as sha256Id,
|
|
1076
|
+
shade,
|
|
1077
|
+
slugify2 as slugify,
|
|
1078
|
+
sortBy2 as sortBy,
|
|
1079
|
+
stableStringify2 as stableStringify,
|
|
1080
|
+
startTimer2 as startTimer,
|
|
1081
|
+
sumBy2 as sumBy,
|
|
1082
|
+
throttle,
|
|
1083
|
+
timed2 as timed,
|
|
1084
|
+
timeoutSignal2 as timeoutSignal,
|
|
1085
|
+
toArray2 as toArray,
|
|
1086
|
+
toError2 as toError,
|
|
1087
|
+
toHex,
|
|
1088
|
+
toRgbString,
|
|
1089
|
+
trimTrailingSlash2 as trimTrailingSlash,
|
|
1090
|
+
truncate2 as truncate,
|
|
1091
|
+
tryCatch2 as tryCatch,
|
|
1092
|
+
typedEntries,
|
|
1093
|
+
typedFromEntries,
|
|
1094
|
+
typedKeys,
|
|
3497
1095
|
typedSwitch,
|
|
3498
|
-
unwrapResult,
|
|
3499
|
-
viteSecretSetPlugin,
|
|
1096
|
+
unwrapResult2 as unwrapResult,
|
|
3500
1097
|
withAlpha,
|
|
3501
|
-
|
|
3502
|
-
zodMinMaxFilter,
|
|
3503
|
-
zodToExample,
|
|
3504
|
-
zodToTypeString
|
|
1098
|
+
withTimeout2 as withTimeout
|
|
3505
1099
|
};
|