import { z } from 'zod'; /** * Comments on pages (issue #91, data-model.md §Comments): threaded * discussions with resolve semantics. Reading follows page read; writing * requires page read plus the pond's `commentPolicy` * (permissions.md §Non-page objects). */ /** Who may write comments: every reader, or pond-wide editors only. */ export const COMMENT_POLICIES = ['readers', 'editors'] as const; export type CommentPolicy = (typeof COMMENT_POLICIES)[number]; const commentBodySchema = z .string() .trim() .min(1, 'validation.required') .max(10_000, 'validation.tooLong'); export const createCommentInputSchema = z.object({ /** Markdown; the api renders it through the shared sanitizing pipeline. */ body: commentBodySchema, /** Reply target: a thread root's id. Absent = new thread. */ parentId: z.string().uuid().nullish(), /** Opaque serialized position in the document (thread roots only). */ anchor: z.string().max(2_000).nullish(), }); export type CreateCommentInput = z.infer; export const updateCommentInputSchema = z.object({ body: commentBodySchema, }); export type UpdateCommentInput = z.infer; export const COMMENT_LIST_FILTERS = ['all', 'open', 'resolved'] as const; export type CommentListFilter = (typeof COMMENT_LIST_FILTERS)[number]; export const commentListQuerySchema = z.object({ filter: z.enum(COMMENT_LIST_FILTERS).default('all'), }); export type CommentListQuery = z.infer; export interface CommentAuthorView { id: string; username: string; displayName: string; } export interface CommentView { id: string; pageId: string; parentId: string | null; /** Null only after a hard account deletion; pseudonymized authors remain. */ author: CommentAuthorView | null; /** The raw Markdown — what the edit form loads. */ body: string; /** Sanitized render of `body` (same pipeline as pages). */ html: string; anchor: string | null; createdAt: string; editedAt: string | null; resolvedAt: string | null; } export interface CommentThreadView { root: CommentView; replies: CommentView[]; resolved: boolean; /** Resolved threads arrive collapsed by default (issue #91 AC). */ collapsed: boolean; } export interface PageCommentsView { threads: CommentThreadView[]; openCount: number; resolvedCount: number; }