@imranq2/fhirpatientsummary 1.0.16 → 1.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -82,33 +82,21 @@ var BLOOD_PRESSURE_LOINC_CODES = {
82
82
  };
83
83
 
84
84
  // src/structures/ips_section_resource_map.ts
85
- var IPSSectionResourceMap = {
86
- ["Patient" /* PATIENT */]: ["Patient"],
87
- ["AllergyIntoleranceSection" /* ALLERGIES */]: ["AllergyIntolerance"],
88
- ["MedicationSummarySection" /* MEDICATIONS */]: ["MedicationRequest", "MedicationStatement", "Medication"],
89
- // Medication resource is needed for identifying name of medication
90
- ["ProblemSection" /* PROBLEMS */]: ["Condition"],
91
- ["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization", "Organization"],
92
- // Immunization can include Organization as a related resource
93
- ["VitalSignsSection" /* VITAL_SIGNS */]: ["Observation"],
94
- ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["DeviceUseStatement", "Device"],
95
- // Device resource is used for medical devices name
96
- ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: ["DiagnosticReport", "Observation"],
97
- ["HistoryOfProceduresSection" /* PROCEDURES */]: ["Procedure"],
98
- ["SocialHistorySection" /* SOCIAL_HISTORY */]: ["Observation"],
99
- ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: ["Observation"],
100
- ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: ["Condition", "ClinicalImpression"],
101
- ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: ["Condition"],
102
- ["PlanOfCareSection" /* CARE_PLAN */]: ["CarePlan"],
103
- ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: ["Consent"]
104
- };
105
85
  var IPSSectionResourceFilters = {
86
+ // Patient section: only Patient resource
87
+ ["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient",
88
+ // Only include allergies
89
+ ["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "AllergyIntolerance",
90
+ // includes MedicationRequest, MedicationStatement. Medication is needed for medication names
91
+ ["MedicationSummarySection" /* MEDICATIONS */]: (resource) => ["MedicationRequest", "MedicationStatement", "Medication"].includes(resource.resourceType),
106
92
  // Only include active conditions
107
93
  ["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => !["inactive", "resolved"].includes(c.code)),
108
94
  // Only include completed immunizations
109
95
  ["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed" || resource.resourceType === "Organization",
110
96
  // Only include vital sign Observations (category.coding contains 'vital-signs')
111
97
  ["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => c.code === "vital-signs")),
98
+ // Includes DeviceUseStatement. Device is needed for linked device details
99
+ ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: (resource) => ["DeviceUseStatement", "Device"].includes(resource.resourceType),
112
100
  // Only include finalized diagnostic reports
113
101
  ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
114
102
  // Only include completed procedures
@@ -124,28 +112,30 @@ var IPSSectionResourceFilters = {
124
112
  // Only include active care plans
125
113
  ["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
126
114
  // Only include active advance directives (Consent resources)
127
- ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active",
128
- // Patient section: only Patient resource
129
- ["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient"
115
+ ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active"
116
+ };
117
+ var IPSSectionSummaryCompositionFilter = {
118
+ ["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === "https://fhir.icanbwell.com/4_0_0/CodeSystem/composition/" && c.code === "allergy_summary_document"),
119
+ ["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === "https://fhir.icanbwell.com/4_0_0/CodeSystem/composition/" && c.code === "vital_summary_document")
130
120
  };
131
121
  var IPSSectionResourceHelper = class {
132
- static getResourceTypesForSection(section) {
133
- return IPSSectionResourceMap[section] || [];
134
- }
135
122
  static getResourceFilterForSection(section) {
136
123
  return IPSSectionResourceFilters[section];
137
124
  }
125
+ static getSummaryCompositionFilterForSection(section) {
126
+ return IPSSectionSummaryCompositionFilter[section];
127
+ }
138
128
  };
139
129
 
140
130
  // src/narratives/templates/typescript/TemplateUtilities.ts
141
131
  import { DateTime } from "luxon";
142
132
  var TemplateUtilities = class {
143
133
  /**
144
- * Constructor to initialize the TemplateUtilities with a FHIR Bundle
145
- * @param bundle - FHIR Bundle containing resources
134
+ * Constructor to initialize the TemplateUtilities with a FHIR resources
135
+ * @param resources - FHIR resources
146
136
  */
147
- constructor(bundle) {
148
- this.bundle = bundle;
137
+ constructor(resources) {
138
+ this.resources = resources;
149
139
  }
150
140
  /**
151
141
  * Formats a CodeableConcept object
@@ -176,7 +166,7 @@ var TemplateUtilities = class {
176
166
  return "";
177
167
  }
178
168
  resolveReference(ref) {
179
- if (!ref || !this.bundle || !this.bundle.entry) {
169
+ if (!ref || !this.resources) {
180
170
  return null;
181
171
  }
182
172
  const referenceParts = ref.reference?.split("/");
@@ -185,10 +175,10 @@ var TemplateUtilities = class {
185
175
  }
186
176
  const referenceResourceType = referenceParts[0];
187
177
  const referenceResourceId = referenceParts[1];
188
- const resource = this.bundle.entry.find((entry) => {
189
- return entry.resource && entry.resource.resourceType === referenceResourceType && entry.resource.id === referenceResourceId;
178
+ const resource = this.resources.find((entry) => {
179
+ return entry.resourceType === referenceResourceType && entry.id === referenceResourceId;
190
180
  });
191
- return resource ? resource.resource : null;
181
+ return resource ? resource : null;
192
182
  }
193
183
  /**
194
184
  * Renders a Device reference
@@ -584,6 +574,137 @@ var TemplateUtilities = class {
584
574
  }
585
575
  return status;
586
576
  }
577
+ extractObservationSummaryValue(data, timezone) {
578
+ if (data["valueQuantity.value"] !== void 0) {
579
+ const value = data["valueQuantity.value"];
580
+ const unit = data["valueQuantity.unit"];
581
+ return unit ? `${value} ${unit}` : `${value}`;
582
+ }
583
+ if (data["valueCodeableConcept.text"] !== void 0) {
584
+ return data["valueCodeableConcept.text"];
585
+ }
586
+ if (data["valueCodeableConcept.coding.display"] !== void 0) {
587
+ return data["valueCodeableConcept.coding.display"];
588
+ }
589
+ if (data["valueString"] !== void 0) {
590
+ return data["valueString"];
591
+ }
592
+ if (data["valueBoolean"] !== void 0) {
593
+ return String(data["valueBoolean"]);
594
+ }
595
+ if (data["valueInteger"] !== void 0) {
596
+ return String(data["valueInteger"]);
597
+ }
598
+ if (data["valueDateTime"] !== void 0) {
599
+ return this.renderTime(data["valueDateTime"], timezone);
600
+ }
601
+ if (data["valuePeriod.start"] !== void 0 || data["valuePeriod.end"] !== void 0) {
602
+ const start = this.renderTime(data["valuePeriod.start"], timezone);
603
+ const end = this.renderTime(data["valuePeriod.end"], timezone);
604
+ if (start && end) {
605
+ return `${start} - ${end}`;
606
+ } else if (start) {
607
+ return `${start}`;
608
+ } else if (end) {
609
+ return `${end}`;
610
+ }
611
+ }
612
+ if (data["valueTime"] !== void 0) {
613
+ return this.renderTime(data["valueTime"], timezone);
614
+ }
615
+ if (data["valueSampledData.origin.value"] !== void 0 || data["valueSampledData.origin.unit"] !== void 0) {
616
+ const originValue = data["valueSampledData.origin.value"];
617
+ const originUnit = data["valueSampledData.origin.unit"];
618
+ let result = "";
619
+ if (originValue !== void 0 && originUnit !== void 0) {
620
+ result = `${originValue} ${originUnit}`;
621
+ } else if (originValue !== void 0) {
622
+ result = `${originValue}`;
623
+ } else if (originUnit !== void 0) {
624
+ result = `${originUnit}`;
625
+ }
626
+ const period = data["valueSampledData.period"];
627
+ const factor = data["valueSampledData.factor"];
628
+ const lowerLimit = data["valueSampledData.lowerLimit"];
629
+ const upperLimit = data["valueSampledData.upperLimit"];
630
+ const sampledData = data["valueSampledData.data"];
631
+ const extras = [];
632
+ if (period !== void 0) extras.push(`period: ${period}`);
633
+ if (factor !== void 0) extras.push(`factor: ${factor}`);
634
+ if (lowerLimit !== void 0) extras.push(`lowerLimit: ${lowerLimit}`);
635
+ if (upperLimit !== void 0) extras.push(`upperLimit: ${upperLimit}`);
636
+ if (sampledData !== void 0) extras.push(`data: ${sampledData}`);
637
+ if (extras.length > 0) {
638
+ result += ` (${extras.join(", ")})`;
639
+ }
640
+ return result;
641
+ }
642
+ if (data["valueRange.low.value"] !== void 0 || data["valueRange.high.value"] !== void 0) {
643
+ let referenceRange = "";
644
+ if (data["valueRange.low.value"] !== void 0) {
645
+ referenceRange += `${data["valueRange.low.value"]}`;
646
+ if (data["valueRange.low.unit"] !== void 0) {
647
+ referenceRange += ` ${data["valueRange.low.unit"]}`;
648
+ }
649
+ referenceRange = referenceRange.trim();
650
+ if (data["valueRange.high.value"] !== void 0) {
651
+ referenceRange += " - ";
652
+ }
653
+ }
654
+ if (data["valueRange.high.value"] !== void 0) {
655
+ referenceRange += `${data["valueRange.high.value"]}`;
656
+ if (data["valueRange.high.unit"] !== void 0) {
657
+ referenceRange += ` ${data["valueRange.high.unit"]}`;
658
+ }
659
+ }
660
+ return referenceRange.trim();
661
+ }
662
+ if (data["valueRatio.numerator.value"] !== void 0 || data["valueRatio.denominator.value"] !== void 0) {
663
+ let ratio = "";
664
+ if (data["valueRatio.numerator.value"] !== void 0) {
665
+ ratio += `${data["valueRatio.numerator.value"]}`;
666
+ if (data["valueRatio.numerator.unit"] !== void 0) {
667
+ ratio += ` ${data["valueRatio.numerator.unit"]}`;
668
+ }
669
+ }
670
+ if (data["valueRatio.denominator.value"] !== void 0) {
671
+ ratio += " / ";
672
+ ratio += `${data["valueRatio.denominator.value"]}`;
673
+ if (data["valueRatio.denominator.unit"] !== void 0) {
674
+ ratio += ` ${data["valueRatio.denominator.unit"]}`;
675
+ }
676
+ }
677
+ return ratio.trim();
678
+ }
679
+ return "";
680
+ }
681
+ extractObservationSummaryReferenceRange(data) {
682
+ let referenceRange = "";
683
+ if (data["referenceRange.low.value"]) {
684
+ referenceRange += `${data["referenceRange.low.value"]} ${data["referenceRange.low.unit"]}`;
685
+ referenceRange.trim();
686
+ if (data["referenceRange.high.value"]) {
687
+ referenceRange += " - ";
688
+ }
689
+ }
690
+ if (data["referenceRange.high.value"]) {
691
+ referenceRange += `${data["referenceRange.high.value"]} ${data["referenceRange.high.unit"]}`;
692
+ }
693
+ return referenceRange.trim();
694
+ }
695
+ extractObservationSummaryEffectiveTime(data, timezone) {
696
+ if (data["effectiveDateTime"]) {
697
+ return this.renderTime(data["effectiveDateTime"], timezone);
698
+ }
699
+ let effectiveTimePeriod = "";
700
+ if (data["effectivePeriod.start"]) {
701
+ effectiveTimePeriod += this.renderTime(data["effectivePeriod.start"], timezone);
702
+ }
703
+ if (data["effectivePeriod.end"]) {
704
+ effectiveTimePeriod += ` - ${this.renderTime(data["effectivePeriod.end"], timezone)}`;
705
+ }
706
+ return effectiveTimePeriod.trim();
707
+ }
587
708
  formatQuantityValue(quantity) {
588
709
  if (!quantity) return "";
589
710
  const parts = [];
@@ -759,7 +880,7 @@ var TemplateUtilities = class {
759
880
  if (parts.length === 2) {
760
881
  const resourceType = parts[0];
761
882
  const resourceId = parts[1];
762
- const resource = this.bundle.entry?.find((entry) => entry.resource?.resourceType === resourceType && entry.resource?.id === resourceId);
883
+ const resource = this.resources?.find((resource2) => resource2.resourceType === resourceType && resource2.id === resourceId);
763
884
  if (resource) {
764
885
  return `${resourceType}/${resourceId}`;
765
886
  }
@@ -799,48 +920,96 @@ var TemplateUtilities = class {
799
920
  // src/narratives/templates/typescript/PatientTemplate.ts
800
921
  var PatientTemplate = class _PatientTemplate {
801
922
  /**
802
- * Generate HTML narrative for Patient resource
803
- * @param resource - FHIR Bundle containing Patient resource
923
+ * Generate HTML narrative for Patient resources
924
+ * @param resources - FHIR Patient resources
804
925
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
805
926
  * @returns HTML string for rendering
806
927
  */
807
- generateNarrative(resource, timezone) {
808
- return _PatientTemplate.generateStaticNarrative(resource, timezone);
928
+ generateNarrative(resources, timezone) {
929
+ return _PatientTemplate.generateStaticNarrative(resources, timezone);
809
930
  }
810
931
  /**
811
932
  * Internal static implementation that actually generates the narrative
812
- * @param resource - FHIR Bundle containing Patient resource
933
+ * @param resources - FHIR Patient resources
813
934
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
814
935
  * @returns HTML string for rendering
815
936
  */
816
937
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
817
- static generateStaticNarrative(resource, timezone) {
818
- const templateUtilities = new TemplateUtilities(resource);
819
- let html = "";
820
- for (const entry of resource.entry || []) {
821
- if (entry.resource?.resourceType === "Patient") {
822
- const patient = entry.resource;
823
- html += `
824
- <div>
825
- <ul>
826
- <li><strong>Name(s):</strong>${this.renderNames(patient)}</li>
827
- <li><strong>Gender:</strong>${patient.gender ? this.capitalize(patient.gender) : ""}</li>
828
- <li><strong>Date of Birth:</strong>${patient.birthDate || ""}</li>
829
- <li><strong>Identifier(s):</strong>${this.renderIdentifiers(patient)}</li>
830
- <li><strong>Telecom:</strong><ul>${this.renderTelecom(patient)}</ul></li>
831
- <li><strong>Address(es):</strong>${this.renderAddresses(patient)}</li>
832
- <li><strong>Marital Status:</strong> ${patient.maritalStatus?.text || ""}</li>
833
- <li><strong>Deceased:</strong>${this.renderDeceased(patient)}</li>
834
- <li><strong>Language(s):</strong>${this.renderCommunication(templateUtilities, patient)}</li>
835
- </ul>
836
- </div>`;
938
+ static generateStaticNarrative(resources, timezone) {
939
+ const templateUtilities = new TemplateUtilities(resources);
940
+ const combinedPatient = this.combinePatients(resources);
941
+ return `<div>
942
+ <ul>
943
+ <li><strong>Name(s):</strong>${this.renderNames(combinedPatient)}</li>
944
+ <li><strong>Gender:</strong>${combinedPatient.gender ? this.capitalize(combinedPatient.gender) : ""}</li>
945
+ <li><strong>Date of Birth:</strong>${combinedPatient.birthDate || ""}</li>
946
+ <li><strong>Identifier(s):</strong>${this.renderIdentifiers(combinedPatient)}</li>
947
+ <li><strong>Telecom:</strong><ul>${this.renderTelecom(combinedPatient)}</ul></li>
948
+ <li><strong>Address(es):</strong>${this.renderAddresses(combinedPatient)}</li>
949
+ <li><strong>Marital Status:</strong> ${combinedPatient.maritalStatus?.text || ""}</li>
950
+ <li><strong>Deceased:</strong>${this.renderDeceased(combinedPatient)}</li>
951
+ <li><strong>Language(s):</strong>${this.renderCommunication(templateUtilities, combinedPatient)}</li>
952
+ </ul>
953
+ </div>`;
954
+ }
955
+ /**
956
+ * Combines multiple patient resources into a single patient object
957
+ * Merges fields, preferring non-empty values
958
+ * @param patients - Array of patient resources
959
+ * @returns Combined patient resource
960
+ */
961
+ static combinePatients(patients) {
962
+ if (patients.length === 1) {
963
+ return patients[0];
964
+ }
965
+ const combined = patients[0];
966
+ const allNames = [];
967
+ const allIdentifiers = [];
968
+ const allTelecom = [];
969
+ const allAddresses = [];
970
+ const allCommunication = [];
971
+ patients.forEach((patient) => {
972
+ if (patient.name) {
973
+ allNames.push(...patient.name);
837
974
  }
838
- }
839
- return html;
975
+ if (patient.identifier) {
976
+ allIdentifiers.push(...patient.identifier);
977
+ }
978
+ if (patient.telecom) {
979
+ allTelecom.push(...patient.telecom);
980
+ }
981
+ if (patient.address) {
982
+ allAddresses.push(...patient.address);
983
+ }
984
+ if (patient.communication) {
985
+ allCommunication.push(...patient.communication);
986
+ }
987
+ if (!combined.gender && patient.gender) {
988
+ combined.gender = patient.gender;
989
+ }
990
+ if (!combined.birthDate && patient.birthDate) {
991
+ combined.birthDate = patient.birthDate;
992
+ }
993
+ if (!combined.maritalStatus && patient.maritalStatus) {
994
+ combined.maritalStatus = patient.maritalStatus;
995
+ }
996
+ if (!combined.deceasedBoolean && patient.deceasedBoolean !== void 0) {
997
+ combined.deceasedBoolean = patient.deceasedBoolean;
998
+ }
999
+ if (!combined.deceasedDateTime && patient.deceasedDateTime) {
1000
+ combined.deceasedDateTime = patient.deceasedDateTime;
1001
+ }
1002
+ });
1003
+ combined.name = allNames;
1004
+ combined.identifier = allIdentifiers;
1005
+ combined.telecom = allTelecom;
1006
+ combined.address = allAddresses;
1007
+ combined.communication = allCommunication;
1008
+ return combined;
840
1009
  }
841
1010
  /**
842
1011
  * Renders patient names as HTML list items
843
- * @param patient - Patient resource
1012
+ * @param patient - Patient resources
844
1013
  * @returns HTML string of list items
845
1014
  */
846
1015
  static renderNames(patient) {
@@ -860,7 +1029,7 @@ var PatientTemplate = class _PatientTemplate {
860
1029
  }
861
1030
  /**
862
1031
  * Renders patient identifiers as HTML list items
863
- * @param patient - Patient resource
1032
+ * @param patient - Patient resources
864
1033
  * @returns HTML string of list items
865
1034
  */
866
1035
  static renderIdentifiers(patient) {
@@ -875,7 +1044,7 @@ var PatientTemplate = class _PatientTemplate {
875
1044
  }
876
1045
  /**
877
1046
  * Renders patient telecom information grouped by system
878
- * @param patient - Patient resource
1047
+ * @param patient - Patient resources
879
1048
  * @returns HTML string grouped by system
880
1049
  */
881
1050
  static renderTelecom(patient) {
@@ -934,7 +1103,7 @@ var PatientTemplate = class _PatientTemplate {
934
1103
  }
935
1104
  /**
936
1105
  * Renders patient addresses as HTML list items
937
- * @param patient - Patient resource
1106
+ * @param patient - Patient resources
938
1107
  * @returns HTML string of list items
939
1108
  */
940
1109
  static renderAddresses(patient) {
@@ -956,9 +1125,18 @@ var PatientTemplate = class _PatientTemplate {
956
1125
  if (address.city) {
957
1126
  addressArray.push(address.city);
958
1127
  }
1128
+ if (address.district) {
1129
+ addressArray.push(address.district);
1130
+ }
1131
+ if (address.state) {
1132
+ addressArray.push(address.state);
1133
+ }
959
1134
  if (address.country) {
960
1135
  addressArray.push(address.country);
961
1136
  }
1137
+ if (address.postalCode) {
1138
+ addressArray.push(address.postalCode);
1139
+ }
962
1140
  }
963
1141
  const addressText = addressArray.join(", ").trim();
964
1142
  if (addressText) {
@@ -969,7 +1147,7 @@ var PatientTemplate = class _PatientTemplate {
969
1147
  }
970
1148
  /**
971
1149
  * Renders patient deceased status
972
- * @param patient - Patient resource
1150
+ * @param patient - Patient resources
973
1151
  * @returns HTML string for deceased status
974
1152
  */
975
1153
  static renderDeceased(patient) {
@@ -984,7 +1162,7 @@ var PatientTemplate = class _PatientTemplate {
984
1162
  /**
985
1163
  * Renders patient communication preferences as HTML list items
986
1164
  * @param templateUtilities - Instance of TemplateUtilities for utility functions
987
- * @param patient - Patient resource
1165
+ * @param patient - Patient resources
988
1166
  * @returns HTML string of list items
989
1167
  */
990
1168
  static renderCommunication(templateUtilities, patient) {
@@ -1019,32 +1197,81 @@ var PatientTemplate = class _PatientTemplate {
1019
1197
  var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
1020
1198
  /**
1021
1199
  * Generate HTML narrative for AllergyIntolerance resources
1022
- * @param resource - FHIR Bundle containing AllergyIntolerance resources
1200
+ * @param resources - FHIR resources array containing AllergyIntolerance resources
1023
1201
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1024
1202
  * @returns HTML string for rendering
1025
1203
  */
1026
- generateNarrative(resource, timezone) {
1027
- return _AllergyIntoleranceTemplate.generateStaticNarrative(resource, timezone);
1204
+ generateNarrative(resources, timezone) {
1205
+ return _AllergyIntoleranceTemplate.generateStaticNarrative(resources, timezone);
1206
+ }
1207
+ /**
1208
+ * Generate HTML narrative for AllergyIntolerance resources using summary
1209
+ * @param resources - FHIR Composition resources
1210
+ * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1211
+ * @returns HTML string for rendering
1212
+ */
1213
+ generateSummaryNarrative(resources, timezone) {
1214
+ const templateUtilities = new TemplateUtilities(resources);
1215
+ let html = `
1216
+ <div>
1217
+ <table>
1218
+ <thead>
1219
+ <tr>
1220
+ <th>Allergen</th>
1221
+ <th>Criticality</th>
1222
+ <th>Recorded Date</th>
1223
+ </tr>
1224
+ </thead>
1225
+ <tbody>`;
1226
+ for (const resourceItem of resources) {
1227
+ for (const rowData of resourceItem.section ?? []) {
1228
+ const data = {};
1229
+ for (const columnData of rowData.section ?? []) {
1230
+ switch (columnData.title) {
1231
+ case "Allergen Name":
1232
+ data["allergen"] = columnData.text?.div ?? "";
1233
+ break;
1234
+ case "Criticality":
1235
+ data["criticality"] = columnData.text?.div ?? "";
1236
+ break;
1237
+ case "Recorded Date":
1238
+ data["recordedDate"] = columnData.text?.div ?? "";
1239
+ break;
1240
+ default:
1241
+ break;
1242
+ }
1243
+ }
1244
+ html += `
1245
+ <tr>
1246
+ <td>${data["allergen"] ?? "-"}</td>
1247
+ <td>${data["criticality"] ?? "-"}</td>
1248
+ <td>${templateUtilities.renderTime(data["recordedDate"], timezone) ?? "-"}</td>
1249
+ </tr>`;
1250
+ }
1251
+ }
1252
+ html += `
1253
+ </tbody>
1254
+ </table>
1255
+ </div>`;
1256
+ return html;
1028
1257
  }
1029
1258
  /**
1030
1259
  * Internal static implementation that actually generates the narrative
1031
- * @param resource - FHIR Bundle containing AllergyIntolerance resources
1260
+ * @param resources - FHIR resources array containing AllergyIntolerance resources
1032
1261
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1033
1262
  * @returns HTML string for rendering
1034
1263
  */
1035
- static generateStaticNarrative(resource, timezone) {
1036
- const templateUtilities = new TemplateUtilities(resource);
1264
+ static generateStaticNarrative(resources, timezone) {
1265
+ const templateUtilities = new TemplateUtilities(resources);
1037
1266
  const activeAllergies = [];
1038
1267
  const resolvedAllergies = [];
1039
- if (resource.entry && Array.isArray(resource.entry)) {
1040
- for (const entry of resource.entry) {
1041
- const allergy = entry.resource;
1042
- const isResolved = allergy.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code));
1043
- if (isResolved) {
1044
- resolvedAllergies.push(allergy);
1045
- } else {
1046
- activeAllergies.push(allergy);
1047
- }
1268
+ for (const resourceItem of resources) {
1269
+ const allergy = resourceItem;
1270
+ const isResolved = allergy.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code));
1271
+ if (isResolved) {
1272
+ resolvedAllergies.push(allergy);
1273
+ } else {
1274
+ activeAllergies.push(allergy);
1048
1275
  }
1049
1276
  }
1050
1277
  activeAllergies.sort((a, b) => {
@@ -1157,12 +1384,12 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
1157
1384
  var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1158
1385
  /**
1159
1386
  * Generate HTML narrative for Medication resources
1160
- * @param resource - FHIR Bundle containing Medication resources
1387
+ * @param resources - FHIR Medication resources
1161
1388
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1162
1389
  * @returns HTML string for rendering
1163
1390
  */
1164
- generateNarrative(resource, timezone) {
1165
- return _MedicationSummaryTemplate.generateStaticNarrative(resource, timezone);
1391
+ generateNarrative(resources, timezone) {
1392
+ return _MedicationSummaryTemplate.generateStaticNarrative(resources, timezone);
1166
1393
  }
1167
1394
  /**
1168
1395
  * Safely parse a date string and return a valid Date object or null
@@ -1227,16 +1454,16 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1227
1454
  }
1228
1455
  /**
1229
1456
  * Internal static implementation that actually generates the narrative
1230
- * @param resource - FHIR Bundle containing Medication resources
1457
+ * @param resources - FHIR Medication resources
1231
1458
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1232
1459
  * @returns HTML string for rendering
1233
1460
  */
1234
1461
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
1235
- static generateStaticNarrative(resource, timezone) {
1236
- const templateUtilities = new TemplateUtilities(resource);
1462
+ static generateStaticNarrative(resources, timezone) {
1463
+ const templateUtilities = new TemplateUtilities(resources);
1237
1464
  let html = "";
1238
- const medicationRequests = this.getMedicationRequests(templateUtilities, resource);
1239
- const medicationStatements = this.getMedicationStatements(templateUtilities, resource);
1465
+ const medicationRequests = this.getMedicationRequests(templateUtilities, resources);
1466
+ const medicationStatements = this.getMedicationStatements(templateUtilities, resources);
1240
1467
  const allActiveMedications = [];
1241
1468
  const allInactiveMedications = [];
1242
1469
  medicationRequests.forEach((mr) => {
@@ -1290,33 +1517,33 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1290
1517
  return html;
1291
1518
  }
1292
1519
  /**
1293
- * Extract MedicationRequest resources from the bundle
1520
+ * Extract MedicationRequest resources
1294
1521
  * @param templateUtilities - Instance of TemplateUtilities for utility functions
1295
- * @param resource - FHIR Bundle
1522
+ * @param resources - FHIR Medication resources
1296
1523
  * @returns Array of MedicationRequest resources
1297
1524
  */
1298
- static getMedicationRequests(templateUtilities, resource) {
1299
- if (!resource.entry || !Array.isArray(resource.entry)) {
1525
+ static getMedicationRequests(templateUtilities, resources) {
1526
+ if (resources.length === 0) {
1300
1527
  return [];
1301
1528
  }
1302
- return resource.entry.filter((entry) => entry.resource?.resourceType === "MedicationRequest").map((entry) => ({
1303
- resource: entry.resource,
1304
- extension: templateUtilities.narrativeLinkExtension(entry.resource)
1529
+ return resources.filter((entry) => entry.resourceType === "MedicationRequest").map((entry) => ({
1530
+ resource: entry,
1531
+ extension: templateUtilities.narrativeLinkExtension(entry)
1305
1532
  }));
1306
1533
  }
1307
1534
  /**
1308
- * Extract MedicationStatement resources from the bundle
1535
+ * Extract MedicationStatement resources
1309
1536
  * @param templateUtilities - Instance of TemplateUtilities for utility functions
1310
- * @param resource - FHIR Bundle
1537
+ * @param resources - FHIR Medication resources
1311
1538
  * @returns Array of MedicationStatement resources
1312
1539
  */
1313
- static getMedicationStatements(templateUtilities, resource) {
1314
- if (!resource.entry || !Array.isArray(resource.entry)) {
1540
+ static getMedicationStatements(templateUtilities, resources) {
1541
+ if (resources.length === 0) {
1315
1542
  return [];
1316
1543
  }
1317
- return resource.entry.filter((entry) => entry.resource?.resourceType === "MedicationStatement").map((entry) => ({
1318
- resource: entry.resource,
1319
- extension: templateUtilities.narrativeLinkExtension(entry.resource)
1544
+ return resources.filter((entry) => entry.resourceType === "MedicationStatement").map((entry) => ({
1545
+ resource: entry,
1546
+ extension: templateUtilities.narrativeLinkExtension(entry)
1320
1547
  }));
1321
1548
  }
1322
1549
  /**
@@ -1412,28 +1639,26 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1412
1639
  var ImmunizationsTemplate = class _ImmunizationsTemplate {
1413
1640
  /**
1414
1641
  * Generate HTML narrative for Immunization resources
1415
- * @param resource - FHIR Bundle containing Immunization resources
1642
+ * @param resources - FHIR resources array containing Immunization resources
1416
1643
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1417
1644
  * @returns HTML string for rendering
1418
1645
  */
1419
- generateNarrative(resource, timezone) {
1420
- if (resource.entry && Array.isArray(resource.entry)) {
1421
- resource.entry.sort((a, b) => {
1422
- const dateA = a.resource?.occurrenceDateTime;
1423
- const dateB = b.resource?.occurrenceDateTime;
1424
- return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1425
- });
1426
- }
1427
- return _ImmunizationsTemplate.generateStaticNarrative(resource, timezone);
1646
+ generateNarrative(resources, timezone) {
1647
+ resources.sort((a, b) => {
1648
+ const dateA = a.occurrenceDateTime;
1649
+ const dateB = b.occurrenceDateTime;
1650
+ return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1651
+ });
1652
+ return _ImmunizationsTemplate.generateStaticNarrative(resources, timezone);
1428
1653
  }
1429
1654
  /**
1430
1655
  * Internal static implementation that actually generates the narrative
1431
- * @param resource - FHIR Bundle containing Immunization resources
1656
+ * @param resources - FHIR resources array containing Immunization resources
1432
1657
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1433
1658
  * @returns HTML string for rendering
1434
1659
  */
1435
- static generateStaticNarrative(resource, timezone) {
1436
- const templateUtilities = new TemplateUtilities(resource);
1660
+ static generateStaticNarrative(resources, timezone) {
1661
+ const templateUtilities = new TemplateUtilities(resources);
1437
1662
  let html = `
1438
1663
  <table>
1439
1664
  <thead>
@@ -1448,21 +1673,20 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
1448
1673
  </tr>
1449
1674
  </thead>
1450
1675
  <tbody>`;
1451
- if (resource.entry && Array.isArray(resource.entry)) {
1452
- for (const entry of resource.entry) {
1453
- if (entry.resource?.resourceType === "Immunization") {
1454
- const imm = entry.resource;
1455
- html += `
1456
- <tr id="${templateUtilities.narrativeLinkId(imm)}">
1457
- <td>${templateUtilities.codeableConcept(imm.vaccineCode)}</td>
1458
- <td>${imm.status || ""}</td>
1459
- <td>${templateUtilities.concatDoseNumber(imm.protocolApplied)}</td>
1460
- <td>${templateUtilities.renderVaccineManufacturer(imm)}</td>
1461
- <td>${imm.lotNumber || ""}</td>
1462
- <td>${templateUtilities.renderNotes(imm.note, timezone)}</td>
1463
- <td>${templateUtilities.renderTime(imm.occurrenceDateTime, timezone)}</td>
1464
- </tr>`;
1465
- }
1676
+ const immunizations = resources.filter((resourceItem) => resourceItem.resourceType === "Immunization");
1677
+ if (immunizations.length > 0) {
1678
+ for (const resourceItem of immunizations) {
1679
+ const imm = resourceItem;
1680
+ html += `
1681
+ <tr id="${templateUtilities.narrativeLinkId(imm)}">
1682
+ <td>${templateUtilities.codeableConcept(imm.vaccineCode)}</td>
1683
+ <td>${imm.status || ""}</td>
1684
+ <td>${templateUtilities.concatDoseNumber(imm.protocolApplied)}</td>
1685
+ <td>${templateUtilities.renderVaccineManufacturer(imm)}</td>
1686
+ <td>${imm.lotNumber || ""}</td>
1687
+ <td>${templateUtilities.renderNotes(imm.note, timezone)}</td>
1688
+ <td>${templateUtilities.renderTime(imm.occurrenceDateTime, timezone)}</td>
1689
+ </tr>`;
1466
1690
  }
1467
1691
  }
1468
1692
  html += `
@@ -1476,23 +1700,23 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
1476
1700
  var ProblemListTemplate = class _ProblemListTemplate {
1477
1701
  /**
1478
1702
  * Generate HTML narrative for Problem List
1479
- * @param resource - FHIR Bundle containing Condition resources
1703
+ * @param resources - FHIR Condition resources
1480
1704
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1481
1705
  * @returns HTML string for rendering
1482
1706
  */
1483
- generateNarrative(resource, timezone) {
1484
- return _ProblemListTemplate.generateStaticNarrative(resource, timezone);
1707
+ generateNarrative(resources, timezone) {
1708
+ return _ProblemListTemplate.generateStaticNarrative(resources, timezone);
1485
1709
  }
1486
1710
  /**
1487
1711
  * Internal static implementation that actually generates the narrative
1488
- * @param resource - FHIR Bundle containing Condition resources
1712
+ * @param resources - FHIR Condition resources
1489
1713
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1490
1714
  * @returns HTML string for rendering
1491
1715
  */
1492
- static generateStaticNarrative(resource, timezone) {
1493
- const templateUtilities = new TemplateUtilities(resource);
1716
+ static generateStaticNarrative(resources, timezone) {
1717
+ const templateUtilities = new TemplateUtilities(resources);
1494
1718
  let html = ``;
1495
- const activeConditions = resource.entry?.map((entry) => entry.resource) || [];
1719
+ const activeConditions = resources.map((entry) => entry) || [];
1496
1720
  activeConditions.sort((a, b) => {
1497
1721
  const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
1498
1722
  const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
@@ -1523,26 +1747,95 @@ var ProblemListTemplate = class _ProblemListTemplate {
1523
1747
  }
1524
1748
  };
1525
1749
 
1750
+ // src/structures/ips_section_constants.ts
1751
+ var VITAL_SIGNS_SUMMARY_COMPONENT_MAP = {
1752
+ "Systolic Blood Pressure": "valueRatio.numerator.value",
1753
+ "Diastolic Blood Pressure": "valueRatio.denominator.value",
1754
+ "Default": "valueString"
1755
+ };
1756
+
1526
1757
  // src/narratives/templates/typescript/VitalSignsTemplate.ts
1527
1758
  var VitalSignsTemplate = class _VitalSignsTemplate {
1528
1759
  /**
1529
1760
  * Generate HTML narrative for Vital Signs
1530
- * @param resource - FHIR Bundle containing Observation resources
1761
+ * @param resources - FHIR Observation resources
1531
1762
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1532
1763
  * @returns HTML string for rendering
1533
1764
  */
1534
- generateNarrative(resource, timezone) {
1535
- return _VitalSignsTemplate.generateStaticNarrative(resource, timezone);
1765
+ generateNarrative(resources, timezone) {
1766
+ return _VitalSignsTemplate.generateStaticNarrative(resources, timezone);
1767
+ }
1768
+ /**
1769
+ * Generate HTML narrative for vital signs using summary
1770
+ * @param resources - FHIR Composition resources
1771
+ * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1772
+ * @returns HTML string for rendering
1773
+ */
1774
+ generateSummaryNarrative(resources, timezone) {
1775
+ const templateUtilities = new TemplateUtilities(resources);
1776
+ let html = `
1777
+ <div>
1778
+ <table>
1779
+ <thead>
1780
+ <tr>
1781
+ <th>Vital Name</th>
1782
+ <th>Result</th>
1783
+ <th>Reference Range</th>
1784
+ <th>Date</th>
1785
+ </tr>
1786
+ </thead>
1787
+ <tbody>`;
1788
+ for (const resourceItem of resources) {
1789
+ for (const rowData of resourceItem.section ?? []) {
1790
+ const data = {};
1791
+ for (const columnData of rowData.section ?? []) {
1792
+ const columnTitle = columnData.title;
1793
+ if (columnTitle) {
1794
+ if (Object.keys(VITAL_SIGNS_SUMMARY_COMPONENT_MAP).includes(
1795
+ columnTitle
1796
+ )) {
1797
+ const vitalData = {};
1798
+ for (const component of columnData.section?.[0]?.section ?? []) {
1799
+ if (component.title) {
1800
+ vitalData[component.title] = component.text?.div ?? "";
1801
+ }
1802
+ }
1803
+ const vitalValue = templateUtilities.extractObservationSummaryValue(
1804
+ vitalData,
1805
+ timezone
1806
+ );
1807
+ if (vitalValue) {
1808
+ const dataKey = VITAL_SIGNS_SUMMARY_COMPONENT_MAP[columnTitle] ?? VITAL_SIGNS_SUMMARY_COMPONENT_MAP["Default"];
1809
+ data[dataKey] = vitalValue;
1810
+ }
1811
+ }
1812
+ data[columnTitle] = columnData.text?.div ?? "";
1813
+ }
1814
+ }
1815
+ html += `
1816
+ <tr>
1817
+ <td>${data["Vital Name"] ?? "-"}</td>
1818
+ <td>${templateUtilities.extractObservationSummaryValue(data, timezone) ?? "-"}</td>
1819
+ <td>${templateUtilities.extractObservationSummaryReferenceRange(data) ?? "-"}</td>
1820
+ <td>${templateUtilities.extractObservationSummaryEffectiveTime(data, timezone) ?? "-"}</td>
1821
+ </tr>`;
1822
+ }
1823
+ }
1824
+ html += `
1825
+ </tbody>
1826
+ </table>
1827
+ </div>`;
1828
+ return html;
1536
1829
  }
1537
1830
  /**
1538
1831
  * Internal static implementation that actually generates the narrative
1539
- * @param resource - FHIR Bundle containing Observation resources
1832
+ * @param resources - FHIR Observation resources
1540
1833
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1541
1834
  * @returns HTML string for rendering
1542
1835
  */
1543
- static generateStaticNarrative(resource, timezone) {
1544
- const templateUtilities = new TemplateUtilities(resource);
1545
- const observations = resource.entry?.map((entry) => entry.resource) || [];
1836
+ static generateStaticNarrative(resources, timezone) {
1837
+ const templateUtilities = new TemplateUtilities(resources);
1838
+ const observations = resources.map((entry) => entry) || [];
1546
1839
  observations.sort((a, b) => {
1547
1840
  const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
1548
1841
  const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
@@ -1552,7 +1845,7 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
1552
1845
  <table>
1553
1846
  <thead>
1554
1847
  <tr>
1555
- <th>Code</th>
1848
+ <th>Vital Name</th>
1556
1849
  <th>Result</th>
1557
1850
  <th>Unit</th>
1558
1851
  <th>Interpretation</th>
@@ -1585,28 +1878,21 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
1585
1878
  var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
1586
1879
  /**
1587
1880
  * Generate HTML narrative for Medical Device resources
1588
- * @param resource - FHIR Bundle containing Device resources
1881
+ * @param resources - FHIR resources array containing Device resources
1589
1882
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1590
1883
  * @returns HTML string for rendering
1591
1884
  */
1592
- generateNarrative(resource, timezone) {
1593
- if (resource.entry && Array.isArray(resource.entry)) {
1594
- resource.entry.sort((a, b) => {
1595
- const dateA = a.resource?.recordedOn;
1596
- const dateB = b.resource?.recordedOn;
1597
- return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1598
- });
1599
- }
1600
- return _MedicalDevicesTemplate.generateStaticNarrative(resource, timezone);
1885
+ generateNarrative(resources, timezone) {
1886
+ return _MedicalDevicesTemplate.generateStaticNarrative(resources, timezone);
1601
1887
  }
1602
1888
  /**
1603
1889
  * Internal static implementation that actually generates the narrative
1604
- * @param resource - FHIR Bundle containing Device resources
1890
+ * @param resources - FHIR resources array containing Device resources
1605
1891
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1606
1892
  * @returns HTML string for rendering
1607
1893
  */
1608
- static generateStaticNarrative(resource, timezone) {
1609
- const templateUtilities = new TemplateUtilities(resource);
1894
+ static generateStaticNarrative(resources, timezone) {
1895
+ const templateUtilities = new TemplateUtilities(resources);
1610
1896
  let html = `
1611
1897
  <table>
1612
1898
  <thead>
@@ -1618,19 +1904,19 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
1618
1904
  </tr>
1619
1905
  </thead>
1620
1906
  <tbody>`;
1621
- if (resource.entry && Array.isArray(resource.entry)) {
1622
- for (const entry of resource.entry) {
1623
- if (entry.resource?.resourceType === "DeviceUseStatement") {
1624
- const dus = entry.resource;
1625
- html += `
1626
- <tr id="${templateUtilities.narrativeLinkId(dus)}">
1627
- <td>${templateUtilities.renderDevice(dus.device)}</td>
1628
- <td>${dus.status || ""}</td>
1629
- <td>${templateUtilities.renderNotes(dus.note, timezone)}</td>
1630
- <td>${templateUtilities.renderRecorded(dus.recordedOn, timezone)}</td>
1631
- </tr>`;
1632
- }
1633
- }
1907
+ const deviceStatements = resources.filter((resourceItem) => resourceItem.resourceType === "DeviceUseStatement").map((resourceItem) => resourceItem).sort((a, b) => {
1908
+ const dateA = a.recordedOn;
1909
+ const dateB = b.recordedOn;
1910
+ return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1911
+ });
1912
+ for (const dus of deviceStatements) {
1913
+ html += `
1914
+ <tr id="${templateUtilities.narrativeLinkId(dus)}">
1915
+ <td>${templateUtilities.renderDevice(dus.device)}</td>
1916
+ <td>${dus.status || ""}</td>
1917
+ <td>${templateUtilities.renderNotes(dus.note, timezone)}</td>
1918
+ <td>${templateUtilities.renderRecorded(dus.recordedOn, timezone)}</td>
1919
+ </tr>`;
1634
1920
  }
1635
1921
  html += `
1636
1922
  </tbody>
@@ -1643,23 +1929,23 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
1643
1929
  var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1644
1930
  /**
1645
1931
  * Generate HTML narrative for Diagnostic Results
1646
- * @param resource - FHIR Bundle containing Observation and DiagnosticReport resources
1932
+ * @param resources - FHIR resources array containing Observation and DiagnosticReport resources
1647
1933
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1648
1934
  * @returns HTML string for rendering
1649
1935
  */
1650
- generateNarrative(resource, timezone) {
1651
- return _DiagnosticResultsTemplate.generateStaticNarrative(resource, timezone);
1936
+ generateNarrative(resources, timezone) {
1937
+ return _DiagnosticResultsTemplate.generateStaticNarrative(resources, timezone);
1652
1938
  }
1653
1939
  /**
1654
1940
  * Internal static implementation that actually generates the narrative
1655
- * @param resource - FHIR Bundle containing Observation and DiagnosticReport resources
1941
+ * @param resources - FHIR resources array containing Observation and DiagnosticReport resources
1656
1942
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1657
1943
  * @returns HTML string for rendering
1658
1944
  */
1659
- static generateStaticNarrative(resource, timezone) {
1660
- const templateUtilities = new TemplateUtilities(resource);
1945
+ static generateStaticNarrative(resources, timezone) {
1946
+ const templateUtilities = new TemplateUtilities(resources);
1661
1947
  let html = "";
1662
- const observations = this.getObservations(resource);
1948
+ const observations = this.getObservations(resources);
1663
1949
  if (observations.length > 0) {
1664
1950
  observations.sort((a, b) => {
1665
1951
  const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
@@ -1668,7 +1954,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1668
1954
  });
1669
1955
  html += this.renderObservations(templateUtilities, observations, timezone);
1670
1956
  }
1671
- const diagnosticReports = this.getDiagnosticReports(resource);
1957
+ const diagnosticReports = this.getDiagnosticReports(resources);
1672
1958
  if (diagnosticReports.length > 0) {
1673
1959
  diagnosticReports.sort((a, b) => {
1674
1960
  const dateA = a.issued;
@@ -1680,26 +1966,20 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1680
1966
  return html;
1681
1967
  }
1682
1968
  /**
1683
- * Extract Observation resources from the bundle
1684
- * @param resource - FHIR Bundle
1685
- * @returns Array of Observation resources
1686
- */
1687
- static getObservations(resource) {
1688
- if (!resource.entry || !Array.isArray(resource.entry)) {
1689
- return [];
1690
- }
1691
- return resource.entry.filter((entry) => entry.resource?.resourceType === "Observation").map((entry) => entry.resource);
1969
+ * Get all Observation resources from the resource array
1970
+ * @param resources - FHIR resources array
1971
+ * @returns Array of Observation resources
1972
+ */
1973
+ static getObservations(resources) {
1974
+ return resources.filter((resourceItem) => resourceItem.resourceType === "Observation").map((resourceItem) => resourceItem);
1692
1975
  }
1693
1976
  /**
1694
- * Extract DiagnosticReport resources from the bundle
1695
- * @param resource - FHIR Bundle
1977
+ * Get all DiagnosticReport resources from the resource array
1978
+ * @param resources - FHIR resources array
1696
1979
  * @returns Array of DiagnosticReport resources
1697
1980
  */
1698
- static getDiagnosticReports(resource) {
1699
- if (!resource.entry || !Array.isArray(resource.entry)) {
1700
- return [];
1701
- }
1702
- return resource.entry.filter((entry) => entry.resource?.resourceType === "DiagnosticReport").map((entry) => entry.resource);
1981
+ static getDiagnosticReports(resources) {
1982
+ return resources.filter((resourceItem) => resourceItem.resourceType === "DiagnosticReport").map((resourceItem) => resourceItem);
1703
1983
  }
1704
1984
  /**
1705
1985
  * Render HTML table for Observation resources
@@ -1785,26 +2065,26 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1785
2065
  var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
1786
2066
  /**
1787
2067
  * Generate HTML narrative for Procedure resources
1788
- * @param resource - FHIR Bundle containing Procedure resources
2068
+ * @param resources - FHIR Procedure resources
1789
2069
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1790
2070
  * @returns HTML string for rendering
1791
2071
  */
1792
- generateNarrative(resource, timezone) {
1793
- resource.entry?.sort((a, b) => {
1794
- const dateA = a.resource.performedDateTime || a.resource.performedPeriod?.start;
1795
- const dateB = b.resource.performedDateTime || b.resource.performedPeriod?.start;
2072
+ generateNarrative(resources, timezone) {
2073
+ resources.sort((a, b) => {
2074
+ const dateA = a.performedDateTime || a.performedPeriod?.start;
2075
+ const dateB = b.performedDateTime || b.performedPeriod?.start;
1796
2076
  return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1797
2077
  });
1798
- return _HistoryOfProceduresTemplate.generateStaticNarrative(resource, timezone);
2078
+ return _HistoryOfProceduresTemplate.generateStaticNarrative(resources, timezone);
1799
2079
  }
1800
2080
  /**
1801
2081
  * Internal static implementation that actually generates the narrative
1802
- * @param resource - FHIR Bundle containing Procedure resources
2082
+ * @param resources - FHIR Procedure resources
1803
2083
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1804
2084
  * @returns HTML string for rendering
1805
2085
  */
1806
- static generateStaticNarrative(resource, timezone) {
1807
- const templateUtilities = new TemplateUtilities(resource);
2086
+ static generateStaticNarrative(resources, timezone) {
2087
+ const templateUtilities = new TemplateUtilities(resources);
1808
2088
  let html = `
1809
2089
  <table>
1810
2090
  <thead>
@@ -1815,16 +2095,14 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
1815
2095
  </tr>
1816
2096
  </thead>
1817
2097
  <tbody>`;
1818
- if (resource.entry && Array.isArray(resource.entry)) {
1819
- for (const entry of resource.entry) {
1820
- const proc = entry.resource;
1821
- html += `
1822
- <tr id="${templateUtilities.narrativeLinkId(proc)}">
1823
- <td>${templateUtilities.codeableConcept(proc.code, "display")}</td>
1824
- <td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
1825
- <td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
1826
- </tr>`;
1827
- }
2098
+ for (const resourceItem of resources) {
2099
+ const proc = resourceItem;
2100
+ html += `
2101
+ <tr id="${templateUtilities.narrativeLinkId(proc)}">
2102
+ <td>${templateUtilities.codeableConcept(proc.code, "display")}</td>
2103
+ <td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
2104
+ <td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
2105
+ </tr>`;
1828
2106
  }
1829
2107
  html += `
1830
2108
  </tbody>
@@ -1837,22 +2115,22 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
1837
2115
  var SocialHistoryTemplate = class _SocialHistoryTemplate {
1838
2116
  /**
1839
2117
  * Generate HTML narrative for Social History
1840
- * @param resource - FHIR Bundle containing Observation resources
2118
+ * @param resources - FHIR Observation resources
1841
2119
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1842
2120
  * @returns HTML string for rendering
1843
2121
  */
1844
- generateNarrative(resource, timezone) {
1845
- return _SocialHistoryTemplate.generateStaticNarrative(resource, timezone);
2122
+ generateNarrative(resources, timezone) {
2123
+ return _SocialHistoryTemplate.generateStaticNarrative(resources, timezone);
1846
2124
  }
1847
2125
  /**
1848
2126
  * Internal static implementation that actually generates the narrative
1849
- * @param resource - FHIR Bundle containing Observation resources
2127
+ * @param resources - FHIR Observation resources
1850
2128
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1851
2129
  * @returns HTML string for rendering
1852
2130
  */
1853
- static generateStaticNarrative(resource, timezone) {
1854
- const templateUtilities = new TemplateUtilities(resource);
1855
- const observations = resource.entry?.map((entry) => entry.resource) || [];
2131
+ static generateStaticNarrative(resources, timezone) {
2132
+ const templateUtilities = new TemplateUtilities(resources);
2133
+ const observations = resources.map((entry) => entry) || [];
1856
2134
  observations.sort((a, b) => {
1857
2135
  const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
1858
2136
  const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
@@ -1891,14 +2169,14 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
1891
2169
  var PastHistoryOfIllnessTemplate = class {
1892
2170
  /**
1893
2171
  * Generate HTML narrative for Past History of Illnesses
1894
- * @param resource - FHIR Bundle containing Condition resources
2172
+ * @param resources - FHIR Condition resources
1895
2173
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1896
2174
  * @returns HTML string for rendering
1897
2175
  */
1898
- generateNarrative(resource, timezone) {
1899
- const templateUtilities = new TemplateUtilities(resource);
2176
+ generateNarrative(resources, timezone) {
2177
+ const templateUtilities = new TemplateUtilities(resources);
1900
2178
  let html = ``;
1901
- const resolvedConditions = resource.entry?.map((entry) => entry.resource) || [];
2179
+ const resolvedConditions = resources.map((entry) => entry) || [];
1902
2180
  resolvedConditions.sort((a, b) => {
1903
2181
  const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
1904
2182
  const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
@@ -1935,13 +2213,13 @@ var PastHistoryOfIllnessTemplate = class {
1935
2213
  var PlanOfCareTemplate = class {
1936
2214
  /**
1937
2215
  * Generate HTML narrative for Plan of Care
1938
- * @param resource - FHIR Bundle containing CarePlan resources
2216
+ * @param resources - FHIR CarePlan resources
1939
2217
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1940
2218
  * @returns HTML string for rendering
1941
2219
  */
1942
- generateNarrative(resource, timezone) {
1943
- const templateUtilities = new TemplateUtilities(resource);
1944
- const carePlans = resource.entry?.map((entry) => entry.resource) || [];
2220
+ generateNarrative(resources, timezone) {
2221
+ const templateUtilities = new TemplateUtilities(resources);
2222
+ const carePlans = resources.map((entry) => entry) || [];
1945
2223
  carePlans.sort((a, b) => {
1946
2224
  const endA = a.period?.end ? new Date(a.period?.end).getTime() : 0;
1947
2225
  const endB = b.period?.end ? new Date(b.period?.end).getTime() : 0;
@@ -1980,37 +2258,35 @@ var PlanOfCareTemplate = class {
1980
2258
  var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
1981
2259
  /**
1982
2260
  * Generate HTML narrative for Functional Status
1983
- * @param resource - FHIR Bundle containing Observation resources
2261
+ * @param resources - FHIR resources array containing Observation resources
1984
2262
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1985
2263
  * @returns HTML string for rendering
1986
2264
  */
1987
- generateNarrative(resource, timezone) {
1988
- return _FunctionalStatusTemplate.generateStaticNarrative(resource, timezone);
2265
+ generateNarrative(resources, timezone) {
2266
+ return _FunctionalStatusTemplate.generateStaticNarrative(resources, timezone);
1989
2267
  }
1990
2268
  /**
1991
2269
  * Internal static implementation that actually generates the narrative
1992
- * @param resource - FHIR Bundle containing Observation resources
2270
+ * @param resources - FHIR resources array containing Observation resources
1993
2271
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1994
2272
  * @returns HTML string for rendering
1995
2273
  */
1996
- static generateStaticNarrative(resource, timezone) {
1997
- const templateUtilities = new TemplateUtilities(resource);
2274
+ static generateStaticNarrative(resources, timezone) {
2275
+ const templateUtilities = new TemplateUtilities(resources);
1998
2276
  let html = ``;
1999
2277
  const activeConditions = [];
2000
2278
  const clinicalImpressions = [];
2001
- if (resource.entry && Array.isArray(resource.entry)) {
2002
- for (const entry of resource.entry) {
2003
- if (entry.resource?.resourceType === "Condition") {
2004
- const cond = entry.resource;
2005
- const isResolved = cond.clinicalStatus?.coding?.some(
2006
- (c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved")
2007
- );
2008
- if (!isResolved) {
2009
- activeConditions.push(cond);
2010
- }
2011
- } else if (entry.resource?.resourceType === "ClinicalImpression") {
2012
- clinicalImpressions.push(entry.resource);
2279
+ for (const resourceItem of resources) {
2280
+ if (resourceItem.resourceType === "Condition") {
2281
+ const cond = resourceItem;
2282
+ const isResolved = cond.clinicalStatus?.coding?.some(
2283
+ (c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved")
2284
+ );
2285
+ if (!isResolved) {
2286
+ activeConditions.push(cond);
2013
2287
  }
2288
+ } else if (resourceItem.resourceType === "ClinicalImpression") {
2289
+ clinicalImpressions.push(resourceItem);
2014
2290
  }
2015
2291
  }
2016
2292
  activeConditions.sort((a, b) => {
@@ -2107,22 +2383,22 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
2107
2383
  var PregnancyTemplate = class _PregnancyTemplate {
2108
2384
  /**
2109
2385
  * Generate HTML narrative for Pregnancy
2110
- * @param resource - FHIR Bundle containing Observation resources
2386
+ * @param resources - FHIR Observation resources
2111
2387
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2112
2388
  * @returns HTML string for rendering
2113
2389
  */
2114
- generateNarrative(resource, timezone) {
2115
- return _PregnancyTemplate.generateStaticNarrative(resource, timezone);
2390
+ generateNarrative(resources, timezone) {
2391
+ return _PregnancyTemplate.generateStaticNarrative(resources, timezone);
2116
2392
  }
2117
2393
  /**
2118
2394
  * Internal static implementation that actually generates the narrative
2119
- * @param resource - FHIR Bundle containing Observation resources
2395
+ * @param resources - FHIR Observation resources
2120
2396
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2121
2397
  * @returns HTML string for rendering
2122
2398
  */
2123
- static generateStaticNarrative(resource, timezone) {
2124
- const templateUtilities = new TemplateUtilities(resource);
2125
- const observations = resource.entry?.map((entry) => entry.resource) || [];
2399
+ static generateStaticNarrative(resources, timezone) {
2400
+ const templateUtilities = new TemplateUtilities(resources);
2401
+ const observations = resources.map((entry) => entry) || [];
2126
2402
  observations.sort((a, b) => {
2127
2403
  const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
2128
2404
  const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
@@ -2138,8 +2414,8 @@ var PregnancyTemplate = class _PregnancyTemplate {
2138
2414
  </tr>
2139
2415
  </thead>
2140
2416
  <tbody>`;
2141
- for (const resource2 of observations) {
2142
- const obs = resource2;
2417
+ for (const resource of observations) {
2418
+ const obs = resource;
2143
2419
  html += `
2144
2420
  <tr id="${templateUtilities.narrativeLinkId(obs)}">
2145
2421
  <td>${templateUtilities.extractPregnancyStatus(obs)}</td>
@@ -2158,29 +2434,27 @@ var PregnancyTemplate = class _PregnancyTemplate {
2158
2434
  var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
2159
2435
  /**
2160
2436
  * Generate HTML narrative for Advance Directives
2161
- * @param resource - FHIR Bundle containing Advance Directive resources
2437
+ * @param resources - FHIR Consent resources
2162
2438
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2163
2439
  * @returns HTML string for rendering
2164
2440
  */
2165
- generateNarrative(resource, timezone) {
2166
- if (resource.entry && Array.isArray(resource.entry)) {
2167
- resource.entry.sort((a, b) => {
2168
- const dateA = new Date(a.resource.dateTime || 0);
2169
- const dateB = new Date(b.resource.dateTime || 0);
2170
- return dateB.getTime() - dateA.getTime();
2171
- });
2172
- }
2173
- return _AdvanceDirectivesTemplate.generateStaticNarrative(resource, timezone);
2441
+ generateNarrative(resources, timezone) {
2442
+ resources.sort((a, b) => {
2443
+ const dateA = new Date(a.dateTime || 0);
2444
+ const dateB = new Date(b.dateTime || 0);
2445
+ return dateB.getTime() - dateA.getTime();
2446
+ });
2447
+ return _AdvanceDirectivesTemplate.generateStaticNarrative(resources, timezone);
2174
2448
  }
2175
2449
  /**
2176
2450
  * Internal static implementation that actually generates the narrative
2177
- * @param resource - FHIR Bundle containing Advance Directive resources
2451
+ * @param resources - FHIR Consent resources
2178
2452
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2179
2453
  * @returns HTML string for rendering
2180
2454
  */
2181
2455
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
2182
- static generateStaticNarrative(resource, timezone) {
2183
- const templateUtilities = new TemplateUtilities(resource);
2456
+ static generateStaticNarrative(resources, timezone) {
2457
+ const templateUtilities = new TemplateUtilities(resources);
2184
2458
  let html = `
2185
2459
  <table>
2186
2460
  <thead>
@@ -2192,17 +2466,15 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
2192
2466
  </tr>
2193
2467
  </thead>
2194
2468
  <tbody>`;
2195
- if (resource.entry && Array.isArray(resource.entry)) {
2196
- for (const entry of resource.entry) {
2197
- const consent = entry.resource;
2198
- html += `
2199
- <tr id="${templateUtilities.narrativeLinkId(consent)}">
2200
- <td>${templateUtilities.codeableConcept(consent.scope, "display")}</td>
2201
- <td>${consent.status || ""}</td>
2202
- <td>${consent.provision?.action ? templateUtilities.concatCodeableConcept(consent.provision.action) : ""}</td>
2203
- <td>${consent.dateTime || ""}</td>
2204
- </tr>`;
2205
- }
2469
+ for (const resourceItem of resources) {
2470
+ const consent = resourceItem;
2471
+ html += `
2472
+ <tr id="${templateUtilities.narrativeLinkId(consent)}">
2473
+ <td>${templateUtilities.codeableConcept(consent.scope, "display")}</td>
2474
+ <td>${consent.status || ""}</td>
2475
+ <td>${consent.provision?.action ? templateUtilities.concatCodeableConcept(consent.provision.action) : ""}</td>
2476
+ <td>${consent.dateTime || ""}</td>
2477
+ </tr>`;
2206
2478
  }
2207
2479
  html += `
2208
2480
  </tbody>
@@ -2216,16 +2488,20 @@ var TypeScriptTemplateMapper = class {
2216
2488
  /**
2217
2489
  * Generates HTML narrative for a specific IPS section
2218
2490
  * @param section - The IPS section
2219
- * @param resource - FHIR Bundle containing resources
2491
+ * @param resources - FHIR resources
2220
2492
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2493
+ * @param useSectionSummary - Whether to use the section summary for narrative generation
2221
2494
  * @returns HTML string for rendering
2222
2495
  */
2223
- static generateNarrative(section, resource, timezone) {
2496
+ static generateNarrative(section, resources, timezone, useSectionSummary = false) {
2224
2497
  const templateClass = this.sectionToTemplate[section];
2225
2498
  if (!templateClass) {
2226
2499
  throw new Error(`No template found for section: ${section}`);
2227
2500
  }
2228
- return templateClass.generateNarrative(resource, timezone);
2501
+ return useSectionSummary ? templateClass.generateSummaryNarrative(
2502
+ resources,
2503
+ timezone
2504
+ ) : templateClass.generateNarrative(resources, timezone);
2229
2505
  }
2230
2506
  };
2231
2507
  // Map of section types to their template classes
@@ -2281,21 +2557,15 @@ var NarrativeGenerator = class {
2281
2557
  * @param section - IPS section type
2282
2558
  * @param resources - Array of domain resources
2283
2559
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2560
+ * @param useSectionSummary - Whether to use section summary for narrative generation (default: false)
2284
2561
  * @returns Generated HTML content or undefined if no resources
2285
2562
  */
2286
- static async generateNarrativeContentAsync(section, resources, timezone) {
2563
+ static async generateNarrativeContentAsync(section, resources, timezone, useSectionSummary = false) {
2287
2564
  if (!resources || resources.length === 0) {
2288
2565
  return void 0;
2289
2566
  }
2290
2567
  try {
2291
- const bundle = {
2292
- resourceType: "Bundle",
2293
- type: "collection",
2294
- entry: resources.map((resource) => ({
2295
- resource
2296
- }))
2297
- };
2298
- const content = TypeScriptTemplateMapper.generateNarrative(section, bundle, timezone);
2568
+ const content = TypeScriptTemplateMapper.generateNarrative(section, resources, timezone, useSectionSummary);
2299
2569
  if (!content) {
2300
2570
  return void 0;
2301
2571
  }
@@ -2346,10 +2616,11 @@ var NarrativeGenerator = class {
2346
2616
  * @param resources - Array of domain resources
2347
2617
  * @param timezone - Optional timezone to use for date formatting
2348
2618
  * @param minify - Whether to minify the HTML content (default: true)
2619
+ * @param useSectionSummary - Whether to use section summary for narrative generation (default: false)
2349
2620
  * @returns Promise that resolves to a FHIR Narrative object or undefined if no resources
2350
2621
  */
2351
- static async generateNarrativeAsync(section, resources, timezone, minify = true) {
2352
- const content = await this.generateNarrativeContentAsync(section, resources, timezone);
2622
+ static async generateNarrativeAsync(section, resources, timezone, minify = true, useSectionSummary = false) {
2623
+ const content = await this.generateNarrativeContentAsync(section, resources, timezone, useSectionSummary);
2353
2624
  if (!content) {
2354
2625
  return void 0;
2355
2626
  }
@@ -2380,22 +2651,51 @@ var ComprehensiveIPSCompositionBuilder = class {
2380
2651
  /**
2381
2652
  * sets the patient resource for the IPS Composition.
2382
2653
  * This is not needed if you are calling read_bundle, but can be used to set the patient resource directly.
2383
- * @param patient - FHIR Patient resource to set
2654
+ * @param patients - FHIR Patient resource to set
2384
2655
  */
2385
- setPatient(patient) {
2386
- if (!patient || patient.resourceType !== "Patient") {
2656
+ setPatient(patients) {
2657
+ if (!Array.isArray(patients)) {
2658
+ patients = [patients];
2659
+ }
2660
+ if (patients.length === 0 || !patients.every((patient) => patient.resourceType === "Patient")) {
2387
2661
  throw new Error("Invalid Patient resource");
2388
2662
  }
2389
- this.patient = patient;
2663
+ this.patients = patients;
2390
2664
  return this;
2391
2665
  }
2392
2666
  /**
2393
2667
  * Adds a section to the composition with async HTML minification
2668
+ * @param narrative - Narrative content for the section
2669
+ * @param sectionType - IPS section type
2670
+ * @param validResources - Array of domain resources
2671
+ */
2672
+ addSectionAsync(narrative, sectionType, validResources) {
2673
+ const sectionEntry = {
2674
+ title: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType,
2675
+ code: {
2676
+ coding: [{
2677
+ system: "http://loinc.org",
2678
+ code: IPS_SECTION_LOINC_CODES[sectionType],
2679
+ display: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
2680
+ }],
2681
+ text: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
2682
+ },
2683
+ text: narrative,
2684
+ entry: validResources.map((resource) => ({
2685
+ reference: `${resource.resourceType}/${resource.id}`,
2686
+ display: resource.resourceType
2687
+ }))
2688
+ };
2689
+ this.sections.push(sectionEntry);
2690
+ return this;
2691
+ }
2692
+ /**
2693
+ * Make and adds a section to the composition with async HTML minification
2394
2694
  * @param sectionType - IPS section type
2395
2695
  * @param validResources - Array of domain resources
2396
2696
  * @param timezone - Optional timezone to use for date formatting
2397
2697
  */
2398
- async addSectionAsync(sectionType, validResources, timezone) {
2698
+ async makeSectionAsync(sectionType, validResources, timezone) {
2399
2699
  for (const resource of validResources) {
2400
2700
  this.resources.add(resource);
2401
2701
  }
@@ -2415,51 +2715,68 @@ var ComprehensiveIPSCompositionBuilder = class {
2415
2715
  } else {
2416
2716
  return this;
2417
2717
  }
2418
- const sectionEntry = {
2419
- title: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType,
2420
- code: {
2421
- coding: [{
2422
- system: "http://loinc.org",
2423
- code: IPS_SECTION_LOINC_CODES[sectionType],
2424
- display: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
2425
- }],
2426
- text: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
2427
- },
2428
- text: narrative,
2429
- entry: validResources.map((resource) => ({
2430
- reference: `${resource.resourceType}/${resource.id}`,
2431
- display: resource.resourceType
2432
- }))
2433
- };
2434
- this.sections.push(sectionEntry);
2718
+ this.addSectionAsync(narrative, sectionType, validResources);
2435
2719
  }
2436
2720
  return this;
2437
2721
  }
2722
+ async makeSectionFromSummaryAsync(sectionType, summaryCompositions, resources, timezone) {
2723
+ const sectionResources = [];
2724
+ for (const summaryComposition of summaryCompositions) {
2725
+ const resourceEntries = summaryComposition?.section?.flatMap((sec) => sec.entry || []) ?? [];
2726
+ resources.forEach((resource) => {
2727
+ if (resourceEntries?.some((entry) => entry.reference === `${resource.resourceType}/${resource.id}`)) {
2728
+ this.resources.add(resource);
2729
+ sectionResources.push(resource);
2730
+ }
2731
+ });
2732
+ }
2733
+ const narrative = await NarrativeGenerator.generateNarrativeAsync(
2734
+ sectionType,
2735
+ summaryCompositions,
2736
+ timezone,
2737
+ true,
2738
+ true
2739
+ );
2740
+ this.addSectionAsync(narrative, sectionType, sectionResources);
2741
+ return this;
2742
+ }
2438
2743
  /**
2439
2744
  * Reads a FHIR Bundle and extracts resources for each section defined in IPSSections.
2440
2745
  * @param bundle - FHIR Bundle containing resources
2441
2746
  * @param timezone - Optional timezone to use for date formatting
2747
+ * @param useSummaryCompositions - Whether to use summary compositions (default: false)
2442
2748
  */
2443
- async readBundleAsync(bundle, timezone) {
2749
+ async readBundleAsync(bundle, timezone, useSummaryCompositions = false) {
2444
2750
  if (!bundle.entry) {
2445
2751
  return this;
2446
2752
  }
2447
- const patientEntry = bundle.entry.find((e) => e.resource?.resourceType === "Patient");
2448
- if (!patientEntry || !patientEntry.resource) {
2753
+ const patientEntries = [];
2754
+ const resources = [];
2755
+ bundle.entry.forEach((e) => {
2756
+ if (e.resource?.resourceType === "Patient") {
2757
+ patientEntries.push(e.resource);
2758
+ this.resources.add(e.resource);
2759
+ } else if (e.resource) {
2760
+ resources.push(e.resource);
2761
+ }
2762
+ });
2763
+ if (patientEntries.length === 0) {
2449
2764
  throw new Error("Patient resource not found in the bundle");
2450
2765
  }
2451
- this.patient = patientEntry.resource;
2452
- const resources = bundle.entry.map((e) => e.resource);
2766
+ this.patients = patientEntries;
2453
2767
  for (const sectionType of Object.values(IPSSections)) {
2454
- const resourceTypesForSection = IPSSectionResourceHelper.getResourceTypesForSection(sectionType);
2455
- const customFilter = IPSSectionResourceHelper.getResourceFilterForSection(sectionType);
2456
- let sectionResources = resources.filter(
2457
- (r) => r && typeof r.resourceType === "string" && resourceTypesForSection.includes(r.resourceType)
2458
- );
2459
- if (customFilter) {
2460
- sectionResources = sectionResources.filter((resource) => resource && customFilter(resource));
2768
+ if (sectionType === "Patient" /* PATIENT */) {
2769
+ continue;
2770
+ }
2771
+ const summaryCompositionFilter = useSummaryCompositions ? IPSSectionResourceHelper.getSummaryCompositionFilterForSection(sectionType) : void 0;
2772
+ const sectionSummary = summaryCompositionFilter ? resources.filter((resource) => summaryCompositionFilter(resource)) : void 0;
2773
+ if (sectionSummary) {
2774
+ await this.makeSectionFromSummaryAsync(sectionType, sectionSummary, resources, timezone);
2775
+ } else {
2776
+ const sectionFilter = IPSSectionResourceHelper.getResourceFilterForSection(sectionType);
2777
+ const sectionResources = resources.filter((resource) => sectionFilter(resource));
2778
+ await this.makeSectionAsync(sectionType, sectionResources, timezone);
2461
2779
  }
2462
- await this.addSectionAsync(sectionType, sectionResources, timezone);
2463
2780
  }
2464
2781
  return this;
2465
2782
  }
@@ -2469,16 +2786,18 @@ var ComprehensiveIPSCompositionBuilder = class {
2469
2786
  * @param authorOrganizationName - Name of the authoring organization
2470
2787
  * @param baseUrl - Base URL for the FHIR server (e.g., 'https://example.com/fhir')
2471
2788
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2789
+ * @param patientId - Optional patient ID to use as primary patient for composition reference
2472
2790
  */
2473
- async buildBundleAsync(authorOrganizationId, authorOrganizationName, baseUrl, timezone) {
2791
+ async buildBundleAsync(authorOrganizationId, authorOrganizationName, baseUrl, timezone, patientId) {
2474
2792
  if (baseUrl.endsWith("/")) {
2475
2793
  baseUrl = baseUrl.slice(0, -1);
2476
2794
  }
2477
- if (!this.patient) {
2795
+ if (!this.patients) {
2478
2796
  throw new Error("Patient resource must be set before building the bundle");
2479
2797
  }
2798
+ const primaryPatientId = patientId ?? this.patients[0].id;
2480
2799
  const composition = {
2481
- id: `Composition-${this.patient.id}`,
2800
+ id: `Composition-${primaryPatientId}`,
2482
2801
  resourceType: "Composition",
2483
2802
  status: "final",
2484
2803
  type: {
@@ -2489,7 +2808,7 @@ var ComprehensiveIPSCompositionBuilder = class {
2489
2808
  }]
2490
2809
  },
2491
2810
  subject: {
2492
- reference: `Patient/${this.patient.id}`
2811
+ reference: `Patient/${primaryPatientId}`
2493
2812
  },
2494
2813
  author: [{
2495
2814
  reference: `Organization/${authorOrganizationId}`,
@@ -2501,7 +2820,7 @@ var ComprehensiveIPSCompositionBuilder = class {
2501
2820
  section: this.sections,
2502
2821
  text: await NarrativeGenerator.generateNarrativeAsync(
2503
2822
  "Patient" /* PATIENT */,
2504
- [this.patient],
2823
+ this.patients,
2505
2824
  timezone,
2506
2825
  true
2507
2826
  )
@@ -2520,9 +2839,11 @@ var ComprehensiveIPSCompositionBuilder = class {
2520
2839
  fullUrl: `${baseUrl}/Composition/${composition.id}`,
2521
2840
  resource: composition
2522
2841
  });
2523
- bundle.entry?.push({
2524
- fullUrl: `${baseUrl}/Patient/${this.patient.id}`,
2525
- resource: this.patient
2842
+ this.patients.forEach((patient) => {
2843
+ bundle.entry?.push({
2844
+ fullUrl: `${baseUrl}/Patient/${patient.id}`,
2845
+ resource: patient
2846
+ });
2526
2847
  });
2527
2848
  this.resources.forEach((resource) => {
2528
2849
  if (resource.resourceType !== "Patient") {