Closes the Calculators migration. AAP 2017 BP percentile (Rosner
quantile splines) was the biggest table-driven calculator in the
codebase: 6 height-LMS arrays × 218 entries + 4 spline coefficient
matrices × 99 rows × 13 terms = ~3,500 numeric constants. Every one
ported verbatim. 14 parity tests prove the TS port returns identical
percentile and classification outputs to the vanilla calculators.js.
shared/clinical/bp.ts — 571 lines
Generated from public/js/calculators.js via awk-extracted lines
89-94 (LMS) and 97-503 (coefficients), wrapped in TS export
declarations. No rewriting, no reformatting, no reordering — the
data bytes are identical to the vanilla source.
Math (calcHeightPercentile, computeBpPercentile,
classifyBpFromPercentiles) ported verbatim from calculators.js:
505-608. Exports a top-level computeBp() helper returning
{ sysPercentile, diaPercentile, heightPercentile, sysClass,
diaClass, classification }.
scripts/capture-calc-vectors.js — BP cases added
Uses new Function() to evaluate the raw LMS + coefficient blocks
from calculators.js directly, then runs the vanilla math against
14 carefully chosen test cases:
• Typical pediatric ages (3, 5, 8, 10, 12 years, both sexes)
• Adult-threshold cross-over (age 13 — uses absolute mmHg cutoffs)
• Stage 1 / Stage 2 hypertension boundaries
• Edge-of-domain (age 1, age 17)
• Tall / short height-percentile outliers
Fixture regenerated to 14 Bhutani + 58 AAP + 13 Fenton + 8 neonatal
+ 12 BMI + 14 BP = 117 total vectors.
shared/clinical/bp.test.ts — exact-match parity
sysPercentile / diaPercentile are integer selections from 99
candidate predicted values, so tests use .toBe() for exact match.
heightPercentile uses toBeCloseTo(6) (double-precision float).
Classification strings must match exactly. 14/14 pass.
client/src/pages/CalculatorPanels.tsx — BpPanel added
Age/sex/height/SBP/DBP inputs, validation (age 1-17, height 50-200
cm), color-coded overall classification + per-measurement
percentile and tier (Normal / Elevated / Stage 1 / Stage 2) in a
4-column result grid with the height percentile for context.
client/src/pages/Calculators.tsx
Dispatch wires bp → BpPanel. PILLS['bp'].ported = true.
LegacyPanel is now dead code — every pill has a real implementation.
Final test suite: 5 files, 136 tests green
(19 calculators + 70 bilirubin + 21 fenton/neonatal + 12 BMI + 14 BP)
Nine of the ten Calculator pills now run in React. Only BP Percentile
(Rosner quantile splines, ~3,000 hand-transcribed coefficients across
6 LMS arrays and 4 spline matrices) remains legacy-linked; that port
deserves its own dedicated session with extra care.
shared/clinical/bmi.ts — CDC 2000 BMI-for-age
• bmiLMS table ported byte-for-byte from calculators.js:739
(74 LMS triples = 37 age points × 2 sexes, 24-240 months).
• normalCDF (Abramowitz & Stegun), calcBmiPercentile, classifyBMI
(including the %-of-95th severe-obesity split), and a top-level
computeBmi helper — all verbatim translations of the vanilla math.
• classification labels preserved exactly so existing e2e screenshots
or reporting continue to read the same text ('Class 2 Severe
Obesity', 'Healthy Weight', etc.).
shared/clinical/bmi.test.ts — 12 captured vectors covering:
both sexes, edges (2y + 20y), interpolated-between-keys (13m),
each classification cliff (underweight / healthy / overweight /
obese / severe class 2 / severe class 3), and the age-clamping
branches (<24 mo and >240 mo). All 12 pass at 10-decimal precision
(percentile to 6 places since the vanilla rounds to 2).
scripts/capture-calc-vectors.js — BMI section added
Same pattern as bilirubin / Fenton: the vanilla data + math are
inlined verbatim, the script runs 12 chosen cases, and writes to
e2e/fixtures/calc-vectors.json. Re-run after any upstream change.
client/src/pages/CalculatorPanels.tsx (new)
• BmiPanel — age/sex/weight/height inputs, calls computeBmi,
renders color-coded classification badge with BMI, percentile,
Z, and % of 95th when percentile ≥85.
• VitalsPanel — 8-band age selector (premie → >12 yr).
VITALS_DATA ported verbatim from calculators.js:1703-1831 with
every HR/RR/SBP/DBP/temp/weight/SpO₂ range and clinical notes
preserved entry-for-entry.
• ResusPanel — weight input drives all 14 drugs (Adenosine,
Amiodarone, Atropine, CaCl, Ca-gluconate, Dextrose, Epi,
Hydrocortisone, Insulin, Lidocaine, Mg, Naloxone, NaHCO₃) with
calc() closures ported verbatim from RESUS_MEDS lines 1873-2050.
Category colors + labels preserved.
• EquipmentPanel — 9-band age selector (premie → 16+ yr).
EQUIP_DATA ported verbatim from calculators.js:2173-2228 with
12 equipment sizes per band (BVM, NPA, OPA, blade, ETT, LMA,
Glidescope, IV, CVL, NGT, chest tube, Foley).
client/src/pages/Calculators.tsx
Dispatch wires bmi → BmiPanel, vitals → VitalsPanel, resus →
ResusPanel, equipment → EquipmentPanel. PILLS flags all four as
ported: true. Only bp remains on LegacyPanel.
Backend tsc + client tsc + vite build + 122/122 vitest (19 calc +
70 bili + 21 fenton/neonatal + 12 bmi) all green.
Finishes Bedside parity. Neonatal, Respiratory, Ventilation, Sepsis,
and Burns replace their LegacyPanel fallbacks. All 15 vanilla
Bedside sub-modules are now real React components.
shared/clinical/fenton.ts — second, higher-accuracy Fenton table
fentonLmsPeditools: 21 GA weeks × 2 sexes (22-42 in 1-week steps)
ported verbatim from public/js/bedside/neonatal.js:20-37. This is
the peditools-derived table that superseded the older hand-rounded
version (still used by the Growth Charts calculator). Adds
calcZNeonatal (|L|<0.001 threshold) + zToPercentileNeonatal
(Abramowitz & Stegun erf form) so neonatal output matches the
vanilla Bedside numbers to 3 decimal places.
neonatalAssess() returns { gaDecimal, gaClass, bwClass,
weightClass, expectedWeight, z, percentile, L, M, S }.
scripts/capture-calc-vectors.js + e2e/fixtures/calc-vectors.json
Adds 8 neonatal vectors (including the 40w5d male 3070g validated
case from the vanilla file's own comment: z = -1.42).
shared/clinical/fenton.test.ts — now covers neonatal too (110 total
vitest cases pass: 19 calculators, 21 fenton, 70 bilirubin).
client/src/pages/BedsidePanels2.tsx — five new panels
• NeonatalPanel — GA+weight+sex inputs drive the Fenton assessment
(gestational-age class, weight-for-GA class, birth-weight
category, Z-score, percentile, expected M). Full NRP pathway
cards (birth→HR<100→HR<60 escalation), NRP drug table scaled
by weight (epi IV/IO/ETT, NS bolus, D10), and 5-element Apgar
scorer with reassuring/moderately-depressed/severely-depressed
guidance.
• RespiratoryPanel — four sub-modes: Asthma (mild/moderate/
severe with full drug tables + "when to intubate" / ABG /
heliox clinical decision boxes), PRAM scorer (0-12),
Westley croup scorer (0-17 with severity-tiered treatment),
Bronchiolitis admission decision tree (age / SpO₂ / hydration
/ distress) with AAP "NOT recommended" list.
• VentilationPanel — target SpO₂ table by population, 6-step
escalation ladder (NC → FM → NRB → HFNC → NIV → intubation)
with live-scaled HFNC flow (1-2 L/kg/min), BVM how-to,
mechanical vent starting settings (TV, rate by age, PEEP,
FiO₂, I:E), gas-exchange adjustment table, and the
oxygenation-vs-ventilation mental model.
• SepsisPanel — Phoenix Sepsis Criteria (JAMA 2024), red-flag
list, age-banded workup + empirical therapy (neonate /
infant / child abx drugs keyed to weight), SSC 2020 first-hour
bundle (0-5 min recognize → >60 min refractory-shock
vasoactive), and resuscitation targets.
• BurnsPanel — full 19-region Lund-Browder age-adjusted table
(ported verbatim), with per-region % input + live TBSA
computation + override. Parkland formula (4 × kg × TBSA,
8h/16h split + per-hr rates), 4-2-1 maintenance, UOP targets,
pearl list (palm rule, no first-degree, analgesia, tetanus),
and ABA burn-center referral criteria.
client/src/pages/BedsidePanels.tsx — dispatch extended to all 15
pills. REAL_BEDSIDE_PANELS now contains every Bedside pill id, so
the legacy-link fallback is dead code that can be pruned later.
Client tsc + vite build + 110/110 vitest tests all green.
Ships the AAP 2022 phototherapy/exchange nomograms, the Bhutani 1999
risk zones, and the Fenton 2013 preterm weight-for-GA chart as real
React calculators. These were the high-risk "table-driven" ports the
migration checkpoint flagged as needing captured vectors before
landing — the whole vector-capture workflow now exists and can be
reused for BP / BMI / growth-beyond-Fenton next.
Workflow, so future calculator ports have a template:
scripts/capture-calc-vectors.js
Standalone Node script. Data tables + math inlined VERBATIM from
public/js/calculators.js (no rewriting, no reformatting). Picks 83
carefully chosen test cases — edge-of-domain, table-key-exact,
interpolated-between-keys, and clinical-decision-boundary values —
and emits e2e/fixtures/calc-vectors.json with
{ inputs, output } pairs produced by the authoritative math.
e2e/fixtures/calc-vectors.json
12 Bhutani cases, 58 AAP 2022 cases, 13 Fenton cases. Re-run
capture-calc-vectors.js whenever the vanilla file changes.
shared/clinical/bilirubin.ts
• 17 HourTable constants ported byte-for-byte from calculators.js
lines 1489-1512: photo 35w/36w/37w/38w/39w/40+ (low risk) +
35w/36w/37w/38+ (medium risk), same 8 for exchange.
• Bhutani zones (p95/p75/p40) from lines 1644-1651.
• interpolateThreshold helper (lines 1514-1525).
• classifyBhutani + classifyAapBili functions mirror the vanilla
click-handler logic.
shared/clinical/fenton.ts
• 15-week × 2-sex LMS table from lines 1168-1183 preserved entry-
for-entry.
• interpolateLMS + calcZ + zToPercentile (Abramowitz & Stegun
normal CDF, lines 2299-2326) ported byte-for-byte.
• fentonWeightForAge returns { L, M, S, z, percentile };
classifySizeForAge labels SGA/<10 / AGA / LGA/>90.
shared/clinical/bilirubin.test.ts + fenton.test.ts
Vitest suites that import e2e/fixtures/calc-vectors.json and assert
classifyBhutani / classifyAapBili / fentonWeightForAge match
every captured vector to 10 decimal places for threshold values
and 6 decimals for the Fenton M (grams, so 6 is >= 1e-3 g).
All 102 tests pass locally (19 prior + 70 bili + 13 Fenton).
client/src/pages/Calculators.tsx
• BiliPanel with AAP 2022 / Bhutani mode switch, GA + risk-factor
dropdowns, color-coded status / zone badges, both threshold
pairs surfaced in the result grid.
• GrowthPanel runs Fenton with sex + GA + weight inputs, emits
Z-score, percentile, L/M/S reference, and SGA/AGA/LGA label.
• PILLS flags bili + growth as ported: true; ActivePanel routes to
the new components. BP / BMI / vitals / resus / equipment
remain legacy-linked until their own vectors land.
e2e/tests/calculators-react.spec.js
Three new parity tests covering above-phototherapy/above-exchange
transitions (38w 72h TSB 20 → phototherapy; TSB 26 → exchange),
Bhutani high-risk classification at 36h TSB 13, and Fenton 32w
male 1795g landing exactly at 50th percentile AGA.
Backend tsc + client tsc + vite build + vitest (102/102) all green.
Bundle 619 kB / 178 kB gz (+10 kB for the bili tables).