/** * Manifest generation handlers */ import { hashString } from "../../utils/hash.js"; import type { ManifestResponse, FieldDescriptor } from "../types.js"; /** Pattern to add spaces before capital letters */ const CAMEL_CASE_PATTERN = /([A-Z])/g; const FIRST_CHAR_PATTERN = /^./; // Collection definition shape for manifest generation interface CollectionDefinition { schema: { _def?: { shape?: () => Record }; shape?: Record; }; admin: { label: string; labelSingular?: string; supports?: string[]; }; } type CollectionMap = Record; /** * Generate admin manifest from collections */ export async function generateManifest( collections: CollectionMap, plugins: Record< string, { adminPages?: Array<{ path: string; component: string }>; widgets?: string[]; } > = {}, ): Promise { const manifestCollections: ManifestResponse["collections"] = {}; for (const [name, definition] of Object.entries(collections)) { // Extract field descriptors from Zod schema const fields = extractFieldDescriptors(definition.schema); manifestCollections[name] = { label: definition.admin.label, labelSingular: definition.admin.labelSingular || definition.admin.label, supports: definition.admin.supports || [], fields, }; } // Generate hash from collections (for cache invalidation) const hash = await hashString(JSON.stringify(manifestCollections)); return { version: "0.1.0", hash, collections: manifestCollections, plugins, }; } /** * Extract field descriptors from Zod schema * Note: This is a simplified implementation that handles common types */ function extractFieldDescriptors(schema: { _def?: { shape?: () => Record }; shape?: Record; }): Record { const fields: Record = {}; // Handle Zod object schema const shape = typeof schema._def?.shape === "function" ? schema._def.shape() : schema.shape || {}; for (const [name, fieldSchema] of Object.entries(shape)) { fields[name] = extractFieldType(name, fieldSchema); } return fields; } /** * Extract field type from Zod schema */ /** Type guard: check if a value is a non-null object */ function isObject(value: unknown): value is Record { return typeof value === "object" && value !== null; } function extractFieldType(name: string, schema: unknown): FieldDescriptor { if (!isObject(schema)) { return { kind: "string", label: formatLabel(name) }; } // Check for custom field markers if (schema.isPortableText) { return { kind: "portableText", label: formatLabel(name) }; } if (schema.isImage) { return { kind: "image", label: formatLabel(name) }; } if (schema.isReference) { return { kind: "reference", label: formatLabel(name) }; } // Handle standard Zod types const def = isObject(schema._def) ? schema._def : undefined; const typeName = typeof def?.typeName === "string" ? def.typeName : undefined; switch (typeName) { case "ZodString": return { kind: "string", label: formatLabel(name) }; case "ZodNumber": return { kind: "number", label: formatLabel(name) }; case "ZodBoolean": return { kind: "boolean", label: formatLabel(name) }; case "ZodDate": return { kind: "datetime", label: formatLabel(name) }; case "ZodEnum": { const values = Array.isArray(def?.values) ? def.values : []; return { kind: "select", label: formatLabel(name), options: values .filter((v): v is string => typeof v === "string") .map((v) => ({ value: v, label: v.charAt(0).toUpperCase() + v.slice(1), })), }; } case "ZodArray": return { kind: "array", label: formatLabel(name) }; case "ZodObject": return { kind: "object", label: formatLabel(name) }; case "ZodOptional": case "ZodDefault": // Unwrap optional/default types if (def?.innerType) { return extractFieldType(name, def.innerType); } return { kind: "string", label: formatLabel(name) }; default: return { kind: "string", label: formatLabel(name) }; } } /** * Format field name as label */ function formatLabel(name: string): string { return name .replace(CAMEL_CASE_PATTERN, " $1") .replace(FIRST_CHAR_PATTERN, (str) => str.toUpperCase()) .trim(); }