import { z } from 'zod'; /** * Page schemas and views shared between api and web (issue #23). A page * carries a Yjs document from day one (ADR 0003); in M2 its state is * saved wholesale over REST as a base64 string. */ export const pageTitleSchema = z .string() .trim() .min(1, 'validation.required') .max(200, 'validation.tooLong'); export const pageSlugSchema = z .string() .trim() .min(1, 'validation.required') .max(60, 'validation.tooLong'); export const createPageInputSchema = z.object({ title: pageTitleSchema, }); export type CreatePageInput = z.infer; export const updatePageInputSchema = z .object({ title: pageTitleSchema, slug: pageSlugSchema, }) .partial(); export type UpdatePageInput = z.infer; /** * Move a page in the manual sidebar order (issue #45): place it between the * `afterId` page (its new predecessor) and the `beforeId` page (its new * successor); either is `null` at an end of the list. The server recomputes * only the moved page's `sort_key`. */ export const repositionPageInputSchema = z.object({ afterId: z.string().min(1).nullable(), beforeId: z.string().min(1).nullable(), }); export type RepositionPageInput = z.infer; export const savePageStateInputSchema = z.object({ /** Base64-encoded Yjs state (`Y.encodeStateAsUpdate`). */ state: z.string().min(1, 'validation.required'), }); export type SavePageStateInput = z.infer; /** Max Yjs document size (operations.md §Limits) — a fixed operational * ceiling, not a per-pond/user quota. */ export const MAX_PAGE_DOCUMENT_BYTES = 5 * 1024 * 1024; export interface PageView { id: string; pondId: string; title: string; slug: string; sortKey: string; createdAt: string; updatedAt: string; deletedAt: string | null; } /** What `GET /pages/:id` returns: page meta plus its base64 Yjs state. */ export interface PageStateView extends PageView { state: string; } /** * A page in the sidebar list (`GET /ponds/:id/pages`) with the ids of the * labels assigned to it (issue #44). The sidebar maps these to colours/names * via the pond's label tree for chips and filters by them (descendant-inclusive * via the shared tree helpers) — so no per-label detail is repeated here. */ export interface PageListItemView extends PageView { labelIds: string[]; } /** Why a version snapshot exists (ADR 0013): automatic (session end / active * interval), a named manual snapshot, or the automatic pre-restore snapshot. */ export type PageVersionTrigger = 'auto' | 'manual' | 'pre_restore'; export const createVersionInputSchema = z.object({ label: z.string().trim().min(1, 'validation.required').max(100, 'validation.tooLong'), }); export type CreateVersionInput = z.infer; /** A version in the history list (issue #41; no snapshot bytes). */ export interface PageVersionView { id: string; pageId: string; trigger: PageVersionTrigger; label: string | null; /** Editor for manual/pre-restore versions; null for automatic snapshots. */ createdBy: string | null; /** Users who edited since the previous version. */ contributorIds: string[]; /** Display names for contributorIds; deleted accounts are omitted. */ contributors: { id: string; name: string }[]; createdAt: string; } /** A single version with its rendered content, for viewing/diffing (issue #42). */ export interface PageVersionContentView extends PageVersionView { /** Read-only HTML render of the snapshot. */ html: string; /** Markdown of the snapshot, for the diff against the current page. */ markdown: string; }