@precisa-saude/fhir 0.32.0 → 0.34.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.
Files changed (76) hide show
  1. package/README.md +11 -9
  2. package/dist/biomarkers.cjs +2 -2
  3. package/dist/biomarkers.js +1 -1
  4. package/dist/{chunk-44QMNDOI.cjs → chunk-376KM7IL.cjs} +18 -11
  5. package/dist/chunk-376KM7IL.cjs.map +1 -0
  6. package/dist/{chunk-WDDW2WCY.js → chunk-4FKZG5GZ.js} +116 -25
  7. package/dist/chunk-4FKZG5GZ.js.map +1 -0
  8. package/dist/{chunk-AZTZYOAV.cjs → chunk-5RC7C7HJ.cjs} +199 -57
  9. package/dist/chunk-5RC7C7HJ.cjs.map +1 -0
  10. package/dist/{chunk-N3ZCOLG2.js → chunk-E6MXDQXW.js} +19 -1
  11. package/dist/chunk-E6MXDQXW.js.map +1 -0
  12. package/dist/{chunk-MNUQ57JR.js → chunk-K3VZ3F5Z.js} +21 -21
  13. package/dist/chunk-K3VZ3F5Z.js.map +1 -0
  14. package/dist/{chunk-4Y43MQQK.js → chunk-NJM45WAH.js} +10 -3
  15. package/dist/chunk-NJM45WAH.js.map +1 -0
  16. package/dist/{chunk-OTVCOSCK.cjs → chunk-NKDUVSDK.cjs} +21 -21
  17. package/dist/chunk-NKDUVSDK.cjs.map +1 -0
  18. package/dist/{chunk-3ILBFLVQ.cjs → chunk-OAFAERDY.cjs} +20 -2
  19. package/dist/chunk-OAFAERDY.cjs.map +1 -0
  20. package/dist/{chunk-PLR54334.js → chunk-P4G534AQ.js} +445 -61
  21. package/dist/chunk-P4G534AQ.js.map +1 -0
  22. package/dist/{chunk-3XIMPALK.js → chunk-Q3H5C6UR.js} +198 -56
  23. package/dist/chunk-Q3H5C6UR.js.map +1 -0
  24. package/dist/{chunk-MKH4Q735.cjs → chunk-T75NZM56.cjs} +116 -25
  25. package/dist/chunk-T75NZM56.cjs.map +1 -0
  26. package/dist/{chunk-555WKD6J.cjs → chunk-VKLWTTUO.cjs} +447 -63
  27. package/dist/chunk-VKLWTTUO.cjs.map +1 -0
  28. package/dist/cli.js +1547 -933
  29. package/dist/{converter-D1Q84Oc9.d.ts → converter-DP8VkkO3.d.cts} +9 -1
  30. package/dist/{converter-DZH9x52r.d.cts → converter-UygMzWlL.d.ts} +9 -1
  31. package/dist/converter.cjs +5 -4
  32. package/dist/converter.cjs.map +1 -1
  33. package/dist/converter.d.cts +3 -2
  34. package/dist/converter.d.ts +3 -2
  35. package/dist/converter.js +4 -3
  36. package/dist/{fhir-types-B1KQmlRb.d.ts → fhir-types-Cn5WFbOI.d.cts} +5 -0
  37. package/dist/{fhir-types-B1KQmlRb.d.cts → fhir-types-Cn5WFbOI.d.ts} +5 -0
  38. package/dist/importer.cjs +5 -4
  39. package/dist/importer.cjs.map +1 -1
  40. package/dist/importer.d.cts +3 -1
  41. package/dist/importer.d.ts +3 -1
  42. package/dist/importer.js +4 -3
  43. package/dist/index.cjs +19 -11
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.d.cts +6 -6
  46. package/dist/index.d.ts +6 -6
  47. package/dist/index.js +34 -26
  48. package/dist/index.js.map +1 -1
  49. package/dist/reference-ranges.cjs +8 -3
  50. package/dist/reference-ranges.cjs.map +1 -1
  51. package/dist/reference-ranges.d.cts +83 -1
  52. package/dist/reference-ranges.d.ts +83 -1
  53. package/dist/reference-ranges.js +9 -4
  54. package/dist/units.cjs +7 -2
  55. package/dist/units.cjs.map +1 -1
  56. package/dist/units.d.cts +53 -5
  57. package/dist/units.d.ts +53 -5
  58. package/dist/units.js +6 -1
  59. package/dist/validators.cjs +4 -2
  60. package/dist/validators.cjs.map +1 -1
  61. package/dist/validators.d.cts +1 -1
  62. package/dist/validators.d.ts +1 -1
  63. package/dist/validators.js +3 -1
  64. package/package.json +1 -1
  65. package/dist/chunk-3ILBFLVQ.cjs.map +0 -1
  66. package/dist/chunk-3XIMPALK.js.map +0 -1
  67. package/dist/chunk-44QMNDOI.cjs.map +0 -1
  68. package/dist/chunk-4Y43MQQK.js.map +0 -1
  69. package/dist/chunk-555WKD6J.cjs.map +0 -1
  70. package/dist/chunk-AZTZYOAV.cjs.map +0 -1
  71. package/dist/chunk-MKH4Q735.cjs.map +0 -1
  72. package/dist/chunk-MNUQ57JR.js.map +0 -1
  73. package/dist/chunk-N3ZCOLG2.js.map +0 -1
  74. package/dist/chunk-OTVCOSCK.cjs.map +0 -1
  75. package/dist/chunk-PLR54334.js.map +0 -1
  76. package/dist/chunk-WDDW2WCY.js.map +0 -1
