import { z } from "zod"; import { OrganizationId } from "./organizations"; /** * Project ID schema, branded for type safety. * @public */ export const ProjectId = z.string().nonempty().brand("ProjectId"); /** * Project ID type, branded for type safety. * @public */ export type ProjectId = z.output; /** * Validates and brands a string as a ProjectId. * @public */ export function brandProjectId(id: string): ProjectId { return ProjectId.parse(id); } const ProjectMemberSchema = z.object({ id: z.string(), createdAt: z.string(), updatedAt: z.string(), isCurrentUser: z.boolean(), isRobot: z.boolean(), roles: z.array( z.object({ name: z.string(), title: z.string(), description: z.string(), }), ), }); /** * @public */ export type ProjectMember = z.output; /** * Project schema — validates and brands API responses * from the `/projects/:id` endpoint. * @public */ export const Project = z.object({ id: ProjectId, displayName: z.string(), studioHost: z.string().nullable(), organizationId: OrganizationId, metadata: z.object({ color: z.string().optional(), externalStudioHost: z.string().optional().nullable(), initialTemplate: z.string().optional(), cliInitializedAt: z.string().optional(), integration: z.string().optional(), }), isBlocked: z.boolean(), isDisabled: z.boolean(), isDisabledByUser: z.boolean(), activityFeedEnabled: z.boolean(), createdAt: z.string(), updatedAt: z.string(), }); /** * Represents a Sanity project with optional members and * features arrays depending on the generic parameters. * By default, neither members nor features are included. * - `Project` — base fields only (default) * - `Project` — includes `members` * - `Project` — includes both * @public */ export type Project< IncludeMembers extends boolean = true, IncludeFeatures extends boolean = true, > = z.output & (IncludeMembers extends true ? { members: ProjectMember[] } : unknown) & (IncludeFeatures extends true ? { features: string[] } : unknown); /** * Validates and parses a raw API response into a branded * Project. The options control which schema is used — * matching what the API returns based on query params. * @public */ export function parseProject< IncludeMembers extends boolean = true, IncludeFeatures extends boolean = true, >( data: unknown, options?: { includeMembers?: IncludeMembers; includeFeatures?: IncludeFeatures; }, ): Project { const includeMembers = options?.includeMembers ?? true; const includeFeatures = options?.includeFeatures ?? true; const extensions = { ...(includeMembers && { members: z.array(ProjectMemberSchema) }), ...(includeFeatures && { features: z.array(z.string()) }), }; const schema = Object.keys(extensions).length > 0 ? Project.extend(extensions) : Project; return schema.parse(data) as Project; }