fhir-data-utils-ts 0.3.4 → 0.3.6
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 +10 -0
- package/CHANGELOG.md +19 -0
- package/README.md +14 -0
- package/dist/builders/diagnostic-report-graph.d.ts +31 -0
- package/dist/builders/diagnostic-report-graph.js +86 -0
- package/dist/builders/index.d.ts +1 -0
- package/dist/builders/index.js +1 -0
- package/dist/claim-helpers/claims-helpers-diagnostic-report.d.ts +12 -0
- package/dist/claim-helpers/claims-helpers-diagnostic-report.js +40 -0
- package/dist/claim-helpers/claims-helpers-encounter.d.ts +2 -0
- package/dist/claim-helpers/claims-helpers-encounter.js +6 -0
- package/dist/claim-helpers/claims-helpers-imaging-study.d.ts +8 -0
- package/dist/claim-helpers/claims-helpers-imaging-study.js +20 -0
- package/dist/claim-helpers/index.d.ts +1 -0
- package/dist/claim-helpers/index.js +1 -0
- package/dist/claims/diagnostic-report-claims.d.ts +3 -36
- package/dist/claims/diagnostic-report-claims.js +4 -12
- package/dist/claims/encounter-claims.d.ts +13 -0
- package/dist/claims/encounter-claims.js +15 -0
- package/dist/claims/imaging-study-claims.d.ts +25 -0
- package/dist/claims/imaging-study-claims.js +43 -0
- package/dist/claims/index.d.ts +1 -0
- package/dist/claims/index.js +1 -0
- package/dist/converters/convert-encounter.js +5 -1
- package/dist/converters/convert-imaging-study.d.ts +4 -0
- package/dist/converters/convert-imaging-study.js +82 -0
- package/dist/converters/index.d.ts +1 -0
- package/dist/converters/index.js +1 -0
- package/dist/flat-claim-resource-graph.js +7 -6
- package/docs/UHC_FHIR_UTILS_MIGRATION.md +10 -0
- package/package.json +1 -1
|
@@ -33,6 +33,16 @@ description: Complete or audit migrations, claim catalogs, converters, builders,
|
|
|
33
33
|
Bundle parsing separate from the claims-first Communication being delivered.
|
|
34
34
|
This package owns their reusable types and validation, never HTTP delivery,
|
|
35
35
|
subscriber persistence, authentication or product notification policy.
|
|
36
|
+
- Keep ImagingStudy searchable metadata, clinical references and protected
|
|
37
|
+
endpoint references in neutral flat claims. DICOM parsing, pixels and object
|
|
38
|
+
storage remain in a separately versioned imaging/storage package.
|
|
39
|
+
- Preserve `Encounter.appointment`; an Appointment lifecycle transition must
|
|
40
|
+
not sever the encounter from the scheduling event that originated it.
|
|
41
|
+
- Build laboratory panels with `buildDiagnosticReportFlatGraph(...)`.
|
|
42
|
+
`DiagnosticReport.code` is mandatory and names the report/panel; result
|
|
43
|
+
measurements are independent `Observation` entries and formatted PDF/image
|
|
44
|
+
evidence is an independent `DocumentReference`. Preliminary state does not
|
|
45
|
+
itself authorize ledger or research projection.
|
|
36
46
|
|
|
37
47
|
## Change with executable contracts
|
|
38
48
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.6 - 2026-09-26
|
|
4
|
+
|
|
5
|
+
- Add a product-neutral claims-first DiagnosticReport graph builder for coded
|
|
6
|
+
laboratory panels, independent Observation results and PDF/image
|
|
7
|
+
DocumentReference evidence.
|
|
8
|
+
- Correct the document builder to emit the canonical DocumentReference claim
|
|
9
|
+
catalog and complete list helpers for report results, performers, specimens
|
|
10
|
+
and graph references.
|
|
11
|
+
|
|
12
|
+
## 0.3.5 - 2026-09-26
|
|
13
|
+
|
|
14
|
+
- Preserve the originating `Encounter.appointment` reference and expose the
|
|
15
|
+
canonical FHIR R4 Encounter lifecycle vocabulary.
|
|
16
|
+
- Add product-neutral ImagingStudy flat claims, helpers and R4 projection for
|
|
17
|
+
searchable clinical metadata and protected endpoint references without
|
|
18
|
+
embedding DICOM payloads.
|
|
19
|
+
- Cover Encounter and ImagingStudy with rich round-trip, catalog, helper,
|
|
20
|
+
documentation and package-surface contracts.
|
|
21
|
+
|
|
3
22
|
## 0.3.4 - 2026-09-23
|
|
4
23
|
|
|
5
24
|
- Add native FHIR R5 `SubscriptionTopic` and HTTPS rest-hook `Subscription`
|
package/README.md
CHANGED
|
@@ -29,6 +29,13 @@ through `<ResourceType>.contained-reference-list`; children do not carry
|
|
|
29
29
|
as `<ResourceType>.language`, `.narrative-status` and `.xhtml`. Native FHIR is
|
|
30
30
|
materialized only by an explicit R4/R5/R6 import or export adapter.
|
|
31
31
|
|
|
32
|
+
Laboratory authoring uses `buildDiagnosticReportFlatGraph(...)`.
|
|
33
|
+
`DiagnosticReport.code` is the coded report or panel type, atomic results are
|
|
34
|
+
independent LOINC-coded `Observation` entries, and PDF/JPEG/PNG evidence is an
|
|
35
|
+
independent `DocumentReference`. Preliminary reports can be reviewed and
|
|
36
|
+
securely exchanged; ledger and research eligibility remain downstream policy
|
|
37
|
+
and normally require the confirmed final lifecycle state.
|
|
38
|
+
|
|
32
39
|
## Public contract
|
|
33
40
|
|
|
34
41
|
The root export and stable subpaths provide:
|
|
@@ -53,6 +60,13 @@ those catalogs. The old `/immunization-flat-claims` and
|
|
|
53
60
|
`/immunization-reminder-plan` paths are deprecated compatibility re-exports;
|
|
54
61
|
new code imports `/builders` or `/builders/immunization`.
|
|
55
62
|
|
|
63
|
+
Encounter projection preserves the originating `Appointment` reference and
|
|
64
|
+
uses the canonical R4 lifecycle (`in-progress` while care is underway,
|
|
65
|
+
`finished` when it ends). ImagingStudy projection stores searchable study
|
|
66
|
+
metadata and protected endpoint references only. DICOM instances and pixel
|
|
67
|
+
payloads remain outside this neutral package and are never embedded as flat
|
|
68
|
+
claims.
|
|
69
|
+
|
|
56
70
|
No consumer should copy claim paths or rebuild these structures with literals.
|
|
57
71
|
Use `fhir-data-utils-ts/claims`, `/converters`, `/fhir-bundle`,
|
|
58
72
|
`/fhir-search-parameters`, `/clinical-terminology`, `/group-r5` or
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type DiagnosticReportStatus } from '../constants/clinical-statuses.js';
|
|
2
|
+
import { type FlatClaimResourceEntry } from '../flat-claim-resource-graph.js';
|
|
3
|
+
export type DiagnosticReportCoding = Readonly<{
|
|
4
|
+
system: string;
|
|
5
|
+
code: string;
|
|
6
|
+
display?: string;
|
|
7
|
+
text?: string;
|
|
8
|
+
}>;
|
|
9
|
+
export type DiagnosticReportFlatInput = Readonly<{
|
|
10
|
+
entryId: string;
|
|
11
|
+
identifier: string;
|
|
12
|
+
status: DiagnosticReportStatus;
|
|
13
|
+
subjectReference: string;
|
|
14
|
+
issuedAt: string;
|
|
15
|
+
reportCode: DiagnosticReportCoding;
|
|
16
|
+
encounterReference?: string;
|
|
17
|
+
performerReferences?: readonly string[];
|
|
18
|
+
results?: readonly FlatClaimResourceEntry[];
|
|
19
|
+
attachments?: readonly FlatClaimResourceEntry[];
|
|
20
|
+
}>;
|
|
21
|
+
/**
|
|
22
|
+
* Builds a claims-first DiagnosticReport graph for human, animal, or One Health
|
|
23
|
+
* laboratory reports. `DiagnosticReport.code` identifies the report/panel;
|
|
24
|
+
* atomic measurements remain independent `Observation` entries and PDF/JPEG/
|
|
25
|
+
* PNG evidence remains independent `DocumentReference` entries.
|
|
26
|
+
*
|
|
27
|
+
* `preliminary` reports can be exchanged and reviewed, but downstream ledger
|
|
28
|
+
* and secondary-use policy decides whether only `final` is eligible for those
|
|
29
|
+
* projections. This builder does not confer authority or consent.
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildDiagnosticReportFlatGraph(input: DiagnosticReportFlatInput): readonly FlatClaimResourceEntry[];
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Copyright 2026 ConnectHealth.info (Connecting Solution & Applications Ltd.) under the Apache License, Version 2.0.
|
|
2
|
+
import { DiagnosticReportClaim } from '../claims/diagnostic-report-claims.js';
|
|
3
|
+
import { DiagnosticReportStatuses } from '../constants/clinical-statuses.js';
|
|
4
|
+
import { ResourceTypesFhirR4 } from '../constants/fhir-resource-types.js';
|
|
5
|
+
import { buildFlatClaimResourceEntry, linkFlatClaimResourceEntries, } from '../flat-claim-resource-graph.js';
|
|
6
|
+
const DIAGNOSTIC_REPORT_STATUSES = new Set(Object.values(DiagnosticReportStatuses));
|
|
7
|
+
/**
|
|
8
|
+
* Builds a claims-first DiagnosticReport graph for human, animal, or One Health
|
|
9
|
+
* laboratory reports. `DiagnosticReport.code` identifies the report/panel;
|
|
10
|
+
* atomic measurements remain independent `Observation` entries and PDF/JPEG/
|
|
11
|
+
* PNG evidence remains independent `DocumentReference` entries.
|
|
12
|
+
*
|
|
13
|
+
* `preliminary` reports can be exchanged and reviewed, but downstream ledger
|
|
14
|
+
* and secondary-use policy decides whether only `final` is eligible for those
|
|
15
|
+
* projections. This builder does not confer authority or consent.
|
|
16
|
+
*/
|
|
17
|
+
export function buildDiagnosticReportFlatGraph(input) {
|
|
18
|
+
const status = required(input.status, 'diagnostic_report_status_required');
|
|
19
|
+
if (!DIAGNOSTIC_REPORT_STATUSES.has(status))
|
|
20
|
+
throw new TypeError('diagnostic_report_status_invalid');
|
|
21
|
+
const claims = {
|
|
22
|
+
[DiagnosticReportClaim.Identifier]: required(input.identifier, 'diagnostic_report_identifier_required'),
|
|
23
|
+
[DiagnosticReportClaim.Status]: status,
|
|
24
|
+
[DiagnosticReportClaim.Subject]: required(input.subjectReference, 'diagnostic_report_subject_required'),
|
|
25
|
+
[DiagnosticReportClaim.Date]: isoDateTime(input.issuedAt, 'diagnostic_report_date_invalid'),
|
|
26
|
+
[DiagnosticReportClaim.Code]: codingToken(input.reportCode),
|
|
27
|
+
};
|
|
28
|
+
if (input.reportCode.display?.trim())
|
|
29
|
+
claims[DiagnosticReportClaim.CodeDisplay] = input.reportCode.display.trim();
|
|
30
|
+
if (input.reportCode.text?.trim())
|
|
31
|
+
claims[DiagnosticReportClaim.CodeText] = input.reportCode.text.trim();
|
|
32
|
+
if (input.encounterReference?.trim()) {
|
|
33
|
+
claims[DiagnosticReportClaim.Encounter] = required(input.encounterReference, 'diagnostic_report_encounter_invalid');
|
|
34
|
+
}
|
|
35
|
+
if (input.performerReferences?.length) {
|
|
36
|
+
claims[DiagnosticReportClaim.Performer] = referenceList(input.performerReferences, 'diagnostic_report_performer_invalid');
|
|
37
|
+
}
|
|
38
|
+
const results = [...(input.results ?? [])];
|
|
39
|
+
if (results.some(entry => entry.resourceType !== ResourceTypesFhirR4.Observation)) {
|
|
40
|
+
throw new TypeError('diagnostic_report_result_invalid');
|
|
41
|
+
}
|
|
42
|
+
const attachments = [...(input.attachments ?? [])];
|
|
43
|
+
if (attachments.some(entry => entry.resourceType !== ResourceTypesFhirR4.DocumentReference)) {
|
|
44
|
+
throw new TypeError('diagnostic_report_attachment_invalid');
|
|
45
|
+
}
|
|
46
|
+
if (results.length)
|
|
47
|
+
claims[DiagnosticReportClaim.Result] = results.map(entry => entry.reference).join(',');
|
|
48
|
+
const report = buildFlatClaimResourceEntry({
|
|
49
|
+
entryId: input.entryId,
|
|
50
|
+
resourceType: ResourceTypesFhirR4.DiagnosticReport,
|
|
51
|
+
claims,
|
|
52
|
+
});
|
|
53
|
+
const children = [...results, ...attachments];
|
|
54
|
+
const graph = [report, ...children];
|
|
55
|
+
return children.length
|
|
56
|
+
? linkFlatClaimResourceEntries(graph, {
|
|
57
|
+
parentEntryId: report.entryId,
|
|
58
|
+
childEntryIds: children.map(entry => entry.entryId),
|
|
59
|
+
})
|
|
60
|
+
: graph;
|
|
61
|
+
}
|
|
62
|
+
function codingToken(value) {
|
|
63
|
+
const system = required(value.system, 'diagnostic_report_code_system_required');
|
|
64
|
+
const code = required(value.code, 'diagnostic_report_code_required');
|
|
65
|
+
if (system.includes('|') || code.includes('|') || system.includes(',') || code.includes(',')) {
|
|
66
|
+
throw new TypeError('diagnostic_report_code_invalid');
|
|
67
|
+
}
|
|
68
|
+
return `${system}|${code}`;
|
|
69
|
+
}
|
|
70
|
+
function referenceList(values, error) {
|
|
71
|
+
const normalized = [...new Set(values.map(value => required(value, error)))];
|
|
72
|
+
if (normalized.some(value => value.includes(',')))
|
|
73
|
+
throw new TypeError(error);
|
|
74
|
+
return normalized.join(',');
|
|
75
|
+
}
|
|
76
|
+
function isoDateTime(value, error) {
|
|
77
|
+
const normalized = required(value, error);
|
|
78
|
+
if (!/^\d{4}-\d{2}-\d{2}T/.test(normalized) || Number.isNaN(Date.parse(normalized)))
|
|
79
|
+
throw new TypeError(error);
|
|
80
|
+
return normalized;
|
|
81
|
+
}
|
|
82
|
+
function required(value, error) {
|
|
83
|
+
if (typeof value !== 'string' || !value.trim())
|
|
84
|
+
throw new TypeError(error);
|
|
85
|
+
return value.trim();
|
|
86
|
+
}
|
package/dist/builders/index.d.ts
CHANGED
package/dist/builders/index.js
CHANGED
|
@@ -8,8 +8,20 @@ export declare function getDiagnosticReportIdentifier(claims: DiagnosticReportIn
|
|
|
8
8
|
export declare function setDiagnosticReportIdentifier(claims: DiagnosticReportInteroperableClaims, value: string): DiagnosticReportInteroperableClaims;
|
|
9
9
|
export declare function getDiagnosticReportResultList(claims: DiagnosticReportInteroperableClaims): string[];
|
|
10
10
|
export declare function setDiagnosticReportResultList(claims: DiagnosticReportInteroperableClaims, values: string | readonly string[]): DiagnosticReportInteroperableClaims;
|
|
11
|
+
export declare function addDiagnosticReportResultList(claims: DiagnosticReportInteroperableClaims, values: string | readonly string[]): DiagnosticReportInteroperableClaims;
|
|
12
|
+
export declare function removeDiagnosticReportResultList(claims: DiagnosticReportInteroperableClaims, values: string | readonly string[]): DiagnosticReportInteroperableClaims;
|
|
13
|
+
export declare function getDiagnosticReportPerformerList(claims: DiagnosticReportInteroperableClaims): string[];
|
|
14
|
+
export declare function setDiagnosticReportPerformerList(claims: DiagnosticReportInteroperableClaims, values: string | readonly string[]): DiagnosticReportInteroperableClaims;
|
|
15
|
+
export declare function addDiagnosticReportPerformerList(claims: DiagnosticReportInteroperableClaims, values: string | readonly string[]): DiagnosticReportInteroperableClaims;
|
|
16
|
+
export declare function removeDiagnosticReportPerformerList(claims: DiagnosticReportInteroperableClaims, values: string | readonly string[]): DiagnosticReportInteroperableClaims;
|
|
17
|
+
export declare function getDiagnosticReportSpecimenList(claims: DiagnosticReportInteroperableClaims): string[];
|
|
18
|
+
export declare function setDiagnosticReportSpecimenList(claims: DiagnosticReportInteroperableClaims, values: string | readonly string[]): DiagnosticReportInteroperableClaims;
|
|
19
|
+
export declare function addDiagnosticReportSpecimenList(claims: DiagnosticReportInteroperableClaims, values: string | readonly string[]): DiagnosticReportInteroperableClaims;
|
|
20
|
+
export declare function removeDiagnosticReportSpecimenList(claims: DiagnosticReportInteroperableClaims, values: string | readonly string[]): DiagnosticReportInteroperableClaims;
|
|
11
21
|
export declare function getDiagnosticReportContainedResourceReferenceList(claims: DiagnosticReportInteroperableClaims): string[];
|
|
12
22
|
export declare function setDiagnosticReportContainedResourceReferenceList(claims: DiagnosticReportInteroperableClaims, values: string | readonly string[]): DiagnosticReportInteroperableClaims;
|
|
23
|
+
export declare function addDiagnosticReportContainedResourceReferenceList(claims: DiagnosticReportInteroperableClaims, values: string | readonly string[]): DiagnosticReportInteroperableClaims;
|
|
24
|
+
export declare function removeDiagnosticReportContainedResourceReferenceList(claims: DiagnosticReportInteroperableClaims, values: string | readonly string[]): DiagnosticReportInteroperableClaims;
|
|
13
25
|
/** @deprecated Use `getDiagnosticReportContainedResourceReferenceList`. */
|
|
14
26
|
export declare function getDiagnosticReportContainedDocumentIdentifierList(claims: DiagnosticReportInteroperableClaims): string[];
|
|
15
27
|
/** @deprecated Use `setDiagnosticReportContainedResourceReferenceList`. */
|
|
@@ -24,6 +24,36 @@ export function getDiagnosticReportResultList(claims) {
|
|
|
24
24
|
export function setDiagnosticReportResultList(claims, values) {
|
|
25
25
|
return setDiagnosticReportClaimList(claims, DiagnosticReportClaim.Result, values);
|
|
26
26
|
}
|
|
27
|
+
export function addDiagnosticReportResultList(claims, values) {
|
|
28
|
+
return addDiagnosticReportClaimList(claims, DiagnosticReportClaim.Result, values);
|
|
29
|
+
}
|
|
30
|
+
export function removeDiagnosticReportResultList(claims, values) {
|
|
31
|
+
return removeDiagnosticReportClaimList(claims, DiagnosticReportClaim.Result, values);
|
|
32
|
+
}
|
|
33
|
+
export function getDiagnosticReportPerformerList(claims) {
|
|
34
|
+
return getDiagnosticReportClaimList(claims, DiagnosticReportClaim.Performer);
|
|
35
|
+
}
|
|
36
|
+
export function setDiagnosticReportPerformerList(claims, values) {
|
|
37
|
+
return setDiagnosticReportClaimList(claims, DiagnosticReportClaim.Performer, values);
|
|
38
|
+
}
|
|
39
|
+
export function addDiagnosticReportPerformerList(claims, values) {
|
|
40
|
+
return addDiagnosticReportClaimList(claims, DiagnosticReportClaim.Performer, values);
|
|
41
|
+
}
|
|
42
|
+
export function removeDiagnosticReportPerformerList(claims, values) {
|
|
43
|
+
return removeDiagnosticReportClaimList(claims, DiagnosticReportClaim.Performer, values);
|
|
44
|
+
}
|
|
45
|
+
export function getDiagnosticReportSpecimenList(claims) {
|
|
46
|
+
return getDiagnosticReportClaimList(claims, DiagnosticReportClaim.Specimen);
|
|
47
|
+
}
|
|
48
|
+
export function setDiagnosticReportSpecimenList(claims, values) {
|
|
49
|
+
return setDiagnosticReportClaimList(claims, DiagnosticReportClaim.Specimen, values);
|
|
50
|
+
}
|
|
51
|
+
export function addDiagnosticReportSpecimenList(claims, values) {
|
|
52
|
+
return addDiagnosticReportClaimList(claims, DiagnosticReportClaim.Specimen, values);
|
|
53
|
+
}
|
|
54
|
+
export function removeDiagnosticReportSpecimenList(claims, values) {
|
|
55
|
+
return removeDiagnosticReportClaimList(claims, DiagnosticReportClaim.Specimen, values);
|
|
56
|
+
}
|
|
27
57
|
export function getDiagnosticReportContainedResourceReferenceList(claims) {
|
|
28
58
|
const canonical = getDiagnosticReportClaimList(claims, DiagnosticReportClaim.ContainedReferenceList);
|
|
29
59
|
const legacy = getDiagnosticReportClaimList(claims, DiagnosticReportClaim.ContainedDocuments);
|
|
@@ -37,6 +67,16 @@ export function setDiagnosticReportContainedResourceReferenceList(claims, values
|
|
|
37
67
|
delete cleaned[DiagnosticReportClaim.ContainedDocuments];
|
|
38
68
|
return cleaned;
|
|
39
69
|
}
|
|
70
|
+
export function addDiagnosticReportContainedResourceReferenceList(claims, values) {
|
|
71
|
+
return setDiagnosticReportContainedResourceReferenceList(claims, [
|
|
72
|
+
...getDiagnosticReportContainedResourceReferenceList(claims),
|
|
73
|
+
...(Array.isArray(values) ? values : [values]),
|
|
74
|
+
]);
|
|
75
|
+
}
|
|
76
|
+
export function removeDiagnosticReportContainedResourceReferenceList(claims, values) {
|
|
77
|
+
const removals = new Set(Array.isArray(values) ? values : [values]);
|
|
78
|
+
return setDiagnosticReportContainedResourceReferenceList(claims, getDiagnosticReportContainedResourceReferenceList(claims).filter(value => !removals.has(value)));
|
|
79
|
+
}
|
|
40
80
|
/** @deprecated Use `getDiagnosticReportContainedResourceReferenceList`. */
|
|
41
81
|
export function getDiagnosticReportContainedDocumentIdentifierList(claims) {
|
|
42
82
|
return getDiagnosticReportContainedResourceReferenceList(claims);
|
|
@@ -8,3 +8,5 @@ export declare function getEncounterIdentifier(claims: EncounterInteroperableCla
|
|
|
8
8
|
export declare function setEncounterIdentifier(claims: EncounterInteroperableClaims, value: string): EncounterInteroperableClaims;
|
|
9
9
|
export declare function getEncounterParticipantList(claims: EncounterInteroperableClaims): string[];
|
|
10
10
|
export declare function setEncounterParticipantList(claims: EncounterInteroperableClaims, values: string | readonly string[]): EncounterInteroperableClaims;
|
|
11
|
+
export declare function getEncounterAppointmentList(claims: EncounterInteroperableClaims): string[];
|
|
12
|
+
export declare function setEncounterAppointmentList(claims: EncounterInteroperableClaims, values: string | readonly string[]): EncounterInteroperableClaims;
|
|
@@ -24,3 +24,9 @@ export function getEncounterParticipantList(claims) {
|
|
|
24
24
|
export function setEncounterParticipantList(claims, values) {
|
|
25
25
|
return setEncounterClaimList(claims, EncounterClaim.Participant, values);
|
|
26
26
|
}
|
|
27
|
+
export function getEncounterAppointmentList(claims) {
|
|
28
|
+
return getEncounterClaimList(claims, EncounterClaim.Appointment);
|
|
29
|
+
}
|
|
30
|
+
export function setEncounterAppointmentList(claims, values) {
|
|
31
|
+
return setEncounterClaimList(claims, EncounterClaim.Appointment, values);
|
|
32
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type GenericInteroperableClaims } from './claim-list-helpers.js';
|
|
2
|
+
export type ImagingStudyInteroperableClaims = GenericInteroperableClaims;
|
|
3
|
+
export declare function getImagingStudyClaimList(claims: ImagingStudyInteroperableClaims, claimKey: string): string[];
|
|
4
|
+
export declare function setImagingStudyClaimList(claims: ImagingStudyInteroperableClaims, claimKey: string, values: string | readonly string[]): ImagingStudyInteroperableClaims;
|
|
5
|
+
export declare function addImagingStudyClaimList(claims: ImagingStudyInteroperableClaims, claimKey: string, values: string | readonly string[]): ImagingStudyInteroperableClaims;
|
|
6
|
+
export declare function removeImagingStudyClaimList(claims: ImagingStudyInteroperableClaims, claimKey: string, values: string | readonly string[]): ImagingStudyInteroperableClaims;
|
|
7
|
+
export declare function getImagingStudyIdentifier(claims: ImagingStudyInteroperableClaims): string;
|
|
8
|
+
export declare function setImagingStudyIdentifier(claims: ImagingStudyInteroperableClaims, value: string): ImagingStudyInteroperableClaims;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ImagingStudyClaim } from '../claims/imaging-study-claims.js';
|
|
2
|
+
import { addClaimValues, getClaimValues, normalizeClaimScalar, removeClaimValues, setClaimValues, } from './claim-list-helpers.js';
|
|
3
|
+
export function getImagingStudyClaimList(claims, claimKey) {
|
|
4
|
+
return getClaimValues(claims, claimKey);
|
|
5
|
+
}
|
|
6
|
+
export function setImagingStudyClaimList(claims, claimKey, values) {
|
|
7
|
+
return setClaimValues(claims, claimKey, values);
|
|
8
|
+
}
|
|
9
|
+
export function addImagingStudyClaimList(claims, claimKey, values) {
|
|
10
|
+
return addClaimValues(claims, claimKey, values);
|
|
11
|
+
}
|
|
12
|
+
export function removeImagingStudyClaimList(claims, claimKey, values) {
|
|
13
|
+
return removeClaimValues(claims, claimKey, values);
|
|
14
|
+
}
|
|
15
|
+
export function getImagingStudyIdentifier(claims) {
|
|
16
|
+
return normalizeClaimScalar(claims[ImagingStudyClaim.Identifier]);
|
|
17
|
+
}
|
|
18
|
+
export function setImagingStudyIdentifier(claims, value) {
|
|
19
|
+
return { ...claims, [ImagingStudyClaim.Identifier]: normalizeClaimScalar(value) };
|
|
20
|
+
}
|
|
@@ -15,6 +15,7 @@ export * from './claims-helpers-document-reference.js';
|
|
|
15
15
|
export * from './claims-helpers-encounter.js';
|
|
16
16
|
export * from './claims-helpers-flag.js';
|
|
17
17
|
export * from './claims-helpers-immunization.js';
|
|
18
|
+
export * from './claims-helpers-imaging-study.js';
|
|
18
19
|
export * from './claims-helpers-location.js';
|
|
19
20
|
export * from './claims-helpers-medication-statement.js';
|
|
20
21
|
export * from './claims-helpers-observation.js';
|
|
@@ -15,6 +15,7 @@ export * from './claims-helpers-document-reference.js';
|
|
|
15
15
|
export * from './claims-helpers-encounter.js';
|
|
16
16
|
export * from './claims-helpers-flag.js';
|
|
17
17
|
export * from './claims-helpers-immunization.js';
|
|
18
|
+
export * from './claims-helpers-imaging-study.js';
|
|
18
19
|
export * from './claims-helpers-location.js';
|
|
19
20
|
export * from './claims-helpers-medication-statement.js';
|
|
20
21
|
export * from './claims-helpers-observation.js';
|
|
@@ -33,31 +33,6 @@ export declare const DiagnosticReportClaim: {
|
|
|
33
33
|
*/
|
|
34
34
|
readonly ContainedDocuments: "DiagnosticReport.contained-documents";
|
|
35
35
|
};
|
|
36
|
-
/**
|
|
37
|
-
* TODO(ips-next):
|
|
38
|
-
* Add resource-level claim helpers in `src/utils/claims-helpers-diagnostic-report.ts`
|
|
39
|
-
* following the same contract already used by Consent/Medication/Condition/Allergy:
|
|
40
|
-
* - `getDiagnosticReportClaimList(...)`
|
|
41
|
-
* - `setDiagnosticReportClaimList(...)`
|
|
42
|
-
* - `addDiagnosticReportClaimList(...)`
|
|
43
|
-
* - `removeDiagnosticReportClaimList(...)`
|
|
44
|
-
* - focused helpers for:
|
|
45
|
-
* - `get/set/add/removeDiagnosticReportResultList`
|
|
46
|
-
* - `get/set/add/removeDiagnosticReportPerformerList`
|
|
47
|
-
* - `get/set/add/removeDiagnosticReportSpecimenList`
|
|
48
|
-
* - `get/set/add/removeDiagnosticReportContainedResourceReferences`
|
|
49
|
-
* - `get/setDiagnosticReportPresentedFormContentType`
|
|
50
|
-
* - `get/setDiagnosticReportPresentedFormData`
|
|
51
|
-
* - `get/setDiagnosticReportPresentedFormUrl`
|
|
52
|
-
*
|
|
53
|
-
* Keep naming aligned with canonical FHIR search parameter names
|
|
54
|
-
* (lowercase with `-`) and reuse the existing `SearchParamNames` + contextualized
|
|
55
|
-
* claim-key mapping pattern.
|
|
56
|
-
*
|
|
57
|
-
* This file is intentionally type-first only for now so GW Core and
|
|
58
|
-
* `common-utils` can already exercise the rest of the bundle workflow without
|
|
59
|
-
* committing to the DiagnosticReport editing surface yet.
|
|
60
|
-
*/
|
|
61
36
|
export type DiagnosticReportClaimKey = typeof DiagnosticReportClaim[keyof typeof DiagnosticReportClaim];
|
|
62
37
|
export declare const DiagnosticReportClaimSpecs: ClaimSpec[];
|
|
63
38
|
export declare const DiagnosticReportSearchParamNames: {
|
|
@@ -95,15 +70,7 @@ export declare enum DiagnosticReportClaimsFhirApi {
|
|
|
95
70
|
}
|
|
96
71
|
export declare const DiagnosticReportSearchParamToClaimKey: Record<DiagnosticReportSearchParamName, DiagnosticReportClaimsFhirApi>;
|
|
97
72
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* Do not invent camelCase names. Prefer official FHIR search parameter names
|
|
103
|
-
* or explicit attachment aliases such as `presented-form-*` and
|
|
104
|
-
* `contained-reference-list`.
|
|
105
|
-
*
|
|
106
|
-
* Keep `DiagnosticReport` paused at the typing/mapping layer until:
|
|
107
|
-
* 1. `claims-helpers-diagnostic-report.ts` exists
|
|
108
|
-
* 2. GW Core readers/tests consume the same shared claim keys
|
|
73
|
+
* Operational attachment and graph claims remain separate from the official
|
|
74
|
+
* SearchParameter catalog above. They support claims-first authoring without
|
|
75
|
+
* pretending that those aliases are searchable FHIR parameters.
|
|
109
76
|
*/
|
|
@@ -54,7 +54,7 @@ export const DiagnosticReportClaimSpecs = [
|
|
|
54
54
|
{ key: DiagnosticReportClaim.PresentedFormContentType, meaning: 'FHIR presentedForm attachment content type.', example: 'application/pdf' },
|
|
55
55
|
{ key: DiagnosticReportClaim.PresentedFormData, meaning: 'FHIR presentedForm inline base64 payload.', example: 'JVBERi0xLjc...' },
|
|
56
56
|
{ key: DiagnosticReportClaim.PresentedFormUrl, meaning: 'FHIR presentedForm external URL.', example: 'https://example.org/report.pdf' },
|
|
57
|
-
{ key: DiagnosticReportClaim.
|
|
57
|
+
{ key: DiagnosticReportClaim.ContainedReferenceList, meaning: 'Canonical CSV of independent result and evidence resource references linked by this report.', example: 'Observation/result-1,DocumentReference/report-1' },
|
|
58
58
|
];
|
|
59
59
|
export const DiagnosticReportSearchParamNames = {
|
|
60
60
|
BasedOn: 'based-on',
|
|
@@ -106,15 +106,7 @@ export const DiagnosticReportSearchParamToClaimKey = {
|
|
|
106
106
|
[DiagnosticReportSearchParamNames.Subject]: DiagnosticReportClaimsFhirApi.Subject,
|
|
107
107
|
};
|
|
108
108
|
/**
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* Do not invent camelCase names. Prefer official FHIR search parameter names
|
|
114
|
-
* or explicit attachment aliases such as `presented-form-*` and
|
|
115
|
-
* `contained-reference-list`.
|
|
116
|
-
*
|
|
117
|
-
* Keep `DiagnosticReport` paused at the typing/mapping layer until:
|
|
118
|
-
* 1. `claims-helpers-diagnostic-report.ts` exists
|
|
119
|
-
* 2. GW Core readers/tests consume the same shared claim keys
|
|
109
|
+
* Operational attachment and graph claims remain separate from the official
|
|
110
|
+
* SearchParameter catalog above. They support claims-first authoring without
|
|
111
|
+
* pretending that those aliases are searchable FHIR parameters.
|
|
120
112
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ClaimSpec } from './claim-types.js';
|
|
2
2
|
export declare const EncounterClaim: {
|
|
3
3
|
readonly Identifier: "Encounter.identifier";
|
|
4
|
+
readonly Appointment: "Encounter.appointment";
|
|
4
5
|
readonly Status: "Encounter.status";
|
|
5
6
|
readonly Class: "Encounter.class";
|
|
6
7
|
readonly Type: "Encounter.type";
|
|
@@ -14,5 +15,17 @@ export declare const EncounterClaim: {
|
|
|
14
15
|
readonly Diagnosis: "Encounter.diagnosis";
|
|
15
16
|
readonly Location: "Encounter.location";
|
|
16
17
|
};
|
|
18
|
+
/** Canonical FHIR R4 Encounter lifecycle codes. */
|
|
19
|
+
export declare enum EncounterStatus {
|
|
20
|
+
Planned = "planned",
|
|
21
|
+
Arrived = "arrived",
|
|
22
|
+
Triaged = "triaged",
|
|
23
|
+
InProgress = "in-progress",
|
|
24
|
+
OnLeave = "onleave",
|
|
25
|
+
Finished = "finished",
|
|
26
|
+
Cancelled = "cancelled",
|
|
27
|
+
EnteredInError = "entered-in-error",
|
|
28
|
+
Unknown = "unknown"
|
|
29
|
+
}
|
|
17
30
|
export type EncounterClaimKey = typeof EncounterClaim[keyof typeof EncounterClaim];
|
|
18
31
|
export declare const EncounterClaimSpecs: ClaimSpec[];
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// File: src/models/interoperable-claims/encounter-claims.ts
|
|
3
3
|
export const EncounterClaim = {
|
|
4
4
|
Identifier: 'Encounter.identifier',
|
|
5
|
+
Appointment: 'Encounter.appointment',
|
|
5
6
|
Status: 'Encounter.status',
|
|
6
7
|
Class: 'Encounter.class',
|
|
7
8
|
Type: 'Encounter.type',
|
|
@@ -15,8 +16,22 @@ export const EncounterClaim = {
|
|
|
15
16
|
Diagnosis: 'Encounter.diagnosis',
|
|
16
17
|
Location: 'Encounter.location',
|
|
17
18
|
};
|
|
19
|
+
/** Canonical FHIR R4 Encounter lifecycle codes. */
|
|
20
|
+
export var EncounterStatus;
|
|
21
|
+
(function (EncounterStatus) {
|
|
22
|
+
EncounterStatus["Planned"] = "planned";
|
|
23
|
+
EncounterStatus["Arrived"] = "arrived";
|
|
24
|
+
EncounterStatus["Triaged"] = "triaged";
|
|
25
|
+
EncounterStatus["InProgress"] = "in-progress";
|
|
26
|
+
EncounterStatus["OnLeave"] = "onleave";
|
|
27
|
+
EncounterStatus["Finished"] = "finished";
|
|
28
|
+
EncounterStatus["Cancelled"] = "cancelled";
|
|
29
|
+
EncounterStatus["EnteredInError"] = "entered-in-error";
|
|
30
|
+
EncounterStatus["Unknown"] = "unknown";
|
|
31
|
+
})(EncounterStatus || (EncounterStatus = {}));
|
|
18
32
|
export const EncounterClaimSpecs = [
|
|
19
33
|
{ key: EncounterClaim.Identifier, meaning: 'Business identifier.', example: 'enc-001' },
|
|
34
|
+
{ key: EncounterClaim.Appointment, meaning: 'Originating appointment references (CSV).', example: 'Appointment/appointment-1' },
|
|
20
35
|
{ key: EncounterClaim.Status, meaning: 'Encounter status.', example: 'finished' },
|
|
21
36
|
{ key: EncounterClaim.Class, meaning: 'Encounter class token.', example: 'http://terminology.hl7.org/CodeSystem/v3-ActCode|AMB' },
|
|
22
37
|
{ key: EncounterClaim.Type, meaning: 'Encounter type token.', example: 'http://snomed.info/sct|185349003' },
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ClaimSpec } from './claim-types.js';
|
|
2
|
+
/** Product-neutral flat claims for searchable FHIR R4 ImagingStudy metadata. */
|
|
3
|
+
export declare const ImagingStudyClaim: {
|
|
4
|
+
readonly Identifier: "ImagingStudy.identifier";
|
|
5
|
+
readonly Status: "ImagingStudy.status";
|
|
6
|
+
readonly Modality: "ImagingStudy.modality";
|
|
7
|
+
readonly Subject: "ImagingStudy.subject";
|
|
8
|
+
readonly Patient: "ImagingStudy.patient";
|
|
9
|
+
readonly Encounter: "ImagingStudy.encounter";
|
|
10
|
+
readonly Started: "ImagingStudy.started";
|
|
11
|
+
readonly BasedOn: "ImagingStudy.based-on";
|
|
12
|
+
readonly Referrer: "ImagingStudy.referrer";
|
|
13
|
+
readonly Interpreter: "ImagingStudy.interpreter";
|
|
14
|
+
readonly Endpoint: "ImagingStudy.endpoint";
|
|
15
|
+
readonly ProcedureReference: "ImagingStudy.procedure-reference";
|
|
16
|
+
readonly ProcedureCode: "ImagingStudy.procedure-code";
|
|
17
|
+
readonly ReasonCode: "ImagingStudy.reason-code";
|
|
18
|
+
readonly ReasonReference: "ImagingStudy.reason-reference";
|
|
19
|
+
readonly Location: "ImagingStudy.location";
|
|
20
|
+
readonly NumberOfSeries: "ImagingStudy.number-of-series";
|
|
21
|
+
readonly NumberOfInstances: "ImagingStudy.number-of-instances";
|
|
22
|
+
readonly Description: "ImagingStudy.description";
|
|
23
|
+
};
|
|
24
|
+
export type ImagingStudyClaimKey = typeof ImagingStudyClaim[keyof typeof ImagingStudyClaim];
|
|
25
|
+
export declare const ImagingStudyClaimSpecs: readonly ClaimSpec[];
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Product-neutral flat claims for searchable FHIR R4 ImagingStudy metadata. */
|
|
2
|
+
export const ImagingStudyClaim = {
|
|
3
|
+
Identifier: 'ImagingStudy.identifier',
|
|
4
|
+
Status: 'ImagingStudy.status',
|
|
5
|
+
Modality: 'ImagingStudy.modality',
|
|
6
|
+
Subject: 'ImagingStudy.subject',
|
|
7
|
+
Patient: 'ImagingStudy.patient',
|
|
8
|
+
Encounter: 'ImagingStudy.encounter',
|
|
9
|
+
Started: 'ImagingStudy.started',
|
|
10
|
+
BasedOn: 'ImagingStudy.based-on',
|
|
11
|
+
Referrer: 'ImagingStudy.referrer',
|
|
12
|
+
Interpreter: 'ImagingStudy.interpreter',
|
|
13
|
+
Endpoint: 'ImagingStudy.endpoint',
|
|
14
|
+
ProcedureReference: 'ImagingStudy.procedure-reference',
|
|
15
|
+
ProcedureCode: 'ImagingStudy.procedure-code',
|
|
16
|
+
ReasonCode: 'ImagingStudy.reason-code',
|
|
17
|
+
ReasonReference: 'ImagingStudy.reason-reference',
|
|
18
|
+
Location: 'ImagingStudy.location',
|
|
19
|
+
NumberOfSeries: 'ImagingStudy.number-of-series',
|
|
20
|
+
NumberOfInstances: 'ImagingStudy.number-of-instances',
|
|
21
|
+
Description: 'ImagingStudy.description',
|
|
22
|
+
};
|
|
23
|
+
export const ImagingStudyClaimSpecs = Object.freeze([
|
|
24
|
+
{ key: ImagingStudyClaim.Identifier, meaning: 'Business or DICOM study identifier.', example: 'imaging-study-1' },
|
|
25
|
+
{ key: ImagingStudyClaim.Status, meaning: 'ImagingStudy lifecycle status.', example: 'available' },
|
|
26
|
+
{ key: ImagingStudyClaim.Modality, meaning: 'Repeatable DICOM modality token.', example: 'http://dicom.nema.org/resources/ontology/DCM|DX' },
|
|
27
|
+
{ key: ImagingStudyClaim.Subject, meaning: 'Canonical subject reference.', example: 'did:web:subject.example' },
|
|
28
|
+
{ key: ImagingStudyClaim.Patient, meaning: 'FHIR R4 patient search alias.', example: 'did:web:subject.example' },
|
|
29
|
+
{ key: ImagingStudyClaim.Encounter, meaning: 'Encounter context reference.', example: 'Encounter/encounter-1' },
|
|
30
|
+
{ key: ImagingStudyClaim.Started, meaning: 'Study start instant.', example: '2026-09-26T10:15:00Z' },
|
|
31
|
+
{ key: ImagingStudyClaim.BasedOn, meaning: 'Originating request references (CSV).', example: 'ServiceRequest/radiography-1' },
|
|
32
|
+
{ key: ImagingStudyClaim.Referrer, meaning: 'Referring practitioner reference.', example: 'PractitionerRole/veterinarian-1' },
|
|
33
|
+
{ key: ImagingStudyClaim.Interpreter, meaning: 'Interpreter references (CSV).', example: 'PractitionerRole/radiologist-1' },
|
|
34
|
+
{ key: ImagingStudyClaim.Endpoint, meaning: 'Protected imaging endpoint references (CSV).', example: 'Endpoint/dicomweb-1' },
|
|
35
|
+
{ key: ImagingStudyClaim.ProcedureReference, meaning: 'Performed procedure reference.', example: 'Procedure/radiography-1' },
|
|
36
|
+
{ key: ImagingStudyClaim.ProcedureCode, meaning: 'Performed procedure coding token.', example: 'http://snomed.info/sct|363680008' },
|
|
37
|
+
{ key: ImagingStudyClaim.ReasonCode, meaning: 'Study reason coding token.', example: 'http://snomed.info/sct|125605004' },
|
|
38
|
+
{ key: ImagingStudyClaim.ReasonReference, meaning: 'Study reason references (CSV).', example: 'Condition/fracture-1' },
|
|
39
|
+
{ key: ImagingStudyClaim.Location, meaning: 'Acquisition location reference.', example: 'Location/imaging-1' },
|
|
40
|
+
{ key: ImagingStudyClaim.NumberOfSeries, meaning: 'Declared series count.', example: '1' },
|
|
41
|
+
{ key: ImagingStudyClaim.NumberOfInstances, meaning: 'Declared instance count.', example: '2' },
|
|
42
|
+
{ key: ImagingStudyClaim.Description, meaning: 'Study description.', example: 'Left forelimb radiographs' },
|
|
43
|
+
]);
|
package/dist/claims/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export * from './encounter-claims.js';
|
|
|
17
17
|
export * from './flag-claims.js';
|
|
18
18
|
export * from './immunization-claims.js';
|
|
19
19
|
export * from './immunization-recommendation-claims.js';
|
|
20
|
+
export * from './imaging-study-claims.js';
|
|
20
21
|
export * from './invoice-claims.js';
|
|
21
22
|
export * from './location-claims.js';
|
|
22
23
|
export * from './medication-statement-claims.js';
|
package/dist/claims/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export * from './encounter-claims.js';
|
|
|
17
17
|
export * from './flag-claims.js';
|
|
18
18
|
export * from './immunization-claims.js';
|
|
19
19
|
export * from './immunization-recommendation-claims.js';
|
|
20
|
+
export * from './imaging-study-claims.js';
|
|
20
21
|
export * from './invoice-claims.js';
|
|
21
22
|
export * from './location-claims.js';
|
|
22
23
|
export * from './medication-statement-claims.js';
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
// Copyright 2026 Conéctate Soluciones y Aplicaciones SL under the Apache License, Version 2.0.
|
|
2
2
|
// File: src/utils/convert-encounter.ts
|
|
3
3
|
import { EncounterClaim } from '../claims/encounter-claims.js';
|
|
4
|
-
import { codingFromValue, codingToValue, referenceToValue } from './convert-shared.js';
|
|
4
|
+
import { codingFromValue, codingToValue, referenceListToCsv, referenceToValue } from './convert-shared.js';
|
|
5
5
|
export function encounterFlatToFhirR4(claims) {
|
|
6
6
|
const subject = claims[EncounterClaim.Subject] ?? claims[EncounterClaim.Patient];
|
|
7
7
|
return {
|
|
8
8
|
resourceType: 'Encounter',
|
|
9
9
|
identifier: claims[EncounterClaim.Identifier] ? [{ value: claims[EncounterClaim.Identifier] }] : undefined,
|
|
10
|
+
appointment: claims[EncounterClaim.Appointment]
|
|
11
|
+
? claims[EncounterClaim.Appointment].split(',').map((reference) => ({ reference: reference.trim() }))
|
|
12
|
+
: undefined,
|
|
10
13
|
status: claims[EncounterClaim.Status],
|
|
11
14
|
class: claims[EncounterClaim.Class]
|
|
12
15
|
? (() => {
|
|
@@ -37,6 +40,7 @@ export function encounterFhirR4ToFlat(resource) {
|
|
|
37
40
|
const klass = resource.class;
|
|
38
41
|
return {
|
|
39
42
|
[EncounterClaim.Identifier]: resource.identifier?.[0]?.value,
|
|
43
|
+
[EncounterClaim.Appointment]: referenceListToCsv(resource.appointment),
|
|
40
44
|
[EncounterClaim.Status]: resource.status,
|
|
41
45
|
[EncounterClaim.Class]: klass?.code ? (klass.system ? `${klass.system}|${klass.code}` : klass.code) : undefined,
|
|
42
46
|
[EncounterClaim.Type]: codingToValue(resource.type?.[0]?.coding?.[0]),
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FhirResource, FlatClaims } from './convert-shared.js';
|
|
2
|
+
/** Projects searchable ImagingStudy metadata; DICOM bytes remain in protected imaging storage. */
|
|
3
|
+
export declare function imagingStudyFlatToFhirR4(claims: FlatClaims): FhirResource;
|
|
4
|
+
export declare function imagingStudyFhirR4ToFlat(resource: FhirResource): FlatClaims;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ImagingStudyClaim } from '../claims/imaging-study-claims.js';
|
|
2
|
+
import { codingFromValue, codingToValue, referenceListToCsv, referenceToValue } from './convert-shared.js';
|
|
3
|
+
function csv(values) {
|
|
4
|
+
if (!Array.isArray(values))
|
|
5
|
+
return undefined;
|
|
6
|
+
const normalized = values.map((value) => String(value || '').trim()).filter(Boolean);
|
|
7
|
+
return normalized.length ? normalized.join(',') : undefined;
|
|
8
|
+
}
|
|
9
|
+
function codingCsv(values) {
|
|
10
|
+
if (!Array.isArray(values))
|
|
11
|
+
return undefined;
|
|
12
|
+
return csv(values.map((value) => codingToValue(value)));
|
|
13
|
+
}
|
|
14
|
+
function codingList(value) {
|
|
15
|
+
if (typeof value !== 'string' || !value.trim())
|
|
16
|
+
return undefined;
|
|
17
|
+
const codings = value.split(',').flatMap((token) => codingFromValue(token.trim()) || []);
|
|
18
|
+
return codings.length ? codings : undefined;
|
|
19
|
+
}
|
|
20
|
+
/** Projects searchable ImagingStudy metadata; DICOM bytes remain in protected imaging storage. */
|
|
21
|
+
export function imagingStudyFlatToFhirR4(claims) {
|
|
22
|
+
const subject = claims[ImagingStudyClaim.Subject] ?? claims[ImagingStudyClaim.Patient];
|
|
23
|
+
return {
|
|
24
|
+
resourceType: 'ImagingStudy',
|
|
25
|
+
identifier: claims[ImagingStudyClaim.Identifier] ? [{ value: claims[ImagingStudyClaim.Identifier] }] : undefined,
|
|
26
|
+
status: claims[ImagingStudyClaim.Status],
|
|
27
|
+
modality: codingList(claims[ImagingStudyClaim.Modality]),
|
|
28
|
+
subject: subject ? { reference: subject } : undefined,
|
|
29
|
+
encounter: claims[ImagingStudyClaim.Encounter] ? { reference: claims[ImagingStudyClaim.Encounter] } : undefined,
|
|
30
|
+
started: claims[ImagingStudyClaim.Started],
|
|
31
|
+
basedOn: references(claims[ImagingStudyClaim.BasedOn]),
|
|
32
|
+
referrer: claims[ImagingStudyClaim.Referrer] ? { reference: claims[ImagingStudyClaim.Referrer] } : undefined,
|
|
33
|
+
interpreter: references(claims[ImagingStudyClaim.Interpreter]),
|
|
34
|
+
endpoint: references(claims[ImagingStudyClaim.Endpoint]),
|
|
35
|
+
procedureReference: claims[ImagingStudyClaim.ProcedureReference] ? { reference: claims[ImagingStudyClaim.ProcedureReference] } : undefined,
|
|
36
|
+
procedureCode: claims[ImagingStudyClaim.ProcedureCode] ? [{ coding: codingFromValue(claims[ImagingStudyClaim.ProcedureCode]) }] : undefined,
|
|
37
|
+
reasonCode: claims[ImagingStudyClaim.ReasonCode] ? [{ coding: codingFromValue(claims[ImagingStudyClaim.ReasonCode]) }] : undefined,
|
|
38
|
+
reasonReference: references(claims[ImagingStudyClaim.ReasonReference]),
|
|
39
|
+
location: claims[ImagingStudyClaim.Location] ? { reference: claims[ImagingStudyClaim.Location] } : undefined,
|
|
40
|
+
numberOfSeries: numberValue(claims[ImagingStudyClaim.NumberOfSeries]),
|
|
41
|
+
numberOfInstances: numberValue(claims[ImagingStudyClaim.NumberOfInstances]),
|
|
42
|
+
description: claims[ImagingStudyClaim.Description],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function imagingStudyFhirR4ToFlat(resource) {
|
|
46
|
+
return {
|
|
47
|
+
[ImagingStudyClaim.Identifier]: resource.identifier?.[0]?.value,
|
|
48
|
+
[ImagingStudyClaim.Status]: resource.status,
|
|
49
|
+
[ImagingStudyClaim.Modality]: codingCsv(resource.modality),
|
|
50
|
+
[ImagingStudyClaim.Subject]: referenceToValue(resource.subject),
|
|
51
|
+
[ImagingStudyClaim.Patient]: referenceToValue(resource.subject),
|
|
52
|
+
[ImagingStudyClaim.Encounter]: referenceToValue(resource.encounter),
|
|
53
|
+
[ImagingStudyClaim.Started]: resource.started,
|
|
54
|
+
[ImagingStudyClaim.BasedOn]: referenceListToCsv(resource.basedOn),
|
|
55
|
+
[ImagingStudyClaim.Referrer]: referenceToValue(resource.referrer),
|
|
56
|
+
[ImagingStudyClaim.Interpreter]: referenceListToCsv(resource.interpreter),
|
|
57
|
+
[ImagingStudyClaim.Endpoint]: referenceListToCsv(resource.endpoint),
|
|
58
|
+
[ImagingStudyClaim.ProcedureReference]: referenceToValue(resource.procedureReference),
|
|
59
|
+
[ImagingStudyClaim.ProcedureCode]: codingToValue(resource.procedureCode?.[0]?.coding?.[0]),
|
|
60
|
+
[ImagingStudyClaim.ReasonCode]: codingToValue(resource.reasonCode?.[0]?.coding?.[0]),
|
|
61
|
+
[ImagingStudyClaim.ReasonReference]: referenceListToCsv(resource.reasonReference),
|
|
62
|
+
[ImagingStudyClaim.Location]: referenceToValue(resource.location),
|
|
63
|
+
[ImagingStudyClaim.NumberOfSeries]: scalarNumber(resource.numberOfSeries),
|
|
64
|
+
[ImagingStudyClaim.NumberOfInstances]: scalarNumber(resource.numberOfInstances),
|
|
65
|
+
[ImagingStudyClaim.Description]: resource.description,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function references(value) {
|
|
69
|
+
if (typeof value !== 'string' || !value.trim())
|
|
70
|
+
return undefined;
|
|
71
|
+
const output = value.split(',').map((reference) => reference.trim()).filter(Boolean).map((reference) => ({ reference }));
|
|
72
|
+
return output.length ? output : undefined;
|
|
73
|
+
}
|
|
74
|
+
function scalarNumber(value) {
|
|
75
|
+
return typeof value === 'number' && Number.isFinite(value) ? String(value) : undefined;
|
|
76
|
+
}
|
|
77
|
+
function numberValue(value) {
|
|
78
|
+
if (typeof value !== 'string' || !value.trim())
|
|
79
|
+
return undefined;
|
|
80
|
+
const number = Number(value);
|
|
81
|
+
return Number.isFinite(number) ? number : undefined;
|
|
82
|
+
}
|
|
@@ -15,6 +15,7 @@ export * from './convert-document-reference.js';
|
|
|
15
15
|
export * from './convert-encounter.js';
|
|
16
16
|
export * from './convert-flag.js';
|
|
17
17
|
export * from './convert-immunization.js';
|
|
18
|
+
export * from './convert-imaging-study.js';
|
|
18
19
|
export * from './convert-location.js';
|
|
19
20
|
export * from './convert-medication-statement.js';
|
|
20
21
|
export * from './convert-observation.js';
|
package/dist/converters/index.js
CHANGED
|
@@ -15,6 +15,7 @@ export * from './convert-document-reference.js';
|
|
|
15
15
|
export * from './convert-encounter.js';
|
|
16
16
|
export * from './convert-flag.js';
|
|
17
17
|
export * from './convert-immunization.js';
|
|
18
|
+
export * from './convert-imaging-study.js';
|
|
18
19
|
export * from './convert-location.js';
|
|
19
20
|
export * from './convert-medication-statement.js';
|
|
20
21
|
export * from './convert-observation.js';
|
|
@@ -70,16 +70,16 @@ export function normalizeLegacyContainedClaims(entry) {
|
|
|
70
70
|
*/
|
|
71
71
|
export function buildDocumentReferenceFlatEntry(input) {
|
|
72
72
|
const claims = {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
[DocumentReferenceClaim.Identifier]: requiredToken(input.identifier, 'document_reference_identifier_required'),
|
|
74
|
+
[DocumentReferenceClaim.ContentType]: input.contentType,
|
|
75
|
+
[DocumentReferenceClaim.ContentHash]: requiredToken(input.contentHash, 'document_reference_contenthash_required'),
|
|
76
76
|
};
|
|
77
77
|
if (input.title?.trim())
|
|
78
|
-
claims[
|
|
78
|
+
claims[DocumentReferenceClaim.Description] = input.title.trim();
|
|
79
79
|
if (input.url?.trim())
|
|
80
|
-
claims[
|
|
80
|
+
claims[DocumentReferenceClaim.Location] = input.url.trim();
|
|
81
81
|
if (input.dataBase64?.trim())
|
|
82
|
-
claims[
|
|
82
|
+
claims[DocumentReferenceClaim.ContentData] = input.dataBase64.trim();
|
|
83
83
|
return buildFlatClaimResourceEntry({ entryId: input.entryId, resourceType: 'DocumentReference', claims });
|
|
84
84
|
}
|
|
85
85
|
function splitCsv(value) {
|
|
@@ -106,3 +106,4 @@ function requiredResourceType(value) {
|
|
|
106
106
|
throw new TypeError('flat_claim_resource_type_invalid');
|
|
107
107
|
return normalized;
|
|
108
108
|
}
|
|
109
|
+
import { DocumentReferenceClaim } from './claims/document-reference-claims.js';
|
|
@@ -28,6 +28,7 @@ and transport assumptions must not be copied.
|
|
|
28
28
|
| Anonymization field inventory | Test input only | A separately reviewed policy contract; old field lists do not become policy automatically |
|
|
29
29
|
| ATC, LOINC, SNOMED and EMA JSON | Validate before reuse | Terminology service fixtures, never authoritative embedded catalogs |
|
|
30
30
|
| DICOM parsing | Extract later | Separate imaging package; do not add `dicom-parser` to the neutral core |
|
|
31
|
+
| ImagingStudy metadata | Rewrite | Neutral flat claims and R4 projection for subject, encounter, modality, procedure and protected endpoint references; DICOM bytes remain outside |
|
|
31
32
|
| SHA-1 attachment hashing | Retire | No compatibility writer; historical read/verification belongs in a versioned adapter |
|
|
32
33
|
| `MessageHeader` transport conventions | Retire | Transport is outside the FHIR data contract |
|
|
33
34
|
| UHC-branded templates and defaults | Retire or move downstream | Product policy belongs in UHC packages, not here |
|
|
@@ -51,6 +52,8 @@ The migration surface is executable rather than documentary only:
|
|
|
51
52
|
- every migrated claim family has a named catalog and shared list helpers;
|
|
52
53
|
- every migrated product-neutral converter participates in the converter
|
|
53
54
|
round-trip matrix, with rich optional-field fixtures for complex resources;
|
|
55
|
+
- Encounter retains its originating Appointment and ImagingStudy retains
|
|
56
|
+
searchable metadata without embedding DICOM object bytes;
|
|
54
57
|
- FHIR primitives, typed searches, Bundle traversal and fallback conversion
|
|
55
58
|
have strict success and rejection tests;
|
|
56
59
|
- Immunization graphs and reminders live once under `src/builders/`; the former
|
|
@@ -80,6 +83,13 @@ one native `Observation.component[]`; it does not export three independent
|
|
|
80
83
|
native Observations. Component entries do not use `is-contained`,
|
|
81
84
|
`contained-parent-reference` or `contained-reference-list`.
|
|
82
85
|
|
|
86
|
+
Diagnostic laboratory authoring is owned here by
|
|
87
|
+
`buildDiagnosticReportFlatGraph(...)`: `DiagnosticReport.code` identifies the
|
|
88
|
+
report/panel, independent Observation entries carry atomic results, and
|
|
89
|
+
independent DocumentReference entries carry PDF/image evidence. Sector roles,
|
|
90
|
+
confirmation, ledger eligibility, consent and research policy remain outside
|
|
91
|
+
this package.
|
|
92
|
+
|
|
83
93
|
Aligned JSON arrays and the older comma-separated `Observation.component-*`
|
|
84
94
|
claims are accepted only by the migration reader. No current writer emits
|
|
85
95
|
either representation.
|