import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core"; import type { Transaction } from "../generated/kysely-tailordb"; import { CommentNotFoundError, ForbiddenError, InvalidBodyError } from "../lib/errors.generated"; export interface UpdatePipelineItemCommentInput { commentId: string; body: string; } const COMMENT_OWN_KEYS = new Set(["commentId", "body"]); // Module-managed PipelineItemComment columns that must never be writable through // the extension-field pass-through — authorUserId backs the author-only check. const COMMENT_RESERVED_KEYS = new Set(["id", "itemId", "authorUserId", "createdAt", "updatedAt"]); export async function run>( db: Transaction, input: UpdatePipelineItemCommentInput & Partial, ctx: CommandContext, ) { // Consumer extension fields (declared via defineModule's pipelineItemComment.fields) // pass through to the update untouched — except reserved model columns. const customFields: Record = {}; for (const [key, value] of Object.entries(input as Record)) { if (!COMMENT_OWN_KEYS.has(key) && !COMMENT_RESERVED_KEYS.has(key)) { customFields[key] = value; } } const comment = await db .selectFrom("PipelineItemComment") .selectAll() .where("id", "=", input.commentId) .forUpdate() .executeTakeFirst(); if (!comment) { return err(new CommentNotFoundError(input.commentId)); } if (comment.authorUserId !== ctx.actorId) { return err(new ForbiddenError(input.commentId)); } if (!input.body.trim()) { return err(new InvalidBodyError(input.commentId)); } const update: Record = { ...customFields, body: input.body, updatedAt: new Date(), }; const updatedComment = await db .updateTable("PipelineItemComment") .set(update) .where("id", "=", input.commentId) .returningAll() .executeTakeFirst(); return ok({ comment: updatedComment ?? { ...comment, ...update } }); }