@precisa-saude/fhir 0.17.4 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-R6FI2RNB.cjs → chunk-HGEQS7P7.cjs} +3 -3
- package/dist/chunk-HGEQS7P7.cjs.map +1 -0
- package/dist/{chunk-NAUVJPLC.js → chunk-HP3OETAJ.js} +3 -3
- package/dist/chunk-HP3OETAJ.js.map +1 -0
- package/dist/cli.js +3 -3
- package/dist/importer.cjs +2 -2
- package/dist/importer.d.cts +13 -1
- package/dist/importer.d.ts +13 -1
- package/dist/importer.js +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-NAUVJPLC.js.map +0 -1
- package/dist/chunk-R6FI2RNB.cjs.map +0 -1
|
@@ -7,8 +7,8 @@ var _chunkONRVND6Ucjs = require('./chunk-ONRVND6U.cjs');
|
|
|
7
7
|
var _chunk3ILBFLVQcjs = require('./chunk-3ILBFLVQ.cjs');
|
|
8
8
|
|
|
9
9
|
// src/importer.ts
|
|
10
|
-
var MAX_OBSERVATIONS =
|
|
11
|
-
var MAX_FILE_SIZE =
|
|
10
|
+
var MAX_OBSERVATIONS = 5e3;
|
|
11
|
+
var MAX_FILE_SIZE = 15 * 1024 * 1024;
|
|
12
12
|
function extractLoincCode(observation) {
|
|
13
13
|
if (!_optionalChain([observation, 'access', _ => _.code, 'optionalAccess', _2 => _2.coding])) return void 0;
|
|
14
14
|
const loincCoding = observation.code.coding.find((c) => c.system === "http://loinc.org");
|
|
@@ -150,4 +150,4 @@ function processImportBundle(data) {
|
|
|
150
150
|
|
|
151
151
|
|
|
152
152
|
exports.MAX_OBSERVATIONS = MAX_OBSERVATIONS; exports.MAX_FILE_SIZE = MAX_FILE_SIZE; exports.extractObservationsFromBundle = extractObservationsFromBundle; exports.mapFHIRObservationToInternal = mapFHIRObservationToInternal; exports.processImportBundle = processImportBundle;
|
|
153
|
-
//# sourceMappingURL=chunk-
|
|
153
|
+
//# sourceMappingURL=chunk-HGEQS7P7.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/fhir-brasil/fhir-brasil/packages/core/dist/chunk-HGEQS7P7.cjs","../src/importer.ts"],"names":[],"mappings":"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","file":"/home/runner/work/fhir-brasil/fhir-brasil/packages/core/dist/chunk-HGEQS7P7.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 { getDefinitionByLoinc, loincToCode } from './biomarkers';\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 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 * Extract LOINC code from an Observation's code.coding array\n */\nfunction extractLoincCode(observation: FHIRObservation): string | undefined {\n if (!observation.code?.coding) return undefined;\n const loincCoding = observation.code.coding.find((c) => c.system === 'http://loinc.org');\n return loincCoding?.code;\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 loincCode = extractLoincCode(observation);\n\n if (!loincCode) {\n return {\n skipped: {\n index,\n reason: 'No LOINC code found in observation coding',\n resourceType: 'Observation',\n },\n };\n }\n\n const internalCode = loincToCode(loincCode);\n if (!internalCode) {\n return {\n skipped: {\n index,\n loincCode,\n reason: `Unknown LOINC code: ${loincCode}`,\n resourceType: 'Observation',\n },\n };\n }\n\n const definition = getDefinitionByLoinc(loincCode);\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"]}
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
} from "./chunk-N3ZCOLG2.js";
|
|
8
8
|
|
|
9
9
|
// src/importer.ts
|
|
10
|
-
var MAX_OBSERVATIONS =
|
|
11
|
-
var MAX_FILE_SIZE =
|
|
10
|
+
var MAX_OBSERVATIONS = 5e3;
|
|
11
|
+
var MAX_FILE_SIZE = 15 * 1024 * 1024;
|
|
12
12
|
function extractLoincCode(observation) {
|
|
13
13
|
if (!observation.code?.coding) return void 0;
|
|
14
14
|
const loincCoding = observation.code.coding.find((c) => c.system === "http://loinc.org");
|
|
@@ -150,4 +150,4 @@ export {
|
|
|
150
150
|
mapFHIRObservationToInternal,
|
|
151
151
|
processImportBundle
|
|
152
152
|
};
|
|
153
|
-
//# sourceMappingURL=chunk-
|
|
153
|
+
//# sourceMappingURL=chunk-HP3OETAJ.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 { getDefinitionByLoinc, loincToCode } from './biomarkers';\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 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 * Extract LOINC code from an Observation's code.coding array\n */\nfunction extractLoincCode(observation: FHIRObservation): string | undefined {\n if (!observation.code?.coding) return undefined;\n const loincCoding = observation.code.coding.find((c) => c.system === 'http://loinc.org');\n return loincCoding?.code;\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 loincCode = extractLoincCode(observation);\n\n if (!loincCode) {\n return {\n skipped: {\n index,\n reason: 'No LOINC code found in observation coding',\n resourceType: 'Observation',\n },\n };\n }\n\n const internalCode = loincToCode(loincCode);\n if (!internalCode) {\n return {\n skipped: {\n index,\n loincCode,\n reason: `Unknown LOINC code: ${loincCode}`,\n resourceType: 'Observation',\n },\n };\n }\n\n const definition = getDefinitionByLoinc(loincCode);\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"],"mappings":"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","names":[]}
|
package/dist/cli.js
CHANGED
|
@@ -3888,8 +3888,8 @@ function validateFHIRImportBundle(data) {
|
|
|
3888
3888
|
}
|
|
3889
3889
|
|
|
3890
3890
|
// src/importer.ts
|
|
3891
|
-
var MAX_OBSERVATIONS =
|
|
3892
|
-
var MAX_FILE_SIZE =
|
|
3891
|
+
var MAX_OBSERVATIONS = 5e3;
|
|
3892
|
+
var MAX_FILE_SIZE = 15 * 1024 * 1024;
|
|
3893
3893
|
function extractLoincCode(observation) {
|
|
3894
3894
|
if (!observation.code?.coding) return void 0;
|
|
3895
3895
|
const loincCoding = observation.code.coding.find((c) => c.system === "http://loinc.org");
|
|
@@ -6192,7 +6192,7 @@ async function main() {
|
|
|
6192
6192
|
strict: false
|
|
6193
6193
|
});
|
|
6194
6194
|
if (values.version) {
|
|
6195
|
-
process.stdout.write(`${"0.
|
|
6195
|
+
process.stdout.write(`${"0.18.0"}
|
|
6196
6196
|
`);
|
|
6197
6197
|
return;
|
|
6198
6198
|
}
|
package/dist/importer.cjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
var
|
|
7
|
+
var _chunkHGEQS7P7cjs = require('./chunk-HGEQS7P7.cjs');
|
|
8
8
|
require('./chunk-ONRVND6U.cjs');
|
|
9
9
|
require('./chunk-3ILBFLVQ.cjs');
|
|
10
10
|
|
|
@@ -13,5 +13,5 @@ require('./chunk-3ILBFLVQ.cjs');
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
exports.MAX_FILE_SIZE =
|
|
16
|
+
exports.MAX_FILE_SIZE = _chunkHGEQS7P7cjs.MAX_FILE_SIZE; exports.MAX_OBSERVATIONS = _chunkHGEQS7P7cjs.MAX_OBSERVATIONS; exports.extractObservationsFromBundle = _chunkHGEQS7P7cjs.extractObservationsFromBundle; exports.mapFHIRObservationToInternal = _chunkHGEQS7P7cjs.mapFHIRObservationToInternal; exports.processImportBundle = _chunkHGEQS7P7cjs.processImportBundle;
|
|
17
17
|
//# sourceMappingURL=importer.cjs.map
|
package/dist/importer.d.cts
CHANGED
|
@@ -35,7 +35,19 @@ interface FHIRImportResult {
|
|
|
35
35
|
skipped: SkippedEntry[];
|
|
36
36
|
totalProcessed: number;
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Limites de importação.
|
|
40
|
+
*
|
|
41
|
+
* Uma Observation exportada ocupa cerca de 1,25KB em JSON compacto e 2,75KB
|
|
42
|
+
* quando o arquivo vem indentado, medido sobre um histórico real de 998
|
|
43
|
+
* Observations em 61 laudos. Nesse tamanho, 5000 Observations dão 6,0MB
|
|
44
|
+
* compactos ou 13,1MB indentados, e por isso o teto de arquivo é 15MB: cobre
|
|
45
|
+
* as duas formas com folga.
|
|
46
|
+
*
|
|
47
|
+
* Na densidade desse mesmo histórico (16 Observations por laudo), 5000
|
|
48
|
+
* equivalem a cerca de 300 laudos.
|
|
49
|
+
*/
|
|
50
|
+
declare const MAX_OBSERVATIONS = 5000;
|
|
39
51
|
declare const MAX_FILE_SIZE: number;
|
|
40
52
|
/**
|
|
41
53
|
* Extract Observation resources from a FHIR Bundle
|
package/dist/importer.d.ts
CHANGED
|
@@ -35,7 +35,19 @@ interface FHIRImportResult {
|
|
|
35
35
|
skipped: SkippedEntry[];
|
|
36
36
|
totalProcessed: number;
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Limites de importação.
|
|
40
|
+
*
|
|
41
|
+
* Uma Observation exportada ocupa cerca de 1,25KB em JSON compacto e 2,75KB
|
|
42
|
+
* quando o arquivo vem indentado, medido sobre um histórico real de 998
|
|
43
|
+
* Observations em 61 laudos. Nesse tamanho, 5000 Observations dão 6,0MB
|
|
44
|
+
* compactos ou 13,1MB indentados, e por isso o teto de arquivo é 15MB: cobre
|
|
45
|
+
* as duas formas com folga.
|
|
46
|
+
*
|
|
47
|
+
* Na densidade desse mesmo histórico (16 Observations por laudo), 5000
|
|
48
|
+
* equivalem a cerca de 300 laudos.
|
|
49
|
+
*/
|
|
50
|
+
declare const MAX_OBSERVATIONS = 5000;
|
|
39
51
|
declare const MAX_FILE_SIZE: number;
|
|
40
52
|
/**
|
|
41
53
|
* Extract Observation resources from a FHIR Bundle
|
package/dist/importer.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -10,7 +10,7 @@ var _chunkNP4YZFUIcjs = require('./chunk-NP4YZFUI.cjs');
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
var
|
|
13
|
+
var _chunkHGEQS7P7cjs = require('./chunk-HGEQS7P7.cjs');
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
|
|
@@ -691,5 +691,5 @@ function cnsToFHIRIdentifier(cns) {
|
|
|
691
691
|
|
|
692
692
|
|
|
693
693
|
|
|
694
|
-
exports.AGE_BRACKETS = AGE_BRACKETS; exports.BIOMARKER_DEFAULT_UNIT = _chunkMJ254F5Kcjs.BIOMARKER_DEFAULT_UNIT; exports.BIOMARKER_DEFINITIONS = _chunkONRVND6Ucjs.BIOMARKER_DEFINITIONS; exports.BIOMARKER_UNITS = _chunkMJ254F5Kcjs.BIOMARKER_UNITS; exports.BODY_FAT_ZONES = BODY_FAT_ZONES; exports.CAC_INDICATOR_CODES = _chunkONRVND6Ucjs.CAC_INDICATOR_CODES; exports.CATEGORY_GROUPS = CATEGORY_GROUPS; exports.CATEGORY_SCREENING_INTERVALS = CATEGORY_SCREENING_INTERVALS; exports.DEXA_CATEGORIES = _chunkONRVND6Ucjs.DEXA_CATEGORIES; exports.DEXA_INDICATOR_CODES = _chunkONRVND6Ucjs.DEXA_INDICATOR_CODES; exports.MAX_FILE_SIZE =
|
|
694
|
+
exports.AGE_BRACKETS = AGE_BRACKETS; exports.BIOMARKER_DEFAULT_UNIT = _chunkMJ254F5Kcjs.BIOMARKER_DEFAULT_UNIT; exports.BIOMARKER_DEFINITIONS = _chunkONRVND6Ucjs.BIOMARKER_DEFINITIONS; exports.BIOMARKER_UNITS = _chunkMJ254F5Kcjs.BIOMARKER_UNITS; exports.BODY_FAT_ZONES = BODY_FAT_ZONES; exports.CAC_INDICATOR_CODES = _chunkONRVND6Ucjs.CAC_INDICATOR_CODES; exports.CATEGORY_GROUPS = CATEGORY_GROUPS; exports.CATEGORY_SCREENING_INTERVALS = CATEGORY_SCREENING_INTERVALS; exports.DEXA_CATEGORIES = _chunkONRVND6Ucjs.DEXA_CATEGORIES; exports.DEXA_INDICATOR_CODES = _chunkONRVND6Ucjs.DEXA_INDICATOR_CODES; exports.MAX_FILE_SIZE = _chunkHGEQS7P7cjs.MAX_FILE_SIZE; exports.MAX_OBSERVATIONS = _chunkHGEQS7P7cjs.MAX_OBSERVATIONS; exports.T_SCORE_ZONES = T_SCORE_ZONES; exports.UNIT_TO_UCUM = _chunkMJ254F5Kcjs.UNIT_TO_UCUM; exports.ZONE_DEFS = ZONE_DEFS; exports.applyFallbackReferenceRanges = _chunkCBGEU6SGcjs.applyFallbackReferenceRanges; exports.biomarkerRangeDefinitions = _chunkCBGEU6SGcjs.biomarkerRangeDefinitions; exports.calculateNextScreeningDate = calculateNextScreeningDate; exports.cnsToFHIRIdentifier = cnsToFHIRIdentifier; exports.codeToLoinc = _chunkONRVND6Ucjs.codeToLoinc; exports.convertUnit = _chunkMJ254F5Kcjs.convertUnit; exports.cpfToFHIRIdentifier = cpfToFHIRIdentifier; exports.defaultReferenceRanges = _chunkCBGEU6SGcjs.defaultReferenceRanges; exports.extractObservationsFromBundle = _chunkHGEQS7P7cjs.extractObservationsFromBundle; exports.filterVisibleBiomarkers = _chunkONRVND6Ucjs.filterVisibleBiomarkers; exports.findCodeByName = _chunkONRVND6Ucjs.findCodeByName; exports.formatCNS = formatCNS; exports.formatCPF = formatCPF; exports.generateCacFullReference = _chunkONRVND6Ucjs.generateCacFullReference; exports.generateDexaFullReference = _chunkONRVND6Ucjs.generateDexaFullReference; exports.generateFilteredLLMReference = _chunkONRVND6Ucjs.generateFilteredLLMReference; exports.generateLLMReference = _chunkONRVND6Ucjs.generateLLMReference; exports.getAllCodes = _chunkONRVND6Ucjs.getAllCodes; exports.getAllDefinitions = _chunkONRVND6Ucjs.getAllDefinitions; exports.getAllLoincCodes = _chunkONRVND6Ucjs.getAllLoincCodes; exports.getAllSearchPatterns = _chunkONRVND6Ucjs.getAllSearchPatterns; exports.getBiomarkersByCategory = _chunkONRVND6Ucjs.getBiomarkersByCategory; exports.getBiomarkersForCategories = _chunkONRVND6Ucjs.getBiomarkersForCategories; exports.getCanonicalUnit = _chunkMJ254F5Kcjs.getCanonicalUnit; exports.getCategoriesByInterval = getCategoriesByInterval; exports.getCategoryGroup = getCategoryGroup; exports.getDaysUntilScreening = getDaysUntilScreening; exports.getDefaultUnit = _chunkMJ254F5Kcjs.getDefaultUnit; exports.getDefinitionByCode = _chunkONRVND6Ucjs.getDefinitionByCode; exports.getDefinitionByLoinc = _chunkONRVND6Ucjs.getDefinitionByLoinc; exports.getDefinitionsBySex = _chunkONRVND6Ucjs.getDefinitionsBySex; exports.getDueCategories = getDueCategories; exports.getFallbackReferenceRange = _chunkCBGEU6SGcjs.getFallbackReferenceRange; exports.getRangeDirection = _chunkCBGEU6SGcjs.getRangeDirection; exports.getReferenceRange = _chunkCBGEU6SGcjs.getReferenceRange; exports.getSIUnit = _chunkMJ254F5Kcjs.getSIUnit; exports.getScreeningInterval = getScreeningInterval; exports.getSexForCode = _chunkONRVND6Ucjs.getSexForCode; exports.getVisibleDefinitions = _chunkONRVND6Ucjs.getVisibleDefinitions; exports.interventionToFHIRMedicationStatement = interventionToFHIRMedicationStatement; exports.interventionToFHIRObservation = interventionToFHIRObservation; exports.interventionsToFHIRBundle = interventionsToFHIRBundle; exports.isBiomarkerVisible = _chunkONRVND6Ucjs.isBiomarkerVisible; exports.isCacDocument = _chunkONRVND6Ucjs.isCacDocument; exports.isDexaDocument = _chunkONRVND6Ucjs.isDexaDocument; exports.isScreeningDue = isScreeningDue; exports.isValidCode = _chunkONRVND6Ucjs.isValidCode; exports.isValidLoinc = _chunkONRVND6Ucjs.isValidLoinc; exports.labObservationToFHIR = _chunkNP4YZFUIcjs.labObservationToFHIR; exports.labReportToFHIR = _chunkNP4YZFUIcjs.labReportToFHIR; exports.labResultToFHIRBundle = _chunkNP4YZFUIcjs.labResultToFHIRBundle; exports.listMappedSubcategories = listMappedSubcategories; exports.loincToCode = _chunkONRVND6Ucjs.loincToCode; exports.mapFHIRObservationToInternal = _chunkHGEQS7P7cjs.mapFHIRObservationToInternal; exports.normalizeCode = _chunkONRVND6Ucjs.normalizeCode; exports.plural = plural; exports.pluralCount = pluralCount; exports.pluralPhrase = pluralPhrase; exports.pluralPhraseCount = pluralPhraseCount; exports.processImportBundle = _chunkHGEQS7P7cjs.processImportBundle; exports.toBiomarkerTests = _chunkONRVND6Ucjs.toBiomarkerTests; exports.unitToUCUM = _chunkMJ254F5Kcjs.unitToUCUM; exports.userProfileToFHIR = _chunkNP4YZFUIcjs.userProfileToFHIR; exports.validateCNS = validateCNS; exports.validateCPF = validateCPF; exports.validateFHIRDiagnosticReport = _chunk3ILBFLVQcjs.validateFHIRDiagnosticReport; exports.validateFHIRImportBundle = _chunk3ILBFLVQcjs.validateFHIRImportBundle; exports.validateFHIRObservation = _chunk3ILBFLVQcjs.validateFHIRObservation; exports.validateLoincNameMatch = _chunkONRVND6Ucjs.validateLoincNameMatch;
|
|
695
695
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
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 { getDefinitionByLoinc, loincToCode } from './biomarkers';\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 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\nconst MAX_OBSERVATIONS = 1000;\nconst MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB\n\n/**\n * Extract LOINC code from an Observation's code.coding array\n */\nfunction extractLoincCode(observation: FHIRObservation): string | undefined {\n if (!observation.code?.coding) return undefined;\n const loincCoding = observation.code.coding.find((c) => c.system === 'http://loinc.org');\n return loincCoding?.code;\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 loincCode = extractLoincCode(observation);\n\n if (!loincCode) {\n return {\n skipped: {\n index,\n reason: 'No LOINC code found in observation coding',\n resourceType: 'Observation',\n },\n };\n }\n\n const internalCode = loincToCode(loincCode);\n if (!internalCode) {\n return {\n skipped: {\n index,\n loincCode,\n reason: `Unknown LOINC code: ${loincCode}`,\n resourceType: 'Observation',\n },\n };\n }\n\n const definition = getDefinitionByLoinc(loincCode);\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"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/fhir-brasil/fhir-brasil/packages/core/dist/chunk-R6FI2RNB.cjs","../src/importer.ts"],"names":[],"mappings":"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","file":"/home/runner/work/fhir-brasil/fhir-brasil/packages/core/dist/chunk-R6FI2RNB.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 { getDefinitionByLoinc, loincToCode } from './biomarkers';\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 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\nconst MAX_OBSERVATIONS = 1000;\nconst MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB\n\n/**\n * Extract LOINC code from an Observation's code.coding array\n */\nfunction extractLoincCode(observation: FHIRObservation): string | undefined {\n if (!observation.code?.coding) return undefined;\n const loincCoding = observation.code.coding.find((c) => c.system === 'http://loinc.org');\n return loincCoding?.code;\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 loincCode = extractLoincCode(observation);\n\n if (!loincCode) {\n return {\n skipped: {\n index,\n reason: 'No LOINC code found in observation coding',\n resourceType: 'Observation',\n },\n };\n }\n\n const internalCode = loincToCode(loincCode);\n if (!internalCode) {\n return {\n skipped: {\n index,\n loincCode,\n reason: `Unknown LOINC code: ${loincCode}`,\n resourceType: 'Observation',\n },\n };\n }\n\n const definition = getDefinitionByLoinc(loincCode);\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"]}
|