@precisa-saude/fhir 0.20.1 → 0.20.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/biomarkers.cjs +2 -2
- package/dist/biomarkers.js +1 -1
- package/dist/{chunk-YYANW65U.js → chunk-G32XWBCX.js} +2 -2
- package/dist/{chunk-ETZXOGAB.cjs → chunk-G6LC6XTM.cjs} +7 -7
- package/dist/{chunk-ETZXOGAB.cjs.map → chunk-G6LC6XTM.cjs.map} +1 -1
- package/dist/{chunk-NXOTXKVB.cjs → chunk-NUTT5IWA.cjs} +46 -9
- package/dist/chunk-NUTT5IWA.cjs.map +1 -0
- package/dist/{chunk-QJF5BZ3J.cjs → chunk-PQQQ5RBB.cjs} +3 -3
- package/dist/{chunk-QJF5BZ3J.cjs.map → chunk-PQQQ5RBB.cjs.map} +1 -1
- package/dist/{chunk-3KIJIRCI.js → chunk-RGRLMISI.js} +46 -9
- package/dist/chunk-RGRLMISI.js.map +1 -0
- package/dist/{chunk-SN5ZJHWS.js → chunk-ZJ3FNWT3.js} +2 -2
- package/dist/cli.js +46 -9
- package/dist/converter.cjs +3 -3
- package/dist/converter.js +2 -2
- package/dist/importer.cjs +3 -3
- package/dist/importer.js +2 -2
- package/dist/index.cjs +5 -5
- package/dist/index.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-3KIJIRCI.js.map +0 -1
- package/dist/chunk-NXOTXKVB.cjs.map +0 -1
- /package/dist/{chunk-YYANW65U.js.map → chunk-G32XWBCX.js.map} +0 -0
- /package/dist/{chunk-SN5ZJHWS.js.map → chunk-ZJ3FNWT3.js.map} +0 -0
package/dist/biomarkers.cjs
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
var
|
|
33
|
+
var _chunkNUTT5IWAcjs = require('./chunk-NUTT5IWA.cjs');
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
|
|
@@ -63,5 +63,5 @@ var _chunkNXOTXKVBcjs = require('./chunk-NXOTXKVB.cjs');
|
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
exports.BIOMARKER_DEFINITIONS =
|
|
66
|
+
exports.BIOMARKER_DEFINITIONS = _chunkNUTT5IWAcjs.BIOMARKER_DEFINITIONS; exports.CAC_INDICATOR_CODES = _chunkNUTT5IWAcjs.CAC_INDICATOR_CODES; exports.DEXA_CATEGORIES = _chunkNUTT5IWAcjs.DEXA_CATEGORIES; exports.DEXA_INDICATOR_CODES = _chunkNUTT5IWAcjs.DEXA_INDICATOR_CODES; exports.codeToLoinc = _chunkNUTT5IWAcjs.codeToLoinc; exports.filterVisibleBiomarkers = _chunkNUTT5IWAcjs.filterVisibleBiomarkers; exports.findCodeByName = _chunkNUTT5IWAcjs.findCodeByName; exports.generateCacFullReference = _chunkNUTT5IWAcjs.generateCacFullReference; exports.generateDexaFullReference = _chunkNUTT5IWAcjs.generateDexaFullReference; exports.generateFilteredLLMReference = _chunkNUTT5IWAcjs.generateFilteredLLMReference; exports.generateLLMReference = _chunkNUTT5IWAcjs.generateLLMReference; exports.getAllCodes = _chunkNUTT5IWAcjs.getAllCodes; exports.getAllDefinitions = _chunkNUTT5IWAcjs.getAllDefinitions; exports.getAllLoincCodes = _chunkNUTT5IWAcjs.getAllLoincCodes; exports.getAllSearchPatterns = _chunkNUTT5IWAcjs.getAllSearchPatterns; exports.getBiomarkersByCategory = _chunkNUTT5IWAcjs.getBiomarkersByCategory; exports.getBiomarkersForCategories = _chunkNUTT5IWAcjs.getBiomarkersForCategories; exports.getDefinitionByCode = _chunkNUTT5IWAcjs.getDefinitionByCode; exports.getDefinitionByLoinc = _chunkNUTT5IWAcjs.getDefinitionByLoinc; exports.getDefinitionsBySex = _chunkNUTT5IWAcjs.getDefinitionsBySex; exports.getSexForCode = _chunkNUTT5IWAcjs.getSexForCode; exports.getVisibleDefinitions = _chunkNUTT5IWAcjs.getVisibleDefinitions; exports.isBiomarkerVisible = _chunkNUTT5IWAcjs.isBiomarkerVisible; exports.isCacDocument = _chunkNUTT5IWAcjs.isCacDocument; exports.isDexaDocument = _chunkNUTT5IWAcjs.isDexaDocument; exports.isValidCode = _chunkNUTT5IWAcjs.isValidCode; exports.isValidLoinc = _chunkNUTT5IWAcjs.isValidLoinc; exports.loincToCode = _chunkNUTT5IWAcjs.loincToCode; exports.normalizeCode = _chunkNUTT5IWAcjs.normalizeCode; exports.toBiomarkerTests = _chunkNUTT5IWAcjs.toBiomarkerTests; exports.validateLoincNameMatch = _chunkNUTT5IWAcjs.validateLoincNameMatch;
|
|
67
67
|
//# sourceMappingURL=biomarkers.cjs.map
|
package/dist/biomarkers.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
isValidCode,
|
|
9
9
|
loincToCode,
|
|
10
10
|
normalizeCode
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-RGRLMISI.js";
|
|
12
12
|
import {
|
|
13
13
|
validateFHIRImportBundle
|
|
14
14
|
} from "./chunk-N3ZCOLG2.js";
|
|
@@ -162,4 +162,4 @@ export {
|
|
|
162
162
|
mapFHIRObservationToInternal,
|
|
163
163
|
processImportBundle
|
|
164
164
|
};
|
|
165
|
-
//# sourceMappingURL=chunk-
|
|
165
|
+
//# sourceMappingURL=chunk-G32XWBCX.js.map
|
|
@@ -8,7 +8,7 @@ var _chunkOR67NJDZcjs = require('./chunk-OR67NJDZ.cjs');
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
var
|
|
11
|
+
var _chunkNUTT5IWAcjs = require('./chunk-NUTT5IWA.cjs');
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
var _chunk3ILBFLVQcjs = require('./chunk-3ILBFLVQ.cjs');
|
|
@@ -33,11 +33,11 @@ function resolveBiomarkerCode(observation) {
|
|
|
33
33
|
} else {
|
|
34
34
|
reason = "No code found in observation coding";
|
|
35
35
|
}
|
|
36
|
-
const fromLoinc = loincCode ?
|
|
36
|
+
const fromLoinc = loincCode ? _chunkNUTT5IWAcjs.loincToCode.call(void 0, loincCode) : void 0;
|
|
37
37
|
if (fromLoinc) return { internalCode: fromLoinc, loincCode, reason };
|
|
38
|
-
if (declaredCode &&
|
|
39
|
-
const canonical =
|
|
40
|
-
return { internalCode: canonical, loincCode:
|
|
38
|
+
if (declaredCode && _chunkNUTT5IWAcjs.isValidCode.call(void 0, declaredCode)) {
|
|
39
|
+
const canonical = _chunkNUTT5IWAcjs.normalizeCode.call(void 0, declaredCode);
|
|
40
|
+
return { internalCode: canonical, loincCode: _chunkNUTT5IWAcjs.codeToLoinc.call(void 0, canonical), reason };
|
|
41
41
|
}
|
|
42
42
|
return { loincCode, reason };
|
|
43
43
|
}
|
|
@@ -74,7 +74,7 @@ function mapFHIRObservationToInternal(observation, index) {
|
|
|
74
74
|
skipped: { index, loincCode, reason, resourceType: "Observation" }
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
|
-
const definition =
|
|
77
|
+
const definition = _chunkNUTT5IWAcjs.getDefinitionByCode.call(void 0, internalCode);
|
|
78
78
|
let value;
|
|
79
79
|
let unit = "";
|
|
80
80
|
let isQualitative = false;
|
|
@@ -162,4 +162,4 @@ function processImportBundle(data) {
|
|
|
162
162
|
|
|
163
163
|
|
|
164
164
|
exports.MAX_OBSERVATIONS = MAX_OBSERVATIONS; exports.MAX_FILE_SIZE = MAX_FILE_SIZE; exports.extractObservationsFromBundle = extractObservationsFromBundle; exports.mapFHIRObservationToInternal = mapFHIRObservationToInternal; exports.processImportBundle = processImportBundle;
|
|
165
|
-
//# sourceMappingURL=chunk-
|
|
165
|
+
//# sourceMappingURL=chunk-G6LC6XTM.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/fhir-brasil/fhir-brasil/packages/core/dist/chunk-ETZXOGAB.cjs","../src/importer.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACF,wDAA6B;AAC7B;AACA;ACgDA,IAAM,iBAAA,EAAmB,GAAA;AACzB,IAAM,cAAA,EAAgB,GAAA,EAAK,KAAA,EAAO,IAAA;AAclC,SAAS,oBAAA,CAAqB,WAAA,EAI5B;AACA,EAAA,MAAM,OAAA,mCAAS,WAAA,mBAAY,IAAA,6BAAM,QAAA,UAAU,CAAC,GAAA;AAC5C,EAAA,MAAM,UAAA,kBAAY,MAAA,qBAAO,IAAA,mBAAK,CAAC,CAAA,EAAA,GAAM,CAAA,CAAE,OAAA,IAAW,8BAAY,CAAA,6BAAG,MAAA;AACjE,EAAA,MAAM,aAAA,kBAAe,MAAA,qBAAO,IAAA,mBAAK,CAAC,CAAA,EAAA,GAAM,CAAA,CAAE,OAAA,IAAW,uCAAqB,CAAA,6BAAG,MAAA;AAE7E,EAAA,MAAM,UAAA,EAAY;AAAA,IAChB,GAAI,UAAA,EAAY,CAAC,CAAA,MAAA,EAAS,SAAS,CAAA,CAAA;AACf,IAAA;AACtB,EAAA;AAOI,EAAA;AACsB,EAAA;AACE,IAAA;AACE,EAAA;AACO,IAAA;AAC1B,IAAA;AACJ,EAAA;AACI,IAAA;AACX,EAAA;AAE8B,EAAA;AACN,EAAA;AAEQ,EAAA;AAOE,IAAA;AAIE,IAAA;AACpC,EAAA;AAE2B,EAAA;AAC7B;AAKmE;AACxC,EAAA;AACU,EAAA;AACA,EAAA;AAC5B,EAAA;AACT;AAK8C;AAIH,EAAA;AACR,EAAA;AAEA,EAAA;AACH,IAAA;AACP,IAAA;AACc,MAAA;AACjC,MAAA;AACF,IAAA;AAEmB,IAAA;AAEjB,MAAA;AACF,IAAA;AAE2B,IAAA;AACQ,MAAA;AACjC,MAAA;AACF,IAAA;AAEmD,IAAA;AACrD,EAAA;AAE+B,EAAA;AACjC;AAME;AAGiC,EAAA;AAEd,EAAA;AACV,IAAA;AACwB,MAAA;AAC/B,IAAA;AACF,EAAA;AAEmB,EAAA;AAGf,EAAA;AACO,EAAA;AACS,EAAA;AAEW,EAAA;AACK,IAAA;AACD,IAAA;AACC,EAAA;AACd,IAAA;AACJ,IAAA;AACX,EAAA;AACE,IAAA;AACI,MAAA;AACP,QAAA;AACA,QAAA;AACQ,QAAA;AACM,QAAA;AAChB,MAAA;AACF,IAAA;AACF,EAAA;AAGmC,EAAA;AACd,EAAA;AACZ,IAAA;AACI,MAAA;AACP,QAAA;AACA,QAAA;AACQ,QAAA;AACM,QAAA;AAChB,MAAA;AACF,IAAA;AACF,EAAA;AAGI,EAAA;AACA,EAAA;AACiC,EAAA;AACR,IAAA;AACA,IAAA;AAC7B,EAAA;AAEsC,EAAA;AACrB,IAAA;AAEK,IAAA;AACpB,IAAA;AAC6B,IAAA;AAC7B,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACkC,IAAA;AAClC,IAAA;AACF,EAAA;AAE+B,EAAA;AACjC;AAKqE;AAE1C,EAAA;AACQ,EAAA;AACxB,IAAA;AACG,MAAA;AACG,MAAA;AACD,MAAA;AACM,MAAA;AAClB,IAAA;AACF,EAAA;AAEe,EAAA;AAGmB,EAAA;AAGO,EAAA;AACK,EAAA;AAEb,EAAA;AAChB,IAAA;AAEc,IAAA;AACK,MAAA;AAC3B,IAAA;AACyB,MAAA;AAChC,IAAA;AACF,EAAA;AAEO,EAAA;AACI,IAAA;AACT,IAAA;AACS,IAAA;AACoB,IAAA;AAC/B,EAAA;AACF;ADnIuC;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/fhir-brasil/fhir-brasil/packages/core/dist/chunk-ETZXOGAB.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 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 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 if (observation.referenceRange?.[0]) {\n referenceMin = observation.referenceRange[0].low?.value;\n referenceMax = observation.referenceRange[0].high?.value;\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 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"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/fhir-brasil/fhir-brasil/packages/core/dist/chunk-G6LC6XTM.cjs","../src/importer.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACF,wDAA6B;AAC7B;AACA;ACgDA,IAAM,iBAAA,EAAmB,GAAA;AACzB,IAAM,cAAA,EAAgB,GAAA,EAAK,KAAA,EAAO,IAAA;AAclC,SAAS,oBAAA,CAAqB,WAAA,EAI5B;AACA,EAAA,MAAM,OAAA,mCAAS,WAAA,mBAAY,IAAA,6BAAM,QAAA,UAAU,CAAC,GAAA;AAC5C,EAAA,MAAM,UAAA,kBAAY,MAAA,qBAAO,IAAA,mBAAK,CAAC,CAAA,EAAA,GAAM,CAAA,CAAE,OAAA,IAAW,8BAAY,CAAA,6BAAG,MAAA;AACjE,EAAA,MAAM,aAAA,kBAAe,MAAA,qBAAO,IAAA,mBAAK,CAAC,CAAA,EAAA,GAAM,CAAA,CAAE,OAAA,IAAW,uCAAqB,CAAA,6BAAG,MAAA;AAE7E,EAAA,MAAM,UAAA,EAAY;AAAA,IAChB,GAAI,UAAA,EAAY,CAAC,CAAA,MAAA,EAAS,SAAS,CAAA,CAAA;AACf,IAAA;AACtB,EAAA;AAOI,EAAA;AACsB,EAAA;AACE,IAAA;AACE,EAAA;AACO,IAAA;AAC1B,IAAA;AACJ,EAAA;AACI,IAAA;AACX,EAAA;AAE8B,EAAA;AACN,EAAA;AAEQ,EAAA;AAOE,IAAA;AAIE,IAAA;AACpC,EAAA;AAE2B,EAAA;AAC7B;AAKmE;AACxC,EAAA;AACU,EAAA;AACA,EAAA;AAC5B,EAAA;AACT;AAK8C;AAIH,EAAA;AACR,EAAA;AAEA,EAAA;AACH,IAAA;AACP,IAAA;AACc,MAAA;AACjC,MAAA;AACF,IAAA;AAEmB,IAAA;AAEjB,MAAA;AACF,IAAA;AAE2B,IAAA;AACQ,MAAA;AACjC,MAAA;AACF,IAAA;AAEmD,IAAA;AACrD,EAAA;AAE+B,EAAA;AACjC;AAME;AAGiC,EAAA;AAEd,EAAA;AACV,IAAA;AACwB,MAAA;AAC/B,IAAA;AACF,EAAA;AAEmB,EAAA;AAGf,EAAA;AACO,EAAA;AACS,EAAA;AAEW,EAAA;AACK,IAAA;AACD,IAAA;AACC,EAAA;AACd,IAAA;AACJ,IAAA;AACX,EAAA;AACE,IAAA;AACI,MAAA;AACP,QAAA;AACA,QAAA;AACQ,QAAA;AACM,QAAA;AAChB,MAAA;AACF,IAAA;AACF,EAAA;AAGmC,EAAA;AACd,EAAA;AACZ,IAAA;AACI,MAAA;AACP,QAAA;AACA,QAAA;AACQ,QAAA;AACM,QAAA;AAChB,MAAA;AACF,IAAA;AACF,EAAA;AAGI,EAAA;AACA,EAAA;AACiC,EAAA;AACR,IAAA;AACA,IAAA;AAC7B,EAAA;AAEsC,EAAA;AACrB,IAAA;AAEK,IAAA;AACpB,IAAA;AAC6B,IAAA;AAC7B,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACkC,IAAA;AAClC,IAAA;AACF,EAAA;AAE+B,EAAA;AACjC;AAKqE;AAE1C,EAAA;AACQ,EAAA;AACxB,IAAA;AACG,MAAA;AACG,MAAA;AACD,MAAA;AACM,MAAA;AAClB,IAAA;AACF,EAAA;AAEe,EAAA;AAGmB,EAAA;AAGO,EAAA;AACK,EAAA;AAEb,EAAA;AAChB,IAAA;AAEc,IAAA;AACK,MAAA;AAC3B,IAAA;AACyB,MAAA;AAChC,IAAA;AACF,EAAA;AAEO,EAAA;AACI,IAAA;AACT,IAAA;AACS,IAAA;AACoB,IAAA;AAC/B,EAAA;AACF;ADnIuC;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/fhir-brasil/fhir-brasil/packages/core/dist/chunk-G6LC6XTM.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 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 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 if (observation.referenceRange?.[0]) {\n referenceMin = observation.referenceRange[0].low?.value;\n referenceMax = observation.referenceRange[0].high?.value;\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 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"]}
|
|
@@ -1433,8 +1433,45 @@ var BIOMARKER_DEFINITIONS = [
|
|
|
1433
1433
|
codeAliases: ["Urine_Microalbumin"],
|
|
1434
1434
|
loinc: "14957-5",
|
|
1435
1435
|
names: {
|
|
1436
|
-
en: [
|
|
1437
|
-
|
|
1436
|
+
en: [
|
|
1437
|
+
"Microalbumin Urine",
|
|
1438
|
+
"Urine Albumin",
|
|
1439
|
+
"Urine Microalbumin",
|
|
1440
|
+
"Microalbumin",
|
|
1441
|
+
"Microalbuminuria",
|
|
1442
|
+
"Albumin, Urine"
|
|
1443
|
+
],
|
|
1444
|
+
// Todas as formas abaixo, em pt e en, nomeiam o MESMO analito: albumina
|
|
1445
|
+
// dosada na urina. Não são exames diferentes.
|
|
1446
|
+
//
|
|
1447
|
+
// "Microalbumina" e "microalbuminúria" são herança de nomenclatura: o
|
|
1448
|
+
// prefixo micro nunca se referiu a uma molécula menor, e sim a uma
|
|
1449
|
+
// faixa de excreção. O analito é o mesmo, e por isso as duas formas
|
|
1450
|
+
// pertencem a esta entrada e não a uma separada.
|
|
1451
|
+
//
|
|
1452
|
+
// "Albumina Urinária" vem primeiro de propósito. O gerador do ValueSet
|
|
1453
|
+
// do IG usa o primeiro nome pt como display, então a ordem decide o
|
|
1454
|
+
// rótulo publicado — e essa é a forma que os laboratórios brasileiros
|
|
1455
|
+
// de fato imprimem.
|
|
1456
|
+
//
|
|
1457
|
+
// As formas foram tiradas de dado real, não inventadas: "Albumin,
|
|
1458
|
+
// Urine" e "Albumina Urinária" aparecem como rótulos `UNKNOWN_` na
|
|
1459
|
+
// auditoria de cobertura da plataforma, ou seja, chegaram em laudo e
|
|
1460
|
+
// não casaram com nada.
|
|
1461
|
+
//
|
|
1462
|
+
// Sem elas o pré-scan casava "Albumina Urinária" com `Albumin`, a
|
|
1463
|
+
// albumina sérica. A troca não é cosmética: albumina na urina é
|
|
1464
|
+
// marcador de lesão renal precoce, medida em mg/L, e a sérica é de
|
|
1465
|
+
// função hepática e estado nutricional, medida em g/dL.
|
|
1466
|
+
pt: [
|
|
1467
|
+
"Albumina Urin\xE1ria",
|
|
1468
|
+
"Albumina Urina",
|
|
1469
|
+
"Microalbumina",
|
|
1470
|
+
"Microalbumin\xFAria",
|
|
1471
|
+
"Microalbumina Urina",
|
|
1472
|
+
"Microalbumina na Urina",
|
|
1473
|
+
"Albumina na Urina"
|
|
1474
|
+
]
|
|
1438
1475
|
},
|
|
1439
1476
|
unit: "mg/L"
|
|
1440
1477
|
},
|
|
@@ -2422,7 +2459,7 @@ var BIOMARKER_DEFINITIONS = [
|
|
|
2422
2459
|
code: "SkinfoldTriceps",
|
|
2423
2460
|
loinc: "8354-3",
|
|
2424
2461
|
names: {
|
|
2425
|
-
en: ["Triceps Skinfold", "Tricipital Skinfold", "Skin Fold Thickness Triceps"],
|
|
2462
|
+
en: ["Triceps Skinfold", "Tricipital Skinfold", "Skin Fold Thickness Triceps", "Triceps"],
|
|
2426
2463
|
pt: ["Dobra Tricipital", "Dobra Cut\xE2nea Tricipital", "Tricipital", "DCT"]
|
|
2427
2464
|
},
|
|
2428
2465
|
unit: "mm"
|
|
@@ -2432,7 +2469,7 @@ var BIOMARKER_DEFINITIONS = [
|
|
|
2432
2469
|
code: "SkinfoldThigh",
|
|
2433
2470
|
loinc: "8353-5",
|
|
2434
2471
|
names: {
|
|
2435
|
-
en: ["Thigh Skinfold", "Skin Fold Thickness Thigh"],
|
|
2472
|
+
en: ["Thigh Skinfold", "Skin Fold Thickness Thigh", "Thigh"],
|
|
2436
2473
|
pt: ["Dobra da Coxa", "Dobra Cut\xE2nea Coxa", "Coxa"]
|
|
2437
2474
|
},
|
|
2438
2475
|
unit: "mm"
|
|
@@ -2451,7 +2488,7 @@ var BIOMARKER_DEFINITIONS = [
|
|
|
2451
2488
|
category: "composicao-corporal",
|
|
2452
2489
|
code: "SkinfoldSubscapular",
|
|
2453
2490
|
names: {
|
|
2454
|
-
en: ["Subscapular Skinfold"],
|
|
2491
|
+
en: ["Subscapular Skinfold", "Subscapular"],
|
|
2455
2492
|
pt: ["Dobra Subescapular", "Dobra Cut\xE2nea Subescapular", "Subescapular"]
|
|
2456
2493
|
},
|
|
2457
2494
|
unit: "mm"
|
|
@@ -2460,7 +2497,7 @@ var BIOMARKER_DEFINITIONS = [
|
|
|
2460
2497
|
category: "composicao-corporal",
|
|
2461
2498
|
code: "SkinfoldSuprailiac",
|
|
2462
2499
|
names: {
|
|
2463
|
-
en: ["Suprailiac Skinfold", "Supra-iliac Skinfold"],
|
|
2500
|
+
en: ["Suprailiac Skinfold", "Supra-iliac Skinfold", "Suprailiac"],
|
|
2464
2501
|
pt: ["Dobra Supra-il\xEDaca", "Dobra Cut\xE2nea Supra-il\xEDaca", "Supra-il\xEDaca", "Suprailiaca"]
|
|
2465
2502
|
},
|
|
2466
2503
|
unit: "mm"
|
|
@@ -2469,7 +2506,7 @@ var BIOMARKER_DEFINITIONS = [
|
|
|
2469
2506
|
category: "composicao-corporal",
|
|
2470
2507
|
code: "SkinfoldChest",
|
|
2471
2508
|
names: {
|
|
2472
|
-
en: ["Chest Skinfold", "Pectoral Skinfold"],
|
|
2509
|
+
en: ["Chest Skinfold", "Pectoral Skinfold", "Chest"],
|
|
2473
2510
|
pt: ["Dobra Peitoral", "Dobra Cut\xE2nea Peitoral", "Peitoral", "Dobra Tor\xE1cica"]
|
|
2474
2511
|
},
|
|
2475
2512
|
unit: "mm"
|
|
@@ -2478,7 +2515,7 @@ var BIOMARKER_DEFINITIONS = [
|
|
|
2478
2515
|
category: "composicao-corporal",
|
|
2479
2516
|
code: "SkinfoldMidaxillary",
|
|
2480
2517
|
names: {
|
|
2481
|
-
en: ["Midaxillary Skinfold", "Mid-axillary Skinfold"],
|
|
2518
|
+
en: ["Midaxillary Skinfold", "Mid-axillary Skinfold", "Midaxillary", "MidAxilla"],
|
|
2482
2519
|
pt: ["Dobra Axilar M\xE9dia", "Dobra Cut\xE2nea Axilar M\xE9dia", "Axilar M\xE9dia"]
|
|
2483
2520
|
},
|
|
2484
2521
|
unit: "mm"
|
|
@@ -3245,4 +3282,4 @@ function getBiomarkersForCategories(categories, options) {
|
|
|
3245
3282
|
|
|
3246
3283
|
|
|
3247
3284
|
exports.BIOMARKER_DEFINITIONS = BIOMARKER_DEFINITIONS; exports.loincToCode = loincToCode; exports.codeToLoinc = codeToLoinc; exports.isValidLoinc = isValidLoinc; exports.isValidCode = isValidCode; exports.normalizeCode = normalizeCode; exports.getSexForCode = getSexForCode; exports.getDefinitionsBySex = getDefinitionsBySex; exports.getDefinitionByCode = getDefinitionByCode; exports.getDefinitionByLoinc = getDefinitionByLoinc; exports.getAllDefinitions = getAllDefinitions; exports.getVisibleDefinitions = getVisibleDefinitions; exports.getAllCodes = getAllCodes; exports.getAllLoincCodes = getAllLoincCodes; exports.generateLLMReference = generateLLMReference; exports.toBiomarkerTests = toBiomarkerTests; exports.getAllSearchPatterns = getAllSearchPatterns; exports.generateFilteredLLMReference = generateFilteredLLMReference; exports.DEXA_INDICATOR_CODES = DEXA_INDICATOR_CODES; exports.DEXA_CATEGORIES = DEXA_CATEGORIES; exports.generateDexaFullReference = generateDexaFullReference; exports.isDexaDocument = isDexaDocument; exports.CAC_INDICATOR_CODES = CAC_INDICATOR_CODES; exports.generateCacFullReference = generateCacFullReference; exports.isCacDocument = isCacDocument; exports.findCodeByName = findCodeByName; exports.validateLoincNameMatch = validateLoincNameMatch; exports.isBiomarkerVisible = isBiomarkerVisible; exports.filterVisibleBiomarkers = filterVisibleBiomarkers; exports.getBiomarkersByCategory = getBiomarkersByCategory; exports.getBiomarkersForCategories = getBiomarkersForCategories;
|
|
3248
|
-
//# sourceMappingURL=chunk-
|
|
3285
|
+
//# sourceMappingURL=chunk-NUTT5IWA.cjs.map
|