diff --git a/lib/scrapers/sixflags.ts b/lib/scrapers/sixflags.ts index a017089..1922b02 100644 --- a/lib/scrapers/sixflags.ts +++ b/lib/scrapers/sixflags.ts @@ -97,6 +97,19 @@ async function fetchApi(url: string, attempt = 0, totalWaitedMs = 0): Promise; } +/** + * Fetch the raw API response for a month — used by scripts/debug.ts. + */ +export async function scrapeMonthRaw( + apiId: number, + year: number, + month: number +): Promise { + const dateParam = `${year}${String(month).padStart(2, "0")}`; + const url = `${API_BASE}/${apiId}?date=${dateParam}`; + return fetchApi(url); +} + /** * Fetch operating hours for an entire month in a single API call. * apiId must be pre-discovered via scripts/discover.ts. diff --git a/package.json b/package.json index b15663b..c822ffe 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "lint": "next lint", "scrape": "tsx scripts/scrape.ts", "scrape:force": "tsx scripts/scrape.ts --rescrape", - "discover": "tsx scripts/discover.ts" + "discover": "tsx scripts/discover.ts", + "debug": "tsx scripts/debug.ts" }, "dependencies": { "better-sqlite3": "^12.8.0", diff --git a/scripts/debug.ts b/scripts/debug.ts new file mode 100644 index 0000000..5f43cf8 --- /dev/null +++ b/scripts/debug.ts @@ -0,0 +1,111 @@ +/** + * Debug a specific park + date to inspect raw API data and parsed output. + * + * Usage: + * npm run debug -- --park greatadventure --date 2026-07-04 + */ + +import { openDb, getApiId } from "../lib/db"; +import { PARKS } from "../lib/parks"; +import { scrapeMonthRaw } from "../lib/scrapers/sixflags"; + +function arg(flag: string): string | undefined { + const i = process.argv.indexOf(flag); + return i !== -1 ? process.argv[i + 1] : undefined; +} + +function fmt24(time: string): string { + const [h, m] = time.split(":").map(Number); + const period = h >= 12 ? "pm" : "am"; + const h12 = h % 12 || 12; + return m === 0 ? `${h12}${period}` : `${h12}:${String(m).padStart(2, "0")}${period}`; +} + +async function main() { + const parkId = arg("--park"); + const dateStr = arg("--date"); + + if (!parkId || !dateStr) { + console.error("Usage: npm run debug -- --park --date "); + console.error("\nAvailable park IDs:"); + for (const p of PARKS) console.error(` ${p.id.padEnd(24)} ${p.name}`); + process.exit(1); + } + + const park = PARKS.find((p) => p.id === parkId); + if (!park) { + console.error(`Unknown park: "${parkId}"`); + process.exit(1); + } + + const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})$/); + if (!match) { + console.error(`Invalid date: "${dateStr}" — expected YYYY-MM-DD`); + process.exit(1); + } + const [, yearStr, monthStr, dayStr] = match; + const year = parseInt(yearStr); + const month = parseInt(monthStr); + const day = parseInt(dayStr); + + const db = openDb(); + const apiId = getApiId(db, park.id); + db.close(); + + if (apiId === null) { + console.error(`No API ID found for ${park.name} — run: npm run discover`); + process.exit(1); + } + + console.log(`\nPark : ${park.name} (${park.id})`); + console.log(`API ID : ${apiId}`); + console.log(`Date : ${dateStr}`); + console.log(`\nFetching ${year}-${String(month).padStart(2, "0")} from API...\n`); + + const raw = await scrapeMonthRaw(apiId, year, month); + + // Find the specific day in the response + const targetDate = `${String(month).padStart(2, "0")}/${String(day).padStart(2, "0")}/${year}`; + const dayData = raw.dates.find((d) => d.date === targetDate); + + if (!dayData) { + console.error(`Date ${dateStr} not found in API response.`); + console.log(`\nDates returned by API:`); + for (const d of raw.dates) console.log(` ${d.date}`); + process.exit(1); + } + + // ── Raw API response for this day ───────────────────────────────────────── + console.log("── Raw API response ─────────────────────────────────────────"); + console.log(JSON.stringify(dayData, null, 2)); + + // ── Parsed result (what the scraper stores) ─────────────────────────────── + console.log("\n── Parsed result ────────────────────────────────────────────"); + const operating = + dayData.operatings?.find((o) => o.operatingTypeName === "Park") ?? + dayData.operatings?.[0]; + const item = operating?.items?.[0]; + const hoursLabel = + item?.timeFrom && item?.timeTo + ? `${fmt24(item.timeFrom)} – ${fmt24(item.timeTo)}` + : undefined; + const isOpen = !dayData.isParkClosed && hoursLabel !== undefined; + + console.log(` isParkClosed : ${dayData.isParkClosed}`); + console.log(` operatings : ${dayData.operatings?.length ?? 0} entr${dayData.operatings?.length === 1 ? "y" : "ies"}`); + if (operating) { + console.log(` selected : "${operating.operatingTypeName}" (${operating.items?.length ?? 0} item(s))`); + if (item) { + console.log(` timeFrom : ${item.timeFrom} → ${fmt24(item.timeFrom)}`); + console.log(` timeTo : ${item.timeTo} → ${fmt24(item.timeTo)}`); + } + } + console.log(` hoursLabel : ${hoursLabel ?? "(none)"}`); + console.log(` isOpen : ${isOpen}`); + console.log(); +} + +main().catch((err) => { + console.error("Fatal:", err); + process.exit(1); +});