UNPKG

periop-calculators

Version:

Evidence-based perioperative risk assessment calculators for healthcare professionals

502 lines (496 loc) 18.9 kB
function validateAge(age) { const errors = []; if (age === undefined || age === null) { errors.push({ code: 'AGE_REQUIRED', message: 'Age is required', field: 'age', }); } else if (age < 18) { errors.push({ code: 'AGE_TOO_LOW', message: 'STOP-BANG is validated for adults 18 years and older', field: 'age', }); } else if (age > 120) { errors.push({ code: 'AGE_INVALID', message: 'Please enter a valid age', field: 'age', }); } return { isValid: errors.length === 0, errors, }; } function validateBMI(bmi, weight, height) { const errors = []; if (bmi === undefined && (weight === undefined || height === undefined)) { errors.push({ code: 'BMI_CALCULATION_ERROR', message: 'Either BMI or both weight and height must be provided', field: 'bmi', }); } else if (bmi !== undefined && (bmi < 10 || bmi > 70)) { errors.push({ code: 'BMI_INVALID', message: 'BMI must be between 10 and 70', field: 'bmi', }); } return { isValid: errors.length === 0, errors, }; } function calculateBMI(weightKg, heightCm) { const heightM = heightCm / 100; return Number((weightKg / (heightM * heightM)).toFixed(1)); } /** * Custom error class for calculator-related errors */ class CalculatorError extends Error { constructor(message, code, field) { super(message); this.name = 'CalculatorError'; this.code = code; this.field = field; // Maintains proper stack trace for where our error was thrown (only available on V8) if (Error.captureStackTrace) { Error.captureStackTrace(this, CalculatorError); } } } /** * Calculate STOP-BANG score for obstructive sleep apnea risk assessment * * STOP-BANG is a validated screening tool for obstructive sleep apnea (OSA) * in surgical patients. It consists of 8 yes/no questions. * * Score interpretation: * - 0-2: Low risk of OSA * - 3-4: Intermediate risk of OSA * - 5-8: High risk of OSA * * @param input - STOP-BANG questionnaire responses * @param demographics - Optional patient demographics for auto-calculation * @returns StopBangResult with score, risk level, and recommendations * @throws Error if required inputs are missing or invalid * * @example * ```typescript * const result = calculateStopBang({ * snoring: true, * tiredness: true, * observed: false, * pressure: true, * bmi: 36, * age: 55, * neckCircumference: 43, * gender: 'male' * }); * console.log(result.score); // 6 * console.log(result.risk); // 'high' * ``` */ function calculateStopBang(input, demographics) { const errors = []; // Determine age const age = input.age ?? demographics?.age; const ageValidation = validateAge(age); if (!ageValidation.isValid) { errors.push(...ageValidation.errors); } // Determine BMI let bmi = input.bmi; if (!bmi && demographics?.weight && demographics?.height) { bmi = calculateBMI(demographics.weight, demographics.height); } const bmiValidation = validateBMI(bmi, demographics?.weight, demographics?.height); if (!bmiValidation.isValid) { errors.push(...bmiValidation.errors); } // Determine gender const gender = input.gender ?? demographics?.sex; if (!gender) { errors.push({ code: 'GENDER_REQUIRED', message: 'Gender is required for STOP-BANG calculation', field: 'gender', }); } // Determine neck circumference const neckCircumference = input.neckCircumference ?? demographics?.neckCircumference; if (errors.length > 0) { throw new CalculatorError(`Validation errors: ${errors.map((e) => e.message).join(', ')}`); } // Calculate individual components const components = { S: input.snoring, T: input.tiredness, O: input.observed, P: input.pressure, B: (bmi ?? 0) > 35, A: (age ?? 0) > 50, N: neckCircumference ? neckCircumference > 40 : false, G: gender === 'male', }; // Calculate total score const score = Object.values(components).filter(Boolean).length; // Determine risk level let risk; if (score <= 2) { risk = 'low'; } else if (score <= 4) { risk = 'intermediate'; } else { risk = 'high'; } // Generate interpretation const interpretation = generateInterpretation$1(score, risk); // Generate recommendations const recommendations = generateRecommendations$1(score, risk, components); return { score, risk, interpretation, components, recommendations, }; } function generateInterpretation$1(score, risk) { const baseInterpretation = `STOP-BANG score of ${score} indicates ${risk} risk of obstructive sleep apnea.`; switch (risk) { case 'low': return `${baseInterpretation} The patient has a low probability of moderate to severe OSA.`; case 'intermediate': return `${baseInterpretation} Further evaluation may be warranted based on clinical judgment and planned procedure.`; case 'high': return `${baseInterpretation} The patient has a high probability of moderate to severe OSA. Consider polysomnography and perioperative precautions.`; } } function generateRecommendations$1(score, risk, components) { const recommendations = []; // Universal recommendations recommendations.push('Document STOP-BANG score in preoperative assessment'); switch (risk) { case 'low': recommendations.push('Proceed with standard anesthetic care'); recommendations.push('No specific OSA precautions required'); break; case 'intermediate': recommendations.push('Consider extended monitoring in PACU'); recommendations.push('Use caution with sedatives and opioids'); recommendations.push('Consider referral for sleep study if multiple risk factors present'); if (components.B || components.N) { recommendations.push('Optimize positioning to maintain airway patency'); } break; case 'high': recommendations.push('Strong consideration for polysomnography before elective surgery'); recommendations.push('Consider using short-acting agents'); recommendations.push('Plan for possible postoperative continuous monitoring'); recommendations.push('Have difficult airway equipment readily available'); recommendations.push('Consider regional anesthesia when appropriate'); recommendations.push('Minimize opioid use - consider multimodal analgesia'); if (score >= 6) { recommendations.push('Consider postoperative ICU admission for major surgery'); } break; } // Specific recommendations based on components if (components.P) { recommendations.push('Ensure blood pressure is optimized preoperatively'); } if (components.B) { recommendations.push('Consider weight loss counseling for elective procedures'); } return recommendations; } /** * Simplified version of STOP-BANG calculation using just the basic inputs * @param input - Basic STOP-BANG inputs * @returns Just the numeric score (0-8) */ function calculateStopBangScore(input) { try { const result = calculateStopBang(input); return result.score; } catch (error) { throw new CalculatorError(`Cannot calculate STOP-BANG score: ${error}`); } } /** * RCRI (Revised Cardiac Risk Index) Calculator * * Reference: Lee TH, et al. Derivation and prospective validation of a simple index * for prediction of cardiac risk of major noncardiac surgery. Circulation. 1999;100(10):1043-9. */ /** * Calculate the Revised Cardiac Risk Index (RCRI) score * @param input RCRI risk factors * @returns RCRI calculation result with score, risk class, and recommendations */ function calculateRCRI(input) { // Calculate total score let score = 0; if (input.highRiskSurgery) score++; if (input.ischemicHeartDisease) score++; if (input.congestiveHeartFailure) score++; if (input.cerebrovascularDisease) score++; if (input.insulinDependentDiabetes) score++; if (input.renalInsufficiency) score++; // Determine risk class and estimated risk let riskClass; let estimatedRisk; let riskPercentage; if (score === 0) { riskClass = 'I'; estimatedRisk = '0.4%'; riskPercentage = 0.4; } else if (score === 1) { riskClass = 'II'; estimatedRisk = '0.9%'; riskPercentage = 0.9; } else if (score === 2) { riskClass = 'III'; estimatedRisk = '6.6%'; riskPercentage = 6.6; } else { riskClass = 'IV'; estimatedRisk = '≥11%'; riskPercentage = 11; } // Generate interpretation const interpretation = generateInterpretation(score, riskClass, estimatedRisk); // Generate recommendations const recommendations = generateRecommendations(score, riskClass, input); return { score, riskClass, estimatedRisk, riskPercentage, interpretation, riskFactors: { highRiskSurgery: input.highRiskSurgery, ischemicHeartDisease: input.ischemicHeartDisease, congestiveHeartFailure: input.congestiveHeartFailure, cerebrovascularDisease: input.cerebrovascularDisease, insulinDependentDiabetes: input.insulinDependentDiabetes, renalInsufficiency: input.renalInsufficiency }, recommendations }; } function generateInterpretation(score, riskClass, estimatedRisk) { if (score === 0) { return `RCRI Class ${riskClass} with no risk factors identified. Estimated risk of major cardiac complications is ${estimatedRisk}. This represents very low cardiac risk.`; } else if (score === 1) { return `RCRI Class ${riskClass} with 1 risk factor. Estimated risk of major cardiac complications is ${estimatedRisk}. This represents low cardiac risk.`; } else if (score === 2) { return `RCRI Class ${riskClass} with 2 risk factors. Estimated risk of major cardiac complications is ${estimatedRisk}. This represents intermediate cardiac risk.`; } else { return `RCRI Class ${riskClass} with ${score} risk factors. Estimated risk of major cardiac complications is ${estimatedRisk}. This represents high cardiac risk.`; } } function generateRecommendations(score, riskClass, input) { const recommendations = []; // General recommendations based on risk class if (score === 0) { recommendations.push('Proceed with surgery with standard perioperative care'); recommendations.push('No additional cardiac testing indicated based on RCRI alone'); } else if (score === 1) { recommendations.push('Consider perioperative beta-blockade if not already prescribed'); recommendations.push('Ensure optimal medical management of cardiac risk factors'); } else if (score === 2) { recommendations.push('Consider cardiology consultation for perioperative risk assessment'); recommendations.push('Optimize medical management before elective surgery'); recommendations.push('Consider non-invasive cardiac testing if it will change management'); } else { recommendations.push('Strongly consider cardiology consultation before surgery'); recommendations.push('Consider additional cardiac testing (stress test, echo) if results would change management'); recommendations.push('Optimize all modifiable risk factors before elective surgery'); recommendations.push('Consider perioperative beta-blockade and statin therapy'); } // Specific recommendations based on risk factors if (input.congestiveHeartFailure) { recommendations.push('Optimize heart failure management preoperatively'); recommendations.push('Consider echocardiography to assess current cardiac function'); } if (input.ischemicHeartDisease) { recommendations.push('Ensure patient is on appropriate antiplatelet therapy considering surgical bleeding risk'); recommendations.push('Continue statin therapy perioperatively'); } if (input.insulinDependentDiabetes) { recommendations.push('Optimize glycemic control perioperatively'); recommendations.push('Monitor for diabetic complications'); } if (input.renalInsufficiency) { recommendations.push('Avoid nephrotoxic medications'); recommendations.push('Consider nephrology consultation for perioperative management'); recommendations.push('Monitor volume status carefully'); } if (input.cerebrovascularDisease) { recommendations.push('Consider carotid evaluation if symptomatic'); recommendations.push('Maintain adequate blood pressure perioperatively'); } // Add monitoring recommendation for all patients recommendations.push('Monitor for signs of cardiac complications postoperatively'); return recommendations; } /** * Helper function to determine if a surgery type is high risk according to RCRI * @param surgeryType Description of the surgery * @returns Whether the surgery is considered high risk */ function isHighRiskSurgery(surgeryType) { const highRiskKeywords = [ 'intraperitoneal', 'intrathoracic', 'suprainguinal vascular', 'aortic', 'major vascular', 'peripheral vascular', 'thoracic', 'abdominal', 'esophagectomy', 'hepatectomy', 'pancreatectomy', 'pneumonectomy' ]; const lowerSurgeryType = surgeryType.toLowerCase(); return highRiskKeywords.some(keyword => lowerSurgeryType.includes(keyword)); } /** * Calculates the Apfel Score for predicting postoperative nausea and vomiting (PONV) * @param input - Patient risk factors * @returns Apfel score result with risk assessment and recommendations * @throws {CalculatorError} If input validation fails */ function calculateApfelScore(input) { // Validate input if (!input || typeof input !== 'object') { throw new CalculatorError('Invalid input: ApfelScoreInput object required'); } // Validate boolean fields const booleanFields = ['female', 'nonSmoker', 'historyOfPONV', 'postoperativeOpioids']; for (const field of booleanFields) { if (typeof input[field] !== 'boolean') { throw new CalculatorError(`Invalid input: ${field} must be a boolean value`); } } // Calculate score (0-4 points) let score = 0; if (input.female) score++; if (input.nonSmoker) score++; if (input.historyOfPONV) score++; if (input.postoperativeOpioids) score++; // Risk percentages based on Apfel et al. 1999 const riskPercentages = { 0: 10, 1: 21, 2: 39, 3: 61, 4: 79 }; const riskPercentage = riskPercentages[score]; // Determine risk category let risk; if (score === 0) { risk = 'low'; } else if (score === 1) { risk = 'moderate'; } else if (score === 2) { risk = 'high'; } else { risk = 'very-high'; } // Generate interpretation const riskFactorCount = score === 0 ? 'no' : score.toString(); const riskFactorPlural = score === 1 ? 'risk factor' : 'risk factors'; const interpretation = `Apfel Score of ${score} with ${riskFactorCount} ${riskFactorPlural} indicates ${risk.replace('-', ' ')} risk for PONV. Estimated incidence: ${riskPercentage}% chance of experiencing postoperative nausea and vomiting.`; // Generate evidence-based recommendations const recommendations = []; if (score === 0) { recommendations.push('Low risk patient - routine PONV prophylaxis may not be necessary'); recommendations.push('Consider prophylaxis if other risk factors present (e.g., type of surgery)'); } else if (score === 1) { recommendations.push('Consider single-agent PONV prophylaxis'); recommendations.push('Options include dexamethasone, ondansetron, or droperidol'); } else if (score === 2) { recommendations.push('Recommend dual-agent PONV prophylaxis'); recommendations.push('Consider combination therapy (e.g., dexamethasone + ondansetron)'); } else if (score >= 3) { recommendations.push('High-risk patient - recommend multimodal PONV prophylaxis'); recommendations.push('Consider 3-4 antiemetic agents from different classes'); recommendations.push('Consider total intravenous anesthesia (TIVA) with propofol'); if (input.postoperativeOpioids) { recommendations.push('Consider regional anesthesia or non-opioid analgesics to reduce opioid requirements'); } } // Always include general recommendations recommendations.push('Monitor for PONV in PACU and postoperative period'); recommendations.push('Have rescue antiemetics readily available'); return { score, riskPercentage, risk, interpretation, riskFactors: { female: input.female, nonSmoker: input.nonSmoker, historyOfPONV: input.historyOfPONV, postoperativeOpioids: input.postoperativeOpioids }, recommendations }; } /** * Returns detailed information about a specific Apfel risk factor * @param factor - The risk factor to get information about * @returns Detailed description of the risk factor */ function getApfelRiskFactorInfo(factor) { const factorInfo = { female: 'Female gender is associated with 2-3 times higher risk of PONV compared to males', nonSmoker: 'Non-smoking status increases PONV risk, possibly due to chronic nicotine exposure providing antiemetic effects in smokers', historyOfPONV: 'Previous PONV or motion sickness is a strong predictor of future PONV episodes', postoperativeOpioids: 'Postoperative opioid use is a dose-dependent risk factor for PONV' }; return factorInfo[factor] || 'Unknown risk factor'; } export { calculateApfelScore, calculateBMI, calculateRCRI, calculateStopBang, calculateStopBangScore, getApfelRiskFactorInfo, isHighRiskSurgery }; //# sourceMappingURL=index.esm.js.map