import { NextResponse } from "next/server"; import { getPaths } from "yt-dlp-transcript-common/lib/paths"; import type { ViewName } from "yt-dlp-transcript-common/views/names"; import { buildCleanablePayload } from "yt-dlp-transcript-common/views/cleanable"; import { buildWidgetActionablePayload } from "yt-dlp-transcript-common/views/widgetActionable"; import { buildWidgetSyncPayload } from "yt-dlp-transcript-common/views/widgetSync"; import { pulseView } from "./pulseView"; import { buildActiveJobsPayload } from "../../jobs/active/buildActiveJobs"; import { buildWorkersPayload } from "../../workers/buildWorkers"; import { buildAutoQueueStatusPayload } from "../../operations/status"; import { buildSchedulerStatusPayload } from "../../scheduler/status"; import { widgetSyncInputs } from "../../widget/lib/syncInputs"; import { cleanableChannels } from "../../cleanup/lib/loadCleanup"; import { loadActionableSummary, widgetActionableRows, } from "../../lib/actionable/loadActionable"; // THE HANDLER TABLE, AND THE COMPLETENESS PROOF. // // `Record` is total: every name in `VIEW_NAMES` must appear here // or the editor does not compile. That is the whole reason the names are a // tuple in `common/views/names.ts` rather than a union spelled out beside this // table — a union and a map drift; a total Record cannot. // // EVERY HANDLER ASSEMBLES ITS OWN INPUTS, and this file must never grow a // shared constructor hoisted out of them. One `liveInputs()` in the dispatcher // would be cheaper by a few milliseconds and would put the pulse view — which // may only OBSERVE — on the constructing path, which is the ~16-flaky-spec // regression from earlier this month rebuilt from the top. Each of these // closures does exactly what its old route file did, no more and in the same // order. // // The handlers are LAZY for the same reason: the dispatcher picks one by name // and calls it, so an unknown name reads nothing at all. export const VIEWS: Record Promise> = { // The change token, and the only handler that reads the request. pulse: pulseView, // The ~1 s poll on the /jobs head, the dashboard and the widget. This one // HEALS: `buildActiveJobsPayload` completes scheduler slots whose job record // is already terminal. (`liveJobRows`, which the server components call, does // not — that asymmetry is deliberate and lives in the view.) activeJobs: async () => NextResponse.json(await buildActiveJobsPayload()), // Live worker status for the Workers page and the read-only widget. workers: async () => NextResponse.json(buildWorkersPayload()), // The Auto-Queue panel: per-kind runner status, effective policy, recent // picks and snapshot-derived pending counts. autoQueueStatus: async () => NextResponse.json(await buildAutoQueueStatusPayload()), // The /operations/sync console: the resolved per-channel schedule, the recent // tick log and the effective scheduler settings. schedulerStatus: async () => NextResponse.json(await buildSchedulerStatusPayload()), // The widget's last-sync and scheduler strips, plus the corpus-wide digest // and backfill scalars. widgetSync: async () => NextResponse.json(buildWidgetSyncPayload(await widgetSyncInputs())), // The widget's "Needs work" strip. The census read and the per-channel // counting (`widgetActionableRows`, which knows the availability exclusions) // are the editor's; the filter and the sort are the view's. widgetActionable: async () => { const summary = await loadActionableSummary(getPaths()); return NextResponse.json( buildWidgetActionablePayload(widgetActionableRows(summary.rows)), ); }, // The cleanable-data badge AND the widget's "Needs cleaning" list — one // snapshot read serving both, as before. cleanable: async () => NextResponse.json(buildCleanablePayload(await cleanableChannels(getPaths()))), };