hanzio 1.2.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.
Files changed (145) hide show
  1. package/README.md +786 -239
  2. package/dist/api-wrapper/builders.d.ts +4 -3
  3. package/dist/api-wrapper/create-client.d.ts +1 -1
  4. package/dist/api-wrapper/describe.d.ts +16 -0
  5. package/dist/api-wrapper/endpoint.d.ts +18 -0
  6. package/dist/api-wrapper/errors.d.ts +39 -2
  7. package/dist/api-wrapper/index.d.ts +5 -0
  8. package/dist/api-wrapper/otel.d.ts +44 -0
  9. package/dist/api-wrapper/paginate.d.ts +12 -0
  10. package/dist/api-wrapper/redaction.d.ts +2 -0
  11. package/dist/api-wrapper/shared.d.ts +16 -2
  12. package/dist/api-wrapper/testing.d.ts +23 -0
  13. package/dist/api-wrapper/transport.d.ts +4 -5
  14. package/dist/api-wrapper/types.d.ts +103 -18
  15. package/dist/array/index.d.ts +50 -4
  16. package/dist/cache/createCache.d.ts +31 -0
  17. package/dist/cache/index.d.ts +19 -2
  18. package/dist/color/contrast.d.ts +40 -0
  19. package/dist/color/index.d.ts +2 -0
  20. package/dist/{math → color}/metricsSeriesColors.d.ts +14 -7
  21. package/dist/cool-console-log/index.d.ts +34 -5
  22. package/dist/crypto/encoding.d.ts +18 -0
  23. package/dist/crypto/hash.d.ts +33 -0
  24. package/dist/crypto/index.d.ts +4 -0
  25. package/dist/crypto/sealer.d.ts +25 -0
  26. package/dist/crypto/webhook.d.ts +20 -0
  27. package/dist/date/index.d.ts +46 -2
  28. package/dist/emitter/index.d.ts +21 -0
  29. package/dist/error/index.d.ts +12 -0
  30. package/dist/format/index.d.ts +102 -0
  31. package/dist/function/index.d.ts +60 -0
  32. package/dist/guard/index.d.ts +20 -0
  33. package/dist/i18n/index.d.ts +37 -0
  34. package/dist/index-0zvtcbzy.js +224 -0
  35. package/dist/index-181e6vfw.js +29 -0
  36. package/dist/index-8r2vdzjg.js +87 -0
  37. package/dist/index-hy7kbb99.js +157 -0
  38. package/dist/{src/secrets/infisical.js → index-j1ybktbr.js} +500 -538
  39. package/dist/index-k9yx6bne.js +89 -0
  40. package/dist/index-kj1phgrx.js +169 -0
  41. package/dist/index-mnkcfyx8.js +78 -0
  42. package/dist/{p-queue/index.js → index-pwt0dz63.js} +7 -11
  43. package/dist/index-rp5pqghz.js +157 -0
  44. package/dist/index-srt06xd2.js +15 -0
  45. package/dist/index-wjyb9bsd.js +263 -0
  46. package/dist/index.d.ts +7 -7
  47. package/dist/jwt/index.d.ts +50 -2
  48. package/dist/math/index.d.ts +2 -2
  49. package/dist/math/linearRegressionTrend.d.ts +14 -1
  50. package/dist/math/scale.d.ts +8 -0
  51. package/dist/object/index.d.ts +19 -0
  52. package/dist/p-queue/index.d.ts +1 -1
  53. package/dist/promise/index.d.ts +97 -2
  54. package/dist/sitemap/index.d.ts +48 -6
  55. package/dist/src/api-wrapper/index.js +60 -1130
  56. package/dist/src/api-wrapper/testing.js +20 -0
  57. package/dist/{cool-console-log → src/cool-console-log}/index.js +80 -33
  58. package/dist/src/crypto/index.js +195 -0
  59. package/dist/src/format/index.js +211 -0
  60. package/dist/src/i18n/index.js +36 -0
  61. package/dist/src/index.js +739 -3145
  62. package/dist/src/jwt/index.js +138 -92
  63. package/dist/src/p-queue/index.js +9 -437
  64. package/dist/src/secrets/index.js +44 -1428
  65. package/dist/src/secrets/vite.js +6 -39
  66. package/dist/src/secrets/worker.js +10 -273
  67. package/dist/src/sitemap/index.js +183 -511
  68. package/dist/src/storage/index.js +119 -0
  69. package/dist/src/zod/index.js +340 -167
  70. package/dist/state/index.d.ts +21 -0
  71. package/dist/storage/index.d.ts +58 -0
  72. package/dist/string/index.d.ts +75 -2
  73. package/dist/typedswitch/index.d.ts +31 -24
  74. package/dist/types/index.d.ts +11 -0
  75. package/dist/url/index.d.ts +50 -1
  76. package/dist/zod/index.d.ts +32 -3
  77. package/dist/zod/introspect.d.ts +19 -0
  78. package/dist/zod/jsonToZodSchemaMapper.d.ts +3 -1
  79. package/dist/zod/zodMinMaxFilter.d.ts +22 -3
  80. package/dist/zod/zodToTypeString.d.ts +8 -3
  81. package/package.json +78 -7
  82. package/src/api-wrapper/actions-impl.ts +11 -72
  83. package/src/api-wrapper/builders.ts +32 -25
  84. package/src/api-wrapper/create-client.ts +171 -41
  85. package/src/api-wrapper/describe.ts +157 -0
  86. package/src/api-wrapper/endpoint.ts +26 -0
  87. package/src/api-wrapper/error-reporting.ts +7 -1
  88. package/src/api-wrapper/errors.ts +107 -2
  89. package/src/api-wrapper/headers.ts +11 -6
  90. package/src/api-wrapper/index.ts +5 -0
  91. package/src/api-wrapper/otel.ts +139 -0
  92. package/src/api-wrapper/paginate.ts +28 -0
  93. package/src/api-wrapper/redaction.ts +42 -0
  94. package/src/api-wrapper/shared.ts +21 -2
  95. package/src/api-wrapper/testing.ts +48 -0
  96. package/src/api-wrapper/transport.ts +41 -43
  97. package/src/api-wrapper/types.ts +185 -18
  98. package/src/array/index.ts +195 -42
  99. package/src/cache/createCache.ts +157 -0
  100. package/src/cache/index.ts +132 -147
  101. package/src/color/contrast.ts +163 -0
  102. package/src/color/index.ts +22 -0
  103. package/src/{math → color}/metricsSeriesColors.ts +47 -49
  104. package/src/cool-console-log/index.ts +128 -41
  105. package/src/crypto/encoding.ts +83 -0
  106. package/src/crypto/hash.ts +170 -0
  107. package/src/crypto/index.ts +26 -0
  108. package/src/crypto/sealer.ts +168 -0
  109. package/src/crypto/webhook.ts +86 -0
  110. package/src/date/index.ts +109 -34
  111. package/src/emitter/index.ts +82 -0
  112. package/src/error/index.ts +63 -0
  113. package/src/format/index.ts +389 -0
  114. package/src/function/index.ts +201 -0
  115. package/src/guard/index.ts +80 -0
  116. package/src/i18n/index.ts +125 -0
  117. package/src/index.ts +11 -7
  118. package/src/jwt/index.ts +199 -125
  119. package/src/math/index.ts +5 -10
  120. package/src/math/linearRegressionTrend.ts +33 -8
  121. package/src/math/scale.ts +17 -0
  122. package/src/object/index.ts +99 -0
  123. package/src/p-queue/index.ts +2 -0
  124. package/src/promise/index.ts +304 -9
  125. package/src/sitemap/index.ts +293 -130
  126. package/src/state/index.ts +133 -81
  127. package/src/storage/index.ts +187 -0
  128. package/src/string/index.ts +261 -23
  129. package/src/typedswitch/index.ts +58 -38
  130. package/src/types/index.ts +14 -0
  131. package/src/url/index.ts +146 -1
  132. package/src/zod/index.ts +105 -9
  133. package/src/zod/introspect.ts +103 -0
  134. package/src/zod/jsonToZodSchemaMapper.ts +44 -21
  135. package/src/zod/zodMinMaxFilter.ts +37 -13
  136. package/src/zod/zodToTypeString.ts +184 -181
  137. package/dist/api-wrapper/index.js +0 -250
  138. package/dist/gzip/index.d.ts +0 -2
  139. package/dist/gzip/index.js +0 -16
  140. package/dist/index.js +0 -1823
  141. package/dist/jwt/index.js +0 -19
  142. package/dist/sitemap/index.js +0 -552
  143. package/dist/src/gzip/index.js +0 -16
  144. package/dist/src/secrets/loaders.js +0 -1529
  145. package/dist/zod/index.js +0 -210
