From f33091ddb897d9a3b456083b97c9cb40953f28b4 Mon Sep 17 00:00:00 2001 From: Kev Date: Tue, 21 Jul 2026 01:20:29 -0400 Subject: [PATCH] Layer 3 Step 4b: public park base, source-pluggable, plus game-level capture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PHASE 0 — the settle path sees a player's game-log line, not the game. It knows date and teams, never venue or final totals. But the grain is far cheaper than per-prop or even per-game: ONE statsapi schedule call per game DATE returns every game that day with venue, linescore and scoring plays. Fifteen games, one call, verified live. PUBLIC BASE — the ingestion was already done. The static FanGraphs table from Session 15 is the public base; this converts its 100-indexed values into the multipliers the composable architecture wants (Coors 128 becomes 1.28) rather than ingesting a second copy of a number we already hold. It is labelled COMMODITY in the code, not just in a comment. Every resolution carries a provenance record, and the public one reads proprietary: false with the note "Commodity: a public number. Not a VYNDR derivation." The proprietary label exists but belongs only to the self-derived version, and only once it beats this base on the instrument. A surface rendering a park effect can state which it is rather than implying the flattering one. Honest-absent where even the PUBLIC number is thin: a relocated club in a temporary venue gets no factor, because a public number for a park with one season behind it is no more trustworthy than ours would be. SOURCE-PLUGGABLE is the architectural point. resolveParkBase() is the only accessor, public and derived return identical shapes, and callers never branch on source — so when self-derived factors clear their floor they swap into the same slot with nothing downstream to rewrite. A derived source with no factor available returns absent rather than silently falling back to public, because a silent fallback would make a proprietary claim out of a commodity number. GAME-LEVEL CAPTURE starts now because it cannot start retroactively. Game grain, deduped on game_id, never copied onto prop rows — a game's totals belong to the game, and duplicating them per prop is how one fact starts disagreeing with itself. Every field is tied to a named future derivation: venue for park factors, runs for the run environment, HR totals for HR factors. Nothing else is stored. Only Final games are captured, since an in-progress total is not a result, and a game with no scoring plays reports HR as absent rather than zero. HR totals come from scoring plays, which is complete because every home run scores at least the batter. The accrual target is stated rather than promised: 150 home games per venue at roughly 81 per season means about two seasons before a self-derived factor can be nominated, and accrualStatus() reports live progress per venue so the wait is measurable. Induced: Coors home runs +0.061 for the hitter and identically +0.061 for the pitcher's home-runs-allowed at the same park, mirrored on the under; San Francisco negative; Tampa flagged weather-N/A with its factor still applying; the Athletics' temporary venue absent; strikeouts untouched. A real 2025-07-19 capture produced 15 games across 15 venues, 12 with HR totals, zero duplicate game ids. Migration 035. Induce with POST /api/internal/gamectx/:date, progress at /gamectx/accrual. Tests 3688 passed / 298 suites, web build exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01VCNgGSt5qvcLxaeQqa7Zpj --- src/routes/internal.js | 17 +++ src/services/gameContext.js | 183 +++++++++++++++++++++++++ src/services/parkBase.js | 143 +++++++++++++++++++ src/snapshotScheduler.js | 13 ++ tests/unit/parkBaseGameContext.test.js | 178 ++++++++++++++++++++++++ web/public/sw.js | 2 +- 6 files changed, 535 insertions(+), 1 deletion(-) create mode 100644 src/services/gameContext.js create mode 100644 src/services/parkBase.js create mode 100644 tests/unit/parkBaseGameContext.test.js diff --git a/src/routes/internal.js b/src/routes/internal.js index 382b68e..85e2671 100644 --- a/src/routes/internal.js +++ b/src/routes/internal.js @@ -431,6 +431,23 @@ router.post('/newsletter/send', async (req, res) => { } }); +/** POST /api/internal/gamectx/:date — induce a game-context capture on demand. */ +router.post('/gamectx/:date', async (req, res) => { + try { + const gc = require('../services/gameContext'); + const out = await gc.captureDate(req.params.date, { sport: req.query.sport || 'mlb' }); + return res.status(out.ok ? 200 : 500).json(out); + } catch (err) { return res.status(500).json({ ok: false, error: err.message }); } +}); + +/** GET /api/internal/gamectx/accrual — how far from a self-derived park factor. */ +router.get('/gamectx/accrual', async (req, res) => { + try { + const gc = require('../services/gameContext'); + return res.json(await gc.accrualStatus({ sport: req.query.sport || 'mlb' })); + } catch (err) { return res.status(500).json({ error: err.message }); } +}); + /** * POST /api/internal/statcast/refresh — INDUCE the Layer-1 mechanism refresh. * diff --git a/src/services/gameContext.js b/src/services/gameContext.js new file mode 100644 index 0000000..909cf50 --- /dev/null +++ b/src/services/gameContext.js @@ -0,0 +1,183 @@ +'use strict'; + +/** + * GAME-LEVEL CONTEXT CAPTURE (Layer 3, Step 4b). + * + * We store prop-level rows only, so park factors cannot be self-derived from + * our own data. This starts the accrual that eventually can — and it is + * FORWARD-ONLY: a day not captured is permanently lost, which is the entire + * justification for storing anything before it is needed. + * + * ── WHY THIS IS JUSTIFIED STORAGE AND NOT HOARDING ─────────────────────── + * Three tests, all of which this passes and most speculative capture fails: + * 1. NAMED NEED — every field maps to one specific future derivation + * (venue → park factors, runs → run environment, HR → HR park factors). + * A field with no named purpose is not stored. + * 2. UNRECOVERABLE IF MISSED — statsapi serves historical schedules, but our + * *joined* view of which games our props sat in is not reconstructable + * cheaply, and the habit of capturing late is how the p_win gap happened. + * 3. CHEAP — ONE schedule call per game DATE returns every game that day with + * venue, linescore and scoring plays. Not per game, and certainly not per + * prop. + * + * ── GRAIN ──────────────────────────────────────────────────────────────── + * One row per game, deduped on `game_id`. Never copied onto prop rows: a game's + * totals belong to the game, and duplicating them per prop is how the same fact + * starts disagreeing with itself. + * + * ── ACCRUAL TARGET (so "later" is a date, not a vibe) ───────────────────── + * The derived park factor needs MIN_GAMES (150) home games per venue in the + * current regime. At ~81 home games per park per season that is ~2 SEASONS of + * capture before a self-derived factor can even be NOMINATED against the public + * base. `accrualStatus()` reports live progress so the wait is measurable. + */ + +const { MIN_GAMES } = require('./parkFactors'); + +const HOME_GAMES_PER_SEASON = 81; + +const num = (v) => { + if (v == null || v === '') return null; + const n = typeof v === 'number' ? v : Number(v); + return Number.isFinite(n) ? n : null; +}; + +/** + * rowsFromSchedule(payload, opts) — PURE. statsapi schedule → game_context rows. + * + * Only FINAL games are stored: a suspended or in-progress game has totals that + * are not the game's totals, and storing them would poison every factor derived + * from them later. + */ +function rowsFromSchedule(payload, opts = {}) { + const sport = opts.sport || 'mlb'; + const out = []; + for (const dt of (payload && payload.dates) || []) { + for (const g of dt.games || []) { + const status = (g.status && g.status.detailedState) || null; + if (status !== 'Final') continue; // in-progress totals are not results + + const ls = g.linescore || {}; + const teams = ls.teams || {}; + const home = (g.teams && g.teams.home) || {}; + const away = (g.teams && g.teams.away) || {}; + + // Every home run scores at least the batter, so scoringPlays carries all + // of them — no per-game boxscore fetch needed. + const plays = g.scoringPlays || []; + const hrPlays = plays.filter((p) => ((p.result || {}).event || '') === 'Home Run'); + const homeHr = hrPlays.filter((p) => (p.about || {}).halfInning === 'bottom').length; + const awayHr = hrPlays.filter((p) => (p.about || {}).halfInning === 'top').length; + + const homeRuns = num((teams.home || {}).runs); + const awayRuns = num((teams.away || {}).runs); + + out.push({ + game_id: `${sport}:${g.gamePk}`, + sport, + season: num(g.season) || (dt.date ? Number(String(dt.date).slice(0, 4)) : null), + game_date: dt.date || (g.gameDate ? String(g.gameDate).slice(0, 10) : null), + venue_id: num((g.venue || {}).id), + venue_name: (g.venue || {}).name || null, + home_team: ((home.team || {}).name) || null, + away_team: ((away.team || {}).name) || null, + home_runs: homeRuns, + away_runs: awayRuns, + total_runs: homeRuns == null || awayRuns == null ? null : homeRuns + awayRuns, + home_hr: hrPlays.length ? homeHr : null, + away_hr: hrPlays.length ? awayHr : null, + total_hr: hrPlays.length ? hrPlays.length : null, + status, + }); + } + } + return out; +} + +const SCHEDULE_URL = (date) => + `https://statsapi.mlb.com/api/v1/schedule?sportId=1&startDate=${date}&endDate=${date}` + + '&gameType=R&hydrate=linescore,venue,scoringplays'; + +/** + * captureDate(date, opts) — ONE call per game DATE, upserted on game_id. + * Idempotent: re-running a date refreshes nothing that matters and never + * duplicates. + */ +async function captureDate(date, opts = {}) { + const sport = opts.sport || 'mlb'; + if (!date) return { ok: false, reason: 'no date' }; + + let payload; + try { + payload = opts.fetchJson + ? await opts.fetchJson(SCHEDULE_URL(date)) + : (await require('axios').get(SCHEDULE_URL(date), { timeout: 60_000 })).data; + } catch (err) { + return { ok: false, reason: `fetch failed: ${err.message}`, date }; + } + + const rows = rowsFromSchedule(payload, { sport }); + if (!rows.length) return { ok: true, date, games: 0, written: 0, reason: 'no final games' }; + + const sb = opts.sb || getClient(); + if (!sb) return { ok: false, reason: 'supabase not configured', games: rows.length, written: 0 }; + + const { error } = await sb.from('game_context').upsert(rows, { onConflict: 'game_id' }); + if (error) return { ok: false, reason: `upsert failed: ${error.message}`, games: rows.length, written: 0 }; + + return { + ok: true, date, games: rows.length, written: rows.length, + venues: new Set(rows.map((r) => r.venue_id)).size, + with_hr: rows.filter((r) => r.total_hr != null).length, + }; +} + +function getClient() { + try { return require('../utils/supabase').getSupabaseServiceClient(); } catch { return null; } +} + +/** + * accrualStatus(opts) — how far from a self-derived factor are we, per venue? + * Makes "later" measurable instead of a promise. + */ +async function accrualStatus(opts = {}) { + const sb = opts.sb || getClient(); + if (!sb) return { ok: false, reason: 'supabase not configured' }; + const { data, error } = await sb + .from('game_context') + .select('venue_id, venue_name') + .eq('sport', opts.sport || 'mlb') + .limit(20000); + if (error) return { ok: false, reason: error.message }; + + const byVenue = new Map(); + for (const r of data || []) { + if (r.venue_id == null) continue; + const v = byVenue.get(r.venue_id) || { venue: r.venue_name, games: 0 }; + v.games += 1; + byVenue.set(r.venue_id, v); + } + const venues = [...byVenue.entries()].map(([id, v]) => ({ + venue_id: id, venue: v.venue, games: v.games, + ready: v.games >= MIN_GAMES, + remaining: Math.max(0, MIN_GAMES - v.games), + })).sort((a, b) => b.games - a.games); + + return { + ok: true, + total_games: (data || []).length, + venues_tracked: venues.length, + venues_ready: venues.filter((v) => v.ready).length, + min_games_per_venue: MIN_GAMES, + seasons_needed_estimate: Math.round((MIN_GAMES / HOME_GAMES_PER_SEASON) * 10) / 10, + venues: venues.slice(0, 5), + }; +} + +module.exports = { + captureDate, + rowsFromSchedule, + accrualStatus, + SCHEDULE_URL, + HOME_GAMES_PER_SEASON, +}; diff --git a/src/services/parkBase.js b/src/services/parkBase.js new file mode 100644 index 0000000..f7d7c84 --- /dev/null +++ b/src/services/parkBase.js @@ -0,0 +1,143 @@ +'use strict'; + +/** + * PARK BASE — the SOURCE-PLUGGABLE environment coefficient (Layer 3, Step 4b). + * + * One slot, two possible sources, identical output shape: + * + * 'public' — the static FanGraphs table (src/data/parkFactors.js, S15). + * COMMODITY. A stable public number anyone can look up. + * 'derived' — our own multi-season derivation (src/services/parkFactors.js, + * S73). Not yet nominated: it needs the game-level accrual that + * `gameContext` starts collecting this session. + * + * The pluggability is the point. When the self-derived factors clear their + * sample floor and beat the public base on the instrument, they swap into this + * same slot — no rearchitecting downstream, because everything above consumes + * `resolveParkBase()` and never a source directly. + * + * ── THE HONESTY LINE ───────────────────────────────────────────────────── + * The public base is COMMODITY. It must never be marketed or described as a + * proprietary park factor — it is a number anyone can read off FanGraphs. The + * proprietary claim is reserved for the self-derived, mechanism-conditioned + * version, and only once it beats this base on the settled ledger. + * `PROVENANCE` below carries that label so any surface rendering a park effect + * can state which it is. + */ + +const publicTable = require('../data/parkFactors'); + +const PROVENANCE = Object.freeze({ + public: { + source: 'fangraphs_public', + label: 'PUBLIC PARK FACTOR', + proprietary: false, + note: 'Commodity: a public number. Not a VYNDR derivation.', + }, + derived: { + source: 'vyndr_derived', + label: 'VYNDR PARK FACTOR', + proprietary: true, + note: 'Self-derived from captured game context. Nominated only after it beats the public base.', + }, +}); + +/** + * Parks whose PUBLIC factor is itself thin or unstable — new builds, relocated + * clubs, temporary venues. A public number for a park with one season behind it + * is no more trustworthy than our own would be, so it is HONEST-ABSENT rather + * than emitted with false confidence. + * + * ATH: the Athletics' temporary Sacramento venue (2025–). SAC/LV as the club + * moves. Extend this list when a park opens or a club relocates — the cost of a + * missing entry is a confident factor for a stadium nobody has data on. + */ +const UNSTABLE_PUBLIC = Object.freeze(new Set(['ATH', 'OAK', 'SAC', 'LV'])); + +/** Domes + retractable roofs — reused from the derived service so the two + * sources agree on which venues weather cannot touch. */ +const { DOME_VENUES } = require('./parkFactors'); +const DOME_TEAMS = Object.freeze(new Set(['TB', 'TOR', 'ARI', 'HOU', 'MIL', 'TEX', 'MIA', 'SEA'])); + +const num = (v) => { + if (v == null || v === '') return null; + const n = typeof v === 'number' ? v : Number(v); + return Number.isFinite(n) ? n : null; +}; + +/** The public table is indexed to 100. The composable architecture wants a + * multiplier around 1.0, so 128 → 1.28. */ +const toMultiplier = (indexed) => { + const n = num(indexed); + return n == null || n <= 0 ? null : Math.round((n / 100) * 1000) / 1000; +}; + +/** + * resolveParkBase({ teamAbbr, venueName, source, derived }) — the ONE accessor. + * + * Returns the same record shape whichever source answers, so callers never + * branch on provenance: + * { state, hr_base, run_base, venue, weather_na, provenance, source } + * with `state` one of 'present' | 'absent' — and `weather_na` orthogonal to + * both, exactly as the S73 derivation defined it (a dome's park factor still + * applies; the flag only tells the weather arc to stand down). + */ +function resolveParkBase({ teamAbbr, venueName, source = 'public', derived = null } = {}) { + const abbr = String(teamAbbr || '').toUpperCase(); + const weatherNa = DOME_TEAMS.has(abbr) || (venueName ? DOME_VENUES.has(venueName) : false); + + const absent = (reason, prov) => ({ + state: 'absent', hr_base: null, run_base: null, + venue: venueName || abbr || null, weather_na: weatherNa, + provenance: prov, source: prov.source, reason, + }); + + // ── self-derived, when it has been nominated ────────────────────────── + if (source === 'derived') { + const prov = PROVENANCE.derived; + if (!derived || derived.state !== 'present') { + return absent('self-derived factor not available for this venue', prov); + } + return { + state: 'present', + hr_base: num(derived.hr_base), + run_base: num(derived.run_base), + venue: derived.venue || venueName || null, + weather_na: derived.weather_na ?? weatherNa, + provenance: prov, source: prov.source, + games: derived.games ?? null, seasons: derived.seasons ?? null, + regime_start: derived.regime_start ?? null, + reason: null, + }; + } + + // ── public (commodity) base ─────────────────────────────────────────── + const prov = PROVENANCE.public; + if (!abbr) return absent('no team/venue to resolve', prov); + if (UNSTABLE_PUBLIC.has(abbr)) { + // Even the PUBLIC number is thin here — a new or temporary park. Emitting + // it would dress a shaky number as a settled one. + return absent('public factor is itself thin/unstable for this venue', prov); + } + const row = publicTable.getParkFactor(abbr); + if (!row) return absent('no public factor for this team', prov); + + const hr = toMultiplier(row.hr); + const run = toMultiplier(row.r); + if (hr == null && run == null) return absent('public factor unparseable', prov); + + return { + state: 'present', + hr_base: hr, run_base: run, + venue: venueName || abbr, weather_na: weatherNa, + provenance: prov, source: prov.source, reason: null, + }; +} + +module.exports = { + resolveParkBase, + PROVENANCE, + UNSTABLE_PUBLIC, + DOME_TEAMS, + __internals: { toMultiplier, num }, +}; diff --git a/src/snapshotScheduler.js b/src/snapshotScheduler.js index 500aa36..817dd2c 100644 --- a/src/snapshotScheduler.js +++ b/src/snapshotScheduler.js @@ -286,6 +286,19 @@ function startSnapshotScheduler(opts = {}) { } } catch (e) { console.warn('[harness] nightly run failed:', e.message); } + // Session 74 — GAME-LEVEL CONTEXT. One schedule call per game DATE (not per + // game, not per prop) capturing venue + final runs + HR totals for the day + // that just completed. Forward-only: a day not captured is permanently + // lost, which is why this runs beside the settle pass rather than waiting + // until a self-derived park factor is wanted. + try { + const gc = opts.gameContext || require('./services/gameContext'); + const d = new Date(now().getTime() - 24 * 3600 * 1000); + const ymd = d.toISOString().slice(0, 10); + const r = await gc.captureDate(ymd, { sport: 'mlb' }); + if (r && r.games) console.log(`[gamectx] ${ymd} — ${r.written}/${r.games} games, ${r.venues} venues, ${r.with_hr} with HR totals`); + } catch (e) { console.warn('[gamectx] capture failed:', e.message); } + // Session 70 — THE MEASUREMENT INSTRUMENT. Attach the de-vigged CLOSING // probability to locked rows from the append-only closing_captures, so // p_win, the close, the archetype vector and the outcome all land on ONE diff --git a/tests/unit/parkBaseGameContext.test.js b/tests/unit/parkBaseGameContext.test.js new file mode 100644 index 0000000..739f084 --- /dev/null +++ b/tests/unit/parkBaseGameContext.test.js @@ -0,0 +1,178 @@ +/* ============================================================ + Session 74 — PUBLIC PARK BASE (source-pluggable) + GAME-CONTEXT CAPTURE. + ============================================================ */ + +const pb = require('../../src/services/parkBase'); +const pf = require('../../src/services/parkFactors'); +const gc = require('../../src/services/gameContext'); +const ch = require('../../src/services/challengerProjection'); + +describe('public base — commodity, and labelled as such', () => { + it('converts the 100-indexed public table to a multiplier', () => { + const col = pb.resolveParkBase({ teamAbbr: 'COL' }); + expect(col.state).toBe('present'); + expect(col.hr_base).toBe(1.28); // 128 → 1.28 + expect(col.run_base).toBe(1.2); + }); + + it('NEVER claims the public number is proprietary', () => { + const col = pb.resolveParkBase({ teamAbbr: 'COL' }); + expect(col.provenance.proprietary).toBe(false); + expect(col.provenance.label).toBe('PUBLIC PARK FACTOR'); + expect(col.provenance.note).toMatch(/Not a VYNDR derivation/); + }); + + it('a pitcher park reads below 1', () => { + expect(pb.resolveParkBase({ teamAbbr: 'SF' }).hr_base).toBeLessThan(1); + }); + + it('HONEST-ABSENT where even the PUBLIC factor is thin/unstable', () => { + // A relocated club in a temporary venue: a public number here is no more + // trustworthy than ours would be. + for (const t of ['ATH', 'SAC']) { + const r = pb.resolveParkBase({ teamAbbr: t }); + expect(r.state).toBe('absent'); + expect(r.hr_base).toBeNull(); + expect(r.reason).toMatch(/thin\/unstable/); + } + }); + + it('unknown team → absent, never a neutral guess', () => { + expect(pb.resolveParkBase({ teamAbbr: 'ZZZ' }).state).toBe('absent'); + expect(pb.resolveParkBase({}).state).toBe('absent'); + }); + + it('flags domes weather_na while the park factor STILL applies', () => { + const tb = pb.resolveParkBase({ teamAbbr: 'TB' }); + expect(tb.weather_na).toBe(true); + expect(tb.state).toBe('present'); // N/A is not absent + }); +}); + +describe('SOURCE-PLUGGABLE — same slot, no rearchitecting', () => { + const derived = { state: 'present', hr_base: 1.11, run_base: 1.05, venue: 'Coors Field', weather_na: false, games: 162, seasons: 4 }; + + it('self-derived swaps into the same shape and IS proprietary', () => { + const d = pb.resolveParkBase({ teamAbbr: 'COL', source: 'derived', derived }); + expect(d.state).toBe('present'); + expect(d.hr_base).toBe(1.11); + expect(d.provenance.proprietary).toBe(true); + expect(d.provenance.label).toBe('VYNDR PARK FACTOR'); + // identical keys to the public answer → callers never branch on source + const p = pb.resolveParkBase({ teamAbbr: 'COL' }); + expect(Object.keys(d)).toEqual(expect.arrayContaining(Object.keys(p))); + }); + + it('derived-but-unavailable is absent, never silently falls back', () => { + const r = pb.resolveParkBase({ teamAbbr: 'COL', source: 'derived', derived: null }); + expect(r.state).toBe('absent'); + expect(r.reason).toMatch(/self-derived factor not available/); + }); +}); + +describe('composes into the challenger, directional by prop-owner', () => { + const env = (b, stat) => ({ multiplier: pf.parkMultiplier({ factor: b, statType: stat }), label: b.provenance.label, venue: b.venue, weather_na: b.weather_na }); + const col = pb.resolveParkBase({ teamAbbr: 'COL' }); + + it('hitter HR and pitcher HR-allowed move the SAME way at the same park', () => { + const h = ch.adjust({ pWin: 0.5, direction: 'over', statType: 'home_runs', environment: env(col, 'home_runs') }); + const p = ch.adjust({ pWin: 0.5, direction: 'over', statType: 'home_runs_allowed', environment: env(col, 'home_runs_allowed') }); + expect(h.delta).toBeGreaterThan(0); + expect(p.delta).toBe(h.delta); + }); + + it('mirrors on the under and does nothing for an unrelated stat', () => { + const over = ch.adjust({ pWin: 0.5, direction: 'over', statType: 'home_runs', environment: env(col, 'home_runs') }); + const under = ch.adjust({ pWin: 0.5, direction: 'under', statType: 'home_runs', environment: env(col, 'home_runs') }); + expect(under.delta).toBeCloseTo(-over.delta, 3); + expect(ch.adjust({ pWin: 0.5, direction: 'over', statType: 'strikeouts', environment: env(col, 'strikeouts') }).delta).toBe(0); + }); + + it('an absent park adjusts nothing', () => { + const ath = pb.resolveParkBase({ teamAbbr: 'ATH' }); + expect(ch.adjust({ pWin: 0.5, direction: 'over', statType: 'home_runs', environment: env(ath, 'home_runs') }).delta).toBe(0); + }); +}); + +describe('game-context capture — game grain, named purpose, finals only', () => { + const payload = { + dates: [{ + date: '2025-07-19', + games: [ + { gamePk: 777094, season: '2025', status: { detailedState: 'Final' }, + venue: { id: 14, name: 'Rogers Centre' }, + teams: { home: { team: { name: 'Toronto Blue Jays' } }, away: { team: { name: 'New York Yankees' } } }, + linescore: { teams: { home: { runs: 5 }, away: { runs: 4 } } }, + scoringPlays: [ + { result: { event: 'Home Run' }, about: { halfInning: 'bottom' } }, + { result: { event: 'Home Run' }, about: { halfInning: 'top' } }, + { result: { event: 'Single' }, about: { halfInning: 'top' } }, + ] }, + { gamePk: 777095, season: '2025', status: { detailedState: 'In Progress' }, + venue: { id: 15, name: 'Other Park' }, linescore: { teams: { home: { runs: 2 }, away: { runs: 1 } } } }, + ], + }], + }; + + it('one row per game, keyed for dedupe', () => { + const rows = gc.rowsFromSchedule(payload); + expect(rows).toHaveLength(1); // the in-progress game is excluded + expect(rows[0].game_id).toBe('mlb:777094'); + expect(new Set(rows.map((r) => r.game_id)).size).toBe(rows.length); + }); + + it('EXCLUDES non-final games — in-progress totals are not results', () => { + expect(gc.rowsFromSchedule(payload).every((r) => r.status === 'Final')).toBe(true); + }); + + it('captures exactly the NAMED-PURPOSE fields', () => { + const r = gc.rowsFromSchedule(payload)[0]; + expect(r.venue_id).toBe(14); // → self-derived park factors + expect(r.total_runs).toBe(9); // → run-environment factors + expect(r.total_hr).toBe(2); // → HR park factors + expect(r.home_hr).toBe(1); + expect(r.away_hr).toBe(1); + // No speculative fields hoarded "just in case". + expect(Object.keys(r).sort()).toEqual([ + 'away_hr', 'away_runs', 'away_team', 'game_date', 'game_id', 'home_hr', + 'home_runs', 'home_team', 'season', 'sport', 'status', 'total_hr', + 'total_runs', 'venue_id', 'venue_name', + ]); + }); + + it('HR totals come from scoring plays — every HR scores, so none are missed', () => { + const r = gc.rowsFromSchedule(payload)[0]; + expect(r.total_hr).toBe(2); // the Single is not counted + }); + + it('a game with no scoring plays reports HR as ABSENT, not zero', () => { + const noPlays = { dates: [{ date: '2025-07-19', games: [{ gamePk: 1, status: { detailedState: 'Final' }, venue: { id: 1 }, linescore: { teams: { home: { runs: 0 }, away: { runs: 0 } } } }] }] }; + expect(gc.rowsFromSchedule(noPlays)[0].total_hr).toBeNull(); + }); + + it('upserts on game_id — capture is idempotent per date', async () => { + let opts = null; + const sb = { from: () => ({ upsert: async (_r, o) => { opts = o; return { error: null }; } }) }; + await gc.captureDate('2025-07-19', { sb, fetchJson: async () => payload }); + expect(opts.onConflict).toBe('game_id'); + }); + + it('one call per DATE — not per game, not per prop', () => { + expect(gc.SCHEDULE_URL('2025-07-19')).toMatch(/startDate=2025-07-19&endDate=2025-07-19/); + expect(gc.SCHEDULE_URL('2025-07-19')).toMatch(/hydrate=linescore,venue,scoringplays/); + }); +}); + +describe('accrual target — "later" is measurable', () => { + it('states the per-venue floor and the season estimate', async () => { + const sb = { from: () => ({ select: () => ({ eq: () => ({ limit: async () => ({ data: [ + ...Array(120).fill({ venue_id: 14, venue_name: 'Rogers Centre' }), + ...Array(160).fill({ venue_id: 19, venue_name: 'Coors Field' }), + ], error: null }) }) }) }) }; + const s = await gc.accrualStatus({ sb }); + expect(s.min_games_per_venue).toBe(pf.MIN_GAMES); + expect(s.venues_ready).toBe(1); // Coors has 160 ≥ 150 + expect(s.seasons_needed_estimate).toBeGreaterThan(1); // ~2 seasons + expect(s.venues.find((v) => v.venue_id === 14).remaining).toBe(30); + }); +}); diff --git a/web/public/sw.js b/web/public/sw.js index 781c659..e82bafa 100644 --- a/web/public/sw.js +++ b/web/public/sw.js @@ -1,2 +1,2 @@ (()=>{"use strict";let e,t,a,s,r,i={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"serwist",runtime:"runtime",suffix:"u">typeof registration?registration.scope:""},n=e=>[i.prefix,e,i.suffix].filter(e=>e&&e.length>0).join("-"),c=e=>e||n(i.precache),o=e=>e||n(i.runtime);var l=class extends Error{details;constructor(e,t){super(((e,...t)=>{let a=e;return t.length>0&&(a+=` :: ${JSON.stringify(t)}`),a})(e,t)),this.name=e,this.details=t}};function h(e){return new Promise(t=>setTimeout(t,e))}let u=new Set;function d(e,t){let a=new URL(e);for(let e of t)a.searchParams.delete(e);return a.href}async function f(e,t,a,s){let r=d(t.url,a);if(t.url===r)return e.match(t,s);let i={...s,ignoreSearch:!0};for(let n of(await e.keys(t,i)))if(r===d(n.url,a))return e.match(n,s)}var p=class{promise;resolve;reject;constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}};let m=async()=>{for(let e of u)await e()},w="-precache-",g=async(e,t=w)=>{let a=(await self.caches.keys()).filter(a=>a.includes(t)&&a.includes(self.registration.scope)&&a!==e);return await Promise.all(a.map(e=>self.caches.delete(e))),a},y=(e,t)=>{let a=t();return e.waitUntil(a),a},_=(e,t)=>t.some(t=>e instanceof t),b=new WeakMap,v=new WeakMap,R=new WeakMap,E={get(e,t,a){if(e instanceof IDBTransaction){if("done"===t)return b.get(e);if("store"===t)return a.objectStoreNames[1]?void 0:a.objectStore(a.objectStoreNames[0])}return q(e[t])},set:(e,t,a)=>(e[t]=a,!0),has:(e,t)=>e instanceof IDBTransaction&&("done"===t||"store"===t)||t in e};function q(e){if(e instanceof IDBRequest){let t;return t=new Promise((t,a)=>{let s=()=>{e.removeEventListener("success",r),e.removeEventListener("error",i)},r=()=>{t(q(e.result)),s()},i=()=>{a(e.error),s()};e.addEventListener("success",r),e.addEventListener("error",i)}),R.set(t,e),t}if(v.has(e))return v.get(e);let t=function(e){if("function"==typeof e)return(r||(r=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])).includes(e)?function(...t){return e.apply(x(this),t),q(this.request)}:function(...t){return q(e.apply(x(this),t))};return(e instanceof IDBTransaction&&function(e){if(b.has(e))return;let t=new Promise((t,a)=>{let s=()=>{e.removeEventListener("complete",r),e.removeEventListener("error",i),e.removeEventListener("abort",i)},r=()=>{t(),s()},i=()=>{a(e.error||new DOMException("AbortError","AbortError")),s()};e.addEventListener("complete",r),e.addEventListener("error",i),e.addEventListener("abort",i)});b.set(e,t)}(e),_(e,s||(s=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])))?new Proxy(e,E):e}(e);return t!==e&&(v.set(e,t),R.set(t,e)),t}let x=e=>R.get(e);function D(e,t,{blocked:a,upgrade:s,blocking:r,terminated:i}={}){let n=indexedDB.open(e,t),c=q(n);return s&&n.addEventListener("upgradeneeded",e=>{s(q(n.result),e.oldVersion,e.newVersion,q(n.transaction),e)}),a&&n.addEventListener("blocked",e=>a(e.oldVersion,e.newVersion,e)),c.then(e=>{i&&e.addEventListener("close",()=>i()),r&&e.addEventListener("versionchange",e=>r(e.oldVersion,e.newVersion,e))}).catch(()=>{}),c}let S=["get","getKey","getAll","getAllKeys","count"],k=["put","add","delete","clear"],T=new Map;function P(e,t){if(!(e instanceof IDBDatabase&&!(t in e)&&"string"==typeof t))return;if(T.get(t))return T.get(t);let a=t.replace(/FromIndex$/,""),s=t!==a,r=k.includes(a);if(!(a in(s?IDBIndex:IDBObjectStore).prototype)||!(r||S.includes(a)))return;let i=async function(e,...t){let i=this.transaction(e,r?"readwrite":"readonly"),n=i.store;return s&&(n=n.index(t.shift())),(await Promise.all([n[a](...t),r&&i.done]))[0]};return T.set(t,i),i}E={...e=E,get:(t,a,s)=>P(t,a)||e.get(t,a,s),has:(t,a)=>!!P(t,a)||e.has(t,a)};let C=["continue","continuePrimaryKey","advance"],N={},I=new WeakMap,U=new WeakMap,L={get(e,t){if(!C.includes(t))return e[t];let a=N[t];return a||(a=N[t]=function(...e){I.set(this,U.get(this)[t](...e))}),a}};async function*A(...e){let t=this;if(t instanceof IDBCursor||(t=await t.openCursor(...e)),!t)return;let a=new Proxy(t,L);for(U.set(a,t),R.set(a,x(t));t;)yield a,t=await (I.get(a)||t.continue()),I.delete(a)}function O(e,t){return t===Symbol.asyncIterator&&_(e,[IDBIndex,IDBObjectStore,IDBCursor])||"iterate"===t&&_(e,[IDBIndex,IDBObjectStore])}E={...t=E,get:(e,a,s)=>O(e,a)?A:t.get(e,a,s),has:(e,a)=>O(e,a)||t.has(e,a)};let M=async(e,t)=>{let s=null;if(e.url&&(s=new URL(e.url).origin),s!==self.location.origin)throw new l("cross-origin-copy-response",{origin:s});let r=e.clone(),i={headers:new Headers(r.headers),status:r.status,statusText:r.statusText},n=t?t(i):i,c=!function(){if(void 0===a){let e=new Response("");if("body"in e)try{new Response(e.body),a=!0}catch{a=!1}a=!1}return a}()?await r.blob():r.body;return new Response(c,n)},B="requests",K="queueName";var F=class{_db=null;async addEntry(e){let t=(await this.getDb()).transaction(B,"readwrite",{durability:"relaxed"});await t.store.add(e),await t.done}async getFirstEntryId(){return(await (await this.getDb()).transaction(B).store.openCursor())?.value.id}async getAllEntriesByQueueName(e){return await (await this.getDb()).getAllFromIndex(B,K,IDBKeyRange.only(e))||[]}async getEntryCountByQueueName(e){return(await this.getDb()).countFromIndex(B,K,IDBKeyRange.only(e))}async deleteEntry(e){await (await this.getDb()).delete(B,e)}async getFirstEntryByQueueName(e){return await this.getEndEntryFromIndex(IDBKeyRange.only(e),"next")}async getLastEntryByQueueName(e){return await this.getEndEntryFromIndex(IDBKeyRange.only(e),"prev")}async getEndEntryFromIndex(e,t){return(await (await this.getDb()).transaction(B).store.index(K).openCursor(e,t))?.value}async getDb(){return this._db||(this._db=await D("serwist-background-sync",3,{upgrade:this._upgradeDb})),this._db}_upgradeDb(e,t){t>0&&t<3&&e.objectStoreNames.contains(B)&&e.deleteObjectStore(B),e.createObjectStore(B,{autoIncrement:!0,keyPath:"id"}).createIndex(K,K,{unique:!1})}},W=class{_queueName;_queueDb;constructor(e){this._queueName=e,this._queueDb=new F}async pushEntry(e){delete e.id,e.queueName=this._queueName,await this._queueDb.addEntry(e)}async unshiftEntry(e){let t=await this._queueDb.getFirstEntryId();t?e.id=t-1:delete e.id,e.queueName=this._queueName,await this._queueDb.addEntry(e)}async popEntry(){return this._removeEntry(await this._queueDb.getLastEntryByQueueName(this._queueName))}async shiftEntry(){return this._removeEntry(await this._queueDb.getFirstEntryByQueueName(this._queueName))}async getAll(){return await this._queueDb.getAllEntriesByQueueName(this._queueName)}async size(){return await this._queueDb.getEntryCountByQueueName(this._queueName)}async deleteEntry(e){await this._queueDb.deleteEntry(e)}async _removeEntry(e){return e&&await this.deleteEntry(e.id),e}};let j=["method","referrer","referrerPolicy","mode","credentials","cache","redirect","integrity","keepalive"];var H=class e{_requestData;static async fromRequest(t){let a={url:t.url,headers:{}};for(let e of("GET"!==t.method&&(a.body=await t.clone().arrayBuffer()),t.headers.forEach((e,t)=>{a.headers[t]=e}),j))void 0!==t[e]&&(a[e]=t[e]);return new e(a)}constructor(e){"navigate"===e.mode&&(e.mode="same-origin"),this._requestData=e}toObject(){let e=Object.assign({},this._requestData);return e.headers=Object.assign({},this._requestData.headers),e.body&&(e.body=e.body.slice(0)),e}toRequest(){return new Request(this._requestData.url,this._requestData)}clone(){return new e(this.toObject())}};let $="serwist-background-sync",V=new Set,Q=e=>{let t={request:new H(e.requestData).toRequest(),timestamp:e.timestamp};return e.metadata&&(t.metadata=e.metadata),t};var G=class{_name;_onSync;_maxRetentionTime;_queueStore;_forceSyncFallback;_syncInProgress=!1;_requestsAddedDuringSync=!1;constructor(e,{forceSyncFallback:t,onSync:a,maxRetentionTime:s}={}){if(V.has(e))throw new l("duplicate-queue-name",{name:e});V.add(e),this._name=e,this._onSync=a||this.replayRequests,this._maxRetentionTime=s||10080,this._forceSyncFallback=!!t,this._queueStore=new W(this._name),this._addSyncListener()}get name(){return this._name}async pushRequest(e){await this._addRequest(e,"push")}async unshiftRequest(e){await this._addRequest(e,"unshift")}async popRequest(){return this._removeRequest("pop")}async shiftRequest(){return this._removeRequest("shift")}async getAll(){let e=await this._queueStore.getAll(),t=Date.now(),a=[];for(let s of e){let e=60*this._maxRetentionTime*1e3;t-s.timestamp>e?await this._queueStore.deleteEntry(s.id):a.push(Q(s))}return a}async size(){return await this._queueStore.size()}async _addRequest({request:e,metadata:t,timestamp:a=Date.now()},s){let r={requestData:(await H.fromRequest(e.clone())).toObject(),timestamp:a};switch(t&&(r.metadata=t),s){case"push":await this._queueStore.pushEntry(r);break;case"unshift":await this._queueStore.unshiftEntry(r)}this._syncInProgress?this._requestsAddedDuringSync=!0:await this.registerSync()}async _removeRequest(e){let t,a=Date.now();switch(e){case"pop":t=await this._queueStore.popEntry();break;case"shift":t=await this._queueStore.shiftEntry()}if(t){let s=60*this._maxRetentionTime*1e3;return a-t.timestamp>s?this._removeRequest(e):Q(t)}}async replayRequests(){let e;for(;e=await this.shiftRequest();)try{await fetch(e.request.clone())}catch{throw await this.unshiftRequest(e),new l("queue-replay-failed",{name:this._name})}}async registerSync(){if("sync"in self.registration&&!this._forceSyncFallback)try{await self.registration.sync.register(`${$}:${this._name}`)}catch(e){}}_addSyncListener(){"sync"in self.registration&&!this._forceSyncFallback?self.addEventListener("sync",e=>{if(e.tag===`${$}:${this._name}`){let t=async()=>{let t;this._syncInProgress=!0;try{await this._onSync({queue:this})}catch(e){if(e instanceof Error)throw e}finally{this._requestsAddedDuringSync&&!(t&&!e.lastChance)&&await this.registerSync(),this._syncInProgress=!1,this._requestsAddedDuringSync=!1}};e.waitUntil(t())}}):this._onSync({queue:this})}static get _queueNames(){return V}},z=class{_queue;constructor(e,t){this._queue=new G(e,t)}async fetchDidFail({request:e}){await this._queue.pushRequest({request:e})}};let Y={cacheWillUpdate:async({response:e})=>200===e.status||0===e.status?e:null};function J(e){return"string"==typeof e?new Request(e):e}var X=class{event;request;url;params;_cacheKeys={};_strategy;_handlerDeferred;_extendLifetimePromises;_plugins;_pluginStateMap;constructor(e,t){for(const a of(this.event=t.event,this.request=t.request,t.url&&(this.url=t.url,this.params=t.params),this._strategy=e,this._handlerDeferred=new p,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map,this._plugins))this._pluginStateMap.set(a,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:t}=this,a=J(e),s=await this.getPreloadResponse();if(s)return s;let r=this.hasCallback("fetchDidFail")?a.clone():null;try{for(let e of this.iterateCallbacks("requestWillFetch"))a=await e({request:a.clone(),event:t})}catch(e){if(e instanceof Error)throw new l("plugin-error-request-will-fetch",{thrownErrorMessage:e.message})}let i=a.clone();try{let e;for(let s of(e=await fetch(a,"navigate"===a.mode?void 0:this._strategy.fetchOptions),this.iterateCallbacks("fetchDidSucceed")))e=await s({event:t,request:i,response:e});return e}catch(e){throw r&&await this.runCallbacks("fetchDidFail",{error:e,event:t,originalRequest:r.clone(),request:i.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),a=t.clone();return this.waitUntil(this.cachePut(e,a)),t}async cacheMatch(e){let t,a=J(e),{cacheName:s,matchOptions:r}=this._strategy,i=await this.getCacheKey(a,"read"),n={...r,cacheName:s};for(let e of(t=await caches.match(i,n),this.iterateCallbacks("cachedResponseWillBeUsed")))t=await e({cacheName:s,matchOptions:r,cachedResponse:t,request:i,event:this.event})||void 0;return t}async cachePut(e,t){let a=J(e);await h(0);let s=await this.getCacheKey(a,"write");if(!t)throw new l("cache-put-with-no-response",{url:new URL(String(s.url),location.href).href.replace(RegExp(`^${location.origin}`),"")});let r=await this._ensureResponseSafeToCache(t);if(!r)return!1;let{cacheName:i,matchOptions:n}=this._strategy,c=await self.caches.open(i),o=this.hasCallback("cacheDidUpdate"),u=o?await f(c,s.clone(),["__WB_REVISION__"],n):null;try{await c.put(s,o?r.clone():r)}catch(e){if(e instanceof Error)throw"QuotaExceededError"===e.name&&await m(),e}for(let e of this.iterateCallbacks("cacheDidUpdate"))await e({cacheName:i,oldResponse:u,newResponse:r.clone(),request:s,event:this.event});return!0}async getCacheKey(e,t){let a=`${e.url} | ${t}`;if(!this._cacheKeys[a]){let s=e;for(let e of this.iterateCallbacks("cacheKeyWillBeUsed"))s=J(await e({mode:t,request:s,event:this.event,params:this.params}));this._cacheKeys[a]=s}return this._cacheKeys[a]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let a of this.iterateCallbacks(e))await a(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if("function"==typeof t[e]){let a=this._pluginStateMap.get(t),s=s=>{let r={...s,state:a};return t[e](r)};yield s}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){let e;for(;e=this._extendLifetimePromises.shift();)await e}destroy(){this._handlerDeferred.resolve(null)}async getPreloadResponse(){if(this.event instanceof FetchEvent&&"navigate"===this.event.request.mode&&"preloadResponse"in this.event)try{let e=await this.event.preloadResponse;if(e)return e}catch(e){return}}async _ensureResponseSafeToCache(e){let t=e,a=!1;for(let e of this.iterateCallbacks("cacheWillUpdate"))if(t=await e({request:this.request,response:t,event:this.event})||void 0,a=!0,!t)break;return!a&&t&&200!==t.status&&(t=void 0),t}},Z=class{cacheName;plugins;fetchOptions;matchOptions;constructor(e={}){this.cacheName=o(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,a="string"==typeof e.request?new Request(e.request):e.request,s=new X(this,e.url?{event:t,request:a,url:e.url,params:e.params}:{event:t,request:a}),r=this._getResponse(s,a,t);return[r,this._awaitComplete(r,s,a,t)]}async _getResponse(e,t,a){let s;await e.runCallbacks("handlerWillStart",{event:a,request:t});try{if(s=await this._handle(t,e),void 0===s||"error"===s.type)throw new l("no-response",{url:t.url})}catch(r){if(r instanceof Error){for(let i of e.iterateCallbacks("handlerDidError"))if(void 0!==(s=await i({error:r,event:a,request:t})))break}if(!s)throw r}for(let r of e.iterateCallbacks("handlerWillRespond"))s=await r({event:a,request:t,response:s});return s}async _awaitComplete(e,t,a,s){let r,i;try{r=await e}catch{}try{await t.runCallbacks("handlerDidRespond",{event:s,request:a,response:r}),await t.doneWaiting()}catch(e){e instanceof Error&&(i=e)}if(await t.runCallbacks("handlerDidComplete",{event:s,request:a,response:r,error:i}),t.destroy(),i)throw i}},ee=class extends Z{_networkTimeoutSeconds;constructor(e={}){super(e),this.plugins.some(e=>"cacheWillUpdate"in e)||this.plugins.unshift(Y),this._networkTimeoutSeconds=e.networkTimeoutSeconds||0}async _handle(e,t){let a,s=[],r=[];if(this._networkTimeoutSeconds){let{id:i,promise:n}=this._getTimeoutPromise({request:e,logs:s,handler:t});a=i,r.push(n)}let i=this._getNetworkPromise({timeoutId:a,request:e,logs:s,handler:t});r.push(i);let n=await t.waitUntil((async()=>await t.waitUntil(Promise.race(r))||await i)());if(!n)throw new l("no-response",{url:e.url});return n}_getTimeoutPromise({request:e,logs:t,handler:a}){let s;return{promise:new Promise(t=>{s=setTimeout(async()=>{t(await a.cacheMatch(e))},1e3*this._networkTimeoutSeconds)}),id:s}}async _getNetworkPromise({timeoutId:e,request:t,logs:a,handler:s}){let r,i;try{i=await s.fetchAndCachePut(t)}catch(e){e instanceof Error&&(r=e)}return e&&clearTimeout(e),(r||!i)&&(i=await s.cacheMatch(t)),i}},et=class extends Z{_networkTimeoutSeconds;constructor(e={}){super(e),this._networkTimeoutSeconds=e.networkTimeoutSeconds||0}async _handle(e,t){let a,s;try{let a=[t.fetch(e)];if(this._networkTimeoutSeconds){let e=h(1e3*this._networkTimeoutSeconds);a.push(e)}if(!(s=await Promise.race(a)))throw Error(`Timed out the network response after ${this._networkTimeoutSeconds} seconds.`)}catch(e){e instanceof Error&&(a=e)}if(!s)throw new l("no-response",{url:e.url,error:a});return s}};let ea=e=>e&&"object"==typeof e?e:{handle:e};var es=class{handler;match;method;catchHandler;constructor(e,t,a="GET"){this.handler=ea(t),this.match=e,this.method=a}setCatchHandler(e){this.catchHandler=ea(e)}},er=class e extends Z{_fallbackToNetwork;static defaultPrecacheCacheabilityPlugin={cacheWillUpdate:async({response:e})=>!e||e.status>=400?null:e};static copyRedirectedCacheableResponsesPlugin={cacheWillUpdate:async({response:e})=>e.redirected?await M(e):e};constructor(t={}){t.cacheName=c(t.cacheName),super(t),this._fallbackToNetwork=!1!==t.fallbackToNetwork,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){let a=await t.getPreloadResponse();if(a)return a;let s=await t.cacheMatch(e);return s||(t.event&&"install"===t.event.type?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,t){let a,s=t.params||{};if(this._fallbackToNetwork){let r=s.integrity,i=e.integrity,n=!i||i===r;a=await t.fetch(new Request(e,{integrity:"no-cors"!==e.mode?i||r:void 0})),r&&n&&"no-cors"!==e.mode&&(this._useDefaultCacheabilityPluginIfNeeded(),await t.cachePut(e,a.clone()))}else throw new l("missing-precache-entry",{cacheName:this.cacheName,url:e.url});return a}async _handleInstall(e,t){this._useDefaultCacheabilityPluginIfNeeded();let a=await t.fetch(e);if(!await t.cachePut(e,a.clone()))throw new l("bad-precaching-response",{url:e.url,status:a.status});return a}_useDefaultCacheabilityPluginIfNeeded(){let t=null,a=0;for(let[s,r]of this.plugins.entries())r!==e.copyRedirectedCacheableResponsesPlugin&&(r===e.defaultPrecacheCacheabilityPlugin&&(t=s),r.cacheWillUpdate&&a++);0===a?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):a>1&&null!==t&&this.plugins.splice(t,1)}},ei=class extends es{_allowlist;_denylist;constructor(e,{allowlist:t=[/./],denylist:a=[]}={}){super(e=>this._match(e),e),this._allowlist=t,this._denylist=a}_match({url:e,request:t}){if(t&&"navigate"!==t.mode)return!1;let a=e.pathname+e.search;for(let e of this._denylist)if(e.test(a))return!1;return!!this._allowlist.some(e=>e.test(a))}},en=class extends es{constructor(e,t,a){super(({url:t})=>{let a=e.exec(t.href);if(a)return t.origin!==location.origin&&0!==a.index?void 0:a.slice(1)},t,a)}};let ec=e=>{if(!e)throw new l("add-to-cache-list-unexpected-type",{entry:e});if("string"==typeof e){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:t,url:a}=e;if(!a)throw new l("add-to-cache-list-unexpected-type",{entry:e});if(!t){let e=new URL(a,location.href);return{cacheKey:e.href,url:e.href}}let s=new URL(a,location.href),r=new URL(a,location.href);return s.searchParams.set("__WB_REVISION__",t),{cacheKey:s.href,url:r.href}};var eo=class{updatedURLs=[];notUpdatedURLs=[];handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)};cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:a})=>{if("install"===e.type&&t?.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;a?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return a}};let el=async(e,t,a)=>{let s=t.map((e,t)=>({index:t,item:e})),r=async e=>{let t=[];for(;;){let r=s.pop();if(!r)return e(t);let i=await a(r.item);t.push({result:i,index:r.index})}},i=Array.from({length:e},()=>new Promise(r));return(await Promise.all(i)).flat().sort((e,t)=>e.indexe.result)};"u">typeof navigator&&/^((?!chrome|android).)*safari/i.test(navigator.userAgent);let eh="cache-entries",eu=e=>{let t=new URL(e,location.href);return t.hash="",t.href};var ed=class{_cacheName;_db=null;constructor(e){this._cacheName=e}_getId(e){return`${this._cacheName}|${eu(e)}`}_upgradeDb(e){let t=e.createObjectStore(eh,{keyPath:"id"});t.createIndex("cacheName","cacheName",{unique:!1}),t.createIndex("timestamp","timestamp",{unique:!1})}_upgradeDbAndDeleteOldDbs(e){this._upgradeDb(e),this._cacheName&&function(e,{blocked:t}={}){let a=indexedDB.deleteDatabase(e);t&&a.addEventListener("blocked",e=>t(e.oldVersion,e)),q(a).then(()=>void 0)}(this._cacheName)}async setTimestamp(e,t){e=eu(e);let a={id:this._getId(e),cacheName:this._cacheName,url:e,timestamp:t},s=(await this.getDb()).transaction(eh,"readwrite",{durability:"relaxed"});await s.store.put(a),await s.done}async getTimestamp(e){return(await (await this.getDb()).get(eh,this._getId(e)))?.timestamp}async expireEntries(e,t){let a=await (await this.getDb()).transaction(eh,"readwrite").store.index("timestamp").openCursor(null,"prev"),s=[],r=0;for(;a;){let i=a.value;i.cacheName===this._cacheName&&(e&&i.timestamp=t?(a.delete(),s.push(i.url)):r++),a=await a.continue()}return s}async getDb(){return this._db||(this._db=await D("serwist-expiration",1,{upgrade:this._upgradeDbAndDeleteOldDbs.bind(this)})),this._db}},ef=class{_isRunning=!1;_rerunRequested=!1;_maxEntries;_maxAgeSeconds;_matchOptions;_cacheName;_timestampModel;constructor(e,t={}){this._maxEntries=t.maxEntries,this._maxAgeSeconds=t.maxAgeSeconds,this._matchOptions=t.matchOptions,this._cacheName=e,this._timestampModel=new ed(e)}async expireEntries(){if(this._isRunning){this._rerunRequested=!0;return}this._isRunning=!0;let e=this._maxAgeSeconds?Date.now()-1e3*this._maxAgeSeconds:0,t=await this._timestampModel.expireEntries(e,this._maxEntries),a=await self.caches.open(this._cacheName);for(let e of t)await a.delete(e,this._matchOptions);this._isRunning=!1,this._rerunRequested&&(this._rerunRequested=!1,this.expireEntries())}async updateTimestamp(e){await this._timestampModel.setTimestamp(e,Date.now())}async isURLExpired(e){if(!this._maxAgeSeconds)return!1;let t=await this._timestampModel.getTimestamp(e),a=Date.now()-1e3*this._maxAgeSeconds;return void 0===t||t{u.add(e)})(()=>this.deleteCacheAndMetadata())}_getCacheExpiration(e){if(e===o())throw new l("expire-custom-caches-only");let t=this._cacheExpirations.get(e);return t||(t=new ef(e,this._config),this._cacheExpirations.set(e,t)),t}cachedResponseWillBeUsed({event:e,cacheName:t,request:a,cachedResponse:s}){if(!s)return null;let r=this._isResponseDateFresh(s),i=this._getCacheExpiration(t),n="last-used"===this._config.maxAgeFrom,c=(async()=>{n&&await i.updateTimestamp(a.url),await i.expireEntries()})();try{e.waitUntil(c)}catch{}return r?s:null}_isResponseDateFresh(e){if("last-used"===this._config.maxAgeFrom)return!0;let t=Date.now();if(!this._config.maxAgeSeconds)return!0;let a=this._getDateHeaderTimestamp(e);return null===a||a>=t-1e3*this._config.maxAgeSeconds}_getDateHeaderTimestamp(e){if(!e.headers.has("date"))return null;let t=new Date(e.headers.get("date")).getTime();return Number.isNaN(t)?null:t}async cacheDidUpdate({cacheName:e,request:t}){let a=this._getCacheExpiration(e);await a.updateTimestamp(t.url),await a.expireEntries()}async deleteCacheAndMetadata(){for(let[e,t]of this._cacheExpirations)await self.caches.delete(e),await t.delete();this._cacheExpirations=new Map}};let em=/^\/(\w+\/)?collect/,ew=({serwist:e,cacheName:t,...a})=>{let s,r,c=t||n(i.googleAnalytics),o=new z("serwist-google-analytics",{maxRetentionTime:2880,onSync:async({queue:e})=>{let t;for(;t=await e.shiftRequest();){let{request:s,timestamp:r}=t,i=new URL(s.url);try{let e="POST"===s.method?new URLSearchParams(await s.clone().text()):i.searchParams,t=r-(Number(e.get("qt"))||0),n=Date.now()-t;if(e.set("qt",String(n)),a.parameterOverrides)for(let t of Object.keys(a.parameterOverrides)){let s=a.parameterOverrides[t];e.set(t,s)}"function"==typeof a.hitFilter&&a.hitFilter.call(null,e),await fetch(new Request(i.origin+i.pathname,{body:e.toString(),method:"POST",mode:"cors",credentials:"omit",headers:{"Content-Type":"text/plain"}}))}catch(a){throw await e.unshiftRequest(t),a}}}});for(let t of[new es(({url:e})=>"www.googletagmanager.com"===e.hostname&&"/gtm.js"===e.pathname,new ee({cacheName:c}),"GET"),new es(({url:e})=>"www.google-analytics.com"===e.hostname&&"/analytics.js"===e.pathname,new ee({cacheName:c}),"GET"),new es(({url:e})=>"www.googletagmanager.com"===e.hostname&&"/gtag/js"===e.pathname,new ee({cacheName:c}),"GET"),new es(s=({url:e})=>"www.google-analytics.com"===e.hostname&&em.test(e.pathname),r=new et({plugins:[o]}),"GET"),new es(s,r,"POST")])e.registerRoute(t)};var eg=class{_fallbackUrls;_serwist;constructor({fallbackUrls:e,serwist:t}){this._fallbackUrls=e,this._serwist=t}async handlerDidError(e){for(let t of this._fallbackUrls)if("string"==typeof t){let e=await this._serwist.matchPrecache(t);if(void 0!==e)return e}else if(t.matcher(e)){let e=await this._serwist.matchPrecache(t.url);if(void 0!==e)return e}}},ey=class extends Z{async _handle(e,t){let a,s=await t.cacheMatch(e);if(s);else try{s=await t.fetchAndCachePut(e)}catch(e){e instanceof Error&&(a=e)}if(!s)throw new l("no-response",{url:e.url,error:a});return s}},e_=class extends es{constructor(e,t){super(({request:a})=>{let s=e.getUrlsToPrecacheKeys();for(let r of function*(e,{directoryIndex:t="index.html",ignoreURLParametersMatching:a=[/^utm_/,/^fbclid$/],cleanURLs:s=!0,urlManipulation:r}={}){let i=new URL(e,location.href);i.hash="",yield i.href;let n=((e,t=[])=>{for(let a of[...e.searchParams.keys()])t.some(e=>e.test(a))&&e.searchParams.delete(a);return e})(i,a);if(yield n.href,t&&n.pathname.endsWith("/")){let e=new URL(n.href);e.pathname+=t,yield e.href}if(s){let e=new URL(n.href);e.pathname+=".html",yield e.href}if(r)for(let e of r({url:i}))yield e.href}(a.url,t)){let t=s.get(r);if(t)return{cacheKey:t,integrity:e.getIntegrityForPrecacheKey(t)}}},e.precacheStrategy)}},eb=class{_precacheController;constructor({precacheController:e}){this._precacheController=e}cacheKeyWillBeUsed=async({request:e,params:t})=>{let a=t?.cacheKey||this._precacheController.getPrecacheKeyForUrl(e.url);return a?new Request(a,{headers:e.headers}):e}},ev=class{_urlsToCacheKeys=new Map;_urlsToCacheModes=new Map;_cacheKeysToIntegrities=new Map;_concurrentPrecaching;_precacheStrategy;_routes;_defaultHandlerMap;_catchHandler;_requestRules;constructor({precacheEntries:e,precacheOptions:t,skipWaiting:a=!1,importScripts:s,navigationPreload:r=!1,cacheId:n,clientsClaim:o=!1,runtimeCaching:l,offlineAnalyticsConfig:h,disableDevLogs:u=!1,fallbacks:d,requestRules:f}={}){const{precacheStrategyOptions:p,precacheRouteOptions:m,precacheMiscOptions:w}=((e,t={})=>{let{cacheName:a,plugins:s=[],fetchOptions:r,matchOptions:i,fallbackToNetwork:n,directoryIndex:o,ignoreURLParametersMatching:l,cleanURLs:h,urlManipulation:u,cleanupOutdatedCaches:d,concurrency:f=10,navigateFallback:p,navigateFallbackAllowlist:m,navigateFallbackDenylist:w}=t??{};return{precacheStrategyOptions:{cacheName:c(a),plugins:[...s,new eb({precacheController:e})],fetchOptions:r,matchOptions:i,fallbackToNetwork:n},precacheRouteOptions:{directoryIndex:o,ignoreURLParametersMatching:l,cleanURLs:h,urlManipulation:u},precacheMiscOptions:{cleanupOutdatedCaches:d,concurrency:f,navigateFallback:p,navigateFallbackAllowlist:m,navigateFallbackDenylist:w}}})(this,t);if(this._concurrentPrecaching=w.concurrency,this._precacheStrategy=new er(p),this._routes=new Map,this._defaultHandlerMap=new Map,this._requestRules=f,this.handleInstall=this.handleInstall.bind(this),this.handleActivate=this.handleActivate.bind(this),this.handleFetch=this.handleFetch.bind(this),this.handleCache=this.handleCache.bind(this),s&&s.length>0&&self.importScripts(...s),r&&self.registration?.navigationPreload&&self.addEventListener("activate",e=>{e.waitUntil(self.registration.navigationPreload.enable().then(()=>{}))}),void 0!==n&&(e=>{var t=e;for(let e of Object.keys(i))(e=>{let a=t[e];"string"==typeof a&&(i[e]=a)})(e)})({prefix:n}),a?self.skipWaiting():self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),o&&self.addEventListener("activate",()=>self.clients.claim()),e&&e.length>0&&this.addToPrecacheList(e),w.cleanupOutdatedCaches&&(e=>{self.addEventListener("activate",t=>{t.waitUntil(g(c(e)).then(e=>{}))})})(p.cacheName),this.registerRoute(new e_(this,m)),w.navigateFallback&&this.registerRoute(new ei(this.createHandlerBoundToUrl(w.navigateFallback),{allowlist:w.navigateFallbackAllowlist,denylist:w.navigateFallbackDenylist})),void 0!==h&&("boolean"==typeof h?h&&ew({serwist:this}):ew({...h,serwist:this})),void 0!==l){if(void 0!==d){const e=new eg({fallbackUrls:d.entries,serwist:this});l.forEach(t=>{t.handler instanceof Z&&!t.handler.plugins.some(e=>"handlerDidError"in e)&&t.handler.plugins.push(e)})}for(const e of l)this.registerCapture(e.matcher,e.handler,e.method)}u&&(self.__WB_DISABLE_DEV_LOGS=!0)}get precacheStrategy(){return this._precacheStrategy}get routes(){return this._routes}addEventListeners(){self.addEventListener("install",this.handleInstall),self.addEventListener("activate",this.handleActivate),self.addEventListener("fetch",this.handleFetch),self.addEventListener("message",this.handleCache)}addToPrecacheList(e){let t=[];for(let a of e){"string"==typeof a?t.push(a):a&&!a.integrity&&void 0===a.revision&&t.push(a.url);let{cacheKey:e,url:s}=ec(a),r="string"!=typeof a&&a.revision?"reload":"default";if(this._urlsToCacheKeys.has(s)&&this._urlsToCacheKeys.get(s)!==e)throw new l("add-to-cache-list-conflicting-entries",{firstEntry:this._urlsToCacheKeys.get(s),secondEntry:e});if("string"!=typeof a&&a.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==a.integrity)throw new l("add-to-cache-list-conflicting-integrities",{url:s});this._cacheKeysToIntegrities.set(e,a.integrity)}this._urlsToCacheKeys.set(s,e),this._urlsToCacheModes.set(s,r)}t.length>0&&console.warn(`Serwist is precaching URLs without revision info: ${t.join(", ")} -This is generally NOT safe. Learn more at https://bit.ly/wb-precache`)}handleInstall(e){return this.registerRequestRules(e),y(e,async()=>{let t=new eo;this.precacheStrategy.plugins.push(t),await el(this._concurrentPrecaching,Array.from(this._urlsToCacheKeys.entries()),async([t,a])=>{let s=this._cacheKeysToIntegrities.get(a),r=this._urlsToCacheModes.get(t),i=new Request(t,{integrity:s,cache:r,credentials:"same-origin"});await Promise.all(this.precacheStrategy.handleAll({event:e,request:i,url:new URL(i.url),params:{cacheKey:a}}))});let{updatedURLs:a,notUpdatedURLs:s}=t;return{updatedURLs:a,notUpdatedURLs:s}})}async registerRequestRules(e){if(this._requestRules&&e?.addRoutes)try{await e.addRoutes(this._requestRules),this._requestRules=void 0}catch(e){throw e}}handleActivate(e){return y(e,async()=>{let e=await self.caches.open(this.precacheStrategy.cacheName),t=await e.keys(),a=new Set(this._urlsToCacheKeys.values()),s=[];for(let r of t)a.has(r.url)||(await e.delete(r),s.push(r.url));return{deletedCacheRequests:s}})}handleFetch(e){let{request:t}=e,a=this.handleRequest({request:t,event:e});a&&e.respondWith(a)}handleCache(e){if(e.data&&"CACHE_URLS"===e.data.type){let{payload:t}=e.data,a=Promise.all(t.urlsToCache.map(t=>{let a;return a="string"==typeof t?new Request(t):new Request(...t),this.handleRequest({request:a,event:e})}));e.waitUntil(a),e.ports?.[0]&&a.then(()=>e.ports[0].postMessage(!0))}}setDefaultHandler(e,t="GET"){this._defaultHandlerMap.set(t,ea(e))}setCatchHandler(e){this._catchHandler=ea(e)}registerCapture(e,t,a){let s=((e,t,a)=>{if("string"==typeof e){let s=new URL(e,location.href);return new es(({url:e})=>e.href===s.href,t,a)}if(e instanceof RegExp)return new en(e,t,a);if("function"==typeof e)return new es(e,t,a);if(e instanceof es)return e;throw new l("unsupported-route-type",{moduleName:"serwist",funcName:"parseRoute",paramName:"capture"})})(e,t,a);return this.registerRoute(s),s}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new l("unregister-route-but-not-found-with-method",{method:e.method});let t=this._routes.get(e.method).indexOf(e);if(t>-1)this._routes.get(e.method).splice(t,1);else throw new l("unregister-route-route-not-registered")}getUrlsToPrecacheKeys(){return this._urlsToCacheKeys}getPrecachedUrls(){return[...this._urlsToCacheKeys.keys()]}getPrecacheKeyForUrl(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForPrecacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,a=this.getPrecacheKeyForUrl(t);if(a)return(await self.caches.open(this.precacheStrategy.cacheName)).match(a)}createHandlerBoundToUrl(e){let t=this.getPrecacheKeyForUrl(e);if(!t)throw new l("non-precached-url",{url:e});return a=>(a.request=new Request(e),a.params={cacheKey:t,...a.params},this.precacheStrategy.handle(a))}handleRequest({request:e,event:t}){let a,s=new URL(e.url,location.href);if(!s.protocol.startsWith("http"))return;let r=s.origin===location.origin,{params:i,route:n}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:s}),c=n?.handler,o=e.method;if(!c&&this._defaultHandlerMap.has(o)&&(c=this._defaultHandlerMap.get(o)),!c)return;try{a=c.handle({url:s,request:e,event:t,params:i})}catch(e){a=Promise.reject(e)}let l=n?.catchHandler;return a instanceof Promise&&(this._catchHandler||l)&&(a=a.catch(async a=>{if(l)try{return await l.handle({url:s,request:e,event:t,params:i})}catch(e){e instanceof Error&&(a=e)}if(this._catchHandler)return this._catchHandler.handle({url:s,request:e,event:t});throw a})),a}findMatchingRoute({url:e,sameOrigin:t,request:a,event:s}){for(let r of this._routes.get(a.method)||[]){let i,n=r.match({url:e,sameOrigin:t,request:a,event:s});if(n)return Array.isArray(i=n)&&0===i.length||n.constructor===Object&&0===Object.keys(n).length?i=void 0:"boolean"==typeof n&&(i=void 0),{route:r,params:i}}return{}}};let eR="/offline",eE=["pages","api-responses","next-static","static-media","fallback","offline-fallback"],eq=[{matcher:({url:e,sameOrigin:t})=>t&&e.pathname.startsWith("/api/"),handler:new ee({cacheName:"api-responses",networkTimeoutSeconds:5,plugins:[new ep({maxEntries:100,maxAgeSeconds:3600})]})},{matcher:({request:e})=>"navigate"===e.mode,handler:new ee({cacheName:"pages",networkTimeoutSeconds:5,plugins:[new ep({maxEntries:50,maxAgeSeconds:86400}),{handlerDidError:async()=>await caches.match(eR)||Response.error()}]})},{matcher:({url:e,sameOrigin:t})=>t&&e.pathname.startsWith("/_next/static/"),handler:new ey({cacheName:"next-static",plugins:[new ep({maxEntries:200,maxAgeSeconds:2592e3})]})},{matcher:({url:e})=>e.pathname.startsWith("/images/")||e.pathname.startsWith("/icons/")||/\.(?:png|jpe?g|gif|svg|webp|ico|woff2?)$/.test(e.pathname),handler:new ey({cacheName:"static-media",plugins:[new ep({maxEntries:100,maxAgeSeconds:2592e3})]})},{matcher:()=>!0,handler:new ee({cacheName:"fallback",networkTimeoutSeconds:5,plugins:[new ep({maxEntries:50,maxAgeSeconds:86400})]})}];new ev({precacheEntries:[{'revision':'ab8abfd07f39ac406789faac7d61b363','url':'/_next/static/LLc4dulGhTEoJJ8Pkgu6A/_buildManifest.js'},{'revision':'b6652df95db52feb4daf4eca35380933','url':'/_next/static/LLc4dulGhTEoJJ8Pkgu6A/_ssgManifest.js'},{'revision':null,'url':'/_next/static/chunks/1079-cffc1eb37436de53.js'},{'revision':null,'url':'/_next/static/chunks/1393-e01c6861d0341a69.js'},{'revision':null,'url':'/_next/static/chunks/1896-a9d2dc75c88c85fb.js'},{'revision':null,'url':'/_next/static/chunks/1958-56cbbb8dba15fd2f.js'},{'revision':null,'url':'/_next/static/chunks/2346-36e36720a5f6591a.js'},{'revision':null,'url':'/_next/static/chunks/3263-1fd6000d3a990905.js'},{'revision':null,'url':'/_next/static/chunks/4836-4871e0bacaaca435.js'},{'revision':null,'url':'/_next/static/chunks/4bd1b696-e356ca5ba0218e27.js'},{'revision':null,'url':'/_next/static/chunks/52774a7f.f2ab7a9b4b8ba576.js'},{'revision':null,'url':'/_next/static/chunks/5741-3085cade73716f0f.js'},{'revision':null,'url':'/_next/static/chunks/5838-7e0aa455e9f24259.js'},{'revision':null,'url':'/_next/static/chunks/6243-06350499fc86734c.js'},{'revision':null,'url':'/_next/static/chunks/7551-4c9aa7502ea6cc30.js'},{'revision':null,'url':'/_next/static/chunks/7602.2868ff821d53ee09.js'},{'revision':null,'url':'/_next/static/chunks/7918-b6bc29652564489b.js'},{'revision':null,'url':'/_next/static/chunks/8200-9a6d7728a5c09911.js'},{'revision':null,'url':'/_next/static/chunks/8500-f62a38ff68ab7f42.js'},{'revision':null,'url':'/_next/static/chunks/9da6db1e-9623f3245f088d02.js'},{'revision':null,'url':'/_next/static/chunks/app/_global-error/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/_not-found/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/about/page-70022fc79d67ea5a.js'},{'revision':null,'url':'/_next/static/chunks/app/account/page-9300f08c33aa8887.js'},{'revision':null,'url':'/_next/static/chunks/app/admin/page-43ddd38031c7119e.js'},{'revision':null,'url':'/_next/static/chunks/app/api/accuracy/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/admin/stats/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/books/%5B...path%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/checkout/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/combat/%5Bdate%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/content/%5B...path%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/desk-showcase/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/desk/pack/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/fight/%5Bid%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/founders/count/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/futures/%5Bsport%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/gamelines/%5Bsport%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/games/%5Bid%5D/props/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/games/%5Bid%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/games/tonight/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/hero-prop/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/hotlist/%5Bsport%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/intelligence/feed/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/ledger/accuracy/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/ledger/mine/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/ledger/model/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/ledger/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/lines/%5B...path%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/live/%5Bsport%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/news/%5Bsport%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/newsletter/subscribe/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/odds/mlb/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/odds/nba/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/odds/soccer/%5Bleague%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/odds/wnba/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/parlay/add-leg/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/parlay/calculate/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/parlay/grade/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/players/search/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/preferences/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/profiles/%5Bhandle%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/profiles/me/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/props/live/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/props/most-parlayed/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/props/top-graded/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/scan/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/schedule/%5Bsport%5D/injuries/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/schedule/%5Bsport%5D/lineups/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/schedule/%5Bsport%5D/pitchers/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/schedule/%5Bsport%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/slips/parse/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/snapshot/%5Bsport%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/snapshot/summary/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stats/cascade/%5Bplayer%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stats/depth/%5Bteam%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stats/leaders/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stats/lineup/%5Bteam%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stats/parlays-graded/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stats/pitcher/%5Bname%5D/arsenal/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stats/player/%5Bname%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stats/public/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/streaks/%5Bsport%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stripe/portal/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/team/%5Babbr%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/ticker/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/user/profile/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/user/recent-scans/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/user/scan-meter/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/user/scans/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/waitlist/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/webhook/nexapay/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/welcome-email/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/auth/callback/page-e6df9771a50edb87.js'},{'revision':null,'url':'/_next/static/chunks/app/blog/%5Bslug%5D/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/blog/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/compare/page-1119694e961d71c4.js'},{'revision':null,'url':'/_next/static/chunks/app/dashboard/page-aa1beeb392637aa1.js'},{'revision':null,'url':'/_next/static/chunks/app/desk/page-7acc556c00f487e5.js'},{'revision':null,'url':'/_next/static/chunks/app/explore/page-4c518cd2ef8bb2cd.js'},{'revision':null,'url':'/_next/static/chunks/app/fight/%5Bid%5D/page-308862cc7b686178.js'},{'revision':null,'url':'/_next/static/chunks/app/forgot-password/page-a251f52b4206e8d1.js'},{'revision':null,'url':'/_next/static/chunks/app/game/%5Bid%5D/page-0a74de29a6580a35.js'},{'revision':null,'url':'/_next/static/chunks/app/help/page-10e0eae5e4e1de7c.js'},{'revision':null,'url':'/_next/static/chunks/app/intelligence/page-af6b3ddd4f155539.js'},{'revision':null,'url':'/_next/static/chunks/app/invite/page-01dda8e223484025.js'},{'revision':null,'url':'/_next/static/chunks/app/layout-156f1e90da4ba847.js'},{'revision':null,'url':'/_next/static/chunks/app/ledger/page-d31795dedaf2ac88.js'},{'revision':null,'url':'/_next/static/chunks/app/login/page-869e21de85065bc1.js'},{'revision':null,'url':'/_next/static/chunks/app/marketplace/page-cbada49ae96a7527.js'},{'revision':null,'url':'/_next/static/chunks/app/methodology/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/not-found-aea5d97d52af5780.js'},{'revision':null,'url':'/_next/static/chunks/app/notifications/page-70022fc79d67ea5a.js'},{'revision':null,'url':'/_next/static/chunks/app/offline/page-a7a57ff039afc1af.js'},{'revision':null,'url':'/_next/static/chunks/app/onboarding/page-507c881a3cf2cdad.js'},{'revision':null,'url':'/_next/static/chunks/app/opengraph-image/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/page-42fce85acbf93cb7.js'},{'revision':null,'url':'/_next/static/chunks/app/parlay/page-9fb644e7cfb54cb7.js'},{'revision':null,'url':'/_next/static/chunks/app/player/%5Bname%5D/layout-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/player/%5Bname%5D/opengraph-image/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/player/%5Bname%5D/page-dc02d996495083aa.js'},{'revision':null,'url':'/_next/static/chunks/app/pricing/page-7719f6003ec52cfd.js'},{'revision':null,'url':'/_next/static/chunks/app/privacy/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/profile/page-8bb575ea4a91bfdb.js'},{'revision':null,'url':'/_next/static/chunks/app/report/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/responsible-gambling/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/scan/page-a0e172f0361a0d79.js'},{'revision':null,'url':'/_next/static/chunks/app/settings/page-9bf003e5a54e5190.js'},{'revision':null,'url':'/_next/static/chunks/app/settings/security/page-f0d118bb0bf17bf8.js'},{'revision':null,'url':'/_next/static/chunks/app/signup/page-d34a7133af025c69.js'},{'revision':null,'url':'/_next/static/chunks/app/slip/page-32a5d1ace27127d2.js'},{'revision':null,'url':'/_next/static/chunks/app/soccer/page-3ae10f80a7ebddf9.js'},{'revision':null,'url':'/_next/static/chunks/app/team/%5Babbr%5D/page-bde75cf1101ae726.js'},{'revision':null,'url':'/_next/static/chunks/app/terminal/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/terms/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/tracker/page-2ce2d278c7e0ed48.js'},{'revision':null,'url':'/_next/static/chunks/app/twitter-image/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/u/%5Bhandle%5D/opengraph-image/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/u/%5Bhandle%5D/page-252629d55ee3c82b.js'},{'revision':null,'url':'/_next/static/chunks/app/u/%5Bhandle%5D/portrait/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/upgrade/cancel/page-3702e818a6853705.js'},{'revision':null,'url':'/_next/static/chunks/app/upgrade/desk/page-0709bc8c6f29ea65.js'},{'revision':null,'url':'/_next/static/chunks/app/upgrade/success/page-dc7c14a4f1c96dbd.js'},{'revision':null,'url':'/_next/static/chunks/app/verify/page-b121fa98601397d8.js'},{'revision':null,'url':'/_next/static/chunks/app/welcome/page-028357260721a9f9.js'},{'revision':null,'url':'/_next/static/chunks/framework-95da69ac6843d788.js'},{'revision':null,'url':'/_next/static/chunks/main-a354262253293123.js'},{'revision':null,'url':'/_next/static/chunks/main-app-4231d5f500f33972.js'},{'revision':null,'url':'/_next/static/chunks/next/dist/client/components/builtin/app-error-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/next/dist/client/components/builtin/forbidden-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/next/dist/client/components/builtin/global-error-756d787dba63aa4d.js'},{'revision':null,'url':'/_next/static/chunks/next/dist/client/components/builtin/unauthorized-a2e62102f89140a7.js'},{'revision':'846118c33b2c0e922d7b3a7676f81f6f','url':'/_next/static/chunks/polyfills-42372ed130431b0a.js'},{'revision':null,'url':'/_next/static/chunks/webpack-c39c325c0a9c381a.js'},{'revision':null,'url':'/_next/static/css/fe8315bb5b899fa0.css'},{'revision':'cd34c7793d9776838308cfbe5d93922b','url':'/_next/static/media/011e180705008d6f-s.woff2'},{'revision':'324703f03c390d2e2a4f387de85fe63d','url':'/_next/static/media/0aa834ed78bf6d07-s.woff2'},{'revision':'9dda5cfc9a46f256d0e131bb535e46f8','url':'/_next/static/media/19cfc7226ec3afaa-s.woff2'},{'revision':'7de6d31e5076eb437418b6e01998ee12','url':'/_next/static/media/20535187d867b7b9-s.woff2'},{'revision':'4e2553027f1d60eff32898367dd4d541','url':'/_next/static/media/21350d82a1f187e9-s.woff2'},{'revision':'0d11e07b5aaafda6ae2efc53256932ec','url':'/_next/static/media/37786be940ec402b-s.woff2'},{'revision':'5de0bff80f9f432b5cf52c1c2f80fe20','url':'/_next/static/media/46e154b2fcbd6033-s.woff2'},{'revision':'6041b3192dc3e1a50141d5d418a24199','url':'/_next/static/media/5356a6a4f2c8c8d8-s.woff2'},{'revision':'f572f7b57d27ec7fd8373961f0b762b0','url':'/_next/static/media/58f386aa6b1a2a92-s.woff2'},{'revision':'e829cbe042e8b2b2e6b99c0eec9e230d','url':'/_next/static/media/656feb427634a431-s.woff2'},{'revision':'54f02056e07c55023315568c637e3a96','url':'/_next/static/media/67957d42bae0796d-s.woff2'},{'revision':'e220ad1849c282590e47db0ff88fc3f3','url':'/_next/static/media/704b853f32d191d5-s.woff2'},{'revision':'662cde3543f3375874e26c26845cec22','url':'/_next/static/media/73cb51aac9c97f90-s.woff2'},{'revision':'afac1fab355fe8db5c2dcaa7623dffb3','url':'/_next/static/media/7ba5fb2a8c88521c-s.woff2'},{'revision':'c94e6e6c23e789fcb0fc60d790c9d2c1','url':'/_next/static/media/886030b0b59bc5a7-s.woff2'},{'revision':'01ba6c2a184b8cba08b0d57167664d75','url':'/_next/static/media/8e9860b6e62d6359-s.woff2'},{'revision':'17a5b2a15bf5647b49cadb9a3bb02e1c','url':'/_next/static/media/92eeb95d069020cc-s.woff2'},{'revision':'4a4e74bed5809194e4bc6538eb1a1e30','url':'/_next/static/media/939c4f875ee75fbb-s.woff2'},{'revision':'6708425c446b05de1c2d471a0eaf4099','url':'/_next/static/media/98e207f02528a563-s.woff2'},{'revision':'13dface50f6dc0bff1015561c84119e6','url':'/_next/static/media/991629005c80bdf1-s.woff2'},{'revision':'216ee1a35b060106ed2852cdd8c026c7','url':'/_next/static/media/99dcf268bda04fe5-s.woff2'},{'revision':'9e494903d6b0ffec1a1e14d34427d44d','url':'/_next/static/media/ba9851c3c22cd980-s.woff2'},{'revision':'782150e6836b9b074d1a798807adcb18','url':'/_next/static/media/bb3ef058b751a6ad-s.p.woff2'},{'revision':'027a89e9ab733a145db70f09b8a18b42','url':'/_next/static/media/c5fe6dc8356a8c31-s.woff2'},{'revision':'75fa2e527eaae805bbf44e3378d1232c','url':'/_next/static/media/d26bbd13d6b70f89-s.woff2'},{'revision':'0ba762bde65aaa682fcc089f8c57288e','url':'/_next/static/media/d29838c109ef09b4-s.woff2'},{'revision':'814fe1615d4dbc7bbd92d26a6bda0905','url':'/_next/static/media/d3ebbfd689654d3a-s.woff2'},{'revision':'e71a597e376ed91862e822b5a5de8c04','url':'/_next/static/media/db96af6b531dc71f-s.woff2'},{'revision':'d54db44de5ccb18886ece2fda72bdfe0','url':'/_next/static/media/df0a9ae256c0569c-s.woff2'},{'revision':'9fb609fe01d739bb9df558e1fba2498e','url':'/_next/static/media/e40af3453d7c920a-s.woff2'},{'revision':'65850a373e258f1c897a2b3d75eb74de','url':'/_next/static/media/e4af272ccee01ff0-s.p.woff2'},{'revision':'de1d49f26130d43f91829088086625f4','url':'/_next/static/media/ef4d5661765d0e49-s.woff2'},{'revision':'0f8d347d49960d05c9430d83e49edeb7','url':'/_next/static/media/f911b923c6adde36-s.woff2'},{'revision':'d7bc0f63f9618b8b700d028d7d242de8','url':'/apple-touch-icon.png'},{'revision':'300b6bcc84329321d40dec63416e9566','url':'/books/bet365.svg'},{'revision':'c5d3df8f35278d119bacd44726536a53','url':'/books/betmgm.svg'},{'revision':'c0898e8b017d76457549b49df00d78dd','url':'/books/betrivers.svg'},{'revision':'8acaa0c6c9c413fe01caf8f5d9cf1ac3','url':'/books/caesars.svg'},{'revision':'7fbceb80c3466f82e5cebed0f84d00f4','url':'/books/draftkings.svg'},{'revision':'d275072bd73625ea22826b370fed3697','url':'/books/fanduel.svg'},{'revision':'8457294d0c8dcb63396875c6ae594dc2','url':'/books/hardrockbet.svg'},{'revision':'77bafdb1e2a6ff85e34038ad06a9fa31','url':'/books/pinnacle.svg'},{'revision':'5616f81ec5f8a9f726a17fb7ac66a54a','url':'/favicon-16.png'},{'revision':'1fc285cfb0116a0523eb34c779a7d282','url':'/favicon-32.png'},{'revision':'583b034f36839a8af92841771eef38b6','url':'/favicon.ico'},{'revision':'b15795128b3691e5703d0ce14ce10827','url':'/favicon.png'},{'revision':'44e9d71551be13574ac2e10063d03aa9','url':'/favicon.svg'},{'revision':'252b8f8c4d0f64c7cd120f182a6c74ab','url':'/icon-192.png'},{'revision':'3e3b53bbd210bd772b36b70165657e7c','url':'/icon-512.png'},{'revision':'252b8f8c4d0f64c7cd120f182a6c74ab','url':'/icons/icon-192.png'},{'revision':'3e3b53bbd210bd772b36b70165657e7c','url':'/icons/icon-512.png'},{'revision':'3e3b53bbd210bd772b36b70165657e7c','url':'/icons/icon-maskable-512.png'},{'revision':'bf670b1fafa1b3127bd50abe86d723b9','url':'/images/player-silhouette.svg'},{'revision':'1cd41b3d92ff160c4635a1ee75bbc34b','url':'/manifest.json'},{'revision':'9f61cf51298661d5c57a782534216240','url':'/og-image.png'},{'revision':'7c759c20b35840eacf9344692cb51061','url':'/og-image.svg'},{'revision':'9327802333275f11e68c3f19f20c160d','url':'/widget.js'}],skipWaiting:!0,clientsClaim:!0,navigationPreload:!0,runtimeCaching:eq}).addEventListeners(),self.addEventListener("install",e=>{e.waitUntil(caches.open("offline-fallback").then(e=>e.add(eR)).catch(()=>{}))}),self.addEventListener("activate",e=>{e.waitUntil(caches.keys().then(e=>Promise.all(e.filter(e=>!eE.includes(e)&&!e.startsWith("serwist")).map(e=>(console.log("[SW] deleting stale cache:",e),caches.delete(e))))))}),self.addEventListener("push",e=>{let t;if(!e.data)return;try{t=e.data.json()}catch{t={title:"VYNDR",body:e.data.text()}}let{title:a="VYNDR",body:s="",icon:r="/icons/icon-192.png",url:i="/",tag:n="vyndr-notification"}=t;e.waitUntil(self.registration.showNotification(a,{body:s,icon:r,badge:"/icons/icon-192.png",tag:n,data:{url:i}}))}),self.addEventListener("notificationclick",e=>{e.notification.close();let t=e.notification.data?.url??"/";e.waitUntil(self.clients.matchAll({type:"window",includeUncontrolled:!0}).then(e=>{let a=e.find(e=>e.url.endsWith(t));return a?a.focus():self.clients.openWindow(t)}))})})(); \ No newline at end of file +This is generally NOT safe. Learn more at https://bit.ly/wb-precache`)}handleInstall(e){return this.registerRequestRules(e),y(e,async()=>{let t=new eo;this.precacheStrategy.plugins.push(t),await el(this._concurrentPrecaching,Array.from(this._urlsToCacheKeys.entries()),async([t,a])=>{let s=this._cacheKeysToIntegrities.get(a),r=this._urlsToCacheModes.get(t),i=new Request(t,{integrity:s,cache:r,credentials:"same-origin"});await Promise.all(this.precacheStrategy.handleAll({event:e,request:i,url:new URL(i.url),params:{cacheKey:a}}))});let{updatedURLs:a,notUpdatedURLs:s}=t;return{updatedURLs:a,notUpdatedURLs:s}})}async registerRequestRules(e){if(this._requestRules&&e?.addRoutes)try{await e.addRoutes(this._requestRules),this._requestRules=void 0}catch(e){throw e}}handleActivate(e){return y(e,async()=>{let e=await self.caches.open(this.precacheStrategy.cacheName),t=await e.keys(),a=new Set(this._urlsToCacheKeys.values()),s=[];for(let r of t)a.has(r.url)||(await e.delete(r),s.push(r.url));return{deletedCacheRequests:s}})}handleFetch(e){let{request:t}=e,a=this.handleRequest({request:t,event:e});a&&e.respondWith(a)}handleCache(e){if(e.data&&"CACHE_URLS"===e.data.type){let{payload:t}=e.data,a=Promise.all(t.urlsToCache.map(t=>{let a;return a="string"==typeof t?new Request(t):new Request(...t),this.handleRequest({request:a,event:e})}));e.waitUntil(a),e.ports?.[0]&&a.then(()=>e.ports[0].postMessage(!0))}}setDefaultHandler(e,t="GET"){this._defaultHandlerMap.set(t,ea(e))}setCatchHandler(e){this._catchHandler=ea(e)}registerCapture(e,t,a){let s=((e,t,a)=>{if("string"==typeof e){let s=new URL(e,location.href);return new es(({url:e})=>e.href===s.href,t,a)}if(e instanceof RegExp)return new en(e,t,a);if("function"==typeof e)return new es(e,t,a);if(e instanceof es)return e;throw new l("unsupported-route-type",{moduleName:"serwist",funcName:"parseRoute",paramName:"capture"})})(e,t,a);return this.registerRoute(s),s}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new l("unregister-route-but-not-found-with-method",{method:e.method});let t=this._routes.get(e.method).indexOf(e);if(t>-1)this._routes.get(e.method).splice(t,1);else throw new l("unregister-route-route-not-registered")}getUrlsToPrecacheKeys(){return this._urlsToCacheKeys}getPrecachedUrls(){return[...this._urlsToCacheKeys.keys()]}getPrecacheKeyForUrl(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForPrecacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,a=this.getPrecacheKeyForUrl(t);if(a)return(await self.caches.open(this.precacheStrategy.cacheName)).match(a)}createHandlerBoundToUrl(e){let t=this.getPrecacheKeyForUrl(e);if(!t)throw new l("non-precached-url",{url:e});return a=>(a.request=new Request(e),a.params={cacheKey:t,...a.params},this.precacheStrategy.handle(a))}handleRequest({request:e,event:t}){let a,s=new URL(e.url,location.href);if(!s.protocol.startsWith("http"))return;let r=s.origin===location.origin,{params:i,route:n}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:s}),c=n?.handler,o=e.method;if(!c&&this._defaultHandlerMap.has(o)&&(c=this._defaultHandlerMap.get(o)),!c)return;try{a=c.handle({url:s,request:e,event:t,params:i})}catch(e){a=Promise.reject(e)}let l=n?.catchHandler;return a instanceof Promise&&(this._catchHandler||l)&&(a=a.catch(async a=>{if(l)try{return await l.handle({url:s,request:e,event:t,params:i})}catch(e){e instanceof Error&&(a=e)}if(this._catchHandler)return this._catchHandler.handle({url:s,request:e,event:t});throw a})),a}findMatchingRoute({url:e,sameOrigin:t,request:a,event:s}){for(let r of this._routes.get(a.method)||[]){let i,n=r.match({url:e,sameOrigin:t,request:a,event:s});if(n)return Array.isArray(i=n)&&0===i.length||n.constructor===Object&&0===Object.keys(n).length?i=void 0:"boolean"==typeof n&&(i=void 0),{route:r,params:i}}return{}}};let eR="/offline",eE=["pages","api-responses","next-static","static-media","fallback","offline-fallback"],eq=[{matcher:({url:e,sameOrigin:t})=>t&&e.pathname.startsWith("/api/"),handler:new ee({cacheName:"api-responses",networkTimeoutSeconds:5,plugins:[new ep({maxEntries:100,maxAgeSeconds:3600})]})},{matcher:({request:e})=>"navigate"===e.mode,handler:new ee({cacheName:"pages",networkTimeoutSeconds:5,plugins:[new ep({maxEntries:50,maxAgeSeconds:86400}),{handlerDidError:async()=>await caches.match(eR)||Response.error()}]})},{matcher:({url:e,sameOrigin:t})=>t&&e.pathname.startsWith("/_next/static/"),handler:new ey({cacheName:"next-static",plugins:[new ep({maxEntries:200,maxAgeSeconds:2592e3})]})},{matcher:({url:e})=>e.pathname.startsWith("/images/")||e.pathname.startsWith("/icons/")||/\.(?:png|jpe?g|gif|svg|webp|ico|woff2?)$/.test(e.pathname),handler:new ey({cacheName:"static-media",plugins:[new ep({maxEntries:100,maxAgeSeconds:2592e3})]})},{matcher:()=>!0,handler:new ee({cacheName:"fallback",networkTimeoutSeconds:5,plugins:[new ep({maxEntries:50,maxAgeSeconds:86400})]})}];new ev({precacheEntries:[{'revision':'ab8abfd07f39ac406789faac7d61b363','url':'/_next/static/GqhaUwxvqWfbQxpA5jOOR/_buildManifest.js'},{'revision':'b6652df95db52feb4daf4eca35380933','url':'/_next/static/GqhaUwxvqWfbQxpA5jOOR/_ssgManifest.js'},{'revision':null,'url':'/_next/static/chunks/1079-cffc1eb37436de53.js'},{'revision':null,'url':'/_next/static/chunks/1393-e01c6861d0341a69.js'},{'revision':null,'url':'/_next/static/chunks/1896-a9d2dc75c88c85fb.js'},{'revision':null,'url':'/_next/static/chunks/1958-56cbbb8dba15fd2f.js'},{'revision':null,'url':'/_next/static/chunks/2346-36e36720a5f6591a.js'},{'revision':null,'url':'/_next/static/chunks/3263-1fd6000d3a990905.js'},{'revision':null,'url':'/_next/static/chunks/4836-4871e0bacaaca435.js'},{'revision':null,'url':'/_next/static/chunks/4bd1b696-e356ca5ba0218e27.js'},{'revision':null,'url':'/_next/static/chunks/52774a7f.f2ab7a9b4b8ba576.js'},{'revision':null,'url':'/_next/static/chunks/5741-3085cade73716f0f.js'},{'revision':null,'url':'/_next/static/chunks/5838-7e0aa455e9f24259.js'},{'revision':null,'url':'/_next/static/chunks/6243-06350499fc86734c.js'},{'revision':null,'url':'/_next/static/chunks/7551-4c9aa7502ea6cc30.js'},{'revision':null,'url':'/_next/static/chunks/7602.2868ff821d53ee09.js'},{'revision':null,'url':'/_next/static/chunks/7918-b6bc29652564489b.js'},{'revision':null,'url':'/_next/static/chunks/8200-9a6d7728a5c09911.js'},{'revision':null,'url':'/_next/static/chunks/8500-f62a38ff68ab7f42.js'},{'revision':null,'url':'/_next/static/chunks/9da6db1e-9623f3245f088d02.js'},{'revision':null,'url':'/_next/static/chunks/app/_global-error/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/_not-found/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/about/page-70022fc79d67ea5a.js'},{'revision':null,'url':'/_next/static/chunks/app/account/page-9300f08c33aa8887.js'},{'revision':null,'url':'/_next/static/chunks/app/admin/page-43ddd38031c7119e.js'},{'revision':null,'url':'/_next/static/chunks/app/api/accuracy/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/admin/stats/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/books/%5B...path%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/checkout/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/combat/%5Bdate%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/content/%5B...path%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/desk-showcase/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/desk/pack/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/fight/%5Bid%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/founders/count/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/futures/%5Bsport%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/gamelines/%5Bsport%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/games/%5Bid%5D/props/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/games/%5Bid%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/games/tonight/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/hero-prop/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/hotlist/%5Bsport%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/intelligence/feed/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/ledger/accuracy/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/ledger/mine/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/ledger/model/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/ledger/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/lines/%5B...path%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/live/%5Bsport%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/news/%5Bsport%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/newsletter/subscribe/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/odds/mlb/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/odds/nba/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/odds/soccer/%5Bleague%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/odds/wnba/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/parlay/add-leg/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/parlay/calculate/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/parlay/grade/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/players/search/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/preferences/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/profiles/%5Bhandle%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/profiles/me/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/props/live/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/props/most-parlayed/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/props/top-graded/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/scan/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/schedule/%5Bsport%5D/injuries/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/schedule/%5Bsport%5D/lineups/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/schedule/%5Bsport%5D/pitchers/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/schedule/%5Bsport%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/slips/parse/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/snapshot/%5Bsport%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/snapshot/summary/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stats/cascade/%5Bplayer%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stats/depth/%5Bteam%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stats/leaders/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stats/lineup/%5Bteam%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stats/parlays-graded/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stats/pitcher/%5Bname%5D/arsenal/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stats/player/%5Bname%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stats/public/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/streaks/%5Bsport%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/stripe/portal/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/team/%5Babbr%5D/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/ticker/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/user/profile/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/user/recent-scans/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/user/scan-meter/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/user/scans/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/waitlist/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/webhook/nexapay/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/api/welcome-email/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/auth/callback/page-e6df9771a50edb87.js'},{'revision':null,'url':'/_next/static/chunks/app/blog/%5Bslug%5D/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/blog/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/compare/page-1119694e961d71c4.js'},{'revision':null,'url':'/_next/static/chunks/app/dashboard/page-aa1beeb392637aa1.js'},{'revision':null,'url':'/_next/static/chunks/app/desk/page-7acc556c00f487e5.js'},{'revision':null,'url':'/_next/static/chunks/app/explore/page-4c518cd2ef8bb2cd.js'},{'revision':null,'url':'/_next/static/chunks/app/fight/%5Bid%5D/page-308862cc7b686178.js'},{'revision':null,'url':'/_next/static/chunks/app/forgot-password/page-a251f52b4206e8d1.js'},{'revision':null,'url':'/_next/static/chunks/app/game/%5Bid%5D/page-0a74de29a6580a35.js'},{'revision':null,'url':'/_next/static/chunks/app/help/page-10e0eae5e4e1de7c.js'},{'revision':null,'url':'/_next/static/chunks/app/intelligence/page-af6b3ddd4f155539.js'},{'revision':null,'url':'/_next/static/chunks/app/invite/page-01dda8e223484025.js'},{'revision':null,'url':'/_next/static/chunks/app/layout-156f1e90da4ba847.js'},{'revision':null,'url':'/_next/static/chunks/app/ledger/page-d31795dedaf2ac88.js'},{'revision':null,'url':'/_next/static/chunks/app/login/page-869e21de85065bc1.js'},{'revision':null,'url':'/_next/static/chunks/app/marketplace/page-cbada49ae96a7527.js'},{'revision':null,'url':'/_next/static/chunks/app/methodology/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/not-found-aea5d97d52af5780.js'},{'revision':null,'url':'/_next/static/chunks/app/notifications/page-70022fc79d67ea5a.js'},{'revision':null,'url':'/_next/static/chunks/app/offline/page-a7a57ff039afc1af.js'},{'revision':null,'url':'/_next/static/chunks/app/onboarding/page-507c881a3cf2cdad.js'},{'revision':null,'url':'/_next/static/chunks/app/opengraph-image/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/page-42fce85acbf93cb7.js'},{'revision':null,'url':'/_next/static/chunks/app/parlay/page-9fb644e7cfb54cb7.js'},{'revision':null,'url':'/_next/static/chunks/app/player/%5Bname%5D/layout-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/player/%5Bname%5D/opengraph-image/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/player/%5Bname%5D/page-dc02d996495083aa.js'},{'revision':null,'url':'/_next/static/chunks/app/pricing/page-7719f6003ec52cfd.js'},{'revision':null,'url':'/_next/static/chunks/app/privacy/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/profile/page-8bb575ea4a91bfdb.js'},{'revision':null,'url':'/_next/static/chunks/app/report/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/responsible-gambling/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/scan/page-a0e172f0361a0d79.js'},{'revision':null,'url':'/_next/static/chunks/app/settings/page-9bf003e5a54e5190.js'},{'revision':null,'url':'/_next/static/chunks/app/settings/security/page-f0d118bb0bf17bf8.js'},{'revision':null,'url':'/_next/static/chunks/app/signup/page-d34a7133af025c69.js'},{'revision':null,'url':'/_next/static/chunks/app/slip/page-32a5d1ace27127d2.js'},{'revision':null,'url':'/_next/static/chunks/app/soccer/page-3ae10f80a7ebddf9.js'},{'revision':null,'url':'/_next/static/chunks/app/team/%5Babbr%5D/page-bde75cf1101ae726.js'},{'revision':null,'url':'/_next/static/chunks/app/terminal/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/terms/page-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/tracker/page-2ce2d278c7e0ed48.js'},{'revision':null,'url':'/_next/static/chunks/app/twitter-image/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/u/%5Bhandle%5D/opengraph-image/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/u/%5Bhandle%5D/page-252629d55ee3c82b.js'},{'revision':null,'url':'/_next/static/chunks/app/u/%5Bhandle%5D/portrait/route-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/app/upgrade/cancel/page-3702e818a6853705.js'},{'revision':null,'url':'/_next/static/chunks/app/upgrade/desk/page-0709bc8c6f29ea65.js'},{'revision':null,'url':'/_next/static/chunks/app/upgrade/success/page-dc7c14a4f1c96dbd.js'},{'revision':null,'url':'/_next/static/chunks/app/verify/page-b121fa98601397d8.js'},{'revision':null,'url':'/_next/static/chunks/app/welcome/page-028357260721a9f9.js'},{'revision':null,'url':'/_next/static/chunks/framework-95da69ac6843d788.js'},{'revision':null,'url':'/_next/static/chunks/main-a354262253293123.js'},{'revision':null,'url':'/_next/static/chunks/main-app-4231d5f500f33972.js'},{'revision':null,'url':'/_next/static/chunks/next/dist/client/components/builtin/app-error-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/next/dist/client/components/builtin/forbidden-a2e62102f89140a7.js'},{'revision':null,'url':'/_next/static/chunks/next/dist/client/components/builtin/global-error-756d787dba63aa4d.js'},{'revision':null,'url':'/_next/static/chunks/next/dist/client/components/builtin/unauthorized-a2e62102f89140a7.js'},{'revision':'846118c33b2c0e922d7b3a7676f81f6f','url':'/_next/static/chunks/polyfills-42372ed130431b0a.js'},{'revision':null,'url':'/_next/static/chunks/webpack-c39c325c0a9c381a.js'},{'revision':null,'url':'/_next/static/css/fe8315bb5b899fa0.css'},{'revision':'cd34c7793d9776838308cfbe5d93922b','url':'/_next/static/media/011e180705008d6f-s.woff2'},{'revision':'324703f03c390d2e2a4f387de85fe63d','url':'/_next/static/media/0aa834ed78bf6d07-s.woff2'},{'revision':'9dda5cfc9a46f256d0e131bb535e46f8','url':'/_next/static/media/19cfc7226ec3afaa-s.woff2'},{'revision':'7de6d31e5076eb437418b6e01998ee12','url':'/_next/static/media/20535187d867b7b9-s.woff2'},{'revision':'4e2553027f1d60eff32898367dd4d541','url':'/_next/static/media/21350d82a1f187e9-s.woff2'},{'revision':'0d11e07b5aaafda6ae2efc53256932ec','url':'/_next/static/media/37786be940ec402b-s.woff2'},{'revision':'5de0bff80f9f432b5cf52c1c2f80fe20','url':'/_next/static/media/46e154b2fcbd6033-s.woff2'},{'revision':'6041b3192dc3e1a50141d5d418a24199','url':'/_next/static/media/5356a6a4f2c8c8d8-s.woff2'},{'revision':'f572f7b57d27ec7fd8373961f0b762b0','url':'/_next/static/media/58f386aa6b1a2a92-s.woff2'},{'revision':'e829cbe042e8b2b2e6b99c0eec9e230d','url':'/_next/static/media/656feb427634a431-s.woff2'},{'revision':'54f02056e07c55023315568c637e3a96','url':'/_next/static/media/67957d42bae0796d-s.woff2'},{'revision':'e220ad1849c282590e47db0ff88fc3f3','url':'/_next/static/media/704b853f32d191d5-s.woff2'},{'revision':'662cde3543f3375874e26c26845cec22','url':'/_next/static/media/73cb51aac9c97f90-s.woff2'},{'revision':'afac1fab355fe8db5c2dcaa7623dffb3','url':'/_next/static/media/7ba5fb2a8c88521c-s.woff2'},{'revision':'c94e6e6c23e789fcb0fc60d790c9d2c1','url':'/_next/static/media/886030b0b59bc5a7-s.woff2'},{'revision':'01ba6c2a184b8cba08b0d57167664d75','url':'/_next/static/media/8e9860b6e62d6359-s.woff2'},{'revision':'17a5b2a15bf5647b49cadb9a3bb02e1c','url':'/_next/static/media/92eeb95d069020cc-s.woff2'},{'revision':'4a4e74bed5809194e4bc6538eb1a1e30','url':'/_next/static/media/939c4f875ee75fbb-s.woff2'},{'revision':'6708425c446b05de1c2d471a0eaf4099','url':'/_next/static/media/98e207f02528a563-s.woff2'},{'revision':'13dface50f6dc0bff1015561c84119e6','url':'/_next/static/media/991629005c80bdf1-s.woff2'},{'revision':'216ee1a35b060106ed2852cdd8c026c7','url':'/_next/static/media/99dcf268bda04fe5-s.woff2'},{'revision':'9e494903d6b0ffec1a1e14d34427d44d','url':'/_next/static/media/ba9851c3c22cd980-s.woff2'},{'revision':'782150e6836b9b074d1a798807adcb18','url':'/_next/static/media/bb3ef058b751a6ad-s.p.woff2'},{'revision':'027a89e9ab733a145db70f09b8a18b42','url':'/_next/static/media/c5fe6dc8356a8c31-s.woff2'},{'revision':'75fa2e527eaae805bbf44e3378d1232c','url':'/_next/static/media/d26bbd13d6b70f89-s.woff2'},{'revision':'0ba762bde65aaa682fcc089f8c57288e','url':'/_next/static/media/d29838c109ef09b4-s.woff2'},{'revision':'814fe1615d4dbc7bbd92d26a6bda0905','url':'/_next/static/media/d3ebbfd689654d3a-s.woff2'},{'revision':'e71a597e376ed91862e822b5a5de8c04','url':'/_next/static/media/db96af6b531dc71f-s.woff2'},{'revision':'d54db44de5ccb18886ece2fda72bdfe0','url':'/_next/static/media/df0a9ae256c0569c-s.woff2'},{'revision':'9fb609fe01d739bb9df558e1fba2498e','url':'/_next/static/media/e40af3453d7c920a-s.woff2'},{'revision':'65850a373e258f1c897a2b3d75eb74de','url':'/_next/static/media/e4af272ccee01ff0-s.p.woff2'},{'revision':'de1d49f26130d43f91829088086625f4','url':'/_next/static/media/ef4d5661765d0e49-s.woff2'},{'revision':'0f8d347d49960d05c9430d83e49edeb7','url':'/_next/static/media/f911b923c6adde36-s.woff2'},{'revision':'d7bc0f63f9618b8b700d028d7d242de8','url':'/apple-touch-icon.png'},{'revision':'300b6bcc84329321d40dec63416e9566','url':'/books/bet365.svg'},{'revision':'c5d3df8f35278d119bacd44726536a53','url':'/books/betmgm.svg'},{'revision':'c0898e8b017d76457549b49df00d78dd','url':'/books/betrivers.svg'},{'revision':'8acaa0c6c9c413fe01caf8f5d9cf1ac3','url':'/books/caesars.svg'},{'revision':'7fbceb80c3466f82e5cebed0f84d00f4','url':'/books/draftkings.svg'},{'revision':'d275072bd73625ea22826b370fed3697','url':'/books/fanduel.svg'},{'revision':'8457294d0c8dcb63396875c6ae594dc2','url':'/books/hardrockbet.svg'},{'revision':'77bafdb1e2a6ff85e34038ad06a9fa31','url':'/books/pinnacle.svg'},{'revision':'5616f81ec5f8a9f726a17fb7ac66a54a','url':'/favicon-16.png'},{'revision':'1fc285cfb0116a0523eb34c779a7d282','url':'/favicon-32.png'},{'revision':'583b034f36839a8af92841771eef38b6','url':'/favicon.ico'},{'revision':'b15795128b3691e5703d0ce14ce10827','url':'/favicon.png'},{'revision':'44e9d71551be13574ac2e10063d03aa9','url':'/favicon.svg'},{'revision':'252b8f8c4d0f64c7cd120f182a6c74ab','url':'/icon-192.png'},{'revision':'3e3b53bbd210bd772b36b70165657e7c','url':'/icon-512.png'},{'revision':'252b8f8c4d0f64c7cd120f182a6c74ab','url':'/icons/icon-192.png'},{'revision':'3e3b53bbd210bd772b36b70165657e7c','url':'/icons/icon-512.png'},{'revision':'3e3b53bbd210bd772b36b70165657e7c','url':'/icons/icon-maskable-512.png'},{'revision':'bf670b1fafa1b3127bd50abe86d723b9','url':'/images/player-silhouette.svg'},{'revision':'1cd41b3d92ff160c4635a1ee75bbc34b','url':'/manifest.json'},{'revision':'9f61cf51298661d5c57a782534216240','url':'/og-image.png'},{'revision':'7c759c20b35840eacf9344692cb51061','url':'/og-image.svg'},{'revision':'9327802333275f11e68c3f19f20c160d','url':'/widget.js'}],skipWaiting:!0,clientsClaim:!0,navigationPreload:!0,runtimeCaching:eq}).addEventListeners(),self.addEventListener("install",e=>{e.waitUntil(caches.open("offline-fallback").then(e=>e.add(eR)).catch(()=>{}))}),self.addEventListener("activate",e=>{e.waitUntil(caches.keys().then(e=>Promise.all(e.filter(e=>!eE.includes(e)&&!e.startsWith("serwist")).map(e=>(console.log("[SW] deleting stale cache:",e),caches.delete(e))))))}),self.addEventListener("push",e=>{let t;if(!e.data)return;try{t=e.data.json()}catch{t={title:"VYNDR",body:e.data.text()}}let{title:a="VYNDR",body:s="",icon:r="/icons/icon-192.png",url:i="/",tag:n="vyndr-notification"}=t;e.waitUntil(self.registration.showNotification(a,{body:s,icon:r,badge:"/icons/icon-192.png",tag:n,data:{url:i}}))}),self.addEventListener("notificationclick",e=>{e.notification.close();let t=e.notification.data?.url??"/";e.waitUntil(self.clients.matchAll({type:"window",includeUncontrolled:!0}).then(e=>{let a=e.find(e=>e.url.endsWith(t));return a?a.focus():self.clients.openWindow(t)}))})})(); \ No newline at end of file