{"version":3,"sources":["../src/a1c.ts","../src/constants.ts","../src/guards.ts","../src/glucose.ts","../src/validators.ts","../src/alignment.ts","../src/conversions.ts","../src/formatters.ts","../src/tir.ts","../src/types.ts","../src/mage.ts","../src/variability.ts","../src/tir-enhanced.ts","../src/metrics/bgi.ts","../src/metrics/adrr.ts","../src/metrics/grade.ts","../src/metrics/gri.ts","../src/metrics/jindex.ts","../src/metrics/modd.ts","../src/metrics/conga.ts","../src/metrics/active-percent.ts","../src/metrics/agp.ts","../src/connectors/dexcom.ts","../src/connectors/libre.ts","../src/connectors/nightscout.ts","../src/interop/fhir.ts","../src/interop/openmhealth.ts"],"names":["formatA1C","val","isValidA1C","value","getA1CCategory","a1c","thresholds","normalMax","prediabetesMax","isA1CInTarget","target","min","max","a1cDelta","current","previous","a1cTrend","readings","delta","HOMA_IR_DENOMINATOR","HOMA_IR_CUTOFFS","HYPO_THRESHOLD_MGDL","HYPER_THRESHOLD_MGDL","HYPO_THRESHOLD_MMOLL","HYPER_THRESHOLD_MMOLL","A1C_TO_EAG_MULTIPLIER","A1C_TO_EAG_CONSTANT","GMI_COEFFICIENTS","MGDL_MMOLL_CONVERSION","MG_DL","MMOL_L","GLUCOSE_COLOR_LOW","GLUCOSE_COLOR_NORMAL","GLUCOSE_COLOR_NORMAL_UP","GLUCOSE_COLOR_NORMAL_DOWN","GLUCOSE_COLOR_ELEVATED","GLUCOSE_COLOR_HIGH","GLUCOSE_ZONE_COLORS","TREND_ARROWS","TIR_VERY_LOW_THRESHOLD_MGDL","TIR_LOW_THRESHOLD_MGDL","TIR_HIGH_THRESHOLD_MGDL","TIR_VERY_HIGH_THRESHOLD_MGDL","TIR_VERY_LOW_THRESHOLD_MMOLL","TIR_LOW_THRESHOLD_MMOLL","TIR_HIGH_THRESHOLD_MMOLL","TIR_VERY_HIGH_THRESHOLD_MMOLL","PREGNANCY_TARGET_LOW_MGDL","PREGNANCY_TARGET_HIGH_MGDL","PREGNANCY_TARGET_LOW_MMOLL","PREGNANCY_TARGET_HIGH_MMOLL","TIR_GOAL_STANDARD","TBR_LEVEL1_GOAL","TBR_LEVEL2_GOAL","TAR_LEVEL1_GOAL","TAR_LEVEL2_GOAL","TIR_GOAL_OLDER_ADULTS","TBR_LEVEL1_GOAL_OLDER_ADULTS","TBR_LEVEL2_GOAL_OLDER_ADULTS","isEstimateGMIOptions","input","candidate","isValidGlucoseString","isHypo","unit","hypoMgdl","hypoMmoll","isHyper","hyperMgdl","hyperMmoll","getGlucoseLabel","parseGlucoseString","match","rawValue","rawUnit","isValidGlucoseValue","isValidInsulin","calculateHOMAIR","glucose","insulin","score","interpretHOMAIR","checkGlycemicAlignment","estimatedAvg","homaResult","flags","estimateA1CFromAvgGlucose","avgMgDl","estimateAvgGlucoseFromA1C","estimateEAG","eAG","estimateA1CFromAverage","avgGlucose","a1cToGMI","estimateGMI","valueOrOptions","resolvedUnit","parsed","gmi","mgDlToMmolL","mmolLToMgDl","convertGlucoseUnit","formatGlucose","options","digits","suffix","formatPercentage","formatDate","iso","timeZone","calculateTIR","inRange","belowRange","aboveRange","r","total","getTIRSummary","result","groupByDay","acc","reading","day","calculateTimeInRange","lower","upper","AllowedGlucoseUnits","glucoseMAGE","validReadings","mean","sum","v","variance","sd","shortWindow","longWindow","direction","_calculateSimpleMAGE","crossingPoints","_findMovingAverageCrossings","turningPoints","_findTurningPoints","excursions","_calculateExcursions","_calculateMAGEFromExcursions","shortMA","_calculateMovingAverage","longMA","crossings","i","prevShort","currShort","prevLong","currLong","values","windowSize","halfWindow","start","end","count","j","extremeIndex","extremeValue","isMaximum","maxVal","minVal","maxIdx","minIdx","maxDiff","minDiff","tp1","tp2","tp3","leftAmplitude","rightAmplitude","targetDirection","filteredExcursions","e","amplitudes","amp","isPeak","isNadir","validAmplitudes","amplitude","excursionAmplitude","glucoseStandardDeviation","glucoseCoefficientOfVariation","glucosePercentiles","percentiles","sorted","a","b","p","rank","BOUNDARY_EPSILON","calculateEnhancedTIR","population","veryLowThreshold","lowThreshold","highThreshold","veryHighThreshold","normalizedReadings","validateNormalizedReadings","veryLow","calculateRangeMetrics","low","high","veryHigh","summary","calculateSummary","meetsTargets","assessTargets","calculatePregnancyTIR","preferredUnit","mgdlCount","useMgdlThresholds","normalizedValue","meetsPregnancyTargets","recommendations","generatePregnancyRecommendations","maxValue","unitSuffix","estimateReadingInterval","timestamps","intervals","lowerBound","upperBound","inclusiveUpper","aboveLower","belowUpper","readingCount","percentage","avgIntervalMinutes","duration","averageValue","totalReadings","totalDuration","daysOfData","dataQuality","getPopulationGoals","isStandard","ranges","tirGoal","tbrLevel1Goal","tbrLevel2Goal","tirMeetsGoal","tbrLevel1Safe","tbrLevel2Safe","tarLevel1Acceptable","tarLevel2Acceptable","overallAssessment","criticalIssues","minorTARIssue","generateRecommendations","params","fbg","glucoseMgDl","glucoseLBGI","valid","g","f","glucoseHBGI","calculateADRR","dayBuckets","mgdl","totalDR","maxRl","maxRh","risk","gradeScore","mmol","inner","logLog","calculateGRADE","hypoThreshold","hyperThreshold","totalGrade","hypoGrade","euGrade","hyperGrade","meanGrade","calculateGRI","hypoComponent","hyperComponent","raw","clampedRaw","zone","calculateJIndex","s","MS_PER_DAY","DEFAULT_TOLERANCE_MS","calculateMODD","toleranceMs","differences","targetTime","lo","hi","bestIdx","bestDist","mid","dist","d","calculateCONGA","hours","lagMs","calculateActivePercent","intervalMin","t","totalMinutes","expectedReadings","actualReadings","activePercent","calculateAGPMetrics","mgdlValues","meanGlucose","cv","tir","griInput","DEXCOM_TREND_MAP","parseDexcomDate","epochMatch","normalizeDexcomTrend","trend","normalizeDexcomEntry","entry","normalizeDexcomEntries","entries","LIBRE_TREND_MAP","normalizeLibreTrend","normalizeLibreEntry","normalizeLibreEntries","NIGHTSCOUT_DIRECTION_MAP","normalizeNightscoutDirection","normalizeNightscoutEntry","timestamp","fallbackDate","date","normalizeNightscoutEntries","LOINC_SYSTEM","UCUM_SYSTEM","buildFHIRCGMSummary","period","components","buildFHIRSensorReading","isMgDl","buildFHIRSensorReadings","OMH_SCHEMA","buildOMHBloodGlucose","buildOMHDataPoint","id","buildOMHBloodGlucoseList"],"mappings":"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","file":"index.mjs","sourcesContent":["// @file src/a1c.ts\n\n/**\n * Formats a clinical A1C value as a percent string (e.g., \"7.2%\").\n * Used for clinical reporting and display.\n * @param val - A1C value (percentage)\n * @returns A1C as string with percent sign\n */\nexport function formatA1C(val: number): string {\n  return `${val.toFixed(1)}%`\n}\n\n/**\n * Validates a clinical A1C value (percentage).\n * Ensures value is within physiologically plausible range for clinical analytics.\n * @param value - Candidate A1C value\n * @returns True if value is a valid A1C percentage\n */\nexport function isValidA1C(value: unknown): boolean {\n  return (\n    typeof value === 'number' &&\n    Number.isFinite(value) &&\n    value > 0 &&\n    value < 20\n  )\n}\n\n/**\n * Returns the clinical category for an A1C value (normal, prediabetes, diabetes, or invalid).\n * Uses ADA thresholds by default, but allows custom cutoffs for research or population-specific use.\n * @param a1c - A1C value (percentage)\n * @returns 'normal' | 'prediabetes' | 'diabetes' | 'invalid'\n */\n/**\n * Returns the clinical category for an A1C value (normal, prediabetes, diabetes, or invalid).\n * Uses ADA thresholds by default, but allows custom cutoffs for research or population-specific use.\n * @param a1c - A1C value (percentage)\n * @param thresholds - Optional custom thresholds: { normalMax?: number; prediabetesMax?: number }\n * @returns 'normal' | 'prediabetes' | 'diabetes' | 'invalid'\n */\nexport function getA1CCategory(\n  a1c: number,\n  thresholds?: { normalMax?: number; prediabetesMax?: number }\n): 'normal' | 'prediabetes' | 'diabetes' | 'invalid' {\n  const normalMax = thresholds?.normalMax ?? 5.7\n  const prediabetesMax = thresholds?.prediabetesMax ?? 6.5\n  if (!isValidA1C(a1c)) return 'invalid'\n  if (a1c <= normalMax) return 'normal'\n  if (a1c <= prediabetesMax) return 'prediabetes'\n  return 'diabetes'\n}\n\n/**\n * Checks if an A1C value is within a target range.\n * @param a1c - A1C value\n * @param target - [min, max] range (default: [6.5, 7.0])\n * @param thresholds - Optional custom thresholds: { min?: number; max?: number }\n * @returns True if in target range\n */\nexport function isA1CInTarget(\n  a1c: number,\n  target: [number, number] = [6.5, 7.0],\n  thresholds?: { min?: number; max?: number }\n): boolean {\n  const min = thresholds?.min ?? target[0]\n  const max = thresholds?.max ?? target[1]\n  return isValidA1C(a1c) && a1c >= min && a1c <= max\n}\n\n/**\n * Calculates the change (delta) between two A1C values.\n * @param current - Current A1C\n * @param previous - Previous A1C\n * @returns Delta (current - previous)\n * @throws If either value is invalid\n */\nexport function a1cDelta(current: number, previous: number): number {\n  if (!isValidA1C(current) || !isValidA1C(previous))\n    throw new Error('Invalid A1C value')\n  return +(current - previous).toFixed(2)\n}\n\n/**\n * Determines the trend of A1C values over time.\n * @param readings - Array of A1C values (chronological order)\n * @returns 'increasing' | 'decreasing' | 'stable' | 'insufficient data'\n */\nexport function a1cTrend(\n  readings: number[]\n): 'increasing' | 'decreasing' | 'stable' | 'insufficient data' {\n  if (!Array.isArray(readings) || readings.length < 2)\n    return 'insufficient data'\n  const delta = readings[readings.length - 1] - readings[0]\n  if (Math.abs(delta) < 0.1) return 'stable'\n  return delta > 0 ? 'increasing' : 'decreasing'\n}\n","// @file src/constants.ts\n\n/**\n * Denominator constant for HOMA-IR calculation.\n * HOMA-IR = (glucose [mg/dL] × insulin [µIU/mL]) / HOMA_IR_DENOMINATOR\n * @see https://www.ncbi.nlm.nih.gov/books/NBK279396/\n */\nexport const HOMA_IR_DENOMINATOR = 405\n\n/**\n * Interpretation cutoffs for HOMA-IR (insulin resistance assessment).\n * These are general categories, not diagnostic.\n */\nexport const HOMA_IR_CUTOFFS = {\n  VERY_SENSITIVE: 1,\n  NORMAL: 2,\n  EARLY_RESISTANCE: 2.9,\n} as const\n\n\n/**\n * Hypoglycemia threshold (mg/dL).\n * Used for detecting low glucose events.\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html\n */\nexport const HYPO_THRESHOLD_MGDL = 70\n\n/**\n * Hyperglycemia threshold (mg/dL).\n * Used for detecting high glucose events.\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html\n */\nexport const HYPER_THRESHOLD_MGDL = 180\n\n/**\n * Hypoglycemia threshold (mmol/L).\n * Used for low glucose detection in international/metric contexts.\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html\n */\nexport const HYPO_THRESHOLD_MMOLL = 3.9\n\n/**\n * Hyperglycemia threshold (mmol/L).\n * Used for high glucose detection in international/metric contexts.\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html\n */\nexport const HYPER_THRESHOLD_MMOLL = 10\n\n/**\n * Multiplier for converting A1C to estimated average glucose (eAG).\n * Used in eAG calculation per CDC/ADA guidelines.\n * @see https://www.cdc.gov/diabetes/managing/managing-blood-sugar/a1c.html\n */\nexport const A1C_TO_EAG_MULTIPLIER = 28.7\n\n/**\n * Constant for converting A1C to estimated average glucose (eAG).\n * Used in eAG calculation per CDC/ADA guidelines.\n * @see https://www.cdc.gov/diabetes/managing/managing-blood-sugar/a1c.html\n */\nexport const A1C_TO_EAG_CONSTANT = 46.7\n\n/**\n * GMI (Glucose Management Indicator) calculation coefficients.\n * Used for estimating GMI from average glucose values.\n * @see https://diatribe.org/glucose-management-indicator-gmi\n */\nexport const GMI_COEFFICIENTS = {\n  /** Slope for mmol/L to GMI conversion */\n  MMOL_L_SLOPE: 1.57,\n  /** Intercept for mmol/L to GMI conversion */\n  MMOL_L_INTERCEPT: 3.5,\n  /** Slope for mg/dL to GMI conversion */\n  MG_DL_SLOPE: 0.03,\n  /** Intercept for mg/dL to GMI conversion */\n  MG_DL_INTERCEPT: 2.4,\n  /** Slope for A1C to GMI conversion */\n  A1C_SLOPE: 0.02392,\n  /** Intercept for A1C to GMI conversion */\n  A1C_INTERCEPT: 3.31,\n} as const\n\n/**\n * Conversion factor between mg/dL and mmol/L.\n * Used for unit conversion.\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html\n */\nexport const MGDL_MMOLL_CONVERSION = 18.0182\n\n/**\n * String literal for mg/dL glucose unit.\n * Used for data interoperability and formatting.\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html\n */\nexport const MG_DL = 'mg/dL' as const\n\n/**\n * String literal for mmol/L unit.\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html\n */\nexport const MMOL_L = 'mmol/L' as const\n\n\n/**\n * Color codes for glucose zones.\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html\n */\nexport const GLUCOSE_COLOR_LOW = '#D32F2F' // Red (low)\nexport const GLUCOSE_COLOR_NORMAL = '#388E3C' // Green (steady normal)\nexport const GLUCOSE_COLOR_NORMAL_UP = '#4CAF50' // Lighter Green (normal, trending up)\nexport const GLUCOSE_COLOR_NORMAL_DOWN = '#2E7D32' // Darker Green (normal, trending down)\nexport const GLUCOSE_COLOR_ELEVATED = '#FBC02D' // Yellow (over 140)\nexport const GLUCOSE_COLOR_HIGH = '#F57C00' // Orange (over 180)\n\n/**\n * Glucose zone color mapping for different statuses and trends.\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html\n */\nexport const GLUCOSE_ZONE_COLORS = {\n  LOW: GLUCOSE_COLOR_LOW, // Red\n  NORMAL: GLUCOSE_COLOR_NORMAL, // Green\n  ELEVATED: GLUCOSE_COLOR_ELEVATED, // Yellow\n  HIGH: GLUCOSE_COLOR_HIGH, // Orange\n  // Colors for trending normal up and down\n  NORMAL_UP: GLUCOSE_COLOR_NORMAL_UP, // Lighter Green\n  NORMAL_DOWN: GLUCOSE_COLOR_NORMAL_DOWN, // Darker Green\n} as const\n\n/**\n * Unicode arrows for glucose trend indication.\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html\n */\nexport const TREND_ARROWS = {\n  STEADY: '→',\n  RISING: '↗',\n  FALLING: '↘',\n  RAPIDRISE: '↑',\n  RAPIDFALL: '↓',\n} as const\n\n// ============================================================================\n// Enhanced Time-in-Range (TIR) Constants\n// Per International Consensus on Time in Range (Battelino et al. 2019)\n// ============================================================================\n\n/**\n * Level 2 hypoglycemia threshold (mg/dL).\n * Readings below this value indicate significant hypoglycemia.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport const TIR_VERY_LOW_THRESHOLD_MGDL = 54\n\n/**\n * Level 1 hypoglycemia threshold (mg/dL).\n * Target range begins at this value.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport const TIR_LOW_THRESHOLD_MGDL = 70\n\n/**\n * Level 1 hyperglycemia threshold (mg/dL).\n * Target range ends at this value.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport const TIR_HIGH_THRESHOLD_MGDL = 180\n\n/**\n * Level 2 hyperglycemia threshold (mg/dL).\n * Readings above this value indicate significant hyperglycemia.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport const TIR_VERY_HIGH_THRESHOLD_MGDL = 250\n\n/**\n * Level 2 hypoglycemia threshold (mmol/L).\n * Readings below this value indicate significant hypoglycemia.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport const TIR_VERY_LOW_THRESHOLD_MMOLL = 3.0\n\n/**\n * Level 1 hypoglycemia threshold (mmol/L).\n * Target range begins at this value.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport const TIR_LOW_THRESHOLD_MMOLL = 3.9\n\n/**\n * Level 1 hyperglycemia threshold (mmol/L).\n * Target range ends at this value.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport const TIR_HIGH_THRESHOLD_MMOLL = 10.0\n\n/**\n * Level 2 hyperglycemia threshold (mmol/L).\n * Readings above this value indicate significant hyperglycemia.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport const TIR_VERY_HIGH_THRESHOLD_MMOLL = 13.9\n\n// ============================================================================\n// Pregnancy-Specific TIR Constants\n// Per ADA Standards of Care in Diabetes (2024, 2025)\n// ============================================================================\n\n/**\n * Lower bound of target glucose range during pregnancy (mg/dL).\n * Applies to Type 1, Type 2, and gestational diabetes.\n * @see {@link https://diabetesjournals.org/care/article/47/Supplement_1/S282 | ADA Standards of Care (2024)}\n */\nexport const PREGNANCY_TARGET_LOW_MGDL = 63\n\n/**\n * Upper bound of target glucose range during pregnancy (mg/dL).\n * Applies to Type 1, Type 2, and gestational diabetes.\n * @see {@link https://diabetesjournals.org/care/article/47/Supplement_1/S282 | ADA Standards of Care (2024)}\n */\nexport const PREGNANCY_TARGET_HIGH_MGDL = 140\n\n/**\n * Lower bound of target glucose range during pregnancy (mmol/L).\n * Applies to Type 1, Type 2, and gestational diabetes.\n * @see {@link https://diabetesjournals.org/care/article/47/Supplement_1/S282 | ADA Standards of Care (2024)}\n */\nexport const PREGNANCY_TARGET_LOW_MMOLL = 3.5\n\n/**\n * Upper bound of target glucose range during pregnancy (mmol/L).\n * Applies to Type 1, Type 2, and gestational diabetes.\n * @see {@link https://diabetesjournals.org/care/article/47/Supplement_1/S282 | ADA Standards of Care (2024)}\n */\nexport const PREGNANCY_TARGET_HIGH_MMOLL = 7.8\n\n// ============================================================================\n// Clinical TIR Goals (percentages)\n// Per International Consensus on Time in Range (Battelino et al. 2019)\n// ============================================================================\n\n/**\n * Target percentage for time-in-range (70-180 mg/dL).\n * Clinical goal: ≥70% of readings in target range.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport const TIR_GOAL_STANDARD = 70\n\n/**\n * Maximum acceptable percentage for Level 1 hypoglycemia (54-69 mg/dL).\n * Clinical goal: <4% of readings in this range.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport const TBR_LEVEL1_GOAL = 4\n\n/**\n * Maximum acceptable percentage for Level 2 hypoglycemia (<54 mg/dL).\n * Clinical goal: <1% of readings in this range.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport const TBR_LEVEL2_GOAL = 1\n\n/**\n * Maximum acceptable percentage for Level 1 hyperglycemia (181-250 mg/dL).\n * Clinical goal: <25% of readings in this range.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport const TAR_LEVEL1_GOAL = 25\n\n/**\n * Maximum acceptable percentage for Level 2 hyperglycemia (>250 mg/dL).\n * Clinical goal: <5% of readings in this range.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport const TAR_LEVEL2_GOAL = 5\n\n/**\n * Target percentage for time-in-range for older/high-risk adults.\n * More lenient goal: ≥50% of readings in target range.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport const TIR_GOAL_OLDER_ADULTS = 50\n\n/**\n * Maximum acceptable percentage for Level 1 hypoglycemia for older/high-risk adults.\n * More stringent goal: <1% of readings in this range.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport const TBR_LEVEL1_GOAL_OLDER_ADULTS = 1\n\n/**\n * Maximum acceptable percentage for Level 2 hypoglycemia for older/high-risk adults.\n * More stringent goal: <0.5% of readings in this range.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport const TBR_LEVEL2_GOAL_OLDER_ADULTS = 0.5\n","import type { EstimateGMIOptions } from './types'\n\n/**\n * Clinical type guard for EstimateGMIOptions.\n * Validates that the input matches the required shape for GMI estimation options (numeric value, string unit).\n * Useful for ensuring safe handling of clinical glucose data and interoperability with analytics functions.\n * @param input - Candidate value to validate.\n * @returns True if input is a valid EstimateGMIOptions object.\n */\nexport function isEstimateGMIOptions(\n  input: unknown\n): input is EstimateGMIOptions {\n  if (typeof input !== 'object' || input === null) return false\n\n  const candidate = input as Record<string, unknown>\n  return (\n    typeof candidate.value === 'number' && typeof candidate.unit === 'string'\n  )\n}\n\n/**\n * Validates a clinical glucose string (e.g., \"100 mg/dL\", \"5.5 mmol/L\").\n * Ensures the string is in a recognized clinical format for glucose values, supporting safe parsing and conversion.\n * @param input - Value to check as a clinical glucose string.\n * @returns True if input is a valid glucose string for clinical use.\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html\n */\nexport function isValidGlucoseString(input: unknown): input is string {\n  if (typeof input !== 'string') return false\n\n  return /^\\d+(\\.\\d+)?\\s+(mg\\/dL|mmol\\/L)$/i.test(input.trim())\n}\n","// @file src/glucose.ts\n\nimport { GlucoseUnit } from './types'\nimport {\n  HYPO_THRESHOLD_MGDL,\n  HYPO_THRESHOLD_MMOLL,\n  HYPER_THRESHOLD_MGDL,\n  HYPER_THRESHOLD_MMOLL,\n  MG_DL,\n  MMOL_L,\n} from './constants'\n\nimport { isValidGlucoseString } from './guards'\n\n/**\n * Checks if a glucose value is below the hypoglycemia threshold for the given unit.\n * Used for detecting low glucose events.\n * @param val - Glucose value (number)\n * @param unit - Glucose unit ('mg/dL' or 'mmol/L'), default: 'mg/dL'\n * @param thresholds - Optional custom thresholds ({ mgdl?: number; mmoll?: number })\n * @returns True if value is below the hypoglycemia threshold\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html\n */\nexport function isHypo(\n  val: number,\n  unit: GlucoseUnit = MG_DL,\n  thresholds?: { mgdl?: number; mmoll?: number }\n): boolean {\n  const hypoMgdl = thresholds?.mgdl ?? HYPO_THRESHOLD_MGDL\n  const hypoMmoll = thresholds?.mmoll ?? HYPO_THRESHOLD_MMOLL\n  return unit === MG_DL ? val < hypoMgdl : val < hypoMmoll\n}\n\n/**\n * Checks if a glucose value is above the hyperglycemia threshold for the given unit.\n * Used for detecting high glucose events.\n * @param val - Glucose value (number)\n * @param unit - Glucose unit ('mg/dL' or 'mmol/L'), default: 'mg/dL'\n * @param thresholds - Optional custom thresholds ({ mgdl?: number; mmoll?: number })\n * @returns True if value is above the hyperglycemia threshold\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html\n */\nexport function isHyper(\n  val: number,\n  unit: GlucoseUnit = MG_DL,\n  thresholds?: { mgdl?: number; mmoll?: number }\n): boolean {\n  const hyperMgdl = thresholds?.mgdl ?? HYPER_THRESHOLD_MGDL\n  const hyperMmoll = thresholds?.mmoll ?? HYPER_THRESHOLD_MMOLL\n  return unit === MG_DL ? val > hyperMgdl : val > hyperMmoll\n}\n\n/**\n * Returns a glucose status label ('low', 'normal', or 'high') based on thresholds for the given unit.\n * Used for charting, alerts, and reporting.\n * @param val - Glucose value (number)\n * @param unit - Glucose unit ('mg/dL' or 'mmol/L'), default: 'mg/dL'\n * @param thresholds - Optional custom thresholds for hypo/hyper ({ hypo?: { mgdl?: number; mmoll?: number }, hyper?: { mgdl?: number; mmoll?: number } })\n * @returns 'low', 'normal', or 'high' based on configured thresholds\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-level-ranges.html\n */\nexport function getGlucoseLabel(\n  val: number,\n  unit: GlucoseUnit = MG_DL,\n  thresholds?: {\n    hypo?: { mgdl?: number; mmoll?: number }\n    hyper?: { mgdl?: number; mmoll?: number }\n  }\n): 'low' | 'normal' | 'high' {\n  if (isHypo(val, unit, thresholds?.hypo)) return 'low'\n  if (isHyper(val, unit, thresholds?.hyper)) return 'high'\n  return 'normal'\n}\n\n/**\n * Parses a glucose string (e.g., \"100 mg/dL\", \"5.5 mmol/L\") into value and unit.\n * Used for robust input validation and data ingestion.\n * @param input - String in the format \"value unit\" (e.g., \"100 mg/dL\")\n * @returns Object with numeric value and validated unit\n * @throws {Error} If input string is invalid or not in expected format\n * @example\n * parseGlucoseString(\"100 mg/dL\") // { value: 100, unit: \"mg/dL\" }\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html\n */\nexport function parseGlucoseString(input: string): {\n  value: number\n  unit: GlucoseUnit\n} {\n  if (!isValidGlucoseString(input)) {\n    throw new Error(\n      'Invalid glucose string format. Use \"100 mg/dL\" or \"5.5 mmol/L\".'\n    )\n  }\n\n  const cleaned = input.trim().replace(/\\s+/g, ' ')\n  const match = cleaned.match(/^([\\d.]+) (mg\\/dL|mmol\\/L)$/i)\n  const [, rawValue, rawUnit] = match!\n\n  return {\n    value: parseFloat(rawValue),\n    unit: rawUnit.toLowerCase() === 'mg/dl' ? MG_DL : MMOL_L,\n  }\n}\n\n/**\n * Validates a glucose value and unit.\n * Ensures value is a positive finite number and unit is supported.\n * @param value - Glucose value to validate\n * @param unit - Glucose unit to validate\n * @returns True if value and unit are valid\n */\nexport function isValidGlucoseValue(value: unknown, unit: unknown): boolean {\n  return (\n    typeof value === 'number' &&\n    Number.isFinite(value) &&\n    value > 0 &&\n    (unit === MG_DL || unit === MMOL_L)\n  )\n}\n","// @file src/validators.ts\n\n/**\n * Validates a clinical fasting insulin value (µIU/mL).\n * Ensures value is a positive finite number and within plausible physiological range.\n * @param value - Candidate insulin value\n * @returns True if value is a valid fasting insulin (µIU/mL)\n * @see https://www.ncbi.nlm.nih.gov/books/NBK279396/ (normal fasting insulin: ~2-25 µIU/mL, but allow wider plausible range for outliers)\n */\nexport function isValidInsulin(value: unknown): value is number {\n  return (\n    typeof value === 'number' &&\n    Number.isFinite(value) &&\n    value > 0 &&\n    value < 1000 // upper bound is generous for outliers, adjust as needed\n  )\n}\n","/**\n * @file src/alignment.ts\n *\n * Utility functions for evaluating glycemic alignment using A1C, fasting glucose, and insulin.\n * These functions are for informational and educational purposes only. They do not constitute\n * medical advice, diagnosis, or treatment.\n */\nimport { isValidA1C } from './a1c'\nimport { isValidGlucoseValue } from './glucose'\nimport { isValidInsulin } from './validators'\nimport {\n  HOMA_IR_DENOMINATOR,\n  HOMA_IR_CUTOFFS,\n  A1C_TO_EAG_MULTIPLIER,\n  A1C_TO_EAG_CONSTANT,\n  MG_DL,\n} from './constants'\n\n/**\n * Calculates HOMA-IR (Homeostatic Model Assessment for Insulin Resistance) from fasting glucose and insulin.\n *\n * Formula: HOMA-IR = (fasting glucose [mg/dL] × fasting insulin [µIU/mL]) / 405\n *\n * Used for estimating insulin resistance. Not a diagnostic tool.\n *\n * @param glucose - Fasting glucose value in mg/dL. Must be a positive finite number.\n * @param insulin - Fasting insulin value in µIU/mL. Must be a positive finite number.\n * @returns Object with numeric HOMA-IR value and interpretation label.\n * @throws {Error} If glucose or insulin are invalid (non-finite, zero, or negative).\n * @see https://pubmed.ncbi.nlm.nih.gov/3899825/ (Original HOMA-IR publication)\n * @see https://diabetesjournals.org/care/article/26/1/118/22567/Prevalence-and-Concomitants-of-Glucose-Intolerance (ADA: Glucose Intolerance and HOMA-IR context)\n */\nexport function calculateHOMAIR(glucose: number, insulin: number) {\n  if (!isValidGlucoseValue(glucose, MG_DL)) {\n    throw new Error(\n      'Invalid fasting glucose value (must be a positive number in mg/dL)'\n    )\n  }\n  if (!isValidInsulin(insulin)) {\n    throw new Error(\n      'Invalid fasting insulin value (must be a positive number in µIU/mL)'\n    )\n  }\n  const score = (glucose * insulin) / HOMA_IR_DENOMINATOR\n  return {\n    value: score,\n    interpretation: interpretHOMAIR(score),\n  }\n}\n\n/**\n * Interprets a numeric HOMA-IR score into an insulin sensitivity/resistance category.\n *\n * Categories:\n *   - Very insulin sensitive: < 1.0\n *   - Normal insulin sensitivity: 1.0–2.0\n *   - Early insulin resistance: 2.0–2.9\n *   - Significant insulin resistance: ≥ 2.9\n *\n * Cutoffs are conventional and may vary by population and lab; not diagnostic.\n *\n * @param score - HOMA-IR numeric value\n * @returns Interpretation string\n */\nfunction interpretHOMAIR(score: number): string {\n  if (score < HOMA_IR_CUTOFFS.VERY_SENSITIVE) return 'Very insulin sensitive'\n  if (score >= HOMA_IR_CUTOFFS.VERY_SENSITIVE && score < HOMA_IR_CUTOFFS.NORMAL)\n    return 'Normal insulin sensitivity'\n  if (\n    score >= HOMA_IR_CUTOFFS.NORMAL &&\n    score < HOMA_IR_CUTOFFS.EARLY_RESISTANCE\n  )\n    return 'Early insulin resistance'\n  return 'Significant insulin resistance'\n}\n\n/**\n * Checks consistency among A1C, fasting glucose, and fasting insulin markers.\n *\n * Returns:\n *   - Estimated average glucose (mg/dL), calculated per CDC formula\n *   - HOMA-IR result (value and interpretation)\n *   - Flags for potential inconsistencies\n *   - Informational note and disclaimer\n *\n * Used for high-level insight and trend alignment, not for diagnosis.\n *\n * @param a1c - A1C value (percentage). Must be a positive finite number.\n * @param glucose - Fasting glucose value in mg/dL. Must be a positive finite number.\n * @param insulin - Fasting insulin value in µIU/mL. Must be a positive finite number.\n * @returns Object with estimated average glucose (mg/dL), HOMA-IR result object, flags array, recommendation string, and disclaimer.\n * @throws {Error} If any input value is invalid (non-finite, zero, or negative).\n * @see https://www.cdc.gov/diabetes/diabetes-testing/prediabetes-a1c-test.html (CDC: eAG formula)\n */\nexport function checkGlycemicAlignment(\n  a1c: number,\n  glucose: number,\n  insulin: number\n) {\n  if (!isValidA1C(a1c)) {\n    throw new Error('Invalid A1C value (must be a positive number < 20%)')\n  }\n  if (!isValidGlucoseValue(glucose, MG_DL)) {\n    throw new Error(\n      'Invalid fasting glucose value (must be a positive number in mg/dL)'\n    )\n  }\n  if (!isValidInsulin(insulin)) {\n    throw new Error(\n      'Invalid fasting insulin value (must be a positive number in µIU/mL)'\n    )\n  }\n\n  const estimatedAvg = A1C_TO_EAG_MULTIPLIER * a1c - A1C_TO_EAG_CONSTANT\n  const homaResult = calculateHOMAIR(glucose, insulin)\n\n  const flags: string[] = []\n\n  if (glucose > estimatedAvg + 20) {\n    flags.push(\n      'Fasting glucose is higher than estimated average glucose from A1C.'\n    )\n  }\n\n  if (insulin < 2 && glucose > 110) {\n    flags.push(\n      'Low insulin with high glucose may indicate reduced insulin secretion.'\n    )\n  }\n\n  return {\n    estimatedAverageGlucose: estimatedAvg,\n    homaIR: homaResult,\n    flags,\n    recommendation: flags.length\n      ? 'Some inconsistencies were detected in your markers. This may occur for various reasons including diet, stress, or lab variability.'\n      : 'Your markers appear consistent based on this informational analysis.',\n    disclaimer:\n      'This tool is for informational and educational purposes only. It does not constitute medical advice, diagnosis, or treatment.',\n  }\n}\n","// @file src/conversions.ts\n\nimport {\n  A1C_TO_EAG_MULTIPLIER,\n  A1C_TO_EAG_CONSTANT,\n  MGDL_MMOLL_CONVERSION,\n  MG_DL,\n  MMOL_L,\n  GMI_COEFFICIENTS,\n} from './constants'\nimport type { GlucoseUnit, EstimateGMIOptions, ConversionResult } from './types'\nimport { isEstimateGMIOptions } from './guards'\nimport { parseGlucoseString } from './glucose'\n\n/**\n * Converts clinical average glucose (mg/dL) to estimated A1C (percentage).\n * Used for clinical analytics and patient reporting.\n * @param avgMgDl - Average glucose in mg/dL\n * @returns Estimated A1C value (percentage)\n * @see https://www.cdc.gov/diabetes/managing/managing-blood-sugar/a1c.html\n */\nexport function estimateA1CFromAvgGlucose(avgMgDl: number): number {\n  return +((avgMgDl + A1C_TO_EAG_CONSTANT) / A1C_TO_EAG_MULTIPLIER).toFixed(2)\n}\n\n/**\n * Converts clinical A1C value (percentage) to estimated average glucose (mg/dL).\n * Used for clinical analytics and patient reporting.\n * @param a1c - A1C value (percentage)\n * @returns Estimated average glucose in mg/dL\n * @see https://www.cdc.gov/diabetes/managing/managing-blood-sugar/a1c.html\n */\nexport function estimateAvgGlucoseFromA1C(a1c: number): number {\n  return Math.round(a1c * A1C_TO_EAG_MULTIPLIER - A1C_TO_EAG_CONSTANT)\n}\n\n/**\n * Estimates eAG (estimated average glucose, mg/dL) from clinical A1C value.\n * Throws if input is negative. Used for clinical and research reporting.\n * @param a1c - A1C value (percentage)\n * @returns Estimated average glucose (mg/dL)\n * @throws {Error} If a1c is negative\n * @see https://www.cdc.gov/diabetes/managing/managing-blood-sugar/a1c.html\n */\nexport function estimateEAG(a1c: number): number {\n  if (a1c < 0) throw new Error('A1C must be positive')\n  const eAG = Number(\n    (a1c * A1C_TO_EAG_MULTIPLIER - A1C_TO_EAG_CONSTANT).toFixed(10)\n  )\n  return Math.round(eAG)\n}\n\n/**\n * Estimates A1C from average glucose.\n * @param avgGlucose - Average glucose value\n * @param unit - Glucose unit (mg/dL or mmol/L)\n * @returns Estimated A1C\n * @see https://www.cdc.gov/diabetes/managing/managing-blood-sugar/a1c.html\n */\nexport function estimateA1CFromAverage(\n  avgGlucose: number,\n  unit: GlucoseUnit = MG_DL\n): number {\n  const glucoseMgdl =\n    unit === MMOL_L ? avgGlucose * MGDL_MMOLL_CONVERSION : avgGlucose\n  return +((glucoseMgdl + 46.7) / 28.7).toFixed(2)\n}\n\n/**\n * Converts A1C to Glucose Management Indicator (GMI).\n * @param a1c - A1C value\n * @returns GMI value\n * @see https://diatribe.org/glucose-management-indicator-gmi\n */\nexport function a1cToGMI(a1c: number): number {\n  return +(\n    GMI_COEFFICIENTS.A1C_INTERCEPT +\n    GMI_COEFFICIENTS.A1C_SLOPE * a1c\n  ).toFixed(2)\n}\n\n/**\n * Estimate Glucose Management Indicator (GMI) from average glucose.\n * @param valueOrOptions - Glucose value, string, or options object\n * @param unit - Glucose unit (if value is a number)\n * @returns GMI value\n * @throws {Error} If unit is required but not provided when input is a number.\n * @throws {Error} If the glucose unit is unsupported.\n * @throws {Error} If the glucose value is not a positive number.\n * @see https://diatribe.org/glucose-management-indicator-gmi\n */\nexport function estimateGMI(\n  valueOrOptions: number | string | EstimateGMIOptions,\n  unit?: GlucoseUnit\n): number {\n  let value: number\n  let resolvedUnit: GlucoseUnit\n\n  if (isEstimateGMIOptions(valueOrOptions)) {\n    value = valueOrOptions.value\n    resolvedUnit = valueOrOptions.unit\n  } else if (typeof valueOrOptions === 'string') {\n    const parsed = parseGlucoseString(valueOrOptions)\n    value = parsed.value\n    resolvedUnit = parsed.unit\n  } else {\n    if (!unit) throw new Error('Unit is required when input is a number.')\n    value = valueOrOptions\n    resolvedUnit = unit\n  }\n\n  if (![MG_DL, MMOL_L].includes(resolvedUnit)) {\n    throw new Error(`Unsupported glucose unit: ${resolvedUnit}`)\n  }\n\n  if (value <= 0 || !Number.isFinite(value)) {\n    throw new Error('Glucose value must be a positive number.')\n  }\n\n  const gmi =\n    resolvedUnit === MMOL_L\n      ? GMI_COEFFICIENTS.MMOL_L_SLOPE * value + GMI_COEFFICIENTS.MMOL_L_INTERCEPT\n      : GMI_COEFFICIENTS.MG_DL_SLOPE * value + GMI_COEFFICIENTS.MG_DL_INTERCEPT\n\n  return parseFloat(gmi.toFixed(1))\n}\n\n/**\n * Converts clinical glucose value from mg/dL to mmol/L.\n * Used for international interoperability and reporting.\n * @param val - Glucose value in mg/dL\n * @returns Value in mmol/L\n * @throws {Error} If val is not a finite number or is negative/zero\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html\n *\n * @example\n * ```typescript\n * const result = mgDlToMmolL(180)\n * console.log(result) // 10.0\n * ```\n */\nexport function mgDlToMmolL(val: number): number {\n  if (!Number.isFinite(val) || val <= 0)\n    throw new Error('Invalid glucose value')\n  return +(val / MGDL_MMOLL_CONVERSION).toFixed(1)\n}\n\n/**\n * Converts clinical glucose value from mmol/L to mg/dL.\n * Used for international interoperability and reporting.\n * @param val - Glucose value in mmol/L\n * @returns Value in mg/dL\n * @throws {Error} If val is not a finite number or is negative/zero\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html\n *\n * @example\n * ```typescript\n * const result = mmolLToMgDl(5.5)\n * console.log(result) // 99\n * ```\n */\nexport function mmolLToMgDl(val: number): number {\n  if (!Number.isFinite(val) || val <= 0)\n    throw new Error('Invalid glucose value')\n  return Math.round(val * MGDL_MMOLL_CONVERSION)\n}\n\n/**\n * Converts clinical glucose value between mg/dL and mmol/L.\n * Used for clinical interoperability and analytics.\n * @param value - Glucose value (number)\n * @param unit - Current glucose unit ('mg/dL' or 'mmol/L')\n * @returns Object with converted value and new unit\n * @throws {Error} If value is not a finite number or is negative/zero\n * @throws {Error} If unit is not a supported glucose unit\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html\n */\nexport function convertGlucoseUnit({\n  value,\n  unit,\n}: {\n  value: number\n  unit: GlucoseUnit\n}): ConversionResult {\n  if (!Number.isFinite(value) || value <= 0)\n    throw new Error('Invalid glucose value')\n  if (![MG_DL, MMOL_L].includes(unit)) throw new Error('Invalid unit')\n  if (unit === MG_DL)\n    return {\n      value: Math.round((value / MGDL_MMOLL_CONVERSION) * 10) / 10,\n      unit: MMOL_L,\n    }\n  return {\n    value: Math.round(value * MGDL_MMOLL_CONVERSION),\n    unit: MG_DL,\n  }\n}\n","// @file src/formatters.ts\n\nimport { GlucoseUnit } from './types'\n\n/**\n * Formats a clinical glucose value with unit and optional rounding.\n * Used for clinical reporting, charting, and data export.\n * @param val - Glucose value (number)\n * @param unit - Glucose unit ('mg/dL' or 'mmol/L')\n * @param options - Formatting options: { digits?: number; suffix?: boolean } (default: { digits: 0, suffix: true })\n * @returns Formatted glucose string (e.g., '5.5 mmol/L', '120 mg/dL')\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html\n */\nexport function formatGlucose(\n  val: number,\n  unit: GlucoseUnit,\n  options: { digits?: number; suffix?: boolean } = {}\n): string {\n  const digits = options.digits ?? 0\n  const suffix = options.suffix ?? true\n  const value = val.toFixed(digits)\n  return suffix ? `${value} ${unit}` : value\n}\n\n/**\n * Formats a value as a clinical percentage string (e.g., '85.0%').\n * Used for reporting TIR, CV, and other clinical metrics.\n * @param val - Value to format (fraction or percent)\n * @param digits - Number of decimal places (default: 1)\n * @returns Formatted percentage string (e.g., '85.0%')\n */\nexport function formatPercentage(val: number, digits = 1): string {\n  return `${val.toFixed(digits)}%`\n}\n\n/**\n * Formats a UTC ISO 8601 timestamp to a local-readable date/time string.\n * Used for clinical charting, logs, and reports. Supports optional IANA time zone.\n * @param iso - ISO 8601 timestamp string (e.g., '2024-03-20T10:00:00Z')\n * @param timeZone - Optional IANA time zone (e.g., 'America/New_York')\n * @returns Localized date/time string (e.g., 'Mar 20, 2024, 06:00 AM')\n * @throws {RangeError} If the ISO string is invalid or cannot be parsed\n */\nexport function formatDate(iso: string, timeZone?: string): string {\n  if (isNaN(Date.parse(iso))) {\n    throw new RangeError('Invalid ISO timestamp')\n  }\n  return new Date(iso).toLocaleString('en-US', {\n    timeZone,\n    year: 'numeric',\n    month: 'short',\n    day: '2-digit',\n    hour: '2-digit',\n    minute: '2-digit',\n  })\n}\n","// @file src/tir.ts\n\nimport { GlucoseReading, TIRResult } from './types'\n\n/**\n * Calculates clinical Time in Range (TIR) metrics for glucose readings.\n * Returns the percentage of readings in, below, and above the specified clinical target range.\n * @param readings - Array of glucose readings to analyze\n * @param target - Object specifying the target range ({ min, max })\n * @returns Object with in-range, below-range, and above-range percentages\n * @see https://care.diabetesjournals.org/content/42/8/1593\n */\nexport function calculateTIR(\n  readings: GlucoseReading[],\n  target: { min: number; max: number }\n): TIRResult {\n  if (readings.length === 0) {\n    return {\n      inRange: 0.0,\n      belowRange: 0.0,\n      aboveRange: 0.0,\n    }\n  }\n\n  let inRange = 0\n  let belowRange = 0\n  let aboveRange = 0\n\n  for (const r of readings) {\n    if (r.value < target.min) belowRange++\n    else if (r.value > target.max) aboveRange++\n    else inRange++\n  }\n\n  const total = readings.length\n  return {\n    inRange: +((inRange / total) * 100).toFixed(1),\n    belowRange: +((belowRange / total) * 100).toFixed(1),\n    aboveRange: +((aboveRange / total) * 100).toFixed(1),\n  }\n}\n\n/**\n * Generates a clinical summary string from a TIRResult object.\n * Used for reporting and visualization of TIR analytics.\n * @param result - TIR result breakdown to summarize\n * @returns String summarizing in-range, below-range, and above-range percentages (e.g., 'In Range: 70%, Below: 10%, Above: 20%')\n */\nexport function getTIRSummary(result: TIRResult): string {\n  return `In Range: ${result.inRange}%, Below: ${result.belowRange}%, Above: ${result.aboveRange}%`\n}\n\n/**\n * Groups glucose readings by date (YYYY-MM-DD).\n * @param readings - Array of glucose readings to group.\n * @returns An object mapping each date string to an array of readings for that day.\n */\nexport function groupByDay(\n  readings: GlucoseReading[]\n): Record<string, GlucoseReading[]> {\n  return readings.reduce((acc, reading) => {\n    const day = reading.timestamp.split('T')[0]\n    acc[day] = acc[day] || []\n    acc[day].push(reading)\n    return acc\n  }, {} as Record<string, GlucoseReading[]>)\n}\n\n/**\n * Calculates the percentage of glucose readings within a specified numeric range.\n * Used for clinical TIR analytics and custom range assessments.\n * @param readings - Array of glucose values (numbers) to analyze\n * @param lower - Lower bound of the target range (inclusive)\n * @param upper - Upper bound of the target range (inclusive)\n * @returns Percentage of readings within the specified range (0-100)\n */\nexport function calculateTimeInRange(\n  readings: number[],\n  lower: number,\n  upper: number\n): number {\n  if (readings.length === 0) {\n    return 0\n  }\n\n  const inRange = readings.filter((r) => r >= lower && r <= upper).length\n  return (inRange / readings.length) * 100\n}\n","// @file src/types.ts\n\nimport { MG_DL, MMOL_L } from './constants'\n\n/**\n * Supported clinical glucose units.\n * Used for all clinical analytics and conversions.\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html\n */\nexport type GlucoseUnit = typeof MG_DL | typeof MMOL_L\n\n/**\n * List of allowed clinical glucose units.\n * Used for input validation and unit conversion.\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html\n */\nexport const AllowedGlucoseUnits: GlucoseUnit[] = [MG_DL, MMOL_L]\n\n/**\n * Single clinical glucose reading.\n * Includes value, unit, and ISO 8601 timestamp for clinical analytics.\n * @see https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7445493/\n */\nexport interface GlucoseReading {\n  readonly value: number\n  readonly unit: GlucoseUnit\n  readonly timestamp: string // ISO 8601\n}\n\n/**\n * Result object for clinical Time-in-Range (TIR) analytics.\n * Percentages for in-range, below-range, and above-range readings.\n * @see https://care.diabetesjournals.org/content/42/8/1593\n */\nexport interface TIRResult {\n  inRange: number\n  belowRange: number\n  aboveRange: number\n}\n\n/**\n * Options for clinical GMI (Glucose Management Indicator) estimation.\n * Used to standardize GMI calculation input.\n * @see https://diatribe.org/glucose-management-indicator-gmi\n */\nexport interface EstimateGMIOptions {\n  value: number\n  unit: GlucoseUnit\n}\n\n/**\n * Result of glucose unit conversion.\n * Provides converted value and new unit for clinical interoperability.\n * @see https://www.diabetes.co.uk/diabetes_care/blood-sugar-conversion.html\n */\nexport interface ConversionResult {\n  /** Converted glucose value */\n  readonly value: number\n  /** New glucose unit after conversion */\n  readonly unit: GlucoseUnit\n}\n\n/**\n * Options for clinical Time-in-Range (TIR) analytics.\n */\nexport interface TIROptions {\n  readings: GlucoseReading[]\n  unit: GlucoseUnit\n  range: [number, number]\n}\n\n/**\n * Single clinical A1C reading (value and ISO date).\n */\nexport interface A1CReading {\n  value: number\n  date: string // ISO 8601 date string\n}\n\n/**\n * Options for clinical glucose statistics analytics.\n * Controls which metrics are calculated and reported.\n */\nexport interface GlucoseStatsOptions {\n  readings: GlucoseReading[]\n  unit: GlucoseUnit\n  range: [number, number]\n  gmi?: boolean\n  a1c?: boolean\n  tir?: boolean\n  tirRange?: [number, number]\n  tirPercent?: boolean\n  tirPercentBelow?: boolean\n  tirPercentAbove?: boolean\n  tirPercentInRange?: boolean\n  tirPercentBelowRounded?: boolean\n  tirPercentAboveRounded?: boolean\n  tirPercentInRangeRounded?: boolean\n}\n\n// ============================================================================\n// Enhanced Time-in-Range (TIR) Types\n// Per International Consensus on Time in Range (Battelino et al. 2019)\n// ============================================================================\n\n/**\n * Population type for TIR target assessment.\n * Different populations have different clinical goals.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport type TIRPopulation = 'standard' | 'older-adults' | 'high-risk'\n\n/**\n * Overall glycemic control assessment based on TIR metrics.\n */\nexport type TIRAssessment = 'excellent' | 'good' | 'needs improvement' | 'concerning'\n\n/**\n * Detailed metrics for a single glucose range.\n * Provides percentage, duration, count, and average value for clinical analysis.\n */\nexport interface RangeMetrics {\n  /** Percentage of readings in this range (0-100) */\n  readonly percentage: number\n  /** Total duration in this range (minutes) */\n  readonly duration: number\n  /** Count of readings in this range */\n  readonly readingCount: number\n  /** Average glucose value in this range (mg/dL or mmol/L) */\n  readonly averageValue: number | null\n}\n\n/**\n * Assessment of whether TIR metrics meet clinical targets.\n * Based on International Consensus on Time in Range (2019).\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport interface TargetAssessment {\n  /** TIR ≥70% for standard population, ≥50% for older adults */\n  readonly tirMeetsGoal: boolean\n  /** TBR Level 1 <4% for standard, <1% for older adults */\n  readonly tbrLevel1Safe: boolean\n  /** TBR Level 2 <1% for standard, <0.5% for older adults */\n  readonly tbrLevel2Safe: boolean\n  /** TAR Level 1 <25% (all populations) */\n  readonly tarLevel1Acceptable: boolean\n  /** TAR Level 2 <5% (all populations) */\n  readonly tarLevel2Acceptable: boolean\n  /** Overall assessment of glycemic control */\n  readonly overallAssessment: TIRAssessment\n  /** Clinical recommendations based on metrics */\n  readonly recommendations: readonly string[]\n}\n\n/**\n * Summary statistics for TIR calculation.\n */\nexport interface TIRSummary {\n  /** Total number of glucose readings analyzed */\n  readonly totalReadings: number\n  /** Total duration of data analyzed (minutes) */\n  readonly totalDuration: number\n  /** Data quality assessment */\n  readonly dataQuality: 'excellent' | 'good' | 'fair' | 'poor'\n}\n\n/**\n * Complete Enhanced Time-in-Range result.\n * Provides detailed breakdown across five clinical glucose ranges per International Consensus 2019.\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n */\nexport interface EnhancedTIRResult {\n  /** Very Low: <54 mg/dL (3.0 mmol/L) - Level 2 Hypoglycemia */\n  readonly veryLow: RangeMetrics\n  /** Low: 54-69 mg/dL (3.0-3.8 mmol/L) - Level 1 Hypoglycemia */\n  readonly low: RangeMetrics\n  /** In Range: 70-180 mg/dL (3.9-10.0 mmol/L) - Target Range */\n  readonly inRange: RangeMetrics\n  /** High: 181-250 mg/dL (10.1-13.9 mmol/L) - Level 1 Hyperglycemia */\n  readonly high: RangeMetrics\n  /** Very High: >250 mg/dL (>13.9 mmol/L) - Level 2 Hyperglycemia */\n  readonly veryHigh: RangeMetrics\n  /** Assessment against clinical targets */\n  readonly meetsTargets: TargetAssessment\n  /** Summary statistics */\n  readonly summary: TIRSummary\n}\n\n/**\n * Options for Enhanced TIR calculation.\n * Allows customization for different clinical populations and use cases.\n */\nexport interface EnhancedTIROptions {\n  /** Population type for target assessment (default: 'standard') */\n  readonly population?: TIRPopulation\n  /** Glucose unit for input validation (default: 'mg/dL') */\n  readonly unit?: GlucoseUnit\n  /** Override for the very low threshold (<54 mg/dL). Value must be provided in mg/dL. */\n  readonly veryLowThreshold?: number\n  /** Override for the low threshold (54-69 mg/dL). Value must be provided in mg/dL. */\n  readonly lowThreshold?: number\n  /** Override for the high threshold (181-250 mg/dL). Value must be provided in mg/dL. */\n  readonly highThreshold?: number\n  /** Override for the very high threshold (>250 mg/dL). Value must be provided in mg/dL. */\n  readonly veryHighThreshold?: number\n}\n\n/**\n * Pregnancy-specific Time-in-Range result.\n * Uses tighter target range per ADA 2024 guidelines for pregnancy.\n * @see {@link https://diabetesjournals.org/care/article/47/Supplement_1/S282 | ADA Standards of Care (2024)}\n */\nexport interface PregnancyTIRResult {\n  /** In Range: 63-140 mg/dL (3.5-7.8 mmol/L) */\n  readonly inRange: RangeMetrics\n  /** Below Range: <63 mg/dL (3.5 mmol/L) */\n  readonly belowRange: RangeMetrics\n  /** Above Range: >140 mg/dL (7.8 mmol/L) */\n  readonly aboveRange: RangeMetrics\n  /** Whether metrics meet pregnancy-specific targets (TIR >70%, TBR <4%, TAR <25%) */\n  readonly meetsPregnancyTargets: boolean\n  /** Clinical recommendations */\n  readonly recommendations: readonly string[]\n  /** Summary statistics */\n  readonly summary: TIRSummary\n}\n\n/**\n * Options for Pregnancy TIR calculation.\n */\nexport interface PregnancyTIROptions {\n  /** Glucose unit for input validation (default: 'mg/dL') */\n  readonly unit?: GlucoseUnit\n}\n","// @file src/mage.ts\n// Mean Amplitude of Glycemic Excursions (MAGE) Implementation\n// Based on Service FJ et al. (1970)\n\n/**\n * Calculates Mean Amplitude of Glycemic Excursions (MAGE).\n * Implements Service FJ et al. (1970) methodology.\n * @param readings - Array of glucose values (mg/dL or mmol/L)\n * @param options - Configuration options for MAGE calculation\n * @returns MAGE value, or NaN if insufficient data or no valid excursions\n * @see https://pubmed.ncbi.nlm.nih.gov/5469118/ (Service FJ, et al. 1970)\n * @see https://journals.sagepub.com/doi/10.1177/19322968211061165 (Fernandes NJ, et al. 2022)\n * @see https://care.diabetesjournals.org/content/42/8/1593 (ADA 2019)\n * @example\n * // Basic usage\n * glucoseMAGE([100, 120, 80, 160, 90, 140, 70, 180])\n * // Advanced usage\n * glucoseMAGE(readings, { shortWindow: 5, longWindow: 32, direction: 'auto' })\n * @remarks\n * - Minimum 24 data points recommended (1 day of hourly readings)\n * - Best suited for continuous glucose monitoring (CGM) data\n * - Not recommended for sparse or irregular measurements\n * - Uses dual moving averages, three-point excursion definition, and prevents double-counting.\n */\nexport function glucoseMAGE(\n  readings: number[],\n  options: MAGEOptions = {}\n): number {\n  // Input validation\n  if (!Array.isArray(readings) || readings.length < 2) {\n    return NaN\n  }\n\n  // Remove any NaN, undefined, null, or infinite values\n  const validReadings = readings.filter(\n    (r) =>\n      typeof r === 'number' &&\n      !isNaN(r) &&\n      isFinite(r) &&\n      r !== null &&\n      r !== undefined\n  )\n\n  if (validReadings.length < 3) {\n    return NaN // Need at least 3 points for MAGE calculation\n  }\n\n  // Calculate standard deviation (required for excursion filtering)\n  const mean =\n    validReadings.reduce((sum, v) => sum + v, 0) / validReadings.length\n  const variance =\n    validReadings.reduce((sum, v) => sum + Math.pow(v - mean, 2), 0) /\n    (validReadings.length - 1)\n  const sd = Math.sqrt(variance)\n\n  if (sd === 0 || !isFinite(sd)) {\n    return NaN // No variability\n  }\n\n  // Extract options with defaults\n  const {\n    shortWindow = Math.max(\n      3,\n      Math.min(5, Math.floor(validReadings.length / 8))\n    ),\n    longWindow = Math.max(\n      shortWindow + 2,\n      Math.min(32, Math.floor(validReadings.length / 3))\n    ),\n    direction = 'auto',\n  } = options\n\n  // For small datasets or when moving averages don't work well, use simple method\n  if (validReadings.length < 10 || longWindow >= validReadings.length - 2) {\n    return _calculateSimpleMAGE(validReadings, sd)\n  }\n\n  try {\n    // Step 1: Calculate moving averages and find crossing points\n    const crossingPoints = _findMovingAverageCrossings(\n      validReadings,\n      shortWindow,\n      longWindow\n    )\n\n    // Step 2: Identify turning points (peaks/nadirs) between crossings\n    const turningPoints = _findTurningPoints(validReadings, crossingPoints)\n\n    if (turningPoints.length < 3) {\n      // Fall back to simple method if not enough turning points\n      return _calculateSimpleMAGE(validReadings, sd)\n    }\n\n    // Step 3: Calculate excursions and filter by SD threshold\n    const excursions = _calculateExcursions(turningPoints, sd)\n\n    if (excursions.length === 0) {\n      // Try simple method as fallback\n      return _calculateSimpleMAGE(validReadings, sd)\n    }\n\n    // Step 4: Select direction (ascending/descending) and calculate MAGE\n    return _calculateMAGEFromExcursions(excursions, direction)\n  /* c8 ignore start */\n  } catch {\n    // If complex algorithm fails, fall back to simple method\n    return _calculateSimpleMAGE(validReadings, sd);\n  }\n  /* c8 ignore stop */\n}\n\n/**\n * Configuration options for MAGE calculation.\n * @property shortWindow - Short moving average window (default: 5)\n * @property longWindow - Long moving average window (default: 32)\n * @property direction - Excursion direction: 'auto', 'ascending', or 'descending'\n */\nexport interface MAGEOptions {\n  /** Short moving average window size (default: 5, recommended range: 1-7) */\n  shortWindow?: number\n\n  /** Long moving average window size (default: 32, recommended range: 16-38) */\n  longWindow?: number\n\n  /**\n   * Direction of excursions to count:\n   * - 'auto': Use first excursion type that exceeds SD threshold (Service 1970 default)\n   * - 'ascending': Count only ascending excursions (MAGE+)\n   * - 'descending': Count only descending excursions (MAGE-)\n   */\n  direction?: 'auto' | 'ascending' | 'descending'\n}\n\n/**\n * Represents a turning point (peak or nadir) in the glucose trace for MAGE analysis.\n * @property index - Index of the turning point in the glucose array\n * @property value - Glucose value at the turning point\n * @property type - 'peak' or 'nadir'\n */\ninterface TurningPoint {\n  /** Index in the original readings array */\n  index: number\n  /** Glucose value at this turning point */\n  value: number\n  /** Type of turning point */\n  type: 'peak' | 'nadir'\n}\n\n/**\n * Represents a valid MAGE excursion with left and right half-excursion amplitudes.\n * @property leftAmplitude - Amplitude from turning point to nadir/peak (left half)\n * @property rightAmplitude - Amplitude from nadir/peak to next turning point (right half)\n * @property direction - 'ascending' or 'descending'\n * @property indices - Indices of the excursion points ([i, j] or [i, j, k])\n */\ninterface Excursion {\n  /** Left half-excursion amplitude */\n  leftAmplitude: number\n  /** Right half-excursion amplitude */\n  rightAmplitude: number\n  /** Direction of the central turning point */\n  direction: 'ascending' | 'descending'\n  /** Indices of the turning points forming this excursion (2 or 3 elements) */\n  indices: [number, number] | [number, number, number]\n}\n\n/**\n * Find crossing points between short and long moving averages.\n * These crossings define intervals where turning points must exist.\n */\nfunction _findMovingAverageCrossings(\n  readings: number[],\n  shortWindow: number,\n  longWindow: number\n): number[] {\n  const shortMA = _calculateMovingAverage(readings, shortWindow)\n  const longMA = _calculateMovingAverage(readings, longWindow)\n\n  const crossings: number[] = [0] // First point is always a crossing\n\n  for (let i = 1; i < shortMA.length; i++) {\n    const prevShort = shortMA[i - 1]\n    const currShort = shortMA[i]\n    const prevLong = longMA[i - 1]\n    const currLong = longMA[i]\n\n    // Check if short MA crossed long MA\n    if (\n      (prevShort <= prevLong && currShort > currLong) ||\n      (prevShort >= prevLong && currShort < currLong)\n    ) {\n      crossings.push(i)\n    }\n  }\n\n  crossings.push(readings.length - 1) // Last point is always a crossing\n  return crossings\n}\n\n/**\n * Calculate simple moving average with specified window size.\n */\nfunction _calculateMovingAverage(\n  values: number[],\n  windowSize: number\n): number[] {\n  const result: number[] = []\n  const halfWindow = Math.floor(windowSize / 2)\n\n  for (let i = 0; i < values.length; i++) {\n    const start = Math.max(0, i - halfWindow)\n    const end = Math.min(values.length - 1, i + halfWindow)\n\n    let sum = 0\n    let count = 0\n    for (let j = start; j <= end; j++) {\n      sum += values[j]\n      count++\n    }\n\n    result.push(sum / count)\n  }\n\n  return result\n}\n\n/**\n * Find alternating turning points (peaks/nadirs) between crossing points.\n * Implements the algorithm with whiplash protection.\n */\nfunction _findTurningPoints(\n  readings: number[],\n  crossings: number[]\n): TurningPoint[] {\n  const turningPoints: TurningPoint[] = []\n\n  for (let i = 0; i < crossings.length - 1; i++) {\n    const start = crossings[i]\n    const end = crossings[i + 1]\n\n    // Find the extreme value in this interval\n    let extremeIndex = start\n    let extremeValue = readings[start]\n    let isMaximum = true\n\n    // Determine if we're looking for a peak or nadir based on alternation\n    if (turningPoints.length > 0) {\n      const lastType = turningPoints[turningPoints.length - 1].type\n      isMaximum = lastType === 'nadir' // Alternate: nadir -> peak, peak -> nadir\n    } else {\n      // For the first interval, find the actual extreme\n      let maxVal = readings[start]\n      let minVal = readings[start]\n      let maxIdx = start\n      let minIdx = start\n\n      for (let j = start + 1; j <= end; j++) {\n        if (readings[j] > maxVal) {\n          maxVal = readings[j]\n          maxIdx = j\n        }\n        /* c8 ignore start -- minVal comparison rarely triggers in typical glucose patterns */\n        if (readings[j] < minVal) {\n          minVal = readings[j]\n          minIdx = j\n        }\n        /* c8 ignore stop */\n      }\n\n      // Choose the more extreme one\n      const maxDiff = Math.abs(maxVal - readings[start])\n      const minDiff = Math.abs(minVal - readings[start])\n      /* c8 ignore start */\n      if (maxDiff >= minDiff) {\n        extremeIndex = maxIdx\n        extremeValue = maxVal\n        isMaximum = true\n      } else {\n        extremeIndex = minIdx\n        extremeValue = minVal\n        isMaximum = false\n      }\n      /* c8 ignore stop */\n    }\n\n    // Find the actual extreme in the interval\n    if (isMaximum) {\n      for (let j = start; j <= end; j++) {\n        if (readings[j] > extremeValue) {\n          extremeValue = readings[j]\n          extremeIndex = j\n        }\n      }\n    } else {\n      for (let j = start; j <= end; j++) {\n        if (readings[j] < extremeValue) {\n          extremeValue = readings[j]\n          extremeIndex = j\n        }\n      }\n    }\n\n    turningPoints.push({\n      index: extremeIndex,\n      value: extremeValue,\n      type: isMaximum ? 'peak' : 'nadir',\n    })\n  }\n\n  return turningPoints\n}\n\n/**\n * Calculate excursions from turning points and filter by SD threshold.\n * Implements the Service 1970 three-point excursion definition.\n */\nfunction _calculateExcursions(\n  turningPoints: TurningPoint[],\n  sd: number\n): Excursion[] {\n  const excursions: Excursion[] = []\n\n  // Need at least 3 turning points for an excursion\n  for (let i = 0; i < turningPoints.length - 2; i++) {\n    const tp1 = turningPoints[i]\n    const tp2 = turningPoints[i + 1]\n    const tp3 = turningPoints[i + 2]\n\n    const leftAmplitude = Math.abs(tp2.value - tp1.value)\n    const rightAmplitude = Math.abs(tp3.value - tp2.value)\n\n    // Both half-excursions must exceed 1 SD (Service 1970 requirement)\n    if (leftAmplitude > sd && rightAmplitude > sd) {\n      excursions.push({\n        leftAmplitude,\n        rightAmplitude,\n        direction: tp2.type === 'peak' ? 'ascending' : 'descending',\n        indices: [tp1.index, tp2.index, tp3.index],\n      })\n    }\n  }\n\n  return excursions\n}\n\n/**\n * Calculate final MAGE value from valid excursions.\n * Implements direction selection and accumulation optimization.\n */\nfunction _calculateMAGEFromExcursions(\n  excursions: Excursion[],\n  direction: 'auto' | 'ascending' | 'descending'\n): number {\n  let targetDirection: 'ascending' | 'descending'\n\n  if (direction === 'auto') {\n    // Use the direction of the first valid excursion (Service 1970 default)\n    targetDirection = excursions[0].direction\n  } else {\n    targetDirection = direction\n  }\n\n  // Filter excursions by direction to prevent double-counting\n  const filteredExcursions = excursions.filter(\n    (e) => e.direction === targetDirection\n  )\n\n  /* c8 ignore start */\n  if (filteredExcursions.length === 0) {\n    return NaN\n  }\n  /* c8 ignore stop */\n\n  // Calculate mean of the appropriate half-excursion amplitudes\n  const amplitudes = filteredExcursions.map((e) =>\n    targetDirection === 'ascending' ? e.leftAmplitude : e.rightAmplitude\n  )\n\n  return amplitudes.reduce((sum, amp) => sum + amp, 0) / amplitudes.length\n}\n\n/**\n * Calculate simple MAGE value for small datasets or as fallback.\n * Uses a more robust approach to find peaks and nadirs.\n */\nfunction _calculateSimpleMAGE(readings: number[], sd: number): number {\n  /* c8 ignore start */\n  if (readings.length < 3) {\n    return NaN\n  }\n  /* c8 ignore stop */\n\n  // Find all local peaks and nadirs using a more robust method\n  const turningPoints: TurningPoint[] = []\n\n  // Use a sliding window approach to identify turning points\n  const windowSize = Math.max(1, Math.min(3, Math.floor(readings.length / 10)))\n\n  for (let i = windowSize; i < readings.length - windowSize; i++) {\n    const current = readings[i]\n    let isPeak = true\n    let isNadir = true\n\n    // Check if current point is higher/lower than surrounding points\n    for (let j = i - windowSize; j <= i + windowSize; j++) {\n      if (j === i) continue\n      if (readings[j] >= current) isPeak = false\n      if (readings[j] <= current) isNadir = false\n    }\n\n    if (isPeak) {\n      turningPoints.push({ index: i, value: current, type: 'peak' })\n    } else if (isNadir) {\n      turningPoints.push({ index: i, value: current, type: 'nadir' })\n    }\n  }\n\n  // If no turning points found with windowing, try simple adjacent comparison\n  /* c8 ignore next 10 */\n  if (turningPoints.length === 0) {\n    for (let i = 1; i < readings.length - 1; i++) {\n      if (readings[i] > readings[i - 1] && readings[i] > readings[i + 1]) {\n        turningPoints.push({ index: i, value: readings[i], type: 'peak' })\n      } else if (\n        readings[i] < readings[i - 1] &&\n        readings[i] < readings[i + 1]\n      ) {\n        turningPoints.push({ index: i, value: readings[i], type: 'nadir' })\n      }\n    }\n  }\n\n  if (turningPoints.length < 2) {\n    return NaN // Need at least 2 turning points\n  }\n\n  // Calculate all possible excursions between consecutive turning points\n  const validAmplitudes: number[] = []\n\n  for (let i = 0; i < turningPoints.length - 1; i++) {\n    const tp1 = turningPoints[i]\n    const tp2 = turningPoints[i + 1]\n\n    const amplitude = Math.abs(tp2.value - tp1.value)\n\n    // Only consider excursions exceeding 1 SD\n    if (amplitude > sd) {\n      validAmplitudes.push(amplitude)\n    }\n  }\n\n  // Also check for three-point excursions (peak-nadir-peak or nadir-peak-nadir)\n  for (let i = 0; i < turningPoints.length - 2; i++) {\n    const tp1 = turningPoints[i]\n    const tp2 = turningPoints[i + 1]\n    const tp3 = turningPoints[i + 2]\n\n    // Check if this forms a valid three-point excursion\n    if (\n      (tp1.type === 'peak' && tp2.type === 'nadir' && tp3.type === 'peak') ||\n      (tp1.type === 'nadir' && tp2.type === 'peak' && tp3.type === 'nadir')\n    ) {\n      const leftAmplitude = Math.abs(tp2.value - tp1.value)\n      const rightAmplitude = Math.abs(tp3.value - tp2.value)\n\n      // Both halves must exceed 1 SD (per Service 1970)\n      if (leftAmplitude > sd && rightAmplitude > sd) {\n        const excursionAmplitude = (leftAmplitude + rightAmplitude) / 2\n        validAmplitudes.push(excursionAmplitude)\n      }\n    }\n  }\n\n  // Calculate MAGE from valid amplitudes\n  /* c8 ignore start */\n  if (validAmplitudes.length === 0) {\n    return NaN\n  }\n  /* c8 ignore stop */\n\n  return (\n    validAmplitudes.reduce((sum, amp) => sum + amp, 0) / validAmplitudes.length\n  )\n}\n","// @file src/variability.ts\n\nimport { glucoseMAGE as mageImpl, type MAGEOptions } from './mage';\n\n/**\n * Calculates the unbiased sample standard deviation (SD) of glucose values.\n * Uses n-1 in the denominator (sample SD), as recommended in research guidelines.\n *\n * @param readings Array of glucose values (numbers)\n * @returns Standard deviation, or NaN if fewer than 2 values\n * @throws {TypeError} If readings is not an array\n * @see {@link https://care.diabetesjournals.org/content/42/8/1593 ADA 2019: Glycemic Targets}\n * @see {@link https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7445493/ ISPAD 2019}\n * @example\n * ```ts\n * glucoseStandardDeviation([100, 120, 140]) // 20\n * glucoseStandardDeviation([]) // NaN\n * ```\n * @remarks\n * - If readings contains <2 values, returns NaN (not enough data for SD).\n * - Handles NaN/Infinity values by propagating them in the result.\n */\nexport function glucoseStandardDeviation(readings: number[]): number {\n  if (!Array.isArray(readings) || readings.length < 2) return NaN;\n  const mean = readings.reduce((sum, v) => sum + v, 0) / readings.length;\n  const variance = readings.reduce((sum, v) => sum + Math.pow(v - mean, 2), 0) / (readings.length - 1);\n  return Math.sqrt(variance);\n}\n\n/**\n * Calculates the coefficient of variation (CV) for glucose values.\n * CV = (SD / mean) × 100. Used to assess glycemic variability.\n *\n * @param readings Array of glucose values (numbers)\n * @returns Coefficient of variation as a percentage, or NaN if <2 values or mean is 0\n * @throws {TypeError} If readings is not an array\n * @see {@link https://care.diabetesjournals.org/content/42/8/1593 ADA 2019: Glycemic Targets}\n * @example\n * ```ts\n * glucoseCoefficientOfVariation([100, 120, 140]) // 18.26\n * glucoseCoefficientOfVariation([100]) // NaN\n * glucoseCoefficientOfVariation([]) // NaN\n * ```\n * @remarks\n * - If readings contains <2 values or mean is 0, returns NaN.\n * - Handles NaN/Infinity values by propagating them in the result.\n */\nexport function glucoseCoefficientOfVariation(readings: number[]): number {\n  if (!Array.isArray(readings) || readings.length < 2) return NaN;\n  const mean = readings.reduce((sum, v) => sum + v, 0) / readings.length;\n  if (mean === 0) return NaN;\n  const sd = glucoseStandardDeviation(readings);\n  return (sd / mean) * 100;\n}\n\n/**\n * Calculates specified percentiles from an array of glucose values using the nearest-rank method.\n * Used for glucose variability assessment.\n * @param readings - Array of glucose values (numbers)\n * @param percentiles - Array of percentiles to calculate (e.g., [10, 25, 50, 75, 90])\n * @returns Object mapping percentile to value, or {} if input is empty\n * @throws {TypeError} If readings or percentiles is not an array\n * @see https://en.wikipedia.org/wiki/Percentile\n * @see https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7445493/ (ISPAD 2019)\n * @example\n * glucosePercentiles([100, 120, 140, 160, 180], [10, 50, 90]) // { 10: 100, 50: 140, 90: 180 }\n * glucosePercentiles([], [10, 50, 90]) // {}\n * @remarks\n * - Returns the value at the nearest-rank for each percentile.\n * - If readings is empty, returns an empty object.\n * - Percentiles outside [0, 100] are ignored.\n */\nexport function glucosePercentiles(readings: number[], percentiles: number[]): Record<number, number> {\n  if (!Array.isArray(readings) || readings.length === 0) return {};\n  const sorted = [...readings].sort((a, b) => a - b);\n  const result: Record<number, number> = {};\n  for (const p of percentiles) {\n    if (typeof p !== 'number' || p < 0 || p > 100) continue;\n    // Nearest-rank method\n    const rank = Math.ceil((p / 100) * sorted.length);\n    result[p] = sorted[Math.max(0, rank - 1)];\n  }\n  return result;\n}\n\n/**\n * Calculates Mean Amplitude of Glycemic Excursions (MAGE) for glucose values.\n * Implements Service FJ et al. (1970) methodology.\n * @param readings - Array of glucose values (mg/dL or mmol/L)\n * @param options - Optional configuration for MAGE calculation\n * @returns MAGE value, or NaN if insufficient data or no valid excursions\n * @see https://pubmed.ncbi.nlm.nih.gov/5469118/ (Service FJ, et al. 1970)\n * @see https://journals.sagepub.com/doi/10.1177/19322968211061165 (Fernandes NJ, et al. 2022)\n * @see https://care.diabetesjournals.org/content/42/8/1593 (ADA 2019)\n * @example\n * glucoseMAGE([100, 120, 80, 160, 90, 140, 70, 180])\n * glucoseMAGE(readings, { direction: 'ascending', shortWindow: 5, longWindow: 32 })\n * @remarks\n * - Minimum 24 data points recommended (1 day of hourly readings)\n * - Best suited for continuous glucose monitoring (CGM) data\n * - Not recommended for sparse or irregular measurements\n * - Uses dual moving averages, three-point excursion definition, and prevents double-counting.\n */\nexport function glucoseMAGE(readings: number[], options?: MAGEOptions): number {\n  return mageImpl(readings, options);\n}\n","// @file src/tir-enhanced.ts\n\nimport type {\n  GlucoseReading,\n  EnhancedTIRResult,\n  EnhancedTIROptions,\n  RangeMetrics,\n  TargetAssessment,\n  TIRSummary,\n  TIRPopulation,\n  TIRAssessment,\n  PregnancyTIRResult,\n  PregnancyTIROptions,\n} from './types'\n\nimport {\n  TIR_VERY_LOW_THRESHOLD_MGDL,\n  TIR_LOW_THRESHOLD_MGDL,\n  TIR_HIGH_THRESHOLD_MGDL,\n  TIR_VERY_HIGH_THRESHOLD_MGDL,\n  TIR_GOAL_STANDARD,\n  TBR_LEVEL1_GOAL,\n  TBR_LEVEL2_GOAL,\n  TAR_LEVEL1_GOAL,\n  TAR_LEVEL2_GOAL,\n  TIR_GOAL_OLDER_ADULTS,\n  TBR_LEVEL1_GOAL_OLDER_ADULTS,\n  TBR_LEVEL2_GOAL_OLDER_ADULTS,\n  PREGNANCY_TARGET_LOW_MGDL,\n  PREGNANCY_TARGET_HIGH_MGDL,\n  PREGNANCY_TARGET_LOW_MMOLL,\n  PREGNANCY_TARGET_HIGH_MMOLL,\n  MGDL_MMOLL_CONVERSION,\n  MG_DL,\n} from './constants'\n\n/**\n * Offset for exclusive boundary classification.\n * Used to ensure boundary values are correctly excluded from ranges.\n */\nconst BOUNDARY_EPSILON = 0.01\n\n/**\n * Glucose reading with normalized value in target unit.\n *\n * @internal\n */\ntype NormalizedReading = GlucoseReading & { normalizedValue: number }\n\n/**\n * Calculates Enhanced Time-in-Range metrics per International Consensus 2019.\n *\n * Provides detailed breakdown of glucose readings across five clinical ranges:\n * - Very Low (<54 mg/dL / 3.0 mmol/L): Level 2 Hypoglycemia\n * - Low (54-69 mg/dL / 3.0-3.8 mmol/L): Level 1 Hypoglycemia\n * - In Range (70-180 mg/dL / 3.9-10.0 mmol/L): Target Range\n * - High (181-250 mg/dL / 10.1-13.9 mmol/L): Level 1 Hyperglycemia\n * - Very High (>250 mg/dL / >13.9 mmol/L): Level 2 Hyperglycemia\n *\n * @param readings - Array of glucose readings with timestamp, value, and unit\n * @param options - Optional configuration for thresholds and population type\n * @returns Enhanced TIR result with detailed metrics and target assessment\n *\n * @example\n * ```typescript\n * const readings: GlucoseReading[] = [\n *   { value: 120, unit: 'mg/dL', timestamp: '2024-01-01T08:00:00Z' },\n *   { value: 95, unit: 'mg/dL', timestamp: '2024-01-01T08:05:00Z' },\n *   // ... more readings\n * ];\n * const result = calculateEnhancedTIR(readings);\n * console.log(`TIR: ${result.inRange.percentage}%`);\n * console.log(`Meets targets: ${result.meetsTargets.tirMeetsGoal}`);\n * ```\n *\n * @throws {Error} If readings array is empty\n * @throws {Error} If readings contain invalid glucose values\n *\n * @see {@link https://diabetesjournals.org/care/article/42/8/1593 | International Consensus on Time in Range (2019)}\n *\n * @remarks\n * - Requires minimum 24 hours of data for meaningful results\n * - Consensus targets: TIR ≥70%, TBR Level 1 <4%, TBR Level 2 <1%, TAR Level 1 <25%, TAR Level 2 <5%\n * - Verify data quality and sensor accuracy before drawing conclusions\n * - Readings are assumed to be evenly distributed for duration calculations\n *\n * @category Enhanced TIR\n * @public\n */\nexport function calculateEnhancedTIR(\n  readings: GlucoseReading[],\n  options?: EnhancedTIROptions\n): EnhancedTIRResult {\n  // Validate input\n  if (readings.length === 0) {\n    throw new Error('Cannot calculate Enhanced TIR: readings array is empty')\n  }\n\n  const population: TIRPopulation = options?.population ?? 'standard'\n\n  const veryLowThreshold =\n    options?.veryLowThreshold ?? TIR_VERY_LOW_THRESHOLD_MGDL\n  const lowThreshold = options?.lowThreshold ?? TIR_LOW_THRESHOLD_MGDL\n  const highThreshold = options?.highThreshold ?? TIR_HIGH_THRESHOLD_MGDL\n  const veryHighThreshold =\n    options?.veryHighThreshold ?? TIR_VERY_HIGH_THRESHOLD_MGDL\n\n  // Convert all readings to mg/dL for consistent thresholding\n  const normalizedReadings: NormalizedReading[] = readings.map((reading) => {\n    const value =\n      reading.unit === MG_DL\n        ? reading.value\n        : reading.value * MGDL_MMOLL_CONVERSION\n    return { ...reading, normalizedValue: value }\n  })\n\n  // Validate glucose values\n  validateNormalizedReadings(normalizedReadings, 600)\n\n  // Calculate metrics for each range\n  // Note: Upper bounds are INCLUSIVE for correct boundary classification\n  const veryLow = calculateRangeMetrics(\n    normalizedReadings,\n    0,\n    veryLowThreshold\n  )\n  const low = calculateRangeMetrics(\n    normalizedReadings,\n    veryLowThreshold,\n    lowThreshold\n  )\n  const inRange = calculateRangeMetrics(\n    normalizedReadings,\n    lowThreshold,\n    highThreshold,\n    true // inclusive upper bound\n  )\n  const high = calculateRangeMetrics(\n    normalizedReadings,\n    highThreshold + BOUNDARY_EPSILON, // exclusive lower bound\n    veryHighThreshold,\n    true // inclusive upper bound\n  )\n  const veryHigh = calculateRangeMetrics(\n    normalizedReadings,\n    veryHighThreshold + BOUNDARY_EPSILON, // exclusive lower bound\n    Infinity,\n    true\n  )\n\n  // Calculate summary\n  const summary = calculateSummary(readings)\n\n  // Assess targets\n  const meetsTargets = assessTargets(\n    { veryLow, low, inRange, high, veryHigh },\n    population\n  )\n\n  return {\n    veryLow,\n    low,\n    inRange,\n    high,\n    veryHigh,\n    meetsTargets,\n    summary,\n  }\n}\n\n/**\n * Calculates Pregnancy-specific Time-in-Range metrics per ADA 2024 guidelines.\n *\n * Uses tighter target range for pregnancy: 63-140 mg/dL (3.5-7.8 mmol/L).\n * Consensus targets: TIR >70%, TBR <4% (Level 1) and <1% (Level 2), TAR <25%.\n *\n * @param readings - Array of glucose readings with timestamp, value, and unit\n * @param options - Optional configuration for glucose unit\n * @returns Pregnancy TIR result with target assessment and recommendations\n *\n * @example\n * ```typescript\n * const readings: GlucoseReading[] = [\n *   { value: 100, unit: 'mg/dL', timestamp: '2024-01-01T08:00:00Z' },\n *   // ... more readings\n * ];\n * const result = calculatePregnancyTIR(readings);\n * console.log(`TIR: ${result.inRange.percentage}%`);\n * console.log(`Meets pregnancy targets: ${result.meetsPregnancyTargets}`);\n * ```\n *\n * @throws {Error} If readings array is empty\n * @throws {Error} If readings contain invalid glucose values\n *\n * @see {@link https://diabetesjournals.org/care/article/47/Supplement_1/S282 | ADA Standards of Care (2024)}\n *\n * @remarks\n * - Target range: 63-140 mg/dL (3.5-7.8 mmol/L)\n * - Tighter targets per published guidelines\n * - This is informational only and does not constitute medical advice\n * - Applies to Type 1, Type 2, and gestational diabetes during pregnancy\n *\n * @category Pregnancy TIR\n * @public\n */\nexport function calculatePregnancyTIR(\n  readings: GlucoseReading[],\n  options?: PregnancyTIROptions\n): PregnancyTIRResult {\n  // Validate input\n  if (readings.length === 0) {\n    throw new Error('Cannot calculate Pregnancy TIR: readings array is empty')\n  }\n\n  // Determine thresholds based on either supplied unit preference or predominant unit\n  const preferredUnit = options?.unit\n  const mgdlCount = readings.filter((r) => r.unit === MG_DL).length\n  const useMgdlThresholds = preferredUnit\n    ? preferredUnit === MG_DL\n    : mgdlCount >= readings.length / 2\n\n  // Use unit-specific thresholds to avoid floating point conversion issues\n  const lowThreshold = useMgdlThresholds\n    ? PREGNANCY_TARGET_LOW_MGDL\n    : PREGNANCY_TARGET_LOW_MMOLL\n  const highThreshold = useMgdlThresholds\n    ? PREGNANCY_TARGET_HIGH_MGDL\n    : PREGNANCY_TARGET_HIGH_MMOLL\n\n  // Convert all readings to the appropriate unit for thresholding\n  const normalizedReadings: NormalizedReading[] = readings.map((reading) => {\n    let normalizedValue: number\n    if (useMgdlThresholds) {\n      normalizedValue =\n        reading.unit === MG_DL\n          ? reading.value\n          : reading.value * MGDL_MMOLL_CONVERSION\n    } else {\n      normalizedValue =\n        reading.unit === MG_DL\n          ? reading.value / MGDL_MMOLL_CONVERSION\n          : reading.value\n    }\n    return { ...reading, normalizedValue }\n  })\n\n  // Validate glucose values\n  const maxValue = useMgdlThresholds ? 600 : 33.3\n  validateNormalizedReadings(normalizedReadings, maxValue)\n\n  // Calculate metrics for pregnancy ranges using unit-specific thresholds\n  const belowRange = calculateRangeMetrics(\n    normalizedReadings,\n    0,\n    lowThreshold\n  )\n  const inRange = calculateRangeMetrics(\n    normalizedReadings,\n    lowThreshold,\n    highThreshold,\n    true // inclusive upper bound\n  )\n  const aboveRange = calculateRangeMetrics(\n    normalizedReadings,\n    highThreshold + BOUNDARY_EPSILON, // exclusive lower bound\n    Infinity,\n    true\n  )\n\n  // Calculate summary\n  const summary = calculateSummary(readings)\n\n  // Assess pregnancy targets\n  const meetsPregnancyTargets =\n    inRange.percentage >= TIR_GOAL_STANDARD && // TIR >70%\n    belowRange.percentage < TBR_LEVEL1_GOAL && // TBR <4%\n    aboveRange.percentage < TAR_LEVEL1_GOAL // TAR <25%\n\n  // Generate recommendations\n  const recommendations = generatePregnancyRecommendations({\n    belowRange,\n    inRange,\n    aboveRange,\n  })\n\n  return {\n    belowRange,\n    inRange,\n    aboveRange,\n    meetsPregnancyTargets,\n    recommendations,\n    summary,\n  }\n}\n\n/**\n * Validates normalized glucose values are within acceptable range.\n *\n * @param readings - Normalized readings to validate\n * @param maxValue - Maximum acceptable value (600 mg/dL or 33.3 mmol/L)\n * @throws {Error} If any reading is invalid\n *\n * @internal\n */\nfunction validateNormalizedReadings(\n  readings: NormalizedReading[],\n  maxValue: number\n): void {\n  for (const reading of readings) {\n    if (\n      reading.normalizedValue < 0 ||\n      reading.normalizedValue > maxValue ||\n      !Number.isFinite(reading.normalizedValue)\n    ) {\n      const unitSuffix = maxValue === 600 ? ' mg/dL' : ''\n      throw new Error(\n        `Invalid glucose value: ${reading.value} ${reading.unit} (normalized: ${reading.normalizedValue}${unitSuffix})`\n      )\n    }\n  }\n}\n\n/**\n * Estimates average interval between readings in minutes.\n *\n * @param readings - Array of glucose readings\n * @returns Average interval in minutes (defaults to 5 for CGM standard)\n *\n * @internal\n */\nfunction estimateReadingInterval(readings: GlucoseReading[]): number {\n  if (readings.length < 2) {\n    return 5 // Default: 5-minute intervals (CGM standard)\n  }\n\n  const timestamps = readings\n    .map((r) => new Date(r.timestamp).getTime())\n    .filter(Number.isFinite)\n    .sort((a, b) => a - b)\n\n  if (timestamps.length < 2) {\n    return 5\n  }\n\n  const intervals: number[] = []\n\n  for (let i = 1; i < Math.min(10, timestamps.length); i++) {\n    const delta = timestamps[i] - timestamps[i - 1]\n    if (delta > 0) {\n      intervals.push(delta)\n    }\n  }\n\n  if (intervals.length === 0) {\n    return 5\n  }\n\n  const avgIntervalMs = intervals.reduce((a, b) => a + b, 0) / intervals.length\n  return avgIntervalMs / (1000 * 60)\n}\n\n/**\n * Calculates detailed metrics for a specific glucose range.\n *\n * @param readings - Normalized glucose readings with value in mg/dL\n * @param lowerBound - Lower bound of range (mg/dL, inclusive)\n * @param upperBound - Upper bound of range (mg/dL)\n * @param inclusiveUpper - Whether upper bound is inclusive (default: false)\n * @returns Range metrics with percentage, duration, count, and average\n *\n * @internal\n */\nfunction calculateRangeMetrics(\n  readings: NormalizedReading[],\n  lowerBound: number,\n  upperBound: number,\n  inclusiveUpper = false\n): RangeMetrics {\n  // Filter readings in this range\n  const inRange = readings.filter((r) => {\n    const aboveLower = r.normalizedValue >= lowerBound\n    const belowUpper = inclusiveUpper\n      ? r.normalizedValue <= upperBound\n      : r.normalizedValue < upperBound\n    return aboveLower && belowUpper\n  })\n\n  const readingCount = inRange.length\n  const percentage = (readingCount / readings.length) * 100\n\n  // Calculate duration (assumes evenly distributed readings)\n  const avgIntervalMinutes = estimateReadingInterval(readings)\n  const duration = readingCount * avgIntervalMinutes\n\n  // Calculate average value in this range\n  const averageValue =\n    readingCount > 0\n      ? inRange.reduce((sum, r) => sum + r.normalizedValue, 0) / readingCount\n      : null\n\n  return {\n    percentage: Math.round(percentage * 10) / 10, // Round to 1 decimal\n    duration: Math.round(duration),\n    readingCount,\n    averageValue: averageValue ? Math.round(averageValue) : null,\n  }\n}\n\n/**\n * Calculates summary statistics for TIR result.\n *\n * @param readings - Array of glucose readings\n * @returns Summary with total readings, duration, and data quality\n *\n * @internal\n */\nfunction calculateSummary(readings: GlucoseReading[]): TIRSummary {\n  const totalReadings = readings.length\n\n  // Calculate total duration\n  const avgIntervalMinutes = estimateReadingInterval(readings)\n  const totalDuration = totalReadings * avgIntervalMinutes\n\n  // Assess data quality based on number of days\n  const daysOfData = totalDuration / (60 * 24)\n  let dataQuality: 'excellent' | 'good' | 'fair' | 'poor'\n  if (daysOfData >= 14) {\n    dataQuality = 'excellent'\n  } else if (daysOfData >= 7) {\n    dataQuality = 'good'\n  } else if (daysOfData >= 3) {\n    dataQuality = 'fair'\n  } else {\n    dataQuality = 'poor'\n  }\n\n  return {\n    totalReadings,\n    totalDuration: Math.round(totalDuration),\n    dataQuality,\n  }\n}\n\n/**\n * Selects population-specific consensus goals.\n *\n * @param population - Population type\n * @returns Object with TIR and TBR goals for the population\n *\n * @internal\n */\nfunction getPopulationGoals(population: TIRPopulation) {\n  const isStandard = population === 'standard'\n  return {\n    tirGoal: isStandard ? TIR_GOAL_STANDARD : TIR_GOAL_OLDER_ADULTS,\n    tbrLevel1Goal: isStandard ? TBR_LEVEL1_GOAL : TBR_LEVEL1_GOAL_OLDER_ADULTS,\n    tbrLevel2Goal: isStandard ? TBR_LEVEL2_GOAL : TBR_LEVEL2_GOAL_OLDER_ADULTS,\n  }\n}\n\n/**\n * Assesses whether TIR metrics meet consensus targets.\n *\n * @param ranges - Calculated range metrics\n * @param population - Population type for target selection\n * @returns Target assessment with goals and recommendations\n *\n * @internal\n */\nfunction assessTargets(\n  ranges: {\n    veryLow: RangeMetrics\n    low: RangeMetrics\n    inRange: RangeMetrics\n    high: RangeMetrics\n    veryHigh: RangeMetrics\n  },\n  population: TIRPopulation\n): TargetAssessment {\n  // Select goals based on population\n  const { tirGoal, tbrLevel1Goal, tbrLevel2Goal } = getPopulationGoals(population)\n\n  // Assess each metric\n  const tirMeetsGoal = ranges.inRange.percentage >= tirGoal\n  const tbrLevel1Safe = ranges.low.percentage < tbrLevel1Goal\n  const tbrLevel2Safe = ranges.veryLow.percentage < tbrLevel2Goal\n  const tarLevel1Acceptable = ranges.high.percentage < TAR_LEVEL1_GOAL\n  const tarLevel2Acceptable = ranges.veryHigh.percentage < TAR_LEVEL2_GOAL\n\n  // Determine overall assessment\n  let overallAssessment: TIRAssessment\n  const criticalIssues = !tbrLevel2Safe || !tarLevel2Acceptable\n\n  // TAR Level 1 slightly over (26-30%) is a minor issue, not major\n  const minorTARIssue = !tarLevel1Acceptable && ranges.high.percentage <= 30\n\n  // Major issues: hypoglycemia or poor TIR (but not minor TAR overage)\n  const majorIssues = !tbrLevel1Safe || !tirMeetsGoal\n\n  if (criticalIssues) {\n    overallAssessment = 'concerning'\n  } else if (majorIssues) {\n    overallAssessment = 'needs improvement'\n  } else if (\n    ranges.inRange.percentage >= tirGoal + 10 &&\n    ranges.veryLow.percentage < 0.5\n  ) {\n    overallAssessment = 'excellent'\n  } else if (minorTARIssue && tirMeetsGoal && tbrLevel1Safe) {\n    // Minor TAR overage (25-30%) but TIR good and no hypos - still \"good\"\n    overallAssessment = 'good'\n  } else {\n    overallAssessment = 'good'\n  }\n\n  // Generate recommendations\n  const recommendations = generateRecommendations({\n    ranges,\n    tirMeetsGoal,\n    tbrLevel1Safe,\n    tbrLevel2Safe,\n    tarLevel1Acceptable,\n    tarLevel2Acceptable,\n    population,\n  })\n\n  return {\n    tirMeetsGoal,\n    tbrLevel1Safe,\n    tbrLevel2Safe,\n    tarLevel1Acceptable,\n    tarLevel2Acceptable,\n    overallAssessment,\n    recommendations,\n  }\n}\n\n/**\n * Generates observations based on TIR metrics.\n *\n * @param params - Assessment parameters\n * @returns Array of informational observations (not medical advice)\n *\n * @internal\n */\nfunction generateRecommendations(params: {\n  ranges: {\n    veryLow: RangeMetrics\n    low: RangeMetrics\n    inRange: RangeMetrics\n    high: RangeMetrics\n    veryHigh: RangeMetrics\n  }\n  tirMeetsGoal: boolean\n  tbrLevel1Safe: boolean\n  tbrLevel2Safe: boolean\n  tarLevel1Acceptable: boolean\n  tarLevel2Acceptable: boolean\n  population: TIRPopulation\n}): readonly string[] {\n  const recommendations: string[] = []\n\n  if (!params.tbrLevel2Safe) {\n    recommendations.push(\n      `Level 2 hypoglycemia (${params.ranges.veryLow.percentage.toFixed(\n        1\n      )}%) exceeds the consensus target.`\n    )\n  }\n\n  if (!params.tarLevel2Acceptable) {\n    recommendations.push(\n      `Level 2 hyperglycemia (${params.ranges.veryHigh.percentage.toFixed(\n        1\n      )}%) exceeds the consensus target.`\n    )\n  }\n\n  if (!params.tbrLevel1Safe && params.tbrLevel2Safe) {\n    recommendations.push(\n      `Level 1 hypoglycemia (${params.ranges.low.percentage.toFixed(\n        1\n      )}%) is elevated.`\n    )\n  }\n\n  if (!params.tarLevel1Acceptable && params.tarLevel2Acceptable) {\n    recommendations.push(\n      `Level 1 hyperglycemia (${params.ranges.high.percentage.toFixed(\n        1\n      )}%) is elevated.`\n    )\n  }\n\n  if (!params.tirMeetsGoal && params.tbrLevel1Safe && params.tbrLevel2Safe) {\n    recommendations.push(\n      `Time-in-range (${params.ranges.inRange.percentage.toFixed(\n        1\n      )}%) is below the consensus target.`\n    )\n  }\n\n  if (\n    params.tirMeetsGoal &&\n    params.tbrLevel1Safe &&\n    params.tbrLevel2Safe &&\n    params.tarLevel1Acceptable &&\n    params.tarLevel2Acceptable\n  ) {\n    recommendations.push(\n      'All metrics meet consensus targets.'\n    )\n    /* c8 ignore start -- defensive fallback when no recommendations were generated */\n  } else if (\n    params.tbrLevel2Safe &&\n    params.tarLevel2Acceptable &&\n    recommendations.length === 0\n  ) {\n    recommendations.push(\n      'Some metrics are outside consensus targets.'\n    )\n  }\n  /* c8 ignore stop */\n\n  if (\n    params.population === 'older-adults' ||\n    params.population === 'high-risk'\n  ) {\n    recommendations.push(\n      'Older/high-risk population targets applied (lower TIR goal, stricter hypoglycemia limits).'\n    )\n  }\n\n  return recommendations\n}\n\n/**\n * Generates pregnancy-specific observations.\n *\n * @param ranges - Pregnancy range metrics\n * @returns Array of pregnancy-specific observations (not medical advice)\n *\n * @internal\n */\nfunction generatePregnancyRecommendations(ranges: {\n  belowRange: RangeMetrics\n  inRange: RangeMetrics\n  aboveRange: RangeMetrics\n}): readonly string[] {\n  const recommendations: string[] = []\n\n  if (ranges.belowRange.percentage >= TBR_LEVEL1_GOAL) {\n    recommendations.push(\n      `Time below range (${ranges.belowRange.percentage.toFixed(\n        1\n      )}%) exceeds the pregnancy consensus target.`\n    )\n  }\n\n  if (ranges.aboveRange.percentage >= TAR_LEVEL1_GOAL) {\n    recommendations.push(\n      `Time above range (${ranges.aboveRange.percentage.toFixed(\n        1\n      )}%) exceeds the pregnancy consensus target.`\n    )\n  }\n\n  if (ranges.inRange.percentage < TIR_GOAL_STANDARD) {\n    recommendations.push(\n      `Time-in-range (${ranges.inRange.percentage.toFixed(\n        1\n      )}%) is below the pregnancy target of 70%.`\n    )\n  }\n\n  if (\n    ranges.inRange.percentage >= TIR_GOAL_STANDARD &&\n    ranges.belowRange.percentage < TBR_LEVEL1_GOAL &&\n    ranges.aboveRange.percentage < TAR_LEVEL1_GOAL\n  ) {\n    recommendations.push(\n      'All metrics meet pregnancy consensus targets.'\n    )\n    /* c8 ignore start -- defensive fallback when no recommendations were generated */\n  } else if (recommendations.length === 0) {\n    recommendations.push(\n      'Some pregnancy metrics are outside consensus targets.'\n    )\n  }\n  /* c8 ignore stop */\n\n  recommendations.push(\n    'Pregnancy target range: 63-140 mg/dL (3.5-7.8 mmol/L).'\n  )\n\n  return recommendations\n}\n","/**\n * @file src/metrics/bgi.ts\n *\n * Low Blood Glucose Index (LBGI) and High Blood Glucose Index (HBGI).\n *\n * These risk indices quantify the risk of hypo- and hyperglycemia from a\n * series of glucose readings. They are asymmetric transforms that weight\n * low values more heavily (LBGI) or high values more heavily (HBGI).\n *\n * Formula (Kovatchev et al. 2006):\n *   f(G) = 1.509 * (ln(G)^1.084 - 5.381)\n *   rl(G) = 10 * f(G)^2   if f(G) < 0, else 0   (low risk)\n *   rh(G) = 10 * f(G)^2   if f(G) > 0, else 0   (high risk)\n *   LBGI = mean(rl)\n *   HBGI = mean(rh)\n *\n * Input values must be in mg/dL. Values <= 0 are skipped.\n *\n * @see https://doi.org/10.2337/dc06-1085  Kovatchev et al. (2006)\n */\n\n/**\n * Computes the blood glucose symmetry function f(G).\n * Used by LBGI, HBGI, and ADRR calculations.\n * @internal\n */\nexport function fbg(glucoseMgDl: number): number {\n  return 1.509 * (Math.pow(Math.log(glucoseMgDl), 1.084) - 5.381)\n}\n\n/**\n * Calculates the Low Blood Glucose Index (LBGI).\n *\n * Quantifies the risk and extent of hypoglycemia from a glucose trace.\n * Higher values indicate greater hypoglycemia risk.\n *\n * Risk categories (Kovatchev 2006):\n *  - < 1.1: low risk\n *  - 1.1 - 2.5: moderate risk\n *  - > 2.5: high risk\n *\n * @param readings - Array of glucose values in mg/dL\n * @returns LBGI value, or NaN if no valid readings\n * @see https://doi.org/10.2337/dc06-1085\n */\nexport function glucoseLBGI(readings: number[]): number {\n  const valid = readings.filter((v) => Number.isFinite(v) && v > 0)\n  if (valid.length === 0) return NaN\n\n  let sum = 0\n  for (const g of valid) {\n    const f = fbg(g)\n    if (f < 0) {\n      sum += 10 * f * f\n    }\n  }\n  return sum / valid.length\n}\n\n/**\n * Calculates the High Blood Glucose Index (HBGI).\n *\n * Quantifies the risk and extent of hyperglycemia from a glucose trace.\n * Higher values indicate greater hyperglycemia risk.\n *\n * Risk categories (Kovatchev 2006):\n *  - < 4.5: low risk\n *  - 4.5 - 9.0: moderate risk\n *  - > 9.0: high risk\n *\n * @param readings - Array of glucose values in mg/dL\n * @returns HBGI value, or NaN if no valid readings\n * @see https://doi.org/10.2337/dc06-1085\n */\nexport function glucoseHBGI(readings: number[]): number {\n  const valid = readings.filter((v) => Number.isFinite(v) && v > 0)\n  if (valid.length === 0) return NaN\n\n  let sum = 0\n  for (const g of valid) {\n    const f = fbg(g)\n    if (f > 0) {\n      sum += 10 * f * f\n    }\n  }\n  return sum / valid.length\n}\n","/**\n * @file src/metrics/adrr.ts\n *\n * Average Daily Risk Range (ADRR).\n *\n * ADRR combines the maximum daily hypo and hyper risk values to produce\n * a single composite risk score. It captures the amplitude of extreme\n * glucose excursions on each day and averages them.\n *\n * Formula (Kovatchev et al. 2006):\n *   For each day d: DR(d) = max(rl over day d) + max(rh over day d)\n *   ADRR = mean(DR) across all days\n *\n * Risk categories:\n *  - < 20: low risk\n *  - 20 - 40: moderate risk\n *  - > 40: high risk\n *\n * @see https://doi.org/10.2337/dc06-1085  Kovatchev et al. (2006)\n */\n\nimport type { GlucoseReading } from '../types'\nimport { MGDL_MMOLL_CONVERSION, MG_DL } from '../constants'\nimport { fbg } from './bgi'\n\n/**\n * Calculates the Average Daily Risk Range (ADRR).\n *\n * @param readings - Array of GlucoseReading objects with timestamps\n * @returns ADRR value, or NaN if no valid readings\n * @see https://doi.org/10.2337/dc06-1085\n */\nexport function calculateADRR(readings: GlucoseReading[]): number {\n  if (readings.length === 0) return NaN\n\n  const dayBuckets = new Map<string, number[]>()\n\n  for (const r of readings) {\n    const mgdl = r.unit === MG_DL ? r.value : r.value * MGDL_MMOLL_CONVERSION\n    if (!Number.isFinite(mgdl) || mgdl <= 0) continue\n\n    const day = r.timestamp.slice(0, 10)\n    if (!dayBuckets.has(day)) dayBuckets.set(day, [])\n    dayBuckets.get(day)!.push(mgdl)\n  }\n\n  if (dayBuckets.size === 0) return NaN\n\n  let totalDR = 0\n  for (const values of dayBuckets.values()) {\n    let maxRl = 0\n    let maxRh = 0\n    for (const g of values) {\n      const f = fbg(g)\n      const risk = 10 * f * f\n      if (f < 0 && risk > maxRl) maxRl = risk\n      if (f > 0 && risk > maxRh) maxRh = risk\n    }\n    totalDR += maxRl + maxRh\n  }\n\n  return Math.round((totalDR / dayBuckets.size) * 100) / 100\n}\n","/**\n * @file src/metrics/grade.ts\n *\n * Glycemic Risk Assessment Diabetes Equation (GRADE).\n *\n * GRADE produces a single risk score from a glucose value, and its\n * partitioned variants break down what percentage of the total GRADE\n * score comes from euglycemic, hyperglycemic, and hypoglycemic readings.\n *\n * Formula (Hill et al. 2007):\n *   GRADE(G) = 425 * (log10(log10(G_mmol)) + 0.16)^2\n *   where G_mmol = G_mgdl / 18.0182\n *\n * Input values must be in mg/dL. Values <= 0 are skipped.\n *\n * @see https://doi.org/10.1111/j.1464-5491.2007.02119.x  Hill et al. (2007)\n */\n\nconst MGDL_TO_MMOL = 18.0182\n\n/**\n * Computes the GRADE score for a single glucose value (mg/dL).\n * @internal\n */\nfunction gradeScore(mgdl: number): number {\n  const mmol = mgdl / MGDL_TO_MMOL\n  if (mmol <= 1) return 0\n  const inner = Math.log10(mmol)\n  /* c8 ignore next -- inner > 0 is guaranteed when mmol > 1 (guarded above) */\n  if (inner <= 0) return 0\n  const logLog = Math.log10(inner) + 0.16\n  return 425 * logLog * logLog\n}\n\n/** Result of the partitioned GRADE calculation. */\nexport interface GRADEResult {\n  /** Mean GRADE score across all valid readings */\n  readonly grade: number\n  /** % of total GRADE contributed by euglycemic readings (70-140 mg/dL) */\n  readonly gradeEuglycemia: number\n  /** % of total GRADE contributed by hyperglycemic readings (>140 mg/dL) */\n  readonly gradeHyperglycemia: number\n  /** % of total GRADE contributed by hypoglycemic readings (<70 mg/dL) */\n  readonly gradeHypoglycemia: number\n}\n\n/**\n * Calculates the GRADE score and its partitioned components.\n *\n * @param readings - Array of glucose values in mg/dL\n * @param hypoThreshold - Upper bound for hypoglycemia (default: 70 mg/dL)\n * @param hyperThreshold - Lower bound for hyperglycemia (default: 140 mg/dL)\n * @returns GRADE result with overall score and percentage breakdown, or NaN fields if no valid data\n * @see https://doi.org/10.1111/j.1464-5491.2007.02119.x\n */\nexport function calculateGRADE(\n  readings: number[],\n  hypoThreshold = 70,\n  hyperThreshold = 140\n): GRADEResult {\n  const valid = readings.filter((v) => Number.isFinite(v) && v > 0)\n  if (valid.length === 0) {\n    return { grade: NaN, gradeEuglycemia: NaN, gradeHyperglycemia: NaN, gradeHypoglycemia: NaN }\n  }\n\n  let totalGrade = 0\n  let hypoGrade = 0\n  let euGrade = 0\n  let hyperGrade = 0\n\n  for (const g of valid) {\n    const score = gradeScore(g)\n    totalGrade += score\n    if (g < hypoThreshold) {\n      hypoGrade += score\n    } else if (g > hyperThreshold) {\n      hyperGrade += score\n    } else {\n      euGrade += score\n    }\n  }\n\n  const meanGrade = totalGrade / valid.length\n\n  if (totalGrade === 0) {\n    return { grade: 0, gradeEuglycemia: 0, gradeHyperglycemia: 0, gradeHypoglycemia: 0 }\n  }\n\n  return {\n    grade: Math.round(meanGrade * 100) / 100,\n    gradeEuglycemia: Math.round((euGrade / totalGrade) * 10000) / 100,\n    gradeHyperglycemia: Math.round((hyperGrade / totalGrade) * 10000) / 100,\n    gradeHypoglycemia: Math.round((hypoGrade / totalGrade) * 10000) / 100,\n  }\n}\n","/**\n * @file src/metrics/gri.ts\n *\n * Glycemia Risk Index (GRI).\n *\n * The GRI is a composite CGM metric that captures both hypo- and\n * hyperglycemia risk in a single 0-100 score. It is derived from four\n * TIR zone percentages using the formula from Klonoff et al. (2023).\n *\n * Formula:\n *   GRI = (3.0 * VLow) + (2.4 * Low) + (1.6 * VHigh) + (0.8 * High)\n *   clamped to [0, 100]\n *\n * Where:\n *   VLow  = % time < 54 mg/dL  (TBR Level 2)\n *   Low   = % time 54-69 mg/dL (TBR Level 1)\n *   VHigh = % time > 250 mg/dL (TAR Level 2)\n *   High  = % time 181-250 mg/dL (TAR Level 1)\n *\n * Risk zones:\n *   A (lowest risk): GRI <= 20\n *   B: 20 < GRI <= 40\n *   C: 40 < GRI <= 60\n *   D: 60 < GRI <= 80\n *   E (highest risk): GRI > 80\n *\n * @see https://doi.org/10.1177/19322968221085273  Klonoff et al. (2023)\n */\n\n/** Input percentages for GRI calculation. */\nexport interface GRIInput {\n  /** % time < 54 mg/dL */\n  readonly veryLowPercent: number\n  /** % time 54-69 mg/dL */\n  readonly lowPercent: number\n  /** % time 181-250 mg/dL */\n  readonly highPercent: number\n  /** % time > 250 mg/dL */\n  readonly veryHighPercent: number\n}\n\n/** GRI result with numeric score and risk zone. */\nexport interface GRIResult {\n  readonly score: number\n  readonly zone: 'A' | 'B' | 'C' | 'D' | 'E'\n  readonly hypoComponent: number\n  readonly hyperComponent: number\n}\n\n/**\n * Calculates the Glycemia Risk Index (GRI) from TIR zone percentages.\n *\n * @param input - Percentages for each out-of-range zone\n * @returns GRI result with composite score and risk zone\n * @see https://doi.org/10.1177/19322968221085273\n */\nexport function calculateGRI(input: GRIInput): GRIResult {\n  const values = [\n    input.veryLowPercent,\n    input.lowPercent,\n    input.highPercent,\n    input.veryHighPercent,\n  ]\n  if (values.some((v) => !Number.isFinite(v) || v < 0 || v > 100)) {\n    throw new RangeError(\n      'GRI inputs must be finite percentages between 0 and 100.'\n    )\n  }\n\n  const hypoComponent = 3.0 * input.veryLowPercent + 2.4 * input.lowPercent\n  const hyperComponent = 1.6 * input.veryHighPercent + 0.8 * input.highPercent\n  const raw = hypoComponent + hyperComponent\n  const clampedRaw = Math.min(100, Math.max(0, raw))\n  const score = Math.round(clampedRaw * 10) / 10\n\n  let zone: GRIResult['zone']\n  if (clampedRaw <= 20) zone = 'A'\n  else if (clampedRaw <= 40) zone = 'B'\n  else if (clampedRaw <= 60) zone = 'C'\n  else if (clampedRaw <= 80) zone = 'D'\n  else zone = 'E'\n\n  return {\n    score,\n    zone,\n    hypoComponent: Math.round(hypoComponent * 10) / 10,\n    hyperComponent: Math.round(hyperComponent * 10) / 10,\n  }\n}\n","/**\n * @file src/metrics/jindex.ts\n *\n * J-Index: a composite measure of both mean glucose and variability.\n *\n * Formula (Wojcicki 1995):\n *   J = 0.001 * (mean + SD)^2\n *\n * Input values must be in mg/dL.\n *\n * @see https://doi.org/10.1055/s-2007-979906  Wojcicki (1995)\n */\n\n/**\n * Calculates the J-Index for a glucose trace.\n *\n * The J-Index captures both central tendency and variability in a\n * single score. Higher values indicate worse glycemic control.\n *\n * @param readings - Array of glucose values in mg/dL\n * @returns J-Index value, or NaN if fewer than 2 valid readings\n * @see https://doi.org/10.1055/s-2007-979906\n */\nexport function calculateJIndex(readings: number[]): number {\n  const valid = readings.filter((v) => Number.isFinite(v) && v > 0)\n  if (valid.length < 2) return NaN\n\n  const mean = valid.reduce((s, v) => s + v, 0) / valid.length\n  const variance =\n    valid.reduce((s, v) => s + (v - mean) ** 2, 0) / (valid.length - 1)\n  const sd = Math.sqrt(variance)\n\n  return Math.round(0.001 * (mean + sd) ** 2 * 100) / 100\n}\n","/**\n * @file src/metrics/modd.ts\n *\n * Mean of Daily Differences (MODD).\n *\n * MODD quantifies day-to-day glucose variability by comparing glucose values\n * at matching times on consecutive days. It measures how consistently glucose\n * behaves across days — lower values indicate more predictable patterns.\n *\n * Formula (Service & Nelson, 1980):\n *   MODD = mean(|G(t) - G(t - 24h)|) for all t where both values exist\n *\n * Readings are matched across days by finding the closest timestamp within\n * a configurable tolerance window (default: 15 minutes).\n *\n * @see https://doi.org/10.2337/diacare.3.1.58  Service & Nelson (1980)\n */\n\nimport type { GlucoseReading } from '../types'\nimport { MGDL_MMOLL_CONVERSION, MG_DL } from '../constants'\n\nconst MS_PER_DAY = 24 * 60 * 60 * 1000\nconst DEFAULT_TOLERANCE_MS = 15 * 60 * 1000 // 15 minutes\n\n/**\n * Options for MODD calculation.\n */\nexport interface MODDOptions {\n  /** Tolerance window in minutes for matching readings across days (default: 15) */\n  readonly toleranceMinutes?: number\n}\n\n/**\n * Calculates Mean of Daily Differences (MODD).\n *\n * @param readings - Array of GlucoseReading objects with timestamps\n * @param options - Optional tolerance configuration\n * @returns MODD value in mg/dL, or NaN if insufficient matched pairs\n * @see https://doi.org/10.2337/diacare.3.1.58\n */\nexport function calculateMODD(\n  readings: GlucoseReading[],\n  options?: MODDOptions\n): number {\n  if (readings.length < 2) return NaN\n\n  const toleranceMs =\n    options?.toleranceMinutes !== undefined\n      ? options.toleranceMinutes * 60 * 1000\n      : DEFAULT_TOLERANCE_MS\n\n  // Convert to timestamped mg/dL values and sort chronologically\n  const sorted = readings\n    .map((r) => ({\n      time: new Date(r.timestamp).getTime(),\n      value: r.unit === MG_DL ? r.value : r.value * MGDL_MMOLL_CONVERSION,\n    }))\n    .filter(\n      (r) =>\n        Number.isFinite(r.time) && Number.isFinite(r.value) && r.value > 0\n    )\n    .sort((a, b) => a.time - b.time)\n\n  if (sorted.length < 2) return NaN\n\n  const differences: number[] = []\n\n  for (let i = 0; i < sorted.length; i++) {\n    const targetTime = sorted[i].time + MS_PER_DAY\n\n    // Binary search for closest reading ~24h later\n    let lo = i + 1\n    let hi = sorted.length - 1\n    let bestIdx = -1\n    let bestDist = Infinity\n\n    while (lo <= hi) {\n      const mid = (lo + hi) >>> 1\n      const dist = Math.abs(sorted[mid].time - targetTime)\n      if (dist < bestDist) {\n        bestDist = dist\n        bestIdx = mid\n      }\n      if (sorted[mid].time < targetTime) {\n        lo = mid + 1\n      } else {\n        hi = mid - 1\n      }\n    }\n\n    if (bestIdx !== -1 && bestDist <= toleranceMs) {\n      differences.push(Math.abs(sorted[bestIdx].value - sorted[i].value))\n    }\n  }\n\n  if (differences.length === 0) return NaN\n\n  return (\n    Math.round(\n      (differences.reduce((s, d) => s + d, 0) / differences.length) * 10\n    ) / 10\n  )\n}\n","/**\n * @file src/metrics/conga.ts\n *\n * Continuous Overall Net Glycemic Action (CONGA).\n *\n * CONGA(n) measures intra-day glucose variability by computing the standard\n * deviation of the differences between glucose values separated by n hours.\n *\n * Formula (McDonnell et al. 2005):\n *   D_t = G(t) - G(t - n_hours)\n *   CONGA(n) = SD(D_t) over all valid pairs\n *\n * Pairs are matched by finding the closest reading within a tolerance\n * window of the target time offset.\n *\n * @see https://doi.org/10.1089/dia.2005.7.253  McDonnell et al. (2005)\n */\n\nimport type { GlucoseReading } from '../types'\nimport { MGDL_MMOLL_CONVERSION, MG_DL } from '../constants'\n\n/** Options for CONGA calculation. */\nexport interface CONGAOptions {\n  /** Number of hours for the time lag (default: 1) */\n  readonly hours?: number\n  /** Tolerance window in minutes for matching readings (default: 15) */\n  readonly toleranceMinutes?: number\n}\n\n/**\n * Calculates CONGA (Continuous Overall Net Glycemic Action).\n *\n * @param readings - Array of GlucoseReading objects with timestamps\n * @param options - Time lag (hours) and tolerance configuration\n * @returns CONGA value in mg/dL, or NaN if insufficient matched pairs\n * @see https://doi.org/10.1089/dia.2005.7.253\n */\nexport function calculateCONGA(\n  readings: GlucoseReading[],\n  options?: CONGAOptions\n): number {\n  if (readings.length < 2) return NaN\n\n  const hours = options?.hours ?? 1\n  const toleranceMs = (options?.toleranceMinutes ?? 15) * 60 * 1000\n  const lagMs = hours * 60 * 60 * 1000\n\n  const sorted = readings\n    .map((r) => ({\n      time: new Date(r.timestamp).getTime(),\n      value: r.unit === MG_DL ? r.value : r.value * MGDL_MMOLL_CONVERSION,\n    }))\n    .filter((r) => Number.isFinite(r.time) && Number.isFinite(r.value) && r.value > 0)\n    .sort((a, b) => a.time - b.time)\n\n  if (sorted.length < 2) return NaN\n\n  const differences: number[] = []\n\n  for (let i = 0; i < sorted.length; i++) {\n    const targetTime = sorted[i].time - lagMs\n    if (targetTime < sorted[0].time) continue\n\n    let bestIdx = -1\n    let bestDist = Infinity\n    let lo = 0\n    let hi = i - 1\n\n    while (lo <= hi) {\n      const mid = (lo + hi) >>> 1\n      const dist = Math.abs(sorted[mid].time - targetTime)\n      if (dist < bestDist) {\n        bestDist = dist\n        bestIdx = mid\n      }\n      if (sorted[mid].time < targetTime) {\n        lo = mid + 1\n      } else {\n        hi = mid - 1\n      }\n    }\n\n    if (bestIdx !== -1 && bestDist <= toleranceMs) {\n      differences.push(sorted[i].value - sorted[bestIdx].value)\n    }\n  }\n\n  if (differences.length < 2) return NaN\n\n  const mean = differences.reduce((s, d) => s + d, 0) / differences.length\n  const variance =\n    differences.reduce((s, d) => s + (d - mean) ** 2, 0) /\n    (differences.length - 1)\n\n  return Math.round(Math.sqrt(variance) * 10) / 10\n}\n","/**\n * @file src/metrics/active-percent.ts\n *\n * Active Percent (CGM Wear Time).\n *\n * Active percent measures the proportion of expected CGM readings that\n * were actually captured during a time period. Clinical guidelines\n * recommend >= 70% active time for meaningful CGM analysis.\n *\n * Formula (Danne et al. 2017):\n *   active% = (actual_readings / expected_readings) * 100\n *   expected_readings = time_span / expected_interval\n *\n * @see https://doi.org/10.2337/dc17-1600  Danne et al. (2017)\n */\n\nimport type { GlucoseReading } from '../types'\n\n/** Options for active percent calculation. */\nexport interface ActivePercentOptions {\n  /** Expected interval between readings in minutes (default: 5 for CGM) */\n  readonly expectedIntervalMinutes?: number\n}\n\n/** Result of the active percent calculation. */\nexport interface ActivePercentResult {\n  /** Percentage of expected readings that were captured (0-100) */\n  readonly activePercent: number\n  /** Number of actual readings in the dataset */\n  readonly actualReadings: number\n  /** Number of expected readings based on time span and interval */\n  readonly expectedReadings: number\n  /** Total time span covered in minutes */\n  readonly totalMinutes: number\n  /** Whether the active percent meets the clinical minimum (>= 70%) */\n  readonly meetsClinicalMinimum: boolean\n}\n\n/**\n * Calculates the CGM active percent (wear time).\n *\n * @param readings - Array of GlucoseReading objects with timestamps\n * @param options - Expected interval configuration\n * @returns Active percent result, or result with NaN if insufficient data\n * @see https://doi.org/10.2337/dc17-1600\n */\nexport function calculateActivePercent(\n  readings: GlucoseReading[],\n  options?: ActivePercentOptions\n): ActivePercentResult {\n  const intervalMin = options?.expectedIntervalMinutes ?? 5\n\n  if (readings.length < 2) {\n    return {\n      activePercent: NaN,\n      actualReadings: readings.length,\n      expectedReadings: 0,\n      totalMinutes: 0,\n      meetsClinicalMinimum: false,\n    }\n  }\n\n  const timestamps = readings\n    .map((r) => new Date(r.timestamp).getTime())\n    .filter((t) => Number.isFinite(t))\n    .sort((a, b) => a - b)\n\n  if (timestamps.length < 2) {\n    return {\n      activePercent: NaN,\n      actualReadings: timestamps.length,\n      expectedReadings: 0,\n      totalMinutes: 0,\n      meetsClinicalMinimum: false,\n    }\n  }\n\n  const spanMs = timestamps[timestamps.length - 1] - timestamps[0]\n  const totalMinutes = spanMs / (1000 * 60)\n  const expectedReadings = Math.floor(totalMinutes / intervalMin) + 1\n  const actualReadings = timestamps.length\n\n  /* c8 ignore start -- expectedReadings is always >= 1 when timestamps.length >= 2 */\n  const activePercent =\n    expectedReadings > 0\n      ? Math.min(100, Math.round((actualReadings / expectedReadings) * 1000) / 10)\n      : NaN\n  /* c8 ignore stop */\n\n  return {\n    activePercent,\n    actualReadings,\n    expectedReadings,\n    totalMinutes: Math.round(totalMinutes * 10) / 10,\n    meetsClinicalMinimum: Number.isFinite(activePercent) && activePercent >= 70,\n  }\n}\n","/**\n * @file src/metrics/agp.ts\n *\n * Aggregate Ambulatory Glucose Profile (AGP) metrics calculator.\n *\n * Computes a comprehensive set of standardized CGM metrics in a single call,\n * returning an object with all Tier 1 metrics from the iglu reference set.\n *\n * @see https://cran.r-project.org/web/packages/iglu/vignettes/metrics_list.html\n */\n\nimport type { GlucoseReading } from '../types'\nimport { MGDL_MMOLL_CONVERSION, MG_DL } from '../constants'\nimport { glucoseLBGI, glucoseHBGI } from './bgi'\nimport { calculateADRR } from './adrr'\nimport { calculateGRADE, type GRADEResult } from './grade'\nimport { calculateGRI, type GRIInput, type GRIResult } from './gri'\nimport { calculateJIndex } from './jindex'\nimport { calculateMODD, type MODDOptions } from './modd'\nimport { calculateCONGA, type CONGAOptions } from './conga'\nimport { calculateActivePercent, type ActivePercentResult, type ActivePercentOptions } from './active-percent'\nimport { calculateEnhancedTIR } from '../tir-enhanced'\nimport { glucoseStandardDeviation, glucoseCoefficientOfVariation } from '../variability'\n\n/** Options for the aggregate AGP metrics calculation. */\nexport interface AGPMetricsOptions {\n  /** MODD tolerance configuration */\n  readonly modd?: MODDOptions\n  /** CONGA configuration (hours, tolerance) */\n  readonly conga?: CONGAOptions\n  /** Active percent configuration (expected interval) */\n  readonly activePercent?: ActivePercentOptions\n}\n\n/** Comprehensive AGP metrics result. */\nexport interface AGPMetricsResult {\n  /** Mean glucose in mg/dL */\n  readonly meanGlucose: number\n  /** Standard deviation in mg/dL */\n  readonly sd: number\n  /** Coefficient of variation (%) */\n  readonly cv: number\n  /** Low Blood Glucose Index */\n  readonly lbgi: number\n  /** High Blood Glucose Index */\n  readonly hbgi: number\n  /** Average Daily Risk Range */\n  readonly adrr: number\n  /** GRADE score and partitioned components */\n  readonly grade: GRADEResult\n  /** Glycemia Risk Index */\n  readonly gri: GRIResult\n  /** J-Index */\n  readonly jIndex: number\n  /** Mean of Daily Differences (mg/dL) */\n  readonly modd: number\n  /** Continuous Overall Net Glycemic Action (mg/dL) */\n  readonly conga: number\n  /** CGM active percent / wear time */\n  readonly activePercent: ActivePercentResult\n  /** Number of valid readings used */\n  readonly totalReadings: number\n}\n\n/**\n * Calculates a comprehensive set of AGP metrics from glucose readings.\n *\n * This is a convenience function that computes all Tier 1 metrics in a\n * single call. Individual metric functions are also available for\n * selective computation.\n *\n * @param readings - Array of GlucoseReading objects with timestamps\n * @param options - Optional configuration for individual metrics\n * @returns Comprehensive AGP metrics result\n * @throws {Error} If readings array is empty\n */\nexport function calculateAGPMetrics(\n  readings: GlucoseReading[],\n  options?: AGPMetricsOptions\n): AGPMetricsResult {\n  if (readings.length === 0) {\n    throw new Error('Cannot calculate AGP metrics: readings array is empty')\n  }\n\n  const mgdlValues = readings.map((r) =>\n    r.unit === MG_DL ? r.value : r.value * MGDL_MMOLL_CONVERSION\n  ).filter((v) => Number.isFinite(v) && v > 0)\n\n  /* c8 ignore start -- defensive fallbacks for edge cases where all values filter out */\n  const meanGlucose = mgdlValues.length > 0\n    ? Math.round((mgdlValues.reduce((s, v) => s + v, 0) / mgdlValues.length) * 10) / 10\n    : NaN\n  /* c8 ignore stop */\n\n  const sd = glucoseStandardDeviation(mgdlValues)\n  const cv = glucoseCoefficientOfVariation(mgdlValues)\n\n  const tir = calculateEnhancedTIR(readings)\n  const griInput: GRIInput = {\n    veryLowPercent: tir.veryLow.percentage,\n    lowPercent: tir.low.percentage,\n    highPercent: tir.high.percentage,\n    veryHighPercent: tir.veryHigh.percentage,\n  }\n\n  return {\n    meanGlucose,\n    /* c8 ignore start -- sd/cv are always finite when mgdlValues.length >= 2 */\n    sd: Number.isFinite(sd) ? Math.round(sd * 10) / 10 : NaN,\n    cv: Number.isFinite(cv) ? Math.round(cv * 10) / 10 : NaN,\n    /* c8 ignore stop */\n    lbgi: glucoseLBGI(mgdlValues),\n    hbgi: glucoseHBGI(mgdlValues),\n    adrr: calculateADRR(readings),\n    grade: calculateGRADE(mgdlValues),\n    gri: calculateGRI(griInput),\n    jIndex: calculateJIndex(mgdlValues),\n    modd: calculateMODD(readings, options?.modd),\n    conga: calculateCONGA(readings, options?.conga),\n    activePercent: calculateActivePercent(readings, options?.activePercent),\n    totalReadings: mgdlValues.length,\n  }\n}\n","/**\n * @file src/connectors/dexcom.ts\n *\n * Pure transformation adapter for Dexcom Share API payloads.\n * Maps raw Dexcom entries into NormalizedCGMReading objects.\n * Does NOT handle authentication — use with any Dexcom Share client library.\n *\n * @see https://github.com/brettfarrow/cgm.js\n * @see https://www.npmjs.com/package/@diakem/dexcom-api-client\n */\n\nimport { MG_DL } from '../constants'\nimport type {\n  DexcomShareEntry,\n  DexcomTrendString,\n  NormalizedCGMReading,\n  CGMTrend,\n} from './types'\n\nconst DEXCOM_TREND_MAP: Record<DexcomTrendString, CGMTrend> = {\n  DoubleUp: 'rapidRising',\n  SingleUp: 'rising',\n  FortyFiveUp: 'slightlyRising',\n  Flat: 'flat',\n  FortyFiveDown: 'slightlyFalling',\n  SingleDown: 'falling',\n  DoubleDown: 'rapidFalling',\n  None: 'unknown',\n  NotComputable: 'unknown',\n  RateOutOfRange: 'unknown',\n}\n\n/**\n * Parses a Dexcom date string into an ISO 8601 string.\n *\n * Dexcom Share returns dates in the format `\"Date(epochMs)\"` or\n * `\"/Date(epochMs)/\"`. This helper handles both, plus plain ISO strings.\n */\nexport function parseDexcomDate(raw: string): string {\n  const epochMatch = raw.match(/Date\\((\\d+)\\)/)\n  if (epochMatch) {\n    return new Date(Number(epochMatch[1])).toISOString()\n  }\n  const parsed = Date.parse(raw)\n  if (isNaN(parsed)) {\n    throw new Error(`Unable to parse Dexcom date: ${raw}`)\n  }\n  return new Date(parsed).toISOString()\n}\n\n/**\n * Normalizes a Dexcom Share trend string into a canonical CGMTrend.\n */\nexport function normalizeDexcomTrend(\n  trend: DexcomTrendString | (string & {}) | null | undefined\n): CGMTrend {\n  if (trend == null) {\n    return 'unknown'\n  }\n  return DEXCOM_TREND_MAP[trend as DexcomTrendString] ?? 'unknown'\n}\n\n/**\n * Converts a single Dexcom Share entry into a NormalizedCGMReading.\n *\n * @param entry - Raw Dexcom Share entry\n * @returns Normalized reading compatible with all diabetic-utils analytics functions\n * @throws {Error} If the date string cannot be parsed\n */\nexport function normalizeDexcomEntry(\n  entry: DexcomShareEntry\n): NormalizedCGMReading {\n  return {\n    value: entry.Value,\n    unit: MG_DL,\n    timestamp: parseDexcomDate(entry.WT),\n    trend: normalizeDexcomTrend(entry.Trend),\n    source: 'dexcom',\n    vendorId: entry.ST ?? entry.DT,\n  }\n}\n\n/**\n * Converts an array of Dexcom Share entries into NormalizedCGMReadings.\n *\n * @param entries - Raw Dexcom Share entries from any Dexcom client library\n * @returns Array of normalized readings, sorted chronologically\n */\nexport function normalizeDexcomEntries(\n  entries: DexcomShareEntry[]\n): NormalizedCGMReading[] {\n  return entries\n    .map(normalizeDexcomEntry)\n    .sort(\n      (a, b) =>\n        new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()\n    )\n}\n","/**\n * @file src/connectors/libre.ts\n *\n * Pure transformation adapter for Libre LinkUp API payloads.\n * Maps raw Libre entries into NormalizedCGMReading objects.\n * Does NOT handle authentication — use with any Libre LinkUp client library.\n *\n * @see https://www.npmjs.com/package/librelinkup-api-client\n * @see https://www.npmjs.com/package/libre-client\n */\n\nimport { MG_DL } from '../constants'\nimport type {\n  LibreLinkUpEntry,\n  LibreTrendValue,\n  NormalizedCGMReading,\n  CGMTrend,\n} from './types'\n\nconst LIBRE_TREND_MAP: Record<LibreTrendValue, CGMTrend> = {\n  1: 'rapidFalling',\n  2: 'falling',\n  3: 'flat',\n  4: 'rising',\n  5: 'rapidRising',\n}\n\n/**\n * Normalizes a Libre LinkUp numeric trend value into a canonical CGMTrend.\n *\n * Accepts the raw API value (which may be null/undefined or out of range)\n * and returns 'unknown' for any invalid or unmapped values.\n */\nexport function normalizeLibreTrend(\n  trend: number | null | undefined\n): CGMTrend {\n  if (trend == null) {\n    return 'unknown'\n  }\n\n  // LibreTrendValue is defined as 1–5; anything else is treated as unknown.\n  if (trend < 1 || trend > 5) {\n    return 'unknown'\n  }\n\n  return LIBRE_TREND_MAP[trend as LibreTrendValue] ?? 'unknown'\n}\n\n/**\n * Converts a single Libre LinkUp entry into a NormalizedCGMReading.\n *\n * @param entry - Raw Libre LinkUp entry\n * @returns Normalized reading compatible with all diabetic-utils analytics functions\n * @throws {Error} If the timestamp cannot be parsed\n */\nexport function normalizeLibreEntry(\n  entry: LibreLinkUpEntry\n): NormalizedCGMReading {\n  const parsed = Date.parse(entry.Timestamp)\n  if (isNaN(parsed)) {\n    throw new Error(`Unable to parse Libre timestamp: ${entry.Timestamp}`)\n  }\n\n  return {\n    value: entry.Value,\n    unit: MG_DL,\n    timestamp: new Date(parsed).toISOString(),\n    trend: normalizeLibreTrend(entry.TrendArrow),\n    source: 'libre',\n  }\n}\n\n/**\n * Converts an array of Libre LinkUp entries into NormalizedCGMReadings.\n *\n * @param entries - Raw Libre LinkUp entries from any Libre client library\n * @returns Array of normalized readings, sorted chronologically\n */\nexport function normalizeLibreEntries(\n  entries: LibreLinkUpEntry[]\n): NormalizedCGMReading[] {\n  return entries\n    .map(normalizeLibreEntry)\n    .sort(\n      (a, b) =>\n        new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()\n    )\n}\n","/**\n * @file src/connectors/nightscout.ts\n *\n * Pure transformation adapter for Nightscout API payloads.\n * Maps raw Nightscout SGV entries into NormalizedCGMReading objects.\n * Does NOT handle authentication — use with the Nightscout REST API directly.\n *\n * @see https://nightscout.github.io/nightscout/setup_variables/#api\n * @see https://www.npmjs.com/package/nightscout\n */\n\nimport { MG_DL } from '../constants'\nimport type {\n  NightscoutEntry,\n  NightscoutDirection,\n  NormalizedCGMReading,\n  CGMTrend,\n} from './types'\n\nconst NIGHTSCOUT_DIRECTION_MAP: Record<string, CGMTrend> = {\n  DoubleUp: 'rapidRising',\n  SingleUp: 'rising',\n  FortyFiveUp: 'slightlyRising',\n  Flat: 'flat',\n  FortyFiveDown: 'slightlyFalling',\n  SingleDown: 'falling',\n  DoubleDown: 'rapidFalling',\n  NONE: 'unknown',\n  'NOT COMPUTABLE': 'unknown',\n  'RATE OUT OF RANGE': 'unknown',\n}\n\n/**\n * Normalizes a Nightscout direction string into a canonical CGMTrend.\n */\nexport function normalizeNightscoutDirection(\n  direction: NightscoutDirection | undefined\n): CGMTrend {\n  if (!direction) return 'unknown'\n  return NIGHTSCOUT_DIRECTION_MAP[direction] ?? 'unknown'\n}\n\n/**\n * Converts a single Nightscout SGV entry into a NormalizedCGMReading.\n *\n * @param entry - Raw Nightscout SGV entry\n * @returns Normalized reading compatible with all diabetic-utils analytics functions\n */\nexport function normalizeNightscoutEntry(\n  entry: NightscoutEntry\n): NormalizedCGMReading {\n  const timestamp = (() => {\n    if (entry.dateString) {\n      const parsed = Date.parse(entry.dateString)\n      if (!Number.isNaN(parsed)) {\n        return new Date(parsed).toISOString()\n      }\n\n      // Fall back to `entry.date` if available and valid\n      if (entry.date !== undefined && entry.date !== null) {\n        const fallbackDate = new Date(entry.date)\n        if (!Number.isNaN(fallbackDate.getTime())) {\n          return fallbackDate.toISOString()\n        }\n      }\n\n      throw new Error(\n        `Unable to parse Nightscout timestamp from 'dateString': ${entry.dateString}`\n      )\n    }\n\n    const date = new Date(entry.date)\n    if (Number.isNaN(date.getTime())) {\n      throw new Error(\n        `Unable to parse Nightscout timestamp from 'date' field: ${String(\n          entry.date\n        )}`\n      )\n    }\n\n    return date.toISOString()\n  })()\n  return {\n    value: entry.sgv,\n    unit: MG_DL,\n    timestamp,\n    trend: normalizeNightscoutDirection(entry.direction),\n    source: 'nightscout',\n    vendorId: entry._id,\n  }\n}\n\n/**\n * Converts an array of Nightscout SGV entries into NormalizedCGMReadings.\n *\n * @param entries - Raw Nightscout entries from the `/api/v1/entries` endpoint\n * @returns Array of normalized readings, sorted chronologically\n */\nexport function normalizeNightscoutEntries(\n  entries: NightscoutEntry[]\n): NormalizedCGMReading[] {\n  return entries\n    .map(normalizeNightscoutEntry)\n    .sort(\n      (a, b) =>\n        new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()\n    )\n}\n","/**\n * @file src/interop/fhir.ts\n *\n * Stateless FHIR-aligned payload builders for CGM data.\n * Generates lightweight JSON payloads aligned with the HL7 CGM IG v1.0.0\n * without requiring any FHIR library dependency.\n *\n * @see https://build.fhir.org/ig/HL7/cgm/index.html\n */\n\nimport type { GlucoseReading } from '../types'\nimport type { EnhancedTIRResult } from '../types'\nimport { MG_DL } from '../constants'\nimport type { FHIRCGMSummary, FHIRCGMSensorReading, FHIRCGMComponent } from './types'\n\nconst LOINC_SYSTEM = 'http://loinc.org'\nconst UCUM_SYSTEM = 'http://unitsofmeasure.org'\n\n/**\n * Builds a FHIR-aligned CGM summary observation from an EnhancedTIRResult.\n *\n * Includes TIR percentages for each range, mean glucose, and CV if provided.\n * The output is a plain JSON object that can be serialized and POSTed to any\n * FHIR-capable server.\n *\n * @param tir - Enhanced TIR result from calculateEnhancedTIR()\n * @param period - Time period covered { start: ISO string, end: ISO string }\n * @param options - Optional: meanGlucose, cv (coefficient of variation)\n * @returns FHIR-aligned CGM summary observation\n */\nexport function buildFHIRCGMSummary(\n  tir: EnhancedTIRResult,\n  period: { start: string; end: string },\n  options?: { meanGlucose?: number; cv?: number }\n): FHIRCGMSummary {\n  const components: FHIRCGMComponent[] = [\n    {\n      code: { coding: [{ system: LOINC_SYSTEM, code: '97507-8', display: 'Time in range (70-180 mg/dL)' }] },\n      valueQuantity: { value: tir.inRange.percentage, unit: '%', system: UCUM_SYSTEM, code: '%' },\n    },\n    {\n      code: { coding: [{ system: LOINC_SYSTEM, code: '97506-0', display: 'Time below range level 1 (54-69 mg/dL)' }] },\n      valueQuantity: { value: tir.low.percentage, unit: '%', system: UCUM_SYSTEM, code: '%' },\n    },\n    {\n      code: { coding: [{ system: LOINC_SYSTEM, code: '97505-2', display: 'Time below range level 2 (<54 mg/dL)' }] },\n      valueQuantity: { value: tir.veryLow.percentage, unit: '%', system: UCUM_SYSTEM, code: '%' },\n    },\n    {\n      code: { coding: [{ system: LOINC_SYSTEM, code: '97508-6', display: 'Time above range level 1 (181-250 mg/dL)' }] },\n      valueQuantity: { value: tir.high.percentage, unit: '%', system: UCUM_SYSTEM, code: '%' },\n    },\n    {\n      code: { coding: [{ system: LOINC_SYSTEM, code: '97509-4', display: 'Time above range level 2 (>250 mg/dL)' }] },\n      valueQuantity: { value: tir.veryHigh.percentage, unit: '%', system: UCUM_SYSTEM, code: '%' },\n    },\n  ]\n\n  if (options?.meanGlucose !== undefined) {\n    components.push({\n      code: { coding: [{ system: LOINC_SYSTEM, code: '97507-0', display: 'Mean glucose' }] },\n      valueQuantity: { value: options.meanGlucose, unit: 'mg/dL', system: UCUM_SYSTEM, code: 'mg/dL' },\n    })\n  }\n\n  if (options?.cv !== undefined) {\n    components.push({\n      code: { coding: [{ system: LOINC_SYSTEM, code: '97506-2', display: 'Coefficient of variation' }] },\n      valueQuantity: { value: options.cv, unit: '%', system: UCUM_SYSTEM, code: '%' },\n    })\n  }\n\n  return {\n    resourceType: 'Observation',\n    status: 'final',\n    code: {\n      coding: [\n        {\n          system: LOINC_SYSTEM,\n          code: '97504-5',\n          display: 'CGM summary',\n        },\n      ],\n    },\n    effectivePeriod: {\n      start: period.start,\n      end: period.end,\n    },\n    component: components,\n  }\n}\n\n/**\n * Converts a single GlucoseReading into a FHIR-aligned CGM sensor reading.\n *\n * @param reading - A GlucoseReading from any source (raw or normalized)\n * @returns FHIR-aligned sensor reading observation\n */\nexport function buildFHIRSensorReading(\n  reading: GlucoseReading\n): FHIRCGMSensorReading {\n  const isMgDl = reading.unit === MG_DL\n  return {\n    resourceType: 'Observation',\n    status: 'final',\n    code: {\n      coding: [\n        {\n          system: LOINC_SYSTEM,\n          code: isMgDl ? '99504-3' : '14745-4',\n          display: isMgDl\n            ? 'Glucose [Mass/volume] in Interstitial fluid'\n            : 'Glucose [Moles/volume] in Interstitial fluid',\n        },\n      ],\n    },\n    effectiveDateTime: reading.timestamp,\n    valueQuantity: {\n      value: reading.value,\n      unit: reading.unit,\n      system: UCUM_SYSTEM,\n      code: isMgDl ? 'mg/dL' : 'mmol/L',\n    },\n  }\n}\n\n/**\n * Converts an array of GlucoseReadings into FHIR-aligned sensor reading observations.\n *\n * @param readings - Array of glucose readings\n * @returns Array of FHIR-aligned sensor reading observations\n */\nexport function buildFHIRSensorReadings(\n  readings: GlucoseReading[]\n): FHIRCGMSensorReading[] {\n  return readings.map(buildFHIRSensorReading)\n}\n","/**\n * @file src/interop/openmhealth.ts\n *\n * Stateless Open mHealth payload builders for glucose data.\n * Generates JSON datapoints aligned with the Open mHealth blood-glucose schema.\n *\n * @see https://www.openmhealth.org/documentation/schema-docs/schema-library/\n */\n\nimport type { GlucoseReading } from '../types'\nimport { MG_DL } from '../constants'\nimport type { OMHBloodGlucose, OMHDataPoint } from './types'\n\nconst OMH_SCHEMA = {\n  namespace: 'omh',\n  name: 'blood-glucose',\n  version: '3.0',\n} as const\n\n/**\n * Converts a GlucoseReading into an Open mHealth blood-glucose body.\n *\n * @param reading - A GlucoseReading from any source\n * @returns OMH blood-glucose body\n */\nexport function buildOMHBloodGlucose(reading: GlucoseReading): OMHBloodGlucose {\n  return {\n    blood_glucose: {\n      value: reading.value,\n      unit: reading.unit === MG_DL ? 'mg/dL' : 'mmol/L',\n    },\n    effective_time_frame: {\n      date_time: reading.timestamp,\n    },\n    specimen_source: 'interstitial fluid',\n  }\n}\n\n/**\n * Wraps a GlucoseReading into a full Open mHealth DataPoint with header.\n *\n * @param reading - A GlucoseReading from any source\n * @param id - Unique datapoint identifier (UUID recommended)\n * @returns Full OMH DataPoint with header and body\n */\nexport function buildOMHDataPoint(\n  reading: GlucoseReading,\n  id: string\n): OMHDataPoint<OMHBloodGlucose> {\n  return {\n    header: {\n      id,\n      schema_id: {\n        namespace: OMH_SCHEMA.namespace,\n        name: OMH_SCHEMA.name,\n        version: OMH_SCHEMA.version,\n      },\n      creation_date_time: new Date().toISOString(),\n    },\n    body: buildOMHBloodGlucose(reading),\n  }\n}\n\n/**\n * Converts an array of GlucoseReadings into Open mHealth blood-glucose bodies.\n *\n * @param readings - Array of glucose readings\n * @returns Array of OMH blood-glucose bodies\n */\nexport function buildOMHBloodGlucoseList(\n  readings: GlucoseReading[]\n): OMHBloodGlucose[] {\n  return readings.map(buildOMHBloodGlucose)\n}\n"]}