hanzio 1.1.0 → 2.0.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 +832 -219
- package/dist/api-wrapper/builders.d.ts +15 -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 +40 -2
- package/dist/api-wrapper/headers.d.ts +2 -1
- 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 +12 -10
- package/dist/api-wrapper/types.d.ts +104 -18
- package/dist/array/index.d.ts +50 -4
- package/dist/cache/createCache.d.ts +31 -0
- package/dist/cache/index.d.ts +45 -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-181e6vfw.js +29 -0
- package/dist/index-8r2vdzjg.js +87 -0
- package/dist/index-hy7kbb99.js +157 -0
- package/dist/{src/secrets/loaders.js → index-j1ybktbr.js} +608 -405
- package/dist/index-k9yx6bne.js +89 -0
- package/dist/index-kj1phgrx.js +169 -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-srt06xd2.js +15 -0
- package/dist/index-wjyb9bsd.js +263 -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 +47 -0
- package/dist/secrets/deadline.d.ts +6 -0
- package/dist/secrets/environment.d.ts +9 -0
- package/dist/secrets/errors.d.ts +11 -0
- package/dist/secrets/index.d.ts +13 -25
- package/dist/secrets/infisical-cli.d.ts +6 -0
- package/dist/secrets/infisical-config.d.ts +17 -0
- package/dist/secrets/infisical-session.d.ts +4 -0
- package/dist/secrets/infisical.d.ts +8 -13
- package/dist/secrets/loaders.d.ts +5 -12
- package/dist/secrets/vite.d.ts +7 -3
- package/dist/secrets/worker-loader.d.ts +9 -0
- package/dist/secrets/worker.d.ts +3 -0
- package/dist/sitemap/index.d.ts +48 -6
- package/dist/src/api-wrapper/index.js +60 -1100
- 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 +863 -2809
- package/dist/src/jwt/index.js +138 -92
- package/dist/src/p-queue/index.js +9 -437
- package/dist/src/secrets/index.js +261 -1377
- package/dist/src/secrets/vite.js +6 -17
- package/dist/src/secrets/worker.js +12 -0
- package/dist/src/sitemap/index.js +183 -511
- package/dist/src/storage/index.js +119 -0
- package/dist/src/zod/index.js +347 -174
- 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 +37 -26
- 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 +87 -7
- package/src/api-wrapper/actions-impl.ts +11 -72
- package/src/api-wrapper/builders.ts +84 -52
- package/src/api-wrapper/create-client.ts +246 -96
- 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 +117 -2
- package/src/api-wrapper/headers.ts +19 -1
- 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 +43 -0
- package/src/api-wrapper/shared.ts +21 -2
- package/src/api-wrapper/testing.ts +48 -0
- package/src/api-wrapper/transport.ts +68 -74
- package/src/api-wrapper/types.ts +186 -18
- package/src/array/index.ts +195 -42
- package/src/cache/createCache.ts +157 -0
- package/src/cache/index.ts +235 -83
- 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 +348 -0
- package/src/secrets/deadline.ts +69 -0
- package/src/secrets/environment.ts +69 -0
- package/src/secrets/errors.ts +29 -0
- package/src/secrets/index.ts +79 -210
- package/src/secrets/infisical-cli.ts +99 -0
- package/src/secrets/infisical-config.ts +70 -0
- package/src/secrets/infisical-session.ts +42 -0
- package/src/secrets/infisical.ts +214 -66
- package/src/secrets/loaders.ts +24 -48
- package/src/secrets/vite.ts +44 -15
- package/src/secrets/worker-loader.ts +56 -0
- package/src/secrets/worker.ts +4 -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 +134 -103
- 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/infisical.js +0 -1194
- package/dist/zod/index.js +0 -210
package/dist/src/index.js
CHANGED
|
@@ -1,2332 +1,91 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return "any";
|
|
87
|
-
case "unknown":
|
|
88
|
-
return "unknown";
|
|
89
|
-
case "record": {
|
|
90
|
-
const valueType = getDefProp(def, "valueType");
|
|
91
|
-
return valueType ? `Record<string, ${zodToTypeString(valueType, indent)}>` : "Record<string, unknown>";
|
|
92
|
-
}
|
|
93
|
-
case "enum": {
|
|
94
|
-
const values = getDefProp(def, "values");
|
|
95
|
-
return values ? values.map((v) => `"${v}"`).join(" | ") : "string";
|
|
96
|
-
}
|
|
97
|
-
case "literal": {
|
|
98
|
-
const value = getDefProp(def, "value");
|
|
99
|
-
return typeof value === "string" ? `"${value}"` : String(value);
|
|
100
|
-
}
|
|
101
|
-
case "union": {
|
|
102
|
-
const options = getDefProp(def, "options");
|
|
103
|
-
return options ? options.map((o) => zodToTypeString(o, indent)).join(" | ") : "unknown";
|
|
104
|
-
}
|
|
105
|
-
case "optional": {
|
|
106
|
-
const inner = getDefProp(def, "innerType");
|
|
107
|
-
return inner ? `${zodToTypeString(inner, indent)} | undefined` : "unknown";
|
|
108
|
-
}
|
|
109
|
-
case "nullable": {
|
|
110
|
-
const inner = getDefProp(def, "innerType");
|
|
111
|
-
return inner ? `${zodToTypeString(inner, indent)} | null` : "unknown";
|
|
112
|
-
}
|
|
113
|
-
case "default": {
|
|
114
|
-
const inner = getDefProp(def, "innerType");
|
|
115
|
-
return inner ? zodToTypeString(inner, indent) : "unknown";
|
|
116
|
-
}
|
|
117
|
-
case "ZodObject":
|
|
118
|
-
case "ZodArray":
|
|
119
|
-
case "ZodString":
|
|
120
|
-
case "ZodNumber":
|
|
121
|
-
case "ZodBoolean":
|
|
122
|
-
case "ZodDate":
|
|
123
|
-
case "ZodNull":
|
|
124
|
-
case "ZodUndefined":
|
|
125
|
-
case "ZodAny":
|
|
126
|
-
case "ZodUnknown":
|
|
127
|
-
case "ZodRecord":
|
|
128
|
-
case "ZodEnum":
|
|
129
|
-
case "ZodLiteral":
|
|
130
|
-
case "ZodUnion":
|
|
131
|
-
case "ZodOptional":
|
|
132
|
-
case "ZodNullable":
|
|
133
|
-
case "ZodDefault":
|
|
134
|
-
return zodToTypeString(schema, indent);
|
|
135
|
-
default:
|
|
136
|
-
return "unknown";
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
function zodToExample(schema) {
|
|
140
|
-
const def = schema._zod?.def ?? schema._def;
|
|
141
|
-
if (!def)
|
|
142
|
-
return null;
|
|
143
|
-
const schemaType = getDefProp(def, "type") ?? getDefProp(def, "typeName");
|
|
144
|
-
switch (schemaType) {
|
|
145
|
-
case "object": {
|
|
146
|
-
const shape = getDefProp(def, "shape");
|
|
147
|
-
if (!shape)
|
|
148
|
-
return {};
|
|
149
|
-
const result = {};
|
|
150
|
-
for (const [key, value] of Object.entries(shape)) {
|
|
151
|
-
const valueType = getDefProp(value._zod?.def ?? value._def, "type");
|
|
152
|
-
const isOptional = valueType === "optional";
|
|
153
|
-
if (!isOptional) {
|
|
154
|
-
result[key] = zodToExample(value);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return result;
|
|
158
|
-
}
|
|
159
|
-
case "array": {
|
|
160
|
-
const itemType = getDefProp(def, "element") ?? getDefProp(def, "type");
|
|
161
|
-
return itemType ? [zodToExample(itemType)] : [];
|
|
162
|
-
}
|
|
163
|
-
case "string":
|
|
164
|
-
return "";
|
|
165
|
-
case "number":
|
|
166
|
-
return 0;
|
|
167
|
-
case "boolean":
|
|
168
|
-
return false;
|
|
169
|
-
case "enum": {
|
|
170
|
-
const values = getDefProp(def, "values");
|
|
171
|
-
return values?.[0] ?? "";
|
|
172
|
-
}
|
|
173
|
-
case "literal":
|
|
174
|
-
return getDefProp(def, "value");
|
|
175
|
-
case "optional":
|
|
176
|
-
case "default": {
|
|
177
|
-
const inner = getDefProp(def, "innerType");
|
|
178
|
-
return inner ? zodToExample(inner) : null;
|
|
179
|
-
}
|
|
180
|
-
case "record":
|
|
181
|
-
return {};
|
|
182
|
-
default:
|
|
183
|
-
return null;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// src/zod/index.ts
|
|
188
|
-
function createZId(typeName) {
|
|
189
|
-
return z3.string().min(1, `${typeName} ID is required`).brand();
|
|
190
|
-
}
|
|
191
|
-
function zSingleOrArray(schema) {
|
|
192
|
-
return z3.union([z3.array(schema), schema]);
|
|
193
|
-
}
|
|
194
|
-
function normalizeSingleOrArray(value) {
|
|
195
|
-
if (value == null)
|
|
196
|
-
return [];
|
|
197
|
-
if (Array.isArray(value))
|
|
198
|
-
return [...value];
|
|
199
|
-
return [value];
|
|
200
|
-
}
|
|
201
|
-
// src/api-wrapper/headers.ts
|
|
202
|
-
var CANONICAL_HEADER_NAMES = {
|
|
203
|
-
"content-type": "Content-Type",
|
|
204
|
-
authorization: "Authorization",
|
|
205
|
-
accept: "Accept",
|
|
206
|
-
"user-agent": "User-Agent",
|
|
207
|
-
"x-request-id": "X-Request-Id",
|
|
208
|
-
"x-correlation-id": "X-Correlation-Id"
|
|
209
|
-
};
|
|
210
|
-
function normalizeHeaders(headers) {
|
|
211
|
-
const result = {};
|
|
212
|
-
const lowerToCanonical = new Map;
|
|
213
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
214
|
-
const lower = key.toLowerCase();
|
|
215
|
-
const canonical = CANONICAL_HEADER_NAMES[lower] ?? key;
|
|
216
|
-
const existing = lowerToCanonical.get(lower);
|
|
217
|
-
if (existing && existing !== canonical)
|
|
218
|
-
delete result[existing];
|
|
219
|
-
lowerToCanonical.set(lower, canonical);
|
|
220
|
-
result[canonical] = value;
|
|
221
|
-
}
|
|
222
|
-
return result;
|
|
223
|
-
}
|
|
224
|
-
function getHeader(headers, name) {
|
|
225
|
-
const lower = name.toLowerCase();
|
|
226
|
-
const canonical = CANONICAL_HEADER_NAMES[lower];
|
|
227
|
-
if (canonical !== undefined) {
|
|
228
|
-
if (headers[canonical] !== undefined)
|
|
229
|
-
return headers[canonical];
|
|
230
|
-
}
|
|
231
|
-
for (const [k, v] of Object.entries(headers)) {
|
|
232
|
-
if (k.toLowerCase() === lower)
|
|
233
|
-
return v;
|
|
234
|
-
}
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
function hasJsonContentType(headers) {
|
|
238
|
-
const ct = getHeader(headers, "content-type") ?? "";
|
|
239
|
-
return ct.includes("application/json");
|
|
240
|
-
}
|
|
241
|
-
function buildFetchBody(body, headers) {
|
|
242
|
-
if (body === undefined || body === null)
|
|
243
|
-
return;
|
|
244
|
-
if (body instanceof FormData || body instanceof Blob)
|
|
245
|
-
return body;
|
|
246
|
-
if (hasJsonContentType(headers))
|
|
247
|
-
return JSON.stringify(body);
|
|
248
|
-
return body;
|
|
249
|
-
}
|
|
250
|
-
function getHeadersAsObject(headers) {
|
|
251
|
-
const result = {};
|
|
252
|
-
headers.forEach((value, key) => {
|
|
253
|
-
result[key] = value;
|
|
254
|
-
});
|
|
255
|
-
return result;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// src/api-wrapper/zod-issues.ts
|
|
259
|
-
import { ZodError } from "zod";
|
|
260
|
-
function formatZodIssues(error, rawData) {
|
|
261
|
-
return error.issues.map((issue) => {
|
|
262
|
-
const pathArray = [...issue.path];
|
|
263
|
-
const path = formatIssuePath(pathArray);
|
|
264
|
-
const value = getValueAtPath(rawData, pathArray);
|
|
265
|
-
const valuePreview = value === undefined ? undefined : previewValue(value);
|
|
266
|
-
const raw = issue;
|
|
267
|
-
const expected = typeof raw.expected === "string" ? raw.expected : undefined;
|
|
268
|
-
const received = typeof raw.received === "string" ? raw.received : value !== undefined ? typeofVerbose(value) : undefined;
|
|
269
|
-
const known = new Set([
|
|
270
|
-
"code",
|
|
271
|
-
"path",
|
|
272
|
-
"message",
|
|
273
|
-
"expected",
|
|
274
|
-
"received",
|
|
275
|
-
"fatal"
|
|
276
|
-
]);
|
|
277
|
-
const extra = {};
|
|
278
|
-
for (const [k, v] of Object.entries(raw)) {
|
|
279
|
-
if (!known.has(k) && v !== undefined)
|
|
280
|
-
extra[k] = v;
|
|
281
|
-
}
|
|
282
|
-
return {
|
|
283
|
-
path,
|
|
284
|
-
pathArray,
|
|
285
|
-
code: issue.code,
|
|
286
|
-
message: issue.message,
|
|
287
|
-
expected,
|
|
288
|
-
received,
|
|
289
|
-
value,
|
|
290
|
-
valuePreview,
|
|
291
|
-
extra: Object.keys(extra).length > 0 ? extra : undefined
|
|
292
|
-
};
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
function renderIssueSummary(issues) {
|
|
296
|
-
if (issues.length === 0)
|
|
297
|
-
return "(no issues reported)";
|
|
298
|
-
return issues.map((issue, idx) => {
|
|
299
|
-
const parts = [];
|
|
300
|
-
parts.push(` [${idx + 1}] ${issue.path}: ${issue.message}`);
|
|
301
|
-
if (issue.expected || issue.received) {
|
|
302
|
-
const exp = issue.expected ? `expected ${issue.expected}` : "";
|
|
303
|
-
const rec = issue.received ? `received ${issue.received}` : "";
|
|
304
|
-
parts.push(` (${[exp, rec].filter(Boolean).join(", ")})`);
|
|
305
|
-
}
|
|
306
|
-
if (issue.valuePreview !== undefined) {
|
|
307
|
-
parts.push(` value: ${issue.valuePreview}`);
|
|
308
|
-
}
|
|
309
|
-
if (issue.extra) {
|
|
310
|
-
for (const [k, v] of Object.entries(issue.extra)) {
|
|
311
|
-
parts.push(` ${k}: ${previewValue(v, 120)}`);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
return parts.join(`
|
|
315
|
-
`);
|
|
316
|
-
}).join(`
|
|
317
|
-
`);
|
|
318
|
-
}
|
|
319
|
-
function formatIssuePath(pathArray) {
|
|
320
|
-
if (pathArray.length === 0)
|
|
321
|
-
return "<root>";
|
|
322
|
-
let out = "";
|
|
323
|
-
for (const segment of pathArray) {
|
|
324
|
-
if (typeof segment === "number") {
|
|
325
|
-
out += `[${segment}]`;
|
|
326
|
-
} else if (/^[A-Za-z_$][\w$]*$/.test(segment)) {
|
|
327
|
-
out += out === "" ? segment : `.${segment}`;
|
|
328
|
-
} else {
|
|
329
|
-
out += `[${JSON.stringify(segment)}]`;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
return out;
|
|
333
|
-
}
|
|
334
|
-
function getValueAtPath(data, pathArray) {
|
|
335
|
-
let current = data;
|
|
336
|
-
for (const segment of pathArray) {
|
|
337
|
-
if (current === null || current === undefined)
|
|
338
|
-
return;
|
|
339
|
-
if (typeof current !== "object")
|
|
340
|
-
return;
|
|
341
|
-
current = current[segment];
|
|
342
|
-
}
|
|
343
|
-
return current;
|
|
344
|
-
}
|
|
345
|
-
function typeofVerbose(value) {
|
|
346
|
-
if (value === null)
|
|
347
|
-
return "null";
|
|
348
|
-
if (Array.isArray(value))
|
|
349
|
-
return "array";
|
|
350
|
-
return typeof value;
|
|
351
|
-
}
|
|
352
|
-
function previewValue(value, maxLen = 200) {
|
|
353
|
-
let str;
|
|
354
|
-
try {
|
|
355
|
-
if (value === undefined)
|
|
356
|
-
str = "undefined";
|
|
357
|
-
else if (typeof value === "string")
|
|
358
|
-
str = JSON.stringify(value);
|
|
359
|
-
else
|
|
360
|
-
str = JSON.stringify(value);
|
|
361
|
-
} catch {
|
|
362
|
-
str = String(value);
|
|
363
|
-
}
|
|
364
|
-
if (str === undefined)
|
|
365
|
-
str = String(value);
|
|
366
|
-
if (str.length > maxLen)
|
|
367
|
-
str = `${str.slice(0, maxLen)}…`;
|
|
368
|
-
return str;
|
|
369
|
-
}
|
|
370
|
-
function makeJsonParseZodError(error, text) {
|
|
371
|
-
const message = error instanceof Error ? `Response body was not valid JSON: ${error.message}` : "Response body was not valid JSON";
|
|
372
|
-
const preview = text.length > 200 ? `${text.slice(0, 200)}…` : text;
|
|
373
|
-
return new ZodError([
|
|
374
|
-
{
|
|
375
|
-
code: "custom",
|
|
376
|
-
path: [],
|
|
377
|
-
message: `${message} (body preview: ${JSON.stringify(preview)})`,
|
|
378
|
-
input: text
|
|
379
|
-
}
|
|
380
|
-
]);
|
|
381
|
-
}
|
|
382
|
-
function tryParseJson(body) {
|
|
383
|
-
if (!body)
|
|
384
|
-
return;
|
|
385
|
-
try {
|
|
386
|
-
return JSON.parse(body);
|
|
387
|
-
} catch {
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// src/api-wrapper/errors.ts
|
|
393
|
-
var MAX_BODY_PREVIEW = 500;
|
|
394
|
-
|
|
395
|
-
class ApiError extends Error {
|
|
396
|
-
context;
|
|
397
|
-
cause;
|
|
398
|
-
constructor(message, context, options) {
|
|
399
|
-
super(message);
|
|
400
|
-
this.name = "ApiError";
|
|
401
|
-
this.context = context;
|
|
402
|
-
this.cause = options?.cause;
|
|
403
|
-
}
|
|
404
|
-
toJSON() {
|
|
405
|
-
return {
|
|
406
|
-
name: this.name,
|
|
407
|
-
message: this.message,
|
|
408
|
-
context: this.context
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
class HttpResponseError extends ApiError {
|
|
414
|
-
status;
|
|
415
|
-
body;
|
|
416
|
-
bodyJson;
|
|
417
|
-
responseHeaders;
|
|
418
|
-
requestId;
|
|
419
|
-
constructor(status, body, responseHeaders, context) {
|
|
420
|
-
const preview = body.length > MAX_BODY_PREVIEW ? `${body.slice(0, MAX_BODY_PREVIEW)}…` : body;
|
|
421
|
-
super(`HTTP ${status} ${context.method} ${context.url} - ${preview}`, context);
|
|
422
|
-
this.name = "HttpResponseError";
|
|
423
|
-
this.status = status;
|
|
424
|
-
this.body = body;
|
|
425
|
-
this.responseHeaders = responseHeaders;
|
|
426
|
-
this.requestId = getHeader(responseHeaders, "x-request-id") ?? getHeader(responseHeaders, "x-correlation-id");
|
|
427
|
-
this.bodyJson = tryParseJson(body);
|
|
428
|
-
}
|
|
429
|
-
toJSON() {
|
|
430
|
-
return {
|
|
431
|
-
...super.toJSON(),
|
|
432
|
-
status: this.status,
|
|
433
|
-
body: this.body,
|
|
434
|
-
bodyJson: this.bodyJson,
|
|
435
|
-
requestId: this.requestId,
|
|
436
|
-
responseHeaders: this.responseHeaders
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
class ResponseValidationError extends ApiError {
|
|
442
|
-
zodError;
|
|
443
|
-
rawResponse;
|
|
444
|
-
issues;
|
|
445
|
-
validationIssues;
|
|
446
|
-
constructor(zodError, rawResponse, context) {
|
|
447
|
-
const issues = formatZodIssues(zodError, rawResponse);
|
|
448
|
-
const summary = renderIssueSummary(issues);
|
|
449
|
-
super(`Response validation failed (${context.method} ${context.url}):
|
|
450
|
-
${summary}`, context, { cause: zodError });
|
|
451
|
-
this.name = "ResponseValidationError";
|
|
452
|
-
this.zodError = zodError;
|
|
453
|
-
this.rawResponse = rawResponse;
|
|
454
|
-
this.issues = issues;
|
|
455
|
-
this.validationIssues = issues.map((i) => `${i.path}: ${i.message}`).join("; ");
|
|
456
|
-
}
|
|
457
|
-
toJSON() {
|
|
458
|
-
return {
|
|
459
|
-
...super.toJSON(),
|
|
460
|
-
issues: this.issues,
|
|
461
|
-
validationIssues: this.validationIssues,
|
|
462
|
-
rawResponsePreview: previewValue(this.rawResponse, 1000)
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
class RequestValidationError extends ApiError {
|
|
468
|
-
zodError;
|
|
469
|
-
target;
|
|
470
|
-
rawInput;
|
|
471
|
-
issues;
|
|
472
|
-
validationIssues;
|
|
473
|
-
constructor(zodError, target, rawInput, context) {
|
|
474
|
-
const issues = formatZodIssues(zodError, rawInput);
|
|
475
|
-
const summary = renderIssueSummary(issues);
|
|
476
|
-
super(`Request ${target} validation failed (${context.method} ${context.url}):
|
|
477
|
-
${summary}`, context, { cause: zodError });
|
|
478
|
-
this.name = "RequestValidationError";
|
|
479
|
-
this.zodError = zodError;
|
|
480
|
-
this.target = target;
|
|
481
|
-
this.rawInput = rawInput;
|
|
482
|
-
this.issues = issues;
|
|
483
|
-
this.validationIssues = issues.map((i) => `${i.path}: ${i.message}`).join("; ");
|
|
484
|
-
}
|
|
485
|
-
toJSON() {
|
|
486
|
-
return {
|
|
487
|
-
...super.toJSON(),
|
|
488
|
-
target: this.target,
|
|
489
|
-
issues: this.issues,
|
|
490
|
-
validationIssues: this.validationIssues
|
|
491
|
-
};
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
class RequestTimeoutError extends ApiError {
|
|
496
|
-
timeoutMs;
|
|
497
|
-
constructor(timeoutMs, context) {
|
|
498
|
-
super(`Request timed out after ${timeoutMs}ms: ${context.method} ${context.url}`, context);
|
|
499
|
-
this.name = "RequestTimeoutError";
|
|
500
|
-
this.timeoutMs = timeoutMs;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
class NetworkError extends ApiError {
|
|
505
|
-
constructor(message, context, cause) {
|
|
506
|
-
super(message, context, { cause });
|
|
507
|
-
this.name = "NetworkError";
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
class RequestAbortedError extends ApiError {
|
|
512
|
-
constructor(context, cause) {
|
|
513
|
-
super(`Request aborted: ${context.method} ${context.url}`, context, {
|
|
514
|
-
cause
|
|
515
|
-
});
|
|
516
|
-
this.name = "RequestAbortedError";
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
class ConfigError extends ApiError {
|
|
521
|
-
constructor(message, context) {
|
|
522
|
-
super(message, context);
|
|
523
|
-
this.name = "ConfigError";
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
class ActionError extends ApiError {
|
|
528
|
-
constructor(message, context, cause) {
|
|
529
|
-
super(message, context, { cause });
|
|
530
|
-
this.name = "ActionError";
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
var isApiError = (e) => e instanceof ApiError;
|
|
534
|
-
var isHttpResponseError = (e) => e instanceof HttpResponseError;
|
|
535
|
-
var isResponseValidationError = (e) => e instanceof ResponseValidationError;
|
|
536
|
-
var isRequestValidationError = (e) => e instanceof RequestValidationError;
|
|
537
|
-
var isRequestTimeoutError = (e) => e instanceof RequestTimeoutError;
|
|
538
|
-
var isNetworkError = (e) => e instanceof NetworkError;
|
|
539
|
-
var isRequestAbortedError = (e) => e instanceof RequestAbortedError;
|
|
540
|
-
var isConfigError = (e) => e instanceof ConfigError;
|
|
541
|
-
var isActionError = (e) => e instanceof ActionError;
|
|
542
|
-
// src/api-wrapper/builders.ts
|
|
543
|
-
import { ZodError as ZodError2 } from "zod";
|
|
544
|
-
function buildUrl(endpoint, params, reqQuery, baseApiUrls, defaultBaseApiUrl, url, configError) {
|
|
545
|
-
if (url) {
|
|
546
|
-
const finalUrl2 = replacePathParams(url, params, configError);
|
|
547
|
-
const queryString2 = new URLSearchParams(buildQueryParamsRaw(endpoint, reqQuery)).toString();
|
|
548
|
-
return {
|
|
549
|
-
finalUrl: finalUrl2,
|
|
550
|
-
fullUrl: queryString2 ? `${finalUrl2}?${queryString2}` : finalUrl2
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
const defaultBaseUrl = defaultBaseApiUrl ?? Object.keys(baseApiUrls)[0];
|
|
554
|
-
if (!defaultBaseUrl) {
|
|
555
|
-
throw configError("At least one base API URL is required");
|
|
556
|
-
}
|
|
557
|
-
const baseUrlKey = endpoint.baseApiUrl ?? defaultBaseUrl;
|
|
558
|
-
const configBaseUrl = baseApiUrls[baseUrlKey];
|
|
559
|
-
if (!configBaseUrl) {
|
|
560
|
-
throw configError(`Unknown base API URL: ${baseUrlKey}`);
|
|
561
|
-
}
|
|
562
|
-
const baseUrl = typeof configBaseUrl === "function" ? configBaseUrl() : configBaseUrl;
|
|
563
|
-
const path = typeof endpoint.path === "function" ? endpoint.path(baseUrl) : endpoint.path;
|
|
564
|
-
const finalUrl = replacePathParams(`${baseUrl}${path}`, params, configError);
|
|
565
|
-
const queryString = new URLSearchParams(buildQueryParamsRaw(endpoint, reqQuery)).toString();
|
|
566
|
-
return {
|
|
567
|
-
finalUrl,
|
|
568
|
-
fullUrl: queryString ? `${finalUrl}?${queryString}` : finalUrl
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
function buildQueryParamsRaw(endpoint, query) {
|
|
572
|
-
const defaultParams = endpoint.reqDefaultQueryParams ?? {};
|
|
573
|
-
const merged = { ...defaultParams, ...query ?? {} };
|
|
574
|
-
return Object.fromEntries(Object.entries(merged).filter(([, value]) => value !== undefined).map(([key, value]) => [key, String(value)]));
|
|
575
|
-
}
|
|
576
|
-
function buildQueryParams(endpoint, query, ctx) {
|
|
577
|
-
const defaultParams = endpoint.reqDefaultQueryParams ?? {};
|
|
578
|
-
let parsedQuery;
|
|
579
|
-
if (endpoint.reqQuerySchema) {
|
|
580
|
-
try {
|
|
581
|
-
parsedQuery = endpoint.reqQuerySchema.parse(query);
|
|
582
|
-
} catch (error) {
|
|
583
|
-
if (error instanceof ZodError2) {
|
|
584
|
-
throw new RequestValidationError(error, "query", query, ctx());
|
|
585
|
-
}
|
|
586
|
-
throw error;
|
|
587
|
-
}
|
|
588
|
-
} else {
|
|
589
|
-
parsedQuery = query ?? {};
|
|
590
|
-
}
|
|
591
|
-
const merged = { ...defaultParams, ...parsedQuery };
|
|
592
|
-
return Object.fromEntries(Object.entries(merged).filter(([, value]) => value !== undefined).map(([key, value]) => [key, String(value)]));
|
|
593
|
-
}
|
|
594
|
-
function buildRequestBody(endpoint, body, ctx) {
|
|
595
|
-
if (!endpoint.reqBodySchema)
|
|
596
|
-
return body;
|
|
597
|
-
let parsedBody;
|
|
598
|
-
try {
|
|
599
|
-
parsedBody = endpoint.reqBodySchema.parse(body);
|
|
600
|
-
} catch (error) {
|
|
601
|
-
if (error instanceof ZodError2) {
|
|
602
|
-
throw new RequestValidationError(error, "body", body, ctx());
|
|
603
|
-
}
|
|
604
|
-
throw error;
|
|
605
|
-
}
|
|
606
|
-
if (endpoint.reqBodyFormat !== "form-data") {
|
|
607
|
-
return parsedBody;
|
|
608
|
-
}
|
|
609
|
-
const formData = new FormData;
|
|
610
|
-
for (const [key, value] of Object.entries(parsedBody)) {
|
|
611
|
-
if (value == null)
|
|
612
|
-
continue;
|
|
613
|
-
formData.set(key, value instanceof Blob ? value : String(value));
|
|
614
|
-
}
|
|
615
|
-
return formData;
|
|
616
|
-
}
|
|
617
|
-
function buildHeaders(endpoint, headers, defaultHeaders, ctx) {
|
|
618
|
-
const contentType = endpoint.reqBodyFormat === "json" ? "application/json" : undefined;
|
|
619
|
-
let parsedHeaders;
|
|
620
|
-
if (endpoint.reqHeadersSchema) {
|
|
621
|
-
try {
|
|
622
|
-
parsedHeaders = endpoint.reqHeadersSchema.parse(headers);
|
|
623
|
-
} catch (error) {
|
|
624
|
-
if (error instanceof ZodError2) {
|
|
625
|
-
throw new RequestValidationError(error, "headers", headers, ctx());
|
|
626
|
-
}
|
|
627
|
-
throw error;
|
|
628
|
-
}
|
|
629
|
-
} else {
|
|
630
|
-
parsedHeaders = headers ?? {};
|
|
631
|
-
}
|
|
632
|
-
return normalizeHeaders({
|
|
633
|
-
...defaultHeaders,
|
|
634
|
-
...contentType && { "Content-Type": contentType },
|
|
635
|
-
...endpoint.defaultHeaders,
|
|
636
|
-
...parsedHeaders
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
function replacePathParams(path, params, configError) {
|
|
640
|
-
return path.replace(/:(\w+)/g, (_, key) => {
|
|
641
|
-
const value = params?.[key];
|
|
642
|
-
if (value === undefined) {
|
|
643
|
-
throw configError(`Missing required path parameter: ${key}`);
|
|
644
|
-
}
|
|
645
|
-
return encodeURIComponent(String(value));
|
|
646
|
-
});
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
// src/api-wrapper/actions-impl.ts
|
|
650
|
-
function createActionCache() {
|
|
651
|
-
const store = new Map;
|
|
652
|
-
const isExpired = (entry) => entry.expiresAt !== undefined && entry.expiresAt <= Date.now();
|
|
653
|
-
const cache = async (key, fn, options) => {
|
|
654
|
-
const existing = store.get(key);
|
|
655
|
-
if (existing && !isExpired(existing)) {
|
|
656
|
-
if (existing.inflight)
|
|
657
|
-
return existing.inflight;
|
|
658
|
-
if (existing.hasValue)
|
|
659
|
-
return existing.value;
|
|
660
|
-
}
|
|
661
|
-
const promise = (async () => fn())();
|
|
662
|
-
const entry = {
|
|
663
|
-
hasValue: false,
|
|
664
|
-
inflight: promise,
|
|
665
|
-
expiresAt: options?.ttlMs !== undefined ? Date.now() + options.ttlMs : undefined
|
|
666
|
-
};
|
|
667
|
-
store.set(key, entry);
|
|
668
|
-
try {
|
|
669
|
-
const value = await promise;
|
|
670
|
-
entry.value = value;
|
|
671
|
-
entry.hasValue = true;
|
|
672
|
-
entry.inflight = undefined;
|
|
673
|
-
return value;
|
|
674
|
-
} catch (error) {
|
|
675
|
-
store.delete(key);
|
|
676
|
-
throw error;
|
|
677
|
-
}
|
|
678
|
-
};
|
|
679
|
-
cache.get = (key) => {
|
|
680
|
-
const entry = store.get(key);
|
|
681
|
-
if (!entry || !entry.hasValue)
|
|
682
|
-
return;
|
|
683
|
-
if (isExpired(entry)) {
|
|
684
|
-
store.delete(key);
|
|
685
|
-
return;
|
|
686
|
-
}
|
|
687
|
-
return entry.value;
|
|
688
|
-
};
|
|
689
|
-
cache.set = (key, value, options) => {
|
|
690
|
-
store.set(key, {
|
|
691
|
-
value,
|
|
692
|
-
hasValue: true,
|
|
693
|
-
expiresAt: options?.ttlMs !== undefined ? Date.now() + options.ttlMs : undefined
|
|
694
|
-
});
|
|
695
|
-
};
|
|
696
|
-
cache.invalidate = (key) => {
|
|
697
|
-
store.delete(key);
|
|
698
|
-
};
|
|
699
|
-
cache.clear = () => {
|
|
700
|
-
store.clear();
|
|
701
|
-
};
|
|
702
|
-
return cache;
|
|
703
|
-
}
|
|
704
|
-
function makeDefineAction() {
|
|
705
|
-
function defineAction(...args) {
|
|
706
|
-
if (args.length === 0) {
|
|
707
|
-
return (def2) => ({
|
|
708
|
-
...def2,
|
|
709
|
-
noRuntimeInput: false
|
|
710
|
-
});
|
|
711
|
-
}
|
|
712
|
-
const def = args[0];
|
|
713
|
-
return {
|
|
714
|
-
...def,
|
|
715
|
-
noRuntimeInput: true
|
|
716
|
-
};
|
|
717
|
-
}
|
|
718
|
-
return defineAction;
|
|
719
|
-
}
|
|
720
|
-
var defineAction = makeDefineAction();
|
|
721
|
-
|
|
722
|
-
// src/api-wrapper/redaction.ts
|
|
723
|
-
var SENSITIVE_HEADER_KEYS = new Set([
|
|
724
|
-
"authorization",
|
|
725
|
-
"cookie",
|
|
726
|
-
"set-cookie",
|
|
727
|
-
"x-api-key",
|
|
728
|
-
"x-auth-token"
|
|
729
|
-
]);
|
|
730
|
-
var SENSITIVE_BODY_KEYS = new Set([
|
|
731
|
-
"password",
|
|
732
|
-
"token",
|
|
733
|
-
"accesstoken",
|
|
734
|
-
"access_token",
|
|
735
|
-
"refreshtoken",
|
|
736
|
-
"refresh_token",
|
|
737
|
-
"secret",
|
|
738
|
-
"client_secret",
|
|
739
|
-
"apikey",
|
|
740
|
-
"api_key"
|
|
741
|
-
]);
|
|
742
|
-
function redactHeaders(headers) {
|
|
743
|
-
if (!headers)
|
|
744
|
-
return;
|
|
745
|
-
const out = {};
|
|
746
|
-
for (const [k, v] of Object.entries(headers)) {
|
|
747
|
-
if (SENSITIVE_HEADER_KEYS.has(k.toLowerCase())) {
|
|
748
|
-
out[k] = "[REDACTED]";
|
|
749
|
-
} else {
|
|
750
|
-
out[k] = v;
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
return out;
|
|
754
|
-
}
|
|
755
|
-
function redactBody(body) {
|
|
756
|
-
if (body === null || body === undefined)
|
|
757
|
-
return body;
|
|
758
|
-
if (typeof body !== "object" || Array.isArray(body)) {
|
|
759
|
-
return body;
|
|
760
|
-
}
|
|
761
|
-
const rec = body;
|
|
762
|
-
const out = { ...rec };
|
|
763
|
-
for (const key of Object.keys(out)) {
|
|
764
|
-
if (SENSITIVE_BODY_KEYS.has(key.toLowerCase())) {
|
|
765
|
-
out[key] = "[REDACTED]";
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
return out;
|
|
769
|
-
}
|
|
770
|
-
function defaultRedactContext(ctx) {
|
|
771
|
-
return {
|
|
772
|
-
...ctx,
|
|
773
|
-
requestHeaders: redactHeaders(ctx.requestHeaders),
|
|
774
|
-
requestBody: redactBody(ctx.requestBody),
|
|
775
|
-
meta: ctx.meta
|
|
776
|
-
};
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
// src/api-wrapper/error-reporting.ts
|
|
780
|
-
function applyErrorContextRedaction(ctx, config) {
|
|
781
|
-
const base = defaultRedactContext(ctx);
|
|
782
|
-
return config.redact ? config.redact(base) : base;
|
|
783
|
-
}
|
|
784
|
-
async function reportApiError(err, config) {
|
|
785
|
-
err.context = applyErrorContextRedaction(err.context, config);
|
|
786
|
-
try {
|
|
787
|
-
await config.onError?.(err);
|
|
788
|
-
} catch {}
|
|
789
|
-
config.logger?.error?.(err);
|
|
790
|
-
return err;
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
// src/api-wrapper/size.ts
|
|
794
|
-
function calculateSizeInMb(data) {
|
|
795
|
-
let bytes;
|
|
796
|
-
if (data === undefined || data === null) {
|
|
797
|
-
bytes = 0;
|
|
798
|
-
} else if (data instanceof Blob) {
|
|
799
|
-
bytes = data.size;
|
|
800
|
-
} else if (typeof FormData !== "undefined" && data instanceof FormData) {
|
|
801
|
-
bytes = 0;
|
|
802
|
-
data.forEach((value) => {
|
|
803
|
-
if (value instanceof Blob)
|
|
804
|
-
bytes += value.size;
|
|
805
|
-
else
|
|
806
|
-
bytes += new TextEncoder().encode(String(value)).length;
|
|
807
|
-
});
|
|
808
|
-
} else if (typeof data === "string") {
|
|
809
|
-
bytes = new TextEncoder().encode(data).length;
|
|
810
|
-
} else {
|
|
811
|
-
bytes = new TextEncoder().encode(JSON.stringify(data) ?? "").length;
|
|
812
|
-
}
|
|
813
|
-
return bytes / (1024 * 1024);
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
// src/api-wrapper/transport.ts
|
|
817
|
-
import { ZodError as ZodError3 } from "zod";
|
|
818
|
-
async function validateResponse(schema, data, ctx) {
|
|
819
|
-
try {
|
|
820
|
-
return await schema.parseAsync(data);
|
|
821
|
-
} catch (error) {
|
|
822
|
-
if (error instanceof ZodError3) {
|
|
823
|
-
throw new ResponseValidationError(error, data, ctx());
|
|
824
|
-
}
|
|
825
|
-
throw error;
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
async function httpRequest(config, ctx) {
|
|
829
|
-
const queryString = config.doNotEncodeQueryParams ? Object.entries(config.queryParams).map(([key, value]) => `${key}=${value}`).join("&") : new URLSearchParams(config.queryParams).toString();
|
|
830
|
-
const fullUrl = queryString ? `${config.url}?${queryString}` : config.url;
|
|
831
|
-
const timeoutController = new AbortController;
|
|
832
|
-
const timeout = config.timeoutMs === undefined ? undefined : setTimeout(() => timeoutController.abort(), config.timeoutMs);
|
|
833
|
-
const signal = mergeSignals(timeoutController.signal, config.userSignal);
|
|
834
|
-
try {
|
|
835
|
-
return await config.fetchFn(fullUrl, {
|
|
836
|
-
method: config.method,
|
|
837
|
-
headers: config.headers,
|
|
838
|
-
body: buildFetchBody(config.body, config.headers),
|
|
839
|
-
signal
|
|
840
|
-
});
|
|
841
|
-
} catch (error) {
|
|
842
|
-
if (isDomAbortError(error)) {
|
|
843
|
-
if (config.userSignal?.aborted) {
|
|
844
|
-
throw new RequestAbortedError(ctx(), error);
|
|
845
|
-
}
|
|
846
|
-
if (timeoutController.signal.aborted && config.timeoutMs !== undefined) {
|
|
847
|
-
throw new RequestTimeoutError(config.timeoutMs, ctx());
|
|
848
|
-
}
|
|
849
|
-
throw new RequestAbortedError(ctx(), error);
|
|
850
|
-
}
|
|
851
|
-
throw new NetworkError(error instanceof Error ? error.message : String(error), ctx(), error);
|
|
852
|
-
} finally {
|
|
853
|
-
if (timeout)
|
|
854
|
-
clearTimeout(timeout);
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
function isDomAbortError(error) {
|
|
858
|
-
return typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
|
|
859
|
-
}
|
|
860
|
-
function mergeSignals(a, b) {
|
|
861
|
-
if (!b)
|
|
862
|
-
return a;
|
|
863
|
-
const anyFn = AbortSignal.any;
|
|
864
|
-
if (typeof anyFn === "function")
|
|
865
|
-
return anyFn([a, b]);
|
|
866
|
-
const controller = new AbortController;
|
|
867
|
-
const onAbort = () => controller.abort();
|
|
868
|
-
if (a.aborted)
|
|
869
|
-
controller.abort();
|
|
870
|
-
else
|
|
871
|
-
a.addEventListener("abort", onAbort, { once: true });
|
|
872
|
-
if (b.aborted)
|
|
873
|
-
controller.abort();
|
|
874
|
-
else
|
|
875
|
-
b.addEventListener("abort", onAbort, { once: true });
|
|
876
|
-
return controller.signal;
|
|
877
|
-
}
|
|
878
|
-
async function readErrorResponse(response) {
|
|
879
|
-
const body = await response.text();
|
|
880
|
-
return {
|
|
881
|
-
body,
|
|
882
|
-
headers: getHeadersAsObject(response.headers),
|
|
883
|
-
status: response.status
|
|
884
|
-
};
|
|
885
|
-
}
|
|
886
|
-
function getRetryDelay(params) {
|
|
887
|
-
const { retryDelay, attempt, response } = params;
|
|
888
|
-
if (response && (response.status === 429 || response.status === 503)) {
|
|
889
|
-
const retryAfter = response.headers.get("retry-after");
|
|
890
|
-
if (retryAfter) {
|
|
891
|
-
const seconds = Number(retryAfter);
|
|
892
|
-
if (Number.isFinite(seconds))
|
|
893
|
-
return seconds * 1000;
|
|
894
|
-
const dateMs = Date.parse(retryAfter);
|
|
895
|
-
if (!Number.isNaN(dateMs)) {
|
|
896
|
-
return Math.max(0, dateMs - Date.now());
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
if (typeof retryDelay === "function")
|
|
901
|
-
return retryDelay(attempt);
|
|
902
|
-
return retryDelay;
|
|
903
|
-
}
|
|
904
|
-
function shouldAttemptRetryOnError(error) {
|
|
905
|
-
if (error instanceof HttpResponseError)
|
|
906
|
-
return false;
|
|
907
|
-
if (error instanceof ResponseValidationError)
|
|
908
|
-
return false;
|
|
909
|
-
if (error instanceof RequestValidationError)
|
|
910
|
-
return false;
|
|
911
|
-
if (error instanceof ConfigError)
|
|
912
|
-
return false;
|
|
913
|
-
if (error instanceof RequestAbortedError)
|
|
914
|
-
return false;
|
|
915
|
-
return true;
|
|
916
|
-
}
|
|
917
|
-
function shouldAttemptRetryOnHttp(retryCtx) {
|
|
918
|
-
const { error, response } = retryCtx;
|
|
919
|
-
if (error)
|
|
920
|
-
return shouldAttemptRetryOnError(error);
|
|
921
|
-
return response ? response.status >= 500 || response.status === 429 : false;
|
|
922
|
-
}
|
|
923
|
-
async function makeRequestWithRetry(args) {
|
|
924
|
-
const {
|
|
925
|
-
config,
|
|
926
|
-
maxRetries,
|
|
927
|
-
retryDelay,
|
|
928
|
-
validateFn,
|
|
929
|
-
shouldRetry,
|
|
930
|
-
logger,
|
|
931
|
-
ctx,
|
|
932
|
-
endpointName,
|
|
933
|
-
method,
|
|
934
|
-
fullUrl,
|
|
935
|
-
meta,
|
|
936
|
-
onResponse,
|
|
937
|
-
onRetry
|
|
938
|
-
} = args;
|
|
939
|
-
let retryCount = 0;
|
|
940
|
-
while (true) {
|
|
941
|
-
let response;
|
|
942
|
-
try {
|
|
943
|
-
logger?.debug?.("API request", config);
|
|
944
|
-
response = await httpRequest(config, (over) => ctx({ attempt: retryCount, ...over }));
|
|
945
|
-
await onResponse?.({
|
|
946
|
-
endpoint: endpointName,
|
|
947
|
-
method,
|
|
948
|
-
url: fullUrl,
|
|
949
|
-
response: response.clone(),
|
|
950
|
-
attempt: retryCount,
|
|
951
|
-
meta
|
|
952
|
-
});
|
|
953
|
-
if (response.status >= 400) {
|
|
954
|
-
const retryCtx = {
|
|
955
|
-
response,
|
|
956
|
-
retryCount,
|
|
957
|
-
maxRetries,
|
|
958
|
-
endpoint: endpointName,
|
|
959
|
-
method,
|
|
960
|
-
url: fullUrl
|
|
961
|
-
};
|
|
962
|
-
if (retryCount < maxRetries && shouldRetry(retryCtx)) {
|
|
963
|
-
const delayMs = getRetryDelay({
|
|
964
|
-
retryDelay,
|
|
965
|
-
attempt: retryCount,
|
|
966
|
-
response
|
|
967
|
-
});
|
|
968
|
-
await onRetry?.({
|
|
969
|
-
...retryCtx,
|
|
970
|
-
delayMs,
|
|
971
|
-
nextAttempt: retryCount + 1
|
|
972
|
-
});
|
|
973
|
-
retryCount++;
|
|
974
|
-
await delay(delayMs);
|
|
975
|
-
continue;
|
|
976
|
-
}
|
|
977
|
-
const { body, headers: responseHeaders } = await readErrorResponse(response);
|
|
978
|
-
throw new HttpResponseError(response.status, body, responseHeaders, ctx({ attempt: retryCount }));
|
|
979
|
-
}
|
|
980
|
-
return {
|
|
981
|
-
validatedData: await validateFn(response),
|
|
982
|
-
retryCount,
|
|
983
|
-
httpStatus: response.status
|
|
984
|
-
};
|
|
985
|
-
} catch (error) {
|
|
986
|
-
if (error instanceof HttpResponseError || error instanceof ResponseValidationError || error instanceof RequestValidationError || error instanceof ConfigError || error instanceof RequestAbortedError) {
|
|
987
|
-
throw error;
|
|
988
|
-
}
|
|
989
|
-
const retryCtx = {
|
|
990
|
-
error,
|
|
991
|
-
retryCount,
|
|
992
|
-
maxRetries,
|
|
993
|
-
endpoint: endpointName,
|
|
994
|
-
method,
|
|
995
|
-
url: fullUrl
|
|
996
|
-
};
|
|
997
|
-
if (retryCount >= maxRetries || !shouldRetry(retryCtx)) {
|
|
998
|
-
throw error;
|
|
999
|
-
}
|
|
1000
|
-
const delayMs = getRetryDelay({
|
|
1001
|
-
retryDelay,
|
|
1002
|
-
attempt: retryCount,
|
|
1003
|
-
response: undefined
|
|
1004
|
-
});
|
|
1005
|
-
await onRetry?.({
|
|
1006
|
-
...retryCtx,
|
|
1007
|
-
delayMs,
|
|
1008
|
-
nextAttempt: retryCount + 1
|
|
1009
|
-
});
|
|
1010
|
-
retryCount++;
|
|
1011
|
-
await delay(delayMs);
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
async function validateAndTransformResponse(endpoint, response, ctx) {
|
|
1016
|
-
const responseHeaders = getHeadersAsObject(response.headers);
|
|
1017
|
-
let responseData;
|
|
1018
|
-
if (hasJsonContentType(responseHeaders)) {
|
|
1019
|
-
const text = await response.text();
|
|
1020
|
-
try {
|
|
1021
|
-
responseData = text === "" ? undefined : JSON.parse(text);
|
|
1022
|
-
} catch (error) {
|
|
1023
|
-
throw new ResponseValidationError(makeJsonParseZodError(error, text), text, ctx());
|
|
1024
|
-
}
|
|
1025
|
-
} else {
|
|
1026
|
-
responseData = await response.text();
|
|
1027
|
-
}
|
|
1028
|
-
const transformedData = endpoint.resFormatter ? endpoint.resFormatter(responseData, responseHeaders) : responseData;
|
|
1029
|
-
return validateResponse(endpoint.resSchema, transformedData, ctx);
|
|
1030
|
-
}
|
|
1031
|
-
function defaultShouldRetry(ctx) {
|
|
1032
|
-
if (ctx.error)
|
|
1033
|
-
return shouldAttemptRetryOnError(ctx.error);
|
|
1034
|
-
return shouldAttemptRetryOnHttp(ctx);
|
|
1035
|
-
}
|
|
1036
|
-
function delay(ms) {
|
|
1037
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
// src/api-wrapper/create-client.ts
|
|
1041
|
-
function isActionInvokeOptionsOnly(value) {
|
|
1042
|
-
if (value === null || value === undefined)
|
|
1043
|
-
return false;
|
|
1044
|
-
if (typeof value !== "object" || Array.isArray(value))
|
|
1045
|
-
return false;
|
|
1046
|
-
return Object.keys(value).every((k) => k === "signal" || k === "meta");
|
|
1047
|
-
}
|
|
1048
|
-
function createApiClientImpl(apiConfig) {
|
|
1049
|
-
let actions = {};
|
|
1050
|
-
const cache = createActionCache();
|
|
1051
|
-
const invokeEndpoint = async (endpointKey, input = {}) => {
|
|
1052
|
-
const endpoint = apiConfig.endpoints[endpointKey];
|
|
1053
|
-
const rawInput = input;
|
|
1054
|
-
const endpointName = apiConfig.name ? `${apiConfig.name}.${String(endpointKey)}` : String(endpointKey);
|
|
1055
|
-
const startedAt = Date.now();
|
|
1056
|
-
const maxRetries = input.retries ?? apiConfig.retries ?? 3;
|
|
1057
|
-
const baseContext = {
|
|
1058
|
-
endpoint: endpointName,
|
|
1059
|
-
method: endpoint?.method ?? "GET",
|
|
1060
|
-
url: "",
|
|
1061
|
-
attempt: 0,
|
|
1062
|
-
maxRetries,
|
|
1063
|
-
elapsedMs: 0,
|
|
1064
|
-
meta: input.meta
|
|
1065
|
-
};
|
|
1066
|
-
const elapsed = () => Date.now() - startedAt;
|
|
1067
|
-
const ctx = (over = {}) => ({
|
|
1068
|
-
...baseContext,
|
|
1069
|
-
elapsedMs: elapsed(),
|
|
1070
|
-
...over
|
|
1071
|
-
});
|
|
1072
|
-
if (!endpoint) {
|
|
1073
|
-
throw await reportApiError(new ConfigError(`Unknown API endpoint: ${String(endpointKey)}`, ctx()), apiConfig);
|
|
1074
|
-
}
|
|
1075
|
-
baseContext.method = endpoint.method;
|
|
1076
|
-
let finalUrl;
|
|
1077
|
-
let fullUrl;
|
|
1078
|
-
try {
|
|
1079
|
-
const built = buildUrl(endpoint, rawInput.reqParams, rawInput.reqQuery, apiConfig.baseApiUrls, apiConfig.defaultBaseApiUrl, input.url, (msg) => new ConfigError(msg, ctx()));
|
|
1080
|
-
finalUrl = built.finalUrl;
|
|
1081
|
-
fullUrl = built.fullUrl;
|
|
1082
|
-
} catch (e) {
|
|
1083
|
-
if (e instanceof ApiError)
|
|
1084
|
-
throw await reportApiError(e, apiConfig);
|
|
1085
|
-
throw e;
|
|
1086
|
-
}
|
|
1087
|
-
baseContext.url = fullUrl;
|
|
1088
|
-
let queryParams;
|
|
1089
|
-
let body;
|
|
1090
|
-
let headers;
|
|
1091
|
-
try {
|
|
1092
|
-
queryParams = buildQueryParams(endpoint, rawInput.reqQuery, ctx);
|
|
1093
|
-
body = buildRequestBody(endpoint, rawInput.reqBody, ctx);
|
|
1094
|
-
headers = buildHeaders(endpoint, rawInput.reqHeaders, apiConfig.defaultHeaders ?? {}, ctx);
|
|
1095
|
-
} catch (e) {
|
|
1096
|
-
if (e instanceof ApiError)
|
|
1097
|
-
throw await reportApiError(e, apiConfig);
|
|
1098
|
-
throw e;
|
|
1099
|
-
}
|
|
1100
|
-
baseContext.requestHeaders = headers;
|
|
1101
|
-
baseContext.requestBody = body;
|
|
1102
|
-
if (apiConfig.onRequest) {
|
|
1103
|
-
try {
|
|
1104
|
-
const result = await apiConfig.onRequest({
|
|
1105
|
-
endpoint: endpointName,
|
|
1106
|
-
method: endpoint.method,
|
|
1107
|
-
url: fullUrl,
|
|
1108
|
-
headers,
|
|
1109
|
-
body,
|
|
1110
|
-
meta: input.meta
|
|
1111
|
-
});
|
|
1112
|
-
if (result?.headers)
|
|
1113
|
-
headers = result.headers;
|
|
1114
|
-
if (result && "body" in result)
|
|
1115
|
-
body = result.body;
|
|
1116
|
-
} catch (e) {
|
|
1117
|
-
if (e instanceof ApiError)
|
|
1118
|
-
throw await reportApiError(e, apiConfig);
|
|
1119
|
-
throw e;
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
const config = {
|
|
1123
|
-
method: endpoint.method,
|
|
1124
|
-
url: finalUrl,
|
|
1125
|
-
queryParams,
|
|
1126
|
-
body,
|
|
1127
|
-
headers,
|
|
1128
|
-
doNotEncodeQueryParams: endpoint.doNotEncodeQueryParams,
|
|
1129
|
-
timeoutMs: input.timeoutMs ?? apiConfig.timeoutMs,
|
|
1130
|
-
fetchFn: input.fetch ?? apiConfig.fetch ?? fetch,
|
|
1131
|
-
userSignal: input.signal
|
|
1132
|
-
};
|
|
1133
|
-
try {
|
|
1134
|
-
const result = await makeRequestWithRetry({
|
|
1135
|
-
config,
|
|
1136
|
-
maxRetries,
|
|
1137
|
-
retryDelay: apiConfig.retryDelayMs ?? 300,
|
|
1138
|
-
validateFn: (response) => validateAndTransformResponse(endpoint, response, ctx),
|
|
1139
|
-
shouldRetry: apiConfig.shouldRetry ?? defaultShouldRetry,
|
|
1140
|
-
logger: apiConfig.logger,
|
|
1141
|
-
ctx,
|
|
1142
|
-
endpointName,
|
|
1143
|
-
method: endpoint.method,
|
|
1144
|
-
fullUrl,
|
|
1145
|
-
meta: input.meta,
|
|
1146
|
-
onResponse: apiConfig.onResponse,
|
|
1147
|
-
onRetry: apiConfig.onRetry
|
|
1148
|
-
});
|
|
1149
|
-
const responseTimeMs = elapsed();
|
|
1150
|
-
const responseSizeMb = calculateSizeInMb(result.validatedData);
|
|
1151
|
-
const requestBodySizeMb = calculateSizeInMb(config.body);
|
|
1152
|
-
return {
|
|
1153
|
-
data: result.validatedData,
|
|
1154
|
-
requestBodySizeMb,
|
|
1155
|
-
responseSizeMb,
|
|
1156
|
-
responseTimeMs,
|
|
1157
|
-
httpStatus: result.httpStatus,
|
|
1158
|
-
retryCount: result.retryCount
|
|
1159
|
-
};
|
|
1160
|
-
} catch (error) {
|
|
1161
|
-
if (error instanceof ApiError)
|
|
1162
|
-
throw await reportApiError(error, apiConfig);
|
|
1163
|
-
throw error;
|
|
1164
|
-
}
|
|
1165
|
-
};
|
|
1166
|
-
async function invokeAction(name, rawInput, options = {}) {
|
|
1167
|
-
const action = actions[name];
|
|
1168
|
-
const inputForHandler = action.noRuntimeInput ? undefined : rawInput;
|
|
1169
|
-
const actionName = apiConfig.name ? `${apiConfig.name}.${name}` : name;
|
|
1170
|
-
const startedAt = Date.now();
|
|
1171
|
-
const baseContext = {
|
|
1172
|
-
endpoint: actionName,
|
|
1173
|
-
method: "GET",
|
|
1174
|
-
url: "",
|
|
1175
|
-
attempt: 0,
|
|
1176
|
-
maxRetries: 0,
|
|
1177
|
-
elapsedMs: 0,
|
|
1178
|
-
meta: options.meta,
|
|
1179
|
-
requestBody: action.noRuntimeInput ? undefined : rawInput
|
|
1180
|
-
};
|
|
1181
|
-
const ctx = (over = {}) => ({
|
|
1182
|
-
...baseContext,
|
|
1183
|
-
elapsedMs: Date.now() - startedAt,
|
|
1184
|
-
...over
|
|
1185
|
-
});
|
|
1186
|
-
apiConfig.logger?.debug?.("Action start", { action: actionName });
|
|
1187
|
-
try {
|
|
1188
|
-
const result = await action.handler({
|
|
1189
|
-
input: inputForHandler,
|
|
1190
|
-
api: proxy,
|
|
1191
|
-
signal: options.signal,
|
|
1192
|
-
meta: options.meta,
|
|
1193
|
-
logger: apiConfig.logger,
|
|
1194
|
-
cache
|
|
1195
|
-
});
|
|
1196
|
-
apiConfig.logger?.debug?.("Action end", {
|
|
1197
|
-
action: actionName,
|
|
1198
|
-
elapsedMs: Date.now() - startedAt
|
|
1199
|
-
});
|
|
1200
|
-
return result;
|
|
1201
|
-
} catch (error) {
|
|
1202
|
-
if (error instanceof ApiError) {
|
|
1203
|
-
throw await reportApiError(error, apiConfig);
|
|
1204
|
-
}
|
|
1205
|
-
const wrapped = new ActionError(`Action ${actionName} failed: ${error instanceof Error ? error.message : String(error)}`, ctx(), error);
|
|
1206
|
-
throw await reportApiError(wrapped, apiConfig);
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
const request = async (key, input, options) => {
|
|
1210
|
-
if (key in actions) {
|
|
1211
|
-
const action = actions[key];
|
|
1212
|
-
if (action.noRuntimeInput) {
|
|
1213
|
-
if (input === undefined || input === null) {
|
|
1214
|
-
return invokeAction(key, undefined, options ?? {});
|
|
1215
|
-
}
|
|
1216
|
-
if (options !== undefined) {
|
|
1217
|
-
return invokeAction(key, undefined, options);
|
|
1218
|
-
}
|
|
1219
|
-
if (isActionInvokeOptionsOnly(input)) {
|
|
1220
|
-
return invokeAction(key, undefined, input);
|
|
1221
|
-
}
|
|
1222
|
-
return invokeAction(key, undefined, {});
|
|
1223
|
-
}
|
|
1224
|
-
return invokeAction(key, input, options);
|
|
1225
|
-
}
|
|
1226
|
-
return invokeEndpoint(key, input ?? {});
|
|
1227
|
-
};
|
|
1228
|
-
const client = { request, cache };
|
|
1229
|
-
const proxy = new Proxy(client, {
|
|
1230
|
-
get(target, prop, receiver) {
|
|
1231
|
-
if (prop in target || typeof prop !== "string") {
|
|
1232
|
-
return Reflect.get(target, prop, receiver);
|
|
1233
|
-
}
|
|
1234
|
-
if (prop in apiConfig.endpoints) {
|
|
1235
|
-
return (input) => invokeEndpoint(prop, input ?? {});
|
|
1236
|
-
}
|
|
1237
|
-
if (prop in actions) {
|
|
1238
|
-
const action = actions[prop];
|
|
1239
|
-
if (action.noRuntimeInput) {
|
|
1240
|
-
return (first, second) => {
|
|
1241
|
-
if (first === undefined || first === null) {
|
|
1242
|
-
return invokeAction(prop, undefined, second ?? {});
|
|
1243
|
-
}
|
|
1244
|
-
if (second !== undefined) {
|
|
1245
|
-
return invokeAction(prop, undefined, second);
|
|
1246
|
-
}
|
|
1247
|
-
if (isActionInvokeOptionsOnly(first)) {
|
|
1248
|
-
return invokeAction(prop, undefined, first);
|
|
1249
|
-
}
|
|
1250
|
-
return invokeAction(prop, undefined, {});
|
|
1251
|
-
};
|
|
1252
|
-
}
|
|
1253
|
-
return (input, opt) => invokeAction(prop, input, opt);
|
|
1254
|
-
}
|
|
1255
|
-
return;
|
|
1256
|
-
}
|
|
1257
|
-
});
|
|
1258
|
-
const rawActions = apiConfig.actions;
|
|
1259
|
-
if (typeof rawActions === "function") {
|
|
1260
|
-
actions = rawActions({
|
|
1261
|
-
api: proxy,
|
|
1262
|
-
defineAction: makeDefineAction()
|
|
1263
|
-
});
|
|
1264
|
-
} else if (rawActions) {
|
|
1265
|
-
actions = rawActions;
|
|
1266
|
-
}
|
|
1267
|
-
const collisions = Object.keys(actions).filter((key) => (key in apiConfig.endpoints));
|
|
1268
|
-
if (collisions.length > 0) {
|
|
1269
|
-
throw new ConfigError(`Action name(s) collide with endpoint name(s): ${collisions.join(", ")}`, {
|
|
1270
|
-
endpoint: collisions[0],
|
|
1271
|
-
method: "GET",
|
|
1272
|
-
url: "",
|
|
1273
|
-
attempt: 0,
|
|
1274
|
-
maxRetries: 0,
|
|
1275
|
-
elapsedMs: 0
|
|
1276
|
-
});
|
|
1277
|
-
}
|
|
1278
|
-
return proxy;
|
|
1279
|
-
}
|
|
1280
|
-
var createApiClient = (apiConfig) => createApiClientImpl(apiConfig);
|
|
1281
|
-
// src/p-queue/index.ts
|
|
1282
|
-
class TimeoutError extends Error {
|
|
1283
|
-
constructor(message) {
|
|
1284
|
-
super(message);
|
|
1285
|
-
this.name = "TimeoutError";
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
|
-
|
|
1289
|
-
class PriorityQueue {
|
|
1290
|
-
#queue = [];
|
|
1291
|
-
enqueue(run, options = {}) {
|
|
1292
|
-
const element = {
|
|
1293
|
-
priority: options.priority ?? 0,
|
|
1294
|
-
id: options.id,
|
|
1295
|
-
run
|
|
1296
|
-
};
|
|
1297
|
-
const index = this.#queue.findIndex((item) => item.priority < element.priority);
|
|
1298
|
-
if (index === -1) {
|
|
1299
|
-
this.#queue.push(element);
|
|
1300
|
-
return;
|
|
1301
|
-
}
|
|
1302
|
-
this.#queue.splice(index, 0, element);
|
|
1303
|
-
}
|
|
1304
|
-
dequeue() {
|
|
1305
|
-
return this.#queue.shift()?.run;
|
|
1306
|
-
}
|
|
1307
|
-
filter(options) {
|
|
1308
|
-
return this.#queue.filter((item) => Object.entries(options).every(([key, value]) => item[key] === value)).map((item) => item.run);
|
|
1309
|
-
}
|
|
1310
|
-
setPriority(id, priority) {
|
|
1311
|
-
const index = this.#queue.findIndex((item2) => item2.id === id);
|
|
1312
|
-
if (index === -1) {
|
|
1313
|
-
throw new ReferenceError(`No promise function with the id "${id}" exists in the queue.`);
|
|
1314
|
-
}
|
|
1315
|
-
const [item] = this.#queue.splice(index, 1);
|
|
1316
|
-
this.enqueue(item.run, { ...item, priority });
|
|
1317
|
-
}
|
|
1318
|
-
remove(id) {
|
|
1319
|
-
const index = this.#queue.findIndex((item) => item.id === id);
|
|
1320
|
-
if (index !== -1) {
|
|
1321
|
-
this.#queue.splice(index, 1);
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
get size() {
|
|
1325
|
-
return this.#queue.length;
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
class PQueue {
|
|
1330
|
-
#listeners = new Map;
|
|
1331
|
-
#queue;
|
|
1332
|
-
#queueClass;
|
|
1333
|
-
#pending = 0;
|
|
1334
|
-
#concurrency;
|
|
1335
|
-
#isPaused;
|
|
1336
|
-
#intervalCap;
|
|
1337
|
-
#interval;
|
|
1338
|
-
#carryoverIntervalCount;
|
|
1339
|
-
#strict;
|
|
1340
|
-
#intervalCount = 0;
|
|
1341
|
-
#intervalEnd = 0;
|
|
1342
|
-
#intervalTimer;
|
|
1343
|
-
#strictTimestamps = [];
|
|
1344
|
-
#rateLimited = false;
|
|
1345
|
-
#idCounter = 1n;
|
|
1346
|
-
#runningTasks = new Map;
|
|
1347
|
-
timeout;
|
|
1348
|
-
constructor(options = {}) {
|
|
1349
|
-
const {
|
|
1350
|
-
concurrency = Number.POSITIVE_INFINITY,
|
|
1351
|
-
autoStart = true,
|
|
1352
|
-
queueClass = PriorityQueue,
|
|
1353
|
-
intervalCap = Number.POSITIVE_INFINITY,
|
|
1354
|
-
interval = 0,
|
|
1355
|
-
carryoverIntervalCount,
|
|
1356
|
-
carryoverConcurrencyCount,
|
|
1357
|
-
strict = false,
|
|
1358
|
-
timeout
|
|
1359
|
-
} = options;
|
|
1360
|
-
if (!(typeof concurrency === "number" && concurrency >= 1)) {
|
|
1361
|
-
throw new TypeError("Expected `concurrency` to be a number from 1 and up");
|
|
1362
|
-
}
|
|
1363
|
-
if (!(typeof intervalCap === "number" && intervalCap >= 1)) {
|
|
1364
|
-
throw new TypeError("Expected `intervalCap` to be a number from 1 and up");
|
|
1365
|
-
}
|
|
1366
|
-
if (!(typeof interval === "number" && Number.isFinite(interval) && interval >= 0)) {
|
|
1367
|
-
throw new TypeError("Expected `interval` to be a finite number >= 0");
|
|
1368
|
-
}
|
|
1369
|
-
if (strict && interval === 0) {
|
|
1370
|
-
throw new TypeError("The `strict` option requires a non-zero `interval`");
|
|
1371
|
-
}
|
|
1372
|
-
if (strict && intervalCap === Number.POSITIVE_INFINITY) {
|
|
1373
|
-
throw new TypeError("The `strict` option requires a finite `intervalCap`");
|
|
1374
|
-
}
|
|
1375
|
-
if (timeout !== undefined && !(Number.isFinite(timeout) && timeout > 0)) {
|
|
1376
|
-
throw new TypeError("Expected `timeout` to be a positive finite number");
|
|
1377
|
-
}
|
|
1378
|
-
this.#queueClass = queueClass;
|
|
1379
|
-
this.#queue = new queueClass;
|
|
1380
|
-
this.#concurrency = concurrency;
|
|
1381
|
-
this.#isPaused = !autoStart;
|
|
1382
|
-
this.#intervalCap = intervalCap;
|
|
1383
|
-
this.#interval = interval;
|
|
1384
|
-
this.#carryoverIntervalCount = carryoverIntervalCount ?? carryoverConcurrencyCount ?? false;
|
|
1385
|
-
this.#strict = strict;
|
|
1386
|
-
this.timeout = timeout;
|
|
1387
|
-
}
|
|
1388
|
-
on(event, listener) {
|
|
1389
|
-
const listeners = this.#listeners.get(event) ?? new Set;
|
|
1390
|
-
listeners.add(listener);
|
|
1391
|
-
this.#listeners.set(event, listeners);
|
|
1392
|
-
return this;
|
|
1393
|
-
}
|
|
1394
|
-
off(event, listener) {
|
|
1395
|
-
this.#listeners.get(event)?.delete(listener);
|
|
1396
|
-
return this;
|
|
1397
|
-
}
|
|
1398
|
-
emit(event, ...arguments_) {
|
|
1399
|
-
const listeners = this.#listeners.get(event);
|
|
1400
|
-
if (!listeners || listeners.size === 0) {
|
|
1401
|
-
return false;
|
|
1402
|
-
}
|
|
1403
|
-
for (const listener of [...listeners]) {
|
|
1404
|
-
listener(...arguments_);
|
|
1405
|
-
}
|
|
1406
|
-
return true;
|
|
1407
|
-
}
|
|
1408
|
-
get concurrency() {
|
|
1409
|
-
return this.#concurrency;
|
|
1410
|
-
}
|
|
1411
|
-
set concurrency(newConcurrency) {
|
|
1412
|
-
if (!(typeof newConcurrency === "number" && newConcurrency >= 1)) {
|
|
1413
|
-
throw new TypeError("Expected `concurrency` to be a number from 1 and up");
|
|
1414
|
-
}
|
|
1415
|
-
this.#concurrency = newConcurrency;
|
|
1416
|
-
this.#processQueue();
|
|
1417
|
-
}
|
|
1418
|
-
get size() {
|
|
1419
|
-
return this.#queue.size;
|
|
1420
|
-
}
|
|
1421
|
-
sizeBy(options) {
|
|
1422
|
-
return this.#queue.filter(options).length;
|
|
1423
|
-
}
|
|
1424
|
-
get pending() {
|
|
1425
|
-
return this.#pending;
|
|
1426
|
-
}
|
|
1427
|
-
get isPaused() {
|
|
1428
|
-
return this.#isPaused;
|
|
1429
|
-
}
|
|
1430
|
-
get isRateLimited() {
|
|
1431
|
-
this.#updateRateLimitState();
|
|
1432
|
-
return this.#rateLimited;
|
|
1433
|
-
}
|
|
1434
|
-
get isSaturated() {
|
|
1435
|
-
return this.#pending >= this.#concurrency && this.#queue.size > 0 || this.isRateLimited && this.#queue.size > 0;
|
|
1436
|
-
}
|
|
1437
|
-
get runningTasks() {
|
|
1438
|
-
return [...this.#runningTasks.values()].map((task) => ({ ...task }));
|
|
1439
|
-
}
|
|
1440
|
-
setPriority(id, priority) {
|
|
1441
|
-
if (typeof priority !== "number" || !Number.isFinite(priority)) {
|
|
1442
|
-
throw new TypeError("Expected `priority` to be a finite number");
|
|
1443
|
-
}
|
|
1444
|
-
this.#queue.setPriority(id, priority);
|
|
1445
|
-
}
|
|
1446
|
-
async add(function_, options = {}) {
|
|
1447
|
-
const taskOptions = {
|
|
1448
|
-
timeout: this.timeout,
|
|
1449
|
-
...options,
|
|
1450
|
-
id: options.id ?? (this.#idCounter++).toString()
|
|
1451
|
-
};
|
|
1452
|
-
return new Promise((resolve, reject) => {
|
|
1453
|
-
const taskSymbol = Symbol(taskOptions.id);
|
|
1454
|
-
let didRun = false;
|
|
1455
|
-
let queuedAbortHandler;
|
|
1456
|
-
let runningAbortHandler;
|
|
1457
|
-
const cleanupQueuedAbort = () => {
|
|
1458
|
-
if (queuedAbortHandler) {
|
|
1459
|
-
taskOptions.signal?.removeEventListener("abort", queuedAbortHandler);
|
|
1460
|
-
queuedAbortHandler = undefined;
|
|
1461
|
-
}
|
|
1462
|
-
};
|
|
1463
|
-
const run = async () => {
|
|
1464
|
-
didRun = true;
|
|
1465
|
-
cleanupQueuedAbort();
|
|
1466
|
-
this.#pending++;
|
|
1467
|
-
this.#runningTasks.set(taskSymbol, {
|
|
1468
|
-
id: taskOptions.id,
|
|
1469
|
-
priority: taskOptions.priority ?? 0,
|
|
1470
|
-
startTime: Date.now(),
|
|
1471
|
-
timeout: taskOptions.timeout
|
|
1472
|
-
});
|
|
1473
|
-
try {
|
|
1474
|
-
taskOptions.signal?.throwIfAborted();
|
|
1475
|
-
const operation = Promise.resolve(function_({ signal: taskOptions.signal }));
|
|
1476
|
-
const result = await this.#withTimeoutAndAbort(operation, taskOptions, (listener) => {
|
|
1477
|
-
runningAbortHandler = listener;
|
|
1478
|
-
});
|
|
1479
|
-
resolve(result);
|
|
1480
|
-
this.emit("completed", result);
|
|
1481
|
-
} catch (error) {
|
|
1482
|
-
reject(error);
|
|
1483
|
-
this.emit("error", error);
|
|
1484
|
-
} finally {
|
|
1485
|
-
if (runningAbortHandler) {
|
|
1486
|
-
taskOptions.signal?.removeEventListener("abort", runningAbortHandler);
|
|
1487
|
-
}
|
|
1488
|
-
this.#runningTasks.delete(taskSymbol);
|
|
1489
|
-
queueMicrotask(() => {
|
|
1490
|
-
this.#next();
|
|
1491
|
-
});
|
|
1492
|
-
}
|
|
1493
|
-
};
|
|
1494
|
-
if (taskOptions.signal?.aborted) {
|
|
1495
|
-
reject(taskOptions.signal.reason);
|
|
1496
|
-
return;
|
|
1497
|
-
}
|
|
1498
|
-
if (taskOptions.signal) {
|
|
1499
|
-
queuedAbortHandler = () => {
|
|
1500
|
-
if (didRun) {
|
|
1501
|
-
return;
|
|
1502
|
-
}
|
|
1503
|
-
this.#queue.remove?.(taskOptions.id);
|
|
1504
|
-
reject(taskOptions.signal?.reason);
|
|
1505
|
-
this.emit("next");
|
|
1506
|
-
this.#tryToStartAnother();
|
|
1507
|
-
};
|
|
1508
|
-
taskOptions.signal.addEventListener("abort", queuedAbortHandler, {
|
|
1509
|
-
once: true
|
|
1510
|
-
});
|
|
1511
|
-
}
|
|
1512
|
-
this.#queue.enqueue(run, taskOptions);
|
|
1513
|
-
this.emit("add");
|
|
1514
|
-
this.#processQueue();
|
|
1515
|
-
});
|
|
1516
|
-
}
|
|
1517
|
-
async addAll(functions, options) {
|
|
1518
|
-
return Promise.all(functions.map(async (function_) => this.add(function_, options)));
|
|
1519
|
-
}
|
|
1520
|
-
start() {
|
|
1521
|
-
if (!this.#isPaused) {
|
|
1522
|
-
return this;
|
|
1523
|
-
}
|
|
1524
|
-
this.#isPaused = false;
|
|
1525
|
-
this.#processQueue();
|
|
1526
|
-
return this;
|
|
1527
|
-
}
|
|
1528
|
-
pause() {
|
|
1529
|
-
this.#isPaused = true;
|
|
1530
|
-
}
|
|
1531
|
-
clear() {
|
|
1532
|
-
this.#queue = new this.#queueClass;
|
|
1533
|
-
this.#clearIntervalTimer();
|
|
1534
|
-
this.#updateRateLimitState();
|
|
1535
|
-
this.emit("empty");
|
|
1536
|
-
if (this.#pending === 0) {
|
|
1537
|
-
this.emit("idle");
|
|
1538
|
-
}
|
|
1539
|
-
this.emit("next");
|
|
1540
|
-
}
|
|
1541
|
-
async onEmpty() {
|
|
1542
|
-
if (this.#queue.size === 0) {
|
|
1543
|
-
return;
|
|
1544
|
-
}
|
|
1545
|
-
await this.#onEvent("empty");
|
|
1546
|
-
}
|
|
1547
|
-
async onIdle() {
|
|
1548
|
-
if (this.#queue.size === 0 && this.#pending === 0) {
|
|
1549
|
-
return;
|
|
1550
|
-
}
|
|
1551
|
-
await this.#onEvent("idle");
|
|
1552
|
-
}
|
|
1553
|
-
async onPendingZero() {
|
|
1554
|
-
if (this.#pending === 0) {
|
|
1555
|
-
return;
|
|
1556
|
-
}
|
|
1557
|
-
await this.#onEvent("pendingZero");
|
|
1558
|
-
}
|
|
1559
|
-
async onRateLimit() {
|
|
1560
|
-
if (this.isRateLimited) {
|
|
1561
|
-
return;
|
|
1562
|
-
}
|
|
1563
|
-
await this.#onEvent("rateLimit");
|
|
1564
|
-
}
|
|
1565
|
-
async onRateLimitCleared() {
|
|
1566
|
-
if (!this.isRateLimited) {
|
|
1567
|
-
return;
|
|
1568
|
-
}
|
|
1569
|
-
await this.#onEvent("rateLimitCleared");
|
|
1570
|
-
}
|
|
1571
|
-
onError() {
|
|
1572
|
-
return new Promise((_resolve, reject) => {
|
|
1573
|
-
const listener = (error) => {
|
|
1574
|
-
this.off("error", listener);
|
|
1575
|
-
reject(error);
|
|
1576
|
-
};
|
|
1577
|
-
this.on("error", listener);
|
|
1578
|
-
});
|
|
1579
|
-
}
|
|
1580
|
-
async onSizeLessThan(limit) {
|
|
1581
|
-
if (this.#queue.size < limit) {
|
|
1582
|
-
return;
|
|
1583
|
-
}
|
|
1584
|
-
await this.#onEvent("next", () => this.#queue.size < limit);
|
|
1585
|
-
}
|
|
1586
|
-
async#onEvent(event, filter) {
|
|
1587
|
-
return new Promise((resolve) => {
|
|
1588
|
-
const listener = () => {
|
|
1589
|
-
if (filter && !filter()) {
|
|
1590
|
-
return;
|
|
1591
|
-
}
|
|
1592
|
-
this.off(event, listener);
|
|
1593
|
-
resolve();
|
|
1594
|
-
};
|
|
1595
|
-
this.on(event, listener);
|
|
1596
|
-
});
|
|
1597
|
-
}
|
|
1598
|
-
async#withTimeoutAndAbort(operation, options, setAbortListener) {
|
|
1599
|
-
const races = [operation];
|
|
1600
|
-
let timeoutId;
|
|
1601
|
-
if (options.timeout) {
|
|
1602
|
-
races.push(new Promise((_resolve, reject) => {
|
|
1603
|
-
timeoutId = setTimeout(() => {
|
|
1604
|
-
reject(new TimeoutError(`Task timed out after ${options.timeout}ms`));
|
|
1605
|
-
}, options.timeout);
|
|
1606
|
-
}));
|
|
1607
|
-
}
|
|
1608
|
-
if (options.signal) {
|
|
1609
|
-
races.push(new Promise((_resolve, reject) => {
|
|
1610
|
-
const listener = () => {
|
|
1611
|
-
reject(options.signal?.reason);
|
|
1612
|
-
};
|
|
1613
|
-
setAbortListener(listener);
|
|
1614
|
-
options.signal?.addEventListener("abort", listener, { once: true });
|
|
1615
|
-
}));
|
|
1616
|
-
}
|
|
1617
|
-
try {
|
|
1618
|
-
return await Promise.race(races);
|
|
1619
|
-
} finally {
|
|
1620
|
-
if (timeoutId) {
|
|
1621
|
-
clearTimeout(timeoutId);
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
#next() {
|
|
1626
|
-
this.#pending--;
|
|
1627
|
-
if (this.#pending === 0) {
|
|
1628
|
-
this.emit("pendingZero");
|
|
1629
|
-
}
|
|
1630
|
-
this.#processQueue();
|
|
1631
|
-
this.emit("next");
|
|
1632
|
-
}
|
|
1633
|
-
#processQueue() {
|
|
1634
|
-
while (this.#tryToStartAnother()) {}
|
|
1635
|
-
this.#updateRateLimitState();
|
|
1636
|
-
}
|
|
1637
|
-
#tryToStartAnother() {
|
|
1638
|
-
if (this.#queue.size === 0) {
|
|
1639
|
-
this.#clearIntervalTimer();
|
|
1640
|
-
this.emit("empty");
|
|
1641
|
-
if (this.#pending === 0) {
|
|
1642
|
-
this.emit("idle");
|
|
1643
|
-
}
|
|
1644
|
-
return false;
|
|
1645
|
-
}
|
|
1646
|
-
if (this.#isPaused || this.#pending >= this.#concurrency || !this.#doesIntervalAllowAnother()) {
|
|
1647
|
-
return false;
|
|
1648
|
-
}
|
|
1649
|
-
const job = this.#queue.dequeue();
|
|
1650
|
-
if (!job) {
|
|
1651
|
-
return false;
|
|
1652
|
-
}
|
|
1653
|
-
this.#consumeIntervalSlot();
|
|
1654
|
-
this.emit("active");
|
|
1655
|
-
job();
|
|
1656
|
-
return true;
|
|
1657
|
-
}
|
|
1658
|
-
#doesIntervalAllowAnother() {
|
|
1659
|
-
if (this.#intervalCap === Number.POSITIVE_INFINITY || this.#interval === 0) {
|
|
1660
|
-
return true;
|
|
1661
|
-
}
|
|
1662
|
-
const now = Date.now();
|
|
1663
|
-
if (this.#strict) {
|
|
1664
|
-
this.#strictTimestamps = this.#strictTimestamps.filter((timestamp) => now - timestamp < this.#interval);
|
|
1665
|
-
if (this.#strictTimestamps.length < this.#intervalCap) {
|
|
1666
|
-
return true;
|
|
1667
|
-
}
|
|
1668
|
-
this.#scheduleIntervalResume(this.#interval - (now - this.#strictTimestamps[0]));
|
|
1669
|
-
return false;
|
|
1670
|
-
}
|
|
1671
|
-
if (this.#intervalEnd === 0 || now >= this.#intervalEnd) {
|
|
1672
|
-
this.#intervalCount = this.#carryoverIntervalCount ? this.#pending : 0;
|
|
1673
|
-
this.#intervalEnd = now + this.#interval;
|
|
1674
|
-
}
|
|
1675
|
-
if (this.#intervalCount < this.#intervalCap) {
|
|
1676
|
-
return true;
|
|
1677
|
-
}
|
|
1678
|
-
this.#scheduleIntervalResume(this.#intervalEnd - now);
|
|
1679
|
-
return false;
|
|
1680
|
-
}
|
|
1681
|
-
#consumeIntervalSlot() {
|
|
1682
|
-
if (this.#intervalCap === Number.POSITIVE_INFINITY || this.#interval === 0) {
|
|
1683
|
-
return;
|
|
1684
|
-
}
|
|
1685
|
-
if (this.#strict) {
|
|
1686
|
-
this.#strictTimestamps.push(Date.now());
|
|
1687
|
-
return;
|
|
1688
|
-
}
|
|
1689
|
-
this.#intervalCount++;
|
|
1690
|
-
}
|
|
1691
|
-
#scheduleIntervalResume(delay2) {
|
|
1692
|
-
if (this.#intervalTimer) {
|
|
1693
|
-
return;
|
|
1694
|
-
}
|
|
1695
|
-
this.#intervalTimer = setTimeout(() => {
|
|
1696
|
-
this.#intervalTimer = undefined;
|
|
1697
|
-
this.#processQueue();
|
|
1698
|
-
}, Math.max(0, delay2));
|
|
1699
|
-
}
|
|
1700
|
-
#clearIntervalTimer() {
|
|
1701
|
-
if (this.#intervalTimer) {
|
|
1702
|
-
clearTimeout(this.#intervalTimer);
|
|
1703
|
-
this.#intervalTimer = undefined;
|
|
1704
|
-
}
|
|
1705
|
-
}
|
|
1706
|
-
#updateRateLimitState() {
|
|
1707
|
-
const previous = this.#rateLimited;
|
|
1708
|
-
const isRateLimited = this.#queue.size > 0 && this.#intervalCap !== Number.POSITIVE_INFINITY && this.#interval > 0 && !this.#doesIntervalAllowAnother();
|
|
1709
|
-
this.#rateLimited = isRateLimited;
|
|
1710
|
-
if (previous !== isRateLimited) {
|
|
1711
|
-
this.emit(isRateLimited ? "rateLimit" : "rateLimitCleared");
|
|
1712
|
-
}
|
|
1713
|
-
}
|
|
1714
|
-
}
|
|
1715
|
-
|
|
1716
|
-
// src/jwt/index.ts
|
|
1717
|
-
var BYTE_CHUNK_SIZE = 32768;
|
|
1718
|
-
var textEncoder = new TextEncoder;
|
|
1719
|
-
var textDecoder = new TextDecoder;
|
|
1720
|
-
async function jwtSign(payload, secret) {
|
|
1721
|
-
const header = encodeJson({ alg: "HS256" });
|
|
1722
|
-
const body = encodeJson({
|
|
1723
|
-
...payload,
|
|
1724
|
-
iat: Math.floor(Date.now() / 1000)
|
|
1725
|
-
});
|
|
1726
|
-
const data = `${header}.${body}`;
|
|
1727
|
-
const signature = await sign(data, secret);
|
|
1728
|
-
return `${data}.${encodeBase64Url(signature)}`;
|
|
1729
|
-
}
|
|
1730
|
-
async function jwtVerify(token, secret) {
|
|
1731
|
-
try {
|
|
1732
|
-
const parts = token.split(".");
|
|
1733
|
-
if (parts.length !== 3)
|
|
1734
|
-
return null;
|
|
1735
|
-
const [encodedHeader, encodedPayload, encodedSignature] = parts;
|
|
1736
|
-
const header = decodeJson(encodedHeader);
|
|
1737
|
-
if (!isJwtHeader(header))
|
|
1738
|
-
return null;
|
|
1739
|
-
const data = `${encodedHeader}.${encodedPayload}`;
|
|
1740
|
-
const signature = decodeBase64Url(encodedSignature);
|
|
1741
|
-
const isValid = await verify(data, signature, secret);
|
|
1742
|
-
if (!isValid)
|
|
1743
|
-
return null;
|
|
1744
|
-
const payload = decodeJson(encodedPayload);
|
|
1745
|
-
if (!isJwtPayload(payload) || !isTimeValid(payload))
|
|
1746
|
-
return null;
|
|
1747
|
-
return payload;
|
|
1748
|
-
} catch (_error) {
|
|
1749
|
-
return null;
|
|
1750
|
-
}
|
|
1751
|
-
}
|
|
1752
|
-
async function sign(data, secret) {
|
|
1753
|
-
const key = await getKey(secret, ["sign"]);
|
|
1754
|
-
return await getSubtleCrypto().sign("HMAC", key, encodeText(data));
|
|
1755
|
-
}
|
|
1756
|
-
async function verify(data, signature, secret) {
|
|
1757
|
-
const key = await getKey(secret, ["verify"]);
|
|
1758
|
-
return await getSubtleCrypto().verify("HMAC", key, signature, encodeText(data));
|
|
1759
|
-
}
|
|
1760
|
-
async function getKey(secret, usages) {
|
|
1761
|
-
return await getSubtleCrypto().importKey("raw", encodeText(secret), {
|
|
1762
|
-
name: "HMAC",
|
|
1763
|
-
hash: "SHA-256"
|
|
1764
|
-
}, false, usages);
|
|
1765
|
-
}
|
|
1766
|
-
function encodeJson(value) {
|
|
1767
|
-
return encodeBase64Url(encodeText(JSON.stringify(value)));
|
|
1768
|
-
}
|
|
1769
|
-
function decodeJson(value) {
|
|
1770
|
-
return JSON.parse(textDecoder.decode(decodeBase64Url(value)));
|
|
1771
|
-
}
|
|
1772
|
-
function encodeBase64Url(value) {
|
|
1773
|
-
const bytes = value instanceof ArrayBuffer ? new Uint8Array(value) : value;
|
|
1774
|
-
return btoa(bytesToBinaryString(bytes)).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
|
1775
|
-
}
|
|
1776
|
-
function decodeBase64Url(value) {
|
|
1777
|
-
const base64 = value.replaceAll("-", "+").replaceAll("_", "/");
|
|
1778
|
-
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
|
|
1779
|
-
return binaryStringToBytes(atob(padded)).buffer;
|
|
1780
|
-
}
|
|
1781
|
-
function encodeText(value) {
|
|
1782
|
-
return toArrayBuffer(textEncoder.encode(value));
|
|
1783
|
-
}
|
|
1784
|
-
function toArrayBuffer(bytes) {
|
|
1785
|
-
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
1786
|
-
}
|
|
1787
|
-
function bytesToBinaryString(bytes) {
|
|
1788
|
-
let binary = "";
|
|
1789
|
-
for (let offset = 0;offset < bytes.length; offset += BYTE_CHUNK_SIZE) {
|
|
1790
|
-
binary += String.fromCharCode(...bytes.subarray(offset, offset + BYTE_CHUNK_SIZE));
|
|
1791
|
-
}
|
|
1792
|
-
return binary;
|
|
1793
|
-
}
|
|
1794
|
-
function binaryStringToBytes(binary) {
|
|
1795
|
-
const bytes = new Uint8Array(binary.length);
|
|
1796
|
-
for (let index = 0;index < binary.length; index += 1) {
|
|
1797
|
-
bytes[index] = binary.charCodeAt(index);
|
|
1798
|
-
}
|
|
1799
|
-
return bytes;
|
|
1800
|
-
}
|
|
1801
|
-
function getSubtleCrypto() {
|
|
1802
|
-
const subtle = globalThis.crypto?.subtle;
|
|
1803
|
-
if (!subtle) {
|
|
1804
|
-
throw new Error("Web Crypto is not available in this runtime");
|
|
1805
|
-
}
|
|
1806
|
-
return subtle;
|
|
1807
|
-
}
|
|
1808
|
-
function isJwtHeader(value) {
|
|
1809
|
-
return typeof value === "object" && value !== null && "alg" in value && value.alg === "HS256";
|
|
1810
|
-
}
|
|
1811
|
-
function isJwtPayload(value) {
|
|
1812
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1813
|
-
}
|
|
1814
|
-
function isTimeValid(payload) {
|
|
1815
|
-
const now = Math.floor(Date.now() / 1000);
|
|
1816
|
-
if (typeof payload.exp === "number" && now >= payload.exp)
|
|
1817
|
-
return false;
|
|
1818
|
-
if (typeof payload.nbf === "number" && now < payload.nbf)
|
|
1819
|
-
return false;
|
|
1820
|
-
return true;
|
|
1821
|
-
}
|
|
1822
|
-
|
|
1823
|
-
// src/sitemap/index.ts
|
|
1824
|
-
var createTimeoutController = (timeout) => {
|
|
1825
|
-
const controller = new AbortController;
|
|
1826
|
-
setTimeout(() => controller.abort(), timeout);
|
|
1827
|
-
return controller;
|
|
1828
|
-
};
|
|
1829
|
-
var discoverSitemaps = async (domain) => {
|
|
1830
|
-
const normalizedDomain = domain.replace(/\/$/, "");
|
|
1831
|
-
const commonLocations = [
|
|
1832
|
-
"/sitemap.xml",
|
|
1833
|
-
"/sitemap_index.xml",
|
|
1834
|
-
"/sitemap/",
|
|
1835
|
-
"/sitemaps.xml",
|
|
1836
|
-
"/sitemap/sitemap.xml"
|
|
1837
|
-
];
|
|
1838
|
-
const sitemaps = new Set;
|
|
1839
|
-
const requestOptions = {
|
|
1840
|
-
headers: {
|
|
1841
|
-
"User-Agent": "SitemapCrawler/1.0"
|
|
1842
|
-
},
|
|
1843
|
-
signal: createTimeoutController(5000).signal
|
|
1844
|
-
};
|
|
1845
|
-
try {
|
|
1846
|
-
const robotsTxt = await fetch(`${normalizedDomain}/robots.txt`, {
|
|
1847
|
-
...requestOptions
|
|
1848
|
-
});
|
|
1849
|
-
if (robotsTxt.ok) {
|
|
1850
|
-
const text = await robotsTxt.text();
|
|
1851
|
-
const sitemapMatches = text.match(/Sitemap: (.*)/gi);
|
|
1852
|
-
if (sitemapMatches) {
|
|
1853
|
-
sitemapMatches.forEach((match) => {
|
|
1854
|
-
const url = match.replace("Sitemap:", "").trim();
|
|
1855
|
-
sitemaps.add(url);
|
|
1856
|
-
});
|
|
1857
|
-
}
|
|
1858
|
-
if (sitemaps.size > 0) {
|
|
1859
|
-
return [...sitemaps];
|
|
1860
|
-
}
|
|
1861
|
-
}
|
|
1862
|
-
} catch (error) {
|
|
1863
|
-
console.warn("Could not fetch robots.txt:", error instanceof Error ? error.message : "Unknown error");
|
|
1864
|
-
}
|
|
1865
|
-
for (const location of commonLocations) {
|
|
1866
|
-
try {
|
|
1867
|
-
const response = await fetch(`${normalizedDomain}${location}`, {
|
|
1868
|
-
...requestOptions,
|
|
1869
|
-
method: "HEAD"
|
|
1870
|
-
});
|
|
1871
|
-
if (response.ok) {
|
|
1872
|
-
return [`${normalizedDomain}${location}`];
|
|
1873
|
-
}
|
|
1874
|
-
} catch (_error) {}
|
|
1875
|
-
}
|
|
1876
|
-
return [];
|
|
1877
|
-
};
|
|
1878
|
-
var getDomainSitemap = async (domain, options = {}) => {
|
|
1879
|
-
const {
|
|
1880
|
-
filterIndexes,
|
|
1881
|
-
concurrency = 10,
|
|
1882
|
-
maxDepth = 5,
|
|
1883
|
-
timeout = 1e4
|
|
1884
|
-
} = options;
|
|
1885
|
-
let normalizedDomain = domain.replace(/\/$/, "");
|
|
1886
|
-
if (!normalizedDomain.startsWith("http")) {
|
|
1887
|
-
normalizedDomain = `https://${normalizedDomain}`;
|
|
1888
|
-
}
|
|
1889
|
-
const sitemapUrls = await discoverSitemaps(normalizedDomain);
|
|
1890
|
-
if (sitemapUrls.length === 0) {
|
|
1891
|
-
throw new Error("No sitemaps found for the domain");
|
|
1892
|
-
}
|
|
1893
|
-
const queue = new PQueue({ concurrency });
|
|
1894
|
-
const result = {
|
|
1895
|
-
links: new Set,
|
|
1896
|
-
visited: new Set
|
|
1897
|
-
};
|
|
1898
|
-
const locRegex = /<loc>([^<]+)<\/loc>/g;
|
|
1899
|
-
const isXmlSitemap = (url) => url.endsWith(".xml") || url.toLowerCase().includes("sitemap");
|
|
1900
|
-
const processSitemap = async (sitemapUrl, depth = 0) => {
|
|
1901
|
-
if (depth > maxDepth || result.visited.has(sitemapUrl)) {
|
|
1902
|
-
return;
|
|
1903
|
-
}
|
|
1904
|
-
result.visited.add(sitemapUrl);
|
|
1905
|
-
try {
|
|
1906
|
-
const controller = createTimeoutController(timeout);
|
|
1907
|
-
const response = await fetch(sitemapUrl, {
|
|
1908
|
-
headers: {
|
|
1909
|
-
"User-Agent": "SitemapCrawler/1.0"
|
|
1910
|
-
},
|
|
1911
|
-
signal: controller.signal
|
|
1912
|
-
});
|
|
1913
|
-
if (!response.ok)
|
|
1914
|
-
throw new Error(`HTTP error! status: ${response.status}`);
|
|
1915
|
-
const data = await response.text();
|
|
1916
|
-
const matches = [...data.matchAll(locRegex)].map((match) => match[1].trim()).filter((link) => {
|
|
1917
|
-
if (filterIndexes && isXmlSitemap(link)) {
|
|
1918
|
-
return link.includes(filterIndexes);
|
|
1919
|
-
}
|
|
1920
|
-
return true;
|
|
1921
|
-
});
|
|
1922
|
-
for (const link of matches) {
|
|
1923
|
-
if (isXmlSitemap(link)) {
|
|
1924
|
-
queue.add(() => processSitemap(link, depth + 1));
|
|
1925
|
-
} else {
|
|
1926
|
-
result.links.add(link);
|
|
1927
|
-
}
|
|
1928
|
-
}
|
|
1929
|
-
} catch (error) {
|
|
1930
|
-
console.warn(`Failed to process sitemap ${sitemapUrl}:`, error instanceof Error ? error.message : "Unknown error");
|
|
1931
|
-
}
|
|
1932
|
-
};
|
|
1933
|
-
await Promise.all(sitemapUrls.map((sitemapUrl) => queue.add(() => processSitemap(sitemapUrl))));
|
|
1934
|
-
await queue.onIdle();
|
|
1935
|
-
return [...result.links];
|
|
1936
|
-
};
|
|
1937
|
-
|
|
1938
|
-
// src/secrets/infisical.ts
|
|
1939
|
-
import { z as z4 } from "zod";
|
|
1940
|
-
var DEFAULT_INFISICAL_SITE_URL = "https://eu.infisical.com";
|
|
1941
|
-
var defaultFetch = globalThis.fetch;
|
|
1942
|
-
var infisicalAuthResponseSchema = z4.object({
|
|
1943
|
-
accessToken: z4.string()
|
|
1944
|
-
});
|
|
1945
|
-
var infisicalSecretSchema = z4.object({
|
|
1946
|
-
secretKey: z4.string(),
|
|
1947
|
-
secretValue: z4.string()
|
|
1948
|
-
});
|
|
1949
|
-
var infisicalSecretsResponseSchema = z4.object({
|
|
1950
|
-
secrets: z4.array(infisicalSecretSchema)
|
|
1951
|
-
});
|
|
1952
|
-
async function fetchInfisicalSecrets({
|
|
1953
|
-
keys,
|
|
1954
|
-
projectId,
|
|
1955
|
-
environment,
|
|
1956
|
-
siteUrl,
|
|
1957
|
-
clientId,
|
|
1958
|
-
clientSecret
|
|
1959
|
-
}) {
|
|
1960
|
-
const infisical = createInfisicalApiClient(siteUrl);
|
|
1961
|
-
const { data: auth } = await infisical.request("login", {
|
|
1962
|
-
reqBody: {
|
|
1963
|
-
clientId,
|
|
1964
|
-
clientSecret
|
|
1965
|
-
}
|
|
1966
|
-
});
|
|
1967
|
-
const { data } = await infisical.request("listSecrets", {
|
|
1968
|
-
reqHeaders: {
|
|
1969
|
-
Authorization: `Bearer ${auth.accessToken}`
|
|
1970
|
-
},
|
|
1971
|
-
reqQuery: {
|
|
1972
|
-
environment,
|
|
1973
|
-
projectId,
|
|
1974
|
-
secretPath: "/",
|
|
1975
|
-
viewSecretValue: true,
|
|
1976
|
-
expandSecretReferences: true,
|
|
1977
|
-
includeImports: true
|
|
1978
|
-
}
|
|
1979
|
-
});
|
|
1980
|
-
const wantedKeys = new Set(keys);
|
|
1981
|
-
return Object.fromEntries(data.secrets.filter((secret) => wantedKeys.has(secret.secretKey)).map((secret) => [secret.secretKey, secret.secretValue]));
|
|
1982
|
-
}
|
|
1983
|
-
async function loadInfisicalSecrets({
|
|
1984
|
-
keys,
|
|
1985
|
-
projectId,
|
|
1986
|
-
environment,
|
|
1987
|
-
siteUrl,
|
|
1988
|
-
clientIdEnvKey,
|
|
1989
|
-
clientSecretEnvKey
|
|
1990
|
-
}) {
|
|
1991
|
-
const clientId = process.env[clientIdEnvKey];
|
|
1992
|
-
const clientSecret = process.env[clientSecretEnvKey];
|
|
1993
|
-
if (!clientId || !clientSecret) {
|
|
1994
|
-
throw new Error("Missing Infisical credentials");
|
|
1995
|
-
}
|
|
1996
|
-
return fetchInfisicalSecrets({
|
|
1997
|
-
keys,
|
|
1998
|
-
projectId,
|
|
1999
|
-
environment,
|
|
2000
|
-
siteUrl,
|
|
2001
|
-
clientId,
|
|
2002
|
-
clientSecret
|
|
2003
|
-
});
|
|
2004
|
-
}
|
|
2005
|
-
function createInfisicalApiClient(siteUrl) {
|
|
2006
|
-
return createApiClient({
|
|
2007
|
-
name: "infisical",
|
|
2008
|
-
baseApiUrls: {
|
|
2009
|
-
infisical: siteUrl
|
|
2010
|
-
},
|
|
2011
|
-
defaultBaseApiUrl: "infisical",
|
|
2012
|
-
fetch: getFetch(),
|
|
2013
|
-
retries: 1,
|
|
2014
|
-
endpoints: {
|
|
2015
|
-
login: {
|
|
2016
|
-
method: "POST",
|
|
2017
|
-
path: "/api/v1/auth/universal-auth/login",
|
|
2018
|
-
reqBodySchema: z4.object({
|
|
2019
|
-
clientId: z4.string(),
|
|
2020
|
-
clientSecret: z4.string()
|
|
2021
|
-
}),
|
|
2022
|
-
resSchema: infisicalAuthResponseSchema,
|
|
2023
|
-
reqBodyFormat: "json"
|
|
2024
|
-
},
|
|
2025
|
-
listSecrets: {
|
|
2026
|
-
method: "GET",
|
|
2027
|
-
path: "/api/v4/secrets",
|
|
2028
|
-
reqHeadersSchema: z4.object({
|
|
2029
|
-
Authorization: z4.string()
|
|
2030
|
-
}),
|
|
2031
|
-
reqQuerySchema: z4.object({
|
|
2032
|
-
projectId: z4.string(),
|
|
2033
|
-
environment: z4.string(),
|
|
2034
|
-
secretPath: z4.string(),
|
|
2035
|
-
viewSecretValue: z4.boolean(),
|
|
2036
|
-
expandSecretReferences: z4.boolean(),
|
|
2037
|
-
includeImports: z4.boolean()
|
|
2038
|
-
}),
|
|
2039
|
-
resSchema: infisicalSecretsResponseSchema
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2042
|
-
});
|
|
2043
|
-
}
|
|
2044
|
-
function getFetch() {
|
|
2045
|
-
return globalThis.fetch ?? defaultFetch;
|
|
2046
|
-
}
|
|
2047
|
-
|
|
2048
|
-
// src/secrets/loaders.ts
|
|
2049
|
-
function cloudflareWorkerEnvLoader(workerEnv) {
|
|
2050
|
-
return async (ctx) => {
|
|
2051
|
-
const out = {};
|
|
2052
|
-
for (const key of ctx.keys) {
|
|
2053
|
-
const str = workerBindingToString(workerEnv[key]);
|
|
2054
|
-
if (str !== undefined) {
|
|
2055
|
-
out[key] = str;
|
|
2056
|
-
}
|
|
2057
|
-
}
|
|
2058
|
-
return out;
|
|
2059
|
-
};
|
|
2060
|
-
}
|
|
2061
|
-
function workerBindingToString(value) {
|
|
2062
|
-
if (value === undefined || value === null) {
|
|
2063
|
-
return;
|
|
2064
|
-
}
|
|
2065
|
-
if (typeof value === "string") {
|
|
2066
|
-
return value === "" ? undefined : value;
|
|
2067
|
-
}
|
|
2068
|
-
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
2069
|
-
return String(value);
|
|
2070
|
-
}
|
|
2071
|
-
if (typeof value === "object") {
|
|
2072
|
-
return JSON.stringify(value);
|
|
2073
|
-
}
|
|
2074
|
-
return String(value);
|
|
2075
|
-
}
|
|
2076
|
-
function infisicalLoader(config) {
|
|
2077
|
-
return async (ctx) => fetchInfisicalSecrets({
|
|
2078
|
-
keys: ctx.keys,
|
|
2079
|
-
projectId: config.projectId,
|
|
2080
|
-
environment: ctx.environment,
|
|
2081
|
-
siteUrl: config.siteUrl ?? DEFAULT_INFISICAL_SITE_URL,
|
|
2082
|
-
clientId: config.clientId,
|
|
2083
|
-
clientSecret: config.clientSecret
|
|
2084
|
-
});
|
|
2085
|
-
}
|
|
2086
|
-
async function processEnvLoader(_ctx) {
|
|
2087
|
-
return {};
|
|
2088
|
-
}
|
|
2089
|
-
|
|
2090
|
-
// src/secrets/vite.ts
|
|
2091
|
-
function getViteDefine(secretSet) {
|
|
2092
|
-
return Object.fromEntries(Object.entries(secretSet.secrets()).map(([key, value]) => [
|
|
2093
|
-
`import.meta.env.${key}`,
|
|
2094
|
-
JSON.stringify(value)
|
|
2095
|
-
]));
|
|
2096
|
-
}
|
|
2097
|
-
function viteSecretSetPlugin(secretSet) {
|
|
2098
|
-
return {
|
|
2099
|
-
name: "hanzio-secret-set",
|
|
2100
|
-
config: () => ({
|
|
2101
|
-
define: getViteDefine(secretSet)
|
|
2102
|
-
})
|
|
2103
|
-
};
|
|
2104
|
-
}
|
|
2105
|
-
|
|
2106
|
-
// src/cool-console-log/index.ts
|
|
2107
|
-
var terminalColors = {
|
|
2108
|
-
reset: "\x1B[0m",
|
|
2109
|
-
bold: "\x1B[1m",
|
|
2110
|
-
dim: "\x1B[2m",
|
|
2111
|
-
italic: "\x1B[3m",
|
|
2112
|
-
underline: "\x1B[4m",
|
|
2113
|
-
black: "\x1B[30m",
|
|
2114
|
-
red: "\x1B[31m",
|
|
2115
|
-
green: "\x1B[32m",
|
|
2116
|
-
yellow: "\x1B[33m",
|
|
2117
|
-
blue: "\x1B[34m",
|
|
2118
|
-
magenta: "\x1B[35m",
|
|
2119
|
-
cyan: "\x1B[36m",
|
|
2120
|
-
white: "\x1B[37m",
|
|
2121
|
-
gray: "\x1B[90m",
|
|
2122
|
-
brightRed: "\x1B[91m",
|
|
2123
|
-
brightGreen: "\x1B[92m",
|
|
2124
|
-
brightYellow: "\x1B[93m",
|
|
2125
|
-
brightBlue: "\x1B[94m",
|
|
2126
|
-
brightMagenta: "\x1B[95m",
|
|
2127
|
-
brightCyan: "\x1B[96m",
|
|
2128
|
-
brightWhite: "\x1B[97m",
|
|
2129
|
-
bgBlack: "\x1B[40m",
|
|
2130
|
-
bgRed: "\x1B[41m",
|
|
2131
|
-
bgGreen: "\x1B[42m",
|
|
2132
|
-
bgYellow: "\x1B[43m",
|
|
2133
|
-
bgBlue: "\x1B[44m",
|
|
2134
|
-
bgMagenta: "\x1B[45m",
|
|
2135
|
-
bgCyan: "\x1B[46m",
|
|
2136
|
-
bgWhite: "\x1B[47m"
|
|
2137
|
-
};
|
|
2138
|
-
var levelColors = {
|
|
2139
|
-
debug: { badge: terminalColors.gray, text: terminalColors.dim },
|
|
2140
|
-
info: { badge: terminalColors.cyan, text: terminalColors.reset },
|
|
2141
|
-
warn: { badge: terminalColors.yellow, text: terminalColors.yellow },
|
|
2142
|
-
error: { badge: terminalColors.red, text: terminalColors.brightRed }
|
|
2143
|
-
};
|
|
2144
|
-
function formatAttributes(attrs) {
|
|
2145
|
-
if (!attrs || Object.keys(attrs).length === 0)
|
|
2146
|
-
return "";
|
|
2147
|
-
const formatted = Object.entries(attrs).map(([k, v]) => `${terminalColors.dim}${k}=${terminalColors.reset}${v}`).join(" ");
|
|
2148
|
-
return ` ${formatted}`;
|
|
2149
|
-
}
|
|
2150
|
-
function createCoolLogger(options = {}) {
|
|
2151
|
-
const { colorsInDevOnly = true, onLog } = options;
|
|
2152
|
-
const isDev = true;
|
|
2153
|
-
const useColors = colorsInDevOnly ? isDev : true;
|
|
2154
|
-
const createLogMethod = (level) => (message, attributes) => {
|
|
2155
|
-
onLog?.(level, message, attributes);
|
|
2156
|
-
if (level === "debug" && !isDev)
|
|
2157
|
-
return;
|
|
2158
|
-
if (useColors) {
|
|
2159
|
-
const { badge, text } = levelColors[level];
|
|
2160
|
-
const label = level.toUpperCase().padEnd(5);
|
|
2161
|
-
const attrStr = formatAttributes(attributes);
|
|
2162
|
-
console.log(`${badge}[${label}]${terminalColors.reset} ${text}${message}${terminalColors.reset}${attrStr}`);
|
|
2163
|
-
} else {
|
|
2164
|
-
console[level](`[${level.toUpperCase()}]`, message, attributes ?? "");
|
|
2165
|
-
}
|
|
2166
|
-
};
|
|
2167
|
-
return {
|
|
2168
|
-
debug: createLogMethod("debug"),
|
|
2169
|
-
info: createLogMethod("info"),
|
|
2170
|
-
warn: createLogMethod("warn"),
|
|
2171
|
-
error: createLogMethod("error")
|
|
2172
|
-
};
|
|
2173
|
-
}
|
|
2174
|
-
function logOperationSummary(operationName, durationMs, success, options = {}) {
|
|
2175
|
-
const { devOnly = true, error } = options;
|
|
2176
|
-
const isDev = true;
|
|
2177
|
-
if (devOnly && !isDev)
|
|
2178
|
-
return;
|
|
2179
|
-
const c = terminalColors;
|
|
2180
|
-
const statusIcon = success ? `${c.brightGreen}✓${c.reset}` : `${c.brightRed}✗${c.reset}`;
|
|
2181
|
-
const statusColor = success ? c.green : c.red;
|
|
2182
|
-
const durationStr = `${c.dim}${durationMs.toFixed(1)}ms${c.reset}`;
|
|
2183
|
-
const errorMessage = error ? ` ${c.dim}·${c.reset} ${c.red}${error instanceof Error ? error.message : String(error)}${c.reset}` : "";
|
|
2184
|
-
console.log(`${statusColor}${c.bold}${operationName}${c.reset} ${statusIcon} ${c.dim}·${c.reset} ${durationStr}${errorMessage}`);
|
|
2185
|
-
}
|
|
2186
|
-
function colorize(text, color, ...modifiers) {
|
|
2187
|
-
const isDev = true;
|
|
2188
|
-
if (!isDev)
|
|
2189
|
-
return text;
|
|
2190
|
-
const colorCodes = [
|
|
2191
|
-
terminalColors[color],
|
|
2192
|
-
...modifiers.map((m) => terminalColors[m])
|
|
2193
|
-
].join("");
|
|
2194
|
-
return `${colorCodes}${text}${terminalColors.reset}`;
|
|
2195
|
-
}
|
|
2196
|
-
function logBanner(text, color = "cyan", options = {}) {
|
|
2197
|
-
const { devOnly = true } = options;
|
|
2198
|
-
const isDev = true;
|
|
2199
|
-
if (devOnly && !isDev) {
|
|
2200
|
-
console.log(`=== ${text} ===`);
|
|
2201
|
-
return;
|
|
2202
|
-
}
|
|
2203
|
-
const c = terminalColors;
|
|
2204
|
-
const padding = 2;
|
|
2205
|
-
const line = "═".repeat(text.length + padding * 2);
|
|
2206
|
-
console.log(`${c[color]}╔${line}╗${c.reset}`);
|
|
2207
|
-
console.log(`${c[color]}║${" ".repeat(padding)}${c.bold}${text}${c.reset}${c[color]}${" ".repeat(padding)}║${c.reset}`);
|
|
2208
|
-
console.log(`${c[color]}╚${line}╝${c.reset}`);
|
|
2209
|
-
}
|
|
2210
|
-
|
|
2211
|
-
// src/secrets/index.ts
|
|
2212
|
-
var logger = createCoolLogger();
|
|
2213
|
-
function getSecretEnvironment() {
|
|
2214
|
-
const env = process.env.SECRETS_ENV || "development";
|
|
2215
|
-
if (env === "production" || env === "prod")
|
|
2216
|
-
return "prod";
|
|
2217
|
-
if (env === "staging")
|
|
2218
|
-
return "staging";
|
|
2219
|
-
return "dev";
|
|
2220
|
-
}
|
|
2221
|
-
async function defineSecretSet(keys, options) {
|
|
2222
|
-
const keySet = [...new Set(keys)];
|
|
2223
|
-
const resolvedOptions = resolveSecretSetOptions(options);
|
|
2224
|
-
let cachedSecrets = null;
|
|
2225
|
-
const loadSecrets = async (force = false) => {
|
|
2226
|
-
const nextSecrets = await loadAndMergeSecrets({
|
|
2227
|
-
keySet,
|
|
2228
|
-
cachedSecrets,
|
|
2229
|
-
force,
|
|
2230
|
-
...resolvedOptions
|
|
2231
|
-
});
|
|
2232
|
-
cachedSecrets = nextSecrets;
|
|
2233
|
-
return nextSecrets;
|
|
2234
|
-
};
|
|
2235
|
-
const secretSet = createSecretSet({
|
|
2236
|
-
getCachedSecrets: () => cachedSecrets,
|
|
2237
|
-
reload: loadSecrets
|
|
2238
|
-
});
|
|
2239
|
-
await loadSecrets();
|
|
2240
|
-
return secretSet;
|
|
2241
|
-
}
|
|
2242
|
-
function resolveSecretSetOptions(options) {
|
|
2243
|
-
const environment = resolveEnvironment(options.environment);
|
|
2244
|
-
const loader = options.loader ? options.loader : (context) => loadInfisicalSecrets({
|
|
2245
|
-
...context,
|
|
2246
|
-
projectId: options.projectId,
|
|
2247
|
-
siteUrl: options.siteUrl ?? DEFAULT_INFISICAL_SITE_URL,
|
|
2248
|
-
clientIdEnvKey: options.clientIdEnvKey ?? "INFISICAL_CLIENT_ID",
|
|
2249
|
-
clientSecretEnvKey: options.clientSecretEnvKey ?? "INFISICAL_CLIENT_SECRET"
|
|
2250
|
-
});
|
|
2251
|
-
return {
|
|
2252
|
-
loader,
|
|
2253
|
-
environment
|
|
2254
|
-
};
|
|
2255
|
-
}
|
|
2256
|
-
async function loadAndMergeSecrets({
|
|
2257
|
-
keySet,
|
|
2258
|
-
cachedSecrets,
|
|
2259
|
-
loader,
|
|
2260
|
-
environment,
|
|
2261
|
-
force
|
|
2262
|
-
}) {
|
|
2263
|
-
const processEnvSecrets = readProcessEnvSecrets(keySet, force ? cachedSecrets : null);
|
|
2264
|
-
const keysMissingFromEnv = keySet.filter((key) => !processEnvSecrets[key]);
|
|
2265
|
-
logSecretEnvironment(environment, keySet.length);
|
|
2266
|
-
const loaderKeys = force ? keySet : keysMissingFromEnv;
|
|
2267
|
-
const loadedSecrets = loaderKeys.length === 0 ? {} : await loader({
|
|
2268
|
-
keys: loaderKeys,
|
|
2269
|
-
environment
|
|
2270
|
-
});
|
|
2271
|
-
const missingKeys = keySet.filter((key) => !(processEnvSecrets[key] ?? loadedSecrets[key]));
|
|
2272
|
-
if (missingKeys.length > 0) {
|
|
2273
|
-
throw new Error(`Missing secrets: ${missingKeys.join(", ")}`);
|
|
2274
|
-
}
|
|
2275
|
-
const nextSecrets = Object.fromEntries(keySet.map((key) => [
|
|
2276
|
-
key,
|
|
2277
|
-
processEnvSecrets[key] ?? loadedSecrets[key]
|
|
2278
|
-
]));
|
|
2279
|
-
for (const [key, value] of Object.entries(nextSecrets)) {
|
|
2280
|
-
process.env[key] = value;
|
|
2281
|
-
}
|
|
2282
|
-
return nextSecrets;
|
|
2283
|
-
}
|
|
2284
|
-
function createSecretSet({
|
|
2285
|
-
getCachedSecrets,
|
|
2286
|
-
reload
|
|
2287
|
-
}) {
|
|
2288
|
-
const secretSet = {
|
|
2289
|
-
secret(key) {
|
|
2290
|
-
const value = getCachedSecrets()?.[key] ?? process.env[key];
|
|
2291
|
-
if (!value) {
|
|
2292
|
-
throw new Error(`Secret ${key} not found`);
|
|
2293
|
-
}
|
|
2294
|
-
return value;
|
|
2295
|
-
},
|
|
2296
|
-
secrets() {
|
|
2297
|
-
const cachedSecrets = getCachedSecrets();
|
|
2298
|
-
if (!cachedSecrets) {
|
|
2299
|
-
throw new Error("Secrets not initialized.");
|
|
2300
|
-
}
|
|
2301
|
-
return { ...cachedSecrets };
|
|
2302
|
-
},
|
|
2303
|
-
async reload() {
|
|
2304
|
-
await reload(true);
|
|
2305
|
-
return secretSet;
|
|
2306
|
-
}
|
|
2307
|
-
};
|
|
2308
|
-
return secretSet;
|
|
2309
|
-
}
|
|
2310
|
-
function resolveEnvironment(environment) {
|
|
2311
|
-
if (typeof environment === "function")
|
|
2312
|
-
return environment();
|
|
2313
|
-
return environment ?? getSecretEnvironment();
|
|
2314
|
-
}
|
|
2315
|
-
function logSecretEnvironment(environment, secretCount) {
|
|
2316
|
-
logger.info(colorize("Loading secrets", "cyan", "bold"), {
|
|
2317
|
-
environment: colorize(environment, "brightCyan", "bold"),
|
|
2318
|
-
count: secretCount
|
|
2319
|
-
});
|
|
2320
|
-
}
|
|
2321
|
-
function readProcessEnvSecrets(keys, previousSecrets = null) {
|
|
2322
|
-
return Object.fromEntries(keys.filter((key) => {
|
|
2323
|
-
const value = process.env[key];
|
|
2324
|
-
return value && value !== previousSecrets?.[key];
|
|
2325
|
-
}).map((key) => [key, process.env[key]]));
|
|
2326
|
-
}
|
|
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";
|
|
2327
86
|
|
|
2328
87
|
// src/typedswitch/index.ts
|
|
2329
|
-
function
|
|
88
|
+
function typedSwitch(value, keyOrCases, casesOrDefault, defaultCase) {
|
|
2330
89
|
let key;
|
|
2331
90
|
let cases;
|
|
2332
91
|
let defaultHandler;
|
|
@@ -2334,213 +93,34 @@ function runSwitch(value, keyOrCases, casesOrDefault, defaultCase) {
|
|
|
2334
93
|
if (typeof value === "string") {
|
|
2335
94
|
key = value;
|
|
2336
95
|
cases = keyOrCases;
|
|
2337
|
-
defaultHandler = casesOrDefault;
|
|
2338
|
-
} else {
|
|
2339
|
-
if (typeof keyOrCases !== "string") {
|
|
2340
|
-
throw new Error("Invalid typedSwitch call: object mode requires a string discriminant key");
|
|
2341
|
-
}
|
|
2342
|
-
discriminantKey = keyOrCases;
|
|
2343
|
-
const discriminantValue = value[discriminantKey];
|
|
2344
|
-
if (typeof discriminantValue !== "string") {
|
|
2345
|
-
throw new Error(`Invalid discriminant value for key "${discriminantKey}": expected string, received ${typeof discriminantValue}`);
|
|
2346
|
-
}
|
|
2347
|
-
key = discriminantValue;
|
|
2348
|
-
cases = casesOrDefault;
|
|
2349
|
-
defaultHandler = defaultCase;
|
|
2350
|
-
}
|
|
2351
|
-
const handler = cases[key];
|
|
2352
|
-
if (handler) {
|
|
2353
|
-
return handler(value);
|
|
2354
|
-
}
|
|
2355
|
-
if (defaultHandler) {
|
|
2356
|
-
return defaultHandler(value);
|
|
2357
|
-
}
|
|
2358
|
-
const availableCases = Object.keys(cases).join(", ");
|
|
2359
|
-
const availableCasesSuffix = availableCases.length > 0 ? ` Available cases: ${availableCases}` : " Available cases: (none)";
|
|
2360
|
-
const contextSuffix = discriminantKey ? ` (discriminant key: "${discriminantKey}")` : "";
|
|
2361
|
-
throw new Error(`Unhandled case: ${key}${contextSuffix}.${availableCasesSuffix}`);
|
|
2362
|
-
}
|
|
2363
|
-
function typedSwitch(value, keyOrCases, casesOrDefault, defaultCase) {
|
|
2364
|
-
return runSwitch(value, keyOrCases, casesOrDefault, defaultCase);
|
|
2365
|
-
}
|
|
2366
|
-
// src/array/index.ts
|
|
2367
|
-
function chunkArray(array, chunkSize) {
|
|
2368
|
-
if (!Number.isInteger(chunkSize) || chunkSize <= 0) {
|
|
2369
|
-
throw new Error("Chunk size must be a positive integer");
|
|
2370
|
-
}
|
|
2371
|
-
const chunks = [];
|
|
2372
|
-
for (let index = 0;index < array.length; index += chunkSize) {
|
|
2373
|
-
chunks.push(array.slice(index, index + chunkSize));
|
|
2374
|
-
}
|
|
2375
|
-
return chunks;
|
|
2376
|
-
}
|
|
2377
|
-
function findArrayDifferenceByKey(current, next, key) {
|
|
2378
|
-
const currentByKey = new Map(current.map((item) => [String(item[key]), item]));
|
|
2379
|
-
const itemsToDelete = [...current];
|
|
2380
|
-
const newItems = [];
|
|
2381
|
-
const upsertItems = [];
|
|
2382
|
-
for (const item of next) {
|
|
2383
|
-
const keyValue = String(item[key]);
|
|
2384
|
-
if (!currentByKey.has(keyValue)) {
|
|
2385
|
-
newItems.push(item);
|
|
2386
|
-
continue;
|
|
2387
|
-
}
|
|
2388
|
-
upsertItems.push(item);
|
|
2389
|
-
const deleteIndex = itemsToDelete.findIndex((currentItem) => String(currentItem[key]) === keyValue);
|
|
2390
|
-
if (deleteIndex >= 0) {
|
|
2391
|
-
itemsToDelete.splice(deleteIndex, 1);
|
|
2392
|
-
}
|
|
2393
|
-
}
|
|
2394
|
-
return {
|
|
2395
|
-
new: newItems,
|
|
2396
|
-
upsert: upsertItems,
|
|
2397
|
-
delete: itemsToDelete
|
|
2398
|
-
};
|
|
2399
|
-
}
|
|
2400
|
-
function getUniqueValues(array) {
|
|
2401
|
-
const seen = new Map;
|
|
2402
|
-
for (const element of array) {
|
|
2403
|
-
const key = JSON.stringify(element);
|
|
2404
|
-
if (!seen.has(key)) {
|
|
2405
|
-
seen.set(key, element);
|
|
2406
|
-
}
|
|
2407
|
-
}
|
|
2408
|
-
return Array.from(seen.values());
|
|
2409
|
-
}
|
|
2410
|
-
function compact(array) {
|
|
2411
|
-
return array.filter((item) => item != null);
|
|
2412
|
-
}
|
|
2413
|
-
function getUniqueValuesByKey(array, key) {
|
|
2414
|
-
const seen = new Set;
|
|
2415
|
-
const result = [];
|
|
2416
|
-
for (const item of array) {
|
|
2417
|
-
const value = item[key];
|
|
2418
|
-
if (seen.has(value))
|
|
2419
|
-
continue;
|
|
2420
|
-
seen.add(value);
|
|
2421
|
-
result.push(item);
|
|
2422
|
-
}
|
|
2423
|
-
return result;
|
|
2424
|
-
}
|
|
2425
|
-
function groupBy(array, keyOrFn) {
|
|
2426
|
-
return array.reduce((result, item) => {
|
|
2427
|
-
const groupKey = typeof keyOrFn === "function" ? keyOrFn(item) : item[keyOrFn];
|
|
2428
|
-
result[groupKey] ??= [];
|
|
2429
|
-
result[groupKey].push(item);
|
|
2430
|
-
return result;
|
|
2431
|
-
}, {});
|
|
2432
|
-
}
|
|
2433
|
-
function keyBy(array, keyOrFn) {
|
|
2434
|
-
const result = {};
|
|
2435
|
-
for (const item of array) {
|
|
2436
|
-
const recordKey = typeof keyOrFn === "function" ? keyOrFn(item) : item[keyOrFn];
|
|
2437
|
-
result[recordKey] = item;
|
|
2438
|
-
}
|
|
2439
|
-
return result;
|
|
2440
|
-
}
|
|
2441
|
-
function partition(array, predicate) {
|
|
2442
|
-
const matched = [];
|
|
2443
|
-
const unmatched = [];
|
|
2444
|
-
array.forEach((item, index) => {
|
|
2445
|
-
if (predicate(item, index)) {
|
|
2446
|
-
matched.push(item);
|
|
2447
|
-
} else {
|
|
2448
|
-
unmatched.push(item);
|
|
2449
|
-
}
|
|
2450
|
-
});
|
|
2451
|
-
return [matched, unmatched];
|
|
2452
|
-
}
|
|
2453
|
-
function pickItemsInArray(array, key, values) {
|
|
2454
|
-
return array.filter((item) => values.includes(item[key]));
|
|
2455
|
-
}
|
|
2456
|
-
// src/string/index.ts
|
|
2457
|
-
function extractNumber(value) {
|
|
2458
|
-
if (!value?.trim())
|
|
2459
|
-
return;
|
|
2460
|
-
const cleanedValue = value.replace(/[^\d.,-]/g, "");
|
|
2461
|
-
if (!cleanedValue)
|
|
2462
|
-
return;
|
|
2463
|
-
const parsedNumber = Number.parseFloat(cleanedValue);
|
|
2464
|
-
return Number.isNaN(parsedNumber) ? undefined : parsedNumber;
|
|
2465
|
-
}
|
|
2466
|
-
function generateId(input) {
|
|
2467
|
-
const data = normalizeIdInput(input);
|
|
2468
|
-
let hash = 0;
|
|
2469
|
-
for (let index = 0;index < data.length; index++) {
|
|
2470
|
-
const char = data.charCodeAt(index);
|
|
2471
|
-
hash = (hash << 5) - hash + char;
|
|
2472
|
-
hash |= 0;
|
|
2473
|
-
}
|
|
2474
|
-
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
2475
|
-
}
|
|
2476
|
-
function slugify(value) {
|
|
2477
|
-
const source = "àáâäæãåāăąçćčđďèéêëēėęěğǵḧîïíīįìłḿñńǹňôöòóœøōõṕŕřßśšşșťțûüùúūǘůűųẃẍÿýžźż·/_,:;";
|
|
2478
|
-
const replacement = "aaaaaaaaaacccddeeeeeeeegghiiiiiilmnnnnooooooooprrsssssttuuuuuuuuuwxyyzzz------";
|
|
2479
|
-
const specialCharacterPattern = new RegExp(source.split("").join("|"), "g");
|
|
2480
|
-
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(/-+$/, "");
|
|
2481
|
-
}
|
|
2482
|
-
function normalizeIdInput(input) {
|
|
2483
|
-
if (input === undefined)
|
|
2484
|
-
return "undefined";
|
|
2485
|
-
if (input === null)
|
|
2486
|
-
return "null";
|
|
2487
|
-
if (typeof input === "string") {
|
|
2488
|
-
return decodeURIComponent(input).split("&").sort().join("&");
|
|
2489
|
-
}
|
|
2490
|
-
if (typeof input === "number" || typeof input === "boolean") {
|
|
2491
|
-
return input.toString();
|
|
2492
|
-
}
|
|
2493
|
-
return JSON.stringify(sortJsonValue(input));
|
|
2494
|
-
}
|
|
2495
|
-
function sortJsonValue(value) {
|
|
2496
|
-
if (value === undefined)
|
|
2497
|
-
return null;
|
|
2498
|
-
if (Array.isArray(value)) {
|
|
2499
|
-
return value.map((item) => sortJsonValue(item));
|
|
96
|
+
defaultHandler = casesOrDefault;
|
|
97
|
+
} else {
|
|
98
|
+
if (typeof keyOrCases !== "string") {
|
|
99
|
+
throw new Error("Invalid typedSwitch call: object mode requires a string discriminant key");
|
|
100
|
+
}
|
|
101
|
+
discriminantKey = keyOrCases;
|
|
102
|
+
const discriminantValue = value[discriminantKey];
|
|
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}`);
|
|
105
|
+
}
|
|
106
|
+
key = String(discriminantValue);
|
|
107
|
+
cases = casesOrDefault;
|
|
108
|
+
defaultHandler = defaultCase;
|
|
2500
109
|
}
|
|
2501
|
-
|
|
2502
|
-
|
|
110
|
+
const handler = Object.hasOwn(cases, key) ? cases[key] : undefined;
|
|
111
|
+
if (handler) {
|
|
112
|
+
return handler(value);
|
|
2503
113
|
}
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
// src/promise/index.ts
|
|
2507
|
-
function isSuccess(result) {
|
|
2508
|
-
return result.error === null;
|
|
2509
|
-
}
|
|
2510
|
-
function isFailure(result) {
|
|
2511
|
-
return result.error !== null;
|
|
2512
|
-
}
|
|
2513
|
-
function unwrapResult(result) {
|
|
2514
|
-
if (isSuccess(result))
|
|
2515
|
-
return result.data;
|
|
2516
|
-
throw result.error;
|
|
2517
|
-
}
|
|
2518
|
-
function backgroundPromise(promiseFactory, options = {}) {
|
|
2519
|
-
const run = () => {
|
|
2520
|
-
promiseFactory().then(() => options.onSuccess?.()).catch((error) => options.onError?.(error));
|
|
2521
|
-
};
|
|
2522
|
-
if (options.scheduler) {
|
|
2523
|
-
options.scheduler(run);
|
|
2524
|
-
return;
|
|
114
|
+
if (defaultHandler) {
|
|
115
|
+
return defaultHandler(value);
|
|
2525
116
|
}
|
|
2526
|
-
|
|
2527
|
-
}
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
...options,
|
|
2531
|
-
scheduler: options.scheduler ?? queueMicrotask
|
|
2532
|
-
});
|
|
2533
|
-
}
|
|
2534
|
-
function promiseTimeout(ms) {
|
|
2535
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
117
|
+
const availableCases = Object.keys(cases).join(", ");
|
|
118
|
+
const availableCasesSuffix = availableCases.length > 0 ? ` Available cases: ${availableCases}` : " Available cases: (none)";
|
|
119
|
+
const contextSuffix = discriminantKey ? ` (discriminant key: "${discriminantKey}")` : "";
|
|
120
|
+
throw new Error(`Unhandled case: ${key}${contextSuffix}.${availableCasesSuffix}`);
|
|
2536
121
|
}
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
const data = await promise;
|
|
2540
|
-
return { data, error: null };
|
|
2541
|
-
} catch (error) {
|
|
2542
|
-
return { data: null, error };
|
|
2543
|
-
}
|
|
122
|
+
function assertNever(value, message) {
|
|
123
|
+
throw new Error(message ?? `Unexpected value: ${String(value)}`);
|
|
2544
124
|
}
|
|
2545
125
|
// src/cache/index.ts
|
|
2546
126
|
function cacheFunction(options) {
|
|
@@ -2551,84 +131,151 @@ function cacheFunction(options) {
|
|
|
2551
131
|
refreshInBackground = false,
|
|
2552
132
|
cacheKeyArgs,
|
|
2553
133
|
cacheKeyFn,
|
|
2554
|
-
onBackgroundRefreshError
|
|
134
|
+
onBackgroundRefreshError,
|
|
135
|
+
store,
|
|
136
|
+
onStoreError,
|
|
137
|
+
maxEntries
|
|
2555
138
|
} = options;
|
|
139
|
+
const externalStore = store;
|
|
2556
140
|
const cache = new Map;
|
|
2557
141
|
const pendingPromises = new Map;
|
|
2558
|
-
const
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
142
|
+
const entryLifetimeMs = refreshInBackground ? cacheTimeMs * 2 : cacheTimeMs;
|
|
143
|
+
let cleanupInterval;
|
|
144
|
+
const stopCleanup = () => {
|
|
145
|
+
if (cleanupInterval !== undefined) {
|
|
146
|
+
clearInterval(cleanupInterval);
|
|
147
|
+
cleanupInterval = undefined;
|
|
2564
148
|
}
|
|
2565
|
-
}
|
|
2566
|
-
const
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
const now = Date.now();
|
|
2570
|
-
const pending = pendingPromises.get(key);
|
|
2571
|
-
if (pending) {
|
|
2572
|
-
return pending;
|
|
149
|
+
};
|
|
150
|
+
const ensureCleanup = () => {
|
|
151
|
+
if (cleanupInterval !== undefined) {
|
|
152
|
+
return;
|
|
2573
153
|
}
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
const
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
} else {
|
|
2580
|
-
if (isCacheFresh && !refreshInBackground) {
|
|
2581
|
-
return cached.value;
|
|
2582
|
-
}
|
|
2583
|
-
if (refreshInBackground) {
|
|
2584
|
-
const existingPending = pendingPromises.get(key);
|
|
2585
|
-
if (existingPending) {
|
|
2586
|
-
return existingPending;
|
|
2587
|
-
}
|
|
2588
|
-
const backgroundUpdate = fn(...args);
|
|
2589
|
-
if (isPromiseLike(backgroundUpdate)) {
|
|
2590
|
-
const backgroundPromise2 = backgroundUpdate.then((resolvedValue) => {
|
|
2591
|
-
cache.set(key, { value: resolvedValue, timestamp: now });
|
|
2592
|
-
pendingPromises.delete(key);
|
|
2593
|
-
return resolvedValue;
|
|
2594
|
-
}).catch((error) => {
|
|
2595
|
-
pendingPromises.delete(key);
|
|
2596
|
-
onBackgroundRefreshError?.(error, { name });
|
|
2597
|
-
});
|
|
2598
|
-
pendingPromises.set(key, backgroundPromise2);
|
|
2599
|
-
} else {
|
|
2600
|
-
cache.set(key, {
|
|
2601
|
-
value: backgroundUpdate,
|
|
2602
|
-
timestamp: now
|
|
2603
|
-
});
|
|
2604
|
-
}
|
|
2605
|
-
return cached.value;
|
|
2606
|
-
}
|
|
2607
|
-
if (isCacheFresh) {
|
|
2608
|
-
return cached.value;
|
|
154
|
+
cleanupInterval = setInterval(() => {
|
|
155
|
+
const now = Date.now();
|
|
156
|
+
for (const [key, entry] of cache.entries()) {
|
|
157
|
+
if (now - entry.timestamp >= entryLifetimeMs) {
|
|
158
|
+
cache.delete(key);
|
|
2609
159
|
}
|
|
2610
160
|
}
|
|
161
|
+
if (cache.size === 0) {
|
|
162
|
+
stopCleanup();
|
|
163
|
+
}
|
|
164
|
+
}, Math.min(cacheTimeMs / 2, 5 * 60 * 1000));
|
|
165
|
+
cleanupInterval.unref?.();
|
|
166
|
+
};
|
|
167
|
+
const buildKey = (args) => {
|
|
168
|
+
const keyArgs = cacheKeyFn ? cacheKeyFn(...args) : cacheKeyArgs ? cacheKeyArgs.map((i) => args[i]) : args;
|
|
169
|
+
return `${name}:${stableStringify2(keyArgs)}`;
|
|
170
|
+
};
|
|
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
|
+
}
|
|
179
|
+
};
|
|
180
|
+
const guardStore = async (operation) => {
|
|
181
|
+
try {
|
|
182
|
+
return await operation();
|
|
183
|
+
} catch (error) {
|
|
184
|
+
onStoreError?.(error, { name });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
const save = (key, value) => {
|
|
189
|
+
const entry = { value, timestamp: Date.now() };
|
|
190
|
+
if (externalStore) {
|
|
191
|
+
return guardStore(() => externalStore.set(key, entry, entryLifetimeMs));
|
|
2611
192
|
}
|
|
193
|
+
touch(key, entry);
|
|
194
|
+
ensureCleanup();
|
|
195
|
+
};
|
|
196
|
+
const run = (key, args, stale) => {
|
|
2612
197
|
const result = fn(...args);
|
|
2613
|
-
if (isPromiseLike(result)) {
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
198
|
+
if (!isPromiseLike(result)) {
|
|
199
|
+
save(key, result);
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
const promise = Promise.resolve(result).then(async (value) => {
|
|
203
|
+
if (pendingPromises.get(key) === promise) {
|
|
204
|
+
await save(key, value);
|
|
205
|
+
}
|
|
206
|
+
return value;
|
|
207
|
+
}, (error) => {
|
|
208
|
+
if (!stale)
|
|
2620
209
|
throw error;
|
|
2621
|
-
});
|
|
2622
|
-
|
|
2623
|
-
|
|
210
|
+
onBackgroundRefreshError?.(error, { name });
|
|
211
|
+
return stale.value;
|
|
212
|
+
}).finally(() => {
|
|
213
|
+
if (pendingPromises.get(key) === promise) {
|
|
214
|
+
pendingPromises.delete(key);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
pendingPromises.set(key, promise);
|
|
218
|
+
return promise;
|
|
219
|
+
};
|
|
220
|
+
const serve = (key, args, entry) => {
|
|
221
|
+
if (!isFresh(entry) && !pendingPromises.has(key)) {
|
|
222
|
+
run(key, args, entry);
|
|
2624
223
|
}
|
|
2625
|
-
|
|
2626
|
-
|
|
224
|
+
return entry.value;
|
|
225
|
+
};
|
|
226
|
+
const callWithStore = (cacheStore, key, args) => {
|
|
227
|
+
const pending = pendingPromises.get(key);
|
|
228
|
+
if (pending && !refreshInBackground) {
|
|
229
|
+
return pending;
|
|
230
|
+
}
|
|
231
|
+
return (async () => {
|
|
232
|
+
const entry = await guardStore(() => cacheStore.get(key));
|
|
233
|
+
const cached = servable(entry);
|
|
234
|
+
if (cached) {
|
|
235
|
+
return serve(key, args, cached);
|
|
236
|
+
}
|
|
237
|
+
if (entry) {
|
|
238
|
+
await guardStore(() => cacheStore.delete(key));
|
|
239
|
+
}
|
|
240
|
+
return pendingPromises.get(key) ?? run(key, args);
|
|
241
|
+
})();
|
|
242
|
+
};
|
|
243
|
+
const wrappedFn = (...args) => {
|
|
244
|
+
const key = buildKey(args);
|
|
245
|
+
if (externalStore) {
|
|
246
|
+
return callWithStore(externalStore, key, args);
|
|
247
|
+
}
|
|
248
|
+
const cached = servable(cache.get(key));
|
|
249
|
+
if (cached) {
|
|
250
|
+
touch(key, cached);
|
|
251
|
+
return serve(key, args, cached);
|
|
252
|
+
}
|
|
253
|
+
cache.delete(key);
|
|
254
|
+
return pendingPromises.get(key) ?? run(key, args);
|
|
2627
255
|
};
|
|
2628
256
|
wrappedFn.clearCache = () => {
|
|
2629
257
|
cache.clear();
|
|
2630
258
|
pendingPromises.clear();
|
|
2631
|
-
|
|
259
|
+
stopCleanup();
|
|
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);
|
|
277
|
+
}
|
|
278
|
+
return servable(cache.get(key))?.value;
|
|
2632
279
|
};
|
|
2633
280
|
return wrappedFn;
|
|
2634
281
|
}
|
|
@@ -2650,84 +297,54 @@ class SimpleStateMachine {
|
|
|
2650
297
|
}
|
|
2651
298
|
async getAvailableActions() {
|
|
2652
299
|
const actionsMap = this.config.stateActionMap[this.getCurrentStatus()] || {};
|
|
2653
|
-
const
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
continue;
|
|
2657
|
-
const validationResult = await this.validateTransition(targetState, { action });
|
|
2658
|
-
result[action] = validationResult;
|
|
2659
|
-
}
|
|
2660
|
-
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]]));
|
|
2661
303
|
}
|
|
2662
304
|
async getPossibleTransitions() {
|
|
2663
305
|
const possibleTransitions = this.config.stateTransitions[this.getCurrentStatus()] || [];
|
|
2664
306
|
const actionsMap = this.config.stateActionMap[this.getCurrentStatus()] || {};
|
|
2665
|
-
const
|
|
2666
|
-
for (const targetState of possibleTransitions) {
|
|
307
|
+
const results = await Promise.all(possibleTransitions.map(async (targetState) => {
|
|
2667
308
|
const actionsToTarget = Object.entries(actionsMap).filter(([, t]) => t === targetState);
|
|
2668
309
|
if (actionsToTarget.length === 0) {
|
|
2669
|
-
|
|
2670
|
-
continue;
|
|
310
|
+
return this.validateTransition(targetState);
|
|
2671
311
|
}
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
action: actionKey
|
|
2677
|
-
});
|
|
2678
|
-
if (r === true) {
|
|
2679
|
-
anyOk = true;
|
|
2680
|
-
break;
|
|
2681
|
-
}
|
|
2682
|
-
lastError = r;
|
|
2683
|
-
}
|
|
2684
|
-
result[targetState] = anyOk ? true : lastError;
|
|
2685
|
-
}
|
|
2686
|
-
return result;
|
|
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]]));
|
|
2687
316
|
}
|
|
2688
|
-
async
|
|
317
|
+
async canExecute(action) {
|
|
2689
318
|
const actionsMap = this.config.stateActionMap[this.getCurrentStatus()];
|
|
2690
319
|
if (!actionsMap) {
|
|
2691
|
-
return {
|
|
2692
|
-
success: false,
|
|
2693
|
-
error: `No actions defined for state '${this.getCurrentStatus()}'`
|
|
2694
|
-
};
|
|
320
|
+
return `No actions defined for state '${this.getCurrentStatus()}'`;
|
|
2695
321
|
}
|
|
2696
322
|
const targetState = actionsMap[action];
|
|
2697
323
|
if (!targetState) {
|
|
2698
|
-
return {
|
|
2699
|
-
success: false,
|
|
2700
|
-
error: `Action '${action}' not allowed in state '${this.getCurrentStatus()}'`
|
|
2701
|
-
};
|
|
324
|
+
return `Action '${action}' not allowed in state '${this.getCurrentStatus()}'`;
|
|
2702
325
|
}
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
success: false,
|
|
2718
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2719
|
-
};
|
|
2720
|
-
}
|
|
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;
|
|
2721
340
|
}
|
|
341
|
+
return { success: true, newState: targetState, result };
|
|
342
|
+
} catch (error) {
|
|
2722
343
|
return {
|
|
2723
|
-
success:
|
|
2724
|
-
|
|
344
|
+
success: false,
|
|
345
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2725
346
|
};
|
|
2726
347
|
}
|
|
2727
|
-
return {
|
|
2728
|
-
success: false,
|
|
2729
|
-
error: validationResult
|
|
2730
|
-
};
|
|
2731
348
|
}
|
|
2732
349
|
async validateTransition(toState, options) {
|
|
2733
350
|
const action = options?.action;
|
|
@@ -2745,7 +362,14 @@ class SimpleStateMachine {
|
|
|
2745
362
|
}
|
|
2746
363
|
const stateValidator = this.config.validators?.[toState];
|
|
2747
364
|
if (stateValidator) {
|
|
2748
|
-
|
|
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);
|
|
2749
373
|
}
|
|
2750
374
|
return true;
|
|
2751
375
|
}
|
|
@@ -2753,17 +377,23 @@ class SimpleStateMachine {
|
|
|
2753
377
|
return Object.keys(this.config.stateTransitions);
|
|
2754
378
|
}
|
|
2755
379
|
getInitialState() {
|
|
2756
|
-
return this.getStates()[0];
|
|
380
|
+
return this.config.initialState ?? this.getStates()[0];
|
|
2757
381
|
}
|
|
2758
382
|
}
|
|
383
|
+
function createStateMachine(config) {
|
|
384
|
+
return {
|
|
385
|
+
config,
|
|
386
|
+
for: (item, stateKey) => new SimpleStateMachine(config, item, stateKey)
|
|
387
|
+
};
|
|
388
|
+
}
|
|
2759
389
|
// src/math/linearRegressionTrend.ts
|
|
2760
|
-
function
|
|
390
|
+
function linearRegression(values) {
|
|
2761
391
|
const n = values.length;
|
|
2762
392
|
if (n < 2)
|
|
2763
|
-
return
|
|
393
|
+
return null;
|
|
2764
394
|
for (const v of values) {
|
|
2765
395
|
if (!Number.isFinite(v))
|
|
2766
|
-
return
|
|
396
|
+
return null;
|
|
2767
397
|
}
|
|
2768
398
|
let sumX = 0;
|
|
2769
399
|
let sumY = 0;
|
|
@@ -2779,24 +409,155 @@ function linearRegressionTrend(values) {
|
|
|
2779
409
|
}
|
|
2780
410
|
const denom = n * sumX2 - sumX * sumX;
|
|
2781
411
|
if (denom === 0)
|
|
2782
|
-
return
|
|
412
|
+
return null;
|
|
2783
413
|
const slope = (n * sumXY - sumX * sumY) / denom;
|
|
2784
414
|
const intercept = (sumY - slope * sumX) / n;
|
|
2785
|
-
const
|
|
415
|
+
const mean = sumY / n;
|
|
416
|
+
const fitted = [];
|
|
417
|
+
let ssRes = 0;
|
|
418
|
+
let ssTot = 0;
|
|
2786
419
|
for (let i = 0;i < n; i++) {
|
|
2787
|
-
|
|
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;
|
|
2788
425
|
}
|
|
2789
|
-
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);
|
|
2790
523
|
}
|
|
2791
|
-
|
|
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
|
+
};
|
|
537
|
+
}
|
|
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
|
|
2792
548
|
var METRICS_SERIES_PALETTE_SIZE = 10;
|
|
2793
|
-
function hashStringToColorIndex(str) {
|
|
549
|
+
function hashStringToColorIndex(str, paletteSize = METRICS_SERIES_PALETTE_SIZE) {
|
|
2794
550
|
let hash = 2166136261;
|
|
2795
551
|
for (let i = 0;i < str.length; i++) {
|
|
2796
552
|
hash ^= str.charCodeAt(i);
|
|
2797
553
|
hash = Math.imul(hash, 16777619);
|
|
2798
554
|
}
|
|
2799
|
-
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)];
|
|
2800
561
|
}
|
|
2801
562
|
function circularHueDistance(a, b, paletteSize) {
|
|
2802
563
|
const diff = Math.abs(a - b);
|
|
@@ -2823,21 +584,20 @@ function seriesColorKey(label, key) {
|
|
|
2823
584
|
return fromLabel;
|
|
2824
585
|
return key.trim() || key;
|
|
2825
586
|
}
|
|
2826
|
-
function resolveSeriesColorMap(keys) {
|
|
587
|
+
function resolveSeriesColorMap(keys, paletteSize = METRICS_SERIES_PALETTE_SIZE) {
|
|
2827
588
|
const result = new Map;
|
|
2828
589
|
const uniqueKeys = [...new Set(keys)];
|
|
2829
590
|
if (uniqueKeys.length === 0)
|
|
2830
591
|
return result;
|
|
2831
|
-
const paletteSize = METRICS_SERIES_PALETTE_SIZE;
|
|
2832
592
|
if (uniqueKeys.length === 1) {
|
|
2833
593
|
const only = uniqueKeys[0];
|
|
2834
|
-
result.set(only, hashStringToColorIndex(only));
|
|
594
|
+
result.set(only, hashStringToColorIndex(only, paletteSize));
|
|
2835
595
|
return result;
|
|
2836
596
|
}
|
|
2837
597
|
const usedIndices = new Set;
|
|
2838
598
|
const entries = uniqueKeys.map((key) => ({
|
|
2839
599
|
key,
|
|
2840
|
-
preferred: hashStringToColorIndex(key)
|
|
600
|
+
preferred: hashStringToColorIndex(key, paletteSize)
|
|
2841
601
|
}));
|
|
2842
602
|
for (const entry of entries) {
|
|
2843
603
|
if (!usedIndices.has(entry.preferred)) {
|
|
@@ -2859,35 +619,25 @@ function resolveSeriesColorMap(keys) {
|
|
|
2859
619
|
}
|
|
2860
620
|
return result;
|
|
2861
621
|
}
|
|
2862
|
-
function createResolveColorsByKeys(keys, getSeriesColor, getSeriesHoverColor) {
|
|
2863
|
-
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);
|
|
2864
625
|
return {
|
|
2865
|
-
getColor: (key) => getSeriesColor(
|
|
2866
|
-
getHoverColor: (key) => getSeriesHoverColor(
|
|
626
|
+
getColor: (key) => getSeriesColor(indexOf(key)),
|
|
627
|
+
getHoverColor: (key) => getSeriesHoverColor(indexOf(key))
|
|
2867
628
|
};
|
|
2868
629
|
}
|
|
2869
|
-
function clamp(value, min, max) {
|
|
2870
|
-
return Math.min(max, Math.max(min, value));
|
|
2871
|
-
}
|
|
2872
630
|
function withAlpha(color, alpha) {
|
|
2873
631
|
const trimmed = color.trim();
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
const g = Number.parseInt(normalized.slice(2, 4), 16);
|
|
2880
|
-
const b = Number.parseInt(normalized.slice(4, 6), 16);
|
|
2881
|
-
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
2882
|
-
}
|
|
2883
|
-
}
|
|
2884
|
-
if (trimmed.startsWith("rgb(")) {
|
|
2885
|
-
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})`;
|
|
2886
637
|
}
|
|
2887
|
-
if (
|
|
2888
|
-
return
|
|
2889
|
-
}
|
|
2890
|
-
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)`;
|
|
2891
641
|
}
|
|
2892
642
|
function getHeatmapCellStyle(params) {
|
|
2893
643
|
const {
|
|
@@ -2910,136 +660,440 @@ function getHeatmapCellStyle(params) {
|
|
|
2910
660
|
backgroundColor: withAlpha(neutralColor ?? baseColor, 0.32)
|
|
2911
661
|
};
|
|
2912
662
|
}
|
|
2913
|
-
const normalized = clamp((value - min) / (max - min), 0, 1);
|
|
2914
|
-
const alpha = minAlpha + (maxAlpha - minAlpha) * normalized;
|
|
2915
663
|
return {
|
|
2916
|
-
backgroundColor: withAlpha(baseColor,
|
|
664
|
+
backgroundColor: withAlpha(baseColor, lerp(minAlpha, maxAlpha, normalize(value, min, max)))
|
|
2917
665
|
};
|
|
2918
666
|
}
|
|
2919
667
|
// src/date/index.ts
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
const prevQYear = qStart === 0 ? year - 1 : year;
|
|
2936
|
-
return {
|
|
2937
|
-
start: new Date(prevQYear, prevQStart, 1),
|
|
2938
|
-
end: new Date(prevQYear, prevQStart + 3, 0)
|
|
2939
|
-
};
|
|
2940
|
-
}
|
|
2941
|
-
case "YEARLY": {
|
|
2942
|
-
return {
|
|
2943
|
-
start: new Date(year - 1, 0, 1),
|
|
2944
|
-
end: new Date(year - 1, 11, 31)
|
|
2945
|
-
};
|
|
2946
|
-
}
|
|
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());
|
|
2947
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);
|
|
2948
710
|
}
|
|
2949
|
-
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;
|
|
2950
719
|
const s = start instanceof Date ? start : new Date(start);
|
|
2951
720
|
const e = end instanceof Date ? end : new Date(end);
|
|
2952
|
-
const
|
|
721
|
+
const format = new Intl.DateTimeFormat(locale, {
|
|
2953
722
|
year: "numeric",
|
|
2954
723
|
month: "long",
|
|
2955
|
-
...includeDay && { day: "numeric" }
|
|
724
|
+
...includeDay && { day: "numeric" },
|
|
725
|
+
timeZone
|
|
726
|
+
});
|
|
727
|
+
return collapse ? format.formatRange(s, e) : `${format.format(s)}${separator}${format.format(e)}`;
|
|
728
|
+
}
|
|
729
|
+
// src/object/index.ts
|
|
730
|
+
function typedKeys(object) {
|
|
731
|
+
return Object.keys(object);
|
|
732
|
+
}
|
|
733
|
+
function typedEntries(object) {
|
|
734
|
+
return Object.entries(object);
|
|
735
|
+
}
|
|
736
|
+
function typedFromEntries(entries) {
|
|
737
|
+
return Object.fromEntries(entries);
|
|
738
|
+
}
|
|
739
|
+
function recordFromKeys(keys, fn) {
|
|
740
|
+
return typedFromEntries(keys.map((key) => [key, fn(key)]));
|
|
741
|
+
}
|
|
742
|
+
function mapValues(object, fn) {
|
|
743
|
+
return typedFromEntries(typedEntries(object).map(([key, value]) => [key, fn(value, key)]));
|
|
744
|
+
}
|
|
745
|
+
function pick(object, keys) {
|
|
746
|
+
const result = {};
|
|
747
|
+
for (const key of keys) {
|
|
748
|
+
if (key in object)
|
|
749
|
+
result[key] = object[key];
|
|
750
|
+
}
|
|
751
|
+
return result;
|
|
752
|
+
}
|
|
753
|
+
function omit(object, keys) {
|
|
754
|
+
const result = { ...object };
|
|
755
|
+
for (const key of keys)
|
|
756
|
+
delete result[key];
|
|
757
|
+
return result;
|
|
758
|
+
}
|
|
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(".");
|
|
764
|
+
}
|
|
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];
|
|
771
|
+
}
|
|
772
|
+
return current;
|
|
773
|
+
}
|
|
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}`);
|
|
781
|
+
}
|
|
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;
|
|
790
|
+
}
|
|
791
|
+
current = next;
|
|
792
|
+
}
|
|
793
|
+
current[keys.at(-1)] = value;
|
|
794
|
+
}
|
|
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;
|
|
804
|
+
};
|
|
805
|
+
const flush = () => {
|
|
806
|
+
const args = lastArgs;
|
|
807
|
+
cancel();
|
|
808
|
+
if (args)
|
|
809
|
+
fn(...args);
|
|
810
|
+
};
|
|
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);
|
|
817
|
+
}
|
|
818
|
+
if (idle && options.leading)
|
|
819
|
+
fn(...args);
|
|
820
|
+
else
|
|
821
|
+
lastArgs = args;
|
|
822
|
+
};
|
|
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);
|
|
836
|
+
};
|
|
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);
|
|
846
|
+
}
|
|
847
|
+
debounced(...args);
|
|
848
|
+
},
|
|
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());
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
}
|
|
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);
|
|
874
|
+
};
|
|
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;
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
function once(fn) {
|
|
889
|
+
let called = false;
|
|
890
|
+
let result;
|
|
891
|
+
return (...args) => {
|
|
892
|
+
if (!called) {
|
|
893
|
+
called = true;
|
|
894
|
+
result = fn(...args);
|
|
895
|
+
}
|
|
896
|
+
return result;
|
|
2956
897
|
};
|
|
2957
|
-
return `${s.toLocaleDateString("en-GB", opts)} — ${e.toLocaleDateString("en-GB", opts)}`;
|
|
2958
898
|
}
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
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();
|
|
924
|
+
}
|
|
925
|
+
if (idleTimer !== undefined)
|
|
926
|
+
unschedule(idleTimer);
|
|
927
|
+
idleTimer = schedule(stop, options.idleMs);
|
|
928
|
+
},
|
|
929
|
+
stop
|
|
930
|
+
};
|
|
931
|
+
}
|
|
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
|
+
};
|
|
942
|
+
};
|
|
943
|
+
return {
|
|
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
|
|
978
|
+
};
|
|
2962
979
|
}
|
|
2963
980
|
export {
|
|
2964
|
-
|
|
2965
|
-
zodToExample,
|
|
2966
|
-
zodMinMaxFilter,
|
|
2967
|
-
zSingleOrArray,
|
|
2968
|
-
withAlpha,
|
|
2969
|
-
viteSecretSetPlugin,
|
|
2970
|
-
unwrapResult,
|
|
2971
|
-
typedSwitch,
|
|
2972
|
-
tryCatch,
|
|
2973
|
-
toGteLteFilter,
|
|
2974
|
-
terminalColors,
|
|
2975
|
-
slugify,
|
|
2976
|
-
seriesColorKey,
|
|
2977
|
-
resolveSeriesColorMap,
|
|
2978
|
-
promiseTimeout,
|
|
2979
|
-
processEnvLoader,
|
|
2980
|
-
pickItemsInArray,
|
|
2981
|
-
partition,
|
|
2982
|
-
normalizeSingleOrArray,
|
|
2983
|
-
logOperationSummary,
|
|
2984
|
-
logBanner,
|
|
2985
|
-
loadInfisicalSecrets,
|
|
2986
|
-
linearRegressionTrend,
|
|
2987
|
-
keyBy,
|
|
2988
|
-
jwtVerify,
|
|
2989
|
-
jwtSign,
|
|
2990
|
-
jsonToZodSchemaMapper,
|
|
2991
|
-
isSuccess,
|
|
2992
|
-
isResponseValidationError,
|
|
2993
|
-
isRequestValidationError,
|
|
2994
|
-
isRequestTimeoutError,
|
|
2995
|
-
isRequestAbortedError,
|
|
2996
|
-
isNetworkError,
|
|
2997
|
-
isHttpResponseError,
|
|
2998
|
-
isFailure,
|
|
2999
|
-
isConfigError,
|
|
3000
|
-
isApiError,
|
|
3001
|
-
isActionError,
|
|
3002
|
-
infisicalLoader,
|
|
3003
|
-
hashStringToColorIndex,
|
|
3004
|
-
groupBy,
|
|
3005
|
-
getViteDefine,
|
|
3006
|
-
getUniqueValuesByKey,
|
|
3007
|
-
getUniqueValues,
|
|
3008
|
-
getSecretEnvironment,
|
|
3009
|
-
getPreviousReportPeriod,
|
|
3010
|
-
getHeatmapCellStyle,
|
|
3011
|
-
getDomainSitemap,
|
|
3012
|
-
getDomainFaviconUrl,
|
|
3013
|
-
generateId,
|
|
3014
|
-
formatPeriodLabel,
|
|
3015
|
-
findArrayDifferenceByKey,
|
|
3016
|
-
fetchInfisicalSecrets,
|
|
3017
|
-
extractNumber,
|
|
3018
|
-
defineSecretSet,
|
|
3019
|
-
defineAction,
|
|
3020
|
-
createZId,
|
|
3021
|
-
createResolveColorsByKeys,
|
|
3022
|
-
createCoolLogger,
|
|
3023
|
-
createApiClient,
|
|
3024
|
-
compact,
|
|
3025
|
-
colorize,
|
|
3026
|
-
cloudflareWorkerEnvLoader,
|
|
3027
|
-
chunkArray,
|
|
3028
|
-
cacheFunction,
|
|
3029
|
-
backgroundPromiseSync,
|
|
3030
|
-
backgroundPromise,
|
|
3031
|
-
TimeoutError,
|
|
3032
|
-
SimpleStateMachine,
|
|
3033
|
-
ResponseValidationError,
|
|
3034
|
-
RequestValidationError,
|
|
3035
|
-
RequestTimeoutError,
|
|
3036
|
-
RequestAbortedError,
|
|
3037
|
-
PriorityQueue,
|
|
3038
|
-
NetworkError,
|
|
981
|
+
InvariantError2 as InvariantError,
|
|
3039
982
|
METRICS_SERIES_PALETTE_SIZE,
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
983
|
+
PromiseTimeoutError2 as PromiseTimeoutError,
|
|
984
|
+
SimpleStateMachine,
|
|
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,
|
|
993
|
+
cacheFunction,
|
|
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,
|
|
1004
|
+
createResolveColorsByKeys,
|
|
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,
|
|
1014
|
+
formatPeriodLabel,
|
|
1015
|
+
generateId2 as generateId,
|
|
1016
|
+
getAtPath,
|
|
1017
|
+
getDomainFaviconUrl2 as getDomainFaviconUrl,
|
|
1018
|
+
getErrorMessage2 as getErrorMessage,
|
|
1019
|
+
getHeatmapCellStyle,
|
|
1020
|
+
getPeriod,
|
|
1021
|
+
getPreviousReportPeriod,
|
|
1022
|
+
getUniqueValues2 as getUniqueValues,
|
|
1023
|
+
getUniqueValuesByKey2 as getUniqueValuesByKey,
|
|
1024
|
+
groupBy2 as groupBy,
|
|
1025
|
+
hasKey2 as hasKey,
|
|
1026
|
+
hashStringToColorIndex,
|
|
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,
|
|
1045
|
+
linearRegressionTrend,
|
|
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,
|
|
1070
|
+
resolveSeriesColorMap,
|
|
1071
|
+
retry2 as retry,
|
|
1072
|
+
safeJsonParse2 as safeJsonParse,
|
|
1073
|
+
seriesColorKey,
|
|
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,
|
|
1095
|
+
typedSwitch,
|
|
1096
|
+
unwrapResult2 as unwrapResult,
|
|
1097
|
+
withAlpha,
|
|
1098
|
+
withTimeout2 as withTimeout
|
|
3045
1099
|
};
|