import { Effect, pipe } from "effect" import { AbstractClass, Class as ConcreteClass, Opaque } from "type-fest" import { z } from "zod" import { DefinedDefaultValuesTag, NewZodSchemaInstanceArgs, NewZodSchemaInstanceInput, TZodSchemaClass } from "." import { Class, ClassesInstances, ClassesStaticMembers, GetClassType, MergeInheritanceTree, MergeInheritanceTreeWithoutOverriding, parseZodTypeEffect } from "./util" export function ZodSchemaClassOf< Super extends AbstractClass, SchemaT extends z.ZodRawShape, SchemaUnknownKeys extends z.UnknownKeysParam, SchemaCatchall extends z.ZodTypeAny, Values extends {}, DefaultValues extends Partial, >( of: Super, { schema, defaultValues }: { schema: z.ZodObject defaultValues: Opaque }, ) { type TZodSchemaClassImpl = TZodSchemaClass< SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, DefaultValues > return class extends (of as unknown as ConcreteClass) { static readonly schema = schema static readonly defaultValues = defaultValues constructor(values: Values) { super() Object.assign(this, values) } static create( ...[values, params]: NewZodSchemaInstanceArgs< NewZodSchemaInstanceInput > ) { return new this( this.schema.parse({ ...this.defaultValues, ...values }, params) ) } static async createPromise( ...[values, params]: NewZodSchemaInstanceArgs< NewZodSchemaInstanceInput > ) { return new this( await this.schema.parseAsync({ ...this.defaultValues, ...values }, params) ) } static createEffect( ...[values, params]: NewZodSchemaInstanceArgs< NewZodSchemaInstanceInput > ) { return pipe( parseZodTypeEffect( this.schema, { ...this.defaultValues, ...values }, params, ), Effect.map(values => new this(values)), ) } static extend< ExtendedSchemaT extends z.ZodRawShape, ExtendedSchemaUnknownKeys extends z.UnknownKeysParam, ExtendedSchemaCatchall extends z.ZodTypeAny, ExtendedValues extends Values, ExtendedDefaultValues extends Partial, >( props: { schema: (props: { schema: z.ZodObject shape: SchemaT }) => z.ZodObject defaultValues: (defaultValues: DefaultValues) => Opaque }, ) { const schema = props.schema({ schema: this.schema, shape: this.schema.shape, }) const defaultValues = props.defaultValues(this.defaultValues) return class extends (this as unknown as ConcreteClass) { static readonly schema = schema static readonly defaultValues = defaultValues } } } as unknown as ( Class< GetClassType, MergeInheritanceTreeWithoutOverriding< ClassesInstances<[ Super, TZodSchemaClassImpl, ]> > & ConstructorParameters > & MergeInheritanceTree< ClassesStaticMembers<[ Super, TZodSchemaClassImpl, ]> > ) } class DefaultRoot {} export function ZodSchemaClass< SchemaT extends z.ZodRawShape, SchemaUnknownKeys extends z.UnknownKeysParam, SchemaCatchall extends z.ZodTypeAny, Values extends {}, DefaultValues extends Partial, >( props: { schema: z.ZodObject defaultValues: Opaque }, ) { return ZodSchemaClassOf(DefaultRoot, props) }