fhir-data-utils-ts 0.6.7 → 0.6.9

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/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - Preserve locally entered `Observation.code.text` when a terminology code has
6
+ not yet been confirmed, so profile-aware authoring remains visible and can be
7
+ coded later without inventing a code.
8
+
9
+ - Add section-specific Observation authoring profiles for vital signs,
10
+ laboratory results, social history and pregnancy history. Each profile owns
11
+ its IPS section, HL7 Observation category and initial claims.
12
+ - Add a reusable synthetic document directory in which every document starts
13
+ with Composition, points to its Encounter context and shares a chronological
14
+ parent/child Encounter journey. ResearchSubject is deliberately absent.
15
+
5
16
  - Export data-only, coverage-excluded authoring schemas for the six minimum IPS
6
17
  resource families used by shared Personal and Professional forms:
7
18
  AllergyIntolerance, MedicationStatement, Immunization, Condition, Procedure
package/README.md CHANGED
@@ -209,6 +209,19 @@ journeys, emergency ServiceRequest plus triage Task, and cohort
209
209
  CommunicationRequest against a frozen Group snapshot. See
210
210
  [the contract guide](docs/CARE_EMERGENCY_ALERT_CONTRACTS.md). Product policy,
211
211
  access, encryption and delivery do not belong in these builders.
212
+
213
+ ## Clinical documents and Encounter journeys
214
+
215
+ A FHIR document always starts with `Composition`; `Encounter` is its optional
216
+ care context through `Composition.encounter`, not the document root. The same
217
+ section viewer can therefore render an IPS, emergency note, progress note or
218
+ diagnostic report by reading the Composition title/type and section graph.
219
+
220
+ Encounter navigation is a separate journey graph. Child service encounters
221
+ use `Encounter.partOf` for the strict care hierarchy and their periods provide
222
+ chronological previous/next navigation. The individual remains the clinical
223
+ subject. `ResearchSubject` is not used for care delivery or document browsing.
224
+ See [Clinical documents and Encounter exploration](docs/CLINICAL_DOCUMENT_ENCOUNTER_EXPLORATION.md).
212
225
  # Coding-review workflow
213
226
 
214
227
  Imported wording remains in the resource's canonical `*-text` flat claim. Each