@@ -0,0 +1,263 @@
1
+ import {
2
+ SecretLoadError2
3
+ } from "./index-srt06xd2.js";
4
+
5
+ // src/secrets/environment.ts
6
+ function getProcessEnvironment() {
7
+ return typeof process === "undefined" ? undefined : process.env;
8
+ }
9
+ function resolveSecretEnvironment(option, env) {
10
+ const explicit = typeof option === "function" ? option() : option;
11
+ if (typeof explicit === "object" && explicit !== null) {
12
+ if (typeof explicit.slug !== "string" || !/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(explicit.slug)) {
13
+ throw new SecretLoadError2("CONFIGURATION", "Custom secret environment requires a valid slug.");
14
+ }
15
+ return explicit.slug;
16
+ }
17
+ const selected = explicit ?? env?.SECRETS_ENV;
18
+ if (selected !== undefined) {
19
+ if (selected === "production" || selected === "prod") {
20
+ return "prod";
21
+ }
22
+ if (selected === "development" || selected === "dev") {
23
+ return "dev";
24
+ }
25
+ if (selected === "staging") {
26
+ return "staging";
27
+ }
28
+ throw new SecretLoadError2("CONFIGURATION", "Invalid secret environment. Use dev, staging, prod, or an explicit { slug } option.");
29
+ }
30
+ if (env?.NODE_ENV === "production" || env?.NODE_ENV === "prod") {
31
+ return "prod";
32
+ }
33
+ if (env?.NODE_ENV === "staging") {
34
+ return "staging";
35
+ }
36
+ return "dev";
37
+ }
38
+
39
+ // src/secrets/deadline.ts
40
+ async function withSecretDeadline(options, operation) {
41
+ const timeoutMs = options.timeoutMs ?? 30000;
42
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2147483647) {
43
+ throw new SecretLoadError2("CONFIGURATION", "Secret timeoutMs must be a positive finite timer duration.");
44
+ }
45
+ const controller = new AbortController;
46
+ const cancel = () => controller.abort(new SecretLoadError2("ABORTED", "Secret loading was cancelled."));
47
+ if (options.signal?.aborted) {
48
+ cancel();
49
+ } else {
50
+ options.signal?.addEventListener("abort", cancel, { once: true });
51
+ }
52
+ const timer = setTimeout(() => controller.abort(new SecretLoadError2("TIMEOUT", `Secret loading timed out after ${timeoutMs}ms.`)), timeoutMs);
53
+ let rejectOnAbort = () => {};
54
+ try {
55
+ const aborted = new Promise((_, reject) => {
56
+ rejectOnAbort = () => reject(controller.signal.reason);
57
+ controller.signal.addEventListener("abort", rejectOnAbort, { once: true });
58
+ });
59
+ if (controller.signal.aborted) {
60
+ throw controller.signal.reason;
61
+ }
62
+ return await Promise.race([operation(controller.signal), aborted]);
63
+ } finally {
64
+ clearTimeout(timer);
65
+ options.signal?.removeEventListener("abort", cancel);
66
+ controller.signal.removeEventListener("abort", rejectOnAbort);
67
+ }
68
+ }
69
+
70
+ // src/secrets/core.ts
71
+ var registryKey = Symbol.for("hanzio.secrets.environmentWrites.v1");
72
+ var shared = globalThis;
73
+ var environmentWrites = shared[registryKey] ?? new WeakMap;
74
+ shared[registryKey] = environmentWrites;
75
+ var defaultLogger = {
76
+ info: (message, attrs) => console.log(`${message} environment=${attrs.environment} count=${attrs.count} source=${attrs.source} durationMs=${attrs.durationMs}`, { sources: attrs.sources }),
77
+ error: (message, attrs) => console.error(message, attrs)
78
+ };
79
+ function readOverrides(keys, env) {
80
+ if (!env) {
81
+ return {};
82
+ }
83
+ const writes = environmentWrites.get(env);
84
+ return Object.fromEntries(keys.flatMap((key) => {
85
+ const value = Object.hasOwn(env, key) ? env[key] : undefined;
86
+ if (writes?.has(key)) {
87
+ if (writes.get(key) === value) {
88
+ return [];
89
+ }
90
+ writes.delete(key);
91
+ }
92
+ return value ? [[key, value]] : [];
93
+ }));
94
+ }
95
+ async function defineSecretSet2(keys, options, environmentVariables) {
96
+ const keySet = [...new Set(keys)];
97
+ const environment = resolveSecretEnvironment(options.environment, environmentVariables);
98
+ const logger = options.logger === false ? undefined : options.logger ?? defaultLogger;
99
+ let cached;
100
+ let parsed;
101
+ let sources;
102
+ let inFlight;
103
+ const log = (level, message, attributes) => {
104
+ try {
105
+ logger?.[level](message, attributes);
106
+ } catch {}
107
+ };
108
+ const loadSnapshot = async (request, signal) => {
109
+ const local = readOverrides(keySet, environmentVariables);
110
+ const missing = keySet.filter((key) => !Object.hasOwn(local, key));
111
+ let loaderSource = options.loader.source ?? "custom loader";
112
+ let loaded = {};
113
+ if (missing.length) {
114
+ try {
115
+ loaded = await options.loader({
116
+ keys: missing,
117
+ environment,
118
+ signal,
119
+ timeoutMs: request.timeoutMs ?? options.timeoutMs ?? 30000,
120
+ reportSource: (source) => {
121
+ loaderSource = source;
122
+ }
123
+ });
124
+ } catch (error) {
125
+ if (error instanceof SecretLoadError2) {
126
+ throw error;
127
+ }
128
+ throw new SecretLoadError2("LOADER", "Secret loader failed.");
129
+ }
130
+ }
131
+ const overrides = readOverrides(keySet, environmentVariables);
132
+ const next = Object.fromEntries(keySet.map((key) => {
133
+ if (Object.hasOwn(overrides, key)) {
134
+ return [key, overrides[key]];
135
+ }
136
+ if (Object.hasOwn(loaded, key)) {
137
+ return [key, loaded[key]];
138
+ }
139
+ return [key, undefined];
140
+ }));
141
+ const missingKeys = keySet.filter((key) => typeof next[key] !== "string" || !next[key]);
142
+ if (missingKeys.length) {
143
+ throw new SecretLoadError2("MISSING_SECRETS", `Missing secrets: ${missingKeys.join(", ")}`);
144
+ }
145
+ let nextParsed;
146
+ try {
147
+ nextParsed = options.schema ? await options.schema.parseAsync({ ...next }) : { ...next };
148
+ nextParsed = structuredClone(nextParsed);
149
+ } catch {
150
+ throw new SecretLoadError2("VALIDATION", "Secret schema validation failed. Check the configured schema; values are omitted.");
151
+ }
152
+ signal.throwIfAborted();
153
+ const nextSources = Object.fromEntries(keySet.map((key) => [
154
+ key,
155
+ Object.hasOwn(overrides, key) ? "process.env" : loaderSource
156
+ ]));
157
+ if (options.writeToProcessEnv !== false && environmentVariables) {
158
+ let writes = environmentWrites.get(environmentVariables);
159
+ if (!writes) {
160
+ writes = new Map;
161
+ environmentWrites.set(environmentVariables, writes);
162
+ }
163
+ for (const key of keySet) {
164
+ if (Object.hasOwn(overrides, key)) {
165
+ continue;
166
+ }
167
+ environmentVariables[key] = next[key];
168
+ writes.set(key, next[key]);
169
+ }
170
+ }
171
+ cached = next;
172
+ parsed = nextParsed;
173
+ sources = nextSources;
174
+ };
175
+ const load = (request = {}) => {
176
+ if (inFlight) {
177
+ return inFlight;
178
+ }
179
+ inFlight = (async () => {
180
+ const start = Date.now();
181
+ try {
182
+ await withSecretDeadline({
183
+ timeoutMs: request.timeoutMs ?? options.timeoutMs,
184
+ signal: request.signal ?? options.signal
185
+ }, (signal) => loadSnapshot(request, signal));
186
+ const sourceCounts = new Map;
187
+ for (const key of keySet) {
188
+ const source = sources[key];
189
+ sourceCounts.set(source, (sourceCounts.get(source) ?? 0) + 1);
190
+ }
191
+ const counts = Object.fromEntries(sourceCounts);
192
+ log("info", "Loaded secrets", {
193
+ environment,
194
+ count: keySet.length,
195
+ durationMs: Date.now() - start,
196
+ source: Object.keys(counts).join(" + ") || "none",
197
+ sources: counts
198
+ });
199
+ } catch (error) {
200
+ const safe = error instanceof SecretLoadError2 ? error : new SecretLoadError2("LOADER", "Secret loading failed.");
201
+ log("error", "Secret loading failed", {
202
+ environment,
203
+ count: keySet.length,
204
+ durationMs: Date.now() - start,
205
+ code: safe.code
206
+ });
207
+ throw safe;
208
+ }
209
+ })().finally(() => {
210
+ inFlight = undefined;
211
+ });
212
+ return inFlight;
213
+ };
214
+ const secretSet = {
215
+ secret(key) {
216
+ if (!Object.hasOwn(cached, key)) {
217
+ throw new SecretLoadError2("MISSING_SECRETS", `Secret ${key} not found`);
218
+ }
219
+ return cached[key];
220
+ },
221
+ secrets: () => ({ ...cached }),
222
+ parsed: () => structuredClone(parsed),
223
+ sources: () => ({ ...sources }),
224
+ async reload(request) {
225
+ await load(request);
226
+ return secretSet;
227
+ }
228
+ };
229
+ await load();
230
+ return secretSet;
231
+ }
232
+
233
+ // src/secrets/worker-loader.ts
234
+ function cloudflareWorkerEnvLoader2(workerEnv) {
235
+ const loader = async (ctx) => {
236
+ const out = Object.create(null);
237
+ for (const key of ctx.keys) {
238
+ const str = workerBindingToString(Object.hasOwn(workerEnv, key) ? workerEnv[key] : undefined);
239
+ if (str !== undefined) {
240
+ out[key] = str;
241
+ }
242
+ }
243
+ return out;
244
+ };
245
+ return Object.assign(loader, { source: "Cloudflare Worker bindings" });
246
+ }
247
+ function workerBindingToString(value) {
248
+ if (value === undefined || value === null) {
249
+ return;
250
+ }
251
+ if (typeof value === "string") {
252
+ return value === "" ? undefined : value;
253
+ }
254
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
255
+ return String(value);
256
+ }
257
+ if (typeof value === "object") {
258
+ return JSON.stringify(value);
259
+ }
260
+ return String(value);
261
+ }
262
+
263
+ export { withSecretDeadline, getProcessEnvironment, resolveSecretEnvironment, defineSecretSet2, cloudflareWorkerEnvLoader2 };
package/dist/index.d.ts CHANGED
@@ -5,12 +5,12 @@ export * from './promise';
5
5
  export * from './cache';
