@precisa-saude/fhir 0.33.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/biomarkers.cjs +4 -2
- package/dist/biomarkers.cjs.map +1 -1
- package/dist/biomarkers.d.cts +34 -1
- package/dist/biomarkers.d.ts +34 -1
- package/dist/biomarkers.js +3 -1
- package/dist/{chunk-X5WVIJGG.js → chunk-6QUGBBV2.js} +12 -3
- package/dist/chunk-6QUGBBV2.js.map +1 -0
- package/dist/{chunk-ZCIZUBMR.cjs → chunk-77LDD6OP.cjs} +20 -11
- package/dist/chunk-77LDD6OP.cjs.map +1 -0
- package/dist/{chunk-LXKFVJF4.cjs → chunk-HZSW5T7E.cjs} +447 -63
- package/dist/chunk-HZSW5T7E.cjs.map +1 -0
- package/dist/{chunk-4FKZG5GZ.js → chunk-KQ4CX67G.js} +35 -1
- package/dist/chunk-KQ4CX67G.js.map +1 -0
- package/dist/{chunk-5RC7C7HJ.cjs → chunk-LNL5QSHP.cjs} +3 -3
- package/dist/{chunk-5RC7C7HJ.cjs.map → chunk-LNL5QSHP.cjs.map} +1 -1
- package/dist/{chunk-WXCX5TJJ.js → chunk-O3FXUPW3.js} +445 -61
- package/dist/chunk-O3FXUPW3.js.map +1 -0
- package/dist/{chunk-KS2YZBAS.js → chunk-TLF3IVSN.js} +14 -6
- package/dist/chunk-TLF3IVSN.js.map +1 -0
- package/dist/{chunk-Q3H5C6UR.js → chunk-U2XW6DY4.js} +2 -2
- package/dist/{chunk-OAFAERDY.cjs → chunk-VMYB7KOE.cjs} +3 -3
- package/dist/{chunk-OAFAERDY.cjs.map → chunk-VMYB7KOE.cjs.map} +1 -1
- package/dist/{chunk-FGZXQWSR.cjs → chunk-XAIIZLZ7.cjs} +16 -8
- package/dist/chunk-XAIIZLZ7.cjs.map +1 -0
- package/dist/{chunk-E6MXDQXW.js → chunk-XT635TWP.js} +2 -2
- package/dist/{chunk-T75NZM56.cjs → chunk-YL65SZ6S.cjs} +38 -4
- package/dist/chunk-YL65SZ6S.cjs.map +1 -0
- package/dist/cli.js +1276 -851
- package/dist/{converter-DZH9x52r.d.cts → converter-CTj83kPz.d.ts} +15 -1
- package/dist/{converter-D1Q84Oc9.d.ts → converter-Dlwb6OfB.d.cts} +15 -1
- package/dist/converter.cjs +5 -4
- package/dist/converter.cjs.map +1 -1
- package/dist/converter.d.cts +3 -2
- package/dist/converter.d.ts +3 -2
- package/dist/converter.js +4 -3
- package/dist/{fhir-types-B1KQmlRb.d.ts → fhir-types-Cn5WFbOI.d.cts} +5 -0
- package/dist/{fhir-types-B1KQmlRb.d.cts → fhir-types-Cn5WFbOI.d.ts} +5 -0
- package/dist/importer.cjs +5 -5
- package/dist/importer.d.cts +5 -1
- package/dist/importer.d.ts +5 -1
- package/dist/importer.js +4 -4
- package/dist/index.cjs +16 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +13 -7
- package/dist/index.js.map +1 -1
- package/dist/reference-ranges.cjs +8 -4
- package/dist/reference-ranges.cjs.map +1 -1
- package/dist/reference-ranges.d.cts +83 -1
- package/dist/reference-ranges.d.ts +83 -1
- package/dist/reference-ranges.js +9 -5
- package/dist/units.cjs +3 -3
- package/dist/units.js +2 -2
- package/dist/validators.cjs +4 -4
- package/dist/validators.d.cts +1 -1
- package/dist/validators.d.ts +1 -1
- package/dist/validators.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-4FKZG5GZ.js.map +0 -1
- package/dist/chunk-FGZXQWSR.cjs.map +0 -1
- package/dist/chunk-KS2YZBAS.js.map +0 -1
- package/dist/chunk-LXKFVJF4.cjs.map +0 -1
- package/dist/chunk-T75NZM56.cjs.map +0 -1
- package/dist/chunk-WXCX5TJJ.js.map +0 -1
- package/dist/chunk-X5WVIJGG.js.map +0 -1
- package/dist/chunk-ZCIZUBMR.cjs.map +0 -1
- /package/dist/{chunk-Q3H5C6UR.js.map → chunk-U2XW6DY4.js.map} +0 -0
- /package/dist/{chunk-E6MXDQXW.js.map → chunk-XT635TWP.js.map} +0 -0
package/README.md
CHANGED
|
@@ -107,6 +107,8 @@ const range = getReferenceRange('TSH', {
|
|
|
107
107
|
// → { max: 2.5, min: 0.1, ... } (alvo ATA 2017 para 1º trimestre)
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
+
Cada faixa diz que tipo de afirmação ela é (`kind`: intervalo de referência do ensaio, limiar de decisão de diretriz ou distribuição populacional) e, por limite, se ele é corte clínico ou só desenho (`minKind`/`maxKind`). Para sinalizar um valor contra a faixa do catálogo, use `flagAgainstCatalogRange`, que ignora os limites de desenho; o `direction` diz para que lado o marcador melhora e não decide flag. Detalhes em [docs/biomarcadores.md](../../docs/biomarcadores.md#tipo-da-faixa).
|
|
111
|
+
|
|
110
112
|
O metadado opcional `fastingRequired` em `BiomarkerReferenceRange` indica se a amostra exige jejum estrito (ex.: `Glucose`, `Insulin`, `HOMA_IR`) ou se o jejum é preferido mas não obrigatório (ex.: `Triglycerides`, que aceita dosagem não-jejum por SBC 2017/2025).
|
|
111
113
|
|
|
112
114
|
## Aviso médico
|
package/dist/biomarkers.cjs
CHANGED
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
var _chunkT75NZM56cjs = require('./chunk-T75NZM56.cjs');
|
|
34
33
|
|
|
34
|
+
var _chunkYL65SZ6Scjs = require('./chunk-YL65SZ6S.cjs');
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
|
|
@@ -63,5 +63,7 @@ var _chunkT75NZM56cjs = require('./chunk-T75NZM56.cjs');
|
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
exports.BIOMARKER_DEFINITIONS = _chunkYL65SZ6Scjs.BIOMARKER_DEFINITIONS; exports.CAC_INDICATOR_CODES = _chunkYL65SZ6Scjs.CAC_INDICATOR_CODES; exports.DEXA_CATEGORIES = _chunkYL65SZ6Scjs.DEXA_CATEGORIES; exports.DEXA_INDICATOR_CODES = _chunkYL65SZ6Scjs.DEXA_INDICATOR_CODES; exports.codeToLoinc = _chunkYL65SZ6Scjs.codeToLoinc; exports.filterVisibleBiomarkers = _chunkYL65SZ6Scjs.filterVisibleBiomarkers; exports.findCodeByName = _chunkYL65SZ6Scjs.findCodeByName; exports.generateCacFullReference = _chunkYL65SZ6Scjs.generateCacFullReference; exports.generateDexaFullReference = _chunkYL65SZ6Scjs.generateDexaFullReference; exports.generateFilteredLLMReference = _chunkYL65SZ6Scjs.generateFilteredLLMReference; exports.generateLLMReference = _chunkYL65SZ6Scjs.generateLLMReference; exports.getAllCodes = _chunkYL65SZ6Scjs.getAllCodes; exports.getAllDefinitions = _chunkYL65SZ6Scjs.getAllDefinitions; exports.getAllLoincCodes = _chunkYL65SZ6Scjs.getAllLoincCodes; exports.getAllSearchPatterns = _chunkYL65SZ6Scjs.getAllSearchPatterns; exports.getBiomarkersByCategory = _chunkYL65SZ6Scjs.getBiomarkersByCategory; exports.getBiomarkersForCategories = _chunkYL65SZ6Scjs.getBiomarkersForCategories; exports.getDefinitionByCode = _chunkYL65SZ6Scjs.getDefinitionByCode; exports.getDefinitionByLoinc = _chunkYL65SZ6Scjs.getDefinitionByLoinc; exports.getDefinitionsBySex = _chunkYL65SZ6Scjs.getDefinitionsBySex; exports.getSexForCode = _chunkYL65SZ6Scjs.getSexForCode; exports.getVisibleDefinitions = _chunkYL65SZ6Scjs.getVisibleDefinitions; exports.isBiomarkerVisible = _chunkYL65SZ6Scjs.isBiomarkerVisible; exports.isCacDocument = _chunkYL65SZ6Scjs.isCacDocument; exports.isDexaDocument = _chunkYL65SZ6Scjs.isDexaDocument; exports.isValidCode = _chunkYL65SZ6Scjs.isValidCode; exports.isValidLoinc = _chunkYL65SZ6Scjs.isValidLoinc; exports.loincToCode = _chunkYL65SZ6Scjs.loincToCode; exports.methodVariantOf = _chunkYL65SZ6Scjs.methodVariantOf; exports.normalizeCode = _chunkYL65SZ6Scjs.normalizeCode; exports.toBiomarkerTests = _chunkYL65SZ6Scjs.toBiomarkerTests; exports.validateLoincNameMatch = _chunkYL65SZ6Scjs.validateLoincNameMatch;
|
|
67
69
|
//# sourceMappingURL=biomarkers.cjs.map
|
package/dist/biomarkers.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/fhir-brasil/fhir-brasil/packages/core/dist/biomarkers.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/fhir-brasil/fhir-brasil/packages/core/dist/biomarkers.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,mlEAAC","file":"/home/runner/work/fhir-brasil/fhir-brasil/packages/core/dist/biomarkers.cjs"}
|
package/dist/biomarkers.d.cts
CHANGED
|
@@ -11,6 +11,30 @@
|
|
|
11
11
|
* The LLM extraction prompt includes these definitions so it can output
|
|
12
12
|
* LOINC codes directly, eliminating the need for name-matching logic.
|
|
13
13
|
*/
|
|
14
|
+
/**
|
|
15
|
+
* Um código LOINC irmão do da entrada, que difere só no eixo Method.
|
|
16
|
+
*
|
|
17
|
+
* O `loinc` da entrada é o código sem método, e continua sendo o padrão: a
|
|
18
|
+
* variante só vale quando o laudo afirma o método por escrito. Separado de
|
|
19
|
+
* `loincAliases`, que quer dizer "código antigo para a mesma coisa".
|
|
20
|
+
*/
|
|
21
|
+
interface MethodVariant {
|
|
22
|
+
/**
|
|
23
|
+
* Trechos que afirmam o método, comparados por token inteiro como as grafias
|
|
24
|
+
* de `names`, na linha do exame e nas seguintes até o próximo exame. Só entra
|
|
25
|
+
* pista que aparece em laudo real; variante sem pista com evidência fica com
|
|
26
|
+
* as listas vazias e não é escolhida pela varredura.
|
|
27
|
+
*/
|
|
28
|
+
cues: {
|
|
29
|
+
en: string[];
|
|
30
|
+
pt: string[];
|
|
31
|
+
};
|
|
32
|
+
loinc: string;
|
|
33
|
+
/** O texto da parte Method do LOINC, como o snapshot a registra. */
|
|
34
|
+
method: string;
|
|
35
|
+
/** Por que este código, e de onde veio a pista. */
|
|
36
|
+
note?: string;
|
|
37
|
+
}
|
|
14
38
|
interface BiomarkerDefinition {
|
|
15
39
|
category: string | string[];
|
|
16
40
|
code: string;
|
|
@@ -18,6 +42,8 @@ interface BiomarkerDefinition {
|
|
|
18
42
|
hidden?: boolean;
|
|
19
43
|
loinc?: string;
|
|
20
44
|
loincAliases?: string[];
|
|
45
|
+
/** Ver `MethodVariant`. */
|
|
46
|
+
methodVariants?: MethodVariant[];
|
|
21
47
|
names: {
|
|
22
48
|
en: string[];
|
|
23
49
|
pt: string[];
|
|
@@ -35,6 +61,13 @@ declare const BIOMARKER_DEFINITIONS: BiomarkerDefinition[];
|
|
|
35
61
|
* Convert LOINC code to internal code
|
|
36
62
|
*/
|
|
37
63
|
declare function loincToCode(loinc: string): string | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* A variante por método de um biomarcador, quando `loinc` é uma delas.
|
|
66
|
+
*
|
|
67
|
+
* Devolve `undefined` para o código sem método e para código que não é
|
|
68
|
+
* variante declarada daquele biomarcador.
|
|
69
|
+
*/
|
|
70
|
+
declare function methodVariantOf(code: string, loinc: string): MethodVariant | undefined;
|
|
38
71
|
/**
|
|
39
72
|
* Convert internal code to LOINC
|
|
40
73
|
*/
|
|
@@ -241,4 +274,4 @@ declare function getBiomarkersForCategories(categories: string[], options?: {
|
|
|
241
274
|
sex?: 'male' | 'female' | 'both';
|
|
242
275
|
}): Record<string, BiomarkerDefinition[]>;
|
|
243
276
|
|
|
244
|
-
export { BIOMARKER_DEFINITIONS, type BiomarkerDefinition, type BiomarkerSearchPattern, CAC_INDICATOR_CODES, DEXA_CATEGORIES, DEXA_INDICATOR_CODES, type SupportedBiomarker, codeToLoinc, filterVisibleBiomarkers, findCodeByName, generateCacFullReference, generateDexaFullReference, generateFilteredLLMReference, generateLLMReference, getAllCodes, getAllDefinitions, getAllLoincCodes, getAllSearchPatterns, getBiomarkersByCategory, getBiomarkersForCategories, getDefinitionByCode, getDefinitionByLoinc, getDefinitionsBySex, getSexForCode, getVisibleDefinitions, isBiomarkerVisible, isCacDocument, isDexaDocument, isValidCode, isValidLoinc, loincToCode, normalizeCode, toBiomarkerTests, validateLoincNameMatch };
|
|
277
|
+
export { BIOMARKER_DEFINITIONS, type BiomarkerDefinition, type BiomarkerSearchPattern, CAC_INDICATOR_CODES, DEXA_CATEGORIES, DEXA_INDICATOR_CODES, type MethodVariant, type SupportedBiomarker, codeToLoinc, filterVisibleBiomarkers, findCodeByName, generateCacFullReference, generateDexaFullReference, generateFilteredLLMReference, generateLLMReference, getAllCodes, getAllDefinitions, getAllLoincCodes, getAllSearchPatterns, getBiomarkersByCategory, getBiomarkersForCategories, getDefinitionByCode, getDefinitionByLoinc, getDefinitionsBySex, getSexForCode, getVisibleDefinitions, isBiomarkerVisible, isCacDocument, isDexaDocument, isValidCode, isValidLoinc, loincToCode, methodVariantOf, normalizeCode, toBiomarkerTests, validateLoincNameMatch };
|
package/dist/biomarkers.d.ts
CHANGED
|
@@ -11,6 +11,30 @@
|
|
|
11
11
|
* The LLM extraction prompt includes these definitions so it can output
|
|
12
12
|
* LOINC codes directly, eliminating the need for name-matching logic.
|
|
13
13
|
*/
|
|
14
|
+
/**
|
|
15
|
+
* Um código LOINC irmão do da entrada, que difere só no eixo Method.
|
|
16
|
+
*
|
|
17
|
+
* O `loinc` da entrada é o código sem método, e continua sendo o padrão: a
|
|
18
|
+
* variante só vale quando o laudo afirma o método por escrito. Separado de
|
|
19
|
+
* `loincAliases`, que quer dizer "código antigo para a mesma coisa".
|
|
20
|
+
*/
|
|
21
|
+
interface MethodVariant {
|
|
22
|
+
/**
|
|
23
|
+
* Trechos que afirmam o método, comparados por token inteiro como as grafias
|
|
24
|
+
* de `names`, na linha do exame e nas seguintes até o próximo exame. Só entra
|
|
25
|
+
* pista que aparece em laudo real; variante sem pista com evidência fica com
|
|
26
|
+
* as listas vazias e não é escolhida pela varredura.
|
|
27
|
+
*/
|
|
28
|
+
cues: {
|
|
29
|
+
en: string[];
|
|
30
|
+
pt: string[];
|
|
31
|
+
};
|
|
32
|
+
loinc: string;
|
|
33
|
+
/** O texto da parte Method do LOINC, como o snapshot a registra. */
|
|
34
|
+
method: string;
|
|
35
|
+
/** Por que este código, e de onde veio a pista. */
|
|
36
|
+
note?: string;
|
|
37
|
+
}
|
|
14
38
|
interface BiomarkerDefinition {
|
|
15
39
|
category: string | string[];
|
|
16
40
|
code: string;
|
|
@@ -18,6 +42,8 @@ interface BiomarkerDefinition {
|
|
|
18
42
|
hidden?: boolean;
|
|
19
43
|
loinc?: string;
|
|
20
44
|
loincAliases?: string[];
|
|
45
|
+
/** Ver `MethodVariant`. */
|
|
46
|
+
methodVariants?: MethodVariant[];
|
|
21
47
|
names: {
|
|
22
48
|
en: string[];
|
|
23
49
|
pt: string[];
|
|
@@ -35,6 +61,13 @@ declare const BIOMARKER_DEFINITIONS: BiomarkerDefinition[];
|
|
|
35
61
|
* Convert LOINC code to internal code
|
|
36
62
|
*/
|
|
37
63
|
declare function loincToCode(loinc: string): string | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* A variante por método de um biomarcador, quando `loinc` é uma delas.
|
|
66
|
+
*
|
|
67
|
+
* Devolve `undefined` para o código sem método e para código que não é
|
|
68
|
+
* variante declarada daquele biomarcador.
|
|
69
|
+
*/
|
|
70
|
+
declare function methodVariantOf(code: string, loinc: string): MethodVariant | undefined;
|
|
38
71
|
/**
|
|
39
72
|
* Convert internal code to LOINC
|
|
40
73
|
*/
|
|
@@ -241,4 +274,4 @@ declare function getBiomarkersForCategories(categories: string[], options?: {
|
|
|
241
274
|
sex?: 'male' | 'female' | 'both';
|
|
242
275
|
}): Record<string, BiomarkerDefinition[]>;
|
|
243
276
|
|
|
244
|
-
export { BIOMARKER_DEFINITIONS, type BiomarkerDefinition, type BiomarkerSearchPattern, CAC_INDICATOR_CODES, DEXA_CATEGORIES, DEXA_INDICATOR_CODES, type SupportedBiomarker, codeToLoinc, filterVisibleBiomarkers, findCodeByName, generateCacFullReference, generateDexaFullReference, generateFilteredLLMReference, generateLLMReference, getAllCodes, getAllDefinitions, getAllLoincCodes, getAllSearchPatterns, getBiomarkersByCategory, getBiomarkersForCategories, getDefinitionByCode, getDefinitionByLoinc, getDefinitionsBySex, getSexForCode, getVisibleDefinitions, isBiomarkerVisible, isCacDocument, isDexaDocument, isValidCode, isValidLoinc, loincToCode, normalizeCode, toBiomarkerTests, validateLoincNameMatch };
|
|
277
|
+
export { BIOMARKER_DEFINITIONS, type BiomarkerDefinition, type BiomarkerSearchPattern, CAC_INDICATOR_CODES, DEXA_CATEGORIES, DEXA_INDICATOR_CODES, type MethodVariant, type SupportedBiomarker, codeToLoinc, filterVisibleBiomarkers, findCodeByName, generateCacFullReference, generateDexaFullReference, generateFilteredLLMReference, generateLLMReference, getAllCodes, getAllDefinitions, getAllLoincCodes, getAllSearchPatterns, getBiomarkersByCategory, getBiomarkersForCategories, getDefinitionByCode, getDefinitionByLoinc, getDefinitionsBySex, getSexForCode, getVisibleDefinitions, isBiomarkerVisible, isCacDocument, isDexaDocument, isValidCode, isValidLoinc, loincToCode, methodVariantOf, normalizeCode, toBiomarkerTests, validateLoincNameMatch };
|
package/dist/biomarkers.js
CHANGED
|
@@ -27,10 +27,11 @@ import {
|
|
|
27
27
|
isValidCode,
|
|
28
28
|
isValidLoinc,
|
|
29
29
|
loincToCode,
|
|
30
|
+
methodVariantOf,
|
|
30
31
|
normalizeCode,
|
|
31
32
|
toBiomarkerTests,
|
|
32
33
|
validateLoincNameMatch
|
|
33
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-KQ4CX67G.js";
|
|
34
35
|
export {
|
|
35
36
|
BIOMARKER_DEFINITIONS,
|
|
36
37
|
CAC_INDICATOR_CODES,
|
|
@@ -60,6 +61,7 @@ export {
|
|
|
60
61
|
isValidCode,
|
|
61
62
|
isValidLoinc,
|
|
62
63
|
loincToCode,
|
|
64
|
+
methodVariantOf,
|
|
63
65
|
normalizeCode,
|
|
64
66
|
toBiomarkerTests,
|
|
65
67
|
validateLoincNameMatch
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
validateFHIRImportBundle
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-XT635TWP.js";
|
|
4
4
|
import {
|
|
5
5
|
BIOMARKER_CODE_SYSTEM,
|
|
6
6
|
LOINC_SYSTEM
|
|
@@ -10,8 +10,9 @@ import {
|
|
|
10
10
|
getDefinitionByCode,
|
|
11
11
|
isValidCode,
|
|
12
12
|
loincToCode,
|
|
13
|
+
methodVariantOf,
|
|
13
14
|
normalizeCode
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-KQ4CX67G.js";
|
|
15
16
|
|
|
16
17
|
// src/importer.ts
|
|
17
18
|
var MAX_OBSERVATIONS = 5e3;
|
|
@@ -107,9 +108,15 @@ function mapFHIRObservationToInternal(observation, index) {
|
|
|
107
108
|
}
|
|
108
109
|
let referenceMin;
|
|
109
110
|
let referenceMax;
|
|
111
|
+
let referenceKind;
|
|
110
112
|
if (observation.referenceRange?.[0]) {
|
|
111
113
|
referenceMin = observation.referenceRange[0].low?.value;
|
|
112
114
|
referenceMax = observation.referenceRange[0].high?.value;
|
|
115
|
+
const meaning = observation.referenceRange[0].type?.coding?.find(
|
|
116
|
+
(c) => c.system === "http://terminology.hl7.org/CodeSystem/referencerange-meaning"
|
|
117
|
+
)?.code;
|
|
118
|
+
if (meaning === "normal") referenceKind = "reference-interval";
|
|
119
|
+
else if (meaning === "recommended") referenceKind = "decision-threshold";
|
|
113
120
|
}
|
|
114
121
|
const imported = {
|
|
115
122
|
biomarkerCode: internalCode,
|
|
@@ -118,6 +125,8 @@ function mapFHIRObservationToInternal(observation, index) {
|
|
|
118
125
|
flag: extractFlag(observation),
|
|
119
126
|
isQualitative,
|
|
120
127
|
loincCode,
|
|
128
|
+
...loincCode && methodVariantOf(internalCode, loincCode) && { methodLoinc: loincCode },
|
|
129
|
+
...referenceKind && { referenceKind },
|
|
121
130
|
referenceMax,
|
|
122
131
|
referenceMin,
|
|
123
132
|
unit: unit || definition?.unit || "",
|
|
@@ -162,4 +171,4 @@ export {
|
|
|
162
171
|
mapFHIRObservationToInternal,
|
|
163
172
|
processImportBundle
|
|
164
173
|
};
|
|
165
|
-
//# sourceMappingURL=chunk-
|
|
174
|
+
//# sourceMappingURL=chunk-6QUGBBV2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/importer.ts"],"sourcesContent":["/**\n * FHIR Importer\n *\n * Parses FHIR R4 Bundles and extracts Observation resources with known LOINC codes,\n * mapping them to internal biomarker codes for storage as lab results.\n */\n\nimport {\n codeToLoinc,\n getDefinitionByCode,\n isValidCode,\n loincToCode,\n methodVariantOf,\n normalizeCode,\n} from './biomarkers';\nimport { BIOMARKER_CODE_SYSTEM, LOINC_SYSTEM } from './code-systems';\nimport type { FHIRBundle, FHIRObservation } from './fhir-types';\nimport { validateFHIRImportBundle } from './validators';\n\nexport interface ImportedObservation {\n biomarkerCode: string;\n biomarkerName: string;\n collectionDate: string;\n flag: 'H' | 'L' | '';\n isQualitative: boolean;\n /** Ausente nos biomarcadores sem LOINC publicado, como composição corporal. */\n loincCode?: string;\n /** O `loincCode` quando ele é uma das variantes por método do biomarcador. */\n methodLoinc?: string;\n /** Lido do `referenceRange.type`, quando o Bundle o traz. */\n referenceKind?: 'decision-threshold' | 'reference-interval';\n referenceMax?: number;\n referenceMin?: number;\n unit: string;\n value: number | string;\n}\n\nexport interface SkippedEntry {\n index: number;\n loincCode?: string;\n reason: string;\n resourceType?: string;\n}\n\nexport interface ImportError {\n details: string;\n field: string;\n}\n\nexport interface FHIRImportResult {\n errors: ImportError[];\n imported: ImportedObservation[];\n skipped: SkippedEntry[];\n totalProcessed: number;\n}\n\n/**\n * Limites de importação.\n *\n * Uma Observation exportada ocupa cerca de 1,25KB em JSON compacto e 2,75KB\n * quando o arquivo vem indentado, medido sobre um histórico real de 998\n * Observations em 61 laudos. Nesse tamanho, 5000 Observations dão 6,0MB\n * compactos ou 13,1MB indentados, e por isso o teto de arquivo é 15MB: cobre\n * as duas formas com folga.\n *\n * Na densidade desse mesmo histórico (16 Observations por laudo), 5000\n * equivalem a cerca de 300 laudos.\n */\nconst MAX_OBSERVATIONS = 5000;\nconst MAX_FILE_SIZE = 15 * 1024 * 1024; // 15MB\n\n/**\n * Resolve o código interno do biomarcador a partir do `code.coding`.\n *\n * LOINC primeiro, que é o vocabulário que arquivos de terceiros usam. Quando\n * não resolve, cai para o coding de códigos internos, presente nos arquivos\n * exportados pela própria plataforma.\n *\n * O fallback cobre dois casos: biomarcadores sem LOINC publicado (composição\n * corporal, densidade óssea, escore de cálcio) e arquivos antigos, exportados\n * quando esses biomarcadores saíam com o placeholder `99999-9`, que não\n * resolve para nada.\n */\nfunction resolveBiomarkerCode(observation: FHIRObservation): {\n internalCode?: string;\n loincCode?: string;\n reason: string;\n} {\n const coding = observation.code?.coding ?? [];\n const loincCode = coding.find((c) => c.system === LOINC_SYSTEM)?.code;\n const declaredCode = coding.find((c) => c.system === BIOMARKER_CODE_SYSTEM)?.code;\n\n const seenCodes = [\n ...(loincCode ? [`LOINC ${loincCode}`] : []),\n ...(declaredCode ? [`biomarker code ${declaredCode}`] : []),\n ];\n\n // Três motivos distintos de descarte, que antes se confundiam num só. Sem\n // essa separação, um arquivo em SNOMED relatava \"nenhum código encontrado\",\n // sugerindo `coding` vazio quando na verdade o código existia e estava num\n // system que não tratamos. Para importação de terceiros, é a diferença entre\n // um diagnóstico acionável e um enigma.\n let reason: string;\n if (seenCodes.length > 0) {\n reason = `Unknown code: ${seenCodes.join(', ')}`;\n } else if (coding.length > 0) {\n const systems = [...new Set(coding.map((c) => c.system ?? '(sem system)'))];\n reason = `No code in a supported system (found: ${systems.join(', ')})`;\n } else {\n reason = 'No code found in observation coding';\n }\n\n const fromLoinc = loincCode ? loincToCode(loincCode) : undefined;\n if (fromLoinc) return { internalCode: fromLoinc, loincCode, reason };\n\n if (declaredCode && isValidCode(declaredCode)) {\n // `isValidCode` aceita alias, mas alias não serve como código armazenado:\n // 49 definições têm um, e tanto `codeToLoinc` quanto as faixas de\n // referência são indexadas só pelo canônico. Sem normalizar, `VLDL_Cholesterol`\n // entraria no lugar de `VLDL` e perderia o LOINC 13458-5 que ele tem.\n // Importar um Bundle é fronteira de dados, que é onde `normalizeCode` deve\n // ser aplicado.\n const canonical = normalizeCode(declaredCode);\n\n // `codeToLoinc` devolve undefined para quem não tem LOINC, que é o caso\n // esperado aqui. O campo fica de fora em vez de receber um valor inventado.\n return { internalCode: canonical, loincCode: codeToLoinc(canonical), reason };\n }\n\n return { loincCode, reason };\n}\n\n/**\n * Extract interpretation flag from Observation\n */\nfunction extractFlag(observation: FHIRObservation): 'H' | 'L' | '' {\n const code = observation.interpretation?.[0]?.coding?.[0]?.code;\n if (code === 'H' || code === 'HH') return 'H';\n if (code === 'L' || code === 'LL') return 'L';\n return '';\n}\n\n/**\n * Extract Observation resources from a FHIR Bundle\n */\nexport function extractObservationsFromBundle(bundle: FHIRBundle): {\n observations: FHIRObservation[];\n skipped: SkippedEntry[];\n} {\n const observations: FHIRObservation[] = [];\n const skipped: SkippedEntry[] = [];\n\n for (let i = 0; i < bundle.entry.length; i++) {\n const entry = bundle.entry[i]!;\n if (!entry.resource) {\n skipped.push({ index: i, reason: 'Entry has no resource' });\n continue;\n }\n\n if (entry.resource.resourceType !== 'Observation') {\n // Non-observation resources are silently skipped (Patient, DiagnosticReport, etc.)\n continue;\n }\n\n if (observations.length >= MAX_OBSERVATIONS) {\n skipped.push({ index: i, reason: `Maximum of ${MAX_OBSERVATIONS} observations exceeded` });\n continue;\n }\n\n observations.push(entry.resource as FHIRObservation);\n }\n\n return { observations, skipped };\n}\n\n/**\n * Map a FHIR Observation to internal format using LOINC→biomarker code lookup\n */\nexport function mapFHIRObservationToInternal(\n observation: FHIRObservation,\n index: number,\n): { observation: ImportedObservation } | { skipped: SkippedEntry } {\n const { internalCode, loincCode, reason } = resolveBiomarkerCode(observation);\n\n if (!internalCode) {\n return {\n skipped: { index, loincCode, reason, resourceType: 'Observation' },\n };\n }\n\n const definition = getDefinitionByCode(internalCode);\n\n // Extract value\n let value: number | string;\n let unit = '';\n let isQualitative = false;\n\n if (observation.valueQuantity?.value !== undefined) {\n value = observation.valueQuantity.value;\n unit = observation.valueQuantity.unit || observation.valueQuantity.code || '';\n } else if (observation.valueString) {\n value = observation.valueString;\n isQualitative = true;\n } else {\n return {\n skipped: {\n index,\n loincCode,\n reason: 'Observation has no value (valueQuantity or valueString)',\n resourceType: 'Observation',\n },\n };\n }\n\n // Extract collection date (effectiveDateTime or effectivePeriod.start)\n const collectionDate = observation.effectiveDateTime || observation.effectivePeriod?.start || '';\n if (!collectionDate) {\n return {\n skipped: {\n index,\n loincCode,\n reason: 'Observation has no effectiveDateTime or effectivePeriod.start',\n resourceType: 'Observation',\n },\n };\n }\n\n // Extract reference ranges\n let referenceMin: number | undefined;\n let referenceMax: number | undefined;\n let referenceKind: ImportedObservation['referenceKind'];\n if (observation.referenceRange?.[0]) {\n referenceMin = observation.referenceRange[0].low?.value;\n referenceMax = observation.referenceRange[0].high?.value;\n const meaning = observation.referenceRange[0].type?.coding?.find(\n (c) => c.system === 'http://terminology.hl7.org/CodeSystem/referencerange-meaning',\n )?.code;\n if (meaning === 'normal') referenceKind = 'reference-interval';\n else if (meaning === 'recommended') referenceKind = 'decision-threshold';\n }\n\n const imported: ImportedObservation = {\n biomarkerCode: internalCode,\n biomarkerName:\n definition?.names.pt[0] || definition?.names.en[0] || observation.code.text || internalCode,\n collectionDate,\n flag: extractFlag(observation),\n isQualitative,\n loincCode,\n ...(loincCode && methodVariantOf(internalCode, loincCode) && { methodLoinc: loincCode }),\n ...(referenceKind && { referenceKind }),\n referenceMax,\n referenceMin,\n unit: unit || definition?.unit || '',\n value,\n };\n\n return { observation: imported };\n}\n\n/**\n * Process a complete FHIR Bundle for import\n */\nexport function processImportBundle(data: unknown): FHIRImportResult {\n // Structural validation\n const validationErrors = validateFHIRImportBundle(data);\n if (validationErrors.length > 0) {\n return {\n errors: validationErrors,\n imported: [],\n skipped: [],\n totalProcessed: 0,\n };\n }\n\n const bundle = data as FHIRBundle;\n\n // Extract observations\n const { observations, skipped } = extractObservationsFromBundle(bundle);\n\n // Map each observation to internal format\n const imported: ImportedObservation[] = [];\n const allSkipped: SkippedEntry[] = [...skipped];\n\n for (let i = 0; i < observations.length; i++) {\n const result = mapFHIRObservationToInternal(observations[i]!, i);\n\n if ('observation' in result) {\n imported.push(result.observation);\n } else {\n allSkipped.push(result.skipped);\n }\n }\n\n return {\n errors: [],\n imported,\n skipped: allSkipped,\n totalProcessed: observations.length,\n };\n}\n\nexport { MAX_FILE_SIZE, MAX_OBSERVATIONS };\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkVMYB7KOEcjs = require('./chunk-VMYB7KOE.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
@@ -11,7 +11,8 @@ var _chunkOR67NJDZcjs = require('./chunk-OR67NJDZ.cjs');
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
var _chunkYL65SZ6Scjs = require('./chunk-YL65SZ6S.cjs');
|
|
15
16
|
|
|
16
17
|
// src/importer.ts
|
|
17
18
|
var MAX_OBSERVATIONS = 5e3;
|
|
@@ -33,11 +34,11 @@ function resolveBiomarkerCode(observation) {
|
|
|
33
34
|
} else {
|
|
34
35
|
reason = "No code found in observation coding";
|
|
35
36
|
}
|
|
36
|
-
const fromLoinc = loincCode ?
|
|
37
|
+
const fromLoinc = loincCode ? _chunkYL65SZ6Scjs.loincToCode.call(void 0, loincCode) : void 0;
|
|
37
38
|
if (fromLoinc) return { internalCode: fromLoinc, loincCode, reason };
|
|
38
|
-
if (declaredCode &&
|
|
39
|
-
const canonical =
|
|
40
|
-
return { internalCode: canonical, loincCode:
|
|
39
|
+
if (declaredCode && _chunkYL65SZ6Scjs.isValidCode.call(void 0, declaredCode)) {
|
|
40
|
+
const canonical = _chunkYL65SZ6Scjs.normalizeCode.call(void 0, declaredCode);
|
|
41
|
+
return { internalCode: canonical, loincCode: _chunkYL65SZ6Scjs.codeToLoinc.call(void 0, canonical), reason };
|
|
41
42
|
}
|
|
42
43
|
return { loincCode, reason };
|
|
43
44
|
}
|
|
@@ -74,7 +75,7 @@ function mapFHIRObservationToInternal(observation, index) {
|
|
|
74
75
|
skipped: { index, loincCode, reason, resourceType: "Observation" }
|
|
75
76
|
};
|
|
76
77
|
}
|
|
77
|
-
const definition =
|
|
78
|
+
const definition = _chunkYL65SZ6Scjs.getDefinitionByCode.call(void 0, internalCode);
|
|
78
79
|
let value;
|
|
79
80
|
let unit = "";
|
|
80
81
|
let isQualitative = false;
|
|
@@ -107,26 +108,34 @@ function mapFHIRObservationToInternal(observation, index) {
|
|
|
107
108
|
}
|
|
108
109
|
let referenceMin;
|
|
109
110
|
let referenceMax;
|
|
111
|
+
let referenceKind;
|
|
110
112
|
if (_optionalChain([observation, 'access', _18 => _18.referenceRange, 'optionalAccess', _19 => _19[0]])) {
|
|
111
113
|
referenceMin = _optionalChain([observation, 'access', _20 => _20.referenceRange, 'access', _21 => _21[0], 'access', _22 => _22.low, 'optionalAccess', _23 => _23.value]);
|
|
112
114
|
referenceMax = _optionalChain([observation, 'access', _24 => _24.referenceRange, 'access', _25 => _25[0], 'access', _26 => _26.high, 'optionalAccess', _27 => _27.value]);
|
|
115
|
+
const meaning = _optionalChain([observation, 'access', _28 => _28.referenceRange, 'access', _29 => _29[0], 'access', _30 => _30.type, 'optionalAccess', _31 => _31.coding, 'optionalAccess', _32 => _32.find, 'call', _33 => _33(
|
|
116
|
+
(c) => c.system === "http://terminology.hl7.org/CodeSystem/referencerange-meaning"
|
|
117
|
+
), 'optionalAccess', _34 => _34.code]);
|
|
118
|
+
if (meaning === "normal") referenceKind = "reference-interval";
|
|
119
|
+
else if (meaning === "recommended") referenceKind = "decision-threshold";
|
|
113
120
|
}
|
|
114
121
|
const imported = {
|
|
115
122
|
biomarkerCode: internalCode,
|
|
116
|
-
biomarkerName: _optionalChain([definition, 'optionalAccess',
|
|
123
|
+
biomarkerName: _optionalChain([definition, 'optionalAccess', _35 => _35.names, 'access', _36 => _36.pt, 'access', _37 => _37[0]]) || _optionalChain([definition, 'optionalAccess', _38 => _38.names, 'access', _39 => _39.en, 'access', _40 => _40[0]]) || observation.code.text || internalCode,
|
|
117
124
|
collectionDate,
|
|
118
125
|
flag: extractFlag(observation),
|
|
119
126
|
isQualitative,
|
|
120
127
|
loincCode,
|
|
128
|
+
...loincCode && _chunkYL65SZ6Scjs.methodVariantOf.call(void 0, internalCode, loincCode) && { methodLoinc: loincCode },
|
|
129
|
+
...referenceKind && { referenceKind },
|
|
121
130
|
referenceMax,
|
|
122
131
|
referenceMin,
|
|
123
|
-
unit: unit || _optionalChain([definition, 'optionalAccess',
|
|
132
|
+
unit: unit || _optionalChain([definition, 'optionalAccess', _41 => _41.unit]) || "",
|
|
124
133
|
value
|
|
125
134
|
};
|
|
126
135
|
return { observation: imported };
|
|
127
136
|
}
|
|
128
137
|
function processImportBundle(data) {
|
|
129
|
-
const validationErrors =
|
|
138
|
+
const validationErrors = _chunkVMYB7KOEcjs.validateFHIRImportBundle.call(void 0, data);
|
|
130
139
|
if (validationErrors.length > 0) {
|
|
131
140
|
return {
|
|
132
141
|
errors: validationErrors,
|
|
@@ -162,4 +171,4 @@ function processImportBundle(data) {
|
|
|
162
171
|
|
|
163
172
|
|
|
164
173
|
exports.MAX_OBSERVATIONS = MAX_OBSERVATIONS; exports.MAX_FILE_SIZE = MAX_FILE_SIZE; exports.extractObservationsFromBundle = extractObservationsFromBundle; exports.mapFHIRObservationToInternal = mapFHIRObservationToInternal; exports.processImportBundle = processImportBundle;
|
|
165
|
-
//# sourceMappingURL=chunk-
|
|
174
|
+
//# sourceMappingURL=chunk-77LDD6OP.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/fhir-brasil/fhir-brasil/packages/core/dist/chunk-77LDD6OP.cjs","../src/importer.ts"],"names":[],"mappings":"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","file":"/home/runner/work/fhir-brasil/fhir-brasil/packages/core/dist/chunk-77LDD6OP.cjs","sourcesContent":[null,"/**\n * FHIR Importer\n *\n * Parses FHIR R4 Bundles and extracts Observation resources with known LOINC codes,\n * mapping them to internal biomarker codes for storage as lab results.\n */\n\nimport {\n codeToLoinc,\n getDefinitionByCode,\n isValidCode,\n loincToCode,\n methodVariantOf,\n normalizeCode,\n} from './biomarkers';\nimport { BIOMARKER_CODE_SYSTEM, LOINC_SYSTEM } from './code-systems';\nimport type { FHIRBundle, FHIRObservation } from './fhir-types';\nimport { validateFHIRImportBundle } from './validators';\n\nexport interface ImportedObservation {\n biomarkerCode: string;\n biomarkerName: string;\n collectionDate: string;\n flag: 'H' | 'L' | '';\n isQualitative: boolean;\n /** Ausente nos biomarcadores sem LOINC publicado, como composição corporal. */\n loincCode?: string;\n /** O `loincCode` quando ele é uma das variantes por método do biomarcador. */\n methodLoinc?: string;\n /** Lido do `referenceRange.type`, quando o Bundle o traz. */\n referenceKind?: 'decision-threshold' | 'reference-interval';\n referenceMax?: number;\n referenceMin?: number;\n unit: string;\n value: number | string;\n}\n\nexport interface SkippedEntry {\n index: number;\n loincCode?: string;\n reason: string;\n resourceType?: string;\n}\n\nexport interface ImportError {\n details: string;\n field: string;\n}\n\nexport interface FHIRImportResult {\n errors: ImportError[];\n imported: ImportedObservation[];\n skipped: SkippedEntry[];\n totalProcessed: number;\n}\n\n/**\n * Limites de importação.\n *\n * Uma Observation exportada ocupa cerca de 1,25KB em JSON compacto e 2,75KB\n * quando o arquivo vem indentado, medido sobre um histórico real de 998\n * Observations em 61 laudos. Nesse tamanho, 5000 Observations dão 6,0MB\n * compactos ou 13,1MB indentados, e por isso o teto de arquivo é 15MB: cobre\n * as duas formas com folga.\n *\n * Na densidade desse mesmo histórico (16 Observations por laudo), 5000\n * equivalem a cerca de 300 laudos.\n */\nconst MAX_OBSERVATIONS = 5000;\nconst MAX_FILE_SIZE = 15 * 1024 * 1024; // 15MB\n\n/**\n * Resolve o código interno do biomarcador a partir do `code.coding`.\n *\n * LOINC primeiro, que é o vocabulário que arquivos de terceiros usam. Quando\n * não resolve, cai para o coding de códigos internos, presente nos arquivos\n * exportados pela própria plataforma.\n *\n * O fallback cobre dois casos: biomarcadores sem LOINC publicado (composição\n * corporal, densidade óssea, escore de cálcio) e arquivos antigos, exportados\n * quando esses biomarcadores saíam com o placeholder `99999-9`, que não\n * resolve para nada.\n */\nfunction resolveBiomarkerCode(observation: FHIRObservation): {\n internalCode?: string;\n loincCode?: string;\n reason: string;\n} {\n const coding = observation.code?.coding ?? [];\n const loincCode = coding.find((c) => c.system === LOINC_SYSTEM)?.code;\n const declaredCode = coding.find((c) => c.system === BIOMARKER_CODE_SYSTEM)?.code;\n\n const seenCodes = [\n ...(loincCode ? [`LOINC ${loincCode}`] : []),\n ...(declaredCode ? [`biomarker code ${declaredCode}`] : []),\n ];\n\n // Três motivos distintos de descarte, que antes se confundiam num só. Sem\n // essa separação, um arquivo em SNOMED relatava \"nenhum código encontrado\",\n // sugerindo `coding` vazio quando na verdade o código existia e estava num\n // system que não tratamos. Para importação de terceiros, é a diferença entre\n // um diagnóstico acionável e um enigma.\n let reason: string;\n if (seenCodes.length > 0) {\n reason = `Unknown code: ${seenCodes.join(', ')}`;\n } else if (coding.length > 0) {\n const systems = [...new Set(coding.map((c) => c.system ?? '(sem system)'))];\n reason = `No code in a supported system (found: ${systems.join(', ')})`;\n } else {\n reason = 'No code found in observation coding';\n }\n\n const fromLoinc = loincCode ? loincToCode(loincCode) : undefined;\n if (fromLoinc) return { internalCode: fromLoinc, loincCode, reason };\n\n if (declaredCode && isValidCode(declaredCode)) {\n // `isValidCode` aceita alias, mas alias não serve como código armazenado:\n // 49 definições têm um, e tanto `codeToLoinc` quanto as faixas de\n // referência são indexadas só pelo canônico. Sem normalizar, `VLDL_Cholesterol`\n // entraria no lugar de `VLDL` e perderia o LOINC 13458-5 que ele tem.\n // Importar um Bundle é fronteira de dados, que é onde `normalizeCode` deve\n // ser aplicado.\n const canonical = normalizeCode(declaredCode);\n\n // `codeToLoinc` devolve undefined para quem não tem LOINC, que é o caso\n // esperado aqui. O campo fica de fora em vez de receber um valor inventado.\n return { internalCode: canonical, loincCode: codeToLoinc(canonical), reason };\n }\n\n return { loincCode, reason };\n}\n\n/**\n * Extract interpretation flag from Observation\n */\nfunction extractFlag(observation: FHIRObservation): 'H' | 'L' | '' {\n const code = observation.interpretation?.[0]?.coding?.[0]?.code;\n if (code === 'H' || code === 'HH') return 'H';\n if (code === 'L' || code === 'LL') return 'L';\n return '';\n}\n\n/**\n * Extract Observation resources from a FHIR Bundle\n */\nexport function extractObservationsFromBundle(bundle: FHIRBundle): {\n observations: FHIRObservation[];\n skipped: SkippedEntry[];\n} {\n const observations: FHIRObservation[] = [];\n const skipped: SkippedEntry[] = [];\n\n for (let i = 0; i < bundle.entry.length; i++) {\n const entry = bundle.entry[i]!;\n if (!entry.resource) {\n skipped.push({ index: i, reason: 'Entry has no resource' });\n continue;\n }\n\n if (entry.resource.resourceType !== 'Observation') {\n // Non-observation resources are silently skipped (Patient, DiagnosticReport, etc.)\n continue;\n }\n\n if (observations.length >= MAX_OBSERVATIONS) {\n skipped.push({ index: i, reason: `Maximum of ${MAX_OBSERVATIONS} observations exceeded` });\n continue;\n }\n\n observations.push(entry.resource as FHIRObservation);\n }\n\n return { observations, skipped };\n}\n\n/**\n * Map a FHIR Observation to internal format using LOINC→biomarker code lookup\n */\nexport function mapFHIRObservationToInternal(\n observation: FHIRObservation,\n index: number,\n): { observation: ImportedObservation } | { skipped: SkippedEntry } {\n const { internalCode, loincCode, reason } = resolveBiomarkerCode(observation);\n\n if (!internalCode) {\n return {\n skipped: { index, loincCode, reason, resourceType: 'Observation' },\n };\n }\n\n const definition = getDefinitionByCode(internalCode);\n\n // Extract value\n let value: number | string;\n let unit = '';\n let isQualitative = false;\n\n if (observation.valueQuantity?.value !== undefined) {\n value = observation.valueQuantity.value;\n unit = observation.valueQuantity.unit || observation.valueQuantity.code || '';\n } else if (observation.valueString) {\n value = observation.valueString;\n isQualitative = true;\n } else {\n return {\n skipped: {\n index,\n loincCode,\n reason: 'Observation has no value (valueQuantity or valueString)',\n resourceType: 'Observation',\n },\n };\n }\n\n // Extract collection date (effectiveDateTime or effectivePeriod.start)\n const collectionDate = observation.effectiveDateTime || observation.effectivePeriod?.start || '';\n if (!collectionDate) {\n return {\n skipped: {\n index,\n loincCode,\n reason: 'Observation has no effectiveDateTime or effectivePeriod.start',\n resourceType: 'Observation',\n },\n };\n }\n\n // Extract reference ranges\n let referenceMin: number | undefined;\n let referenceMax: number | undefined;\n let referenceKind: ImportedObservation['referenceKind'];\n if (observation.referenceRange?.[0]) {\n referenceMin = observation.referenceRange[0].low?.value;\n referenceMax = observation.referenceRange[0].high?.value;\n const meaning = observation.referenceRange[0].type?.coding?.find(\n (c) => c.system === 'http://terminology.hl7.org/CodeSystem/referencerange-meaning',\n )?.code;\n if (meaning === 'normal') referenceKind = 'reference-interval';\n else if (meaning === 'recommended') referenceKind = 'decision-threshold';\n }\n\n const imported: ImportedObservation = {\n biomarkerCode: internalCode,\n biomarkerName:\n definition?.names.pt[0] || definition?.names.en[0] || observation.code.text || internalCode,\n collectionDate,\n flag: extractFlag(observation),\n isQualitative,\n loincCode,\n ...(loincCode && methodVariantOf(internalCode, loincCode) && { methodLoinc: loincCode }),\n ...(referenceKind && { referenceKind }),\n referenceMax,\n referenceMin,\n unit: unit || definition?.unit || '',\n value,\n };\n\n return { observation: imported };\n}\n\n/**\n * Process a complete FHIR Bundle for import\n */\nexport function processImportBundle(data: unknown): FHIRImportResult {\n // Structural validation\n const validationErrors = validateFHIRImportBundle(data);\n if (validationErrors.length > 0) {\n return {\n errors: validationErrors,\n imported: [],\n skipped: [],\n totalProcessed: 0,\n };\n }\n\n const bundle = data as FHIRBundle;\n\n // Extract observations\n const { observations, skipped } = extractObservationsFromBundle(bundle);\n\n // Map each observation to internal format\n const imported: ImportedObservation[] = [];\n const allSkipped: SkippedEntry[] = [...skipped];\n\n for (let i = 0; i < observations.length; i++) {\n const result = mapFHIRObservationToInternal(observations[i]!, i);\n\n if ('observation' in result) {\n imported.push(result.observation);\n } else {\n allSkipped.push(result.skipped);\n }\n }\n\n return {\n errors: [],\n imported,\n skipped: allSkipped,\n totalProcessed: observations.length,\n };\n}\n\nexport { MAX_FILE_SIZE, MAX_OBSERVATIONS };\n"]}
|