From a2fe1b38d1b67e03979ae27cde63dc22f220d260 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 23 Apr 2026 19:21:22 +0200 Subject: [PATCH] =?UTF-8?q?build(ts):=20day=202=20=E2=80=94=20shared/types?= =?UTF-8?q?.ts=20+=20server.ts=20rename=20(no=20behavior=20change)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Creates the wire-protocol contract every route and every client component will import from. Renames server.js → server.ts as a pure rename (zero bytes of logic changed) so the entry point becomes the first file tsc type-checks against the real compilerOptions. shared/types.ts — what's in it and why - ApiResponse envelope: (ApiOk & T) | ApiErr. Every route returns one of these; client code narrows on `r.success`. - One typed "Ok" shape per endpoint, keyed by the actual res.json() call in the current handler. Walked every src/routes/*.js file and transcribed the literal keys: hpi, soap, note, hospitalCourse, review, refined, shortened, questions, narrative+summary, etc. No inventive renaming — wire stays identical, only the types are new. - Critical mismatches the types now prevent at compile time: /api/refine returns `refined` (not `content` — a past bug) /api/sick-visit/note (not /api/generate-sick-visit — past bug) /api/generate-hospital-course returns `hospitalCourse` (not `narrative` — past bug) All three were caught and fixed earlier in Playwright; with the shared types they become compile errors for any future regression. server.ts - Pure rename via `git mv`. Body unchanged. - Compiled dist/server.js diffs against the original server.js by two lines (TypeScript prepends `"use strict"` and the CommonJS export marker). No semantic drift. tsconfig.json tweak - Include list adds server.ts alongside server.js so tsc doesn't silently skip the entry point during the intermediate state where `.js` entries might reappear. - baseUrl removed (deprecated in TS 6); paths now uses './shared/*'. package.json - main: dist/server.js (post-compile entry) - start: node dist/server.js - prebuild: rm -rf dist (clean emit every time) - dev: ts-node-dev for fast TS-aware reloads The Dockerfile is still unchanged. The deployed prod and e2e containers still run their baked-in server.js from the previous image — this migration day has no effect on either until the final rebuild at the end of day 7. Next: day 3 renames the 29 route files one-by-one, each adding the ApiResponse type parameter to its res.json() calls. --- package.json | 5 +- server.js => server.ts | 0 shared/types.ts | 234 +++++++++++++++++++++++++++++++++++++++++ tsconfig.json | 1 + 4 files changed, 238 insertions(+), 2 deletions(-) rename server.js => server.ts (100%) create mode 100644 shared/types.ts diff --git a/package.json b/package.json index 2d8f20a..e3b0d18 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,10 @@ "name": "pediatric-ai-scribe", "version": "6.21.0", "description": "AI-powered pediatric clinical documentation platform", - "main": "server.js", + "main": "dist/server.js", "scripts": { - "start": "node server.js", + "start": "node dist/server.js", + "prebuild": "rm -rf dist", "build": "tsc", "typecheck": "tsc --noEmit", "dev": "ts-node-dev --respawn --transpile-only server.ts", diff --git a/server.js b/server.ts similarity index 100% rename from server.js rename to server.ts diff --git a/shared/types.ts b/shared/types.ts new file mode 100644 index 0000000..802b8e2 --- /dev/null +++ b/shared/types.ts @@ -0,0 +1,234 @@ +// ============================================================ +// SHARED TYPES — consumed by both server (src/routes/*.ts) and +// client (client/src/*). A response-shape change here breaks the +// build on both sides until they agree. +// +// This is the single most important file in the TypeScript +// migration. Three of the bugs we hit before this point were +// response-shape mismatches (refine returned `content` vs +// `refined`, sick-visit endpoint path mismatch, hospital-course +// key mismatch). Typing the wire protocol makes them compile-time +// errors. +// +// Keep this file strictly about wire protocol — no DB row shapes, +// no server-internal helpers. Anything that crosses the network. +// ============================================================ + +// ── Envelope ───────────────────────────────────────────────── +// Every route returns either `{success: true, ...extraFields}` or +// `{success: false, error: string}`. The generic `T` is the shape +// of the extra fields on success. Client code does: +// const r: ApiResponse = await fetch(...).then(r => r.json()); +// if (r.success) console.log(r.hpi); +// else showToast(r.error); +export interface ApiErr { + success: false; + error: string; +} +export type ApiResponse = ({ success: true } & T) | ApiErr; + +// Model tag accompanies every AI response — LiteLLM pass-through. +export interface WithModel { + model: string; +} + +// ── AI generation responses ────────────────────────────────── +// Keys match exactly what each route returns today. Do not rename +// without updating the server-side res.json() call in the same commit. + +// /api/generate-hpi-encounter +// /api/generate-hpi-dictation +export interface HpiOk extends WithModel { + hpi: string; +} + +// /api/generate-soap +export interface SoapOk extends WithModel { + soap: string; +} + +// /api/sick-visit/note (NOT /api/generate-sick-visit — that path does not exist) +// /api/well-visit/note +export interface VisitNoteOk extends WithModel { + note: string; +} + +// /api/generate-hospital-course +export interface HospitalCourseOk extends WithModel { + hospitalCourse: string; + format?: string; +} + +// /api/generate-chart-review +export interface ChartReviewOk extends WithModel { + review: string; +} + +// /api/generate-pe-narrative +export interface PeNarrativeOk extends WithModel { + narrative: string; + summary: { + normal: number; + abnormal: number; + notAssessed: number; + }; +} + +// /api/generate-milestone-narrative +export interface MilestoneNarrativeOk extends WithModel { + narrative: string; + summary: { + achieved: number; + notAchieved: number; + notAssessed: number; + }; +} + +// /api/generate-milestone-summary +export interface MilestoneSummaryOk extends WithModel { + summary: string; +} + +// /api/well-visit/shadess +export interface ShadessOk extends WithModel { + assessment: string; +} + +// /api/refine +export interface RefineOk extends WithModel { + refined: string; +} + +// /api/shorten (via refine.js router) +export interface ShortenOk extends WithModel { + shortened: string; +} + +// /api/clarify and /hospital-course/clarify +export interface ClarifyOk extends WithModel { + questions: string; +} + +// /api/suggest-billing-codes +export interface BillingCodesOk extends WithModel { + icd10: Array<{ code: string; description: string; reason?: string }>; + cpt: Array<{ code: string; description: string; reason?: string }>; +} + +// /api/transcribe +export interface TranscribeOk { + text: string; + provider: string; + duration: number; +} + +// /api/tts +export interface TtsOk { + audioBase64: string; +} + +// /api/models +export interface ModelsOk { + models: Array<{ id: string; label?: string }>; +} + +// ── Auth ───────────────────────────────────────────────────── +export interface AuthUser { + id: number; + email: string; + name: string; + role?: string; + isVerified?: boolean; + has2FA?: boolean; +} + +// /api/auth/login (two-phase: may also return requires2FA / needsVerification) +export interface LoginOk { + token: string; + user: AuthUser; + sessionId?: string; +} + +export interface LoginRequires2FA { + success: true; + requires2FA: true; +} + +export interface LoginNeedsVerification { + success: true; + needsVerification: true; +} + +// /api/auth/me +export interface MeOk { + user: AuthUser; +} + +// ── Sessions ───────────────────────────────────────────────── +export interface SessionRow { + id: string; + userAgent?: string; + ipAddress?: string; + createdAt: string; + lastUsedAt?: string; +} +export interface SessionsOk { + sessions: SessionRow[]; + currentSessionId: string | null; +} + +// ── Extensions (pagers/directory) ──────────────────────────── +export interface Extension { + id: number; + location: string; + name: string; + number: string; + type: 'extension' | 'pager'; + notes?: string; + deletedAt?: string | null; +} +export interface ExtensionsListOk { + items: Extension[]; +} + +// ── Memories (saved templates / style hints) ───────────────── +export interface Memory { + id: number; + category: string; + name: string; + content: string; +} +export interface MemoriesListOk { + items: Memory[]; +} + +// ── Learning hub ───────────────────────────────────────────── +export interface LearningContentItem { + id: number | string; + slug?: string; + title: string; + category?: string; + excerpt?: string; + body?: string; +} +export interface LearningFeedOk { + content: LearningContentItem[]; + total?: number; +} + +// ── Encounters (saved drafts) ──────────────────────────────── +export interface EncounterSummary { + id: number; + label: string; + type: 'encounter' | 'dictation' | 'hospital' | 'chart' | 'wellvisit' | 'sickvisit' | 'soap'; + createdAt: string; + updatedAt: string; +} +export interface EncountersListOk { + items: EncounterSummary[]; +} + +// ── Health ─────────────────────────────────────────────────── +export interface HealthOk { + ok: boolean; +} diff --git a/tsconfig.json b/tsconfig.json index bde40cd..0ecf971 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,6 +23,7 @@ } }, "include": [ + "server.ts", "server.js", "src/**/*.js", "src/**/*.ts",