export interface GlucoseReading { /** ISO-8601 timestamp. */ timestamp: string; /** Glucose in mg/dL. */ mgdl: number; /** Optional trend arrow if provided by upstream. */ trend?: string; } export interface TimeInRangeStats { count: number; in_range_pct: number; below_pct: number; above_pct: number; range: { low: number; high: number; }; buckets: { below: number; in_range: number; above: number; }; } export interface GlucoseSummary { count: number; mean_mgdl: number; median_mgdl: number; min_mgdl: number; max_mgdl: number; std_mgdl: number; /** Glucose Management Indicator (estimated A1C) — Bergenstal 2018 formula. */ gmi_pct: number; cv_pct: number; diabetic_tir: TimeInRangeStats; metabolic_health_tir: TimeInRangeStats; } export declare function timeInRange(readings: GlucoseReading[], low: number, high: number): TimeInRangeStats; export interface TimeInRangeWindowResult extends TimeInRangeStats { /** Effective ISO timestamp the window starts at (defaults to earliest reading if absent). */ start_time?: string; /** Effective ISO timestamp the window ends at (defaults to latest reading if absent). */ end_time?: string; /** Total readings considered before any window/hour-of-day filter. */ total_readings: number; /** Readings after window + hour-of-day filtering (alias of `count` for spec compatibility). */ readings_in_window: number; /** Mean glucose (mg/dL) of readings in the window. 0 when window is empty. */ mean_glucose: number; /** Median glucose (mg/dL) of readings in the window. 0 when window is empty. */ median_glucose: number; /** GMI (estimated A1C, %) for the in-window mean — ADA / Bergenstal 2018. 0 when window is empty. */ gmi: number; /** Resolved hour-of-day filter (UTC) applied on top of the time window, if any. */ hour_of_day_filter?: { start_hour: number; end_hour: number; preset?: TimeWindowPreset; }; } export type TimeWindowPreset = "all" | "wake" | "sleep"; /** * Filter readings to those in [start_time, end_time] (inclusive), optionally * restricted to a recurring hour-of-day window (e.g. wake = 06:00-22:00, * sleep = 22:00-06:00), then compute time-in-range over the filtered set. * Empty result correctly returns `readings_in_window: 0` without crashing. * * Defaults: start=earliest reading, end=latest reading, time_window="all". * * v0.3.2: * - Adds explicit `total_readings`, `readings_in_window`, `mean_glucose`, * `median_glucose`, `gmi` so callers don't have to recompute them. * - Adds `time_window` ("all" | "wake" | "sleep") and explicit * `start_hour`/`end_hour` (0-24) for recurring hour-of-day filters that * span midnight (sleep) or stay within a single day (wake). Wake defaults * to 06:00-22:00; sleep defaults to 22:00-06:00. Hour comparisons use UTC * so they're deterministic regardless of process timezone — callers who * need local-time semantics should pass explicit `start_hour`/`end_hour` * derived from their own tz. * - GMI uses the ADA / Bergenstal 2018 formula: * GMI(%) = 3.31 + 0.02392 × mean_glucose_mg_dL * so mean=154 mg/dL → GMI ≈ 7.0. */ export declare function timeInRangeWindow(readings: GlucoseReading[], options?: { start_time?: string; end_time?: string; low?: number; high?: number; time_window?: TimeWindowPreset; start_hour?: number; end_hour?: number; }): TimeInRangeWindowResult; export declare function summarize(readings: GlucoseReading[]): GlucoseSummary; export interface MealGlucoseResponse { meal_time: string; baseline_mgdl: number; peak_mgdl: number; peak_delta_mgdl: number; peak_time_minutes: number; return_to_baseline_minutes: number | null; band: "excellent" | "good" | "moderate" | "poor"; } /** * Estimate glucose response to a meal: * - baseline = last reading <= meal_time (or first available) * - peak = max in [meal_time, meal_time + 3h] * - return_to_baseline = first time after peak where mgdl <= baseline + 10 * * Bands per Levels-style spike thresholds: * - excellent: peak_delta < 30 mg/dL * - good: peak_delta < 50 * - moderate: peak_delta < 80 * - poor: peak_delta >= 80 */ export declare function mealResponse(readings: GlucoseReading[], mealTimeIso: string): MealGlucoseResponse | null; export interface HypoEvent { /** ISO timestamp of first reading below threshold that started the event. */ started_at: string; /** ISO timestamp of last reading that was still below threshold (event end). */ ended_at: string; /** Total duration in minutes (inclusive of start, exclusive of end+1). */ duration_minutes: number; /** Lowest mg/dL observed during the event. */ min_glucose_mg_dl: number; /** Mean mg/dL across all in-event readings. */ mean_glucose_mg_dl: number; /** ADA Level 1 (<70) → "level_1" ; Level 2 (<54) → "level_2". */ severity: "level_1" | "level_2"; /** * Minutes from end-of-event until the first reading ≥ threshold + 10 mg/dL. * null when we never see a recovery reading inside the loaded data. */ recovery_time_minutes: number | null; } export interface HypoEventsResult { events: HypoEvent[]; total_events: number; total_minutes_below: number; mean_min_glucose: number; events_per_day: number; summary: string; recommendations: string[]; thresholds: { level_1_mg_dl: number; level_2_mg_dl: number; }; min_duration_minutes: number; observed_window: { start: string | null; end: string | null; days: number; }; } /** * v0.3.3 — Detect hypoglycemia events from a stream of CGM readings. * * Per ADA standards: * Level 1 hypo = < 70 mg/dL (alert value) * Level 2 hypo = < 54 mg/dL (clinically significant) * * An "event" is a contiguous run of readings below `threshold_mg_dl` lasting * at least `min_duration_minutes`. Severity reflects the lowest mg/dL during * the event (level_2 if it ever crossed the severe threshold, else level_1). * * Recovery time = minutes from event end to the first reading ≥ threshold + 10 * (matches the "out of hypo" definition used in DPP and ADA TIR reporting). */ export declare function detectHypoEvents(readings: GlucoseReading[], options?: { threshold_mg_dl?: number; severe_threshold_mg_dl?: number; min_duration_minutes?: number; from?: string; to?: string; }): HypoEventsResult; /** * Generate sample readings simulating a Dexcom 5-minute interval over the last N hours. * Used in mock mode when no Dexcom token is configured. */ export declare function mockReadings(hours?: number, baseline?: number): GlucoseReading[];