@imranq2/fhirpatientsummary 1.0.16 → 1.0.17

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.cjs CHANGED
@@ -110,33 +110,21 @@ var BLOOD_PRESSURE_LOINC_CODES = {
110
110
  };
111
111
 
112
112
  // src/structures/ips_section_resource_map.ts
113
- var IPSSectionResourceMap = {
114
- ["Patient" /* PATIENT */]: ["Patient"],
115
- ["AllergyIntoleranceSection" /* ALLERGIES */]: ["AllergyIntolerance"],
116
- ["MedicationSummarySection" /* MEDICATIONS */]: ["MedicationRequest", "MedicationStatement", "Medication"],
117
- // Medication resource is needed for identifying name of medication
118
- ["ProblemSection" /* PROBLEMS */]: ["Condition"],
119
- ["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization", "Organization"],
120
- // Immunization can include Organization as a related resource
121
- ["VitalSignsSection" /* VITAL_SIGNS */]: ["Observation"],
122
- ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["DeviceUseStatement", "Device"],
123
- // Device resource is used for medical devices name
124
- ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: ["DiagnosticReport", "Observation"],
125
- ["HistoryOfProceduresSection" /* PROCEDURES */]: ["Procedure"],
126
- ["SocialHistorySection" /* SOCIAL_HISTORY */]: ["Observation"],
127
- ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: ["Observation"],
128
- ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: ["Condition", "ClinicalImpression"],
129
- ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: ["Condition"],
130
- ["PlanOfCareSection" /* CARE_PLAN */]: ["CarePlan"],
131
- ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: ["Consent"]
132
- };
133
113
  var IPSSectionResourceFilters = {
114
+ // Patient section: only Patient resource
115
+ ["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient",
116
+ // Only include allergies
117
+ ["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "AllergyIntolerance",
118
+ // includes MedicationRequest, MedicationStatement. Medication is needed for medication names
119
+ ["MedicationSummarySection" /* MEDICATIONS */]: (resource) => ["MedicationRequest", "MedicationStatement", "Medication"].includes(resource.resourceType),
134
120
  // Only include active conditions
135
121
  ["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => !["inactive", "resolved"].includes(c.code)),
136
122
  // Only include completed immunizations
137
123
  ["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed" || resource.resourceType === "Organization",
138
124
  // Only include vital sign Observations (category.coding contains 'vital-signs')
139
125
  ["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => c.code === "vital-signs")),
126
+ // Includes DeviceUseStatement. Device is needed for linked device details
127
+ ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: (resource) => ["DeviceUseStatement", "Device"].includes(resource.resourceType),
140
128
  // Only include finalized diagnostic reports
141
129
  ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
142
130
  // Only include completed procedures
@@ -152,14 +140,9 @@ var IPSSectionResourceFilters = {
152
140
  // Only include active care plans
153
141
  ["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
154
142
  // Only include active advance directives (Consent resources)
155
- ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active",
156
- // Patient section: only Patient resource
157
- ["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient"
143
+ ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active"
158
144
  };
159
145
  var IPSSectionResourceHelper = class {
160
- static getResourceTypesForSection(section) {
161
- return IPSSectionResourceMap[section] || [];
162
- }
163
146
  static getResourceFilterForSection(section) {
164
147
  return IPSSectionResourceFilters[section];
165
148
  }
@@ -169,11 +152,11 @@ var IPSSectionResourceHelper = class {
169
152
  var import_luxon = require("luxon");
170
153
  var TemplateUtilities = class {
171
154
  /**
172
- * Constructor to initialize the TemplateUtilities with a FHIR Bundle
173
- * @param bundle - FHIR Bundle containing resources
155
+ * Constructor to initialize the TemplateUtilities with a FHIR resources
156
+ * @param resources - FHIR resources
174
157
  */
175
- constructor(bundle) {
176
- this.bundle = bundle;
158
+ constructor(resources) {
159
+ this.resources = resources;
177
160
  }
178
161
  /**
179
162
  * Formats a CodeableConcept object
@@ -204,7 +187,7 @@ var TemplateUtilities = class {
204
187
  return "";
205
188
  }
206
189
  resolveReference(ref) {
207
- if (!ref || !this.bundle || !this.bundle.entry) {
190
+ if (!ref || !this.resources) {
208
191
  return null;
209
192
  }
210
193
  const referenceParts = ref.reference?.split("/");
@@ -213,10 +196,10 @@ var TemplateUtilities = class {
213
196
  }
214
197
  const referenceResourceType = referenceParts[0];
215
198
  const referenceResourceId = referenceParts[1];
216
- const resource = this.bundle.entry.find((entry) => {
217
- return entry.resource && entry.resource.resourceType === referenceResourceType && entry.resource.id === referenceResourceId;
199
+ const resource = this.resources.find((entry) => {
200
+ return entry.resourceType === referenceResourceType && entry.id === referenceResourceId;
218
201
  });
219
- return resource ? resource.resource : null;
202
+ return resource ? resource : null;
220
203
  }
221
204
  /**
222
205
  * Renders a Device reference
@@ -787,7 +770,7 @@ var TemplateUtilities = class {
787
770
  if (parts.length === 2) {
788
771
  const resourceType = parts[0];
789
772
  const resourceId = parts[1];
790
- const resource = this.bundle.entry?.find((entry) => entry.resource?.resourceType === resourceType && entry.resource?.id === resourceId);
773
+ const resource = this.resources?.find((resource2) => resource2.resourceType === resourceType && resource2.id === resourceId);
791
774
  if (resource) {
792
775
  return `${resourceType}/${resourceId}`;
793
776
  }
@@ -827,48 +810,96 @@ var TemplateUtilities = class {
827
810
  // src/narratives/templates/typescript/PatientTemplate.ts
828
811
  var PatientTemplate = class _PatientTemplate {
829
812
  /**
830
- * Generate HTML narrative for Patient resource
831
- * @param resource - FHIR Bundle containing Patient resource
813
+ * Generate HTML narrative for Patient resources
814
+ * @param resources - FHIR Patient resources
832
815
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
833
816
  * @returns HTML string for rendering
834
817
  */
835
- generateNarrative(resource, timezone) {
836
- return _PatientTemplate.generateStaticNarrative(resource, timezone);
818
+ generateNarrative(resources, timezone) {
819
+ return _PatientTemplate.generateStaticNarrative(resources, timezone);
837
820
  }
838
821
  /**
839
822
  * Internal static implementation that actually generates the narrative
840
- * @param resource - FHIR Bundle containing Patient resource
823
+ * @param resources - FHIR Patient resources
841
824
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
842
825
  * @returns HTML string for rendering
843
826
  */
844
827
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
845
- static generateStaticNarrative(resource, timezone) {
846
- const templateUtilities = new TemplateUtilities(resource);
847
- let html = "";
848
- for (const entry of resource.entry || []) {
849
- if (entry.resource?.resourceType === "Patient") {
850
- const patient = entry.resource;
851
- html += `
852
- <div>
853
- <ul>
854
- <li><strong>Name(s):</strong>${this.renderNames(patient)}</li>
855
- <li><strong>Gender:</strong>${patient.gender ? this.capitalize(patient.gender) : ""}</li>
856
- <li><strong>Date of Birth:</strong>${patient.birthDate || ""}</li>
857
- <li><strong>Identifier(s):</strong>${this.renderIdentifiers(patient)}</li>
858
- <li><strong>Telecom:</strong><ul>${this.renderTelecom(patient)}</ul></li>
859
- <li><strong>Address(es):</strong>${this.renderAddresses(patient)}</li>
860
- <li><strong>Marital Status:</strong> ${patient.maritalStatus?.text || ""}</li>
861
- <li><strong>Deceased:</strong>${this.renderDeceased(patient)}</li>
862
- <li><strong>Language(s):</strong>${this.renderCommunication(templateUtilities, patient)}</li>
863
- </ul>
864
- </div>`;
828
+ static generateStaticNarrative(resources, timezone) {
829
+ const templateUtilities = new TemplateUtilities(resources);
830
+ const combinedPatient = this.combinePatients(resources);
831
+ return `<div>
832
+ <ul>
833
+ <li><strong>Name(s):</strong>${this.renderNames(combinedPatient)}</li>
834
+ <li><strong>Gender:</strong>${combinedPatient.gender ? this.capitalize(combinedPatient.gender) : ""}</li>
835
+ <li><strong>Date of Birth:</strong>${combinedPatient.birthDate || ""}</li>
836
+ <li><strong>Identifier(s):</strong>${this.renderIdentifiers(combinedPatient)}</li>
837
+ <li><strong>Telecom:</strong><ul>${this.renderTelecom(combinedPatient)}</ul></li>
838
+ <li><strong>Address(es):</strong>${this.renderAddresses(combinedPatient)}</li>
839
+ <li><strong>Marital Status:</strong> ${combinedPatient.maritalStatus?.text || ""}</li>
840
+ <li><strong>Deceased:</strong>${this.renderDeceased(combinedPatient)}</li>
841
+ <li><strong>Language(s):</strong>${this.renderCommunication(templateUtilities, combinedPatient)}</li>
842
+ </ul>
843
+ </div>`;
844
+ }
845
+ /**
846
+ * Combines multiple patient resources into a single patient object
847
+ * Merges fields, preferring non-empty values
848
+ * @param patients - Array of patient resources
849
+ * @returns Combined patient resource
850
+ */
851
+ static combinePatients(patients) {
852
+ if (patients.length === 1) {
853
+ return patients[0];
854
+ }
855
+ const combined = patients[0];
856
+ const allNames = [];
857
+ const allIdentifiers = [];
858
+ const allTelecom = [];
859
+ const allAddresses = [];
860
+ const allCommunication = [];
861
+ patients.forEach((patient) => {
862
+ if (patient.name) {
863
+ allNames.push(...patient.name);
865
864
  }
866
- }
867
- return html;
865
+ if (patient.identifier) {
866
+ allIdentifiers.push(...patient.identifier);
867
+ }
868
+ if (patient.telecom) {
869
+ allTelecom.push(...patient.telecom);
870
+ }
871
+ if (patient.address) {
872
+ allAddresses.push(...patient.address);
873
+ }
874
+ if (patient.communication) {
875
+ allCommunication.push(...patient.communication);
876
+ }
877
+ if (!combined.gender && patient.gender) {
878
+ combined.gender = patient.gender;
879
+ }
880
+ if (!combined.birthDate && patient.birthDate) {
881
+ combined.birthDate = patient.birthDate;
882
+ }
883
+ if (!combined.maritalStatus && patient.maritalStatus) {
884
+ combined.maritalStatus = patient.maritalStatus;
885
+ }
886
+ if (!combined.deceasedBoolean && patient.deceasedBoolean !== void 0) {
887
+ combined.deceasedBoolean = patient.deceasedBoolean;
888
+ }
889
+ if (!combined.deceasedDateTime && patient.deceasedDateTime) {
890
+ combined.deceasedDateTime = patient.deceasedDateTime;
891
+ }
892
+ });
893
+ combined.name = allNames;
894
+ combined.identifier = allIdentifiers;
895
+ combined.telecom = allTelecom;
896
+ combined.address = allAddresses;
897
+ combined.communication = allCommunication;
898
+ return combined;
868
899
  }
869
900
  /**
870
901
  * Renders patient names as HTML list items
871
- * @param patient - Patient resource
902
+ * @param patient - Patient resources
872
903
  * @returns HTML string of list items
873
904
  */
874
905
  static renderNames(patient) {
@@ -888,7 +919,7 @@ var PatientTemplate = class _PatientTemplate {
888
919
  }
889
920
  /**
890
921
  * Renders patient identifiers as HTML list items
891
- * @param patient - Patient resource
922
+ * @param patient - Patient resources
892
923
  * @returns HTML string of list items
893
924
  */
894
925
  static renderIdentifiers(patient) {
@@ -903,7 +934,7 @@ var PatientTemplate = class _PatientTemplate {
903
934
  }
904
935
  /**
905
936
  * Renders patient telecom information grouped by system
906
- * @param patient - Patient resource
937
+ * @param patient - Patient resources
907
938
  * @returns HTML string grouped by system
908
939
  */
909
940
  static renderTelecom(patient) {
@@ -962,7 +993,7 @@ var PatientTemplate = class _PatientTemplate {
962
993
  }
963
994
  /**
964
995
  * Renders patient addresses as HTML list items
965
- * @param patient - Patient resource
996
+ * @param patient - Patient resources
966
997
  * @returns HTML string of list items
967
998
  */
968
999
  static renderAddresses(patient) {
@@ -984,9 +1015,18 @@ var PatientTemplate = class _PatientTemplate {
984
1015
  if (address.city) {
985
1016
  addressArray.push(address.city);
986
1017
  }
1018
+ if (address.district) {
1019
+ addressArray.push(address.district);
1020
+ }
1021
+ if (address.state) {
1022
+ addressArray.push(address.state);
1023
+ }
987
1024
  if (address.country) {
988
1025
  addressArray.push(address.country);
989
1026
  }
1027
+ if (address.postalCode) {
1028
+ addressArray.push(address.postalCode);
1029
+ }
990
1030
  }
991
1031
  const addressText = addressArray.join(", ").trim();
992
1032
  if (addressText) {
@@ -997,7 +1037,7 @@ var PatientTemplate = class _PatientTemplate {
997
1037
  }
998
1038
  /**
999
1039
  * Renders patient deceased status
1000
- * @param patient - Patient resource
1040
+ * @param patient - Patient resources
1001
1041
  * @returns HTML string for deceased status
1002
1042
  */
1003
1043
  static renderDeceased(patient) {
@@ -1012,7 +1052,7 @@ var PatientTemplate = class _PatientTemplate {
1012
1052
  /**
1013
1053
  * Renders patient communication preferences as HTML list items
1014
1054
  * @param templateUtilities - Instance of TemplateUtilities for utility functions
1015
- * @param patient - Patient resource
1055
+ * @param patient - Patient resources
1016
1056
  * @returns HTML string of list items
1017
1057
  */
1018
1058
  static renderCommunication(templateUtilities, patient) {
@@ -1047,32 +1087,30 @@ var PatientTemplate = class _PatientTemplate {
1047
1087
  var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
1048
1088
  /**
1049
1089
  * Generate HTML narrative for AllergyIntolerance resources
1050
- * @param resource - FHIR Bundle containing AllergyIntolerance resources
1090
+ * @param resources - FHIR resources array containing AllergyIntolerance resources
1051
1091
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1052
1092
  * @returns HTML string for rendering
1053
1093
  */
1054
- generateNarrative(resource, timezone) {
1055
- return _AllergyIntoleranceTemplate.generateStaticNarrative(resource, timezone);
1094
+ generateNarrative(resources, timezone) {
1095
+ return _AllergyIntoleranceTemplate.generateStaticNarrative(resources, timezone);
1056
1096
  }
1057
1097
  /**
1058
1098
  * Internal static implementation that actually generates the narrative
1059
- * @param resource - FHIR Bundle containing AllergyIntolerance resources
1099
+ * @param resources - FHIR resources array containing AllergyIntolerance resources
1060
1100
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1061
1101
  * @returns HTML string for rendering
1062
1102
  */
1063
- static generateStaticNarrative(resource, timezone) {
1064
- const templateUtilities = new TemplateUtilities(resource);
1103
+ static generateStaticNarrative(resources, timezone) {
1104
+ const templateUtilities = new TemplateUtilities(resources);
1065
1105
  const activeAllergies = [];
1066
1106
  const resolvedAllergies = [];
1067
- if (resource.entry && Array.isArray(resource.entry)) {
1068
- for (const entry of resource.entry) {
1069
- const allergy = entry.resource;
1070
- const isResolved = allergy.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code));
1071
- if (isResolved) {
1072
- resolvedAllergies.push(allergy);
1073
- } else {
1074
- activeAllergies.push(allergy);
1075
- }
1107
+ for (const resourceItem of resources) {
1108
+ const allergy = resourceItem;
1109
+ const isResolved = allergy.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code));
1110
+ if (isResolved) {
1111
+ resolvedAllergies.push(allergy);
1112
+ } else {
1113
+ activeAllergies.push(allergy);
1076
1114
  }
1077
1115
  }
1078
1116
  activeAllergies.sort((a, b) => {
@@ -1185,12 +1223,12 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
1185
1223
  var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1186
1224
  /**
1187
1225
  * Generate HTML narrative for Medication resources
1188
- * @param resource - FHIR Bundle containing Medication resources
1226
+ * @param resources - FHIR Medication resources
1189
1227
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1190
1228
  * @returns HTML string for rendering
1191
1229
  */
1192
- generateNarrative(resource, timezone) {
1193
- return _MedicationSummaryTemplate.generateStaticNarrative(resource, timezone);
1230
+ generateNarrative(resources, timezone) {
1231
+ return _MedicationSummaryTemplate.generateStaticNarrative(resources, timezone);
1194
1232
  }
1195
1233
  /**
1196
1234
  * Safely parse a date string and return a valid Date object or null
@@ -1255,16 +1293,16 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1255
1293
  }
1256
1294
  /**
1257
1295
  * Internal static implementation that actually generates the narrative
1258
- * @param resource - FHIR Bundle containing Medication resources
1296
+ * @param resources - FHIR Medication resources
1259
1297
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1260
1298
  * @returns HTML string for rendering
1261
1299
  */
1262
1300
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
1263
- static generateStaticNarrative(resource, timezone) {
1264
- const templateUtilities = new TemplateUtilities(resource);
1301
+ static generateStaticNarrative(resources, timezone) {
1302
+ const templateUtilities = new TemplateUtilities(resources);
1265
1303
  let html = "";
1266
- const medicationRequests = this.getMedicationRequests(templateUtilities, resource);
1267
- const medicationStatements = this.getMedicationStatements(templateUtilities, resource);
1304
+ const medicationRequests = this.getMedicationRequests(templateUtilities, resources);
1305
+ const medicationStatements = this.getMedicationStatements(templateUtilities, resources);
1268
1306
  const allActiveMedications = [];
1269
1307
  const allInactiveMedications = [];
1270
1308
  medicationRequests.forEach((mr) => {
@@ -1318,33 +1356,33 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1318
1356
  return html;
1319
1357
  }
1320
1358
  /**
1321
- * Extract MedicationRequest resources from the bundle
1359
+ * Extract MedicationRequest resources
1322
1360
  * @param templateUtilities - Instance of TemplateUtilities for utility functions
1323
- * @param resource - FHIR Bundle
1361
+ * @param resources - FHIR Medication resources
1324
1362
  * @returns Array of MedicationRequest resources
1325
1363
  */
1326
- static getMedicationRequests(templateUtilities, resource) {
1327
- if (!resource.entry || !Array.isArray(resource.entry)) {
1364
+ static getMedicationRequests(templateUtilities, resources) {
1365
+ if (resources.length === 0) {
1328
1366
  return [];
1329
1367
  }
1330
- return resource.entry.filter((entry) => entry.resource?.resourceType === "MedicationRequest").map((entry) => ({
1331
- resource: entry.resource,
1332
- extension: templateUtilities.narrativeLinkExtension(entry.resource)
1368
+ return resources.filter((entry) => entry.resourceType === "MedicationRequest").map((entry) => ({
1369
+ resource: entry,
1370
+ extension: templateUtilities.narrativeLinkExtension(entry)
1333
1371
  }));
1334
1372
  }
1335
1373
  /**
1336
- * Extract MedicationStatement resources from the bundle
1374
+ * Extract MedicationStatement resources
1337
1375
  * @param templateUtilities - Instance of TemplateUtilities for utility functions
1338
- * @param resource - FHIR Bundle
1376
+ * @param resources - FHIR Medication resources
1339
1377
  * @returns Array of MedicationStatement resources
1340
1378
  */
1341
- static getMedicationStatements(templateUtilities, resource) {
1342
- if (!resource.entry || !Array.isArray(resource.entry)) {
1379
+ static getMedicationStatements(templateUtilities, resources) {
1380
+ if (resources.length === 0) {
1343
1381
  return [];
1344
1382
  }
1345
- return resource.entry.filter((entry) => entry.resource?.resourceType === "MedicationStatement").map((entry) => ({
1346
- resource: entry.resource,
1347
- extension: templateUtilities.narrativeLinkExtension(entry.resource)
1383
+ return resources.filter((entry) => entry.resourceType === "MedicationStatement").map((entry) => ({
1384
+ resource: entry,
1385
+ extension: templateUtilities.narrativeLinkExtension(entry)
1348
1386
  }));
1349
1387
  }
1350
1388
  /**
@@ -1440,28 +1478,26 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1440
1478
  var ImmunizationsTemplate = class _ImmunizationsTemplate {
1441
1479
  /**
1442
1480
  * Generate HTML narrative for Immunization resources
1443
- * @param resource - FHIR Bundle containing Immunization resources
1481
+ * @param resources - FHIR resources array containing Immunization resources
1444
1482
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1445
1483
  * @returns HTML string for rendering
1446
1484
  */
1447
- generateNarrative(resource, timezone) {
1448
- if (resource.entry && Array.isArray(resource.entry)) {
1449
- resource.entry.sort((a, b) => {
1450
- const dateA = a.resource?.occurrenceDateTime;
1451
- const dateB = b.resource?.occurrenceDateTime;
1452
- return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1453
- });
1454
- }
1455
- return _ImmunizationsTemplate.generateStaticNarrative(resource, timezone);
1485
+ generateNarrative(resources, timezone) {
1486
+ resources.sort((a, b) => {
1487
+ const dateA = a.occurrenceDateTime;
1488
+ const dateB = b.occurrenceDateTime;
1489
+ return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1490
+ });
1491
+ return _ImmunizationsTemplate.generateStaticNarrative(resources, timezone);
1456
1492
  }
1457
1493
  /**
1458
1494
  * Internal static implementation that actually generates the narrative
1459
- * @param resource - FHIR Bundle containing Immunization resources
1495
+ * @param resources - FHIR resources array containing Immunization resources
1460
1496
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1461
1497
  * @returns HTML string for rendering
1462
1498
  */
1463
- static generateStaticNarrative(resource, timezone) {
1464
- const templateUtilities = new TemplateUtilities(resource);
1499
+ static generateStaticNarrative(resources, timezone) {
1500
+ const templateUtilities = new TemplateUtilities(resources);
1465
1501
  let html = `
1466
1502
  <table>
1467
1503
  <thead>
@@ -1476,21 +1512,20 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
1476
1512
  </tr>
1477
1513
  </thead>
1478
1514
  <tbody>`;
1479
- if (resource.entry && Array.isArray(resource.entry)) {
1480
- for (const entry of resource.entry) {
1481
- if (entry.resource?.resourceType === "Immunization") {
1482
- const imm = entry.resource;
1483
- html += `
1484
- <tr id="${templateUtilities.narrativeLinkId(imm)}">
1485
- <td>${templateUtilities.codeableConcept(imm.vaccineCode)}</td>
1486
- <td>${imm.status || ""}</td>
1487
- <td>${templateUtilities.concatDoseNumber(imm.protocolApplied)}</td>
1488
- <td>${templateUtilities.renderVaccineManufacturer(imm)}</td>
1489
- <td>${imm.lotNumber || ""}</td>
1490
- <td>${templateUtilities.renderNotes(imm.note, timezone)}</td>
1491
- <td>${templateUtilities.renderTime(imm.occurrenceDateTime, timezone)}</td>
1492
- </tr>`;
1493
- }
1515
+ const immunizations = resources.filter((resourceItem) => resourceItem.resourceType === "Immunization");
1516
+ if (immunizations.length > 0) {
1517
+ for (const resourceItem of immunizations) {
1518
+ const imm = resourceItem;
1519
+ html += `
1520
+ <tr id="${templateUtilities.narrativeLinkId(imm)}">
1521
+ <td>${templateUtilities.codeableConcept(imm.vaccineCode)}</td>
1522
+ <td>${imm.status || ""}</td>
1523
+ <td>${templateUtilities.concatDoseNumber(imm.protocolApplied)}</td>
1524
+ <td>${templateUtilities.renderVaccineManufacturer(imm)}</td>
1525
+ <td>${imm.lotNumber || ""}</td>
1526
+ <td>${templateUtilities.renderNotes(imm.note, timezone)}</td>
1527
+ <td>${templateUtilities.renderTime(imm.occurrenceDateTime, timezone)}</td>
1528
+ </tr>`;
1494
1529
  }
1495
1530
  }
1496
1531
  html += `
@@ -1504,23 +1539,23 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
1504
1539
  var ProblemListTemplate = class _ProblemListTemplate {
1505
1540
  /**
1506
1541
  * Generate HTML narrative for Problem List
1507
- * @param resource - FHIR Bundle containing Condition resources
1542
+ * @param resources - FHIR Condition resources
1508
1543
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1509
1544
  * @returns HTML string for rendering
1510
1545
  */
1511
- generateNarrative(resource, timezone) {
1512
- return _ProblemListTemplate.generateStaticNarrative(resource, timezone);
1546
+ generateNarrative(resources, timezone) {
1547
+ return _ProblemListTemplate.generateStaticNarrative(resources, timezone);
1513
1548
  }
1514
1549
  /**
1515
1550
  * Internal static implementation that actually generates the narrative
1516
- * @param resource - FHIR Bundle containing Condition resources
1551
+ * @param resources - FHIR Condition resources
1517
1552
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1518
1553
  * @returns HTML string for rendering
1519
1554
  */
1520
- static generateStaticNarrative(resource, timezone) {
1521
- const templateUtilities = new TemplateUtilities(resource);
1555
+ static generateStaticNarrative(resources, timezone) {
1556
+ const templateUtilities = new TemplateUtilities(resources);
1522
1557
  let html = ``;
1523
- const activeConditions = resource.entry?.map((entry) => entry.resource) || [];
1558
+ const activeConditions = resources.map((entry) => entry) || [];
1524
1559
  activeConditions.sort((a, b) => {
1525
1560
  const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
1526
1561
  const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
@@ -1555,22 +1590,22 @@ var ProblemListTemplate = class _ProblemListTemplate {
1555
1590
  var VitalSignsTemplate = class _VitalSignsTemplate {
1556
1591
  /**
1557
1592
  * Generate HTML narrative for Vital Signs
1558
- * @param resource - FHIR Bundle containing Observation resources
1593
+ * @param resources - FHIR Observation resources
1559
1594
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1560
1595
  * @returns HTML string for rendering
1561
1596
  */
1562
- generateNarrative(resource, timezone) {
1563
- return _VitalSignsTemplate.generateStaticNarrative(resource, timezone);
1597
+ generateNarrative(resources, timezone) {
1598
+ return _VitalSignsTemplate.generateStaticNarrative(resources, timezone);
1564
1599
  }
1565
1600
  /**
1566
1601
  * Internal static implementation that actually generates the narrative
1567
- * @param resource - FHIR Bundle containing Observation resources
1602
+ * @param resources - FHIR Observation resources
1568
1603
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1569
1604
  * @returns HTML string for rendering
1570
1605
  */
1571
- static generateStaticNarrative(resource, timezone) {
1572
- const templateUtilities = new TemplateUtilities(resource);
1573
- const observations = resource.entry?.map((entry) => entry.resource) || [];
1606
+ static generateStaticNarrative(resources, timezone) {
1607
+ const templateUtilities = new TemplateUtilities(resources);
1608
+ const observations = resources.map((entry) => entry) || [];
1574
1609
  observations.sort((a, b) => {
1575
1610
  const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
1576
1611
  const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
@@ -1613,28 +1648,21 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
1613
1648
  var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
1614
1649
  /**
1615
1650
  * Generate HTML narrative for Medical Device resources
1616
- * @param resource - FHIR Bundle containing Device resources
1651
+ * @param resources - FHIR resources array containing Device resources
1617
1652
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1618
1653
  * @returns HTML string for rendering
1619
1654
  */
1620
- generateNarrative(resource, timezone) {
1621
- if (resource.entry && Array.isArray(resource.entry)) {
1622
- resource.entry.sort((a, b) => {
1623
- const dateA = a.resource?.recordedOn;
1624
- const dateB = b.resource?.recordedOn;
1625
- return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1626
- });
1627
- }
1628
- return _MedicalDevicesTemplate.generateStaticNarrative(resource, timezone);
1655
+ generateNarrative(resources, timezone) {
1656
+ return _MedicalDevicesTemplate.generateStaticNarrative(resources, timezone);
1629
1657
  }
1630
1658
  /**
1631
1659
  * Internal static implementation that actually generates the narrative
1632
- * @param resource - FHIR Bundle containing Device resources
1660
+ * @param resources - FHIR resources array containing Device resources
1633
1661
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1634
1662
  * @returns HTML string for rendering
1635
1663
  */
1636
- static generateStaticNarrative(resource, timezone) {
1637
- const templateUtilities = new TemplateUtilities(resource);
1664
+ static generateStaticNarrative(resources, timezone) {
1665
+ const templateUtilities = new TemplateUtilities(resources);
1638
1666
  let html = `
1639
1667
  <table>
1640
1668
  <thead>
@@ -1646,19 +1674,19 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
1646
1674
  </tr>
1647
1675
  </thead>
1648
1676
  <tbody>`;
1649
- if (resource.entry && Array.isArray(resource.entry)) {
1650
- for (const entry of resource.entry) {
1651
- if (entry.resource?.resourceType === "DeviceUseStatement") {
1652
- const dus = entry.resource;
1653
- html += `
1654
- <tr id="${templateUtilities.narrativeLinkId(dus)}">
1655
- <td>${templateUtilities.renderDevice(dus.device)}</td>
1656
- <td>${dus.status || ""}</td>
1657
- <td>${templateUtilities.renderNotes(dus.note, timezone)}</td>
1658
- <td>${templateUtilities.renderRecorded(dus.recordedOn, timezone)}</td>
1659
- </tr>`;
1660
- }
1661
- }
1677
+ const deviceStatements = resources.filter((resourceItem) => resourceItem.resourceType === "DeviceUseStatement").map((resourceItem) => resourceItem).sort((a, b) => {
1678
+ const dateA = a.recordedOn;
1679
+ const dateB = b.recordedOn;
1680
+ return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1681
+ });
1682
+ for (const dus of deviceStatements) {
1683
+ html += `
1684
+ <tr id="${templateUtilities.narrativeLinkId(dus)}">
1685
+ <td>${templateUtilities.renderDevice(dus.device)}</td>
1686
+ <td>${dus.status || ""}</td>
1687
+ <td>${templateUtilities.renderNotes(dus.note, timezone)}</td>
1688
+ <td>${templateUtilities.renderRecorded(dus.recordedOn, timezone)}</td>
1689
+ </tr>`;
1662
1690
  }
1663
1691
  html += `
1664
1692
  </tbody>
@@ -1671,23 +1699,23 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
1671
1699
  var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1672
1700
  /**
1673
1701
  * Generate HTML narrative for Diagnostic Results
1674
- * @param resource - FHIR Bundle containing Observation and DiagnosticReport resources
1702
+ * @param resources - FHIR resources array containing Observation and DiagnosticReport resources
1675
1703
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1676
1704
  * @returns HTML string for rendering
1677
1705
  */
1678
- generateNarrative(resource, timezone) {
1679
- return _DiagnosticResultsTemplate.generateStaticNarrative(resource, timezone);
1706
+ generateNarrative(resources, timezone) {
1707
+ return _DiagnosticResultsTemplate.generateStaticNarrative(resources, timezone);
1680
1708
  }
1681
1709
  /**
1682
1710
  * Internal static implementation that actually generates the narrative
1683
- * @param resource - FHIR Bundle containing Observation and DiagnosticReport resources
1711
+ * @param resources - FHIR resources array containing Observation and DiagnosticReport resources
1684
1712
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1685
1713
  * @returns HTML string for rendering
1686
1714
  */
1687
- static generateStaticNarrative(resource, timezone) {
1688
- const templateUtilities = new TemplateUtilities(resource);
1715
+ static generateStaticNarrative(resources, timezone) {
1716
+ const templateUtilities = new TemplateUtilities(resources);
1689
1717
  let html = "";
1690
- const observations = this.getObservations(resource);
1718
+ const observations = this.getObservations(resources);
1691
1719
  if (observations.length > 0) {
1692
1720
  observations.sort((a, b) => {
1693
1721
  const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
@@ -1696,7 +1724,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1696
1724
  });
1697
1725
  html += this.renderObservations(templateUtilities, observations, timezone);
1698
1726
  }
1699
- const diagnosticReports = this.getDiagnosticReports(resource);
1727
+ const diagnosticReports = this.getDiagnosticReports(resources);
1700
1728
  if (diagnosticReports.length > 0) {
1701
1729
  diagnosticReports.sort((a, b) => {
1702
1730
  const dateA = a.issued;
@@ -1708,26 +1736,20 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1708
1736
  return html;
1709
1737
  }
1710
1738
  /**
1711
- * Extract Observation resources from the bundle
1712
- * @param resource - FHIR Bundle
1713
- * @returns Array of Observation resources
1714
- */
1715
- static getObservations(resource) {
1716
- if (!resource.entry || !Array.isArray(resource.entry)) {
1717
- return [];
1718
- }
1719
- return resource.entry.filter((entry) => entry.resource?.resourceType === "Observation").map((entry) => entry.resource);
1739
+ * Get all Observation resources from the resource array
1740
+ * @param resources - FHIR resources array
1741
+ * @returns Array of Observation resources
1742
+ */
1743
+ static getObservations(resources) {
1744
+ return resources.filter((resourceItem) => resourceItem.resourceType === "Observation").map((resourceItem) => resourceItem);
1720
1745
  }
1721
1746
  /**
1722
- * Extract DiagnosticReport resources from the bundle
1723
- * @param resource - FHIR Bundle
1747
+ * Get all DiagnosticReport resources from the resource array
1748
+ * @param resources - FHIR resources array
1724
1749
  * @returns Array of DiagnosticReport resources
1725
1750
  */
1726
- static getDiagnosticReports(resource) {
1727
- if (!resource.entry || !Array.isArray(resource.entry)) {
1728
- return [];
1729
- }
1730
- return resource.entry.filter((entry) => entry.resource?.resourceType === "DiagnosticReport").map((entry) => entry.resource);
1751
+ static getDiagnosticReports(resources) {
1752
+ return resources.filter((resourceItem) => resourceItem.resourceType === "DiagnosticReport").map((resourceItem) => resourceItem);
1731
1753
  }
1732
1754
  /**
1733
1755
  * Render HTML table for Observation resources
@@ -1813,26 +1835,26 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1813
1835
  var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
1814
1836
  /**
1815
1837
  * Generate HTML narrative for Procedure resources
1816
- * @param resource - FHIR Bundle containing Procedure resources
1838
+ * @param resources - FHIR Procedure resources
1817
1839
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1818
1840
  * @returns HTML string for rendering
1819
1841
  */
1820
- generateNarrative(resource, timezone) {
1821
- resource.entry?.sort((a, b) => {
1822
- const dateA = a.resource.performedDateTime || a.resource.performedPeriod?.start;
1823
- const dateB = b.resource.performedDateTime || b.resource.performedPeriod?.start;
1842
+ generateNarrative(resources, timezone) {
1843
+ resources.sort((a, b) => {
1844
+ const dateA = a.performedDateTime || a.performedPeriod?.start;
1845
+ const dateB = b.performedDateTime || b.performedPeriod?.start;
1824
1846
  return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
1825
1847
  });
1826
- return _HistoryOfProceduresTemplate.generateStaticNarrative(resource, timezone);
1848
+ return _HistoryOfProceduresTemplate.generateStaticNarrative(resources, timezone);
1827
1849
  }
1828
1850
  /**
1829
1851
  * Internal static implementation that actually generates the narrative
1830
- * @param resource - FHIR Bundle containing Procedure resources
1852
+ * @param resources - FHIR Procedure resources
1831
1853
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1832
1854
  * @returns HTML string for rendering
1833
1855
  */
1834
- static generateStaticNarrative(resource, timezone) {
1835
- const templateUtilities = new TemplateUtilities(resource);
1856
+ static generateStaticNarrative(resources, timezone) {
1857
+ const templateUtilities = new TemplateUtilities(resources);
1836
1858
  let html = `
1837
1859
  <table>
1838
1860
  <thead>
@@ -1843,16 +1865,14 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
1843
1865
  </tr>
1844
1866
  </thead>
1845
1867
  <tbody>`;
1846
- if (resource.entry && Array.isArray(resource.entry)) {
1847
- for (const entry of resource.entry) {
1848
- const proc = entry.resource;
1849
- html += `
1850
- <tr id="${templateUtilities.narrativeLinkId(proc)}">
1851
- <td>${templateUtilities.codeableConcept(proc.code, "display")}</td>
1852
- <td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
1853
- <td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
1854
- </tr>`;
1855
- }
1868
+ for (const resourceItem of resources) {
1869
+ const proc = resourceItem;
1870
+ html += `
1871
+ <tr id="${templateUtilities.narrativeLinkId(proc)}">
1872
+ <td>${templateUtilities.codeableConcept(proc.code, "display")}</td>
1873
+ <td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
1874
+ <td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
1875
+ </tr>`;
1856
1876
  }
1857
1877
  html += `
1858
1878
  </tbody>
@@ -1865,22 +1885,22 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
1865
1885
  var SocialHistoryTemplate = class _SocialHistoryTemplate {
1866
1886
  /**
1867
1887
  * Generate HTML narrative for Social History
1868
- * @param resource - FHIR Bundle containing Observation resources
1888
+ * @param resources - FHIR Observation resources
1869
1889
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1870
1890
  * @returns HTML string for rendering
1871
1891
  */
1872
- generateNarrative(resource, timezone) {
1873
- return _SocialHistoryTemplate.generateStaticNarrative(resource, timezone);
1892
+ generateNarrative(resources, timezone) {
1893
+ return _SocialHistoryTemplate.generateStaticNarrative(resources, timezone);
1874
1894
  }
1875
1895
  /**
1876
1896
  * Internal static implementation that actually generates the narrative
1877
- * @param resource - FHIR Bundle containing Observation resources
1897
+ * @param resources - FHIR Observation resources
1878
1898
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1879
1899
  * @returns HTML string for rendering
1880
1900
  */
1881
- static generateStaticNarrative(resource, timezone) {
1882
- const templateUtilities = new TemplateUtilities(resource);
1883
- const observations = resource.entry?.map((entry) => entry.resource) || [];
1901
+ static generateStaticNarrative(resources, timezone) {
1902
+ const templateUtilities = new TemplateUtilities(resources);
1903
+ const observations = resources.map((entry) => entry) || [];
1884
1904
  observations.sort((a, b) => {
1885
1905
  const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
1886
1906
  const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
@@ -1919,14 +1939,14 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
1919
1939
  var PastHistoryOfIllnessTemplate = class {
1920
1940
  /**
1921
1941
  * Generate HTML narrative for Past History of Illnesses
1922
- * @param resource - FHIR Bundle containing Condition resources
1942
+ * @param resources - FHIR Condition resources
1923
1943
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1924
1944
  * @returns HTML string for rendering
1925
1945
  */
1926
- generateNarrative(resource, timezone) {
1927
- const templateUtilities = new TemplateUtilities(resource);
1946
+ generateNarrative(resources, timezone) {
1947
+ const templateUtilities = new TemplateUtilities(resources);
1928
1948
  let html = ``;
1929
- const resolvedConditions = resource.entry?.map((entry) => entry.resource) || [];
1949
+ const resolvedConditions = resources.map((entry) => entry) || [];
1930
1950
  resolvedConditions.sort((a, b) => {
1931
1951
  const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
1932
1952
  const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
@@ -1963,13 +1983,13 @@ var PastHistoryOfIllnessTemplate = class {
1963
1983
  var PlanOfCareTemplate = class {
1964
1984
  /**
1965
1985
  * Generate HTML narrative for Plan of Care
1966
- * @param resource - FHIR Bundle containing CarePlan resources
1986
+ * @param resources - FHIR CarePlan resources
1967
1987
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1968
1988
  * @returns HTML string for rendering
1969
1989
  */
1970
- generateNarrative(resource, timezone) {
1971
- const templateUtilities = new TemplateUtilities(resource);
1972
- const carePlans = resource.entry?.map((entry) => entry.resource) || [];
1990
+ generateNarrative(resources, timezone) {
1991
+ const templateUtilities = new TemplateUtilities(resources);
1992
+ const carePlans = resources.map((entry) => entry) || [];
1973
1993
  carePlans.sort((a, b) => {
1974
1994
  const endA = a.period?.end ? new Date(a.period?.end).getTime() : 0;
1975
1995
  const endB = b.period?.end ? new Date(b.period?.end).getTime() : 0;
@@ -2008,37 +2028,35 @@ var PlanOfCareTemplate = class {
2008
2028
  var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
2009
2029
  /**
2010
2030
  * Generate HTML narrative for Functional Status
2011
- * @param resource - FHIR Bundle containing Observation resources
2031
+ * @param resources - FHIR resources array containing Observation resources
2012
2032
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2013
2033
  * @returns HTML string for rendering
2014
2034
  */
2015
- generateNarrative(resource, timezone) {
2016
- return _FunctionalStatusTemplate.generateStaticNarrative(resource, timezone);
2035
+ generateNarrative(resources, timezone) {
2036
+ return _FunctionalStatusTemplate.generateStaticNarrative(resources, timezone);
2017
2037
  }
2018
2038
  /**
2019
2039
  * Internal static implementation that actually generates the narrative
2020
- * @param resource - FHIR Bundle containing Observation resources
2040
+ * @param resources - FHIR resources array containing Observation resources
2021
2041
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2022
2042
  * @returns HTML string for rendering
2023
2043
  */
2024
- static generateStaticNarrative(resource, timezone) {
2025
- const templateUtilities = new TemplateUtilities(resource);
2044
+ static generateStaticNarrative(resources, timezone) {
2045
+ const templateUtilities = new TemplateUtilities(resources);
2026
2046
  let html = ``;
2027
2047
  const activeConditions = [];
2028
2048
  const clinicalImpressions = [];
2029
- if (resource.entry && Array.isArray(resource.entry)) {
2030
- for (const entry of resource.entry) {
2031
- if (entry.resource?.resourceType === "Condition") {
2032
- const cond = entry.resource;
2033
- const isResolved = cond.clinicalStatus?.coding?.some(
2034
- (c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved")
2035
- );
2036
- if (!isResolved) {
2037
- activeConditions.push(cond);
2038
- }
2039
- } else if (entry.resource?.resourceType === "ClinicalImpression") {
2040
- clinicalImpressions.push(entry.resource);
2049
+ for (const resourceItem of resources) {
2050
+ if (resourceItem.resourceType === "Condition") {
2051
+ const cond = resourceItem;
2052
+ const isResolved = cond.clinicalStatus?.coding?.some(
2053
+ (c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved")
2054
+ );
2055
+ if (!isResolved) {
2056
+ activeConditions.push(cond);
2041
2057
  }
2058
+ } else if (resourceItem.resourceType === "ClinicalImpression") {
2059
+ clinicalImpressions.push(resourceItem);
2042
2060
  }
2043
2061
  }
2044
2062
  activeConditions.sort((a, b) => {
@@ -2135,22 +2153,22 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
2135
2153
  var PregnancyTemplate = class _PregnancyTemplate {
2136
2154
  /**
2137
2155
  * Generate HTML narrative for Pregnancy
2138
- * @param resource - FHIR Bundle containing Observation resources
2156
+ * @param resources - FHIR Observation resources
2139
2157
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2140
2158
  * @returns HTML string for rendering
2141
2159
  */
2142
- generateNarrative(resource, timezone) {
2143
- return _PregnancyTemplate.generateStaticNarrative(resource, timezone);
2160
+ generateNarrative(resources, timezone) {
2161
+ return _PregnancyTemplate.generateStaticNarrative(resources, timezone);
2144
2162
  }
2145
2163
  /**
2146
2164
  * Internal static implementation that actually generates the narrative
2147
- * @param resource - FHIR Bundle containing Observation resources
2165
+ * @param resources - FHIR Observation resources
2148
2166
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2149
2167
  * @returns HTML string for rendering
2150
2168
  */
2151
- static generateStaticNarrative(resource, timezone) {
2152
- const templateUtilities = new TemplateUtilities(resource);
2153
- const observations = resource.entry?.map((entry) => entry.resource) || [];
2169
+ static generateStaticNarrative(resources, timezone) {
2170
+ const templateUtilities = new TemplateUtilities(resources);
2171
+ const observations = resources.map((entry) => entry) || [];
2154
2172
  observations.sort((a, b) => {
2155
2173
  const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
2156
2174
  const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
@@ -2166,8 +2184,8 @@ var PregnancyTemplate = class _PregnancyTemplate {
2166
2184
  </tr>
2167
2185
  </thead>
2168
2186
  <tbody>`;
2169
- for (const resource2 of observations) {
2170
- const obs = resource2;
2187
+ for (const resource of observations) {
2188
+ const obs = resource;
2171
2189
  html += `
2172
2190
  <tr id="${templateUtilities.narrativeLinkId(obs)}">
2173
2191
  <td>${templateUtilities.extractPregnancyStatus(obs)}</td>
@@ -2186,29 +2204,27 @@ var PregnancyTemplate = class _PregnancyTemplate {
2186
2204
  var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
2187
2205
  /**
2188
2206
  * Generate HTML narrative for Advance Directives
2189
- * @param resource - FHIR Bundle containing Advance Directive resources
2207
+ * @param resources - FHIR Consent resources
2190
2208
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2191
2209
  * @returns HTML string for rendering
2192
2210
  */
2193
- generateNarrative(resource, timezone) {
2194
- if (resource.entry && Array.isArray(resource.entry)) {
2195
- resource.entry.sort((a, b) => {
2196
- const dateA = new Date(a.resource.dateTime || 0);
2197
- const dateB = new Date(b.resource.dateTime || 0);
2198
- return dateB.getTime() - dateA.getTime();
2199
- });
2200
- }
2201
- return _AdvanceDirectivesTemplate.generateStaticNarrative(resource, timezone);
2211
+ generateNarrative(resources, timezone) {
2212
+ resources.sort((a, b) => {
2213
+ const dateA = new Date(a.dateTime || 0);
2214
+ const dateB = new Date(b.dateTime || 0);
2215
+ return dateB.getTime() - dateA.getTime();
2216
+ });
2217
+ return _AdvanceDirectivesTemplate.generateStaticNarrative(resources, timezone);
2202
2218
  }
2203
2219
  /**
2204
2220
  * Internal static implementation that actually generates the narrative
2205
- * @param resource - FHIR Bundle containing Advance Directive resources
2221
+ * @param resources - FHIR Consent resources
2206
2222
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2207
2223
  * @returns HTML string for rendering
2208
2224
  */
2209
2225
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
2210
- static generateStaticNarrative(resource, timezone) {
2211
- const templateUtilities = new TemplateUtilities(resource);
2226
+ static generateStaticNarrative(resources, timezone) {
2227
+ const templateUtilities = new TemplateUtilities(resources);
2212
2228
  let html = `
2213
2229
  <table>
2214
2230
  <thead>
@@ -2220,17 +2236,15 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
2220
2236
  </tr>
2221
2237
  </thead>
2222
2238
  <tbody>`;
2223
- if (resource.entry && Array.isArray(resource.entry)) {
2224
- for (const entry of resource.entry) {
2225
- const consent = entry.resource;
2226
- html += `
2227
- <tr id="${templateUtilities.narrativeLinkId(consent)}">
2228
- <td>${templateUtilities.codeableConcept(consent.scope, "display")}</td>
2229
- <td>${consent.status || ""}</td>
2230
- <td>${consent.provision?.action ? templateUtilities.concatCodeableConcept(consent.provision.action) : ""}</td>
2231
- <td>${consent.dateTime || ""}</td>
2232
- </tr>`;
2233
- }
2239
+ for (const resourceItem of resources) {
2240
+ const consent = resourceItem;
2241
+ html += `
2242
+ <tr id="${templateUtilities.narrativeLinkId(consent)}">
2243
+ <td>${templateUtilities.codeableConcept(consent.scope, "display")}</td>
2244
+ <td>${consent.status || ""}</td>
2245
+ <td>${consent.provision?.action ? templateUtilities.concatCodeableConcept(consent.provision.action) : ""}</td>
2246
+ <td>${consent.dateTime || ""}</td>
2247
+ </tr>`;
2234
2248
  }
2235
2249
  html += `
2236
2250
  </tbody>
@@ -2244,16 +2258,16 @@ var TypeScriptTemplateMapper = class {
2244
2258
  /**
2245
2259
  * Generates HTML narrative for a specific IPS section
2246
2260
  * @param section - The IPS section
2247
- * @param resource - FHIR Bundle containing resources
2261
+ * @param resources - FHIR resources
2248
2262
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2249
2263
  * @returns HTML string for rendering
2250
2264
  */
2251
- static generateNarrative(section, resource, timezone) {
2265
+ static generateNarrative(section, resources, timezone) {
2252
2266
  const templateClass = this.sectionToTemplate[section];
2253
2267
  if (!templateClass) {
2254
2268
  throw new Error(`No template found for section: ${section}`);
2255
2269
  }
2256
- return templateClass.generateNarrative(resource, timezone);
2270
+ return templateClass.generateNarrative(resources, timezone);
2257
2271
  }
2258
2272
  };
2259
2273
  // Map of section types to their template classes
@@ -2316,14 +2330,7 @@ var NarrativeGenerator = class {
2316
2330
  return void 0;
2317
2331
  }
2318
2332
  try {
2319
- const bundle = {
2320
- resourceType: "Bundle",
2321
- type: "collection",
2322
- entry: resources.map((resource) => ({
2323
- resource
2324
- }))
2325
- };
2326
- const content = TypeScriptTemplateMapper.generateNarrative(section, bundle, timezone);
2333
+ const content = TypeScriptTemplateMapper.generateNarrative(section, resources, timezone);
2327
2334
  if (!content) {
2328
2335
  return void 0;
2329
2336
  }
@@ -2408,13 +2415,16 @@ var ComprehensiveIPSCompositionBuilder = class {
2408
2415
  /**
2409
2416
  * sets the patient resource for the IPS Composition.
2410
2417
  * This is not needed if you are calling read_bundle, but can be used to set the patient resource directly.
2411
- * @param patient - FHIR Patient resource to set
2418
+ * @param patients - FHIR Patient resource to set
2412
2419
  */
2413
- setPatient(patient) {
2414
- if (!patient || patient.resourceType !== "Patient") {
2420
+ setPatient(patients) {
2421
+ if (!Array.isArray(patients)) {
2422
+ patients = [patients];
2423
+ }
2424
+ if (patients.length === 0 || !patients.every((patient) => patient.resourceType === "Patient")) {
2415
2425
  throw new Error("Invalid Patient resource");
2416
2426
  }
2417
- this.patient = patient;
2427
+ this.patients = patients;
2418
2428
  return this;
2419
2429
  }
2420
2430
  /**
@@ -2472,21 +2482,26 @@ var ComprehensiveIPSCompositionBuilder = class {
2472
2482
  if (!bundle.entry) {
2473
2483
  return this;
2474
2484
  }
2475
- const patientEntry = bundle.entry.find((e) => e.resource?.resourceType === "Patient");
2476
- if (!patientEntry || !patientEntry.resource) {
2485
+ const patientEntries = [];
2486
+ const resources = [];
2487
+ bundle.entry.forEach((e) => {
2488
+ if (e.resource?.resourceType === "Patient") {
2489
+ patientEntries.push(e.resource);
2490
+ this.resources.add(e.resource);
2491
+ } else if (e.resource) {
2492
+ resources.push(e.resource);
2493
+ }
2494
+ });
2495
+ if (patientEntries.length === 0) {
2477
2496
  throw new Error("Patient resource not found in the bundle");
2478
2497
  }
2479
- this.patient = patientEntry.resource;
2480
- const resources = bundle.entry.map((e) => e.resource);
2498
+ this.patients = patientEntries;
2481
2499
  for (const sectionType of Object.values(IPSSections)) {
2482
- const resourceTypesForSection = IPSSectionResourceHelper.getResourceTypesForSection(sectionType);
2483
- const customFilter = IPSSectionResourceHelper.getResourceFilterForSection(sectionType);
2484
- let sectionResources = resources.filter(
2485
- (r) => r && typeof r.resourceType === "string" && resourceTypesForSection.includes(r.resourceType)
2486
- );
2487
- if (customFilter) {
2488
- sectionResources = sectionResources.filter((resource) => resource && customFilter(resource));
2500
+ if (sectionType === "Patient" /* PATIENT */) {
2501
+ continue;
2489
2502
  }
2503
+ const sectionFilter = IPSSectionResourceHelper.getResourceFilterForSection(sectionType);
2504
+ const sectionResources = resources.filter((resource) => sectionFilter(resource));
2490
2505
  await this.addSectionAsync(sectionType, sectionResources, timezone);
2491
2506
  }
2492
2507
  return this;
@@ -2497,16 +2512,18 @@ var ComprehensiveIPSCompositionBuilder = class {
2497
2512
  * @param authorOrganizationName - Name of the authoring organization
2498
2513
  * @param baseUrl - Base URL for the FHIR server (e.g., 'https://example.com/fhir')
2499
2514
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2515
+ * @param patientId - Optional patient ID to use as primary patient for composition reference
2500
2516
  */
2501
- async buildBundleAsync(authorOrganizationId, authorOrganizationName, baseUrl, timezone) {
2517
+ async buildBundleAsync(authorOrganizationId, authorOrganizationName, baseUrl, timezone, patientId) {
2502
2518
  if (baseUrl.endsWith("/")) {
2503
2519
  baseUrl = baseUrl.slice(0, -1);
2504
2520
  }
2505
- if (!this.patient) {
2521
+ if (!this.patients) {
2506
2522
  throw new Error("Patient resource must be set before building the bundle");
2507
2523
  }
2524
+ const primaryPatientId = patientId ?? this.patients[0].id;
2508
2525
  const composition = {
2509
- id: `Composition-${this.patient.id}`,
2526
+ id: `Composition-${primaryPatientId}`,
2510
2527
  resourceType: "Composition",
2511
2528
  status: "final",
2512
2529
  type: {
@@ -2517,7 +2534,7 @@ var ComprehensiveIPSCompositionBuilder = class {
2517
2534
  }]
2518
2535
  },
2519
2536
  subject: {
2520
- reference: `Patient/${this.patient.id}`
2537
+ reference: `Patient/${primaryPatientId}`
2521
2538
  },
2522
2539
  author: [{
2523
2540
  reference: `Organization/${authorOrganizationId}`,
@@ -2529,7 +2546,7 @@ var ComprehensiveIPSCompositionBuilder = class {
2529
2546
  section: this.sections,
2530
2547
  text: await NarrativeGenerator.generateNarrativeAsync(
2531
2548
  "Patient" /* PATIENT */,
2532
- [this.patient],
2549
+ this.patients,
2533
2550
  timezone,
2534
2551
  true
2535
2552
  )
@@ -2548,9 +2565,11 @@ var ComprehensiveIPSCompositionBuilder = class {
2548
2565
  fullUrl: `${baseUrl}/Composition/${composition.id}`,
2549
2566
  resource: composition
2550
2567
  });
2551
- bundle.entry?.push({
2552
- fullUrl: `${baseUrl}/Patient/${this.patient.id}`,
2553
- resource: this.patient
2568
+ this.patients.forEach((patient) => {
2569
+ bundle.entry?.push({
2570
+ fullUrl: `${baseUrl}/Patient/${patient.id}`,
2571
+ resource: patient
2572
+ });
2554
2573
  });
2555
2574
  this.resources.forEach((resource) => {
2556
2575
  if (resource.resourceType !== "Patient") {