periop-calculators
Version:
Evidence-based perioperative risk assessment calculators for healthcare professionals
713 lines (706 loc) • 27.1 kB
JavaScript
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$2(score, risk);
// Generate recommendations
const recommendations = generateRecommendations$2(score, risk, components);
return {
score,
risk,
interpretation,
components,
recommendations,
};
}
function generateInterpretation$2(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$2(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$1(score, riskClass, estimatedRisk);
// Generate recommendations
const recommendations = generateRecommendations$1(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$1(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$1(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';
}
/**
* Calculates the MELD (Model for End-Stage Liver Disease) Score
*
* The MELD score is a validated scoring system used to assess the severity of
* end-stage liver disease and predict short-term mortality. It is widely used
* in surgical risk assessment and liver transplant allocation.
*
* Formula: MELD = 3.78 × ln(bilirubin) + 11.2 × ln(INR) + 9.57 × ln(creatinine) + 6.43
*
* @param input - Laboratory values and dialysis status
* @returns MELD score result with risk assessment and recommendations
* @throws {CalculatorError} If input validation fails
*
* @example
* ```typescript
* const result = calculateMELDScore({
* bilirubin: 2.5,
* creatinine: 1.8,
* inr: 1.6,
* dialysis: false
* });
* console.log(result.score); // 18
* console.log(result.risk); // 'moderate'
* ```
*/
function calculateMELDScore(input) {
// Validate input
validateMELDInput(input);
// Apply constraints per MELD scoring guidelines
const bilirubin = Math.max(1.0, Math.min(input.bilirubin, 4.0));
const inr = Math.max(1.0, Math.min(input.inr, 4.0));
// If dialysis in past week, creatinine is set to 4.0
const creatinine = input.dialysis
? 4.0
: Math.max(1.0, Math.min(input.creatinine, 4.0));
// Calculate raw MELD score using the original formula
// MELD = 3.78 × ln(bilirubin) + 11.2 × ln(INR) + 9.57 × ln(creatinine) + 6.43
const rawScore = 3.78 * Math.log(bilirubin) +
11.2 * Math.log(inr) +
9.57 * Math.log(creatinine) +
6.43;
// Round to nearest integer and apply bounds (6-40)
const score = Math.max(6, Math.min(40, Math.round(rawScore * 10) / 10));
const finalScore = Math.round(score);
// Determine risk category and mortality estimates
const { risk, mortalityPercentage, mortalityRisk } = getMortalityRisk(finalScore);
// Generate interpretation
const interpretation = generateInterpretation(finalScore, risk, mortalityPercentage);
// Generate recommendations
const recommendations = generateRecommendations(finalScore, risk, input);
return {
score: finalScore,
mortalityRisk,
mortalityPercentage,
risk,
interpretation,
labValues: {
bilirubin: input.bilirubin,
creatinine: input.creatinine,
inr: input.inr,
dialysis: input.dialysis || false
},
recommendations
};
}
/**
* Validates MELD score input parameters
*/
function validateMELDInput(input) {
if (!input || typeof input !== 'object') {
throw new CalculatorError('Invalid input: MELDScoreInput object required');
}
// Validate bilirubin
if (typeof input.bilirubin !== 'number' || input.bilirubin <= 0) {
throw new CalculatorError('Invalid bilirubin: must be a positive number in mg/dL');
}
if (input.bilirubin > 50) {
throw new CalculatorError('Invalid bilirubin: value appears too high (>50 mg/dL), please verify units');
}
// Validate creatinine
if (typeof input.creatinine !== 'number' || input.creatinine <= 0) {
throw new CalculatorError('Invalid creatinine: must be a positive number in mg/dL');
}
if (input.creatinine > 15) {
throw new CalculatorError('Invalid creatinine: value appears too high (>15 mg/dL), please verify units');
}
// Validate INR
if (typeof input.inr !== 'number' || input.inr <= 0) {
throw new CalculatorError('Invalid INR: must be a positive number');
}
if (input.inr > 10) {
throw new CalculatorError('Invalid INR: value appears too high (>10), please verify');
}
// Validate dialysis (optional boolean)
if (input.dialysis !== undefined && typeof input.dialysis !== 'boolean') {
throw new CalculatorError('Invalid dialysis: must be a boolean value');
}
}
/**
* Determines mortality risk based on MELD score
*/
function getMortalityRisk(score) {
if (score <= 9) {
return {
risk: 'low',
mortalityPercentage: 1.9,
mortalityRisk: '1.9%'
};
}
else if (score <= 19) {
return {
risk: 'moderate',
mortalityPercentage: 6.0,
mortalityRisk: '6.0%'
};
}
else if (score <= 29) {
return {
risk: 'high',
mortalityPercentage: 19.6,
mortalityRisk: '19.6%'
};
}
else {
return {
risk: 'very-high',
mortalityPercentage: 52.6,
mortalityRisk: '52.6%'
};
}
}
/**
* Generates clinical interpretation based on MELD score
*/
function generateInterpretation(score, risk, mortalityPercentage) {
const riskDescription = risk.replace('-', ' ');
return `MELD score of ${score} indicates ${riskDescription} risk of 3-month mortality (${mortalityPercentage}%). ` +
`This score is used to assess liver disease severity and perioperative risk in patients with end-stage liver disease.`;
}
/**
* Generates evidence-based clinical recommendations
*/
function generateRecommendations(score, risk, input) {
const recommendations = [];
// General recommendations
recommendations.push('Document MELD score in preoperative assessment');
recommendations.push('Consider hepatology consultation for liver disease management');
// Risk-specific recommendations
if (score <= 9) {
recommendations.push('Low surgical risk - proceed with standard perioperative care');
recommendations.push('Monitor liver function perioperatively');
}
else if (score <= 19) {
recommendations.push('Moderate surgical risk - optimize liver function before elective surgery');
recommendations.push('Consider enhanced postoperative monitoring');
recommendations.push('Avoid hepatotoxic medications when possible');
}
else if (score <= 29) {
recommendations.push('High surgical risk - multidisciplinary evaluation recommended');
recommendations.push('Consider deferring elective surgery until liver function improves');
recommendations.push('If surgery necessary, plan for intensive postoperative care');
recommendations.push('Evaluate for liver transplant candidacy');
}
else {
recommendations.push('Very high surgical risk - surgery should be avoided unless life-threatening');
recommendations.push('Urgent liver transplant evaluation indicated');
recommendations.push('If emergency surgery required, plan for ICU-level care');
recommendations.push('Multidisciplinary team approach essential');
}
// Specific lab value recommendations
if (input.bilirubin > 3.0) {
recommendations.push('Elevated bilirubin - investigate for biliary obstruction or worsening liver function');
}
if (input.inr > 2.0) {
recommendations.push('Significantly elevated INR - consider vitamin K, FFP, or PCC for procedural correction');
}
if (input.creatinine > 2.0 || input.dialysis) {
recommendations.push('Renal dysfunction present - avoid nephrotoxic agents and monitor fluid balance');
if (input.dialysis) {
recommendations.push('Patient on dialysis - coordinate timing with nephrology team');
}
}
// Universal monitoring recommendations
recommendations.push('Monitor for hepatic encephalopathy');
recommendations.push('Assess for ascites and portal hypertension');
recommendations.push('Evaluate coagulation status before procedures');
return recommendations;
}
/**
* Helper function to determine if a MELD score indicates high surgical risk
* @param score MELD score
* @returns Whether the score indicates high or very high risk
*/
function isHighRiskMELD(score) {
return score >= 20;
}
/**
* Helper function to get MELD score interpretation without full calculation
* @param score MELD score
* @returns Risk category
*/
function getMELDRiskCategory(score) {
if (score <= 9)
return 'low';
if (score <= 19)
return 'moderate';
if (score <= 29)
return 'high';
return 'very-high';
}
export { calculateApfelScore, calculateBMI, calculateMELDScore, calculateRCRI, calculateStopBang, calculateStopBangScore, getApfelRiskFactorInfo, getMELDRiskCategory, isHighRiskMELD, isHighRiskSurgery };
//# sourceMappingURL=index.esm.js.map