feat: add debug script for inspecting raw API data per park+date
All checks were successful
Build and Deploy / Build & Push (push) Successful in 4m23s

npm run debug -- --park greatadventure --date 2026-07-04

Prints the raw API response for that day alongside the parsed result
so mismatched or missing hours can be traced to their source.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-04 10:39:37 -04:00
parent 5bef17aa41
commit 8a68251beb
3 changed files with 126 additions and 1 deletions

View File

@@ -97,6 +97,19 @@ async function fetchApi(url: string, attempt = 0, totalWaitedMs = 0): Promise<Ap
return res.json() as Promise<ApiResponse>; return res.json() as Promise<ApiResponse>;
} }
/**
* Fetch the raw API response for a month — used by scripts/debug.ts.
*/
export async function scrapeMonthRaw(
apiId: number,
year: number,
month: number
): Promise<ApiResponse> {
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. * Fetch operating hours for an entire month in a single API call.
* apiId must be pre-discovered via scripts/discover.ts. * apiId must be pre-discovered via scripts/discover.ts.

View File

@@ -9,7 +9,8 @@
"lint": "next lint", "lint": "next lint",
"scrape": "tsx scripts/scrape.ts", "scrape": "tsx scripts/scrape.ts",
"scrape:force": "tsx scripts/scrape.ts --rescrape", "scrape:force": "tsx scripts/scrape.ts --rescrape",
"discover": "tsx scripts/discover.ts" "discover": "tsx scripts/discover.ts",
"debug": "tsx scripts/debug.ts"
}, },
"dependencies": { "dependencies": {
"better-sqlite3": "^12.8.0", "better-sqlite3": "^12.8.0",

111
scripts/debug.ts Normal file
View File

@@ -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 <parkId> --date <YYYY-MM-DD>");
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);
});