hanzio 1.2.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +786 -239
- package/dist/api-wrapper/builders.d.ts +4 -3
- package/dist/api-wrapper/create-client.d.ts +1 -1
- package/dist/api-wrapper/describe.d.ts +16 -0
- package/dist/api-wrapper/endpoint.d.ts +18 -0
- package/dist/api-wrapper/errors.d.ts +39 -2
- package/dist/api-wrapper/index.d.ts +5 -0
- package/dist/api-wrapper/otel.d.ts +44 -0
- package/dist/api-wrapper/paginate.d.ts +12 -0
- package/dist/api-wrapper/redaction.d.ts +2 -0
- package/dist/api-wrapper/shared.d.ts +16 -2
- package/dist/api-wrapper/testing.d.ts +23 -0
- package/dist/api-wrapper/transport.d.ts +4 -5
- package/dist/api-wrapper/types.d.ts +103 -18
- package/dist/array/index.d.ts +50 -4
- package/dist/cache/createCache.d.ts +31 -0
- package/dist/cache/index.d.ts +19 -2
- package/dist/color/contrast.d.ts +40 -0
- package/dist/color/index.d.ts +2 -0
- package/dist/{math → color}/metricsSeriesColors.d.ts +14 -7
- package/dist/cool-console-log/index.d.ts +34 -5
- package/dist/crypto/encoding.d.ts +18 -0
- package/dist/crypto/hash.d.ts +33 -0
- package/dist/crypto/index.d.ts +4 -0
- package/dist/crypto/sealer.d.ts +25 -0
- package/dist/crypto/webhook.d.ts +20 -0
- package/dist/date/index.d.ts +46 -2
- package/dist/emitter/index.d.ts +21 -0
- package/dist/error/index.d.ts +12 -0
- package/dist/format/index.d.ts +102 -0
- package/dist/function/index.d.ts +60 -0
- package/dist/guard/index.d.ts +20 -0
- package/dist/i18n/index.d.ts +37 -0
- package/dist/index-0zvtcbzy.js +224 -0
- package/dist/index-8r2vdzjg.js +87 -0
- package/dist/index-hy7kbb99.js +157 -0
- package/dist/index-hyt6c56h.js +290 -0
- package/dist/{src/secrets/infisical.js → index-j1ybktbr.js} +500 -538
- package/dist/index-k9yx6bne.js +89 -0
- package/dist/index-kj1phgrx.js +169 -0
- package/dist/index-ktynw3vs.js +37 -0
- package/dist/index-mnkcfyx8.js +78 -0
- package/dist/{p-queue/index.js → index-pwt0dz63.js} +7 -11
- package/dist/index-rp5pqghz.js +157 -0
- package/dist/index-w882zry3.js +29 -0
- package/dist/index.d.ts +7 -7
- package/dist/jwt/index.d.ts +50 -2
- package/dist/math/index.d.ts +2 -2
- package/dist/math/linearRegressionTrend.d.ts +14 -1
- package/dist/math/scale.d.ts +8 -0
- package/dist/object/index.d.ts +19 -0
- package/dist/p-queue/index.d.ts +1 -1
- package/dist/promise/index.d.ts +97 -2
- package/dist/secrets/core.d.ts +7 -0
- package/dist/secrets/index.d.ts +3 -1
- package/dist/secrets/public-config.d.ts +7 -0
- package/dist/secrets/vite.d.ts +1 -0
- package/dist/secrets/worker.d.ts +1 -0
- package/dist/sitemap/index.d.ts +48 -6
- package/dist/src/api-wrapper/index.js +60 -1130
- package/dist/src/api-wrapper/testing.js +20 -0
- package/dist/{cool-console-log → src/cool-console-log}/index.js +80 -33
- package/dist/src/crypto/index.js +195 -0
- package/dist/src/format/index.js +211 -0
- package/dist/src/i18n/index.js +36 -0
- package/dist/src/index.js +739 -3145
- package/dist/src/jwt/index.js +138 -92
- package/dist/src/p-queue/index.js +9 -437
- package/dist/src/secrets/index.js +50 -1428
- package/dist/src/secrets/vite.js +10 -39
- package/dist/src/secrets/worker.js +14 -273
- package/dist/src/sitemap/index.js +183 -511
- package/dist/src/storage/index.js +119 -0
- package/dist/src/zod/index.js +340 -167
- package/dist/state/index.d.ts +21 -0
- package/dist/storage/index.d.ts +58 -0
- package/dist/string/index.d.ts +75 -2
- package/dist/typedswitch/index.d.ts +31 -24
- package/dist/types/index.d.ts +11 -0
- package/dist/url/index.d.ts +50 -1
- package/dist/zod/index.d.ts +32 -3
- package/dist/zod/introspect.d.ts +19 -0
- package/dist/zod/jsonToZodSchemaMapper.d.ts +3 -1
- package/dist/zod/zodMinMaxFilter.d.ts +22 -3
- package/dist/zod/zodToTypeString.d.ts +8 -3
- package/package.json +78 -7
- package/src/api-wrapper/actions-impl.ts +11 -72
- package/src/api-wrapper/builders.ts +32 -25
- package/src/api-wrapper/create-client.ts +171 -41
- package/src/api-wrapper/describe.ts +157 -0
- package/src/api-wrapper/endpoint.ts +26 -0
- package/src/api-wrapper/error-reporting.ts +7 -1
- package/src/api-wrapper/errors.ts +107 -2
- package/src/api-wrapper/headers.ts +11 -6
- package/src/api-wrapper/index.ts +5 -0
- package/src/api-wrapper/otel.ts +139 -0
- package/src/api-wrapper/paginate.ts +28 -0
- package/src/api-wrapper/redaction.ts +42 -0
- package/src/api-wrapper/shared.ts +21 -2
- package/src/api-wrapper/testing.ts +48 -0
- package/src/api-wrapper/transport.ts +41 -43
- package/src/api-wrapper/types.ts +185 -18
- package/src/array/index.ts +195 -42
- package/src/cache/createCache.ts +157 -0
- package/src/cache/index.ts +132 -147
- package/src/color/contrast.ts +163 -0
- package/src/color/index.ts +22 -0
- package/src/{math → color}/metricsSeriesColors.ts +47 -49
- package/src/cool-console-log/index.ts +128 -41
- package/src/crypto/encoding.ts +83 -0
- package/src/crypto/hash.ts +170 -0
- package/src/crypto/index.ts +26 -0
- package/src/crypto/sealer.ts +168 -0
- package/src/crypto/webhook.ts +86 -0
- package/src/date/index.ts +109 -34
- package/src/emitter/index.ts +82 -0
- package/src/error/index.ts +63 -0
- package/src/format/index.ts +389 -0
- package/src/function/index.ts +201 -0
- package/src/guard/index.ts +80 -0
- package/src/i18n/index.ts +125 -0
- package/src/index.ts +11 -7
- package/src/jwt/index.ts +199 -125
- package/src/math/index.ts +5 -10
- package/src/math/linearRegressionTrend.ts +33 -8
- package/src/math/scale.ts +17 -0
- package/src/object/index.ts +99 -0
- package/src/p-queue/index.ts +2 -0
- package/src/promise/index.ts +304 -9
- package/src/secrets/core.ts +72 -16
- package/src/secrets/index.ts +16 -4
- package/src/secrets/public-config.ts +47 -0
- package/src/secrets/vite.ts +2 -0
- package/src/secrets/worker.ts +2 -0
- package/src/sitemap/index.ts +293 -130
- package/src/state/index.ts +133 -81
- package/src/storage/index.ts +187 -0
- package/src/string/index.ts +261 -23
- package/src/typedswitch/index.ts +58 -38
- package/src/types/index.ts +14 -0
- package/src/url/index.ts +146 -1
- package/src/zod/index.ts +105 -9
- package/src/zod/introspect.ts +103 -0
- package/src/zod/jsonToZodSchemaMapper.ts +44 -21
- package/src/zod/zodMinMaxFilter.ts +37 -13
- package/src/zod/zodToTypeString.ts +184 -181
- package/dist/api-wrapper/index.js +0 -250
- package/dist/gzip/index.d.ts +0 -2
- package/dist/gzip/index.js +0 -16
- package/dist/index.js +0 -1823
- package/dist/jwt/index.js +0 -19
- package/dist/sitemap/index.js +0 -552
- package/dist/src/gzip/index.js +0 -16
- package/dist/src/secrets/loaders.js +0 -1529
- package/dist/zod/index.js +0 -210
|
@@ -1,552 +1,224 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
#queue = [];
|
|
11
|
-
enqueue(run, options = {}) {
|
|
12
|
-
const element = {
|
|
13
|
-
priority: options.priority ?? 0,
|
|
14
|
-
id: options.id,
|
|
15
|
-
run
|
|
16
|
-
};
|
|
17
|
-
const index = this.#queue.findIndex((item) => item.priority < element.priority);
|
|
18
|
-
if (index === -1) {
|
|
19
|
-
this.#queue.push(element);
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
this.#queue.splice(index, 0, element);
|
|
23
|
-
}
|
|
24
|
-
dequeue() {
|
|
25
|
-
return this.#queue.shift()?.run;
|
|
26
|
-
}
|
|
27
|
-
filter(options) {
|
|
28
|
-
return this.#queue.filter((item) => Object.entries(options).every(([key, value]) => item[key] === value)).map((item) => item.run);
|
|
29
|
-
}
|
|
30
|
-
setPriority(id, priority) {
|
|
31
|
-
const index = this.#queue.findIndex((item) => item.id === id);
|
|
32
|
-
if (index === -1) {
|
|
33
|
-
throw new ReferenceError(`No promise function with the id "${id}" exists in the queue.`);
|
|
34
|
-
}
|
|
35
|
-
const [item] = this.#queue.splice(index, 1);
|
|
36
|
-
this.enqueue(item.run, { ...item, priority });
|
|
37
|
-
}
|
|
38
|
-
remove(id) {
|
|
39
|
-
const index = this.#queue.findIndex((item) => item.id === id);
|
|
40
|
-
if (index !== -1) {
|
|
41
|
-
this.#queue.splice(index, 1);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
get size() {
|
|
45
|
-
return this.#queue.length;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
PQueue2
|
|
3
|
+
} from "../../index-pwt0dz63.js";
|
|
4
|
+
import {
|
|
5
|
+
escapeRegExp2
|
|
6
|
+
} from "../../index-kj1phgrx.js";
|
|
7
|
+
import {
|
|
8
|
+
normalizeDomain2
|
|
9
|
+
} from "../../index-mnkcfyx8.js";
|
|
48
10
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
timeout;
|
|
68
|
-
constructor(options = {}) {
|
|
69
|
-
const {
|
|
70
|
-
concurrency = Number.POSITIVE_INFINITY,
|
|
71
|
-
autoStart = true,
|
|
72
|
-
queueClass = PriorityQueue,
|
|
73
|
-
intervalCap = Number.POSITIVE_INFINITY,
|
|
74
|
-
interval = 0,
|
|
75
|
-
carryoverIntervalCount,
|
|
76
|
-
carryoverConcurrencyCount,
|
|
77
|
-
strict = false,
|
|
78
|
-
timeout
|
|
79
|
-
} = options;
|
|
80
|
-
if (!(typeof concurrency === "number" && concurrency >= 1)) {
|
|
81
|
-
throw new TypeError("Expected `concurrency` to be a number from 1 and up");
|
|
82
|
-
}
|
|
83
|
-
if (!(typeof intervalCap === "number" && intervalCap >= 1)) {
|
|
84
|
-
throw new TypeError("Expected `intervalCap` to be a number from 1 and up");
|
|
85
|
-
}
|
|
86
|
-
if (!(typeof interval === "number" && Number.isFinite(interval) && interval >= 0)) {
|
|
87
|
-
throw new TypeError("Expected `interval` to be a finite number >= 0");
|
|
88
|
-
}
|
|
89
|
-
if (strict && interval === 0) {
|
|
90
|
-
throw new TypeError("The `strict` option requires a non-zero `interval`");
|
|
91
|
-
}
|
|
92
|
-
if (strict && intervalCap === Number.POSITIVE_INFINITY) {
|
|
93
|
-
throw new TypeError("The `strict` option requires a finite `intervalCap`");
|
|
94
|
-
}
|
|
95
|
-
if (timeout !== undefined && !(Number.isFinite(timeout) && timeout > 0)) {
|
|
96
|
-
throw new TypeError("Expected `timeout` to be a positive finite number");
|
|
97
|
-
}
|
|
98
|
-
this.#queueClass = queueClass;
|
|
99
|
-
this.#queue = new queueClass;
|
|
100
|
-
this.#concurrency = concurrency;
|
|
101
|
-
this.#isPaused = !autoStart;
|
|
102
|
-
this.#intervalCap = intervalCap;
|
|
103
|
-
this.#interval = interval;
|
|
104
|
-
this.#carryoverIntervalCount = carryoverIntervalCount ?? carryoverConcurrencyCount ?? false;
|
|
105
|
-
this.#strict = strict;
|
|
106
|
-
this.timeout = timeout;
|
|
107
|
-
}
|
|
108
|
-
on(event, listener) {
|
|
109
|
-
const listeners = this.#listeners.get(event) ?? new Set;
|
|
110
|
-
listeners.add(listener);
|
|
111
|
-
this.#listeners.set(event, listeners);
|
|
112
|
-
return this;
|
|
113
|
-
}
|
|
114
|
-
off(event, listener) {
|
|
115
|
-
this.#listeners.get(event)?.delete(listener);
|
|
116
|
-
return this;
|
|
117
|
-
}
|
|
118
|
-
emit(event, ...arguments_) {
|
|
119
|
-
const listeners = this.#listeners.get(event);
|
|
120
|
-
if (!listeners || listeners.size === 0) {
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
for (const listener of [...listeners]) {
|
|
124
|
-
listener(...arguments_);
|
|
125
|
-
}
|
|
11
|
+
// src/sitemap/index.ts
|
|
12
|
+
var COMMON_LOCATIONS = [
|
|
13
|
+
"/sitemap.xml",
|
|
14
|
+
"/sitemap_index.xml",
|
|
15
|
+
"/sitemap/",
|
|
16
|
+
"/sitemaps.xml",
|
|
17
|
+
"/sitemap/sitemap.xml"
|
|
18
|
+
];
|
|
19
|
+
var XML_ENTITIES = {
|
|
20
|
+
amp: "&",
|
|
21
|
+
lt: "<",
|
|
22
|
+
gt: ">",
|
|
23
|
+
quot: '"',
|
|
24
|
+
apos: "'"
|
|
25
|
+
};
|
|
26
|
+
var decodeXml = (value) => value.replace(/&(#[xX][\da-fA-F]+|#\d+|amp|lt|gt|quot|apos);/g, (_, entity) => entity[0] !== "#" ? XML_ENTITIES[entity] : String.fromCodePoint(entity[1] === "x" || entity[1] === "X" ? Number.parseInt(entity.slice(2), 16) : Number(entity.slice(1))));
|
|
27
|
+
var matchesFilter = (filter, url) => {
|
|
28
|
+
if (filter === undefined)
|
|
126
29
|
return true;
|
|
30
|
+
if (typeof filter === "string")
|
|
31
|
+
return url.includes(filter);
|
|
32
|
+
if (typeof filter === "function")
|
|
33
|
+
return filter(url);
|
|
34
|
+
return url.search(filter) !== -1;
|
|
35
|
+
};
|
|
36
|
+
var defaultOnError = (url, error) => console.warn(`Failed to fetch ${url}:`, error instanceof Error ? error.message : "Unknown error");
|
|
37
|
+
var readBody = async (response) => {
|
|
38
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
39
|
+
if (bytes[0] !== 31 || bytes[1] !== 139) {
|
|
40
|
+
return new TextDecoder().decode(bytes);
|
|
127
41
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
sizeBy(options) {
|
|
142
|
-
return this.#queue.filter(options).length;
|
|
143
|
-
}
|
|
144
|
-
get pending() {
|
|
145
|
-
return this.#pending;
|
|
146
|
-
}
|
|
147
|
-
get isPaused() {
|
|
148
|
-
return this.#isPaused;
|
|
149
|
-
}
|
|
150
|
-
get isRateLimited() {
|
|
151
|
-
this.#updateRateLimitState();
|
|
152
|
-
return this.#rateLimited;
|
|
153
|
-
}
|
|
154
|
-
get isSaturated() {
|
|
155
|
-
return this.#pending >= this.#concurrency && this.#queue.size > 0 || this.isRateLimited && this.#queue.size > 0;
|
|
156
|
-
}
|
|
157
|
-
get runningTasks() {
|
|
158
|
-
return [...this.#runningTasks.values()].map((task) => ({ ...task }));
|
|
159
|
-
}
|
|
160
|
-
setPriority(id, priority) {
|
|
161
|
-
if (typeof priority !== "number" || !Number.isFinite(priority)) {
|
|
162
|
-
throw new TypeError("Expected `priority` to be a finite number");
|
|
163
|
-
}
|
|
164
|
-
this.#queue.setPriority(id, priority);
|
|
165
|
-
}
|
|
166
|
-
async add(function_, options = {}) {
|
|
167
|
-
const taskOptions = {
|
|
168
|
-
timeout: this.timeout,
|
|
169
|
-
...options,
|
|
170
|
-
id: options.id ?? (this.#idCounter++).toString()
|
|
171
|
-
};
|
|
172
|
-
return new Promise((resolve, reject) => {
|
|
173
|
-
const taskSymbol = Symbol(taskOptions.id);
|
|
174
|
-
let didRun = false;
|
|
175
|
-
let queuedAbortHandler;
|
|
176
|
-
let runningAbortHandler;
|
|
177
|
-
const cleanupQueuedAbort = () => {
|
|
178
|
-
if (queuedAbortHandler) {
|
|
179
|
-
taskOptions.signal?.removeEventListener("abort", queuedAbortHandler);
|
|
180
|
-
queuedAbortHandler = undefined;
|
|
181
|
-
}
|
|
182
|
-
};
|
|
183
|
-
const run = async () => {
|
|
184
|
-
didRun = true;
|
|
185
|
-
cleanupQueuedAbort();
|
|
186
|
-
this.#pending++;
|
|
187
|
-
this.#runningTasks.set(taskSymbol, {
|
|
188
|
-
id: taskOptions.id,
|
|
189
|
-
priority: taskOptions.priority ?? 0,
|
|
190
|
-
startTime: Date.now(),
|
|
191
|
-
timeout: taskOptions.timeout
|
|
192
|
-
});
|
|
193
|
-
try {
|
|
194
|
-
taskOptions.signal?.throwIfAborted();
|
|
195
|
-
const operation = Promise.resolve(function_({ signal: taskOptions.signal }));
|
|
196
|
-
const result = await this.#withTimeoutAndAbort(operation, taskOptions, (listener) => {
|
|
197
|
-
runningAbortHandler = listener;
|
|
198
|
-
});
|
|
199
|
-
resolve(result);
|
|
200
|
-
this.emit("completed", result);
|
|
201
|
-
} catch (error) {
|
|
202
|
-
reject(error);
|
|
203
|
-
this.emit("error", error);
|
|
204
|
-
} finally {
|
|
205
|
-
if (runningAbortHandler) {
|
|
206
|
-
taskOptions.signal?.removeEventListener("abort", runningAbortHandler);
|
|
207
|
-
}
|
|
208
|
-
this.#runningTasks.delete(taskSymbol);
|
|
209
|
-
queueMicrotask(() => {
|
|
210
|
-
this.#next();
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
if (taskOptions.signal?.aborted) {
|
|
215
|
-
reject(taskOptions.signal.reason);
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
if (taskOptions.signal) {
|
|
219
|
-
queuedAbortHandler = () => {
|
|
220
|
-
if (didRun) {
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
this.#queue.remove?.(taskOptions.id);
|
|
224
|
-
reject(taskOptions.signal?.reason);
|
|
225
|
-
this.emit("next");
|
|
226
|
-
this.#tryToStartAnother();
|
|
227
|
-
};
|
|
228
|
-
taskOptions.signal.addEventListener("abort", queuedAbortHandler, {
|
|
229
|
-
once: true
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
this.#queue.enqueue(run, taskOptions);
|
|
233
|
-
this.emit("add");
|
|
234
|
-
this.#processQueue();
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
async addAll(functions, options) {
|
|
238
|
-
return Promise.all(functions.map(async (function_) => this.add(function_, options)));
|
|
239
|
-
}
|
|
240
|
-
start() {
|
|
241
|
-
if (!this.#isPaused) {
|
|
242
|
-
return this;
|
|
243
|
-
}
|
|
244
|
-
this.#isPaused = false;
|
|
245
|
-
this.#processQueue();
|
|
246
|
-
return this;
|
|
247
|
-
}
|
|
248
|
-
pause() {
|
|
249
|
-
this.#isPaused = true;
|
|
250
|
-
}
|
|
251
|
-
clear() {
|
|
252
|
-
this.#queue = new this.#queueClass;
|
|
253
|
-
this.#clearIntervalTimer();
|
|
254
|
-
this.#updateRateLimitState();
|
|
255
|
-
this.emit("empty");
|
|
256
|
-
if (this.#pending === 0) {
|
|
257
|
-
this.emit("idle");
|
|
258
|
-
}
|
|
259
|
-
this.emit("next");
|
|
260
|
-
}
|
|
261
|
-
async onEmpty() {
|
|
262
|
-
if (this.#queue.size === 0) {
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
await this.#onEvent("empty");
|
|
266
|
-
}
|
|
267
|
-
async onIdle() {
|
|
268
|
-
if (this.#queue.size === 0 && this.#pending === 0) {
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
await this.#onEvent("idle");
|
|
272
|
-
}
|
|
273
|
-
async onPendingZero() {
|
|
274
|
-
if (this.#pending === 0) {
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
await this.#onEvent("pendingZero");
|
|
278
|
-
}
|
|
279
|
-
async onRateLimit() {
|
|
280
|
-
if (this.isRateLimited) {
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
await this.#onEvent("rateLimit");
|
|
284
|
-
}
|
|
285
|
-
async onRateLimitCleared() {
|
|
286
|
-
if (!this.isRateLimited) {
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
await this.#onEvent("rateLimitCleared");
|
|
290
|
-
}
|
|
291
|
-
onError() {
|
|
292
|
-
return new Promise((_resolve, reject) => {
|
|
293
|
-
const listener = (error) => {
|
|
294
|
-
this.off("error", listener);
|
|
295
|
-
reject(error);
|
|
296
|
-
};
|
|
297
|
-
this.on("error", listener);
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
async onSizeLessThan(limit) {
|
|
301
|
-
if (this.#queue.size < limit) {
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
await this.#onEvent("next", () => this.#queue.size < limit);
|
|
305
|
-
}
|
|
306
|
-
async#onEvent(event, filter) {
|
|
307
|
-
return new Promise((resolve) => {
|
|
308
|
-
const listener = () => {
|
|
309
|
-
if (filter && !filter()) {
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
this.off(event, listener);
|
|
313
|
-
resolve();
|
|
314
|
-
};
|
|
315
|
-
this.on(event, listener);
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
async#withTimeoutAndAbort(operation, options, setAbortListener) {
|
|
319
|
-
const races = [operation];
|
|
320
|
-
let timeoutId;
|
|
321
|
-
if (options.timeout) {
|
|
322
|
-
races.push(new Promise((_resolve, reject) => {
|
|
323
|
-
timeoutId = setTimeout(() => {
|
|
324
|
-
reject(new TimeoutError(`Task timed out after ${options.timeout}ms`));
|
|
325
|
-
}, options.timeout);
|
|
326
|
-
}));
|
|
327
|
-
}
|
|
328
|
-
if (options.signal) {
|
|
329
|
-
races.push(new Promise((_resolve, reject) => {
|
|
330
|
-
const listener = () => {
|
|
331
|
-
reject(options.signal?.reason);
|
|
332
|
-
};
|
|
333
|
-
setAbortListener(listener);
|
|
334
|
-
options.signal?.addEventListener("abort", listener, { once: true });
|
|
335
|
-
}));
|
|
336
|
-
}
|
|
337
|
-
try {
|
|
338
|
-
return await Promise.race(races);
|
|
339
|
-
} finally {
|
|
340
|
-
if (timeoutId) {
|
|
341
|
-
clearTimeout(timeoutId);
|
|
42
|
+
return new Response(new Blob([bytes]).stream().pipeThrough(new DecompressionStream("gzip"))).text();
|
|
43
|
+
};
|
|
44
|
+
var parseSitemap = (xml) => ({
|
|
45
|
+
isIndex: /<sitemapindex[\s>]/i.test(xml),
|
|
46
|
+
entries: [...xml.matchAll(/<(url|sitemap)[\s>][\s\S]*?<\/\1>/gi)].flatMap(([block]) => {
|
|
47
|
+
const loc = block.match(/<loc>\s*(?:<!\[CDATA\[([\s\S]*?)\]\]>|([^<]*?))\s*<\/loc>/i);
|
|
48
|
+
if (!loc)
|
|
49
|
+
return [];
|
|
50
|
+
const lastmod = block.match(/<lastmod>\s*([^<]*?)\s*<\/lastmod>/i)?.[1];
|
|
51
|
+
return [
|
|
52
|
+
{
|
|
53
|
+
loc: (loc[1] ?? decodeXml(loc[2])).trim(),
|
|
54
|
+
...lastmod ? { lastmod } : {}
|
|
342
55
|
}
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
if (
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
56
|
+
];
|
|
57
|
+
})
|
|
58
|
+
});
|
|
59
|
+
var productToken = (userAgent) => userAgent.trim() === "*" ? "*" : /^[a-z_-]+/i.exec(userAgent.trim())?.[0].toLowerCase() ?? "";
|
|
60
|
+
function parseRobotsTxt(body, userAgent = "*") {
|
|
61
|
+
const groups = [];
|
|
62
|
+
const sitemaps = new Set;
|
|
63
|
+
let group;
|
|
64
|
+
let inRules = false;
|
|
65
|
+
for (const line of body.split(/\r\n|\r|\n/)) {
|
|
66
|
+
const match = /^\s*([a-z-]+)\s*:\s*(.*?)\s*$/i.exec(line.replace(/#.*/, ""));
|
|
67
|
+
if (!match)
|
|
68
|
+
continue;
|
|
69
|
+
const field = match[1].toLowerCase();
|
|
70
|
+
const value = match[2];
|
|
71
|
+
if (field === "sitemap") {
|
|
72
|
+
if (value)
|
|
73
|
+
sitemaps.add(value);
|
|
74
|
+
} else if (field === "user-agent") {
|
|
75
|
+
if (!group || inRules) {
|
|
76
|
+
group = { agents: [], allow: [], disallow: [] };
|
|
77
|
+
groups.push(group);
|
|
78
|
+
inRules = false;
|
|
363
79
|
}
|
|
364
|
-
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
80
|
+
group.agents.push(productToken(value));
|
|
81
|
+
} else if (group && (field === "allow" || field === "disallow")) {
|
|
82
|
+
inRules = true;
|
|
83
|
+
if (value)
|
|
84
|
+
group[field].push(value);
|
|
85
|
+
} else if (group && field === "crawl-delay") {
|
|
86
|
+
inRules = true;
|
|
87
|
+
const seconds = Number(value);
|
|
88
|
+
if (value && seconds >= 0)
|
|
89
|
+
group.crawlDelaySec ??= seconds;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const token = productToken(userAgent);
|
|
93
|
+
const named = groups.filter((g) => g.agents.includes(token));
|
|
94
|
+
const selected = named.length > 0 ? named : groups.filter((g) => g.agents.includes("*"));
|
|
95
|
+
const crawlDelaySec = selected.find((g) => g.crawlDelaySec !== undefined)?.crawlDelaySec;
|
|
96
|
+
return {
|
|
97
|
+
allow: selected.flatMap((g) => g.allow),
|
|
98
|
+
disallow: selected.flatMap((g) => g.disallow),
|
|
99
|
+
sitemaps: [...sitemaps],
|
|
100
|
+
...crawlDelaySec === undefined ? {} : { crawlDelaySec }
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
var patternCache = new Map;
|
|
104
|
+
var matchesRobotsPattern = (pattern, path) => {
|
|
105
|
+
let regex = patternCache.get(pattern);
|
|
106
|
+
if (!regex) {
|
|
107
|
+
const anchored = pattern.endsWith("$");
|
|
108
|
+
const source = (anchored ? pattern.slice(0, -1) : pattern).split("*").map(escapeRegExp2).join(".*");
|
|
109
|
+
regex = new RegExp(`^${source}${anchored ? "$" : ""}`);
|
|
110
|
+
if (patternCache.size >= 1000)
|
|
111
|
+
patternCache.clear();
|
|
112
|
+
patternCache.set(pattern, regex);
|
|
113
|
+
}
|
|
114
|
+
return regex.test(path);
|
|
115
|
+
};
|
|
116
|
+
function isAllowedByRobots(rules, url) {
|
|
117
|
+
let path;
|
|
118
|
+
try {
|
|
119
|
+
const parsed = new URL(url, "http://localhost");
|
|
120
|
+
path = parsed.pathname + parsed.search;
|
|
121
|
+
} catch {
|
|
376
122
|
return true;
|
|
377
123
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
124
|
+
if (path === "/robots.txt")
|
|
125
|
+
return true;
|
|
126
|
+
let best;
|
|
127
|
+
for (const [allow, patterns] of [
|
|
128
|
+
[true, rules.allow],
|
|
129
|
+
[false, rules.disallow]
|
|
130
|
+
]) {
|
|
131
|
+
for (const pattern of patterns) {
|
|
132
|
+
if (pattern.length > (best?.length ?? -1) && matchesRobotsPattern(pattern, path)) {
|
|
133
|
+
best = { allow, length: pattern.length };
|
|
387
134
|
}
|
|
388
|
-
this.#scheduleIntervalResume(this.#interval - (now - this.#strictTimestamps[0]));
|
|
389
|
-
return false;
|
|
390
|
-
}
|
|
391
|
-
if (this.#intervalEnd === 0 || now >= this.#intervalEnd) {
|
|
392
|
-
this.#intervalCount = this.#carryoverIntervalCount ? this.#pending : 0;
|
|
393
|
-
this.#intervalEnd = now + this.#interval;
|
|
394
|
-
}
|
|
395
|
-
if (this.#intervalCount < this.#intervalCap) {
|
|
396
|
-
return true;
|
|
397
|
-
}
|
|
398
|
-
this.#scheduleIntervalResume(this.#intervalEnd - now);
|
|
399
|
-
return false;
|
|
400
|
-
}
|
|
401
|
-
#consumeIntervalSlot() {
|
|
402
|
-
if (this.#intervalCap === Number.POSITIVE_INFINITY || this.#interval === 0) {
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
if (this.#strict) {
|
|
406
|
-
this.#strictTimestamps.push(Date.now());
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
this.#intervalCount++;
|
|
410
|
-
}
|
|
411
|
-
#scheduleIntervalResume(delay) {
|
|
412
|
-
if (this.#intervalTimer) {
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
this.#intervalTimer = setTimeout(() => {
|
|
416
|
-
this.#intervalTimer = undefined;
|
|
417
|
-
this.#processQueue();
|
|
418
|
-
}, Math.max(0, delay));
|
|
419
|
-
}
|
|
420
|
-
#clearIntervalTimer() {
|
|
421
|
-
if (this.#intervalTimer) {
|
|
422
|
-
clearTimeout(this.#intervalTimer);
|
|
423
|
-
this.#intervalTimer = undefined;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
#updateRateLimitState() {
|
|
427
|
-
const previous = this.#rateLimited;
|
|
428
|
-
const isRateLimited = this.#queue.size > 0 && this.#intervalCap !== Number.POSITIVE_INFINITY && this.#interval > 0 && !this.#doesIntervalAllowAnother();
|
|
429
|
-
this.#rateLimited = isRateLimited;
|
|
430
|
-
if (previous !== isRateLimited) {
|
|
431
|
-
this.emit(isRateLimited ? "rateLimit" : "rateLimitCleared");
|
|
432
135
|
}
|
|
433
136
|
}
|
|
137
|
+
return best?.allow ?? true;
|
|
434
138
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
var createTimeoutController = (timeout) => {
|
|
438
|
-
const controller = new AbortController;
|
|
439
|
-
setTimeout(() => controller.abort(), timeout);
|
|
440
|
-
return controller;
|
|
441
|
-
};
|
|
442
|
-
var discoverSitemaps = async (domain) => {
|
|
443
|
-
const normalizedDomain = domain.replace(/\/$/, "");
|
|
444
|
-
const commonLocations = [
|
|
445
|
-
"/sitemap.xml",
|
|
446
|
-
"/sitemap_index.xml",
|
|
447
|
-
"/sitemap/",
|
|
448
|
-
"/sitemaps.xml",
|
|
449
|
-
"/sitemap/sitemap.xml"
|
|
450
|
-
];
|
|
451
|
-
const sitemaps = new Set;
|
|
452
|
-
const requestOptions = {
|
|
453
|
-
headers: {
|
|
454
|
-
"User-Agent": "SitemapCrawler/1.0"
|
|
455
|
-
},
|
|
456
|
-
signal: createTimeoutController(5000).signal
|
|
457
|
-
};
|
|
139
|
+
var fetchRobots = async (origin, request, userAgent, onError) => {
|
|
140
|
+
const robotsUrl = `${origin}/robots.txt`;
|
|
458
141
|
try {
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
if (robotsTxt.ok) {
|
|
463
|
-
const text = await robotsTxt.text();
|
|
464
|
-
const sitemapMatches = text.match(/Sitemap: (.*)/gi);
|
|
465
|
-
if (sitemapMatches) {
|
|
466
|
-
sitemapMatches.forEach((match) => {
|
|
467
|
-
const url = match.replace("Sitemap:", "").trim();
|
|
468
|
-
sitemaps.add(url);
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
if (sitemaps.size > 0) {
|
|
472
|
-
return [...sitemaps];
|
|
473
|
-
}
|
|
474
|
-
}
|
|
142
|
+
const response = await request(robotsUrl);
|
|
143
|
+
if (response.ok)
|
|
144
|
+
return parseRobotsTxt(await response.text(), userAgent);
|
|
475
145
|
} catch (error) {
|
|
476
|
-
|
|
146
|
+
onError(robotsUrl, error);
|
|
477
147
|
}
|
|
478
|
-
|
|
148
|
+
return;
|
|
149
|
+
};
|
|
150
|
+
var probeSitemapLocations = async (origin, request) => {
|
|
151
|
+
for (const location of COMMON_LOCATIONS) {
|
|
479
152
|
try {
|
|
480
|
-
const response = await
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
if (response.ok) {
|
|
485
|
-
return [`${normalizedDomain}${location}`];
|
|
486
|
-
}
|
|
487
|
-
} catch (_error) {}
|
|
153
|
+
const response = await request(`${origin}${location}`, "HEAD");
|
|
154
|
+
if (response.ok)
|
|
155
|
+
return [`${origin}${location}`];
|
|
156
|
+
} catch {}
|
|
488
157
|
}
|
|
489
158
|
return [];
|
|
490
159
|
};
|
|
491
|
-
var
|
|
160
|
+
var getDomainSitemapEntries = async (domain, options = {}) => {
|
|
492
161
|
const {
|
|
493
162
|
filterIndexes,
|
|
163
|
+
filterUrls,
|
|
494
164
|
concurrency = 10,
|
|
495
165
|
maxDepth = 5,
|
|
496
|
-
|
|
166
|
+
maxUrls = Number.POSITIVE_INFINITY,
|
|
167
|
+
timeout = 1e4,
|
|
168
|
+
userAgent = "SitemapCrawler/1.0",
|
|
169
|
+
fetch: fetchFn = globalThis.fetch,
|
|
170
|
+
onError = defaultOnError,
|
|
171
|
+
respectRobots = false
|
|
497
172
|
} = options;
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
173
|
+
const request = (url, method = "GET") => fetchFn(url, {
|
|
174
|
+
method,
|
|
175
|
+
headers: { "User-Agent": userAgent },
|
|
176
|
+
signal: AbortSignal.timeout(timeout)
|
|
177
|
+
});
|
|
178
|
+
const origin = normalizeDomain2(domain);
|
|
179
|
+
const robots = await fetchRobots(origin, request, userAgent, onError);
|
|
180
|
+
const sitemapUrls = robots?.sitemaps.length ? robots.sitemaps : await probeSitemapLocations(origin, request);
|
|
181
|
+
const keepUrl = (url) => matchesFilter(filterUrls, url) && (!respectRobots || !robots || isAllowedByRobots(robots, url));
|
|
503
182
|
if (sitemapUrls.length === 0) {
|
|
504
183
|
throw new Error("No sitemaps found for the domain");
|
|
505
184
|
}
|
|
506
|
-
const queue = new
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
visited: new Set
|
|
510
|
-
};
|
|
511
|
-
const locRegex = /<loc>([^<]+)<\/loc>/g;
|
|
512
|
-
const isXmlSitemap = (url) => url.endsWith(".xml") || url.toLowerCase().includes("sitemap");
|
|
185
|
+
const queue = new PQueue2({ concurrency });
|
|
186
|
+
const entries = new Map;
|
|
187
|
+
const visited = new Set;
|
|
513
188
|
const processSitemap = async (sitemapUrl, depth = 0) => {
|
|
514
|
-
if (depth > maxDepth ||
|
|
189
|
+
if (depth > maxDepth || visited.has(sitemapUrl) || entries.size >= maxUrls) {
|
|
515
190
|
return;
|
|
516
191
|
}
|
|
517
|
-
|
|
192
|
+
visited.add(sitemapUrl);
|
|
518
193
|
try {
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
headers: {
|
|
522
|
-
"User-Agent": "SitemapCrawler/1.0"
|
|
523
|
-
},
|
|
524
|
-
signal: controller.signal
|
|
525
|
-
});
|
|
526
|
-
if (!response.ok)
|
|
194
|
+
const response = await request(sitemapUrl);
|
|
195
|
+
if (!response.ok) {
|
|
527
196
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
528
|
-
|
|
529
|
-
const
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
result.links.add(link);
|
|
197
|
+
}
|
|
198
|
+
const { isIndex, entries: found } = parseSitemap(await readBody(response));
|
|
199
|
+
for (const entry of found) {
|
|
200
|
+
if (isIndex) {
|
|
201
|
+
if (matchesFilter(filterIndexes, entry.loc)) {
|
|
202
|
+
queue.add(() => processSitemap(entry.loc, depth + 1));
|
|
203
|
+
}
|
|
204
|
+
} else if (entries.size >= maxUrls) {
|
|
205
|
+
return;
|
|
206
|
+
} else if (keepUrl(entry.loc)) {
|
|
207
|
+
entries.set(entry.loc, entries.get(entry.loc) ?? entry);
|
|
540
208
|
}
|
|
541
209
|
}
|
|
542
210
|
} catch (error) {
|
|
543
|
-
|
|
211
|
+
onError(sitemapUrl, error);
|
|
544
212
|
}
|
|
545
213
|
};
|
|
546
214
|
await Promise.all(sitemapUrls.map((sitemapUrl) => queue.add(() => processSitemap(sitemapUrl))));
|
|
547
215
|
await queue.onIdle();
|
|
548
|
-
return [...
|
|
216
|
+
return [...entries.values()];
|
|
549
217
|
};
|
|
218
|
+
var getDomainSitemap = async (domain, options = {}) => (await getDomainSitemapEntries(domain, options)).map((entry) => entry.loc);
|
|
550
219
|
export {
|
|
551
|
-
getDomainSitemap
|
|
220
|
+
getDomainSitemap,
|
|
221
|
+
getDomainSitemapEntries,
|
|
222
|
+
isAllowedByRobots,
|
|
223
|
+
parseRobotsTxt
|
|
552
224
|
};
|