doesitarm/helpers/api/client.js
ThatGuySam 6cfbfbf530 Keep prod health checks and route fallbacks from failing on stale API entries
Add a Bun health script that exercises top-level, dynamic, and representative video routes against one or more hosts so prod regressions are visible from a single command.

Device pages now fall back to the bundled device list when the external API misses a slug, and orphaned tv slugs redirect to /benchmarks instead of returning a 500. Video fallback logic reuses the existing YouTube-to-listing builder so route reconstruction stays aligned with the current build logic.

Constraint: The external API host can lag behind the frontend build and omit per-slug JSON files that public routes still expect
Rejected: Import the generated video list directly | static/video-list.json is too large for a safe SSR fallback
Rejected: Leave missing tv routes as 500s | a stale public URL should degrade to a useful redirect instead of breaking the request
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep route fallbacks tied to build-time artifacts from the same repo so frontend and fallback data stay in sync
Tested: bun scripts/health http://127.0.0.1:4322; vitest ./test/prebuild/config-node.test.js ./test/prebuild/site-listings.test.js; pnpm run netlify-build
Not-tested: live production deploy before push
2026-04-06 10:51:49 -05:00

92 lines
2.6 KiB
JavaScript

// Based on GitHub Proxy demo
// https://gist.github.com/v1vendi/75d5e5dad7a2d1ef3fcb48234e4528cb
// Example uses:
// DoesItAPI.get() // GET /api
// DoesItAPI.apps.get() // GET /api/apps.json
// DoesItAPI.apps(7).get() // GET /api/apps/7.json
// DoesItAPI.apps(7).whatever.delete() // DELETE /api/apps/7/whatever.json
// DoesItAPI.apps.put({ whatever: 1 })
// GET /api/tiles/public/static/3/4/2.json?turn=37038&games=wot
// DoesItAPI.tiles.public.static(3)(4)(`${2}.json`).get({ turn: 37, games: 'wot' })
import axios from 'axios'
import { getApiUrl } from '~/helpers/url.js'
// Use msw
import '~/test/msw/use.js'
// const defaultFetchMethod = (...args) => console.log(...args) // mock
const defaultFetchMethod = async function (...args) {
return axios(...args)
.then( response => response.data )
.catch( error => {
if ( error?.response?.status !== 404 ) {
console.error( error )
}
throw error
})
}
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
const HTTP_METHODS = [
'GET',
// 'POST',
// 'PUT',
// 'DELETE',
// 'PATCH'
]
const apiBaseUrl = `${ getApiUrl().replace(/\/$/, '') }/api`
export function generateAPI ( {
apiUrl = apiBaseUrl,
fetchMethod = defaultFetchMethod
} = {} ) {
// console.log( 'apiUrl', apiUrl )
// a hack, so we can use field either as property or a method
const callable = () => {}
callable.url = apiUrl
return new Proxy(callable, {
get({ url }, propKey) {
// If we're just getting the url, return it
if ( propKey === 'url' ) return `${ url }.json`
// If we're using an HTTP method
// then do a request to the url
if ( HTTP_METHODS.includes(propKey.toUpperCase()) ) {
return (data) => fetchMethod({
url: `${ url }.json`,
method: propKey.toUpperCase(),
data
})
}
// Otherwise drill down to the next property
// Example:
// From DoesItAPI.kind...
// To DoesItAPI.kind.apps...
return generateAPI({ apiUrl: `${url}/${propKey}` })
},
// Handles when () goes after a property key
// Example: DoesItAPI() or DoesItAPI.app()
// Proxy.handler.apply - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/apply
apply({ url }, thisArg, [arg] = []) {
const apiUrl = arg ? `${url}/${arg}` : url
return generateAPI({ apiUrl: apiUrl })
}
})
}
export const DoesItAPI = generateAPI()