Files
HealthLog/src/lib/query-keys/illness.ts
T
Marc-André Bombeck 1de63f2b5c perf(illness): compute the recovery gap lazily and gate it on signal density
The illness list load triggered the cross-episode correlation fan-out on
every mount, so the page waited on up to 24 per-episode reads before it
painted. Move that work behind an explicit opt-in: the insights endpoint
takes an includeRecoveryGap query param (default off), the list reads the
count breakdown on a single fast query, and the recovery gap is computed
only when the viewer opens the Analyse expansion.

Tighten the typical-gap gate against coincidental data. A gap now counts
only from an episode that cleared a per-episode measurement floor
(MIN_GAP_MEASUREMENT_DAYS), at least MIN_EPISODES_FOR_TYPICAL_GAP such
episodes must qualify, and the median magnitude must reach
MIN_TYPICAL_GAP_MAGNITUDE_DAYS — a 0/+-1-day gap is the coincidental
baseline and is withheld as noise.
2026-06-20 11:58:39 +02:00

48 lines
2.3 KiB
TypeScript

/**
* Query keys — illness / condition journal (v1.18.1): the episode list,
* a single episode, and a single episode-day day-log.
* Part of the centralized factory; aggregated in `./index.ts`.
*/
export const illnessKeys = {
/**
* v1.18.1 — illness-journal surfaces. `illness()` is the root prefix
* every illness write invalidates through (`illnessDependentKeys` in
* `./index.ts`). The episode list, a single episode read, and a per-day
* day-log each get their own slot; any episode / day-log write evicts
* the whole `["illness"]` prefix so the history list and the open sheet
* repaint in lockstep.
*/
illness: () => ["illness"] as const,
illnessEpisodes: (includeResolved: boolean) =>
["illness", "episodes", includeResolved] as const,
illnessEpisode: (id: string) => ["illness", "episode", id] as const,
illnessDayLog: (episodeId: string, date: string) =>
["illness", "day-log", episodeId, date] as const,
/**
* v1.18.3 — the date-less day-log LIST for an episode (full historical
* scroll on the detail timeline + iOS healthlog-iOS#30). Distinct slot from
* the single-day read above; an illness write evicts the whole `["illness"]`
* prefix so both repaint together.
*/
illnessDayLogList: (episodeId: string, sortDir: string) =>
["illness", "day-log-list", episodeId, sortDir] as const,
/**
* v1.18.1 P3 — the per-episode retrospective correlation findings
* (pre-onset scan, nadir, recovery-gap). Server-authoritative; the read
* is gated, so the surface pattern-matches `status` rather than recomputing.
*/
illnessCorrelation: (id: string) => ["illness", "correlation", id] as const,
/**
* v1.18.1 P3 — the cross-episode retrospective summary ("sick N times ·
* typical recovery gap X days") over a trailing window in days.
*
* v1.18.9 — `includeRecoveryGap` is part of the key: the lightweight
* count-only read (list load) and the heavy recovery-gap read (the
* "Analyse" expansion) hit the same endpoint with a different response
* shape, so they MUST cache under distinct keys (same-key + different
* payload silently poisons the cache).
*/
illnessInsights: (windowDays: number, includeRecoveryGap: boolean) =>
["illness", "insights", windowDays, includeRecoveryGap] as const,
};