paperclip/packages/shared/src/validators/project.ts

59 lines
2 KiB
TypeScript
Raw Normal View History

import { z } from "zod";
import { PROJECT_STATUSES } from "../constants.js";
const projectWorkspaceFields = {
name: z.string().min(1).optional(),
cwd: z.string().min(1).optional().nullable(),
repoUrl: z.string().url().optional().nullable(),
repoRef: z.string().optional().nullable(),
metadata: z.record(z.unknown()).optional().nullable(),
};
export const createProjectWorkspaceSchema = z.object({
...projectWorkspaceFields,
isPrimary: z.boolean().optional().default(false),
}).superRefine((value, ctx) => {
const hasCwd = typeof value.cwd === "string" && value.cwd.trim().length > 0;
const hasRepo = typeof value.repoUrl === "string" && value.repoUrl.trim().length > 0;
if (!hasCwd && !hasRepo) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Workspace requires at least one of cwd or repoUrl.",
path: ["cwd"],
});
}
});
export type CreateProjectWorkspace = z.infer<typeof createProjectWorkspaceSchema>;
export const updateProjectWorkspaceSchema = z.object({
...projectWorkspaceFields,
isPrimary: z.boolean().optional(),
}).partial();
export type UpdateProjectWorkspace = z.infer<typeof updateProjectWorkspaceSchema>;
const projectFields = {
/** @deprecated Use goalIds instead */
goalId: z.string().uuid().optional().nullable(),
goalIds: z.array(z.string().uuid()).optional(),
name: z.string().min(1),
description: z.string().optional().nullable(),
status: z.enum(PROJECT_STATUSES).optional().default("backlog"),
leadAgentId: z.string().uuid().optional().nullable(),
targetDate: z.string().optional().nullable(),
color: z.string().optional().nullable(),
archivedAt: z.string().datetime().optional().nullable(),
};
export const createProjectSchema = z.object({
...projectFields,
workspace: createProjectWorkspaceSchema.optional(),
});
export type CreateProject = z.infer<typeof createProjectSchema>;
export const updateProjectSchema = z.object(projectFields).partial();
export type UpdateProject = z.infer<typeof updateProjectSchema>;