@precisa-saude/fhir-calculators 0.1.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +28 -20
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/fhir-brasil/fhir-brasil/packages/calculators/dist/index.cjs","../src/phenoage/index.ts","../src/phenoage/constants.ts","../src/phenoage/calculator.ts","../src/phenoage/unit-converters.ts","../src/brdmrisc/index.ts","../src/brdmrisc/constants.ts","../src/brdmrisc/calculator.ts","../src/brdmrisc/unit-converters.ts","../src/derived/calculator.ts"],"names":["BIOMARKER_NAMES_PT","BIOMARKER_RANGES","validateBiomarkers","CONVERSION_FACTORS","TARGET_UNITS"],"mappings":"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AA;AACR,IAAA;AACF,EAAA;AACA,EAAA;AACO,IAAA;AACS,IAAA;AACP,MAAA;AACC,MAAA;AACS,MAAA;AACjB,IAAA;AACI,IAAA;AACO,IAAA;AACA,IAAA;AACL,IAAA;AACE,IAAA;AACR,IAAA;AACF,EAAA;AACA,EAAA;AACO,IAAA;AACS,IAAA;AACP,MAAA;AACE,MAAA;AACD,MAAA;AACS,MAAA;AACjB,IAAA;AACI,IAAA;AACO,IAAA;AACA,IAAA;AACL,IAAA;AACE,IAAA;AACR,IAAA;AACF,EAAA;AAAA;AAGA,EAAA;AACO,IAAA;AACS,IAAA;AACP,MAAA;AACU,MAAA;AACR,MAAA;AACT,IAAA;AACI,IAAA;AACO,IAAA;AACA,IAAA;AACL,IAAA;AACE,IAAA;AACR,IAAA;AACF,EAAA;AACA,EAAA;AACO,IAAA;AACS,IAAA;AACP,MAAA;AACU,MAAA;AACV,MAAA;AACE,MAAA;AACT,IAAA;AACI,IAAA;AACO,IAAA;AACA,IAAA;AACL,IAAA;AACE,IAAA;AACR,IAAA;AACF,EAAA;AACA,EAAA;AACO,IAAA;AACS,IAAA;AACP,MAAA;AACU,MAAA;AACR,MAAA;AACA,MAAA;AACT,IAAA;AACI,IAAA;AACO,IAAA;AACA,IAAA;AACL,IAAA;AACE,IAAA;AACR,IAAA;AACF,EAAA;AACA,EAAA;AACO,IAAA;AACS,IAAA;AACP,MAAA;AACU,MAAA;AACV,MAAA;AACE,MAAA;AACA,MAAA;AACT,IAAA;AACI,IAAA;AACO,IAAA;AACA,IAAA;AACL,IAAA;AACE,IAAA;AACR,IAAA;AACF,EAAA;AACA,EAAA;AACO,IAAA;AACS,IAAA;AACP,MAAA;AACU,MAAA;AACV,MAAA;AACU,MAAA;AACR,MAAA;AACT,IAAA;AACI,IAAA;AACO,IAAA;AACA,IAAA;AACL,IAAA;AACE,IAAA;AACR,IAAA;AACF,EAAA;AACA,EAAA;AACO,IAAA;AACS,IAAA;AACP,MAAA;AACU,MAAA;AACV,MAAA;AACC,MAAA;AACS,MAAA;AACR,MAAA;AACT,IAAA;AACI,IAAA;AACO,IAAA;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","file":"/home/runner/work/fhir-brasil/fhir-brasil/packages/calculators/dist/index.cjs","sourcesContent":[null,"export * from './calculator';\nexport * from './constants';\nexport * from './types';\nexport * from './unit-converters';\n","/**\n * PhenoAge Calculator Constants\n *\n * Coefficients from Levine et al. (2018) Table 1\n * Units: albumin (g/L), creatinine (μmol/L), glucose (mmol/L),\n * CRP (ln of mg/L), lymphocyte (%), MCV (fL), RDW (%),\n * ALP (U/L), WBC (10^9/L), age (years)\n */\n\n/**\n * Regression coefficients from the Cox proportional hazards model\n */\nexport const PHENOAGE_COEFFICIENTS = {\n age: 0.0804,\n albumin: -0.0336,\n alkalinePhosphatase: 0.0019,\n creatinine: 0.0095,\n glucose: 0.1953,\n intercept: -19.9067,\n logCrp: 0.0954,\n lymphocytePercent: -0.012,\n mcv: 0.0268,\n rdw: 0.3306,\n wbc: 0.0554,\n} as const;\n\n/**\n * Gompertz mortality model parameters\n */\nexport const GOMPERTZ_PARAMS = {\n ageCoefficient: 0.090165,\n baseAge: 141.50225,\n gamma: 0.0076927,\n mortalityConstant: 0.00553,\n} as const;\n\n/**\n * Biomarker reference ranges for validation\n */\nexport const BIOMARKER_RANGES = {\n albumin: { max: 60, min: 10, typical: { max: 50, min: 35 }, unit: 'g/L' },\n alkalinePhosphatase: { max: 500, min: 10, typical: { max: 130, min: 40 }, unit: 'U/L' },\n chronologicalAge: { max: 120, min: 18, typical: { max: 100, min: 18 }, unit: 'anos' },\n creatinine: { max: 500, min: 20, typical: { max: 110, min: 60 }, unit: 'μmol/L' },\n crp: { max: 200, min: 0.01, typical: { max: 3, min: 0 }, unit: 'mg/L' },\n glucose: { max: 30, min: 2, typical: { max: 6.0, min: 4.0 }, unit: 'mmol/L' },\n lymphocytePercent: { max: 80, min: 1, typical: { max: 40, min: 20 }, unit: '%' },\n mcv: { max: 150, min: 50, typical: { max: 100, min: 80 }, unit: 'fL' },\n rdw: { max: 25, min: 8, typical: { max: 15, min: 11 }, unit: '%' },\n wbc: { max: 30, min: 1, typical: { max: 11.0, min: 4.0 }, unit: '10^9/L' },\n} as const;\n\n/**\n * Mapping from FHIR biomarker codes to PhenoAge input fields\n */\nexport const FHIR_CODE_TO_PHENOAGE: Record<string, keyof typeof BIOMARKER_RANGES> = {\n Albumin: 'albumin',\n AlkalinePhosphatase: 'alkalinePhosphatase',\n Creatinine: 'creatinine',\n CRP: 'crp',\n Glucose: 'glucose',\n Lymphocytes: 'lymphocytePercent',\n MCV: 'mcv',\n RDW: 'rdw',\n WBC: 'wbc',\n};\n\n/**\n * Required biomarker codes for PhenoAge calculation\n */\nexport const REQUIRED_BIOMARKERS = [\n 'Albumin',\n 'Creatinine',\n 'Glucose',\n 'CRP',\n 'Lymphocytes',\n 'MCV',\n 'RDW',\n 'AlkalinePhosphatase',\n 'WBC',\n] as const;\n\n/**\n * Portuguese names for biomarkers (for display)\n */\nexport const BIOMARKER_NAMES_PT: Record<string, string> = {\n age: 'Idade',\n albumin: 'Albumina',\n alkalinePhosphatase: 'Fosfatase Alcalina',\n creatinine: 'Creatinina',\n crp: 'PCR',\n glucose: 'Glicose',\n intercept: 'Intercepto',\n lymphocytePercent: 'Linfócitos',\n mcv: 'VCM',\n rdw: 'RDW',\n wbc: 'Leucócitos',\n};\n\n/**\n * Short descriptions for biomarkers (for tooltips)\n * Explains what each biomarker indicates in the context of aging\n */\nexport const BIOMARKER_DESCRIPTIONS_PT: Record<string, string> = {\n age: 'Sua idade cronológica em anos',\n albumin: 'Proteína do fígado que indica estado nutricional e função hepática',\n alkalinePhosphatase: 'Enzima que reflete saúde do fígado e dos ossos',\n creatinine: 'Marcador de função renal produzido pelos músculos',\n crp: 'Proteína C-reativa, indica inflamação no corpo',\n glucose: 'Nível de açúcar no sangue, relacionado ao metabolismo',\n intercept: 'Constante da fórmula de regressão',\n lymphocytePercent: 'Células de defesa do sistema imunológico',\n mcv: 'Volume médio das hemácias, indica saúde das células vermelhas',\n rdw: 'Variação no tamanho das hemácias, marcador de inflamação',\n wbc: 'Total de células brancas, indica estado imunológico',\n};\n\n/**\n * Biomarker information for lab order requests\n * Includes LOINC codes and English names for lab requisitions\n */\nexport interface BiomarkerLabInfo {\n loincCode: string;\n nameEn: string;\n namePt: string;\n}\n\n/**\n * Lab order information for PhenoAge biomarkers\n * Mapped by FHIR code\n */\nexport const BIOMARKER_LAB_INFO: Record<string, BiomarkerLabInfo> = {\n Albumin: {\n loincCode: '1751-7',\n nameEn: 'Albumin [Mass/volume] in Serum or Plasma',\n namePt: 'Albumina',\n },\n AlkalinePhosphatase: {\n loincCode: '6768-6',\n nameEn: 'Alkaline phosphatase [Enzymatic activity/volume] in Serum or Plasma',\n namePt: 'Fosfatase Alcalina',\n },\n Creatinine: {\n loincCode: '2160-0',\n nameEn: 'Creatinine [Mass/volume] in Serum or Plasma',\n namePt: 'Creatinina',\n },\n CRP: {\n loincCode: '1988-5',\n nameEn: 'C reactive protein [Mass/volume] in Serum or Plasma',\n namePt: 'Proteína C-Reativa (PCR)',\n },\n Glucose: {\n loincCode: '2345-7',\n nameEn: 'Glucose [Mass/volume] in Serum or Plasma',\n namePt: 'Glicose',\n },\n Lymphocytes: {\n loincCode: '736-9',\n nameEn: 'Lymphocytes/100 leukocytes in Blood by Automated count',\n namePt: 'Linfócitos (%)',\n },\n MCV: {\n loincCode: '787-2',\n nameEn: 'MCV [Entitic volume] by Automated count',\n namePt: 'Volume Corpuscular Médio (VCM)',\n },\n RDW: {\n loincCode: '788-0',\n nameEn: 'Erythrocyte distribution width [Ratio] by Automated count',\n namePt: 'Amplitude de Distribuição dos Eritrócitos (RDW)',\n },\n WBC: {\n loincCode: '6690-2',\n nameEn: 'Leukocytes [#/volume] in Blood by Automated count',\n namePt: 'Contagem de Leucócitos',\n },\n};\n","/**\n * PhenoAge Calculator\n *\n * Implements the Levine PhenoAge algorithm from:\n * \"An epigenetic biomarker of aging for lifespan and healthspan\" (Aging, 2018)\n */\n\nimport {\n BIOMARKER_NAMES_PT,\n BIOMARKER_RANGES,\n GOMPERTZ_PARAMS,\n PHENOAGE_COEFFICIENTS,\n} from './constants';\nimport type { ComponentBreakdown, PhenoAgeInput, PhenoAgeResult } from './types';\n\n/**\n * Calculates the linear predictor (xb) from biomarkers\n * @returns Object with xb value and breakdown of each component\n */\nconst calculateLinearPredictor = (\n input: PhenoAgeInput,\n): { xb: number; breakdown: ComponentBreakdown[] } => {\n const logCrp = Math.log(Math.max(input.crp, 0.1)); // Avoid log(0)\n\n const components: Array<{\n key: string;\n value: number;\n coefficient: number;\n unit: string;\n displayValue?: string;\n }> = [\n {\n coefficient: PHENOAGE_COEFFICIENTS.albumin,\n key: 'albumin',\n unit: BIOMARKER_RANGES.albumin.unit,\n value: input.albumin,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.creatinine,\n key: 'creatinine',\n unit: BIOMARKER_RANGES.creatinine.unit,\n value: input.creatinine,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.glucose,\n key: 'glucose',\n unit: BIOMARKER_RANGES.glucose.unit,\n value: input.glucose,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.logCrp,\n displayValue: `ln(${input.crp.toFixed(2)})`,\n key: 'crp',\n unit: 'ln(mg/L)',\n value: logCrp,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.lymphocytePercent,\n key: 'lymphocytePercent',\n unit: BIOMARKER_RANGES.lymphocytePercent.unit,\n value: input.lymphocytePercent,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.mcv,\n key: 'mcv',\n unit: BIOMARKER_RANGES.mcv.unit,\n value: input.mcv,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.rdw,\n key: 'rdw',\n unit: BIOMARKER_RANGES.rdw.unit,\n value: input.rdw,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.alkalinePhosphatase,\n key: 'alkalinePhosphatase',\n unit: BIOMARKER_RANGES.alkalinePhosphatase.unit,\n value: input.alkalinePhosphatase,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.wbc,\n key: 'wbc',\n unit: BIOMARKER_RANGES.wbc.unit,\n value: input.wbc,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.age,\n displayValue: `${Math.round(input.chronologicalAge)} anos`,\n key: 'age',\n unit: 'anos',\n value: input.chronologicalAge,\n },\n ];\n\n const breakdown: ComponentBreakdown[] = components.map((c) => ({\n coefficient: c.coefficient,\n contribution: Math.round(c.coefficient * c.value * 10000) / 10000,\n key: c.key,\n name: BIOMARKER_NAMES_PT[c.key] ?? c.key,\n valueWithUnit: c.displayValue ? `${c.displayValue}` : `${c.value.toFixed(2)} ${c.unit}`,\n }));\n\n // Add intercept\n breakdown.push({\n coefficient: PHENOAGE_COEFFICIENTS.intercept,\n contribution: PHENOAGE_COEFFICIENTS.intercept,\n key: 'intercept',\n name: BIOMARKER_NAMES_PT['intercept'] ?? 'Intercepto',\n valueWithUnit: '-',\n });\n\n const xb =\n PHENOAGE_COEFFICIENTS.intercept +\n components.reduce((sum, c) => sum + c.coefficient * c.value, 0);\n\n return { breakdown, xb };\n};\n\n/**\n * Calculates mortality score from linear predictor using Gompertz model\n */\nconst calculateMortalityScore = (xb: number): number => {\n const { gamma } = GOMPERTZ_PARAMS;\n\n // exp(xb) represents the hazard ratio\n const hazardRatio = Math.exp(xb);\n\n // Cumulative hazard over 120 months (10 years)\n const cumulativeHazard = (hazardRatio * (Math.exp(120 * gamma) - 1)) / gamma;\n\n // Convert to probability\n return 1 - Math.exp(-cumulativeHazard);\n};\n\n/**\n * Converts mortality score to PhenoAge\n */\nconst mortalityScoreToPhenoAge = (mortalityScore: number): number => {\n const { ageCoefficient, baseAge, mortalityConstant } = GOMPERTZ_PARAMS;\n\n // Inverse transformation to get biological age\n const innerLog = Math.log(1 - mortalityScore);\n const outerLog = Math.log(-mortalityConstant * innerLog);\n\n return baseAge + outerLog / ageCoefficient;\n};\n\n/**\n * Main PhenoAge calculation function\n *\n * @param input - Biomarker values in SI units plus chronological age\n * @returns Complete PhenoAge result with breakdown\n */\nexport const calculatePhenoAge = (input: PhenoAgeInput): PhenoAgeResult => {\n // Validate all inputs are valid numbers (not NaN)\n const inputValues = {\n albumin: input.albumin,\n alkalinePhosphatase: input.alkalinePhosphatase,\n chronologicalAge: input.chronologicalAge,\n creatinine: input.creatinine,\n crp: input.crp,\n glucose: input.glucose,\n lymphocytePercent: input.lymphocytePercent,\n mcv: input.mcv,\n rdw: input.rdw,\n wbc: input.wbc,\n };\n\n for (const [key, value] of Object.entries(inputValues)) {\n if (typeof value !== 'number' || Number.isNaN(value)) {\n throw new Error(`Invalid input value for ${key}: ${value}`);\n }\n }\n\n // Calculate linear predictor with breakdown\n const { breakdown, xb } = calculateLinearPredictor(input);\n\n // Calculate mortality score\n const mortalityScore = calculateMortalityScore(xb);\n\n // Convert to PhenoAge\n const phenoAge = mortalityScoreToPhenoAge(mortalityScore);\n\n // Calculate age difference\n const ageDifference = phenoAge - input.chronologicalAge;\n\n return {\n ageDifference: Math.round(ageDifference * 10) / 10,\n breakdown,\n calculatedAt: new Date().toISOString(),\n chronologicalAge: input.chronologicalAge,\n linearPredictor: Math.round(xb * 10000) / 10000,\n mortalityScore: Math.round(mortalityScore * 10000) / 10000,\n phenoAge: Math.round(phenoAge * 10) / 10,\n };\n};\n\n/**\n * Validates biomarker values are within acceptable ranges\n */\nexport const validateBiomarkers = (\n input: PhenoAgeInput,\n): { isValid: boolean; errors: string[] } => {\n const errors: string[] = [];\n\n const checkRange = (value: number, key: keyof typeof BIOMARKER_RANGES, name: string) => {\n const range = BIOMARKER_RANGES[key];\n if (value < range.min || value > range.max) {\n errors.push(`${name} (${value}) fora do intervalo esperado [${range.min}-${range.max}]`);\n }\n };\n\n checkRange(input.albumin, 'albumin', 'Albumina');\n checkRange(input.creatinine, 'creatinine', 'Creatinina');\n checkRange(input.glucose, 'glucose', 'Glicose');\n checkRange(input.crp, 'crp', 'PCR');\n checkRange(input.lymphocytePercent, 'lymphocytePercent', 'Linfócitos');\n checkRange(input.mcv, 'mcv', 'VCM');\n checkRange(input.rdw, 'rdw', 'RDW');\n checkRange(input.alkalinePhosphatase, 'alkalinePhosphatase', 'Fosfatase Alcalina');\n checkRange(input.wbc, 'wbc', 'Leucócitos');\n checkRange(input.chronologicalAge, 'chronologicalAge', 'Idade');\n\n // Special validation for CRP (must be positive for log transform)\n if (input.crp <= 0) {\n errors.push('PCR deve ser maior que 0');\n }\n\n return {\n errors,\n isValid: errors.length === 0,\n };\n};\n","/**\n * Unit Converters for PhenoAge Calculator\n *\n * Brazilian labs (Weinmann, Fleury, etc.) often report in different units\n * than the SI units required by the PhenoAge algorithm.\n */\n\n/**\n * Conversion factors for each biomarker\n * Key: source unit, Value: multiplier to get SI unit\n */\nexport const CONVERSION_FACTORS: Record<string, Record<string, number>> = {\n albumin: {\n 'g/dL': 10, // g/dL → g/L\n 'g/L': 1,\n },\n alkalinePhosphatase: {\n 'U/L': 1,\n },\n creatinine: {\n 'mg/dL': 88.4, // mg/dL → μmol/L\n 'umol/L': 1,\n 'μmol/L': 1,\n },\n crp: {\n 'mg/dL': 10, // mg/dL → mg/L\n 'mg/L': 1,\n },\n glucose: {\n 'mg/dL': 1 / 18.0182, // mg/dL → mmol/L\n 'mmol/L': 1,\n },\n lymphocytePercent: {\n '%': 1,\n },\n mcv: {\n fL: 1,\n },\n rdw: {\n '%': 1,\n },\n wbc: {\n '/uL': 0.001,\n '/µL': 0.001, // cells/μL → 10^9/L\n '10^9/L': 1,\n '1000/μL': 1,\n '10³/µL': 1, // Same as 10^9/L\n 'K/uL': 1,\n 'K/µL': 1,\n 'Thousand/uL': 1, // Brazilian labs often use this format\n 'thousand/uL': 1,\n 'Thousand/µL': 1,\n 'thousand/µL': 1,\n },\n};\n\n/**\n * Target units for PhenoAge calculation (SI units)\n */\nexport const TARGET_UNITS: Record<string, string> = {\n albumin: 'g/L',\n alkalinePhosphatase: 'U/L',\n creatinine: 'μmol/L',\n crp: 'mg/L',\n glucose: 'mmol/L',\n lymphocytePercent: '%',\n mcv: 'fL',\n rdw: '%',\n wbc: '10^9/L',\n};\n\n/**\n * Convert a biomarker value from source unit to SI unit\n *\n * @param biomarker - The biomarker key (e.g., 'albumin', 'glucose')\n * @param value - The numeric value\n * @param sourceUnit - The unit of the input value\n * @returns Converted value in SI units\n * @throws Error if the unit is not recognized\n */\nexport const convertToSI = (biomarker: string, value: number, sourceUnit: string): number => {\n const factors = CONVERSION_FACTORS[biomarker];\n\n if (!factors) {\n throw new Error(`Unknown biomarker: ${biomarker}`);\n }\n\n // Normalize unit string for matching\n const normalizedUnit = sourceUnit.trim();\n const factor = factors[normalizedUnit];\n\n if (factor === undefined) {\n const normalizedLower = normalizedUnit.toLowerCase();\n\n // First try exact case-insensitive match\n const exactMatch = Object.keys(factors).find((key) => key.toLowerCase() === normalizedLower);\n if (exactMatch) {\n return value * factors[exactMatch]!;\n }\n\n // For partial matches, sort keys by length (longest first) to prefer more specific matches\n // This prevents \"/µL\" from matching before \"10³/µL\"\n const sortedKeys = Object.keys(factors).sort((a, b) => b.length - a.length);\n const partialMatch = sortedKeys.find((key) => normalizedLower.includes(key.toLowerCase()));\n\n if (partialMatch) {\n return value * factors[partialMatch]!;\n }\n\n throw new Error(`Unknown unit \"${sourceUnit}\" for ${biomarker}`);\n }\n\n return value * factor;\n};\n\n/**\n * Check if a value needs conversion\n *\n * @param biomarker - The biomarker key\n * @param sourceUnit - The current unit\n * @returns true if conversion is needed\n */\nexport const needsConversion = (biomarker: string, sourceUnit: string): boolean => {\n const targetUnit = TARGET_UNITS[biomarker];\n if (!targetUnit) return false;\n\n const normalizedSource = sourceUnit.trim().toLowerCase();\n const normalizedTarget = targetUnit.toLowerCase();\n\n return normalizedSource !== normalizedTarget;\n};\n\n/**\n * Auto-detect unit and convert to SI if needed\n * Uses heuristics based on typical value ranges\n *\n * @param biomarker - The biomarker key\n * @param value - The numeric value\n * @param unit - Optional unit hint\n * @returns Object with converted value and detected unit\n */\nexport const autoConvertToSI = (\n biomarker: string,\n value: number,\n unit?: string,\n): { value: number; unit: string; wasConverted: boolean } => {\n // If unit is provided and we know how to convert it\n if (unit) {\n try {\n const converted = convertToSI(biomarker, value, unit);\n const wasConverted = needsConversion(biomarker, unit);\n return {\n unit: TARGET_UNITS[biomarker] || unit,\n value: converted,\n wasConverted,\n };\n } catch {\n // Fall through to heuristic detection\n }\n }\n\n // Heuristic-based detection for common cases\n switch (biomarker) {\n case 'albumin':\n // g/dL typically 3-5, g/L typically 30-50\n if (value < 10) {\n return { unit: 'g/L', value: value * 10, wasConverted: true };\n }\n return { unit: 'g/L', value, wasConverted: false };\n\n case 'creatinine':\n // mg/dL typically 0.5-1.5, μmol/L typically 45-130\n if (value < 15) {\n return { unit: 'μmol/L', value: value * 88.4, wasConverted: true };\n }\n return { unit: 'μmol/L', value, wasConverted: false };\n\n case 'glucose':\n // mg/dL typically 70-140, mmol/L typically 4-8\n if (value > 20) {\n return { unit: 'mmol/L', value: value / 18.0182, wasConverted: true };\n }\n return { unit: 'mmol/L', value, wasConverted: false };\n\n case 'wbc':\n // cells/μL typically 4000-11000, 10^9/L typically 4-11\n if (value > 100) {\n return { unit: '10^9/L', value: value / 1000, wasConverted: true };\n }\n return { unit: '10^9/L', value, wasConverted: false };\n\n default:\n return { unit: TARGET_UNITS[biomarker] || '', value, wasConverted: false };\n }\n};\n","export * from './calculator';\nexport * from './constants';\nexport * from './types';\nexport * from './unit-converters';\n","/**\n * BrDMrisc Calculator Constants\n *\n * All 14 model definitions with coefficients extracted from:\n * Bracco et al. (2023) Table 2 & Supplementary Material\n * DOI: 10.3389/fendo.2023.1166147\n *\n * Models 1-6 are lab-only (MVP scope).\n * Models 7-14 include clinical variables (Phase 2).\n *\n * Follow-up period: 7.4 years (ELSA-Brasil median)\n * Units expected: FPG (mg/dL), HbA1c (%), triglycerides (mg/dL), HDL-c (mg/dL)\n */\n\nimport type { BrDMriscInput, BrDMriscModelDefinition } from './types';\n\n/**\n * ELSA-Brasil median follow-up in years.\n * Used to extrapolate the logistic regression probability to 10-year risk.\n */\nexport const FOLLOW_UP_YEARS = 7.4;\n\n/**\n * All 14 BrDMrisc models.\n * Models ordered by ID. Lab-only models (1-6) are the MVP.\n *\n * Coefficients from Supplementary Table S2 of the paper,\n * verified against the Shiny app demo at:\n * https://paulabracco.shinyapps.io/BrDMrisc_pt/\n */\nexport const BRDMRISC_MODELS: BrDMriscModelDefinition[] = [\n // === Lab-only models (MVP) ===\n {\n auc: 0.776,\n coefficients: {\n fpg: 0.0352,\n },\n id: 1,\n intercept: -5.8282,\n isLabOnly: true,\n name: 'FPG only',\n namePt: 'Apenas Glicemia de Jejum',\n requiredBiomarkers: ['fpg'],\n },\n {\n auc: 0.668,\n coefficients: {\n hba1c: 0.731,\n },\n id: 2,\n intercept: -6.3199,\n isLabOnly: true,\n name: 'HbA1c only',\n namePt: 'Apenas HbA1c',\n requiredBiomarkers: ['hba1c'],\n },\n {\n auc: 0.793,\n coefficients: {\n fpg: 0.029,\n hba1c: 0.4427,\n },\n id: 3,\n intercept: -7.37,\n isLabOnly: true,\n name: 'FPG + HbA1c',\n namePt: 'Glicemia + HbA1c',\n requiredBiomarkers: ['fpg', 'hba1c'],\n },\n {\n auc: 0.79,\n coefficients: {\n fpg: 0.0345,\n triglycerides: 0.0017,\n },\n id: 4,\n intercept: -5.9706,\n isLabOnly: true,\n name: 'FPG + Triglycerides',\n namePt: 'Glicemia + Triglicerídeos',\n requiredBiomarkers: ['fpg', 'triglycerides'],\n },\n {\n auc: 0.796,\n coefficients: {\n fpg: 0.0338,\n hdlc: -0.0161,\n triglycerides: 0.0011,\n },\n id: 5,\n intercept: -5.3879,\n isLabOnly: true,\n name: 'FPG + Lipids',\n namePt: 'Glicemia + Lipídios',\n requiredBiomarkers: ['fpg', 'triglycerides', 'hdlc'],\n },\n {\n auc: 0.813,\n coefficients: {\n fpg: 0.0267,\n hba1c: 0.3943,\n hdlc: -0.0149,\n triglycerides: 0.0009,\n },\n id: 6,\n intercept: -6.9195,\n isLabOnly: true,\n name: 'FPG + HbA1c + Lipids',\n namePt: 'Glicemia + HbA1c + Lipídios',\n requiredBiomarkers: ['fpg', 'hba1c', 'triglycerides', 'hdlc'],\n },\n\n // === Clinical models (Phase 2 — require user input) ===\n {\n auc: 0.744,\n coefficients: {\n bmi: 0.0642,\n familyHistory: 0.5915,\n waist: 0.0111,\n },\n id: 7,\n intercept: -6.5023,\n isLabOnly: false,\n name: 'Clinical only',\n namePt: 'Apenas Clínico',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory'],\n },\n {\n auc: 0.8,\n coefficients: {\n bmi: 0.0454,\n familyHistory: 0.475,\n fpg: 0.0305,\n waist: 0.0047,\n },\n id: 8,\n intercept: -7.3297,\n isLabOnly: false,\n name: 'Clinical + FPG',\n namePt: 'Clínico + Glicemia',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg'],\n },\n {\n auc: 0.725,\n coefficients: {\n bmi: 0.0471,\n familyHistory: 0.5284,\n hba1c: 0.6001,\n waist: 0.0063,\n },\n id: 9,\n intercept: -8.0917,\n isLabOnly: false,\n name: 'Clinical + HbA1c',\n namePt: 'Clínico + HbA1c',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'hba1c'],\n },\n {\n auc: 0.814,\n coefficients: {\n bmi: 0.0373,\n familyHistory: 0.4393,\n fpg: 0.0253,\n hba1c: 0.3688,\n waist: 0.0029,\n },\n id: 10,\n intercept: -8.5817,\n isLabOnly: false,\n name: 'Clinical + FPG + HbA1c',\n namePt: 'Clínico + Glicemia + HbA1c',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg', 'hba1c'],\n },\n {\n auc: 0.808,\n coefficients: {\n bmi: 0.0427,\n familyHistory: 0.4532,\n fpg: 0.0299,\n triglycerides: 0.0015,\n waist: 0.0031,\n },\n id: 11,\n intercept: -7.5028,\n isLabOnly: false,\n name: 'Clinical + FPG + Triglycerides',\n namePt: 'Clínico + Glicemia + Triglicerídeos',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg', 'triglycerides'],\n },\n {\n auc: 0.813,\n coefficients: {\n bmi: 0.0408,\n familyHistory: 0.4396,\n fpg: 0.0293,\n hdlc: -0.0128,\n triglycerides: 0.0009,\n waist: 0.0019,\n },\n id: 12,\n intercept: -6.9757,\n isLabOnly: false,\n name: 'Clinical + FPG + Lipids',\n namePt: 'Clínico + Glicemia + Lipídios',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg', 'triglycerides', 'hdlc'],\n },\n {\n auc: 0.822,\n coefficients: {\n bmi: 0.0334,\n familyHistory: 0.4107,\n fpg: 0.0238,\n hba1c: 0.3266,\n hdlc: -0.0117,\n triglycerides: 0.0006,\n waist: 0.0015,\n },\n id: 13,\n intercept: -8.2,\n isLabOnly: false,\n name: 'Clinical + FPG + HbA1c + Lipids',\n namePt: 'Clínico + Glicemia + HbA1c + Lipídios',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg', 'hba1c', 'triglycerides', 'hdlc'],\n },\n {\n auc: 0.699,\n coefficients: {\n bmi: 0.0494,\n ethnicity: 0.2901,\n familyHistory: 0.557,\n hypertension: 0.3653,\n waist: 0.0071,\n },\n id: 14,\n intercept: -7.244,\n isLabOnly: false,\n name: 'Clinical extended',\n namePt: 'Clínico Estendido',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'ethnicity', 'hypertension'],\n },\n];\n\n/**\n * Lab-only models for MVP, ordered by priority (highest AUC first)\n */\nexport const LAB_ONLY_MODELS = BRDMRISC_MODELS.filter((m) => m.isLabOnly);\n\n/**\n * Model selection priority for lab-only models (highest AUC first)\n */\nexport const LAB_MODEL_PRIORITY = [6, 5, 3, 4, 1, 2] as const;\n\n/**\n * FHIR biomarker code → BrDMrisc input field mapping\n */\nexport const FHIR_CODE_TO_BRDMRISC: Record<string, keyof BrDMriscInput> = {\n Glucose: 'fpg',\n HbA1c: 'hba1c',\n HDL: 'hdlc',\n Triglycerides: 'triglycerides',\n};\n\n/**\n * Required FHIR biomarker codes (all 4 lab biomarkers)\n */\nexport const BRDMRISC_BIOMARKER_CODES = ['Glucose', 'HbA1c', 'Triglycerides', 'HDL'] as const;\n\n/**\n * Portuguese names for biomarkers\n */\nexport const BIOMARKER_NAMES_PT: Record<string, string> = {\n fpg: 'Glicemia de Jejum',\n hba1c: 'Hemoglobina Glicada',\n hdlc: 'HDL-colesterol',\n intercept: 'Intercepto',\n triglycerides: 'Triglicerídeos',\n};\n\n/**\n * Biomarker units used by the model\n */\nexport const BIOMARKER_UNITS: Record<string, string> = {\n fpg: 'mg/dL',\n hba1c: '%',\n hdlc: 'mg/dL',\n triglycerides: 'mg/dL',\n};\n\n/**\n * Biomarker validation ranges (plausible values)\n */\nexport const BIOMARKER_RANGES: Record<string, { min: number; max: number; unit: string }> = {\n fpg: { max: 500, min: 40, unit: 'mg/dL' },\n hba1c: { max: 20, min: 3, unit: '%' },\n hdlc: { max: 150, min: 10, unit: 'mg/dL' },\n triglycerides: { max: 2000, min: 20, unit: 'mg/dL' },\n};\n\n/**\n * Risk category thresholds based on clinical recommendations\n * from Bracco et al. (2023). The paper notes that 20% triggers\n * intensive prevention.\n */\nexport const RISK_THRESHOLDS = {\n high: 0.2, // 20-35% — high risk\n moderate: 0.1, // 10-20% — moderate risk\n veryHigh: 0.35, // >= 35% — very high risk\n} as const;\n","/**\n * BrDMrisc Calculator\n *\n * Implements the BrDMrisc algorithm from:\n * Bracco et al. (2023) - \"BrDMrisc: a Brazilian diabetes risk score\n * for screening of type 2 diabetes mellitus\"\n * Frontiers in Endocrinology, DOI: 10.3389/fendo.2023.1166147\n *\n * Formula:\n * x = intercept + Σ(coeff_i × value_i)\n * p = 1 / (1 + exp(-x)) — logistic regression probability (~7.4 years)\n * risk10y = 1 - (1 - p)^(10/7.4) — extrapolated to 10-year risk\n */\n\nimport {\n BIOMARKER_NAMES_PT,\n BIOMARKER_RANGES,\n BIOMARKER_UNITS,\n BRDMRISC_MODELS,\n FOLLOW_UP_YEARS,\n LAB_MODEL_PRIORITY,\n RISK_THRESHOLDS,\n} from './constants';\nimport type {\n BrDMriscInput,\n BrDMriscModelDefinition,\n BrDMriscResult,\n ComponentBreakdown,\n RiskCategory,\n} from './types';\n\n/**\n * Select the best available model based on which biomarkers are present.\n * Iterates through models in priority order (highest AUC first) and\n * returns the first one whose requirements are fully met.\n */\nexport const selectModel = (\n input: BrDMriscInput,\n labOnly = true,\n): BrDMriscModelDefinition | null => {\n const available = new Set<string>();\n if (input.fpg !== undefined && !Number.isNaN(input.fpg)) available.add('fpg');\n if (input.hba1c !== undefined && !Number.isNaN(input.hba1c)) available.add('hba1c');\n if (input.triglycerides !== undefined && !Number.isNaN(input.triglycerides))\n available.add('triglycerides');\n if (input.hdlc !== undefined && !Number.isNaN(input.hdlc)) available.add('hdlc');\n\n if (available.size === 0) return null;\n\n const priority = labOnly ? LAB_MODEL_PRIORITY : BRDMRISC_MODELS.map((m) => m.id);\n\n for (const modelId of priority) {\n const model = BRDMRISC_MODELS.find((m) => m.id === modelId);\n if (!model) continue;\n if (labOnly && !model.isLabOnly) continue;\n\n const hasAll = model.requiredBiomarkers.every((b) => available.has(b));\n if (hasAll) return model;\n }\n\n return null;\n};\n\n/**\n * Classify risk percentage into a category\n */\nexport const classifyRisk = (riskPercent: number): RiskCategory => {\n const risk = riskPercent / 100;\n if (risk >= RISK_THRESHOLDS.veryHigh) return 'very-high';\n if (risk >= RISK_THRESHOLDS.high) return 'high';\n if (risk >= RISK_THRESHOLDS.moderate) return 'moderate';\n return 'low';\n};\n\n/**\n * Calculate BrDMrisc 10-year diabetes risk\n */\nexport const calculateBrDMrisc = (\n input: BrDMriscInput,\n model?: BrDMriscModelDefinition,\n): BrDMriscResult => {\n const selectedModel = model ?? selectModel(input);\n if (!selectedModel) {\n throw new Error('No suitable model found for the available biomarkers');\n }\n\n // Build breakdown and compute linear predictor\n const breakdown: ComponentBreakdown[] = [];\n let x = selectedModel.intercept;\n\n for (const [key, coeff] of Object.entries(selectedModel.coefficients)) {\n const value = input[key as keyof BrDMriscInput];\n if (value === undefined) {\n throw new Error(`Missing required biomarker: ${key}`);\n }\n\n const contribution = coeff * value;\n x += contribution;\n\n breakdown.push({\n coefficient: coeff,\n contribution: Math.round(contribution * 10000) / 10000,\n key,\n name: BIOMARKER_NAMES_PT[key] ?? key,\n value,\n valueWithUnit: `${value.toFixed(1)} ${BIOMARKER_UNITS[key] ?? ''}`.trim(),\n });\n }\n\n // Add intercept to breakdown\n breakdown.push({\n coefficient: selectedModel.intercept,\n contribution: selectedModel.intercept,\n key: 'intercept',\n name: BIOMARKER_NAMES_PT['intercept'] ?? 'Intercepto',\n value: 1,\n valueWithUnit: '-',\n });\n\n // Logistic regression → probability at ~7.4 years\n const p = 1 / (1 + Math.exp(-x));\n\n // Extrapolate to 10-year risk\n // risk10y = 1 - (1 - p)^(10/followUp)\n const risk10y = 1 - Math.pow(1 - p, 10 / FOLLOW_UP_YEARS);\n\n const riskPercent = Math.round(risk10y * 1000) / 10; // One decimal place\n\n return {\n breakdown,\n calculatedAt: new Date().toISOString(),\n modelUsed: selectedModel,\n risk10y: Math.round(risk10y * 10000) / 10000,\n riskCategory: classifyRisk(riskPercent),\n riskPercent,\n };\n};\n\n/**\n * Validate biomarker values are within plausible ranges\n */\nexport const validateBiomarkers = (\n input: BrDMriscInput,\n): { isValid: boolean; errors: string[] } => {\n const errors: string[] = [];\n\n const checkRange = (value: number | undefined, key: string, name: string) => {\n if (value === undefined) return;\n const range = BIOMARKER_RANGES[key];\n if (!range) return;\n if (value < range.min || value > range.max) {\n errors.push(\n `${name} (${value}) fora do intervalo esperado [${range.min}-${range.max} ${range.unit}]`,\n );\n }\n };\n\n checkRange(input.fpg, 'fpg', 'Glicemia de Jejum');\n checkRange(input.hba1c, 'hba1c', 'HbA1c');\n checkRange(input.triglycerides, 'triglycerides', 'Triglicerídeos');\n checkRange(input.hdlc, 'hdlc', 'HDL-colesterol');\n\n return {\n errors,\n isValid: errors.length === 0,\n };\n};\n","/**\n * Unit Converters for BrDMrisc Calculator\n *\n * BrDMrisc expects: FPG (mg/dL), HbA1c (%), triglycerides (mg/dL), HDL-c (mg/dL)\n * Brazilian labs typically report in these units already, but some may\n * use mmol/L for glucose or mmol/mol for HbA1c (IFCC standard).\n */\n\n/**\n * Conversion factors for each biomarker\n * Key: source unit, Value: multiplier to get target unit\n */\nexport const CONVERSION_FACTORS: Record<string, Record<string, number>> = {\n fpg: {\n 'mg/dL': 1,\n 'mmol/L': 18.0182, // mmol/L → mg/dL\n },\n hba1c: {\n '%': 1,\n // IFCC mmol/mol → NGSP %: HbA1c(%) = 0.0915 × HbA1c(mmol/mol) + 2.15\n // Handled as special case in convertToTargetUnit\n },\n hdlc: {\n 'mg/dL': 1,\n 'mmol/L': 38.67, // mmol/L → mg/dL\n },\n triglycerides: {\n 'mg/dL': 1,\n 'mmol/L': 88.57, // mmol/L → mg/dL\n },\n};\n\n/**\n * Target units for BrDMrisc calculation\n */\nexport const TARGET_UNITS: Record<string, string> = {\n fpg: 'mg/dL',\n hba1c: '%',\n hdlc: 'mg/dL',\n triglycerides: 'mg/dL',\n};\n\n/**\n * Convert a biomarker value to the target unit expected by BrDMrisc\n */\nexport const convertToTargetUnit = (\n biomarker: string,\n value: number,\n sourceUnit: string,\n): number => {\n const normalizedUnit = sourceUnit.trim();\n\n // Special case: HbA1c IFCC (mmol/mol) → NGSP (%)\n if (biomarker === 'hba1c' && normalizedUnit.includes('mmol/mol')) {\n return 0.0915 * value + 2.15;\n }\n\n const factors = CONVERSION_FACTORS[biomarker];\n if (!factors) return value;\n\n // Try exact match first\n const factor = factors[normalizedUnit];\n if (factor !== undefined) return value * factor;\n\n // Case-insensitive match\n const match = Object.keys(factors).find((k) => k.toLowerCase() === normalizedUnit.toLowerCase());\n if (match) return value * factors[match]!;\n\n return value;\n};\n\n/**\n * Auto-detect unit and convert to target if needed.\n * Uses heuristics based on typical value ranges.\n */\nexport const autoConvertToTarget = (\n biomarker: string,\n value: number,\n unit?: string,\n): { value: number; unit: string; wasConverted: boolean } => {\n const targetUnit = TARGET_UNITS[biomarker] || '';\n\n if (unit) {\n const normalizedUnit = unit.trim().toLowerCase();\n const normalizedTarget = targetUnit.toLowerCase();\n\n if (normalizedUnit === normalizedTarget) {\n return { unit: targetUnit, value, wasConverted: false };\n }\n\n const converted = convertToTargetUnit(biomarker, value, unit);\n return {\n unit: targetUnit,\n value: converted,\n wasConverted: normalizedUnit !== normalizedTarget,\n };\n }\n\n // Heuristic-based detection\n switch (biomarker) {\n case 'fpg':\n // mmol/L typically 3-20, mg/dL typically 60-500\n if (value < 30) {\n return { unit: 'mg/dL', value: value * 18.0182, wasConverted: true };\n }\n return { unit: 'mg/dL', value, wasConverted: false };\n\n case 'hba1c':\n // IFCC mmol/mol typically 20-120, NGSP % typically 4-15\n if (value > 20) {\n return { unit: '%', value: 0.0915 * value + 2.15, wasConverted: true };\n }\n return { unit: '%', value, wasConverted: false };\n\n case 'triglycerides':\n // mmol/L typically 0.5-10, mg/dL typically 50-2000\n if (value < 15) {\n return { unit: 'mg/dL', value: value * 88.57, wasConverted: true };\n }\n return { unit: 'mg/dL', value, wasConverted: false };\n\n case 'hdlc':\n // mmol/L typically 0.5-3, mg/dL typically 20-100\n if (value < 5) {\n return { unit: 'mg/dL', value: value * 38.67, wasConverted: true };\n }\n return { unit: 'mg/dL', value, wasConverted: false };\n\n default:\n return { unit: targetUnit, value, wasConverted: false };\n }\n};\n","/**\n * Derived Biomarker Calculator\n *\n * Derives biomarkers from existing extracted values when the lab report\n * doesn't include them directly.\n *\n * Currently supports:\n * - HOMA-IR = (Fasting Glucose × Fasting Insulin) / 405\n * - VLDL = Triglycerides / 5\n * - BMI = weight_kg / (height_m)² (requires UserContext with height)\n */\n\nimport { codeToLoinc } from '@precisa-saude/fhir';\n\nexport interface BiomarkerInput {\n code: string;\n value: number | string;\n unit?: string;\n}\n\nexport interface DerivedBiomarker {\n code: string;\n loincCode?: string;\n name: string;\n unit: string;\n value: number;\n}\n\nexport interface DerivedOptions {\n codeToLoinc?: (code: string) => string | undefined;\n userContext?: { heightCm?: number };\n}\n\ninterface CalculationDef {\n calculate: (values: Map<string, number>) => number;\n code: string;\n inputs: string[];\n unit: string;\n}\n\ninterface ContextCalculationDef {\n calculate: (values: Map<string, number>, ctx: { heightCm?: number }) => number;\n canCalculate: (ctx: { heightCm?: number }) => boolean;\n code: string;\n inputs: string[];\n unit: string;\n}\n\nconst CALCULATIONS: CalculationDef[] = [\n {\n calculate: (v) => (v.get('Glucose')! * v.get('Insulin')!) / 405,\n code: 'HOMA_IR',\n inputs: ['Glucose', 'Insulin'],\n unit: 'index',\n },\n {\n calculate: (v) => v.get('Triglycerides')! / 5,\n code: 'VLDL',\n inputs: ['Triglycerides'],\n unit: 'mg/dL',\n },\n];\n\nconst CONTEXT_CALCULATIONS: ContextCalculationDef[] = [\n {\n calculate: (v, ctx) => {\n const weightKg = v.get('TotalMass')!;\n const heightM = ctx.heightCm! / 100;\n return weightKg / (heightM * heightM);\n },\n canCalculate: (ctx) =>\n typeof ctx.heightCm === 'number' && ctx.heightCm >= 50 && ctx.heightCm <= 250,\n code: 'BMI',\n inputs: ['TotalMass'],\n unit: 'kg/m2',\n },\n];\n\n/**\n * Compute derived biomarkers from existing extracted values.\n * Only adds a calculated biomarker if:\n * - All required inputs are present with numeric values\n * - The biomarker isn't already present in the results\n */\nexport function computeDerivedBiomarkers(\n biomarkers: BiomarkerInput[],\n options?: DerivedOptions,\n): DerivedBiomarker[] {\n const lookupLoinc = options?.codeToLoinc ?? codeToLoinc;\n const byCode = new Map<string, BiomarkerInput>();\n for (const b of biomarkers) {\n byCode.set(b.code, b);\n }\n\n const added: DerivedBiomarker[] = [];\n\n for (const calc of CALCULATIONS) {\n if (byCode.has(calc.code)) continue;\n\n const values = new Map<string, number>();\n let allPresent = true;\n\n for (const input of calc.inputs) {\n const b = byCode.get(input);\n if (!b || typeof b.value !== 'number') {\n allPresent = false;\n break;\n }\n values.set(input, b.value);\n }\n\n if (!allPresent) continue;\n\n const rawValue = calc.calculate(values);\n const value = parseFloat(rawValue.toPrecision(10));\n const loinc = lookupLoinc(calc.code);\n\n added.push({\n code: calc.code,\n loincCode: loinc ?? undefined,\n name: calc.code,\n unit: calc.unit,\n value,\n });\n }\n\n if (options?.userContext) {\n for (const calc of CONTEXT_CALCULATIONS) {\n if (byCode.has(calc.code)) continue;\n if (!calc.canCalculate(options.userContext)) continue;\n\n const values = new Map<string, number>();\n let allPresent = true;\n\n for (const input of calc.inputs) {\n const b = byCode.get(input);\n if (!b || typeof b.value !== 'number') {\n allPresent = false;\n break;\n }\n values.set(input, b.value);\n }\n\n if (!allPresent) continue;\n\n const rawValue = calc.calculate(values, options.userContext);\n const value = parseFloat(rawValue.toPrecision(10));\n const loinc = lookupLoinc(calc.code);\n\n added.push({\n code: calc.code,\n loincCode: loinc ?? undefined,\n name: calc.code,\n unit: calc.unit,\n value,\n });\n }\n }\n\n return added;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/fhir-brasil/fhir-brasil/packages/calculators/dist/index.cjs","../src/phenoage/index.ts","../src/phenoage/constants.ts","../src/phenoage/calculator.ts","../src/phenoage/unit-converters.ts","../src/brdmrisc/index.ts","../src/brdmrisc/constants.ts","../src/brdmrisc/calculator.ts","../src/brdmrisc/unit-converters.ts","../src/derived/calculator.ts"],"names":["BIOMARKER_NAMES_PT","BIOMARKER_RANGES","validateBiomarkers","CONVERSION_FACTORS","TARGET_UNITS"],"mappings":"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AA;AACR,IAAA;AACF,EAAA;AACA,EAAA;AACO,IAAA;AACS,IAAA;AACP,MAAA;AACC,MAAA;AACS,MAAA;AACjB,IAAA;AACI,IAAA;AACO,IAAA;AACA,IAAA;AACL,IAAA;AACE,IAAA;AACR,IAAA;AACF,EAAA;AACA,EAAA;AACO,IAAA;AACS,IAAA;AACP,MAAA;AACE,MAAA;AACD,MAAA;AACS,MAAA;AACjB,IAAA;AACI,IAAA;AACO,IAAA;AACA,IAAA;AACL,IAAA;AACE,IAAA;AACR,IAAA;AACF,EAAA;AAAA;AAGA,EAAA;AACO,IAAA;AACS,IAAA;AACP,MAAA;AACU,MAAA;AACR,MAAA;AACT,IAAA;AACI,IAAA;AACO,IAAA;AACA,IAAA;AACL,IAAA;AACE,IAAA;AACR,IAAA;AACF,EAAA;AACA,EAAA;AACO,IAAA;AACS,IAAA;AACP,MAAA;AACU,MAAA;AACV,MAAA;AACE,MAAA;AACT,IAAA;AACI,IAAA;AACO,IAAA;AACA,IAAA;AACL,IAAA;AACE,IAAA;AACR,IAAA;AACF,EAAA;AACA,EAAA;AACO,IAAA;AACS,IAAA;AACP,MAAA;AACU,MAAA;AACR,MAAA;AACA,MAAA;AACT,IAAA;AACI,IAAA;AACO,IAAA;AACA,IAAA;AACL,IAAA;AACE,IAAA;AACR,IAAA;AACF,EAAA;AACA,EAAA;AACO,IAAA;AACS,IAAA;AACP,MAAA;AACU,MAAA;AACV,MAAA;AACE,MAAA;AACA,MAAA;AACT,IAAA;AACI,IAAA;AACO,IAAA;AACA,IAAA;AACL,IAAA;AACE,IAAA;AACR,IAAA;AACF,EAAA;AACA,EAAA;AACO,IAAA;AACS,IAAA;AACP,MAAA;AACU,MAAA;AACV,MAAA;AACU,MAAA;AACR,MAAA;AACT,IAAA;AACI,IAAA;AACO,IAAA;AACA,IAAA;AACL,IAAA;AACE,IAAA;AACR,IAAA;AACF,EAAA;AACA,EAAA;AACO,IAAA;AACS,IAAA;AACP,MAAA;AACU,MAAA;AACV,MAAA;AACC,MAAA;AACS,MAAA;AACR,MAAA;AACT,IAAA;AACI,IAAA;AACO,IAAA;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","file":"/home/runner/work/fhir-brasil/fhir-brasil/packages/calculators/dist/index.cjs","sourcesContent":[null,"export * from './calculator';\nexport * from './constants';\nexport * from './types';\nexport * from './unit-converters';\n","/**\n * PhenoAge Calculator Constants\n *\n * Coefficients from Levine et al. (2018) Table 1\n * Units: albumin (g/L), creatinine (μmol/L), glucose (mmol/L),\n * CRP (ln of mg/L), lymphocyte (%), MCV (fL), RDW (%),\n * ALP (U/L), WBC (10^9/L), age (years)\n */\n\n/**\n * Regression coefficients from the Cox proportional hazards model\n */\nexport const PHENOAGE_COEFFICIENTS = {\n age: 0.0804,\n albumin: -0.0336,\n alkalinePhosphatase: 0.0019,\n creatinine: 0.0095,\n glucose: 0.1953,\n intercept: -19.9067,\n logCrp: 0.0954,\n lymphocytePercent: -0.012,\n mcv: 0.0268,\n rdw: 0.3306,\n wbc: 0.0554,\n} as const;\n\n/**\n * Gompertz mortality model parameters\n */\nexport const GOMPERTZ_PARAMS = {\n ageCoefficient: 0.090165,\n baseAge: 141.50225,\n gamma: 0.0076927,\n mortalityConstant: 0.00553,\n} as const;\n\n/**\n * Biomarker reference ranges for validation\n */\nexport const BIOMARKER_RANGES = {\n albumin: { max: 60, min: 10, typical: { max: 50, min: 35 }, unit: 'g/L' },\n alkalinePhosphatase: { max: 500, min: 10, typical: { max: 130, min: 40 }, unit: 'U/L' },\n chronologicalAge: { max: 120, min: 18, typical: { max: 100, min: 18 }, unit: 'anos' },\n creatinine: { max: 500, min: 20, typical: { max: 110, min: 60 }, unit: 'μmol/L' },\n crp: { max: 200, min: 0.01, typical: { max: 3, min: 0 }, unit: 'mg/L' },\n glucose: { max: 30, min: 2, typical: { max: 6.0, min: 4.0 }, unit: 'mmol/L' },\n lymphocytePercent: { max: 80, min: 1, typical: { max: 40, min: 20 }, unit: '%' },\n mcv: { max: 150, min: 50, typical: { max: 100, min: 80 }, unit: 'fL' },\n rdw: { max: 25, min: 8, typical: { max: 15, min: 11 }, unit: '%' },\n wbc: { max: 30, min: 1, typical: { max: 11.0, min: 4.0 }, unit: '10^9/L' },\n} as const;\n\n/**\n * Mapping from FHIR biomarker codes to PhenoAge input fields\n */\nexport const FHIR_CODE_TO_PHENOAGE: Record<string, keyof typeof BIOMARKER_RANGES> = {\n Albumin: 'albumin',\n AlkalinePhosphatase: 'alkalinePhosphatase',\n Creatinine: 'creatinine',\n CRP: 'crp',\n Glucose: 'glucose',\n Lymphocytes: 'lymphocytePercent',\n MCV: 'mcv',\n RDW: 'rdw',\n WBC: 'wbc',\n};\n\n/**\n * Required biomarker codes for PhenoAge calculation\n */\nexport const REQUIRED_BIOMARKERS = [\n 'Albumin',\n 'Creatinine',\n 'Glucose',\n 'CRP',\n 'Lymphocytes',\n 'MCV',\n 'RDW',\n 'AlkalinePhosphatase',\n 'WBC',\n] as const;\n\n/**\n * Portuguese names for biomarkers (for display)\n */\nexport const BIOMARKER_NAMES_PT: Record<string, string> = {\n age: 'Idade',\n albumin: 'Albumina',\n alkalinePhosphatase: 'Fosfatase Alcalina',\n creatinine: 'Creatinina',\n crp: 'PCR',\n glucose: 'Glicose',\n intercept: 'Intercepto',\n lymphocytePercent: 'Linfócitos',\n mcv: 'VCM',\n rdw: 'RDW',\n wbc: 'Leucócitos',\n};\n\n/**\n * Short descriptions for biomarkers (for tooltips)\n * Explains what each biomarker indicates in the context of aging\n */\nexport const BIOMARKER_DESCRIPTIONS_PT: Record<string, string> = {\n age: 'Sua idade cronológica em anos',\n albumin: 'Proteína do fígado que indica estado nutricional e função hepática',\n alkalinePhosphatase: 'Enzima que reflete saúde do fígado e dos ossos',\n creatinine: 'Marcador de função renal produzido pelos músculos',\n crp: 'Proteína C-reativa, indica inflamação no corpo',\n glucose: 'Nível de açúcar no sangue, relacionado ao metabolismo',\n intercept: 'Constante da fórmula de regressão',\n lymphocytePercent: 'Células de defesa do sistema imunológico',\n mcv: 'Volume médio das hemácias, indica saúde das células vermelhas',\n rdw: 'Variação no tamanho das hemácias, marcador de inflamação',\n wbc: 'Total de células brancas, indica estado imunológico',\n};\n\n/**\n * Biomarker information for lab order requests\n * Includes LOINC codes and English names for lab requisitions\n */\nexport interface BiomarkerLabInfo {\n loincCode: string;\n nameEn: string;\n namePt: string;\n}\n\n/**\n * Lab order information for PhenoAge biomarkers\n * Mapped by FHIR code\n */\nexport const BIOMARKER_LAB_INFO: Record<string, BiomarkerLabInfo> = {\n Albumin: {\n loincCode: '1751-7',\n nameEn: 'Albumin [Mass/volume] in Serum or Plasma',\n namePt: 'Albumina',\n },\n AlkalinePhosphatase: {\n loincCode: '6768-6',\n nameEn: 'Alkaline phosphatase [Enzymatic activity/volume] in Serum or Plasma',\n namePt: 'Fosfatase Alcalina',\n },\n Creatinine: {\n loincCode: '2160-0',\n nameEn: 'Creatinine [Mass/volume] in Serum or Plasma',\n namePt: 'Creatinina',\n },\n CRP: {\n loincCode: '1988-5',\n nameEn: 'C reactive protein [Mass/volume] in Serum or Plasma',\n namePt: 'Proteína C-Reativa (PCR)',\n },\n Glucose: {\n loincCode: '2345-7',\n nameEn: 'Glucose [Mass/volume] in Serum or Plasma',\n namePt: 'Glicose',\n },\n Lymphocytes: {\n loincCode: '736-9',\n nameEn: 'Lymphocytes/100 leukocytes in Blood by Automated count',\n namePt: 'Linfócitos (%)',\n },\n MCV: {\n loincCode: '787-2',\n nameEn: 'MCV [Entitic volume] by Automated count',\n namePt: 'Volume Corpuscular Médio (VCM)',\n },\n RDW: {\n loincCode: '788-0',\n nameEn: 'Erythrocyte distribution width [Ratio] by Automated count',\n namePt: 'Amplitude de Distribuição dos Eritrócitos (RDW)',\n },\n WBC: {\n loincCode: '6690-2',\n nameEn: 'Leukocytes [#/volume] in Blood by Automated count',\n namePt: 'Contagem de Leucócitos',\n },\n};\n","/**\n * PhenoAge Calculator\n *\n * Implements the Levine PhenoAge algorithm from:\n * \"An epigenetic biomarker of aging for lifespan and healthspan\" (Aging, 2018)\n */\n\nimport {\n BIOMARKER_NAMES_PT,\n BIOMARKER_RANGES,\n GOMPERTZ_PARAMS,\n PHENOAGE_COEFFICIENTS,\n} from './constants';\nimport type { ComponentBreakdown, PhenoAgeInput, PhenoAgeResult } from './types';\n\n/**\n * Calculates the linear predictor (xb) from biomarkers\n * @returns Object with xb value and breakdown of each component\n */\nconst calculateLinearPredictor = (\n input: PhenoAgeInput,\n): { xb: number; breakdown: ComponentBreakdown[] } => {\n const logCrp = Math.log(Math.max(input.crp, 0.1)); // Avoid log(0)\n\n const components: Array<{\n key: string;\n value: number;\n coefficient: number;\n unit: string;\n displayValue?: string;\n }> = [\n {\n coefficient: PHENOAGE_COEFFICIENTS.albumin,\n key: 'albumin',\n unit: BIOMARKER_RANGES.albumin.unit,\n value: input.albumin,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.creatinine,\n key: 'creatinine',\n unit: BIOMARKER_RANGES.creatinine.unit,\n value: input.creatinine,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.glucose,\n key: 'glucose',\n unit: BIOMARKER_RANGES.glucose.unit,\n value: input.glucose,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.logCrp,\n displayValue: `ln(${input.crp.toFixed(2)})`,\n key: 'crp',\n unit: 'ln(mg/L)',\n value: logCrp,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.lymphocytePercent,\n key: 'lymphocytePercent',\n unit: BIOMARKER_RANGES.lymphocytePercent.unit,\n value: input.lymphocytePercent,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.mcv,\n key: 'mcv',\n unit: BIOMARKER_RANGES.mcv.unit,\n value: input.mcv,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.rdw,\n key: 'rdw',\n unit: BIOMARKER_RANGES.rdw.unit,\n value: input.rdw,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.alkalinePhosphatase,\n key: 'alkalinePhosphatase',\n unit: BIOMARKER_RANGES.alkalinePhosphatase.unit,\n value: input.alkalinePhosphatase,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.wbc,\n key: 'wbc',\n unit: BIOMARKER_RANGES.wbc.unit,\n value: input.wbc,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.age,\n displayValue: `${Math.round(input.chronologicalAge)} anos`,\n key: 'age',\n unit: 'anos',\n value: input.chronologicalAge,\n },\n ];\n\n const breakdown: ComponentBreakdown[] = components.map((c) => ({\n coefficient: c.coefficient,\n contribution: Math.round(c.coefficient * c.value * 10000) / 10000,\n key: c.key,\n name: BIOMARKER_NAMES_PT[c.key] ?? c.key,\n valueWithUnit: c.displayValue ? `${c.displayValue}` : `${c.value.toFixed(2)} ${c.unit}`,\n }));\n\n // Add intercept\n breakdown.push({\n coefficient: PHENOAGE_COEFFICIENTS.intercept,\n contribution: PHENOAGE_COEFFICIENTS.intercept,\n key: 'intercept',\n name: BIOMARKER_NAMES_PT['intercept'] ?? 'Intercepto',\n valueWithUnit: '-',\n });\n\n const xb =\n PHENOAGE_COEFFICIENTS.intercept +\n components.reduce((sum, c) => sum + c.coefficient * c.value, 0);\n\n return { breakdown, xb };\n};\n\n/**\n * Calculates mortality score from linear predictor using Gompertz model\n */\nconst calculateMortalityScore = (xb: number): number => {\n const { gamma } = GOMPERTZ_PARAMS;\n\n // exp(xb) represents the hazard ratio\n const hazardRatio = Math.exp(xb);\n\n // Cumulative hazard over 120 months (10 years)\n const cumulativeHazard = (hazardRatio * (Math.exp(120 * gamma) - 1)) / gamma;\n\n // Convert to probability\n return 1 - Math.exp(-cumulativeHazard);\n};\n\n/**\n * Converts mortality score to PhenoAge\n */\nconst mortalityScoreToPhenoAge = (mortalityScore: number): number => {\n const { ageCoefficient, baseAge, mortalityConstant } = GOMPERTZ_PARAMS;\n\n // Inverse transformation to get biological age\n const innerLog = Math.log(1 - mortalityScore);\n const outerLog = Math.log(-mortalityConstant * innerLog);\n\n return baseAge + outerLog / ageCoefficient;\n};\n\n/**\n * Main PhenoAge calculation function\n *\n * @param input - Biomarker values in SI units plus chronological age\n * @returns Complete PhenoAge result with breakdown\n */\nexport const calculatePhenoAge = (input: PhenoAgeInput): PhenoAgeResult => {\n // Validate all inputs are valid numbers (not NaN)\n const inputValues = {\n albumin: input.albumin,\n alkalinePhosphatase: input.alkalinePhosphatase,\n chronologicalAge: input.chronologicalAge,\n creatinine: input.creatinine,\n crp: input.crp,\n glucose: input.glucose,\n lymphocytePercent: input.lymphocytePercent,\n mcv: input.mcv,\n rdw: input.rdw,\n wbc: input.wbc,\n };\n\n for (const [key, value] of Object.entries(inputValues)) {\n if (typeof value !== 'number' || Number.isNaN(value)) {\n throw new Error(`Invalid input value for ${key}: ${value}`);\n }\n }\n\n // Calculate linear predictor with breakdown\n const { breakdown, xb } = calculateLinearPredictor(input);\n\n // Calculate mortality score\n const mortalityScore = calculateMortalityScore(xb);\n\n // Convert to PhenoAge\n const phenoAge = mortalityScoreToPhenoAge(mortalityScore);\n\n // Calculate age difference\n const ageDifference = phenoAge - input.chronologicalAge;\n\n return {\n ageDifference: Math.round(ageDifference * 10) / 10,\n breakdown,\n calculatedAt: new Date().toISOString(),\n chronologicalAge: input.chronologicalAge,\n linearPredictor: Math.round(xb * 10000) / 10000,\n mortalityScore: Math.round(mortalityScore * 10000) / 10000,\n phenoAge: Math.round(phenoAge * 10) / 10,\n };\n};\n\n/**\n * Validates biomarker values are within acceptable ranges\n */\nexport const validateBiomarkers = (\n input: PhenoAgeInput,\n): { isValid: boolean; errors: string[] } => {\n const errors: string[] = [];\n\n const checkRange = (value: number, key: keyof typeof BIOMARKER_RANGES, name: string) => {\n const range = BIOMARKER_RANGES[key];\n if (value < range.min || value > range.max) {\n errors.push(`${name} (${value}) fora do intervalo esperado [${range.min}-${range.max}]`);\n }\n };\n\n checkRange(input.albumin, 'albumin', 'Albumina');\n checkRange(input.creatinine, 'creatinine', 'Creatinina');\n checkRange(input.glucose, 'glucose', 'Glicose');\n checkRange(input.crp, 'crp', 'PCR');\n checkRange(input.lymphocytePercent, 'lymphocytePercent', 'Linfócitos');\n checkRange(input.mcv, 'mcv', 'VCM');\n checkRange(input.rdw, 'rdw', 'RDW');\n checkRange(input.alkalinePhosphatase, 'alkalinePhosphatase', 'Fosfatase Alcalina');\n checkRange(input.wbc, 'wbc', 'Leucócitos');\n checkRange(input.chronologicalAge, 'chronologicalAge', 'Idade');\n\n // Special validation for CRP (must be positive for log transform)\n if (input.crp <= 0) {\n errors.push('PCR deve ser maior que 0');\n }\n\n return {\n errors,\n isValid: errors.length === 0,\n };\n};\n","/**\n * Unit Converters for PhenoAge Calculator\n *\n * Brazilian labs (Weinmann, Fleury, etc.) often report in different units\n * than the SI units required by the PhenoAge algorithm.\n */\n\n/**\n * Conversion factors for each biomarker\n * Key: source unit, Value: multiplier to get SI unit\n */\nexport const CONVERSION_FACTORS: Record<string, Record<string, number>> = {\n albumin: {\n 'g/dL': 10, // g/dL → g/L\n 'g/L': 1,\n },\n alkalinePhosphatase: {\n 'U/L': 1,\n },\n creatinine: {\n 'mg/dL': 88.4, // mg/dL → μmol/L\n 'umol/L': 1,\n 'μmol/L': 1,\n },\n crp: {\n 'mg/dL': 10, // mg/dL → mg/L\n 'mg/L': 1,\n },\n glucose: {\n 'mg/dL': 1 / 18.0182, // mg/dL → mmol/L\n 'mmol/L': 1,\n },\n lymphocytePercent: {\n '%': 1,\n },\n mcv: {\n fL: 1,\n },\n rdw: {\n '%': 1,\n },\n wbc: {\n '/uL': 0.001,\n '/µL': 0.001, // cells/μL → 10^9/L\n '10^9/L': 1,\n '1000/μL': 1,\n '10³/µL': 1, // Same as 10^9/L\n 'K/uL': 1,\n 'K/µL': 1,\n 'Thousand/uL': 1, // Brazilian labs often use this format\n 'thousand/uL': 1,\n 'Thousand/µL': 1,\n 'thousand/µL': 1,\n },\n};\n\n/**\n * Target units for PhenoAge calculation (SI units)\n */\nexport const TARGET_UNITS: Record<string, string> = {\n albumin: 'g/L',\n alkalinePhosphatase: 'U/L',\n creatinine: 'μmol/L',\n crp: 'mg/L',\n glucose: 'mmol/L',\n lymphocytePercent: '%',\n mcv: 'fL',\n rdw: '%',\n wbc: '10^9/L',\n};\n\n/**\n * Convert a biomarker value from source unit to SI unit\n *\n * @param biomarker - The biomarker key (e.g., 'albumin', 'glucose')\n * @param value - The numeric value\n * @param sourceUnit - The unit of the input value\n * @returns Converted value in SI units\n * @throws Error if the unit is not recognized\n */\nexport const convertToSI = (biomarker: string, value: number, sourceUnit: string): number => {\n const factors = CONVERSION_FACTORS[biomarker];\n\n if (!factors) {\n throw new Error(`Unknown biomarker: ${biomarker}`);\n }\n\n // Normalize unit string for matching\n const normalizedUnit = sourceUnit.trim();\n const factor = factors[normalizedUnit];\n\n if (factor === undefined) {\n const normalizedLower = normalizedUnit.toLowerCase();\n\n // First try exact case-insensitive match\n const exactMatch = Object.keys(factors).find((key) => key.toLowerCase() === normalizedLower);\n if (exactMatch) {\n return value * factors[exactMatch]!;\n }\n\n // For partial matches, sort keys by length (longest first) to prefer more specific matches\n // This prevents \"/µL\" from matching before \"10³/µL\"\n const sortedKeys = Object.keys(factors).sort((a, b) => b.length - a.length);\n const partialMatch = sortedKeys.find((key) => normalizedLower.includes(key.toLowerCase()));\n\n if (partialMatch) {\n return value * factors[partialMatch]!;\n }\n\n throw new Error(`Unknown unit \"${sourceUnit}\" for ${biomarker}`);\n }\n\n return value * factor;\n};\n\n/**\n * Check if a value needs conversion\n *\n * @param biomarker - The biomarker key\n * @param sourceUnit - The current unit\n * @returns true if conversion is needed\n */\nexport const needsConversion = (biomarker: string, sourceUnit: string): boolean => {\n const targetUnit = TARGET_UNITS[biomarker];\n if (!targetUnit) return false;\n\n const normalizedSource = sourceUnit.trim().toLowerCase();\n const normalizedTarget = targetUnit.toLowerCase();\n\n return normalizedSource !== normalizedTarget;\n};\n\n/**\n * Auto-detect unit and convert to SI if needed\n * Uses heuristics based on typical value ranges\n *\n * @param biomarker - The biomarker key\n * @param value - The numeric value\n * @param unit - Optional unit hint\n * @returns Object with converted value and detected unit\n */\nexport const autoConvertToSI = (\n biomarker: string,\n value: number,\n unit?: string,\n): { value: number; unit: string; wasConverted: boolean } => {\n // If unit is provided and we know how to convert it\n if (unit) {\n try {\n const converted = convertToSI(biomarker, value, unit);\n const wasConverted = needsConversion(biomarker, unit);\n return {\n unit: TARGET_UNITS[biomarker] || unit,\n value: converted,\n wasConverted,\n };\n } catch {\n // Fall through to heuristic detection\n }\n }\n\n // Heuristic-based detection for common cases\n switch (biomarker) {\n case 'albumin':\n // g/dL typically 3-5, g/L typically 30-50\n if (value < 10) {\n return { unit: 'g/L', value: value * 10, wasConverted: true };\n }\n return { unit: 'g/L', value, wasConverted: false };\n\n case 'creatinine':\n // mg/dL typically 0.5-1.5, μmol/L typically 45-130\n if (value < 15) {\n return { unit: 'μmol/L', value: value * 88.4, wasConverted: true };\n }\n return { unit: 'μmol/L', value, wasConverted: false };\n\n case 'glucose':\n // mg/dL typically 70-140, mmol/L typically 4-8\n if (value > 20) {\n return { unit: 'mmol/L', value: value / 18.0182, wasConverted: true };\n }\n return { unit: 'mmol/L', value, wasConverted: false };\n\n case 'wbc':\n // cells/μL typically 4000-11000, 10^9/L typically 4-11\n if (value > 100) {\n return { unit: '10^9/L', value: value / 1000, wasConverted: true };\n }\n return { unit: '10^9/L', value, wasConverted: false };\n\n default:\n return { unit: TARGET_UNITS[biomarker] || '', value, wasConverted: false };\n }\n};\n","export * from './calculator';\nexport * from './constants';\nexport * from './types';\nexport * from './unit-converters';\n","/**\n * BrDMrisc Calculator Constants\n *\n * All 14 model definitions with coefficients extracted from:\n * Bracco et al. (2023) Table 2 & Supplementary Material\n * DOI: 10.3389/fendo.2023.1166147\n *\n * Models 1-6 are lab-only (MVP scope).\n * Models 7-14 include clinical variables (Phase 2).\n *\n * Follow-up period: 7.4 years (ELSA-Brasil median)\n * Units expected: FPG (mg/dL), HbA1c (%), triglycerides (mg/dL), HDL-c (mg/dL)\n */\n\nimport type { BrDMriscInput, BrDMriscModelDefinition } from './types';\n\n/**\n * ELSA-Brasil median follow-up in years.\n * Used to extrapolate the logistic regression probability to 10-year risk.\n */\nexport const FOLLOW_UP_YEARS = 7.4;\n\n/**\n * All 14 BrDMrisc models.\n * Models ordered by ID. Lab-only models (1-6) are the MVP.\n *\n * Coefficients from Supplementary Table S2 of the paper,\n * verified against the Shiny app demo at:\n * https://paulabracco.shinyapps.io/BrDMrisc_pt/\n */\nexport const BRDMRISC_MODELS: BrDMriscModelDefinition[] = [\n // === Lab-only models (MVP) ===\n {\n auc: 0.776,\n coefficients: {\n fpg: 0.0352,\n },\n id: 1,\n intercept: -5.8282,\n isLabOnly: true,\n name: 'FPG only',\n namePt: 'Apenas Glicemia de Jejum',\n requiredBiomarkers: ['fpg'],\n },\n {\n auc: 0.668,\n coefficients: {\n hba1c: 0.731,\n },\n id: 2,\n intercept: -6.3199,\n isLabOnly: true,\n name: 'HbA1c only',\n namePt: 'Apenas HbA1c',\n requiredBiomarkers: ['hba1c'],\n },\n {\n auc: 0.793,\n coefficients: {\n fpg: 0.029,\n hba1c: 0.4427,\n },\n id: 3,\n intercept: -7.37,\n isLabOnly: true,\n name: 'FPG + HbA1c',\n namePt: 'Glicemia + HbA1c',\n requiredBiomarkers: ['fpg', 'hba1c'],\n },\n {\n auc: 0.79,\n coefficients: {\n fpg: 0.0345,\n triglycerides: 0.0017,\n },\n id: 4,\n intercept: -5.9706,\n isLabOnly: true,\n name: 'FPG + Triglycerides',\n namePt: 'Glicemia + Triglicerídeos',\n requiredBiomarkers: ['fpg', 'triglycerides'],\n },\n {\n auc: 0.796,\n coefficients: {\n fpg: 0.0338,\n hdlc: -0.0161,\n triglycerides: 0.0011,\n },\n id: 5,\n intercept: -5.3879,\n isLabOnly: true,\n name: 'FPG + Lipids',\n namePt: 'Glicemia + Lipídios',\n requiredBiomarkers: ['fpg', 'triglycerides', 'hdlc'],\n },\n {\n auc: 0.813,\n coefficients: {\n fpg: 0.0267,\n hba1c: 0.3943,\n hdlc: -0.0149,\n triglycerides: 0.0009,\n },\n id: 6,\n intercept: -6.9195,\n isLabOnly: true,\n name: 'FPG + HbA1c + Lipids',\n namePt: 'Glicemia + HbA1c + Lipídios',\n requiredBiomarkers: ['fpg', 'hba1c', 'triglycerides', 'hdlc'],\n },\n\n // === Clinical models (Phase 2 — require user input) ===\n {\n auc: 0.744,\n coefficients: {\n bmi: 0.0642,\n familyHistory: 0.5915,\n waist: 0.0111,\n },\n id: 7,\n intercept: -6.5023,\n isLabOnly: false,\n name: 'Clinical only',\n namePt: 'Apenas Clínico',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory'],\n },\n {\n auc: 0.8,\n coefficients: {\n bmi: 0.0454,\n familyHistory: 0.475,\n fpg: 0.0305,\n waist: 0.0047,\n },\n id: 8,\n intercept: -7.3297,\n isLabOnly: false,\n name: 'Clinical + FPG',\n namePt: 'Clínico + Glicemia',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg'],\n },\n {\n auc: 0.725,\n coefficients: {\n bmi: 0.0471,\n familyHistory: 0.5284,\n hba1c: 0.6001,\n waist: 0.0063,\n },\n id: 9,\n intercept: -8.0917,\n isLabOnly: false,\n name: 'Clinical + HbA1c',\n namePt: 'Clínico + HbA1c',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'hba1c'],\n },\n {\n auc: 0.814,\n coefficients: {\n bmi: 0.0373,\n familyHistory: 0.4393,\n fpg: 0.0253,\n hba1c: 0.3688,\n waist: 0.0029,\n },\n id: 10,\n intercept: -8.5817,\n isLabOnly: false,\n name: 'Clinical + FPG + HbA1c',\n namePt: 'Clínico + Glicemia + HbA1c',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg', 'hba1c'],\n },\n {\n auc: 0.808,\n coefficients: {\n bmi: 0.0427,\n familyHistory: 0.4532,\n fpg: 0.0299,\n triglycerides: 0.0015,\n waist: 0.0031,\n },\n id: 11,\n intercept: -7.5028,\n isLabOnly: false,\n name: 'Clinical + FPG + Triglycerides',\n namePt: 'Clínico + Glicemia + Triglicerídeos',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg', 'triglycerides'],\n },\n {\n auc: 0.813,\n coefficients: {\n bmi: 0.0408,\n familyHistory: 0.4396,\n fpg: 0.0293,\n hdlc: -0.0128,\n triglycerides: 0.0009,\n waist: 0.0019,\n },\n id: 12,\n intercept: -6.9757,\n isLabOnly: false,\n name: 'Clinical + FPG + Lipids',\n namePt: 'Clínico + Glicemia + Lipídios',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg', 'triglycerides', 'hdlc'],\n },\n {\n auc: 0.822,\n coefficients: {\n bmi: 0.0334,\n familyHistory: 0.4107,\n fpg: 0.0238,\n hba1c: 0.3266,\n hdlc: -0.0117,\n triglycerides: 0.0006,\n waist: 0.0015,\n },\n id: 13,\n intercept: -8.2,\n isLabOnly: false,\n name: 'Clinical + FPG + HbA1c + Lipids',\n namePt: 'Clínico + Glicemia + HbA1c + Lipídios',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg', 'hba1c', 'triglycerides', 'hdlc'],\n },\n {\n auc: 0.699,\n coefficients: {\n bmi: 0.0494,\n ethnicity: 0.2901,\n familyHistory: 0.557,\n hypertension: 0.3653,\n waist: 0.0071,\n },\n id: 14,\n intercept: -7.244,\n isLabOnly: false,\n name: 'Clinical extended',\n namePt: 'Clínico Estendido',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'ethnicity', 'hypertension'],\n },\n];\n\n/**\n * Lab-only models for MVP, ordered by priority (highest AUC first)\n */\nexport const LAB_ONLY_MODELS = BRDMRISC_MODELS.filter((m) => m.isLabOnly);\n\n/**\n * Model selection priority for lab-only models (highest AUC first)\n */\nexport const LAB_MODEL_PRIORITY = [6, 5, 3, 4, 1, 2] as const;\n\n/**\n * FHIR biomarker code → BrDMrisc input field mapping\n */\nexport const FHIR_CODE_TO_BRDMRISC: Record<string, keyof BrDMriscInput> = {\n Glucose: 'fpg',\n HbA1c: 'hba1c',\n HDL: 'hdlc',\n Triglycerides: 'triglycerides',\n};\n\n/**\n * Required FHIR biomarker codes (all 4 lab biomarkers)\n */\nexport const BRDMRISC_BIOMARKER_CODES = ['Glucose', 'HbA1c', 'Triglycerides', 'HDL'] as const;\n\n/**\n * Portuguese names for biomarkers\n */\nexport const BIOMARKER_NAMES_PT: Record<string, string> = {\n fpg: 'Glicemia de Jejum',\n hba1c: 'Hemoglobina Glicada',\n hdlc: 'HDL-colesterol',\n intercept: 'Intercepto',\n triglycerides: 'Triglicerídeos',\n};\n\n/**\n * Biomarker units used by the model\n */\nexport const BIOMARKER_UNITS: Record<string, string> = {\n fpg: 'mg/dL',\n hba1c: '%',\n hdlc: 'mg/dL',\n triglycerides: 'mg/dL',\n};\n\n/**\n * Biomarker validation ranges (plausible values)\n */\nexport const BIOMARKER_RANGES: Record<string, { min: number; max: number; unit: string }> = {\n fpg: { max: 500, min: 40, unit: 'mg/dL' },\n hba1c: { max: 20, min: 3, unit: '%' },\n hdlc: { max: 150, min: 10, unit: 'mg/dL' },\n triglycerides: { max: 2000, min: 20, unit: 'mg/dL' },\n};\n\n/**\n * Risk category thresholds based on clinical recommendations\n * from Bracco et al. (2023). The paper notes that 20% triggers\n * intensive prevention.\n */\nexport const RISK_THRESHOLDS = {\n high: 0.2, // 20-35% — high risk\n moderate: 0.1, // 10-20% — moderate risk\n veryHigh: 0.35, // >= 35% — very high risk\n} as const;\n","/**\n * BrDMrisc Calculator\n *\n * Implements the BrDMrisc algorithm from:\n * Bracco et al. (2023) - \"BrDMrisc: a Brazilian diabetes risk score\n * for screening of type 2 diabetes mellitus\"\n * Frontiers in Endocrinology, DOI: 10.3389/fendo.2023.1166147\n *\n * Formula:\n * x = intercept + Σ(coeff_i × value_i)\n * p = 1 / (1 + exp(-x)) — logistic regression probability (~7.4 years)\n * risk10y = 1 - (1 - p)^(10/7.4) — extrapolated to 10-year risk\n */\n\nimport {\n BIOMARKER_NAMES_PT,\n BIOMARKER_RANGES,\n BIOMARKER_UNITS,\n BRDMRISC_MODELS,\n FOLLOW_UP_YEARS,\n LAB_MODEL_PRIORITY,\n RISK_THRESHOLDS,\n} from './constants';\nimport type {\n BrDMriscInput,\n BrDMriscModelDefinition,\n BrDMriscResult,\n ComponentBreakdown,\n RiskCategory,\n} from './types';\n\n/**\n * Select the best available model based on which biomarkers are present.\n * Iterates through models in priority order (highest AUC first) and\n * returns the first one whose requirements are fully met.\n */\nexport const selectModel = (\n input: BrDMriscInput,\n labOnly = true,\n): BrDMriscModelDefinition | null => {\n const available = new Set<string>();\n if (input.fpg !== undefined && !Number.isNaN(input.fpg)) available.add('fpg');\n if (input.hba1c !== undefined && !Number.isNaN(input.hba1c)) available.add('hba1c');\n if (input.triglycerides !== undefined && !Number.isNaN(input.triglycerides))\n available.add('triglycerides');\n if (input.hdlc !== undefined && !Number.isNaN(input.hdlc)) available.add('hdlc');\n\n if (available.size === 0) return null;\n\n const priority = labOnly ? LAB_MODEL_PRIORITY : BRDMRISC_MODELS.map((m) => m.id);\n\n for (const modelId of priority) {\n const model = BRDMRISC_MODELS.find((m) => m.id === modelId);\n if (!model) continue;\n if (labOnly && !model.isLabOnly) continue;\n\n const hasAll = model.requiredBiomarkers.every((b) => available.has(b));\n if (hasAll) return model;\n }\n\n return null;\n};\n\n/**\n * Classify risk percentage into a category\n */\nexport const classifyRisk = (riskPercent: number): RiskCategory => {\n const risk = riskPercent / 100;\n if (risk >= RISK_THRESHOLDS.veryHigh) return 'very-high';\n if (risk >= RISK_THRESHOLDS.high) return 'high';\n if (risk >= RISK_THRESHOLDS.moderate) return 'moderate';\n return 'low';\n};\n\n/**\n * Calculate BrDMrisc 10-year diabetes risk\n */\nexport const calculateBrDMrisc = (\n input: BrDMriscInput,\n model?: BrDMriscModelDefinition,\n): BrDMriscResult => {\n const selectedModel = model ?? selectModel(input);\n if (!selectedModel) {\n throw new Error('No suitable model found for the available biomarkers');\n }\n\n // Build breakdown and compute linear predictor\n const breakdown: ComponentBreakdown[] = [];\n let x = selectedModel.intercept;\n\n for (const [key, coeff] of Object.entries(selectedModel.coefficients)) {\n const value = input[key as keyof BrDMriscInput];\n if (value === undefined) {\n throw new Error(`Missing required biomarker: ${key}`);\n }\n\n const contribution = coeff * value;\n x += contribution;\n\n breakdown.push({\n coefficient: coeff,\n contribution: Math.round(contribution * 10000) / 10000,\n key,\n name: BIOMARKER_NAMES_PT[key] ?? key,\n value,\n valueWithUnit: `${value.toFixed(1)} ${BIOMARKER_UNITS[key] ?? ''}`.trim(),\n });\n }\n\n // Add intercept to breakdown\n breakdown.push({\n coefficient: selectedModel.intercept,\n contribution: selectedModel.intercept,\n key: 'intercept',\n name: BIOMARKER_NAMES_PT['intercept'] ?? 'Intercepto',\n value: 1,\n valueWithUnit: '-',\n });\n\n // Logistic regression → probability at ~7.4 years\n const p = 1 / (1 + Math.exp(-x));\n\n // Extrapolate to 10-year risk\n // risk10y = 1 - (1 - p)^(10/followUp)\n const risk10y = 1 - Math.pow(1 - p, 10 / FOLLOW_UP_YEARS);\n\n const riskPercent = Math.round(risk10y * 1000) / 10; // One decimal place\n\n return {\n breakdown,\n calculatedAt: new Date().toISOString(),\n modelUsed: selectedModel,\n risk10y: Math.round(risk10y * 10000) / 10000,\n riskCategory: classifyRisk(riskPercent),\n riskPercent,\n };\n};\n\n/**\n * Validate biomarker values are within plausible ranges\n */\nexport const validateBiomarkers = (\n input: BrDMriscInput,\n): { isValid: boolean; errors: string[] } => {\n const errors: string[] = [];\n\n const checkRange = (value: number | undefined, key: string, name: string) => {\n if (value === undefined) return;\n const range = BIOMARKER_RANGES[key];\n if (!range) return;\n if (value < range.min || value > range.max) {\n errors.push(\n `${name} (${value}) fora do intervalo esperado [${range.min}-${range.max} ${range.unit}]`,\n );\n }\n };\n\n checkRange(input.fpg, 'fpg', 'Glicemia de Jejum');\n checkRange(input.hba1c, 'hba1c', 'HbA1c');\n checkRange(input.triglycerides, 'triglycerides', 'Triglicerídeos');\n checkRange(input.hdlc, 'hdlc', 'HDL-colesterol');\n\n return {\n errors,\n isValid: errors.length === 0,\n };\n};\n","/**\n * Unit Converters for BrDMrisc Calculator\n *\n * BrDMrisc expects: FPG (mg/dL), HbA1c (%), triglycerides (mg/dL), HDL-c (mg/dL)\n * Brazilian labs typically report in these units already, but some may\n * use mmol/L for glucose or mmol/mol for HbA1c (IFCC standard).\n */\n\n/**\n * Conversion factors for each biomarker\n * Key: source unit, Value: multiplier to get target unit\n */\nexport const CONVERSION_FACTORS: Record<string, Record<string, number>> = {\n fpg: {\n 'mg/dL': 1,\n 'mmol/L': 18.0182, // mmol/L → mg/dL\n },\n hba1c: {\n '%': 1,\n // IFCC mmol/mol → NGSP %: HbA1c(%) = 0.0915 × HbA1c(mmol/mol) + 2.15\n // Handled as special case in convertToTargetUnit\n },\n hdlc: {\n 'mg/dL': 1,\n 'mmol/L': 38.67, // mmol/L → mg/dL\n },\n triglycerides: {\n 'mg/dL': 1,\n 'mmol/L': 88.57, // mmol/L → mg/dL\n },\n};\n\n/**\n * Target units for BrDMrisc calculation\n */\nexport const TARGET_UNITS: Record<string, string> = {\n fpg: 'mg/dL',\n hba1c: '%',\n hdlc: 'mg/dL',\n triglycerides: 'mg/dL',\n};\n\n/**\n * Convert a biomarker value to the target unit expected by BrDMrisc\n */\nexport const convertToTargetUnit = (\n biomarker: string,\n value: number,\n sourceUnit: string,\n): number => {\n const normalizedUnit = sourceUnit.trim();\n\n // Special case: HbA1c IFCC (mmol/mol) → NGSP (%)\n if (biomarker === 'hba1c' && normalizedUnit.includes('mmol/mol')) {\n return 0.0915 * value + 2.15;\n }\n\n const factors = CONVERSION_FACTORS[biomarker];\n if (!factors) return value;\n\n // Try exact match first\n const factor = factors[normalizedUnit];\n if (factor !== undefined) return value * factor;\n\n // Case-insensitive match\n const match = Object.keys(factors).find((k) => k.toLowerCase() === normalizedUnit.toLowerCase());\n if (match) return value * factors[match]!;\n\n return value;\n};\n\n/**\n * Auto-detect unit and convert to target if needed.\n * Uses heuristics based on typical value ranges.\n */\nexport const autoConvertToTarget = (\n biomarker: string,\n value: number,\n unit?: string,\n): { value: number; unit: string; wasConverted: boolean } => {\n const targetUnit = TARGET_UNITS[biomarker] || '';\n\n if (unit) {\n const normalizedUnit = unit.trim().toLowerCase();\n const normalizedTarget = targetUnit.toLowerCase();\n\n if (normalizedUnit === normalizedTarget) {\n return { unit: targetUnit, value, wasConverted: false };\n }\n\n const converted = convertToTargetUnit(biomarker, value, unit);\n return {\n unit: targetUnit,\n value: converted,\n wasConverted: normalizedUnit !== normalizedTarget,\n };\n }\n\n // Heuristic-based detection\n switch (biomarker) {\n case 'fpg':\n // mmol/L typically 3-20, mg/dL typically 60-500\n if (value < 30) {\n return { unit: 'mg/dL', value: value * 18.0182, wasConverted: true };\n }\n return { unit: 'mg/dL', value, wasConverted: false };\n\n case 'hba1c':\n // IFCC mmol/mol typically 20-120, NGSP % typically 4-15\n if (value > 20) {\n return { unit: '%', value: 0.0915 * value + 2.15, wasConverted: true };\n }\n return { unit: '%', value, wasConverted: false };\n\n case 'triglycerides':\n // mmol/L typically 0.5-10, mg/dL typically 50-2000\n if (value < 15) {\n return { unit: 'mg/dL', value: value * 88.57, wasConverted: true };\n }\n return { unit: 'mg/dL', value, wasConverted: false };\n\n case 'hdlc':\n // mmol/L typically 0.5-3, mg/dL typically 20-100\n if (value < 5) {\n return { unit: 'mg/dL', value: value * 38.67, wasConverted: true };\n }\n return { unit: 'mg/dL', value, wasConverted: false };\n\n default:\n return { unit: targetUnit, value, wasConverted: false };\n }\n};\n","/**\n * Derived Biomarker Calculator\n *\n * Derives biomarkers from existing extracted values when the lab report\n * doesn't include them directly.\n *\n * Currently supports:\n * - HOMA-IR = (Fasting Glucose × Fasting Insulin) / 405\n * - VLDL = Triglycerides / 5\n * - BMI = weight_kg / (height_m)² (requires UserContext with height)\n */\n\nimport { codeToLoinc } from '@precisa-saude/fhir';\n\nexport interface BiomarkerInput {\n code: string;\n unit?: string;\n value: number | string;\n}\n\nexport interface DerivedBiomarker {\n code: string;\n loincCode?: string;\n name: string;\n unit: string;\n value: number;\n}\n\nexport interface DerivedOptions {\n codeToLoinc?: (code: string) => string | undefined;\n userContext?: { heightCm?: number };\n}\n\ninterface CalculationDef {\n calculate: (values: Map<string, number>) => number;\n code: string;\n inputs: string[];\n unit: string;\n}\n\ninterface ContextCalculationDef {\n calculate: (values: Map<string, number>, ctx: { heightCm?: number }) => number;\n canCalculate: (ctx: { heightCm?: number }) => boolean;\n code: string;\n inputs: string[];\n unit: string;\n}\n\nconst CALCULATIONS: CalculationDef[] = [\n {\n calculate: (v) => (v.get('Glucose')! * v.get('Insulin')!) / 405,\n code: 'HOMA_IR',\n inputs: ['Glucose', 'Insulin'],\n unit: 'index',\n },\n {\n calculate: (v) => v.get('Triglycerides')! / 5,\n code: 'VLDL',\n inputs: ['Triglycerides'],\n unit: 'mg/dL',\n },\n];\n\nconst CONTEXT_CALCULATIONS: ContextCalculationDef[] = [\n {\n calculate: (v, ctx) => {\n const weightKg = v.get('TotalMass')!;\n const heightM = ctx.heightCm! / 100;\n return weightKg / (heightM * heightM);\n },\n canCalculate: (ctx) =>\n typeof ctx.heightCm === 'number' && ctx.heightCm >= 50 && ctx.heightCm <= 250,\n code: 'BMI',\n inputs: ['TotalMass'],\n unit: 'kg/m2',\n },\n];\n\n/**\n * Compute derived biomarkers from existing extracted values.\n * Only adds a calculated biomarker if:\n * - All required inputs are present with numeric values\n * - The biomarker isn't already present in the results\n */\nexport function computeDerivedBiomarkers(\n biomarkers: BiomarkerInput[],\n options?: DerivedOptions,\n): DerivedBiomarker[] {\n const lookupLoinc = options?.codeToLoinc ?? codeToLoinc;\n const byCode = new Map<string, BiomarkerInput>();\n for (const b of biomarkers) {\n byCode.set(b.code, b);\n }\n\n const added: DerivedBiomarker[] = [];\n\n for (const calc of CALCULATIONS) {\n if (byCode.has(calc.code)) continue;\n\n const values = new Map<string, number>();\n let allPresent = true;\n\n for (const input of calc.inputs) {\n const b = byCode.get(input);\n if (!b || typeof b.value !== 'number') {\n allPresent = false;\n break;\n }\n values.set(input, b.value);\n }\n\n if (!allPresent) continue;\n\n const rawValue = calc.calculate(values);\n const value = parseFloat(rawValue.toPrecision(10));\n const loinc = lookupLoinc(calc.code);\n\n added.push({\n code: calc.code,\n loincCode: loinc ?? undefined,\n name: calc.code,\n unit: calc.unit,\n value,\n });\n }\n\n if (options?.userContext) {\n for (const calc of CONTEXT_CALCULATIONS) {\n if (byCode.has(calc.code)) continue;\n if (!calc.canCalculate(options.userContext)) continue;\n\n const values = new Map<string, number>();\n let allPresent = true;\n\n for (const input of calc.inputs) {\n const b = byCode.get(input);\n if (!b || typeof b.value !== 'number') {\n allPresent = false;\n break;\n }\n values.set(input, b.value);\n }\n\n if (!allPresent) continue;\n\n const rawValue = calc.calculate(values, options.userContext);\n const value = parseFloat(rawValue.toPrecision(10));\n const loinc = lookupLoinc(calc.code);\n\n added.push({\n code: calc.code,\n loincCode: loinc ?? undefined,\n name: calc.code,\n unit: calc.unit,\n value,\n });\n }\n }\n\n return added;\n}\n"]}
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/phenoage/index.ts","../src/phenoage/constants.ts","../src/phenoage/calculator.ts","../src/phenoage/unit-converters.ts","../src/brdmrisc/index.ts","../src/brdmrisc/constants.ts","../src/brdmrisc/calculator.ts","../src/brdmrisc/unit-converters.ts","../src/derived/calculator.ts"],"sourcesContent":["export * from './calculator';\nexport * from './constants';\nexport * from './types';\nexport * from './unit-converters';\n","/**\n * PhenoAge Calculator Constants\n *\n * Coefficients from Levine et al. (2018) Table 1\n * Units: albumin (g/L), creatinine (μmol/L), glucose (mmol/L),\n * CRP (ln of mg/L), lymphocyte (%), MCV (fL), RDW (%),\n * ALP (U/L), WBC (10^9/L), age (years)\n */\n\n/**\n * Regression coefficients from the Cox proportional hazards model\n */\nexport const PHENOAGE_COEFFICIENTS = {\n age: 0.0804,\n albumin: -0.0336,\n alkalinePhosphatase: 0.0019,\n creatinine: 0.0095,\n glucose: 0.1953,\n intercept: -19.9067,\n logCrp: 0.0954,\n lymphocytePercent: -0.012,\n mcv: 0.0268,\n rdw: 0.3306,\n wbc: 0.0554,\n} as const;\n\n/**\n * Gompertz mortality model parameters\n */\nexport const GOMPERTZ_PARAMS = {\n ageCoefficient: 0.090165,\n baseAge: 141.50225,\n gamma: 0.0076927,\n mortalityConstant: 0.00553,\n} as const;\n\n/**\n * Biomarker reference ranges for validation\n */\nexport const BIOMARKER_RANGES = {\n albumin: { max: 60, min: 10, typical: { max: 50, min: 35 }, unit: 'g/L' },\n alkalinePhosphatase: { max: 500, min: 10, typical: { max: 130, min: 40 }, unit: 'U/L' },\n chronologicalAge: { max: 120, min: 18, typical: { max: 100, min: 18 }, unit: 'anos' },\n creatinine: { max: 500, min: 20, typical: { max: 110, min: 60 }, unit: 'μmol/L' },\n crp: { max: 200, min: 0.01, typical: { max: 3, min: 0 }, unit: 'mg/L' },\n glucose: { max: 30, min: 2, typical: { max: 6.0, min: 4.0 }, unit: 'mmol/L' },\n lymphocytePercent: { max: 80, min: 1, typical: { max: 40, min: 20 }, unit: '%' },\n mcv: { max: 150, min: 50, typical: { max: 100, min: 80 }, unit: 'fL' },\n rdw: { max: 25, min: 8, typical: { max: 15, min: 11 }, unit: '%' },\n wbc: { max: 30, min: 1, typical: { max: 11.0, min: 4.0 }, unit: '10^9/L' },\n} as const;\n\n/**\n * Mapping from FHIR biomarker codes to PhenoAge input fields\n */\nexport const FHIR_CODE_TO_PHENOAGE: Record<string, keyof typeof BIOMARKER_RANGES> = {\n Albumin: 'albumin',\n AlkalinePhosphatase: 'alkalinePhosphatase',\n Creatinine: 'creatinine',\n CRP: 'crp',\n Glucose: 'glucose',\n Lymphocytes: 'lymphocytePercent',\n MCV: 'mcv',\n RDW: 'rdw',\n WBC: 'wbc',\n};\n\n/**\n * Required biomarker codes for PhenoAge calculation\n */\nexport const REQUIRED_BIOMARKERS = [\n 'Albumin',\n 'Creatinine',\n 'Glucose',\n 'CRP',\n 'Lymphocytes',\n 'MCV',\n 'RDW',\n 'AlkalinePhosphatase',\n 'WBC',\n] as const;\n\n/**\n * Portuguese names for biomarkers (for display)\n */\nexport const BIOMARKER_NAMES_PT: Record<string, string> = {\n age: 'Idade',\n albumin: 'Albumina',\n alkalinePhosphatase: 'Fosfatase Alcalina',\n creatinine: 'Creatinina',\n crp: 'PCR',\n glucose: 'Glicose',\n intercept: 'Intercepto',\n lymphocytePercent: 'Linfócitos',\n mcv: 'VCM',\n rdw: 'RDW',\n wbc: 'Leucócitos',\n};\n\n/**\n * Short descriptions for biomarkers (for tooltips)\n * Explains what each biomarker indicates in the context of aging\n */\nexport const BIOMARKER_DESCRIPTIONS_PT: Record<string, string> = {\n age: 'Sua idade cronológica em anos',\n albumin: 'Proteína do fígado que indica estado nutricional e função hepática',\n alkalinePhosphatase: 'Enzima que reflete saúde do fígado e dos ossos',\n creatinine: 'Marcador de função renal produzido pelos músculos',\n crp: 'Proteína C-reativa, indica inflamação no corpo',\n glucose: 'Nível de açúcar no sangue, relacionado ao metabolismo',\n intercept: 'Constante da fórmula de regressão',\n lymphocytePercent: 'Células de defesa do sistema imunológico',\n mcv: 'Volume médio das hemácias, indica saúde das células vermelhas',\n rdw: 'Variação no tamanho das hemácias, marcador de inflamação',\n wbc: 'Total de células brancas, indica estado imunológico',\n};\n\n/**\n * Biomarker information for lab order requests\n * Includes LOINC codes and English names for lab requisitions\n */\nexport interface BiomarkerLabInfo {\n loincCode: string;\n nameEn: string;\n namePt: string;\n}\n\n/**\n * Lab order information for PhenoAge biomarkers\n * Mapped by FHIR code\n */\nexport const BIOMARKER_LAB_INFO: Record<string, BiomarkerLabInfo> = {\n Albumin: {\n loincCode: '1751-7',\n nameEn: 'Albumin [Mass/volume] in Serum or Plasma',\n namePt: 'Albumina',\n },\n AlkalinePhosphatase: {\n loincCode: '6768-6',\n nameEn: 'Alkaline phosphatase [Enzymatic activity/volume] in Serum or Plasma',\n namePt: 'Fosfatase Alcalina',\n },\n Creatinine: {\n loincCode: '2160-0',\n nameEn: 'Creatinine [Mass/volume] in Serum or Plasma',\n namePt: 'Creatinina',\n },\n CRP: {\n loincCode: '1988-5',\n nameEn: 'C reactive protein [Mass/volume] in Serum or Plasma',\n namePt: 'Proteína C-Reativa (PCR)',\n },\n Glucose: {\n loincCode: '2345-7',\n nameEn: 'Glucose [Mass/volume] in Serum or Plasma',\n namePt: 'Glicose',\n },\n Lymphocytes: {\n loincCode: '736-9',\n nameEn: 'Lymphocytes/100 leukocytes in Blood by Automated count',\n namePt: 'Linfócitos (%)',\n },\n MCV: {\n loincCode: '787-2',\n nameEn: 'MCV [Entitic volume] by Automated count',\n namePt: 'Volume Corpuscular Médio (VCM)',\n },\n RDW: {\n loincCode: '788-0',\n nameEn: 'Erythrocyte distribution width [Ratio] by Automated count',\n namePt: 'Amplitude de Distribuição dos Eritrócitos (RDW)',\n },\n WBC: {\n loincCode: '6690-2',\n nameEn: 'Leukocytes [#/volume] in Blood by Automated count',\n namePt: 'Contagem de Leucócitos',\n },\n};\n","/**\n * PhenoAge Calculator\n *\n * Implements the Levine PhenoAge algorithm from:\n * \"An epigenetic biomarker of aging for lifespan and healthspan\" (Aging, 2018)\n */\n\nimport {\n BIOMARKER_NAMES_PT,\n BIOMARKER_RANGES,\n GOMPERTZ_PARAMS,\n PHENOAGE_COEFFICIENTS,\n} from './constants';\nimport type { ComponentBreakdown, PhenoAgeInput, PhenoAgeResult } from './types';\n\n/**\n * Calculates the linear predictor (xb) from biomarkers\n * @returns Object with xb value and breakdown of each component\n */\nconst calculateLinearPredictor = (\n input: PhenoAgeInput,\n): { xb: number; breakdown: ComponentBreakdown[] } => {\n const logCrp = Math.log(Math.max(input.crp, 0.1)); // Avoid log(0)\n\n const components: Array<{\n key: string;\n value: number;\n coefficient: number;\n unit: string;\n displayValue?: string;\n }> = [\n {\n coefficient: PHENOAGE_COEFFICIENTS.albumin,\n key: 'albumin',\n unit: BIOMARKER_RANGES.albumin.unit,\n value: input.albumin,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.creatinine,\n key: 'creatinine',\n unit: BIOMARKER_RANGES.creatinine.unit,\n value: input.creatinine,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.glucose,\n key: 'glucose',\n unit: BIOMARKER_RANGES.glucose.unit,\n value: input.glucose,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.logCrp,\n displayValue: `ln(${input.crp.toFixed(2)})`,\n key: 'crp',\n unit: 'ln(mg/L)',\n value: logCrp,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.lymphocytePercent,\n key: 'lymphocytePercent',\n unit: BIOMARKER_RANGES.lymphocytePercent.unit,\n value: input.lymphocytePercent,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.mcv,\n key: 'mcv',\n unit: BIOMARKER_RANGES.mcv.unit,\n value: input.mcv,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.rdw,\n key: 'rdw',\n unit: BIOMARKER_RANGES.rdw.unit,\n value: input.rdw,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.alkalinePhosphatase,\n key: 'alkalinePhosphatase',\n unit: BIOMARKER_RANGES.alkalinePhosphatase.unit,\n value: input.alkalinePhosphatase,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.wbc,\n key: 'wbc',\n unit: BIOMARKER_RANGES.wbc.unit,\n value: input.wbc,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.age,\n displayValue: `${Math.round(input.chronologicalAge)} anos`,\n key: 'age',\n unit: 'anos',\n value: input.chronologicalAge,\n },\n ];\n\n const breakdown: ComponentBreakdown[] = components.map((c) => ({\n coefficient: c.coefficient,\n contribution: Math.round(c.coefficient * c.value * 10000) / 10000,\n key: c.key,\n name: BIOMARKER_NAMES_PT[c.key] ?? c.key,\n valueWithUnit: c.displayValue ? `${c.displayValue}` : `${c.value.toFixed(2)} ${c.unit}`,\n }));\n\n // Add intercept\n breakdown.push({\n coefficient: PHENOAGE_COEFFICIENTS.intercept,\n contribution: PHENOAGE_COEFFICIENTS.intercept,\n key: 'intercept',\n name: BIOMARKER_NAMES_PT['intercept'] ?? 'Intercepto',\n valueWithUnit: '-',\n });\n\n const xb =\n PHENOAGE_COEFFICIENTS.intercept +\n components.reduce((sum, c) => sum + c.coefficient * c.value, 0);\n\n return { breakdown, xb };\n};\n\n/**\n * Calculates mortality score from linear predictor using Gompertz model\n */\nconst calculateMortalityScore = (xb: number): number => {\n const { gamma } = GOMPERTZ_PARAMS;\n\n // exp(xb) represents the hazard ratio\n const hazardRatio = Math.exp(xb);\n\n // Cumulative hazard over 120 months (10 years)\n const cumulativeHazard = (hazardRatio * (Math.exp(120 * gamma) - 1)) / gamma;\n\n // Convert to probability\n return 1 - Math.exp(-cumulativeHazard);\n};\n\n/**\n * Converts mortality score to PhenoAge\n */\nconst mortalityScoreToPhenoAge = (mortalityScore: number): number => {\n const { ageCoefficient, baseAge, mortalityConstant } = GOMPERTZ_PARAMS;\n\n // Inverse transformation to get biological age\n const innerLog = Math.log(1 - mortalityScore);\n const outerLog = Math.log(-mortalityConstant * innerLog);\n\n return baseAge + outerLog / ageCoefficient;\n};\n\n/**\n * Main PhenoAge calculation function\n *\n * @param input - Biomarker values in SI units plus chronological age\n * @returns Complete PhenoAge result with breakdown\n */\nexport const calculatePhenoAge = (input: PhenoAgeInput): PhenoAgeResult => {\n // Validate all inputs are valid numbers (not NaN)\n const inputValues = {\n albumin: input.albumin,\n alkalinePhosphatase: input.alkalinePhosphatase,\n chronologicalAge: input.chronologicalAge,\n creatinine: input.creatinine,\n crp: input.crp,\n glucose: input.glucose,\n lymphocytePercent: input.lymphocytePercent,\n mcv: input.mcv,\n rdw: input.rdw,\n wbc: input.wbc,\n };\n\n for (const [key, value] of Object.entries(inputValues)) {\n if (typeof value !== 'number' || Number.isNaN(value)) {\n throw new Error(`Invalid input value for ${key}: ${value}`);\n }\n }\n\n // Calculate linear predictor with breakdown\n const { breakdown, xb } = calculateLinearPredictor(input);\n\n // Calculate mortality score\n const mortalityScore = calculateMortalityScore(xb);\n\n // Convert to PhenoAge\n const phenoAge = mortalityScoreToPhenoAge(mortalityScore);\n\n // Calculate age difference\n const ageDifference = phenoAge - input.chronologicalAge;\n\n return {\n ageDifference: Math.round(ageDifference * 10) / 10,\n breakdown,\n calculatedAt: new Date().toISOString(),\n chronologicalAge: input.chronologicalAge,\n linearPredictor: Math.round(xb * 10000) / 10000,\n mortalityScore: Math.round(mortalityScore * 10000) / 10000,\n phenoAge: Math.round(phenoAge * 10) / 10,\n };\n};\n\n/**\n * Validates biomarker values are within acceptable ranges\n */\nexport const validateBiomarkers = (\n input: PhenoAgeInput,\n): { isValid: boolean; errors: string[] } => {\n const errors: string[] = [];\n\n const checkRange = (value: number, key: keyof typeof BIOMARKER_RANGES, name: string) => {\n const range = BIOMARKER_RANGES[key];\n if (value < range.min || value > range.max) {\n errors.push(`${name} (${value}) fora do intervalo esperado [${range.min}-${range.max}]`);\n }\n };\n\n checkRange(input.albumin, 'albumin', 'Albumina');\n checkRange(input.creatinine, 'creatinine', 'Creatinina');\n checkRange(input.glucose, 'glucose', 'Glicose');\n checkRange(input.crp, 'crp', 'PCR');\n checkRange(input.lymphocytePercent, 'lymphocytePercent', 'Linfócitos');\n checkRange(input.mcv, 'mcv', 'VCM');\n checkRange(input.rdw, 'rdw', 'RDW');\n checkRange(input.alkalinePhosphatase, 'alkalinePhosphatase', 'Fosfatase Alcalina');\n checkRange(input.wbc, 'wbc', 'Leucócitos');\n checkRange(input.chronologicalAge, 'chronologicalAge', 'Idade');\n\n // Special validation for CRP (must be positive for log transform)\n if (input.crp <= 0) {\n errors.push('PCR deve ser maior que 0');\n }\n\n return {\n errors,\n isValid: errors.length === 0,\n };\n};\n","/**\n * Unit Converters for PhenoAge Calculator\n *\n * Brazilian labs (Weinmann, Fleury, etc.) often report in different units\n * than the SI units required by the PhenoAge algorithm.\n */\n\n/**\n * Conversion factors for each biomarker\n * Key: source unit, Value: multiplier to get SI unit\n */\nexport const CONVERSION_FACTORS: Record<string, Record<string, number>> = {\n albumin: {\n 'g/dL': 10, // g/dL → g/L\n 'g/L': 1,\n },\n alkalinePhosphatase: {\n 'U/L': 1,\n },\n creatinine: {\n 'mg/dL': 88.4, // mg/dL → μmol/L\n 'umol/L': 1,\n 'μmol/L': 1,\n },\n crp: {\n 'mg/dL': 10, // mg/dL → mg/L\n 'mg/L': 1,\n },\n glucose: {\n 'mg/dL': 1 / 18.0182, // mg/dL → mmol/L\n 'mmol/L': 1,\n },\n lymphocytePercent: {\n '%': 1,\n },\n mcv: {\n fL: 1,\n },\n rdw: {\n '%': 1,\n },\n wbc: {\n '/uL': 0.001,\n '/µL': 0.001, // cells/μL → 10^9/L\n '10^9/L': 1,\n '1000/μL': 1,\n '10³/µL': 1, // Same as 10^9/L\n 'K/uL': 1,\n 'K/µL': 1,\n 'Thousand/uL': 1, // Brazilian labs often use this format\n 'thousand/uL': 1,\n 'Thousand/µL': 1,\n 'thousand/µL': 1,\n },\n};\n\n/**\n * Target units for PhenoAge calculation (SI units)\n */\nexport const TARGET_UNITS: Record<string, string> = {\n albumin: 'g/L',\n alkalinePhosphatase: 'U/L',\n creatinine: 'μmol/L',\n crp: 'mg/L',\n glucose: 'mmol/L',\n lymphocytePercent: '%',\n mcv: 'fL',\n rdw: '%',\n wbc: '10^9/L',\n};\n\n/**\n * Convert a biomarker value from source unit to SI unit\n *\n * @param biomarker - The biomarker key (e.g., 'albumin', 'glucose')\n * @param value - The numeric value\n * @param sourceUnit - The unit of the input value\n * @returns Converted value in SI units\n * @throws Error if the unit is not recognized\n */\nexport const convertToSI = (biomarker: string, value: number, sourceUnit: string): number => {\n const factors = CONVERSION_FACTORS[biomarker];\n\n if (!factors) {\n throw new Error(`Unknown biomarker: ${biomarker}`);\n }\n\n // Normalize unit string for matching\n const normalizedUnit = sourceUnit.trim();\n const factor = factors[normalizedUnit];\n\n if (factor === undefined) {\n const normalizedLower = normalizedUnit.toLowerCase();\n\n // First try exact case-insensitive match\n const exactMatch = Object.keys(factors).find((key) => key.toLowerCase() === normalizedLower);\n if (exactMatch) {\n return value * factors[exactMatch]!;\n }\n\n // For partial matches, sort keys by length (longest first) to prefer more specific matches\n // This prevents \"/µL\" from matching before \"10³/µL\"\n const sortedKeys = Object.keys(factors).sort((a, b) => b.length - a.length);\n const partialMatch = sortedKeys.find((key) => normalizedLower.includes(key.toLowerCase()));\n\n if (partialMatch) {\n return value * factors[partialMatch]!;\n }\n\n throw new Error(`Unknown unit \"${sourceUnit}\" for ${biomarker}`);\n }\n\n return value * factor;\n};\n\n/**\n * Check if a value needs conversion\n *\n * @param biomarker - The biomarker key\n * @param sourceUnit - The current unit\n * @returns true if conversion is needed\n */\nexport const needsConversion = (biomarker: string, sourceUnit: string): boolean => {\n const targetUnit = TARGET_UNITS[biomarker];\n if (!targetUnit) return false;\n\n const normalizedSource = sourceUnit.trim().toLowerCase();\n const normalizedTarget = targetUnit.toLowerCase();\n\n return normalizedSource !== normalizedTarget;\n};\n\n/**\n * Auto-detect unit and convert to SI if needed\n * Uses heuristics based on typical value ranges\n *\n * @param biomarker - The biomarker key\n * @param value - The numeric value\n * @param unit - Optional unit hint\n * @returns Object with converted value and detected unit\n */\nexport const autoConvertToSI = (\n biomarker: string,\n value: number,\n unit?: string,\n): { value: number; unit: string; wasConverted: boolean } => {\n // If unit is provided and we know how to convert it\n if (unit) {\n try {\n const converted = convertToSI(biomarker, value, unit);\n const wasConverted = needsConversion(biomarker, unit);\n return {\n unit: TARGET_UNITS[biomarker] || unit,\n value: converted,\n wasConverted,\n };\n } catch {\n // Fall through to heuristic detection\n }\n }\n\n // Heuristic-based detection for common cases\n switch (biomarker) {\n case 'albumin':\n // g/dL typically 3-5, g/L typically 30-50\n if (value < 10) {\n return { unit: 'g/L', value: value * 10, wasConverted: true };\n }\n return { unit: 'g/L', value, wasConverted: false };\n\n case 'creatinine':\n // mg/dL typically 0.5-1.5, μmol/L typically 45-130\n if (value < 15) {\n return { unit: 'μmol/L', value: value * 88.4, wasConverted: true };\n }\n return { unit: 'μmol/L', value, wasConverted: false };\n\n case 'glucose':\n // mg/dL typically 70-140, mmol/L typically 4-8\n if (value > 20) {\n return { unit: 'mmol/L', value: value / 18.0182, wasConverted: true };\n }\n return { unit: 'mmol/L', value, wasConverted: false };\n\n case 'wbc':\n // cells/μL typically 4000-11000, 10^9/L typically 4-11\n if (value > 100) {\n return { unit: '10^9/L', value: value / 1000, wasConverted: true };\n }\n return { unit: '10^9/L', value, wasConverted: false };\n\n default:\n return { unit: TARGET_UNITS[biomarker] || '', value, wasConverted: false };\n }\n};\n","export * from './calculator';\nexport * from './constants';\nexport * from './types';\nexport * from './unit-converters';\n","/**\n * BrDMrisc Calculator Constants\n *\n * All 14 model definitions with coefficients extracted from:\n * Bracco et al. (2023) Table 2 & Supplementary Material\n * DOI: 10.3389/fendo.2023.1166147\n *\n * Models 1-6 are lab-only (MVP scope).\n * Models 7-14 include clinical variables (Phase 2).\n *\n * Follow-up period: 7.4 years (ELSA-Brasil median)\n * Units expected: FPG (mg/dL), HbA1c (%), triglycerides (mg/dL), HDL-c (mg/dL)\n */\n\nimport type { BrDMriscInput, BrDMriscModelDefinition } from './types';\n\n/**\n * ELSA-Brasil median follow-up in years.\n * Used to extrapolate the logistic regression probability to 10-year risk.\n */\nexport const FOLLOW_UP_YEARS = 7.4;\n\n/**\n * All 14 BrDMrisc models.\n * Models ordered by ID. Lab-only models (1-6) are the MVP.\n *\n * Coefficients from Supplementary Table S2 of the paper,\n * verified against the Shiny app demo at:\n * https://paulabracco.shinyapps.io/BrDMrisc_pt/\n */\nexport const BRDMRISC_MODELS: BrDMriscModelDefinition[] = [\n // === Lab-only models (MVP) ===\n {\n auc: 0.776,\n coefficients: {\n fpg: 0.0352,\n },\n id: 1,\n intercept: -5.8282,\n isLabOnly: true,\n name: 'FPG only',\n namePt: 'Apenas Glicemia de Jejum',\n requiredBiomarkers: ['fpg'],\n },\n {\n auc: 0.668,\n coefficients: {\n hba1c: 0.731,\n },\n id: 2,\n intercept: -6.3199,\n isLabOnly: true,\n name: 'HbA1c only',\n namePt: 'Apenas HbA1c',\n requiredBiomarkers: ['hba1c'],\n },\n {\n auc: 0.793,\n coefficients: {\n fpg: 0.029,\n hba1c: 0.4427,\n },\n id: 3,\n intercept: -7.37,\n isLabOnly: true,\n name: 'FPG + HbA1c',\n namePt: 'Glicemia + HbA1c',\n requiredBiomarkers: ['fpg', 'hba1c'],\n },\n {\n auc: 0.79,\n coefficients: {\n fpg: 0.0345,\n triglycerides: 0.0017,\n },\n id: 4,\n intercept: -5.9706,\n isLabOnly: true,\n name: 'FPG + Triglycerides',\n namePt: 'Glicemia + Triglicerídeos',\n requiredBiomarkers: ['fpg', 'triglycerides'],\n },\n {\n auc: 0.796,\n coefficients: {\n fpg: 0.0338,\n hdlc: -0.0161,\n triglycerides: 0.0011,\n },\n id: 5,\n intercept: -5.3879,\n isLabOnly: true,\n name: 'FPG + Lipids',\n namePt: 'Glicemia + Lipídios',\n requiredBiomarkers: ['fpg', 'triglycerides', 'hdlc'],\n },\n {\n auc: 0.813,\n coefficients: {\n fpg: 0.0267,\n hba1c: 0.3943,\n hdlc: -0.0149,\n triglycerides: 0.0009,\n },\n id: 6,\n intercept: -6.9195,\n isLabOnly: true,\n name: 'FPG + HbA1c + Lipids',\n namePt: 'Glicemia + HbA1c + Lipídios',\n requiredBiomarkers: ['fpg', 'hba1c', 'triglycerides', 'hdlc'],\n },\n\n // === Clinical models (Phase 2 — require user input) ===\n {\n auc: 0.744,\n coefficients: {\n bmi: 0.0642,\n familyHistory: 0.5915,\n waist: 0.0111,\n },\n id: 7,\n intercept: -6.5023,\n isLabOnly: false,\n name: 'Clinical only',\n namePt: 'Apenas Clínico',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory'],\n },\n {\n auc: 0.8,\n coefficients: {\n bmi: 0.0454,\n familyHistory: 0.475,\n fpg: 0.0305,\n waist: 0.0047,\n },\n id: 8,\n intercept: -7.3297,\n isLabOnly: false,\n name: 'Clinical + FPG',\n namePt: 'Clínico + Glicemia',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg'],\n },\n {\n auc: 0.725,\n coefficients: {\n bmi: 0.0471,\n familyHistory: 0.5284,\n hba1c: 0.6001,\n waist: 0.0063,\n },\n id: 9,\n intercept: -8.0917,\n isLabOnly: false,\n name: 'Clinical + HbA1c',\n namePt: 'Clínico + HbA1c',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'hba1c'],\n },\n {\n auc: 0.814,\n coefficients: {\n bmi: 0.0373,\n familyHistory: 0.4393,\n fpg: 0.0253,\n hba1c: 0.3688,\n waist: 0.0029,\n },\n id: 10,\n intercept: -8.5817,\n isLabOnly: false,\n name: 'Clinical + FPG + HbA1c',\n namePt: 'Clínico + Glicemia + HbA1c',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg', 'hba1c'],\n },\n {\n auc: 0.808,\n coefficients: {\n bmi: 0.0427,\n familyHistory: 0.4532,\n fpg: 0.0299,\n triglycerides: 0.0015,\n waist: 0.0031,\n },\n id: 11,\n intercept: -7.5028,\n isLabOnly: false,\n name: 'Clinical + FPG + Triglycerides',\n namePt: 'Clínico + Glicemia + Triglicerídeos',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg', 'triglycerides'],\n },\n {\n auc: 0.813,\n coefficients: {\n bmi: 0.0408,\n familyHistory: 0.4396,\n fpg: 0.0293,\n hdlc: -0.0128,\n triglycerides: 0.0009,\n waist: 0.0019,\n },\n id: 12,\n intercept: -6.9757,\n isLabOnly: false,\n name: 'Clinical + FPG + Lipids',\n namePt: 'Clínico + Glicemia + Lipídios',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg', 'triglycerides', 'hdlc'],\n },\n {\n auc: 0.822,\n coefficients: {\n bmi: 0.0334,\n familyHistory: 0.4107,\n fpg: 0.0238,\n hba1c: 0.3266,\n hdlc: -0.0117,\n triglycerides: 0.0006,\n waist: 0.0015,\n },\n id: 13,\n intercept: -8.2,\n isLabOnly: false,\n name: 'Clinical + FPG + HbA1c + Lipids',\n namePt: 'Clínico + Glicemia + HbA1c + Lipídios',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg', 'hba1c', 'triglycerides', 'hdlc'],\n },\n {\n auc: 0.699,\n coefficients: {\n bmi: 0.0494,\n ethnicity: 0.2901,\n familyHistory: 0.557,\n hypertension: 0.3653,\n waist: 0.0071,\n },\n id: 14,\n intercept: -7.244,\n isLabOnly: false,\n name: 'Clinical extended',\n namePt: 'Clínico Estendido',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'ethnicity', 'hypertension'],\n },\n];\n\n/**\n * Lab-only models for MVP, ordered by priority (highest AUC first)\n */\nexport const LAB_ONLY_MODELS = BRDMRISC_MODELS.filter((m) => m.isLabOnly);\n\n/**\n * Model selection priority for lab-only models (highest AUC first)\n */\nexport const LAB_MODEL_PRIORITY = [6, 5, 3, 4, 1, 2] as const;\n\n/**\n * FHIR biomarker code → BrDMrisc input field mapping\n */\nexport const FHIR_CODE_TO_BRDMRISC: Record<string, keyof BrDMriscInput> = {\n Glucose: 'fpg',\n HbA1c: 'hba1c',\n HDL: 'hdlc',\n Triglycerides: 'triglycerides',\n};\n\n/**\n * Required FHIR biomarker codes (all 4 lab biomarkers)\n */\nexport const BRDMRISC_BIOMARKER_CODES = ['Glucose', 'HbA1c', 'Triglycerides', 'HDL'] as const;\n\n/**\n * Portuguese names for biomarkers\n */\nexport const BIOMARKER_NAMES_PT: Record<string, string> = {\n fpg: 'Glicemia de Jejum',\n hba1c: 'Hemoglobina Glicada',\n hdlc: 'HDL-colesterol',\n intercept: 'Intercepto',\n triglycerides: 'Triglicerídeos',\n};\n\n/**\n * Biomarker units used by the model\n */\nexport const BIOMARKER_UNITS: Record<string, string> = {\n fpg: 'mg/dL',\n hba1c: '%',\n hdlc: 'mg/dL',\n triglycerides: 'mg/dL',\n};\n\n/**\n * Biomarker validation ranges (plausible values)\n */\nexport const BIOMARKER_RANGES: Record<string, { min: number; max: number; unit: string }> = {\n fpg: { max: 500, min: 40, unit: 'mg/dL' },\n hba1c: { max: 20, min: 3, unit: '%' },\n hdlc: { max: 150, min: 10, unit: 'mg/dL' },\n triglycerides: { max: 2000, min: 20, unit: 'mg/dL' },\n};\n\n/**\n * Risk category thresholds based on clinical recommendations\n * from Bracco et al. (2023). The paper notes that 20% triggers\n * intensive prevention.\n */\nexport const RISK_THRESHOLDS = {\n high: 0.2, // 20-35% — high risk\n moderate: 0.1, // 10-20% — moderate risk\n veryHigh: 0.35, // >= 35% — very high risk\n} as const;\n","/**\n * BrDMrisc Calculator\n *\n * Implements the BrDMrisc algorithm from:\n * Bracco et al. (2023) - \"BrDMrisc: a Brazilian diabetes risk score\n * for screening of type 2 diabetes mellitus\"\n * Frontiers in Endocrinology, DOI: 10.3389/fendo.2023.1166147\n *\n * Formula:\n * x = intercept + Σ(coeff_i × value_i)\n * p = 1 / (1 + exp(-x)) — logistic regression probability (~7.4 years)\n * risk10y = 1 - (1 - p)^(10/7.4) — extrapolated to 10-year risk\n */\n\nimport {\n BIOMARKER_NAMES_PT,\n BIOMARKER_RANGES,\n BIOMARKER_UNITS,\n BRDMRISC_MODELS,\n FOLLOW_UP_YEARS,\n LAB_MODEL_PRIORITY,\n RISK_THRESHOLDS,\n} from './constants';\nimport type {\n BrDMriscInput,\n BrDMriscModelDefinition,\n BrDMriscResult,\n ComponentBreakdown,\n RiskCategory,\n} from './types';\n\n/**\n * Select the best available model based on which biomarkers are present.\n * Iterates through models in priority order (highest AUC first) and\n * returns the first one whose requirements are fully met.\n */\nexport const selectModel = (\n input: BrDMriscInput,\n labOnly = true,\n): BrDMriscModelDefinition | null => {\n const available = new Set<string>();\n if (input.fpg !== undefined && !Number.isNaN(input.fpg)) available.add('fpg');\n if (input.hba1c !== undefined && !Number.isNaN(input.hba1c)) available.add('hba1c');\n if (input.triglycerides !== undefined && !Number.isNaN(input.triglycerides))\n available.add('triglycerides');\n if (input.hdlc !== undefined && !Number.isNaN(input.hdlc)) available.add('hdlc');\n\n if (available.size === 0) return null;\n\n const priority = labOnly ? LAB_MODEL_PRIORITY : BRDMRISC_MODELS.map((m) => m.id);\n\n for (const modelId of priority) {\n const model = BRDMRISC_MODELS.find((m) => m.id === modelId);\n if (!model) continue;\n if (labOnly && !model.isLabOnly) continue;\n\n const hasAll = model.requiredBiomarkers.every((b) => available.has(b));\n if (hasAll) return model;\n }\n\n return null;\n};\n\n/**\n * Classify risk percentage into a category\n */\nexport const classifyRisk = (riskPercent: number): RiskCategory => {\n const risk = riskPercent / 100;\n if (risk >= RISK_THRESHOLDS.veryHigh) return 'very-high';\n if (risk >= RISK_THRESHOLDS.high) return 'high';\n if (risk >= RISK_THRESHOLDS.moderate) return 'moderate';\n return 'low';\n};\n\n/**\n * Calculate BrDMrisc 10-year diabetes risk\n */\nexport const calculateBrDMrisc = (\n input: BrDMriscInput,\n model?: BrDMriscModelDefinition,\n): BrDMriscResult => {\n const selectedModel = model ?? selectModel(input);\n if (!selectedModel) {\n throw new Error('No suitable model found for the available biomarkers');\n }\n\n // Build breakdown and compute linear predictor\n const breakdown: ComponentBreakdown[] = [];\n let x = selectedModel.intercept;\n\n for (const [key, coeff] of Object.entries(selectedModel.coefficients)) {\n const value = input[key as keyof BrDMriscInput];\n if (value === undefined) {\n throw new Error(`Missing required biomarker: ${key}`);\n }\n\n const contribution = coeff * value;\n x += contribution;\n\n breakdown.push({\n coefficient: coeff,\n contribution: Math.round(contribution * 10000) / 10000,\n key,\n name: BIOMARKER_NAMES_PT[key] ?? key,\n value,\n valueWithUnit: `${value.toFixed(1)} ${BIOMARKER_UNITS[key] ?? ''}`.trim(),\n });\n }\n\n // Add intercept to breakdown\n breakdown.push({\n coefficient: selectedModel.intercept,\n contribution: selectedModel.intercept,\n key: 'intercept',\n name: BIOMARKER_NAMES_PT['intercept'] ?? 'Intercepto',\n value: 1,\n valueWithUnit: '-',\n });\n\n // Logistic regression → probability at ~7.4 years\n const p = 1 / (1 + Math.exp(-x));\n\n // Extrapolate to 10-year risk\n // risk10y = 1 - (1 - p)^(10/followUp)\n const risk10y = 1 - Math.pow(1 - p, 10 / FOLLOW_UP_YEARS);\n\n const riskPercent = Math.round(risk10y * 1000) / 10; // One decimal place\n\n return {\n breakdown,\n calculatedAt: new Date().toISOString(),\n modelUsed: selectedModel,\n risk10y: Math.round(risk10y * 10000) / 10000,\n riskCategory: classifyRisk(riskPercent),\n riskPercent,\n };\n};\n\n/**\n * Validate biomarker values are within plausible ranges\n */\nexport const validateBiomarkers = (\n input: BrDMriscInput,\n): { isValid: boolean; errors: string[] } => {\n const errors: string[] = [];\n\n const checkRange = (value: number | undefined, key: string, name: string) => {\n if (value === undefined) return;\n const range = BIOMARKER_RANGES[key];\n if (!range) return;\n if (value < range.min || value > range.max) {\n errors.push(\n `${name} (${value}) fora do intervalo esperado [${range.min}-${range.max} ${range.unit}]`,\n );\n }\n };\n\n checkRange(input.fpg, 'fpg', 'Glicemia de Jejum');\n checkRange(input.hba1c, 'hba1c', 'HbA1c');\n checkRange(input.triglycerides, 'triglycerides', 'Triglicerídeos');\n checkRange(input.hdlc, 'hdlc', 'HDL-colesterol');\n\n return {\n errors,\n isValid: errors.length === 0,\n };\n};\n","/**\n * Unit Converters for BrDMrisc Calculator\n *\n * BrDMrisc expects: FPG (mg/dL), HbA1c (%), triglycerides (mg/dL), HDL-c (mg/dL)\n * Brazilian labs typically report in these units already, but some may\n * use mmol/L for glucose or mmol/mol for HbA1c (IFCC standard).\n */\n\n/**\n * Conversion factors for each biomarker\n * Key: source unit, Value: multiplier to get target unit\n */\nexport const CONVERSION_FACTORS: Record<string, Record<string, number>> = {\n fpg: {\n 'mg/dL': 1,\n 'mmol/L': 18.0182, // mmol/L → mg/dL\n },\n hba1c: {\n '%': 1,\n // IFCC mmol/mol → NGSP %: HbA1c(%) = 0.0915 × HbA1c(mmol/mol) + 2.15\n // Handled as special case in convertToTargetUnit\n },\n hdlc: {\n 'mg/dL': 1,\n 'mmol/L': 38.67, // mmol/L → mg/dL\n },\n triglycerides: {\n 'mg/dL': 1,\n 'mmol/L': 88.57, // mmol/L → mg/dL\n },\n};\n\n/**\n * Target units for BrDMrisc calculation\n */\nexport const TARGET_UNITS: Record<string, string> = {\n fpg: 'mg/dL',\n hba1c: '%',\n hdlc: 'mg/dL',\n triglycerides: 'mg/dL',\n};\n\n/**\n * Convert a biomarker value to the target unit expected by BrDMrisc\n */\nexport const convertToTargetUnit = (\n biomarker: string,\n value: number,\n sourceUnit: string,\n): number => {\n const normalizedUnit = sourceUnit.trim();\n\n // Special case: HbA1c IFCC (mmol/mol) → NGSP (%)\n if (biomarker === 'hba1c' && normalizedUnit.includes('mmol/mol')) {\n return 0.0915 * value + 2.15;\n }\n\n const factors = CONVERSION_FACTORS[biomarker];\n if (!factors) return value;\n\n // Try exact match first\n const factor = factors[normalizedUnit];\n if (factor !== undefined) return value * factor;\n\n // Case-insensitive match\n const match = Object.keys(factors).find((k) => k.toLowerCase() === normalizedUnit.toLowerCase());\n if (match) return value * factors[match]!;\n\n return value;\n};\n\n/**\n * Auto-detect unit and convert to target if needed.\n * Uses heuristics based on typical value ranges.\n */\nexport const autoConvertToTarget = (\n biomarker: string,\n value: number,\n unit?: string,\n): { value: number; unit: string; wasConverted: boolean } => {\n const targetUnit = TARGET_UNITS[biomarker] || '';\n\n if (unit) {\n const normalizedUnit = unit.trim().toLowerCase();\n const normalizedTarget = targetUnit.toLowerCase();\n\n if (normalizedUnit === normalizedTarget) {\n return { unit: targetUnit, value, wasConverted: false };\n }\n\n const converted = convertToTargetUnit(biomarker, value, unit);\n return {\n unit: targetUnit,\n value: converted,\n wasConverted: normalizedUnit !== normalizedTarget,\n };\n }\n\n // Heuristic-based detection\n switch (biomarker) {\n case 'fpg':\n // mmol/L typically 3-20, mg/dL typically 60-500\n if (value < 30) {\n return { unit: 'mg/dL', value: value * 18.0182, wasConverted: true };\n }\n return { unit: 'mg/dL', value, wasConverted: false };\n\n case 'hba1c':\n // IFCC mmol/mol typically 20-120, NGSP % typically 4-15\n if (value > 20) {\n return { unit: '%', value: 0.0915 * value + 2.15, wasConverted: true };\n }\n return { unit: '%', value, wasConverted: false };\n\n case 'triglycerides':\n // mmol/L typically 0.5-10, mg/dL typically 50-2000\n if (value < 15) {\n return { unit: 'mg/dL', value: value * 88.57, wasConverted: true };\n }\n return { unit: 'mg/dL', value, wasConverted: false };\n\n case 'hdlc':\n // mmol/L typically 0.5-3, mg/dL typically 20-100\n if (value < 5) {\n return { unit: 'mg/dL', value: value * 38.67, wasConverted: true };\n }\n return { unit: 'mg/dL', value, wasConverted: false };\n\n default:\n return { unit: targetUnit, value, wasConverted: false };\n }\n};\n","/**\n * Derived Biomarker Calculator\n *\n * Derives biomarkers from existing extracted values when the lab report\n * doesn't include them directly.\n *\n * Currently supports:\n * - HOMA-IR = (Fasting Glucose × Fasting Insulin) / 405\n * - VLDL = Triglycerides / 5\n * - BMI = weight_kg / (height_m)² (requires UserContext with height)\n */\n\nimport { codeToLoinc } from '@precisa-saude/fhir';\n\nexport interface BiomarkerInput {\n code: string;\n value: number | string;\n unit?: string;\n}\n\nexport interface DerivedBiomarker {\n code: string;\n loincCode?: string;\n name: string;\n unit: string;\n value: number;\n}\n\nexport interface DerivedOptions {\n codeToLoinc?: (code: string) => string | undefined;\n userContext?: { heightCm?: number };\n}\n\ninterface CalculationDef {\n calculate: (values: Map<string, number>) => number;\n code: string;\n inputs: string[];\n unit: string;\n}\n\ninterface ContextCalculationDef {\n calculate: (values: Map<string, number>, ctx: { heightCm?: number }) => number;\n canCalculate: (ctx: { heightCm?: number }) => boolean;\n code: string;\n inputs: string[];\n unit: string;\n}\n\nconst CALCULATIONS: CalculationDef[] = [\n {\n calculate: (v) => (v.get('Glucose')! * v.get('Insulin')!) / 405,\n code: 'HOMA_IR',\n inputs: ['Glucose', 'Insulin'],\n unit: 'index',\n },\n {\n calculate: (v) => v.get('Triglycerides')! / 5,\n code: 'VLDL',\n inputs: ['Triglycerides'],\n unit: 'mg/dL',\n },\n];\n\nconst CONTEXT_CALCULATIONS: ContextCalculationDef[] = [\n {\n calculate: (v, ctx) => {\n const weightKg = v.get('TotalMass')!;\n const heightM = ctx.heightCm! / 100;\n return weightKg / (heightM * heightM);\n },\n canCalculate: (ctx) =>\n typeof ctx.heightCm === 'number' && ctx.heightCm >= 50 && ctx.heightCm <= 250,\n code: 'BMI',\n inputs: ['TotalMass'],\n unit: 'kg/m2',\n },\n];\n\n/**\n * Compute derived biomarkers from existing extracted values.\n * Only adds a calculated biomarker if:\n * - All required inputs are present with numeric values\n * - The biomarker isn't already present in the results\n */\nexport function computeDerivedBiomarkers(\n biomarkers: BiomarkerInput[],\n options?: DerivedOptions,\n): DerivedBiomarker[] {\n const lookupLoinc = options?.codeToLoinc ?? codeToLoinc;\n const byCode = new Map<string, BiomarkerInput>();\n for (const b of biomarkers) {\n byCode.set(b.code, b);\n }\n\n const added: DerivedBiomarker[] = [];\n\n for (const calc of CALCULATIONS) {\n if (byCode.has(calc.code)) continue;\n\n const values = new Map<string, number>();\n let allPresent = true;\n\n for (const input of calc.inputs) {\n const b = byCode.get(input);\n if (!b || typeof b.value !== 'number') {\n allPresent = false;\n break;\n }\n values.set(input, b.value);\n }\n\n if (!allPresent) continue;\n\n const rawValue = calc.calculate(values);\n const value = parseFloat(rawValue.toPrecision(10));\n const loinc = lookupLoinc(calc.code);\n\n added.push({\n code: calc.code,\n loincCode: loinc ?? undefined,\n name: calc.code,\n unit: calc.unit,\n value,\n });\n }\n\n if (options?.userContext) {\n for (const calc of CONTEXT_CALCULATIONS) {\n if (byCode.has(calc.code)) continue;\n if (!calc.canCalculate(options.userContext)) continue;\n\n const values = new Map<string, number>();\n let allPresent = true;\n\n for (const input of calc.inputs) {\n const b = byCode.get(input);\n if (!b || typeof b.value !== 'number') {\n allPresent = false;\n break;\n }\n values.set(input, b.value);\n }\n\n if (!allPresent) continue;\n\n const rawValue = calc.calculate(values, options.userContext);\n const value = parseFloat(rawValue.toPrecision(10));\n const loinc = lookupLoinc(calc.code);\n\n added.push({\n code: calc.code,\n loincCode: loinc ?? undefined,\n name: calc.code,\n unit: calc.unit,\n value,\n });\n }\n }\n\n return added;\n}\n"],"mappings":"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","names":["BIOMARKER_NAMES_PT","BIOMARKER_RANGES","CONVERSION_FACTORS","TARGET_UNITS","validateBiomarkers","BIOMARKER_NAMES_PT","BIOMARKER_RANGES","BIOMARKER_NAMES_PT","validateBiomarkers","BIOMARKER_RANGES","CONVERSION_FACTORS","TARGET_UNITS"]}
|
|
1
|
+
{"version":3,"sources":["../src/phenoage/index.ts","../src/phenoage/constants.ts","../src/phenoage/calculator.ts","../src/phenoage/unit-converters.ts","../src/brdmrisc/index.ts","../src/brdmrisc/constants.ts","../src/brdmrisc/calculator.ts","../src/brdmrisc/unit-converters.ts","../src/derived/calculator.ts"],"sourcesContent":["export * from './calculator';\nexport * from './constants';\nexport * from './types';\nexport * from './unit-converters';\n","/**\n * PhenoAge Calculator Constants\n *\n * Coefficients from Levine et al. (2018) Table 1\n * Units: albumin (g/L), creatinine (μmol/L), glucose (mmol/L),\n * CRP (ln of mg/L), lymphocyte (%), MCV (fL), RDW (%),\n * ALP (U/L), WBC (10^9/L), age (years)\n */\n\n/**\n * Regression coefficients from the Cox proportional hazards model\n */\nexport const PHENOAGE_COEFFICIENTS = {\n age: 0.0804,\n albumin: -0.0336,\n alkalinePhosphatase: 0.0019,\n creatinine: 0.0095,\n glucose: 0.1953,\n intercept: -19.9067,\n logCrp: 0.0954,\n lymphocytePercent: -0.012,\n mcv: 0.0268,\n rdw: 0.3306,\n wbc: 0.0554,\n} as const;\n\n/**\n * Gompertz mortality model parameters\n */\nexport const GOMPERTZ_PARAMS = {\n ageCoefficient: 0.090165,\n baseAge: 141.50225,\n gamma: 0.0076927,\n mortalityConstant: 0.00553,\n} as const;\n\n/**\n * Biomarker reference ranges for validation\n */\nexport const BIOMARKER_RANGES = {\n albumin: { max: 60, min: 10, typical: { max: 50, min: 35 }, unit: 'g/L' },\n alkalinePhosphatase: { max: 500, min: 10, typical: { max: 130, min: 40 }, unit: 'U/L' },\n chronologicalAge: { max: 120, min: 18, typical: { max: 100, min: 18 }, unit: 'anos' },\n creatinine: { max: 500, min: 20, typical: { max: 110, min: 60 }, unit: 'μmol/L' },\n crp: { max: 200, min: 0.01, typical: { max: 3, min: 0 }, unit: 'mg/L' },\n glucose: { max: 30, min: 2, typical: { max: 6.0, min: 4.0 }, unit: 'mmol/L' },\n lymphocytePercent: { max: 80, min: 1, typical: { max: 40, min: 20 }, unit: '%' },\n mcv: { max: 150, min: 50, typical: { max: 100, min: 80 }, unit: 'fL' },\n rdw: { max: 25, min: 8, typical: { max: 15, min: 11 }, unit: '%' },\n wbc: { max: 30, min: 1, typical: { max: 11.0, min: 4.0 }, unit: '10^9/L' },\n} as const;\n\n/**\n * Mapping from FHIR biomarker codes to PhenoAge input fields\n */\nexport const FHIR_CODE_TO_PHENOAGE: Record<string, keyof typeof BIOMARKER_RANGES> = {\n Albumin: 'albumin',\n AlkalinePhosphatase: 'alkalinePhosphatase',\n Creatinine: 'creatinine',\n CRP: 'crp',\n Glucose: 'glucose',\n Lymphocytes: 'lymphocytePercent',\n MCV: 'mcv',\n RDW: 'rdw',\n WBC: 'wbc',\n};\n\n/**\n * Required biomarker codes for PhenoAge calculation\n */\nexport const REQUIRED_BIOMARKERS = [\n 'Albumin',\n 'Creatinine',\n 'Glucose',\n 'CRP',\n 'Lymphocytes',\n 'MCV',\n 'RDW',\n 'AlkalinePhosphatase',\n 'WBC',\n] as const;\n\n/**\n * Portuguese names for biomarkers (for display)\n */\nexport const BIOMARKER_NAMES_PT: Record<string, string> = {\n age: 'Idade',\n albumin: 'Albumina',\n alkalinePhosphatase: 'Fosfatase Alcalina',\n creatinine: 'Creatinina',\n crp: 'PCR',\n glucose: 'Glicose',\n intercept: 'Intercepto',\n lymphocytePercent: 'Linfócitos',\n mcv: 'VCM',\n rdw: 'RDW',\n wbc: 'Leucócitos',\n};\n\n/**\n * Short descriptions for biomarkers (for tooltips)\n * Explains what each biomarker indicates in the context of aging\n */\nexport const BIOMARKER_DESCRIPTIONS_PT: Record<string, string> = {\n age: 'Sua idade cronológica em anos',\n albumin: 'Proteína do fígado que indica estado nutricional e função hepática',\n alkalinePhosphatase: 'Enzima que reflete saúde do fígado e dos ossos',\n creatinine: 'Marcador de função renal produzido pelos músculos',\n crp: 'Proteína C-reativa, indica inflamação no corpo',\n glucose: 'Nível de açúcar no sangue, relacionado ao metabolismo',\n intercept: 'Constante da fórmula de regressão',\n lymphocytePercent: 'Células de defesa do sistema imunológico',\n mcv: 'Volume médio das hemácias, indica saúde das células vermelhas',\n rdw: 'Variação no tamanho das hemácias, marcador de inflamação',\n wbc: 'Total de células brancas, indica estado imunológico',\n};\n\n/**\n * Biomarker information for lab order requests\n * Includes LOINC codes and English names for lab requisitions\n */\nexport interface BiomarkerLabInfo {\n loincCode: string;\n nameEn: string;\n namePt: string;\n}\n\n/**\n * Lab order information for PhenoAge biomarkers\n * Mapped by FHIR code\n */\nexport const BIOMARKER_LAB_INFO: Record<string, BiomarkerLabInfo> = {\n Albumin: {\n loincCode: '1751-7',\n nameEn: 'Albumin [Mass/volume] in Serum or Plasma',\n namePt: 'Albumina',\n },\n AlkalinePhosphatase: {\n loincCode: '6768-6',\n nameEn: 'Alkaline phosphatase [Enzymatic activity/volume] in Serum or Plasma',\n namePt: 'Fosfatase Alcalina',\n },\n Creatinine: {\n loincCode: '2160-0',\n nameEn: 'Creatinine [Mass/volume] in Serum or Plasma',\n namePt: 'Creatinina',\n },\n CRP: {\n loincCode: '1988-5',\n nameEn: 'C reactive protein [Mass/volume] in Serum or Plasma',\n namePt: 'Proteína C-Reativa (PCR)',\n },\n Glucose: {\n loincCode: '2345-7',\n nameEn: 'Glucose [Mass/volume] in Serum or Plasma',\n namePt: 'Glicose',\n },\n Lymphocytes: {\n loincCode: '736-9',\n nameEn: 'Lymphocytes/100 leukocytes in Blood by Automated count',\n namePt: 'Linfócitos (%)',\n },\n MCV: {\n loincCode: '787-2',\n nameEn: 'MCV [Entitic volume] by Automated count',\n namePt: 'Volume Corpuscular Médio (VCM)',\n },\n RDW: {\n loincCode: '788-0',\n nameEn: 'Erythrocyte distribution width [Ratio] by Automated count',\n namePt: 'Amplitude de Distribuição dos Eritrócitos (RDW)',\n },\n WBC: {\n loincCode: '6690-2',\n nameEn: 'Leukocytes [#/volume] in Blood by Automated count',\n namePt: 'Contagem de Leucócitos',\n },\n};\n","/**\n * PhenoAge Calculator\n *\n * Implements the Levine PhenoAge algorithm from:\n * \"An epigenetic biomarker of aging for lifespan and healthspan\" (Aging, 2018)\n */\n\nimport {\n BIOMARKER_NAMES_PT,\n BIOMARKER_RANGES,\n GOMPERTZ_PARAMS,\n PHENOAGE_COEFFICIENTS,\n} from './constants';\nimport type { ComponentBreakdown, PhenoAgeInput, PhenoAgeResult } from './types';\n\n/**\n * Calculates the linear predictor (xb) from biomarkers\n * @returns Object with xb value and breakdown of each component\n */\nconst calculateLinearPredictor = (\n input: PhenoAgeInput,\n): { xb: number; breakdown: ComponentBreakdown[] } => {\n const logCrp = Math.log(Math.max(input.crp, 0.1)); // Avoid log(0)\n\n const components: Array<{\n key: string;\n value: number;\n coefficient: number;\n unit: string;\n displayValue?: string;\n }> = [\n {\n coefficient: PHENOAGE_COEFFICIENTS.albumin,\n key: 'albumin',\n unit: BIOMARKER_RANGES.albumin.unit,\n value: input.albumin,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.creatinine,\n key: 'creatinine',\n unit: BIOMARKER_RANGES.creatinine.unit,\n value: input.creatinine,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.glucose,\n key: 'glucose',\n unit: BIOMARKER_RANGES.glucose.unit,\n value: input.glucose,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.logCrp,\n displayValue: `ln(${input.crp.toFixed(2)})`,\n key: 'crp',\n unit: 'ln(mg/L)',\n value: logCrp,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.lymphocytePercent,\n key: 'lymphocytePercent',\n unit: BIOMARKER_RANGES.lymphocytePercent.unit,\n value: input.lymphocytePercent,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.mcv,\n key: 'mcv',\n unit: BIOMARKER_RANGES.mcv.unit,\n value: input.mcv,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.rdw,\n key: 'rdw',\n unit: BIOMARKER_RANGES.rdw.unit,\n value: input.rdw,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.alkalinePhosphatase,\n key: 'alkalinePhosphatase',\n unit: BIOMARKER_RANGES.alkalinePhosphatase.unit,\n value: input.alkalinePhosphatase,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.wbc,\n key: 'wbc',\n unit: BIOMARKER_RANGES.wbc.unit,\n value: input.wbc,\n },\n {\n coefficient: PHENOAGE_COEFFICIENTS.age,\n displayValue: `${Math.round(input.chronologicalAge)} anos`,\n key: 'age',\n unit: 'anos',\n value: input.chronologicalAge,\n },\n ];\n\n const breakdown: ComponentBreakdown[] = components.map((c) => ({\n coefficient: c.coefficient,\n contribution: Math.round(c.coefficient * c.value * 10000) / 10000,\n key: c.key,\n name: BIOMARKER_NAMES_PT[c.key] ?? c.key,\n valueWithUnit: c.displayValue ? `${c.displayValue}` : `${c.value.toFixed(2)} ${c.unit}`,\n }));\n\n // Add intercept\n breakdown.push({\n coefficient: PHENOAGE_COEFFICIENTS.intercept,\n contribution: PHENOAGE_COEFFICIENTS.intercept,\n key: 'intercept',\n name: BIOMARKER_NAMES_PT['intercept'] ?? 'Intercepto',\n valueWithUnit: '-',\n });\n\n const xb =\n PHENOAGE_COEFFICIENTS.intercept +\n components.reduce((sum, c) => sum + c.coefficient * c.value, 0);\n\n return { breakdown, xb };\n};\n\n/**\n * Calculates mortality score from linear predictor using Gompertz model\n */\nconst calculateMortalityScore = (xb: number): number => {\n const { gamma } = GOMPERTZ_PARAMS;\n\n // exp(xb) represents the hazard ratio\n const hazardRatio = Math.exp(xb);\n\n // Cumulative hazard over 120 months (10 years)\n const cumulativeHazard = (hazardRatio * (Math.exp(120 * gamma) - 1)) / gamma;\n\n // Convert to probability\n return 1 - Math.exp(-cumulativeHazard);\n};\n\n/**\n * Converts mortality score to PhenoAge\n */\nconst mortalityScoreToPhenoAge = (mortalityScore: number): number => {\n const { ageCoefficient, baseAge, mortalityConstant } = GOMPERTZ_PARAMS;\n\n // Inverse transformation to get biological age\n const innerLog = Math.log(1 - mortalityScore);\n const outerLog = Math.log(-mortalityConstant * innerLog);\n\n return baseAge + outerLog / ageCoefficient;\n};\n\n/**\n * Main PhenoAge calculation function\n *\n * @param input - Biomarker values in SI units plus chronological age\n * @returns Complete PhenoAge result with breakdown\n */\nexport const calculatePhenoAge = (input: PhenoAgeInput): PhenoAgeResult => {\n // Validate all inputs are valid numbers (not NaN)\n const inputValues = {\n albumin: input.albumin,\n alkalinePhosphatase: input.alkalinePhosphatase,\n chronologicalAge: input.chronologicalAge,\n creatinine: input.creatinine,\n crp: input.crp,\n glucose: input.glucose,\n lymphocytePercent: input.lymphocytePercent,\n mcv: input.mcv,\n rdw: input.rdw,\n wbc: input.wbc,\n };\n\n for (const [key, value] of Object.entries(inputValues)) {\n if (typeof value !== 'number' || Number.isNaN(value)) {\n throw new Error(`Invalid input value for ${key}: ${value}`);\n }\n }\n\n // Calculate linear predictor with breakdown\n const { breakdown, xb } = calculateLinearPredictor(input);\n\n // Calculate mortality score\n const mortalityScore = calculateMortalityScore(xb);\n\n // Convert to PhenoAge\n const phenoAge = mortalityScoreToPhenoAge(mortalityScore);\n\n // Calculate age difference\n const ageDifference = phenoAge - input.chronologicalAge;\n\n return {\n ageDifference: Math.round(ageDifference * 10) / 10,\n breakdown,\n calculatedAt: new Date().toISOString(),\n chronologicalAge: input.chronologicalAge,\n linearPredictor: Math.round(xb * 10000) / 10000,\n mortalityScore: Math.round(mortalityScore * 10000) / 10000,\n phenoAge: Math.round(phenoAge * 10) / 10,\n };\n};\n\n/**\n * Validates biomarker values are within acceptable ranges\n */\nexport const validateBiomarkers = (\n input: PhenoAgeInput,\n): { isValid: boolean; errors: string[] } => {\n const errors: string[] = [];\n\n const checkRange = (value: number, key: keyof typeof BIOMARKER_RANGES, name: string) => {\n const range = BIOMARKER_RANGES[key];\n if (value < range.min || value > range.max) {\n errors.push(`${name} (${value}) fora do intervalo esperado [${range.min}-${range.max}]`);\n }\n };\n\n checkRange(input.albumin, 'albumin', 'Albumina');\n checkRange(input.creatinine, 'creatinine', 'Creatinina');\n checkRange(input.glucose, 'glucose', 'Glicose');\n checkRange(input.crp, 'crp', 'PCR');\n checkRange(input.lymphocytePercent, 'lymphocytePercent', 'Linfócitos');\n checkRange(input.mcv, 'mcv', 'VCM');\n checkRange(input.rdw, 'rdw', 'RDW');\n checkRange(input.alkalinePhosphatase, 'alkalinePhosphatase', 'Fosfatase Alcalina');\n checkRange(input.wbc, 'wbc', 'Leucócitos');\n checkRange(input.chronologicalAge, 'chronologicalAge', 'Idade');\n\n // Special validation for CRP (must be positive for log transform)\n if (input.crp <= 0) {\n errors.push('PCR deve ser maior que 0');\n }\n\n return {\n errors,\n isValid: errors.length === 0,\n };\n};\n","/**\n * Unit Converters for PhenoAge Calculator\n *\n * Brazilian labs (Weinmann, Fleury, etc.) often report in different units\n * than the SI units required by the PhenoAge algorithm.\n */\n\n/**\n * Conversion factors for each biomarker\n * Key: source unit, Value: multiplier to get SI unit\n */\nexport const CONVERSION_FACTORS: Record<string, Record<string, number>> = {\n albumin: {\n 'g/dL': 10, // g/dL → g/L\n 'g/L': 1,\n },\n alkalinePhosphatase: {\n 'U/L': 1,\n },\n creatinine: {\n 'mg/dL': 88.4, // mg/dL → μmol/L\n 'umol/L': 1,\n 'μmol/L': 1,\n },\n crp: {\n 'mg/dL': 10, // mg/dL → mg/L\n 'mg/L': 1,\n },\n glucose: {\n 'mg/dL': 1 / 18.0182, // mg/dL → mmol/L\n 'mmol/L': 1,\n },\n lymphocytePercent: {\n '%': 1,\n },\n mcv: {\n fL: 1,\n },\n rdw: {\n '%': 1,\n },\n wbc: {\n '/uL': 0.001,\n '/µL': 0.001, // cells/μL → 10^9/L\n '10^9/L': 1,\n '1000/μL': 1,\n '10³/µL': 1, // Same as 10^9/L\n 'K/uL': 1,\n 'K/µL': 1,\n 'Thousand/uL': 1, // Brazilian labs often use this format\n 'thousand/uL': 1,\n 'Thousand/µL': 1,\n 'thousand/µL': 1,\n },\n};\n\n/**\n * Target units for PhenoAge calculation (SI units)\n */\nexport const TARGET_UNITS: Record<string, string> = {\n albumin: 'g/L',\n alkalinePhosphatase: 'U/L',\n creatinine: 'μmol/L',\n crp: 'mg/L',\n glucose: 'mmol/L',\n lymphocytePercent: '%',\n mcv: 'fL',\n rdw: '%',\n wbc: '10^9/L',\n};\n\n/**\n * Convert a biomarker value from source unit to SI unit\n *\n * @param biomarker - The biomarker key (e.g., 'albumin', 'glucose')\n * @param value - The numeric value\n * @param sourceUnit - The unit of the input value\n * @returns Converted value in SI units\n * @throws Error if the unit is not recognized\n */\nexport const convertToSI = (biomarker: string, value: number, sourceUnit: string): number => {\n const factors = CONVERSION_FACTORS[biomarker];\n\n if (!factors) {\n throw new Error(`Unknown biomarker: ${biomarker}`);\n }\n\n // Normalize unit string for matching\n const normalizedUnit = sourceUnit.trim();\n const factor = factors[normalizedUnit];\n\n if (factor === undefined) {\n const normalizedLower = normalizedUnit.toLowerCase();\n\n // First try exact case-insensitive match\n const exactMatch = Object.keys(factors).find((key) => key.toLowerCase() === normalizedLower);\n if (exactMatch) {\n return value * factors[exactMatch]!;\n }\n\n // For partial matches, sort keys by length (longest first) to prefer more specific matches\n // This prevents \"/µL\" from matching before \"10³/µL\"\n const sortedKeys = Object.keys(factors).sort((a, b) => b.length - a.length);\n const partialMatch = sortedKeys.find((key) => normalizedLower.includes(key.toLowerCase()));\n\n if (partialMatch) {\n return value * factors[partialMatch]!;\n }\n\n throw new Error(`Unknown unit \"${sourceUnit}\" for ${biomarker}`);\n }\n\n return value * factor;\n};\n\n/**\n * Check if a value needs conversion\n *\n * @param biomarker - The biomarker key\n * @param sourceUnit - The current unit\n * @returns true if conversion is needed\n */\nexport const needsConversion = (biomarker: string, sourceUnit: string): boolean => {\n const targetUnit = TARGET_UNITS[biomarker];\n if (!targetUnit) return false;\n\n const normalizedSource = sourceUnit.trim().toLowerCase();\n const normalizedTarget = targetUnit.toLowerCase();\n\n return normalizedSource !== normalizedTarget;\n};\n\n/**\n * Auto-detect unit and convert to SI if needed\n * Uses heuristics based on typical value ranges\n *\n * @param biomarker - The biomarker key\n * @param value - The numeric value\n * @param unit - Optional unit hint\n * @returns Object with converted value and detected unit\n */\nexport const autoConvertToSI = (\n biomarker: string,\n value: number,\n unit?: string,\n): { value: number; unit: string; wasConverted: boolean } => {\n // If unit is provided and we know how to convert it\n if (unit) {\n try {\n const converted = convertToSI(biomarker, value, unit);\n const wasConverted = needsConversion(biomarker, unit);\n return {\n unit: TARGET_UNITS[biomarker] || unit,\n value: converted,\n wasConverted,\n };\n } catch {\n // Fall through to heuristic detection\n }\n }\n\n // Heuristic-based detection for common cases\n switch (biomarker) {\n case 'albumin':\n // g/dL typically 3-5, g/L typically 30-50\n if (value < 10) {\n return { unit: 'g/L', value: value * 10, wasConverted: true };\n }\n return { unit: 'g/L', value, wasConverted: false };\n\n case 'creatinine':\n // mg/dL typically 0.5-1.5, μmol/L typically 45-130\n if (value < 15) {\n return { unit: 'μmol/L', value: value * 88.4, wasConverted: true };\n }\n return { unit: 'μmol/L', value, wasConverted: false };\n\n case 'glucose':\n // mg/dL typically 70-140, mmol/L typically 4-8\n if (value > 20) {\n return { unit: 'mmol/L', value: value / 18.0182, wasConverted: true };\n }\n return { unit: 'mmol/L', value, wasConverted: false };\n\n case 'wbc':\n // cells/μL typically 4000-11000, 10^9/L typically 4-11\n if (value > 100) {\n return { unit: '10^9/L', value: value / 1000, wasConverted: true };\n }\n return { unit: '10^9/L', value, wasConverted: false };\n\n default:\n return { unit: TARGET_UNITS[biomarker] || '', value, wasConverted: false };\n }\n};\n","export * from './calculator';\nexport * from './constants';\nexport * from './types';\nexport * from './unit-converters';\n","/**\n * BrDMrisc Calculator Constants\n *\n * All 14 model definitions with coefficients extracted from:\n * Bracco et al. (2023) Table 2 & Supplementary Material\n * DOI: 10.3389/fendo.2023.1166147\n *\n * Models 1-6 are lab-only (MVP scope).\n * Models 7-14 include clinical variables (Phase 2).\n *\n * Follow-up period: 7.4 years (ELSA-Brasil median)\n * Units expected: FPG (mg/dL), HbA1c (%), triglycerides (mg/dL), HDL-c (mg/dL)\n */\n\nimport type { BrDMriscInput, BrDMriscModelDefinition } from './types';\n\n/**\n * ELSA-Brasil median follow-up in years.\n * Used to extrapolate the logistic regression probability to 10-year risk.\n */\nexport const FOLLOW_UP_YEARS = 7.4;\n\n/**\n * All 14 BrDMrisc models.\n * Models ordered by ID. Lab-only models (1-6) are the MVP.\n *\n * Coefficients from Supplementary Table S2 of the paper,\n * verified against the Shiny app demo at:\n * https://paulabracco.shinyapps.io/BrDMrisc_pt/\n */\nexport const BRDMRISC_MODELS: BrDMriscModelDefinition[] = [\n // === Lab-only models (MVP) ===\n {\n auc: 0.776,\n coefficients: {\n fpg: 0.0352,\n },\n id: 1,\n intercept: -5.8282,\n isLabOnly: true,\n name: 'FPG only',\n namePt: 'Apenas Glicemia de Jejum',\n requiredBiomarkers: ['fpg'],\n },\n {\n auc: 0.668,\n coefficients: {\n hba1c: 0.731,\n },\n id: 2,\n intercept: -6.3199,\n isLabOnly: true,\n name: 'HbA1c only',\n namePt: 'Apenas HbA1c',\n requiredBiomarkers: ['hba1c'],\n },\n {\n auc: 0.793,\n coefficients: {\n fpg: 0.029,\n hba1c: 0.4427,\n },\n id: 3,\n intercept: -7.37,\n isLabOnly: true,\n name: 'FPG + HbA1c',\n namePt: 'Glicemia + HbA1c',\n requiredBiomarkers: ['fpg', 'hba1c'],\n },\n {\n auc: 0.79,\n coefficients: {\n fpg: 0.0345,\n triglycerides: 0.0017,\n },\n id: 4,\n intercept: -5.9706,\n isLabOnly: true,\n name: 'FPG + Triglycerides',\n namePt: 'Glicemia + Triglicerídeos',\n requiredBiomarkers: ['fpg', 'triglycerides'],\n },\n {\n auc: 0.796,\n coefficients: {\n fpg: 0.0338,\n hdlc: -0.0161,\n triglycerides: 0.0011,\n },\n id: 5,\n intercept: -5.3879,\n isLabOnly: true,\n name: 'FPG + Lipids',\n namePt: 'Glicemia + Lipídios',\n requiredBiomarkers: ['fpg', 'triglycerides', 'hdlc'],\n },\n {\n auc: 0.813,\n coefficients: {\n fpg: 0.0267,\n hba1c: 0.3943,\n hdlc: -0.0149,\n triglycerides: 0.0009,\n },\n id: 6,\n intercept: -6.9195,\n isLabOnly: true,\n name: 'FPG + HbA1c + Lipids',\n namePt: 'Glicemia + HbA1c + Lipídios',\n requiredBiomarkers: ['fpg', 'hba1c', 'triglycerides', 'hdlc'],\n },\n\n // === Clinical models (Phase 2 — require user input) ===\n {\n auc: 0.744,\n coefficients: {\n bmi: 0.0642,\n familyHistory: 0.5915,\n waist: 0.0111,\n },\n id: 7,\n intercept: -6.5023,\n isLabOnly: false,\n name: 'Clinical only',\n namePt: 'Apenas Clínico',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory'],\n },\n {\n auc: 0.8,\n coefficients: {\n bmi: 0.0454,\n familyHistory: 0.475,\n fpg: 0.0305,\n waist: 0.0047,\n },\n id: 8,\n intercept: -7.3297,\n isLabOnly: false,\n name: 'Clinical + FPG',\n namePt: 'Clínico + Glicemia',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg'],\n },\n {\n auc: 0.725,\n coefficients: {\n bmi: 0.0471,\n familyHistory: 0.5284,\n hba1c: 0.6001,\n waist: 0.0063,\n },\n id: 9,\n intercept: -8.0917,\n isLabOnly: false,\n name: 'Clinical + HbA1c',\n namePt: 'Clínico + HbA1c',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'hba1c'],\n },\n {\n auc: 0.814,\n coefficients: {\n bmi: 0.0373,\n familyHistory: 0.4393,\n fpg: 0.0253,\n hba1c: 0.3688,\n waist: 0.0029,\n },\n id: 10,\n intercept: -8.5817,\n isLabOnly: false,\n name: 'Clinical + FPG + HbA1c',\n namePt: 'Clínico + Glicemia + HbA1c',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg', 'hba1c'],\n },\n {\n auc: 0.808,\n coefficients: {\n bmi: 0.0427,\n familyHistory: 0.4532,\n fpg: 0.0299,\n triglycerides: 0.0015,\n waist: 0.0031,\n },\n id: 11,\n intercept: -7.5028,\n isLabOnly: false,\n name: 'Clinical + FPG + Triglycerides',\n namePt: 'Clínico + Glicemia + Triglicerídeos',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg', 'triglycerides'],\n },\n {\n auc: 0.813,\n coefficients: {\n bmi: 0.0408,\n familyHistory: 0.4396,\n fpg: 0.0293,\n hdlc: -0.0128,\n triglycerides: 0.0009,\n waist: 0.0019,\n },\n id: 12,\n intercept: -6.9757,\n isLabOnly: false,\n name: 'Clinical + FPG + Lipids',\n namePt: 'Clínico + Glicemia + Lipídios',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg', 'triglycerides', 'hdlc'],\n },\n {\n auc: 0.822,\n coefficients: {\n bmi: 0.0334,\n familyHistory: 0.4107,\n fpg: 0.0238,\n hba1c: 0.3266,\n hdlc: -0.0117,\n triglycerides: 0.0006,\n waist: 0.0015,\n },\n id: 13,\n intercept: -8.2,\n isLabOnly: false,\n name: 'Clinical + FPG + HbA1c + Lipids',\n namePt: 'Clínico + Glicemia + HbA1c + Lipídios',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'fpg', 'hba1c', 'triglycerides', 'hdlc'],\n },\n {\n auc: 0.699,\n coefficients: {\n bmi: 0.0494,\n ethnicity: 0.2901,\n familyHistory: 0.557,\n hypertension: 0.3653,\n waist: 0.0071,\n },\n id: 14,\n intercept: -7.244,\n isLabOnly: false,\n name: 'Clinical extended',\n namePt: 'Clínico Estendido',\n requiredBiomarkers: ['bmi', 'waist', 'familyHistory', 'ethnicity', 'hypertension'],\n },\n];\n\n/**\n * Lab-only models for MVP, ordered by priority (highest AUC first)\n */\nexport const LAB_ONLY_MODELS = BRDMRISC_MODELS.filter((m) => m.isLabOnly);\n\n/**\n * Model selection priority for lab-only models (highest AUC first)\n */\nexport const LAB_MODEL_PRIORITY = [6, 5, 3, 4, 1, 2] as const;\n\n/**\n * FHIR biomarker code → BrDMrisc input field mapping\n */\nexport const FHIR_CODE_TO_BRDMRISC: Record<string, keyof BrDMriscInput> = {\n Glucose: 'fpg',\n HbA1c: 'hba1c',\n HDL: 'hdlc',\n Triglycerides: 'triglycerides',\n};\n\n/**\n * Required FHIR biomarker codes (all 4 lab biomarkers)\n */\nexport const BRDMRISC_BIOMARKER_CODES = ['Glucose', 'HbA1c', 'Triglycerides', 'HDL'] as const;\n\n/**\n * Portuguese names for biomarkers\n */\nexport const BIOMARKER_NAMES_PT: Record<string, string> = {\n fpg: 'Glicemia de Jejum',\n hba1c: 'Hemoglobina Glicada',\n hdlc: 'HDL-colesterol',\n intercept: 'Intercepto',\n triglycerides: 'Triglicerídeos',\n};\n\n/**\n * Biomarker units used by the model\n */\nexport const BIOMARKER_UNITS: Record<string, string> = {\n fpg: 'mg/dL',\n hba1c: '%',\n hdlc: 'mg/dL',\n triglycerides: 'mg/dL',\n};\n\n/**\n * Biomarker validation ranges (plausible values)\n */\nexport const BIOMARKER_RANGES: Record<string, { min: number; max: number; unit: string }> = {\n fpg: { max: 500, min: 40, unit: 'mg/dL' },\n hba1c: { max: 20, min: 3, unit: '%' },\n hdlc: { max: 150, min: 10, unit: 'mg/dL' },\n triglycerides: { max: 2000, min: 20, unit: 'mg/dL' },\n};\n\n/**\n * Risk category thresholds based on clinical recommendations\n * from Bracco et al. (2023). The paper notes that 20% triggers\n * intensive prevention.\n */\nexport const RISK_THRESHOLDS = {\n high: 0.2, // 20-35% — high risk\n moderate: 0.1, // 10-20% — moderate risk\n veryHigh: 0.35, // >= 35% — very high risk\n} as const;\n","/**\n * BrDMrisc Calculator\n *\n * Implements the BrDMrisc algorithm from:\n * Bracco et al. (2023) - \"BrDMrisc: a Brazilian diabetes risk score\n * for screening of type 2 diabetes mellitus\"\n * Frontiers in Endocrinology, DOI: 10.3389/fendo.2023.1166147\n *\n * Formula:\n * x = intercept + Σ(coeff_i × value_i)\n * p = 1 / (1 + exp(-x)) — logistic regression probability (~7.4 years)\n * risk10y = 1 - (1 - p)^(10/7.4) — extrapolated to 10-year risk\n */\n\nimport {\n BIOMARKER_NAMES_PT,\n BIOMARKER_RANGES,\n BIOMARKER_UNITS,\n BRDMRISC_MODELS,\n FOLLOW_UP_YEARS,\n LAB_MODEL_PRIORITY,\n RISK_THRESHOLDS,\n} from './constants';\nimport type {\n BrDMriscInput,\n BrDMriscModelDefinition,\n BrDMriscResult,\n ComponentBreakdown,\n RiskCategory,\n} from './types';\n\n/**\n * Select the best available model based on which biomarkers are present.\n * Iterates through models in priority order (highest AUC first) and\n * returns the first one whose requirements are fully met.\n */\nexport const selectModel = (\n input: BrDMriscInput,\n labOnly = true,\n): BrDMriscModelDefinition | null => {\n const available = new Set<string>();\n if (input.fpg !== undefined && !Number.isNaN(input.fpg)) available.add('fpg');\n if (input.hba1c !== undefined && !Number.isNaN(input.hba1c)) available.add('hba1c');\n if (input.triglycerides !== undefined && !Number.isNaN(input.triglycerides))\n available.add('triglycerides');\n if (input.hdlc !== undefined && !Number.isNaN(input.hdlc)) available.add('hdlc');\n\n if (available.size === 0) return null;\n\n const priority = labOnly ? LAB_MODEL_PRIORITY : BRDMRISC_MODELS.map((m) => m.id);\n\n for (const modelId of priority) {\n const model = BRDMRISC_MODELS.find((m) => m.id === modelId);\n if (!model) continue;\n if (labOnly && !model.isLabOnly) continue;\n\n const hasAll = model.requiredBiomarkers.every((b) => available.has(b));\n if (hasAll) return model;\n }\n\n return null;\n};\n\n/**\n * Classify risk percentage into a category\n */\nexport const classifyRisk = (riskPercent: number): RiskCategory => {\n const risk = riskPercent / 100;\n if (risk >= RISK_THRESHOLDS.veryHigh) return 'very-high';\n if (risk >= RISK_THRESHOLDS.high) return 'high';\n if (risk >= RISK_THRESHOLDS.moderate) return 'moderate';\n return 'low';\n};\n\n/**\n * Calculate BrDMrisc 10-year diabetes risk\n */\nexport const calculateBrDMrisc = (\n input: BrDMriscInput,\n model?: BrDMriscModelDefinition,\n): BrDMriscResult => {\n const selectedModel = model ?? selectModel(input);\n if (!selectedModel) {\n throw new Error('No suitable model found for the available biomarkers');\n }\n\n // Build breakdown and compute linear predictor\n const breakdown: ComponentBreakdown[] = [];\n let x = selectedModel.intercept;\n\n for (const [key, coeff] of Object.entries(selectedModel.coefficients)) {\n const value = input[key as keyof BrDMriscInput];\n if (value === undefined) {\n throw new Error(`Missing required biomarker: ${key}`);\n }\n\n const contribution = coeff * value;\n x += contribution;\n\n breakdown.push({\n coefficient: coeff,\n contribution: Math.round(contribution * 10000) / 10000,\n key,\n name: BIOMARKER_NAMES_PT[key] ?? key,\n value,\n valueWithUnit: `${value.toFixed(1)} ${BIOMARKER_UNITS[key] ?? ''}`.trim(),\n });\n }\n\n // Add intercept to breakdown\n breakdown.push({\n coefficient: selectedModel.intercept,\n contribution: selectedModel.intercept,\n key: 'intercept',\n name: BIOMARKER_NAMES_PT['intercept'] ?? 'Intercepto',\n value: 1,\n valueWithUnit: '-',\n });\n\n // Logistic regression → probability at ~7.4 years\n const p = 1 / (1 + Math.exp(-x));\n\n // Extrapolate to 10-year risk\n // risk10y = 1 - (1 - p)^(10/followUp)\n const risk10y = 1 - Math.pow(1 - p, 10 / FOLLOW_UP_YEARS);\n\n const riskPercent = Math.round(risk10y * 1000) / 10; // One decimal place\n\n return {\n breakdown,\n calculatedAt: new Date().toISOString(),\n modelUsed: selectedModel,\n risk10y: Math.round(risk10y * 10000) / 10000,\n riskCategory: classifyRisk(riskPercent),\n riskPercent,\n };\n};\n\n/**\n * Validate biomarker values are within plausible ranges\n */\nexport const validateBiomarkers = (\n input: BrDMriscInput,\n): { isValid: boolean; errors: string[] } => {\n const errors: string[] = [];\n\n const checkRange = (value: number | undefined, key: string, name: string) => {\n if (value === undefined) return;\n const range = BIOMARKER_RANGES[key];\n if (!range) return;\n if (value < range.min || value > range.max) {\n errors.push(\n `${name} (${value}) fora do intervalo esperado [${range.min}-${range.max} ${range.unit}]`,\n );\n }\n };\n\n checkRange(input.fpg, 'fpg', 'Glicemia de Jejum');\n checkRange(input.hba1c, 'hba1c', 'HbA1c');\n checkRange(input.triglycerides, 'triglycerides', 'Triglicerídeos');\n checkRange(input.hdlc, 'hdlc', 'HDL-colesterol');\n\n return {\n errors,\n isValid: errors.length === 0,\n };\n};\n","/**\n * Unit Converters for BrDMrisc Calculator\n *\n * BrDMrisc expects: FPG (mg/dL), HbA1c (%), triglycerides (mg/dL), HDL-c (mg/dL)\n * Brazilian labs typically report in these units already, but some may\n * use mmol/L for glucose or mmol/mol for HbA1c (IFCC standard).\n */\n\n/**\n * Conversion factors for each biomarker\n * Key: source unit, Value: multiplier to get target unit\n */\nexport const CONVERSION_FACTORS: Record<string, Record<string, number>> = {\n fpg: {\n 'mg/dL': 1,\n 'mmol/L': 18.0182, // mmol/L → mg/dL\n },\n hba1c: {\n '%': 1,\n // IFCC mmol/mol → NGSP %: HbA1c(%) = 0.0915 × HbA1c(mmol/mol) + 2.15\n // Handled as special case in convertToTargetUnit\n },\n hdlc: {\n 'mg/dL': 1,\n 'mmol/L': 38.67, // mmol/L → mg/dL\n },\n triglycerides: {\n 'mg/dL': 1,\n 'mmol/L': 88.57, // mmol/L → mg/dL\n },\n};\n\n/**\n * Target units for BrDMrisc calculation\n */\nexport const TARGET_UNITS: Record<string, string> = {\n fpg: 'mg/dL',\n hba1c: '%',\n hdlc: 'mg/dL',\n triglycerides: 'mg/dL',\n};\n\n/**\n * Convert a biomarker value to the target unit expected by BrDMrisc\n */\nexport const convertToTargetUnit = (\n biomarker: string,\n value: number,\n sourceUnit: string,\n): number => {\n const normalizedUnit = sourceUnit.trim();\n\n // Special case: HbA1c IFCC (mmol/mol) → NGSP (%)\n if (biomarker === 'hba1c' && normalizedUnit.includes('mmol/mol')) {\n return 0.0915 * value + 2.15;\n }\n\n const factors = CONVERSION_FACTORS[biomarker];\n if (!factors) return value;\n\n // Try exact match first\n const factor = factors[normalizedUnit];\n if (factor !== undefined) return value * factor;\n\n // Case-insensitive match\n const match = Object.keys(factors).find((k) => k.toLowerCase() === normalizedUnit.toLowerCase());\n if (match) return value * factors[match]!;\n\n return value;\n};\n\n/**\n * Auto-detect unit and convert to target if needed.\n * Uses heuristics based on typical value ranges.\n */\nexport const autoConvertToTarget = (\n biomarker: string,\n value: number,\n unit?: string,\n): { value: number; unit: string; wasConverted: boolean } => {\n const targetUnit = TARGET_UNITS[biomarker] || '';\n\n if (unit) {\n const normalizedUnit = unit.trim().toLowerCase();\n const normalizedTarget = targetUnit.toLowerCase();\n\n if (normalizedUnit === normalizedTarget) {\n return { unit: targetUnit, value, wasConverted: false };\n }\n\n const converted = convertToTargetUnit(biomarker, value, unit);\n return {\n unit: targetUnit,\n value: converted,\n wasConverted: normalizedUnit !== normalizedTarget,\n };\n }\n\n // Heuristic-based detection\n switch (biomarker) {\n case 'fpg':\n // mmol/L typically 3-20, mg/dL typically 60-500\n if (value < 30) {\n return { unit: 'mg/dL', value: value * 18.0182, wasConverted: true };\n }\n return { unit: 'mg/dL', value, wasConverted: false };\n\n case 'hba1c':\n // IFCC mmol/mol typically 20-120, NGSP % typically 4-15\n if (value > 20) {\n return { unit: '%', value: 0.0915 * value + 2.15, wasConverted: true };\n }\n return { unit: '%', value, wasConverted: false };\n\n case 'triglycerides':\n // mmol/L typically 0.5-10, mg/dL typically 50-2000\n if (value < 15) {\n return { unit: 'mg/dL', value: value * 88.57, wasConverted: true };\n }\n return { unit: 'mg/dL', value, wasConverted: false };\n\n case 'hdlc':\n // mmol/L typically 0.5-3, mg/dL typically 20-100\n if (value < 5) {\n return { unit: 'mg/dL', value: value * 38.67, wasConverted: true };\n }\n return { unit: 'mg/dL', value, wasConverted: false };\n\n default:\n return { unit: targetUnit, value, wasConverted: false };\n }\n};\n","/**\n * Derived Biomarker Calculator\n *\n * Derives biomarkers from existing extracted values when the lab report\n * doesn't include them directly.\n *\n * Currently supports:\n * - HOMA-IR = (Fasting Glucose × Fasting Insulin) / 405\n * - VLDL = Triglycerides / 5\n * - BMI = weight_kg / (height_m)² (requires UserContext with height)\n */\n\nimport { codeToLoinc } from '@precisa-saude/fhir';\n\nexport interface BiomarkerInput {\n code: string;\n unit?: string;\n value: number | string;\n}\n\nexport interface DerivedBiomarker {\n code: string;\n loincCode?: string;\n name: string;\n unit: string;\n value: number;\n}\n\nexport interface DerivedOptions {\n codeToLoinc?: (code: string) => string | undefined;\n userContext?: { heightCm?: number };\n}\n\ninterface CalculationDef {\n calculate: (values: Map<string, number>) => number;\n code: string;\n inputs: string[];\n unit: string;\n}\n\ninterface ContextCalculationDef {\n calculate: (values: Map<string, number>, ctx: { heightCm?: number }) => number;\n canCalculate: (ctx: { heightCm?: number }) => boolean;\n code: string;\n inputs: string[];\n unit: string;\n}\n\nconst CALCULATIONS: CalculationDef[] = [\n {\n calculate: (v) => (v.get('Glucose')! * v.get('Insulin')!) / 405,\n code: 'HOMA_IR',\n inputs: ['Glucose', 'Insulin'],\n unit: 'index',\n },\n {\n calculate: (v) => v.get('Triglycerides')! / 5,\n code: 'VLDL',\n inputs: ['Triglycerides'],\n unit: 'mg/dL',\n },\n];\n\nconst CONTEXT_CALCULATIONS: ContextCalculationDef[] = [\n {\n calculate: (v, ctx) => {\n const weightKg = v.get('TotalMass')!;\n const heightM = ctx.heightCm! / 100;\n return weightKg / (heightM * heightM);\n },\n canCalculate: (ctx) =>\n typeof ctx.heightCm === 'number' && ctx.heightCm >= 50 && ctx.heightCm <= 250,\n code: 'BMI',\n inputs: ['TotalMass'],\n unit: 'kg/m2',\n },\n];\n\n/**\n * Compute derived biomarkers from existing extracted values.\n * Only adds a calculated biomarker if:\n * - All required inputs are present with numeric values\n * - The biomarker isn't already present in the results\n */\nexport function computeDerivedBiomarkers(\n biomarkers: BiomarkerInput[],\n options?: DerivedOptions,\n): DerivedBiomarker[] {\n const lookupLoinc = options?.codeToLoinc ?? codeToLoinc;\n const byCode = new Map<string, BiomarkerInput>();\n for (const b of biomarkers) {\n byCode.set(b.code, b);\n }\n\n const added: DerivedBiomarker[] = [];\n\n for (const calc of CALCULATIONS) {\n if (byCode.has(calc.code)) continue;\n\n const values = new Map<string, number>();\n let allPresent = true;\n\n for (const input of calc.inputs) {\n const b = byCode.get(input);\n if (!b || typeof b.value !== 'number') {\n allPresent = false;\n break;\n }\n values.set(input, b.value);\n }\n\n if (!allPresent) continue;\n\n const rawValue = calc.calculate(values);\n const value = parseFloat(rawValue.toPrecision(10));\n const loinc = lookupLoinc(calc.code);\n\n added.push({\n code: calc.code,\n loincCode: loinc ?? undefined,\n name: calc.code,\n unit: calc.unit,\n value,\n });\n }\n\n if (options?.userContext) {\n for (const calc of CONTEXT_CALCULATIONS) {\n if (byCode.has(calc.code)) continue;\n if (!calc.canCalculate(options.userContext)) continue;\n\n const values = new Map<string, number>();\n let allPresent = true;\n\n for (const input of calc.inputs) {\n const b = byCode.get(input);\n if (!b || typeof b.value !== 'number') {\n allPresent = false;\n break;\n }\n values.set(input, b.value);\n }\n\n if (!allPresent) continue;\n\n const rawValue = calc.calculate(values, options.userContext);\n const value = parseFloat(rawValue.toPrecision(10));\n const loinc = lookupLoinc(calc.code);\n\n added.push({\n code: calc.code,\n loincCode: loinc ?? undefined,\n name: calc.code,\n unit: calc.unit,\n value,\n });\n }\n }\n\n return added;\n}\n"],"mappings":"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","names":["BIOMARKER_NAMES_PT","BIOMARKER_RANGES","CONVERSION_FACTORS","TARGET_UNITS","validateBiomarkers","BIOMARKER_NAMES_PT","BIOMARKER_RANGES","BIOMARKER_NAMES_PT","validateBiomarkers","BIOMARKER_RANGES","CONVERSION_FACTORS","TARGET_UNITS"]}
|
package/package.json
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@precisa-saude/fhir-calculators",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Calculadoras clínicas — PhenoAge, BrDMrisc, biomarcadores derivados (HOMA-IR, VLDL, IMC)",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"fhir",
|
|
7
|
+
"phenoage",
|
|
8
|
+
"brdmrisc",
|
|
9
|
+
"homa-ir",
|
|
10
|
+
"clinical-calculator"
|
|
11
|
+
],
|
|
5
12
|
"homepage": "https://github.com/Precisa-Saude/fhir-brasil/tree/main/packages/calculators#readme",
|
|
6
13
|
"bugs": "https://github.com/Precisa-Saude/fhir-brasil/issues",
|
|
7
|
-
"
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/Precisa-Saude/fhir-brasil.git",
|
|
17
|
+
"directory": "packages/calculators"
|
|
18
|
+
},
|
|
19
|
+
"license": "Apache-2.0",
|
|
20
|
+
"sideEffects": false,
|
|
8
21
|
"type": "module",
|
|
9
|
-
"main": "./dist/index.cjs",
|
|
10
|
-
"module": "./dist/index.js",
|
|
11
|
-
"types": "./dist/index.d.ts",
|
|
12
22
|
"exports": {
|
|
13
23
|
".": {
|
|
14
24
|
"import": {
|
|
@@ -21,24 +31,19 @@
|
|
|
21
31
|
}
|
|
22
32
|
}
|
|
23
33
|
},
|
|
24
|
-
"
|
|
34
|
+
"main": "./dist/index.cjs",
|
|
35
|
+
"module": "./dist/index.js",
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"files": [
|
|
38
|
+
"dist"
|
|
39
|
+
],
|
|
25
40
|
"scripts": {
|
|
26
41
|
"build": "tsup",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
42
|
+
"clean": "rm -rf dist .turbo",
|
|
43
|
+
"lint": "eslint src/ --max-warnings 0",
|
|
29
44
|
"test": "vitest run --passWithNoTests",
|
|
30
45
|
"test:coverage": "vitest run --coverage",
|
|
31
|
-
"
|
|
32
|
-
},
|
|
33
|
-
"keywords": ["fhir", "phenoage", "brdmrisc", "homa-ir", "clinical-calculator"],
|
|
34
|
-
"license": "Apache-2.0",
|
|
35
|
-
"repository": {
|
|
36
|
-
"type": "git",
|
|
37
|
-
"url": "https://github.com/Precisa-Saude/fhir-brasil.git",
|
|
38
|
-
"directory": "packages/calculators"
|
|
39
|
-
},
|
|
40
|
-
"publishConfig": {
|
|
41
|
-
"access": "public"
|
|
46
|
+
"typecheck": "tsc --noEmit"
|
|
42
47
|
},
|
|
43
48
|
"dependencies": {
|
|
44
49
|
"@precisa-saude/fhir": "workspace:*"
|
|
@@ -47,5 +52,8 @@
|
|
|
47
52
|
"tsup": "^8.3.5",
|
|
48
53
|
"typescript": "~5.7.3",
|
|
49
54
|
"vitest": "^2.1.8"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
50
58
|
}
|
|
51
59
|
}
|