mattrguerra/meridian-ops-boardmainPrivate on GitHub, published here in full

history-file.ts

Language TypeScript1.9 KB48 lines

apps/dashboard/src/history-file.ts

  1. /**
  2. * Reading and writing `data/dashboard-history.json`. Node only.
  3. *
  4. * Split out of history.ts because the Cloudflare Worker needs `rowFrom`,
  5. * `previousRun` and `delta` but has no filesystem, and a static `node:fs` import
  6. * resolves whether or not the symbol is used. The Worker keeps the same rows in
  7. * KV; only the storage differs.
  8. */
  9. import { readFile, writeFile, mkdir } from 'node:fs/promises'
  10. import { dirname } from 'node:path'
  11. import { fileURLToPath } from 'node:url'
  12. import { KEEP, type HistoryRow } from './history.js'
  13. export const HISTORY_FILE = new URL('../../../data/dashboard-history.json', import.meta.url)
  14. export async function readHistory(file: URL = HISTORY_FILE): Promise<HistoryRow[]> {
  15. try {
  16. const parsed: unknown = JSON.parse(await readFile(file, 'utf8'))
  17. if (!Array.isArray(parsed)) return []
  18. // A hand-edited or half-written file must not take the build down; a missing
  19. // delta is a smaller problem than a dashboard that will not render.
  20. return parsed.filter((r): r is HistoryRow => !!r && typeof r === 'object' && typeof (r as HistoryRow).day === 'string')
  21. } catch {
  22. return []
  23. }
  24. }
  25. /**
  26. * Append today's row, replacing any earlier row for the same day, and trim.
  27. * Returns the rows as they were before this run, so the caller can diff against
  28. * them without racing itself.
  29. */
  30. export async function recordRun(
  31. row: HistoryRow,
  32. file: URL = HISTORY_FILE,
  33. ): Promise<HistoryRow[]> {
  34. const before = await readHistory(file)
  35. const kept = before.filter((r) => r.day !== row.day)
  36. const next = [...kept, row].sort((a, b) => a.day.localeCompare(b.day)).slice(-KEEP)
  37. try {
  38. await mkdir(dirname(fileURLToPath(file)), { recursive: true })
  39. await writeFile(file, `${JSON.stringify(next, null, 2)}\n`, 'utf8')
  40. } catch {
  41. // Read-only checkout, CI, whatever. Deltas stop working; nothing else does.
  42. }
  43. return before
  44. }