mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 22:26:13 +00:00
refactor(query-keys): route long-tail surfaces through the factory
The settings / medications / admin / hooks tree still declared
bare-literal `queryKey: [ … ]` arrays at ~70 call sites. Centralise
them through `queryKeys.<entry>()` so mutation-invalidation prefixes
stay symmetrical and a future rename only touches one location.
New factory entries cover per-medication compliance / titration /
cadence / glp1-details / drug-level-chart / intake-list, the
Withings status read, the paginated admin audit-log filter, and the
workouts-recent list opts. Existing entries cover everything else
(passkeys, tokens, settings, telegram, integrations, research-mode,
admin status / users / backups / feedback / host-metrics / app-logs /
ai-quality / assistant-flags, coach-prefs, feature-flags, etc.).
Lockstep updates:
- `eslint-plugins/healthlog/queryKey-factory.js` extends
`GUARDED_DIRECTORIES` to cover `src/components/{settings,
medications,admin}` + `src/hooks`, plus the three guarded files
under `src/app/medications/**` and `src/app/targets/page.tsx`.
- `src/lib/__tests__/query-keys.test.ts` mirrors the guard's
`guardedRoots` and pins the shape of every new factory entry.
Tests stay green: 4 736 unit; the factory walker reports zero
bare-literal `queryKey:` in any newly-guarded surface.
This commit is contained in:
1 parent
6850818271
commit
e2018b2d15
43 files changed
+332
-118
No files matched your search
@@ -100,6 +100,74 @@ describe("queryKeys factory", () => {
|
||||
"dashboard-medication-compliance",
|
||||
);
|
||||
});
|
||||
|
||||
// v1.4.42 W3-QUERYKEY-LONGTAIL — pin the shapes of the new factory
|
||||
// entries so a future rename can't drift the cache layout silently.
|
||||
it("exposes the per-medication sub-keys under the medications prefix", () => {
|
||||
expect(queryKeys.medicationCompliance("med1")).toEqual([
|
||||
"medications",
|
||||
"med1",
|
||||
"compliance",
|
||||
]);
|
||||
expect(queryKeys.medicationTitration("med1")).toEqual([
|
||||
"medications",
|
||||
"med1",
|
||||
"titration",
|
||||
]);
|
||||
expect(queryKeys.medicationCadence("med1")).toEqual([
|
||||
"medications",
|
||||
"med1",
|
||||
"cadence",
|
||||
]);
|
||||
expect(queryKeys.medicationGlp1Details("med1")).toEqual([
|
||||
"medications",
|
||||
"med1",
|
||||
"glp1-details",
|
||||
]);
|
||||
expect(queryKeys.medicationIntakeDrugLevelChart("med1")).toEqual([
|
||||
"medications",
|
||||
"med1",
|
||||
"intake",
|
||||
"drug-level-chart",
|
||||
]);
|
||||
const listKey = queryKeys.medicationIntakeList("med1", {
|
||||
sortBy: "takenAt",
|
||||
sortDir: "desc",
|
||||
limit: 25,
|
||||
offset: 0,
|
||||
status: "completed",
|
||||
});
|
||||
expect(listKey[0]).toBe("medications");
|
||||
expect(listKey[1]).toBe("med1");
|
||||
expect(listKey[2]).toBe("intake");
|
||||
expect(listKey[3]).toBe("list");
|
||||
});
|
||||
|
||||
it("exposes withingsStatus under the withings prefix", () => {
|
||||
expect(queryKeys.withingsStatus()).toEqual(["withings", "status"]);
|
||||
});
|
||||
|
||||
it("packs adminAuditLogFiltered params into a stable filtered shape", () => {
|
||||
const key = queryKeys.adminAuditLogFiltered({
|
||||
filter: "all",
|
||||
page: 1,
|
||||
perPage: 50,
|
||||
actor: "",
|
||||
actionFilter: "",
|
||||
target: "",
|
||||
range: "7d",
|
||||
});
|
||||
expect(key[0]).toBe("admin");
|
||||
expect(key[1]).toBe("audit-log");
|
||||
expect(key[2]).toBe("filtered");
|
||||
expect(key.length).toBe(10);
|
||||
});
|
||||
|
||||
it("packs workoutsRecentList opts under the workouts-recent prefix", () => {
|
||||
const key = queryKeys.workoutsRecentList({ limit: 3 });
|
||||
expect(key[0]).toBe("workouts");
|
||||
expect(key[1]).toBe("recent");
|
||||
});
|
||||
});
|
||||
|
||||
describe("dependent-key bundles", () => {
|
||||
@@ -195,11 +263,19 @@ describe("queryKey factory enforcement", () => {
|
||||
join(repoRoot, "src", "app", "page.tsx"),
|
||||
join(repoRoot, "src", "hooks", "use-auth.ts"),
|
||||
// v1.4.41 W-FRONTEND-FACTORY — auth, notifications, and the
|
||||
// about-section migrated to the factory. Future waves extend
|
||||
// this list as they migrate their own surface.
|
||||
// about-section migrated to the factory.
|
||||
join(repoRoot, "src", "app", "auth"),
|
||||
join(repoRoot, "src", "app", "notifications"),
|
||||
join(repoRoot, "src", "components", "settings", "about-section.tsx"),
|
||||
// v1.4.42 W3-QUERYKEY-LONGTAIL — settings / medications /
|
||||
// admin / hooks now route every read through the factory.
|
||||
join(repoRoot, "src", "components", "settings"),
|
||||
join(repoRoot, "src", "components", "medications"),
|
||||
join(repoRoot, "src", "components", "admin"),
|
||||
join(repoRoot, "src", "hooks"),
|
||||
join(repoRoot, "src", "app", "medications", "page.tsx"),
|
||||
join(repoRoot, "src", "app", "medications", "[id]", "history", "page.tsx"),
|
||||
join(repoRoot, "src", "app", "targets", "page.tsx"),
|
||||
];
|
||||
|
||||
function collect(): string[] {
|
||||
|
||||
@@ -73,6 +73,38 @@ export const queryKeys = {
|
||||
medicationDetail: (id: string) => ["medications", id] as const,
|
||||
medicationComplianceChart: (medicationId: string) =>
|
||||
["compliance-chart-inline", medicationId] as const,
|
||||
/**
|
||||
* v1.4.42 W3-QUERYKEY-LONGTAIL — per-medication compliance KPI used
|
||||
* by `medication-card` + `glp1-medication-card`. Centralising lets
|
||||
* the intake mutation invalidate every compliance read through the
|
||||
* `["medications"]` prefix instead of one bare-literal at a time.
|
||||
*/
|
||||
medicationCompliance: (medicationId: string) =>
|
||||
["medications", medicationId, "compliance"] as const,
|
||||
medicationTitration: (medicationId: string) =>
|
||||
["medications", medicationId, "titration"] as const,
|
||||
medicationCadence: (medicationId: string) =>
|
||||
["medications", medicationId, "cadence"] as const,
|
||||
medicationGlp1Details: (medicationId: string) =>
|
||||
["medications", medicationId, "glp1-details"] as const,
|
||||
medicationIntakeDrugLevelChart: (medicationId: string) =>
|
||||
["medications", medicationId, "intake", "drug-level-chart"] as const,
|
||||
/**
|
||||
* v1.4.42 — intake-history list with sort / paging / status filter.
|
||||
* The opaque params object lives at index 4 so the
|
||||
* `["medications", id, "intake", "list"]` prefix invalidates every
|
||||
* sort/page combination on an intake mutation.
|
||||
*/
|
||||
medicationIntakeList: (
|
||||
medicationId: string,
|
||||
params: {
|
||||
sortBy: string;
|
||||
sortDir: string;
|
||||
limit: number;
|
||||
offset: number;
|
||||
status: string;
|
||||
},
|
||||
) => ["medications", medicationId, "intake", "list", params] as const,
|
||||
/**
|
||||
* v1.4.40 W-RSC — the dashboard-level compliance chart (aggregate
|
||||
* across every scheduled medication) was a bare `["medication-
|
||||
@@ -143,11 +175,44 @@ export const queryKeys = {
|
||||
["admin", "host-metrics", window] as const,
|
||||
adminAuditActions: () => ["admin", "audit-log", "actions"] as const,
|
||||
adminAuditOverview: () => ["admin", "audit-log", "overview-preview"] as const,
|
||||
/**
|
||||
* v1.4.42 W3-QUERYKEY-LONGTAIL — paginated + filtered audit-log
|
||||
* read used by the login-overview admin section. The `filtered`
|
||||
* discriminator at index 2 keeps the no-arg `adminAuditLog(filter)`
|
||||
* cache slot byte-distinct so its consumers don't collide.
|
||||
*/
|
||||
adminAuditLogFiltered: (params: {
|
||||
filter: string;
|
||||
page: number;
|
||||
perPage: number;
|
||||
actor: string;
|
||||
actionFilter: string;
|
||||
target: string;
|
||||
range: string;
|
||||
}) =>
|
||||
[
|
||||
"admin",
|
||||
"audit-log",
|
||||
"filtered",
|
||||
params.filter,
|
||||
params.page,
|
||||
params.perPage,
|
||||
params.actor,
|
||||
params.actionFilter,
|
||||
params.target,
|
||||
params.range,
|
||||
] as const,
|
||||
|
||||
tokens: () => ["tokens"] as const,
|
||||
telegram: () => ["telegram"] as const,
|
||||
telegramSettings: () => ["telegram", "settings"] as const,
|
||||
withings: () => ["withings"] as const,
|
||||
/**
|
||||
* v1.4.42 W3-QUERYKEY-LONGTAIL — the per-card Withings status read.
|
||||
* Shares the `["withings"]` prefix with `withings()` so a disconnect
|
||||
* mutation invalidates both at once.
|
||||
*/
|
||||
withingsStatus: () => ["withings", "status"] as const,
|
||||
|
||||
// v1.4.32 — workout list + detail caches. `workouts()` is the
|
||||
// root key invalidated by the batch-ingest mutation; the recent +
|
||||
@@ -155,6 +220,18 @@ export const queryKeys = {
|
||||
// detail page share a cache slot with the list page.
|
||||
workouts: () => ["workouts"] as const,
|
||||
workoutsRecent: () => ["workouts", "recent"] as const,
|
||||
/**
|
||||
* v1.4.42 W3-QUERYKEY-LONGTAIL — the `useWorkouts` hook used to
|
||||
* spread `workoutsRecent()` and append an opts object inline. The
|
||||
* factory now owns the full shape so the hook never reaches for a
|
||||
* literal-array wrapper.
|
||||
*/
|
||||
workoutsRecentList: (opts: {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
since?: string;
|
||||
sportType?: string;
|
||||
}) => ["workouts", "recent", opts] as const,
|
||||
workoutDetail: (id: string) => ["workouts", id] as const,
|
||||
|
||||
adminSettings: () => ["admin", "settings"] as const,
|
||||
|
||||
Reference in new issue
Block a user