diff --git a/apps/site/next-data/generators/majorNodeReleases.mjs b/apps/site/next-data/generators/majorNodeReleases.mjs index 0a9a06e8cd5e4..108bc14156160 100644 --- a/apps/site/next-data/generators/majorNodeReleases.mjs +++ b/apps/site/next-data/generators/majorNodeReleases.mjs @@ -2,11 +2,13 @@ import nodevu from '@nodevu/core'; +import { fetchWithRetry } from '#site/util/fetch'; + /** * Filters Node.js release data to return only major releases with documented support. */ export default async function getMajorNodeReleases() { - const nodevuData = await nodevu({ fetch }); + const nodevuData = await nodevu({ fetch: fetchWithRetry }); return Object.entries(nodevuData).filter(([version, { support }]) => { // Filter out those without documented support diff --git a/apps/site/util/fetch.ts b/apps/site/util/fetch.ts index 61378982c6616..b998879503ef1 100644 --- a/apps/site/util/fetch.ts +++ b/apps/site/util/fetch.ts @@ -1,15 +1,14 @@ -import { setTimeout } from 'node:timers/promises'; - type RetryOptions = RequestInit & { maxRetry?: number; delay?: number; }; -type FetchError = { - cause: { - code: string; - }; -}; +const isTimeoutError = (e: unknown): boolean => + e instanceof Error && + typeof e.cause === 'object' && + e.cause !== null && + 'code' in e.cause && + e.cause.code === 'ETIMEDOUT'; export const fetchWithRetry = async ( url: string, @@ -17,18 +16,18 @@ export const fetchWithRetry = async ( ) => { for (let i = 1; i <= maxRetry; i++) { try { - return fetch(url, options); + return await fetch(url, options); } catch (e) { console.debug( `fetch of ${url} failed at ${Date.now()}, attempt ${i}/${maxRetry}`, e ); - if (i === maxRetry || (e as FetchError).cause.code !== 'ETIMEDOUT') { + if (i === maxRetry || !isTimeoutError(e)) { throw e; } - await setTimeout(delay * i); + await new Promise(resolve => setTimeout(resolve, delay * i)); } } };