1
8
9import { z } from "zod";
10
11import { LIMITS } from "../constants.js";
12
13export const commentAnchorSchema = z
14 .object({
15 type: z.enum(["page", "block", "record", "selection"]),
16
17 refId: z.string().min(1),
18
19 blockId: z.string().min(1).optional(),
20
21 recordId: z.string().min(1).optional(),
22
23 selection: z
24 .object({
25 from: z.number().int().min(0),
26 to: z.number().int().min(0),
27 })
28 .optional(),
29 })
30 .strict()
31 .superRefine((val, ctx) => {
32 if (val.type === "selection" && !val.blockId) {
33 ctx.addIssue({
34 code: z.ZodIssueCode.custom,
35 message: "selection anchors require blockId",
36 path: ["blockId"],
37 });
38 }
39 if (val.type === "record" && !val.recordId) {
40 ctx.addIssue({
41 code: z.ZodIssueCode.custom,
42 message: "record anchors require recordId",
43 path: ["recordId"],
44 });
45 }
46 });
47export type CommentAnchor = z.infer<typeof commentAnchorSchema>;
48
49export const createCommentSchema = z
50 .object({
51 anchor: commentAnchorSchema,
52 body: z
53 .string()
54 .min(LIMITS.MIN_COMMENT, "Comment cannot be empty")
55 .max(LIMITS.MAX_COMMENT, `Comment must be at most ${LIMITS.MAX_COMMENT} characters`),
56 })
57 .strict();
58export type CreateComment = z.infer<typeof createCommentSchema>;
59
60export const replyCommentSchema = z
61 .object({
62
63 commentId: z.string().min(1),
64 body: z
65 .string()
66 .min(LIMITS.MIN_COMMENT, "Reply cannot be empty")
67 .max(LIMITS.MAX_COMMENT, `Reply must be at most ${LIMITS.MAX_COMMENT} characters`),
68 })
69 .strict();
70export type ReplyComment = z.infer<typeof replyCommentSchema>;
71
72export const updateCommentSchema = z
73 .object({
74 body: z.string().min(1).max(LIMITS.MAX_COMMENT).optional(),
75 resolved: z.boolean().optional(),
76 })
77 .strict()
78 .refine((v) => Object.keys(v).length > 0, { message: "No fields to update" });
79export type UpdateComment = z.infer<typeof updateCommentSchema>;
80