Merge branch 'release/v1.43.0' into v143/fixui

This commit is contained in:
Marc-André Bombeck committed 2026-10-16 05:55:14 +02:00
commit beb7ef4ccc
93 files changed
+4369 -3956

No files matched your search

+3 -2
View File
@@ -10,6 +10,7 @@ import { redactSecrets } from "@/lib/logging/redact";
import type { WideEvent } from "@/lib/logging/types";
import { NextRequest } from "next/server";
import { z } from "zod/v4";
import { isoDateTime } from "@/lib/validations/datetime";
export const dynamic = "force-dynamic";
@@ -26,8 +27,8 @@ const querySchema = z.object({
traceId: z.string().trim().min(1).max(200).optional(),
level: z.enum(["debug", "info", "warn", "error"]).optional(),
action: z.string().trim().min(1).max(200).optional(),
since: z.string().datetime().optional(),
until: z.string().datetime().optional(),
since: isoDateTime({ offset: false }).optional(),
until: isoDateTime({ offset: false }).optional(),
limit: z.coerce.number().int().min(1).max(PROBLEMS_BUFFER_MAX).default(200),
});
+3 -2
View File
@@ -6,6 +6,7 @@ import { redactSecrets } from "@/lib/logging/redact";
import { getAuditLogRetentionDays } from "@/lib/jobs/audit-log-cleanup";
import { NextRequest } from "next/server";
import { z } from "zod/v4";
import { isoDateTime } from "@/lib/validations/datetime";
/**
* v1.4.16 phase B4: parse the union of legacy + new query params. Legacy
@@ -27,8 +28,8 @@ const querySchema = z.object({
actor: z.string().trim().min(1).max(200).optional(),
action: z.string().trim().min(1).max(200).optional(),
target: z.string().trim().min(1).max(200).optional(),
since: z.string().datetime().optional(),
until: z.string().datetime().optional(),
since: isoDateTime({ offset: false }).optional(),
until: isoDateTime({ offset: false }).optional(),
});
export const dynamic = "force-dynamic";
+4 -3
View File
@@ -41,6 +41,7 @@ import { moodDateKey } from "@/lib/mood/date-key";
import { DEFAULT_TIMEZONE } from "@/lib/tz/format";
import { deriveA1 } from "@/lib/mood/level-a";
import { zonedWallClockToUtc } from "@/lib/tz/wall-clock";
import { isoDateTime } from "@/lib/validations/datetime";
// Derived from canonical enum so round-trip export → import covers every
// type. Previous hardcoded subset silently dropped 4 of 11 types
@@ -51,12 +52,12 @@ const measurementSchema = z
value: z.number(),
unit: z.string(),
// v1.17.1 — share the canonical entry-instant bound used by the single
// POST + edit paths (v1.17 W1b). Previously a bare `z.string().datetime()`
// POST + edit paths (v1.17 W1b). Previously a bare datetime check
// with no ceiling, so a crafted JSON import could forward-date a reading.
// `validateEntryInstant` rejects any future instant beyond a 5-min skew
// tolerance and any instant before 1900; the field transforms to a `Date`.
measuredAt: validateEntryInstant(
z.iso.datetime({ offset: true }).transform((s) => new Date(s)),
isoDateTime().transform((s) => new Date(s)),
),
glucoseContext: glucoseContextEnum.optional(),
source: z.string().optional(),
@@ -96,7 +97,7 @@ const moodEntrySchema = z
// v1.17.1 — same entry-instant bound as measurements; an imported mood
// log cannot be forward-dated. Optional + transforms to a `Date` when set.
loggedAt: validateEntryInstant(
z.iso.datetime({ offset: true }).transform((s) => new Date(s)),
isoDateTime().transform((s) => new Date(s)),
).optional(),
// v1.12.1 — optional source-stable id (e.g. a Daylio row id). When
// present, the import upserts on `(userId, source, externalId)` so a
+2 -1
View File
@@ -46,6 +46,7 @@ import { annotate } from "@/lib/logging/context";
import { checkRateLimit } from "@/lib/rate-limit";
import { prisma } from "@/lib/db";
import { persistEcgRecording } from "@/lib/ecg/persist-recording";
import { isoDateTime } from "@/lib/validations/datetime";
export const dynamic = "force-dynamic";
@@ -96,7 +97,7 @@ const ingestSourceEnum = z.enum(["APPLE_HEALTH"]);
const ecgIngestSchema = z
.object({
externalRecordingId: z.string().min(1).max(120),
recordedAt: z.iso.datetime({ offset: true }),
recordedAt: isoDateTime(),
// HealthKit hands both of these over as doubles. A watch reports its
// average heart rate with a fraction (64.7) often enough that requiring
// an integer refused most new recordings while the old ones, synced when
+3 -2
View File
@@ -112,6 +112,7 @@ import {
type MeasurementSource,
type MeasurementType,
} from "@/generated/prisma/client";
import { isoDateTime } from "@/lib/validations/datetime";
// v1.4.25 W16c — historical-backfill threshold for PR push
// suppression. A batch larger than this fires the detection job with
@@ -156,8 +157,8 @@ const batchEntrySchema = z.object({
hkIdentifier: z.string().min(1).max(120),
value: z.number().finite(),
unit: z.string().min(1).max(60),
startDate: z.iso.datetime({ offset: true }),
endDate: z.iso.datetime({ offset: true }),
startDate: isoDateTime(),
endDate: isoDateTime(),
sleepStage: z.number().int().min(0).max(20).optional(),
// v1.10.0 — categorical events (WX-B). The integer `HKCategoryValue`
// codepoint for an EVENT-class category sample (irregular-rhythm,
@@ -47,6 +47,7 @@ import {
import { assertMedicationOwnership } from "@/lib/medications/route-guards";
import { dueSchedules } from "@/lib/medications/intake-tracking";
import { DEFAULT_TIMEZONE } from "@/lib/tz/format";
import { isoDateTime } from "@/lib/validations/datetime";
type RouteParams = { params: Promise<{ id: string }> };
@@ -60,12 +61,10 @@ const RATE_WINDOW_MS = 60_000;
const querySchema = z
.object({
from: z.iso
.datetime({ offset: true })
from: isoDateTime()
.transform((s) => new Date(s))
.optional(),
to: z.iso
.datetime({ offset: true })
to: isoDateTime()
.transform((s) => new Date(s))
.optional(),
})
+3 -4
View File
@@ -101,6 +101,7 @@ import {
INTAKE_NOT_TRACKED_REASON,
isRecordOnly,
} from "@/lib/medications/intake-tracking";
import { isoDateTime } from "@/lib/validations/datetime";
const MAX_ENTRIES_PER_BATCH = 500;
const BATCH_RATE_LIMIT_MAX = 60;
@@ -109,8 +110,7 @@ const BATCH_RATE_LIMIT_WINDOW_MS = 60 * 1000;
const bulkEntrySchema = z
.object({
medicationId: z.string().min(1),
scheduledFor: z.iso
.datetime({ offset: true })
scheduledFor: isoDateTime()
.transform((s) => new Date(s))
.optional(),
// v1.16.9 — shared plausibility bounds (no future beyond clock skew,
@@ -135,8 +135,7 @@ const bulkEntrySchema = z
// entry skipped (`force_slot_invalid`) per the bulk per-entry contract.
// Ignored on non-taken entries (a pending echo / skip carries no take
// to attribute).
forceSlotInstant: z.iso
.datetime({ offset: true })
forceSlotInstant: isoDateTime()
.transform((s) => new Date(s))
.optional(),
// v1.16.4 — per-entry dose override, mirroring the single intake
+2 -1
View File
@@ -63,6 +63,7 @@ import {
droppedLinkKeysForWire,
NO_DROPPED_LINK_KEYS,
} from "@/lib/mood/tag-links";
import { isoDateTime } from "@/lib/validations/datetime";
const MAX_ENTRIES_PER_BATCH = 500;
const BATCH_RATE_LIMIT_MAX = 60;
@@ -135,7 +136,7 @@ const bulkEntrySchema = z.object({
* @per-entry-parse: moodContextSchema, in the loop below
*/
context: z.unknown().optional(),
moodLoggedAt: z.iso.datetime({ offset: true }).transform((s) => new Date(s)),
moodLoggedAt: isoDateTime().transform((s) => new Date(s)),
source: moodSourceEnum.optional().default("MANUAL"),
/**
* Optional iOS-side identifier (e.g. SwiftData row UUID) that lets