End‑to‑end specification for a Mock HL7 Analyzer with a Vite + React + TypeScript frontend and a Node.js (Express) TypeScript backend. The system simulates a lab analyzer that accepts HL7 v2 orders (OML/ORM) over MLLP, manages a worklist, generates results, and emits ORU/OUL back to CARE. Includes REST APIs, WebSocket feeds, TCP endpoints, UI pages, data contracts, and operational considerations.
- 
Frontend: Vite + React + TS, shadcn/ui, React Router, Zustand (or Redux Toolkit) for state, WebSocket for live logs, Axios for REST.
 - 
Backend: Node 20 + TypeScript; Express for REST; ws for WebSocket; custom MLLP server/client; Prisma ORM (SQLite dev, Postgres prod); Zod for schema validation; pino for logs; Prometheus metrics.
 - 
Services:
- MLLP IN: listens for OML/ORM; ACKs; persists work.
 - Scheduler/Engine: transitions queued→processing→prelim→final; generates values.
 - MLLP OUT: sends ORU/OUL to configured endpoints; handles ACK/Retry.
 - REST & WS: configuration, worklist, scenarios, logs, stats.
 
 
| Purpose | Port | Protocol | 
|---|---|---|
| Web UI + REST API | 8080 | HTTP/HTTPS (Express) | 
| Live Logs / Events | 8080/ws | WebSocket (JSON messages) | 
| MLLP IN (orders) | 2575 | TCP (MLLP) / optional TLS on 2576 | 
| MLLP OUT (results) | 2577 | TCP (MLLP client to CARE) / optional TLS on 2578 | 
MLLP framing: 0x0B … 0x1C 0x0D.
.env
NODE_ENV=development
PORT_HTTP=8080
PORT_WS=8080
PORT_MLLP_IN=2575
CARE_MLLP_HOST=care-middleware
CARE_MLLP_PORT=2577
MLLP_TLS=false
ACK_TIMEOUT_MS=5000
RETRY_MAX=5
RETRY_BACKOFF_MS=2000
PRELIM_ENABLED=true
DUR_QUEUE_TO_START_MS=3000
DUR_ANALYSIS_MS=5000
DUR_PRELIM_TO_FINAL_MS=4000
DB_URL=file:./dev.db
AUTH_MODE=apikey
API_KEY=change-me
Patient(id, mrn, name, birthDate, sex)
Specimen(id, accession, type, collectedAt, patientId)
WorkItem(id, specimenId, testCode, loinc, name, status, startedAt, finishedAt, channel, scenarioId)
Result(id, workItemId, value, unit, refLow, refHigh, flag, method, deviceId, status, effectiveAt)
Endpoint(id, name, host, port, tls, mode, ackRequired, lastAckLatencyMs)
CatalogTest(id, deviceCode, loinc, name, unit, refLow, refHigh, dtype, genMin, genMax, dist, abnormalProb)
MessageLog(id, direction, type, raw, parsedJson, sha256, status, correlationKey, createdAt)
Scenario(id, name, stepsJson, createdAt)
User(id, email, role)
status enums: WorkItem: queued|in_process|preliminary|final|corrected. Result: prelim|final|corrected.
- 
GET /api/config → Current configuration
 - 
PUT /api/config → Update config
{ "resultsEndpoint": {"host":"care-mw","port":2577,"tls":false,"mode":"ORU"}, "ackTimeoutMs":5000, "retry": {"max":5,"backoffMs":2000}, "prelimEnabled": true, "durations": {"queueToStartMs":3000, "analysisMs":5000, "prelimToFinalMs":4000} } 
- GET /api/catalog/tests → list
 - PUT /api/catalog/tests → bulk upsert array of tests
 - PUT /api/catalog/mappings/loinc → upsert device↔LOINC
 - PUT /api/catalog/mappings/units → unit normalization map
 
