// THE POLLED VIEWS, BY NAME — the contract `/api/view/[name]` serves. // // Eight endpoints used to be eight route files that differed only in which // builder they called. They are one dynamic route now, and this tuple is what // makes that route total: `editor/app/api/view/views.ts` declares its handler // table as `Record`, so a name added here without a handler is a // tsc error rather than a 500 somebody finds in production. That completeness // proof is the entire reason the names live in a shared, importless module // instead of in the route beside the table. // // WHAT MAY BE ADDED. A view is something a client POLLS for state it draws: // read-only, unauthenticated, cheap enough to ask for on a timer, and safe for // anyone who can reach the editor at all. Nothing that mutates, nothing that // takes a credential, and — stated because the temptation is obvious — NO // `/api/test/*` NAME MAY EVER JOIN THIS TUPLE. Those routes fabricate jobs, // restart lanes and set the worker token; they are gated on // `E2E_TEST_ROUTES` precisely so they do not exist on a real editor, and // putting one behind this dispatcher would hand it the one surface here that // has no guard at all. export const VIEW_NAMES = [ "pulse", "activeJobs", "workers", "autoQueueStatus", "schedulerStatus", "widgetSync", "widgetActionable", "cleanable", ] as const; export type ViewName = (typeof VIEW_NAMES)[number]; // OBSERVE OR CONSTRUCT, declared per view. // // An "observe" view may look at singletons that already exist and at file // mtimes, and MUST NOT bring anything into being: no registry, no settings // parse, no worker pool, no corpus read. `pulse` is the only one, and the rule // is not stylistic — constructing from the pulse poll is what made ~16 specs // flaky earlier this month, because a 1-second timer was racing the very // singletons the page under test was setting up. // // Everything else is "construct": it reads what it needs, each handler // assembling its OWN inputs. Do not hoist a shared constructor into the // dispatcher "for efficiency" — that is the same regression wearing a // different hat, and it would drag an observe view into constructing. export const VIEW_CONTRACT: Record = { pulse: "observe", activeJobs: "construct", workers: "construct", autoQueueStatus: "construct", schedulerStatus: "construct", widgetSync: "construct", widgetActionable: "construct", cleanable: "construct", };