import { mkdir, rename } from "node:fs/promises"; import { CODE_SOURCES } from "../src/ingest/codes/registry.ts"; import { buildCodesFeed } from "../src/ingest/codes/build.ts"; import { CodesFeedSchema, type CodesFeed } from "../src/shared/codes.ts"; import { SnapshotStore, freshnessAt } from "../src/ingest/snapshots.ts"; import type { SourceHealth } from "../src/shared/feed.ts"; export async function buildCodes() { const root = process.env.SNAPSHOT_DIR ?? "snapshots", out = process.env.PUBLIC_DIR ?? "public", store = new SnapshotStore(root); const firstPath = `${root}/codes-first-seen.json`; let first: Record = {}; try { first = await Bun.file(firstPath).json(); } catch { /* First offline build uses the fixture's capture date. */ } const previous: CodesFeed = { schemaVersion: 1, codes: [], sources: [] }; const codes: CodesFeed["codes"] = []; const sources: SourceHealth[] = []; for (const source of CODE_SOURCES) { const snapshot = await store.read(source.id), at = snapshot ? freshnessAt(snapshot) : (( await Bun.file( "fixtures/genshin/game8-codes-2026-10-16.meta.json", ).json() ).contentChangedAt as string), html = snapshot?.html ?? (await Bun.file("fixtures/genshin/game8-codes-2026-10-16.html").text()); const parsed = source.parse(html, { now: new Date().toISOString(), sourceId: source.id, sourceUrl: source.url, game: source.game, }); const built = buildCodesFeed(previous, parsed, at); for (const code of built.codes) { code.firstObservedAt = first[code.id] ?? code.firstObservedAt; first[code.id] = code.firstObservedAt; codes.push(code); } sources.push({ sourceId: source.id, game: source.game, url: source.url, lastSuccessAt: at, lastConfirmedAt: snapshot?.meta.lastConfirmedAt ?? null, contentChangedAt: snapshot?.meta.contentChangedAt ?? at, eventCount: parsed.length, parsedCount: parsed.length, statesNoEvents: false, }); } const feed = CodesFeedSchema.parse({ schemaVersion: 1, codes, sources }); await mkdir(`${out}/data`, { recursive: true }); await Bun.write(`${out}/data/codes.v1.json`, JSON.stringify(feed)); await mkdir(root, { recursive: true }); await Bun.write(firstPath + ".tmp", JSON.stringify(first, null, 2) + "\n"); await rename(firstPath + ".tmp", firstPath); console.log(`build-codes: ${codes.length} current codes`); } if (import.meta.main) await buildCodes();