lint: enforce api-fetch wrapper across src/lib/queries

Add src/lib/queries/ to the api-fetch-required ENFORCED_ROOTS so the
rule matches its own header comment, then route the two flagged query
hooks through apiGet instead of hand-rolling the envelope unwrap.
This commit is contained in:
Marc-André Bombeck committed 2026-06-20 17:10:24 +02:00
1 parent 234c43e4da
commit 320c33b918
3 files changed
+18 -21

No files matched your search

+10 -2
View File
@@ -43,8 +43,16 @@ const EXEMPT_FILES = ["src/lib/api/api-fetch.ts"];
// server-side lib code never self-calls `/api/` routes, and the
// safe-fetch rule already covers its outbound calls. `src/hooks/` is
// client-only by construction (v1.16.4) — its `/api/` reads route
// through the wrapper like every component.
const ENFORCED_ROOTS = ["src/components/", "src/app/", "src/hooks/"];
// through the wrapper like every component. `src/lib/queries/` is the
// one `src/lib/` exception: it holds client-only TanStack-Query hooks
// (every file is `"use client"`), so its `/api/` reads route through the
// wrapper too.
const ENFORCED_ROOTS = [
"src/components/",
"src/app/",
"src/hooks/",
"src/lib/queries/",
];
function toPosix(filename) {
return filename.replace(/\\/g, "/");
+3 -7
View File
@@ -3,6 +3,7 @@
import { useQuery, type UseQueryResult } from "@tanstack/react-query";
import { useAuth } from "@/hooks/use-auth";
import { apiGet } from "@/lib/api/api-fetch";
import { queryKeys } from "@/lib/query-keys";
import type {
AchievementMetrics,
@@ -65,13 +66,8 @@ export interface UseAchievementsQueryOptions {
refetchInterval?: number | false;
}
async function fetchAchievements(): Promise<AchievementsPayload> {
const res = await fetch("/api/gamification/achievements");
if (!res.ok) {
throw new Error(`Failed to load achievements (${res.status})`);
}
const json = (await res.json()) as { data: AchievementsPayload };
return json.data;
function fetchAchievements(): Promise<AchievementsPayload> {
return apiGet<AchievementsPayload>("/api/gamification/achievements");
}
/**
+5 -12
View File
@@ -3,6 +3,7 @@
import { useQuery, type UseQueryResult } from "@tanstack/react-query";
import { useAuth } from "@/hooks/use-auth";
import { apiGet } from "@/lib/api/api-fetch";
import { queryKeys } from "@/lib/query-keys";
import { DASHBOARD_REFETCH_INTERVAL_MS } from "@/lib/queries/refetch-interval";
import type { DataSummary } from "@/lib/analytics/trends";
@@ -114,18 +115,10 @@ export interface UseAnalyticsQueryOptions {
enabled?: boolean;
}
async function fetchAnalytics(
slice: AnalyticsSlice,
): Promise<AnalyticsRawPayload> {
const res =
slice === "summaries"
? await fetch("/api/analytics?slice=summaries")
: await fetch("/api/analytics");
if (!res.ok) {
throw new Error(`Failed to load analytics (${res.status})`);
}
const json = (await res.json()) as { data: AnalyticsRawPayload };
return json.data;
function fetchAnalytics(slice: AnalyticsSlice): Promise<AnalyticsRawPayload> {
return apiGet<AnalyticsRawPayload>(
slice === "summaries" ? "/api/analytics?slice=summaries" : "/api/analytics",
);
}
/**