/** * @factory/shared — comment DTOs (PRODUCT-SPEC §14). * * Comments attach to pages, blocks, text selections or database records. * Replies and reactions are modeled by the API; this module covers * creation inputs shared FE/BE. */ import { z } from "zod"; import { LIMITS } from "../constants.js"; export const commentAnchorSchema = z .object({ type: z.enum(["page", "block", "record", "selection"]), /** Page id — always present (the anchor's owning page). */ refId: z.string().min(1), /** Block id for `block`/`selection` anchors. */ blockId: z.string().min(1).optional(), /** Record id for `record` anchors. */ recordId: z.string().min(1).optional(), /** Text selection range within a block: {from: number, to: number}. */ selection: z .object({ from: z.number().int().min(0), to: z.number().int().min(0), }) .optional(), }) .strict() .superRefine((val, ctx) => { if (val.type === "selection" && !val.blockId) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "selection anchors require blockId", path: ["blockId"], }); } if (val.type === "record" && !val.recordId) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "record anchors require recordId", path: ["recordId"], }); } }); export type CommentAnchor = z.infer; export const createCommentSchema = z .object({ anchor: commentAnchorSchema, body: z .string() .min(LIMITS.MIN_COMMENT, "Comment cannot be empty") .max(LIMITS.MAX_COMMENT, `Comment must be at most ${LIMITS.MAX_COMMENT} characters`), }) .strict(); export type CreateComment = z.infer; export const replyCommentSchema = z .object({ /** Parent comment id. */ commentId: z.string().min(1), body: z .string() .min(LIMITS.MIN_COMMENT, "Reply cannot be empty") .max(LIMITS.MAX_COMMENT, `Reply must be at most ${LIMITS.MAX_COMMENT} characters`), }) .strict(); export type ReplyComment = z.infer; export const updateCommentSchema = z .object({ body: z.string().min(1).max(LIMITS.MAX_COMMENT).optional(), resolved: z.boolean().optional(), }) .strict() .refine((v) => Object.keys(v).length > 0, { message: "No fields to update" }); export type UpdateComment = z.infer;