@erosolaraijs/cure 1.0.1 → 1.0.2
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/bin/cure.d.ts +2 -5
- package/dist/bin/cure.d.ts.map +1 -1
- package/dist/bin/cure.js +285 -124
- package/dist/bin/cure.js.map +1 -1
- package/dist/clinician/decisionSupport.d.ts +325 -0
- package/dist/clinician/decisionSupport.d.ts.map +1 -0
- package/dist/clinician/decisionSupport.js +604 -0
- package/dist/clinician/decisionSupport.js.map +1 -0
- package/dist/clinician/index.d.ts +5 -0
- package/dist/clinician/index.d.ts.map +1 -0
- package/dist/clinician/index.js +5 -0
- package/dist/clinician/index.js.map +1 -0
- package/dist/compliance/index.d.ts +5 -0
- package/dist/compliance/index.d.ts.map +1 -0
- package/dist/compliance/index.js +5 -0
- package/dist/compliance/index.js.map +1 -0
- package/dist/integrations/clinicalTrials/index.d.ts +5 -0
- package/dist/integrations/clinicalTrials/index.d.ts.map +1 -0
- package/dist/integrations/clinicalTrials/index.js +5 -0
- package/dist/integrations/clinicalTrials/index.js.map +1 -0
- package/dist/integrations/ehr/index.d.ts +5 -0
- package/dist/integrations/ehr/index.d.ts.map +1 -0
- package/dist/integrations/ehr/index.js +5 -0
- package/dist/integrations/ehr/index.js.map +1 -0
- package/dist/integrations/genomics/index.d.ts +5 -0
- package/dist/integrations/genomics/index.d.ts.map +1 -0
- package/dist/integrations/genomics/index.js +5 -0
- package/dist/integrations/genomics/index.js.map +1 -0
- package/dist/integrations/index.d.ts +7 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +10 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/ml/index.d.ts +5 -0
- package/dist/ml/index.d.ts.map +1 -0
- package/dist/ml/index.js +5 -0
- package/dist/ml/index.js.map +1 -0
- package/dist/ml/outcomePredictor.d.ts +297 -0
- package/dist/ml/outcomePredictor.d.ts.map +1 -0
- package/dist/ml/outcomePredictor.js +823 -0
- package/dist/ml/outcomePredictor.js.map +1 -0
- package/dist/patient/index.d.ts +5 -0
- package/dist/patient/index.d.ts.map +1 -0
- package/dist/patient/index.js +5 -0
- package/dist/patient/index.js.map +1 -0
- package/dist/patient/patientPortal.d.ts +337 -0
- package/dist/patient/patientPortal.d.ts.map +1 -0
- package/dist/patient/patientPortal.js +667 -0
- package/dist/patient/patientPortal.js.map +1 -0
- package/dist/safety/drugInteractions.d.ts +230 -0
- package/dist/safety/drugInteractions.d.ts.map +1 -0
- package/dist/safety/drugInteractions.js +697 -0
- package/dist/safety/drugInteractions.js.map +1 -0
- package/dist/safety/index.d.ts +5 -0
- package/dist/safety/index.d.ts.map +1 -0
- package/dist/safety/index.js +5 -0
- package/dist/safety/index.js.map +1 -0
- package/dist/validation/index.d.ts +5 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +5 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/retrospectiveValidator.d.ts +246 -0
- package/dist/validation/retrospectiveValidator.d.ts.map +1 -0
- package/dist/validation/retrospectiveValidator.js +602 -0
- package/dist/validation/retrospectiveValidator.js.map +1 -0
- package/package.json +1 -1
- package/src/bin/cure.ts +331 -140
- package/src/clinician/decisionSupport.ts +949 -0
- package/src/patient/patientPortal.ts +1039 -0
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drug Interaction and Safety Checking System
|
|
3
|
+
*
|
|
4
|
+
* Provides comprehensive drug safety checks:
|
|
5
|
+
* - Drug-drug interactions
|
|
6
|
+
* - Contraindication checking
|
|
7
|
+
* - Dosing validation
|
|
8
|
+
* - Allergy cross-reactivity
|
|
9
|
+
* - Organ function-based adjustments
|
|
10
|
+
* - Pregnancy/lactation safety
|
|
11
|
+
* - QT prolongation risk assessment
|
|
12
|
+
* - CYP450 interaction analysis
|
|
13
|
+
*
|
|
14
|
+
* Data sources would include:
|
|
15
|
+
* - FDA drug labels
|
|
16
|
+
* - DrugBank
|
|
17
|
+
* - PharmGKB
|
|
18
|
+
* - Clinical Pharmacology databases
|
|
19
|
+
*/
|
|
20
|
+
import { EventEmitter } from 'events';
|
|
21
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
22
|
+
// DRUG SAFETY SERVICE
|
|
23
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
24
|
+
export class DrugSafetyService extends EventEmitter {
|
|
25
|
+
interactionDatabase = new Map();
|
|
26
|
+
contraindicationDatabase = new Map();
|
|
27
|
+
dosingGuidelines = new Map();
|
|
28
|
+
allergenCrossReactivity = new Map();
|
|
29
|
+
qtProlongingDrugs = new Set();
|
|
30
|
+
blackBoxWarnings = new Map();
|
|
31
|
+
constructor() {
|
|
32
|
+
super();
|
|
33
|
+
this.initializeDatabases();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Perform comprehensive safety check
|
|
37
|
+
*/
|
|
38
|
+
async performSafetyCheck(patient, proposedDrugs) {
|
|
39
|
+
const alerts = [];
|
|
40
|
+
const recommendations = [];
|
|
41
|
+
// Get all drugs (current + proposed)
|
|
42
|
+
const allDrugs = [
|
|
43
|
+
...patient.currentMedications.map(m => m.drug),
|
|
44
|
+
...proposedDrugs.map(d => d.drug)
|
|
45
|
+
];
|
|
46
|
+
// 1. Check drug-drug interactions
|
|
47
|
+
const interactionAlerts = await this.checkDrugInteractions(allDrugs);
|
|
48
|
+
alerts.push(...interactionAlerts);
|
|
49
|
+
// 2. Check contraindications based on patient conditions
|
|
50
|
+
const contraindicationAlerts = await this.checkContraindications(proposedDrugs.map(d => d.drug), patient);
|
|
51
|
+
alerts.push(...contraindicationAlerts);
|
|
52
|
+
// 3. Check allergies and cross-reactivity
|
|
53
|
+
const allergyAlerts = await this.checkAllergies(proposedDrugs.map(d => d.drug), patient.allergies);
|
|
54
|
+
alerts.push(...allergyAlerts);
|
|
55
|
+
// 4. Check dosing based on organ function
|
|
56
|
+
const dosingAlerts = await this.checkDosing(proposedDrugs, patient);
|
|
57
|
+
alerts.push(...dosingAlerts);
|
|
58
|
+
// 5. Check QT prolongation risk
|
|
59
|
+
const qtAlerts = await this.checkQTProlongation(allDrugs, patient);
|
|
60
|
+
alerts.push(...qtAlerts);
|
|
61
|
+
// 6. Check pregnancy/lactation
|
|
62
|
+
if (patient.demographics.pregnant || patient.demographics.breastfeeding) {
|
|
63
|
+
const pregnancyAlerts = await this.checkPregnancySafety(proposedDrugs.map(d => d.drug), patient);
|
|
64
|
+
alerts.push(...pregnancyAlerts);
|
|
65
|
+
}
|
|
66
|
+
// 7. Check black box warnings
|
|
67
|
+
const blackBoxAlerts = await this.checkBlackBoxWarnings(proposedDrugs.map(d => d.drug));
|
|
68
|
+
alerts.push(...blackBoxAlerts);
|
|
69
|
+
// 8. Check pharmacogenomic considerations
|
|
70
|
+
if (patient.geneticFactors) {
|
|
71
|
+
const pgxAlerts = await this.checkPharmacogenomics(proposedDrugs.map(d => d.drug), patient.geneticFactors);
|
|
72
|
+
alerts.push(...pgxAlerts);
|
|
73
|
+
}
|
|
74
|
+
// Count alerts by severity
|
|
75
|
+
const criticalAlerts = alerts.filter(a => a.severity === 'critical').length;
|
|
76
|
+
const highAlerts = alerts.filter(a => a.severity === 'high').length;
|
|
77
|
+
const moderateAlerts = alerts.filter(a => a.severity === 'moderate').length;
|
|
78
|
+
const lowAlerts = alerts.filter(a => a.severity === 'low').length;
|
|
79
|
+
// Determine overall risk
|
|
80
|
+
let overallRisk;
|
|
81
|
+
if (criticalAlerts > 0)
|
|
82
|
+
overallRisk = 'critical';
|
|
83
|
+
else if (highAlerts > 0)
|
|
84
|
+
overallRisk = 'high';
|
|
85
|
+
else if (moderateAlerts > 0)
|
|
86
|
+
overallRisk = 'moderate';
|
|
87
|
+
else if (lowAlerts > 0)
|
|
88
|
+
overallRisk = 'low';
|
|
89
|
+
else
|
|
90
|
+
overallRisk = 'minimal';
|
|
91
|
+
// Generate recommendations
|
|
92
|
+
for (const alert of alerts.filter(a => a.severity === 'critical' || a.severity === 'high')) {
|
|
93
|
+
recommendations.push(alert.recommendation);
|
|
94
|
+
}
|
|
95
|
+
// Determine if safe to proceed
|
|
96
|
+
const safe = criticalAlerts === 0 && !alerts.some(a => !a.overridable);
|
|
97
|
+
// Emit event
|
|
98
|
+
this.emit('safety-check-completed', { patientId: patient.patientId, safe, alertCount: alerts.length });
|
|
99
|
+
return {
|
|
100
|
+
safe,
|
|
101
|
+
alerts: alerts.sort((a, b) => {
|
|
102
|
+
const severityOrder = { critical: 0, high: 1, moderate: 2, low: 3 };
|
|
103
|
+
return severityOrder[a.severity] - severityOrder[b.severity];
|
|
104
|
+
}),
|
|
105
|
+
summary: {
|
|
106
|
+
criticalAlerts,
|
|
107
|
+
highAlerts,
|
|
108
|
+
moderateAlerts,
|
|
109
|
+
lowAlerts,
|
|
110
|
+
overallRisk
|
|
111
|
+
},
|
|
112
|
+
recommendations
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Check drug-drug interactions
|
|
117
|
+
*/
|
|
118
|
+
async checkDrugInteractions(drugs) {
|
|
119
|
+
const alerts = [];
|
|
120
|
+
const checkedPairs = new Set();
|
|
121
|
+
for (let i = 0; i < drugs.length; i++) {
|
|
122
|
+
for (let j = i + 1; j < drugs.length; j++) {
|
|
123
|
+
const drug1 = drugs[i].toLowerCase();
|
|
124
|
+
const drug2 = drugs[j].toLowerCase();
|
|
125
|
+
const pairKey = [drug1, drug2].sort().join('|');
|
|
126
|
+
if (checkedPairs.has(pairKey))
|
|
127
|
+
continue;
|
|
128
|
+
checkedPairs.add(pairKey);
|
|
129
|
+
const interaction = this.findInteraction(drug1, drug2);
|
|
130
|
+
if (interaction) {
|
|
131
|
+
alerts.push({
|
|
132
|
+
id: `DDI-${Date.now()}-${i}-${j}`,
|
|
133
|
+
type: 'interaction',
|
|
134
|
+
severity: this.mapInteractionSeverity(interaction.severity),
|
|
135
|
+
title: `Drug Interaction: ${drugs[i]} + ${drugs[j]}`,
|
|
136
|
+
description: interaction.clinicalEffect,
|
|
137
|
+
affectedDrugs: [drugs[i], drugs[j]],
|
|
138
|
+
recommendation: interaction.management,
|
|
139
|
+
overridable: interaction.severity !== 'contraindicated',
|
|
140
|
+
requiresDocumentation: interaction.severity === 'major' || interaction.severity === 'contraindicated',
|
|
141
|
+
references: interaction.references
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return alerts;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Check contraindications
|
|
150
|
+
*/
|
|
151
|
+
async checkContraindications(drugs, patient) {
|
|
152
|
+
const alerts = [];
|
|
153
|
+
// Check renal contraindications
|
|
154
|
+
if (patient.renalFunction.gfr < 30) {
|
|
155
|
+
const renalContraindicated = ['metformin', 'nsaids', 'lithium', 'gadolinium'];
|
|
156
|
+
for (const drug of drugs) {
|
|
157
|
+
if (renalContraindicated.some(c => drug.toLowerCase().includes(c))) {
|
|
158
|
+
alerts.push({
|
|
159
|
+
id: `CI-RENAL-${Date.now()}`,
|
|
160
|
+
type: 'contraindication',
|
|
161
|
+
severity: patient.renalFunction.gfr < 15 ? 'critical' : 'high',
|
|
162
|
+
title: `Renal Contraindication: ${drug}`,
|
|
163
|
+
description: `${drug} is contraindicated or requires significant dose adjustment with GFR ${patient.renalFunction.gfr} mL/min`,
|
|
164
|
+
affectedDrugs: [drug],
|
|
165
|
+
recommendation: `Consider alternative therapy or significant dose reduction. Consult nephrology if essential.`,
|
|
166
|
+
overridable: patient.renalFunction.gfr >= 15,
|
|
167
|
+
requiresDocumentation: true
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Check hepatic contraindications
|
|
173
|
+
if (patient.hepaticFunction.childPughClass === 'C') {
|
|
174
|
+
const hepaticContraindicated = ['methotrexate', 'valproic-acid', 'isoniazid', 'ketoconazole'];
|
|
175
|
+
for (const drug of drugs) {
|
|
176
|
+
if (hepaticContraindicated.some(c => drug.toLowerCase().includes(c))) {
|
|
177
|
+
alerts.push({
|
|
178
|
+
id: `CI-HEPATIC-${Date.now()}`,
|
|
179
|
+
type: 'contraindication',
|
|
180
|
+
severity: 'critical',
|
|
181
|
+
title: `Hepatic Contraindication: ${drug}`,
|
|
182
|
+
description: `${drug} is contraindicated in Child-Pugh Class C liver disease`,
|
|
183
|
+
affectedDrugs: [drug],
|
|
184
|
+
recommendation: `Avoid use. Consider alternative therapy.`,
|
|
185
|
+
overridable: false,
|
|
186
|
+
requiresDocumentation: true
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Check cardiac contraindications
|
|
192
|
+
if (patient.cardiacStatus.lvef && patient.cardiacStatus.lvef < 40) {
|
|
193
|
+
const cardiacCaution = ['anthracycline', 'trastuzumab', 'sunitinib', 'sorafenib'];
|
|
194
|
+
for (const drug of drugs) {
|
|
195
|
+
if (cardiacCaution.some(c => drug.toLowerCase().includes(c))) {
|
|
196
|
+
alerts.push({
|
|
197
|
+
id: `CI-CARDIAC-${Date.now()}`,
|
|
198
|
+
type: 'contraindication',
|
|
199
|
+
severity: 'high',
|
|
200
|
+
title: `Cardiac Caution: ${drug}`,
|
|
201
|
+
description: `${drug} has cardiotoxicity risk. Patient LVEF is ${patient.cardiacStatus.lvef}%`,
|
|
202
|
+
affectedDrugs: [drug],
|
|
203
|
+
recommendation: `Baseline and serial cardiac monitoring required. Consider cardiology consultation.`,
|
|
204
|
+
overridable: true,
|
|
205
|
+
requiresDocumentation: true
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return alerts;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Check allergy cross-reactivity
|
|
214
|
+
*/
|
|
215
|
+
async checkAllergies(drugs, allergies) {
|
|
216
|
+
const alerts = [];
|
|
217
|
+
for (const allergy of allergies) {
|
|
218
|
+
for (const drug of drugs) {
|
|
219
|
+
const crossReactivity = this.checkCrossReactivity(allergy.allergen, drug);
|
|
220
|
+
if (crossReactivity.crossReactivity) {
|
|
221
|
+
const severity = allergy.severity === 'severe' && crossReactivity.riskLevel === 'high'
|
|
222
|
+
? 'critical'
|
|
223
|
+
: allergy.severity === 'severe' || crossReactivity.riskLevel === 'high'
|
|
224
|
+
? 'high'
|
|
225
|
+
: 'moderate';
|
|
226
|
+
alerts.push({
|
|
227
|
+
id: `ALLERGY-${Date.now()}`,
|
|
228
|
+
type: 'allergy',
|
|
229
|
+
severity,
|
|
230
|
+
title: `Allergy Alert: ${drug}`,
|
|
231
|
+
description: `Patient allergic to ${allergy.allergen}. ${drug} has ${crossReactivity.riskLevel} cross-reactivity risk.`,
|
|
232
|
+
affectedDrugs: [drug],
|
|
233
|
+
recommendation: crossReactivity.recommendation,
|
|
234
|
+
overridable: allergy.severity !== 'severe' || crossReactivity.riskLevel !== 'high',
|
|
235
|
+
requiresDocumentation: true
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return alerts;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Check dosing based on organ function
|
|
244
|
+
*/
|
|
245
|
+
async checkDosing(drugs, patient) {
|
|
246
|
+
const alerts = [];
|
|
247
|
+
for (const drugOrder of drugs) {
|
|
248
|
+
const guidelines = this.dosingGuidelines.get(drugOrder.drug.toLowerCase());
|
|
249
|
+
if (!guidelines)
|
|
250
|
+
continue;
|
|
251
|
+
for (const guideline of guidelines) {
|
|
252
|
+
// Check renal adjustment
|
|
253
|
+
if (guideline.renalAdjustment) {
|
|
254
|
+
for (const adj of guideline.renalAdjustment) {
|
|
255
|
+
if (patient.renalFunction.gfr < adj.gfrThreshold) {
|
|
256
|
+
alerts.push({
|
|
257
|
+
id: `DOSE-RENAL-${Date.now()}`,
|
|
258
|
+
type: 'dosing',
|
|
259
|
+
severity: 'moderate',
|
|
260
|
+
title: `Dose Adjustment Required: ${drugOrder.drug}`,
|
|
261
|
+
description: `Patient GFR ${patient.renalFunction.gfr} requires dose adjustment`,
|
|
262
|
+
affectedDrugs: [drugOrder.drug],
|
|
263
|
+
recommendation: adj.adjustment,
|
|
264
|
+
overridable: true,
|
|
265
|
+
requiresDocumentation: true
|
|
266
|
+
});
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// Check hepatic adjustment
|
|
272
|
+
if (guideline.hepaticAdjustment && patient.hepaticFunction.childPughClass) {
|
|
273
|
+
const adj = guideline.hepaticAdjustment.find(a => a.childPughClass === patient.hepaticFunction.childPughClass);
|
|
274
|
+
if (adj) {
|
|
275
|
+
alerts.push({
|
|
276
|
+
id: `DOSE-HEPATIC-${Date.now()}`,
|
|
277
|
+
type: 'dosing',
|
|
278
|
+
severity: patient.hepaticFunction.childPughClass === 'C' ? 'high' : 'moderate',
|
|
279
|
+
title: `Hepatic Dose Adjustment: ${drugOrder.drug}`,
|
|
280
|
+
description: `Child-Pugh Class ${patient.hepaticFunction.childPughClass} requires dose adjustment`,
|
|
281
|
+
affectedDrugs: [drugOrder.drug],
|
|
282
|
+
recommendation: adj.adjustment,
|
|
283
|
+
overridable: patient.hepaticFunction.childPughClass !== 'C',
|
|
284
|
+
requiresDocumentation: true
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Check age-based adjustment
|
|
289
|
+
if (guideline.ageAdjustment) {
|
|
290
|
+
for (const adj of guideline.ageAdjustment) {
|
|
291
|
+
if (patient.demographics.age >= adj.ageThreshold) {
|
|
292
|
+
alerts.push({
|
|
293
|
+
id: `DOSE-AGE-${Date.now()}`,
|
|
294
|
+
type: 'dosing',
|
|
295
|
+
severity: 'low',
|
|
296
|
+
title: `Age-Based Consideration: ${drugOrder.drug}`,
|
|
297
|
+
description: `Patient age ${patient.demographics.age} may require adjustment`,
|
|
298
|
+
affectedDrugs: [drugOrder.drug],
|
|
299
|
+
recommendation: adj.adjustment,
|
|
300
|
+
overridable: true,
|
|
301
|
+
requiresDocumentation: false
|
|
302
|
+
});
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return alerts;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Check QT prolongation risk
|
|
313
|
+
*/
|
|
314
|
+
async checkQTProlongation(drugs, patient) {
|
|
315
|
+
const alerts = [];
|
|
316
|
+
const qtDrugs = drugs.filter(d => this.qtProlongingDrugs.has(d.toLowerCase()));
|
|
317
|
+
if (qtDrugs.length > 0) {
|
|
318
|
+
// Check baseline QTc
|
|
319
|
+
const baselineQTc = patient.cardiacStatus.qtcInterval;
|
|
320
|
+
let riskLevel = 'moderate';
|
|
321
|
+
if (baselineQTc && baselineQTc > 500) {
|
|
322
|
+
riskLevel = 'critical';
|
|
323
|
+
}
|
|
324
|
+
else if (baselineQTc && baselineQTc > 470) {
|
|
325
|
+
riskLevel = 'high';
|
|
326
|
+
}
|
|
327
|
+
else if (qtDrugs.length >= 2) {
|
|
328
|
+
riskLevel = 'high';
|
|
329
|
+
}
|
|
330
|
+
// Check for additional risk factors
|
|
331
|
+
const riskFactors = [];
|
|
332
|
+
if (patient.demographics.gender === 'female')
|
|
333
|
+
riskFactors.push('Female gender');
|
|
334
|
+
if (patient.demographics.age > 65)
|
|
335
|
+
riskFactors.push('Age > 65');
|
|
336
|
+
if (patient.cardiacStatus.arrhythmiaHistory)
|
|
337
|
+
riskFactors.push('Arrhythmia history');
|
|
338
|
+
// Would also check electrolytes (K, Mg) if available
|
|
339
|
+
if (qtDrugs.length >= 2 || riskLevel === 'critical' || riskLevel === 'high') {
|
|
340
|
+
alerts.push({
|
|
341
|
+
id: `QT-${Date.now()}`,
|
|
342
|
+
type: 'qt-prolongation',
|
|
343
|
+
severity: riskLevel,
|
|
344
|
+
title: `QT Prolongation Risk: ${qtDrugs.join(', ')}`,
|
|
345
|
+
description: `${qtDrugs.length} QT-prolonging drug(s) ordered. ${riskFactors.length > 0 ? 'Risk factors: ' + riskFactors.join(', ') : ''}`,
|
|
346
|
+
affectedDrugs: qtDrugs,
|
|
347
|
+
recommendation: `Monitor QTc. Consider ECG at baseline and after steady state. Avoid if QTc > 500ms.`,
|
|
348
|
+
overridable: riskLevel !== 'critical',
|
|
349
|
+
requiresDocumentation: true
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return alerts;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Check pregnancy and lactation safety
|
|
357
|
+
*/
|
|
358
|
+
async checkPregnancySafety(drugs, patient) {
|
|
359
|
+
const alerts = [];
|
|
360
|
+
// Known teratogenic drugs
|
|
361
|
+
const categoryX = [
|
|
362
|
+
'methotrexate', 'thalidomide', 'lenalidomide', 'pomalidomide',
|
|
363
|
+
'isotretinoin', 'warfarin', 'ribavirin', 'leflunomide'
|
|
364
|
+
];
|
|
365
|
+
const categoryD = [
|
|
366
|
+
'doxorubicin', 'cyclophosphamide', 'imatinib', 'tamoxifen',
|
|
367
|
+
'carbamazepine', 'phenytoin', 'valproic acid', 'lithium'
|
|
368
|
+
];
|
|
369
|
+
for (const drug of drugs) {
|
|
370
|
+
const drugLower = drug.toLowerCase();
|
|
371
|
+
if (categoryX.some(x => drugLower.includes(x))) {
|
|
372
|
+
alerts.push({
|
|
373
|
+
id: `PREG-X-${Date.now()}`,
|
|
374
|
+
type: 'pregnancy',
|
|
375
|
+
severity: 'critical',
|
|
376
|
+
title: `Pregnancy Contraindication: ${drug}`,
|
|
377
|
+
description: `${drug} is absolutely contraindicated in pregnancy (Category X equivalent)`,
|
|
378
|
+
affectedDrugs: [drug],
|
|
379
|
+
recommendation: `Do not use. Use alternative therapy. Ensure contraception.`,
|
|
380
|
+
overridable: false,
|
|
381
|
+
requiresDocumentation: true
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
else if (categoryD.some(d => drugLower.includes(d))) {
|
|
385
|
+
alerts.push({
|
|
386
|
+
id: `PREG-D-${Date.now()}`,
|
|
387
|
+
type: 'pregnancy',
|
|
388
|
+
severity: 'high',
|
|
389
|
+
title: `Pregnancy Risk: ${drug}`,
|
|
390
|
+
description: `${drug} has evidence of fetal risk but may be used if benefit outweighs risk`,
|
|
391
|
+
affectedDrugs: [drug],
|
|
392
|
+
recommendation: `Document informed consent. Discuss risks with patient. Consider alternatives.`,
|
|
393
|
+
overridable: true,
|
|
394
|
+
requiresDocumentation: true
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return alerts;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Check black box warnings
|
|
402
|
+
*/
|
|
403
|
+
async checkBlackBoxWarnings(drugs) {
|
|
404
|
+
const alerts = [];
|
|
405
|
+
for (const drug of drugs) {
|
|
406
|
+
const warnings = this.blackBoxWarnings.get(drug.toLowerCase());
|
|
407
|
+
if (warnings && warnings.length > 0) {
|
|
408
|
+
alerts.push({
|
|
409
|
+
id: `BBW-${Date.now()}`,
|
|
410
|
+
type: 'black-box',
|
|
411
|
+
severity: 'high',
|
|
412
|
+
title: `Black Box Warning: ${drug}`,
|
|
413
|
+
description: warnings.join('; '),
|
|
414
|
+
affectedDrugs: [drug],
|
|
415
|
+
recommendation: `Ensure appropriate monitoring and patient education. Document informed consent.`,
|
|
416
|
+
overridable: true,
|
|
417
|
+
requiresDocumentation: true
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return alerts;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Check pharmacogenomic considerations
|
|
425
|
+
*/
|
|
426
|
+
async checkPharmacogenomics(drugs, genetics) {
|
|
427
|
+
const alerts = [];
|
|
428
|
+
// DPYD and fluoropyrimidines
|
|
429
|
+
if (genetics.dpyd && genetics.dpyd !== 'normal') {
|
|
430
|
+
const fluoropyrimidines = ['5-fu', 'fluorouracil', 'capecitabine'];
|
|
431
|
+
const affected = drugs.filter(d => fluoropyrimidines.some(f => d.toLowerCase().includes(f)));
|
|
432
|
+
if (affected.length > 0) {
|
|
433
|
+
const severity = genetics.dpyd === 'deficient' ? 'critical' : 'high';
|
|
434
|
+
alerts.push({
|
|
435
|
+
id: `PGX-DPYD-${Date.now()}`,
|
|
436
|
+
type: 'organ-function',
|
|
437
|
+
severity,
|
|
438
|
+
title: `DPYD Deficiency: ${affected.join(', ')}`,
|
|
439
|
+
description: `Patient is DPYD ${genetics.dpyd}. High risk of severe/fatal toxicity with fluoropyrimidines.`,
|
|
440
|
+
affectedDrugs: affected,
|
|
441
|
+
recommendation: genetics.dpyd === 'deficient'
|
|
442
|
+
? 'Contraindicated. Use alternative therapy.'
|
|
443
|
+
: 'Reduce dose by 50%. Consider alternative.',
|
|
444
|
+
overridable: genetics.dpyd !== 'deficient',
|
|
445
|
+
requiresDocumentation: true
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
// UGT1A1 and irinotecan
|
|
450
|
+
if (genetics.ugt1a1 === '*28/*28') {
|
|
451
|
+
const irinotecan = drugs.filter(d => d.toLowerCase().includes('irinotecan'));
|
|
452
|
+
if (irinotecan.length > 0) {
|
|
453
|
+
alerts.push({
|
|
454
|
+
id: `PGX-UGT1A1-${Date.now()}`,
|
|
455
|
+
type: 'organ-function',
|
|
456
|
+
severity: 'high',
|
|
457
|
+
title: `UGT1A1 Polymorphism: Irinotecan`,
|
|
458
|
+
description: `Patient is UGT1A1*28/*28. Increased risk of severe neutropenia and diarrhea.`,
|
|
459
|
+
affectedDrugs: irinotecan,
|
|
460
|
+
recommendation: `Reduce initial dose by 1 level. Monitor closely.`,
|
|
461
|
+
overridable: true,
|
|
462
|
+
requiresDocumentation: true
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
// TPMT and thiopurines
|
|
467
|
+
if (genetics.tpmt && genetics.tpmt !== 'normal') {
|
|
468
|
+
const thiopurines = ['azathioprine', '6-mercaptopurine', 'thioguanine'];
|
|
469
|
+
const affected = drugs.filter(d => thiopurines.some(t => d.toLowerCase().includes(t)));
|
|
470
|
+
if (affected.length > 0) {
|
|
471
|
+
const severity = genetics.tpmt === 'poor' ? 'critical' : 'high';
|
|
472
|
+
alerts.push({
|
|
473
|
+
id: `PGX-TPMT-${Date.now()}`,
|
|
474
|
+
type: 'organ-function',
|
|
475
|
+
severity,
|
|
476
|
+
title: `TPMT Deficiency: ${affected.join(', ')}`,
|
|
477
|
+
description: `Patient is TPMT ${genetics.tpmt} metabolizer. Risk of severe myelosuppression.`,
|
|
478
|
+
affectedDrugs: affected,
|
|
479
|
+
recommendation: genetics.tpmt === 'poor'
|
|
480
|
+
? 'Reduce dose by 90% or use alternative.'
|
|
481
|
+
: 'Reduce dose by 50%. Monitor CBC closely.',
|
|
482
|
+
overridable: genetics.tpmt !== 'poor',
|
|
483
|
+
requiresDocumentation: true
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return alerts;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Get recommended alternatives for a contraindicated drug
|
|
491
|
+
*/
|
|
492
|
+
getAlternatives(drug, reason, patientProfile) {
|
|
493
|
+
const alternatives = [];
|
|
494
|
+
// Drug class-based alternatives
|
|
495
|
+
const alternativeMap = {
|
|
496
|
+
// Chemotherapy
|
|
497
|
+
'cisplatin': ['carboplatin', 'oxaliplatin'],
|
|
498
|
+
'doxorubicin': ['liposomal doxorubicin', 'epirubicin'],
|
|
499
|
+
'ifosfamide': ['cyclophosphamide'],
|
|
500
|
+
// Targeted therapy
|
|
501
|
+
'osimertinib': ['afatinib', 'erlotinib', 'gefitinib'],
|
|
502
|
+
'alectinib': ['brigatinib', 'lorlatinib', 'crizotinib'],
|
|
503
|
+
// Immunotherapy (generally fewer direct alternatives)
|
|
504
|
+
'pembrolizumab': ['nivolumab', 'atezolizumab'],
|
|
505
|
+
'nivolumab': ['pembrolizumab', 'atezolizumab'],
|
|
506
|
+
// Supportive care
|
|
507
|
+
'ondansetron': ['granisetron', 'palonosetron'],
|
|
508
|
+
'metoclopramide': ['prochlorperazine', 'promethazine']
|
|
509
|
+
};
|
|
510
|
+
const drugLower = drug.toLowerCase();
|
|
511
|
+
for (const [key, alts] of Object.entries(alternativeMap)) {
|
|
512
|
+
if (drugLower.includes(key)) {
|
|
513
|
+
alternatives.push(...alts);
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
// Filter alternatives based on patient's contraindications
|
|
518
|
+
return alternatives.filter(alt => {
|
|
519
|
+
const interaction = this.findInteraction(alt, drug);
|
|
520
|
+
if (interaction?.severity === 'contraindicated')
|
|
521
|
+
return false;
|
|
522
|
+
return true;
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
526
|
+
// HELPER METHODS
|
|
527
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
528
|
+
initializeDatabases() {
|
|
529
|
+
// Initialize drug interactions (simplified - production would have comprehensive database)
|
|
530
|
+
this.addInteraction({
|
|
531
|
+
drug1: 'pembrolizumab',
|
|
532
|
+
drug2: 'corticosteroids',
|
|
533
|
+
severity: 'moderate',
|
|
534
|
+
type: 'pharmacodynamic',
|
|
535
|
+
mechanism: 'High-dose corticosteroids may reduce immunotherapy efficacy',
|
|
536
|
+
clinicalEffect: 'Reduced antitumor immune response',
|
|
537
|
+
management: 'Use lowest effective steroid dose. Consider steroid-sparing agents for irAE management.',
|
|
538
|
+
documentation: 'established'
|
|
539
|
+
});
|
|
540
|
+
this.addInteraction({
|
|
541
|
+
drug1: 'warfarin',
|
|
542
|
+
drug2: 'capecitabine',
|
|
543
|
+
severity: 'major',
|
|
544
|
+
type: 'pharmacokinetic',
|
|
545
|
+
mechanism: 'Capecitabine inhibits CYP2C9, increasing warfarin effect',
|
|
546
|
+
clinicalEffect: 'Increased INR and bleeding risk',
|
|
547
|
+
management: 'Monitor INR closely. Consider LMWH alternative.',
|
|
548
|
+
documentation: 'established'
|
|
549
|
+
});
|
|
550
|
+
this.addInteraction({
|
|
551
|
+
drug1: 'osimertinib',
|
|
552
|
+
drug2: 'strong cyp3a4 inducers',
|
|
553
|
+
severity: 'major',
|
|
554
|
+
type: 'pharmacokinetic',
|
|
555
|
+
mechanism: 'CYP3A4 inducers decrease osimertinib exposure',
|
|
556
|
+
clinicalEffect: 'Reduced efficacy',
|
|
557
|
+
management: 'Avoid concomitant use with strong CYP3A4 inducers',
|
|
558
|
+
documentation: 'established'
|
|
559
|
+
});
|
|
560
|
+
// Initialize QT-prolonging drugs
|
|
561
|
+
const qtDrugs = [
|
|
562
|
+
'ondansetron', 'granisetron', 'haloperidol', 'droperidol',
|
|
563
|
+
'methadone', 'sotalol', 'amiodarone', 'dofetilide',
|
|
564
|
+
'ibutilide', 'quinidine', 'procainamide', 'disopyramide',
|
|
565
|
+
'arsenic trioxide', 'crizotinib', 'lapatinib', 'nilotinib',
|
|
566
|
+
'pazopanib', 'sunitinib', 'vandetanib', 'vemurafenib'
|
|
567
|
+
];
|
|
568
|
+
qtDrugs.forEach(d => this.qtProlongingDrugs.add(d.toLowerCase()));
|
|
569
|
+
// Initialize black box warnings
|
|
570
|
+
this.blackBoxWarnings.set('pembrolizumab', [
|
|
571
|
+
'Immune-mediated adverse reactions can be severe and fatal',
|
|
572
|
+
'Monitor for immune-mediated pneumonitis, colitis, hepatitis, endocrinopathies, nephritis, and dermatologic reactions'
|
|
573
|
+
]);
|
|
574
|
+
this.blackBoxWarnings.set('trastuzumab', [
|
|
575
|
+
'Cardiomyopathy: Evaluate LVEF prior to and during treatment',
|
|
576
|
+
'Infusion reactions including fatal cases'
|
|
577
|
+
]);
|
|
578
|
+
this.blackBoxWarnings.set('bevacizumab', [
|
|
579
|
+
'Gastrointestinal perforation',
|
|
580
|
+
'Surgery and wound healing complications',
|
|
581
|
+
'Hemorrhage'
|
|
582
|
+
]);
|
|
583
|
+
// Initialize allergy cross-reactivity
|
|
584
|
+
this.allergenCrossReactivity.set('penicillin', ['amoxicillin', 'ampicillin', 'piperacillin', 'cephalosporins']);
|
|
585
|
+
this.allergenCrossReactivity.set('sulfa', ['sulfamethoxazole', 'sulfasalazine', 'celecoxib', 'furosemide']);
|
|
586
|
+
this.allergenCrossReactivity.set('platinum', ['cisplatin', 'carboplatin', 'oxaliplatin']);
|
|
587
|
+
// Initialize dosing guidelines
|
|
588
|
+
this.dosingGuidelines.set('carboplatin', [{
|
|
589
|
+
drug: 'carboplatin',
|
|
590
|
+
indication: 'solid tumors',
|
|
591
|
+
standardDose: 'AUC 5-6 (Calvert formula)',
|
|
592
|
+
renalAdjustment: [
|
|
593
|
+
{ gfrThreshold: 60, adjustment: 'Use Calvert formula with measured or estimated GFR' },
|
|
594
|
+
{ gfrThreshold: 30, adjustment: 'Reduce target AUC. Consider nephrology consult' },
|
|
595
|
+
{ gfrThreshold: 15, adjustment: 'Avoid or significant dose reduction' }
|
|
596
|
+
],
|
|
597
|
+
weightBasedDosing: {
|
|
598
|
+
formula: 'Dose (mg) = Target AUC × (GFR + 25)',
|
|
599
|
+
maxDose: 'Cap GFR at 125 mL/min'
|
|
600
|
+
}
|
|
601
|
+
}]);
|
|
602
|
+
this.dosingGuidelines.set('cisplatin', [{
|
|
603
|
+
drug: 'cisplatin',
|
|
604
|
+
indication: 'solid tumors',
|
|
605
|
+
standardDose: '75-100 mg/m²',
|
|
606
|
+
renalAdjustment: [
|
|
607
|
+
{ gfrThreshold: 60, adjustment: 'Standard dose with aggressive hydration' },
|
|
608
|
+
{ gfrThreshold: 45, adjustment: 'Reduce dose 25-50%' },
|
|
609
|
+
{ gfrThreshold: 30, adjustment: 'Avoid - use carboplatin alternative' }
|
|
610
|
+
]
|
|
611
|
+
}]);
|
|
612
|
+
}
|
|
613
|
+
addInteraction(interaction) {
|
|
614
|
+
const key1 = interaction.drug1.toLowerCase();
|
|
615
|
+
const key2 = interaction.drug2.toLowerCase();
|
|
616
|
+
if (!this.interactionDatabase.has(key1)) {
|
|
617
|
+
this.interactionDatabase.set(key1, []);
|
|
618
|
+
}
|
|
619
|
+
if (!this.interactionDatabase.has(key2)) {
|
|
620
|
+
this.interactionDatabase.set(key2, []);
|
|
621
|
+
}
|
|
622
|
+
this.interactionDatabase.get(key1).push(interaction);
|
|
623
|
+
this.interactionDatabase.get(key2).push(interaction);
|
|
624
|
+
}
|
|
625
|
+
findInteraction(drug1, drug2) {
|
|
626
|
+
const interactions = this.interactionDatabase.get(drug1.toLowerCase());
|
|
627
|
+
if (!interactions)
|
|
628
|
+
return null;
|
|
629
|
+
return interactions.find(i => i.drug2.toLowerCase() === drug2.toLowerCase() ||
|
|
630
|
+
i.drug1.toLowerCase() === drug2.toLowerCase()) || null;
|
|
631
|
+
}
|
|
632
|
+
mapInteractionSeverity(severity) {
|
|
633
|
+
switch (severity) {
|
|
634
|
+
case 'contraindicated': return 'critical';
|
|
635
|
+
case 'major': return 'high';
|
|
636
|
+
case 'moderate': return 'moderate';
|
|
637
|
+
case 'minor': return 'low';
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
checkCrossReactivity(allergen, drug) {
|
|
641
|
+
const relatedAllergens = this.allergenCrossReactivity.get(allergen.toLowerCase()) || [];
|
|
642
|
+
const crossReacts = relatedAllergens.some(a => drug.toLowerCase().includes(a));
|
|
643
|
+
let riskLevel = 'none';
|
|
644
|
+
if (crossReacts) {
|
|
645
|
+
// Platinum cross-reactivity is high
|
|
646
|
+
if (allergen.toLowerCase().includes('platinum')) {
|
|
647
|
+
riskLevel = 'high';
|
|
648
|
+
}
|
|
649
|
+
// Penicillin-cephalosporin is lower risk
|
|
650
|
+
else if (allergen.toLowerCase().includes('penicillin') && drug.toLowerCase().includes('cephalosporin')) {
|
|
651
|
+
riskLevel = 'low'; // ~2% cross-reactivity
|
|
652
|
+
}
|
|
653
|
+
else {
|
|
654
|
+
riskLevel = 'moderate';
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return {
|
|
658
|
+
reportedAllergy: allergen,
|
|
659
|
+
drugToCheck: drug,
|
|
660
|
+
crossReactivity: crossReacts,
|
|
661
|
+
riskLevel,
|
|
662
|
+
relatedAllergens,
|
|
663
|
+
recommendation: crossReacts
|
|
664
|
+
? `Consider alternative. If essential, administer with caution and monitor.`
|
|
665
|
+
: `No known cross-reactivity. Standard precautions.`
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Generate a medication reconciliation report
|
|
670
|
+
*/
|
|
671
|
+
async generateMedicationReconciliation(patient) {
|
|
672
|
+
const currentMeds = patient.currentMedications.map(m => m.drug);
|
|
673
|
+
const interactionAlerts = await this.checkDrugInteractions(currentMeds);
|
|
674
|
+
const interactions = [];
|
|
675
|
+
for (let i = 0; i < currentMeds.length; i++) {
|
|
676
|
+
for (let j = i + 1; j < currentMeds.length; j++) {
|
|
677
|
+
const int = this.findInteraction(currentMeds[i], currentMeds[j]);
|
|
678
|
+
if (int)
|
|
679
|
+
interactions.push(int);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
const recommendations = [];
|
|
683
|
+
for (const int of interactions.filter(i => i.severity === 'major' || i.severity === 'contraindicated')) {
|
|
684
|
+
recommendations.push(int.management);
|
|
685
|
+
}
|
|
686
|
+
return {
|
|
687
|
+
currentMedications: currentMeds,
|
|
688
|
+
interactions,
|
|
689
|
+
recommendations,
|
|
690
|
+
summary: interactions.length > 0
|
|
691
|
+
? `Found ${interactions.length} drug interaction(s), including ${interactions.filter(i => i.severity === 'major' || i.severity === 'contraindicated').length} major interaction(s).`
|
|
692
|
+
: 'No significant drug interactions identified.'
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
export default DrugSafetyService;
|
|
697
|
+
//# sourceMappingURL=drugInteractions.js.map
|