6
6
  export * from './state';
7
7
  export * from './math';
8
+ export * from './color';
8
9
  export * from './date';
9
10
  export * from './url';
10
- export * from './zod';
11
- export * from './api-wrapper';
12
- export * from './p-queue';
13
- export * from './cool-console-log';
14
- export * from './jwt';
15
- export * from './sitemap';
16
- export * from './secrets';
11
+ export * from './error';
12
+ export * from './guard';
13
+ export * from './object';
14
+ export * from './types';
15
+ export * from './function';
16
+ export * from './emitter';
@@ -1,3 +1,51 @@
1
+ import type { Result } from '../promise';
1
2
  export type JWTPayload = Record<string, unknown>;
2
- export declare function jwtSign<Payload extends JWTPayload>(payload: Payload, secret: string): Promise<string>;
3
- export declare function jwtVerify<Payload extends JWTPayload>(token: string, secret: string): Promise<Payload | null>;
3
+ export type JwtAlgorithm = 'HS256' | 'HS384' | 'HS512';
4
+ export type JwtHeader = {
5
+ alg: string;
6
+ typ?: string;
7
+ } & Record<string, unknown>;
8
+ export type JwtSignOptions = {
9
+ /** Seconds from now until `exp`. */
10
+ expiresIn?: number;
11
+ /** Seconds from now until `nbf`. */
12
+ notBefore?: number;
13
+ issuer?: string;
14
+ audience?: string | string[];
15
+ /** Default: `'HS256'` */
16
+ algorithm?: JwtAlgorithm;
17
+ };
18
+ export type JwtVerifyOptions = {
19
+ /** Clock skew tolerance for `exp`/`nbf`, in seconds. Default: 0 */
20
+ leewaySec?: number;
21
+ /** Required `iss` value. */
22
+ issuer?: string;
23
+ /** Required `aud` value (matches a string `aud` or an entry of an array `aud`). */
24
+ audience?: string;
25
+ /** Accepted header `alg` values. Default: all of HS256/HS384/HS512. */
26
+ algorithms?: JwtAlgorithm[];
27
+ };
28
+ export type JwtVerifyErrorCode = 'malformed' | 'unsupported_alg' | 'invalid_signature' | 'expired' | 'not_yet_valid' | 'invalid_issuer' | 'invalid_audience';
29
+ export declare class JwtVerifyError extends Error {
30
+ readonly code: JwtVerifyErrorCode;
31
+ constructor(code: JwtVerifyErrorCode);
32
+ }
33
+ /**
34
+ * Signs `payload` as a compact JWS. `iat` is always set to now.
35
+ * Claims derived from `options` (`exp`, `nbf`, `iss`, `aud`) override the same
36
+ * claims in `payload`; payload claims are kept when the option is not given.
37
+ */
38
+ export declare function jwtSign<Payload extends JWTPayload>(payload: Payload, secret: string, options?: JwtSignOptions): Promise<string>;
39
+ /** Verifies a token. Returns `null` for any failure; use `jwtVerifyResult` for the reason. */
40
+ export declare function jwtVerify<Payload extends JWTPayload>(token: string, secret: string, options?: JwtVerifyOptions): Promise<Payload | null>;
41
+ /**
42
+ * Verifies signature, `exp`/`nbf` and optional `iss`/`aud`.
43
+ * Token problems are returned as a failed `Result`; configuration problems
44
+ * (no Web Crypto, empty secret) throw.
45
+ */
46
+ export declare function jwtVerifyResult<Payload extends JWTPayload>(token: string, secret: string, options?: JwtVerifyOptions): Promise<Result<Payload, JwtVerifyError>>;
47
+ /** Decodes header and payload WITHOUT verifying the signature. Never trust the result for auth. */
48
+ export declare function jwtDecode<Payload extends JWTPayload = JWTPayload>(token: string): {
49
+ header: JwtHeader;
50
+ payload: Payload;
51
+ } | null;
@@ -1,2 +1,2 @@
1
- export { linearRegressionTrend } from './linearRegressionTrend';
2
- export { createResolveColorsByKeys, getHeatmapCellStyle, hashStringToColorIndex, METRICS_SERIES_PALETTE_SIZE, type ResolveColorsByKeysResult, resolveSeriesColorMap, seriesColorKey, withAlpha } from './metricsSeriesColors';
1
+ export { type LinearRegression, linearRegression, linearRegressionTrend } from './linearRegressionTrend';
2
+ export { clamp, lerp, normalize } from './scale';
@@ -1,5 +1,18 @@
1
+ export type LinearRegression = {
2
+ slope: number;
3
+ intercept: number;
4
+ /** Coefficient of determination (0..1). 1 when all values are equal (exact fit). */
5
+ r2: number;
6
+ /** Fitted y for each index. */
7
+ fitted: number[];
8
+ };
1
9
  /**
2
10
  * Least-squares linear regression for y at x = 0, 1, …, n − 1.
3
- * Returns fitted y for each index, or [] if n < 2 or any input is non-finite.
11
+ * Returns `null` if n < 2 or any input is non-finite.
12
+ */
13
+ export declare function linearRegression(values: number[]): LinearRegression | null;
14
+ /**
15
+ * Fitted y for each index of `values` (see `linearRegression`), or [] if n < 2
16
+ * or any input is non-finite.
4
17
  */