- 
POST /api/orders/hl7 → Ingest raw HL7 order (bypass MLLP)
{"hl7":"<raw HL7 string>"} - 
GET /api/worklist?state=queued|in_process|final|*"
 - 
POST /api/worklist/{id}/start → move to in_process
 - 
POST /api/worklist/{id}/prelim → mark prelim (optional)
 - 
POST /api/worklist/{id}/finalize → finalize with optional value override
{"value":"6.8","unit":"mmol/L","flag":"H","note":"manual override"} 
- 
POST /api/results/emit → emit final results as ORU/OUL
{"workItemIds":["wi_123","wi_456"], "mode":"ORU", "splitPanels":true} - 
POST /api/results/correct → send corrected result (OBX-11=C)
{"observationId":"obs_1","newValue":"7.1","reason":"delta check"} 
- 
POST /api/scenarios → define & run scenario
{ "name":"lost-ack-then-retry", "steps":[ {"action":"ingest-hl7","template":"oml_o21","vars":{ "SR":"SR-001", "ACC":"ACC-789", "TEST":"2345-7"}}, {"action":"set-failure","type":"drop-ack","count":1}, {"action":"advance","state":"final","afterMs":4000}, {"action":"emit-results"} ] } - 
POST /api/failures → inject transient failures
{"type":"bad-unit"} 
- GET /api/messages?direction=in|out&type=ORM,OML,ORU,OUL,ACK&limit=100
 - GET /api/messages/{id} → raw + parsed + ack chain
 - GET /api/stats → throughput, ack latency, errors, queue depth
 
- Event types: 
mllp_in,mllp_out,ack,state_change,error,metric. - Payload example:
 
{"type":"mllp_out","ts":"2025-10-18T12:34:56Z","messageType":"ORU^R01","msgId":"MSG123","correlationKey":"MSG123.SR-001.ACC-789"}- Listens on 
PORT_MLLP_IN(2575). Accepts OML^O21 / ORM^O01. - Parses 
MSH, PID, ORC, OBR, [SPM]→ creates Patient (if needed), Specimen, WorkItems. - Returns ACK (
MSA|AA) orAE/ARon parse error. - Idempotency: 
{MSH-10}.{ORC-2}.{ORC-3}. 
- Cron/timers move items through states using 
DUR_*config. - Deterministic generator by seed + test distribution; optional random noise.
 
- Composes ORU^R01 (default) or OUL^R22 (LAW mode).
 - Handles ACK wait (ACK_TIMEOUT_MS), retry with backoff up to RETRY_MAX.
 - Dead-letter queue on exhausted retries; surfaces via 
/api/statsand WS. 
MSH|^~\&|CARE|CAREHOSP|MOCKDEV|ANALYZER|{DTM}||OML^O21|{MSGID}|P|2.5.1
PID|1||{MRN}^^^CARE^MR||{LAST}^{FIRST}||{DOB}|{SEX}
ORC|NW|{PLACER}|{FILLER}
OBR|1|{PLACER}|{FILLER}|{TEST}^{NAME}^{LN}
SPM|1|{ACCESSION}|||{COLLDT}
MSH|^~\&|MOCKDEV|LAB|CARE|HOSP|{DTM}||ORU^R01|{MSGID}|P|2.5.1
PID|1||{MRN}^^^CARE^MR||{LAST}^{FIRST}||{DOB}|{SEX}
ORC|RE|{PLACER}|{FILLER}
OBR|1|{PLACER}|{FILLER}|{TEST}^{NAME}^{LN}||||||||||||||||||F
SPM|1|{ACCESSION}
OBX|1|NM|{TEST}^{NAME}^{LN}||{VALUE}|{UCUM}^{UCUM}^UCUM|{LOW}-{HIGH}|{FLAG}|F|||{EFFT}|||{METHOD}|{DEVICEID}
- / Dashboard: summary metrics, connection status, queue depth, ACK latency.
 - /worklist Worklist: table (Accession, Patient, Test, Status, Value, Actions).
 - /analyzer Analyzer view: channels, sliders (throughput, error prob), start/finalize.
 - /mappings Test catalog & code↔LOINC mappings editor.
 - /endpoints Configure outbound endpoints, ACK policy, TLS; test connection.
 - /messages Message log: live stream + filters; detail drawer (raw HL7 | parsed JSON | ACK chain).
 - /scenarios Scenario runner & failure injection.
 - /settings App settings, API key management, PII redaction toggle.
 
