Merge branch 'fix/v1437-review' into release/v1.43.7

# Conflicts:
#	src/app/api/analytics/route.ts
This commit is contained in:
Marc-André Bombeck committed 2026-10-20 06:20:42 +02:00
commit 45c027aee2
217 files changed
+3636 -2127

No files matched your search

+18 -5
View File
@@ -589,7 +589,7 @@ export const listMeasurementsSchema = z
// Omitting `aggregate` keeps the raw wire shape (iOS contract); the
// chart-data client must opt in explicitly.
aggregate: z.enum(["raw", "daily", "weekly", "monthly"]).optional(),
// v1.4.36 W1 — opt-in source switch for daily-aggregate reads. When
// v1.4.36 — opt-in source switch for daily-aggregate reads. When
// `source=rollup` + `aggregate=daily`, the route reads from the
// persistent `measurement_rollups` DAY buckets instead of running a
// live `date_trunc` GROUP BY scan over the raw `measurements` table.
@@ -612,7 +612,7 @@ export const listMeasurementsSchema = z
// `value: { gte, lte }` range. Server-authoritative; validated min<=max.
valueMin: z.coerce.number().finite().optional(),
valueMax: z.coerce.number().finite().optional(),
// v1.4.37 W7c — list-view "one row per day" mode for cumulative
// v1.4.37 — list-view "one row per day" mode for cumulative
// types (steps, active energy, distance, flights, daylight). When
// `groupBy=day` is set and `type` is a cumulative HK type, the route
// returns one synthesised row per user-TZ day with `value` = SUM
@@ -623,18 +623,18 @@ export const listMeasurementsSchema = z
// picks the page's local days in Postgres and reports the distinct-day
// count as `meta.total` (#1245).
groupBy: z.enum(["day"]).optional(),
// v1.4.37 W7c — drill-down to per-sample rows for a single day in
// v1.4.37 — drill-down to per-sample rows for a single day in
// the user's IANA timezone. Format `YYYY-MM-DD`; the route resolves
// the day boundary against the user's `User.timezone`. Used by the
// expandable list row to reveal the chunks that contributed to the
// collapsed daily total.
//
// v1.4.37 W10 — a drill-down returns one bounded page rather than a
// v1.4.37 — a drill-down returns one bounded page rather than a
// cursor, so it refuses a non-zero `offset`.
dayKey: z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/, "dayKey must be YYYY-MM-DD")
// v1.4.37 W10 — a `YYYY-MM-DD` string can satisfy the regex while
// v1.4.37 — a `YYYY-MM-DD` string can satisfy the regex while
// still being an impossible calendar date (`2026-02-30`,
// `2026-13-01`). `new Date("2026-02-30T00:00:00Z")` silently
// overflows to March 2, so the drill-down would return rows from
@@ -665,6 +665,19 @@ export const listMeasurementsSchema = z
"limit must be <= 1000 when dayKey is set; drill-down responses are capped at 1000 rows",
path: ["limit"],
})
// A value range filters single readings, and a day-grouped row is the sum
// of a whole day: filtering its readings would return partial days summed
// as if they were whole. The combination has no honest answer, so it is
// refused rather than served.
.refine(
({ groupBy, valueMin, valueMax }) =>
groupBy !== "day" || (valueMin == null && valueMax == null),
{
message:
"valueMin / valueMax filter single readings and cannot be combined with groupBy=day",
path: ["groupBy"],
},
)
// v1.18.5 — an inverted value range (`valueMin > valueMax`) is a client
// bug, not an empty result set; reject it at the validator with a 422 so
// the filter UI can surface the mismatch instead of silently returning