fhir-data-utils-ts 0.3.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/.codex/skills/govern-fhir-data-utilities/SKILL.md +12 -0
  2. package/.codex/skills/govern-fhir-observation-components/SKILL.md +11 -0
  3. package/CHANGELOG.md +20 -0
  4. package/README.md +18 -0
  5. package/dist/care-emergency-alert.d.ts +70 -0
  6. package/dist/care-emergency-alert.js +135 -0
  7. package/dist/claims/communication-request-claims.d.ts +21 -0
  8. package/dist/claims/communication-request-claims.js +27 -0
  9. package/dist/claims/composition-claims.d.ts +1 -0
  10. package/dist/claims/composition-claims.js +1 -0
  11. package/dist/claims/encounter-claims.d.ts +1 -0
  12. package/dist/claims/encounter-claims.js +2 -0
  13. package/dist/claims/index.d.ts +2 -0
  14. package/dist/claims/index.js +2 -0
  15. package/dist/claims/service-request-claims.d.ts +24 -0
  16. package/dist/claims/service-request-claims.js +33 -0
  17. package/dist/constants/fhir-resource-types.d.ts +3 -0
  18. package/dist/constants/fhir-resource-types.js +1 -0
  19. package/dist/converters/convert-communication-request.d.ts +5 -0
  20. package/dist/converters/convert-communication-request.js +40 -0
  21. package/dist/converters/convert-composition.js +12 -2
  22. package/dist/converters/convert-encounter.js +2 -0
  23. package/dist/converters/convert-service-request.d.ts +5 -0
  24. package/dist/converters/convert-service-request.js +47 -0
  25. package/dist/converters/index.d.ts +2 -0
  26. package/dist/converters/index.js +2 -0
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.js +1 -0
  29. package/dist/observation-claims.d.ts +3 -0
  30. package/dist/observation-claims.js +3 -0
  31. package/dist/vital-sign-observations.d.ts +75 -1
  32. package/dist/vital-sign-observations.js +225 -9
  33. package/docs/CARE_EMERGENCY_ALERT_CONTRACTS.md +50 -0
  34. package/docs/VITAL_SIGN_DAILY_DOCUMENT.md +79 -0
  35. package/package.json +5 -1
@@ -38,6 +38,18 @@ description: Complete or audit migrations, claim catalogs, converters, builders,
38
38
  storage remain in a separately versioned imaging/storage package.
39
39
  - Preserve `Encounter.appointment`; an Appointment lifecycle transition must
40
40
  not sever the encounter from the scheduling event that originated it.
41
+ - Represent actual care movement as independent child Encounters linked with
42
+ `Encounter.partOf` and their own Location. The graph is descriptive and does
43
+ not grant family or professional access.
44
+ - Represent resilient emergency intake as a canonical `ServiceRequest` and
45
+ acknowledgement/triage as a separate durable `Task` focused on it. Never
46
+ infer authorization from requester, performer or owner references.
47
+ - Represent a population campaign with one `CommunicationRequest` against a
48
+ frozen enumerated Group snapshot. Consent, exclusions, channel status and
49
+ eligibility are rechecked per recipient outside this neutral package.
50
+ - Emit coded values as compact `system|code`. Do not create split
51
+ `code-system`, `code-value` or stored `code-display` claims; preserve local
52
+ text separately and resolve trusted displays through terminology.
41
53
  - Build laboratory panels with `buildDiagnosticReportFlatGraph(...)`.
42
54
  `DiagnosticReport.code` is mandatory and names the report/panel; result
43
55
  measurements are independent `Observation` entries and formatted PDF/image
@@ -53,6 +53,17 @@ description: Implement or audit neutral FHIR Observation claim catalogs, vital-s
53
53
  copy `Observation.*`, LOINC, UCUM or category strings into portals,
54
54
  assistants, tests or snippets.
55
55
  - Keep displays separate from `system|code` tokens and local-language text.
56
+ - For repeated vital signs, keep every raw reading as `registered`, require the
57
+ configured cadence before deriving a `preliminary` mean, and preserve the
58
+ source links through `Observation.derived-from`.
59
+ - Persist the EventTiming bucket through the canonical
60
+ `Observation.date-when` claim. Do not infer MORN/EVE from a server time zone.
61
+ - Build one preliminary Composition per explicit local date and IANA time
62
+ zone. Only authorized confirmation returns a final daily document with a
63
+ professional attester and final top-level Observations.
64
+ - Keep ledger hashing, individual-index writes, Digital Twin materialization
65
+ and research eligibility outside this neutral package. A final graph is data
66
+ state, not authorization for those side effects.
56
67
 
57
68
  ## Change with TDD
58
69
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 - 2026-09-27
4
+
5
+ - Add the neutral daily vital-sign document lifecycle: registered raw readings,
6
+ preliminary three-reading means, MORN/EVE EventTiming, one preliminary daily
7
+ Composition and explicit professional finalization.
8
+ - Preserve mean provenance through `Observation.derived-from`, validate the
9
+ local calendar day with an IANA time zone and project Composition status plus
10
+ section entry references at the FHIR R4 boundary.
11
+ - Add governed UCUM Fahrenheit and avoirdupois-pound input alongside Celsius
12
+ and kilograms without moving regional UI preferences into shared FHIR data.
13
+
14
+ ## 0.3.7 - 2026-09-26
15
+
16
+ - Add claims-first FHIR contracts for Location-linked Encounter care journeys,
17
+ emergency ServiceRequest plus triage Task, and Group-snapshot
18
+ CommunicationRequest campaigns.
19
+ - Add R4 projections for ServiceRequest and CommunicationRequest and preserve
20
+ `Encounter.partOf` without introducing split coding claims or persisted code
21
+ displays.
22
+
3
23
  ## 0.3.6 - 2026-09-26