StatusBar(MLLP lights, mode switch, metrics)WorklistTable(virtualized); row actions: Start, Prelim, Finalize, Emit, CorrectMessageViewer(syntax-highlight HL7, JSON pane)EndpointForm,MappingGrid,ScenarioBuilderMetricsCards(Prometheus snapshots via REST)
- Zustand slices: 
config,worklist,messages,scenarios,metrics. - WebSocket listener → dispatches live events to stores.
 - Axios instance with API key and retry; Zod schemas for responses.
 
- Happy path: Order arrives → appears in Worklist (queued) → auto start → prelim/final → Emit → ACK OK.
 - Correction: Select final row → Correct → input new value → emit corrected ORU (OBX-11=C).
 - Failure test: Run scenario → UI shows dropped ACK, retries, eventual success or DLQ.
 
- REST: API key or JWT; CORS locked to expected origins.
 - Optional mTLS for MLLP.
 - Roles: 
operator,engineer,admin(feature gating in UI and REST guards). - PII masking in logs (configurable); raw HL7 accessible to 
engineer/adminonly. 
- /api/stats for UI cards; /metrics Prometheus: 
mllp_in_total,mllp_out_total,ack_latency_seconds,order_queue_depth,retries_total,dlq_total. - Pino structured logs (JSON); request/response IDs; correlation keys included.
 
docker-compose.yml (excerpt)
services:
  analyzer:
    build: ./server
    env_file: .env
    ports:
      - "8080:8080"
      - "2575:2575"
    depends_on: [db]
  web:
    build: ./web
    environment:
      - VITE_API_BASE=http://localhost:8080
    ports:
      - "5173:5173"
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD=dev
      POSTGRES_DB=mock_analyzer
    ports:
      - "5432:5432"
- Unit: HL7 parser/serializer, value generator, MLLP framing.
 - Integration: send OML via MLLP; assert ACK; transition to final; emit ORU; assert CARE ACK (use a mock MLLP server in CI).
 - E2E: Cypress/Playwright for UI flows; record/replay HL7 samples; scenario runs.
 - Conformance: validate messages with HL7 v2 profiles (z-schema/Zod for fields; optional NIST tooling offline).
 
export type WorkItemDTO = {
  id: string;
  accession: string;
  patient: { mrn: string; name: string };
  test: { deviceCode: string; loinc?: string; name: string };
  status: 'queued'|'in_process'|'preliminary'|'final'|'corrected';
  value?: string; unit?: string; flag?: 'L'|'N'|'H';
  startedAt?: string; finishedAt?: string;
};export type ConfigDTO = {
  resultsEndpoint: { host: string; port: number; tls: boolean; mode: 'ORU'|'OUL' };
  ackTimeoutMs: number;
  retry: { max: number; backoffMs: number };
  prelimEnabled: boolean;
  durations: { queueToStartMs: number; analysisMs: number; prelimToFinalMs: number };
};- Receive OML/ORM on 2575; respond with ACK; order visible in Worklist within 1s.
 - Auto-process to final within configured durations; REST reflects state transitions.
 - Emit ORU to configured CARE endpoint; receive ACK within 
ACK_TIMEOUT_MS; retries on failure. - Message logs store raw HL7 + parsed JSON + correlation keys; searchable via REST & visible in UI.
 - WebSocket broadcasts events for UI real-time updates.
 - Security: API key enforced for all REST write ops; PII masking toggle works.