mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
fix(insights): keep the server-only rollup readers out of the insights client bundle
The v1.9.0 time-range work imported ANALYTICS_RANGES as a value into the time-range pills and the insights layout-prefs hook from range-delta.ts. That module also pulls the rollup readers and the Postgres client, so the value import dragged pg (dns/fs/net/tls) into the browser bundle and broke the production build with eight "Module not found" errors. Move the client-safe pieces — the range list, its type, the per-range window length, and the period-over-period result shapes — into a new range-shared module that imports nothing server-side. The insights client components import from there; range-delta.ts re-exports the same names so the server route and the OpenAPI registry keep their import paths. The window-composition logic and the rollup/db imports stay server-side. No runtime behaviour change — a pure module-boundary split. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> (cherry picked from commit 75124e76c1e987d8a022fb39d891049340199b38)
This commit is contained in:
1 parent
1c91abcf2a
commit
a87d6b94e4
6 files changed
+80
-48
No files matched your search
@@ -10,7 +10,7 @@ import {
|
||||
type TrendDirectionSentiment,
|
||||
} from "@/lib/insights/trend-sentiment";
|
||||
import type { AnalyticsRangeData } from "@/hooks/use-analytics-range";
|
||||
import type { AnalyticsRange } from "@/lib/analytics/range-delta";
|
||||
import type { AnalyticsRange } from "@/lib/analytics/range-shared";
|
||||
|
||||
/**
|
||||
* v1.9.0 — period-over-period delta caption beside the time-range pills.
|
||||
|
||||
@@ -7,7 +7,7 @@ import { cn } from "@/lib/utils";
|
||||
import {
|
||||
ANALYTICS_RANGES,
|
||||
type AnalyticsRange,
|
||||
} from "@/lib/analytics/range-delta";
|
||||
} from "@/lib/analytics/range-shared";
|
||||
|
||||
/**
|
||||
* v1.9.0 — `7d / 30d / 90d / 1y` segmented control for the Insights metric
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
import type { AnalyticsRange } from "@/lib/analytics/range-delta";
|
||||
import type { AnalyticsRange } from "@/lib/analytics/range-shared";
|
||||
|
||||
/**
|
||||
* v1.9.0 — client reader for the single-metric period-over-period range
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import {
|
||||
ANALYTICS_RANGES,
|
||||
type AnalyticsRange,
|
||||
} from "@/lib/analytics/range-delta";
|
||||
} from "@/lib/analytics/range-shared";
|
||||
|
||||
const RANGE_STORAGE_KEY = "healthlog.insights.range";
|
||||
const DEFAULT_RANGE: AnalyticsRange = "30d";
|
||||
|
||||
@@ -33,53 +33,27 @@ import {
|
||||
readBestGranularityRollups,
|
||||
type RollupBucketRow,
|
||||
} from "@/lib/rollups/measurement-read-wmy";
|
||||
import {
|
||||
ANALYTICS_RANGES,
|
||||
rangeWindowDays,
|
||||
type AnalyticsRange,
|
||||
type RangeDeltaResult,
|
||||
type WindowAggregate,
|
||||
} from "@/lib/analytics/range-shared";
|
||||
|
||||
const DAY_MS = 24 * 60 * 60 * 1000;
|
||||
|
||||
/** The user-selectable ranges. */
|
||||
export const ANALYTICS_RANGES = ["7d", "30d", "90d", "1y"] as const;
|
||||
export type AnalyticsRange = (typeof ANALYTICS_RANGES)[number];
|
||||
|
||||
/** Trailing-window length, in days, for each range. */
|
||||
const RANGE_WINDOW_DAYS: Record<AnalyticsRange, number> = {
|
||||
"7d": 7,
|
||||
"30d": 30,
|
||||
"90d": 90,
|
||||
"1y": 365,
|
||||
// The range constants + result shapes live in the client-safe
|
||||
// `range-shared` module so the insights client bundle never pulls the
|
||||
// server-only rollup readers (and `pg`) through a range import. Re-exported
|
||||
// here so existing server-side callers keep importing from `range-delta`.
|
||||
export {
|
||||
ANALYTICS_RANGES,
|
||||
rangeWindowDays,
|
||||
type AnalyticsRange,
|
||||
type RangeDeltaResult,
|
||||
type WindowAggregate,
|
||||
};
|
||||
|
||||
export function rangeWindowDays(range: AnalyticsRange): number {
|
||||
return RANGE_WINDOW_DAYS[range];
|
||||
}
|
||||
|
||||
/** A composed window aggregate. `mean`/etc. are null when the window is empty. */
|
||||
export interface WindowAggregate {
|
||||
count: number;
|
||||
min: number | null;
|
||||
max: number | null;
|
||||
mean: number | null;
|
||||
sum: number | null;
|
||||
}
|
||||
|
||||
export interface RangeDeltaResult {
|
||||
range: AnalyticsRange;
|
||||
windowDays: number;
|
||||
/** The granularity the underlying rollup read resolved against. */
|
||||
granularity: string;
|
||||
current: WindowAggregate;
|
||||
previous: WindowAggregate;
|
||||
/**
|
||||
* `current.mean - previous.mean`, or null when either window has no mean
|
||||
* (no data in the current or prior window) — never a misleading 0.
|
||||
*/
|
||||
delta: number | null;
|
||||
/**
|
||||
* `delta / previous.mean` as a fraction (0.03 = +3 %), or null when the
|
||||
* prior window has no mean / a zero mean (no divide-by-zero, no misleading
|
||||
* 0 %). The client renders "no prior-period data" in that case.
|
||||
*/
|
||||
deltaPct: number | null;
|
||||
}
|
||||
const DAY_MS = 24 * 60 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Slice the resolved buckets into the current half (`[now-N, now)`) and the
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* v1.9.0 — client-safe range constants and types for the Insights metric
|
||||
* pages.
|
||||
*
|
||||
* The user-selectable range list, its type, the per-range window length, and
|
||||
* the period-over-period result shapes are shared between the client (the
|
||||
* pills, the delta caption, the range hook, the layout-prefs hook) and the
|
||||
* server (`/api/analytics/range` + `range-delta.ts`). This module imports
|
||||
* NOTHING server-side — no rollup readers, no `db`, no Prisma client — so the
|
||||
* client bundle never transitively pulls `pg` (dns/fs/net/tls) through a range
|
||||
* import. The server-only window-composition logic lives in `range-delta.ts`,
|
||||
* which re-exports these names for its own callers.
|
||||
*/
|
||||
|
||||
/** The user-selectable ranges. */
|
||||
export const ANALYTICS_RANGES = ["7d", "30d", "90d", "1y"] as const;
|
||||
export type AnalyticsRange = (typeof ANALYTICS_RANGES)[number];
|
||||
|
||||
/** Trailing-window length, in days, for each range. */
|
||||
const RANGE_WINDOW_DAYS: Record<AnalyticsRange, number> = {
|
||||
"7d": 7,
|
||||
"30d": 30,
|
||||
"90d": 90,
|
||||
"1y": 365,
|
||||
};
|
||||
|
||||
export function rangeWindowDays(range: AnalyticsRange): number {
|
||||
return RANGE_WINDOW_DAYS[range];
|
||||
}
|
||||
|
||||
/** A composed window aggregate. `mean`/etc. are null when the window is empty. */
|
||||
export interface WindowAggregate {
|
||||
count: number;
|
||||
min: number | null;
|
||||
max: number | null;
|
||||
mean: number | null;
|
||||
sum: number | null;
|
||||
}
|
||||
|
||||
export interface RangeDeltaResult {
|
||||
range: AnalyticsRange;
|
||||
windowDays: number;
|
||||
/** The granularity the underlying rollup read resolved against. */
|
||||
granularity: string;
|
||||
current: WindowAggregate;
|
||||
previous: WindowAggregate;
|
||||
/**
|
||||
* `current.mean - previous.mean`, or null when either window has no mean
|
||||
* (no data in the current or prior window) — never a misleading 0.
|
||||
*/
|
||||
delta: number | null;
|
||||
/**
|
||||
* `delta / previous.mean` as a fraction (0.03 = +3 %), or null when the
|
||||
* prior window has no mean / a zero mean (no divide-by-zero, no misleading
|
||||
* 0 %). The client renders "no prior-period data" in that case.
|
||||
*/
|
||||
deltaPct: number | null;
|
||||
}
|
||||
Reference in new issue
Block a user