import type { ApplogValue } from '../../applog/datom-types.ts' import type { ISchemaAdapter, VMAttributeDef } from '../types.ts' import type { ZodObject, ZodType, ZodTypeAny } from 'zod' /** * Create a schema adapter from a Zod object schema. * * Usage: * ```ts * import { z } from 'zod' * import { createZodAdapter } from '@wovin/core/viewmodel/adapters/zod' * * const MySchema = z.object({ * name: z.string(), * age: z.number().optional(), * }) * * const adapter = createZodAdapter(MySchema, 'myEntity') * ``` * * The type parameter `T` is the inferred type of the schema. */ export function createZodAdapter = Record>( zodSchema: ZodObject>, entityPrefix: string, options?: { /** Override at-paths for specific attributes */ atOverrides?: Record /** Default values */ defaults?: Partial /** Property name to at-path mapping function */ toAtPath?: (entityPrefix: string, attrName: string) => string }, ): ISchemaAdapter { const shape = zodSchema.shape as Record const toAtPath = options?.toAtPath ?? ((prefix, name) => `${prefix}/${name}`) const attributeDefs: VMAttributeDef[] = Object.entries(shape).map(([name, fieldSchema]: [string, ZodTypeAny]) => { const isOptional = fieldSchema.isOptional() || fieldSchema.isNullable() const atPath = options?.atOverrides?.[name] ?? toAtPath(entityPrefix, name) return { name, atPath, optional: isOptional, } }) return { getAttributeDefs: () => attributeDefs, getDefaults: () => (options?.defaults ?? {}) as Partial, getEntityPrefix: () => entityPrefix, createValidator: () => (value: unknown): value is T => zodSchema.safeParse(value).success, } }