Sessions 29-30: Content templates + PropLine 3-key adapter + MLB Stats API + ESPN summary (1694 tests)

This commit is contained in:
Kev
2026-06-14 22:29:01 -04:00
parent 927c4a5c65
commit a3351e2135
14 changed files with 1091 additions and 27 deletions
+42 -4
View File
@@ -28,6 +28,22 @@
const PROVIDERS = {
// === ODDS / LINES ===
// Session 30 — PropLine is now PRIMARY for player props. 3 free keys
// rotate for 3,000 req/day combined (vs odds-api's 500/MONTH), and its
// response shape is The-Odds-API-compatible. The proplineAdapter handles
// which physical key to use; the gateway counts total propline calls
// against the 3,000/day cap here. odds-api drops to priority 2 (backup
// we conserve). Configured when at least key 1 is present.
'propline': {
name: 'PropLine',
envKey: 'PROPLINE_API_KEY_1',
quotaType: 'daily',
quotaLimit: 3000,
resetDay: null,
sports: ['nba', 'wnba', 'mlb', 'nfl', 'nhl'],
capabilities: ['odds', 'props', 'lines', 'spreads'],
priority: 1,
},
'odds-api': {
name: 'The Odds API',
envKey: 'ODDS_API_KEY',
@@ -36,7 +52,7 @@ const PROVIDERS = {
resetDay: 1,
sports: ['nba', 'wnba', 'mlb', 'soccer_wc', 'nfl', 'nhl'],
capabilities: ['odds', 'props', 'lines', 'spreads'],
priority: 1,
priority: 2,
},
// Session 21 — correction. ODDSPAPI is NOT a live-props fallback
// for the-odds-api. It serves Pinnacle CLOSING lines, captured at
@@ -90,6 +106,21 @@ const PROVIDERS = {
capabilities: ['box_scores', 'schedules', 'player_stats', 'bvp'],
priority: 1,
},
// Session 30 — official MLB data. FREE, no auth, unlimited. The
// mlbStatsAdapter does NOT route through the gateway (no quota to
// track); this entry is informational + surfaces it on the admin
// dashboard. `noAuth` marks it always-configured (no env key gate).
'mlb-stats': {
name: 'MLB Stats API',
envKey: null,
noAuth: true,
quotaType: 'unlimited',
quotaLimit: null,
resetDay: null,
sports: ['mlb'],
capabilities: ['game_logs', 'season_averages', 'splits', 'probable_pitchers', 'bvp'],
priority: 1,
},
// === SOCCER ===
'api-football': {
@@ -136,9 +167,16 @@ function listProviderIds() {
* by the admin dashboard to render only providers the operator has
* actually wired up.
*/
// A provider counts as configured when its key is present OR it needs no
// auth at all (e.g. MLB Stats API). Dead providers are always excluded.
function isProviderConfigured(cfg) {
if (!cfg || cfg.status === 'dead') return false;
return cfg.noAuth === true || !!(cfg.envKey && process.env[cfg.envKey]);
}
function getConfiguredProviders() {
return Object.entries(PROVIDERS)
.filter(([, cfg]) => !!process.env[cfg.envKey] && cfg.status !== 'dead')
.filter(([, cfg]) => isProviderConfigured(cfg))
.map(([id, cfg]) => ({ id, ...cfg }));
}
@@ -156,10 +194,9 @@ function getFallbackChain(capability, sport, excludeId) {
return Object.entries(PROVIDERS)
.filter(([id, cfg]) =>
id !== excludeId &&
cfg.status !== 'dead' && // Session 23 — skip retired providers
cfg.capabilities.includes(capability) &&
(!sport || cfg.sports.includes(sport)) &&
!!process.env[cfg.envKey],
isProviderConfigured(cfg), // excludes dead + unconfigured, includes noAuth
)
.sort((a, b) => a[1].priority - b[1].priority)
.map(([id]) => id);
@@ -173,4 +210,5 @@ module.exports = {
getConfiguredProviders,
getFallbackChain,
isDeadProvider,
isProviderConfigured,
};