Built and maintained by Mark Learst.
A TypeScript toolkit for diabetes analytics and health data.
A modern, strictly-typed utility library for glucose, A1C, insulin, and diabetes metrics. Designed for reliability and transparencyβno bloat, no guesswork, just robust utilities with referenced formulas from published guidelines.
Disclaimer: This library is for informational and educational purposes only. It does not constitute medical advice, diagnosis, or treatment. Always consult a qualified healthcare provider for medical decisions.
v1.5.0 adds a full advanced CGM metrics suite, CGM vendor adapters, and health data interoperability (FHIR, Open mHealth).
A complete set of published CGM analytics, each with peer-reviewed references:
- ADRR: Average Daily Risk Range (Kovatchev 2006)
- GRADE: Glycemic Risk Assessment Diabetes Equation with hypo/eu/hyper partitioning (Hill 2007)
- J-Index: Composite mean + variability score (Wojcicki 1995)
- CONGA: Continuous Overall Net Glycemic Action for intra-day variability (McDonnell 2005)
- Active Percent: CGM wear-time tracking against clinical thresholds (Danne 2017)
- AGP Aggregate: Single-call
calculateAGPMetrics()computes all Tier 1 metrics at once - LBGI / HBGI: Low/High Blood Glucose Index (Kovatchev 2006)
- GRI: Glycemia Risk Index with zone A-E classification (Klonoff 2023)
- MODD: Mean of Daily Differences for day-to-day variability (Service 1980)
Pure transformation helpers that normalize vendor payloads into a canonical NormalizedCGMReading type:
- Dexcom Share β normalize Dexcom Share API responses
- Libre LinkUp β normalize Libre LinkUp API responses
- Nightscout β normalize Nightscout SGV entries
Build standards-compliant payloads for health data exchange:
- FHIR CGM IG β HL7 FHIR-aligned CGM summary and sensor reading observations
- Open mHealth β OMH blood-glucose datapoints with full header support
- 100% coverage across lines, branches, functions, and statements
- New edge-case coverage for out-of-order timestamps, mixed units, and cross-module interactions
npm install diabetic-utils
# or
pnpm add diabetic-utils
# or
yarn add diabetic-utilsRequirements: TypeScript 4.5+ or JavaScript (ES2020+)
import {
mgDlToMmolL,
mmolLToMgDl,
estimateGMI,
estimateA1CFromAverage
} from 'diabetic-utils'
// Glucose unit conversions
mgDlToMmolL(180) // β 10.0
mmolLToMgDl(5.5) // β 99
// GMI calculation (multiple input formats)
estimateGMI(100, 'mg/dL') // β 5.4
estimateGMI('5.5 mmol/L') // β 5.4
estimateGMI({ value: 100, unit: 'mg/dL' }) // β 5.4
// A1C estimation
estimateA1CFromAverage(154, 'mg/dL') // β 7.0import { calculateEnhancedTIR } from 'diabetic-utils'
import type { GlucoseReading } from 'diabetic-utils'
const readings: GlucoseReading[] = [
{ value: 120, unit: 'mg/dL', timestamp: '2024-01-01T08:00:00Z' },
{ value: 95, unit: 'mg/dL', timestamp: '2024-01-01T08:05:00Z' },
{ value: 180, unit: 'mg/dL', timestamp: '2024-01-01T08:10:00Z' },
// ... more readings
]
const result = calculateEnhancedTIR(readings)
console.log(`TIR: ${result.inRange.percentage}%`)
// TIR: 72.5%
console.log(`Very Low: ${result.veryLow.percentage}%`)
// Very Low: 0.5%
console.log(`Assessment: ${result.meetsTargets.overallAssessment}`)
// Assessment: good
console.log(result.meetsTargets.recommendations)
// ['All metrics meet consensus targets.']import { calculatePregnancyTIR } from 'diabetic-utils'
const result = calculatePregnancyTIR(readings)
console.log(`TIR (63-140 mg/dL): ${result.inRange.percentage}%`)
// TIR (63-140 mg/dL): 85.2%
console.log(`Meets pregnancy targets: ${result.meetsPregnancyTargets}`)
// Meets pregnancy targets: true
console.log(result.recommendations)
// ['All metrics meet pregnancy consensus targets.', ...]import { calculateAGPMetrics } from 'diabetic-utils'
import type { GlucoseReading } from 'diabetic-utils'
const readings: GlucoseReading[] = [
{ value: 120, unit: 'mg/dL', timestamp: '2024-01-01T08:00:00Z' },
{ value: 95, unit: 'mg/dL', timestamp: '2024-01-01T08:05:00Z' },
{ value: 180, unit: 'mg/dL', timestamp: '2024-01-01T08:10:00Z' },
// ... more readings across multiple days
]
const agp = calculateAGPMetrics(readings)
console.log(`Mean: ${agp.meanGlucose} mg/dL`)
console.log(`SD: ${agp.sd}, CV: ${agp.cv}%`)
console.log(`LBGI: ${agp.lbgi}, HBGI: ${agp.hbgi}`)
console.log(`ADRR: ${agp.adrr}`)
console.log(`GRADE: ${agp.grade.gradeScore}`)
console.log(`GRI: ${agp.gri.gri} (Zone ${agp.gri.zone})`)
console.log(`J-Index: ${agp.jIndex}`)
console.log(`MODD: ${agp.modd} mg/dL`)
console.log(`CONGA: ${agp.conga} mg/dL`)
console.log(`Active: ${agp.activePercent.activePercent}%`)import {
normalizeDexcomEntries,
normalizeLibreEntries,
normalizeNightscoutEntries
} from 'diabetic-utils'
// Normalize vendor data into a canonical format
const dexcomReadings = normalizeDexcomEntries(dexcomShareResponse)
const libreReadings = normalizeLibreEntries(libreLinkUpResponse)
const nightscoutReadings = normalizeNightscoutEntries(nightscoutSGVEntries)
// All return NormalizedCGMReading[] with:
// { value, unit, timestamp, trend, source }
// Ready to pass into any diabetic-utils analytics functionimport {
buildFHIRCGMSummary,
buildFHIRSensorReading,
buildOMHDataPoint
} from 'diabetic-utils'
// Build a FHIR CGM summary observation
const fhirSummary = buildFHIRCGMSummary(tirResult, {
start: '2024-01-01',
end: '2024-01-14'
})
// Build a FHIR sensor reading observation
const fhirReading = buildFHIRSensorReading({
value: 120, unit: 'mg/dL', timestamp: '2024-01-01T08:00:00Z'
})
// Build an Open mHealth blood-glucose datapoint
const omhPoint = buildOMHDataPoint(
{ value: 120, unit: 'mg/dL', timestamp: '2024-01-01T08:00:00Z' },
'reading-001'
)import {
getGlucoseLabel,
isHypo,
isHyper,
isValidGlucoseValue
} from 'diabetic-utils'
// Label glucose values
getGlucoseLabel(60, 'mg/dL') // β 'low'
getGlucoseLabel(5.5, 'mmol/L') // β 'normal'
getGlucoseLabel(200, 'mg/dL') // β 'high'
// Threshold checks
isHypo(65, 'mg/dL') // β true
isHyper(180, 'mg/dL') // β false
// Validation
isValidGlucoseValue(120, 'mg/dL') // β true
isValidGlucoseValue(-10, 'mg/dL') // β falseimport {
glucoseStandardDeviation,
glucoseCoefficientOfVariation,
glucosePercentiles,
glucoseMAGE
} from 'diabetic-utils'
const data = [90, 100, 110, 120, 130, 140, 150, 160, 170, 180]
// Standard deviation (unbiased sample SD, n-1)
glucoseStandardDeviation(data) // β 30.28
// Coefficient of variation (CV%)
glucoseCoefficientOfVariation(data) // β 22.43
// Percentiles (nearest-rank method)
glucosePercentiles(data, [10, 50, 90])
// β { 10: 90, 50: 130, 90: 170 }
// MAGE (Mean Amplitude of Glycemic Excursions)
const mage = glucoseMAGE([100, 120, 80, 160, 90, 140, 70, 180])
console.log(`MAGE: ${mage} mg/dL`)import { getGlucoseLabel, isHypo, getA1CCategory } from 'diabetic-utils'
// Custom hypoglycemia threshold
isHypo(75, 'mg/dL', { mgdl: 80 }) // β true
// Custom hyperglycemia threshold
isHyper(9.0, 'mmol/L', { mmoll: 8.5 }) // β true
// Custom glucose label thresholds
getGlucoseLabel(75, 'mg/dL', {
hypo: { mgdl: 80 },
hyper: { mgdl: 160 }
}) // β 'low'
// Custom A1C category cutoffs
getA1CCategory(6.5, {
normalMax: 6.0,
prediabetesMax: 7.0
}) // β 'prediabetes'- β Glucose Conversions: mg/dL β mmol/L
- β A1C Calculations: GMI, eAG, A1C estimation
- β Time-in-Range: Enhanced TIR (5 ranges), Pregnancy TIR
- β HOMA-IR: Insulin resistance calculation
- β Variability Metrics: SD, CV, MAGE, percentiles
- β Validation: Input guards, string parsing
- β Labeling: Glucose status (low/normal/high)
- β LBGI / HBGI: Low/High Blood Glucose Index (Kovatchev 2006)
- β GRI: Glycemia Risk Index with zone A-E classification (Klonoff 2023)
- β MODD: Mean of Daily Differences for day-to-day variability (Service 1980)
- β ADRR: Average Daily Risk Range (Kovatchev 2006)
- β GRADE: Glycemic Risk Assessment Diabetes Equation with partitioning (Hill 2007)
- β J-Index: Composite mean + variability score (Wojcicki 1995)
- β CONGA: Continuous Overall Net Glycemic Action (McDonnell 2005)
- β Active Percent: CGM wear-time tracking (Danne 2017)
- β AGP Aggregate: All Tier 1 metrics in a single call
- β Dexcom Share: Normalize Dexcom Share API responses
- β Libre LinkUp: Normalize Libre LinkUp API responses
- β Nightscout: Normalize Nightscout SGV entries
- β
Canonical Type:
NormalizedCGMReadingwith trend + source metadata
- β FHIR CGM IG: Build HL7 FHIR-aligned CGM summary and sensor reading payloads
- β Open mHealth: Build OMH blood-glucose datapoints
- β
TypeScript-First: 100% strict mode, zero
anytypes - β 100% Test Coverage: 337 tests, all edge cases covered
- β Zero Dependencies: No bloat, tree-shakable
- β Published References: ADA, CDC, ISPAD, PubMed citations
- β TSDoc: Complete API documentation
- β ESM + CJS: Works everywhere
- β Type Predicates: Better type narrowing
- β Named Constants: Self-documenting formulas
Every formula, threshold, and calculation references published guidelines:
- International Consensus on Time in Range (2019) - TIR calculations
- ADA Standards of Care (2024) - Pregnancy targets, A1C guidelines
- ISPAD Guidelines (2018) - Glucose variability metrics
- NIH/NIDDK - HOMA-IR, eAG formulas
- Kovatchev et al. (2006) - LBGI, HBGI, ADRR
- Hill et al. (2007) - GRADE
- Klonoff et al. (2023) - GRI
- Wojcicki (1995) - J-Index
- McDonnell et al. (2005) - CONGA
- Danne et al. (2017) - Active Percent
- 100% Test Coverage - Every line tested
- Type-Safe - Catch errors at compile time
- Zero Dependencies - Small bundle, no supply chain risk
- Modern ESM - Tree-shakable, works with Vite, Next.js, etc.
- Clear API - Predictable function signatures
- Great DX - Autocomplete with literal types
- Working Examples - Copy-paste ready code
- Test Helpers - Utilities for your own tests
Only TypeScript/JavaScript library with:
- Full AGP metrics suite in a single call
- Enhanced TIR (5-range breakdown) and Pregnancy TIR
- MAGE calculation (Service 1970)
- ADRR, GRADE, J-Index, CONGA, and Active Percent
- CGM vendor adapters (Dexcom, Libre, Nightscout)
- FHIR CGM IG-aligned export utilities
- LBGI/HBGI, GRI, and MODD metrics
- Type predicates for validation
mgDlToMmolL(value)- Convert mg/dL to mmol/LmmolLToMgDl(value)- Convert mmol/L to mg/dLconvertGlucoseUnit({ value, unit })- Generic unit conversion
estimateA1CFromAverage(glucose, unit)- A1C from average glucoseestimateGMI(input, unit?)- GMI from average glucosea1cToGMI(a1c)- Convert A1C to GMIestimateAvgGlucoseFromA1C(a1c)- A1C to estimated average glucose (mg/dL)
calculateTimeInRange(readings, low, high)- Basic TIRcalculateEnhancedTIR(readings, options?)- 5-range TIRcalculatePregnancyTIR(readings, options?)- Pregnancy TIR
getGlucoseLabel(value, unit, thresholds?)- Label as low/normal/highisHypo(value, unit, threshold?)- Check hypoglycemiaisHyper(value, unit, threshold?)- Check hyperglycemiaisValidGlucoseValue(value, unit)- Validate glucose value
getA1CCategory(a1c, cutoffs?)- Categorize A1CisA1CInTarget(a1c, target?)- Check if A1C meets target
glucoseStandardDeviation(readings)- SD (unbiased)glucoseCoefficientOfVariation(readings)- CV%glucosePercentiles(readings, percentiles)- Percentile ranksglucoseMAGE(readings, options?)- Mean Amplitude of Glycemic Excursions
calculateHOMAIR(glucose, insulin, unit)- HOMA-IRisValidInsulin(value)- Validate insulin value
calculateAGPMetrics(readings, options?)- All Tier 1 metrics in a single callglucoseLBGI(readings)- Low Blood Glucose Index (Kovatchev 2006)glucoseHBGI(readings)- High Blood Glucose Index (Kovatchev 2006)calculateADRR(readings)- Average Daily Risk Range (Kovatchev 2006)calculateGRADE(readings)- Glycemic Risk Assessment Diabetes Equation (Hill 2007)calculateGRI(input)- Glycemia Risk Index with zone A-E (Klonoff 2023)calculateJIndex(readings)- J-Index composite score (Wojcicki 1995)calculateMODD(readings, options?)- Mean of Daily Differences (Service 1980)calculateCONGA(readings, options?)- Continuous Overall Net Glycemic Action (McDonnell 2005)calculateActivePercent(readings, options?)- CGM wear-time percentage (Danne 2017)
normalizeDexcomEntries(entries)- Dexcom Share β NormalizedCGMReading[]normalizeLibreEntries(entries)- Libre LinkUp β NormalizedCGMReading[]normalizeNightscoutEntries(entries)- Nightscout SGV β NormalizedCGMReading[]
buildFHIRCGMSummary(tir, period, options?)- FHIR CGM summary observationbuildFHIRSensorReading(reading)- FHIR sensor reading observationbuildFHIRSensorReadings(readings)- FHIR sensor reading observations from a list of readingsbuildOMHBloodGlucose(reading)- Open mHealth blood-glucose bodybuildOMHBloodGlucoseList(readings)- Open mHealth blood-glucose bodies from a list of readingsbuildOMHDataPoint(reading, id)- Full OMH datapoint with header
parseGlucoseString(str)- Parse "120 mg/dL" β { value, unit }formatGlucose(value, unit)- Format glucose with unitisValidGlucoseString(str)- Validate glucose string
The repository includes test utilities in tests/test-helpers.ts for contributors and downstream developers:
// In your test files (not published to npm β copy as needed)
import {
createGlucoseReadings,
COMMON_TEST_VALUES,
TEST_TIMESTAMP_BASE
} from './tests/test-helpers'
// Create test data easily
const readings = createGlucoseReadings([100, 110, 120], 'mg/dL', 5)
// β 3 readings at 5-minute intervals
// Use common test values
const { NORMAL_GLUCOSE_MGDL, HYPO_GLUCOSE_MGDL } = COMMON_TEST_VALUESAll calculations reference peer-reviewed published sources:
- Time-in-Range: International Consensus (2019)
- Pregnancy TIR: ADA Standards of Care (2024)
- ADA 2026 Standards: ADA Standards of Care (2026)
- A1C/eAG: Nathan et al. (2008)
- HOMA-IR: Matthews et al. (1985)
- MAGE: Service et al. (1970)
- LBGI/HBGI/ADRR: Kovatchev et al. (2006)
- GRI: Klonoff et al. (2023)
- MODD: Service & Nelson (1980)
- GRADE: Hill et al. (2007)
- J-Index: Wojcicki (1995)
- CONGA: McDonnell et al. (2005)
- Active Percent: Danne et al. (2017)
- Variability: ISPAD Guidelines (2018)
- FHIR CGM IG: HL7 CGM IG v1.0.0
diabetic-utils/
βββ src/
β βββ index.ts # Main exports
β βββ constants.ts # Clinical thresholds & formulas
β βββ types.ts # TypeScript types
β βββ conversions.ts # Glucose unit conversions
β βββ a1c.ts # A1C & GMI calculations
β βββ tir.ts # Basic time-in-range
β βββ tir-enhanced.ts # Enhanced & pregnancy TIR
β βββ glucose.ts # Glucose utilities
β βββ alignment.ts # HOMA-IR
β βββ variability.ts # SD, CV, percentiles
β βββ mage.ts # MAGE calculation
β βββ formatters.ts # String formatting
β βββ guards.ts # Type guards
β βββ validators.ts # Input validation
β βββ connectors/ # CGM vendor adapters
β β βββ dexcom.ts # Dexcom Share normalization
β β βββ libre.ts # Libre LinkUp normalization
β β βββ nightscout.ts # Nightscout SGV normalization
β β βββ types.ts # Vendor & canonical types
β βββ interop/ # Health data interoperability
β β βββ fhir.ts # FHIR CGM IG payload builders
β β βββ openmhealth.ts # Open mHealth payload builders
β β βββ types.ts # Interop payload types
β βββ metrics/ # Advanced CGM metrics
β βββ agp.ts # Aggregate AGP metrics
β βββ bgi.ts # LBGI / HBGI
β βββ adrr.ts # Average Daily Risk Range
β βββ grade.ts # GRADE score
β βββ gri.ts # Glycemia Risk Index
β βββ jindex.ts # J-Index
β βββ modd.ts # Mean of Daily Differences
β βββ conga.ts # CONGA
β βββ active-percent.ts # CGM wear time
βββ tests/
β βββ test-helpers.ts # Shared test utilities
β βββ *.test.ts # 100% coverage tests (337 tests)
βββ dist/ # Built output (ESM + CJS)
Key Principles:
- β Zero dependencies
- β Tree-shakable modules
- β Strict TypeScript
- β 100% test coverage
- β Published references in TSDoc
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create your feature branch:
git checkout -b feat/my-feature - Add tests for any new functionality
- Ensure 100% coverage:
pnpm test:coverage - Commit with conventional commits:
git commit -m "feat: add new feature" - Push to your branch:
git push origin feat/my-feature - Open a pull request
# Install dependencies
pnpm install
# Run tests
pnpm test
# Run tests with coverage
pnpm test:coverage
# Build library
pnpm buildSee CHANGELOG.md for detailed release notes and version history.
This project is licensed under the MIT License. See the LICENSE file for details.
Β© 2024β2026 Mark Learst
Use it, fork it, build something that matters.
- π¦ NPM Package
- π API Documentation
- π GitHub Repository
- π Website (coming soon)
Mark Learst Full-stack developer, diabetes advocate, and open source contributor.
- π¦ X (Twitter): @marklearst
- πΌ LinkedIn: Mark Learst
- π Portfolio: marklearst.com
π¬ Using diabetic-utils in your project? Let me knowβI'd love to feature it! β Star the repo and help us build the best diabetes toolkit together!
- π Bug Reports: Open an issue
- π‘ Feature Requests: Start a discussion
- π§ Email: mark@marklearst.com
I built diabetic-utils because I believe in the power of data-driven diabetes management. As someone who's lived with diabetes, I know how hard it can be to make sense of the numbers.
That's why I've poured my heart into creating a library that's both accurate and easy to use. Whether you're building an app, working on research, or just trying to understand your own data, I hope diabetic-utils can help.
Let's work together to make diabetes management better, one data point at a time. π©Έ
Built with β€οΈ by the diabetes community, for the diabetes community.
