mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
Rework the per-day location resolution so it can no longer fabricate weather for
days before the home was set. Precedence, first hit wins:
1. an explicit dated location period (a trip, or a corrected stretch of
history) covering the day;
2. DEVICE — a reserved slot for a future client-supplied coarse per-day
location (ranks between the period and home; not produced server-side yet);
3. the home location, but only for days on/after its effective-from date;
4. otherwise the day is skipped.
The home route stamps `homeSince` whenever the home is set/updated and returns
it. The overview exposes it. Backfill bounds are now optional and default to the
conservative `[homeSince .. today]` span instead of a fixed reach onto the
current home; days before `homeSince` still resolve to a skip in the worker, so
even a wider explicit start cannot back-fill weather for the pre-home past. To
reach further back a user adds an explicit location period for that range.
Add unit coverage for the precedence (including the effective-from boundary and
the skip-not-guess rule) and the default backfill range.
76 lines
2.3 KiB
TypeScript
76 lines
2.3 KiB
TypeScript
/**
|
|
* v1.25 (W-ENV) — environmental-context request validation.
|
|
*
|
|
* The home location, the travel overrides, and the backfill span. Coordinates
|
|
* are bounded to valid lat/lon; the labels are length-capped plaintext (a city
|
|
* name, never a quasi-identifier). Dates are strict YYYY-MM-DD. The shared
|
|
* shapes are reused by the OpenAPI registry so the wire contract stays
|
|
* single-source.
|
|
*/
|
|
import { z } from "zod/v4";
|
|
|
|
/** Strict YYYY-MM-DD calendar day. */
|
|
export const dayStringSchema = z
|
|
.string()
|
|
.regex(/^\d{4}-\d{2}-\d{2}$/, "Expected a YYYY-MM-DD date");
|
|
|
|
const latSchema = z.number().gte(-90).lte(90);
|
|
const lonSchema = z.number().gte(-180).lte(180);
|
|
const labelSchema = z.string().trim().min(1).max(200);
|
|
|
|
/** A coarse home location set in settings (city-level granularity). */
|
|
export const homeLocationSchema = z
|
|
.object({
|
|
lat: latSchema,
|
|
lon: lonSchema,
|
|
label: labelSchema,
|
|
/** IANA timezone (anchors the stored day-key). */
|
|
timezone: z.string().trim().min(1).max(64),
|
|
})
|
|
.strict();
|
|
|
|
export type HomeLocationInput = z.infer<typeof homeLocationSchema>;
|
|
|
|
/** A manual travel override covering an inclusive date range. */
|
|
export const travelLocationSchema = z
|
|
.object({
|
|
startDate: dayStringSchema,
|
|
endDate: dayStringSchema,
|
|
lat: latSchema,
|
|
lon: lonSchema,
|
|
label: labelSchema,
|
|
})
|
|
.strict()
|
|
.refine((v) => v.startDate <= v.endDate, {
|
|
message: "startDate must be on or before endDate",
|
|
path: ["endDate"],
|
|
});
|
|
|
|
export type TravelLocationInput = z.infer<typeof travelLocationSchema>;
|
|
|
|
/**
|
|
* A backfill request: fetch + store the environment rows over a date range.
|
|
* Both bounds are optional — an omitted span defaults to the conservative
|
|
* `[homeSince .. today]` range on the server (never a fixed reach into the past
|
|
* onto the current home).
|
|
*/
|
|
export const environmentBackfillSchema = z
|
|
.object({
|
|
startDate: dayStringSchema.optional(),
|
|
endDate: dayStringSchema.optional(),
|
|
})
|
|
.strict()
|
|
.refine((v) => !v.startDate || !v.endDate || v.startDate <= v.endDate, {
|
|
message: "startDate must be on or before endDate",
|
|
path: ["endDate"],
|
|
});
|
|
|
|
export type EnvironmentBackfillInput = z.infer<
|
|
typeof environmentBackfillSchema
|
|
>;
|
|
|
|
/** Geocoding search query (`?q=`). */
|
|
export const geocodeQuerySchema = z
|
|
.object({ q: z.string().trim().min(1).max(120) })
|
|
.strict();
|