chore(knip): drop fourteen orphaned types + scope contracts ignore-block

Every `z.infer<>` source-of-truth type the residual flagged had no
external consumer:
  - `MetricSource`, `AIRecommendation`, `AIRecommendationRationale`,
    `AICitation`, `AIWarning`, `StoryboardAnnotation` in
    `src/lib/ai/schema.ts`
  - `InsightFinding` in `src/lib/ai/types.ts`
  - `CoachChatRequest` in `src/lib/ai/coach/types.ts`
  - `ApplyProfileInput` in `src/lib/auth/profile-update.ts`
  - `CreateSideEffectInput`, `ListSideEffectsInput` in
    `src/lib/medications/side-effects/validators.ts`
  - `AnalyticsSlicePayload` in `src/lib/queries/use-analytics-query.ts`
  - `WithingsWebhookAuthOutcome` in `src/lib/withings/webhook-handler.ts`

Plus three stragglers — `ThresholdOverride` (the persisted shape is the
sibling `ThresholdOverridesJson`), `ChartRangePreset` + the now-orphaned
`CHART_RANGE_PRESETS` constant it was derived from (no chart reads the
preset list), and the `WebPushChannelConfig` + `ApnsChannelConfig` +
`ChannelConfig` triplet in `src/lib/notifications/types.ts` (the
dispatcher imports the two real channel configs by name; the empty
records and the union added nothing).

`src/lib/validations/**` keeps its `z.infer<>` types — that's the
external API surface the iOS client matches against. Muted via
`ignoreIssues` so the gate can enforce while the contract surface
stays self-documenting.
This commit is contained in:
Marc-André Bombeck committed 2026-05-21 19:06:22 +02:00
1 parent c032dfb617
commit 0c48ecc39c
11 files changed
+2 -77

No files matched your search

