Files
HealthLog/src/lib/export/visits-backup-schema.ts
T
Marc-André Bombeck 12749f6839 refactor(backup): give each domain its own slice of the payload schema
The element schemas move out of validations/backup.ts into one
<domain>-backup-schema.ts per domain, beside its export and restore, with
the shared primitives in backup-schema-kit.ts. validations/backup.ts now
composes the slices into backupPayloadSchema in the same key order, with
the same defaults and the checks that span sections, so the parsed
payload, every validation message and the OpenAPI contract are unchanged.

The lazy-schema guard now covers the slices too: a slice is reached only
through the payload schema or another slice, never from a domain's export
or restore. The external-id guard carries the payload's exemption to its
slices, and mutation testing scores the slices with the payload schema.
2026-10-22 08:02:14 +02:00

77 lines
3.0 KiB
TypeScript

/**
* The visits slice of the backup payload schema.
*
* Part of the visits 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 {
EncounterKind,
EncounterStatus,
Laterality,
} from "@/generated/prisma/enums";
import { base64BytesSchema, isoDateTime } from "@/lib/export/backup-schema-kit";
/**
* A practitioner, an encounter, and the edges between an encounter and the
* things it produced.
*
* `id` is required on both records, unlike the lab result above: a link
* addresses an encounter and its far side by id and nothing else on either row
* is unique enough to rebuild the reference from. Free text follows the note
* contract: ciphertext as base64 on a disaster-recovery file, the readable
* value on a portable one (sealed under the receiving host's key on restore).
*/
export const practitionerBackupSchema = z
.object({
id: z.string().min(1),
name: z.string().min(1),
specialty: z.string().nullable().optional(),
practice: z.string().nullable().optional(),
location: z.string().nullable().optional(),
phone: z.string().nullable().optional(),
/// The address and phone ciphertext, DR payloads only.
locationEncrypted: base64BytesSchema.nullable().optional(),
phoneEncrypted: base64BytesSchema.nullable().optional(),
noteEncrypted: base64BytesSchema.nullable().optional(),
note: z.string().nullable().optional(),
createdAt: isoDateTime,
updatedAt: isoDateTime,
deletedAt: isoDateTime.nullable().optional(),
})
.passthrough();
export const encounterBackupSchema = z
.object({
id: z.string().min(1),
occurredAt: isoDateTime,
status: z.enum(EncounterStatus),
kind: z.enum(EncounterKind),
practitionerId: z.string().nullable().optional(),
reasonEncrypted: base64BytesSchema.nullable().optional(),
outcomeEncrypted: base64BytesSchema.nullable().optional(),
// The procedure's body site and side. Optional so a file written
// before they existed still parses; the visit restores with neither.
bodySiteEncrypted: base64BytesSchema.nullable().optional(),
// The readable reason, outcome and body site of a portable file.
reason: z.string().nullable().optional(),
outcome: z.string().nullable().optional(),
bodySite: z.string().nullable().optional(),
laterality: z.enum(Laterality).nullable().optional(),
// Remapped against the restored reminders (they travel since v1.37.20);
// dropped to NULL, with the drop named, only when the file lacks the row.
reminderId: z.string().nullable().optional(),
createdAt: isoDateTime,
updatedAt: isoDateTime,
deletedAt: isoDateTime.nullable().optional(),
})
.passthrough();
export const encounterLinkBackupSchema = z
.object({
encounterId: z.string().min(1),
targetId: z.string().min(1),
createdAt: isoDateTime,
})
.passthrough();