function
<Component />
return
const
interface
{ }
color.primary
TS
() => {}
spacing.xl
dev
tokens
[ ]
code
<T>
type
async
font.sans
JS
TypeScript
export
[React, ...skills]
components
import
border.radius
props
default
?.
tokens
shadow.lg
await
&&
class
<Props>
accessibility
variables
transition
JSX
</>
theme
motion
( )
hooks
state
utils
wcag
responsive
grid
flex
scale
{ml}
Back to work
HEALTH TECH

GlucoseIQ

Headless TypeScript library for CGM and glucose data. Five packages covering 17 clinical metrics, device connectors, FHIR and Open mHealth interop, SVG rendering, React bindings, and seeded test fixtures.

Role
Creator & Maintainer
Timeline
2025 - Present
Technologies
TypeScriptNode.js 24React 19TurborepoSVGFHIRChangesets

Impact

17 metrics

AGP, TIR, GRI, MAGE, CONGA, MODD and more, from the clinical literature

Zero deps

Headless core with no runtime dependencies, held to 100% coverage

1.0.0-next

Published to npm as a release candidate, approaching 1.0

The Problem

Continuous glucose monitors produce a reading every five minutes. Turning that stream into something a person can act on means computing metrics that already exist in the clinical literature, and most teams building diabetes software end up reimplementing them.

  • Time in Range, AGP percentile bands, and variability metrics rewritten per project
  • Unit handling that silently mixes mg/dL and mmol/L in the same calculation
  • Analytics welded to a chart library, so the numbers cannot be used anywhere else
  • Vendor exports from Dexcom, Libre, and Nightscout each parsed by hand
  • No way to test the pipeline without real patient data

Diabetic Utils proved the calculations were worth packaging. GlucoseIQ is what happens when the same work is treated as a product: five packages, a size budget, and total coverage on the core.

It also has a harder constraint. This is health software written by someone who is not a clinician, so the most important thing the library does is refuse to claim more than it can support.

The Solution

GlucoseIQ is a headless analytics core with optional packages layered on top. The core computes and returns data. Nothing in it renders to a screen or reaches for the network.

A Core That Refuses to Overstate

The API is shaped so the caller cannot skip the question of whether the data was good enough to analyze:

function summarize(readings: GlucoseReading[]) {
  const report = analyzeGlucose(readings, { timeZone: 'America/Detroit' })

  // `valid` only means at least one usable reading survived filtering.
  if (!report.valid || report.timeInRange === null) {
    return { status: 'no-data' } as const
  }

  // Whether that data is adequate is a separate question,
  // and it gets a separate field rather than folding into `valid`.
  const { meetsCGMStandard, daysOfData, activePercent } =
    report.dataSufficiency

  if (!meetsCGMStandard) {
    return { status: 'insufficient', daysOfData, activePercent } as const
  }

  return {
    status: 'ok',
    timeInRange: report.timeInRange.inRange.percentage,
    gmi: report.gmi,
  } as const
}

Two separate gates, because they answer different questions. Report blocks are nullable, so a caller cannot read a percentage without acknowledging it may not exist. Adequacy is then its own check: a week of patchy data can produce a perfectly real Time in Range number that should not be presented as a clinical summary.

Metrics From the Literature

Seventeen metric modules, each named for the published measure it implements rather than an invented abstraction:

  • AGP percentile profiles, Time in Range, and time-above and time-below episodes
  • Variability: MAGE, CONGA, MODD, ADRR, J-Index, M-value, GVI and PGS
  • Risk and control indices: GRI, GRADE, IGC, BGI, AUC
  • Meal response, curve shape, and active sensor percentage

Connectors and Interop

Vendor formats normalize into one reading shape, and results can leave in formats other health systems already read:

  • Connectors for Dexcom, Libre, and Nightscout, with declared capabilities per source
  • FHIR and Open mHealth output for systems that speak those standards
  • Mixed-unit-aware APIs normalize declared mg/dL and mmol/L before calculating
  • CSV parsing with an explicit contract rather than best-effort guessing

Rendering Without Owning the DOM

Renderers return SVG strings for AGP charts, Time in Range bars, and trend tiles. The host decides how to embed, sanitize, or deliver that string, so the same call works in a React app, a server-rendered report, or a PDF pipeline.

Data You Can Test With

A dedicated package ships fixed-seed synthetic CGM data and scenario fixtures, so tests, demos, and documentation run on realistic glucose traces without touching anyone's real readings.

Technical Implementation

Five Packages Over One Core

  • @glucoseiq/core for analytics, connectors, interop, and SVG renderers
  • @glucoseiq/react for hooks and headless components, React 18 and 19
  • @glucoseiq/tokens for the five-zone glucose palette, thresholds, and trend glyphs
  • @glucoseiq/testing for fixed-seed synthetic data and scenario fixtures
  • @glucoseiq/cli for analyzing mapped CSV without writing any application code

What the Core Is Not Allowed to Grow Into

The core has no runtime dependencies and no peer dependencies, and both facts are enforced rather than aspirational:

  • Vitest thresholds at 100% for lines, functions, branches, and statements
  • A gzip budget on the reachable core ESM, failing the build when it is exceeded
  • Packed-package consumer tests, so what publishes is what gets tested
  • Release gated on build, lint, type checking, coverage, size, consumer tests, and the docs build

Input Contracts Written Down

The CSV parser states its behavior instead of guessing, which is what makes it safe to point at an unfamiliar export:

  • A custom delimiter must be exactly one UTF-16 code unit; double quote, NUL, carriage return, and line feed are rejected
  • Blank or BOM-only input returns an empty result, and so does a valid header-only file
  • Missing required headers throw; individually invalid rows are skipped
  • Renderer dimensions must be finite positive numbers, and text values are escaped

Documentation as a Test Target

The docs site is built with Fumadocs, and its code samples are type-checked and executed in CI. A snippet that stops compiling fails the build, so the examples cannot drift away from the API they describe.

Scope & Status

GlucoseIQ is for informational, educational, and software development use. It is not medical advice and is not a medical device. That line is in the README, the docs, and this case study for the same reason: a library that computes clinical-looking numbers has an obligation to say what it is not.

The same care applies inside the API, where anything project-defined is labeled as project-defined:

  • glucoseIQScore is non-diagnostic, a project-defined score derived from GRI rather than a published measure

  • buildAGPProfile returns AGP-style bands, a percentile series rather than a standardized complete AGP report

  • valid is not sufficient, which is why data adequacy gets its own field instead of being folded into a boolean

  • Embedding policy stays with the host, because a library returning an SVG string cannot know where it is about to be placed

Packages publish under the 1.0.0-next tag. The analytics are settled and covered; what remains before 1.0 is the public API surface. The prerelease tag is the honest way to say so.

© 2026 Mark Learst.Crafted with precision
privacy
v2026.1.0