+2 -1
View File
@@ -72,6 +72,7 @@
],
"ignoreExportsUsedInFile": true,
"ignoreIssues": {
"src/components/ui/**": ["exports", "types"]
"src/components/ui/**": ["exports", "types"],
"src/lib/validations/**": ["types"]
}
}
-2
View File
@@ -97,8 +97,6 @@ export const coachChatRequestSchema = z.object({
scope: coachScopeSchema.optional(),
});
export type CoachChatRequest = z.infer<typeof coachChatRequestSchema>;
/**
* SSE event shapes emitted by the streaming endpoint.
*
-14
View File
@@ -67,8 +67,6 @@ export const metricSourceSchema = z.object({
n: z.number().int().nonnegative().optional(),
});
export type MetricSource = z.infer<typeof metricSourceSchema>;
/**
* Set of valid `referenceId` values, derived from
* `MEDICAL_REFERENCES` so the two cannot drift. Validated lazily
@@ -118,10 +116,6 @@ export const aiRecommendationRationaleSchema = z.object({
deviation: z.string().min(1, "rationale.deviation required"),
});
export type AIRecommendationRationale = z.infer<
typeof aiRecommendationRationaleSchema
>;
export const aiRecommendationSchema = z
.object({
/**
@@ -191,8 +185,6 @@ export const aiRecommendationSchema = z
}
});
export type AIRecommendation = z.infer<typeof aiRecommendationSchema>;
export const aiCitationSchema = z.object({
/** Snapshot key being cited. */
type: z.string().min(1, "citation.type required"),
@@ -202,8 +194,6 @@ export const aiCitationSchema = z.object({
summary: z.string().min(1, "citation.summary required"),
});
export type AICitation = z.infer<typeof aiCitationSchema>;
export const aiWarningSchema = z.object({
/** Topic — "blood_pressure", "pulse", etc. */
topic: z.string().min(1, "warning.topic required"),
@@ -213,8 +203,6 @@ export const aiWarningSchema = z.object({
severity: recommendationSeveritySchema.optional(),
});
export type AIWarning = z.infer<typeof aiWarningSchema>;
/**
* v1.4.20 phase B1 — Daily Briefing block.
*
@@ -419,8 +407,6 @@ export const storyboardAnnotationSchema = z.object({
.max(400, "storyboardAnnotation.detail must be <= 400 chars"),
});
export type StoryboardAnnotation = z.infer<typeof storyboardAnnotationSchema>;
export const storyboardAnnotationsSchema = z
.array(storyboardAnnotationSchema)
.max(20, "storyboardAnnotations can hold at most 20 entries");
-1
View File
@@ -99,7 +99,6 @@ export const insightResultSchema = z.object({
});
export type InsightResult = z.infer<typeof insightResultSchema>;
export type InsightFinding = z.infer<typeof insightFindingSchema>;
// ─── Provider Types ────────────────────────────────────────
-7
View File
@@ -42,13 +42,6 @@ export type ThresholdMetric =
| "BONE_MASS"
| "OXYGEN_SATURATION";
export interface ThresholdOverride {
min: number;
max: number;
/** Set true when the value was entered by the user (not the computed default). */
isOverride?: boolean;
}
/**
* Shape persisted in User.thresholdsJson. Any metric missing = use default.
*/
-2
View File
@@ -21,8 +21,6 @@ const extendedProfileSchema = profileSchema.extend({
moodReminderEnabled: z.boolean().optional(),
});
export type ApplyProfileInput = z.infer<typeof extendedProfileSchema>;
export interface ApplyProfileResult {
ok: true;
user: {
-18
View File
@@ -15,21 +15,3 @@
/** Full-size trend-card body height (non-mini chart cards on the dashboard). */
export const CHART_HEIGHT_PX = 240;
/**
* Range-preset options for charts that expose a 7d / 30d / 90d / All
* range selector. Each entry carries the i18n keys for the visible
* label + sr-only title, plus a `points` count used by the underlying
* `HealthChart` query window. `points: 0` denotes "all data".
*
* Re-used by the GLP-1 dashboard tile so its range strip stays in
* lockstep with the rest of the trend strip.
*/
export const CHART_RANGE_PRESETS = [
{ labelKey: "charts.points7Label", titleKey: "charts.points7Title", points: 7 },
{ labelKey: "charts.points30Label", titleKey: "charts.points30Title", points: 30 },
{ labelKey: "charts.points90Label", titleKey: "charts.points90Title", points: 90 },
{ labelKey: "charts.pointsAllLabel", titleKey: "charts.pointsAllTitle", points: 0 },
] as const;
export type ChartRangePreset = (typeof CHART_RANGE_PRESETS)[number];
@@ -73,5 +73,3 @@ export const listSideEffectsSchema = z.object({
limit: z.coerce.number().int().min(1).max(200).optional().default(50),
});
export type CreateSideEffectInput = z.infer<typeof createSideEffectSchema>;
export type ListSideEffectsInput = z.infer<typeof listSideEffectsSchema>;
-16
View File
@@ -71,22 +71,6 @@ export interface NtfyChannelConfig {
authToken?: string;
}
export type WebPushChannelConfig = Record<string, never>;
/**
* APNs channel config is empty by design — the per-device APNs token +
* environment live on the `Device` model, not on the channel row. The
* channel row exists so per-event preference toggles in
* `NotificationPreference` apply uniformly across all four channels.
*/
export type ApnsChannelConfig = Record<string, never>;
export type ChannelConfig =
| TelegramChannelConfig
| NtfyChannelConfig
| WebPushChannelConfig
| ApnsChannelConfig;
// ── Notification payload ──
export interface NotificationPayload {
-10
View File
@@ -5,7 +5,6 @@ import { useQuery, type UseQueryResult } from "@tanstack/react-query";
import { useAuth } from "@/hooks/use-auth";
import { queryKeys } from "@/lib/query-keys";
import type { DataSummary } from "@/lib/analytics/trends";
import type { SubPageAnalyticsData } from "@/types/analytics";
/**
* v1.4.33 IW2 — single TanStack-Query wrapper for `/api/analytics`.
@@ -39,15 +38,6 @@ import type { SubPageAnalyticsData } from "@/types/analytics";
* stubbed on the slim path per the IW1 follow-up.
*/
/**
* Strict structural subset shared between the slim slice and the thick
* default payload. Consumers that only read `summaries` should type
* the hook return as `AnalyticsSlicePayload` (or import
* `SubPageAnalyticsData` directly — same shape, kept aliased for
* call-site clarity).
*/
export type AnalyticsSlicePayload = SubPageAnalyticsData;
/**
* Discriminator for the server-side slice. `undefined` (the default)
* routes onto the thick payload; `"summaries"` routes onto IW1's
-4
View File
@@ -43,10 +43,6 @@ const WITHINGS_SLEEP_APPLI = 44;
const WITHINGS_ACTIVITY_QUEUE = "withings-activity-sync";
const WITHINGS_SLEEP_QUEUE = "withings-sleep-sync";
export type WithingsWebhookAuthOutcome =
| { ok: true }
| { ok: false; reason: "missing_secret" | "mismatch" | "not_configured" };
/**
* Constant-time comparison helper. Returns false unless both inputs have
* the same byte length AND match exactly.