Proves the POC in production: the category-groups list is now built with
the typed Kysely builder (category_groups added to the schema; COLLATE
NOCASE via sql``) and executed synchronously via all(). Byte-identical
behavior — categoryGroups test 4/4, suite 248/248, typecheck:server clean.
Establishes the migration recipe for rolling typed SQL out query-by-query.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Establishes the canonical route error pattern (see docs/CODE_STANDARDS.md):
handlers `throw ValidationError/NotFoundError/ConflictError(...)` and let
Express 5 forward to the single terminal handler — no per-handler
try/catch swallows, no ad-hoc `res.status().json({error})`. Only the
UNIQUE-constraint → 409 mapping keeps a tight try/catch (`asConflict`).
- apiError factories NotFoundError/ConflictError now take an optional
`field` (parity with ValidationError) so form-field info is preserved.
- routes/categories.cts fully converted; identical status/messages, now
uniform `{ error, message, code, field }` bodies.
- Test harnesses (categoryGroups, categoryReorder) updated to simulate the
terminal handler (thrown ApiError → formatted response).
Verified: 236/236 server tests; e2e probe 17/17; live smoke confirms
400 VALIDATION_ERROR / 404 NOT_FOUND with correct field + status.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Every routes/*.js → .cts, with handler signatures typed against the shared
http Req/Res/Next types. Routes carry @ts-nocheck for now — they're thin glue
over the (fully typed) services, so full route-level type-checking is deferred;
the handler-param typing is a head-start. server.js + test requires updated to
the explicit .cts paths.
Behavior-preserving. Verified: check:server 0, typecheck:server 0, suite 226/226,
real boot → /api/health + /api/about 200 (all routes mount).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>