@imranq2/fhirpatientsummary 1.0.26 → 1.0.28

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
@@ -115,6 +115,8 @@ var VITAL_SIGNS_SUMMARY_COMPONENT_MAP = {
115
115
  "Diastolic Blood Pressure": "valueRatio.denominator.value",
116
116
  "Default": "valueString"
117
117
  };
118
+ var RESULT_SUMMARY_OBSERVATION_CATEGORIES = ["laboratory", "Lab", "LAB"];
119
+ var RESULT_SUMMARY_OBSERVATION_DATE_FILTER = 2 * 365 * 24 * 60 * 60 * 1e3;
118
120
  var IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM = "https://fhir.icanbwell.com/4_0_0/CodeSystem/composition/";
119
121
 
120
122
  // src/structures/ips_section_resource_map.ts
@@ -134,7 +136,7 @@ var IPSSectionResourceFilters = {
134
136
  // Includes DeviceUseStatement. Device is needed for linked device details
135
137
  ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: (resource) => ["DeviceUseStatement", "Device"].includes(resource.resourceType),
136
138
  // Only include finalized diagnostic reports
137
- ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
139
+ ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => RESULT_SUMMARY_OBSERVATION_CATEGORIES.includes(c.code))),
138
140
  // Only include completed procedures
139
141
  ["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Procedure" && resource.status === "completed",
140
142
  // Only include social history Observations
@@ -156,7 +158,7 @@ var IPSSectionSummaryCompositionFilter = {
156
158
  ["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && c.code === "careplan_summary_document"),
157
159
  ["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && c.code === "immunization_summary_document"),
158
160
  ["MedicationSummarySection" /* MEDICATIONS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && c.code === "medication_summary_document"),
159
- ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && ["lab_summary_document", "diagnosticreportlab_summary_document"].includes(c.code)),
161
+ // [IPSSections.DIAGNOSTIC_REPORTS]: (resource) => resource.resourceType === 'Composition' && resource.type?.coding?.some((c: any) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && ["lab_summary_document", "diagnosticreportlab_summary_document"].includes(c.code)),
160
162
  ["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && c.code === "procedure_summary_document")
161
163
  };
162
164
  var IPSSectionResourceHelper = class {
@@ -246,7 +248,7 @@ var TemplateUtilities = class {
246
248
  renderOrganization(orgRef) {
247
249
  const organization = orgRef && this.resolveReference(orgRef);
248
250
  if (organization && organization.resourceType === "Organization" && organization.name) {
249
- return organization.name;
251
+ return this.renderTextAsHtml(organization.name);
250
252
  }
251
253
  return "";
252
254
  }
@@ -258,7 +260,7 @@ var TemplateUtilities = class {
258
260
  renderVaccineManufacturer(immunization) {
259
261
  const organization = immunization.manufacturer && this.resolveReference(immunization.manufacturer);
260
262
  if (organization && organization.resourceType === "Organization" && organization.name) {
261
- return organization.name;
263
+ return this.renderTextAsHtml(organization.name);
262
264
  }
263
265
  return "";
264
266
  }
@@ -297,7 +299,7 @@ var TemplateUtilities = class {
297
299
  */
298
300
  renderMedicationCode(medication) {
299
301
  if (medication && medication.code) {
300
- return this.codeableConcept(medication.code, "display");
302
+ return this.renderTextAsHtml(this.codeableConcept(medication.code, "display"));
301
303
  }
302
304
  return "";
303
305
  }
@@ -308,7 +310,7 @@ var TemplateUtilities = class {
308
310
  */
309
311
  renderDoseNumber(doseNumber) {
310
312
  if (doseNumber && doseNumber.value !== void 0) {
311
- return doseNumber.value.toString();
313
+ return this.renderTextAsHtml(doseNumber.value.toString());
312
314
  }
313
315
  return "";
314
316
  }
@@ -319,7 +321,7 @@ var TemplateUtilities = class {
319
321
  */
320
322
  renderValueUnit(value) {
321
323
  if (value && value.constructor?.name === "Quantity" && value.unit) {
322
- return value.unit;
324
+ return this.renderTextAsHtml(value.unit);
323
325
  }
324
326
  return "";
325
327
  }
@@ -385,7 +387,7 @@ var TemplateUtilities = class {
385
387
  * @returns Comma-separated string of items
386
388
  */
387
389
  safeConcat(list, attr) {
388
- return this.concat(list || [], attr);
390
+ return this.renderTextAsHtml(this.concat(list || [], attr));
389
391
  }
390
392
  /**
391
393
  * Concatenates text from a list of CodeableConcept objects
@@ -402,7 +404,7 @@ var TemplateUtilities = class {
402
404
  items.push(item.text);
403
405
  }
404
406
  }
405
- return items.join(", ");
407
+ return this.renderTextAsHtml(items.join(", "));
406
408
  }
407
409
  /**
408
410
  * Concatenates reaction manifestations
@@ -423,7 +425,7 @@ var TemplateUtilities = class {
423
425
  }
424
426
  }
425
427
  }
426
- return texts.join(", ");
428
+ return this.renderTextAsHtml(texts.join(", "));
427
429
  }
428
430
  /**
429
431
  * Concatenates dose numbers
@@ -440,7 +442,7 @@ var TemplateUtilities = class {
440
442
  doseNumbers.push(item.doseNumberPositiveInt.toString());
441
443
  }
442
444
  }
443
- return doseNumbers.join(", ");
445
+ return this.renderTextAsHtml(doseNumbers.join(", "));
444
446
  }
445
447
  /**
446
448
  * Concatenates dosage routes
@@ -457,7 +459,7 @@ var TemplateUtilities = class {
457
459
  routes.push(item.route.text);
458
460
  }
459
461
  }
460
- return routes.join(", ");
462
+ return this.renderTextAsHtml(routes.join(", "));
461
463
  }
462
464
  /**
463
465
  * Returns the first item from a list of CodeableConcept objects
@@ -466,7 +468,7 @@ var TemplateUtilities = class {
466
468
  */
467
469
  firstFromCodeableConceptList(list) {
468
470
  if (list && Array.isArray(list) && list[0]) {
469
- return this.codeableConcept(list[0], "display");
471
+ return this.renderTextAsHtml(this.codeableConcept(list[0], "display"));
470
472
  }
471
473
  return "";
472
474
  }
@@ -485,7 +487,7 @@ var TemplateUtilities = class {
485
487
  texts.push(item.text);
486
488
  }
487
489
  }
488
- return texts.join(", ");
490
+ return this.renderTextAsHtml(texts.join(", "));
489
491
  }
490
492
  /**
491
493
  * Renders component codes
@@ -620,9 +622,18 @@ var TemplateUtilities = class {
620
622
  }
621
623
  return status;
622
624
  }
625
+ formatFloatValue(value) {
626
+ if (typeof value === "number") {
627
+ return value.toFixed(2).replace(/\.?0+$/, "");
628
+ } else if (typeof value === "string" && !isNaN(Number(value))) {
629
+ return parseFloat(value).toFixed(2).replace(/\.?0+$/, "");
630
+ }
631
+ return value;
632
+ }
623
633
  extractObservationSummaryValue(data, timezone) {
624
634
  if (data["valueQuantity.value"] !== void 0) {
625
- const value = data["valueQuantity.value"];
635
+ let value = data["valueQuantity.value"];
636
+ value = this.formatFloatValue(value);
626
637
  const unit = data["valueQuantity.unit"];
627
638
  return unit ? `${value} ${unit}` : `${value}`;
628
639
  }
@@ -639,7 +650,9 @@ var TemplateUtilities = class {
639
650
  return String(data["valueBoolean"]);
640
651
  }
641
652
  if (data["valueInteger"] !== void 0) {
642
- return String(data["valueInteger"]);
653
+ let value = String(data["valueInteger"]);
654
+ value = this.formatFloatValue(value);
655
+ return value;
643
656
  }
644
657
  if (data["valueDateTime"] !== void 0) {
645
658
  return this.renderTime(data["valueDateTime"], timezone);
@@ -659,7 +672,8 @@ var TemplateUtilities = class {
659
672
  return this.renderTime(data["valueTime"], timezone);
660
673
  }
661
674
  if (data["valueSampledData.origin.value"] !== void 0 || data["valueSampledData.origin.unit"] !== void 0) {
662
- const originValue = data["valueSampledData.origin.value"];
675
+ let originValue = data["valueSampledData.origin.value"];
676
+ originValue = this.formatFloatValue(originValue);
663
677
  const originUnit = data["valueSampledData.origin.unit"];
664
678
  let result = "";
665
679
  if (originValue !== void 0 && originUnit !== void 0) {
@@ -669,10 +683,10 @@ var TemplateUtilities = class {
669
683
  } else if (originUnit !== void 0) {
670
684
  result = `${originUnit}`;
671
685
  }
672
- const period = data["valueSampledData.period"];
673
- const factor = data["valueSampledData.factor"];
674
- const lowerLimit = data["valueSampledData.lowerLimit"];
675
- const upperLimit = data["valueSampledData.upperLimit"];
686
+ const period = this.formatFloatValue(data["valueSampledData.period"]);
687
+ const factor = this.formatFloatValue(data["valueSampledData.factor"]);
688
+ const lowerLimit = this.formatFloatValue(data["valueSampledData.lowerLimit"]);
689
+ const upperLimit = this.formatFloatValue(data["valueSampledData.upperLimit"]);
676
690
  const sampledData = data["valueSampledData.data"];
677
691
  const extras = [];
678
692
  if (period !== void 0) extras.push(`period: ${period}`);
@@ -708,14 +722,14 @@ var TemplateUtilities = class {
708
722
  if (data["valueRatio.numerator.value"] !== void 0 || data["valueRatio.denominator.value"] !== void 0) {
709
723
  let ratio = "";
710
724
  if (data["valueRatio.numerator.value"] !== void 0) {
711
- ratio += `${data["valueRatio.numerator.value"]}`;
725
+ ratio += `${this.formatFloatValue(data["valueRatio.numerator.value"])}`;
712
726
  if (data["valueRatio.numerator.unit"] !== void 0) {
713
727
  ratio += ` ${data["valueRatio.numerator.unit"]}`;
714
728
  }
715
729
  }
716
730
  if (data["valueRatio.denominator.value"] !== void 0) {
717
731
  ratio += " / ";
718
- ratio += `${data["valueRatio.denominator.value"]}`;
732
+ ratio += `${this.formatFloatValue(data["valueRatio.denominator.value"])}`;
719
733
  if (data["valueRatio.denominator.unit"] !== void 0) {
720
734
  ratio += ` ${data["valueRatio.denominator.unit"]}`;
721
735
  }
@@ -805,9 +819,11 @@ var TemplateUtilities = class {
805
819
  return "";
806
820
  }
807
821
  /**
808
- * Renders text as HTML, escaping special characters and replacing newlines with <br />
809
- * @param text - The text to render
810
- * @private
822
+ * Public method to render plain text as HTML, escaping special characters and replacing newlines with <br />.
823
+ * This method should be used whenever displaying user-supplied or FHIR resource text in HTML to prevent XSS vulnerabilities
824
+ * and to preserve formatting. Use this in templates or UI components that need to safely display multi-line or arbitrary text.
825
+ * @param text - The text to render as HTML
826
+ * @returns The HTML-safe string with newlines converted to <br />
811
827
  */
812
828
  renderTextAsHtml(text) {
813
829
  if (!text || text.trim() === "") {
@@ -871,7 +887,7 @@ var TemplateUtilities = class {
871
887
  dateTime = import_luxon.DateTime.fromISO(String(dateValue));
872
888
  }
873
889
  if (!dateTime.isValid) {
874
- return String(dateValue);
890
+ return this.renderTextAsHtml(String(dateValue));
875
891
  }
876
892
  if (dateOnly) {
877
893
  dateTime = dateTime.toUTC();
@@ -887,9 +903,9 @@ var TemplateUtilities = class {
887
903
  hour12: true,
888
904
  timeZoneName: "short"
889
905
  };
890
- return dateTime.toLocaleString(formatOptions);
906
+ return this.renderTextAsHtml(dateTime.toLocaleString(formatOptions));
891
907
  } catch {
892
- return String(dateValue);
908
+ return this.renderTextAsHtml(String(dateValue));
893
909
  }
894
910
  }
895
911
  /**
@@ -969,6 +985,313 @@ var TemplateUtilities = class {
969
985
 
970
986
  // src/constants.ts
971
987
  var ADDRESS_SIMILARITY_THRESHOLD = 70;
988
+ var LAB_LOINC_MAP = {
989
+ // METABOLIC PANELS (VERY COMMONLY ORDERED)
990
+ "Basic Metabolic Panel": [
991
+ "24321-2",
992
+ // Basic metabolic 2000 panel - Serum or Plasma
993
+ "51990-0"
994
+ // Basic metabolic 2000 panel - Blood
995
+ ],
996
+ "Comprehensive Metabolic Panel": [
997
+ "24323-8"
998
+ // Comprehensive metabolic 2000 panel - Serum or Plasma
999
+ ],
1000
+ // CBC COMPONENTS
1001
+ Hemoglobin: [
1002
+ "718-7"
1003
+ // Hemoglobin [Mass/volume] in Blood
1004
+ ],
1005
+ Hematocrit: [
1006
+ "4544-3"
1007
+ // Hematocrit [Volume Fraction] of Blood by Automated count
1008
+ ],
1009
+ "White Blood Cell Count": [
1010
+ "6690-2"
1011
+ // Leukocytes [///volume] in Blood by Automated count
1012
+ ],
1013
+ "Platelet Count": [
1014
+ "777-3"
1015
+ // Platelets [///volume] in Blood by Automated count
1016
+ ],
1017
+ "Complete Blood Count": [
1018
+ "58410-2",
1019
+ // CBC panel - Blood by Automated count
1020
+ "57021-8",
1021
+ // CBC W Auto Differential panel - Blood
1022
+ "69738-3"
1023
+ // CBC W Auto Differential panel - Blood by Automated count
1024
+ ],
1025
+ // CHEMISTRY - GLUCOSE
1026
+ Glucose: [
1027
+ "2345-7",
1028
+ // Glucose [Mass/volume] in Serum or Plasma
1029
+ "1558-6",
1030
+ // Fasting glucose [Mass/volume] in Serum or Plasma
1031
+ "2339-0"
1032
+ // Glucose [Mass/volume] in Blood
1033
+ ],
1034
+ // RENAL FUNCTION
1035
+ Creatinine: [
1036
+ "2160-0"
1037
+ // Creatinine [Mass/volume] in Serum or Plasma
1038
+ ],
1039
+ "Blood Urea Nitrogen": [
1040
+ "3094-0",
1041
+ // Urea nitrogen [Mass/volume] in Serum or Plasma
1042
+ "6299-2"
1043
+ // Urea nitrogen [Mass/volume] in Blood
1044
+ ],
1045
+ // ELECTROLYTES
1046
+ Sodium: [
1047
+ "2951-2"
1048
+ // Sodium [Moles/volume] in Serum or Plasma
1049
+ ],
1050
+ Potassium: [
1051
+ "2823-3"
1052
+ // Potassium [Moles/volume] in Serum or Plasma
1053
+ ],
1054
+ Chloride: [
1055
+ "2075-0"
1056
+ // Chloride [Moles/volume] in Serum or Plasma
1057
+ ],
1058
+ Calcium: [
1059
+ "17861-6",
1060
+ // Calcium [Mass/volume] in Serum or Plasma
1061
+ "1994-3"
1062
+ // Calcium.ionized [Moles/volume] in Serum or Plasma
1063
+ ],
1064
+ Magnesium: [
1065
+ "19123-9"
1066
+ // Magnesium [Mass/volume] in Serum or Plasma
1067
+ ],
1068
+ Phosphate: [
1069
+ "14879-1"
1070
+ // Phosphate [Mass/volume] in Serum or Plasma
1071
+ ],
1072
+ // PROTEINS
1073
+ Albumin: [
1074
+ "1751-7"
1075
+ // Albumin [Mass/volume] in Serum or Plasma
1076
+ ],
1077
+ "Total Protein": [
1078
+ "2885-2"
1079
+ // Protein [Mass/volume] in Serum or Plasma
1080
+ ],
1081
+ // LIVER FUNCTION
1082
+ Bilirubin: [
1083
+ "1975-2",
1084
+ // Bilirubin.total [Mass/volume] in Serum or Plasma
1085
+ "1968-7",
1086
+ // Bilirubin.direct [Mass/volume] in Serum or Plasma
1087
+ "1971-1"
1088
+ // Bilirubin.indirect [Mass/volume] in Serum or Plasma
1089
+ ],
1090
+ "Alkaline Phosphatase": [
1091
+ "6768-6"
1092
+ // Alkaline phosphatase [Enzymatic activity/volume] in Serum or Plasma
1093
+ ],
1094
+ AST: [
1095
+ "1920-8"
1096
+ // Aspartate aminotransferase [Enzymatic activity/volume] in Serum or Plasma
1097
+ ],
1098
+ ALT: [
1099
+ "1742-6"
1100
+ // Alanine aminotransferase [Enzymatic activity/volume] in Serum or Plasma
1101
+ ],
1102
+ GGT: [
1103
+ "2324-2"
1104
+ // Gamma glutamyl transferase [Enzymatic activity/volume] in Serum or Plasma
1105
+ ],
1106
+ // ENDOCRINE
1107
+ TSH: [
1108
+ "3016-3"
1109
+ // Thyrotropin [Units/volume] in Serum or Plasma
1110
+ ],
1111
+ "Free T4": [
1112
+ "3024-7"
1113
+ // Thyroxine (T4) free [Mass/volume] in Serum or Plasma
1114
+ ],
1115
+ "Total T4": [
1116
+ "3026-2"
1117
+ // Thyroxine (T4) [Mass/volume] in Serum or Plasma
1118
+ ],
1119
+ "Free T3": [
1120
+ "3051-0"
1121
+ // Triiodothyronine (T3) free [Mass/volume] in Serum or Plasma
1122
+ ],
1123
+ "Total T3": [
1124
+ "3053-6"
1125
+ // Triiodothyronine (T3) [Mass/volume] in Serum or Plasma
1126
+ ],
1127
+ HbA1c: [
1128
+ "4548-4",
1129
+ // Hemoglobin A1c/Hemoglobin.total in Blood
1130
+ "17856-6"
1131
+ // Hemoglobin A1c/Hemoglobin.total in Blood by HPLC
1132
+ ],
1133
+ // LIPID PANEL
1134
+ "Lipid Panel": [
1135
+ "24331-1",
1136
+ // Lipid 1996 panel - Serum or Plasma
1137
+ "57698-3"
1138
+ // Lipid panel with direct LDL - Serum or Plasma
1139
+ ],
1140
+ "Cholesterol Total": [
1141
+ "2093-3"
1142
+ // Cholesterol [Mass/volume] in Serum or Plasma
1143
+ ],
1144
+ "HDL Cholesterol": [
1145
+ "2085-9"
1146
+ // Cholesterol in HDL [Mass/volume] in Serum or Plasma
1147
+ ],
1148
+ "LDL Cholesterol": [
1149
+ "13457-7",
1150
+ // Cholesterol in LDL [Mass/volume] in Serum or Plasma by calculation
1151
+ "18262-6"
1152
+ // Cholesterol in LDL [Mass/volume] in Serum or Plasma by Direct assay
1153
+ ],
1154
+ Triglycerides: [
1155
+ "2571-8"
1156
+ // Triglyceride [Mass/volume] in Serum or Plasma
1157
+ ],
1158
+ // COAGULATION STUDIES (COMMONLY MISSING!)
1159
+ PT: [
1160
+ "5902-2"
1161
+ // Prothrombin time (PT)
1162
+ ],
1163
+ INR: [
1164
+ "6301-6"
1165
+ // INR in Platelet poor plasma by Coagulation assay
1166
+ ],
1167
+ PTT: [
1168
+ "3173-2",
1169
+ // aPTT in Blood by Coagulation assay
1170
+ "14979-9"
1171
+ // aPTT in Platelet poor plasma by Coagulation assay
1172
+ ],
1173
+ Fibrinogen: [
1174
+ "3255-7"
1175
+ // Fibrinogen [Mass/volume] in Platelet poor plasma by Coagulation assay
1176
+ ],
1177
+ "D-Dimer": [
1178
+ "48065-7",
1179
+ // D-dimer FEU [Mass/volume] in Platelet poor plasma
1180
+ "48066-5"
1181
+ // D-dimer DDU [Mass/volume] in Platelet poor plasma
1182
+ ],
1183
+ // CARDIAC MARKERS (CRITICAL FOR ER!)
1184
+ "Troponin I": [
1185
+ "10839-9",
1186
+ // Troponin I.cardiac [Mass/volume] in Serum or Plasma
1187
+ "42757-5",
1188
+ // Troponin I.cardiac [Mass/volume] in Blood
1189
+ "89579-7"
1190
+ // Troponin I.cardiac [Mass/volume] in Serum or Plasma by High sensitivity method
1191
+ ],
1192
+ "Troponin T": [
1193
+ "6598-7",
1194
+ // Troponin T.cardiac [Mass/volume] in Serum or Plasma
1195
+ "48425-3"
1196
+ // Troponin T.cardiac [Mass/volume] in Serum or Plasma by High sensitivity method
1197
+ ],
1198
+ BNP: [
1199
+ "30934-4"
1200
+ // BNP [Mass/volume] in Serum or Plasma
1201
+ ],
1202
+ "NT-proBNP": [
1203
+ "33762-6"
1204
+ // NT-proBNP [Mass/volume] in Serum or Plasma
1205
+ ],
1206
+ "CK-MB": [
1207
+ "13969-1"
1208
+ // Creatine kinase.MB [Mass/volume] in Serum or Plasma
1209
+ ],
1210
+ // INFLAMMATORY MARKERS
1211
+ CRP: [
1212
+ "1988-5",
1213
+ // C reactive protein [Mass/volume] in Serum or Plasma
1214
+ "30522-7"
1215
+ // C reactive protein [Mass/volume] in Serum or Plasma by High sensitivity method
1216
+ ],
1217
+ ESR: [
1218
+ "30341-2",
1219
+ // Erythrocyte sedimentation rate by Westergren method
1220
+ "4537-7"
1221
+ // Erythrocyte sedimentation rate
1222
+ ],
1223
+ // VITAMINS & MINERALS (VERY HIGH VOLUME!)
1224
+ "Vitamin D": [
1225
+ "1990-1",
1226
+ // Vitamin D [Mass/volume] in Serum or Plasma (obsolete, but still used)
1227
+ "14635-7",
1228
+ // 25-Hydroxyvitamin D3 [Mass/volume] in Serum or Plasma
1229
+ "62292-8"
1230
+ // 25-Hydroxyvitamin D2+D3 [Mass/volume] in Serum or Plasma
1231
+ ],
1232
+ "Vitamin B12": [
1233
+ "2132-9"
1234
+ // Cobalamin (Vitamin B12) [Mass/volume] in Serum or Plasma
1235
+ ],
1236
+ Folate: [
1237
+ "2284-8",
1238
+ // Folate [Mass/volume] in Serum or Plasma
1239
+ "15152-2"
1240
+ // Folate [Mass/volume] in Red Blood Cells
1241
+ ],
1242
+ Iron: [
1243
+ "2498-4"
1244
+ // Iron [Mass/volume] in Serum or Plasma
1245
+ ],
1246
+ Ferritin: [
1247
+ "2276-4"
1248
+ // Ferritin [Mass/volume] in Serum or Plasma
1249
+ ],
1250
+ TIBC: [
1251
+ "2500-7"
1252
+ // Iron binding capacity [Mass/volume] in Serum or Plasma
1253
+ ],
1254
+ // OTHER COMMON TESTS
1255
+ PSA: [
1256
+ "2857-1",
1257
+ // Prostate specific Ag [Mass/volume] in Serum or Plasma
1258
+ "10886-0"
1259
+ // Prostate specific Ag Free [Mass/volume] in Serum or Plasma
1260
+ ],
1261
+ "Uric Acid": [
1262
+ "3084-1"
1263
+ // Urate [Mass/volume] in Serum or Plasma
1264
+ ],
1265
+ LDH: [
1266
+ "2532-0",
1267
+ // Lactate dehydrogenase [Enzymatic activity/volume] in Serum or Plasma
1268
+ "14804-9"
1269
+ // Lactate dehydrogenase [Enzymatic activity/volume] in Serum or Plasma by Lactate to pyruvate reaction
1270
+ ],
1271
+ Amylase: [
1272
+ "1798-8"
1273
+ // Amylase [Enzymatic activity/volume] in Serum or Plasma
1274
+ ],
1275
+ Lipase: [
1276
+ "3040-3"
1277
+ // Lipase [Enzymatic activity/volume] in Serum or Plasma
1278
+ ],
1279
+ hCG: [
1280
+ "21198-7",
1281
+ // Choriogonadotropin.beta subunit [Units/volume] in Serum or Plasma
1282
+ "2118-8",
1283
+ // Choriogonadotropin (pregnancy test) [Presence] in Serum or Plasma
1284
+ "2106-3"
1285
+ // Choriogonadotropin (pregnancy test) [Presence] in Urine
1286
+ ],
1287
+ // URINALYSIS
1288
+ Urinalysis: [
1289
+ "24357-6",
1290
+ // Urinalysis macro (dipstick) panel - Urine
1291
+ "24356-8"
1292
+ // Urinalysis complete panel - Urine
1293
+ ]
1294
+ };
972
1295
 
973
1296
  // src/narratives/templates/typescript/PatientTemplate.ts
974
1297
  var PatientTemplate = class _PatientTemplate {
@@ -1286,7 +1609,7 @@ var PatientTemplate = class _PatientTemplate {
1286
1609
  const uniqueLanguages = /* @__PURE__ */ new Set();
1287
1610
  const preferredLanguages = /* @__PURE__ */ new Set();
1288
1611
  patient.communication.forEach((comm) => {
1289
- const language = templateUtilities.codeableConcept(comm.language);
1612
+ const language = templateUtilities.renderTextAsHtml(templateUtilities.codeableConcept(comm.language));
1290
1613
  if (language) {
1291
1614
  if (comm.preferred) {
1292
1615
  preferredLanguages.add(language);
@@ -1344,13 +1667,13 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
1344
1667
  for (const columnData of rowData.section ?? []) {
1345
1668
  switch (columnData.title) {
1346
1669
  case "Allergen Name":
1347
- data["allergen"] = columnData.text?.div ?? "";
1670
+ data["allergen"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1348
1671
  break;
1349
1672
  case "Criticality":
1350
- data["criticality"] = columnData.text?.div ?? "";
1673
+ data["criticality"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1351
1674
  break;
1352
1675
  case "Recorded Date":
1353
- data["recordedDate"] = columnData.text?.div ?? "";
1676
+ data["recordedDate"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1354
1677
  break;
1355
1678
  default:
1356
1679
  break;
@@ -1471,11 +1794,11 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
1471
1794
  for (const allergy of allergies) {
1472
1795
  html += `
1473
1796
  <tr id="${templateUtilities.narrativeLinkId(allergy.extension)}">
1474
- <td class="Name"><span class="AllergenName">${templateUtilities.codeableConcept(allergy.code)}</span></td>
1475
- <td class="Status">${templateUtilities.codeableConcept(allergy.clinicalStatus) || "-"}</td>
1476
- <td class="Category">${templateUtilities.safeConcat(allergy.category) || "-"}</td>
1477
- <td class="Reaction">${templateUtilities.concatReactionManifestation(allergy.reaction) || "-"}</td>
1478
- <td class="OnsetDate">${templateUtilities.renderTime(allergy.onsetDateTime, timezone) || "-"}</td>
1797
+ <td class="Name"><span class="AllergenName">${templateUtilities.renderTextAsHtml(templateUtilities.codeableConcept(allergy.code))}</span></td>
1798
+ <td class="Status">${templateUtilities.renderTextAsHtml(templateUtilities.codeableConcept(allergy.clinicalStatus)) || "-"}</td>
1799
+ <td class="Category">${templateUtilities.renderTextAsHtml(templateUtilities.safeConcat(allergy.category)) || "-"}</td>
1800
+ <td class="Reaction">${templateUtilities.renderTextAsHtml(templateUtilities.concatReactionManifestation(allergy.reaction)) || "-"}</td>
1801
+ <td class="OnsetDate">${templateUtilities.renderTextAsHtml(templateUtilities.renderTime(allergy.onsetDateTime, timezone)) || "-"}</td>
1479
1802
  <td class="Comments">${templateUtilities.renderNotes(allergy.note, timezone, { styled: true, warning: true })}</td>`;
1480
1803
  if (includeResolved) {
1481
1804
  let endDate = "-";
@@ -1488,7 +1811,7 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
1488
1811
  }
1489
1812
  }
1490
1813
  html += `
1491
- <td class="ResolvedDate">${endDate}</td>`;
1814
+ <td class="ResolvedDate">${templateUtilities.renderTextAsHtml(endDate)}</td>`;
1492
1815
  }
1493
1816
  html += `</tr>`;
1494
1817
  }
@@ -1511,17 +1834,21 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1511
1834
  * Generate HTML narrative for Medication resources using summary
1512
1835
  * @param resources - FHIR Composition resources
1513
1836
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1837
+ * @param now - Optional current date to use for calculations (defaults to new Date())
1514
1838
  * @returns HTML string for rendering
1515
1839
  */
1516
- generateSummaryNarrative(resources, timezone) {
1840
+ generateSummaryNarrative(resources, timezone, now) {
1517
1841
  const templateUtilities = new TemplateUtilities(resources);
1518
1842
  let isSummaryCreated = false;
1843
+ const currentDate = now || /* @__PURE__ */ new Date();
1844
+ const twelveMonthsAgo = new Date(currentDate.getFullYear(), currentDate.getMonth() - 12, currentDate.getDate());
1519
1845
  let html = `
1520
1846
  <div>
1521
1847
  <table>
1522
1848
  <thead>
1523
1849
  <tr>
1524
1850
  <th>Medication</th>
1851
+ <th>Status</th>
1525
1852
  <th>Sig</th>
1526
1853
  <th>Days of Supply</th>
1527
1854
  <th>Refills</th>
@@ -1535,40 +1862,48 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
1535
1862
  for (const columnData of rowData.section ?? []) {
1536
1863
  switch (columnData.title) {
1537
1864
  case "Medication Name":
1538
- data["medication"] = columnData.text?.div ?? "";
1865
+ data["medication"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1539
1866
  break;
1540
1867
  case "Status":
1541
- data["status"] = columnData.text?.div ?? "";
1868
+ data["status"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1542
1869
  break;
1543
1870
  case "Prescriber Instruction":
1544
- data["sig-prescriber"] = columnData.text?.div ?? "";
1871
+ data["sig-prescriber"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1545
1872
  break;
1546
1873
  case "Pharmacy Instruction":
1547
- data["sig-pharmacy"] = columnData.text?.div ?? "";
1874
+ data["sig-pharmacy"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1548
1875
  break;
1549
1876
  case "Days Of Supply":
1550
- data["daysOfSupply"] = columnData.text?.div ?? "";
1877
+ data["daysOfSupply"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1551
1878
  break;
1552
1879
  case "Refills Remaining":
1553
- data["refills"] = columnData.text?.div ?? "";
1880
+ data["refills"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1554
1881
  break;
1555
1882
  case "Authored On Date":
1556
- data["startDate"] = columnData.text?.div ?? "";
1883
+ data["startDate"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1557
1884
  break;
1558
1885
  default:
1559
1886
  break;
1560
1887
  }
1561
1888
  }
1562
- if (data["status"] === "active") {
1889
+ let startDateObj;
1890
+ if (data["startDate"]) {
1891
+ startDateObj = new Date(data["startDate"]);
1892
+ if (isNaN(startDateObj.getTime())) {
1893
+ startDateObj = void 0;
1894
+ }
1895
+ }
1896
+ if (data["status"] === "active" || startDateObj && startDateObj >= twelveMonthsAgo) {
1563
1897
  isSummaryCreated = true;
1564
1898
  html += `
1565
- <tr>
1566
- <td>${data["medication"]}</td>
1567
- <td>${data["sig-prescriber"] || data["sig-pharmacy"]}</td>
1568
- <td>${data["daysOfSupply"]}</td>
1569
- <td>${data["refills"]}</td>
1570
- <td>${templateUtilities.renderTime(data["startDate"], timezone)}</td>
1571
- </tr>`;
1899
+ <tr>
1900
+ <td>${templateUtilities.renderTextAsHtml(data["medication"])}</td>
1901
+ <td>${templateUtilities.renderTextAsHtml(data["status"])}</td>
1902
+ <td>${templateUtilities.renderTextAsHtml(data["sig-prescriber"] || data["sig-pharmacy"])}</td>
1903
+ <td>${templateUtilities.renderTextAsHtml(data["daysOfSupply"])}</td>
1904
+ <td>${templateUtilities.renderTextAsHtml(data["refills"])}</td>
1905
+ <td>${templateUtilities.renderTime(data["startDate"], timezone)}</td>
1906
+ </tr>`;
1572
1907
  }
1573
1908
  }
1574
1909
  }
@@ -1790,13 +2125,13 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
1790
2125
  for (const columnData of rowData.section ?? []) {
1791
2126
  switch (columnData.title) {
1792
2127
  case "Immunization Name":
1793
- data["immunization"] = columnData.text?.div ?? "";
2128
+ data["immunization"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1794
2129
  break;
1795
2130
  case "Status":
1796
- data["status"] = columnData.text?.div ?? "";
2131
+ data["status"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1797
2132
  break;
1798
2133
  case "occurrenceDateTime":
1799
- data["occurrenceDateTime"] = columnData.text?.div ?? "";
2134
+ data["occurrenceDateTime"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1800
2135
  break;
1801
2136
  default:
1802
2137
  break;
@@ -1847,7 +2182,7 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
1847
2182
  const imm = resourceItem;
1848
2183
  html += `
1849
2184
  <tr id="${templateUtilities.narrativeLinkId(imm)}">
1850
- <td>${templateUtilities.codeableConcept(imm.vaccineCode)}</td>
2185
+ <td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConcept(imm.vaccineCode))}</td>
1851
2186
  <td>${imm.status || ""}</td>
1852
2187
  <td>${templateUtilities.concatDoseNumber(imm.protocolApplied)}</td>
1853
2188
  <td>${templateUtilities.renderVaccineManufacturer(imm)}</td>
@@ -1903,7 +2238,7 @@ var ProblemListTemplate = class _ProblemListTemplate {
1903
2238
  <tbody>`;
1904
2239
  const addedConditionCodes = /* @__PURE__ */ new Set();
1905
2240
  for (const cond of activeConditions) {
1906
- const conditionCode = templateUtilities.codeableConcept(cond.code);
2241
+ const conditionCode = templateUtilities.renderTextAsHtml(templateUtilities.codeableConcept(cond.code));
1907
2242
  if (!addedConditionCodes.has(conditionCode)) {
1908
2243
  addedConditionCodes.add(conditionCode);
1909
2244
  html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
@@ -1963,7 +2298,7 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
1963
2298
  const vitalData = {};
1964
2299
  for (const component of columnData.section?.[0]?.section ?? []) {
1965
2300
  if (component.title) {
1966
- vitalData[component.title] = component.text?.div ?? "";
2301
+ vitalData[component.title] = templateUtilities.renderTextAsHtml(component.text?.div ?? "");
1967
2302
  }
1968
2303
  }
1969
2304
  const vitalValue = templateUtilities.extractObservationSummaryValue(
@@ -1975,7 +2310,7 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
1975
2310
  data[dataKey] = vitalValue;
1976
2311
  }
1977
2312
  }
1978
- data[columnTitle] = columnData.text?.div ?? "";
2313
+ data[columnTitle] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1979
2314
  }
1980
2315
  }
1981
2316
  isSummaryCreated = true;
@@ -2025,7 +2360,7 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
2025
2360
  for (const obs of observations) {
2026
2361
  html += `
2027
2362
  <tr id="${templateUtilities.narrativeLinkId(obs)}">
2028
- <td>${templateUtilities.codeableConcept(obs.code, "display")}</td>
2363
+ <td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConcept(obs.code, "display"))}</td>
2029
2364
  <td>${templateUtilities.extractObservationValue(obs)}</td>
2030
2365
  <td>${templateUtilities.extractObservationValueUnit(obs)}</td>
2031
2366
  <td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
@@ -2079,10 +2414,10 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
2079
2414
  for (const dus of deviceStatements) {
2080
2415
  html += `
2081
2416
  <tr id="${templateUtilities.narrativeLinkId(dus)}">
2082
- <td>${templateUtilities.renderDevice(dus.device)}</td>
2083
- <td>${dus.status || ""}</td>
2417
+ <td>${templateUtilities.renderTextAsHtml(templateUtilities.renderDevice(dus.device))}</td>
2418
+ <td>${templateUtilities.renderTextAsHtml(dus.status || "")}</td>
2084
2419
  <td>${templateUtilities.renderNotes(dus.note, timezone)}</td>
2085
- <td>${templateUtilities.renderRecorded(dus.recordedOn, timezone)}</td>
2420
+ <td>${templateUtilities.renderTextAsHtml(templateUtilities.renderRecorded(dus.recordedOn, timezone))}</td>
2086
2421
  </tr>`;
2087
2422
  }
2088
2423
  html += `
@@ -2093,6 +2428,12 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
2093
2428
  };
2094
2429
 
2095
2430
  // src/narratives/templates/typescript/DiagnosticResultsTemplate.ts
2431
+ var loincToLabName = {};
2432
+ for (const [labName, loincCodes] of Object.entries(LAB_LOINC_MAP)) {
2433
+ for (const code of loincCodes) {
2434
+ loincToLabName[code] = labName;
2435
+ }
2436
+ }
2096
2437
  var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
2097
2438
  /**
2098
2439
  * Generate HTML narrative for Diagnostic Results
@@ -2212,149 +2553,150 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
2212
2553
  * Helper function to extract observation field data
2213
2554
  * @param column - Column data from the summary
2214
2555
  * @param targetData - Record to populate with extracted data
2556
+ * @param templateUtilities - Instance of TemplateUtilities for utility functions
2215
2557
  */
2216
- extractSummaryObservationFields(column, targetData) {
2558
+ extractSummaryObservationFields(column, targetData, templateUtilities) {
2217
2559
  switch (column.title) {
2218
2560
  case "Labs Name":
2219
- targetData["code"] = column.text?.div ?? "";
2561
+ targetData["code"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2220
2562
  break;
2221
2563
  case "effectiveDateTime":
2222
- targetData["effectiveDateTime"] = column.text?.div ?? "";
2564
+ targetData["effectiveDateTime"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2223
2565
  break;
2224
2566
  case "effectivePeriod.start":
2225
- targetData["effectivePeriodStart"] = column.text?.div ?? "";
2567
+ targetData["effectivePeriodStart"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2226
2568
  break;
2227
2569
  case "effectivePeriod.end":
2228
- targetData["effectivePeriodEnd"] = column.text?.div ?? "";
2570
+ targetData["effectivePeriodEnd"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2229
2571
  break;
2230
2572
  // valueQuantity
2231
2573
  case "valueQuantity.value":
2232
- targetData["value"] = column.text?.div ?? "";
2574
+ targetData["value"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2233
2575
  targetData["valueType"] = "valueQuantity";
2234
2576
  break;
2235
2577
  case "valueQuantity.unit":
2236
- targetData["unit"] = column.text?.div ?? "";
2578
+ targetData["unit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2237
2579
  break;
2238
2580
  // valueCodeableConcept
2239
2581
  case "valueCodeableConcept.text":
2240
- targetData["value"] = column.text?.div ?? "";
2582
+ targetData["value"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2241
2583
  targetData["valueType"] = "valueCodeableConcept";
2242
2584
  break;
2243
2585
  case "valueCodeableConcept.coding.display":
2244
2586
  if (!targetData["value"]) {
2245
- targetData["value"] = column.text?.div ?? "";
2587
+ targetData["value"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2246
2588
  targetData["valueType"] = "valueCodeableConcept";
2247
2589
  }
2248
2590
  break;
2249
2591
  // valueString
2250
2592
  case "valueString":
2251
- targetData["value"] = column.text?.div ?? "";
2593
+ targetData["value"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2252
2594
  targetData["valueType"] = "valueString";
2253
2595
  break;
2254
2596
  // valueBoolean
2255
2597
  case "valueBoolean":
2256
- targetData["value"] = column.text?.div ?? "";
2598
+ targetData["value"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2257
2599
  targetData["valueType"] = "valueBoolean";
2258
2600
  break;
2259
2601
  // valueInteger
2260
2602
  case "valueInteger":
2261
- targetData["value"] = column.text?.div ?? "";
2603
+ targetData["value"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2262
2604
  targetData["valueType"] = "valueInteger";
2263
2605
  break;
2264
2606
  // valueDateTime
2265
2607
  case "valueDateTime":
2266
- targetData["value"] = column.text?.div ?? "";
2608
+ targetData["value"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2267
2609
  targetData["valueType"] = "valueDateTime";
2268
2610
  break;
2269
2611
  // valuePeriod
2270
2612
  case "valuePeriod.start":
2271
- targetData["valuePeriodStart"] = column.text?.div ?? "";
2613
+ targetData["valuePeriodStart"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2272
2614
  targetData["valueType"] = "valuePeriod";
2273
2615
  break;
2274
2616
  case "valuePeriod.end":
2275
- targetData["valuePeriodEnd"] = column.text?.div ?? "";
2617
+ targetData["valuePeriodEnd"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2276
2618
  targetData["valueType"] = "valuePeriod";
2277
2619
  break;
2278
2620
  // valueTime
2279
2621
  case "valueTime":
2280
- targetData["value"] = column.text?.div ?? "";
2622
+ targetData["value"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2281
2623
  targetData["valueType"] = "valueTime";
2282
2624
  break;
2283
2625
  // valueSampledData
2284
2626
  case "valueSampledData.origin.value":
2285
- targetData["sampledDataOriginValue"] = column.text?.div ?? "";
2627
+ targetData["sampledDataOriginValue"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2286
2628
  targetData["valueType"] = "valueSampledData";
2287
2629
  break;
2288
2630
  case "valueSampledData.origin.unit":
2289
- targetData["sampledDataOriginUnit"] = column.text?.div ?? "";
2631
+ targetData["sampledDataOriginUnit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2290
2632
  break;
2291
2633
  case "valueSampledData.period":
2292
- targetData["sampledDataPeriod"] = column.text?.div ?? "";
2634
+ targetData["sampledDataPeriod"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2293
2635
  break;
2294
2636
  case "valueSampledData.factor":
2295
- targetData["sampledDataFactor"] = column.text?.div ?? "";
2637
+ targetData["sampledDataFactor"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2296
2638
  break;
2297
2639
  case "valueSampledData.lowerLimit":
2298
- targetData["sampledDataLowerLimit"] = column.text?.div ?? "";
2640
+ targetData["sampledDataLowerLimit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2299
2641
  break;
2300
2642
  case "valueSampledData.upperLimit":
2301
- targetData["sampledDataUpperLimit"] = column.text?.div ?? "";
2643
+ targetData["sampledDataUpperLimit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2302
2644
  break;
2303
2645
  case "valueSampledData.data":
2304
- targetData["sampledDataData"] = column.text?.div ?? "";
2646
+ targetData["sampledDataData"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2305
2647
  break;
2306
2648
  // valueRange
2307
2649
  case "valueRange.low.value":
2308
- targetData["valueRangeLowValue"] = column.text?.div ?? "";
2650
+ targetData["valueRangeLowValue"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2309
2651
  targetData["valueType"] = "valueRange";
2310
2652
  break;
2311
2653
  case "valueRange.low.unit":
2312
- targetData["valueRangeLowUnit"] = column.text?.div ?? "";
2654
+ targetData["valueRangeLowUnit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2313
2655
  break;
2314
2656
  case "valueRange.high.value":
2315
- targetData["valueRangeHighValue"] = column.text?.div ?? "";
2657
+ targetData["valueRangeHighValue"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2316
2658
  break;
2317
2659
  case "valueRange.high.unit":
2318
- targetData["valueRangeHighUnit"] = column.text?.div ?? "";
2660
+ targetData["valueRangeHighUnit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2319
2661
  break;
2320
2662
  // valueRatio
2321
2663
  case "valueRatio.numerator.value":
2322
- targetData["valueRatioNumeratorValue"] = column.text?.div ?? "";
2664
+ targetData["valueRatioNumeratorValue"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2323
2665
  targetData["valueType"] = "valueRatio";
2324
2666
  break;
2325
2667
  case "valueRatio.numerator.unit":
2326
- targetData["valueRatioNumeratorUnit"] = column.text?.div ?? "";
2668
+ targetData["valueRatioNumeratorUnit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2327
2669
  break;
2328
2670
  case "valueRatio.denominator.value":
2329
- targetData["valueRatioDenominatorValue"] = column.text?.div ?? "";
2671
+ targetData["valueRatioDenominatorValue"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2330
2672
  break;
2331
2673
  case "valueRatio.denominator.unit":
2332
- targetData["valueRatioDenominatorUnit"] = column.text?.div ?? "";
2674
+ targetData["valueRatioDenominatorUnit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2333
2675
  break;
2334
2676
  // referenceRange
2335
2677
  case "referenceRange.low.value":
2336
- targetData["referenceRangeLow"] = column.text?.div ?? "";
2678
+ targetData["referenceRangeLow"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2337
2679
  break;
2338
2680
  case "referenceRange.low.unit":
2339
- targetData["referenceRangeLowUnit"] = column.text?.div ?? "";
2681
+ targetData["referenceRangeLowUnit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2340
2682
  break;
2341
2683
  case "referenceRange.high.value":
2342
- targetData["referenceRangeHigh"] = column.text?.div ?? "";
2684
+ targetData["referenceRangeHigh"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2343
2685
  break;
2344
2686
  case "referenceRange.high.unit":
2345
- targetData["referenceRangeHighUnit"] = column.text?.div ?? "";
2687
+ targetData["referenceRangeHighUnit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2346
2688
  break;
2347
2689
  case "referenceRange.age.low.value":
2348
- targetData["referenceRangeAgeLowValue"] = column.text?.div ?? "";
2690
+ targetData["referenceRangeAgeLowValue"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2349
2691
  break;
2350
2692
  case "referenceRange.age.low.unit":
2351
- targetData["referenceRangeAgeLowUnit"] = column.text?.div ?? "";
2693
+ targetData["referenceRangeAgeLowUnit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2352
2694
  break;
2353
2695
  case "referenceRange.age.high.value":
2354
- targetData["referenceRangeAgeHighValue"] = column.text?.div ?? "";
2696
+ targetData["referenceRangeAgeHighValue"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2355
2697
  break;
2356
2698
  case "referenceRange.age.high.unit":
2357
- targetData["referenceRangeAgeHighUnit"] = column.text?.div ?? "";
2699
+ targetData["referenceRangeAgeHighUnit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
2358
2700
  break;
2359
2701
  default:
2360
2702
  break;
@@ -2407,28 +2749,28 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
2407
2749
  for (const componentSection of columnData.section) {
2408
2750
  const componentData = {};
2409
2751
  for (const nestedColumn of componentSection.section ?? []) {
2410
- this.extractSummaryObservationFields(nestedColumn, componentData);
2752
+ this.extractSummaryObservationFields(nestedColumn, componentData, templateUtilities);
2411
2753
  }
2412
2754
  if (Object.keys(componentData).length > 0) {
2413
2755
  components.push(componentData);
2414
2756
  }
2415
2757
  }
2416
2758
  } else {
2417
- this.extractSummaryObservationFields(columnData, data);
2759
+ this.extractSummaryObservationFields(columnData, data, templateUtilities);
2418
2760
  }
2419
2761
  } else if (resourceItem.title === "DiagnosticReportLab Summary Grouped by DiagnosticReport|Lab Code") {
2420
2762
  switch (columnData.title) {
2421
2763
  case "Diagnostic Report Name":
2422
- data["report"] = columnData.text?.div ?? "";
2764
+ data["report"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
2423
2765
  break;
2424
2766
  case "Performer":
2425
- data["performer"] = columnData.text?.div ?? "";
2767
+ data["performer"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
2426
2768
  break;
2427
2769
  case "Issued Date":
2428
- data["issued"] = columnData.text?.div ?? "";
2770
+ data["issued"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
2429
2771
  break;
2430
2772
  case "Status":
2431
- data["status"] = columnData.text?.div ?? "";
2773
+ data["status"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
2432
2774
  break;
2433
2775
  default:
2434
2776
  break;
@@ -2453,8 +2795,8 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
2453
2795
  observationhtml += `
2454
2796
  <tr>
2455
2797
  <td>${componentCode}</td>
2456
- <td>${component["formattedValue"] ?? "-"}</td>
2457
- <td>${component["referenceRange"]?.trim() ?? "-"}</td>
2798
+ <td>${templateUtilities.renderTextAsHtml(component["formattedValue"]) ?? "-"}</td>
2799
+ <td>${templateUtilities.renderTextAsHtml(component["referenceRange"])?.trim() ?? "-"}</td>
2458
2800
  <td>${date ?? "-"}</td>
2459
2801
  </tr>`;
2460
2802
  }
@@ -2467,8 +2809,8 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
2467
2809
  observationhtml += `
2468
2810
  <tr>
2469
2811
  <td>${data["code"] ?? "-"}</td>
2470
- <td>${data["formattedValue"] ?? "-"}</td>
2471
- <td>${data["referenceRange"]?.trim() ?? "-"}</td>
2812
+ <td>${templateUtilities.renderTextAsHtml(data["formattedValue"]) ?? "-"}</td>
2813
+ <td>${templateUtilities.renderTextAsHtml(data["referenceRange"])?.trim() ?? "-"}</td>
2472
2814
  <td>${date ?? "-"}</td>
2473
2815
  </tr>`;
2474
2816
  }
@@ -2519,30 +2861,80 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
2519
2861
  const observations = this.getObservations(resources);
2520
2862
  if (observations.length > 0) {
2521
2863
  observations.sort((a, b) => {
2522
- const dateA = a.effectiveDateTime || a.effectivePeriod?.start || a.effectivePeriod?.end;
2523
- const dateB = b.effectiveDateTime || b.effectivePeriod?.start || b.effectivePeriod?.end;
2524
- return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
2864
+ const dateA = this.getObservationDate(a);
2865
+ const dateB = this.getObservationDate(b);
2866
+ return dateA && dateB ? dateB.getTime() - dateA.getTime() : 0;
2525
2867
  });
2868
+ this.filterObservationForLoincCodes(observations);
2526
2869
  html += this.renderObservations(templateUtilities, observations, timezone);
2527
2870
  }
2528
- const diagnosticReports = this.getDiagnosticReports(resources);
2529
- if (diagnosticReports.length > 0) {
2530
- diagnosticReports.sort((a, b) => {
2531
- const dateA = a.issued;
2532
- const dateB = b.issued;
2533
- return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
2534
- });
2535
- html += this.renderDiagnosticReports(templateUtilities, diagnosticReports, timezone);
2871
+ if (process.env.ENABLE_DIAGNOSTIC_REPORTS_IN_SUMMARY === "true") {
2872
+ const diagnosticReports = this.getDiagnosticReports(resources);
2873
+ if (diagnosticReports.length > 0) {
2874
+ diagnosticReports.sort((a, b) => {
2875
+ const dateA = a.issued;
2876
+ const dateB = b.issued;
2877
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
2878
+ });
2879
+ html += this.renderDiagnosticReports(templateUtilities, diagnosticReports, timezone);
2880
+ }
2536
2881
  }
2537
2882
  return html;
2538
2883
  }
2884
+ static filterObservationForLoincCodes(observations) {
2885
+ const labsAdded = /* @__PURE__ */ new Set();
2886
+ const filteredObservations = [];
2887
+ for (const obs of observations) {
2888
+ const loincCode = this.getObservationLoincCode(obs);
2889
+ if (loincCode && loincToLabName[loincCode]) {
2890
+ const labName = loincToLabName[loincCode];
2891
+ if (!labsAdded.has(labName)) {
2892
+ labsAdded.add(labName);
2893
+ filteredObservations.push(obs);
2894
+ }
2895
+ }
2896
+ }
2897
+ observations.length = 0;
2898
+ observations.push(...filteredObservations);
2899
+ }
2900
+ static getObservationLoincCode(obs) {
2901
+ if (obs.code && obs.code.coding) {
2902
+ for (const coding of obs.code.coding) {
2903
+ if (coding.system && coding.system.toLowerCase().includes("loinc") && coding.code) {
2904
+ return coding.code;
2905
+ }
2906
+ }
2907
+ }
2908
+ return void 0;
2909
+ }
2910
+ static getObservationDate(obs) {
2911
+ let obsDate = void 0;
2912
+ if (obs.effectiveDateTime) {
2913
+ obsDate = new Date(obs.effectiveDateTime);
2914
+ } else if (obs.effectivePeriod) {
2915
+ if (obs.effectivePeriod.start) {
2916
+ obsDate = new Date(obs.effectivePeriod.start);
2917
+ } else if (obs.effectivePeriod.end) {
2918
+ obsDate = new Date(obs.effectivePeriod.end);
2919
+ }
2920
+ }
2921
+ return obsDate;
2922
+ }
2539
2923
  /**
2540
2924
  * Get all Observation resources from the resource array
2541
2925
  * @param resources - FHIR resources array
2542
2926
  * @returns Array of Observation resources
2543
2927
  */
2544
2928
  static getObservations(resources) {
2545
- return resources.filter((resourceItem) => resourceItem.resourceType === "Observation").map((resourceItem) => resourceItem);
2929
+ return resources.filter((resourceItem) => {
2930
+ if (resourceItem.resourceType === "Observation") {
2931
+ const obsDate = this.getObservationDate(resourceItem);
2932
+ if (obsDate && obsDate >= new Date(Date.now() - RESULT_SUMMARY_OBSERVATION_DATE_FILTER)) {
2933
+ return true;
2934
+ }
2935
+ }
2936
+ return false;
2937
+ }).map((resourceItem) => resourceItem);
2546
2938
  }
2547
2939
  /**
2548
2940
  * Get all DiagnosticReport resources from the resource array
@@ -2560,34 +2952,32 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
2560
2952
  * @returns HTML string for rendering
2561
2953
  */
2562
2954
  static renderObservations(templateUtilities, observations, timezone) {
2563
- let html = `
2564
- <h3>Observations</h3>
2955
+ let html = "";
2956
+ if (process.env.ENABLE_DIAGNOSTIC_REPORTS_IN_SUMMARY === "true") {
2957
+ html += `
2958
+ <h3>Observations</h3>`;
2959
+ }
2960
+ html += `
2565
2961
  <table>
2566
2962
  <thead>
2567
2963
  <tr>
2568
2964
  <th>Code</th>
2569
2965
  <th>Result</th>
2570
- <th>Unit</th>
2571
- <th>Interpretation</th>
2572
2966
  <th>Reference Range</th>
2573
- <th>Comments</th>
2574
2967
  <th>Date</th>
2575
2968
  </tr>
2576
2969
  </thead>
2577
2970
  <tbody>`;
2578
2971
  const observationAdded = /* @__PURE__ */ new Set();
2579
2972
  for (const obs of observations) {
2580
- const obsCode = templateUtilities.codeableConcept(obs.code);
2973
+ const obsCode = templateUtilities.renderTextAsHtml(templateUtilities.codeableConcept(obs.code));
2581
2974
  if (!observationAdded.has(obsCode)) {
2582
2975
  observationAdded.add(obsCode);
2583
2976
  html += `
2584
2977
  <tr id="${templateUtilities.narrativeLinkId(obs)}">
2585
2978
  <td>${obsCode}</td>
2586
2979
  <td>${templateUtilities.extractObservationValue(obs)}</td>
2587
- <td>${templateUtilities.extractObservationValueUnit(obs)}</td>
2588
- <td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
2589
2980
  <td>${templateUtilities.concatReferenceRange(obs.referenceRange)}</td>
2590
- <td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
2591
2981
  <td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
2592
2982
  </tr>`;
2593
2983
  }
@@ -2619,7 +3009,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
2619
3009
  <tbody>`;
2620
3010
  const diagnosticReportAdded = /* @__PURE__ */ new Set();
2621
3011
  for (const report of reports) {
2622
- const reportName = templateUtilities.codeableConcept(report.code);
3012
+ const reportName = templateUtilities.renderTextAsHtml(templateUtilities.codeableConcept(report.code));
2623
3013
  if (!diagnosticReportAdded.has(reportName)) {
2624
3014
  diagnosticReportAdded.add(reportName);
2625
3015
  let resultCount = "";
@@ -2733,7 +3123,7 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
2733
3123
  const proc = resourceItem;
2734
3124
  html += `
2735
3125
  <tr id="${templateUtilities.narrativeLinkId(proc)}">
2736
- <td>${templateUtilities.codeableConcept(proc.code, "display")}</td>
3126
+ <td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConcept(proc.code, "display"))}</td>
2737
3127
  <td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
2738
3128
  <td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
2739
3129
  </tr>`;
@@ -2785,7 +3175,7 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
2785
3175
  for (const obs of observations) {
2786
3176
  html += `
2787
3177
  <tr id="${templateUtilities.narrativeLinkId(obs)}">
2788
- <td>${templateUtilities.codeableConcept(obs.code)}</td>
3178
+ <td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConcept(obs.code))}</td>
2789
3179
  <td>${templateUtilities.extractObservationValue(obs)}</td>
2790
3180
  <td>${templateUtilities.extractObservationValueUnit(obs)}</td>
2791
3181
  <td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
@@ -2830,7 +3220,7 @@ var PastHistoryOfIllnessTemplate = class {
2830
3220
  <tbody>`;
2831
3221
  const addedConditionCodes = /* @__PURE__ */ new Set();
2832
3222
  for (const cond of resolvedConditions) {
2833
- const conditionCode = templateUtilities.codeableConcept(cond.code);
3223
+ const conditionCode = templateUtilities.renderTextAsHtml(templateUtilities.codeableConcept(cond.code));
2834
3224
  if (!addedConditionCodes.has(conditionCode)) {
2835
3225
  addedConditionCodes.add(conditionCode);
2836
3226
  html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
@@ -2916,7 +3306,7 @@ var PlanOfCareTemplate = class {
2916
3306
  const data = {};
2917
3307
  for (const columnData of rowData.section ?? []) {
2918
3308
  if (columnData.title) {
2919
- data[columnData.title] = columnData.text?.div ?? "";
3309
+ data[columnData.title] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
2920
3310
  }
2921
3311
  }
2922
3312
  if (data["status"] !== "active") {
@@ -2992,7 +3382,7 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
2992
3382
  <tbody>`;
2993
3383
  const addedConditionCodes = /* @__PURE__ */ new Set();
2994
3384
  for (const cond of activeConditions) {
2995
- const conditionCode = templateUtilities.codeableConcept(cond.code);
3385
+ const conditionCode = templateUtilities.renderTextAsHtml(templateUtilities.codeableConcept(cond.code));
2996
3386
  if (!addedConditionCodes.has(conditionCode)) {
2997
3387
  addedConditionCodes.add(conditionCode);
2998
3388
  html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
@@ -3037,7 +3427,7 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
3037
3427
  if (impression.finding && impression.finding.length > 0) {
3038
3428
  findingsHtml = "<ul>";
3039
3429
  for (const finding of impression.finding) {
3040
- const findingText = finding.itemCodeableConcept ? templateUtilities.codeableConcept(finding.itemCodeableConcept) : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
3430
+ const findingText = finding.itemCodeableConcept ? templateUtilities.renderTextAsHtml(templateUtilities.codeableConcept(finding.itemCodeableConcept)) : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
3041
3431
  const cause = finding.basis || "";
3042
3432
  findingsHtml += `<li>${findingText}${cause ? ` - ${cause}` : ""}</li>`;
3043
3433
  }
@@ -3098,9 +3488,9 @@ var PregnancyTemplate = class _PregnancyTemplate {
3098
3488
  const obs = resource;
3099
3489
  html += `
3100
3490
  <tr id="${templateUtilities.narrativeLinkId(obs)}">
3101
- <td>${templateUtilities.extractPregnancyStatus(obs)}</td>
3491
+ <td>${templateUtilities.renderTextAsHtml(templateUtilities.extractPregnancyStatus(obs))}</td>
3102
3492
  <td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
3103
- <td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
3493
+ <td>${obs.effectiveDateTime ? templateUtilities.renderTextAsHtml(templateUtilities.renderTime(obs.effectiveDateTime, timezone)) : obs.effectivePeriod ? templateUtilities.renderTextAsHtml(templateUtilities.renderPeriod(obs.effectivePeriod, timezone)) : ""}</td>
3104
3494
  </tr>`;
3105
3495
  }
3106
3496
  html += `
@@ -3150,7 +3540,7 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
3150
3540
  const consent = resourceItem;
3151
3541
  html += `
3152
3542
  <tr id="${templateUtilities.narrativeLinkId(consent)}">
3153
- <td>${templateUtilities.codeableConcept(consent.scope, "display")}</td>
3543
+ <td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConcept(consent.scope, "display"))}</td>
3154
3544
  <td>${consent.status || ""}</td>
3155
3545
  <td>${consent.provision?.action ? templateUtilities.concatCodeableConcept(consent.provision.action) : ""}</td>
3156
3546
  <td>${consent.dateTime || ""}</td>
@@ -3171,16 +3561,18 @@ var TypeScriptTemplateMapper = class {
3171
3561
  * @param resources - FHIR resources
3172
3562
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
3173
3563
  * @param useSectionSummary - Whether to use the section summary for narrative generation
3564
+ * @param now - Optional current date to use for generating relative dates in the narrative
3174
3565
  * @returns HTML string for rendering
3175
3566
  */
3176
- static generateNarrative(section, resources, timezone, useSectionSummary = false) {
3567
+ static generateNarrative(section, resources, timezone, useSectionSummary = false, now) {
3177
3568
  const templateClass = this.sectionToTemplate[section];
3178
3569
  if (!templateClass) {
3179
3570
  throw new Error(`No template found for section: ${section}`);
3180
3571
  }
3181
3572
  return useSectionSummary ? templateClass.generateSummaryNarrative(
3182
3573
  resources,
3183
- timezone
3574
+ timezone,
3575
+ now
3184
3576
  ) : templateClass.generateNarrative(resources, timezone);
3185
3577
  }
3186
3578
  };
@@ -3238,14 +3630,15 @@ var NarrativeGenerator = class {
3238
3630
  * @param resources - Array of domain resources
3239
3631
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
3240
3632
  * @param useSectionSummary - Whether to use section summary for narrative generation (default: false)
3633
+ * @param now - Optional date parameter
3241
3634
  * @returns Generated HTML content or undefined if no resources
3242
3635
  */
3243
- static async generateNarrativeContentAsync(section, resources, timezone, useSectionSummary = false) {
3636
+ static async generateNarrativeContentAsync(section, resources, timezone, useSectionSummary = false, now) {
3244
3637
  if (!resources || resources.length === 0) {
3245
3638
  return void 0;
3246
3639
  }
3247
3640
  try {
3248
- const content = TypeScriptTemplateMapper.generateNarrative(section, resources, timezone, useSectionSummary);
3641
+ const content = TypeScriptTemplateMapper.generateNarrative(section, resources, timezone, useSectionSummary, now);
3249
3642
  if (!content) {
3250
3643
  return void 0;
3251
3644
  }
@@ -3267,8 +3660,8 @@ var NarrativeGenerator = class {
3267
3660
  const options = aggressive ? AGGRESSIVE_MINIFY_OPTIONS : DEFAULT_MINIFY_OPTIONS;
3268
3661
  return await (0, import_html_minifier_terser.minify)(html, options);
3269
3662
  } catch (error) {
3270
- console.warn("HTML minification failed", error);
3271
- return html;
3663
+ console.warn("HTML minification failed", error, html);
3664
+ return `${error instanceof Error ? error.message : String(error)}`;
3272
3665
  }
3273
3666
  }
3274
3667
  /**
@@ -3297,10 +3690,11 @@ var NarrativeGenerator = class {
3297
3690
  * @param timezone - Optional timezone to use for date formatting
3298
3691
  * @param minify - Whether to minify the HTML content (default: true)
3299
3692
  * @param useSectionSummary - Whether to use section summary for narrative generation (default: false)
3693
+ * @param now - Optional date parameter
3300
3694
  * @returns Promise that resolves to a FHIR Narrative object or undefined if no resources
3301
3695
  */
3302
- static async generateNarrativeAsync(section, resources, timezone, minify = true, useSectionSummary = false) {
3303
- const content = await this.generateNarrativeContentAsync(section, resources, timezone, useSectionSummary);
3696
+ static async generateNarrativeAsync(section, resources, timezone, minify = true, useSectionSummary = false, now) {
3697
+ const content = await this.generateNarrativeContentAsync(section, resources, timezone, useSectionSummary, now);
3304
3698
  if (!content) {
3305
3699
  return void 0;
3306
3700
  }
@@ -3477,8 +3871,9 @@ var ComprehensiveIPSCompositionBuilder = class {
3477
3871
  * @param baseUrl - Base URL for the FHIR server (e.g., 'https://example.com/fhir')
3478
3872
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
3479
3873
  * @param patientId - Optional patient ID to use as primary patient for composition reference
3874
+ * @param now - Optional current date to use for composition date (defaults to new Date())
3480
3875
  */
3481
- async buildBundleAsync(authorOrganizationId, authorOrganizationName, baseUrl, timezone, patientId) {
3876
+ async buildBundleAsync(authorOrganizationId, authorOrganizationName, baseUrl, timezone, patientId, now) {
3482
3877
  if (baseUrl.endsWith("/")) {
3483
3878
  baseUrl = baseUrl.slice(0, -1);
3484
3879
  }
@@ -3505,20 +3900,22 @@ var ComprehensiveIPSCompositionBuilder = class {
3505
3900
  // Assuming patient is also a practitioner for simplicity
3506
3901
  display: authorOrganizationName
3507
3902
  }],
3508
- date: (/* @__PURE__ */ new Date()).toISOString(),
3903
+ date: (now || /* @__PURE__ */ new Date()).toISOString(),
3509
3904
  title: "International Patient Summary",
3510
3905
  section: this.sections,
3511
3906
  text: await NarrativeGenerator.generateNarrativeAsync(
3512
3907
  "Patient" /* PATIENT */,
3513
3908
  this.patients,
3514
3909
  timezone,
3515
- true
3910
+ true,
3911
+ false,
3912
+ now
3516
3913
  )
3517
3914
  };
3518
3915
  const bundle = {
3519
3916
  resourceType: "Bundle",
3520
3917
  type: "document",
3521
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3918
+ timestamp: (now || /* @__PURE__ */ new Date()).toISOString(),
3522
3919
  identifier: {
3523
3920
  "system": "urn:ietf:rfc:3986",
3524
3921
  "value": "urn:uuid:4dcfd353-49fd-4ab0-b521-c8d57ced74d6"