Session 24: Connect everything — Slate wired to all sources, copy fixed, nav fixed, startup prefetch, language button removed (1571 tests)

This commit is contained in:
Kev
2026-06-12 15:45:19 -04:00
parent 0538205fab
commit 433e827103
15 changed files with 586 additions and 99 deletions
+7
View File
@@ -4,6 +4,9 @@ const app = require('./app');
// otherwise only manifests when the gateway tries to fall over and
// finds no chain.
const { getConfiguredProviders, listProviderIds } = require('./config/providers');
// Session 24 — warm the Tank01 cache after boot so streaks / hot lists /
// game lines have data on the first page load. Non-blocking; see module.
const { scheduleStartupPrefetch } = require('./startupPrefetch');
// Default 3001 — Next.js owns 3000 locally and in production. The poller,
// internal cron, and BASE_URL conventions all assume 3001 for the Express
@@ -18,4 +21,8 @@ app.listen(PORT, () => {
if (missing.length) {
console.warn(`[VYNDR] providers missing keys: ${missing.join(', ')}`);
}
// Session 24 — fire-and-forget cache warm. 5s delay so Redis is ready.
// Skips itself when RAPID_API_KEY is unset; never blocks or crashes boot.
scheduleStartupPrefetch();
});
+64
View File
@@ -0,0 +1,64 @@
/**
* Startup prefetch (Session 24).
*
* Warms the Tank01 game-log + game-lines cache shortly after the server
* boots, so the streaks / hot-list panels and the game-lines strip have
* data on the FIRST page load instead of self-hiding until a user
* happens to trigger a fetch.
*
* Hard rules:
* - NON-BLOCKING. Server readiness must never wait on this. The caller
* schedules it and moves on.
* - NEVER crashes the process. Every failure is swallowed + logged.
* - Skips entirely when RAPID_API_KEY is absent (nothing to warm, and
* no point spinning the prefetch's no-op passes).
*
* The prefetch module owns its own quota budget (`--max`), so a runaway
* can't blow the RapidAPI monthly cap.
*/
const prefetch = require('../scripts/tank01-prefetch');
const DEFAULTS = Object.freeze({
sports: ['nba', 'mlb'],
maxRequests: 40, // conservative — bounded by the prefetch's own budget
});
/**
* Run the prefetch once. Awaitable for tests; callers in server.js do NOT
* await it. Returns the prefetch summary, or null when skipped/failed.
*/
async function runStartupPrefetch(opts = {}) {
const sports = opts.sports || DEFAULTS.sports;
const maxRequests = opts.maxRequests || DEFAULTS.maxRequests;
if (!process.env.RAPID_API_KEY) {
console.log('[VYNDR] startup prefetch skipped — RAPID_API_KEY not set');
return null;
}
try {
const argv = ['node', 'startup-prefetch', `--sports=${sports.join(',')}`, `--max=${maxRequests}`];
const summary = await prefetch.main(argv);
console.log('[VYNDR] startup prefetch complete');
return summary;
} catch (err) {
// Swallow — a flaky RapidAPI must never take the API server down.
console.warn('[VYNDR] startup prefetch failed:', err.message);
return null;
}
}
/**
* Schedule the prefetch to run after `delayMs` (default 5s so Redis is
* ready). Returns the timer handle (unref'd so it never keeps the event
* loop alive on its own). Fully fire-and-forget.
*/
function scheduleStartupPrefetch(opts = {}) {
const delayMs = opts.delayMs != null ? opts.delayMs : 5000;
const timer = setTimeout(() => { void runStartupPrefetch(opts); }, delayMs);
if (typeof timer.unref === 'function') timer.unref();
return timer;
}
module.exports = { runStartupPrefetch, scheduleStartupPrefetch, DEFAULTS };