4
24
 
5
25
  - Add a product-neutral claims-first DiagnosticReport graph builder for coded
package/README.md CHANGED
@@ -105,6 +105,15 @@ without inventing a new flat-claim family for each scale.
105
105
  `Observation.user-selected` provenance flag. Consumers submit its `entries`
106
106
  without reconstructing component claims or relationships.
107
107
 
108
+ Repeated morning/evening capture uses `buildRepeatedVitalSignAverage`: three
109
+ two-minute raw readings remain `registered`, while their derived arithmetic
110
+ mean is `preliminary`. `buildVitalSignDailyDocument` groups the day's top-level
111
+ Observations under one preliminary Composition; only
112
+ `finalizeVitalSignDailyDocument` returns a final Composition and final
113
+ top-level Observations with a professional attester. Ledger hashing, index
114
+ updates, Digital Twin and research eligibility remain owning-service policy.
115
+ See [Daily vital-sign document lifecycle](docs/VITAL_SIGN_DAILY_DOCUMENT.md).
116
+
108
117
  See [the UHC FHIR utilities migration](docs/UHC_FHIR_UTILS_MIGRATION.md) for the
109
118
  reuse/rewrite/retire inventory and the rule for moving frozen contracts without
110
119
  changing any `gdc-*` repository.
@@ -150,6 +159,15 @@ its canonical `Communication.recipient` claim.
150
159
  coverage over converters, builders and core FHIR utilities. The package also
151
160
  tests every stable export subpath. Release validation additionally inspects the
152
161
  packed artifact and installs it in an isolated consumer before publication.
162
+
163
+ ## Care, emergency and population-alert graphs
164
+
165
+ The package provides the neutral FHIR foundation for the three SOSChain
166
+ journeys proven first in VetChain/PetChain: Location-linked Encounter care
167
+ journeys, emergency ServiceRequest plus triage Task, and cohort
168
+ CommunicationRequest against a frozen Group snapshot. See
169
+ [the contract guide](docs/CARE_EMERGENCY_ALERT_CONTRACTS.md). Product policy,
170
+ access, encryption and delivery do not belong in these builders.
153
171
  # Coding-review workflow
154
172
 
155
173
  Imported wording remains in the resource's canonical `*-text` flat claim. Each