5
18
  export declare function linearRegressionTrend(values: number[]): number[];
@@ -0,0 +1,8 @@
1
+ export declare function clamp(value: number, min: number, max: number): number;
2
+ /** Linear interpolation: `t = 0` → `start`, `t = 1` → `end`. */
3
+ export declare function lerp(start: number, end: number, t: number): number;
4
+ /**
5
+ * Position of `value` within `[min, max]` as 0..1 (clamped). Returns 0 when
6
+ * the range is empty.
7
+ */
8
+ export declare function normalize(value: number, min: number, max: number): number;
@@ -0,0 +1,19 @@
1
+ /** `Object.keys` typed as the object's string keys. */
2
+ export declare function typedKeys<T extends object>(object: T): (keyof T & string)[];
3
+ /** `Object.entries` typed as `[key, value]` pairs per key. */
4
+ export declare function typedEntries<T extends object>(object: T): {
5
+ [K in keyof T & string]: [K, T[K]];
6
+ }[keyof T & string][];
7
+ /** `Object.fromEntries` typed as `Record<K, V>`. */
8
+ export declare function typedFromEntries<const K extends PropertyKey, V>(entries: Iterable<readonly [K, V]>): Record<K, V>;
9
+ /** Builds a record with one entry per key. */
10
+ export declare function recordFromKeys<const K extends PropertyKey, V>(keys: readonly K[], fn: (key: K) => V): Record<K, V>;
11
+ export declare function mapValues<T extends object, R>(object: T, fn: (value: T[keyof T & string], key: keyof T & string) => R): Record<keyof T & string, R>;
12
+ export declare function pick<T extends object, const K extends keyof T>(object: T, keys: readonly K[]): Pick<T, K>;
13
+ export declare function omit<T extends object, const K extends keyof T>(object: T, keys: readonly K[]): Omit<T, K>;
14
+ type Path = string | readonly (string | number)[];
15
+ /** Reads a nested value by dot path (`a.b.0.c`) or segment array; undefined when missing. */
16
+ export declare function getAtPath(object: unknown, path: Path): unknown;
17
+ /** Writes a nested value in place, creating arrays for numeric segments and objects otherwise. */
18
+ export declare function setAtPath(object: object, path: Path, value: unknown): void;
19
+ export {};
@@ -92,4 +92,4 @@ export default class PQueue<QueueType extends Queue<RunFunction, EnqueueOptionsT
92
92
  onError(): Promise<never>;
