searchfetch 3.0.0 → 3.0.1
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/index.js +42 -6
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -244,6 +244,33 @@ function mapSearchParams(engine, query, region, safeSearch) {
|
|
|
244
244
|
|
|
245
245
|
// === FETCH ===============================================================
|
|
246
246
|
|
|
247
|
+
const FETCH_MAX_ATTEMPTS = 2;
|
|
248
|
+
const HTTP_429_RETRY_DELAY_MS = 2000;
|
|
249
|
+
|
|
250
|
+
function sleep(ms) {
|
|
251
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function parseRetryAfterMs(value) {
|
|
255
|
+
if (!value) return HTTP_429_RETRY_DELAY_MS;
|
|
256
|
+
const seconds = Number(value);
|
|
257
|
+
if (Number.isFinite(seconds) && seconds >= 0) {
|
|
258
|
+
return Math.min(seconds * 1000, 30000);
|
|
259
|
+
}
|
|
260
|
+
const dateMs = Date.parse(value);
|
|
261
|
+
if (Number.isFinite(dateMs)) {
|
|
262
|
+
return Math.min(Math.max(dateMs - Date.now(), 0), 30000);
|
|
263
|
+
}
|
|
264
|
+
return HTTP_429_RETRY_DELAY_MS;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function makeHttpStatusError(status, url, retryAfterMs = null) {
|
|
268
|
+
const err = new Error(`Access denied: HTTP ${status} when fetching ${url}`);
|
|
269
|
+
err.httpStatus = status;
|
|
270
|
+
err.retryAfterMs = retryAfterMs;
|
|
271
|
+
return err;
|
|
272
|
+
}
|
|
273
|
+
|
|
247
274
|
function isAccessDenied($) {
|
|
248
275
|
const title = ($("title").text() || "").toLowerCase();
|
|
249
276
|
const bodyText = ($("body").text() || "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
@@ -325,8 +352,10 @@ async function fetchHtml(url, template, blockMedia) {
|
|
|
325
352
|
if (response) {
|
|
326
353
|
const status = response.status();
|
|
327
354
|
if ([401, 403, 429].includes(status)) {
|
|
328
|
-
throw
|
|
329
|
-
|
|
355
|
+
throw makeHttpStatusError(
|
|
356
|
+
status,
|
|
357
|
+
url,
|
|
358
|
+
status === 429 ? parseRetryAfterMs(response.headers()["retry-after"]) : null,
|
|
330
359
|
);
|
|
331
360
|
}
|
|
332
361
|
}
|
|
@@ -352,18 +381,25 @@ async function fetchHtml(url, template, blockMedia) {
|
|
|
352
381
|
|
|
353
382
|
async function fetchHtmlWithRetry(url, template, blockMedia) {
|
|
354
383
|
let lastError;
|
|
355
|
-
for (let attempt = 0; attempt <
|
|
384
|
+
for (let attempt = 0; attempt < FETCH_MAX_ATTEMPTS; attempt++) {
|
|
356
385
|
try {
|
|
357
386
|
return await fetchHtml(url, template, blockMedia);
|
|
358
387
|
} catch (err) {
|
|
359
388
|
lastError = err;
|
|
360
389
|
if (
|
|
361
|
-
attempt
|
|
390
|
+
attempt < FETCH_MAX_ATTEMPTS - 1 &&
|
|
391
|
+
err.httpStatus === 429
|
|
392
|
+
) {
|
|
393
|
+
await sleep(err.retryAfterMs ?? HTTP_429_RETRY_DELAY_MS);
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
if (
|
|
397
|
+
attempt < FETCH_MAX_ATTEMPTS - 1 &&
|
|
362
398
|
(err.message.includes("net::") ||
|
|
363
399
|
err.message.includes("ERR_") ||
|
|
364
400
|
err.message.includes("Navigation failed"))
|
|
365
401
|
) {
|
|
366
|
-
|
|
402
|
+
await sleep(500);
|
|
367
403
|
continue;
|
|
368
404
|
}
|
|
369
405
|
throw err;
|
|
@@ -920,7 +956,7 @@ function resolveSearchTemplate(engine, query, region, safeSearch) {
|
|
|
920
956
|
|
|
921
957
|
// === MCP SERVER & TOOLS ==================================================
|
|
922
958
|
|
|
923
|
-
const server = new McpServer({ name: "searchfetch", version: "3.0.
|
|
959
|
+
const server = new McpServer({ name: "searchfetch", version: "3.0.1" });
|
|
924
960
|
|
|
925
961
|
// --- websearch tool ---
|
|
926
962
|
|