@@ -0,0 +1,70 @@
1
+ import { type FlatClaimResourceEntry } from './flat-claim-resource-graph.js';
2
+ export type ClinicalCoding = Readonly<{
3
+ system: string;
4
+ code: string;
5
+ display?: string;
6
+ text?: string;
7
+ }>;
8
+ export type EncounterJourneyNode = Readonly<{
9
+ entryId: string;
10
+ identifier: string;
11
+ status: 'planned' | 'arrived' | 'triaged' | 'in-progress' | 'onleave' | 'finished' | 'cancelled';
12
+ periodStart: string;
13
+ periodEnd?: string;
14
+ locationReference: string;
15
+ type?: ClinicalCoding;
16
+ }>;
17
+ /**
18
+ * Builds one actual-care Encounter graph from an originating Appointment.
19
+ * Child service/location encounters inherit subject and provider and reference
20
+ * the parent with FHIR `Encounter.partOf`; Appointment remains planning input.
21
+ */
22
+ export declare function buildEncounterCareJourneyFlatGraph(input: Readonly<{
23
+ parent: EncounterJourneyNode & Readonly<{
24
+ subjectReference: string;
25
+ appointmentReference?: string;
26
+ serviceProviderReference: string;
27
+ }>;
28
+ stages?: readonly EncounterJourneyNode[];
29
+ }>): readonly FlatClaimResourceEntry[];
30
+ /** Builds one standard claims-first ServiceRequest; caller identity is input, not inferred authority. */
31
+ export declare function buildServiceRequestFlatEntry(input: Readonly<{
32
+ entryId: string;
33
+ identifier: string;
34
+ status: 'draft' | 'active' | 'on-hold' | 'revoked' | 'completed' | 'entered-in-error' | 'unknown';
35
+ intent: 'proposal' | 'plan' | 'directive' | 'order' | 'original-order' | 'reflex-order' | 'filler-order' | 'instance-order' | 'option';
36
+ priority?: 'routine' | 'urgent' | 'asap' | 'stat';
37
+ category?: ClinicalCoding;
38
+ code: ClinicalCoding;
39
+ subjectReference: string;
40
+ encounterReference?: string;
41
+ requesterReference: string;
42
+ performerReferences: readonly string[];
43
+ authoredAt: string;
44
+ occurrence?: string;
45
+ reasonCodes?: readonly ClinicalCoding[];
46
+ reasonReferences?: readonly string[];
47
+ }>): FlatClaimResourceEntry;
48
+ /** Builds the independent durable Task used to acknowledge and triage one ServiceRequest. */
49
+ export declare function buildServiceRequestTriageTaskFlatEntry(input: Readonly<{
50
+ entryId: string;
51
+ identifier: string;
52
+ serviceRequestReference: string;
53
+ subjectReference: string;
54
+ requesterReference: string;
55
+ ownerReference: string;
56
+ authoredAt: string;
57
+ }>): FlatClaimResourceEntry;
58
+ /** Builds one campaign-level request against a frozen enumerated Group snapshot. */
59
+ export declare function buildCohortCommunicationRequestFlatEntry(input: Readonly<{
60
+ entryId: string;
61
+ identifier: string;
62
+ status: 'draft' | 'active' | 'on-hold' | 'revoked' | 'completed' | 'entered-in-error' | 'unknown';
63
+ priority?: 'routine' | 'urgent' | 'asap' | 'stat';
64
+ category: ClinicalCoding;
65
+ subjectReference: string;
66
+ requesterReference: string;
67
+ authoredAt: string;
68
+ occurrence?: string;
69
+ reasonReferences: readonly string[];
70
+ }>): FlatClaimResourceEntry;
@@ -0,0 +1,135 @@
1
+ import { CommunicationRequestClaim, EncounterClaim, ServiceRequestClaim, TaskClaim, } from './claims/index.js';
2
+ import { buildFlatClaimResourceEntry } from './flat-claim-resource-graph.js';
3
+ /**
4
+ * Builds one actual-care Encounter graph from an originating Appointment.
5
+ * Child service/location encounters inherit subject and provider and reference
6
+ * the parent with FHIR `Encounter.partOf`; Appointment remains planning input.
7
+ */
8
+ export function buildEncounterCareJourneyFlatGraph(input) {
9
+ const parent = encounterEntry(input.parent);
10
+ const stages = (input.stages ?? []).map(stage => encounterEntry({
11
+ ...stage,
12
+ subjectReference: input.parent.subjectReference,
13
+ serviceProviderReference: input.parent.serviceProviderReference,
14
+ partOfReference: parent.reference,
15
+ }));
16
+ return Object.freeze([parent, ...stages]);
17
+ }
18
+ /** Builds one standard claims-first ServiceRequest; caller identity is input, not inferred authority. */
19
+ export function buildServiceRequestFlatEntry(input) {
20
+ const claims = {
21
+ [ServiceRequestClaim.Identifier]: required(input.identifier, 'service_request_identifier_required'),
22
+ [ServiceRequestClaim.Status]: input.status,
23
+ [ServiceRequestClaim.Intent]: input.intent,
24
+ [ServiceRequestClaim.Code]: coding(input.code, 'service_request_code_invalid'),
25
+ [ServiceRequestClaim.Subject]: reference(input.subjectReference, 'service_request_subject_invalid'),
26
+ [ServiceRequestClaim.Requester]: reference(input.requesterReference, 'service_request_requester_invalid'),
27
+ [ServiceRequestClaim.Performer]: references(input.performerReferences, 'service_request_performer_required'),
28
+ [ServiceRequestClaim.AuthoredOn]: dateTime(input.authoredAt, 'service_request_authored_on_invalid'),
29
+ };
30
+ if (input.priority)
31
+ claims[ServiceRequestClaim.Priority] = input.priority;
32
+ if (input.category)
33
+ claims[ServiceRequestClaim.Category] = coding(input.category, 'service_request_category_invalid');
34
+ if (input.code.text?.trim())
35
+ claims[ServiceRequestClaim.CodeText] = input.code.text.trim();
36
+ if (input.encounterReference)
37
+ claims[ServiceRequestClaim.Encounter] = reference(input.encounterReference, 'service_request_encounter_invalid');
38
+ if (input.occurrence)
39
+ claims[ServiceRequestClaim.Occurrence] = dateTime(input.occurrence, 'service_request_occurrence_invalid');
40
+ if (input.reasonCodes?.length)
41
+ claims[ServiceRequestClaim.ReasonCode] = input.reasonCodes.map(value => coding(value, 'service_request_reason_code_invalid')).join(',');
42
+ if (input.reasonReferences?.length)
43
+ claims[ServiceRequestClaim.ReasonReference] = references(input.reasonReferences, 'service_request_reason_reference_invalid');
44
+ return buildFlatClaimResourceEntry({ entryId: input.entryId, resourceType: 'ServiceRequest', claims });
45
+ }
46
+ /** Builds the independent durable Task used to acknowledge and triage one ServiceRequest. */
47
+ export function buildServiceRequestTriageTaskFlatEntry(input) {
48
+ return buildFlatClaimResourceEntry({
49
+ entryId: input.entryId,
50
+ resourceType: 'Task',
51
+ claims: {
52
+ [TaskClaim.Identifier]: required(input.identifier, 'triage_task_identifier_required'),
53
+ [TaskClaim.Status]: 'requested',
54
+ [TaskClaim.Intent]: 'order',
55
+ [TaskClaim.Code]: 'triage',
56
+ [TaskClaim.For]: reference(input.subjectReference, 'triage_task_subject_invalid'),
57
+ [TaskClaim.Requester]: reference(input.requesterReference, 'triage_task_requester_invalid'),
58
+ [TaskClaim.Owner]: reference(input.ownerReference, 'triage_task_owner_invalid'),
59
+ [TaskClaim.Focus]: reference(input.serviceRequestReference, 'triage_task_focus_invalid'),
60
+ [TaskClaim.AuthoredOn]: dateTime(input.authoredAt, 'triage_task_authored_on_invalid'),
61
+ },
62
+ });
63
+ }
64
+ /** Builds one campaign-level request against a frozen enumerated Group snapshot. */
65
+ export function buildCohortCommunicationRequestFlatEntry(input) {
66
+ const claims = {
67
+ [CommunicationRequestClaim.Identifier]: required(input.identifier, 'communication_request_identifier_required'),
68
+ [CommunicationRequestClaim.Status]: input.status,
69
+ [CommunicationRequestClaim.Category]: coding(input.category, 'communication_request_category_invalid'),
70
+ [CommunicationRequestClaim.Subject]: groupReference(input.subjectReference, 'communication_request_subject_group_required'),
71
+ [CommunicationRequestClaim.Requester]: reference(input.requesterReference, 'communication_request_requester_invalid'),
72
+ [CommunicationRequestClaim.AuthoredOn]: dateTime(input.authoredAt, 'communication_request_authored_on_invalid'),
73
+ [CommunicationRequestClaim.ReasonReference]: references(input.reasonReferences, 'communication_request_reason_reference_required'),
74
+ };
75
+ if (input.priority)
76
+ claims[CommunicationRequestClaim.Priority] = input.priority;
77
+ if (input.occurrence)
78
+ claims[CommunicationRequestClaim.Occurrence] = dateTime(input.occurrence, 'communication_request_occurrence_invalid');
79
+ return buildFlatClaimResourceEntry({ entryId: input.entryId, resourceType: 'CommunicationRequest', claims });
80
+ }
81
+ function encounterEntry(input) {
82
+ const claims = {
83
+ [EncounterClaim.Identifier]: required(input.identifier, 'encounter_identifier_required'),
84
+ [EncounterClaim.Status]: input.status,
85
+ [EncounterClaim.Subject]: reference(input.subjectReference, 'encounter_subject_invalid'),
86
+ [EncounterClaim.ServiceProvider]: reference(input.serviceProviderReference, 'encounter_service_provider_invalid'),
87
+ [EncounterClaim.PeriodStart]: dateTime(input.periodStart, 'encounter_period_start_invalid'),
88
+ [EncounterClaim.Location]: reference(input.locationReference, 'encounter_location_invalid'),
89
+ };
90
+ if (input.periodEnd)
91
+ claims[EncounterClaim.PeriodEnd] = dateTime(input.periodEnd, 'encounter_period_end_invalid');
92
+ if (input.appointmentReference)
93
+ claims[EncounterClaim.Appointment] = reference(input.appointmentReference, 'encounter_appointment_invalid');
94
+ if (input.partOfReference)
95
+ claims[EncounterClaim.PartOf] = reference(input.partOfReference, 'encounter_part_of_invalid');
96
+ if (input.type)
97
+ claims[EncounterClaim.Type] = coding(input.type, 'encounter_type_invalid');
98
+ return buildFlatClaimResourceEntry({ entryId: input.entryId, resourceType: 'Encounter', claims });
99
+ }
100
+ function coding(value, error) {
101
+ const system = required(value.system, error);
102
+ const code = required(value.code, error);
103
+ if (system.includes('|') || code.includes('|') || system.includes(',') || code.includes(','))
104
+ throw new TypeError(error);
105
+ return `${system}|${code}`;
106
+ }
107
+ function references(values, error) {
108
+ const normalized = [...new Set(values.map(value => reference(value, error)))];
109
+ if (!normalized.length)
110
+ throw new TypeError(error);
111
+ return normalized.join(',');
112
+ }
113
+ function reference(value, error) {
114
+ const normalized = required(value, error);
115
+ if (normalized.includes(','))
116
+ throw new TypeError(error);
117
+ return normalized;
118
+ }
119
+ function groupReference(value, error) {
120
+ const normalized = reference(value, error);
121
+ if (!normalized.startsWith('Group/'))
122
+ throw new TypeError(error);
123
+ return normalized;
124
+ }
125
+ function dateTime(value, error) {
126
+ const normalized = required(value, error);
127
+ if (!/^\d{4}-\d{2}-\d{2}T/.test(normalized) || Number.isNaN(Date.parse(normalized)))
128
+ throw new TypeError(error);
129
+ return normalized;
130
+ }
131
+ function required(value, error) {
132
+ if (typeof value !== 'string' || !value.trim())
133
+ throw new TypeError(error);
134
+ return value.trim();
135
+ }
@@ -0,0 +1,21 @@
1
+ import type { ClaimSpec } from './claim-types.js';
2
+ /** Canonical claims-first keys for FHIR CommunicationRequest. */
3
+ export declare const CommunicationRequestClaim: Readonly<{
4
+ readonly Identifier: "CommunicationRequest.identifier";
5
+ readonly BasedOn: "CommunicationRequest.based-on";
6
+ readonly Status: "CommunicationRequest.status";
7
+ readonly Priority: "CommunicationRequest.priority";
8
+ readonly Category: "CommunicationRequest.category";
9
+ readonly Subject: "CommunicationRequest.subject";
10
+ readonly About: "CommunicationRequest.about";
11
+ readonly Encounter: "CommunicationRequest.encounter";
12
+ readonly Requester: "CommunicationRequest.requester";
13
+ readonly Recipient: "CommunicationRequest.recipient";
14
+ readonly AuthoredOn: "CommunicationRequest.authored-on";
15
+ readonly Occurrence: "CommunicationRequest.occurrence";
16
+ readonly ReasonCode: "CommunicationRequest.reason-code";
17
+ readonly ReasonReference: "CommunicationRequest.reason-reference";
18
+ readonly PayloadContent: "CommunicationRequest.payload-content";
19
+ }>;
20
+ export type CommunicationRequestClaimKey = typeof CommunicationRequestClaim[keyof typeof CommunicationRequestClaim];
21
+ export declare const CommunicationRequestClaimSpecs: readonly ClaimSpec[];
@@ -0,0 +1,27 @@
1
+ /** Canonical claims-first keys for FHIR CommunicationRequest. */
2
+ export const CommunicationRequestClaim = Object.freeze({
3
+ Identifier: 'CommunicationRequest.identifier',
4
+ BasedOn: 'CommunicationRequest.based-on',
5
+ Status: 'CommunicationRequest.status',
6
+ Priority: 'CommunicationRequest.priority',
7
+ Category: 'CommunicationRequest.category',
8
+ Subject: 'CommunicationRequest.subject',
9
+ About: 'CommunicationRequest.about',
10
+ Encounter: 'CommunicationRequest.encounter',
11
+ Requester: 'CommunicationRequest.requester',
12
+ Recipient: 'CommunicationRequest.recipient',
13
+ AuthoredOn: 'CommunicationRequest.authored-on',
14
+ Occurrence: 'CommunicationRequest.occurrence',
15
+ ReasonCode: 'CommunicationRequest.reason-code',
16
+ ReasonReference: 'CommunicationRequest.reason-reference',
17
+ PayloadContent: 'CommunicationRequest.payload-content',
18
+ });
19
+ export const CommunicationRequestClaimSpecs = Object.freeze([
20
+ { key: CommunicationRequestClaim.Identifier, meaning: 'Business identifier.', example: 'urn:uuid:campaign-1' },
21
+ { key: CommunicationRequestClaim.Status, meaning: 'Request lifecycle status.', example: 'active' },
22
+ { key: CommunicationRequestClaim.Priority, meaning: 'Delivery priority.', example: 'routine' },
23
+ { key: CommunicationRequestClaim.Category, meaning: 'Communication category token.', example: 'http://terminology.hl7.org/CodeSystem/communication-category|notification' },
24
+ { key: CommunicationRequestClaim.Subject, meaning: 'Subject or evaluated Group reference.', example: 'Group/snapshot-1' },
25
+ { key: CommunicationRequestClaim.Requester, meaning: 'Requesting organization or service.', example: 'Organization/blood-bank-1' },
26
+ { key: CommunicationRequestClaim.AuthoredOn, meaning: 'Request authoring time.', example: '2026-09-26T11:00:00Z' },
27
+ ]);
@@ -1,4 +1,5 @@
1
1
  export declare const CompositionClaim: {
2
+ readonly Status: "Composition.status";
2
3
  readonly Subject: "Composition.subject";
3
4
  readonly Section: "Composition.section";
4
5
  readonly Author: "Composition.author";
@@ -1,6 +1,7 @@
1
1
  // Copyright 2026 Conéctate Soluciones y Aplicaciones SL under the Apache License, Version 2.0.
2
2
  // File: src/models/interoperable-claims/composition-claims.ts
3
3
  export const CompositionClaim = {
4
+ Status: 'Composition.status',
4
5
  Subject: 'Composition.subject',
5
6
  Section: 'Composition.section',
6
7
  Author: 'Composition.author',
@@ -14,6 +14,7 @@ export declare const EncounterClaim: {
14
14
  readonly ReasonCode: "Encounter.reason-code";
15
15
  readonly Diagnosis: "Encounter.diagnosis";
16
16
  readonly Location: "Encounter.location";
17
+ readonly PartOf: "Encounter.part-of";
17
18
  };
18
19
  /** Canonical FHIR R4 Encounter lifecycle codes. */
19
20
  export declare enum EncounterStatus {
@@ -15,6 +15,7 @@ export const EncounterClaim = {
15
15
  ReasonCode: 'Encounter.reason-code',
16
16
  Diagnosis: 'Encounter.diagnosis',
17
17
  Location: 'Encounter.location',
18
+ PartOf: 'Encounter.part-of',
18
19
  };
19
20
  /** Canonical FHIR R4 Encounter lifecycle codes. */
20
21
  export var EncounterStatus;
@@ -44,4 +45,5 @@ export const EncounterClaimSpecs = [
44
45
  { key: EncounterClaim.ReasonCode, meaning: 'Reason code token.', example: 'http://snomed.info/sct|65363002' },
45
46
  { key: EncounterClaim.Diagnosis, meaning: 'Diagnosis references (CSV).', example: 'Condition/cond-1' },
46
47
  { key: EncounterClaim.Location, meaning: 'Location references (CSV).', example: 'Location/loc-1' },
48
+ { key: EncounterClaim.PartOf, meaning: 'Parent Encounter reference.', example: 'Encounter/encounter-1' },
47
49
  ];
@@ -5,6 +5,7 @@ export * from './appointment-response-claims.js';
5
5
  export * from './care-plan-claims.js';
6
6
  export * from './clinical-impression-claims.js';
7
7
  export * from './communication-claims.js';
8
+ export * from './communication-request-claims.js';
8
9
  export * from './composition-claims.js';
9
10
  export * from './consent-claims.js';
10
11
  export * from './condition-claims.js';
@@ -27,4 +28,5 @@ export * from './practitioner-role-claims.js';
27
28
  export * from './procedure-claims.js';
28
29
  export * from './related-person-claims.js';
29
30
  export * from './research-subject-claims.js';
31
+ export * from './service-request-claims.js';
30
32
  export * from './task-claims.js';
@@ -5,6 +5,7 @@ export * from './appointment-response-claims.js';
5
5
  export * from './care-plan-claims.js';
6
6
  export * from './clinical-impression-claims.js';
7
7
  export * from './communication-claims.js';
8
+ export * from './communication-request-claims.js';
8
9
  export * from './composition-claims.js';
9
10
  export * from './consent-claims.js';
10
11
  export * from './condition-claims.js';
@@ -27,4 +28,5 @@ export * from './practitioner-role-claims.js';
27
28
  export * from './procedure-claims.js';
28
29
  export * from './related-person-claims.js';
29
30
  export * from './research-subject-claims.js';
31
+ export * from './service-request-claims.js';
30
32
  export * from './task-claims.js';
@@ -0,0 +1,24 @@
1
+ import type { ClaimSpec } from './claim-types.js';
2
+ /** Canonical claims-first keys for FHIR ServiceRequest. */
3
+ export declare const ServiceRequestClaim: Readonly<{
4
+ readonly Identifier: "ServiceRequest.identifier";
5
+ readonly Status: "ServiceRequest.status";
6
+ readonly Intent: "ServiceRequest.intent";
7
+ readonly Priority: "ServiceRequest.priority";
8
+ readonly Category: "ServiceRequest.category";
9
+ readonly Code: "ServiceRequest.code";
10
+ readonly CodeText: "ServiceRequest.code-text";
11
+ readonly Subject: "ServiceRequest.subject";
12
+ readonly Encounter: "ServiceRequest.encounter";
13
+ readonly Requester: "ServiceRequest.requester";
14
+ readonly Performer: "ServiceRequest.performer";
15
+ readonly AuthoredOn: "ServiceRequest.authored-on";
16
+ readonly Occurrence: "ServiceRequest.occurrence";
17
+ readonly ReasonCode: "ServiceRequest.reason-code";
18
+ readonly ReasonReference: "ServiceRequest.reason-reference";
19
+ readonly SupportingInfo: "ServiceRequest.supporting-info";
20
+ readonly Specimen: "ServiceRequest.specimen";
21
+ readonly BodySite: "ServiceRequest.body-site";
22
+ }>;
23
+ export type ServiceRequestClaimKey = typeof ServiceRequestClaim[keyof typeof ServiceRequestClaim];
24
+ export declare const ServiceRequestClaimSpecs: readonly ClaimSpec[];
@@ -0,0 +1,33 @@
1
+ /** Canonical claims-first keys for FHIR ServiceRequest. */
2
+ export const ServiceRequestClaim = Object.freeze({
3
+ Identifier: 'ServiceRequest.identifier',
4
+ Status: 'ServiceRequest.status',
5
+ Intent: 'ServiceRequest.intent',
6
+ Priority: 'ServiceRequest.priority',
7
+ Category: 'ServiceRequest.category',
8
+ Code: 'ServiceRequest.code',
9
+ CodeText: 'ServiceRequest.code-text',
10
+ Subject: 'ServiceRequest.subject',
11
+ Encounter: 'ServiceRequest.encounter',
12
+ Requester: 'ServiceRequest.requester',
13
+ Performer: 'ServiceRequest.performer',
14
+ AuthoredOn: 'ServiceRequest.authored-on',
15
+ Occurrence: 'ServiceRequest.occurrence',
16
+ ReasonCode: 'ServiceRequest.reason-code',
17
+ ReasonReference: 'ServiceRequest.reason-reference',
18
+ SupportingInfo: 'ServiceRequest.supporting-info',
19
+ Specimen: 'ServiceRequest.specimen',
20
+ BodySite: 'ServiceRequest.body-site',
21
+ });
22
+ export const ServiceRequestClaimSpecs = Object.freeze([
23
+ { key: ServiceRequestClaim.Identifier, meaning: 'Business identifier.', example: 'urn:uuid:request-1' },
24
+ { key: ServiceRequestClaim.Status, meaning: 'Request lifecycle status.', example: 'active' },
25
+ { key: ServiceRequestClaim.Intent, meaning: 'Request intent.', example: 'order' },
26
+ { key: ServiceRequestClaim.Priority, meaning: 'Request priority.', example: 'stat' },
27
+ { key: ServiceRequestClaim.Category, meaning: 'Category token.', example: 'http://snomed.info/sct|409971007' },
28
+ { key: ServiceRequestClaim.Code, meaning: 'Requested service token.', example: 'http://snomed.info/sct|225358003' },
29
+ { key: ServiceRequestClaim.Subject, meaning: 'Subject reference.', example: 'Patient/subject-1' },
30
+ { key: ServiceRequestClaim.Requester, meaning: 'Requester reference.', example: 'RelatedPerson/requester-1' },
31
+ { key: ServiceRequestClaim.Performer, meaning: 'Requested performer references (CSV).', example: 'HealthcareService/emergency-1' },
32
+ { key: ServiceRequestClaim.AuthoredOn, meaning: 'Request authoring time.', example: '2026-09-26T10:00:00Z' },
33
+ ]);
@@ -13,6 +13,7 @@ export declare const ResourceTypesFhirR4: Readonly<{
13
13
  readonly CarePlan: "CarePlan";
14
14
  readonly ClinicalImpression: "ClinicalImpression";
15
15
  readonly Communication: "Communication";
16
+ readonly CommunicationRequest: "CommunicationRequest";
16
17
  readonly Composition: "Composition";
17
18
  readonly Condition: "Condition";
18
19
  readonly Consent: "Consent";
@@ -68,6 +69,7 @@ export declare const FhirResourceTypes: Readonly<{
68
69
  readonly CarePlan: "CarePlan";
69
70
  readonly ClinicalImpression: "ClinicalImpression";
70
71
  readonly Communication: "Communication";
72
+ readonly CommunicationRequest: "CommunicationRequest";
71
73
  readonly Composition: "Composition";
72
74
  readonly Condition: "Condition";
73
75
  readonly Consent: "Consent";
@@ -119,6 +121,7 @@ export declare const ResourceTypesFhirR5: Readonly<{
119
121
  readonly CarePlan: "CarePlan";
120
122
  readonly ClinicalImpression: "ClinicalImpression";
121
123
  readonly Communication: "Communication";
124
+ readonly CommunicationRequest: "CommunicationRequest";
122
125
  readonly Composition: "Composition";
123
126
  readonly Condition: "Condition";
124
127
  readonly Consent: "Consent";
@@ -13,6 +13,7 @@ export const ResourceTypesFhirR4 = Object.freeze({
13
13
  CarePlan: 'CarePlan',
14
14
  ClinicalImpression: 'ClinicalImpression',
15
15
  Communication: 'Communication',
16
+ CommunicationRequest: 'CommunicationRequest',
16
17
  Composition: 'Composition',
17
18
  Condition: 'Condition',
18
19
  Consent: 'Consent',
@@ -0,0 +1,5 @@
1
+ import type { FhirResource, FlatClaims } from './convert-shared.js';
2
+ /** Projects one governed communication request to the FHIR R4 wire shape. */
3
+ export declare function communicationRequestFlatToFhirR4(claims: FlatClaims): FhirResource;
4
+ /** Reads a FHIR R4 CommunicationRequest into canonical flat claims. */
5
+ export declare function communicationRequestFhirR4ToFlat(resource: FhirResource): FlatClaims;
@@ -0,0 +1,40 @@
1
+ import { CommunicationRequestClaim } from '../claims/communication-request-claims.js';
2
+ import { codingFromValue, codingListFromCsv, codingListToCsv, codingToValue, referenceListToCsv, referenceToValue } from './convert-shared.js';
3
+ /** Projects one governed communication request to the FHIR R4 wire shape. */
4
+ export function communicationRequestFlatToFhirR4(claims) {
5
+ return {
6
+ resourceType: 'CommunicationRequest', identifier: claims[CommunicationRequestClaim.Identifier] ? [{ value: claims[CommunicationRequestClaim.Identifier] }] : undefined,
7
+ basedOn: referenceCsv(claims[CommunicationRequestClaim.BasedOn]), status: claims[CommunicationRequestClaim.Status], priority: claims[CommunicationRequestClaim.Priority],
8
+ category: claims[CommunicationRequestClaim.Category] ? [{ coding: codingFromValue(claims[CommunicationRequestClaim.Category]) }] : undefined,
9
+ subject: claims[CommunicationRequestClaim.Subject] ? { reference: claims[CommunicationRequestClaim.Subject] } : undefined,
10
+ about: referenceCsv(claims[CommunicationRequestClaim.About]), encounter: claims[CommunicationRequestClaim.Encounter] ? { reference: claims[CommunicationRequestClaim.Encounter] } : undefined,
11
+ requester: claims[CommunicationRequestClaim.Requester] ? { reference: claims[CommunicationRequestClaim.Requester] } : undefined,
12
+ recipient: referenceCsv(claims[CommunicationRequestClaim.Recipient]), authoredOn: claims[CommunicationRequestClaim.AuthoredOn],
13
+ occurrenceDateTime: claims[CommunicationRequestClaim.Occurrence],
14
+ reasonCode: claims[CommunicationRequestClaim.ReasonCode] ? [{ coding: codingListFromCsv(claims[CommunicationRequestClaim.ReasonCode]) }] : undefined,
15
+ reasonReference: referenceCsv(claims[CommunicationRequestClaim.ReasonReference]),
16
+ payload: claims[CommunicationRequestClaim.PayloadContent] ? [{ contentString: claims[CommunicationRequestClaim.PayloadContent] }] : undefined,
17
+ };
18
+ }
19
+ /** Reads a FHIR R4 CommunicationRequest into canonical flat claims. */
20
+ export function communicationRequestFhirR4ToFlat(resource) {
21
+ return {
22
+ [CommunicationRequestClaim.Identifier]: resource.identifier?.[0]?.value,
23
+ [CommunicationRequestClaim.BasedOn]: referenceListToCsv(resource.basedOn),
24
+ [CommunicationRequestClaim.Status]: resource.status, [CommunicationRequestClaim.Priority]: resource.priority,
25
+ [CommunicationRequestClaim.Category]: codingToValue(resource.category?.[0]?.coding?.[0]),
26
+ [CommunicationRequestClaim.Subject]: referenceToValue(resource.subject),
27
+ [CommunicationRequestClaim.About]: referenceListToCsv(resource.about),
28
+ [CommunicationRequestClaim.Encounter]: referenceToValue(resource.encounter),
29
+ [CommunicationRequestClaim.Requester]: referenceToValue(resource.requester),
30
+ [CommunicationRequestClaim.Recipient]: referenceListToCsv(resource.recipient),
31
+ [CommunicationRequestClaim.AuthoredOn]: resource.authoredOn, [CommunicationRequestClaim.Occurrence]: resource.occurrenceDateTime,
32
+ [CommunicationRequestClaim.ReasonCode]: codingListToCsv(resource.reasonCode?.flatMap(value => value.coding ?? [])),
33
+ [CommunicationRequestClaim.ReasonReference]: referenceListToCsv(resource.reasonReference),
34
+ [CommunicationRequestClaim.PayloadContent]: resource.payload?.[0]?.contentString,
35
+ };
36
+ }
37
+ function referenceCsv(value) {
38
+ const references = value?.split(',').map(reference => reference.trim()).filter(Boolean) ?? [];
39
+ return references.length ? references.map(reference => ({ reference })) : undefined;
40
+ }
@@ -31,6 +31,7 @@ function alignedAttesterValue(attesters, select) {
31
31
  export function compositionFlatToFhirR4(claims) {
32
32
  return {
33
33
  resourceType: 'Composition',
34
+ status: claims[CompositionClaim.Status],
34
35
  identifier: claims[CompositionClaim.Identifier] ? { value: claims[CompositionClaim.Identifier] } : undefined,
35
36
  subject: claims[CompositionClaim.Subject] ? { reference: claims[CompositionClaim.Subject] } : undefined,
36
37
  author: claims[CompositionClaim.Author] ? claims[CompositionClaim.Author].split(',').map((reference) => ({ reference: reference.trim() })) : undefined,
@@ -41,7 +42,9 @@ export function compositionFlatToFhirR4(claims) {
41
42
  type: claims[CompositionClaim.Type] ? { coding: codingFromValue(claims[CompositionClaim.Type]) } : undefined,
42
43
  section: claims[CompositionClaim.Section]
43
44
  ? claims[CompositionClaim.Section].split(',').map((value) => ({ code: { coding: codingFromValue(value.trim()) } }))
44
- : undefined,
45
+ : claims[CompositionClaim.Entry]
46
+ ? [{ entry: claims[CompositionClaim.Entry].split(',').map((reference) => ({ reference: reference.trim() })) }]
47
+ : undefined,
45
48
  };
46
49
  }
47
50
  export function compositionFhirR4ToFlat(resource) {
@@ -53,7 +56,14 @@ export function compositionFhirR4ToFlat(resource) {
53
56
  .map((item) => codingToValue(item?.code?.coding?.[0]))
54
57
  .filter((item) => Boolean(item))
55
58
  : [];
59
+ const entryReferences = Array.isArray(resource.section)
60
+ ? resource.section
61
+ .flatMap((section) => Array.isArray(section.entry) ? section.entry : [])
62
+ .map((entry) => referenceToValue(entry))
63
+ .filter((item) => Boolean(item))
64
+ : [];
56
65
  return {
66
+ [CompositionClaim.Status]: resource.status,
57
67
  [CompositionClaim.Subject]: referenceToValue(resource.subject),
58
68
  [CompositionClaim.Section]: sectionValues.length ? sectionValues.join(',') : undefined,
59
69
  [CompositionClaim.Author]: referenceListToCsv(resource.author),
@@ -62,7 +72,7 @@ export function compositionFhirR4ToFlat(resource) {
62
72
  [CompositionClaim.AttesterTime]: alignedAttesterValue(attesters, (attester) => attester.time),
63
73
  [CompositionClaim.Custodian]: referenceToValue(resource.custodian),
64
74
  [CompositionClaim.Date]: resource.date,
65
- [CompositionClaim.Entry]: undefined,
75
+ [CompositionClaim.Entry]: entryReferences.length ? entryReferences.join(',') : undefined,
66
76
  [CompositionClaim.Type]: codingToValue(resource.type?.coding?.[0]),
67
77
  [CompositionClaim.Identifier]: resource.identifier?.value,
68
78
  [CompositionClaim.Title]: resource.title,
@@ -34,6 +34,7 @@ export function encounterFlatToFhirR4(claims) {
34
34
  location: claims[EncounterClaim.Location]
35
35
  ? claims[EncounterClaim.Location].split(',').map((reference) => ({ location: { reference: reference.trim() } }))
36
36
  : undefined,
37
+ partOf: claims[EncounterClaim.PartOf] ? { reference: claims[EncounterClaim.PartOf] } : undefined,
37
38
  };
38
39
  }
39
40
  export function encounterFhirR4ToFlat(resource) {
@@ -68,5 +69,6 @@ export function encounterFhirR4ToFlat(resource) {
68
69
  .filter((item) => Boolean(item))
69
70
  .join(',')
70
71
  : undefined,
72
+ [EncounterClaim.PartOf]: referenceToValue(resource.partOf),
71
73
  };
72
74
  }
@@ -0,0 +1,5 @@
1
+ import type { FhirResource, FlatClaims } from './convert-shared.js';
2
+ /** Projects canonical ServiceRequest flat claims to the FHIR R4 wire shape. */
3
+ export declare function serviceRequestFlatToFhirR4(claims: FlatClaims): FhirResource;
4
+ /** Reads a FHIR R4 ServiceRequest into canonical compact-token flat claims. */
5
+ export declare function serviceRequestFhirR4ToFlat(resource: FhirResource): FlatClaims;