93
93
  onSizeLessThan(limit: number): Promise<void>;
94
94
  }
95
- export {};
95
+ export { PQueue };
@@ -4,18 +4,113 @@ export type BackgroundPromiseOptions = {
4
4
  scheduler?: (callback: () => void) => void;
5
5
  };
6
6
  export type Success<T> = {
7
+ ok: true;
7
8
  data: T;
8
9
  error: null;
9
10
  };
10
11
  export type Failure<E> = {
12
+ ok: false;
11
13
  data: null;
12
14
  error: E;
13
15
  };
14
16
  export type Result<T, E = Error> = Success<T> | Failure<E>;
17
+ export declare function ok<T>(data: T): Success<T>;
18
+ export declare function err<E>(error: E): Failure<E>;
15
19
  export declare function isSuccess<T, E>(result: Result<T, E>): result is Success<T>;
16
20
  export declare function isFailure<T, E>(result: Result<T, E>): result is Failure<E>;
17
21
  export declare function unwrapResult<T, E>(result: Result<T, E>): T;
18
22
  export declare function backgroundPromise<T>(promiseFactory: () => Promise<T>, options?: BackgroundPromiseOptions): void;
19
23
  export declare function backgroundPromiseSync<T>(promiseFactory: () => Promise<T>, options?: BackgroundPromiseOptions): void;
