mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
style: apply prettier 3.9.1 formatting
Mechanical reflow from the prettier 3.9.1 bump; no behaviour change.
This commit is contained in:
1 parent
6329540ade
commit
0f2b548138
90 files changed
+123
-406
No files matched your search
@@ -36,12 +36,8 @@ arrays:
|
||||
|
||||
```json
|
||||
{
|
||||
"measurements": [
|
||||
/* … */
|
||||
],
|
||||
"moodEntries": [
|
||||
/* … */
|
||||
]
|
||||
"measurements": [/* … */],
|
||||
"moodEntries": [/* … */]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -123,10 +123,7 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
* legacy / unmapped code. Secret-free by construction.
|
||||
*/
|
||||
type TestFailureCode =
|
||||
| "credentials"
|
||||
| "rate_limited"
|
||||
| "server_error"
|
||||
| "unreachable";
|
||||
"credentials" | "rate_limited" | "server_error" | "unreachable";
|
||||
|
||||
/**
|
||||
* Map an upstream provider failure to a human-readable, secret-free
|
||||
|
||||
@@ -53,10 +53,7 @@ const bodySchema = z.object({
|
||||
});
|
||||
|
||||
export type CheckUserBranch =
|
||||
| "not_found"
|
||||
| "passkey_only"
|
||||
| "email_fallback"
|
||||
| "exists";
|
||||
"not_found" | "passkey_only" | "email_fallback" | "exists";
|
||||
|
||||
export const POST = apiHandler(async (request: NextRequest) => {
|
||||
// v1.4.43 W13 M-4 — tighter shared bucket on trust-chain misconfig.
|
||||
|
||||
@@ -175,8 +175,7 @@ describe("GET /api/dashboard/summary", () => {
|
||||
// The previous shape emitted "Gewicht" / "kg" / "Schritte" verbatim,
|
||||
// which injected DE into an English iOS UI.
|
||||
const weightCard = body.data.metrics.find((m) => m.id === "weight") as
|
||||
| { titleKey: string; unitKey: string }
|
||||
| undefined;
|
||||
{ titleKey: string; unitKey: string } | undefined;
|
||||
expect(weightCard?.titleKey).toBe("dashboard.metric.title.weight");
|
||||
expect(weightCard?.unitKey).toBe("dashboard.metric.unit.weight");
|
||||
expect(
|
||||
|
||||
@@ -226,9 +226,10 @@ async function buildComparisonSnapshotForUser(
|
||||
orderBy: { measuredAt: "asc" },
|
||||
select: { measuredAt: true, value: true },
|
||||
});
|
||||
dataPoints = measurements.map(
|
||||
(m): DataPoint => ({ date: m.measuredAt, value: m.value }),
|
||||
);
|
||||
dataPoints = measurements.map((m): DataPoint => ({
|
||||
date: m.measuredAt,
|
||||
value: m.value,
|
||||
}));
|
||||
}
|
||||
const summary = summarize(dataPoints);
|
||||
const baselineAvg =
|
||||
|
||||
@@ -542,8 +542,7 @@ async function postBatch(request: NextRequest): Promise<Response> {
|
||||
unit: p.row.unit as string,
|
||||
measuredAt: p.row.measuredAt as Date,
|
||||
externalSourceVersion: p.row.externalSourceVersion as
|
||||
| string
|
||||
| null,
|
||||
string | null,
|
||||
deviceType: p.row.deviceType as Prepared["row"]["deviceType"],
|
||||
sleepStage: p.row.sleepStage as Prepared["row"]["sleepStage"],
|
||||
},
|
||||
|
||||
+2
-4
@@ -205,8 +205,7 @@ export default function DashboardPage() {
|
||||
bpInTargetCount90: snap.extras?.bpInTargetCount90 ?? null,
|
||||
bpInTargetSpanDays90: snap.extras?.bpInTargetSpanDays90 ?? null,
|
||||
glucoseByContext: snap.extras?.glucoseByContext as
|
||||
| Record<string, DataSummary>
|
||||
| undefined,
|
||||
Record<string, DataSummary> | undefined,
|
||||
};
|
||||
}
|
||||
// v1.4.39.3 — the merge moved to `mergeSlimAndThickAnalytics` so
|
||||
@@ -235,8 +234,7 @@ export default function DashboardPage() {
|
||||
bpInTargetCount90: merged.bpInTargetCount90,
|
||||
bpInTargetSpanDays90: merged.bpInTargetSpanDays90,
|
||||
glucoseByContext: merged.glucoseByContext as
|
||||
| Record<string, DataSummary>
|
||||
| undefined,
|
||||
Record<string, DataSummary> | undefined,
|
||||
};
|
||||
}, [
|
||||
snapshotEnabled,
|
||||
|
||||
@@ -279,11 +279,7 @@ export function useAuthActionLabels(): Record<string, string> {
|
||||
* mirroring the convention established by `useAuthActionLabels`.
|
||||
*/
|
||||
export type AuthProvider =
|
||||
| "password"
|
||||
| "passkey"
|
||||
| "api_token"
|
||||
| "withings"
|
||||
| "unknown";
|
||||
"password" | "passkey" | "api_token" | "withings" | "unknown";
|
||||
|
||||
export function providerForAction(action: string): AuthProvider {
|
||||
if (
|
||||
|
||||
@@ -494,8 +494,7 @@ export function resolveVerticalMarkerPositions(
|
||||
|
||||
export function resolveAnnotationPositions(
|
||||
annotations:
|
||||
| Array<{ date: string; label: string; color: string }>
|
||||
| undefined,
|
||||
Array<{ date: string; label: string; color: string }> | undefined,
|
||||
chartData: Array<{ timestamp: number }> | undefined,
|
||||
): ResolvedAnnotation[] {
|
||||
if (!annotations || !chartData || chartData.length === 0) return [];
|
||||
@@ -2021,8 +2020,7 @@ export function HealthChart({
|
||||
// current ↔ compare values come from the SAME
|
||||
// payload object.
|
||||
const hoverPoint = payload[0]?.payload as
|
||||
| ChartDataPoint
|
||||
| undefined;
|
||||
ChartDataPoint | undefined;
|
||||
for (const item of payload) {
|
||||
if (typeof item.value !== "number") continue;
|
||||
const dataKey = String(item.dataKey ?? "");
|
||||
@@ -2047,8 +2045,7 @@ export function HealthChart({
|
||||
const compareValue =
|
||||
effectiveCompareBaseline !== "none" && hoverPoint
|
||||
? (hoverPoint[`${dataKey}_compare`] as
|
||||
| number
|
||||
| undefined)
|
||||
number | undefined)
|
||||
: undefined;
|
||||
if (
|
||||
effectiveCompareBaseline !== "none" &&
|
||||
|
||||
@@ -840,8 +840,7 @@ export function MoodChart({
|
||||
const dateLabel = ts ? tzFmt.date(new Date(ts)) : "";
|
||||
const rows: RichTooltipRow[] = [];
|
||||
const hoverPoint = payload[0]?.payload as
|
||||
| ChartDataPoint
|
||||
| undefined;
|
||||
ChartDataPoint | undefined;
|
||||
for (const item of payload) {
|
||||
if (typeof item.value !== "number") continue;
|
||||
const dataKey = String(item.dataKey ?? "");
|
||||
|
||||
@@ -19,14 +19,7 @@ import { cn } from "@/lib/utils";
|
||||
*/
|
||||
|
||||
export type CyclePhaseCrosstabDisplay =
|
||||
| "hours"
|
||||
| "steps"
|
||||
| "bpm"
|
||||
| "ms"
|
||||
| "kg"
|
||||
| "celsius"
|
||||
| "glucose"
|
||||
| "mood";
|
||||
"hours" | "steps" | "bpm" | "ms" | "kg" | "celsius" | "glucose" | "mood";
|
||||
|
||||
export type CyclePhaseCrosstabConfidence = "low" | "medium" | "high";
|
||||
|
||||
|
||||
@@ -10,17 +10,10 @@ export type CyclePhase = "MENSTRUAL" | "FOLLICULAR" | "OVULATORY" | "LUTEAL";
|
||||
export type FlowLevel = "NONE" | "SPOTTING" | "LIGHT" | "MEDIUM" | "HEAVY";
|
||||
|
||||
export type OvulationTest =
|
||||
| "NEGATIVE"
|
||||
| "POSITIVE_LH_SURGE"
|
||||
| "ESTROGEN_SURGE"
|
||||
| "INDETERMINATE";
|
||||
"NEGATIVE" | "POSITIVE_LH_SURGE" | "ESTROGEN_SURGE" | "INDETERMINATE";
|
||||
|
||||
export type CervicalMucus =
|
||||
| "DRY"
|
||||
| "STICKY"
|
||||
| "CREAMY"
|
||||
| "WATERY"
|
||||
| "EGG_WHITE";
|
||||
"DRY" | "STICKY" | "CREAMY" | "WATERY" | "EGG_WHITE";
|
||||
|
||||
export type CycleGoal =
|
||||
| "GENERAL_HEALTH"
|
||||
|
||||
@@ -27,10 +27,7 @@ import { MedicationIntakeQuickAdd } from "@/components/dashboard/medication-inta
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
|
||||
export type QuickEntryDialog =
|
||||
| "measurement"
|
||||
| "mood"
|
||||
| "medicationIntake"
|
||||
| null;
|
||||
"measurement" | "mood" | "medicationIntake" | null;
|
||||
|
||||
/**
|
||||
* v1.11.3 F3 — guard the quick-entry sheets against an accidental
|
||||
|
||||
@@ -13,10 +13,7 @@ export type IllnessType =
|
||||
| "OTHER";
|
||||
|
||||
export type IllnessLifecycle =
|
||||
| "ACUTE"
|
||||
| "CHRONIC_ONGOING"
|
||||
| "RECURRING"
|
||||
| "FLARE";
|
||||
"ACUTE" | "CHRONIC_ONGOING" | "RECURRING" | "FLARE";
|
||||
|
||||
export interface IllnessSymptomSelection {
|
||||
key: string;
|
||||
|
||||
@@ -36,11 +36,7 @@
|
||||
|
||||
/** Lifecycle of one answered question's adopt offer. */
|
||||
export type GuidedAdoption =
|
||||
| "pending"
|
||||
| "adopted"
|
||||
| "duplicate"
|
||||
| "declined"
|
||||
| "failed";
|
||||
"pending" | "adopted" | "duplicate" | "declined" | "failed";
|
||||
|
||||
export interface GuidedOutcome {
|
||||
/** Index of the question in the session snapshot (progress label). */
|
||||
|
||||
@@ -1046,8 +1046,7 @@ interface ChatBubbleProps {
|
||||
* bubble falls back to that so the card survives reload.
|
||||
*/
|
||||
suggestedAction?:
|
||||
| import("@/lib/ai/coach/suggest-action").CoachSuggestedAction
|
||||
| null;
|
||||
import("@/lib/ai/coach/suggest-action").CoachSuggestedAction | null;
|
||||
providerType?: string | null;
|
||||
inProgress?: boolean;
|
||||
errorCode?: string | null;
|
||||
|
||||
@@ -72,8 +72,7 @@ export function GlucoseClinicalPanel() {
|
||||
if (!clinical) return null;
|
||||
|
||||
const byContext = query.data?.glucoseByContext as
|
||||
| Record<string, DataSummary>
|
||||
| undefined;
|
||||
Record<string, DataSummary> | undefined;
|
||||
|
||||
const formatGlucoseValue = (mgdl: number): string => {
|
||||
const v = convertGlucose(mgdl, glucoseUnit);
|
||||
|
||||
@@ -47,11 +47,7 @@ export type HealthScoreBand = "green" | "yellow" | "red";
|
||||
* client doesn't pull a heavy analytics import for one string union).
|
||||
*/
|
||||
export type HealthScoreComponentSource =
|
||||
| "manual"
|
||||
| "withings"
|
||||
| "appleHealth"
|
||||
| "mixed"
|
||||
| "none";
|
||||
"manual" | "withings" | "appleHealth" | "mixed" | "none";
|
||||
|
||||
export interface HealthScoreCardComponent {
|
||||
value: number | null;
|
||||
|
||||
@@ -23,11 +23,7 @@ import type { DailyBriefing as DailyBriefingPayload } from "@/lib/ai/schema";
|
||||
* `insights.derived.composite.READINESS.component.{key}` labels.
|
||||
*/
|
||||
export type ReadinessContributorKey =
|
||||
| "rhr"
|
||||
| "hrv"
|
||||
| "sleep"
|
||||
| "respiratory"
|
||||
| "mood";
|
||||
"rhr" | "hrv" | "sleep" | "respiratory" | "mood";
|
||||
|
||||
/**
|
||||
* Insights redesign hero strip.
|
||||
|
||||
@@ -86,12 +86,7 @@ export interface InsightsTabStripProps {
|
||||
* mounts / no regenerate) falls back to the prior unconditional success.
|
||||
*/
|
||||
regenerateOutcome?:
|
||||
| "fresh"
|
||||
| "empty"
|
||||
| "rate-limited"
|
||||
| "timeout"
|
||||
| "no-provider"
|
||||
| null;
|
||||
"fresh" | "empty" | "rate-limited" | "timeout" | "no-provider" | null;
|
||||
/**
|
||||
* v1.4.27 F19 — analytics + event-driven availability inputs the
|
||||
* gating helper reads. When omitted the strip falls back to its
|
||||
|
||||
@@ -21,13 +21,7 @@ import type { MoodInfluenceConfidence } from "./mood-tag-influence";
|
||||
*/
|
||||
|
||||
export type MoodFactorCrosstabDisplay =
|
||||
| "hours"
|
||||
| "score"
|
||||
| "steps"
|
||||
| "bpm"
|
||||
| "ms"
|
||||
| "kg"
|
||||
| "mmHg";
|
||||
"hours" | "score" | "steps" | "bpm" | "ms" | "kg" | "mmHg";
|
||||
export type MoodFactorCrosstabMode = "sameDay" | "nextDay";
|
||||
|
||||
export interface MoodFactorMetricCrosstabRow {
|
||||
|
||||
@@ -18,10 +18,7 @@ import { useTranslations } from "@/lib/i18n/context";
|
||||
*/
|
||||
|
||||
export type MoodStabilityBand =
|
||||
| "verySteady"
|
||||
| "steady"
|
||||
| "variable"
|
||||
| "veryVariable";
|
||||
"verySteady" | "steady" | "variable" | "veryVariable";
|
||||
|
||||
export interface MoodStabilityData {
|
||||
score: number;
|
||||
|
||||
@@ -118,8 +118,7 @@ export function MoodTimeOfDayChart({
|
||||
labelStyle={{ color: "var(--dracula-fg)" }}
|
||||
formatter={(_value, _name, item) => {
|
||||
const payload = item?.payload as
|
||||
| { avgScore: number | null; count: number }
|
||||
| undefined;
|
||||
{ avgScore: number | null; count: number } | undefined;
|
||||
if (!payload || payload.avgScore == null) {
|
||||
return [t("insights.mood.weekdayNoData"), ""];
|
||||
}
|
||||
|
||||
@@ -125,8 +125,7 @@ export function MoodWeekdayChart({ weekday }: { weekday: MoodWeekdayRow[] }) {
|
||||
labelStyle={{ color: "var(--dracula-fg)" }}
|
||||
formatter={(_value, _name, item) => {
|
||||
const payload = item?.payload as
|
||||
| { avgScore: number | null; count: number }
|
||||
| undefined;
|
||||
{ avgScore: number | null; count: number } | undefined;
|
||||
if (!payload || payload.avgScore == null) {
|
||||
return [t("insights.mood.weekdayNoData"), ""];
|
||||
}
|
||||
|
||||
@@ -217,8 +217,7 @@ function RationaleCard({
|
||||
}: {
|
||||
rationale: InsightRecommendationRationale;
|
||||
metricSource:
|
||||
| { type: string; timeRange: string; summary: string }
|
||||
| undefined;
|
||||
{ type: string; timeRange: string; summary: string } | undefined;
|
||||
referenceId: string | undefined;
|
||||
confidence: number | undefined;
|
||||
locale: Locale;
|
||||
|
||||
@@ -147,11 +147,7 @@ export function nextAdvisorPollInterval(
|
||||
* means only the transient 503 surface (provider chain unavailable).
|
||||
*/
|
||||
export type AdvisorFetchOutcome =
|
||||
| "fresh"
|
||||
| "empty"
|
||||
| "rate-limited"
|
||||
| "timeout"
|
||||
| "no-provider";
|
||||
"fresh" | "empty" | "rate-limited" | "timeout" | "no-provider";
|
||||
|
||||
interface AdvisorFetchResult {
|
||||
payload: InsightAdvisorPayload | null;
|
||||
|
||||
@@ -188,8 +188,7 @@ export function LabBiomarkerChart({
|
||||
content={(props) => {
|
||||
const active = props.active ?? false;
|
||||
const payload = props.payload as
|
||||
| ReadonlyArray<{ payload?: ChartPoint }>
|
||||
| undefined;
|
||||
ReadonlyArray<{ payload?: ChartPoint }> | undefined;
|
||||
const point = payload?.[0]?.payload;
|
||||
if (!active || !point) {
|
||||
return <RichChartTooltip active={false} rows={[]} />;
|
||||
|
||||
@@ -133,8 +133,7 @@ describe("runRecordIntake — shared C1 failure toast + C2 Undo", () => {
|
||||
const [message, opts] = vi.mocked(toast.success).mock.calls[0];
|
||||
expect(message).toBe("medications.intakeToastTaken:Mounjaro");
|
||||
const action = opts?.action as
|
||||
| { label: string; onClick: (e: never) => void }
|
||||
| undefined;
|
||||
{ label: string; onClick: (e: never) => void } | undefined;
|
||||
expect(action?.label).toBe("medications.intakeUndo");
|
||||
|
||||
// Firing the action calls undoIntake with the just-created event id.
|
||||
|
||||
@@ -98,10 +98,7 @@ export interface TableMedication {
|
||||
}
|
||||
|
||||
export type MedicationTableSortColumn =
|
||||
| "name"
|
||||
| "nextDue"
|
||||
| "compliance"
|
||||
| "stock";
|
||||
"name" | "nextDue" | "compliance" | "stock";
|
||||
|
||||
export interface MedicationTableSort {
|
||||
column: MedicationTableSortColumn;
|
||||
|
||||
@@ -144,9 +144,7 @@ export function ScheduleTimesEditor({
|
||||
...(s.scheduleType
|
||||
? {
|
||||
scheduleType: s.scheduleType as
|
||||
| "SCHEDULED"
|
||||
| "PRN"
|
||||
| "CYCLIC",
|
||||
"SCHEDULED" | "PRN" | "CYCLIC",
|
||||
}
|
||||
: {}),
|
||||
...(typeof s.cyclicOnWeeks === "number"
|
||||
|
||||
@@ -107,9 +107,7 @@ export function GraceRow({
|
||||
...(s.scheduleType
|
||||
? {
|
||||
scheduleType: s.scheduleType as
|
||||
| "SCHEDULED"
|
||||
| "PRN"
|
||||
| "CYCLIC",
|
||||
"SCHEDULED" | "PRN" | "CYCLIC",
|
||||
}
|
||||
: {}),
|
||||
...(typeof s.cyclicOnWeeks === "number"
|
||||
|
||||
@@ -25,11 +25,7 @@ import type { LucideIcon } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export type SettingsInfoTileTone =
|
||||
| "info"
|
||||
| "warning"
|
||||
| "success"
|
||||
| "primary"
|
||||
| "neutral";
|
||||
"info" | "warning" | "success" | "primary" | "neutral";
|
||||
|
||||
const TONE_SURFACE: Record<SettingsInfoTileTone, string> = {
|
||||
info: "border-info/40 bg-info/10",
|
||||
|
||||
@@ -47,8 +47,7 @@ export function resolveInitialTimezone(
|
||||
* provided error text (which has no key) rides `text` verbatim.
|
||||
*/
|
||||
export type StatusMessage =
|
||||
| { key: string; params?: Record<string, string | number> }
|
||||
| { text: string };
|
||||
{ key: string; params?: Record<string, string | number> } | { text: string };
|
||||
|
||||
export function statusText(
|
||||
msg: StatusMessage,
|
||||
|
||||
@@ -45,11 +45,7 @@ import { cn } from "@/lib/utils";
|
||||
* - disconnected → user clicked Disconnect; "Not connected" copy.
|
||||
*/
|
||||
export type IntegrationPillState =
|
||||
| "connected"
|
||||
| "warning"
|
||||
| "error"
|
||||
| "parked"
|
||||
| "disconnected";
|
||||
"connected" | "warning" | "error" | "parked" | "disconnected";
|
||||
|
||||
interface IntegrationStatusPillProps {
|
||||
state: IntegrationPillState;
|
||||
|
||||
@@ -52,8 +52,7 @@ const WITHINGS_OAUTH_ERROR_KEYS: Record<string, string> = {
|
||||
};
|
||||
|
||||
type WithingsOauthOutcome =
|
||||
| { kind: "connected" }
|
||||
| { kind: "error"; reason: string };
|
||||
{ kind: "connected" } | { kind: "error"; reason: string };
|
||||
|
||||
/**
|
||||
* The OAuth providers whose callbacks redirect back to the settings page with a
|
||||
|
||||
@@ -23,12 +23,7 @@ import { useTranslations } from "@/lib/i18n/context";
|
||||
export const INTEGRATION_DOCS_BASE = "https://docs.healthlog.dev/integrations";
|
||||
|
||||
export type IntegrationDocsProvider =
|
||||
| "whoop"
|
||||
| "withings"
|
||||
| "fitbit"
|
||||
| "polar"
|
||||
| "oura"
|
||||
| "nightscout";
|
||||
"whoop" | "withings" | "fitbit" | "polar" | "oura" | "nightscout";
|
||||
|
||||
export function integrationDocsHref(provider: IntegrationDocsProvider): string {
|
||||
return `${INTEGRATION_DOCS_BASE}/${provider}`;
|
||||
|
||||
@@ -19,18 +19,9 @@ import { queryKeys } from "@/lib/query-keys";
|
||||
// IntegrationStatusPill now owns state + last-sync presentation, and
|
||||
// the actionable error message is shown inline above the action row.
|
||||
export type IntegrationKey =
|
||||
| "withings"
|
||||
| "whoop"
|
||||
| "fitbit"
|
||||
| "moodlog"
|
||||
| "polar"
|
||||
| "oura";
|
||||
"withings" | "whoop" | "fitbit" | "moodlog" | "polar" | "oura";
|
||||
export type IntegrationState =
|
||||
| "connected"
|
||||
| "error_transient"
|
||||
| "error_reauth"
|
||||
| "disconnected"
|
||||
| "parked";
|
||||
"connected" | "error_transient" | "error_reauth" | "disconnected" | "parked";
|
||||
|
||||
export interface IntegrationStatusViewModel {
|
||||
integration: IntegrationKey;
|
||||
|
||||
@@ -41,10 +41,7 @@ import { apiGet, apiPost } from "@/lib/api/api-fetch";
|
||||
type ChannelType = "TELEGRAM" | "NTFY" | "WEB_PUSH" | "APNS";
|
||||
|
||||
type ChannelState =
|
||||
| "active"
|
||||
| "auto_disabled"
|
||||
| "manually_disabled"
|
||||
| "sending_paused";
|
||||
"active" | "auto_disabled" | "manually_disabled" | "sending_paused";
|
||||
|
||||
interface ChannelStatus {
|
||||
id: string;
|
||||
|
||||
@@ -43,8 +43,7 @@ export type SuggestionSuppressReason =
|
||||
| "duplicate";
|
||||
|
||||
export type SuggestionDecision =
|
||||
| { surface: true }
|
||||
| { surface: false; reason: SuggestionSuppressReason };
|
||||
{ surface: true } | { surface: false; reason: SuggestionSuppressReason };
|
||||
|
||||
/**
|
||||
* Pure pref-level decision (no DB). Applies opt-out, the explicit stop
|
||||
|
||||
@@ -337,8 +337,7 @@ async function getMedicationCompliance(
|
||||
const compliance = pickSection(snapshot.sections, "compliance");
|
||||
// GLP-1 context rides the `weeklyContext` block.
|
||||
const weeklyContext = pickSection(snapshot.sections, "weeklyContext") as
|
||||
| { glp1?: unknown }
|
||||
| undefined;
|
||||
{ glp1?: unknown } | undefined;
|
||||
const glp1 = weeklyContext?.glp1;
|
||||
if (compliance === undefined && glp1 === undefined) {
|
||||
return { present: false, reason: "no_data" };
|
||||
@@ -377,8 +376,7 @@ async function getLabs(
|
||||
scopeFor([], fallbackWindow, fallbackWindow, sharedScope),
|
||||
);
|
||||
const labs = pickSection(snapshot.sections, "labs") as
|
||||
| { recent?: Array<{ name?: string; analyte?: string }> }
|
||||
| undefined;
|
||||
{ recent?: Array<{ name?: string; analyte?: string }> } | undefined;
|
||||
if (labs === undefined) return { present: false, reason: "no_data" };
|
||||
|
||||
const analyte = parsed.data.analyte?.trim().toLowerCase();
|
||||
|
||||
@@ -202,8 +202,7 @@ export async function requestDeviceCode(): Promise<DeviceCodeStart> {
|
||||
}
|
||||
|
||||
export type DevicePollResult =
|
||||
| { status: "pending" }
|
||||
| { status: "connected"; creds: CodexCreds };
|
||||
{ status: "pending" } | { status: "connected"; creds: CodexCreds };
|
||||
|
||||
/**
|
||||
* One poll attempt against the device-auth token endpoint. Returns
|
||||
|
||||
@@ -31,11 +31,7 @@
|
||||
export type MedicalReferenceOrg = "AHA" | "ESH" | "ESC" | "WHO" | "DGE" | "ADA";
|
||||
|
||||
export type MedicalReferenceMetric =
|
||||
| "bp"
|
||||
| "weight"
|
||||
| "pulse"
|
||||
| "mood"
|
||||
| "medication";
|
||||
"bp" | "weight" | "pulse" | "mood" | "medication";
|
||||
|
||||
export interface MedicalReference {
|
||||
/** Stable slug — used as `recommendation.referenceId`. */
|
||||
|
||||
+11
-19
@@ -103,11 +103,7 @@ export type InsightResult = z.infer<typeof insightResultSchema>;
|
||||
// ─── Provider Types ────────────────────────────────────────
|
||||
|
||||
export type ProviderType =
|
||||
| "codex"
|
||||
| "admin-key"
|
||||
| "anthropic"
|
||||
| "local"
|
||||
| "none";
|
||||
"codex" | "admin-key" | "anthropic" | "local" | "none";
|
||||
|
||||
export interface AIProvider {
|
||||
type: ProviderType;
|
||||
@@ -368,20 +364,16 @@ export function singleUserTurn(p: {
|
||||
? p.user
|
||||
: [
|
||||
{ type: "text", text: p.user },
|
||||
...images.map(
|
||||
(img): AiImagePart => ({
|
||||
type: "image",
|
||||
mediaType: img.mediaType,
|
||||
dataBase64: img.dataBase64,
|
||||
}),
|
||||
),
|
||||
...documents.map(
|
||||
(doc): AiDocPart => ({
|
||||
type: "document",
|
||||
mediaType: doc.mediaType,
|
||||
dataBase64: doc.dataBase64,
|
||||
}),
|
||||
),
|
||||
...images.map((img): AiImagePart => ({
|
||||
type: "image",
|
||||
mediaType: img.mediaType,
|
||||
dataBase64: img.dataBase64,
|
||||
})),
|
||||
...documents.map((doc): AiDocPart => ({
|
||||
type: "document",
|
||||
mediaType: doc.mediaType,
|
||||
dataBase64: doc.dataBase64,
|
||||
})),
|
||||
];
|
||||
return {
|
||||
system: p.system,
|
||||
|
||||
@@ -77,11 +77,7 @@ export interface IntakeEvent {
|
||||
}
|
||||
|
||||
export type IntakeTimingClass =
|
||||
| "early"
|
||||
| "on_time"
|
||||
| "late"
|
||||
| "very_late"
|
||||
| "missed";
|
||||
"early" | "on_time" | "late" | "very_late" | "missed";
|
||||
|
||||
export interface ComplianceResult {
|
||||
totalExpected: number;
|
||||
|
||||
@@ -54,8 +54,7 @@ export interface AnalyticsThickLike extends AnalyticsSlimLike {
|
||||
export interface MergedDashboardAnalytics {
|
||||
summaries: Record<string, DataSummary>;
|
||||
lastSeenByType?:
|
||||
| Record<string, { lastSeenAt: string; daysAgo: number } | null>
|
||||
| undefined;
|
||||
Record<string, { lastSeenAt: string; daysAgo: number } | null> | undefined;
|
||||
bpInTargetPct: number | null;
|
||||
bpInTargetPct7d: number | null;
|
||||
bpInTargetPct30d: number | null;
|
||||
@@ -98,13 +97,13 @@ export function mergeSlimAndThickAnalytics(
|
||||
|
||||
const slimFreshnessHasContent = hasContent(slim?.lastSeenByType ?? null);
|
||||
const lastSeenByType:
|
||||
| Record<string, { lastSeenAt: string; daysAgo: number } | null>
|
||||
| undefined = slimFreshnessHasContent
|
||||
? (slim!.lastSeenByType as Record<
|
||||
string,
|
||||
{ lastSeenAt: string; daysAgo: number } | null
|
||||
>)
|
||||
: (thick?.lastSeenByType ?? undefined);
|
||||
Record<string, { lastSeenAt: string; daysAgo: number } | null> | undefined =
|
||||
slimFreshnessHasContent
|
||||
? (slim!.lastSeenByType as Record<
|
||||
string,
|
||||
{ lastSeenAt: string; daysAgo: number } | null
|
||||
>)
|
||||
: (thick?.lastSeenByType ?? undefined);
|
||||
|
||||
return {
|
||||
summaries,
|
||||
|
||||
@@ -212,8 +212,7 @@ export function pickCanonicalSourceRows<T extends SourcePickerRow>(
|
||||
>();
|
||||
for (const row of pickedRows) {
|
||||
const typeKey = (row.type ?? "__default__") as
|
||||
| MeasurementType
|
||||
| "__default__";
|
||||
MeasurementType | "__default__";
|
||||
const set = presentByType.get(typeKey) ?? new Set<DeviceType>();
|
||||
set.add(normalizeDeviceType(row.deviceType));
|
||||
presentByType.set(typeKey, set);
|
||||
@@ -248,8 +247,7 @@ export function pickCanonicalSourceRows<T extends SourcePickerRow>(
|
||||
|
||||
for (const row of pickedRows) {
|
||||
const typeKey = (row.type ?? "__default__") as
|
||||
| MeasurementType
|
||||
| "__default__";
|
||||
MeasurementType | "__default__";
|
||||
const winner = winningDeviceTypeByRowType.get(typeKey);
|
||||
if (winner === "__fallback__") {
|
||||
// Per-type fallback: keep every row of this type.
|
||||
|
||||
@@ -88,9 +88,7 @@ vi.mock("@/lib/db", () => ({
|
||||
let count = 0;
|
||||
for (const row of dbState.apiTokens) {
|
||||
const tokenHashFilter = where.tokenHash as
|
||||
| string
|
||||
| { in: string[] }
|
||||
| undefined;
|
||||
string | { in: string[] } | undefined;
|
||||
const matches =
|
||||
!tokenHashFilter ||
|
||||
(typeof tokenHashFilter === "string"
|
||||
|
||||
@@ -79,13 +79,7 @@ const authenticationResponseSchema = z
|
||||
.loose();
|
||||
|
||||
type Transport =
|
||||
| "ble"
|
||||
| "cable"
|
||||
| "hybrid"
|
||||
| "internal"
|
||||
| "nfc"
|
||||
| "smart-card"
|
||||
| "usb";
|
||||
"ble" | "cable" | "hybrid" | "internal" | "nfc" | "smart-card" | "usb";
|
||||
|
||||
const CHALLENGE_TTL_MS = 5 * 60 * 1000;
|
||||
|
||||
|
||||
@@ -73,13 +73,7 @@ const registrationResponseSchema = z
|
||||
.loose();
|
||||
|
||||
type Transport =
|
||||
| "ble"
|
||||
| "cable"
|
||||
| "hybrid"
|
||||
| "internal"
|
||||
| "nfc"
|
||||
| "smart-card"
|
||||
| "usb";
|
||||
"ble" | "cable" | "hybrid" | "internal" | "nfc" | "smart-card" | "usb";
|
||||
|
||||
// The relying-party config (`rpName` / `getRpId` / `getExpectedOrigin`) is
|
||||
// shared with the second-factor security-key path via `@/lib/auth/webauthn-rp`
|
||||
|
||||
@@ -83,10 +83,7 @@ export async function issueAccessAndRefresh(
|
||||
}
|
||||
|
||||
export type RotationFailureReason =
|
||||
| "not_found"
|
||||
| "expired"
|
||||
| "already_used"
|
||||
| "revoked";
|
||||
"not_found" | "expired" | "already_used" | "revoked";
|
||||
|
||||
export type RotationResult =
|
||||
| { ok: true; bundle: IssuedRefreshBundle }
|
||||
|
||||
@@ -29,8 +29,7 @@ import type { ConsentReceipt } from "@/lib/consent/receipts";
|
||||
|
||||
/** Outcome of an idempotent web-grant call. */
|
||||
export type WebConsentGrantResult =
|
||||
| { minted: true; receipt: ConsentReceipt }
|
||||
| { minted: false };
|
||||
{ minted: true; receipt: ConsentReceipt } | { minted: false };
|
||||
|
||||
/**
|
||||
* Ensure the web user has an active `ai_full` consent receipt, minting one
|
||||
|
||||
@@ -70,14 +70,7 @@ export const PHASE_ORDINAL: Record<CyclePhase, number> = {
|
||||
|
||||
/** Display-unit hint the client formatter branches on. */
|
||||
export type PhaseCrosstabDisplay =
|
||||
| "hours"
|
||||
| "steps"
|
||||
| "bpm"
|
||||
| "ms"
|
||||
| "kg"
|
||||
| "celsius"
|
||||
| "glucose"
|
||||
| "mood";
|
||||
"hours" | "steps" | "bpm" | "ms" | "kg" | "celsius" | "glucose" | "mood";
|
||||
|
||||
/**
|
||||
* Synthetic non-MeasurementType channel key for the MOOD outcome. Mood lives in
|
||||
|
||||
+3
-13
@@ -22,17 +22,10 @@
|
||||
export type FlowLevel = "NONE" | "SPOTTING" | "LIGHT" | "MEDIUM" | "HEAVY";
|
||||
|
||||
export type OvulationTest =
|
||||
| "NEGATIVE"
|
||||
| "POSITIVE_LH_SURGE"
|
||||
| "ESTROGEN_SURGE"
|
||||
| "INDETERMINATE";
|
||||
"NEGATIVE" | "POSITIVE_LH_SURGE" | "ESTROGEN_SURGE" | "INDETERMINATE";
|
||||
|
||||
export type CervicalMucus =
|
||||
| "DRY"
|
||||
| "STICKY"
|
||||
| "CREAMY"
|
||||
| "WATERY"
|
||||
| "EGG_WHITE";
|
||||
"DRY" | "STICKY" | "CREAMY" | "WATERY" | "EGG_WHITE";
|
||||
|
||||
/**
|
||||
* Which symptothermal SECONDARY sign pairs with the temperature primary sign
|
||||
@@ -53,10 +46,7 @@ export type CervixOpening = "CLOSED" | "OPEN";
|
||||
export type CyclePhase = "MENSTRUAL" | "FOLLICULAR" | "OVULATORY" | "LUTEAL";
|
||||
|
||||
export type PredictionMethod =
|
||||
| "CALENDAR"
|
||||
| "SYMPTOTHERMAL"
|
||||
| "TEMPERATURE_TREND"
|
||||
| "BLENDED";
|
||||
"CALENDAR" | "SYMPTOTHERMAL" | "TEMPERATURE_TREND" | "BLENDED";
|
||||
|
||||
export type CycleTrackingGoal =
|
||||
| "GENERAL_HEALTH"
|
||||
|
||||
@@ -478,12 +478,7 @@ export interface FitbitMappedMeasurement {
|
||||
|
||||
/** HealthLog `SleepStage` values a Fitbit sleep stage maps onto. */
|
||||
export type FitbitSleepStage =
|
||||
| "IN_BED"
|
||||
| "AWAKE"
|
||||
| "ASLEEP"
|
||||
| "REM"
|
||||
| "CORE"
|
||||
| "DEEP";
|
||||
"IN_BED" | "AWAKE" | "ASLEEP" | "REM" | "CORE" | "DEEP";
|
||||
|
||||
// ── Weight + body fat (/body/log/{weight,fat}) ─────────────────
|
||||
//
|
||||
|
||||
@@ -53,12 +53,7 @@ export type AchievementMetricKey =
|
||||
* leaked to the DOM)
|
||||
*/
|
||||
export type AchievementCategory =
|
||||
| "medication"
|
||||
| "vitals"
|
||||
| "mood"
|
||||
| "security"
|
||||
| "engagement"
|
||||
| "hidden";
|
||||
"medication" | "vitals" | "mood" | "security" | "engagement" | "hidden";
|
||||
|
||||
export interface AchievementDefinition {
|
||||
id: string;
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
* returns the decoded UTF-8 text on success.
|
||||
*/
|
||||
export type CappedReadResult =
|
||||
| { ok: true; text: string }
|
||||
| { ok: false; reason: "too_large" };
|
||||
{ ok: true; text: string } | { ok: false; reason: "too_large" };
|
||||
|
||||
export async function readBodyCapped(
|
||||
source: { headers: Headers; body: ReadableStream<Uint8Array> | null },
|
||||
|
||||
@@ -60,9 +60,7 @@ function getIdempotencyKey(request: Request | NextRequest): string | null {
|
||||
* - `null` — no live row; the caller may claim + run.
|
||||
*/
|
||||
type CacheLookup =
|
||||
| { kind: "replay"; response: NextResponse }
|
||||
| { kind: "pending" }
|
||||
| null;
|
||||
{ kind: "replay"; response: NextResponse } | { kind: "pending" } | null;
|
||||
|
||||
/**
|
||||
* Look up the state of a (userId, key, method, path) tuple. Distinguishes
|
||||
|
||||
@@ -29,8 +29,7 @@ export function isIllnessEnabled(userId: string): Promise<boolean> {
|
||||
* verbatim — mirrors `CycleGateResult`.
|
||||
*/
|
||||
export type IllnessGateResult =
|
||||
| { enabled: true }
|
||||
| { enabled: false; response: Response };
|
||||
{ enabled: true } | { enabled: false; response: Response };
|
||||
|
||||
/**
|
||||
* Enforce the gate for an `/api/illness/*` route. Returns a 403
|
||||
|
||||
@@ -409,9 +409,10 @@ export async function buildComparisonSnapshotForUser(
|
||||
orderBy: { measuredAt: "asc" },
|
||||
select: { measuredAt: true, value: true },
|
||||
});
|
||||
dataPoints = measurements.map(
|
||||
(m): DataPoint => ({ date: m.measuredAt, value: m.value }),
|
||||
);
|
||||
dataPoints = measurements.map((m): DataPoint => ({
|
||||
date: m.measuredAt,
|
||||
value: m.value,
|
||||
}));
|
||||
}
|
||||
const summary = summarize(dataPoints);
|
||||
const baselineAvg =
|
||||
|
||||
@@ -83,11 +83,7 @@ const DEFAULTS: Required<ChronotypeOptions> = {
|
||||
* surface.
|
||||
*/
|
||||
export type ChronotypeBand =
|
||||
| "extreme_early"
|
||||
| "early"
|
||||
| "intermediate"
|
||||
| "late"
|
||||
| "extreme_late";
|
||||
"extreme_early" | "early" | "intermediate" | "late" | "extreme_late";
|
||||
|
||||
/**
|
||||
* Band a corrected mid-sleep (MSFsc, minutes-of-day) into the MCTQ chronotype
|
||||
|
||||
@@ -112,12 +112,7 @@ export interface ChronotypeDto {
|
||||
msfMinutes: number | null;
|
||||
msfScMinutes: number | null;
|
||||
band:
|
||||
| "extreme_early"
|
||||
| "early"
|
||||
| "intermediate"
|
||||
| "late"
|
||||
| "extreme_late"
|
||||
| null;
|
||||
"extreme_early" | "early" | "intermediate" | "late" | "extreme_late" | null;
|
||||
socialJetlagMinutes: number | null;
|
||||
freeNightsCounted: number;
|
||||
workNightsCounted: number;
|
||||
|
||||
@@ -28,12 +28,7 @@ export type DerivedConfidenceBand = "high" | "medium" | "low" | "draft";
|
||||
* = no data backed the value.
|
||||
*/
|
||||
export type DerivedProvenanceSource =
|
||||
| "DAY"
|
||||
| "WEEK"
|
||||
| "MONTH"
|
||||
| "YEAR"
|
||||
| "live"
|
||||
| "none";
|
||||
"DAY" | "WEEK" | "MONTH" | "YEAR" | "live" | "none";
|
||||
|
||||
/**
|
||||
* Where a derived value's inputs came from. Lets every surface show the
|
||||
|
||||
@@ -281,13 +281,7 @@ export interface AggregatedFeatures {
|
||||
export interface SignalOfDay {
|
||||
/** Briefing `sourceMetric` discriminator the UI pins an icon + route on. */
|
||||
metric:
|
||||
| "bp"
|
||||
| "weight"
|
||||
| "pulse"
|
||||
| "mood"
|
||||
| "sleep"
|
||||
| "resting_hr"
|
||||
| "glucose";
|
||||
"bp" | "weight" | "pulse" | "mood" | "sleep" | "resting_hr" | "glucose";
|
||||
/** Natural-language label (no enum leak into prose). */
|
||||
label: string;
|
||||
/** Unit string when the metric carries one. */
|
||||
|
||||
@@ -1677,10 +1677,7 @@ export const STABILITY_MIN_DAYS = 7;
|
||||
export const STABILITY_SD_FULL_SCALE = 1.5;
|
||||
|
||||
export type StabilityBand =
|
||||
| "verySteady"
|
||||
| "steady"
|
||||
| "variable"
|
||||
| "veryVariable";
|
||||
"verySteady" | "steady" | "variable" | "veryVariable";
|
||||
|
||||
export interface MoodStability {
|
||||
/** 0..100; higher = steadier (lower day-to-day variance). */
|
||||
|
||||
@@ -171,8 +171,7 @@ export interface PeriodNarrativeInsufficient {
|
||||
}
|
||||
|
||||
export type PeriodNarrativeResult =
|
||||
| PeriodNarrativeContext
|
||||
| PeriodNarrativeInsufficient;
|
||||
PeriodNarrativeContext | PeriodNarrativeInsufficient;
|
||||
|
||||
// ── pure helpers ──────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -25,10 +25,7 @@
|
||||
|
||||
/** Outcome buckets shared across the HTTP-status-driven integrations. */
|
||||
export type IntegrationClassification =
|
||||
| "success"
|
||||
| "transient"
|
||||
| "reauth_required"
|
||||
| "persistent";
|
||||
"success" | "transient" | "reauth_required" | "persistent";
|
||||
|
||||
export interface ClassifiedHttpResponse {
|
||||
classification: IntegrationClassification;
|
||||
|
||||
@@ -108,11 +108,7 @@ export function toFailureKind(
|
||||
* trail.
|
||||
*/
|
||||
export type IntegrationState =
|
||||
| "connected"
|
||||
| "error_transient"
|
||||
| "error_reauth"
|
||||
| "disconnected"
|
||||
| "parked";
|
||||
"connected" | "error_transient" | "error_reauth" | "disconnected" | "parked";
|
||||
|
||||
/**
|
||||
* The ladder at which a streak of failures escalates from "user-visible
|
||||
|
||||
@@ -124,11 +124,7 @@ export function resolvePlanMetric(metric: string): ResolvedMetric | null {
|
||||
}
|
||||
|
||||
export type ExperimentVerdict =
|
||||
| "improved"
|
||||
| "worsened"
|
||||
| "no_change"
|
||||
| "changed"
|
||||
| "insufficient";
|
||||
"improved" | "worsened" | "no_change" | "changed" | "insufficient";
|
||||
|
||||
export interface ExperimentOutcomeInput {
|
||||
label: string;
|
||||
|
||||
@@ -76,9 +76,7 @@ export const FERTILE_SOON_LEAD_DAYS = 2;
|
||||
export const PERIOD_CONFIRM_GRACE_DAYS = 3;
|
||||
|
||||
export type CycleReminderEvent =
|
||||
| "CYCLE_PERIOD_SOON"
|
||||
| "CYCLE_PERIOD_CONFIRM"
|
||||
| "CYCLE_FERTILE_SOON";
|
||||
"CYCLE_PERIOD_SOON" | "CYCLE_PERIOD_CONFIRM" | "CYCLE_FERTILE_SOON";
|
||||
|
||||
export interface CycleReminderSummary {
|
||||
candidatesScanned: number;
|
||||
|
||||
@@ -183,11 +183,7 @@ export interface ForceWarmResult {
|
||||
* card warm below.
|
||||
*/
|
||||
comprehensive:
|
||||
| GenerateOutcome["status"]
|
||||
| "timeout"
|
||||
| "fresh"
|
||||
| "backoff"
|
||||
| "capped";
|
||||
GenerateOutcome["status"] | "timeout" | "fresh" | "backoff" | "capped";
|
||||
/** Count of specialised per-status assessments written warm. */
|
||||
assessmentsWarmed: number;
|
||||
/** Count of generic per-HealthKit-metric assessments written warm. */
|
||||
|
||||
@@ -18,10 +18,7 @@ export const OCR_MAX_BYTES = 12 * 1024 * 1024;
|
||||
|
||||
/** The MIME types the extract route accepts (no HEIC in v1). */
|
||||
export type OcrUploadMime =
|
||||
| "image/jpeg"
|
||||
| "image/png"
|
||||
| "image/webp"
|
||||
| "application/pdf";
|
||||
"image/jpeg" | "image/png" | "image/webp" | "application/pdf";
|
||||
|
||||
/** Thrown by `readBoundedBody` when the stream exceeds the byte cap. */
|
||||
export class BodyTooLargeError extends Error {
|
||||
|
||||
@@ -53,10 +53,7 @@ export interface ResolvedClient {
|
||||
* `SafeFetchError.kind` + message + cause) for the `/authorize` annotation.
|
||||
*/
|
||||
export type ClientResolutionReason =
|
||||
| "unknown_client"
|
||||
| "invalid_metadata"
|
||||
| "ssrf_blocked"
|
||||
| "fetch_failed";
|
||||
"unknown_client" | "invalid_metadata" | "ssrf_blocked" | "fetch_failed";
|
||||
|
||||
export type ClientResolution =
|
||||
| { ok: true; client: ResolvedClient }
|
||||
|
||||
@@ -140,8 +140,7 @@ function plainMetricText(rid: string, result: unknown): string {
|
||||
};
|
||||
if (!r?.present) return `No recent ${rid} data is on file.`;
|
||||
const section = (r.data?.section ?? r.data) as
|
||||
| Record<string, unknown>
|
||||
| undefined;
|
||||
Record<string, unknown> | undefined;
|
||||
const agg = section?.aggregate as Record<string, unknown> | undefined;
|
||||
const parts: string[] = [];
|
||||
const mean = num(agg?.mean);
|
||||
|
||||
@@ -49,8 +49,7 @@ function buildPagingFindMany(allRows: PerSampleRow[]) {
|
||||
const or = (and[1] as { OR?: Array<Record<string, unknown>> }).OR;
|
||||
const gt = or?.[0]?.measuredAt as { gt?: Date } | undefined;
|
||||
const tie = or?.[1] as
|
||||
| { measuredAt?: Date; id?: { gt?: string } }
|
||||
| undefined;
|
||||
{ measuredAt?: Date; id?: { gt?: string } } | undefined;
|
||||
afterTime = gt?.gt ? gt.gt.getTime() : null;
|
||||
afterId = tie?.id?.gt ?? null;
|
||||
}
|
||||
|
||||
@@ -38,11 +38,7 @@ import type {
|
||||
* in the pipeline once we observe the production sample volume.
|
||||
*/
|
||||
export type AppleHealthAggregation =
|
||||
| "sum"
|
||||
| "mean"
|
||||
| "latest"
|
||||
| "max"
|
||||
| "median";
|
||||
"sum" | "mean" | "latest" | "max" | "median";
|
||||
|
||||
export interface AppleHealthMapping {
|
||||
/** HealthKit identifier (e.g. `HKQuantityTypeIdentifierBodyMass`). */
|
||||
|
||||
@@ -128,8 +128,7 @@ export function bucketRowsByDay(
|
||||
|
||||
/** Outcome of writing a single per-day bucket. */
|
||||
export type DayWriteOutcome =
|
||||
| { kind: "written"; sourceRowsRemoved: number }
|
||||
| { kind: "skipped-conflict" };
|
||||
{ kind: "written"; sourceRowsRemoved: number } | { kind: "skipped-conflict" };
|
||||
|
||||
/**
|
||||
* Context a per-day write strategy receives. The strategy owns its own
|
||||
|
||||
@@ -69,12 +69,7 @@ const SLEEP_STAGE_NAME_TO_CODEPOINT: Record<string, number> = {
|
||||
|
||||
/** Phase the worker is currently in. */
|
||||
export type ImportJobPhase =
|
||||
| "queued"
|
||||
| "unpacking"
|
||||
| "parsing"
|
||||
| "upserting"
|
||||
| "done"
|
||||
| "failed";
|
||||
"queued" | "unpacking" | "parsing" | "upserting" | "done" | "failed";
|
||||
|
||||
/**
|
||||
* Live snapshot the worker writes to `ImportJob.progress` every
|
||||
|
||||
@@ -33,11 +33,7 @@
|
||||
* UI looks up by id, not by brand name.
|
||||
*/
|
||||
export type Glp1DrugId =
|
||||
| "tirzepatide"
|
||||
| "semaglutide"
|
||||
| "liraglutide"
|
||||
| "dulaglutide"
|
||||
| "exenatide";
|
||||
"tirzepatide" | "semaglutide" | "liraglutide" | "dulaglutide" | "exenatide";
|
||||
|
||||
/**
|
||||
* Route enum — most agonists are subcutaneous; Rybelsus is oral
|
||||
|
||||
@@ -67,12 +67,7 @@ export interface HistoryIntake {
|
||||
}
|
||||
|
||||
export type DoseHistoryStatus =
|
||||
| "taken_on_time"
|
||||
| "taken_late"
|
||||
| "skipped"
|
||||
| "missed"
|
||||
| "upcoming"
|
||||
| "ad_hoc";
|
||||
"taken_on_time" | "taken_late" | "skipped" | "missed" | "upcoming" | "ad_hoc";
|
||||
|
||||
/**
|
||||
* v1.15.20 — the due-context an ad-hoc row carries: the nearest scheduled
|
||||
|
||||
@@ -135,11 +135,7 @@ export type SideEffectSeverity = 1 | 2 | 3 | 4 | 5;
|
||||
|
||||
/** Locale-agnostic semantic labels for the severity ladder. */
|
||||
export type SideEffectSeverityLabel =
|
||||
| "mild"
|
||||
| "moderate"
|
||||
| "significant"
|
||||
| "severe"
|
||||
| "verySevere";
|
||||
"mild" | "moderate" | "significant" | "severe" | "verySevere";
|
||||
|
||||
/**
|
||||
* Maps a 1-5 severity integer to its locale-agnostic semantic label.
|
||||
|
||||
@@ -240,8 +240,7 @@ export async function resolveModuleMap(
|
||||
* return verbatim — mirrors `CycleGateResult`.
|
||||
*/
|
||||
export type ModuleGateResult =
|
||||
| { enabled: true }
|
||||
| { enabled: false; response: Response };
|
||||
{ enabled: true } | { enabled: false; response: Response };
|
||||
|
||||
/**
|
||||
* Enforce the gate for a module-scoped route. Returns a 403
|
||||
|
||||
@@ -22,13 +22,7 @@
|
||||
*/
|
||||
|
||||
export type MoodTagIconGroup =
|
||||
| "emotions"
|
||||
| "activities"
|
||||
| "health"
|
||||
| "food"
|
||||
| "weather"
|
||||
| "places"
|
||||
| "misc";
|
||||
"emotions" | "activities" | "health" | "food" | "weather" | "places" | "misc";
|
||||
|
||||
export interface MoodTagIconCatalogEntry {
|
||||
/** Lucide icon name — the value stored on `mood_tags.icon` / `mood_tag_categories.icon`. */
|
||||
|
||||
@@ -267,9 +267,7 @@ async function fetchCollection<T>(
|
||||
// 204 No Content — a window with no records. Polar returns an empty body.
|
||||
if (res.status === 204) return [];
|
||||
const json = (await res.json().catch(() => null)) as
|
||||
| CollectionResult<T>
|
||||
| Record<string, T[]>
|
||||
| null;
|
||||
CollectionResult<T> | Record<string, T[]> | null;
|
||||
if (!json) return [];
|
||||
const list = (json as Record<string, unknown>)[recordsKey];
|
||||
return Array.isArray(list) ? (list as T[]) : [];
|
||||
|
||||
@@ -458,10 +458,7 @@ export function referenceLabel(metric: ReferenceMetric): string {
|
||||
* (device-derived / baseline-only signals).
|
||||
*/
|
||||
export type ReferencePlacement =
|
||||
| "within"
|
||||
| "slightly-outside"
|
||||
| "outside"
|
||||
| "insufficient";
|
||||
"within" | "slightly-outside" | "outside" | "insufficient";
|
||||
|
||||
/**
|
||||
* Place a value against a metric's population bands. Returns
|
||||
|
||||
@@ -28,11 +28,7 @@
|
||||
|
||||
/** The sync domains the feed serves. */
|
||||
export type SyncDomain =
|
||||
| "measurements"
|
||||
| "mood"
|
||||
| "intakes"
|
||||
| "cycleDays"
|
||||
| "cycles";
|
||||
"measurements" | "mood" | "intakes" | "cycleDays" | "cycles";
|
||||
|
||||
export const SYNC_DOMAINS: readonly SyncDomain[] = [
|
||||
"measurements",
|
||||
|
||||
@@ -43,10 +43,7 @@ import type { TrafficRange } from "@/lib/analytics/value-bands";
|
||||
import { thresholdMetricForContext } from "@/lib/glucose";
|
||||
|
||||
export type GlucoseContextKey =
|
||||
| "FASTING"
|
||||
| "POSTPRANDIAL"
|
||||
| "RANDOM"
|
||||
| "BEDTIME";
|
||||
"FASTING" | "POSTPRANDIAL" | "RANDOM" | "BEDTIME";
|
||||
|
||||
/**
|
||||
* ADA glycemic GOAL bands for people living with diabetes (mg/dL).
|
||||
|
||||
@@ -136,10 +136,7 @@ export function isNearUtc(tz: string, now: Date = new Date()): boolean {
|
||||
}
|
||||
|
||||
export type FormatInUserTzShape =
|
||||
| "iso-with-offset"
|
||||
| "wall-clock"
|
||||
| "datetime"
|
||||
| "date";
|
||||
"iso-with-offset" | "wall-clock" | "datetime" | "date";
|
||||
|
||||
/**
|
||||
* Format an instant in the user's timezone.
|
||||
|
||||
@@ -164,9 +164,7 @@ export interface MedicationStatementFactData {
|
||||
}
|
||||
|
||||
export type FactData =
|
||||
| ConditionFactData
|
||||
| ObservationFactData
|
||||
| MedicationStatementFactData;
|
||||
ConditionFactData | ObservationFactData | MedicationStatementFactData;
|
||||
|
||||
/** The staged-fact DTO the routes return + the review UI consumes. */
|
||||
export interface ExtractedFactDto {
|
||||
|
||||
@@ -53,10 +53,7 @@
|
||||
|
||||
/** Outcome buckets for a Withings response. */
|
||||
export type WithingsClassification =
|
||||
| "success"
|
||||
| "transient"
|
||||
| "reauth_required"
|
||||
| "persistent";
|
||||
"success" | "transient" | "reauth_required" | "persistent";
|
||||
|
||||
export interface ClassifiedResponse {
|
||||
classification: WithingsClassification;
|
||||
|
||||
@@ -165,8 +165,7 @@ describe("GET /api/analytics — chunked per-type reads (Sr-H1)", () => {
|
||||
// which writes the row-count slot under `slim_summaries` rather than
|
||||
// the old `bp_aggregate.row_count`.
|
||||
const analytics = httpEvent!.meta!.analytics as
|
||||
| { slim_summaries?: { row_count?: number } }
|
||||
| undefined;
|
||||
{ slim_summaries?: { row_count?: number } } | undefined;
|
||||
expect(analytics?.slim_summaries?.row_count).toBe(ROW_COUNT);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user