package/dist/cli.js CHANGED
@@ -124,6 +124,10 @@ var BIOMARKER_DEFINITIONS = [
124
124
  {
125
125
  category: "coracao",
126
126
  code: "LDL_Medium",
127
+ // 96735-6 é "in Serum" só, enquanto as vizinhas LDL_Small (43727-7),
128
+ // LDL_ParticleNumber (54434-6) e HDL_Large (43729-3) são Ser/Plas. É o
129
+ // único código para a subfração média, e o perfil por RMN roda em soro.
130
+ // Material registrado de propósito.
127
131
  loinc: "96735-6",
128
132
  names: {
129
133
  en: ["LDL Medium", "Medium LDL Particles"],
@@ -1091,14 +1095,16 @@ var BIOMARKER_DEFINITIONS = [
1091
1095
  },
1092
1096
  {
1093
1097
  // LOINC 99620-7 = "Omega 3 fatty acids (w3) [Moles/volume] in RBC.lysate".
1094
- // Alinha à matriz das entradas irmãs Omega3_EPA (75097-6) e Omega3_DHA
1095
- // (75095-0), ambas em hemácias. Anteriormente 35178-3 (mesmo analito em
1096
- // Ser/Plas), incompatível com o uso clínico do Índice Ômega-3 (Harris &
1097
- // von Schacky 2004), que é definido em membranas de hemácias.
1098
- // Ressalva: LOINC declara moles/volume; o biomarcador armazena %.
1099
- // Não há LOINC vigente para "Omega 3 total em % em RBC" — o `Omega3_Index`
1100
- // (88998-0) cobre apenas EPA+DHA. Mantemos `unit: '%'` por consistência
1101
- // com EPA/DHA (75095-0/75097-6 são [Entitic substance], também não-%).
1098
+ // Anteriormente 35178-3 (mesmo analito em Ser/Plas), incompatível com o
1099
+ // uso clínico do Índice Ômega-3 (Harris & von Schacky 2004), que é
1100
+ // definido em membranas de hemácias.
1101
+ //
1102
+ // Compromisso declarado, e não erro: o LOINC 2.82 não tem código de fração
1103
+ // ("ômega-3 total / ácidos graxos totais") para o total, só para os ácidos
1104
+ // individuais e para EPA+DPA+DHA (90911-9). O componente confere; a
1105
+ // propriedade é moles/volume enquanto o laudo imprime %. O teste de eixos
1106
+ // (`loinc-axes.test.ts`) lista esta entrada como exceção com este motivo.
1107
+ // Mesmo compromisso em `Omega6_Total` (99621-5).
1102
1108
  category: "nutrientes",
1103
1109
  code: "Omega3_Total",
1104
1110
  loinc: "99620-7",
@@ -1112,7 +1118,18 @@ var BIOMARKER_DEFINITIONS = [
1112
1118
  category: "nutrientes",
1113
1119
  code: "Omega3_DHA",
1114
1120
  codeAliases: ["DHA"],
1115
- loinc: "75095-0",
1121
+ // Ácidos graxos individuais: fração do total (% dos ácidos graxos C14-C22),
1122
+ // que é o que o perfil de ácidos graxos e o Índice Ômega-3 imprimem. Os
1123
+ // códigos vêm do painel 90918-4 ("Fatty acid omega-3 and omega-6 panel -
1124
+ // Blood"), o mesmo de onde já saem as razões AA/EPA (90909-3) e ω6/ω3
1125
+ // (90910-1). Até out/2026 apontavam para os códigos [Entitic substance]
1126
+ // em hemácias (75095-0, 75097-6, 75110-7, 75117-2), que são quantidade por
1127
+ // célula, e para 48371-9, que é moles/volume em soro: componente certo,
1128
+ // propriedade errada nos cinco, desde mar/2026. Ficam como alias porque o
1129
+ // analito é o mesmo. Material: o painel em soro/plasma (88884-2, C14-C24)
1130
+ // existe, e é a troca a fazer se um laboratório imprimir o perfil em soro.
1131
+ loinc: "90914-3",
1132
+ loincAliases: ["75095-0"],
1116
1133
  names: {
1117
1134
  en: ["Omega-3 DHA", "DHA", "Docosahexaenoic Acid"],
1118
1135
  pt: ["\xD4mega-3: DHA", "DHA", "\xC1cido Docosahexaenoico"]
@@ -1123,7 +1140,9 @@ var BIOMARKER_DEFINITIONS = [
1123
1140
  category: "nutrientes",
1124
1141
  code: "Omega3_DPA",
1125
1142
  codeAliases: ["DPA"],
1126
- loinc: "48371-9",
1143
+ // Fração em sangue, painel 90918-4. Ver `Omega3_DHA`.
1144
+ loinc: "90913-5",
1145
+ loincAliases: ["48371-9"],
1127
1146
  names: {
1128
1147
  en: ["Omega-3 DPA", "DPA", "Docosapentaenoic Acid"],
1129
1148
  pt: ["\xD4mega-3: DPA", "DPA", "\xC1cido Docosapentaenoico"]
@@ -1134,7 +1153,9 @@ var BIOMARKER_DEFINITIONS = [
1134
1153
  category: "nutrientes",
1135
1154
  code: "Omega3_EPA",
1136
1155
  codeAliases: ["EPA"],
1137
- loinc: "75097-6",
1156
+ // Fração em sangue, painel 90918-4. Ver `Omega3_DHA`.
1157
+ loinc: "90912-7",
1158
+ loincAliases: ["75097-6"],
1138
1159
  names: {
1139
1160
  en: ["Omega-3 EPA", "EPA", "Eicosapentaenoic Acid"],
1140
1161
  pt: ["\xD4mega-3: EPA", "EPA", "\xC1cido Eicosapentaenoico"]
@@ -1144,7 +1165,12 @@ var BIOMARKER_DEFINITIONS = [
1144
1165
  {
1145
1166
  category: "nutrientes",
1146
1167
  code: "EPADPADHA",
1147
- loinc: "90908-5",
1168
+ // 90911-9 é a fração EPA+DPA+DHA / ácidos graxos C14-C22 em sangue, o
1169
+ // valor em %. Até out/2026 apontava para 90908-5, que é a *interpretação*
1170
+ // desse mesmo valor (ordinal: risco alto, moderado, baixo), e não o
1171
+ // número. Sem alias: código ordinal carregando valor numérico é o erro
1172
+ // que se quer parar de aceitar.
1173
+ loinc: "90911-9",
1148
1174
  names: {
1149
1175
  en: ["Omega-3 EPA+DPA+DHA", "EPA+DPA+DHA"],
1150
1176
  pt: ["\xD4mega-3: EPA+DPA+DHA"]
@@ -1165,7 +1191,12 @@ var BIOMARKER_DEFINITIONS = [
1165
1191
  {
1166
1192
  category: "nutrientes",
1167
1193
  code: "Omega6_Total",
1168
- loinc: "35177-5",
1194
+ // 99621-5 é "Omega 6 fatty acids (w6) [Moles/volume] in RBC.lysate", o
1195
+ // par exato do 99620-7 de `Omega3_Total`, com o mesmo compromisso de
1196
+ // propriedade documentado lá. Até out/2026 apontava para 35177-5, que é
1197
+ // ácidos graxos poli-insaturados totais em soro: ômega-3 e ômega-6
1198
+ // somados, outro componente. Sem alias.
1199
+ loinc: "99621-5",
1169
1200
  names: {
1170
1201
  en: ["Omega-6 Total", "Total Omega-6"],
1171
1202
  pt: ["\xD4mega-6 Total"]
@@ -1176,7 +1207,9 @@ var BIOMARKER_DEFINITIONS = [
1176
1207
  category: "nutrientes",
1177
1208
  code: "Omega6_AA",
1178
1209
  codeAliases: ["Arachidonic_Acid"],
1179
- loinc: "75110-7",
1210
+ // Fração em sangue, painel 90918-4. Ver `Omega3_DHA`.
1211
+ loinc: "90916-8",
1212
+ loincAliases: ["75110-7"],
1180
1213
  names: {
1181
1214
  en: ["Omega-6 Arachidonic Acid", "Arachidonic Acid", "AA"],
1182
1215
  pt: ["\xD4mega-6: \xC1cido Araquid\xF4nico", "\xC1cido Araquid\xF4nico", "AA"]
@@ -1187,7 +1220,9 @@ var BIOMARKER_DEFINITIONS = [
1187
1220
  category: "nutrientes",
1188
1221
  code: "Omega6_LA",
1189
1222
  codeAliases: ["Linoleic_Acid"],
1190
- loinc: "75117-2",
1223
+ // Fração em sangue, painel 90918-4. Ver `Omega3_DHA`.
1224
+ loinc: "90917-6",
1225
+ loincAliases: ["75117-2"],
1191
1226
  names: {
1192
1227
  en: ["Omega-6 Linoleic Acid", "Linoleic Acid", "LA"],
1193
1228
  pt: ["\xD4mega-6: \xC1cido Linoleico", "\xC1cido Linoleico", "LA"]
@@ -1268,7 +1303,11 @@ var BIOMARKER_DEFINITIONS = [
1268
1303
  {
1269
1304
  category: "nutrientes",
1270
1305
  code: "Zinc",
1271
- loinc: "8245-3",
1306
+ // 5763-8 é "Zinc [Mass/volume] in Serum or Plasma", o zinco sérico da
1307
+ // rotina brasileira (o nome em inglês já dizia "Serum Zinc"). Até out/2026
1308
+ // apontava para 8245-3, o mesmo analito em sangue total, que tem faixa de
1309
+ // referência própria. Sem alias: sangue total é outro material.
1310
+ loinc: "5763-8",
1272
1311
  names: {
1273
1312
  en: ["Zinc", "Serum Zinc"],
1274
1313
  pt: ["Zinco"]
@@ -1369,6 +1408,9 @@ var BIOMARKER_DEFINITIONS = [
1369
1408
  {
1370
1409
  category: "figado",
1371
1410
  code: "Globulin",
1411
+ // 2336-6 é "Globulin [Mass/volume] in Serum", soro só: o LOINC 2.82 não
1412
+ // tem a versão Ser/Plas da globulina total (as frações alfa, beta e gama
1413
+ // têm). Material registrado de propósito.
1372
1414
  loinc: "2336-6",
1373
1415
  names: {
1374
1416
  en: ["Globulin", "Serum Globulin"],
@@ -1648,6 +1690,13 @@ var BIOMARKER_DEFINITIONS = [
1648
1690
  {
1649
1691
  category: "rins",
1650
1692
  code: "BUN_Creatinine_Ratio",
1693
+ // 3097-3 é "Urea nitrogen/Creatinine [Mass Ratio]", a razão BUN/creatinina,
1694
+ // e a faixa de referência (10-20, Tietz) é a dessa convenção. O laudo
1695
+ // brasileiro imprime "Relação Ureia/Creatinina", cuja faixa é outra (cerca
1696
+ // de 21-43) e cujo código é 56997-0 ("Urea/Creatinine [Mass Ratio] in
1697
+ // Serum or Plasma"). Trocar só o código deixaria a faixa errada do outro
1698
+ // lado; a separação em duas grandezas, como em `Glucose_Fasting`, é
1699
+ // decisão à parte, registrada em `reference-ranges.ts`.
1651
1700
  loinc: "3097-3",
1652
1701
  names: {
1653
1702
  en: ["BUN/Creatinine Ratio", "Urea/Creatinine Ratio"],
@@ -1815,7 +1864,12 @@ var BIOMARKER_DEFINITIONS = [
1815
1864
  category: "urina",
1816
1865
  code: "Bacteria_Urine",
1817
1866
  codeAliases: ["UrineBacteria"],
1818
- loinc: "630-4",
1867
+ // 5769-5 é "Bacteria [#/area] in Urine sediment by Microscopy high power
1868
+ // field", a contagem do sedimento que a unidade /HPF declara. Até out/2026
1869
+ // apontava para 630-4, "Bacteria identified in Urine by Culture": outro
1870
+ // exame, nominal, que identifica a espécie em cultura. Sem alias:
1871
+ // urocultura não é sedimento.
1872
+ loinc: "5769-5",
1819
1873
  names: {
1820
1874
  en: ["Urine Bacteria", "Bacteria Urine", "Bacteria, Urine", "Bacteria"],
1821
1875
  pt: ["Bact\xE9rias na Urina", "Bact\xE9rias"]
@@ -1934,7 +1988,14 @@ var BIOMARKER_DEFINITIONS = [
1934
1988
  {
1935
1989
  category: "urina",
1936
1990
  code: "RBC_Urine",
1937
- loinc: "5808-1",
1991
+ // 13945-1 é "Erythrocytes [#/area] in Urine sediment by Microscopy high
1992
+ // power field", o /HPF que o laudo imprime e que `Leukocytes_Urine` já usa
1993
+ // em 5821-4. Até out/2026 apontava para 5808-1, o mesmo exame em #/volume
1994
+ // (por mL): analito e material certos, propriedade errada. Fica como alias
1995
+ // porque o analito é o mesmo, e laudo de analisador automático que conte
1996
+ // por mL continua resolvendo em RBC_Urine.
1997
+ loinc: "13945-1",
1998
+ loincAliases: ["5808-1"],
1938
1999
  names: {
1939
2000
  en: ["Urine RBC", "Red Blood Cells in Urine", "RBC Urine", "RBC, Urine"],
1940
2001
  pt: ["Hem\xE1cias na Urina"]
@@ -2029,6 +2090,10 @@ var BIOMARKER_DEFINITIONS = [
2029
2090
  {
2030
2091
  category: "autoimunidade",
2031
2092
  code: "Gliadin_Deamidated_IgA",
2093
+ // 63453-5 e 63459-2 (IgG) afirmam método, "by Immunoassay", e material,
2094
+ // "Serum". É o que os kits de DGP usam. A propriedade é unidades
2095
+ // arbitrárias por volume, e o laudo imprime U/mL; até out/2026 a unidade
2096
+ // declarada era "U", sem o volume.
2032
2097
  loinc: "63453-5",
2033
2098
  names: {
2034
2099
  en: [
@@ -2042,7 +2107,7 @@ var BIOMARKER_DEFINITIONS = [
2042
2107
  ],
2043
2108
  pt: ["Anticorpos Anti-Gliadina Deamidada IgA", "DGP IgA"]
2044
2109
  },
2045
- unit: "U"
2110
+ unit: "U/mL"
2046
2111
  },
2047
2112
  {
2048
2113
  category: "autoimunidade",
@@ -2060,7 +2125,7 @@ var BIOMARKER_DEFINITIONS = [
2060
2125
  ],
2061
2126
  pt: ["Anticorpos Anti-Gliadina Deamidada IgG", "DGP IgG"]
2062
2127
  },
2063
- unit: "U"
2128
+ unit: "U/mL"
2064
2129
  },
2065
2130
  {
2066
2131
  category: "autoimunidade",
@@ -2451,6 +2516,10 @@ var BIOMARKER_DEFINITIONS = [
2451
2516
  {
2452
2517
  category: "composicao-corporal",
2453
2518
  code: "TotalBodyWater",
2519
+ // 101683-1 é "Body water mass", em kg; o aparelho de bioimpedância imprime
2520
+ // litros. É o único código de água corporal total no LOINC 2.82 (o outro,
2521
+ // 101684-9, é o percentual), e para água 1 L pesa 1 kg, então o número não
2522
+ // muda. O teste de eixos lista a entrada como exceção com este motivo.
2454
2523
  loinc: "101683-1",
2455
2524
  names: {
2456
2525
  en: ["Total Body Water", "Body Water", "TBW", "Total Water"],
@@ -2516,6 +2585,10 @@ var BIOMARKER_DEFINITIONS = [
2516
2585
  {
2517
2586
  category: "composicao-corporal",
2518
2587
  code: "PhaseAngle",
2588
+ // 107160-4 é "Phase angle Xc/R [Ratio] Bioelectrical impedance analysis",
2589
+ // único código de ângulo de fase. O aparelho imprime graus (arctan Xc/R)
2590
+ // e o LOINC declara razão com unidade-exemplo %. Fica em graus, que é o
2591
+ // que o laudo traz, e o teste de eixos lista a entrada como exceção.
2519
2592
  loinc: "107160-4",
2520
2593
  names: {
2521
2594
  en: ["Phase Angle", "Whole Body Phase Angle", "PhA", "AnglePhase"],
@@ -2606,9 +2679,9 @@ var BIOMARKER_DEFINITIONS = [
2606
2679
  {
2607
2680
  category: "composicao-corporal",
2608
2681
  code: "WaistCircumference",
2609
- // 8280-0 é a medida em si. 56086-2, que parecia o óbvio pela busca, é
2610
- // "Adult Waist Circumference Protocol", um protocolo PhenX e não um
2611
- // resultado.
2682
+ // 8280-0 é a medida em si, e afirma sítio e método: "at umbilicus by Tape
2683
+ // measure". 56086-2, que parecia o óbvio pela busca, é "Adult Waist
2684
+ // Circumference Protocol", um protocolo PhenX e não um resultado.
2612
2685
  loinc: "8280-0",
2613
2686
  names: {
2614
2687
  en: ["Waist Circumference", "Abdominal Circumference", "Waist"],
@@ -3018,6 +3091,10 @@ var BIOMARKER_DEFINITIONS = [
3018
3091
  {
3019
3092
  category: "coracao",
3020
3093
  code: "TroponinI",
3094
+ // 49563-0 afirma método: limite de detecção ≤ 0,01 ng/mL, o ensaio de alta
3095
+ // sensibilidade. Um resultado de troponina I convencional pede o código
3096
+ // sem método, 10839-9. Assimétrico com `TroponinT`, que fica no 6598-7 sem
3097
+ // método de propósito: a hs-cTnT em ng/L é a convenção dos laudos.
3021
3098
  loinc: "49563-0",
3022
3099
  names: {
3023
3100
  en: ["Troponin I", "cTnI", "Cardiac Troponin I", "hs-TnI", "High-Sensitivity Troponin I"],
@@ -3028,6 +3105,9 @@ var BIOMARKER_DEFINITIONS = [
3028
3105
  {
3029
3106
  category: "coracao",
3030
3107
  code: "TroponinT",
3108
+ // 6598-7 não afirma método. A unidade ng/L é a convenção do ensaio de alta
3109
+ // sensibilidade (hs-cTnT), e o ensaio convencional em ng/mL converte por
3110
+ // fator exato (ver `units.ts`). Ver `TroponinI` para a assimetria.
3031
3111
  loinc: "6598-7",
3032
3112
  names: {
3033
3113
  en: ["Troponin T", "cTnT", "Cardiac Troponin T", "hs-TnT", "High-Sensitivity Troponin T"],
@@ -3064,6 +3144,11 @@ var BIOMARKER_DEFINITIONS = [
3064
3144
  {
3065
3145
  category: "figado",
3066
3146
  code: "LDH",
3147
+ // 14804-9 afirma método: reação lactato → piruvato (L→P). O código sem
3148
+ // método, 2532-0, está DISCOURAGED, então um método tem que ser escolhido,
3149
+ // e L→P é o do procedimento de referência IFCC que os laboratórios
3150
+ // brasileiros usam, ainda que o laudo raramente imprima a direção. Se um
3151
+ // laudo declarar P→L, o código é outro (14805-6).
3067
3152
  loinc: "14804-9",
3068
3153
  // 2532-0 é o código genérico anterior, que o LOINC marca como DISCOURAGED.
3069
3154
  // Fica como alias para que laudo antigo e dado já armazenado continuem
@@ -3194,7 +3279,13 @@ var BIOMARKER_DEFINITIONS = [
3194
3279
  {
3195
3280
  category: "pancreas",
3196
3281
  code: "BetaHydroxybutyrate",
3197
- loinc: "53060-0",
3282
+ // 6873-4 é "Beta hydroxybutyrate [Moles/volume] in Serum or Plasma", o
3283
+ // que o laudo dosa em mmol/L. Até out/2026 apontava para 53060-0, que é
3284
+ // butirilcarnitina (C4) em líquido amniótico: analito e material errados
3285
+ // desde abr/2026, e nenhum check pegou porque o código existe e está
3286
+ // ACTIVE. Sem alias para o código antigo: ele nunca significou BHB, e
3287
+ // aceitá-lo na importação faria um laudo de carnitina virar cetona.
3288
+ loinc: "6873-4",
3198
3289
  names: {
3199
3290
  en: ["Beta-Hydroxybutyrate", "\u03B2-Hydroxybutyrate", "BHB", "Ketone Bodies"],
3200
3291
  pt: ["Beta-Hidroxibutirato", "\u03B2-Hidroxibutirato", "BHB", "Corpos Cet\xF4nicos"]
@@ -3325,97 +3416,99 @@ var BIOMARKER_CODE_SYSTEM = "http://fhir-brasil.dev/biomarker-codes";
3325
3416
  var UNIT_TO_UCUM = {
3326
3417
  // Dimensionless / special measurements
3327
3418
  "[pH]": "[pH]",
3419
+ "{index}": "{index}",
3328
3420
  "{ratio}": "{ratio}",
3421
+ "{score}": "{score}",
3329
3422
  "{specific gravity}": "{specific gravity}",
3423
+ "/HPF": "/[HPF]",
3424
+ "/LPF": "/[LPF]",
3330
3425
  "/\xB5L": "/uL",
3331
3426
  // Percentage
3332
3427
  "%": "%",
3333
3428
  // Count units
3334
3429
  "10\xB3/\xB5L": "10*3/uL",
3335
3430
  "10\u2076/\xB5L": "10*6/uL",
3431
+ Angstrom: "Ao",
3432
+ AU: "{score}",
3433
+ cm: "cm",
3434
+ "cm\xB3": "cm3",
3435
+ deg: "deg",
3336
3436
  // Volume
3337
3437
  fL: "fL",
3338
3438
  g: "g",
3439
+ "g/cm\xB2": "g/cm2",
3339
3440
  // Mass concentration
3340
3441
  "g/dL": "g/dL",
3442
+ "g/L": "g/L",
3443
+ "IU/L": "[IU]/L",
3444
+ "IU/mL": "[IU]/mL",
3445
+ "K/uL": "10*3/uL",
3446
+ "kcal/d": "kcal/d",
3447
+ kg: "kg",
3448
+ "kg/m2": "kg/m2",
3449
+ "kg/m\xB2": "kg/m2",
3450
+ "kU/L": "k[IU]/L",
3341
3451
  L: "L",
3452
+ "M/uL": "10*6/uL",
3453
+ "mcg/dL": "ug/dL",
3454
+ "mcg/L": "ug/L",
3455
+ "mcg/mL": "ug/mL",
3342
3456
  // Molar concentration
3343
3457
  "mEq/L": "meq/L",
3344
3458
  mg: "mg",
3345
3459
  "mg/dL": "mg/dL",
3460
+ "mg/g": "mg/g",
3461
+ "mg/L": "mg/L",
3462
+ "mIU/L": "m[IU]/L",
3463
+ "mIU/mL": "m[IU]/mL",
3346
3464
  mL: "mL",
3465
+ "mL/min/1.73m\xB2": "mL/min/{1.73_m2}",
3466
+ mm: "mm",
3467
+ "mm/h": "mm/h",
3468
+ "mm/hr": "mm/h",
3347
3469
  "mmol/L": "mmol/L",
3470
+ "mUI/L": "m[IU]/L",
3348
3471
  "mUI/mL": "m[IU]/mL",
3349
3472
  ng: "ng",
3350
3473
  "ng/dL": "ng/dL",
3474
+ "ng/L": "ng/L",
3351
3475
  "ng/mL": "ng/mL",
3352
3476
  "nmol/L": "nmol/L",
3353
3477
  // Mass
3354
3478
  pg: "pg",
3355
3479
  "pg/mL": "pg/mL",
3356
3480
  pH: "[pH]",
3481
+ "pmol/L": "pmol/L",
3482
+ raz\u00E3o: "{ratio}",
3483
+ score: "{score}",
3484
+ segundos: "s",
3357
3485
  // Enzyme activity
3486
+ U: "U",
3358
3487
  "U/L": "U/L",
3359
3488
  // Special units
3360
3489
  "U/mL": "U/mL",
3490
+ "ug/dL": "ug/dL",
3491
+ "ug/L": "ug/L",
3361
3492
  "UI/L": "[IU]/L",
3362
3493
  "UI/mL": "[IU]/mL",
3494
+ "uIU/mL": "u[IU]/mL",
3495
+ "umol/L": "umol/L",
3363
3496
  \u00B5g: "ug",
3364
3497
  "\xB5g/dL": "ug/dL",
3498
+ "\xB5g/L": "ug/L",
3365
3499
  "\xB5mol/L": "umol/L",
3366
- "\xB5UI/mL": "u[IU]/mL"
3500
+ "\xB5UI/mL": "u[IU]/mL",
3501
+ \u00EDndice: "{index}"
3367
3502
  };
3368
- var BIOMARKER_DEFAULT_UNIT = {
3369
- // Proteins
3370
- Albumin: "g/dL",
3371
- AlkalinePhosphatase: "U/L",
3372
- // Liver
3373
- ALT: "U/L",
3374
- AST: "U/L",
3375
- Calcium: "mg/dL",
3376
- Chloride: "mEq/L",
3377
- // Lipids
3378
- Cholesterol: "mg/dL",
3379
- // Kidney
3380
- Creatinine: "mg/dL",
3381
- eAG: "mg/dL",
3382
- Ferritin: "ng/mL",
3383
- FolicAcid: "ng/mL",
3384
- GGT: "U/L",
3385
- // Glucose/Diabetes
3386
- Glucose: "mg/dL",
3387
- HbA1c: "%",
3388
- Hct: "%",
3389
- HDL: "mg/dL",
3390
- // Hematology
3391
- Hgb: "g/dL",
3392
- Insulin: "\xB5UI/mL",
3393
- // Iron studies
3394
- Iron: "\xB5g/dL",
3395
- LDL: "mg/dL",
3396
- Magnesium: "mg/dL",
3397
- NonHDL_Cholesterol: "mg/dL",
3398
- // Urinalysis - dimensionless
3503
+ var DIMENSIONLESS_DEFAULTS = {
3399
3504
  pH_Urine: "[pH]",
3400
- Potassium: "mEq/L",
3401
- RDW: "%",
3402
- // Electrolytes
3403
- Sodium: "mEq/L",
3404
- SpecificGravity_Urine: "{specific gravity}",
3405
- T3Free: "pg/mL",
3406
- T4Free: "ng/dL",
3407
- TIBC: "\xB5g/dL",
3408
- TotalProtein: "g/dL",
3409
- TransferrinSaturation: "%",
3410
- Triglycerides: "mg/dL",
3411
- // Thyroid
3412
- TSH: "\xB5UI/mL",
3413
- Urea: "mg/dL",
3414
- UricAcid: "mg/dL",
3415
- // Vitamins
3416
- VitaminB12: "pg/mL",
3417
- VitaminD: "ng/mL",
3418
- VLDL: "mg/dL"
3505
+ SpecificGravity_Urine: "{specific gravity}"
3506
+ };
3507
+ var BIOMARKER_DEFAULT_UNIT = {
3508
+ ...Object.fromEntries(
3509
+ BIOMARKER_DEFINITIONS.filter((b) => b.unit).map((b) => [b.code, b.unit])
3510
+ ),
3511
+ ...DIMENSIONLESS_DEFAULTS
3419
3512
  };
3420
3513
  function unitToUCUM(unit) {
3421
3514
  return UNIT_TO_UCUM[unit] || unit;
@@ -3423,6 +3516,128 @@ function unitToUCUM(unit) {
3423
3516
  function getDefaultUnit(biomarkerCode) {
3424
3517
  return BIOMARKER_DEFAULT_UNIT[biomarkerCode] || "";
3425
3518
  }
3519
+ var UCUM_ATOMS = /* @__PURE__ */ new Set([
3520
+ "m",
3521
+ "g",
3522
+ "s",
3523
+ "L",
3524
+ "l",
3525
+ "mol",
3526
+ "eq",
3527
+ "U",
3528
+ "cal",
3529
+ "h",
3530
+ "d",
3531
+ "min",
3532
+ "wk",
3533
+ "mo",
3534
+ "a",
3535
+ "Ao",
3536
+ "deg",
3537
+ "K",
3538
+ "A",
3539
+ "N",
3540
+ "Pa",
3541
+ "J",
3542
+ "W",
3543
+ "Hz",
3544
+ "V",
3545
+ "C",
3546
+ "osm",
3547
+ "t",
3548
+ "u",
3549
+ "bar",
3550
+ "atm",
3551
+ "rad",
3552
+ "sr",
3553
+ "cd",
3554
+ "Bq",
3555
+ "Gy",
3556
+ "Sv",
3557
+ "lm",
3558
+ "lx",
3559
+ "Cel",
3560
+ "B",
3561
+ "Np"
3562
+ ]);
3563
+ var UCUM_PREFIXES = /* @__PURE__ */ new Set([
3564
+ "Y",
3565
+ "Z",
3566
+ "E",
3567
+ "P",
3568
+ "T",
3569
+ "G",
3570
+ "M",
3571
+ "k",
3572
+ "h",
3573
+ "da",
3574
+ "d",
3575
+ "c",
3576
+ "m",
3577
+ "u",
3578
+ "n",
3579
+ "p",
3580
+ "f",
3581
+ "a",
3582
+ "z",
3583
+ "y"
3584
+ ]);
3585
+ function isUcumAtom(token) {
3586
+ if (token === "%" || /^10\*-?\d+$/.test(token) || /^\d+$/.test(token)) return true;
3587
+ const bracket = /^([A-Za-z]{0,2})\[[^[\]{}\s]+\]$/.exec(token);
3588
+ if (bracket) return bracket[1] === "" || UCUM_PREFIXES.has(bracket[1]);
3589
+ if (UCUM_ATOMS.has(token)) return true;
3590
+ for (const prefix of UCUM_PREFIXES) {
3591
+ if (token.startsWith(prefix) && UCUM_ATOMS.has(token.slice(prefix.length))) return true;
3592
+ }
3593
+ return false;
3594
+ }
3595
+ function isUcumComponent(token) {
3596
+ const m = /^(.*?)(\{[^{}]*\})?$/.exec(token);
3597
+ if (!m) return false;
3598
+ const [, body, annotation] = m;
3599
+ if (!body) return annotation !== void 0;
3600
+ const exp = /^(.+?)([+-]?\d+)?$/.exec(body);
3601
+ if (!exp) return false;
3602
+ const [, atom, exponent] = exp;
3603
+ if (isUcumAtom(body)) return true;
3604
+ if (exponent === void 0) return false;
3605
+ return isUcumAtom(atom);
3606
+ }
3607
+ function isUcumCode(code) {
3608
+ if (!code || /[^\x21-\x7E ]/.test(code)) return false;
3609
+ const terms = [];
3610
+ let current = "";
3611
+ let braces = 0;
3612
+ let inBracket = false;
3613
+ for (const ch of code) {
3614
+ if (ch === "{") braces++;
3615
+ else if (ch === "}") {
3616
+ if (braces === 0) return false;
3617
+ braces--;
3618
+ } else if (ch === "[") inBracket = true;
3619
+ else if (ch === "]") inBracket = false;
3620
+ if ((ch === "." || ch === "/") && braces === 0 && !inBracket) {
3621
+ terms.push(current, ch);
3622
+ current = "";
3623
+ continue;
3624
+ }
3625
+ current += ch;
3626
+ }
3627
+ if (braces !== 0 || inBracket) return false;
3628
+ terms.push(current);
3629
+ if (terms[0] === "" && terms[1] === "/") terms[0] = "1";
3630
+ let expectOperand = true;
3631
+ for (const t of terms) {
3632
+ if (expectOperand) {
3633
+ if (!isUcumComponent(t)) return false;
3634
+ } else if (t !== "." && t !== "/") {
3635
+ return false;
3636
+ }
3637
+ expectOperand = !expectOperand;
3638
+ }
3639
+ return !expectOperand;
3640
+ }
3426
3641
  var CBC_DIFF_ALIASES = {
3427
3642
  "/ul": "/uL",
3428
3643
  "/\xB5l": "/uL",
@@ -3724,7 +3939,7 @@ var BIOMARKER_UNITS = {
3724
3939
  "miu/ml": "mIU/mL",
3725
3940
  "ui/l": "mIU/mL"
3726
3941
  },
3727
- canonicalUcum: "mIU/mL",
3942
+ canonicalUcum: "m[iU]/mL",
3728
3943
  canonicalUnit: "mIU/mL",
3729
3944
  siUcum: "[iU]/L",
3730
3945
  siUnit: "IU/L"
@@ -3859,7 +4074,7 @@ var BIOMARKER_UNITS = {
3859
4074
  "miu/ml": "mIU/mL",
3860
4075
  "ui/l": "mIU/mL"
3861
4076
  },
3862
- canonicalUcum: "mIU/mL",
4077
+ canonicalUcum: "m[iU]/mL",
3863
4078
  canonicalUnit: "mIU/mL",
3864
4079
  siUcum: "[iU]/L",
3865
4080
  siUnit: "IU/L"
@@ -4126,7 +4341,7 @@ var BIOMARKER_UNITS = {
4126
4341
  },
4127
4342
  canonicalUcum: "u[iU]/mL",
4128
4343
  canonicalUnit: "uIU/mL",
4129
- siUcum: "mIU/L",
4344
+ siUcum: "m[iU]/L",
4130
4345
  siUnit: "mIU/L"
4131
4346
  },
4132
4347
  Urea: {
@@ -4181,635 +4396,105 @@ var BIOMARKER_UNITS = {
4181
4396
  function getCanonicalUnit(code) {
4182
4397
  return BIOMARKER_UNITS[code]?.canonicalUnit ?? null;
4183
4398
  }
4184
-
4185
- // src/converter.ts
4186
- function interpretationCode(flag) {
4187
- switch (flag) {
4188
- case "H":
4189
- return "H";
4190
- // High
4191
- case "L":
4192
- return "L";
4193
- // Low
4194
- default:
4195
- return "N";
4196
- }
4399
+ function normalizeUnit(unit, config) {
4400
+ return config.aliases[unit.toLowerCase()] ?? config.aliases[unit] ?? unit;
4197
4401
  }
4198
- function interpretationDisplay(flag) {
4199
- switch (flag) {
4200
- case "H":
4201
- return "High";
4202
- case "L":
4203
- return "Low";
4204
- default:
4205
- return "Normal";
4402
+ function resolveUcum(unit, biomarkerCode) {
4403
+ if (!unit) return void 0;
4404
+ const config = biomarkerCode ? BIOMARKER_UNITS[biomarkerCode] : void 0;
4405
+ if (config) {
4406
+ const normalized = normalizeUnit(unit, config);
4407
+ if (normalized === config.canonicalUnit) return config.canonicalUcum;
4408
+ if (normalized === config.siUnit) return config.siUcum;
4206
4409
  }
4410
+ const mapped = UNIT_TO_UCUM[unit];
4411
+ if (mapped) return mapped;
4412
+ return isUcumCode(unit) ? unit : void 0;
4207
4413
  }
4208
- var SEX_CODING = {
4209
- female: {
4210
- code: "female",
4211
- display: "Female",
4212
- system: "http://hl7.org/fhir/administrative-gender"
4414
+
4415
+ // src/reference-ranges.ts
4416
+ var biomarkerRangeDefinitions = {
4417
+ Albumin_Creatinine_Ratio: {
4418
+ default: { max: 30, min: 0, optimalMax: 20, optimalMin: 0, unit: "mg/g" },
4419
+ kind: "decision-threshold",
4420
+ source: "tietz-7ed-2015"
4213
4421
  },
4214
- male: { code: "male", display: "Male", system: "http://hl7.org/fhir/administrative-gender" }
4215
- };
4216
- var buildReferenceRanges = (observation, sourceUnit, ucumUnit) => {
4217
- const quantity = (value) => ({
4218
- code: ucumUnit,
4219
- system: "http://unitsofmeasure.org",
4220
- unit: sourceUnit,
4221
- value
4222
- });
4223
- const toRanges = (low, high, sex) => {
4224
- if (low === void 0 && high === void 0) return [];
4225
- return [
4422
+ Adiponectin: {
4423
+ default: { max: 26, min: 4, optimalMax: 20, optimalMin: 8, unit: "mcg/mL" },
4424
+ kind: "reference-interval",
4425
+ source: "tietz-7ed-2015"
4426
+ },
4427
+ ADMA: {
4428
+ default: { max: 0.7, min: 0.3, optimalMax: 0.55, optimalMin: 0.3, unit: "umol/L" },
4429
+ kind: "reference-interval",
4430
+ source: "nemeth-adma-2017"
4431
+ },
4432
+ AFP: {
4433
+ default: { max: 10, min: 0, optimalMax: 8, optimalMin: 0, unit: "ng/mL" },
4434
+ kind: "reference-interval",
4435
+ source: "sturgeon-nacb-2008"
4436
+ },
4437
+ Albumin_Globulin_Ratio: {
4438
+ default: { max: 2.5, min: 1, optimalMax: 2.2, optimalMin: 1.2, unit: "" },
4439
+ kind: "reference-interval",
4440
+ source: "tietz-7ed-2015"
4441
+ },
4442
+ Albumin: {
4443
+ default: { max: 5, min: 3.5, optimalMax: 4.8, optimalMin: 4, unit: "g/dL" },
4444
+ kind: "reference-interval",
4445
+ source: "pns-bioquimica-2019"
4446
+ },
4447
+ AlkalinePhosphatase: {
4448
+ default: { max: 147, min: 44, optimalMax: 120, optimalMin: 50, unit: "U/L" },
4449
+ kind: "reference-interval",
4450
+ source: "tietz-7ed-2015",
4451
+ variants: [
4226
4452
  {
4227
- ...sex === void 0 ? {} : { appliesTo: [{ coding: [SEX_CODING[sex]] }] },
4228
- ...high === void 0 ? {} : { high: quantity(high) },
4229
- ...low === void 0 ? {} : { low: quantity(low) }
4230
- }
4231
- ];
4232
- };
4233
- if (observation.referenceRanges && observation.referenceRanges.length > 0) {
4234
- return observation.referenceRanges.flatMap((r) => toRanges(r.low, r.high, r.appliesTo));
4235
- }
4236
- return toRanges(observation.referenceMin, observation.referenceMax);
4237
- };
4238
- function labObservationToFHIR(observation, patientId, laboratoryName) {
4239
- const loincCode = codeToLoinc(observation.biomarkerCode);
4240
- const sourceUnit = observation.unit || getDefaultUnit(observation.biomarkerCode) || observation.unit;
4241
- const ucumUnit = unitToUCUM(sourceUnit);
4242
- const isQualitative = observation.isQualitative || typeof observation.value === "string";
4243
- const fhirObs = {
4244
- category: [
4453
+ ageMin: 18,
4454
+ range: { max: 147, min: 44, optimalMax: 120, optimalMin: 50, unit: "U/L" },
4455
+ sex: "M"
4456
+ },
4245
4457
  {
4246
- coding: [
4247
- {
4248
- code: "laboratory",
4249
- display: "Laboratory",
4250
- system: "http://terminology.hl7.org/CodeSystem/observation-category"
4251
- }
4252
- ]
4458
+ ageMin: 18,
4459
+ range: { max: 104, min: 35, optimalMax: 90, optimalMin: 40, unit: "U/L" },
4460
+ sex: "F"
4253
4461
  }
4254
- ],
4255
- code: {
4256
- // Sem LOINC, o coding LOINC simplesmente não sai. Antes ia `99999-9`,
4257
- // que não é código LOINC nenhum: publicava sob `http://loinc.org` uma
4258
- // afirmação falsa, e quem consumisse o bundle confiando no system
4259
- // trataria aquilo como código de verdade. Composição corporal, densidade
4260
- // óssea e escore de cálcio não têm LOINC, e o certo é a lacuna explícita.
4261
- coding: [
4262
- ...loincCode ? [
4263
- {
4264
- code: loincCode,
4265
- display: observation.biomarkerName,
4266
- system: LOINC_SYSTEM
4267
- }
4268
- ] : [],
4269
- {
4270
- code: observation.biomarkerCode,
4271
- display: observation.biomarkerName,
4272
- system: BIOMARKER_CODE_SYSTEM
4273
- }
4274
- ],
4275
- text: observation.biomarkerName
4276
- },
4277
- effectiveDateTime: observation.collectionDate,
4278
- id: `${observation.reportId}-${observation.biomarkerCode}`,
4279
- interpretation: [
4462
+ ]
4463
+ },
4464
+ ALT: {
4465
+ default: { max: 35, min: 0, optimalMax: 25, optimalMin: 0, unit: "U/L" },
4466
+ kind: "reference-interval",
4467
+ source: "tietz-7ed-2015",
4468
+ variants: [
4280
4469
  {
4281
- coding: [
4282
- {
4283
- code: interpretationCode(observation.flag),
4284
- display: interpretationDisplay(observation.flag),
4285
- system: "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation"
4286
- }
4287
- ]
4288
- }
4289
- ],
4290
- performer: laboratoryName ? [{ display: laboratoryName }] : void 0,
4291
- resourceType: "Observation",
4292
- status: "final",
4293
- subject: {
4294
- reference: `Patient/${patientId}`
4295
- }
4296
- };
4297
- if (isQualitative) {
4298
- fhirObs.valueString = String(observation.value);
4299
- } else {
4300
- fhirObs.valueQuantity = {
4301
- code: ucumUnit,
4302
- system: "http://unitsofmeasure.org",
4303
- unit: sourceUnit,
4304
- value: observation.value
4305
- };
4306
- const referenceRange = buildReferenceRanges(observation, sourceUnit, ucumUnit);
4307
- if (referenceRange.length > 0) fhirObs.referenceRange = referenceRange;
4308
- }
4309
- return fhirObs;
4310
- }
4311
- function labReportToFHIR(report, patientId, observationIds) {
4312
- let status;
4313
- switch (report.processingStatus) {
4314
- case "complete":
4315
- status = "final";
4316
- break;
4317
- case "partial":
4318
- status = "partial";
4319
- break;
4320
- case "pending_review":
4321
- status = "preliminary";
4322
- break;
4323
- default:
4324
- status = "final";
4325
- }
4326
- return {
4327
- category: [
4470
+ ageMin: 18,
4471
+ range: { max: 56, min: 7, optimalMax: 35, optimalMin: 10, unit: "U/L" },
4472
+ sex: "M"
4473
+ },
4328
4474
  {
4329
- coding: [
4330
- {
4331
- code: "LAB",
4332
- display: "Laboratory",
4333
- system: "http://terminology.hl7.org/CodeSystem/v2-0074"
4334
- }
4335
- ]
4475
+ ageMin: 18,
4476
+ range: { max: 45, min: 7, optimalMax: 30, optimalMin: 10, unit: "U/L" },
4477
+ sex: "F"
4336
4478
  }
4337
- ],
4338
- code: {
4339
- coding: [
4340
- {
4341
- code: "11502-2",
4342
- // Laboratory report
4343
- display: "Laboratory report",
4344
- system: "http://loinc.org"
4345
- }
4346
- ],
4347
- text: "Laboratory Results"
4348
- },
4349
- conclusion: report.overallStatus === "NORMAL" ? "All results within normal limits" : "One or more abnormal results detected",
4350
- conclusionCode: report.overallStatus === "ANORMAL" ? [
4479
+ ]
4480
+ },
4481
+ // AMH — fortemente dependente de idade e sexo. As variantes femininas por
4482
+ // faixa etária são a referência clínica; o `default` foi ajustado para
4483
+ // refletir uma faixa adulta conservadora (~18-40 anos, mulheres em idade
4484
+ // reprodutiva geral). optimalMax anterior (6.9 ng/mL) sobrepunha-se a
4485
+ // valores sugestivos de SOP ou risco de OHSS; clinicamente, valores acima
4486
+ // de ~4 ng/mL já merecem investigação.
4487
+ AMH: {
4488
+ default: { max: 6, min: 1, optimalMax: 4, optimalMin: 1.5, unit: "ng/mL" },
4489
+ kind: "reference-interval",
4490
+ source: "tietz-7ed-2015",
4491
+ variants: [
4351
4492
  {
4352
- coding: [
4353
- {
4354
- code: "A",
4355
- display: "Abnormal",
4356
- system: "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation"
4357
- }
4358
- ]
4359
- }
4360
- ] : void 0,
4361
- effectiveDateTime: report.collectionDate,
4362
- id: report.reportId,
4363
- issued: report.createdAt,
4364
- performer: report.laboratoryName ? [{ display: report.laboratoryName }] : void 0,
4365
- resourceType: "DiagnosticReport",
4366
- result: observationIds.map((id) => ({ reference: `Observation/${id}` })),
4367
- status,
4368
- subject: {
4369
- reference: `Patient/${patientId}`
4370
- }
4371
- };
4372
- }
4373
- function userProfileToFHIR(profile) {
4374
- const nameParts = profile.name.split(" ");
4375
- const given = nameParts.slice(0, -1);
4376
- const family = nameParts[nameParts.length - 1] || "";
4377
- return {
4378
- address: profile.address ? [
4379
- {
4380
- city: profile.address.city,
4381
- country: profile.address.country || "BR",
4382
- line: [
4383
- profile.address.street && profile.address.number ? `${profile.address.street}, ${profile.address.number}` : profile.address.street,
4384
- profile.address.complement
4385
- ].filter(Boolean),
4386
- postalCode: profile.address.postalCode,
4387
- state: profile.address.state
4388
- }
4389
- ] : void 0,
4390
- birthDate: profile.birthDate,
4391
- gender: profile.gender,
4392
- id: profile.userId,
4393
- name: [
4394
- {
4395
- family,
4396
- given: given.length > 0 ? given : void 0,
4397
- text: profile.name
4398
- }
4399
- ],
4400
- resourceType: "Patient",
4401
- telecom: [
4402
- ...profile.email ? [{ system: "email", value: profile.email }] : [],
4403
- ...profile.phone ? [{ system: "phone", value: profile.phone }] : []
4404
- ].length > 0 ? [
4405
- ...profile.email ? [{ system: "email", value: profile.email }] : [],
4406
- ...profile.phone ? [{ system: "phone", value: profile.phone }] : []
4407
- ] : void 0
4408
- };
4409
- }
4410
- function labResultToFHIRBundle(report, observations, userProfile) {
4411
- const patientId = userProfile.userId;
4412
- const fhirObservations = observations.map((obs) => {
4413
- const resource = labObservationToFHIR(
4414
- { ...obs, collectionDate: report.collectionDate },
4415
- patientId,
4416
- report.laboratoryName
4417
- );
4418
- return { fullUrl: entryFullUrl(resource), resource };
4419
- });
4420
- const observationIds = fhirObservations.map((entry) => entry.resource.id);
4421
- const diagnosticReport = labReportToFHIR(report, patientId, observationIds);
4422
- const fhirPatient = userProfileToFHIR(userProfile);
4423
- return {
4424
- entry: [
4425
- { fullUrl: entryFullUrl(fhirPatient), resource: fhirPatient },
4426
- { fullUrl: entryFullUrl(diagnosticReport), resource: diagnosticReport },
4427
- ...fhirObservations
4428
- ],
4429
- resourceType: "Bundle",
4430
- type: "collection"
4431
- };
4432
- }
4433
-
4434
- // src/cli/commands/convert.ts
4435
- async function convert(args, json) {
4436
- const raw = await getInput(args[0]);
4437
- const data = parseJson(
4438
- raw,
4439
- "JSON inv\xE1lido. Forne\xE7a um objeto com { report, observations, profile }."
4440
- );
4441
- if (!data.report || !data.observations || !data.profile) {
4442
- exitWithError(
4443
- 'Formato esperado: { "report": LabReportData, "observations": LabObservationData[], "profile": UserProfileData }'
4444
- );
4445
- }
4446
- const bundle = labResultToFHIRBundle(data.report, data.observations, data.profile);
4447
- if (json) {
4448
- outputJson(bundle);
4449
- } else {
4450
- outputText(JSON.stringify(bundle, null, 2));
4451
- }
4452
- }
4453
-
4454
- // src/validators.ts
4455
- function validateFHIRImportBundle(data) {
4456
- const errors = [];
4457
- if (!data || typeof data !== "object") {
4458
- errors.push({ details: "Input must be a JSON object", field: "root" });
4459
- return errors;
4460
- }
4461
- const bundle = data;
4462
- if (bundle.resourceType !== "Bundle") {
4463
- errors.push({
4464
- details: `Expected resourceType "Bundle", got "${String(bundle.resourceType)}"`,
4465
- field: "resourceType"
4466
- });
4467
- }
4468
- if (!Array.isArray(bundle.entry)) {
4469
- errors.push({ details: 'Bundle must contain an "entry" array', field: "entry" });
4470
- } else if (bundle.entry.length === 0) {
4471
- errors.push({ details: "Bundle entry array is empty", field: "entry" });
4472
- }
4473
- return errors;
4474
- }
4475
-
4476
- // src/importer.ts
4477
- var MAX_OBSERVATIONS = 5e3;
4478
- var MAX_FILE_SIZE = 15 * 1024 * 1024;
4479
- function resolveBiomarkerCode(observation) {
4480
- const coding = observation.code?.coding ?? [];
4481
- const loincCode = coding.find((c) => c.system === LOINC_SYSTEM)?.code;
4482
- const declaredCode = coding.find((c) => c.system === BIOMARKER_CODE_SYSTEM)?.code;
4483
- const seenCodes = [
4484
- ...loincCode ? [`LOINC ${loincCode}`] : [],
4485
- ...declaredCode ? [`biomarker code ${declaredCode}`] : []
4486
- ];
4487
- let reason;
4488
- if (seenCodes.length > 0) {
4489
- reason = `Unknown code: ${seenCodes.join(", ")}`;
4490
- } else if (coding.length > 0) {
4491
- const systems = [...new Set(coding.map((c) => c.system ?? "(sem system)"))];
4492
- reason = `No code in a supported system (found: ${systems.join(", ")})`;
4493
- } else {
4494
- reason = "No code found in observation coding";
4495
- }
4496
- const fromLoinc = loincCode ? loincToCode(loincCode) : void 0;
4497
- if (fromLoinc) return { internalCode: fromLoinc, loincCode, reason };
4498
- if (declaredCode && isValidCode(declaredCode)) {
4499
- const canonical = normalizeCode(declaredCode);
4500
- return { internalCode: canonical, loincCode: codeToLoinc(canonical), reason };
4501
- }
4502
- return { loincCode, reason };
4503
- }
4504
- function extractFlag(observation) {
4505
- const code = observation.interpretation?.[0]?.coding?.[0]?.code;
4506
- if (code === "H" || code === "HH") return "H";
4507
- if (code === "L" || code === "LL") return "L";
4508
- return "";
4509
- }
4510
- function extractObservationsFromBundle(bundle) {
4511
- const observations = [];
4512
- const skipped = [];
4513
- for (let i = 0; i < bundle.entry.length; i++) {
4514
- const entry = bundle.entry[i];
4515
- if (!entry.resource) {
4516
- skipped.push({ index: i, reason: "Entry has no resource" });
4517
- continue;
4518
- }
4519
- if (entry.resource.resourceType !== "Observation") {
4520
- continue;
4521
- }
4522
- if (observations.length >= MAX_OBSERVATIONS) {
4523
- skipped.push({ index: i, reason: `Maximum of ${MAX_OBSERVATIONS} observations exceeded` });
4524
- continue;
4525
- }
4526
- observations.push(entry.resource);
4527
- }
4528
- return { observations, skipped };
4529
- }
4530
- function mapFHIRObservationToInternal(observation, index) {
4531
- const { internalCode, loincCode, reason } = resolveBiomarkerCode(observation);
4532
- if (!internalCode) {
4533
- return {
4534
- skipped: { index, loincCode, reason, resourceType: "Observation" }
4535
- };
4536
- }
4537
- const definition = getDefinitionByCode(internalCode);
4538
- let value;
4539
- let unit = "";
4540
- let isQualitative = false;
4541
- if (observation.valueQuantity?.value !== void 0) {
4542
- value = observation.valueQuantity.value;
4543
- unit = observation.valueQuantity.unit || observation.valueQuantity.code || "";
4544
- } else if (observation.valueString) {
4545
- value = observation.valueString;
4546
- isQualitative = true;
4547
- } else {
4548
- return {
4549
- skipped: {
4550
- index,
4551
- loincCode,
4552
- reason: "Observation has no value (valueQuantity or valueString)",
4553
- resourceType: "Observation"
4554
- }
4555
- };
4556
- }
4557
- const collectionDate = observation.effectiveDateTime || observation.effectivePeriod?.start || "";
4558
- if (!collectionDate) {
4559
- return {
4560
- skipped: {
4561
- index,
4562
- loincCode,
4563
- reason: "Observation has no effectiveDateTime or effectivePeriod.start",
4564
- resourceType: "Observation"
4565
- }
4566
- };
4567
- }
4568
- let referenceMin;
4569
- let referenceMax;
4570
- if (observation.referenceRange?.[0]) {
4571
- referenceMin = observation.referenceRange[0].low?.value;
4572
- referenceMax = observation.referenceRange[0].high?.value;
4573
- }
4574
- const imported = {
4575
- biomarkerCode: internalCode,
4576
- biomarkerName: definition?.names.pt[0] || definition?.names.en[0] || observation.code.text || internalCode,
4577
- collectionDate,
4578
- flag: extractFlag(observation),
4579
- isQualitative,
4580
- loincCode,
4581
- referenceMax,
4582
- referenceMin,
4583
- unit: unit || definition?.unit || "",
4584
- value
4585
- };
4586
- return { observation: imported };
4587
- }
4588
- function processImportBundle(data) {
4589
- const validationErrors = validateFHIRImportBundle(data);
4590
- if (validationErrors.length > 0) {
4591
- return {
4592
- errors: validationErrors,
4593
- imported: [],
4594
- skipped: [],
4595
- totalProcessed: 0
4596
- };
4597
- }
4598
- const bundle = data;
4599
- const { observations, skipped } = extractObservationsFromBundle(bundle);
4600
- const imported = [];
4601
- const allSkipped = [...skipped];
4602
- for (let i = 0; i < observations.length; i++) {
4603
- const result = mapFHIRObservationToInternal(observations[i], i);
4604
- if ("observation" in result) {
4605
- imported.push(result.observation);
4606
- } else {
4607
- allSkipped.push(result.skipped);
4608
- }
4609
- }
4610
- return {
4611
- errors: [],
4612
- imported,
4613
- skipped: allSkipped,
4614
- totalProcessed: observations.length
4615
- };
4616
- }
4617
-
4618
- // src/cli/commands/import.ts
4619
- async function importBundle(args, json) {
4620
- const raw = await getInput(args[0]);
4621
- const bundle = parseJson(raw, "JSON inv\xE1lido.");
4622
- const result = processImportBundle(bundle);
4623
- if (json) {
4624
- outputJson(result);
4625
- return;
4626
- }
4627
- outputText(`Processados: ${result.totalProcessed}`);
4628
- outputText(`Importados: ${result.imported.length}`);
4629
- outputText(`Ignorados: ${result.skipped.length}`);
4630
- outputText(`Erros: ${result.errors.length}`);
4631
- if (result.imported.length > 0) {
4632
- outputText("\nObserva\xE7\xF5es importadas:");
4633
- for (const obs of result.imported) {
4634
- outputText(` ${obs.biomarkerCode} (${obs.loincCode}): ${obs.value} ${obs.unit}`);
4635
- }
4636
- }
4637
- if (result.errors.length > 0) {
4638
- outputText("\nErros:");
4639
- for (const err of result.errors) {
4640
- outputText(` ${err.field}: ${err.details}`);
4641
- }
4642
- }
4643
- }
4644
-
4645
- // src/cli/commands/list.ts
4646
- import { parseArgs } from "util";
4647
- async function list(args, json) {
4648
- const { values } = parseArgs({
4649
- args,
4650
- options: {
4651
- category: { type: "string" },
4652
- visible: { default: false, type: "boolean" }
4653
- },
4654
- strict: false
4655
- });
4656
- let defs;
4657
- if (values.category) {
4658
- defs = getBiomarkersByCategory(values.category);
4659
- if (defs.length === 0) {
4660
- exitWithError(`Categoria n\xE3o encontrada ou vazia: ${values.category}`);
4661
- }
4662
- } else if (values.visible) {
4663
- defs = getVisibleDefinitions();
4664
- } else {
4665
- defs = getAllDefinitions();
4666
- }
4667
- if (json) {
4668
- outputJson(defs);
4669
- return;
4670
- }
4671
- const rows = defs.map((d) => [
4672
- d.code,
4673
- d.loinc ?? "\u2014",
4674
- d.names.pt[0] ?? "",
4675
- d.unit ?? "\u2014",
4676
- Array.isArray(d.category) ? d.category.join(", ") : d.category
4677
- ]);
4678
- outputText(formatTable(["C\xF3digo", "LOINC", "Nome (pt)", "Unidade", "Categoria"], rows));
4679
- outputText(`
4680
- Total: ${defs.length} biomarcadores`);
4681
- }
4682
-
4683
- // src/cli/commands/loinc-map.ts
4684
- async function loincMap(_args, json) {
4685
- const defs = getAllDefinitions().filter((d) => d.loinc);
4686
- if (json) {
4687
- const map = Object.fromEntries(defs.map((d) => [d.loinc, d.code]));
4688
- outputJson(map);
4689
- return;
4690
- }
4691
- const rows = defs.map((d) => [d.loinc, d.code, d.names.pt[0] ?? d.names.en[0] ?? ""]);
4692
- outputText(formatTable(["LOINC", "C\xF3digo", "Nome (pt)"], rows));
4693
- outputText(`
4694
- Total: ${rows.length} mapeamentos`);
4695
- }
4696
-
4697
- // src/cli/commands/lookup.ts
4698
- function displayDefinition(def) {
4699
- const cat = Array.isArray(def.category) ? def.category.join(", ") : def.category;
4700
- outputText(
4701
- [
4702
- `Biomarcador: ${def.code}`,
4703
- ` LOINC: ${def.loinc ?? "\u2014"}`,
4704
- ` Nomes (pt): ${def.names.pt.join(", ")}`,
4705
- ` Nomes (en): ${def.names.en.join(", ")}`,
4706
- ` Categoria: ${cat}`,
4707
- ` Unidade: ${def.unit ?? "\u2014"}`,
4708
- ` Sexo: ${def.sex ?? "ambos"}`,
4709
- ` Vis\xEDvel: ${def.hidden ? "n\xE3o" : "sim"}`
4710
- ].join("\n")
4711
- );
4712
- }
4713
- async function lookup(args, json) {
4714
- const code = args[0];
4715
- if (!code) exitWithError("Uso: fhir-bio lookup <c\xF3digo>");
4716
- const def = getDefinitionByCode(code);
4717
- if (!def) exitWithError(`Biomarcador n\xE3o encontrado: ${code}`);
4718
- if (json) {
4719
- outputJson(def);
4720
- } else {
4721
- displayDefinition(def);
4722
- }
4723
- }
4724
- async function lookupLoinc(args, json) {
4725
- const loinc = args[0];
4726
- if (!loinc) exitWithError("Uso: fhir-bio lookup-loinc <loinc>");
4727
- const def = getDefinitionByLoinc(loinc);
4728
- if (!def) exitWithError(`Biomarcador n\xE3o encontrado para LOINC: ${loinc}`);
4729
- if (json) {
4730
- outputJson(def);
4731
- } else {
4732
- displayDefinition(def);
4733
- }
4734
- }
4735
-
4736
- // src/cli/commands/range.ts
4737
- import { parseArgs as parseArgs2 } from "util";
4738
-
4739
- // src/reference-ranges.ts
4740
- var biomarkerRangeDefinitions = {
4741
- Albumin_Creatinine_Ratio: {
4742
- default: { max: 30, min: 0, optimalMax: 20, optimalMin: 0, unit: "mg/g" },
4743
- source: "tietz-7ed-2015"
4744
- },
4745
- Adiponectin: {
4746
- default: { max: 26, min: 4, optimalMax: 20, optimalMin: 8, unit: "mcg/mL" },
4747
- source: "tietz-7ed-2015"
4748
- },
4749
- ADMA: {
4750
- default: { max: 0.7, min: 0.3, optimalMax: 0.55, optimalMin: 0.3, unit: "umol/L" },
4751
- source: "nemeth-adma-2017"
4752
- },
4753
- AFP: {
4754
- default: { max: 10, min: 0, optimalMax: 8, optimalMin: 0, unit: "ng/mL" },
4755
- source: "sturgeon-nacb-2008"
4756
- },
4757
- Albumin_Globulin_Ratio: {
4758
- default: { max: 2.5, min: 1, optimalMax: 2.2, optimalMin: 1.2, unit: "" },
4759
- source: "tietz-7ed-2015"
4760
- },
4761
- Albumin: {
4762
- default: { max: 5, min: 3.5, optimalMax: 4.8, optimalMin: 4, unit: "g/dL" },
4763
- source: "pns-bioquimica-2019"
4764
- },
4765
- AlkalinePhosphatase: {
4766
- default: { max: 147, min: 44, optimalMax: 120, optimalMin: 50, unit: "U/L" },
4767
- source: "tietz-7ed-2015",
4768
- variants: [
4769
- {
4770
- ageMin: 18,
4771
- range: { max: 147, min: 44, optimalMax: 120, optimalMin: 50, unit: "U/L" },
4772
- sex: "M"
4773
- },
4774
- {
4775
- ageMin: 18,
4776
- range: { max: 104, min: 35, optimalMax: 90, optimalMin: 40, unit: "U/L" },
4777
- sex: "F"
4778
- }
4779
- ]
4780
- },
4781
- ALT: {
4782
- default: { max: 35, min: 0, optimalMax: 25, optimalMin: 0, unit: "U/L" },
4783
- source: "tietz-7ed-2015",
4784
- variants: [
4785
- {
4786
- ageMin: 18,
4787
- range: { max: 56, min: 7, optimalMax: 35, optimalMin: 10, unit: "U/L" },
4788
- sex: "M"
4789
- },
4790
- {
4791
- ageMin: 18,
4792
- range: { max: 45, min: 7, optimalMax: 30, optimalMin: 10, unit: "U/L" },
4793
- sex: "F"
4794
- }
4795
- ]
4796
- },
4797
- // AMH — fortemente dependente de idade e sexo. As variantes femininas por
4798
- // faixa etária são a referência clínica; o `default` foi ajustado para
4799
- // refletir uma faixa adulta conservadora (~18-40 anos, mulheres em idade
4800
- // reprodutiva geral). optimalMax anterior (6.9 ng/mL) sobrepunha-se a
4801
- // valores sugestivos de SOP ou risco de OHSS; clinicamente, valores acima
4802
- // de ~4 ng/mL já merecem investigação.
4803
- AMH: {
4804
- default: { max: 6, min: 1, optimalMax: 4, optimalMin: 1.5, unit: "ng/mL" },
4805
- source: "tietz-7ed-2015",
4806
- variants: [
4807
- {
4808
- ageMax: 24,
4809
- ageMin: 18,
4810
- range: { max: 13, min: 1.5, optimalMax: 10, optimalMin: 3, unit: "ng/mL" },
4811
- sex: "F"
4812
- },
4493
+ ageMax: 24,
4494
+ ageMin: 18,
4495
+ range: { max: 13, min: 1.5, optimalMax: 10, optimalMin: 3, unit: "ng/mL" },
4496
+ sex: "F"
4497
+ },
4813
4498
  {
4814
4499
  ageMax: 30,
4815
4500
  ageMin: 25,
@@ -4837,14 +4522,17 @@ var biomarkerRangeDefinitions = {
4837
4522
  },
4838
4523
  Ammonia: {
4839
4524
  default: { max: 45, min: 15, optimalMax: 40, optimalMin: 20, unit: "umol/L" },
4525
+ kind: "reference-interval",
4840
4526
  source: "tietz-7ed-2015"
4841
4527
  },
4842
4528
  AntiThyroglobulin: {
4843
4529
  default: { max: 115, min: 0, optimalMax: 40, optimalMin: 0, unit: "IU/mL" },
4530
+ kind: "reference-interval",
4844
4531
  source: "tietz-7ed-2015"
4845
4532
  },
4846
4533
  AntiTPO: {
4847
4534
  default: { max: 34, min: 0, optimalMax: 9, optimalMin: 0, unit: "IU/mL" },
4535
+ kind: "reference-interval",
4848
4536
  source: "tietz-7ed-2015"
4849
4537
  },
4850
4538
  // Aortic valve calcium — no standardized clinical threshold; use mild cutoff
@@ -4852,23 +4540,28 @@ var biomarkerRangeDefinitions = {
4852
4540
  AorticValveCalcium: {
4853
4541
  default: { max: 99, min: 0, optimalMax: 0, optimalMin: 0, unit: "AU" },
4854
4542
  direction: "lower-better",
4543
+ kind: "decision-threshold",
4855
4544
  source: "rumberger-cac-1999"
4856
4545
  },
4857
4546
  ApoA1: {
4858
4547
  default: { max: 200, min: 100, optimalMax: 180, optimalMin: 120, unit: "mg/dL" },
4548
+ kind: "reference-interval",
4859
4549
  source: "contois-apoa1-1996"
4860
4550
  },
4861
4551
  ApoB: {
4862
4552
  default: { max: 90, min: 0, optimalMax: 70, optimalMin: 0, unit: "mg/dL" },
4863
4553
  direction: "lower-better",
4554
+ kind: "decision-threshold",
4864
4555
  source: "sbc-lipids-2025"
4865
4556
  },
4866
4557
  ApoCIII: {
4867
4558
  default: { max: 10, min: 0, optimalMax: 7, optimalMin: 0, unit: "mg/dL" },
4559
+ kind: "decision-threshold",
4868
4560
  source: "khetarpal-apociii-2016"
4869
4561
  },
4870
4562
  Omega6_AA: {
4871
4563
  default: { max: 15, min: 5, optimalMax: 12, optimalMin: 7, unit: "%" },
4564
+ kind: "decision-threshold",
4872
4565
  source: "simopoulos-omega-ratio-2002"
4873
4566
  },
4874
4567
  // Razão Ácido Araquidônico/EPA (sangue total) — `lower-better`: razão menor =
@@ -4881,16 +4574,19 @@ var biomarkerRangeDefinitions = {
4881
4574
  // dentro do intervalo. Sem `optimal*`: não há corte ótimo de AA/EPA com fonte.
4882
4575
  // Em telas de relatório único, prefira o intervalo impresso pelo laboratório.
4883
4576
  AA_EPA_Ratio: {
4884
- default: { max: 40.7, min: 3.7, unit: "" },
4577
+ default: { max: 40.7, min: 3.7, minKind: "display", unit: "" },
4885
4578
  direction: "lower-better",
4579
+ kind: "population",
4886
4580
  source: "torrissen-omega3-dbs-2025"
4887
4581
  },
4888
4582
  Arsenic: {
4889
4583
  default: { max: 35, min: 0, optimalMax: 20, optimalMin: 0, unit: "mcg/L" },
4584
+ kind: "decision-threshold",
4890
4585
  source: "nr7-pcmso-2020"
4891
4586
  },
4892
4587
  AST: {
4893
4588
  default: { max: 35, min: 0, optimalMax: 25, optimalMin: 0, unit: "U/L" },
4589
+ kind: "reference-interval",
4894
4590
  source: "tietz-7ed-2015",
4895
4591
  variants: [
4896
4592
  {
@@ -4909,6 +4605,7 @@ var biomarkerRangeDefinitions = {
4909
4605
  // Tabela 2 da PNS 2019. Ver o comentário em `Neutrophils_Abs`.
4910
4606
  Basophils_Abs: {
4911
4607
  default: { max: 0.072, min: 0, unit: "K/uL" },
4608
+ kind: "reference-interval",
4912
4609
  source: "pns-hemograma-2019",
4913
4610
  variants: [
4914
4611
  { ageMin: 18, range: { max: 0.062, min: 0, unit: "K/uL" }, sex: "M" },
@@ -4917,6 +4614,7 @@ var biomarkerRangeDefinitions = {
4917
4614
  },
4918
4615
  Bicarbonate: {
4919
4616
  default: { max: 29, min: 23, optimalMax: 28, optimalMin: 24, unit: "mEq/L" },
4617
+ kind: "reference-interval",
4920
4618
  source: "tietz-7ed-2015"
4921
4619
  },
4922
4620
  // β-Hidroxibutirato (BHB) sérico — corpos cetônicos. Faixa derivada de
@@ -4926,23 +4624,28 @@ var biomarkerRangeDefinitions = {
4926
4624
  // superior aqui reflete estado pós-prandial habitual, não jejum.
4927
4625
  BetaHydroxybutyrate: {
4928
4626
  default: { max: 0.28, min: 0.02, optimalMax: 0.28, optimalMin: 0.02, unit: "mmol/L" },
4627
+ kind: "reference-interval",
4929
4628
  source: "klee-bhb-2020"
4930
4629
  },
4931
4630
  BilirubinDirect: {
4932
4631
  default: { max: 0.3, min: 0, optimalMax: 0.2, optimalMin: 0, unit: "mg/dL" },
4632
+ kind: "reference-interval",
4933
4633
  source: "tietz-7ed-2015"
4934
4634
  },
4935
4635
  BilirubinIndirect: {
4936
4636
  default: { max: 0.8, min: 0, optimalMax: 0.6, optimalMin: 0, unit: "mg/dL" },
4637
+ kind: "reference-interval",
4937
4638
  source: "tietz-7ed-2015"
4938
4639
  },
4939
4640
  BilirubinTotal: {
4940
4641
  default: { max: 1.2, min: 0.1, optimalMax: 0.9, optimalMin: 0.2, unit: "mg/dL" },
4642
+ kind: "reference-interval",
4941
4643
  source: "tietz-7ed-2015"
4942
4644
  },
4943
4645
  // BNP: 35 pg/mL = corte não-agudo para triagem de IC (SBC IC 2018)
4944
4646
  BNP: {
4945
4647
  default: { max: 35, min: 0, optimalMax: 20, optimalMin: 0, unit: "pg/mL" },
4648
+ kind: "decision-threshold",
4946
4649
  source: "sbc-ic-2018"
4947
4650
  },
4948
4651
  // Razão BUN/Creatinina — faixa 10-20 aplica-se ao Nitrogênio Ureico (BUN).
@@ -4953,14 +4656,17 @@ var biomarkerRangeDefinitions = {
4953
4656
  // distinta. Ver issue #41 para alinhamento de nomenclatura/LOINC.
4954
4657
  BUN_Creatinine_Ratio: {
4955
4658
  default: { max: 20, min: 10, optimalMax: 18, optimalMin: 12, unit: "" },
4659
+ kind: "reference-interval",
4956
4660
  source: "tietz-7ed-2015"
4957
4661
  },
4958
4662
  CA125: {
4959
4663
  default: { max: 35, min: 0, optimalMax: 25, optimalMin: 0, unit: "U/mL" },
4664
+ kind: "reference-interval",
4960
4665
  source: "sturgeon-nacb-2008"
4961
4666
  },
4962
4667
  CA199: {
4963
4668
  default: { max: 37, min: 0, optimalMax: 30, optimalMin: 0, unit: "U/mL" },
4669
+ kind: "reference-interval",
4964
4670
  source: "sturgeon-nacb-2008"
4965
4671
  },
4966
4672
  // CAC: Agatston classification — 0=none, 1-99=mild, 100-399=moderate, 400+=severe
@@ -4968,44 +4674,53 @@ var biomarkerRangeDefinitions = {
4968
4674
  CAC: {
4969
4675
  default: { max: 99, min: 0, optimalMax: 0, optimalMin: 0, unit: "AU" },
4970
4676
  direction: "lower-better",
4677
+ kind: "decision-threshold",
4971
4678
  source: "rumberger-cac-1999"
4972
4679
  },
4973
4680
  // Per-vessel scores — same Agatston classification applied per vessel
4974
4681
  CAC_LAD: {
4975
4682
  default: { max: 99, min: 0, optimalMax: 0, optimalMin: 0, unit: "AU" },
4976
4683
  direction: "lower-better",
4684
+ kind: "decision-threshold",
4977
4685
  source: "rumberger-cac-1999"
4978
4686
  },
4979
4687
  CAC_LCX: {
4980
4688
  default: { max: 99, min: 0, optimalMax: 0, optimalMin: 0, unit: "AU" },
4981
4689
  direction: "lower-better",
4690
+ kind: "decision-threshold",
4982
4691
  source: "rumberger-cac-1999"
4983
4692
  },
4984
4693
  CAC_LMA: {
4985
4694
  default: { max: 99, min: 0, optimalMax: 0, optimalMin: 0, unit: "AU" },
4986
4695
  direction: "lower-better",
4696
+ kind: "decision-threshold",
4987
4697
  source: "rumberger-cac-1999"
4988
4698
  },
4989
4699
  CAC_Percentile: {
4990
4700
  default: { max: 50, min: 0, optimalMax: 25, optimalMin: 0, unit: "%", warningMax: 75 },
4991
4701
  direction: "lower-better",
4702
+ kind: "decision-threshold",
4992
4703
  source: "rumberger-cac-1999"
4993
4704
  },
4994
4705
  CAC_RCA: {
4995
4706
  default: { max: 99, min: 0, optimalMax: 0, optimalMin: 0, unit: "AU" },
4996
4707
  direction: "lower-better",
4708
+ kind: "decision-threshold",
4997
4709
  source: "rumberger-cac-1999"
4998
4710
  },
4999
4711
  Cadmium: {
5000
4712
  default: { max: 1.2, min: 0, optimalMax: 0.5, optimalMin: 0, unit: "mcg/L" },
4713
+ kind: "decision-threshold",
5001
4714
  source: "nr7-pcmso-2020"
5002
4715
  },
5003
4716
  Calcium: {
5004
4717
  default: { max: 10.5, min: 8.5, optimalMax: 10, optimalMin: 9, unit: "mg/dL" },
4718
+ kind: "reference-interval",
5005
4719
  source: "tietz-7ed-2015"
5006
4720
  },
5007
4721
  CEA: {
5008
4722
  default: { max: 3, min: 0, optimalMax: 2.5, optimalMin: 0, unit: "ng/mL" },
4723
+ kind: "reference-interval",
5009
4724
  source: "sturgeon-nacb-2008"
5010
4725
  },
5011
4726
  // CK: valores conservadores baseados em faixas laboratoriais típicas
@@ -5030,6 +4745,7 @@ var biomarkerRangeDefinitions = {
5030
4745
  // autodeclaração de raça/Cor". Ver a issue #3 para a decisão registrada.
5031
4746
  CK: {
5032
4747
  default: { max: 314, min: 30, optimalMax: 170, optimalMin: 40, unit: "U/L" },
4748
+ kind: "reference-interval",
5033
4749
  source: "kalaria-ck-ri-2026",
5034
4750
  variants: [
5035
4751
  { sex: "F", range: { max: 170, min: 30, optimalMax: 150, optimalMin: 40, unit: "U/L" } }
@@ -5037,6 +4753,7 @@ var biomarkerRangeDefinitions = {
5037
4753
  },
5038
4754
  Chloride: {
5039
4755
  default: { max: 106, min: 98, optimalMax: 105, optimalMin: 100, unit: "mEq/L" },
4756
+ kind: "reference-interval",
5040
4757
  source: "tietz-7ed-2015"
5041
4758
  },
5042
4759
  // Colesterol total — SBC 2017/2025: valor desejável < 190 mg/dL para adultos,
@@ -5044,6 +4761,7 @@ var biomarkerRangeDefinitions = {
5044
4761
  // (NCEP, 2002), superada pelas atualizações brasileiras.
5045
4762
  Cholesterol: {
5046
4763
  default: { max: 190, min: 0, optimalMax: 170, optimalMin: 0, unit: "mg/dL" },
4764
+ kind: "decision-threshold",
5047
4765
  source: "sbc-lipids-2025"
5048
4766
  },
5049
4767
  // Índice de Castelli I (CT/HDL-c)
@@ -5051,6 +4769,7 @@ var biomarkerRangeDefinitions = {
5051
4769
  // Índice de Castelli I — SBC 2017: M <4.9, F <4.3
5052
4770
  default: { max: 4.9, min: 0, optimalMax: 3.5, optimalMin: 0, unit: "" },
5053
4771
  direction: "lower-better",
4772
+ kind: "decision-threshold",
5054
4773
  source: "castelli-ratio-1992",
5055
4774
  variants: [
5056
4775
  {
@@ -5067,22 +4786,27 @@ var biomarkerRangeDefinitions = {
5067
4786
  },
5068
4787
  CO2: {
5069
4788
  default: { max: 29, min: 23, optimalMax: 28, optimalMin: 24, unit: "mEq/L" },
4789
+ kind: "reference-interval",
5070
4790
  source: "tietz-7ed-2015"
5071
4791
  },
5072
4792
  Copper: {
5073
4793
  default: { max: 175, min: 70, optimalMax: 150, optimalMin: 85, unit: "mcg/dL" },
4794
+ kind: "reference-interval",
5074
4795
  source: "tietz-7ed-2015"
5075
4796
  },
5076
4797
  CoQ10: {
5077
4798
  default: { max: 1.5, min: 0.5, optimalMax: 1.3, optimalMin: 0.7, unit: "mg/L" },
4799
+ kind: "reference-interval",
5078
4800
  source: "tietz-7ed-2015"
5079
4801
  },
5080
4802
  Cortisol: {
5081
4803
  default: { max: 25, min: 5, optimalMax: 20, optimalMin: 10, unit: "mcg/dL" },
4804
+ kind: "reference-interval",
5082
4805
  source: "tietz-7ed-2015"
5083
4806
  },
5084
4807
  CortisolFree: {
5085
4808
  default: { max: 2.5, min: 0.5, optimalMax: 2, optimalMin: 0.8, unit: "mcg/dL" },
4809
+ kind: "reference-interval",
5086
4810
  source: "tietz-7ed-2015"
5087
4811
  },
5088
4812
  CPeptide: {
@@ -5094,10 +4818,12 @@ var biomarkerRangeDefinitions = {
5094
4818
  optimalMin: 1,
5095
4819
  unit: "ng/mL"
5096
4820
  },
4821
+ kind: "reference-interval",
5097
4822
  source: "tietz-7ed-2015"
5098
4823
  },
5099
4824
  Creatinine: {
5100
4825
  default: { max: 1.2, min: 0.6, optimalMax: 1, optimalMin: 0.7, unit: "mg/dL" },
4826
+ kind: "reference-interval",
5101
4827
  source: "pns-bioquimica-2019",
5102
4828
  variants: [
5103
4829
  // Gestação: hiperfiltração glomerular (↑ 40–50% GFR) reduz a creatinina
@@ -5123,10 +4849,12 @@ var biomarkerRangeDefinitions = {
5123
4849
  CRP: {
5124
4850
  default: { max: 3, min: 0, optimalMax: 1, optimalMin: 0, unit: "mg/L" },
5125
4851
  direction: "lower-better",
4852
+ kind: "decision-threshold",
5126
4853
  source: "tietz-7ed-2015"
5127
4854
  },
5128
4855
  CystatinC: {
5129
4856
  default: { max: 1, min: 0.5, optimalMax: 0.9, optimalMin: 0.6, unit: "mg/L" },
4857
+ kind: "reference-interval",
5130
4858
  source: "tietz-7ed-2015"
5131
4859
  },
5132
4860
  // D-Dímero — exame de exclusão de TEV (tromboembolismo venoso) com
@@ -5140,6 +4868,7 @@ var biomarkerRangeDefinitions = {
5140
4868
  DDimer: {
5141
4869
  default: { max: 500, min: 0, unit: "ng/mL" },
5142
4870
  direction: "lower-better",
4871
+ kind: "decision-threshold",
5143
4872
  source: "wells-ddimer-2003",
5144
4873
  variants: [
5145
4874
  // Corte ajustado por idade — ESC 2019 (Konstantinides et al.) e
@@ -5153,10 +4882,12 @@ var biomarkerRangeDefinitions = {
5153
4882
  },
5154
4883
  Omega3_DHA: {
5155
4884
  default: { max: 8, min: 2, optimalMax: 6.5, optimalMin: 3.5, unit: "%" },
4885
+ kind: "decision-threshold",
5156
4886
  source: "harris-omega3-2004"
5157
4887
  },
5158
4888
  DHEAS: {
5159
4889
  default: { max: 500, min: 100, optimalMax: 400, optimalMin: 150, unit: "mcg/dL" },
4890
+ kind: "reference-interval",
5160
4891
  source: "tietz-7ed-2015",
5161
4892
  variants: [
5162
4893
  {
@@ -5197,20 +4928,30 @@ var biomarkerRangeDefinitions = {
5197
4928
  },
5198
4929
  Omega3_DPA: {
5199
4930
  default: { max: 2, min: 0.3, optimalMax: 1.5, optimalMin: 0.5, unit: "%" },
4931
+ kind: "decision-threshold",
5200
4932
  source: "harris-omega3-2004"
5201
4933
  },
5202
4934
  eGFR: {
5203
- default: { max: 120, min: 60, optimalMax: 120, optimalMin: 90, unit: "mL/min/1.73m\xB2" },
4935
+ default: {
4936
+ max: 120,
4937
+ maxKind: "display",
4938
+ min: 60,
4939
+ optimalMax: 120,
4940
+ optimalMin: 90,
4941
+ unit: "mL/min/1.73m\xB2"
4942
+ },
5204
4943
  // KDIGO 2024: TFG 60-89 (G2) sem marcador de lesão renal não é DRC, em
5205
4944
  // nenhuma faixa etária. A variante por idade que existia aqui rebaixava o
5206
4945
  // piso só para 60+ e ainda limitava o teto a 90, o que sinalizava como
5207
4946
  // alterado qualquer idoso com função preservada.
4947
+ kind: "decision-threshold",
5208
4948
  source: "kdigo-ckd-2024"
5209
4949
  },
5210
4950
  // `Eosinophils` (%) fica sem faixa de propósito e `Eosinophils_Abs` usa os limites da
5211
4951
  // Tabela 2 da PNS 2019. Ver o comentário em `Neutrophils_Abs`.
5212
4952
  Eosinophils_Abs: {
5213
4953
  default: { max: 0.66, min: 0, unit: "K/uL" },
4954
+ kind: "reference-interval",
5214
4955
  source: "pns-hemograma-2019",
5215
4956
  variants: [
5216
4957
  { ageMin: 18, range: { max: 0.66, min: 0, unit: "K/uL" }, sex: "M" },
@@ -5219,18 +4960,22 @@ var biomarkerRangeDefinitions = {
5219
4960
  },
5220
4961
  Omega3_EPA: {
5221
4962
  default: { max: 3.5, min: 0.5, optimalMax: 2.5, optimalMin: 1, unit: "%" },
4963
+ kind: "decision-threshold",
5222
4964
  source: "harris-omega3-2004"
5223
4965
  },
5224
4966
  EPADPADHA: {
5225
4967
  default: { max: 10, min: 3, optimalMax: 9, optimalMin: 5, unit: "%" },
4968
+ kind: "decision-threshold",
5226
4969
  source: "harris-omega3-2004"
5227
4970
  },
5228
4971
  ESR: {
5229
4972
  default: { max: 20, min: 0, optimalMax: 10, optimalMin: 0, unit: "mm/hr" },
4973
+ kind: "reference-interval",
5230
4974
  source: "tietz-7ed-2015"
5231
4975
  },
5232
4976
  Estradiol: {
5233
4977
  default: { max: 40, min: 10, optimalMax: 35, optimalMin: 15, unit: "pg/mL" },
4978
+ kind: "reference-interval",
5234
4979
  source: "tietz-7ed-2015",
5235
4980
  variants: [
5236
4981
  {
@@ -5253,6 +4998,7 @@ var biomarkerRangeDefinitions = {
5253
4998
  },
5254
4999
  F2Isoprostanes: {
5255
5000
  default: { max: 86, min: 0, optimalMax: 60, optimalMin: 0, unit: "pg/mL" },
5001
+ kind: "reference-interval",
5256
5002
  source: "tietz-7ed-2015"
5257
5003
  },
5258
5004
  // Ferritina — WHO 2020 é o documento de referência específico para avaliação
@@ -5262,6 +5008,7 @@ var biomarkerRangeDefinitions = {
5262
5008
  // tipo de faixa, mas documentado aqui para consumidores).
5263
5009
  Ferritin: {
5264
5010
  default: { max: 150, min: 12, optimalMax: 120, optimalMin: 30, unit: "ng/mL" },
5011
+ kind: "decision-threshold",
5265
5012
  source: "who-iron-2020",
5266
5013
  variants: [
5267
5014
  // Gestação: ferritina cai fisiologicamente no 2º/3º trimestre pela expansão
@@ -5294,14 +5041,17 @@ var biomarkerRangeDefinitions = {
5294
5041
  // Fibrinogênio: 200-400 mg/dL — faixa de referência padrão (método de Clauss)
5295
5042
  Fibrinogen: {
5296
5043
  default: { max: 400, min: 200, optimalMax: 350, optimalMin: 250, unit: "mg/dL" },
5044
+ kind: "reference-interval",
5297
5045
  source: "tietz-7ed-2015"
5298
5046
  },
5299
5047
  FolicAcid: {
5300
5048
  default: { max: 20, min: 3, optimalMax: 15, optimalMin: 5, unit: "ng/mL" },
5049
+ kind: "reference-interval",
5301
5050
  source: "tietz-7ed-2015"
5302
5051
  },
5303
5052
  FSH: {
5304
5053
  default: { max: 12.4, min: 1.5, optimalMax: 10, optimalMin: 3, unit: "mIU/mL" },
5054
+ kind: "reference-interval",
5305
5055
  source: "tietz-7ed-2015",
5306
5056
  variants: [
5307
5057
  {
@@ -5325,26 +5075,29 @@ var biomarkerRangeDefinitions = {
5325
5075
  GGT: {
5326
5076
  default: { max: 55, min: 0, optimalMax: 30, optimalMin: 0, unit: "U/L" },
5327
5077
  direction: "lower-better",
5078
+ kind: "reference-interval",
5328
5079
  source: "tietz-7ed-2015",
5329
5080
  variants: [
5330
5081
  {
5331
5082
  ageMin: 18,
5332
- range: { max: 61, min: 8, optimalMax: 40, optimalMin: 10, unit: "U/L" },
5083
+ range: { max: 61, min: 8, minKind: "display", optimalMax: 40, optimalMin: 10, unit: "U/L" },
5333
5084
  sex: "M"
5334
5085
  },
5335
5086
  {
5336
5087
  ageMin: 18,
5337
- range: { max: 36, min: 5, optimalMax: 25, optimalMin: 8, unit: "U/L" },
5088
+ range: { max: 36, min: 5, minKind: "display", optimalMax: 25, optimalMin: 8, unit: "U/L" },
5338
5089
  sex: "F"
5339
5090
  }
5340
5091
  ]
5341
5092
  },
5342
5093
  Ghrelin: {
5343
5094
  default: { max: 1e3, min: 300, optimalMax: 800, optimalMin: 400, unit: "pg/mL" },
5095
+ kind: "reference-interval",
5344
5096
  source: "tietz-7ed-2015"
5345
5097
  },
5346
5098
  Globulin: {
5347
5099
  default: { max: 3.5, min: 2, optimalMax: 3.2, optimalMin: 2.3, unit: "g/dL" },
5100
+ kind: "reference-interval",
5348
5101
  source: "tietz-7ed-2015"
5349
5102
  },
5350
5103
  // A faixa é de jejum, então mora na definição que afirma jejum. Glicose
@@ -5367,6 +5120,7 @@ var biomarkerRangeDefinitions = {
5367
5120
  optimalMin: 70,
5368
5121
  unit: "mg/dL"
5369
5122
  },
5123
+ kind: "decision-threshold",
5370
5124
  source: "sbd-diabetes-2024",
5371
5125
  variants: [
5372
5126
  // Gestação: DMG (IADPSG/SBD 2024) usa cortes mais restritivos na glicemia
@@ -5388,10 +5142,12 @@ var biomarkerRangeDefinitions = {
5388
5142
  },
5389
5143
  GlycoMark: {
5390
5144
  default: { max: 40, min: 10, optimalMax: 35, optimalMin: 15, unit: "mcg/mL" },
5145
+ kind: "reference-interval",
5391
5146
  source: "tietz-7ed-2015"
5392
5147
  },
5393
5148
  GrowthHormone: {
5394
5149
  default: { max: 5, min: 0, optimalMax: 3, optimalMin: 0, unit: "ng/mL" },
5150
+ kind: "reference-interval",
5395
5151
  source: "tietz-7ed-2015"
5396
5152
  },
5397
5153
  // HbA1c — SBD 2024 não estabelece piso de referência clinicamente acionável.
@@ -5401,11 +5157,13 @@ var biomarkerRangeDefinitions = {
5401
5157
  // criar flag clínico em valores fisiologicamente baixos. optimalMin preserva
5402
5158
  // o alvo fisiológico da fração glicada.
5403
5159
  HbA1c: {
5404
- default: { max: 5.7, min: 2, optimalMax: 5.3, optimalMin: 4.5, unit: "%" },
5160
+ default: { max: 5.7, min: 2, minKind: "display", optimalMax: 5.3, optimalMin: 4.5, unit: "%" },
5161
+ kind: "decision-threshold",
5405
5162
  source: "sbd-diabetes-2024"
5406
5163
  },
5407
5164
  Hct: {
5408
5165
  default: { max: 50, min: 36, optimalMax: 48, optimalMin: 40, unit: "%" },
5166
+ kind: "reference-interval",
5409
5167
  source: "pns-hemograma-2019",
5410
5168
  variants: [
5411
5169
  {
@@ -5427,18 +5185,40 @@ var biomarkerRangeDefinitions = {
5427
5185
  // não como ponto de corte clínico; consumidores devem tratar `direction:
5428
5186
  // higher-better` como sinal autoritativo.
5429
5187
  HDL: {
5430
- default: { max: 100, min: 40, optimalMax: 100, optimalMin: 50, unit: "mg/dL" },
5188
+ default: {
5189
+ max: 100,
5190
+ maxKind: "display",
5191
+ min: 40,
5192
+ optimalMax: 100,
5193
+ optimalMin: 50,
5194
+ unit: "mg/dL"
5195
+ },
5431
5196
  direction: "higher-better",
5197
+ kind: "decision-threshold",
5432
5198
  source: "sbc-lipids-2025",
5433
5199
  variants: [
5434
5200
  {
5435
5201
  ageMin: 18,
5436
- range: { max: 100, min: 40, optimalMax: 100, optimalMin: 45, unit: "mg/dL" },
5202
+ range: {
5203
+ max: 100,
5204
+ maxKind: "display",
5205
+ min: 40,
5206
+ optimalMax: 100,
5207
+ optimalMin: 45,
5208
+ unit: "mg/dL"
5209
+ },
5437
5210
  sex: "M"
5438
5211
  },
5439
5212
  {
5440
5213
  ageMin: 18,
5441
- range: { max: 100, min: 50, optimalMax: 100, optimalMin: 55, unit: "mg/dL" },
5214
+ range: {
5215
+ max: 100,
5216
+ maxKind: "display",
5217
+ min: 50,
5218
+ optimalMax: 100,
5219
+ optimalMin: 55,
5220
+ unit: "mg/dL"
5221
+ },
5442
5222
  sex: "F"
5443
5223
  }
5444
5224
  ]
@@ -5446,12 +5226,21 @@ var biomarkerRangeDefinitions = {
5446
5226
  HDL_Large: {
5447
5227
  // HDL Large: higher is better
5448
5228
  // Quest Ion Mobility reference: Male 4334-10815, Female 5038-17886 nmol/L, optimal >6729
5449
- default: { max: 17886, min: 4334, optimalMax: 17886, optimalMin: 6729, unit: "nmol/L" },
5229
+ default: {
5230
+ max: 17886,
5231
+ maxKind: "display",
5232
+ min: 4334,
5233
+ optimalMax: 17886,
5234
+ optimalMin: 6729,
5235
+ unit: "nmol/L"
5236
+ },
5450
5237
  direction: "higher-better",
5238
+ kind: "reference-interval",
5451
5239
  source: "caulfield-ionmobility-2008"
5452
5240
  },
5453
5241
  Hgb: {
5454
5242
  default: { max: 17.5, min: 12, optimalMax: 16, optimalMin: 13.5, unit: "g/dL" },
5243
+ kind: "reference-interval",
5455
5244
  source: "pns-hemograma-2019",
5456
5245
  variants: [
5457
5246
  // Gestação: hemodiluição fisiológica reduz o piso aceitável.
@@ -5507,19 +5296,23 @@ var biomarkerRangeDefinitions = {
5507
5296
  warningMax: 2.71
5508
5297
  },
5509
5298
  direction: "lower-better",
5299
+ kind: "decision-threshold",
5510
5300
  source: "geloneze-brams-2009"
5511
5301
  },
5512
5302
  Homocysteine: {
5513
- default: { max: 15, min: 4, optimalMax: 10, optimalMin: 5, unit: "umol/L" },
5303
+ default: { max: 15, min: 4, minKind: "display", optimalMax: 10, optimalMin: 5, unit: "umol/L" },
5514
5304
  direction: "lower-better",
5305
+ kind: "reference-interval",
5515
5306
  source: "selhub-homocysteine-1999"
5516
5307
  },
5517
5308
  IGF1: {
5518
5309
  default: { max: 350, min: 100, optimalMax: 300, optimalMin: 150, unit: "ng/mL" },
5310
+ kind: "reference-interval",
5519
5311
  source: "tietz-7ed-2015"
5520
5312
  },
5521
5313
  ImmatureGranulocytes: {
5522
5314
  default: { max: 1, min: 0, optimalMax: 0.5, optimalMin: 0, unit: "%" },
5315
+ kind: "reference-interval",
5523
5316
  source: "tietz-7ed-2015"
5524
5317
  },
5525
5318
  Insulin: {
@@ -5531,10 +5324,12 @@ var biomarkerRangeDefinitions = {
5531
5324
  optimalMin: 3,
5532
5325
  unit: "uIU/mL"
5533
5326
  },
5327
+ kind: "reference-interval",
5534
5328
  source: "tietz-7ed-2015"
5535
5329
  },
5536
5330
  Iron: {
5537
5331
  default: { max: 170, min: 60, optimalMax: 140, optimalMin: 80, unit: "mcg/dL" },
5332
+ kind: "reference-interval",
5538
5333
  source: "sbpc-ml-2021",
5539
5334
  variants: [
5540
5335
  {
@@ -5551,6 +5346,7 @@ var biomarkerRangeDefinitions = {
5551
5346
  },
5552
5347
  Lactate: {
5553
5348
  default: { max: 2.2, min: 0.5, optimalMax: 1.8, optimalMin: 0.7, unit: "mmol/L" },
5349
+ kind: "reference-interval",
5554
5350
  source: "tietz-7ed-2015"
5555
5351
  },
5556
5352
  // DHL/LDH — método IFCC a 37 °C (padrão atual na maioria dos laboratórios
@@ -5562,6 +5358,7 @@ var biomarkerRangeDefinitions = {
5562
5358
  LDH: {
5563
5359
  default: { max: 248, min: 0, optimalMax: 248, optimalMin: 0, unit: "U/L" },
5564
5360
  direction: "lower-better",
5361
+ kind: "reference-interval",
5565
5362
  source: "schumann-ifcc-ldh-2002",
5566
5363
  variants: [
5567
5364
  {
@@ -5579,59 +5376,109 @@ var biomarkerRangeDefinitions = {
5579
5376
  LDL: {
5580
5377
  default: { max: 100, min: 0, optimalMax: 70, optimalMin: 0, unit: "mg/dL" },
5581
5378
  direction: "lower-better",
5379
+ kind: "decision-threshold",
5582
5380
  source: "sbc-lipids-2025"
5583
5381
  },
5584
5382
  LDL_Medium: {
5585
5383
  // LDL Medium (LDL Média): lower is better
5586
5384
  // Quest Ion Mobility reference: Male 167-485, Female 121-397 nmol/L, optimal <215
5587
- default: { max: 485, min: 121, optimalMax: 215, optimalMin: 121, unit: "nmol/L" },
5385
+ default: {
5386
+ max: 485,
5387
+ min: 121,
5388
+ minKind: "display",
5389
+ optimalMax: 215,
5390
+ optimalMin: 121,
5391
+ unit: "nmol/L"
5392
+ },
5588
5393
  direction: "lower-better",
5394
+ kind: "reference-interval",
5589
5395
  source: "caulfield-ionmobility-2008"
5590
5396
  },
5591
5397
  LDL_ParticleNumber: {
5592
5398
  // LDL Particle Number: lower is better
5593
5399
  // Quest Ion Mobility reference: 1016-2185 nmol/L, optimal <1138
5594
- default: { max: 2185, min: 1016, optimalMax: 1138, optimalMin: 1016, unit: "nmol/L" },
5400
+ default: {
5401
+ max: 2185,
5402
+ min: 1016,
5403
+ minKind: "display",
5404
+ optimalMax: 1138,
5405
+ optimalMin: 1016,
5406
+ unit: "nmol/L"
5407
+ },
5595
5408
  direction: "lower-better",
5409
+ kind: "reference-interval",
5596
5410
  source: "caulfield-ionmobility-2008"
5597
5411
  },
5598
5412
  LDL_Peak_Size: {
5599
5413
  // LDL Peak Size: higher is better (larger particles less atherogenic)
5600
5414
  // Quest Ion Mobility reference: optimal >222.9 Å (22.29 nm)
5601
- default: { max: 250, min: 217.4, optimalMax: 250, optimalMin: 222.9, unit: "Angstrom" },
5415
+ default: {
5416
+ max: 250,
5417
+ maxKind: "display",
5418
+ min: 217.4,
5419
+ optimalMax: 250,
5420
+ optimalMin: 222.9,
5421
+ unit: "Angstrom"
5422
+ },
5602
5423
  direction: "higher-better",
5424
+ kind: "reference-interval",
5603
5425
  source: "caulfield-ionmobility-2008"
5604
5426
  },
5605
5427
  LDL_Small: {
5606
5428
  // LDL Small (LDL Pequena): lower is better (small dense LDL is most atherogenic)
5607
5429
  // Quest Ion Mobility reference: Male 123-441, Female 126-382 nmol/L, optimal <142
5608
- default: { max: 441, min: 123, optimalMax: 142, optimalMin: 123, unit: "nmol/L" },
5430
+ default: {
5431
+ max: 441,
5432
+ min: 123,
5433
+ minKind: "display",
5434
+ optimalMax: 142,
5435
+ optimalMin: 123,
5436
+ unit: "nmol/L"
5437
+ },
5609
5438
  direction: "lower-better",
5439
+ kind: "reference-interval",
5610
5440
  source: "caulfield-ionmobility-2008"
5611
5441
  },
5612
5442
  Lead: {
5613
5443
  default: { max: 5, min: 0, optimalMax: 2, optimalMin: 0, unit: "mcg/dL" },
5444
+ kind: "decision-threshold",
5614
5445
  source: "nr7-pcmso-2020"
5615
5446
  },
5616
5447
  Leptin: {
5617
- default: { max: 15, min: 2, optimalMax: 12, optimalMin: 3, unit: "ng/mL" },
5448
+ default: { max: 15, min: 2, minKind: "clinical", optimalMax: 12, optimalMin: 3, unit: "ng/mL" },
5618
5449
  direction: "lower-better",
5450
+ kind: "reference-interval",
5619
5451
  source: "tietz-7ed-2015",
5620
5452
  variants: [
5621
5453
  {
5622
5454
  ageMin: 18,
5623
- range: { max: 10, min: 1, optimalMax: 8, optimalMin: 2, unit: "ng/mL" },
5455
+ range: {
5456
+ max: 10,
5457
+ min: 1,
5458
+ minKind: "clinical",
5459
+ optimalMax: 8,
5460
+ optimalMin: 2,
5461
+ unit: "ng/mL"
5462
+ },
5624
5463
  sex: "M"
5625
5464
  },
5626
5465
  {
5627
5466
  ageMin: 18,
5628
- range: { max: 25, min: 4, optimalMax: 18, optimalMin: 5, unit: "ng/mL" },
5467
+ range: {
5468
+ max: 25,
5469
+ min: 4,
5470
+ minKind: "clinical",
5471
+ optimalMax: 18,
5472
+ optimalMin: 5,
5473
+ unit: "ng/mL"
5474
+ },
5629
5475
  sex: "F"
5630
5476
  }
5631
5477
  ]
5632
5478
  },
5633
5479
  LH: {
5634
5480
  default: { max: 8.6, min: 1.7, optimalMax: 8, optimalMin: 2, unit: "mIU/mL" },
5481
+ kind: "reference-interval",
5635
5482
  source: "tietz-7ed-2015",
5636
5483
  variants: [
5637
5484
  {
@@ -5654,6 +5501,7 @@ var biomarkerRangeDefinitions = {
5654
5501
  },
5655
5502
  Omega6_LA: {
5656
5503
  default: { max: 35, min: 15, optimalMax: 28, optimalMin: 18, unit: "%" },
5504
+ kind: "decision-threshold",
5657
5505
  source: "simopoulos-omega-ratio-2002"
5658
5506
  },
5659
5507
  // Lp(a) — SBC 2025 (seção 3.1.1.2.7): níveis ≥ 50 mg/dL ou ≥ 125 nmol/L são
@@ -5665,12 +5513,14 @@ var biomarkerRangeDefinitions = {
5665
5513
  Lipoprotein_a: {
5666
5514
  default: { max: 125, min: 0, optimalMax: 75, optimalMin: 0, unit: "nmol/L" },
5667
5515
  direction: "lower-better",
5516
+ kind: "decision-threshold",
5668
5517
  source: "sbc-lipids-2025"
5669
5518
  },
5670
5519
  // `Lymphocytes` (%) fica sem faixa de propósito e `Lymphocytes_Abs` usa os limites da
5671
5520
  // Tabela 2 da PNS 2019. Ver o comentário em `Neutrophils_Abs`.
5672
5521
  Lymphocytes_Abs: {
5673
5522
  default: { max: 3.414, min: 0.72, unit: "K/uL" },
5523
+ kind: "reference-interval",
5674
5524
  source: "pns-hemograma-2019",
5675
5525
  variants: [
5676
5526
  { ageMin: 18, range: { max: 3.37, min: 0.72, unit: "K/uL" }, sex: "M" },
@@ -5679,40 +5529,49 @@ var biomarkerRangeDefinitions = {
5679
5529
  },
5680
5530
  Magnesium: {
5681
5531
  default: { max: 2.2, min: 1.7, optimalMax: 2.1, optimalMin: 1.9, unit: "mg/dL" },
5532
+ kind: "reference-interval",
5682
5533
  source: "tietz-7ed-2015"
5683
5534
  },
5684
5535
  Magnesium_RBC: {
5685
5536
  default: { max: 6.8, min: 4, optimalMax: 6, optimalMin: 4.5, unit: "mg/dL" },
5537
+ kind: "reference-interval",
5686
5538
  source: "tietz-7ed-2015"
5687
5539
  },
5688
5540
  MCH: {
5689
5541
  default: { max: 33, min: 27, optimalMax: 32, optimalMin: 28, unit: "pg" },
5542
+ kind: "reference-interval",
5690
5543
  source: "pns-hemograma-2019"
5691
5544
  },
5692
5545
  MCHC: {
5693
5546
  default: { max: 36, min: 32, optimalMax: 35, optimalMin: 33, unit: "g/dL" },
5547
+ kind: "reference-interval",
5694
5548
  source: "pns-hemograma-2019"
5695
5549
  },
5696
5550
  MCV: {
5697
5551
  default: { max: 100, min: 80, optimalMax: 98, optimalMin: 82, unit: "fL" },
5552
+ kind: "reference-interval",
5698
5553
  source: "pns-hemograma-2019"
5699
5554
  },
5700
5555
  Mercury: {
5701
5556
  default: { max: 10, min: 0, optimalMax: 5, optimalMin: 0, unit: "mcg/L" },
5557
+ kind: "decision-threshold",
5702
5558
  source: "nr7-pcmso-2020"
5703
5559
  },
5704
5560
  Microalbumin: {
5705
5561
  default: { max: 30, min: 0, optimalMax: 20, optimalMin: 0, unit: "mg/L" },
5562
+ kind: "decision-threshold",
5706
5563
  source: "kdigo-ckd-2024"
5707
5564
  },
5708
5565
  MMA: {
5709
5566
  default: { max: 378, min: 0, optimalMax: 270, optimalMin: 0, unit: "nmol/L" },
5567
+ kind: "reference-interval",
5710
5568
  source: "tietz-7ed-2015"
5711
5569
  },
5712
5570
  // `Monocytes` (%) fica sem faixa de propósito e `Monocytes_Abs` usa os limites da
5713
5571
  // Tabela 2 da PNS 2019. Ver o comentário em `Neutrophils_Abs`.
5714
5572
  Monocytes_Abs: {
5715
5573
  default: { max: 0.812, min: 0.011, unit: "K/uL" },
5574
+ kind: "reference-interval",
5716
5575
  source: "pns-hemograma-2019",
5717
5576
  variants: [
5718
5577
  { ageMin: 18, range: { max: 0.812, min: 0.011, unit: "K/uL" }, sex: "M" },
@@ -5721,11 +5580,13 @@ var biomarkerRangeDefinitions = {
5721
5580
  },
5722
5581
  MPV: {
5723
5582
  default: { max: 11.5, min: 7.5, optimalMax: 10.5, optimalMin: 8, unit: "fL" },
5583
+ kind: "reference-interval",
5724
5584
  source: "pns-hemograma-2019"
5725
5585
  },
5726
5586
  // MPO: Meuwese 2007 (EPIC-Norfolk) — risco CV elevado >322 pmol/L
5727
5587
  Myeloperoxidase: {
5728
5588
  default: { max: 470, min: 0, optimalMax: 322, optimalMin: 0, unit: "pmol/L" },
5589
+ kind: "decision-threshold",
5729
5590
  source: "meuwese-mpo-2007"
5730
5591
  },
5731
5592
  // Leucograma diferencial em percentual (`Neutrophils`, `Lymphocytes`,
@@ -5749,6 +5610,7 @@ var biomarkerRangeDefinitions = {
5749
5610
  // (menor mínimo e maior máximo entre homens e mulheres).
5750
5611
  Neutrophils_Abs: {
5751
5612
  default: { max: 6.474, min: 0.576, unit: "K/uL" },
5613
+ kind: "reference-interval",
5752
5614
  source: "pns-hemograma-2019",
5753
5615
  variants: [
5754
5616
  { ageMin: 18, range: { max: 5.971, min: 0.576, unit: "K/uL" }, sex: "M" },
@@ -5758,22 +5620,27 @@ var biomarkerRangeDefinitions = {
5758
5620
  NonHDL_Cholesterol: {
5759
5621
  default: { max: 130, min: 0, optimalMax: 100, optimalMin: 0, unit: "mg/dL" },
5760
5622
  direction: "lower-better",
5623
+ kind: "decision-threshold",
5761
5624
  source: "sbc-lipids-2025"
5762
5625
  },
5763
5626
  NRBC: {
5764
5627
  default: { max: 0, min: 0, optimalMax: 0, optimalMin: 0, unit: "/100WBC" },
5628
+ kind: "reference-interval",
5765
5629
  source: "tietz-7ed-2015"
5766
5630
  },
5767
5631
  NTproBNP: {
5768
5632
  default: { max: 125, min: 0, optimalMax: 75, optimalMin: 0, unit: "pg/mL" },
5633
+ kind: "decision-threshold",
5769
5634
  source: "sbc-ic-2018"
5770
5635
  },
5771
5636
  Oleic_Acid: {
5772
5637
  default: { max: 25, min: 15, optimalMax: 22, optimalMin: 18, unit: "%" },
5638
+ kind: "reference-interval",
5773
5639
  source: "tietz-7ed-2015"
5774
5640
  },
5775
5641
  Omega3_Index: {
5776
5642
  default: { max: 8, min: 4, optimalMax: 8, optimalMin: 5.5, unit: "%" },
5643
+ kind: "decision-threshold",
5777
5644
  source: "harris-omega3-2004"
5778
5645
  },
5779
5646
  // Ômega-3 Total (% de ácidos graxos eritrocitários) — Harris & von Schacky
@@ -5783,39 +5650,55 @@ var biomarkerRangeDefinitions = {
5783
5650
  // a literatura e o próprio texto educacional do `platform`.
5784
5651
  Omega3_Total: {
5785
5652
  default: { max: 12, min: 3, optimalMax: 10, optimalMin: 8, unit: "%" },
5653
+ kind: "decision-threshold",
5786
5654
  source: "harris-omega3-2004"
5787
5655
  },
5788
5656
  Omega6_Total: {
5789
5657
  default: { max: 40, min: 20, optimalMax: 35, optimalMin: 25, unit: "%" },
5658
+ kind: "decision-threshold",
5790
5659
  source: "simopoulos-omega-ratio-2002"
5791
5660
  },
5792
5661
  Omega6_Omega3_Ratio: {
5793
- default: { max: 10, min: 1, optimalMax: 4, optimalMin: 1, unit: "" },
5662
+ default: {
5663
+ max: 10,
5664
+ min: 1,
5665
+ minKind: "display",
5666
+ optimalMax: 4,
5667
+ optimalMin: 1,
5668
+ unit: ""
5669
+ },
5794
5670
  direction: "lower-better",
5671
+ kind: "decision-threshold",
5795
5672
  source: "simopoulos-omega-ratio-2002"
5796
5673
  },
5797
5674
  OmegaCheck: {
5798
5675
  default: { max: 8, min: 4, optimalMax: 8, optimalMin: 5.5, unit: "%" },
5676
+ kind: "decision-threshold",
5799
5677
  source: "harris-omega3-2004"
5800
5678
  },
5801
5679
  Palmitic_Acid: {
5802
5680
  default: { max: 30, min: 20, optimalMax: 27, optimalMin: 22, unit: "%" },
5681
+ kind: "reference-interval",
5803
5682
  source: "tietz-7ed-2015"
5804
5683
  },
5805
5684
  Phosphorus: {
5806
5685
  default: { max: 4.5, min: 2.5, optimalMax: 4, optimalMin: 3, unit: "mg/dL" },
5686
+ kind: "reference-interval",
5807
5687
  source: "tietz-7ed-2015"
5808
5688
  },
5809
5689
  Platelets: {
5810
5690
  default: { max: 400, min: 150, optimalMax: 350, optimalMin: 180, unit: "K/uL" },
5691
+ kind: "reference-interval",
5811
5692
  source: "pns-hemograma-2019"
5812
5693
  },
5813
5694
  Potassium: {
5814
5695
  default: { max: 5, min: 3.5, optimalMax: 4.6, optimalMin: 3.8, unit: "mEq/L" },
5696
+ kind: "reference-interval",
5815
5697
  source: "tietz-7ed-2015"
5816
5698
  },
5817
5699
  Prealbumin: {
5818
5700
  default: { max: 38, min: 18, optimalMax: 35, optimalMin: 20, unit: "mg/dL" },
5701
+ kind: "reference-interval",
5819
5702
  source: "tietz-7ed-2015"
5820
5703
  },
5821
5704
  // Progesterona — varia drasticamente com a fase do ciclo menstrual.
@@ -5826,6 +5709,7 @@ var biomarkerRangeDefinitions = {
5826
5709
  // ciclo, indisponível neste contexto.
5827
5710
  Progesterone: {
5828
5711
  default: { max: 0.9, min: 0.1, optimalMax: 0.7, optimalMin: 0.2, unit: "ng/mL" },
5712
+ kind: "reference-interval",
5829
5713
  source: "tietz-7ed-2015",
5830
5714
  variants: [
5831
5715
  {
@@ -5852,6 +5736,7 @@ var biomarkerRangeDefinitions = {
5852
5736
  },
5853
5737
  Prolactin: {
5854
5738
  default: { max: 18, min: 2, optimalMax: 15, optimalMin: 4, unit: "ng/mL" },
5739
+ kind: "reference-interval",
5855
5740
  source: "tietz-7ed-2015",
5856
5741
  variants: [
5857
5742
  {
@@ -5868,6 +5753,7 @@ var biomarkerRangeDefinitions = {
5868
5753
  },
5869
5754
  PSA: {
5870
5755
  default: { max: 4, min: 0, optimalMax: 2.5, optimalMin: 0, unit: "ng/mL" },
5756
+ kind: "reference-interval",
5871
5757
  source: "sturgeon-nacb-2008",
5872
5758
  variants: [
5873
5759
  {
@@ -5897,6 +5783,7 @@ var biomarkerRangeDefinitions = {
5897
5783
  },
5898
5784
  RBC: {
5899
5785
  default: { max: 5.5, min: 4, optimalMax: 5.2, optimalMin: 4.3, unit: "M/uL" },
5786
+ kind: "reference-interval",
5900
5787
  source: "pns-hemograma-2019",
5901
5788
  variants: [
5902
5789
  {
@@ -5913,22 +5800,27 @@ var biomarkerRangeDefinitions = {
5913
5800
  },
5914
5801
  RDW: {
5915
5802
  default: { max: 14.5, min: 11.5, optimalMax: 14, optimalMin: 12, unit: "%" },
5803
+ kind: "reference-interval",
5916
5804
  source: "pns-hemograma-2019"
5917
5805
  },
5918
5806
  Reticulocytes: {
5919
5807
  default: { max: 2.5, min: 0.5, optimalMax: 2, optimalMin: 0.8, unit: "%" },
5808
+ kind: "reference-interval",
5920
5809
  source: "pns-hemograma-2019"
5921
5810
  },
5922
5811
  SDMA: {
5923
5812
  default: { max: 0.6, min: 0.3, optimalMax: 0.5, optimalMin: 0.3, unit: "umol/L" },
5813
+ kind: "reference-interval",
5924
5814
  source: "schwedhelm-sdma-2011"
5925
5815
  },
5926
5816
  Selenium: {
5927
5817
  default: { max: 150, min: 70, optimalMax: 125, optimalMin: 85, unit: "\xB5g/L" },
5818
+ kind: "reference-interval",
5928
5819
  source: "tietz-7ed-2015"
5929
5820
  },
5930
5821
  SHBG: {
5931
5822
  default: { max: 54, min: 18, optimalMax: 50, optimalMin: 20, unit: "nmol/L" },
5823
+ kind: "reference-interval",
5932
5824
  source: "tietz-7ed-2015",
5933
5825
  variants: [
5934
5826
  {
@@ -5945,34 +5837,42 @@ var biomarkerRangeDefinitions = {
5945
5837
  },
5946
5838
  Sodium: {
5947
5839
  default: { max: 145, min: 136, optimalMax: 143, optimalMin: 138, unit: "mEq/L" },
5840
+ kind: "reference-interval",
5948
5841
  source: "tietz-7ed-2015"
5949
5842
  },
5950
5843
  SpecificGravity_Urine: {
5951
5844
  default: { max: 1.03, min: 1.005, optimalMax: 1.025, optimalMin: 1.01, unit: "SG" },
5845
+ kind: "reference-interval",
5952
5846
  source: "tietz-7ed-2015"
5953
5847
  },
5954
5848
  Stearic_Acid: {
5955
5849
  default: { max: 14, min: 8, optimalMax: 12, optimalMin: 10, unit: "%" },
5850
+ kind: "reference-interval",
5956
5851
  source: "tietz-7ed-2015"
5957
5852
  },
5958
5853
  T3Free: {
5959
5854
  default: { max: 4.2, min: 2.3, optimalMax: 3.8, optimalMin: 2.8, unit: "pg/mL" },
5855
+ kind: "reference-interval",
5960
5856
  source: "tietz-7ed-2015"
5961
5857
  },
5962
5858
  T3Reverse: {
5963
5859
  default: { max: 24, min: 10, optimalMax: 20, optimalMin: 12, unit: "ng/dL" },
5860
+ kind: "reference-interval",
5964
5861
  source: "tietz-7ed-2015"
5965
5862
  },
5966
5863
  T4Free: {
5967
5864
  default: { max: 1.8, min: 0.8, optimalMax: 1.5, optimalMin: 1, unit: "ng/dL" },
5865
+ kind: "reference-interval",
5968
5866
  source: "tietz-7ed-2015"
5969
5867
  },
5970
5868
  T4Total: {
5971
5869
  default: { max: 12, min: 4.5, optimalMax: 10, optimalMin: 6, unit: "ug/dL" },
5870
+ kind: "reference-interval",
5972
5871
  source: "tietz-7ed-2015"
5973
5872
  },
5974
5873
  Testosterone: {
5975
5874
  default: { max: 1e3, min: 300, optimalMax: 800, optimalMin: 500, unit: "ng/dL" },
5875
+ kind: "reference-interval",
5976
5876
  source: "tietz-7ed-2015",
5977
5877
  variants: [
5978
5878
  {
@@ -5995,12 +5895,14 @@ var biomarkerRangeDefinitions = {
5995
5895
  },
5996
5896
  TestosteroneBioavailable: {
5997
5897
  default: { max: 200, min: 50, optimalMax: 150, optimalMin: 80, unit: "ng/dL" },
5898
+ kind: "reference-interval",
5998
5899
  source: "tietz-7ed-2015"
5999
5900
  },
6000
5901
  // TestosteroneFree - using direct immunoassay ranges (Quest/LabCorp standard)
6001
5902
  // Previous values (9.3-26.5 pg/mL) were based on equilibrium dialysis method
6002
5903
  TestosteroneFree: {
6003
5904
  default: { max: 155, min: 35, optimalMax: 120, optimalMin: 50, unit: "pg/mL" },
5905
+ kind: "reference-interval",
6004
5906
  source: "tietz-7ed-2015",
6005
5907
  variants: [
6006
5908
  {
@@ -6023,22 +5925,27 @@ var biomarkerRangeDefinitions = {
6023
5925
  },
6024
5926
  TIBC: {
6025
5927
  default: { max: 370, min: 250, optimalMax: 350, optimalMin: 280, unit: "mcg/dL" },
5928
+ kind: "reference-interval",
6026
5929
  source: "tietz-7ed-2015"
6027
5930
  },
6028
5931
  TotalProtein: {
6029
5932
  default: { max: 8.3, min: 6, optimalMax: 7.8, optimalMin: 6.5, unit: "g/dL" },
5933
+ kind: "reference-interval",
6030
5934
  source: "tietz-7ed-2015"
6031
5935
  },
6032
5936
  Trans_Fat_Index: {
6033
5937
  default: { max: 1, min: 0, optimalMax: 0.5, optimalMin: 0, unit: "%" },
5938
+ kind: "reference-interval",
6034
5939
  source: "tietz-7ed-2015"
6035
5940
  },
6036
5941
  Transferrin: {
6037
5942
  default: { max: 360, min: 200, optimalMax: 340, optimalMin: 220, unit: "mg/dL" },
5943
+ kind: "reference-interval",
6038
5944
  source: "tietz-7ed-2015"
6039
5945
  },
6040
5946
  TransferrinSaturation: {
6041
5947
  default: { max: 50, min: 20, optimalMax: 45, optimalMin: 25, unit: "%" },
5948
+ kind: "reference-interval",
6042
5949
  source: "tietz-7ed-2015",
6043
5950
  variants: [
6044
5951
  {
@@ -6066,16 +5973,19 @@ var biomarkerRangeDefinitions = {
6066
5973
  unit: "mg/dL"
6067
5974
  },
6068
5975
  direction: "lower-better",
5976
+ kind: "decision-threshold",
6069
5977
  source: "sbc-lipids-2025"
6070
5978
  },
6071
5979
  // TroponinI: 0.04 ng/mL = percentil 99 (ensaio Siemens TnI-Ultra)
6072
5980
  TroponinI: {
6073
5981
  default: { max: 0.04, min: 0, optimalMax: 0.02, optimalMin: 0, unit: "ng/mL" },
5982
+ kind: "reference-interval",
6074
5983
  source: "keller-tni-2013"
6075
5984
  },
6076
5985
  // TroponinT: 14 ng/L = percentil 99 hs-cTnT (ensaio Roche Elecsys 5ª geração)
6077
5986
  TroponinT: {
6078
5987
  default: { max: 14, min: 0, optimalMax: 10, optimalMin: 0, unit: "ng/L" },
5988
+ kind: "reference-interval",
6079
5989
  source: "giannitsis-hstnt-2010"
6080
5990
  },
6081
5991
  // TSH — 0.4–4.0 é a faixa de referência adulta geral. O alvo 2.5 µIU/mL é
@@ -6085,6 +5995,7 @@ var biomarkerRangeDefinitions = {
6085
5995
  // expandido (tolerância fisiológica do eixo em idosos, SBEM 2013).
6086
5996
  TSH: {
6087
5997
  default: { max: 4, min: 0.4, optimalMax: 3, optimalMin: 1, unit: "uIU/mL" },
5998
+ kind: "reference-interval",
6088
5999
  source: "sbem-thyroid-2013",
6089
6000
  variants: [
6090
6001
  // Variantes gestacionais (ATA 2017 / SBEM): supressão fisiológica por hCG
@@ -6133,10 +6044,12 @@ var biomarkerRangeDefinitions = {
6133
6044
  // deve ser revisto em biomarkers.ts — ver issue #41.
6134
6045
  Urea: {
6135
6046
  default: { max: 50, min: 15, optimalMax: 40, optimalMin: 20, unit: "mg/dL" },
6047
+ kind: "reference-interval",
6136
6048
  source: "tietz-7ed-2015"
6137
6049
  },
6138
6050
  UricAcid: {
6139
6051
  default: { max: 7, min: 2.5, optimalMax: 5.5, optimalMin: 3, unit: "mg/dL" },
6052
+ kind: "reference-interval",
6140
6053
  source: "tietz-7ed-2015",
6141
6054
  variants: [
6142
6055
  {
@@ -6153,50 +6066,62 @@ var biomarkerRangeDefinitions = {
6153
6066
  },
6154
6067
  Urobilinogen_Urine: {
6155
6068
  default: { max: 1, min: 0.1, optimalMax: 1, optimalMin: 0.1, unit: "mg/dL" },
6069
+ kind: "reference-interval",
6156
6070
  source: "tietz-7ed-2015"
6157
6071
  },
6158
6072
  pH_Urine: {
6159
6073
  default: { max: 8, min: 4.5, optimalMax: 7, optimalMin: 5.5, unit: "pH" },
6074
+ kind: "reference-interval",
6160
6075
  source: "tietz-7ed-2015"
6161
6076
  },
6162
6077
  HyalineCasts_Urine: {
6163
6078
  default: { max: 2, min: 0, optimalMax: 1, optimalMin: 0, unit: "/LPF" },
6079
+ kind: "reference-interval",
6164
6080
  source: "tietz-7ed-2015"
6165
6081
  },
6166
6082
  RBC_Urine: {
6167
6083
  default: { max: 3, min: 0, optimalMax: 1, optimalMin: 0, unit: "/HPF" },
6084
+ kind: "reference-interval",
6168
6085
  source: "tietz-7ed-2015"
6169
6086
  },
6170
6087
  SquamousEpithelial_Urine: {
6171
6088
  default: { max: 15, min: 0, optimalMax: 5, optimalMin: 0, unit: "/HPF" },
6089
+ kind: "reference-interval",
6172
6090
  source: "tietz-7ed-2015"
6173
6091
  },
6174
6092
  Leukocytes_Urine: {
6175
6093
  default: { max: 5, min: 0, optimalMax: 2, optimalMin: 0, unit: "/HPF" },
6094
+ kind: "reference-interval",
6176
6095
  source: "tietz-7ed-2015"
6177
6096
  },
6178
6097
  Folate: {
6179
6098
  default: { max: 20, min: 3.9, optimalMax: 17, optimalMin: 5, unit: "ng/mL" },
6099
+ kind: "reference-interval",
6180
6100
  source: "tietz-7ed-2015"
6181
6101
  },
6182
6102
  VitaminA: {
6183
6103
  default: { max: 100, min: 20, optimalMax: 80, optimalMin: 30, unit: "mcg/dL" },
6104
+ kind: "reference-interval",
6184
6105
  source: "tietz-7ed-2015"
6185
6106
  },
6186
6107
  VitaminB1: {
6187
6108
  default: { max: 180, min: 70, optimalMax: 150, optimalMin: 80, unit: "nmol/L" },
6109
+ kind: "reference-interval",
6188
6110
  source: "tietz-7ed-2015"
6189
6111
  },
6190
6112
  VitaminB12: {
6191
6113
  default: { max: 900, min: 200, optimalMax: 800, optimalMin: 400, unit: "pg/mL" },
6114
+ kind: "reference-interval",
6192
6115
  source: "tietz-7ed-2015"
6193
6116
  },
6194
6117
  VitaminB6: {
6195
6118
  default: { max: 50, min: 5, optimalMax: 40, optimalMin: 10, unit: "ng/mL" },
6119
+ kind: "reference-interval",
6196
6120
  source: "tietz-7ed-2015"
6197
6121
  },
6198
6122
  VitaminC: {
6199
6123
  default: { max: 2, min: 0.4, optimalMax: 1.5, optimalMin: 0.6, unit: "mg/dL" },
6124
+ kind: "reference-interval",
6200
6125
  source: "tietz-7ed-2015"
6201
6126
  },
6202
6127
  // Vitamina D — posicionamento conjunto SBEM/SBPC-ML 2017:
@@ -6213,6 +6138,7 @@ var biomarkerRangeDefinitions = {
6213
6138
  // quando a modelagem de comorbidades chegar ao schema.
6214
6139
  VitaminD: {
6215
6140
  default: { max: 100, min: 20, optimalMax: 70, optimalMin: 40, unit: "ng/mL" },
6141
+ kind: "decision-threshold",
6216
6142
  source: "ferreira-vitd-2017",
6217
6143
  variants: [
6218
6144
  {
@@ -6224,10 +6150,12 @@ var biomarkerRangeDefinitions = {
6224
6150
  },
6225
6151
  VitaminD_1_25: {
6226
6152
  default: { max: 72, min: 18, optimalMax: 60, optimalMin: 25, unit: "pg/mL" },
6153
+ kind: "reference-interval",
6227
6154
  source: "tietz-7ed-2015"
6228
6155
  },
6229
6156
  VitaminE: {
6230
6157
  default: { max: 17, min: 5.5, optimalMax: 14, optimalMin: 7, unit: "mg/L" },
6158
+ kind: "reference-interval",
6231
6159
  source: "tietz-7ed-2015"
6232
6160
  },
6233
6161
  // VLDL — estimado clinicamente via TG/5 (método Friedewald). O corte de
@@ -6236,30 +6164,34 @@ var biomarkerRangeDefinitions = {
6236
6164
  // método de cálculo, mas a faixa de referência tem respaldo na diretriz
6237
6165
  // SBC 2025.
6238
6166
  VLDL: {
6239
- default: { max: 30, min: 2, optimalMax: 20, optimalMin: 5, unit: "mg/dL" },
6167
+ default: { max: 30, min: 2, minKind: "display", optimalMax: 20, optimalMin: 5, unit: "mg/dL" },
6168
+ kind: "decision-threshold",
6240
6169
  source: "sbc-lipids-2025"
6241
6170
  },
6242
6171
  WBC: {
6243
6172
  default: { max: 11, min: 4, optimalMax: 8, optimalMin: 5, unit: "K/uL" },
6173
+ kind: "reference-interval",
6244
6174
  source: "pns-hemograma-2019"
6245
6175
  },
6246
6176
  Zinc: {
6247
6177
  default: { max: 120, min: 60, optimalMax: 100, optimalMin: 70, unit: "mcg/dL" },
6178
+ kind: "reference-interval",
6248
6179
  source: "tietz-7ed-2015"
6249
6180
  },
6250
6181
  AndroidFatPct: {
6251
- default: { max: 35, min: 10, optimalMax: 25, optimalMin: 15, unit: "%" },
6182
+ default: { max: 35, min: 10, minKind: "display", optimalMax: 25, optimalMin: 15, unit: "%" },
6252
6183
  direction: "lower-better",
6184
+ kind: "population",
6253
6185
  source: "kelly-dxa-2009",
6254
6186
  variants: [
6255
6187
  {
6256
6188
  ageMin: 18,
6257
- range: { max: 30, min: 10, optimalMax: 22, optimalMin: 12, unit: "%" },
6189
+ range: { max: 30, min: 10, minKind: "display", optimalMax: 22, optimalMin: 12, unit: "%" },
6258
6190
  sex: "M"
6259
6191
  },
6260
6192
  {
6261
6193
  ageMin: 18,
6262
- range: { max: 42, min: 15, optimalMax: 32, optimalMin: 20, unit: "%" },
6194
+ range: { max: 42, min: 15, minKind: "display", optimalMax: 32, optimalMin: 20, unit: "%" },
6263
6195
  sex: "F"
6264
6196
  }
6265
6197
  ]
@@ -6310,6 +6242,7 @@ var biomarkerRangeDefinitions = {
6310
6242
  CalfCircumference: {
6311
6243
  default: { unit: "cm" },
6312
6244
  direction: "higher-better",
6245
+ kind: "decision-threshold",
6313
6246
  source: "ewgsop2-2019",
6314
6247
  // O corte não é específico por sexo na fonte; as duas variantes existem
6315
6248
  // porque `RangeVariant` exige o campo, e carregam o mesmo número.
@@ -6321,38 +6254,76 @@ var biomarkerRangeDefinitions = {
6321
6254
  WaistToHeightRatio: {
6322
6255
  default: { max: 0.5, unit: "" },
6323
6256
  direction: "lower-better",
6257
+ kind: "decision-threshold",
6324
6258
  source: "browning-ashwell-2010"
6325
6259
  },
6326
6260
  AndroidGynoidRatio: {
6327
- default: { max: 1.2, min: 0.5, optimalMax: 1, optimalMin: 0.6, unit: "" },
6261
+ default: { max: 1.2, min: 0.5, minKind: "display", optimalMax: 1, optimalMin: 0.6, unit: "" },
6328
6262
  direction: "lower-better",
6263
+ kind: "population",
6329
6264
  source: "kelly-dxa-2009",
6330
6265
  variants: [
6331
6266
  {
6332
6267
  ageMin: 18,
6333
- range: { max: 1.2, min: 0.6, optimalMax: 1, optimalMin: 0.7, unit: "" },
6268
+ range: {
6269
+ max: 1.2,
6270
+ min: 0.6,
6271
+ minKind: "display",
6272
+ optimalMax: 1,
6273
+ optimalMin: 0.7,
6274
+ unit: ""
6275
+ },
6334
6276
  sex: "M"
6335
6277
  },
6336
6278
  {
6337
6279
  ageMin: 18,
6338
- range: { max: 1, min: 0.4, optimalMax: 0.8, optimalMin: 0.5, unit: "" },
6280
+ range: {
6281
+ max: 1,
6282
+ min: 0.4,
6283
+ minKind: "display",
6284
+ optimalMax: 0.8,
6285
+ optimalMin: 0.5,
6286
+ unit: ""
6287
+ },
6339
6288
  sex: "F"
6340
6289
  }
6341
6290
  ]
6342
6291
  },
6343
6292
  BMC: {
6344
- default: { max: 3.5, min: 2, optimalMax: 3.2, optimalMin: 2.3, unit: "kg" },
6293
+ default: {
6294
+ max: 3.5,
6295
+ maxKind: "display",
6296
+ min: 2,
6297
+ optimalMax: 3.2,
6298
+ optimalMin: 2.3,
6299
+ unit: "kg"
6300
+ },
6345
6301
  direction: "higher-better",
6302
+ kind: "population",
6346
6303
  source: "kelly-dxa-2009",
6347
6304
  variants: [
6348
6305
  {
6349
6306
  ageMin: 18,
6350
- range: { max: 4, min: 2.5, optimalMax: 3.5, optimalMin: 2.8, unit: "kg" },
6307
+ range: {
6308
+ max: 4,
6309
+ maxKind: "display",
6310
+ min: 2.5,
6311
+ optimalMax: 3.5,
6312
+ optimalMin: 2.8,
6313
+ unit: "kg"
6314
+ },
6351
6315
  sex: "M"
6352
6316
  },
6353
6317
  {
6354
6318
  ageMin: 18,
6355
- range: { max: 3, min: 1.8, optimalMax: 2.7, optimalMin: 2, unit: "kg" },
6319
+ range: {
6320
+ max: 3,
6321
+ maxKind: "display",
6322
+ min: 1.8,
6323
+ optimalMax: 2.7,
6324
+ optimalMin: 2,
6325
+ unit: "kg"
6326
+ },
6356
6327
  sex: "F"
6357
6328
  }
6358
6329
  ]
@@ -6360,142 +6331,148 @@ var biomarkerRangeDefinitions = {
6360
6331
  // BodyFatPct — Age-bracketed ranges from Gallagher et al. Am J Clin Nutr 2000;72:694-701
6361
6332
  // (PMID: 10966886) and ACSM Guidelines for Exercise Testing, 11th Ed (2021)
6362
6333
  BodyFatPct: {
6363
- default: { max: 30, min: 10, optimalMax: 25, optimalMin: 15, unit: "%" },
6334
+ default: { max: 30, min: 10, minKind: "clinical", optimalMax: 25, optimalMin: 15, unit: "%" },
6364
6335
  direction: "lower-better",
6336
+ kind: "decision-threshold",
6365
6337
  source: "gallagher-bodyfat-2000",
6366
6338
  variants: [
6367
6339
  // Men — age brackets
6368
6340
  {
6369
6341
  ageMax: 25,
6370
6342
  ageMin: 18,
6371
- range: { max: 25, min: 5, optimalMax: 20, optimalMin: 10, unit: "%" },
6343
+ range: { max: 25, min: 5, minKind: "clinical", optimalMax: 20, optimalMin: 10, unit: "%" },
6372
6344
  sex: "M"
6373
6345
  },
6374
6346
  {
6375
6347
  ageMax: 35,
6376
6348
  ageMin: 26,
6377
- range: { max: 26, min: 5, optimalMax: 21, optimalMin: 11, unit: "%" },
6349
+ range: { max: 26, min: 5, minKind: "clinical", optimalMax: 21, optimalMin: 11, unit: "%" },
6378
6350
  sex: "M"
6379
6351
  },
6380
6352
  {
6381
6353
  ageMax: 45,
6382
6354
  ageMin: 36,
6383
- range: { max: 27, min: 5, optimalMax: 22, optimalMin: 12, unit: "%" },
6355
+ range: { max: 27, min: 5, minKind: "clinical", optimalMax: 22, optimalMin: 12, unit: "%" },
6384
6356
  sex: "M"
6385
6357
  },
6386
6358
  {
6387
6359
  ageMax: 55,
6388
6360
  ageMin: 46,
6389
- range: { max: 28, min: 5, optimalMax: 23, optimalMin: 13, unit: "%" },
6361
+ range: { max: 28, min: 5, minKind: "clinical", optimalMax: 23, optimalMin: 13, unit: "%" },
6390
6362
  sex: "M"
6391
6363
  },
6392
6364
  {
6393
6365
  ageMin: 56,
6394
- range: { max: 29, min: 5, optimalMax: 24, optimalMin: 14, unit: "%" },
6366
+ range: { max: 29, min: 5, minKind: "clinical", optimalMax: 24, optimalMin: 14, unit: "%" },
6395
6367
  sex: "M"
6396
6368
  },
6397
6369
  // Women — age brackets
6398
6370
  {
6399
6371
  ageMax: 25,
6400
6372
  ageMin: 18,
6401
- range: { max: 32, min: 13, optimalMax: 28, optimalMin: 18, unit: "%" },
6373
+ range: { max: 32, min: 13, minKind: "clinical", optimalMax: 28, optimalMin: 18, unit: "%" },
6402
6374
  sex: "F"
6403
6375
  },
6404
6376
  {
6405
6377
  ageMax: 35,
6406
6378
  ageMin: 26,
6407
- range: { max: 33, min: 13, optimalMax: 29, optimalMin: 18, unit: "%" },
6379
+ range: { max: 33, min: 13, minKind: "clinical", optimalMax: 29, optimalMin: 18, unit: "%" },
6408
6380
  sex: "F"
6409
6381
  },
6410
6382
  {
6411
6383
  ageMax: 45,
6412
6384
  ageMin: 36,
6413
- range: { max: 34, min: 13, optimalMax: 30, optimalMin: 19, unit: "%" },
6385
+ range: { max: 34, min: 13, minKind: "clinical", optimalMax: 30, optimalMin: 19, unit: "%" },
6414
6386
  sex: "F"
6415
6387
  },
6416
6388
  {
6417
6389
  ageMax: 55,
6418
6390
  ageMin: 46,
6419
- range: { max: 35, min: 13, optimalMax: 31, optimalMin: 20, unit: "%" },
6391
+ range: { max: 35, min: 13, minKind: "clinical", optimalMax: 31, optimalMin: 20, unit: "%" },
6420
6392
  sex: "F"
6421
6393
  },
6422
6394
  {
6423
6395
  ageMin: 56,
6424
- range: { max: 36, min: 13, optimalMax: 32, optimalMin: 20, unit: "%" },
6396
+ range: { max: 36, min: 13, minKind: "clinical", optimalMax: 32, optimalMin: 20, unit: "%" },
6425
6397
  sex: "F"
6426
6398
  }
6427
6399
  ]
6428
6400
  },
6429
6401
  FatFreeMass: {
6430
- default: { max: 80, min: 40, optimalMax: 70, optimalMin: 45, unit: "kg" },
6402
+ default: { max: 80, maxKind: "display", min: 40, optimalMax: 70, optimalMin: 45, unit: "kg" },
6431
6403
  direction: "higher-better",
6404
+ kind: "population",
6432
6405
  source: "kelly-dxa-2009",
6433
6406
  variants: [
6434
6407
  {
6435
6408
  ageMin: 18,
6436
- range: { max: 90, min: 50, optimalMax: 80, optimalMin: 55, unit: "kg" },
6409
+ range: { max: 90, maxKind: "display", min: 50, optimalMax: 80, optimalMin: 55, unit: "kg" },
6437
6410
  sex: "M"
6438
6411
  },
6439
6412
  {
6440
6413
  ageMin: 18,
6441
- range: { max: 60, min: 35, optimalMax: 52, optimalMin: 38, unit: "kg" },
6414
+ range: { max: 60, maxKind: "display", min: 35, optimalMax: 52, optimalMin: 38, unit: "kg" },
6442
6415
  sex: "F"
6443
6416
  }
6444
6417
  ]
6445
6418
  },
6446
6419
  FatMass: {
6447
- default: { max: 30, min: 5, optimalMax: 20, optimalMin: 8, unit: "kg" },
6420
+ default: { max: 30, min: 5, minKind: "clinical", optimalMax: 20, optimalMin: 8, unit: "kg" },
6448
6421
  direction: "lower-better",
6422
+ kind: "population",
6449
6423
  source: "kelly-dxa-2009",
6450
6424
  variants: [
6451
6425
  {
6452
6426
  ageMin: 18,
6453
- range: { max: 25, min: 5, optimalMax: 18, optimalMin: 7, unit: "kg" },
6427
+ range: { max: 25, min: 5, minKind: "clinical", optimalMax: 18, optimalMin: 7, unit: "kg" },
6454
6428
  sex: "M"
6455
6429
  },
6456
6430
  {
6457
6431
  ageMin: 18,
6458
- range: { max: 30, min: 8, optimalMax: 22, optimalMin: 10, unit: "kg" },
6432
+ range: { max: 30, min: 8, minKind: "clinical", optimalMax: 22, optimalMin: 10, unit: "kg" },
6459
6433
  sex: "F"
6460
6434
  }
6461
6435
  ]
6462
6436
  },
6463
6437
  GynoidFatPct: {
6464
- default: { max: 45, min: 15, optimalMax: 35, optimalMin: 20, unit: "%" },
6438
+ default: { max: 45, min: 15, minKind: "display", optimalMax: 35, optimalMin: 20, unit: "%" },
6465
6439
  direction: "lower-better",
6440
+ kind: "population",
6466
6441
  source: "kelly-dxa-2009",
6467
6442
  variants: [
6468
6443
  {
6469
6444
  ageMin: 18,
6470
- range: { max: 35, min: 12, optimalMax: 28, optimalMin: 15, unit: "%" },
6445
+ range: { max: 35, min: 12, minKind: "display", optimalMax: 28, optimalMin: 15, unit: "%" },
6471
6446
  sex: "M"
6472
6447
  },
6473
6448
  {
6474
6449
  ageMin: 18,
6475
- range: { max: 50, min: 20, optimalMax: 40, optimalMin: 25, unit: "%" },
6450
+ range: { max: 50, min: 20, minKind: "display", optimalMax: 40, optimalMin: 25, unit: "%" },
6476
6451
  sex: "F"
6477
6452
  }
6478
6453
  ]
6479
6454
  },
6480
6455
  LeanMass: {
6481
- default: { max: 75, min: 35, optimalMax: 65, optimalMin: 40, unit: "kg" },
6456
+ default: { max: 75, maxKind: "display", min: 35, optimalMax: 65, optimalMin: 40, unit: "kg" },
6482
6457
  direction: "higher-better",
6458
+ kind: "population",
6483
6459
  source: "kelly-dxa-2009",
6484
6460
  variants: [
6485
6461
  {
6486
6462
  ageMin: 18,
6487
- range: { max: 85, min: 45, optimalMax: 75, optimalMin: 50, unit: "kg" },
6463
+ range: { max: 85, maxKind: "display", min: 45, optimalMax: 75, optimalMin: 50, unit: "kg" },
6488
6464
  sex: "M"
6489
6465
  },
6490
6466
  {
6491
6467
  ageMin: 18,
6492
- range: { max: 55, min: 30, optimalMax: 48, optimalMin: 35, unit: "kg" },
6468
+ range: { max: 55, maxKind: "display", min: 30, optimalMax: 48, optimalMin: 35, unit: "kg" },
6493
6469
  sex: "F"
6494
6470
  }
6495
6471
  ]
6496
6472
  },
6497
6473
  TotalMass: {
6498
6474
  default: { max: 100, min: 50, optimalMax: 85, optimalMin: 55, unit: "kg" },
6475
+ kind: "population",
6499
6476
  source: "kelly-dxa-2009",
6500
6477
  variants: [
6501
6478
  {
@@ -6510,226 +6487,856 @@ var biomarkerRangeDefinitions = {
6510
6487
  }
6511
6488
  ]
6512
6489
  },
6513
- VATMass: {
6514
- default: { max: 1.5, min: 0, optimalMax: 0.8, optimalMin: 0, unit: "kg" },
6515
- direction: "lower-better",
6516
- source: "ofenheimer-vat-2020",
6490
+ VATMass: {
6491
+ default: { max: 1.5, min: 0, optimalMax: 0.8, optimalMin: 0, unit: "kg" },
6492
+ direction: "lower-better",
6493
+ kind: "population",
6494
+ source: "ofenheimer-vat-2020",
6495
+ variants: [
6496
+ {
6497
+ ageMin: 18,
6498
+ range: { max: 1.5, min: 0, optimalMax: 1, optimalMin: 0, unit: "kg" },
6499
+ sex: "M"
6500
+ },
6501
+ {
6502
+ ageMin: 18,
6503
+ range: { max: 1, min: 0, optimalMax: 0.5, optimalMin: 0, unit: "kg" },
6504
+ sex: "F"
6505
+ }
6506
+ ]
6507
+ },
6508
+ VATVolume: {
6509
+ default: {
6510
+ max: 852,
6511
+ min: 0,
6512
+ optimalMax: 600,
6513
+ optimalMin: 0,
6514
+ unit: "cm\xB3",
6515
+ warningMax: 1837
6516
+ },
6517
+ direction: "lower-better",
6518
+ kind: "decision-threshold",
6519
+ source: "ge-corescan"
6520
+ },
6521
+ BMD_Total: {
6522
+ default: {
6523
+ max: 1.4,
6524
+ maxKind: "display",
6525
+ min: 0.9,
6526
+ optimalMax: 1.3,
6527
+ optimalMin: 1,
6528
+ unit: "g/cm\xB2"
6529
+ },
6530
+ direction: "higher-better",
6531
+ kind: "decision-threshold",
6532
+ source: "who-osteoporosis-1994"
6533
+ },
6534
+ TScore_Total: {
6535
+ default: {
6536
+ max: 4,
6537
+ maxKind: "display",
6538
+ min: -1,
6539
+ optimalMax: 2,
6540
+ optimalMin: -0.5,
6541
+ unit: ""
6542
+ },
6543
+ direction: "higher-better",
6544
+ kind: "decision-threshold",
6545
+ source: "who-osteoporosis-1994"
6546
+ },
6547
+ ZScore_Total: {
6548
+ default: {
6549
+ max: 2,
6550
+ maxKind: "display",
6551
+ min: -2,
6552
+ optimalMax: 1,
6553
+ optimalMin: -1,
6554
+ unit: ""
6555
+ },
6556
+ direction: "higher-better",
6557
+ kind: "decision-threshold",
6558
+ source: "who-osteoporosis-1994"
6559
+ },
6560
+ Amylase: {
6561
+ default: { max: 100, min: 28, optimalMax: 90, optimalMin: 35, unit: "U/L" },
6562
+ kind: "reference-interval",
6563
+ source: "tietz-7ed-2015"
6564
+ },
6565
+ Creatinine_Urine: {
6566
+ default: { max: 300, min: 20, optimalMax: 250, optimalMin: 40, unit: "mg/dL" },
6567
+ kind: "reference-interval",
6568
+ source: "tietz-7ed-2015",
6569
+ variants: [
6570
+ {
6571
+ ageMin: 18,
6572
+ range: { max: 300, min: 40, optimalMax: 260, optimalMin: 60, unit: "mg/dL" },
6573
+ sex: "M"
6574
+ },
6575
+ {
6576
+ ageMin: 18,
6577
+ range: { max: 220, min: 20, optimalMax: 180, optimalMin: 40, unit: "mg/dL" },
6578
+ sex: "F"
6579
+ }
6580
+ ]
6581
+ },
6582
+ IgA: {
6583
+ default: { max: 400, min: 70, optimalMax: 350, optimalMin: 100, unit: "mg/dL" },
6584
+ kind: "reference-interval",
6585
+ source: "tietz-7ed-2015"
6586
+ },
6587
+ Lipase: {
6588
+ default: { max: 60, min: 0, optimalMax: 50, optimalMin: 10, unit: "U/L" },
6589
+ kind: "reference-interval",
6590
+ source: "tietz-7ed-2015"
6591
+ },
6592
+ Microalbumin_Urine: {
6593
+ default: { max: 30, min: 0, optimalMax: 20, optimalMin: 0, unit: "mg/L" },
6594
+ kind: "decision-threshold",
6595
+ source: "kdigo-ckd-2024"
6596
+ },
6597
+ PSA_Free: {
6598
+ default: { max: 1.5, min: 0, optimalMax: 1, optimalMin: 0, unit: "ng/mL" },
6599
+ kind: "reference-interval",
6600
+ source: "sturgeon-nacb-2008"
6601
+ },
6602
+ // Relação PSA livre/total — `higher-better`: razão maior = menor risco de
6603
+ // câncer de próstata, logo não há limite superior "ruim" (acima do normal é
6604
+ // verde). Faixas alinhadas à tabela clínica: ≥25% baixo risco (verde),
6605
+ // 10–25% atenção — intermediário/moderado (âmbar), <10% alto risco >50%
6606
+ // (vermelho). Limites: `min` (25) é o piso do verde e `warningMin` (10) o
6607
+ // piso do âmbar — o vermelho é estritamente abaixo de 10% (< 10%); 10%
6608
+ // pertence ao âmbar, conforme a faixa "10–15% moderado" da tabela.
6609
+ PSA_FreeRatio: {
6610
+ default: {
6611
+ max: 100,
6612
+ maxKind: "display",
6613
+ min: 25,
6614
+ optimalMax: 100,
6615
+ optimalMin: 30,
6616
+ unit: "%",
6617
+ warningMin: 10
6618
+ },
6619
+ direction: "higher-better",
6620
+ kind: "decision-threshold",
6621
+ source: "sturgeon-nacb-2008"
6622
+ },
6623
+ // BMI - WHO classification: 18.5–24.9 normal, 25–29.9 overweight, ≥30 obese
6624
+ BMI: {
6625
+ default: { max: 30, min: 18.5, optimalMax: 24.9, optimalMin: 18.5, unit: "kg/m2" },
6626
+ kind: "decision-threshold",
6627
+ source: "who-obesity-2000"
6628
+ },
6629
+ // eAG — Estimated Average Glucose, derivada da HbA1c pela fórmula ADAG:
6630
+ // eAG (mg/dL) = 28,7 × HbA1c(%) − 46,7 (NATHAN, D. M. et al. Translating the
6631
+ // A1C assay into estimated average glucose values. Diabetes Care, v. 31, n. 8,
6632
+ // p. 1473-1478, 2008. DOI: 10.2337/dc08-0545).
6633
+ //
6634
+ // As faixas espelham os cortes diagnósticos de HbA1c da SBD (ver entrada
6635
+ // `HbA1c`) convertidos por essa fórmula, mantendo a eAG coerente com o
6636
+ // marcador de origem:
6637
+ // optimalMax 105 ← HbA1c 5,3 % (teto do controle ótimo)
6638
+ // max 117 ← HbA1c 5,7 % (teto do não diabético → início do pré-diabetes)
6639
+ // warningMax 137 ← HbA1c 6,4 % (teto do pré-diabetes → acima disso, diabetes)
6640
+ // optimalMin 82 ← HbA1c 4,5 % (piso do alvo fisiológico da entrada HbA1c)
6641
+ // min 11 ← HbA1c 2,0 % (mesmo piso de sanidade da entrada HbA1c)
6642
+ //
6643
+ // O piso era 70 nos dois campos, e não vinha desta conversão: 70 é o limite
6644
+ // inferior clássico da glicemia **de jejum**, transplantado para uma média
6645
+ // estimada de ~3 meses. Pela fórmula, 70 equivale a HbA1c 4,07 % — ou seja,
6646
+ // marcava como alterado exatamente quem a entrada `HbA1c` documenta que não
6647
+ // deve ser sinalizado, já que HbA1c baixa reflete hemólise, perda sanguínea
6648
+ // ou hemoglobinopatia, e não doença do metabolismo glicêmico. Espelhar o piso
6649
+ // do HbA1c mantém as duas entradas coerentes, que é o que o comentário sempre
6650
+ // afirmou fazer.
6651
+ //
6652
+ // Substitui a faixa anterior (optimalMax 126 = corte de glicemia de jejum;
6653
+ // max 154 = meta terapêutica do diabético, HbA1c 7 %), que classificava como
6654
+ // "Normal" valores já pré-diabéticos e diabéticos.
6655
+ eAG: {
6656
+ default: {
6657
+ max: 117,
6658
+ min: 11,
6659
+ minKind: "display",
6660
+ optimalMax: 105,
6661
+ optimalMin: 82,
6662
+ unit: "mg/dL",
6663
+ warningMax: 137
6664
+ },
6665
+ kind: "decision-threshold",
6666
+ source: "sbd-diabetes-2024"
6667
+ },
6668
+ // INR - International Normalized Ratio (non-anticoagulated patients)
6669
+ INR: {
6670
+ default: { max: 1.2, min: 0.8, optimalMax: 1.1, optimalMin: 0.9, unit: "ratio" },
6671
+ kind: "reference-interval",
6672
+ source: "tietz-7ed-2015"
6673
+ },
6674
+ // Prothrombin Time
6675
+ ProthrombinTime: {
6676
+ default: { max: 13.5, min: 11, optimalMax: 13, optimalMin: 11, unit: "seconds" },
6677
+ kind: "reference-interval",
6678
+ source: "tietz-7ed-2015"
6679
+ },
6680
+ // DHT - Dihydrotestosterone (adult male reference; female values are much lower)
6681
+ DHT: {
6682
+ default: { max: 85, min: 30, optimalMax: 85, optimalMin: 30, unit: "ng/dL" },
6683
+ kind: "reference-interval",
6684
+ source: "tietz-7ed-2015",
6517
6685
  variants: [
6518
6686
  {
6519
6687
  ageMin: 18,
6520
- range: { max: 1.5, min: 0, optimalMax: 1, optimalMin: 0, unit: "kg" },
6688
+ range: { max: 85, min: 30, optimalMax: 85, optimalMin: 30, unit: "ng/dL" },
6521
6689
  sex: "M"
6522
6690
  },
6523
6691
  {
6524
6692
  ageMin: 18,
6525
- range: { max: 1, min: 0, optimalMax: 0.5, optimalMin: 0, unit: "kg" },
6693
+ range: { max: 22, min: 4, optimalMax: 22, optimalMin: 4, unit: "ng/dL" },
6526
6694
  sex: "F"
6527
6695
  }
6528
6696
  ]
6529
6697
  },
6530
- VATVolume: {
6531
- default: {
6532
- max: 852,
6533
- min: 0,
6534
- optimalMax: 600,
6535
- optimalMin: 0,
6536
- unit: "cm\xB3",
6537
- warningMax: 1837
6538
- },
6698
+ // Total IgE - higher values suggest atopy/allergic sensitization
6699
+ IgE_Total: {
6700
+ default: { max: 100, min: 0, optimalMax: 100, optimalMin: 0, unit: "IU/mL" },
6539
6701
  direction: "lower-better",
6540
- source: "ge-corescan"
6541
- },
6542
- BMD_Total: {
6543
- default: { max: 1.4, min: 0.9, optimalMax: 1.3, optimalMin: 1, unit: "g/cm\xB2" },
6544
- direction: "higher-better",
6545
- source: "who-osteoporosis-1994"
6702
+ kind: "reference-interval",
6703
+ source: "tietz-7ed-2015"
6546
6704
  },
6547
- TScore_Total: {
6548
- default: { max: 4, min: -1, optimalMax: 2, optimalMin: -0.5, unit: "" },
6549
- direction: "higher-better",
6550
- source: "who-osteoporosis-1994"
6705
+ // IgG - Immunoglobulin G
6706
+ IgG: {
6707
+ default: { max: 1600, min: 700, optimalMax: 1600, optimalMin: 700, unit: "mg/dL" },
6708
+ kind: "reference-interval",
6709
+ source: "tietz-7ed-2015"
6551
6710
  },
6552
- ZScore_Total: {
6553
- default: { max: 2, min: -2, optimalMax: 1, optimalMin: -1, unit: "" },
6554
- direction: "higher-better",
6555
- source: "who-osteoporosis-1994"
6711
+ // IgE E1 Cat Dander - Class 0 (≤0.35 kU/L) = negative
6712
+ IgE_E1_CatDander: {
6713
+ default: { max: 0.35, min: 0, optimalMax: 0.35, optimalMin: 0, unit: "kU/L" },
6714
+ direction: "lower-better",
6715
+ kind: "decision-threshold",
6716
+ source: "tietz-7ed-2015"
6556
6717
  },
6557
- Amylase: {
6558
- default: { max: 100, min: 28, optimalMax: 90, optimalMin: 35, unit: "U/L" },
6718
+ // IgE GX1 Grasses - Class 0 (≤0.35 kU/L) = negative
6719
+ IgE_GX1_Grasses: {
6720
+ default: { max: 0.35, min: 0, optimalMax: 0.35, optimalMin: 0, unit: "kU/L" },
6721
+ direction: "lower-better",
6722
+ kind: "decision-threshold",
6559
6723
  source: "tietz-7ed-2015"
6724
+ }
6725
+ };
6726
+ var defaultReferenceRanges = Object.fromEntries(
6727
+ Object.entries(biomarkerRangeDefinitions).map(([code, def]) => [
6728
+ code,
6729
+ {
6730
+ ...def.default,
6731
+ ...def.kind && { kind: def.kind },
6732
+ ...def.source && { source: def.source }
6733
+ }
6734
+ ])
6735
+ );
6736
+ function getReferenceRange(testCode, context) {
6737
+ const definition = biomarkerRangeDefinitions[testCode];
6738
+ if (!definition) return void 0;
6739
+ const withSource = (range2, kind = definition.kind) => ({
6740
+ ...range2,
6741
+ ...kind && { kind },
6742
+ ...definition.source && { source: definition.source }
6743
+ });
6744
+ if (!context || !definition.variants || definition.variants.length === 0) {
6745
+ return withSource(definition.default);
6746
+ }
6747
+ const { age, biologicalSex, pregnancyTrimester, pregnant } = context;
6748
+ const hasPregnancyVariant = definition.variants.some((v) => v.pregnant === true);
6749
+ for (const variant of definition.variants) {
6750
+ if (variant.pregnant === true) {
6751
+ if (!pregnant) continue;
6752
+ if (variant.pregnancyTrimester !== void 0 && variant.pregnancyTrimester !== pregnancyTrimester) {
6753
+ continue;
6754
+ }
6755
+ } else if (pregnant && hasPregnancyVariant) {
6756
+ continue;
6757
+ }
6758
+ if (variant.sex !== "all" && variant.sex !== biologicalSex) {
6759
+ continue;
6760
+ }
6761
+ if (variant.ageMin !== void 0 && (age === void 0 || age < variant.ageMin)) {
6762
+ continue;
6763
+ }
6764
+ if (variant.ageMax !== void 0 && (age === void 0 || age > variant.ageMax)) {
6765
+ continue;
6766
+ }
6767
+ return withSource(variant.range, variant.kind ?? definition.kind);
6768
+ }
6769
+ return withSource(definition.default);
6770
+ }
6771
+ function getRangeDirection(testCode) {
6772
+ return biomarkerRangeDefinitions[testCode]?.direction ?? "range";
6773
+ }
6774
+ function referenceRangeMeaning(kind) {
6775
+ const system = "http://terminology.hl7.org/CodeSystem/referencerange-meaning";
6776
+ if (kind === "reference-interval") return { code: "normal", display: "Normal Range", system };
6777
+ if (kind === "decision-threshold") {
6778
+ return { code: "recommended", display: "Recommended Range", system };
6779
+ }
6780
+ return void 0;
6781
+ }
6782
+
6783
+ // src/converter.ts
6784
+ function interpretationCode(flag) {
6785
+ switch (flag) {
6786
+ case "H":
6787
+ return "H";
6788
+ // High
6789
+ case "L":
6790
+ return "L";
6791
+ // Low
6792
+ default:
6793
+ return "N";
6794
+ }
6795
+ }
6796
+ function interpretationDisplay(flag) {
6797
+ switch (flag) {
6798
+ case "H":
6799
+ return "High";
6800
+ case "L":
6801
+ return "Low";
6802
+ default:
6803
+ return "Normal";
6804
+ }
6805
+ }
6806
+ var SEX_CODING = {
6807
+ female: {
6808
+ code: "female",
6809
+ display: "Female",
6810
+ system: "http://hl7.org/fhir/administrative-gender"
6560
6811
  },
6561
- Creatinine_Urine: {
6562
- default: { max: 300, min: 20, optimalMax: 250, optimalMin: 40, unit: "mg/dL" },
6563
- source: "tietz-7ed-2015",
6564
- variants: [
6812
+ male: { code: "male", display: "Male", system: "http://hl7.org/fhir/administrative-gender" }
6813
+ };
6814
+ var buildReferenceRanges = (observation, quantity) => {
6815
+ const toRanges = (low, high, sex) => {
6816
+ if (low === void 0 && high === void 0) return [];
6817
+ return [
6565
6818
  {
6566
- ageMin: 18,
6567
- range: { max: 300, min: 40, optimalMax: 260, optimalMin: 60, unit: "mg/dL" },
6568
- sex: "M"
6569
- },
6819
+ ...sex === void 0 ? {} : { appliesTo: [{ coding: [SEX_CODING[sex]] }] },
6820
+ ...high === void 0 ? {} : { high: quantity(high) },
6821
+ ...low === void 0 ? {} : { low: quantity(low) }
6822
+ }
6823
+ ];
6824
+ };
6825
+ if (observation.referenceRanges && observation.referenceRanges.length > 0) {
6826
+ return observation.referenceRanges.flatMap((r) => toRanges(r.low, r.high, r.appliesTo));
6827
+ }
6828
+ const meaning = observation.referenceKind && referenceRangeMeaning(observation.referenceKind);
6829
+ return toRanges(observation.referenceMin, observation.referenceMax).map(
6830
+ (range2) => meaning ? { ...range2, type: { coding: [meaning] } } : range2
6831
+ );
6832
+ };
6833
+ function labObservationToFHIR(observation, patientId, laboratoryName) {
6834
+ const loincCode = codeToLoinc(observation.biomarkerCode);
6835
+ const sourceUnit = observation.unit || getDefaultUnit(observation.biomarkerCode) || observation.unit;
6836
+ const ucumUnit = resolveUcum(sourceUnit, observation.biomarkerCode);
6837
+ const quantity = (value) => ({
6838
+ ...ucumUnit ? { code: ucumUnit, system: "http://unitsofmeasure.org" } : {},
6839
+ ...sourceUnit ? { unit: sourceUnit } : {},
6840
+ value
6841
+ });
6842
+ const isQualitative = observation.isQualitative || typeof observation.value === "string";
6843
+ const fhirObs = {
6844
+ category: [
6845
+ {
6846
+ coding: [
6847
+ {
6848
+ code: "laboratory",
6849
+ display: "Laboratory",
6850
+ system: "http://terminology.hl7.org/CodeSystem/observation-category"
6851
+ }
6852
+ ]
6853
+ }
6854
+ ],
6855
+ code: {
6856
+ // Sem LOINC, o coding LOINC simplesmente não sai. Antes ia `99999-9`,
6857
+ // que não é código LOINC nenhum: publicava sob `http://loinc.org` uma
6858
+ // afirmação falsa, e quem consumisse o bundle confiando no system
6859
+ // trataria aquilo como código de verdade. Composição corporal, densidade
6860
+ // óssea e escore de cálcio não têm LOINC, e o certo é a lacuna explícita.
6861
+ coding: [
6862
+ ...loincCode ? [
6863
+ {
6864
+ code: loincCode,
6865
+ display: observation.biomarkerName,
6866
+ system: LOINC_SYSTEM
6867
+ }
6868
+ ] : [],
6869
+ {
6870
+ code: observation.biomarkerCode,
6871
+ display: observation.biomarkerName,
6872
+ system: BIOMARKER_CODE_SYSTEM
6873
+ }
6874
+ ],
6875
+ text: observation.biomarkerName
6876
+ },
6877
+ effectiveDateTime: observation.collectionDate,
6878
+ id: `${observation.reportId}-${observation.biomarkerCode}`,
6879
+ interpretation: [
6880
+ {
6881
+ coding: [
6882
+ {
6883
+ code: interpretationCode(observation.flag),
6884
+ display: interpretationDisplay(observation.flag),
6885
+ system: "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation"
6886
+ }
6887
+ ]
6888
+ }
6889
+ ],
6890
+ performer: laboratoryName ? [{ display: laboratoryName }] : void 0,
6891
+ resourceType: "Observation",
6892
+ status: "final",
6893
+ subject: {
6894
+ reference: `Patient/${patientId}`
6895
+ }
6896
+ };
6897
+ if (isQualitative) {
6898
+ fhirObs.valueString = String(observation.value);
6899
+ } else {
6900
+ fhirObs.valueQuantity = quantity(observation.value);
6901
+ const referenceRange = buildReferenceRanges(observation, quantity);
6902
+ if (referenceRange.length > 0) fhirObs.referenceRange = referenceRange;
6903
+ }
6904
+ return fhirObs;
6905
+ }
6906
+ function labReportToFHIR(report, patientId, observationIds) {
6907
+ let status;
6908
+ switch (report.processingStatus) {
6909
+ case "complete":
6910
+ status = "final";
6911
+ break;
6912
+ case "partial":
6913
+ status = "partial";
6914
+ break;
6915
+ case "pending_review":
6916
+ status = "preliminary";
6917
+ break;
6918
+ default:
6919
+ status = "final";
6920
+ }
6921
+ return {
6922
+ category: [
6923
+ {
6924
+ coding: [
6925
+ {
6926
+ code: "LAB",
6927
+ display: "Laboratory",
6928
+ system: "http://terminology.hl7.org/CodeSystem/v2-0074"
6929
+ }
6930
+ ]
6931
+ }
6932
+ ],
6933
+ code: {
6934
+ coding: [
6935
+ {
6936
+ code: "11502-2",
6937
+ // Laboratory report
6938
+ display: "Laboratory report",
6939
+ system: "http://loinc.org"
6940
+ }
6941
+ ],
6942
+ text: "Laboratory Results"
6943
+ },
6944
+ conclusion: report.overallStatus === "NORMAL" ? "All results within normal limits" : "One or more abnormal results detected",
6945
+ conclusionCode: report.overallStatus === "ANORMAL" ? [
6570
6946
  {
6571
- ageMin: 18,
6572
- range: { max: 220, min: 20, optimalMax: 180, optimalMin: 40, unit: "mg/dL" },
6573
- sex: "F"
6947
+ coding: [
6948
+ {
6949
+ code: "A",
6950
+ display: "Abnormal",
6951
+ system: "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation"
6952
+ }
6953
+ ]
6574
6954
  }
6575
- ]
6576
- },
6577
- IgA: {
6578
- default: { max: 400, min: 70, optimalMax: 350, optimalMin: 100, unit: "mg/dL" },
6579
- source: "tietz-7ed-2015"
6580
- },
6581
- Lipase: {
6582
- default: { max: 60, min: 0, optimalMax: 50, optimalMin: 10, unit: "U/L" },
6583
- source: "tietz-7ed-2015"
6584
- },
6585
- Microalbumin_Urine: {
6586
- default: { max: 30, min: 0, optimalMax: 20, optimalMin: 0, unit: "mg/L" },
6587
- source: "kdigo-ckd-2024"
6588
- },
6589
- PSA_Free: {
6590
- default: { max: 1.5, min: 0, optimalMax: 1, optimalMin: 0, unit: "ng/mL" },
6591
- source: "sturgeon-nacb-2008"
6592
- },
6593
- // Relação PSA livre/total — `higher-better`: razão maior = menor risco de
6594
- // câncer de próstata, logo não há limite superior "ruim" (acima do normal é
6595
- // verde). Faixas alinhadas à tabela clínica: ≥25% baixo risco (verde),
6596
- // 10–25% atenção — intermediário/moderado (âmbar), <10% alto risco >50%
6597
- // (vermelho). Limites: `min` (25) é o piso do verde e `warningMin` (10) o
6598
- // piso do âmbar — o vermelho é estritamente abaixo de 10% (< 10%); 10%
6599
- // pertence ao âmbar, conforme a faixa "10–15% moderado" da tabela.
6600
- PSA_FreeRatio: {
6601
- default: { max: 100, min: 25, optimalMax: 100, optimalMin: 30, unit: "%", warningMin: 10 },
6602
- direction: "higher-better",
6603
- source: "sturgeon-nacb-2008"
6604
- },
6605
- // BMI - WHO classification: 18.5–24.9 normal, 25–29.9 overweight, ≥30 obese
6606
- BMI: {
6607
- default: { max: 30, min: 18.5, optimalMax: 24.9, optimalMin: 18.5, unit: "kg/m2" },
6608
- source: "who-obesity-2000"
6609
- },
6610
- // eAG — Estimated Average Glucose, derivada da HbA1c pela fórmula ADAG:
6611
- // eAG (mg/dL) = 28,7 × HbA1c(%) − 46,7 (NATHAN, D. M. et al. Translating the
6612
- // A1C assay into estimated average glucose values. Diabetes Care, v. 31, n. 8,
6613
- // p. 1473-1478, 2008. DOI: 10.2337/dc08-0545).
6614
- //
6615
- // As faixas espelham os cortes diagnósticos de HbA1c da SBD (ver entrada
6616
- // `HbA1c`) convertidos por essa fórmula, mantendo a eAG coerente com o
6617
- // marcador de origem:
6618
- // optimalMax 105 ← HbA1c 5,3 % (teto do controle ótimo)
6619
- // max 117 ← HbA1c 5,7 % (teto do não diabético → início do pré-diabetes)
6620
- // warningMax 137 ← HbA1c 6,4 % (teto do pré-diabetes → acima disso, diabetes)
6621
- // optimalMin 82 ← HbA1c 4,5 % (piso do alvo fisiológico da entrada HbA1c)
6622
- // min 11 ← HbA1c 2,0 % (mesmo piso de sanidade da entrada HbA1c)
6623
- //
6624
- // O piso era 70 nos dois campos, e não vinha desta conversão: 70 é o limite
6625
- // inferior clássico da glicemia **de jejum**, transplantado para uma média
6626
- // estimada de ~3 meses. Pela fórmula, 70 equivale a HbA1c 4,07 % — ou seja,
6627
- // marcava como alterado exatamente quem a entrada `HbA1c` documenta que não
6628
- // deve ser sinalizado, já que HbA1c baixa reflete hemólise, perda sanguínea
6629
- // ou hemoglobinopatia, e não doença do metabolismo glicêmico. Espelhar o piso
6630
- // do HbA1c mantém as duas entradas coerentes, que é o que o comentário sempre
6631
- // afirmou fazer.
6632
- //
6633
- // Substitui a faixa anterior (optimalMax 126 = corte de glicemia de jejum;
6634
- // max 154 = meta terapêutica do diabético, HbA1c 7 %), que classificava como
6635
- // "Normal" valores já pré-diabéticos e diabéticos.
6636
- eAG: {
6637
- default: { max: 117, min: 11, optimalMax: 105, optimalMin: 82, unit: "mg/dL", warningMax: 137 },
6638
- source: "sbd-diabetes-2024"
6639
- },
6640
- // INR - International Normalized Ratio (non-anticoagulated patients)
6641
- INR: {
6642
- default: { max: 1.2, min: 0.8, optimalMax: 1.1, optimalMin: 0.9, unit: "ratio" },
6643
- source: "tietz-7ed-2015"
6644
- },
6645
- // Prothrombin Time
6646
- ProthrombinTime: {
6647
- default: { max: 13.5, min: 11, optimalMax: 13, optimalMin: 11, unit: "seconds" },
6648
- source: "tietz-7ed-2015"
6649
- },
6650
- // DHT - Dihydrotestosterone (adult male reference; female values are much lower)
6651
- DHT: {
6652
- default: { max: 85, min: 30, optimalMax: 85, optimalMin: 30, unit: "ng/dL" },
6653
- source: "tietz-7ed-2015",
6654
- variants: [
6955
+ ] : void 0,
6956
+ effectiveDateTime: report.collectionDate,
6957
+ id: report.reportId,
6958
+ issued: report.createdAt,
6959
+ performer: report.laboratoryName ? [{ display: report.laboratoryName }] : void 0,
6960
+ resourceType: "DiagnosticReport",
6961
+ result: observationIds.map((id) => ({ reference: `Observation/${id}` })),
6962
+ status,
6963
+ subject: {
6964
+ reference: `Patient/${patientId}`
6965
+ }
6966
+ };
6967
+ }
6968
+ function userProfileToFHIR(profile) {
6969
+ const nameParts = profile.name.split(" ");
6970
+ const given = nameParts.slice(0, -1);
6971
+ const family = nameParts[nameParts.length - 1] || "";
6972
+ return {
6973
+ address: profile.address ? [
6655
6974
  {
6656
- ageMin: 18,
6657
- range: { max: 85, min: 30, optimalMax: 85, optimalMin: 30, unit: "ng/dL" },
6658
- sex: "M"
6659
- },
6975
+ city: profile.address.city,
6976
+ country: profile.address.country || "BR",
6977
+ line: [
6978
+ profile.address.street && profile.address.number ? `${profile.address.street}, ${profile.address.number}` : profile.address.street,
6979
+ profile.address.complement
6980
+ ].filter(Boolean),
6981
+ postalCode: profile.address.postalCode,
6982
+ state: profile.address.state
6983
+ }
6984
+ ] : void 0,
6985
+ birthDate: profile.birthDate,
6986
+ gender: profile.gender,
6987
+ id: profile.userId,
6988
+ name: [
6660
6989
  {
6661
- ageMin: 18,
6662
- range: { max: 22, min: 4, optimalMax: 22, optimalMin: 4, unit: "ng/dL" },
6663
- sex: "F"
6990
+ family,
6991
+ given: given.length > 0 ? given : void 0,
6992
+ text: profile.name
6664
6993
  }
6665
- ]
6666
- },
6667
- // Total IgE - higher values suggest atopy/allergic sensitization
6668
- IgE_Total: {
6669
- default: { max: 100, min: 0, optimalMax: 100, optimalMin: 0, unit: "IU/mL" },
6670
- direction: "lower-better",
6671
- source: "tietz-7ed-2015"
6672
- },
6673
- // IgG - Immunoglobulin G
6674
- IgG: {
6675
- default: { max: 1600, min: 700, optimalMax: 1600, optimalMin: 700, unit: "mg/dL" },
6676
- source: "tietz-7ed-2015"
6677
- },
6678
- // IgE E1 Cat Dander - Class 0 (≤0.35 kU/L) = negative
6679
- IgE_E1_CatDander: {
6680
- default: { max: 0.35, min: 0, optimalMax: 0.35, optimalMin: 0, unit: "kU/L" },
6681
- direction: "lower-better",
6682
- source: "tietz-7ed-2015"
6683
- },
6684
- // IgE GX1 Grasses - Class 0 (≤0.35 kU/L) = negative
6685
- IgE_GX1_Grasses: {
6686
- default: { max: 0.35, min: 0, optimalMax: 0.35, optimalMin: 0, unit: "kU/L" },
6687
- direction: "lower-better",
6688
- source: "tietz-7ed-2015"
6994
+ ],
6995
+ resourceType: "Patient",
6996
+ telecom: [
6997
+ ...profile.email ? [{ system: "email", value: profile.email }] : [],
6998
+ ...profile.phone ? [{ system: "phone", value: profile.phone }] : []
6999
+ ].length > 0 ? [
7000
+ ...profile.email ? [{ system: "email", value: profile.email }] : [],
7001
+ ...profile.phone ? [{ system: "phone", value: profile.phone }] : []
7002
+ ] : void 0
7003
+ };
7004
+ }
7005
+ function labResultToFHIRBundle(report, observations, userProfile) {
7006
+ const patientId = userProfile.userId;
7007
+ const fhirObservations = observations.map((obs) => {
7008
+ const resource = labObservationToFHIR(
7009
+ { ...obs, collectionDate: report.collectionDate },
7010
+ patientId,
7011
+ report.laboratoryName
7012
+ );
7013
+ return { fullUrl: entryFullUrl(resource), resource };
7014
+ });
7015
+ const observationIds = fhirObservations.map((entry) => entry.resource.id);
7016
+ const diagnosticReport = labReportToFHIR(report, patientId, observationIds);
7017
+ const fhirPatient = userProfileToFHIR(userProfile);
7018
+ return {
7019
+ entry: [
7020
+ { fullUrl: entryFullUrl(fhirPatient), resource: fhirPatient },
7021
+ { fullUrl: entryFullUrl(diagnosticReport), resource: diagnosticReport },
7022
+ ...fhirObservations
7023
+ ],
7024
+ resourceType: "Bundle",
7025
+ type: "collection"
7026
+ };
7027
+ }
7028
+
7029
+ // src/cli/commands/convert.ts
7030
+ async function convert(args, json) {
7031
+ const raw = await getInput(args[0]);
7032
+ const data = parseJson(
7033
+ raw,
7034
+ "JSON inv\xE1lido. Forne\xE7a um objeto com { report, observations, profile }."
7035
+ );
7036
+ if (!data.report || !data.observations || !data.profile) {
7037
+ exitWithError(
7038
+ 'Formato esperado: { "report": LabReportData, "observations": LabObservationData[], "profile": UserProfileData }'
7039
+ );
6689
7040
  }
6690
- };
6691
- var defaultReferenceRanges = Object.fromEntries(
6692
- Object.entries(biomarkerRangeDefinitions).map(([code, def]) => [
6693
- code,
6694
- def.source ? { ...def.default, source: def.source } : def.default
6695
- ])
6696
- );
6697
- function getReferenceRange(testCode, context) {
6698
- const definition = biomarkerRangeDefinitions[testCode];
6699
- if (!definition) return void 0;
6700
- const withSource = (range2) => definition.source ? { ...range2, source: definition.source } : range2;
6701
- if (!context || !definition.variants || definition.variants.length === 0) {
6702
- return withSource(definition.default);
7041
+ const bundle = labResultToFHIRBundle(data.report, data.observations, data.profile);
7042
+ if (json) {
7043
+ outputJson(bundle);
7044
+ } else {
7045
+ outputText(JSON.stringify(bundle, null, 2));
7046
+ }
7047
+ }
7048
+
7049
+ // src/validators.ts
7050
+ function validateFHIRImportBundle(data) {
7051
+ const errors = [];
7052
+ if (!data || typeof data !== "object") {
7053
+ errors.push({ details: "Input must be a JSON object", field: "root" });
7054
+ return errors;
7055
+ }
7056
+ const bundle = data;
7057
+ if (bundle.resourceType !== "Bundle") {
7058
+ errors.push({
7059
+ details: `Expected resourceType "Bundle", got "${String(bundle.resourceType)}"`,
7060
+ field: "resourceType"
7061
+ });
7062
+ }
7063
+ if (!Array.isArray(bundle.entry)) {
7064
+ errors.push({ details: 'Bundle must contain an "entry" array', field: "entry" });
7065
+ } else if (bundle.entry.length === 0) {
7066
+ errors.push({ details: "Bundle entry array is empty", field: "entry" });
7067
+ }
7068
+ return errors;
7069
+ }
7070
+
7071
+ // src/importer.ts
7072
+ var MAX_OBSERVATIONS = 5e3;
7073
+ var MAX_FILE_SIZE = 15 * 1024 * 1024;
7074
+ function resolveBiomarkerCode(observation) {
7075
+ const coding = observation.code?.coding ?? [];
7076
+ const loincCode = coding.find((c) => c.system === LOINC_SYSTEM)?.code;
7077
+ const declaredCode = coding.find((c) => c.system === BIOMARKER_CODE_SYSTEM)?.code;
7078
+ const seenCodes = [
7079
+ ...loincCode ? [`LOINC ${loincCode}`] : [],
7080
+ ...declaredCode ? [`biomarker code ${declaredCode}`] : []
7081
+ ];
7082
+ let reason;
7083
+ if (seenCodes.length > 0) {
7084
+ reason = `Unknown code: ${seenCodes.join(", ")}`;
7085
+ } else if (coding.length > 0) {
7086
+ const systems = [...new Set(coding.map((c) => c.system ?? "(sem system)"))];
7087
+ reason = `No code in a supported system (found: ${systems.join(", ")})`;
7088
+ } else {
7089
+ reason = "No code found in observation coding";
6703
7090
  }
6704
- const { age, biologicalSex, pregnancyTrimester, pregnant } = context;
6705
- const hasPregnancyVariant = definition.variants.some((v) => v.pregnant === true);
6706
- for (const variant of definition.variants) {
6707
- if (variant.pregnant === true) {
6708
- if (!pregnant) continue;
6709
- if (variant.pregnancyTrimester !== void 0 && variant.pregnancyTrimester !== pregnancyTrimester) {
6710
- continue;
6711
- }
6712
- } else if (pregnant && hasPregnancyVariant) {
7091
+ const fromLoinc = loincCode ? loincToCode(loincCode) : void 0;
7092
+ if (fromLoinc) return { internalCode: fromLoinc, loincCode, reason };
7093
+ if (declaredCode && isValidCode(declaredCode)) {
7094
+ const canonical = normalizeCode(declaredCode);
7095
+ return { internalCode: canonical, loincCode: codeToLoinc(canonical), reason };
7096
+ }
7097
+ return { loincCode, reason };
7098
+ }
7099
+ function extractFlag(observation) {
7100
+ const code = observation.interpretation?.[0]?.coding?.[0]?.code;
7101
+ if (code === "H" || code === "HH") return "H";
7102
+ if (code === "L" || code === "LL") return "L";
7103
+ return "";
7104
+ }
7105
+ function extractObservationsFromBundle(bundle) {
7106
+ const observations = [];
7107
+ const skipped = [];
7108
+ for (let i = 0; i < bundle.entry.length; i++) {
7109
+ const entry = bundle.entry[i];
7110
+ if (!entry.resource) {
7111
+ skipped.push({ index: i, reason: "Entry has no resource" });
6713
7112
  continue;
6714
7113
  }
6715
- if (variant.sex !== "all" && variant.sex !== biologicalSex) {
7114
+ if (entry.resource.resourceType !== "Observation") {
6716
7115
  continue;
6717
7116
  }
6718
- if (variant.ageMin !== void 0 && (age === void 0 || age < variant.ageMin)) {
7117
+ if (observations.length >= MAX_OBSERVATIONS) {
7118
+ skipped.push({ index: i, reason: `Maximum of ${MAX_OBSERVATIONS} observations exceeded` });
6719
7119
  continue;
6720
7120
  }
6721
- if (variant.ageMax !== void 0 && (age === void 0 || age > variant.ageMax)) {
6722
- continue;
7121
+ observations.push(entry.resource);
7122
+ }
7123
+ return { observations, skipped };
7124
+ }
7125
+ function mapFHIRObservationToInternal(observation, index) {
7126
+ const { internalCode, loincCode, reason } = resolveBiomarkerCode(observation);
7127
+ if (!internalCode) {
7128
+ return {
7129
+ skipped: { index, loincCode, reason, resourceType: "Observation" }
7130
+ };
7131
+ }
7132
+ const definition = getDefinitionByCode(internalCode);
7133
+ let value;
7134
+ let unit = "";
7135
+ let isQualitative = false;
7136
+ if (observation.valueQuantity?.value !== void 0) {
7137
+ value = observation.valueQuantity.value;
7138
+ unit = observation.valueQuantity.unit || observation.valueQuantity.code || "";
7139
+ } else if (observation.valueString) {
7140
+ value = observation.valueString;
7141
+ isQualitative = true;
7142
+ } else {
7143
+ return {
7144
+ skipped: {
7145
+ index,
7146
+ loincCode,
7147
+ reason: "Observation has no value (valueQuantity or valueString)",
7148
+ resourceType: "Observation"
7149
+ }
7150
+ };
7151
+ }
7152
+ const collectionDate = observation.effectiveDateTime || observation.effectivePeriod?.start || "";
7153
+ if (!collectionDate) {
7154
+ return {
7155
+ skipped: {
7156
+ index,
7157
+ loincCode,
7158
+ reason: "Observation has no effectiveDateTime or effectivePeriod.start",
7159
+ resourceType: "Observation"
7160
+ }
7161
+ };
7162
+ }
7163
+ let referenceMin;
7164
+ let referenceMax;
7165
+ let referenceKind;
7166
+ if (observation.referenceRange?.[0]) {
7167
+ referenceMin = observation.referenceRange[0].low?.value;
7168
+ referenceMax = observation.referenceRange[0].high?.value;
7169
+ const meaning = observation.referenceRange[0].type?.coding?.find(
7170
+ (c) => c.system === "http://terminology.hl7.org/CodeSystem/referencerange-meaning"
7171
+ )?.code;
7172
+ if (meaning === "normal") referenceKind = "reference-interval";
7173
+ else if (meaning === "recommended") referenceKind = "decision-threshold";
7174
+ }
7175
+ const imported = {
7176
+ biomarkerCode: internalCode,
7177
+ biomarkerName: definition?.names.pt[0] || definition?.names.en[0] || observation.code.text || internalCode,
7178
+ collectionDate,
7179
+ flag: extractFlag(observation),
7180
+ isQualitative,
7181
+ loincCode,
7182
+ ...referenceKind && { referenceKind },
7183
+ referenceMax,
7184
+ referenceMin,
7185
+ unit: unit || definition?.unit || "",
7186
+ value
7187
+ };
7188
+ return { observation: imported };
7189
+ }
7190
+ function processImportBundle(data) {
7191
+ const validationErrors = validateFHIRImportBundle(data);
7192
+ if (validationErrors.length > 0) {
7193
+ return {
7194
+ errors: validationErrors,
7195
+ imported: [],
7196
+ skipped: [],
7197
+ totalProcessed: 0
7198
+ };
7199
+ }
7200
+ const bundle = data;
7201
+ const { observations, skipped } = extractObservationsFromBundle(bundle);
7202
+ const imported = [];
7203
+ const allSkipped = [...skipped];
7204
+ for (let i = 0; i < observations.length; i++) {
7205
+ const result = mapFHIRObservationToInternal(observations[i], i);
7206
+ if ("observation" in result) {
7207
+ imported.push(result.observation);
7208
+ } else {
7209
+ allSkipped.push(result.skipped);
6723
7210
  }
6724
- return withSource(variant.range);
6725
7211
  }
6726
- return withSource(definition.default);
7212
+ return {
7213
+ errors: [],
7214
+ imported,
7215
+ skipped: allSkipped,
7216
+ totalProcessed: observations.length
7217
+ };
6727
7218
  }
6728
- function getRangeDirection(testCode) {
6729
- return biomarkerRangeDefinitions[testCode]?.direction ?? "range";
7219
+
7220
+ // src/cli/commands/import.ts
7221
+ async function importBundle(args, json) {
7222
+ const raw = await getInput(args[0]);
7223
+ const bundle = parseJson(raw, "JSON inv\xE1lido.");
7224
+ const result = processImportBundle(bundle);
7225
+ if (json) {
7226
+ outputJson(result);
7227
+ return;
7228
+ }
7229
+ outputText(`Processados: ${result.totalProcessed}`);
7230
+ outputText(`Importados: ${result.imported.length}`);
7231
+ outputText(`Ignorados: ${result.skipped.length}`);
7232
+ outputText(`Erros: ${result.errors.length}`);
7233
+ if (result.imported.length > 0) {
7234
+ outputText("\nObserva\xE7\xF5es importadas:");
7235
+ for (const obs of result.imported) {
7236
+ outputText(` ${obs.biomarkerCode} (${obs.loincCode}): ${obs.value} ${obs.unit}`);
7237
+ }
7238
+ }
7239
+ if (result.errors.length > 0) {
7240
+ outputText("\nErros:");
7241
+ for (const err of result.errors) {
7242
+ outputText(` ${err.field}: ${err.details}`);
7243
+ }
7244
+ }
7245
+ }
7246
+
7247
+ // src/cli/commands/list.ts
7248
+ import { parseArgs } from "util";
7249
+ async function list(args, json) {
7250
+ const { values } = parseArgs({
7251
+ args,
7252
+ options: {
7253
+ category: { type: "string" },
7254
+ visible: { default: false, type: "boolean" }
7255
+ },
7256
+ strict: false
7257
+ });
7258
+ let defs;
7259
+ if (values.category) {
7260
+ defs = getBiomarkersByCategory(values.category);
7261
+ if (defs.length === 0) {
7262
+ exitWithError(`Categoria n\xE3o encontrada ou vazia: ${values.category}`);
7263
+ }
7264
+ } else if (values.visible) {
7265
+ defs = getVisibleDefinitions();
7266
+ } else {
7267
+ defs = getAllDefinitions();
7268
+ }
7269
+ if (json) {
7270
+ outputJson(defs);
7271
+ return;
7272
+ }
7273
+ const rows = defs.map((d) => [
7274
+ d.code,
7275
+ d.loinc ?? "\u2014",
7276
+ d.names.pt[0] ?? "",
7277
+ d.unit ?? "\u2014",
7278
+ Array.isArray(d.category) ? d.category.join(", ") : d.category
7279
+ ]);
7280
+ outputText(formatTable(["C\xF3digo", "LOINC", "Nome (pt)", "Unidade", "Categoria"], rows));
7281
+ outputText(`
7282
+ Total: ${defs.length} biomarcadores`);
7283
+ }
7284
+
7285
+ // src/cli/commands/loinc-map.ts
7286
+ async function loincMap(_args, json) {
7287
+ const defs = getAllDefinitions().filter((d) => d.loinc);
7288
+ if (json) {
7289
+ const map = Object.fromEntries(defs.map((d) => [d.loinc, d.code]));
7290
+ outputJson(map);
7291
+ return;
7292
+ }
7293
+ const rows = defs.map((d) => [d.loinc, d.code, d.names.pt[0] ?? d.names.en[0] ?? ""]);
7294
+ outputText(formatTable(["LOINC", "C\xF3digo", "Nome (pt)"], rows));
7295
+ outputText(`
7296
+ Total: ${rows.length} mapeamentos`);
7297
+ }
7298
+
7299
+ // src/cli/commands/lookup.ts
7300
+ function displayDefinition(def) {
7301
+ const cat = Array.isArray(def.category) ? def.category.join(", ") : def.category;
7302
+ outputText(
7303
+ [
7304
+ `Biomarcador: ${def.code}`,
7305
+ ` LOINC: ${def.loinc ?? "\u2014"}`,
7306
+ ` Nomes (pt): ${def.names.pt.join(", ")}`,
7307
+ ` Nomes (en): ${def.names.en.join(", ")}`,
7308
+ ` Categoria: ${cat}`,
7309
+ ` Unidade: ${def.unit ?? "\u2014"}`,
7310
+ ` Sexo: ${def.sex ?? "ambos"}`,
7311
+ ` Vis\xEDvel: ${def.hidden ? "n\xE3o" : "sim"}`
7312
+ ].join("\n")
7313
+ );
7314
+ }
7315
+ async function lookup(args, json) {
7316
+ const code = args[0];
7317
+ if (!code) exitWithError("Uso: fhir-bio lookup <c\xF3digo>");
7318
+ const def = getDefinitionByCode(code);
7319
+ if (!def) exitWithError(`Biomarcador n\xE3o encontrado: ${code}`);
7320
+ if (json) {
7321
+ outputJson(def);
7322
+ } else {
7323
+ displayDefinition(def);
7324
+ }
7325
+ }
7326
+ async function lookupLoinc(args, json) {
7327
+ const loinc = args[0];
7328
+ if (!loinc) exitWithError("Uso: fhir-bio lookup-loinc <loinc>");
7329
+ const def = getDefinitionByLoinc(loinc);
7330
+ if (!def) exitWithError(`Biomarcador n\xE3o encontrado para LOINC: ${loinc}`);
7331
+ if (json) {
7332
+ outputJson(def);
7333
+ } else {
7334
+ displayDefinition(def);
7335
+ }
6730
7336
  }
6731
7337
 
6732
7338
  // src/cli/commands/range.ts
7339
+ import { parseArgs as parseArgs2 } from "util";
6733
7340
  async function range(args, json) {
6734
7341
  const { positionals, values } = parseArgs2({
6735
7342
  allowPositionals: true,
@@ -6766,11 +7373,18 @@ async function range(args, json) {
6766
7373
  const sexLabel = ctx.biologicalSex === "M" ? "Homem" : ctx.biologicalSex === "F" ? "Mulher" : "Geral";
6767
7374
  const ageLabel = ctx.age !== void 0 ? `, ${ctx.age} anos` : "";
6768
7375
  const fmt = (v) => v !== void 0 ? String(v) : "\u2014";
7376
+ const desenho = (kind) => kind === "display" ? " (desenho, n\xE3o \xE9 corte)" : "";
7377
+ const tipos = {
7378
+ "decision-threshold": "limiar de decis\xE3o",
7379
+ population: "distribui\xE7\xE3o populacional",
7380
+ "reference-interval": "intervalo de refer\xEAncia"
7381
+ };
6769
7382
  outputText(
6770
7383
  [
6771
7384
  `Faixa de Refer\xEAncia: ${code} (${sexLabel}${ageLabel})`,
6772
- ` M\xEDnimo: ${fmt(ref.min)} ${ref.unit}`,
6773
- ` M\xE1ximo: ${fmt(ref.max)} ${ref.unit}`,
7385
+ ` Tipo: ${ref.kind ? tipos[ref.kind] : "\u2014"}`,
7386
+ ` M\xEDnimo: ${fmt(ref.min)} ${ref.unit}${desenho(ref.minKind)}`,
7387
+ ` M\xE1ximo: ${fmt(ref.max)} ${ref.unit}${desenho(ref.maxKind)}`,
6774
7388
  ` \xD3timo (m\xEDn): ${fmt(ref.optimalMin)} ${ref.unit}`,
6775
7389
  ` \xD3timo (m\xE1x): ${fmt(ref.optimalMax)} ${ref.unit}`,
6776
7390
  ` Alerta (m\xE1x): ${fmt(ref.warningMax)} ${ref.unit}`,
@@ -7263,7 +7877,7 @@ var COMMANDS = {
7263
7877
  async function main() {
7264
7878
  const { command, help, json, resto, version } = dividirArgv(process.argv.slice(2));
7265
7879
  if (version) {
7266
- process.stdout.write(`${"0.32.0"}
7880
+ process.stdout.write(`${"0.34.0"}
7267
7881
  `);
7268
7882
  return;
7269
7883
  }