20
- export declare function promiseTimeout(ms: number): Promise<void>;
21
- export declare function tryCatch<T, E = Error>(promise: Promise<T>): Promise<Result<T, E>>;
24
+ /** Resolves after `ms`; rejects with `signal.reason` if the signal aborts first. */
25
+ export declare function promiseTimeout(ms: number, options?: {
26
+ signal?: AbortSignal;
27
+ }): Promise<void>;
28
+ /**
29
+ * Accepts a promise or a function; sync throws inside the function are
30
+ * captured too.
31
+ */
32
+ export declare function tryCatch<T, E = Error>(input: Promise<T> | (() => T | Promise<T>)): Promise<Result<T, E>>;
33
+ export declare class PromiseTimeoutError extends Error {
34
+ readonly timeoutMs: number;
35
+ constructor(timeoutMs: number, message?: string);
36
+ }
37
+ /**
38
+ * Rejects with `PromiseTimeoutError` after `ms`, or with `signal.reason` when
39
+ * the signal aborts. Pass a function to receive a signal that aborts on
40
+ * timeout, so the underlying work can stop too.
41
+ */
42
+ export declare function withTimeout<T>(input: Promise<T> | ((signal: AbortSignal) => Promise<T>), ms: number, options?: {
43
+ signal?: AbortSignal;
44
+ message?: string;
45
+ }): Promise<T>;
46
+ export type BackoffOptions = {
47
+ /** Delay for the first retry (default 300ms). */
48
+ baseMs?: number;
49
+ /** Upper bound for any delay (default 30s). */
50
+ maxMs?: number;
51
+ /**
52
+ * `true`/`'full'`: random delay in [0, computed] (default).
53
+ * `'partial'`: computed plus 0–`jitterRatio` extra (may exceed `maxMs`).
54
+ */
55
+ jitter?: boolean | 'full' | 'partial';
56
+ /** Max extra fraction for `'partial'` jitter (default 0.25). */
57
+ jitterRatio?: number;
58
+ /** Source of randomness in [0, 1) (default `Math.random`). */
59
+ random?: () => number;
60
+ };
61
+ /** Exponential backoff delay for a 1-based retry attempt. */
62
+ export declare function exponentialBackoff(attempt: number, options?: BackoffOptions): number;
63
+ export type RetryOptions = {
64
+ /** Retries after the first attempt (default 3). */
65
+ retries?: number;
66
+ /** Delay before retry `attempt` (1-based). Default: `exponentialBackoff`. */
67
+ delayMs?: number | ((attempt: number, error: unknown) => number);
68
+ /** Return false to stop retrying and rethrow (default: always retry). */
69
+ shouldRetry?: (error: unknown, attempt: number) => boolean;
70
+ onRetry?: (error: unknown, attempt: number, delayMs: number) => void;
71
+ signal?: AbortSignal;
72
+ };
73
+ export declare function retry<T>(fn: (context: {
74
+ attempt: number;
75
+ signal?: AbortSignal;
76
+ }) => Promise<T> | T, options?: RetryOptions): Promise<T>;
77
+ /** Maps with bounded concurrency; results keep input order. */
78
+ export declare function pMap<T, R>(items: Iterable<T>, fn: (item: T, index: number) => Promise<R> | R, options?: {
79
+ concurrency?: number;
80
+ signal?: AbortSignal;
81
+ }): Promise<R[]>;
82
+ /** Starts a clock; call the returned function to read elapsed ms. */
83
+ export declare function startTimer(): () => number;
84
+ /** Runs `fn` and reports how long it took (throws pass through). */
85
+ export declare function timed<T>(fn: () => Promise<T> | T): Promise<{
86
+ result: T;
87
+ durationMs: number;
88
+ }>;
89
+ /**
90
+ * Aborts when any input aborts, with that signal's `reason`. Undefined inputs
91
+ * are skipped; no inputs gives a signal that never aborts.
92
+ */
93
+ export declare function anySignal(...signals: (AbortSignal | undefined)[]): AbortSignal;
94
+ /** Aborts with a `TimeoutError` after `ms`, or earlier if `signal` aborts. */
95
+ export declare function timeoutSignal(ms: number, signal?: AbortSignal): AbortSignal;
96
+ /**
97
+ * Settles like `promise`, but rejects with `signal.reason` as soon as the
98
+ * signal aborts. The underlying work is not cancelled.
99
+ */
100
+ export declare function raceAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T>;
101
+ export type LatestGuard = {
102
+ /** Starts a new generation; earlier tokens become stale. */
103
+ next(): number;
104
+ isCurrent(token: number): boolean;
105
+ /** Marks every outstanding token stale. */
106
+ invalidate(): void;
107
+ /** Runs `fn` under a new token; stale results and errors become `{ stale: true }`. */
108
+ run<T>(fn: () => Promise<T> | T): Promise<{
109
+ stale: true;
110
+ } | {
111
+ stale: false;
112
+ value: T;
113
+ }>;
114
+ };
115
+ /** Ignores results of async work superseded by a newer call. */
116
+ export declare function createLatestGuard(): LatestGuard;
@@ -1,14 +1,56 @@
1
- interface SitemapOptions {
2
- filterIndexes?: string;
1
+ /** Substring, RegExp, or predicate. */
2
+ export type SitemapUrlFilter = string | RegExp | ((url: string) => boolean);
3
+ export interface SitemapOptions {
4
+ /** Only follow nested sitemaps (from a `<sitemapindex>`) that match. */
5
+ filterIndexes?: SitemapUrlFilter;
6
+ /** Only keep page URLs (from a `<urlset>`) that match. */
7
+ filterUrls?: SitemapUrlFilter;
3
8
  concurrency?: number;
4
9
  maxDepth?: number;
10
+ /** Stop once this many URLs have been collected. */
11
+ maxUrls?: number;
12
+ /** Per-request timeout in ms. */
5
13
  timeout?: number;
14
+ userAgent?: string;
15
+ fetch?: typeof fetch;
16
+ /** Called for failed robots.txt/sitemap requests. Defaults to `console.warn`. */
17
+ onError?: (url: string, error: unknown) => void;
18
+ /** Drop page URLs that robots.txt disallows for `userAgent`. Defaults to `false`. */
19
+ respectRobots?: boolean;
6
20
  }
21
+ /** The robots.txt rules that apply to one user agent. */
22
+ export interface RobotsRules {
23
+ allow: string[];
24
+ disallow: string[];
25
+ /** Every `Sitemap:` line, whatever group it is in. */
26
+ sitemaps: string[];
27
+ crawlDelaySec?: number;
28
+ }
29
+ export interface SitemapEntry {
30
+ loc: string;
31
+ lastmod?: string;
32
+ }
33
+ /**
34
+ * Parses robots.txt for one user agent (RFC 9309): the groups naming its
35
+ * product token (case-insensitive, versions ignored), else the `*` groups.
36
+ * Several matching groups are merged. Empty `Allow`/`Disallow` values are dropped.
37
+ */
38
+ export declare function parseRobotsTxt(body: string, userAgent?: string): RobotsRules;
39
+ /**
40
+ * Whether `url` (absolute, or a path) may be crawled under `rules` (RFC 9309):
41
+ * the longest matching pattern wins and `Allow` wins a tie. Patterns support
42
+ * `*` and a trailing `$`, and match the path plus query string.
43
+ */
44
+ export declare function isAllowedByRobots(rules: Pick<RobotsRules, 'allow' | 'disallow'>, url: string): boolean;
45
+ /**
46
+ * Gets all entries (`loc` + optional `lastmod`) from a domain by discovering and crawling its sitemaps
47
+ * @param domain - The root domain (e.g., 'example.com' or 'https://example.com')
48
+ * @throws When no sitemap can be discovered
49
+ */
50
+ export declare const getDomainSitemapEntries: (domain: string, options?: SitemapOptions) => Promise<SitemapEntry[]>;
7
51
  /**
8
52
  * Gets all URLs from a domain by discovering and crawling its sitemaps
9
- * @param domain - The root domain (e.g., 'https://example.com')
10
- * @param options - Configuration options for the crawler
11
- * @returns Promise<string[]> - Array of unique URLs found in the sitemap
53
+ * @param domain - The root domain (e.g., 'example.com' or 'https://example.com')
54
+ * @returns Array of unique URLs found in the sitemap
12
55
  */
13
56
  export declare const getDomainSitemap: (domain: string, options?: SitemapOptions) => Promise<string[]>;
14
- export {};