// Local browser QA only: a fixture of /api/clinician with fictional people and records, the
// preview's demo API (local reader only), and the built front end. Binds to 127.0.0.1.
// Sign in as emeka.okafor@example.test; the codes are FIXTURE_EMAIL_CODE and FIXTURE_TOTP_CODE in data.ts.
import { randomUUID } from "node:crypto";
import { readFile } from "node:fs/promises";
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
import { extname, resolve, sep } from "node:path";
import { z } from "zod";
import { CASES } from "../../server/cases";
import { assess } from "../../server/context";
import { runEvaluation } from "../../server/evaluation";
import { extract } from "../../server/model";
import { baseSchema, coverageReviewSchema } from "../../server/schemas";
import { getDraft, getReport, keepDraft, keepReport } from "../../server/sessions";
import { ENGINE_VERSION, RULES_VERSION } from "../../../../src/domain/checks/engine";
import { qrSvg } from "../../../../src/clinician/qr";
import { CHECK_SIGNED_OFF } from "../../../../src/domain/checks/signoff";
import { viewHours } from "../../src/api/adapt";
import type * as W from "../../src/api/wire";
import type { Variant } from "./calls";
import { FIXTURE_EMAIL, FIXTURE_EMAIL_CODE, FIXTURE_TOTP_SECRET } from "./data";
import { HttpError, invalid, route, text, type Ctx, type Route } from "./http";
import { checkInRoutes } from "./routes-checkins";
import { patientRoutes } from "./routes-patients";
import { createStore, totpValid, type Options, type Session, type Store } from "./store";
import { wireClinic, wireClinician, wireMe } from "./wire";
const COOKIE = "context_fixture";
const TWELVE_HOURS = 12 * 3_600_000;
// The backend's patient routes: the step-up, then the terms (src/clinician/session.ts).
const needsStepUp = (level: Route["auth"]) => level === "step" || level === "patients";
const me = (store: Store, s: Session) => wireMe(store.clinician, { createdAt: s.createdAt, stepUp: s.stepUp, locked: s.locked }, store.totpEnrolled);
const recovery = () => Array.from({ length: 10 }, () => randomUUID().replace(/-/g, "").slice(0, 10));
// As the backend (src/clinician/profile.ts): numbers Context texts are stored in E.164, hours are HH:MM spans.
function textable(value: unknown, path: string): string {
let cleaned = text(value, 32).replace(/[\s().-]/g, "");
if (cleaned.startsWith("00")) cleaned = `+${cleaned.slice(2)}`;
if (!/^\+[1-9]\d{6,14}$/.test(cleaned)) throw invalid("Check the highlighted fields.", [{ path, message: "Use the full number with its country code, for example +234 803 123 4567" }]);
return cleaned;
}
function knownZone(value: unknown): string {
try {
new Intl.DateTimeFormat("en", { timeZone: String(value) });
return String(value);
} catch {
throw invalid("Check the highlighted fields.", [{ path: "timezone", message: "Not a known time zone" }]);
}
}
const clock = (value: unknown) => typeof value === "string" && /^([01]\d|2[0-3]):[0-5]\d$/.test(value);
function weekly(value: unknown, path: string): W.WeeklyHours {
const spans = Array.isArray(value) ? (value as W.WeeklyHours) : [];
const ok = Array.isArray(value) && spans.length <= 21 && spans.every((h) => Number.isInteger(h.day) && h.day >= 1 && h.day <= 7 && clock(h.open) && clock(h.close) && h.open < h.close);
if (!ok) throw invalid("Check the highlighted fields.", [{ path, message: "Opening must come before closing" }]);
return spans.map(({ day, open, close }) => ({ day, open, close }));
}
const accountRoutes: Route[] = [
route("POST", "/access-requests", "public", (ctx) => {
const b = ctx.body ?? {};
const required = ["fullName", "profession", "licenceBody", "licenceNumber", "countryCode", "facilityName", "facilityPhone", "workEmail", "mobilePhone", "heardFrom", "termsVersion"];
const missing = required.filter((k) => !text(b[k]));
if (missing.length || b.acceptTerms !== true) throw invalid("Check the highlighted fields.", missing.map((path) => ({ path, message: "Required" })));
if (!/^[A-Z]{2}$/.test(String(b.countryCode))) throw invalid("Check the highlighted fields.", [{ path: "countryCode", message: "Use a two-letter country code" }]);
if (b.countryCode === "NG" && b.licenceBody !== (b.profession === "doctor" ? "MDCN" : "PCN"))
throw invalid("Check the highlighted fields.", [{ path: "licenceBody", message: `In Nigeria, ${b.profession}s are registered with the ${b.profession === "doctor" ? "MDCN" : "PCN"}` }]);
ctx.store.accessRequests.push(b);
return { status: "received", message: "Thank you. We sent a confirmation to your work email and usually reply within 2 working days." } satisfies W.AccessRequestReceived;
}, 202),
route("GET", "/invite-preview/:token", "public", (ctx) => {
const invite = [...ctx.store.invitations.values()].find((i) => i.token === decodeURIComponent(ctx.params[0]));
if (!invite) throw new HttpError(404, "INVITE_NOT_FOUND", "This invitation link is not valid.");
return {
status: "pending",
patientName: invite.patientName,
withPerson: invite.withPerson,
doctorName: "Dr Emeka Okafor",
clinicName: "Surulere Family Clinic",
doctorEmail: FIXTURE_EMAIL,
expiresAt: invite.expiresAt,
} satisfies W.InvitePreview;
}),
route("GET", "/me", "session", (ctx) => me(ctx.store, ctx.session!)),
route("GET", "/step-up", "session", (ctx) => ({
totpEnrolled: ctx.store.totpEnrolled, recoveryCodesLeft: ctx.store.totpRecoveryCodes.length, stepUpDone: ctx.session!.stepUp && !ctx.session!.locked,
locked: ctx.session!.locked, pausedUntil: null,
}) satisfies W.StepUpState),
route("POST", "/step-up/totp", "session", (ctx) => {
if (ctx.store.totpEnrolled && !ctx.session!.stepUp)
throw new HttpError(403, "STEP_UP_REQUIRED", "Enter a code from your current authenticator app before setting up a new one.");
const otpauthUrl = `otpauth://totp/MARMAR%20Context:${encodeURIComponent(FIXTURE_EMAIL)}?secret=${FIXTURE_TOTP_SECRET}&issuer=MARMAR%20Context&algorithm=SHA1&digits=6&period=30`;
// The backend's own QR drawing, as the real API answers.
return { secret: FIXTURE_TOTP_SECRET, otpauthUrl, qrSvg: qrSvg(otpauthUrl) } satisfies W.TotpStart;
}, 201),
route("POST", "/step-up/totp/confirm", "session", (ctx) => {
if (!totpValid(text(ctx.body?.code, 20))) throw new HttpError(400, "CODE_INCORRECT", "That code didn't match. Check the time on your phone and try the newest code.");
ctx.store.totpEnrolled = true;
ctx.store.totpRecoveryCodes = recovery();
Object.assign(ctx.session!, { stepUp: true, locked: false, lastActiveAt: Date.now() });
return { stepUpDone: true, recoveryCodes: ctx.store.totpRecoveryCodes } satisfies W.TotpConfirmed;
}),
route("POST", "/step-up/verify", "session", (ctx) => {
if (!ctx.store.totpEnrolled) throw new HttpError(409, "NO_AUTHENTICATOR", "Set up an authenticator app first.");
const code = text(ctx.body?.code, 20).replace(/\s/g, "");
const used = ctx.store.totpRecoveryCodes.indexOf(code);
if (!/^\d{6}$/.test(code) ? used < 0 : !totpValid(code))
throw new HttpError(400, "CODE_INCORRECT", "That code didn't match. Try the newest code from your authenticator app.");
if (used >= 0) ctx.store.totpRecoveryCodes.splice(used, 1);
Object.assign(ctx.session!, { stepUp: true, locked: false, lastActiveAt: Date.now() });
return { stepUpDone: true, method: used >= 0 ? "recovery_code" : "totp" } satisfies W.StepUpVerified;
}),
route("POST", "/session/lock", "session", (ctx) => {
Object.assign(ctx.session!, { stepUp: false, locked: true });
return { locked: true };
}),
route("POST", "/sessions/sign-out-everywhere", "session", (ctx) => {
const signedOut = ctx.store.sessions.size;
ctx.store.sessions.clear();
ctx.res.setHeader("Set-Cookie", `${COOKIE}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`);
return { signedOut };
}),
route("POST", "/me/terms", "session", (ctx) => {
if (!text(ctx.body?.termsVersion, 32) || !text(ctx.body?.dpaVersion, 32)) throw invalid("Check the highlighted fields.");
ctx.store.clinician.termsAcceptedAt = new Date().toISOString();
return { clinician: wireClinician(ctx.store.clinician) };
}),
route("PATCH", "/me", "step", (ctx) => {
const b = ctx.body ?? {};
const c = ctx.store.clinician;
const allowed = ["title", "displayName", "pronoun", "mobilePhone", "idleLockMinutes"];
if (!Object.keys(b).length || Object.keys(b).some((k) => !allowed.includes(k))) throw invalid("Check the highlighted fields.");
if (b.pronoun !== undefined && b.pronoun !== null && !["he", "she", "they"].includes(b.pronoun)) throw invalid("Check the highlighted fields.", [{ path: "pronoun", message: "Choose he, she or they" }]);
if (b.idleLockMinutes !== undefined && ![15, 30, 60].includes(b.idleLockMinutes)) throw invalid("Check the highlighted fields.", [{ path: "idleLockMinutes", message: "Choose 15, 30 or 60" }]);
if (b.displayName !== undefined) {
if (text(b.displayName, 80).length < 2) throw invalid("Check the highlighted fields.", [{ path: "displayName", message: "Too short" }]);
c.spokenName = text(b.displayName, 80);
}
if (b.title !== undefined) c.title = b.title ? text(b.title, 24) : "";
if (b.pronoun !== undefined) c.pronoun = b.pronoun;
if (b.mobilePhone !== undefined) c.mobilePhone = b.mobilePhone === null ? null : textable(b.mobilePhone, "mobilePhone");
if (b.idleLockMinutes) c.idleLockMinutes = b.idleLockMinutes;
return { clinician: wireClinician(c) };
}),
route("GET", "/clinic", "session", ({ store }) => ({ clinic: wireClinic(store.clinic) })),
route("PATCH", "/clinic", "step", (ctx) => {
const b = ctx.body ?? {};
const c = ctx.store.clinic;
if (b.expectedVersion !== undefined && b.expectedVersion !== c.version)
throw new HttpError(409, "CLINIC_CHANGED", "Someone changed the clinic details since you opened them. Reload and try again.");
if (b.phone && !/^\+?[0-9 ()-]{7,20}$/.test(String(b.phone))) throw invalid("Check the highlighted fields.", [{ path: "phone", message: "Use digits, spaces and an optional + only" }]);
if (b.name !== undefined) c.name = text(b.name, 120);
if (b.addressLine !== undefined) c.address = text(b.addressLine, 300);
if (b.phone !== undefined) c.phone = text(b.phone, 20);
if (b.timezone !== undefined) c.timeZone = knownZone(b.timezone);
if (b.openingHours !== undefined) c.openingHours = viewHours(weekly(b.openingHours, "openingHours"));
if (b.alertWatchHours !== undefined) c.alertWatchHours = viewHours(weekly(b.alertWatchHours, "alertWatchHours"));
if (b.backupAlertName !== undefined || b.backupAlertPhone !== undefined || b.backupAlertEmail !== undefined) {
const contact = { name: text(b.backupAlertName ?? "", 120), phone: b.backupAlertPhone ? textable(b.backupAlertPhone, "backupAlertPhone") : "",
email: b.backupAlertEmail ? String(b.backupAlertEmail).trim().toLowerCase() : "" };
if (contact.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(contact.email)) throw invalid("Check the highlighted fields.", [{ path: "backupAlertEmail", message: "Invalid email address" }]);
c.backupContact = contact.name || contact.phone || contact.email ? contact : null;
}
if (Array.isArray(b.emergencyNumbers))
c.emergencyNumbers = b.emergencyNumbers.map((n: { number: string; label: string; testDialledAt: string | null }) => ({ number: n.number, label: n.label, testedAt: n.testDialledAt, source: "clinic" as const }));
c.version += 1;
return { clinic: wireClinic(c) };
}),
];
const routes = [...accountRoutes, ...patientRoutes, ...checkInRoutes];
async function readBody(req: IncomingMessage, limit = 2_000_000): Promise<Buffer> {
const chunks: Buffer[] = [];
let size = 0;
for await (const chunk of req) {
size += chunk.length;
if (size > limit) throw new HttpError(413, "TOO_LARGE", "The request is too large.");
chunks.push(chunk);
}
return Buffer.concat(chunks);
}
// FIXTURE_RENAME="Mariam=Bola" renames a fictional person in every response (for a demo film).
const renames = (process.env.FIXTURE_RENAME ?? "").split(",").filter((r) => r.includes("=")).map((r) => r.split("=") as [string, string]);
const renamed = (text: string) => renames.reduce((t, [from, to]) => t.replaceAll(from, to), text);
const json = (res: ServerResponse, status: number, body: unknown) => {
if (res.headersSent) return;
res.writeHead(status, { "Content-Type": "application/json", "Cache-Control": "no-store" });
res.end(renamed(JSON.stringify(body)));
};
const cookie = (req: IncomingMessage) =>
Object.fromEntries((req.headers.cookie ?? "").split(";").map((p) => p.trim().split("=")).filter((p) => p.length === 2))[COOKIE] as string | undefined;
function authenticate(store: Store, req: IncomingMessage, level: Route["auth"]): Session | null {
const s = store.sessions.get(cookie(req) ?? "");
if (level === "public") return s ?? null;
if (!s) throw new HttpError(401, "SIGN_IN_REQUIRED", "Sign in to continue.");
const now = Date.now();
if (now - s.createdAt > TWELVE_HOURS) {
store.sessions.delete(s.token);
throw new HttpError(401, "SESSION_EXPIRED", "Your session ended after 12 hours. Sign in again.");
}
if (!s.locked && now - s.lastActiveAt > store.clinician.idleLockMinutes * 60_000) Object.assign(s, { stepUp: false, locked: true });
if (needsStepUp(level) && s.locked) throw new HttpError(403, "SESSION_LOCKED", "Your session locked while it wasn't in use. Enter a code from your authenticator app to continue.");
if (needsStepUp(level) && (!store.totpEnrolled || !s.stepUp)) throw new HttpError(403, "STEP_UP_REQUIRED", "Enter a code from your authenticator app to see patient records.");
if (level === "patients" && !store.clinician.termsAcceptedAt) throw new HttpError(403, "TERMS_REQUIRED", "Accept the clinician terms to work with real patients.");
// Background polls and streams do not count as activity (ACC-7).
if (req.headers["x-context-background"] !== "1") s.lastActiveAt = now;
return s;
}
/** Better Auth's email one-time code routes, as the clinician instance exposes them. */
async function authRoute(store: Store, req: IncomingMessage, res: ServerResponse, path: string) {
const body = JSON.parse((await readBody(req)).toString() || "{}");
if (path === "/email-otp/send-verification-otp") {
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(body.email ?? ""))) throw invalid("Enter your work email.");
console.log(`[fixture] sign-in code requested (${body.email === FIXTURE_EMAIL ? "known clinician" : "unknown email, no code sent"})`);
return json(res, 200, { success: true });
}
if (path === "/sign-in/email-otp") {
if (String(body.email).toLowerCase() !== FIXTURE_EMAIL || String(body.otp) !== FIXTURE_EMAIL_CODE)
throw new HttpError(400, "INVALID_OTP", "That code is not right or has expired. Request a new code.");
const token = randomUUID();
store.sessions.set(token, { token, email: FIXTURE_EMAIL, stepUp: false, locked: false, createdAt: Date.now(), lastActiveAt: Date.now() });
res.setHeader("Set-Cookie", `${COOKIE}=${token}; Path=/; HttpOnly; SameSite=Lax`);
return json(res, 200, { token: null });
}
if (path === "/sign-out") {
store.sessions.delete(cookie(req) ?? "");
res.setHeader("Set-Cookie", `${COOKIE}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`);
return json(res, 200, { success: true });
}
throw new HttpError(404, "NOT_FOUND", "Unknown sign-in route.");
}
function stream(ctx: Ctx) {
const call = ctx.store.calls.get(decodeURIComponent(ctx.params[0]));
if (!call || ctx.store.options.noSse) throw new HttpError(404, "NOT_FOUND", "No event stream here. Poll /updates instead.");
const after = Number(ctx.req.headers["last-event-id"] ?? ctx.url.searchParams.get("lastEventId") ?? 0) || 0;
ctx.res.writeHead(200, { "Content-Type": "text/event-stream", "Cache-Control": "no-cache, no-transform", "X-Accel-Buffering": "no", Connection: "keep-alive" });
const send = (e: W.CheckInEvent) => ctx.res.write(`id: ${e.id}\nevent: ${e.type}\ndata: ${renamed(JSON.stringify(e))}\n\n`);
ctx.res.write("retry: 3000\n\n");
call.events.filter((e) => e.id > after).forEach(send);
call.listeners.add(send);
const ping = setInterval(() => ctx.res.write(": ping\n\n"), 25_000);
ctx.req.on("close", () => {
call.listeners.delete(send);
clearInterval(ping);
});
}
/** The preview's demo API for Try a fictional patient: local reader only, no uploads. */
async function demoApi(req: IncomingMessage, res: ServerResponse, path: string) {
if (req.method === "GET" && path === "/api/status")
return json(res, 200, { engineVersion: ENGINE_VERSION, rulesVersion: RULES_VERSION, rulesReviewed: CHECK_SIGNED_OFF, modelConfigured: false, hosted: true, modelConnectionManaged: true, model: null, modelVerifiedAt: null });
if (req.method === "GET" && path === "/api/cases") return json(res, 200, CASES);
if (req.method === "GET" && path.startsWith("/api/reports/")) return json(res, 200, getReport(path.slice("/api/reports/".length), "fixture"));
if (req.method !== "POST") throw new HttpError(405, "METHOD", "Method not allowed.");
const parsed = JSON.parse((await readBody(req, 120_000)).toString() || "{}");
if (path === "/api/evaluate") return json(res, 200, runEvaluation());
if (path === "/api/extract") {
const data = baseSchema.extend({ useModel: z.boolean().default(false), freshModel: z.boolean().default(false) }).strict().parse(parsed);
if (data.useModel) throw invalid("The demo uses the local reader only.");
return json(res, 200, keepDraft(await extract(data.sources, false), data.sources, "fixture"));
}
if (path === "/api/assess") {
const data = baseSchema
.extend({ draftId: z.string().uuid(), review: z.record(z.string(), z.enum(["include", "exclude"])), coverageReview: coverageReviewSchema, fingerprint: z.string().length(64), acknowledgeUnparsed: z.boolean() })
.strict()
.parse(parsed);
const draft = getDraft(data.draftId, data.sources, "fixture");
return json(res, 200, keepReport(data.draftId, assess(data.sources, data.review, data.fingerprint, data.acknowledgeUnparsed, draft, data.coverageReview), data.review, "fixture"));
}
throw new HttpError(404, "NOT_FOUND", "Unknown endpoint.");
}
const mime: Record<string, string> = { ".html": "text/html", ".js": "text/javascript", ".css": "text/css", ".svg": "image/svg+xml", ".png": "image/png", ".ttf": "font/ttf", ".woff2": "font/woff2", ".json": "application/json" };
export function createFixtureServer(options: Options, distDir: string) {
const store = createStore(options);
const root = resolve(distDir);
const server = createServer(async (req, res) => {
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "127.0.0.1"}`);
const path = url.pathname;
try {
if (path === "/__fixture/variant" && req.method === "POST") {
store.nextVariant = (JSON.parse((await readBody(req)).toString() || "{}").variant ?? null) as Variant | null;
return json(res, 200, { nextVariant: store.nextVariant });
}
if (path.startsWith("/__fixture/uploads/") && req.method === "PUT") {
const up = store.uploads.get(path.split("/").pop()!);
if (!up) throw new HttpError(404, "NOT_FOUND", "Unknown upload.");
up.data = await readBody(req, 16 * 1024 * 1024);
res.writeHead(200).end();
return;
}
if (path.startsWith("/api/auth/clinician/")) return await authRoute(store, req, res, path.slice("/api/auth/clinician".length));
if (path.startsWith("/api/clinician/")) {
const sub = path.slice("/api/clinician".length);
if (req.method === "GET" && /^\/check-ins\/[^/]+\/events$/.test(sub)) {
const session = authenticate(store, req, "patients");
return stream({ req, res, url, params: [sub.split("/")[2]], body: null, session, store });
}
for (const r of routes) {
const match = r.method === req.method ? r.pattern.exec(sub) : null;
if (!match) continue;
const session = authenticate(store, req, r.auth);
const raw = req.method === "GET" ? "" : (await readBody(req)).toString();
const body = raw ? JSON.parse(raw) : {};
const result = await r.handler({ req, res, url, params: match.slice(1), body, session, store });
if (result !== undefined) json(res, r.status, result);
return;
}
throw new HttpError(404, "NOT_FOUND", "Unknown clinician endpoint.");
}
if (path.startsWith("/api/")) return await demoApi(req, res, path);
const file = resolve(root, `.${decodeURIComponent(path)}`);
if (!file.startsWith(root + sep) && file !== root) throw new HttpError(403, "FORBIDDEN", "Invalid path.");
const target = extname(file) ? file : resolve(root, "index.html");
const data = await readFile(target).catch(() => null);
if (!data) throw new HttpError(404, "NOT_FOUND", extname(file) ? "File not found." : "Run npm run build first.");
res.writeHead(200, { "Content-Type": mime[extname(target)] ?? "application/octet-stream" });
res.end(data);
} catch (error) {
if (error instanceof HttpError) return json(res, error.status, { code: error.code, message: error.message, ...(error.details === undefined ? {} : Array.isArray(error.details) ? { details: error.details } : (error.details as object)) });
if (error instanceof z.ZodError || error instanceof SyntaxError) return json(res, 400, { code: "VALIDATION_ERROR", message: "The request is not valid." });
json(res, 400, { code: "REQUEST_FAILED", message: error instanceof Error ? error.message : "The request failed." });
}
});
return { server, store };
}