@@ -44,9 +44,9 @@ export function observationFromFlatToFhirR4(claims) {
44
44
  identifier: claims[ObservationClaim.Identifier] ? [{ value: claims[ObservationClaim.Identifier] }] : undefined,
45
45
  status: claims[ObservationClaim.Status],
46
46
  category: claims[ObservationClaim.Category] ? claims[ObservationClaim.Category].split(',').map((value) => ({ coding: codingFromValue(value.trim()) })) : undefined,
47
- code: codeToken
47
+ code: codeToken || claims[ObservationClaim.CodeText]
48
48
  ? {
49
- coding: codeCoding,
49
+ ...(codeCoding ? { coding: codeCoding } : {}),
50
50
  ...(claims[ObservationClaim.CodeText] ? { text: claims[ObservationClaim.CodeText] } : {}),
51
51
  }
52
52
  : undefined,
@@ -0,0 +1,172 @@
1
+ /** Product-neutral synthetic document directory linked to a chronological Encounter hierarchy. */
2
+ export declare const FHIR_CLINICAL_DOCUMENT_ENCOUNTER_JOURNEY_TEST_DATA: Readonly<{
3
+ subject: Readonly<{
4
+ reference: "Patient/jane-doe-demo";
5
+ displayName: "Jane Doe";
6
+ }>;
7
+ encounterCount: number;
8
+ documentCount: 3;
9
+ encounters: readonly (Readonly<{
10
+ resourceType: "Encounter";
11
+ id: "admission-demo";
12
+ status: "in-progress";
13
+ class: Readonly<{
14
+ code: "IMP";
15
+ display: "Inpatient encounter";
16
+ }>;
17
+ subject: Readonly<{
18
+ reference: "Patient/jane-doe-demo";
19
+ }>;
20
+ serviceProvider: Readonly<{
21
+ reference: "Organization/demo-hospital";
22
+ }>;
23
+ actualPeriod: Readonly<{
24
+ start: "2026-10-03T08:00:00Z";
25
+ }>;
26
+ }> | Readonly<{
27
+ resourceType: "Encounter";
28
+ id: string;
29
+ status: "completed" | "in-progress";
30
+ class: Readonly<{
31
+ code: "IMP";
32
+ display: "Inpatient encounter";
33
+ }>;
34
+ subject: Readonly<{
35
+ reference: "Patient/jane-doe-demo";
36
+ }>;
37
+ serviceType: readonly {
38
+ concept: Readonly<{
39
+ text: string;
40
+ }>;
41
+ }[];
42
+ serviceProvider: Readonly<{
43
+ reference: "Organization/demo-hospital";
44
+ }>;
45
+ actualPeriod: Readonly<{
46
+ end?: string | undefined;
47
+ start: string;
48
+ }>;
49
+ location: readonly {
50
+ location: Readonly<{
51
+ reference: `Location/${string}`;
52
+ display: string;
53
+ }>;
54
+ }[];
55
+ partOf: Readonly<{
56
+ reference: "Encounter/admission-demo";
57
+ }>;
58
+ }>)[];
59
+ documents: readonly Readonly<{
60
+ resourceType: "Bundle";
61
+ id: `bundle-${string}`;
62
+ identifier: Readonly<{
63
+ system: "urn:ietf:rfc:3986";
64
+ value: `urn:uuid:${string}`;
65
+ }>;
66
+ type: "document";
67
+ timestamp: string;
68
+ entry: readonly (Readonly<{
69
+ fullUrl: string;
70
+ resource: Readonly<{
71
+ resourceType: "Composition";
72
+ id: string;
73
+ status: "final";
74
+ type: Readonly<{
75
+ text: string;
76
+ }>;
77
+ subject: readonly {
78
+ reference: "Patient/jane-doe-demo";
79
+ }[];
80
+ encounter: Readonly<{
81
+ reference: string;
82
+ }>;
83
+ date: string;
84
+ author: readonly {
85
+ reference: string;
86
+ }[];
87
+ title: string;
88
+ section: readonly {
89
+ title: string;
90
+ entry: readonly {
91
+ reference: string;
92
+ }[];
93
+ }[];
94
+ }>;
95
+ }> | Readonly<{
96
+ fullUrl: "Patient/jane-doe-demo";
97
+ resource: Readonly<{
98
+ resourceType: "Patient";
99
+ id: "jane-doe-demo";
100
+ name: readonly {
101
+ given: readonly string[];
102
+ family: string;
103
+ }[];
104
+ }>;
105
+ }> | Readonly<{
106
+ fullUrl: "Organization/demo-hospital";
107
+ resource: Readonly<{
108
+ resourceType: "Organization";
109
+ id: "demo-hospital";
110
+ name: "Demo Hospital";
111
+ }>;
112
+ }> | Readonly<{
113
+ fullUrl: "Encounter/admission-demo";
114
+ resource: Readonly<{
115
+ resourceType: "Encounter";
116
+ id: "admission-demo";
117
+ status: "in-progress";
118
+ class: Readonly<{
119
+ code: "IMP";
120
+ display: "Inpatient encounter";
121
+ }>;
122
+ subject: Readonly<{
123
+ reference: "Patient/jane-doe-demo";
124
+ }>;
125
+ serviceProvider: Readonly<{
126
+ reference: "Organization/demo-hospital";
127
+ }>;
128
+ actualPeriod: Readonly<{
129
+ start: "2026-10-03T08:00:00Z";
130
+ }>;
131
+ }>;
132
+ }> | Readonly<{
133
+ fullUrl: string;
134
+ resource: Readonly<{
135
+ resourceType: "Encounter";
136
+ id: string;
137
+ status: "completed" | "in-progress";
138
+ class: Readonly<{
139
+ code: "IMP";
140
+ display: "Inpatient encounter";
141
+ }>;
142
+ subject: Readonly<{
143
+ reference: "Patient/jane-doe-demo";
144
+ }>;
145
+ serviceType: readonly {
146
+ concept: Readonly<{
147
+ text: string;
148
+ }>;
149
+ }[];
150
+ serviceProvider: Readonly<{
151
+ reference: "Organization/demo-hospital";
152
+ }>;
153
+ actualPeriod: Readonly<{
154
+ end?: string | undefined;
155
+ start: string;
156
+ }>;
157
+ location: readonly {
158
+ location: Readonly<{
159
+ reference: `Location/${string}`;
160
+ display: string;
161
+ }>;
162
+ }[];
163
+ partOf: Readonly<{
164
+ reference: "Encounter/admission-demo";
165
+ }>;
166
+ }>;
167
+ }> | Readonly<{
168
+ fullUrl: string;
169
+ resource: Readonly<Record<string, unknown>>;
170
+ }>)[];
171
+ }>[];
172
+ }>;
@@ -0,0 +1,152 @@
1
+ /** Product-neutral synthetic document directory linked to a chronological Encounter hierarchy. */
2
+ const subject = Object.freeze({
3
+ reference: 'Patient/jane-doe-demo',
4
+ displayName: 'Jane Doe',
5
+ });
6
+ const organizationReference = 'Organization/demo-hospital';
7
+ const parentEncounterReference = 'Encounter/admission-demo';
8
+ const parentEncounter = Object.freeze({
9
+ resourceType: 'Encounter',
10
+ id: 'admission-demo',
11
+ status: 'in-progress',
12
+ class: Object.freeze({ code: 'IMP', display: 'Inpatient encounter' }),
13
+ subject: Object.freeze({ reference: subject.reference }),
14
+ serviceProvider: Object.freeze({ reference: organizationReference }),
15
+ actualPeriod: Object.freeze({ start: '2026-10-03T08:00:00Z' }),
16
+ });
17
+ function stage(input) {
18
+ return Object.freeze({
19
+ resourceType: 'Encounter',
20
+ id: input.id,
21
+ status: input.status,
22
+ class: Object.freeze({ code: 'IMP', display: 'Inpatient encounter' }),
23
+ subject: Object.freeze({ reference: subject.reference }),
24
+ serviceType: Object.freeze([{ concept: Object.freeze({ text: input.serviceDisplay }) }]),
25
+ serviceProvider: Object.freeze({ reference: organizationReference }),
26
+ actualPeriod: Object.freeze({ start: input.start, ...(input.end ? { end: input.end } : {}) }),
27
+ location: Object.freeze([{ location: Object.freeze({ reference: `Location/${input.id}`, display: input.locationDisplay }) }]),
28
+ partOf: Object.freeze({ reference: parentEncounterReference }),
29
+ });
30
+ }
31
+ const emergencyEncounter = stage({
32
+ id: 'emergency-demo',
33
+ status: 'completed',
34
+ serviceDisplay: 'Emergency department',
35
+ locationDisplay: 'Emergency bay',
36
+ start: '2026-10-03T08:05:00Z',
37
+ end: '2026-10-03T10:10:00Z',
38
+ });
39
+ const imagingEncounter = stage({
40
+ id: 'imaging-demo',
41
+ status: 'completed',
42
+ serviceDisplay: 'Diagnostic imaging',
43
+ locationDisplay: 'Imaging department',
44
+ start: '2026-10-03T10:10:00Z',
45
+ end: '2026-10-03T11:00:00Z',
46
+ });
47
+ const intensiveCareEncounter = stage({
48
+ id: 'intensive-care-demo',
49
+ status: 'in-progress',
50
+ serviceDisplay: 'Intensive care',
51
+ locationDisplay: 'Intensive care unit',
52
+ start: '2026-10-03T11:00:00Z',
53
+ });
54
+ const encounters = Object.freeze([
55
+ parentEncounter,
56
+ emergencyEncounter,
57
+ imagingEncounter,
58
+ intensiveCareEncounter,
59
+ ]);
60
+ function documentBundle(input) {
61
+ const compositionReference = `Composition/${input.id}`;
62
+ const clinicalReference = `${input.clinicalResource.resourceType}/${input.clinicalResource.id}`;
63
+ const encounterReference = `Encounter/${input.encounter.id}`;
64
+ const composition = Object.freeze({
65
+ resourceType: 'Composition',
66
+ id: input.id,
67
+ status: 'final',
68
+ type: Object.freeze({ text: input.type }),
69
+ subject: Object.freeze([{ reference: subject.reference }]),
70
+ encounter: Object.freeze({ reference: encounterReference }),
71
+ date: input.timestamp,
72
+ author: Object.freeze([{ reference: organizationReference }]),
73
+ title: input.title,
74
+ section: Object.freeze([{
75
+ title: input.sectionTitle,
76
+ entry: Object.freeze([{ reference: clinicalReference }]),
77
+ }]),
78
+ });
79
+ return Object.freeze({
80
+ resourceType: 'Bundle',
81
+ id: `bundle-${input.id}`,
82
+ identifier: Object.freeze({ system: 'urn:ietf:rfc:3986', value: `urn:uuid:${input.id}` }),
83
+ type: 'document',
84
+ timestamp: input.timestamp,
85
+ entry: Object.freeze([
86
+ Object.freeze({ fullUrl: compositionReference, resource: composition }),
87
+ Object.freeze({ fullUrl: subject.reference, resource: Object.freeze({ resourceType: 'Patient', id: 'jane-doe-demo', name: Object.freeze([{ given: Object.freeze(['Jane']), family: 'Doe' }]) }) }),
88
+ Object.freeze({ fullUrl: organizationReference, resource: Object.freeze({ resourceType: 'Organization', id: 'demo-hospital', name: 'Demo Hospital' }) }),
89
+ Object.freeze({ fullUrl: parentEncounterReference, resource: parentEncounter }),
90
+ Object.freeze({ fullUrl: encounterReference, resource: input.encounter }),
91
+ Object.freeze({ fullUrl: clinicalReference, resource: input.clinicalResource }),
92
+ ]),
93
+ });
94
+ }
95
+ const emergencyNote = documentBundle({
96
+ id: 'emergency-note-demo',
97
+ title: 'Emergency assessment',
98
+ type: 'Emergency department note',
99
+ encounter: emergencyEncounter,
100
+ sectionTitle: 'Emergency assessment',
101
+ timestamp: '2026-10-03T10:00:00Z',
102
+ clinicalResource: Object.freeze({
103
+ resourceType: 'ClinicalImpression',
104
+ id: 'emergency-assessment-demo',
105
+ status: 'completed',
106
+ subject: Object.freeze({ reference: subject.reference }),
107
+ encounter: Object.freeze({ reference: `Encounter/${emergencyEncounter.id}` }),
108
+ summary: 'Initial emergency assessment completed',
109
+ }),
110
+ });
111
+ const imagingReport = documentBundle({
112
+ id: 'imaging-report-demo',
113
+ title: 'Diagnostic imaging report',
114
+ type: 'Diagnostic imaging report',
115
+ encounter: imagingEncounter,
116
+ sectionTitle: 'Imaging result',
117
+ timestamp: '2026-10-03T10:55:00Z',
118
+ clinicalResource: Object.freeze({
119
+ resourceType: 'DiagnosticReport',
120
+ id: 'imaging-result-demo',
121
+ status: 'final',
122
+ subject: Object.freeze({ reference: subject.reference }),
123
+ encounter: Object.freeze({ reference: `Encounter/${imagingEncounter.id}` }),
124
+ code: Object.freeze({ text: 'Diagnostic imaging assessment' }),
125
+ conclusion: 'No acute finding requiring intervention',
126
+ }),
127
+ });
128
+ const intensiveCareNote = documentBundle({
129
+ id: 'intensive-care-note-demo',
130
+ title: 'Intensive care progress note',
131
+ type: 'Progress note',
132
+ encounter: intensiveCareEncounter,
133
+ sectionTitle: 'Current assessment',
134
+ timestamp: '2026-10-03T11:30:00Z',
135
+ clinicalResource: Object.freeze({
136
+ resourceType: 'Observation',
137
+ id: 'intensive-care-observation-demo',
138
+ status: 'final',
139
+ subject: Object.freeze({ reference: subject.reference }),
140
+ encounter: Object.freeze({ reference: `Encounter/${intensiveCareEncounter.id}` }),
141
+ code: Object.freeze({ text: 'Current clinical status' }),
142
+ valueString: 'Stable under continuous monitoring',
143
+ effectiveDateTime: '2026-10-03T11:25:00Z',
144
+ }),
145
+ });
146
+ export const FHIR_CLINICAL_DOCUMENT_ENCOUNTER_JOURNEY_TEST_DATA = Object.freeze({
147
+ subject,
148
+ encounterCount: encounters.length,
149
+ documentCount: 3,
150
+ encounters,
151
+ documents: Object.freeze([emergencyNote, imagingReport, intensiveCareNote]),
152
+ });
@@ -4,3 +4,4 @@ export * from './encounter-transfer.data.js';
4
4
  export * from './ips-all-sections.data.js';
5
5
  export * from './health-credential-qr.data.js';
6
6
  export * from './ips-clinical-artifact-schemas.data.js';
7
+ export * from './clinical-document-encounter-journey.data.js';
@@ -4,3 +4,4 @@ export * from './encounter-transfer.data.js';
4
4
  export * from './ips-all-sections.data.js';
5
5
  export * from './health-credential-qr.data.js';
6
6
  export * from './ips-clinical-artifact-schemas.data.js';
7
+ export * from './clinical-document-encounter-journey.data.js';
@@ -1,4 +1,23 @@
1
1
  /** Repository-safe synthetic IPS document used by portal demos and visual tests. */
2
+ /** Canonical LOINC section codes used by the reusable IPS reader and editors. */
3
+ export declare const FHIR_IPS_SECTION_CODES: Readonly<{
4
+ readonly Problems: "11450-4";
5
+ readonly Allergies: "48765-2";
6
+ readonly Medications: "10160-0";
7
+ readonly Immunizations: "11369-6";
8
+ readonly DiagnosticResults: "30954-2";
9
+ readonly Procedures: "47519-4";
10
+ readonly MedicalDevices: "46264-8";
11
+ readonly VitalSigns: "8716-3";
12
+ readonly SocialHistory: "29762-2";
13
+ readonly Alerts: "104605-1";
14
+ readonly Goals: "81338-6";
15
+ readonly AdvanceDirectives: "42348-3";
16
+ readonly FunctionalStatus: "47420-5";
17
+ readonly PastIllness: "11348-0";
18
+ readonly PregnancyHistory: "10162-6";
19
+ readonly PlanOfCare: "18776-5";
20
+ }>;
2
21
  /** Primary IPS section placement for the minimum shared clinical authoring forms. */
3
22
  export declare const FHIR_IPS_PRIMARY_AUTHORING_SECTION_CODE_BY_RESOURCE_TYPE: Readonly<{
4
23
  readonly Condition: "11450-4";
@@ -20,14 +20,33 @@ const sectionDefinitions = Object.freeze([
20
20
  ['10162-6', 'Pregnancy history', 'Observation', 'Not pregnant'],
21
21
  ['18776-5', 'Plan of care', 'CarePlan', 'Emergency follow-up plan'],
22
22
  ]);
23
+ /** Canonical LOINC section codes used by the reusable IPS reader and editors. */
24
+ export const FHIR_IPS_SECTION_CODES = Object.freeze({
25
+ Problems: sectionDefinitions[0][0],
26
+ Allergies: sectionDefinitions[1][0],
27
+ Medications: sectionDefinitions[2][0],
28
+ Immunizations: sectionDefinitions[3][0],
29
+ DiagnosticResults: sectionDefinitions[4][0],
30
+ Procedures: sectionDefinitions[5][0],
31
+ MedicalDevices: sectionDefinitions[6][0],
32
+ VitalSigns: sectionDefinitions[7][0],
33
+ SocialHistory: sectionDefinitions[8][0],
34
+ Alerts: sectionDefinitions[9][0],
35
+ Goals: sectionDefinitions[10][0],
36
+ AdvanceDirectives: sectionDefinitions[11][0],
37
+ FunctionalStatus: sectionDefinitions[12][0],
38
+ PastIllness: sectionDefinitions[13][0],
39
+ PregnancyHistory: sectionDefinitions[14][0],
40
+ PlanOfCare: sectionDefinitions[15][0],
41
+ });
23
42
  /** Primary IPS section placement for the minimum shared clinical authoring forms. */
24
43
  export const FHIR_IPS_PRIMARY_AUTHORING_SECTION_CODE_BY_RESOURCE_TYPE = Object.freeze({
25
- Condition: sectionDefinitions[0][0],
26
- AllergyIntolerance: sectionDefinitions[1][0],
27
- MedicationStatement: sectionDefinitions[2][0],
28
- Immunization: sectionDefinitions[3][0],
29
- Procedure: sectionDefinitions[5][0],
30
- Observation: sectionDefinitions[7][0],
44
+ Condition: FHIR_IPS_SECTION_CODES.Problems,
45
+ AllergyIntolerance: FHIR_IPS_SECTION_CODES.Allergies,
46
+ MedicationStatement: FHIR_IPS_SECTION_CODES.Medications,
47
+ Immunization: FHIR_IPS_SECTION_CODES.Immunizations,
48
+ Procedure: FHIR_IPS_SECTION_CODES.Procedures,
49
+ Observation: FHIR_IPS_SECTION_CODES.VitalSigns,
31
50
  });
32
51
  function concept(text, code) {
33
52
  return { text, coding: [{ system: SNOMED_SYSTEM, code, display: text }] };
@@ -15,6 +15,19 @@ export type FhirIpsClinicalArtifactSchema = Readonly<{
15
15
  /** Source-text claims for which a user may explicitly confirm a terminology candidate. */
16
16
  terminologyTextClaims: readonly string[];
17
17
  }>;
18
+ export declare const FhirIpsObservationArtifactKinds: Readonly<{
19
+ readonly VitalSigns: "vital-signs";
20
+ readonly Laboratory: "laboratory";
21
+ readonly SocialHistory: "social-history";
22
+ readonly PregnancyHistory: "pregnancy-history";
23
+ }>;
24
+ export type FhirIpsObservationArtifactKind = typeof FhirIpsObservationArtifactKinds[keyof typeof FhirIpsObservationArtifactKinds];
25
+ export type FhirIpsObservationArtifactSchema = FhirIpsClinicalArtifactSchema & Readonly<{
26
+ kind: FhirIpsObservationArtifactKind;
27
+ initialClaims: Readonly<Record<string, string>>;
28
+ }>;
29
+ /** Section-specific Observation authoring contracts; a resource type alone is not a sufficient selector. */
30
+ export declare const FHIR_IPS_OBSERVATION_ARTIFACT_SCHEMAS: Readonly<Record<FhirIpsObservationArtifactKind, FhirIpsObservationArtifactSchema>>;
18
31
  /**
19
32
  * Minimum reusable IPS authoring surface for Personal and Professional portals.
20
33
  *
@@ -4,8 +4,8 @@ import { ConditionClaim } from '../claims/condition-claims.js';
4
4
  import { ImmunizationClaim } from '../claims/immunization-claims.js';
5
5
  import { MedicationStatementClaim } from '../claims/medication-statement-claims.js';
6
6
  import { ProcedureClaim } from '../claims/procedure-claims.js';
7
- import { ObservationClaim } from '../observation-claims.js';
8
- import { FHIR_IPS_PRIMARY_AUTHORING_SECTION_CODE_BY_RESOURCE_TYPE } from './ips-all-sections.data.js';
7
+ import { ObservationCategoryCodes, ObservationClaim } from '../observation-claims.js';
8
+ import { FHIR_IPS_PRIMARY_AUTHORING_SECTION_CODE_BY_RESOURCE_TYPE, FHIR_IPS_SECTION_CODES } from './ips-all-sections.data.js';
9
9
  export const FhirIpsClinicalArtifactTypes = Object.freeze({
10
10
  AllergyIntolerance: 'AllergyIntolerance',
11
11
  MedicationStatement: 'MedicationStatement',
@@ -14,6 +14,60 @@ export const FhirIpsClinicalArtifactTypes = Object.freeze({
14
14
  Procedure: 'Procedure',
15
15
  Observation: 'Observation',
16
16
  });
17
+ export const FhirIpsObservationArtifactKinds = Object.freeze({
18
+ VitalSigns: 'vital-signs',
19
+ Laboratory: 'laboratory',
20
+ SocialHistory: 'social-history',
21
+ PregnancyHistory: 'pregnancy-history',
22
+ });
23
+ const observationRequiredClaims = Object.freeze([
24
+ ObservationClaim.Category,
25
+ ObservationClaim.CodeText,
26
+ ObservationClaim.Status,
27
+ ]);
28
+ const observationOptionalClaims = Object.freeze([
29
+ ObservationClaim.Code,
30
+ ObservationClaim.CodeDisplay,
31
+ ObservationClaim.Date,
32
+ ObservationClaim.EffectiveDateTime,
33
+ ObservationClaim.Encounter,
34
+ ObservationClaim.Performer,
35
+ ObservationClaim.Method,
36
+ ObservationClaim.Specimen,
37
+ ObservationClaim.Device,
38
+ ObservationClaim.ValueQuantityNumber,
39
+ ObservationClaim.ValueQuantityUnit,
40
+ ObservationClaim.ValueString,
41
+ ObservationClaim.ValueConcept,
42
+ ObservationClaim.ValueConceptText,
43
+ ObservationClaim.ValueConceptDisplay,
44
+ ObservationClaim.ReferenceRangeLowNumber,
45
+ ObservationClaim.ReferenceRangeHighNumber,
46
+ ObservationClaim.ReferenceRangeUnit,
47
+ ObservationClaim.ReferenceRangeText,
48
+ ObservationClaim.Note,
49
+ ]);
50
+ function observationSchema(kind, sectionCode, category) {
51
+ return Object.freeze({
52
+ kind,
53
+ resourceType: FhirIpsClinicalArtifactTypes.Observation,
54
+ sectionCode,
55
+ requiredClaims: observationRequiredClaims,
56
+ optionalClaims: observationOptionalClaims,
57
+ terminologyTextClaims: Object.freeze([ObservationClaim.CodeText]),
58
+ initialClaims: Object.freeze({
59
+ [ObservationClaim.Category]: category,
60
+ [ObservationClaim.Status]: 'final',
61
+ }),
62
+ });
63
+ }
64
+ /** Section-specific Observation authoring contracts; a resource type alone is not a sufficient selector. */
65
+ export const FHIR_IPS_OBSERVATION_ARTIFACT_SCHEMAS = Object.freeze({
66
+ [FhirIpsObservationArtifactKinds.VitalSigns]: observationSchema(FhirIpsObservationArtifactKinds.VitalSigns, FHIR_IPS_SECTION_CODES.VitalSigns, ObservationCategoryCodes.VitalSigns.claim),
67
+ [FhirIpsObservationArtifactKinds.Laboratory]: observationSchema(FhirIpsObservationArtifactKinds.Laboratory, FHIR_IPS_SECTION_CODES.DiagnosticResults, ObservationCategoryCodes.Laboratory.claim),
68
+ [FhirIpsObservationArtifactKinds.SocialHistory]: observationSchema(FhirIpsObservationArtifactKinds.SocialHistory, FHIR_IPS_SECTION_CODES.SocialHistory, ObservationCategoryCodes.SocialHistory.claim),
69
+ [FhirIpsObservationArtifactKinds.PregnancyHistory]: observationSchema(FhirIpsObservationArtifactKinds.PregnancyHistory, FHIR_IPS_SECTION_CODES.PregnancyHistory, ObservationCategoryCodes.Survey.claim),
70
+ });
17
71
  /**
18
72
  * Minimum reusable IPS authoring surface for Personal and Professional portals.
19
73
  *
@@ -59,10 +113,6 @@ export const FHIR_IPS_CLINICAL_ARTIFACT_SCHEMAS = Object.freeze({
59
113
  terminologyTextClaims: Object.freeze([ProcedureClaim.CodeText]),
60
114
  }),
61
115
  [FhirIpsClinicalArtifactTypes.Observation]: Object.freeze({
62
- resourceType: FhirIpsClinicalArtifactTypes.Observation,
63
- sectionCode: FHIR_IPS_PRIMARY_AUTHORING_SECTION_CODE_BY_RESOURCE_TYPE.Observation,
64
- requiredClaims: Object.freeze([ObservationClaim.Category, ObservationClaim.CodeText, ObservationClaim.Status]),
65
- optionalClaims: Object.freeze([ObservationClaim.Code, ObservationClaim.CodeDisplay, ObservationClaim.Date, ObservationClaim.EffectiveDateTime, ObservationClaim.Performer, ObservationClaim.Method, ObservationClaim.Specimen, ObservationClaim.Device, ObservationClaim.ValueQuantityNumber, ObservationClaim.ValueQuantityUnit, ObservationClaim.ValueString, ObservationClaim.ValueConcept, ObservationClaim.ReferenceRangeLowNumber, ObservationClaim.ReferenceRangeHighNumber, ObservationClaim.ReferenceRangeUnit, ObservationClaim.ReferenceRangeText, ObservationClaim.Note]),
66
- terminologyTextClaims: Object.freeze([ObservationClaim.CodeText]),
116
+ ...FHIR_IPS_OBSERVATION_ARTIFACT_SCHEMAS[FhirIpsObservationArtifactKinds.VitalSigns],
67
117
  }),
68
118
  });
@@ -0,0 +1,41 @@
1
+ # Clinical documents and Encounter exploration
2
+
3
+ ## The two connected graphs
4
+
5
+ A clinical document is a `Bundle` with `type=document`. Its first resource is
6
+ always a `Composition`. The Composition gives the document its human title,
7
+ type, authorship, subject and sections. IPS is one document profile; an
8
+ emergency note, progress note or diagnostic report uses the same document
9
+ shape with a different Composition type and section set.
10
+
11
+ The care journey is an Encounter graph, not a ResearchSubject graph. The
12
+ individual remains the subject of each Encounter. A document associates with
13
+ the care step it describes through `Composition.encounter`, and clinical
14
+ entries such as Observation or DiagnosticReport may repeat that Encounter
15
+ reference when their own FHIR definition provides it.
16
+
17
+ ## Navigation
18
+
19
+ One parent Encounter can represent the admission or overall episode represented
20
+ inside the current provider. Emergency, imaging, surgery, ward and intensive
21
+ care steps can be independent child Encounters linked with `Encounter.partOf`.
22
+ Their actual periods provide a deterministic chronological order for previous
23
+ and next navigation. `EncounterHistory` records status/class/location
24
+ milestones for one Encounter; it does not replace the Encounter hierarchy.
25
+
26
+ The reusable viewer receives authorized documents and Encounters. Selecting an
27
+ Encounter shows documents whose `Composition.encounter` references it. Opening
28
+ one of those documents renders its Composition sections with the same reader
29
+ used for IPS. Missing permissions remain missing data; the browser must not
30
+ infer or fetch another subject merely because two Encounter references are
31
+ related.
32
+
33
+ ## Write boundaries
34
+
35
+ - A multi-section attested clinical document is Composition-first.
36
+ - A single-section native update may be sent independently and carries its
37
+ section at the Communication boundary.
38
+ - Encounter, EncounterHistory, Composition and each clinical entry remain
39
+ independently persisted claims-first resources.
40
+ - Authorization, audit and provider persistence belong to the BFF/GW; the
41
+ shared fixture and viewer grant no access.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fhir-data-utils-ts",
3
- "version": "0.6.7",
3
+ "version": "0.6.9",
4
4
  "description": "FHIR-version-neutral flat-claim and SearchParameter data utilities",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",