fhir-data-utils-ts 0.2.4 → 0.3.2
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/.codex/skills/govern-fhir-data-utilities/SKILL.md +58 -0
- package/.codex/skills/govern-fhir-data-utilities/agents/openai.yaml +4 -0
- package/.codex/skills/govern-fhir-observation-components/SKILL.md +18 -0
- package/CHANGELOG.md +39 -0
- package/README.md +47 -0
- package/dist/builders/immunization-graph.d.ts +36 -0
- package/dist/builders/immunization-graph.js +115 -0
- package/dist/builders/immunization-reminders.d.ts +35 -0
- package/dist/builders/immunization-reminders.js +104 -0
- package/dist/builders/index.d.ts +2 -0
- package/dist/builders/index.js +2 -0
- package/dist/claim-helpers/claim-list-helpers.d.ts +21 -0
- package/dist/claim-helpers/claim-list-helpers.js +54 -0
- package/dist/claim-helpers/claims-helpers-allergy-intolerance.d.ts +18 -0
- package/dist/claim-helpers/claims-helpers-allergy-intolerance.js +65 -0
- package/dist/claim-helpers/claims-helpers-appointment-response.d.ts +8 -0
- package/dist/claim-helpers/claims-helpers-appointment-response.js +20 -0
- package/dist/claim-helpers/claims-helpers-appointment.d.ts +10 -0
- package/dist/claim-helpers/claims-helpers-appointment.js +26 -0
- package/dist/claim-helpers/claims-helpers-care-plan.d.ts +8 -0
- package/dist/claim-helpers/claims-helpers-care-plan.js +20 -0
- package/dist/claim-helpers/claims-helpers-clinical-impression.d.ts +10 -0
- package/dist/claim-helpers/claims-helpers-clinical-impression.js +26 -0
- package/dist/claim-helpers/claims-helpers-communication.d.ts +19 -0
- package/dist/claim-helpers/claims-helpers-communication.js +63 -0
- package/dist/claim-helpers/claims-helpers-composition.d.ts +12 -0
- package/dist/claim-helpers/claims-helpers-composition.js +32 -0
- package/dist/claim-helpers/claims-helpers-condition.d.ts +18 -0
- package/dist/claim-helpers/claims-helpers-condition.js +65 -0
- package/dist/claim-helpers/claims-helpers-coverage.d.ts +10 -0
- package/dist/claim-helpers/claims-helpers-coverage.js +26 -0
- package/dist/claim-helpers/claims-helpers-device-use-statement.d.ts +8 -0
- package/dist/claim-helpers/claims-helpers-device-use-statement.js +20 -0
- package/dist/claim-helpers/claims-helpers-device.d.ts +8 -0
- package/dist/claim-helpers/claims-helpers-device.js +20 -0
- package/dist/claim-helpers/claims-helpers-diagnostic-report.d.ts +16 -0
- package/dist/claim-helpers/claims-helpers-diagnostic-report.js +47 -0
- package/dist/claim-helpers/claims-helpers-document-reference.d.ts +8 -0
- package/dist/claim-helpers/claims-helpers-document-reference.js +20 -0
- package/dist/claim-helpers/claims-helpers-encounter.d.ts +10 -0
- package/dist/claim-helpers/claims-helpers-encounter.js +26 -0
- package/dist/claim-helpers/claims-helpers-flag.d.ts +8 -0
- package/dist/claim-helpers/claims-helpers-flag.js +20 -0
- package/dist/claim-helpers/claims-helpers-immunization.d.ts +8 -0
- package/dist/claim-helpers/claims-helpers-immunization.js +20 -0
- package/dist/claim-helpers/claims-helpers-location.d.ts +10 -0
- package/dist/claim-helpers/claims-helpers-location.js +26 -0
- package/dist/claim-helpers/claims-helpers-medication-statement.d.ts +96 -0
- package/dist/claim-helpers/claims-helpers-medication-statement.js +278 -0
- package/dist/claim-helpers/claims-helpers-observation.d.ts +8 -0
- package/dist/claim-helpers/claims-helpers-observation.js +20 -0
- package/dist/claim-helpers/claims-helpers-organization.d.ts +12 -0
- package/dist/claim-helpers/claims-helpers-organization.js +32 -0
- package/dist/claim-helpers/claims-helpers-procedure.d.ts +8 -0
- package/dist/claim-helpers/claims-helpers-procedure.js +20 -0
- package/dist/claim-helpers/claims-helpers-related-person.d.ts +22 -0
- package/dist/claim-helpers/claims-helpers-related-person.js +53 -0
- package/dist/claim-helpers/index.d.ts +23 -0
- package/dist/claim-helpers/index.js +23 -0
- package/dist/claims/allergy-intolerance-claims.d.ts +153 -0
- package/dist/claims/allergy-intolerance-claims.js +194 -0
- package/dist/claims/appointment-claims.d.ts +226 -0
- package/dist/claims/appointment-claims.js +218 -0
- package/dist/claims/appointment-response-claims.d.ts +63 -0
- package/dist/claims/appointment-response-claims.js +58 -0
- package/dist/claims/care-plan-claims.d.ts +37 -0
- package/dist/claims/care-plan-claims.js +59 -0
- package/dist/claims/claim-types.d.ts +9 -0
- package/dist/claims/claim-types.js +3 -0
- package/dist/claims/clinical-impression-claims.d.ts +17 -0
- package/dist/claims/clinical-impression-claims.js +30 -0
- package/dist/claims/communication-claims.d.ts +55 -0
- package/dist/claims/communication-claims.js +57 -0
- package/dist/claims/composition-claims.d.ts +27 -0
- package/dist/claims/composition-claims.js +27 -0
- package/dist/claims/condition-claims.d.ts +79 -0
- package/dist/claims/condition-claims.js +104 -0
- package/dist/claims/consent-claims.d.ts +195 -0
- package/dist/claims/consent-claims.js +74 -0
- package/dist/claims/coverage-claims.d.ts +16 -0
- package/dist/claims/coverage-claims.js +28 -0
- package/dist/claims/device-claims.d.ts +20 -0
- package/dist/claims/device-claims.js +36 -0
- package/dist/claims/device-use-statement-claims.d.ts +17 -0
- package/dist/claims/device-use-statement-claims.js +28 -0
- package/dist/claims/diagnostic-report-claims.d.ts +109 -0
- package/dist/claims/diagnostic-report-claims.js +120 -0
- package/dist/claims/document-reference-claims.d.ts +39 -0
- package/dist/claims/document-reference-claims.js +96 -0
- package/dist/claims/encounter-claims.d.ts +18 -0
- package/dist/claims/encounter-claims.js +32 -0
- package/dist/claims/flag-claims.d.ts +19 -0
- package/dist/claims/flag-claims.js +33 -0
- package/dist/claims/immunization-claims.d.ts +31 -0
- package/dist/claims/immunization-claims.js +54 -0
- package/dist/claims/immunization-recommendation-claims.d.ts +19 -0
- package/dist/claims/immunization-recommendation-claims.js +26 -0
- package/dist/claims/index.d.ts +29 -0
- package/dist/claims/index.js +29 -0
- package/dist/claims/invoice-claims.d.ts +277 -0
- package/dist/claims/invoice-claims.js +357 -0
- package/dist/claims/location-claims.d.ts +22 -0
- package/dist/claims/location-claims.js +34 -0
- package/dist/claims/medication-claims.d.ts +15 -0
- package/dist/claims/medication-claims.js +23 -0
- package/dist/claims/medication-statement-claims.d.ts +539 -0
- package/dist/claims/medication-statement-claims.js +469 -0
- package/dist/claims/organization-claims.d.ts +18 -0
- package/dist/claims/organization-claims.js +27 -0
- package/dist/claims/practitioner-role-claims.d.ts +18 -0
- package/dist/claims/practitioner-role-claims.js +33 -0
- package/dist/claims/procedure-claims.d.ts +24 -0
- package/dist/claims/procedure-claims.js +44 -0
- package/dist/claims/related-person-claims.d.ts +30 -0
- package/dist/claims/related-person-claims.js +44 -0
- package/dist/claims/research-subject-claims.d.ts +4 -0
- package/dist/claims/research-subject-claims.js +4 -0
- package/dist/claims/task-claims.d.ts +61 -0
- package/dist/claims/task-claims.js +64 -0
- package/dist/clinical-terminology.d.ts +73 -0
- package/dist/clinical-terminology.js +77 -0
- package/dist/constants/clinical-statuses.d.ts +102 -0
- package/dist/constants/clinical-statuses.js +93 -0
- package/dist/constants/fhir-code-systems.d.ts +15 -0
- package/dist/constants/fhir-code-systems.js +15 -0
- package/dist/constants/fhir-resource-types.d.ts +162 -0
- package/dist/constants/fhir-resource-types.js +70 -0
- package/dist/constants/fhir-versions.d.ts +10 -0
- package/dist/constants/fhir-versions.js +10 -0
- package/dist/constants/index.d.ts +4 -0
- package/dist/constants/index.js +4 -0
- package/dist/converters/convert-allergy-intolerance.d.ts +3 -0
- package/dist/converters/convert-allergy-intolerance.js +85 -0
- package/dist/converters/convert-appointment-response.d.ts +3 -0
- package/dist/converters/convert-appointment-response.js +32 -0
- package/dist/converters/convert-appointment.d.ts +3 -0
- package/dist/converters/convert-appointment.js +61 -0
- package/dist/converters/convert-care-plan.d.ts +3 -0
- package/dist/converters/convert-care-plan.js +93 -0
- package/dist/converters/convert-clinical-impression.d.ts +3 -0
- package/dist/converters/convert-clinical-impression.js +42 -0
- package/dist/converters/convert-composition.d.ts +3 -0
- package/dist/converters/convert-composition.js +70 -0
- package/dist/converters/convert-condition.d.ts +3 -0
- package/dist/converters/convert-condition.js +51 -0
- package/dist/converters/convert-consent.d.ts +9 -0
- package/dist/converters/convert-consent.js +174 -0
- package/dist/converters/convert-coverage.d.ts +3 -0
- package/dist/converters/convert-coverage.js +51 -0
- package/dist/converters/convert-device-use-statement.d.ts +3 -0
- package/dist/converters/convert-device-use-statement.js +36 -0
- package/dist/converters/convert-device.d.ts +3 -0
- package/dist/converters/convert-device.js +48 -0
- package/dist/converters/convert-diagnostic-report.d.ts +3 -0
- package/dist/converters/convert-diagnostic-report.js +63 -0
- package/dist/converters/convert-document-reference.d.ts +3 -0
- package/dist/converters/convert-document-reference.js +54 -0
- package/dist/converters/convert-encounter.d.ts +3 -0
- package/dist/converters/convert-encounter.js +68 -0
- package/dist/converters/convert-flag.d.ts +3 -0
- package/dist/converters/convert-flag.js +42 -0
- package/dist/converters/convert-immunization.d.ts +3 -0
- package/dist/converters/convert-immunization.js +80 -0
- package/dist/converters/convert-location.d.ts +3 -0
- package/dist/converters/convert-location.js +44 -0
- package/dist/converters/convert-medication-statement.d.ts +3 -0
- package/dist/converters/convert-medication-statement.js +142 -0
- package/dist/converters/convert-observation.d.ts +33 -0
- package/dist/converters/convert-observation.js +246 -0
- package/dist/converters/convert-organization.d.ts +3 -0
- package/dist/converters/convert-organization.js +39 -0
- package/dist/converters/convert-practitioner-role.d.ts +3 -0
- package/dist/converters/convert-practitioner-role.js +62 -0
- package/dist/converters/convert-procedure.d.ts +3 -0
- package/dist/converters/convert-procedure.js +64 -0
- package/dist/converters/convert-related-person.d.ts +3 -0
- package/dist/converters/convert-related-person.js +31 -0
- package/dist/converters/convert-shared.d.ts +38 -0
- package/dist/converters/convert-shared.js +190 -0
- package/dist/converters/index.d.ts +24 -0
- package/dist/converters/index.js +24 -0
- package/dist/fhir-bundle.d.ts +9 -0
- package/dist/fhir-bundle.js +43 -0
- package/dist/fhir-search-parameters.d.ts +18 -0
- package/dist/fhir-search-parameters.js +52 -0
- package/dist/fhir-tokens.d.ts +11 -0
- package/dist/fhir-tokens.js +26 -0
- package/dist/fhir-types.d.ts +77 -0
- package/dist/fhir-types.js +1 -0
- package/dist/fhir-values.d.ts +13 -0
- package/dist/fhir-values.js +53 -0
- package/dist/group-r5.d.ts +121 -0
- package/dist/group-r5.js +301 -0
- package/dist/imaging-types.d.ts +30 -0
- package/dist/imaging-types.js +1 -0
- package/dist/immunization-flat-claims.d.ts +2 -0
- package/dist/immunization-flat-claims.js +2 -0
- package/dist/immunization-reminder-plan.d.ts +2 -0
- package/dist/immunization-reminder-plan.js +2 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +19 -0
- package/dist/observation-claims.d.ts +170 -0
- package/dist/observation-claims.js +184 -0
- package/dist/related-person.d.ts +16 -0
- package/dist/related-person.js +14 -0
- package/dist/research-study-r5.d.ts +163 -0
- package/dist/research-study-r5.js +490 -0
- package/dist/search-models/index.d.ts +7 -0
- package/dist/search-models/index.js +7 -0
- package/dist/search-models/params/AdverseEvent.params.model.d.ts +14 -0
- package/dist/search-models/params/AdverseEvent.params.model.js +16 -0
- package/dist/search-models/params/AllergyIntolerance.params.model.d.ts +24 -0
- package/dist/search-models/params/AllergyIntolerance.params.model.js +27 -0
- package/dist/search-models/params/Appointment.params.model.d.ts +12 -0
- package/dist/search-models/params/Appointment.params.model.js +14 -0
- package/dist/search-models/params/ClinicalImpression.params.model.d.ts +14 -0
- package/dist/search-models/params/ClinicalImpression.params.model.js +15 -0
- package/dist/search-models/params/Condition.params.model.d.ts +16 -0
- package/dist/search-models/params/Condition.params.model.js +17 -0
- package/dist/search-models/params/DetectedIssue.params.model.d.ts +9 -0
- package/dist/search-models/params/DetectedIssue.params.model.js +11 -0
- package/dist/search-models/params/DiagnosticReport.params.model.d.ts +13 -0
- package/dist/search-models/params/DiagnosticReport.params.model.js +14 -0
- package/dist/search-models/params/Encounter.params.model.d.ts +15 -0
- package/dist/search-models/params/Encounter.params.model.js +17 -0
- package/dist/search-models/params/Immunization.params.model.d.ts +14 -0
- package/dist/search-models/params/Immunization.params.model.js +16 -0
- package/dist/search-models/params/Medication.params.model.d.ts +35 -0
- package/dist/search-models/params/Medication.params.model.js +45 -0
- package/dist/search-models/params/Observation.params.model.d.ts +326 -0
- package/dist/search-models/params/Observation.params.model.js +244 -0
- package/dist/search-models/params/Procedure.params.model.d.ts +15 -0
- package/dist/search-models/params/Procedure.params.model.js +17 -0
- package/dist/search-models/params/Search.params.model.d.ts +75 -0
- package/dist/search-models/params/Search.params.model.js +69 -0
- package/dist/search-models/params/ServiceRequest.params.model.d.ts +14 -0
- package/dist/search-models/params/ServiceRequest.params.model.js +16 -0
- package/dist/search-models/params/Specimen.params.model.d.ts +10 -0
- package/dist/search-models/params/Specimen.params.model.js +12 -0
- package/dist/search-models/params/index.d.ts +15 -0
- package/dist/search-models/params/index.js +15 -0
- package/dist/search-models/templates/AdverseEvent.template.model.d.ts +16 -0
- package/dist/search-models/templates/AdverseEvent.template.model.js +2 -0
- package/dist/search-models/templates/AllergyIntolerance.template.model.d.ts +24 -0
- package/dist/search-models/templates/AllergyIntolerance.template.model.js +1 -0
- package/dist/search-models/templates/Appointment.template.model.d.ts +13 -0
- package/dist/search-models/templates/Appointment.template.model.js +1 -0
- package/dist/search-models/templates/ClinicalImpression.template.model.d.ts +16 -0
- package/dist/search-models/templates/ClinicalImpression.template.model.js +1 -0
- package/dist/search-models/templates/Condition.template.model.d.ts +18 -0
- package/dist/search-models/templates/Condition.template.model.js +1 -0
- package/dist/search-models/templates/DetectedIssue.template.model.d.ts +11 -0
- package/dist/search-models/templates/DetectedIssue.template.model.js +1 -0
- package/dist/search-models/templates/DiagnosticReport.template.model.d.ts +15 -0
- package/dist/search-models/templates/DiagnosticReport.template.model.js +1 -0
- package/dist/search-models/templates/Encounter.template.model.d.ts +17 -0
- package/dist/search-models/templates/Encounter.template.model.js +1 -0
- package/dist/search-models/templates/Immunization.template.model.d.ts +18 -0
- package/dist/search-models/templates/Immunization.template.model.js +2 -0
- package/dist/search-models/templates/Medication.template.model.d.ts +37 -0
- package/dist/search-models/templates/Medication.template.model.js +1 -0
- package/dist/search-models/templates/Observation.template.model.d.ts +18 -0
- package/dist/search-models/templates/Observation.template.model.js +1 -0
- package/dist/search-models/templates/Procedure.template.model.d.ts +17 -0
- package/dist/search-models/templates/Procedure.template.model.js +1 -0
- package/dist/search-models/templates/ServiceRequest.template.model.d.ts +16 -0
- package/dist/search-models/templates/ServiceRequest.template.model.js +1 -0
- package/dist/search-models/templates/Specimen.template.model.d.ts +12 -0
- package/dist/search-models/templates/Specimen.template.model.js +1 -0
- package/dist/search-models/templates/index.d.ts +14 -0
- package/dist/search-models/templates/index.js +14 -0
- package/dist/terminology-models/AtcModel.d.ts +15 -0
- package/dist/terminology-models/AtcModel.js +2 -0
- package/dist/terminology-models/CommonModels.d.ts +70 -0
- package/dist/terminology-models/CommonModels.js +55 -0
- package/dist/terminology-models/FhirModels.d.ts +60 -0
- package/dist/terminology-models/FhirModels.js +34 -0
- package/dist/terminology-models/LoincModels.d.ts +24 -0
- package/dist/terminology-models/LoincModels.js +2 -0
- package/dist/terminology-models/SnomedModels.d.ts +13 -0
- package/dist/terminology-models/SnomedModels.js +2 -0
- package/dist/terminology-models/index.d.ts +6 -0
- package/dist/terminology-models/index.js +6 -0
- package/docs/UHC_FHIR_UTILS_MIGRATION.md +51 -8
- package/package.json +83 -2
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// Copyright 2026 Conéctate Soluciones y Aplicaciones SL under the Apache License, Version 2.0.
|
|
2
|
+
// File: src/utils/convert-shared.ts
|
|
3
|
+
export function isPlainObject(value) {
|
|
4
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
export function codingFromValue(value) {
|
|
7
|
+
if (!value)
|
|
8
|
+
return undefined;
|
|
9
|
+
const [system, code] = value.split('|');
|
|
10
|
+
if (!code)
|
|
11
|
+
return [{ code: system }];
|
|
12
|
+
return system ? [{ system, code }] : [{ code }];
|
|
13
|
+
}
|
|
14
|
+
/** Convert one canonical comma-separated token list into FHIR Coding entries. */
|
|
15
|
+
export function codingListFromCsv(value) {
|
|
16
|
+
const codings = String(value || '')
|
|
17
|
+
.split(',')
|
|
18
|
+
.map((token) => token.trim())
|
|
19
|
+
.filter(Boolean)
|
|
20
|
+
.flatMap((token) => codingFromValue(token) ?? []);
|
|
21
|
+
return codings.length ? codings : undefined;
|
|
22
|
+
}
|
|
23
|
+
export function codingToValue(coding) {
|
|
24
|
+
if (!coding?.code)
|
|
25
|
+
return undefined;
|
|
26
|
+
return coding.system ? `${coding.system}|${coding.code}` : coding.code;
|
|
27
|
+
}
|
|
28
|
+
export function referenceToValue(reference) {
|
|
29
|
+
return reference?.reference;
|
|
30
|
+
}
|
|
31
|
+
export function referenceListToCsv(references) {
|
|
32
|
+
const values = (references || [])
|
|
33
|
+
.map((item) => item?.reference)
|
|
34
|
+
.filter((item) => Boolean(item));
|
|
35
|
+
return values.length ? values.join(',') : undefined;
|
|
36
|
+
}
|
|
37
|
+
export function codingListToCsv(codings) {
|
|
38
|
+
const values = (codings || [])
|
|
39
|
+
.map((item) => codingToValue(item))
|
|
40
|
+
.filter((item) => Boolean(item));
|
|
41
|
+
return values.length ? values.join(',') : undefined;
|
|
42
|
+
}
|
|
43
|
+
export function requireClaim(claims, key) {
|
|
44
|
+
const value = claims[key];
|
|
45
|
+
if (!value)
|
|
46
|
+
throw new Error(`Missing required claim: ${key}`);
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
export function requireSubjectIdentifier(value, key) {
|
|
50
|
+
if (!value.startsWith('urn:') && !value.startsWith('did:web:')) {
|
|
51
|
+
throw new Error(`Invalid ${key}: expected urn:* or did:web:*`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export function requireDidWeb(value, key) {
|
|
55
|
+
if (!value.startsWith('did:web:')) {
|
|
56
|
+
throw new Error(`Invalid ${key}: expected did:web:*`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function stringifyClaimLeaf(value) {
|
|
60
|
+
if (value === undefined || value === null)
|
|
61
|
+
return undefined;
|
|
62
|
+
if (typeof value === 'string')
|
|
63
|
+
return value;
|
|
64
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
65
|
+
return String(value);
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
function flattenClaimPath(prefix, value, out) {
|
|
69
|
+
const primitive = stringifyClaimLeaf(value);
|
|
70
|
+
if (primitive !== undefined) {
|
|
71
|
+
out[prefix] = primitive;
|
|
72
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
73
|
+
out[`${prefix}#type`] = typeof value;
|
|
74
|
+
}
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (Array.isArray(value)) {
|
|
78
|
+
value.forEach((entry, index) => {
|
|
79
|
+
flattenClaimPath(`${prefix}[${index}]`, entry, out);
|
|
80
|
+
});
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (isPlainObject(value)) {
|
|
84
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
85
|
+
if (key === 'claims' && prefix.endsWith('.meta'))
|
|
86
|
+
continue;
|
|
87
|
+
if (key === 'meta' && isPlainObject(nestedValue) && 'claims' in nestedValue) {
|
|
88
|
+
const nestedMeta = { ...nestedValue };
|
|
89
|
+
delete nestedMeta.claims;
|
|
90
|
+
if (Object.keys(nestedMeta).length === 0)
|
|
91
|
+
continue;
|
|
92
|
+
flattenClaimPath(`${prefix}.${key}`, nestedMeta, out);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
flattenClaimPath(`${prefix}.${key}`, nestedValue, out);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function parseClaimPath(path) {
|
|
100
|
+
const segments = [];
|
|
101
|
+
const re = /([^[.\]]+)|\[(\d+)\]/g;
|
|
102
|
+
let match;
|
|
103
|
+
while ((match = re.exec(path)) !== null) {
|
|
104
|
+
if (match[1])
|
|
105
|
+
segments.push(match[1]);
|
|
106
|
+
else if (match[2])
|
|
107
|
+
segments.push(Number(match[2]));
|
|
108
|
+
}
|
|
109
|
+
return segments;
|
|
110
|
+
}
|
|
111
|
+
function coerceClaimLeaf(value, typeHint) {
|
|
112
|
+
if (typeHint === 'boolean')
|
|
113
|
+
return value === 'true';
|
|
114
|
+
if (typeHint === 'number')
|
|
115
|
+
return Number(value);
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
function assignInflatedPath(target, path, value, typeHint) {
|
|
119
|
+
const segments = parseClaimPath(path);
|
|
120
|
+
if (segments.length === 0)
|
|
121
|
+
return;
|
|
122
|
+
let cursor = target;
|
|
123
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
124
|
+
const segment = segments[index];
|
|
125
|
+
const nextSegment = segments[index + 1];
|
|
126
|
+
const isLast = index === segments.length - 1;
|
|
127
|
+
if (typeof segment === 'number') {
|
|
128
|
+
if (!Array.isArray(cursor))
|
|
129
|
+
return;
|
|
130
|
+
if (isLast) {
|
|
131
|
+
cursor[segment] = coerceClaimLeaf(value, typeHint);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (cursor[segment] === undefined)
|
|
135
|
+
cursor[segment] = typeof nextSegment === 'number' ? [] : {};
|
|
136
|
+
cursor = cursor[segment];
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (!isPlainObject(cursor))
|
|
140
|
+
return;
|
|
141
|
+
if (isLast) {
|
|
142
|
+
cursor[segment] = coerceClaimLeaf(value, typeHint);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (cursor[segment] === undefined)
|
|
146
|
+
cursor[segment] = typeof nextSegment === 'number' ? [] : {};
|
|
147
|
+
cursor = cursor[segment];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Generic structural fallback for unsupported resources.
|
|
152
|
+
*/
|
|
153
|
+
export function fhirResourceToFlatClaims(resource, context = 'org.hl7.fhir.api') {
|
|
154
|
+
if (!resource?.resourceType) {
|
|
155
|
+
throw new Error('FHIR resource must define resourceType.');
|
|
156
|
+
}
|
|
157
|
+
const out = { '@context': context };
|
|
158
|
+
for (const [key, value] of Object.entries(resource)) {
|
|
159
|
+
if (key === 'resourceType')
|
|
160
|
+
continue;
|
|
161
|
+
flattenClaimPath(`${resource.resourceType}.${key}`, value, out);
|
|
162
|
+
}
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Generic structural regeneration for unsupported resources.
|
|
167
|
+
*/
|
|
168
|
+
export function flatClaimsToFhirResource(claims) {
|
|
169
|
+
const typeHints = new Map();
|
|
170
|
+
for (const [key, value] of Object.entries(claims || {})) {
|
|
171
|
+
if (key.endsWith('#type') && typeof value === 'string') {
|
|
172
|
+
typeHints.set(key.slice(0, -5), value);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const entries = Object.entries(claims || {}).filter(([key, value]) => key !== '@context' && !key.endsWith('#type') && value !== undefined);
|
|
176
|
+
const firstClaimKey = entries[0]?.[0];
|
|
177
|
+
if (!firstClaimKey || !firstClaimKey.includes('.')) {
|
|
178
|
+
throw new Error('Flat claims must contain at least one contextualized resource claim.');
|
|
179
|
+
}
|
|
180
|
+
const resourceType = firstClaimKey.split('.')[0];
|
|
181
|
+
const resource = { resourceType };
|
|
182
|
+
for (const [key, value] of entries) {
|
|
183
|
+
if (typeof value !== 'string')
|
|
184
|
+
continue;
|
|
185
|
+
if (!key.startsWith(`${resourceType}.`))
|
|
186
|
+
continue;
|
|
187
|
+
assignInflatedPath(resource, key.slice(resourceType.length + 1), value, typeHints.get(key));
|
|
188
|
+
}
|
|
189
|
+
return resource;
|
|
190
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export * from './convert-shared.js';
|
|
2
|
+
export * from './convert-allergy-intolerance.js';
|
|
3
|
+
export * from './convert-appointment-response.js';
|
|
4
|
+
export * from './convert-appointment.js';
|
|
5
|
+
export * from './convert-care-plan.js';
|
|
6
|
+
export * from './convert-clinical-impression.js';
|
|
7
|
+
export * from './convert-composition.js';
|
|
8
|
+
export * from './convert-consent.js';
|
|
9
|
+
export * from './convert-condition.js';
|
|
10
|
+
export * from './convert-coverage.js';
|
|
11
|
+
export * from './convert-device-use-statement.js';
|
|
12
|
+
export * from './convert-device.js';
|
|
13
|
+
export * from './convert-diagnostic-report.js';
|
|
14
|
+
export * from './convert-document-reference.js';
|
|
15
|
+
export * from './convert-encounter.js';
|
|
16
|
+
export * from './convert-flag.js';
|
|
17
|
+
export * from './convert-immunization.js';
|
|
18
|
+
export * from './convert-location.js';
|
|
19
|
+
export * from './convert-medication-statement.js';
|
|
20
|
+
export * from './convert-observation.js';
|
|
21
|
+
export * from './convert-organization.js';
|
|
22
|
+
export * from './convert-practitioner-role.js';
|
|
23
|
+
export * from './convert-procedure.js';
|
|
24
|
+
export * from './convert-related-person.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export * from './convert-shared.js';
|
|
2
|
+
export * from './convert-allergy-intolerance.js';
|
|
3
|
+
export * from './convert-appointment-response.js';
|
|
4
|
+
export * from './convert-appointment.js';
|
|
5
|
+
export * from './convert-care-plan.js';
|
|
6
|
+
export * from './convert-clinical-impression.js';
|
|
7
|
+
export * from './convert-composition.js';
|
|
8
|
+
export * from './convert-consent.js';
|
|
9
|
+
export * from './convert-condition.js';
|
|
10
|
+
export * from './convert-coverage.js';
|
|
11
|
+
export * from './convert-device-use-statement.js';
|
|
12
|
+
export * from './convert-device.js';
|
|
13
|
+
export * from './convert-diagnostic-report.js';
|
|
14
|
+
export * from './convert-document-reference.js';
|
|
15
|
+
export * from './convert-encounter.js';
|
|
16
|
+
export * from './convert-flag.js';
|
|
17
|
+
export * from './convert-immunization.js';
|
|
18
|
+
export * from './convert-location.js';
|
|
19
|
+
export * from './convert-medication-statement.js';
|
|
20
|
+
export * from './convert-observation.js';
|
|
21
|
+
export * from './convert-organization.js';
|
|
22
|
+
export * from './convert-practitioner-role.js';
|
|
23
|
+
export * from './convert-procedure.js';
|
|
24
|
+
export * from './convert-related-person.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FhirBundle, FhirResource } from './fhir-types.js';
|
|
2
|
+
export declare function readBundleResources<TResource extends FhirResource = FhirResource>(bundle: FhirBundle<TResource>): readonly TResource[];
|
|
3
|
+
export declare function findBundleResources<TResource extends FhirResource = FhirResource>(bundle: FhirBundle, resourceType: string): readonly TResource[];
|
|
4
|
+
/** Traverses top-level and native contained resources without changing ownership. */
|
|
5
|
+
export declare function walkBundleResources(bundle: FhirBundle): readonly FhirResource[];
|
|
6
|
+
/** Classifies top-level Bundle resources in encounter order without cloning or mutation. */
|
|
7
|
+
export declare function classifyBundleResources(bundle: FhirBundle): ReadonlyMap<string, readonly FhirResource[]>;
|
|
8
|
+
/** Resolves `ResourceType/id`, `urn:uuid:*`, an entry fullUrl, or a bare resource id. */
|
|
9
|
+
export declare function findBundleResourceByReference(bundle: FhirBundle, reference: string): FhirResource | undefined;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export function readBundleResources(bundle) {
|
|
2
|
+
if (bundle.resourceType !== 'Bundle')
|
|
3
|
+
throw new TypeError('fhir_bundle_required');
|
|
4
|
+
return Object.freeze((bundle.entry ?? []).flatMap(entry => entry.resource ? [entry.resource] : []));
|
|
5
|
+
}
|
|
6
|
+
export function findBundleResources(bundle, resourceType) {
|
|
7
|
+
return Object.freeze(readBundleResources(bundle).filter(resource => resource.resourceType === resourceType));
|
|
8
|
+
}
|
|
9
|
+
/** Traverses top-level and native contained resources without changing ownership. */
|
|
10
|
+
export function walkBundleResources(bundle) {
|
|
11
|
+
const output = [];
|
|
12
|
+
const visit = (resource) => {
|
|
13
|
+
output.push(resource);
|
|
14
|
+
for (const child of resource.contained ?? [])
|
|
15
|
+
visit(child);
|
|
16
|
+
};
|
|
17
|
+
for (const resource of readBundleResources(bundle))
|
|
18
|
+
visit(resource);
|
|
19
|
+
return Object.freeze(output);
|
|
20
|
+
}
|
|
21
|
+
/** Classifies top-level Bundle resources in encounter order without cloning or mutation. */
|
|
22
|
+
export function classifyBundleResources(bundle) {
|
|
23
|
+
const classified = new Map();
|
|
24
|
+
for (const resource of readBundleResources(bundle)) {
|
|
25
|
+
const resources = classified.get(resource.resourceType) ?? [];
|
|
26
|
+
resources.push(resource);
|
|
27
|
+
classified.set(resource.resourceType, resources);
|
|
28
|
+
}
|
|
29
|
+
return classified;
|
|
30
|
+
}
|
|
31
|
+
/** Resolves `ResourceType/id`, `urn:uuid:*`, an entry fullUrl, or a bare resource id. */
|
|
32
|
+
export function findBundleResourceByReference(bundle, reference) {
|
|
33
|
+
const target = reference.trim();
|
|
34
|
+
if (!target)
|
|
35
|
+
throw new TypeError('fhir_reference_required');
|
|
36
|
+
const directEntry = bundle.entry?.find(entry => entry.fullUrl === target);
|
|
37
|
+
if (directEntry?.resource)
|
|
38
|
+
return directEntry.resource;
|
|
39
|
+
const [resourceType, id] = target.includes('/')
|
|
40
|
+
? target.split('/', 2)
|
|
41
|
+
: [undefined, target.startsWith('urn:uuid:') ? target.slice('urn:uuid:'.length) : target];
|
|
42
|
+
return readBundleResources(bundle).find(resource => resource.id === id && (!resourceType || resource.resourceType === resourceType));
|
|
43
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type FhirSearchParameterType = 'number' | 'date' | 'string' | 'token' | 'reference' | 'composite' | 'quantity' | 'uri';
|
|
2
|
+
export type FhirSearchPrefix = 'eq' | 'ne' | 'gt' | 'lt' | 'ge' | 'le' | 'sa' | 'eb' | 'ap';
|
|
3
|
+
export type FhirSearchParameter = Readonly<{
|
|
4
|
+
name: string;
|
|
5
|
+
type: FhirSearchParameterType;
|
|
6
|
+
value: string | number;
|
|
7
|
+
prefix?: FhirSearchPrefix;
|
|
8
|
+
system?: string;
|
|
9
|
+
unit?: string;
|
|
10
|
+
}>;
|
|
11
|
+
/** Parses one typed FHIR query value without the lossy integer coercion of the retired UHC helper. */
|
|
12
|
+
export declare function parseFhirSearchParameter(input: Readonly<{
|
|
13
|
+
name: string;
|
|
14
|
+
type: FhirSearchParameterType;
|
|
15
|
+
value: string;
|
|
16
|
+
}>): FhirSearchParameter;
|
|
17
|
+
/** Repeats parameters for AND; callers may use comma-separated values for FHIR OR. */
|
|
18
|
+
export declare function buildFhirSearchQuery(parameters: readonly FhirSearchParameter[]): string;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const PREFIX = /^(eq|ne|gt|lt|ge|le|sa|eb|ap)/;
|
|
2
|
+
/** Parses one typed FHIR query value without the lossy integer coercion of the retired UHC helper. */
|
|
3
|
+
export function parseFhirSearchParameter(input) {
|
|
4
|
+
const name = required(input.name, 'fhir_search_name_required');
|
|
5
|
+
const raw = required(input.value, 'fhir_search_value_required');
|
|
6
|
+
if (input.type === 'token') {
|
|
7
|
+
const separator = raw.lastIndexOf('|');
|
|
8
|
+
return Object.freeze({ name, type: input.type, value: separator < 0 ? raw : raw.slice(separator + 1), ...(separator < 0 ? {} : { system: raw.slice(0, separator) }) });
|
|
9
|
+
}
|
|
10
|
+
if (input.type === 'quantity') {
|
|
11
|
+
const match = raw.match(PREFIX);
|
|
12
|
+
const withoutPrefix = match ? raw.slice(2) : raw;
|
|
13
|
+
const [numberValue, system, unit] = withoutPrefix.split('|');
|
|
14
|
+
const value = Number(numberValue);
|
|
15
|
+
if (!Number.isFinite(value))
|
|
16
|
+
throw new TypeError('fhir_search_quantity_invalid');
|
|
17
|
+
return Object.freeze({ name, type: input.type, value, ...(match ? { prefix: match[1] } : {}), ...(system ? { system } : {}), ...(unit ? { unit } : {}) });
|
|
18
|
+
}
|
|
19
|
+
if (input.type === 'number') {
|
|
20
|
+
const match = raw.match(PREFIX);
|
|
21
|
+
const value = Number(match ? raw.slice(2) : raw);
|
|
22
|
+
if (!Number.isFinite(value))
|
|
23
|
+
throw new TypeError('fhir_search_number_invalid');
|
|
24
|
+
return Object.freeze({ name, type: input.type, value, ...(match ? { prefix: match[1] } : {}) });
|
|
25
|
+
}
|
|
26
|
+
if (input.type === 'date') {
|
|
27
|
+
const match = raw.match(PREFIX);
|
|
28
|
+
return Object.freeze({ name, type: input.type, value: match ? raw.slice(2) : raw, ...(match ? { prefix: match[1] } : {}) });
|
|
29
|
+
}
|
|
30
|
+
return Object.freeze({ name, type: input.type, value: raw });
|
|
31
|
+
}
|
|
32
|
+
/** Repeats parameters for AND; callers may use comma-separated values for FHIR OR. */
|
|
33
|
+
export function buildFhirSearchQuery(parameters) {
|
|
34
|
+
const query = new URLSearchParams();
|
|
35
|
+
for (const parameter of parameters) {
|
|
36
|
+
const prefix = parameter.prefix ?? '';
|
|
37
|
+
const scalar = String(parameter.value);
|
|
38
|
+
const value = parameter.type === 'token' && parameter.system
|
|
39
|
+
? `${parameter.system}|${scalar}`
|
|
40
|
+
: parameter.type === 'quantity' && (parameter.system || parameter.unit)
|
|
41
|
+
? `${prefix}${scalar}|${parameter.system ?? ''}|${parameter.unit ?? ''}`
|
|
42
|
+
: `${prefix}${scalar}`;
|
|
43
|
+
query.append(required(parameter.name, 'fhir_search_name_required'), value);
|
|
44
|
+
}
|
|
45
|
+
return query.toString();
|
|
46
|
+
}
|
|
47
|
+
function required(value, error) {
|
|
48
|
+
const normalized = value.trim();
|
|
49
|
+
if (!normalized)
|
|
50
|
+
throw new TypeError(error);
|
|
51
|
+
return normalized;
|
|
52
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FhirContactPoint } from './fhir-types.js';
|
|
2
|
+
/** Builds the FHIR token wire form after rejecting empty or ambiguous input. */
|
|
3
|
+
export declare function makeFhirToken(system: string, code: string): string;
|
|
4
|
+
/** Escapes FHIR search delimiters according to the search value grammar. */
|
|
5
|
+
export declare function escapeFhirSearchValue(value: string): string;
|
|
6
|
+
/** Builds one comma-separated FHIR OR value; repeated query keys remain AND. */
|
|
7
|
+
export declare function joinFhirOrList(values?: readonly (string | number)[]): string | undefined;
|
|
8
|
+
/** Converts valid ContactPoint values to token search values. */
|
|
9
|
+
export declare function telecomToTokens(resource: Readonly<{
|
|
10
|
+
telecom?: readonly FhirContactPoint[];
|
|
11
|
+
}>): readonly string[];
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Builds the FHIR token wire form after rejecting empty or ambiguous input. */
|
|
2
|
+
export function makeFhirToken(system, code) {
|
|
3
|
+
const normalizedSystem = required(system, 'fhir_token_system_required');
|
|
4
|
+
const normalizedCode = required(code, 'fhir_token_code_required');
|
|
5
|
+
if (normalizedSystem.includes('|') || normalizedCode.includes('|'))
|
|
6
|
+
throw new TypeError('fhir_token_invalid');
|
|
7
|
+
return `${normalizedSystem}|${normalizedCode}`;
|
|
8
|
+
}
|
|
9
|
+
/** Escapes FHIR search delimiters according to the search value grammar. */
|
|
10
|
+
export function escapeFhirSearchValue(value) {
|
|
11
|
+
return String(value).replace(/[\\,|$]/g, '\\$&');
|
|
12
|
+
}
|
|
13
|
+
/** Builds one comma-separated FHIR OR value; repeated query keys remain AND. */
|
|
14
|
+
export function joinFhirOrList(values) {
|
|
15
|
+
return values?.length ? values.map(value => escapeFhirSearchValue(String(value))).join(',') : undefined;
|
|
16
|
+
}
|
|
17
|
+
/** Converts valid ContactPoint values to token search values. */
|
|
18
|
+
export function telecomToTokens(resource) {
|
|
19
|
+
return Object.freeze((resource.telecom ?? []).flatMap(point => point.system && point.value?.trim() ? [makeFhirToken(point.system, point.value.trim())] : []));
|
|
20
|
+
}
|
|
21
|
+
function required(value, error) {
|
|
22
|
+
const normalized = value.trim();
|
|
23
|
+
if (!normalized)
|
|
24
|
+
throw new TypeError(error);
|
|
25
|
+
return normalized;
|
|
26
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/** Version-neutral FHIR primitives used by flat-claim adapters. */
|
|
2
|
+
export type FhirPrimitive = string | number | boolean;
|
|
3
|
+
export type FhirCoding = Readonly<{
|
|
4
|
+
system?: string;
|
|
5
|
+
version?: string;
|
|
6
|
+
code?: string;
|
|
7
|
+
display?: string;
|
|
8
|
+
userSelected?: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
export type FhirCodeableConcept = Readonly<{
|
|
11
|
+
coding?: readonly FhirCoding[];
|
|
12
|
+
text?: string;
|
|
13
|
+
}>;
|
|
14
|
+
export type FhirIdentifier = Readonly<{
|
|
15
|
+
use?: string;
|
|
16
|
+
type?: FhirCodeableConcept;
|
|
17
|
+
system?: string;
|
|
18
|
+
value?: string;
|
|
19
|
+
period?: FhirPeriod;
|
|
20
|
+
assigner?: FhirReference;
|
|
21
|
+
}>;
|
|
22
|
+
export type FhirReference = Readonly<{
|
|
23
|
+
reference?: string;
|
|
24
|
+
type?: string;
|
|
25
|
+
identifier?: FhirIdentifier;
|
|
26
|
+
display?: string;
|
|
27
|
+
}>;
|
|
28
|
+
export type FhirQuantity = Readonly<{
|
|
29
|
+
value?: number;
|
|
30
|
+
comparator?: '<' | '<=' | '>=' | '>';
|
|
31
|
+
unit?: string;
|
|
32
|
+
system?: string;
|
|
33
|
+
code?: string;
|
|
34
|
+
}>;
|
|
35
|
+
export type FhirPeriod = Readonly<{
|
|
36
|
+
start?: string;
|
|
37
|
+
end?: string;
|
|
38
|
+
}>;
|
|
39
|
+
export type FhirAnnotation = Readonly<{
|
|
40
|
+
authorReference?: FhirReference;
|
|
41
|
+
authorString?: string;
|
|
42
|
+
time?: string;
|
|
43
|
+
text: string;
|
|
44
|
+
}>;
|
|
45
|
+
export type FhirContactPoint = Readonly<{
|
|
46
|
+
system?: 'phone' | 'fax' | 'email' | 'pager' | 'url' | 'sms' | 'other';
|
|
47
|
+
value?: string;
|
|
48
|
+
use?: 'home' | 'work' | 'temp' | 'old' | 'mobile';
|
|
49
|
+
rank?: number;
|
|
50
|
+
period?: FhirPeriod;
|
|
51
|
+
}>;
|
|
52
|
+
export type FhirResource = Readonly<{
|
|
53
|
+
resourceType: string;
|
|
54
|
+
id?: string;
|
|
55
|
+
language?: string;
|
|
56
|
+
meta?: Readonly<Record<string, unknown>>;
|
|
57
|
+
contained?: readonly FhirResource[];
|
|
58
|
+
[key: string]: unknown;
|
|
59
|
+
}>;
|
|
60
|
+
export type FhirBundleEntry<TResource extends FhirResource = FhirResource> = Readonly<{
|
|
61
|
+
fullUrl?: string;
|
|
62
|
+
resource?: TResource;
|
|
63
|
+
request?: Readonly<{
|
|
64
|
+
method?: string;
|
|
65
|
+
url?: string;
|
|
66
|
+
}>;
|
|
67
|
+
response?: Readonly<{
|
|
68
|
+
status?: string;
|
|
69
|
+
location?: string;
|
|
70
|
+
}>;
|
|
71
|
+
}>;
|
|
72
|
+
export type FhirBundle<TResource extends FhirResource = FhirResource> = FhirResource & Readonly<{
|
|
73
|
+
resourceType: 'Bundle';
|
|
74
|
+
type?: string;
|
|
75
|
+
total?: number;
|
|
76
|
+
entry?: readonly FhirBundleEntry<TResource>[];
|
|
77
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FhirCoding, FhirIdentifier, FhirQuantity, FhirReference } from './fhir-types.js';
|
|
2
|
+
/** Serializes a Coding as the established `system|code` flat token. */
|
|
3
|
+
export declare function codingToToken(coding: FhirCoding | undefined): string | undefined;
|
|
4
|
+
/** Parses a flat coding token without treating URI colons or slashes as separators. */
|
|
5
|
+
export declare function tokenToCoding(token: string | undefined): FhirCoding | undefined;
|
|
6
|
+
/** Serializes an Identifier without discarding its assigning system. */
|
|
7
|
+
export declare function identifierToToken(identifier: FhirIdentifier | undefined): string | undefined;
|
|
8
|
+
export declare function tokenToIdentifier(token: string | undefined): FhirIdentifier | undefined;
|
|
9
|
+
export declare function referenceToString(reference: FhirReference | undefined): string | undefined;
|
|
10
|
+
export declare function stringToReference(reference: string | undefined): FhirReference | undefined;
|
|
11
|
+
/** Serializes Quantity as `value|system|code`; unit remains display-only. */
|
|
12
|
+
export declare function quantityToToken(quantity: FhirQuantity | undefined): string | undefined;
|
|
13
|
+
export declare function tokenToQuantity(token: string | undefined): FhirQuantity | undefined;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** Serializes a Coding as the established `system|code` flat token. */
|
|
2
|
+
export function codingToToken(coding) {
|
|
3
|
+
const code = coding?.code?.trim();
|
|
4
|
+
if (!code)
|
|
5
|
+
return undefined;
|
|
6
|
+
const system = coding?.system?.trim();
|
|
7
|
+
return system ? `${system}|${code}` : code;
|
|
8
|
+
}
|
|
9
|
+
/** Parses a flat coding token without treating URI colons or slashes as separators. */
|
|
10
|
+
export function tokenToCoding(token) {
|
|
11
|
+
const value = token?.trim();
|
|
12
|
+
if (!value)
|
|
13
|
+
return undefined;
|
|
14
|
+
const separator = value.lastIndexOf('|');
|
|
15
|
+
return separator < 0
|
|
16
|
+
? Object.freeze({ code: value })
|
|
17
|
+
: Object.freeze({ system: value.slice(0, separator) || undefined, code: value.slice(separator + 1) });
|
|
18
|
+
}
|
|
19
|
+
/** Serializes an Identifier without discarding its assigning system. */
|
|
20
|
+
export function identifierToToken(identifier) {
|
|
21
|
+
const value = identifier?.value?.trim();
|
|
22
|
+
if (!value)
|
|
23
|
+
return undefined;
|
|
24
|
+
const system = identifier?.system?.trim();
|
|
25
|
+
return system ? `${system}|${value}` : value;
|
|
26
|
+
}
|
|
27
|
+
export function tokenToIdentifier(token) {
|
|
28
|
+
const coding = tokenToCoding(token);
|
|
29
|
+
return coding?.code ? Object.freeze({ system: coding.system, value: coding.code }) : undefined;
|
|
30
|
+
}
|
|
31
|
+
export function referenceToString(reference) {
|
|
32
|
+
return reference?.reference?.trim() || undefined;
|
|
33
|
+
}
|
|
34
|
+
export function stringToReference(reference) {
|
|
35
|
+
const value = reference?.trim();
|
|
36
|
+
return value ? Object.freeze({ reference: value }) : undefined;
|
|
37
|
+
}
|
|
38
|
+
/** Serializes Quantity as `value|system|code`; unit remains display-only. */
|
|
39
|
+
export function quantityToToken(quantity) {
|
|
40
|
+
if (quantity?.value === undefined || !Number.isFinite(quantity.value))
|
|
41
|
+
return undefined;
|
|
42
|
+
return [String(quantity.value), quantity.system?.trim() ?? '', quantity.code?.trim() ?? ''].join('|');
|
|
43
|
+
}
|
|
44
|
+
export function tokenToQuantity(token) {
|
|
45
|
+
const value = token?.trim();
|
|
46
|
+
if (!value)
|
|
47
|
+
return undefined;
|
|
48
|
+
const [numberValue, system, code] = value.split('|');
|
|
49
|
+
const numeric = Number(numberValue);
|
|
50
|
+
if (!Number.isFinite(numeric))
|
|
51
|
+
throw new TypeError('fhir_quantity_token_invalid');
|
|
52
|
+
return Object.freeze({ value: numeric, ...(system ? { system } : {}), ...(code ? { code, unit: code } : {}) });
|
|
53
|
+
}
|