Files
HealthLog/src/lib/export/sensitive-backup-schema.ts
T
Marc-André Bombeck 02cc0631fa feat(mental-health): record which wording a check-in was answered in
An administration now stores `wording`: `validated` (the published
validated version of the reader's language, the original for English),
`unvalidated_translation` (HealthLog's own translation) or `original_en`
(the English original, chosen by a reader of another language). The
client sends only `englishOriginal`; the server resolves the rest from
the wording registry, so a client cannot claim a validated wording the
registry does not hold. `locale` keeps the reader's language, which
still selects the crisis resources.

Additive migration 0475: a nullable column with no backfill, since which
text earlier rows showed depended on releases since replaced. Every read
returns the field, the OpenAPI contract carries it and `englishOriginal`,
and the disaster-recovery backup carries and restores it (the parity
goldens gain the column; their pinned timestamps move by one step
because the fill writes one more column).

The PHQ-9 item-9 crisis signpost is answered from the answers alone and
is proven in every wording through the real route.
2026-10-23 08:14:15 +02:00

53 lines
2.0 KiB
TypeScript

/**
* The sensitive slice of the backup payload schema.
*
* Part of the sensitive backup domain, beside its export and restore.
* Loaded only through `src/lib/validations/backup.ts`, which composes every
* slice into `backupPayloadSchema` on first use (the schema is ~12 MB of
* heap; see `backup-schema-lazy-guard.test.ts`).
*/
import { z } from "zod/v4";
import { AssessmentInstrument } from "@/generated/prisma/enums";
import { base64BytesSchema, isoDateTime } from "@/lib/export/backup-schema-kit";
import { ASSESSMENT_WORDINGS } from "@/lib/mental-health/wording/types";
/**
* A completed screener administration. `responsesEncrypted` is REQUIRED rather
* than optional, unlike every other ciphertext field in this file: the column
* is NOT NULL, so a file that omits it describes a row that cannot be written,
* and saying so at the schema is better than discovering it inside the
* transaction that has already wiped the account.
*/
export const mentalHealthAssessmentBackupSchema = z
.object({
id: z.string().min(1),
instrument: z.enum(AssessmentInstrument),
locale: z.string().min(1),
wording: z.enum(ASSESSMENT_WORDINGS).nullable().optional(),
version: z.string().optional(),
responsesEncrypted: base64BytesSchema,
totalScore: z.number().int(),
severityBand: z.string().min(1),
item9Flagged: z.boolean().optional(),
crisisShownAt: isoDateTime.nullable().optional(),
takenAt: isoDateTime,
tz: z.string().nullable().optional(),
source: z.string().optional(),
externalId: z.string().nullable().optional(),
syncVersion: z.number().int().optional(),
deletedAt: isoDateTime.nullable().optional(),
createdAt: isoDateTime,
updatedAt: isoDateTime,
})
.passthrough();
export const consentReceiptBackupSchema = z
.object({
id: z.string().min(1),
kind: z.string().min(1),
artefact: z.string().min(1),
signedAt: isoDateTime,
revokedAt: isoDateTime.nullable().optional(),
createdAt: isoDateTime.optional(),
})
.passthrough();