mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
`openapi:check` compares the registry against the committed YAML, so a route that was never registered produced no drift and no failure. These nineteen had shipped without a contract: the health probe, the dashboard analytics aggregate, the profile summary, personal records, the badge grid, the whole environmental-context module, both onboarding writes, the Apple Health import pair, the external medication ingest, the self-context adopt, the AI-provider update and the AI connection probe. Each entry is read off the route rather than inferred from a neighbour, so it carries what a client cannot guess: that `/api/health` answers a bare object and not the envelope, that `/api/analytics` records a score row and mints pattern ids on a GET, that `/api/ai/test` reports a provider failure as 200 with `ok: false` because a 5xx comes back as somebody's HTML error page, that the ingest route replays with 200 and creates with 201, and that a home location re-stamps its effective-from instant on every write. Three route-local request schemas move to `src/lib/validations/` so the published shape and the runtime parse are one object rather than two copies — a route module may only export handlers, which is why they could not be imported where they were. The AI-provider PATCH has no schema to move: it hand-rolls its parse field by field, so its entry says so and spells out that a wrongly-typed key is skipped in silence. The generated YAML is deliberately not in this commit.
57 lines
2.3 KiB
TypeScript
57 lines
2.3 KiB
TypeScript
/**
|
|
* Request schemas for the two onboarding write endpoints.
|
|
*
|
|
* They live outside the route files for the same reason
|
|
* `src/lib/validations/about-me.ts` does: a route module may only export
|
|
* handlers plus the Next.js route config, so the OpenAPI registry cannot
|
|
* import a schema declared inside one. Keeping the shapes here lets the
|
|
* published contract and the runtime parser be the same object rather than
|
|
* two hand-kept copies.
|
|
*
|
|
* Prisma-free by construction — `@/lib/onboarding/goals` is pure data — so
|
|
* the generator script can pull them in without dragging the server graph
|
|
* along.
|
|
*/
|
|
import { z } from "zod/v4";
|
|
|
|
import { ONBOARDING_GOAL_SLUGS } from "@/lib/onboarding/goals";
|
|
|
|
/**
|
|
* `POST /api/onboarding/step` — the step-by-step wizard checkpoint.
|
|
*
|
|
* `step` is the step being COMPLETED and must equal the stored step plus one;
|
|
* the route refuses anything else with 409 rather than clamping. `goals`
|
|
* rides the step-2 submit and is validated against the closed slug set, so an
|
|
* unknown slug fails the whole request instead of being silently dropped.
|
|
*/
|
|
export const onboardingStepSchema = z.object({
|
|
step: z.number().int().min(1).max(4),
|
|
goals: z
|
|
.array(z.enum(ONBOARDING_GOAL_SLUGS))
|
|
.max(ONBOARDING_GOAL_SLUGS.length)
|
|
.optional(),
|
|
});
|
|
|
|
export type OnboardingStepInput = z.infer<typeof onboardingStepSchema>;
|
|
|
|
/**
|
|
* `POST /api/onboarding/complete` — the legacy single-shot completion path.
|
|
*
|
|
* Every field is optional: the endpoint's job is the completion stamp, and
|
|
* the profile fields are whatever the wizard happened to collect. A field
|
|
* that parses but is falsy (an empty `displayName`, `heightCm: 0`) is not
|
|
* written — the route only assigns truthy values.
|
|
*/
|
|
export const onboardingCompleteSchema = z.object({
|
|
displayName: z.string().trim().min(1).max(50).optional(),
|
|
heightCm: z.number().min(50).max(300).optional(),
|
|
dateOfBirth: z.string().optional(),
|
|
// The same three values the profile schema stores. A narrower enum here
|
|
// rejected the whole onboarding submission over a field the account is
|
|
// allowed to hold, and left the value unrecordable until the person found
|
|
// the setting again afterwards.
|
|
gender: z.enum(["MALE", "FEMALE", "OTHER"]).optional(),
|
|
});
|
|
|
|
export type OnboardingCompleteInput = z.infer<typeof onboardingCompleteSchema>;
|