fix(build): retry transient sitemap endpoint fetches

The TypeScript helper refactor exposed a separate production risk: builds can fail when the sitemap-endpoints API returns a transient 5xx during Pagefind index generation. Move that fetch into a small helper with retry logic and add a focused prebuild test so this failure mode is caught without waiting on a full deploy.

Constraint: Must keep Pagefind index generation behavior the same when the endpoint is healthy
Rejected: Ignore the failure as external-only | transient 5xx responses can still block deploys and CI repeatedly
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: When external build inputs can fail transiently, add a small retryable helper plus a direct test before relying on full deploy verification
Tested: pnpm exec vitest run test/prebuild/load-sitemap-endpoints.test.ts; pnpm run typecheck; pnpm run test-prebuild
Not-tested: Full production redeploy completion before push
This commit is contained in:
ThatGuySam 2026-04-04 18:18:23 -05:00
parent cd41143f0d
commit f1cb66c477
3 changed files with 159 additions and 21 deletions

View file

@ -0,0 +1,63 @@
import fs from 'fs-extra'
import axios from 'axios'
import {
sitemapEndpointsPath
} from '~/helpers/pagefind/config.js'
function shouldRetryError ( error: unknown ) {
const status = ( error as { response?: { status?: number } } )?.response?.status
return typeof status === 'number' && status >= 500
}
async function fetchJsonWithRetries (
url: string,
{
attempts = 3,
delayMs = 1000
}: {
attempts?: number
delayMs?: number
} = {}
) {
let lastError: unknown
for ( let attempt = 1; attempt <= attempts; attempt += 1 ) {
try {
const response = await axios.get( url )
return response.data
} catch ( error ) {
lastError = error
if ( attempt >= attempts || !shouldRetryError( error ) ) {
throw error
}
await new Promise( resolve => setTimeout( resolve, delayMs ) )
}
}
throw lastError
}
export async function loadSitemapEndpoints () {
if ( await fs.pathExists( sitemapEndpointsPath ) ) {
return await fs.readJson( sitemapEndpointsPath )
}
if ( !process.env.PUBLIC_API_DOMAIN ) {
throw new Error(`Missing ${ sitemapEndpointsPath } and PUBLIC_API_DOMAIN is not set`)
}
const apiUrl = new URL( process.env.PUBLIC_API_DOMAIN )
apiUrl.pathname = sitemapEndpointsPath.replace(/^\.?\/?static\//, '/')
return await fetchJsonWithRetries( apiUrl.toString() )
}
export {
fetchJsonWithRetries,
shouldRetryError
}