0.1.2 (#3)
All checks were successful
continuous-integration/drone/push Build is passing

Co-authored-by: Julien Valverdé <julien.valverde@mailo.com>
Reviewed-on: https://git.jvalver.de/Thilawyn/zod-schema-class/pulls/3
This commit was merged in pull request #3.
This commit is contained in:
Julien Valverdé
2024-03-11 19:44:21 +01:00
parent 019066bb9c
commit 2e09d9a0e0
56 changed files with 1042 additions and 1778 deletions

View File

@@ -1,54 +0,0 @@
import { z } from "zod"
import { Class, ClassType } from "./util"
export type SchemableClass<
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
Values extends {},
DefaultValues extends Partial<Values>,
Type extends ClassType = "AbstractClass"
> = (
Class<
Type,
{
readonly schema: z.ZodObject<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
Values
>
readonly defaultValues: DefaultValues
} & Values,
Parameters<(values: Values) => never>
> & {
readonly schema: z.ZodObject<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
Values
>
readonly defaultValues: DefaultValues
}
)
export type SchemableClassInput<
Values extends {},
DefaultValues extends Partial<Values>,
> = {
[Key in Exclude<keyof Values, keyof DefaultValues>]: Values[Key]
} & {
[Key in keyof DefaultValues]?: Key extends keyof Values
? Values[Key]
: never
}

View File

@@ -0,0 +1,133 @@
import { Trait, TraitExpressionBuilder, expression } from "@thilawyn/traitify-ts"
import { AbstractClass } from "type-fest"
import { EmptyObject } from "type-fest/source/empty-object"
import { JsonifiableObject } from "type-fest/source/jsonifiable"
import { z } from "zod"
import { JsonifiableZodSchemaObject } from "../traits/JsonifiableZodSchemaObject"
import { ZodSchemaObject } from "../traits/ZodSchemaObject"
export class ZodSchemaClassBuilder<
Superclass extends AbstractClass<object>,
const Traits extends readonly Trait<any, any, any, any>[],
Schemas extends object,
> {
declare ["constructor"]: typeof ZodSchemaClassBuilder
constructor(
protected readonly expression: TraitExpressionBuilder<Superclass, Traits>,
protected readonly schemas: Schemas,
) {}
schema<
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
SchemaWithDefaultValuesT extends z.ZodRawShape,
SchemaWithDefaultValuesUnknownKeys extends z.UnknownKeysParam,
SchemaWithDefaultValuesCatchall extends z.ZodTypeAny,
Values extends object,
PartialValues extends Partial<Values>,
>(
this: ZodSchemaClassBuilder<Superclass, Traits, EmptyObject>,
props: {
schema: z.ZodObject<SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, Values>
schemaWithDefaultValues: (
schema: z.ZodObject<SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, Values>
) => z.ZodObject<SchemaWithDefaultValuesT, SchemaWithDefaultValuesUnknownKeys, SchemaWithDefaultValuesCatchall, Values, PartialValues>
},
) {
const schema = props.schema
const schemaWithDefaultValues = props.schemaWithDefaultValues(props.schema)
abstract class ZodSchemaObjectConstructor {
constructor(values: Values) {
Object.assign(this, values)
}
}
return new this.constructor(
this.expression
.extends(ZodSchemaObjectConstructor as AbstractClass<Values, [values: Values]>)
.expresses(ZodSchemaObject(schema, schemaWithDefaultValues)),
{ schema, schemaWithDefaultValues } as const,
)
}
jsonifiable<
S extends {
readonly schema: z.ZodObject<SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, Values>,
readonly schemaWithDefaultValues: z.ZodObject<SchemaWithDefaultValuesT, SchemaWithDefaultValuesUnknownKeys, SchemaWithDefaultValuesCatchall, Values, PartialValues>,
jsonifySchema?: never
dejsonifySchema?: never
},
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
SchemaWithDefaultValuesT extends z.ZodRawShape,
SchemaWithDefaultValuesUnknownKeys extends z.UnknownKeysParam,
SchemaWithDefaultValuesCatchall extends z.ZodTypeAny,
Values extends object,
PartialValues extends Partial<Values>,
JsonifySchemaT extends z.ZodRawShape,
JsonifySchemaUnknownKeys extends z.UnknownKeysParam,
JsonifySchemaCatchall extends z.ZodTypeAny,
DejsonifySchemaT extends z.ZodRawShape,
DejsonifySchemaUnknownKeys extends z.UnknownKeysParam,
DejsonifySchemaCatchall extends z.ZodTypeAny,
JsonifiedValues extends JsonifiableObject,
>(
this: ZodSchemaClassBuilder<Superclass, Traits, S | {
readonly schema: z.ZodObject<SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, Values>,
readonly schemaWithDefaultValues: z.ZodObject<SchemaWithDefaultValuesT, SchemaWithDefaultValuesUnknownKeys, SchemaWithDefaultValuesCatchall, Values, PartialValues>,
jsonifySchema?: never
dejsonifySchema?: never
}>,
props: {
jsonifySchema: (
schema: z.ZodObject<SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, Values>
) => z.ZodObject<JsonifySchemaT, JsonifySchemaUnknownKeys, JsonifySchemaCatchall, JsonifiedValues, Values>
dejsonifySchema: (
schema: z.ZodObject<SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, Values>
) => z.ZodObject<DejsonifySchemaT, DejsonifySchemaUnknownKeys, DejsonifySchemaCatchall, Values, JsonifiedValues>
},
) {
const jsonifySchema = props.jsonifySchema(this.schemas.schema)
const dejsonifySchema = props.dejsonifySchema(this.schemas.schema)
return new this.constructor(
this.expression.expresses(
JsonifiableZodSchemaObject(
this.schemas.schema,
this.schemas.schemaWithDefaultValues,
jsonifySchema,
dejsonifySchema,
)
),
{ ...this.schemas as S, jsonifySchema, dejsonifySchema } as const,
)
}
toExpression() {
return this.expression
}
}
export const zodSchemaClass = new ZodSchemaClassBuilder(expression, {})

View File

@@ -0,0 +1,196 @@
// import { expression } from "@thilawyn/traitify-ts"
// import { AbstractClass, Simplify } from "type-fest"
// import { JsonifiableObject } from "type-fest/source/jsonifiable"
// import { z } from "zod"
// import { JsonifiableZodSchemaAbstractClass } from "../shapes/JsonifiableZodSchemaClass"
// import { ZodSchemaAbstractClass } from "../shapes/ZodSchemaClass"
// import { Extend, Override, StaticMembers } from "../util"
// export class ZodSchemaClassExtender<
// Superclass extends AbstractClass<object>,
// Subclass extends AbstractClass<object>,
// > {
// declare ["constructor"]: typeof ZodSchemaClassExtender
// constructor(
// readonly superclass: Superclass,
// readonly subclass: Subclass,
// ) {}
// schema<
// Super extends ZodSchemaAbstractClass<any, SuperSchemaT, SuperSchemaUnknownKeys, SuperSchemaCatchall, SuperValues, SuperDefaultValues>,
// SuperSchemaT extends z.ZodRawShape,
// SuperSchemaUnknownKeys extends z.UnknownKeysParam,
// SuperSchemaCatchall extends z.ZodTypeAny,
// SuperValues extends object,
// SuperDefaultValues extends Partial<SuperValues>,
// SchemaT extends z.ZodRawShape,
// SchemaUnknownKeys extends z.UnknownKeysParam,
// SchemaCatchall extends z.ZodTypeAny,
// Values extends SuperValues,
// DefaultValues extends Partial<Values>,
// >(
// this: ZodSchemaClassExtender<
// Super | ZodSchemaAbstractClass<any, SuperSchemaT, SuperSchemaUnknownKeys, SuperSchemaCatchall, SuperValues, SuperDefaultValues>,
// any
// >,
// props: {
// schema: (schema: Super["schema"]) => z.ZodObject<SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, Values>
// defaultValues: (defaultValues: SuperDefaultValues) => DefaultValues
// },
// ) {
// const schema = props.schema(this.superclass.schema)
// const defaultValues = props.defaultValues(this.superclass.defaultValues)
// class Schemas extends (this.superclass as AbstractClass<object, any[]>) {
// static readonly schema = schema
// static readonly defaultValues = defaultValues
// }
// return new this.constructor(
// this.superclass as Super,
// Schemas as unknown as AbstractClass<
// Simplify<
// Extend<[InstanceType<Super>, Values]>
// >,
// [values: Values]
// > &
// Simplify<
// Override<[
// StaticMembers<Super>,
// StaticMembers<typeof Schemas>,
// ]>
// >
// )
// }
// jsonifiable<
// /** Superclass jsonifiable schemas */
// Super extends JsonifiableZodSchemaAbstractClass<
// any,
// SuperJsonifySchemaT,
// SuperJsonifySchemaUnknownKeys,
// SuperJsonifySchemaCatchall,
// SuperDejsonifySchemaT,
// SuperDejsonifySchemaUnknownKeys,
// SuperDejsonifySchemaCatchall,
// SuperJsonifiedValues,
// SuperValues
// >,
// SuperJsonifySchemaT extends z.ZodRawShape,
// SuperJsonifySchemaUnknownKeys extends z.UnknownKeysParam,
// SuperJsonifySchemaCatchall extends z.ZodTypeAny,
// SuperDejsonifySchemaT extends z.ZodRawShape,
// SuperDejsonifySchemaUnknownKeys extends z.UnknownKeysParam,
// SuperDejsonifySchemaCatchall extends z.ZodTypeAny,
// SuperJsonifiedValues extends JsonifiableObject,
// SuperValues extends object,
// /** New schemas */
// Self extends ZodSchemaAbstractClass<any, SelfSchemaT, SelfSchemaUnknownKeys, SelfSchemaCatchall, SelfValues, SelfDefaultValues>,
// SelfSchemaT extends z.ZodRawShape,
// SelfSchemaUnknownKeys extends z.UnknownKeysParam,
// SelfSchemaCatchall extends z.ZodTypeAny,
// SelfValues extends object,
// SelfDefaultValues extends Partial<SelfValues>,
// /* New jsonifiable schemas */
// JsonifySchemaT extends z.ZodRawShape,
// JsonifySchemaUnknownKeys extends z.UnknownKeysParam,
// JsonifySchemaCatchall extends z.ZodTypeAny,
// DejsonifySchemaT extends z.ZodRawShape,
// DejsonifySchemaUnknownKeys extends z.UnknownKeysParam,
// DejsonifySchemaCatchall extends z.ZodTypeAny,
// JsonifiedValues extends SuperJsonifiedValues,
// Values extends SelfValues,
// >(
// this: ZodSchemaClassExtender<
// Super | JsonifiableZodSchemaAbstractClass<
// any,
// SuperJsonifySchemaT,
// SuperJsonifySchemaUnknownKeys,
// SuperJsonifySchemaCatchall,
// SuperDejsonifySchemaT,
// SuperDejsonifySchemaUnknownKeys,
// SuperDejsonifySchemaCatchall,
// SuperJsonifiedValues,
// SuperValues
// >,
// Self | ZodSchemaAbstractClass<any, SelfSchemaT, SelfSchemaUnknownKeys, SelfSchemaCatchall, SelfValues, SelfDefaultValues>
// >,
// props: {
// jsonifySchema: (
// schema: Self["schema"],
// jsonifySchema: Super["jsonifySchema"],
// ) => z.ZodObject<JsonifySchemaT, JsonifySchemaUnknownKeys, JsonifySchemaCatchall, JsonifiedValues, Values>
// dejsonifySchema: (
// schema: Self["schema"],
// dejsonifySchema: Super["dejsonifySchema"],
// ) => z.ZodObject<DejsonifySchemaT, DejsonifySchemaUnknownKeys, DejsonifySchemaCatchall, Values, JsonifiedValues>
// },
// ) {
// const jsonifySchema = props.jsonifySchema(this.subclass.schema, this.superclass.jsonifySchema)
// const dejsonifySchema = props.dejsonifySchema(this.subclass.schema, this.superclass.dejsonifySchema)
// class JsonifiableSchemas extends (this.subclass as AbstractClass<object>) {
// static readonly jsonifySchema = jsonifySchema
// readonly jsonifySchema = jsonifySchema
// static readonly dejsonifySchema = dejsonifySchema
// readonly dejsonifySchema = dejsonifySchema
// }
// return new this.constructor(
// this.superclass as Super,
// JsonifiableSchemas as unknown as AbstractClass<
// Simplify<
// Override<[InstanceType<Self>, JsonifiableSchemas]>
// >,
// ConstructorParameters<
// ZodSchemaAbstractClass<any, SelfSchemaT, SelfSchemaUnknownKeys, SelfSchemaCatchall, SelfValues, SelfDefaultValues>
// >
// > &
// Simplify<
// Override<[
// StaticMembers<Self>,
// StaticMembers<typeof JsonifiableSchemas>,
// ]>
// >,
// )
// }
// toClass() {
// return this.subclass
// }
// toExpressionBuilder() {
// return expression.extends(this.subclass)
// }
// }

View File

@@ -1,8 +0,0 @@
import { Opaque } from "type-fest"
export type DefinedDefaultValuesTag = "@thilawyn/schemable-class/DefinedDefaultValues"
export function defineDefaultValues<T>(values: T) {
return values as Opaque<T, DefinedDefaultValuesTag>
}

View File

@@ -1,95 +0,0 @@
import { AbstractClass, Class as ConcreteClass, Opaque } from "type-fest"
import { z } from "zod"
import { DefinedDefaultValuesTag, SchemableClass } from "."
import { StaticMembers } from "./util"
export function extendSchemableClass<
C extends SchemableClass<
ExtendSchemaT,
ExtendSchemaUnknownKeys,
ExtendSchemaCatchall,
ExtendSchemaValues,
ExtendDefaultValues
>,
ExtendSchemaT extends z.ZodRawShape,
ExtendSchemaUnknownKeys extends z.UnknownKeysParam,
ExtendSchemaCatchall extends z.ZodTypeAny,
ExtendSchemaValues extends {},
ExtendDefaultValues extends Partial<ExtendSchemaValues>,
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
SchemaValues extends ExtendSchemaValues,
DefaultValues extends Partial<SchemaValues>,
>(
extend: C | SchemableClass<
ExtendSchemaT,
ExtendSchemaUnknownKeys,
ExtendSchemaCatchall,
ExtendSchemaValues,
ExtendDefaultValues
>,
props: {
schema: (props: {
schema: z.ZodObject<
ExtendSchemaT,
ExtendSchemaUnknownKeys,
ExtendSchemaCatchall,
ExtendSchemaValues,
ExtendSchemaValues
>
shape: ExtendSchemaT
}) => z.ZodObject<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
SchemaValues,
SchemaValues
>
defaultValues: (defaultValues: ExtendDefaultValues) => Opaque<DefaultValues, DefinedDefaultValuesTag>
},
) {
type Class<T, Arguments extends unknown[]> = (
C extends ConcreteClass<any>
? ConcreteClass<T, Arguments>
: AbstractClass<T, Arguments>
)
const schema = props.schema({
schema: extend.schema,
shape: extend.schema.shape,
})
const defaultValues = props.defaultValues(extend.defaultValues)
return class extends extend {
static readonly schema = schema
readonly schema = schema
static readonly defaultValues = defaultValues
readonly defaultValues = defaultValues
} as unknown as (
Class<
Omit<InstanceType<C>, "schema" | "defaultValues" | keyof ExtendSchemaValues> &
{
readonly schema: z.ZodObject<SchemaT, SchemaUnknownKeys, SchemaCatchall, SchemaValues, SchemaValues>,
readonly defaultValues: DefaultValues,
} &
SchemaValues,
Parameters<(values: SchemaValues) => void>
> &
Omit<StaticMembers<C>, "schema" | "defaultValues"> &
{
readonly schema: z.ZodObject<SchemaT, SchemaUnknownKeys, SchemaCatchall, SchemaValues, SchemaValues>,
readonly defaultValues: DefaultValues,
}
)
}

View File

@@ -1,5 +0,0 @@
export * from "./SchemableClass"
export * from "./defineDefaultValues"
export * from "./extendSchemableClass"
export * from "./makeSchemableClass"
export * from "./newSchemable"

View File

@@ -1,80 +0,0 @@
import { Effect } from "effect"
import { JsonifiableObject } from "type-fest/source/jsonifiable"
import { z } from "zod"
import { SchemableClass } from ".."
import { Class, ClassType } from "../util"
export type JsonifiableSchemableClass<
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
Values extends {},
DefaultValues extends Partial<Values>,
JsonifySchemaT extends z.ZodRawShape,
JsonifySchemaUnknownKeys extends z.UnknownKeysParam,
JsonifySchemaCatchall extends z.ZodTypeAny,
DejsonifySchemaT extends z.ZodRawShape,
DejsonifySchemaUnknownKeys extends z.UnknownKeysParam,
DejsonifySchemaCatchall extends z.ZodTypeAny,
JsonifiedValues extends JsonifiableObject,
Type extends ClassType = "AbstractClass"
> = (
SchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
Type
> &
Class<
Type,
{
readonly jsonifySchema: z.ZodObject<
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
JsonifiedValues,
Values
>
readonly dejsonifySchema: z.ZodObject<
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
Values,
JsonifiedValues
>
jsonify(): JsonifiedValues
jsonifyPromise(): Promise<JsonifiedValues>
jsonifyEffect(): Effect.Effect<never, z.ZodError<Values>, JsonifiedValues>
},
any[]
> & {
readonly jsonifySchema: z.ZodObject<
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
JsonifiedValues,
Values
>
readonly dejsonifySchema: z.ZodObject<
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
Values,
JsonifiedValues
>
}
)

View File

@@ -1,213 +0,0 @@
import { Effect, pipe } from "effect"
import { JsonifiableObject } from "type-fest/source/jsonifiable"
import { z } from "zod"
import { JsonifiableSchemableClass } from "."
import { parseZodTypeEffect } from "../util"
export function dejsonifySchemable<
C extends JsonifiableSchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
JsonifiedValues,
"Class"
>,
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
Values extends {},
DefaultValues extends Partial<Values>,
JsonifySchemaT extends z.ZodRawShape,
JsonifySchemaUnknownKeys extends z.UnknownKeysParam,
JsonifySchemaCatchall extends z.ZodTypeAny,
DejsonifySchemaT extends z.ZodRawShape,
DejsonifySchemaUnknownKeys extends z.UnknownKeysParam,
DejsonifySchemaCatchall extends z.ZodTypeAny,
JsonifiedValues extends JsonifiableObject,
>(
class_: C | JsonifiableSchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
JsonifiedValues,
"Class"
>,
values: JsonifiedValues,
params?: Partial<z.ParseParams>,
) {
return new class_(
class_.dejsonifySchema.parse(values, params)
) as InstanceType<C>
}
export async function dejsonifySchemablePromise<
C extends JsonifiableSchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
JsonifiedValues,
"Class"
>,
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
Values extends {},
DefaultValues extends Partial<Values>,
JsonifySchemaT extends z.ZodRawShape,
JsonifySchemaUnknownKeys extends z.UnknownKeysParam,
JsonifySchemaCatchall extends z.ZodTypeAny,
DejsonifySchemaT extends z.ZodRawShape,
DejsonifySchemaUnknownKeys extends z.UnknownKeysParam,
DejsonifySchemaCatchall extends z.ZodTypeAny,
JsonifiedValues extends JsonifiableObject,
>(
class_: C | JsonifiableSchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
JsonifiedValues,
"Class"
>,
values: JsonifiedValues,
params?: Partial<z.ParseParams>,
) {
return new class_(
await class_.dejsonifySchema.parseAsync(values, params)
) as InstanceType<C>
}
export function dejsonifySchemableEffect<
C extends JsonifiableSchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
JsonifiedValues,
"Class"
>,
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
Values extends {},
DefaultValues extends Partial<Values>,
JsonifySchemaT extends z.ZodRawShape,
JsonifySchemaUnknownKeys extends z.UnknownKeysParam,
JsonifySchemaCatchall extends z.ZodTypeAny,
DejsonifySchemaT extends z.ZodRawShape,
DejsonifySchemaUnknownKeys extends z.UnknownKeysParam,
DejsonifySchemaCatchall extends z.ZodTypeAny,
JsonifiedValues extends JsonifiableObject,
>(
class_: C | JsonifiableSchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
JsonifiedValues,
"Class"
>,
values: JsonifiedValues,
params?: Partial<z.ParseParams>,
) {
return pipe(
parseZodTypeEffect(class_.dejsonifySchema, values, params),
Effect.map(values => new class_(values) as InstanceType<C>),
)
}

View File

@@ -1,4 +0,0 @@
export * from "./JsonifiableSchemableClass"
export * from "./dejsonifySchemable"
export * from "./makeJsonifiableSchemableClass"
export * from "./schema"

View File

@@ -1,134 +0,0 @@
import { Effect } from "effect"
import { AbstractClass, Class as ConcreteClass } from "type-fest"
import { JsonifiableObject } from "type-fest/source/jsonifiable"
import { z } from "zod"
import { SchemableClass } from ".."
import { StaticMembers, parseZodTypeEffect } from "../util"
export function makeJsonifiableSchemableClass<
C extends SchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues
>,
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
Values extends {},
DefaultValues extends Partial<Values>,
JsonifySchemaT extends z.ZodRawShape,
JsonifySchemaUnknownKeys extends z.UnknownKeysParam,
JsonifySchemaCatchall extends z.ZodTypeAny,
DejsonifySchemaT extends z.ZodRawShape,
DejsonifySchemaUnknownKeys extends z.UnknownKeysParam,
DejsonifySchemaCatchall extends z.ZodTypeAny,
JsonifiedValues extends JsonifiableObject,
>(
extend: C | SchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues
>,
props: {
jsonifySchema: (props: {
schema: z.ZodObject<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
Values
>
shape: SchemaT
}) => z.ZodObject<
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
JsonifiedValues,
Values
>
dejsonifySchema: (props: {
schema: z.ZodObject<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
Values
>
shape: SchemaT
}) => z.ZodObject<
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
Values,
JsonifiedValues
>
},
) {
type Class<T, Arguments extends unknown[]> = (
C extends ConcreteClass<any>
? ConcreteClass<T, Arguments>
: AbstractClass<T, Arguments>
)
const jsonifySchema = props.jsonifySchema({
schema: extend.schema,
shape: extend.schema.shape,
})
const dejsonifySchema = props.dejsonifySchema({
schema: extend.schema,
shape: extend.schema.shape,
})
return class extends extend {
static readonly jsonifySchema = jsonifySchema
readonly jsonifySchema = jsonifySchema
static readonly dejsonifySchema = dejsonifySchema
readonly dejsonifySchema = dejsonifySchema
jsonify() {
return this.jsonifySchema.parse(this)
}
jsonifyPromise() {
return this.jsonifySchema.parseAsync(this)
}
jsonifyEffect() {
return parseZodTypeEffect(this.jsonifySchema, this)
}
} as unknown as (
Class<
InstanceType<C> & {
readonly jsonifySchema: z.ZodObject<JsonifySchemaT, JsonifySchemaUnknownKeys, JsonifySchemaCatchall, JsonifiedValues, Values>,
readonly dejsonifySchema: z.ZodObject<DejsonifySchemaT, DejsonifySchemaUnknownKeys, DejsonifySchemaCatchall, Values, JsonifiedValues>,
jsonify(): JsonifiedValues
jsonifyPromise(): Promise<JsonifiedValues>
jsonifyEffect(): Effect.Effect<never, z.ZodError<Values>, JsonifiedValues>
},
ConstructorParameters<C>
> &
StaticMembers<C> & {
readonly jsonifySchema: z.ZodObject<JsonifySchemaT, JsonifySchemaUnknownKeys, JsonifySchemaCatchall, JsonifiedValues, Values>,
readonly dejsonifySchema: z.ZodObject<DejsonifySchemaT, DejsonifySchemaUnknownKeys, DejsonifySchemaCatchall, Values, JsonifiedValues>,
}
)
}

View File

@@ -1,4 +0,0 @@
export * from "./bigint"
export * from "./date"
export * from "./decimal"
export * from "./schemable"

View File

@@ -1,139 +0,0 @@
import { JsonifiableObject } from "type-fest/source/jsonifiable"
import { z } from "zod"
import { JsonifiableSchemableClass } from ".."
export function jsonifySchemableSchema<
C extends JsonifiableSchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
JsonifiedValues,
"Class"
>,
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
Values extends {},
DefaultValues extends Partial<Values>,
JsonifySchemaT extends z.ZodRawShape,
JsonifySchemaUnknownKeys extends z.UnknownKeysParam,
JsonifySchemaCatchall extends z.ZodTypeAny,
DejsonifySchemaT extends z.ZodRawShape,
DejsonifySchemaUnknownKeys extends z.UnknownKeysParam,
DejsonifySchemaCatchall extends z.ZodTypeAny,
JsonifiedValues extends JsonifiableObject,
S extends z.ZodType<InstanceType<C>, z.ZodTypeDef, InstanceType<C>>,
>(
class_: C | JsonifiableSchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
JsonifiedValues,
"Class"
>,
schema: S,
) {
return schema.pipe(class_.jsonifySchema)
}
export function dejsonifySchemableSchema<
C extends JsonifiableSchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
JsonifiedValues,
"Class"
>,
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
Values extends {},
DefaultValues extends Partial<Values>,
JsonifySchemaT extends z.ZodRawShape,
JsonifySchemaUnknownKeys extends z.UnknownKeysParam,
JsonifySchemaCatchall extends z.ZodTypeAny,
DejsonifySchemaT extends z.ZodRawShape,
DejsonifySchemaUnknownKeys extends z.UnknownKeysParam,
DejsonifySchemaCatchall extends z.ZodTypeAny,
JsonifiedValues extends JsonifiableObject,
S extends z.ZodType<InstanceType<C>, z.ZodTypeDef, InstanceType<C>>,
>(
class_: C | JsonifiableSchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
JsonifiedValues,
"Class"
>,
schema: S,
) {
return class_.dejsonifySchema.transform(v => new class_(v)).pipe(schema)
}

View File

@@ -1,90 +0,0 @@
import { Class } from "type-fest"
import { z } from "zod"
/**
* Configuration for creating a schemable object with validation schemas.
* @template Values - The type representing the expected values.
* @template Input - The type representing the input values.
* @template SchemaT - The type representing the base validation schema.
* @template SchemaUnknownKeys - The type representing the unknown keys behavior in the base validation schema.
* @template SchemaCatchall - The type representing the catchall behavior in the base validation schema.
* @template SchemaWithDefaultValuesT - The type representing the validation schema with default values.
* @template SchemaWithDefaultValuesUnknownKeys - The type representing the unknown keys behavior in the validation schema with default values.
* @template SchemaWithDefaultValuesCatchall - The type representing the catchall behavior in the validation schema with default values.
*/
export type SchemableConfig<
Values extends {} = {},
Input extends {} = {},
SchemaT extends z.ZodRawShape = z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam = z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny = z.ZodTypeAny,
SchemaWithDefaultValuesT extends z.ZodRawShape = z.ZodRawShape,
SchemaWithDefaultValuesUnknownKeys extends z.UnknownKeysParam = z.UnknownKeysParam,
SchemaWithDefaultValuesCatchall extends z.ZodTypeAny = z.ZodTypeAny,
> = {
readonly values: Values
readonly input: Input
readonly schema: z.ZodObject<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
Values
>
readonly schemaWithDefaultValues: z.ZodObject<
SchemaWithDefaultValuesT,
SchemaWithDefaultValuesUnknownKeys,
SchemaWithDefaultValuesCatchall,
Values,
Input
>
}
/**
* Represents a class with validation schemas.
* @template $Config - The configuration type for the schemable object.
*/
export type SchemableClass<
$Config extends SchemableConfig
> = (
Class<
SchemableObject<$Config>,
SchemableClassConstructorParams<$Config>
> & {
readonly $schemableConfig: $Config
readonly schema: $Config["schema"]
readonly schemaWithDefaultValues: $Config["schemaWithDefaultValues"]
}
)
/**
* Represents the constructor parameters for the schemable object class.
* @template $Config - The configuration type for the schemable object.
*/
export type SchemableClassConstructorParams<
$Config extends SchemableConfig
> = (
Parameters<
(data: $Config["values"]) => void
>
)
/**
* Represents an object with validation schemas.
* @template $Config - The configuration type for the schemable object.
*/
export type SchemableObject<
$Config extends SchemableConfig
> = (
{
readonly $schemableConfig: $Config
readonly schema: $Config["schema"]
readonly schemaWithDefaultValues: $Config["schemaWithDefaultValues"]
} & $Config["values"]
)

View File

@@ -1,3 +0,0 @@
export * from "./SchemableClass"
export * from "./makeSchemableClass"
export * from "./newSchemable"

View File

@@ -1,66 +0,0 @@
import { Effect } from "effect"
import { Class } from "type-fest"
import { JsonifiableObject } from "type-fest/source/jsonifiable"
import { z } from "zod"
import { SchemableClassConstructorParams, SchemableConfig } from ".."
export type JsonifiableSchemableConfig<
$SchemableConfig extends SchemableConfig = SchemableConfig,
JsonifiedValues extends JsonifiableObject = {},
JsonifySchemaT extends z.ZodRawShape = z.ZodRawShape,
JsonifySchemaUnknownKeys extends z.UnknownKeysParam = z.UnknownKeysParam,
JsonifySchemaCatchall extends z.ZodTypeAny = z.ZodTypeAny,
DejsonifySchemaT extends z.ZodRawShape = z.ZodRawShape,
DejsonifySchemaUnknownKeys extends z.UnknownKeysParam = z.UnknownKeysParam,
DejsonifySchemaCatchall extends z.ZodTypeAny = z.ZodTypeAny,
> = {
readonly $schemableConfig: $SchemableConfig
readonly jsonifiedValues: JsonifiedValues
readonly jsonifySchema: z.ZodObject<
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
JsonifiedValues,
$SchemableConfig["values"]
>
readonly dejsonifySchema: z.ZodObject<
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
$SchemableConfig["values"],
JsonifiedValues
>
}
export type JsonifiableSchemableClass<
$Config extends JsonifiableSchemableConfig
> = (
Class<
JsonifiableSchemableObject<$Config>,
SchemableClassConstructorParams<$Config["$schemableConfig"]>
> & {
readonly $jsonifiableSchemableConfig: $Config
readonly jsonifySchema: $Config["jsonifySchema"]
readonly dejsonifySchema: $Config["dejsonifySchema"]
}
)
export type JsonifiableSchemableObject<
$Config extends JsonifiableSchemableConfig
> = {
readonly $jsonifiableSchemableConfig: $Config
readonly jsonifySchema: $Config["jsonifySchema"]
readonly dejsonifySchema: $Config["dejsonifySchema"]
jsonify(): $Config["jsonifiedValues"]
jsonifyPromise(): Promise<$Config["jsonifiedValues"]>
jsonifyEffect(): Effect.Effect<never, z.ZodError<$Config["$schemableConfig"]["values"]>, $Config["jsonifiedValues"]>
}

View File

@@ -1,47 +0,0 @@
import { Effect, pipe } from "effect"
import { z } from "zod"
import { JsonifiableSchemableClass, JsonifiableSchemableConfig } from "."
import { parseZodTypeEffect } from "../util"
export const dejsonifySchemable = <
C extends JsonifiableSchemableClass<$Config>,
$Config extends JsonifiableSchemableConfig,
>(
class_: C | JsonifiableSchemableClass<$Config>,
values: $Config["jsonifiedValues"],
params?: Partial<z.ParseParams>,
) =>
new class_(class_.dejsonifySchema.parse(values, params)) as InstanceType<C>
export const dejsonifySchemablePromise = async <
C extends JsonifiableSchemableClass<$Config>,
$Config extends JsonifiableSchemableConfig,
>(
class_: C | JsonifiableSchemableClass<$Config>,
values: $Config["jsonifiedValues"],
params?: Partial<z.ParseParams>,
) =>
new class_(await class_.dejsonifySchema.parseAsync(values, params)) as InstanceType<C>
export const dejsonifySchemableEffect = <
C extends JsonifiableSchemableClass<$Config>,
$Config extends JsonifiableSchemableConfig,
>(
class_: C | JsonifiableSchemableClass<$Config>,
values: $Config["jsonifiedValues"],
params?: Partial<z.ParseParams>,
) => pipe(
parseZodTypeEffect<
z.output<typeof class_.dejsonifySchema>,
z.input<typeof class_.dejsonifySchema>
>(
class_.dejsonifySchema,
values,
params,
),
Effect.map(values => new class_(values) as InstanceType<C>),
)

View File

@@ -1,4 +0,0 @@
export * from "./JsonifiableSchemableClass"
export * from "./dejsonifySchemable"
export * from "./makeJsonifiableSchemableClass"
export * from "./schema"

View File

@@ -1,98 +0,0 @@
import { Class } from "type-fest"
import { JsonifiableObject } from "type-fest/source/jsonifiable"
import { z } from "zod"
import { JsonifiableSchemableClass, JsonifiableSchemableConfig, JsonifiableSchemableObject } from "."
import { SchemableClass, SchemableConfig } from ".."
import { StaticMembers, parseZodTypeEffect } from "../util"
export function makeJsonifiableSchemableClass<
C extends SchemableClass<$SchemableConfig>,
$SchemableConfig extends SchemableConfig,
JsonifiedValues extends JsonifiableObject,
JsonifySchemaT extends z.ZodRawShape,
JsonifySchemaUnknownKeys extends z.UnknownKeysParam,
JsonifySchemaCatchall extends z.ZodTypeAny,
DejsonifySchemaT extends z.ZodRawShape,
DejsonifySchemaUnknownKeys extends z.UnknownKeysParam,
DejsonifySchemaCatchall extends z.ZodTypeAny,
>(
class_: C | SchemableClass<$SchemableConfig>,
props: {
jsonifySchema: (props: {
schema: $SchemableConfig["schema"]
s: $SchemableConfig["schema"]["shape"]
}) => z.ZodObject<
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
JsonifiedValues,
$SchemableConfig["values"]
>
dejsonifySchema: (props: {
schema: $SchemableConfig["schema"]
s: $SchemableConfig["schema"]["shape"]
}) => z.ZodObject<
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
$SchemableConfig["values"],
JsonifiedValues
>
},
) {
const jsonifySchema = props.jsonifySchema({
schema: class_.schema,
s: class_.schema.shape,
})
const dejsonifySchema = props.dejsonifySchema({
schema: class_.schema,
s: class_.schema.shape,
})
const $jsonifiableSchemableConfig = {
$schemableConfig: class_.$schemableConfig,
jsonifiedValues: undefined as unknown as JsonifiedValues,
jsonifySchema: undefined as unknown as typeof jsonifySchema,
dejsonifySchema: undefined as unknown as typeof dejsonifySchema,
} as const satisfies JsonifiableSchemableConfig
const jsonifiableClass = class JsonifiableSchemableObject extends class_ {
static readonly $jsonifiableSchemableConfig = $jsonifiableSchemableConfig
static readonly jsonifySchema = jsonifySchema
static readonly dejsonifySchema = dejsonifySchema
readonly $jsonifiableSchemableConfig = $jsonifiableSchemableConfig
readonly jsonifySchema = jsonifySchema
readonly dejsonifySchema = dejsonifySchema
jsonify() {
return this.jsonifySchema.parse(this)
}
jsonifyPromise() {
return this.jsonifySchema.parseAsync(this)
}
jsonifyEffect() {
return parseZodTypeEffect(this.jsonifySchema, this)
}
} satisfies JsonifiableSchemableClass<typeof $jsonifiableSchemableConfig>
return jsonifiableClass as unknown as (
Class<
InstanceType<C> & JsonifiableSchemableObject<typeof $jsonifiableSchemableConfig>,
ConstructorParameters<C>
> &
StaticMembers<C> &
StaticMembers<JsonifiableSchemableClass<typeof $jsonifiableSchemableConfig>>
)
}

View File

@@ -1,18 +0,0 @@
import { z } from "zod"
export const jsonifyBigIntSchema = <S extends z.ZodBigInt>(schema: S) =>
schema.transform(v => v.toString())
export const dejsonifyBigIntSchema = <S extends z.ZodBigInt>(schema: S) =>
z
.string()
.transform(v => {
try {
return BigInt(v)
}
catch (e) {
return v
}
})
.pipe(schema)

View File

@@ -1,18 +0,0 @@
import { z } from "zod"
export const jsonifyDateSchema = <S extends z.ZodDate>(schema: S) =>
schema.transform(v => v.toString())
export const dejsonifyDateSchema = <S extends z.ZodDate>(schema: S) =>
z
.string()
.transform(v => {
try {
return new Date(v)
}
catch (e) {
return v
}
})
.pipe(schema)

View File

@@ -1,19 +0,0 @@
import { Decimal } from "decimal.js"
import { z } from "zod"
export const jsonifyDecimalSchema = <S extends z.ZodType<Decimal, z.ZodTypeDef, Decimal>>(schema: S) =>
schema.transform(v => v.toJSON())
export const dejsonifyDecimalSchema = <S extends z.ZodType<Decimal, z.ZodTypeDef, Decimal>>(schema: S) =>
z
.string()
.transform(v => {
try {
return new Decimal(v)
}
catch (e) {
return v
}
})
.pipe(schema)

View File

@@ -1,4 +0,0 @@
export * from "./bigint"
export * from "./date"
export * from "./decimal"
export * from "./schemable"

View File

@@ -1,25 +0,0 @@
import { z } from "zod"
import { JsonifiableSchemableClass, JsonifiableSchemableConfig } from ".."
// TODO: try to find a way to get rid of the 'class_' arg
export const jsonifySchemableSchema = <
C extends JsonifiableSchemableClass<$Config>,
$Config extends JsonifiableSchemableConfig,
S extends z.ZodType<InstanceType<C>, z.ZodTypeDef, InstanceType<C>>,
>(
class_: C | JsonifiableSchemableClass<$Config>,
schema: S,
) =>
schema.pipe(class_.jsonifySchema)
// TODO: try to find a way to get rid of the 'class_' arg
export const dejsonifySchemableSchema = <
C extends JsonifiableSchemableClass<$Config>,
$Config extends JsonifiableSchemableConfig,
S extends z.ZodType<InstanceType<C>, z.ZodTypeDef, InstanceType<C>>,
>(
class_: C | JsonifiableSchemableClass<$Config>,
schema: S,
) =>
class_.dejsonifySchema.transform(v => new class_(v)).pipe(schema)

View File

@@ -1,49 +0,0 @@
import { z } from "zod"
import { SchemableClass, SchemableConfig } from "."
import { zodObjectRemoveDefaults } from "./util"
export function makeSchemableClass<
SchemaWithDefaultValuesT extends z.ZodRawShape,
SchemaWithDefaultValuesUnknownKeys extends z.UnknownKeysParam,
SchemaWithDefaultValuesCatchall extends z.ZodTypeAny,
SchemaWithDefaultValuesOutput extends SchemaWithDefaultValuesInput, // TODO: apply "StripSchemaInputDefaults"?
SchemaWithDefaultValuesInput extends {},
>(
{
schema: schemaWithDefaultValues
}: {
schema: z.ZodObject<
SchemaWithDefaultValuesT,
SchemaWithDefaultValuesUnknownKeys,
SchemaWithDefaultValuesCatchall,
SchemaWithDefaultValuesOutput,
SchemaWithDefaultValuesInput
>
}
) {
const schema = zodObjectRemoveDefaults(schemaWithDefaultValues)
const $schemableConfig = {
values: undefined as unknown as z.output<typeof schemaWithDefaultValues>,
input: undefined as unknown as z.input<typeof schemaWithDefaultValues>,
schema: undefined as unknown as typeof schema,
schemaWithDefaultValues: undefined as unknown as typeof schemaWithDefaultValues,
} as const satisfies SchemableConfig
return class SchemableObject {
static readonly $schemableConfig = $schemableConfig
static readonly schema = schema
static readonly schemaWithDefaultValues = schemaWithDefaultValues
readonly $schemableConfig = $schemableConfig
readonly schema = schema
readonly schemaWithDefaultValues = schemaWithDefaultValues
constructor(data: z.output<typeof schema>) {
Object.assign(this, data)
}
} as SchemableClass<typeof $schemableConfig>
}

View File

@@ -1,77 +0,0 @@
import { Effect, pipe } from "effect"
import { HasRequiredKeys } from "type-fest"
import { z } from "zod"
import { SchemableClass, SchemableConfig } from "."
import { parseZodTypeEffect } from "./util"
type ParamsArgs = [] | [Partial<z.ParseParams>]
type NewSchemableArgs<Input extends object> =
HasRequiredKeys<Input> extends true
? [Input, ...ParamsArgs]
: [] | [Input, ...ParamsArgs]
/**
* Creates a new instance of a SchemableClass with default values.
*
* @param class_ - The SchemableClass.
* @param values - The values to be parsed and used to create the instance.
* @param params - Optional parameters for parsing.
* @returns A new instance of the specified SchemableClass.
*/
export const newSchemable = <
C extends SchemableClass<$Config>,
$Config extends SchemableConfig,
>(
class_: C | SchemableClass<$Config>,
...[values, params]: NewSchemableArgs<$Config["input"]>
) =>
new class_(class_.schemaWithDefaultValues.parse(values || {}, params)) as InstanceType<C>
/**
* Creates a new instance of a SchemableClass with default values asynchronously.
*
* @param class_ - The SchemableClass.
* @param values - The values to be parsed and used to create the instance.
* @param params - Optional parameters for parsing.
* @returns A Promise resolving to a new instance of the specified SchemableClass.
*/
export const newSchemablePromise = async <
C extends SchemableClass<$Config>,
$Config extends SchemableConfig,
>(
class_: C | SchemableClass<$Config>,
...[values, params]: NewSchemableArgs<$Config["input"]>
) =>
new class_(await class_.schemaWithDefaultValues.parseAsync(values || {}, params)) as InstanceType<C>
/**
* Creates a new instance of a SchemableClass with default values as an Effect.
*
* @param class_ - The SchemableClass.
* @param values - The values to be parsed and used to create the instance.
* @param params - Optional parameters for parsing.
* @returns An Effect producing a new instance of the specified SchemableClass.
*/
export const newSchemableEffect = <
C extends SchemableClass<$Config>,
$Config extends SchemableConfig,
>(
class_: C | SchemableClass<$Config>,
...[values, params]: NewSchemableArgs<$Config["input"]>
) => pipe(
parseZodTypeEffect<
z.output<typeof class_.schemaWithDefaultValues>,
z.input<typeof class_.schemaWithDefaultValues>
>(
class_.schemaWithDefaultValues,
values || {},
params,
),
Effect.map(values => new class_(values) as InstanceType<C>),
)

View File

@@ -1,64 +0,0 @@
import { z } from "zod"
import { makeSchemableClass, newSchemable } from "."
import { dejsonifyBigIntSchema, dejsonifySchemable, dejsonifySchemableSchema, jsonifyBigIntSchema, jsonifySchemableSchema, makeJsonifiableSchemableClass } from "./jsonifiable"
const GroupSchema = z.object({
/** Group ID */
id: z.bigint(),
/** Group name */
name: z.string(),
})
const GroupSchemableObject = makeSchemableClass({ schema: GroupSchema })
const GroupJsonifiableSchemableObject = makeJsonifiableSchemableClass(GroupSchemableObject, {
jsonifySchema: ({ schema, s }) => schema.extend({
id: jsonifyBigIntSchema(s.id)
}),
dejsonifySchema: ({ schema, s }) => schema.extend({
id: dejsonifyBigIntSchema(s.id)
}),
})
class Group extends GroupJsonifiableSchemableObject {}
const UserSchema = z.object({
/** User ID */
id: z.bigint(),
/** Name string */
name: z.string(),
/** User group */
group: z.instanceof(Group),
})
const UserSchemableObject = makeSchemableClass({ schema: UserSchema })
const UserJsonifiableSchemableObject = makeJsonifiableSchemableClass(UserSchemableObject, {
jsonifySchema: ({ schema, s }) => schema.extend({
id: jsonifyBigIntSchema(s.id),
group: jsonifySchemableSchema(Group, s.group),
}),
dejsonifySchema: ({ schema, s }) => schema.extend({
id: dejsonifyBigIntSchema(s.id),
group: dejsonifySchemableSchema(Group, s.group),
}),
})
class User extends UserJsonifiableSchemableObject {}
const group1 = new Group({ id: 1n, name: "Group 1" })
const user1 = new User({ id: 1n, name: "User 1", group: group1 })
const user2 = newSchemable(User, { id: 2n, name: "User 2", group: group1 })
const jsonifiedUser2 = user2.jsonify()
const dejsonifiedUser2 = dejsonifySchemable(User, jsonifiedUser2)
console.log(dejsonifiedUser2)

View File

@@ -1,82 +0,0 @@
import { Effect, pipe } from "effect"
import { mapValues } from "lodash-es"
import { z } from "zod"
/**
* Represents the static members of a class.
* @template C - The class type.
*/
export type StaticMembers<C> = {
[Key in keyof C as Key extends "prototype" ? never : Key]: C[Key]
}
/**
* Removes default values from a ZodObject schema and returns a new schema.
*
* @param schema - The ZodObject schema to process.
* @returns A new ZodObject schema with default values removed.
*/
export const zodObjectRemoveDefaults = <
T extends z.ZodRawShape,
UnknownKeys extends z.UnknownKeysParam,
Catchall extends z.ZodTypeAny,
Output extends {},
Input extends {},
>(
schema: z.ZodObject<
T,
UnknownKeys,
Catchall,
Output,
Input
>
) =>
schema.extend(zodShapeRemoveDefaults(schema.shape))
/**
* Removes default values from a ZodObject shape and returns a new shape.
*
* @param shape - The ZodObject shape to process.
* @returns A new shape with default values removed.
*/
export const zodShapeRemoveDefaults = <
Shape extends z.ZodRawShape
>(
shape: Shape
): {
[K in keyof Shape]:
Shape[K] extends z.ZodDefault<infer T>
? T
: Shape[K]
} =>
mapValues(shape, el =>
el instanceof z.ZodDefault
? el.removeDefault()
: el
)
/**
* Parses a value using a ZodType schema wrapped in an Effect monad.
*
* @param schema - The ZodType schema to use for parsing.
* @param args - The arguments to pass to the `safeParseAsync` method of the schema.
* @returns An Effect monad representing the parsing result.
*/
export const parseZodTypeEffect = <
Output,
Input,
>(
schema: z.ZodType<Output, z.ZodTypeDef, Input>,
...args: Parameters<typeof schema.safeParseAsync>
) => pipe(
Effect.promise(() => schema.safeParseAsync(...args)),
Effect.flatMap(response =>
response.success
? Effect.succeed(response.data)
: Effect.fail(response.error)
),
)

4
src/lib.ts Normal file
View File

@@ -0,0 +1,4 @@
export { ZodSchemaClassBuilder, zodSchemaClass } from "./builders/ZodSchemaClassBuilder"
export { JsonifiableZodSchemaObject } from "./traits/JsonifiableZodSchemaObject"
export { MobXObservableZodSchemaObject } from "./traits/MobXObservableZodSchemaObject"
export { ZodSchemaObject, ZodSchemaObjectTrait } from "./traits/ZodSchemaObject"

View File

@@ -1,82 +0,0 @@
import { AbstractClass, Class as ConcreteClass, Opaque } from "type-fest"
import { z } from "zod"
import { DefinedDefaultValuesTag } from "."
import { StaticMembers } from "./util"
export function makeSchemableClassFrom<
C extends AbstractClass<{
schema?: never
defaultValues?: never
}, []> & {
schema?: never
defaultValues?: never
},
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
Values extends {},
DefaultValues extends Partial<Values>,
>(
extend: C,
{ schema, defaultValues }: {
schema: z.ZodObject<SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, Values>
defaultValues: Opaque<DefaultValues, DefinedDefaultValuesTag>
},
) {
type Class<T, Arguments extends unknown[]> = (
C extends ConcreteClass<any>
? ConcreteClass<T, Arguments>
: AbstractClass<T, Arguments>
)
return class extends (extend as unknown as ConcreteClass<any, []>) {
static readonly schema = schema
readonly schema = schema
static readonly defaultValues = defaultValues
readonly defaultValues = defaultValues
constructor(values: Values) {
super()
Object.assign(this, values)
}
} as unknown as (
Class<
InstanceType<C> &
{
readonly schema: z.ZodObject<SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, Values>,
readonly defaultValues: DefaultValues,
} &
Values,
Parameters<(values: Values) => void>
> &
StaticMembers<C> &
{
readonly schema: z.ZodObject<SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, Values>,
readonly defaultValues: DefaultValues,
}
)
}
export function makeSchemableClass<
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
Values extends {},
DefaultValues extends Partial<Values>,
>(
props: {
schema: z.ZodObject<SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, Values>
defaultValues: Opaque<DefaultValues, DefinedDefaultValuesTag>
}
) {
return makeSchemableClassFrom(Object, props)
}

View File

@@ -1,127 +0,0 @@
import { Effect, pipe } from "effect"
import { HasRequiredKeys } from "type-fest"
import { z } from "zod"
import { SchemableClass, SchemableClassInput } from "."
import { parseZodTypeEffect } from "./util"
type ParamsArgs = [] | [params: Partial<z.ParseParams>]
type NewSchemableArgs<Input extends object> =
HasRequiredKeys<Input> extends true
? [values: Input, ...args: ParamsArgs]
: [] | [values: Input, ...args: ParamsArgs]
export function newSchemable<
C extends SchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
"Class"
>,
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
Values extends {},
DefaultValues extends Partial<Values>,
>(
class_: C | SchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
"Class"
>,
...[values, params]: NewSchemableArgs<
SchemableClassInput<Values, DefaultValues>
>
) {
return new class_(
class_.schema.parse({ ...class_.defaultValues, ...values }, params)
) as InstanceType<C>
}
export async function newSchemablePromise<
C extends SchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
"Class"
>,
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
Values extends {},
DefaultValues extends Partial<Values>,
>(
class_: C | SchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
"Class"
>,
...[values, params]: NewSchemableArgs<
SchemableClassInput<Values, DefaultValues>
>
) {
return new class_(
await class_.schema.parseAsync({ ...class_.defaultValues, ...values }, params)
) as InstanceType<C>
}
export function newSchemableEffect<
C extends SchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
"Class"
>,
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
Values extends {},
DefaultValues extends Partial<Values>,
>(
class_: C | SchemableClass<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
Values,
DefaultValues,
"Class"
>,
...[values, params]: NewSchemableArgs<
SchemableClassInput<Values, DefaultValues>
>
) {
return pipe(
parseZodTypeEffect(
class_.schema,
{ ...class_.defaultValues, ...values },
params,
),
Effect.map(values => new class_(values) as InstanceType<C>),
)
}

View File

@@ -1 +0,0 @@
export * from "./makeSchemableClassObservable"

View File

@@ -1,29 +0,0 @@
import { mapValues } from "lodash-es"
import { makeObservable, observable } from "mobx"
import { AbstractConstructor } from "type-fest"
import { z } from "zod"
import { SchemableClass } from ".."
export function makeSchemableClassObservable<
C extends SchemableClass<SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, DefaultValues>,
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
Values extends {},
DefaultValues extends Partial<Values>,
>(
extend: C | SchemableClass<SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, DefaultValues>
) {
return class extends (extend as AbstractConstructor<any>) {
constructor(...args: any[]) {
super(...args)
makeObservable(this,
mapValues(this.schema.shape, () => observable)
)
}
} as unknown as C
}

View File

@@ -0,0 +1,6 @@
import { option } from "./option"
export const effect = {
option,
} as const

View File

@@ -0,0 +1,21 @@
import { Option } from "effect"
import { identity } from "lodash-es"
import { z } from "zod"
export const option = {
option: <S extends z.ZodSchema>(schema: S) =>
z.union([option.some(schema), option.none(schema)]),
some: <S extends z.ZodSchema>(schema: S) => z
.custom<Option.Some<z.output<S>>>(v => Option.isOption(v) && Option.isSome(v), "Not an Option")
.pipe(z.object({ value: schema }).passthrough())
.transform<Option.Some<z.output<S>>>(identity),
none: <S extends z.ZodSchema | unknown = unknown>(_schema?: S) =>
z.custom<Option.None<
S extends z.ZodSchema
? z.output<S>
: unknown
>>(v => Option.isOption(v) && Option.isNone(v), "Not an Option"),
} as const

View File

@@ -1,9 +1,9 @@
import { identity } from "lodash-es"
import { Opaque } from "type-fest"
import { z } from "zod"
import { identity } from "../../util"
export type JsonifiedBigInt = Opaque<string, "@thilawyn/schemable-class/JsonifiedBigInt">
export type JsonifiedBigInt = Opaque<string, "@thilawyn/zod-schema-class/JsonifiedBigInt">
export function jsonifyBigIntSchema<S extends z.ZodBigInt>(schema: S) {

View File

@@ -1,9 +1,9 @@
import { identity } from "lodash-es"
import { Opaque } from "type-fest"
import { z } from "zod"
import { identity } from "../../util"
export type JsonifiedDate = Opaque<string, "@thilawyn/schemable-class/JsonifiedDate">
export type JsonifiedDate = Opaque<string, "@thilawyn/zod-schema-class/JsonifiedDate">
export function jsonifyDateSchema<S extends z.ZodDate>(schema: S) {

View File

@@ -1,10 +1,10 @@
import { Decimal } from "decimal.js"
import { identity } from "lodash-es"
import { Opaque } from "type-fest"
import { z } from "zod"
import { identity } from "../../util"
export type JsonifiedDecimal = Opaque<string, "@thilawyn/schemable-class/JsonifiedDecimal">
export type JsonifiedDecimal = Opaque<string, "@thilawyn/zod-schema-class/JsonifiedDecimal">
export function jsonifyDecimalSchema<

View File

@@ -0,0 +1,16 @@
import { dejsonifyBigIntSchema, jsonifyBigIntSchema } from "./bigint"
import { dejsonifyDateSchema, jsonifyDateSchema } from "./date"
import { dejsonifyDecimalSchema, jsonifyDecimalSchema } from "./decimal"
export const jsonify = {
bigint: jsonifyBigIntSchema,
date: jsonifyDateSchema,
decimal: jsonifyDecimalSchema,
} as const
export const dejsonify = {
bigint: dejsonifyBigIntSchema,
date: dejsonifyDateSchema,
decimal: dejsonifyDecimalSchema,
} as const

2
src/schema/lib.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from "./effect"
export * from "./jsonifiable"

View File

@@ -0,0 +1,78 @@
import { AbstractClass, Class } from "type-fest"
import { JsonifiableObject } from "type-fest/source/jsonifiable"
import { z } from "zod"
export type JsonifiableZodSchemaClass<
Instance extends Values,
JsonifySchemaT extends z.ZodRawShape,
JsonifySchemaUnknownKeys extends z.UnknownKeysParam,
JsonifySchemaCatchall extends z.ZodTypeAny,
DejsonifySchemaT extends z.ZodRawShape,
DejsonifySchemaUnknownKeys extends z.UnknownKeysParam,
DejsonifySchemaCatchall extends z.ZodTypeAny,
JsonifiedValues extends JsonifiableObject,
Values extends object,
> = (
Class<Instance, [values: Values]> &
JsonifiableZodSchemas<
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
JsonifiedValues,
Values
>
)
export type JsonifiableZodSchemaAbstractClass<
Instance extends Values,
JsonifySchemaT extends z.ZodRawShape,
JsonifySchemaUnknownKeys extends z.UnknownKeysParam,
JsonifySchemaCatchall extends z.ZodTypeAny,
DejsonifySchemaT extends z.ZodRawShape,
DejsonifySchemaUnknownKeys extends z.UnknownKeysParam,
DejsonifySchemaCatchall extends z.ZodTypeAny,
JsonifiedValues extends JsonifiableObject,
Values extends object,
> = (
AbstractClass<Instance, [values: Values]> &
JsonifiableZodSchemas<
JsonifySchemaT,
JsonifySchemaUnknownKeys,
JsonifySchemaCatchall,
DejsonifySchemaT,
DejsonifySchemaUnknownKeys,
DejsonifySchemaCatchall,
JsonifiedValues,
Values
>
)
export type JsonifiableZodSchemas<
JsonifySchemaT extends z.ZodRawShape,
JsonifySchemaUnknownKeys extends z.UnknownKeysParam,
JsonifySchemaCatchall extends z.ZodTypeAny,
DejsonifySchemaT extends z.ZodRawShape,
DejsonifySchemaUnknownKeys extends z.UnknownKeysParam,
DejsonifySchemaCatchall extends z.ZodTypeAny,
JsonifiedValues extends JsonifiableObject,
Values extends object,
> = {
readonly jsonifySchema: z.ZodObject<JsonifySchemaT, JsonifySchemaUnknownKeys, JsonifySchemaCatchall, JsonifiedValues, Values>
readonly dejsonifySchema: z.ZodObject<DejsonifySchemaT, DejsonifySchemaUnknownKeys, DejsonifySchemaCatchall, Values, JsonifiedValues>
}

View File

@@ -0,0 +1,55 @@
import { AbstractClass, Class } from "type-fest"
import { z } from "zod"
export type ZodSchemaClass<
Instance extends Values,
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
SchemaWithDefaultValuesT extends z.ZodRawShape,
SchemaWithDefaultValuesUnknownKeys extends z.UnknownKeysParam,
SchemaWithDefaultValuesCatchall extends z.ZodTypeAny,
Values extends object,
PartialValues extends Partial<Values>,
> = (
Class<Instance, [values: Values]> &
ZodSchemas<SchemaT, SchemaUnknownKeys, SchemaCatchall, SchemaWithDefaultValuesT, SchemaWithDefaultValuesUnknownKeys, SchemaWithDefaultValuesCatchall, Values, PartialValues>
)
export type ZodSchemaAbstractClass<
Instance extends Values,
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
SchemaWithDefaultValuesT extends z.ZodRawShape,
SchemaWithDefaultValuesUnknownKeys extends z.UnknownKeysParam,
SchemaWithDefaultValuesCatchall extends z.ZodTypeAny,
Values extends object,
PartialValues extends Partial<Values>,
> = (
AbstractClass<Instance, [values: Values]> &
ZodSchemas<SchemaT, SchemaUnknownKeys, SchemaCatchall, SchemaWithDefaultValuesT, SchemaWithDefaultValuesUnknownKeys, SchemaWithDefaultValuesCatchall, Values, PartialValues>
)
export type ZodSchemas<
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
SchemaWithDefaultValuesT extends z.ZodRawShape,
SchemaWithDefaultValuesUnknownKeys extends z.UnknownKeysParam,
SchemaWithDefaultValuesCatchall extends z.ZodTypeAny,
Values extends object,
PartialValues extends Partial<Values>,
> = {
readonly schema: z.ZodObject<SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, Values>
readonly schemaWithDefaultValues: z.ZodObject<SchemaWithDefaultValuesT, SchemaWithDefaultValuesUnknownKeys, SchemaWithDefaultValuesCatchall, Values, PartialValues>
}

View File

@@ -1,63 +1,64 @@
import { pipeInto } from "ts-functional-pipe"
import { Implements } from "@thilawyn/traitify-ts"
import { z } from "zod"
import { defineDefaultValues, extendSchemableClass, makeSchemableClass, newSchemable } from "."
import { dejsonifyBigIntSchema, dejsonifySchemable, jsonifyBigIntSchema, makeJsonifiableSchemableClass } from "./jsonifiable"
import { makeSchemableClassObservable } from "./observable"
import { zodSchemaClass } from "./builders/ZodSchemaClassBuilder"
import { dejsonify, jsonify } from "./schema/jsonifiable"
import { MobXObservableZodSchemaObject } from "./traits/MobXObservableZodSchemaObject"
const UserLevel = z.enum(["User", "Admin"])
class User extends pipeInto(
makeSchemableClass({
const exp = zodSchemaClass
.schema({
schema: z.object({
id: z.bigint(),
name: z.string(),
level: UserLevel,
/** User ID */
id: z.bigint(),
/** Username */
name: z.string(),
}),
defaultValues: defineDefaultValues({
level: "User" as const
schemaWithDefaultValues: s => s.extend({
id: s.shape.id.default(-1n),
}),
}),
v => makeSchemableClassObservable(v),
v => makeJsonifiableSchemableClass(v, {
jsonifySchema: ({ schema, shape }) => schema.extend({
id: jsonifyBigIntSchema(shape.id)
})
.jsonifiable({
jsonifySchema: s => s.extend({
id: jsonify.bigint(s.shape.id)
}),
dejsonifySchema: ({ schema, shape }) => schema.extend({
id: dejsonifyBigIntSchema(shape.id)
dejsonifySchema: s => s.extend({
id: dejsonify.bigint(s.shape.id)
}),
}),
) {}
User.schema
})
.toExpression()
.expresses(MobXObservableZodSchemaObject)
.build()
const user1 = newSchemable(User, { id: 1n, name: "User" })
user1.schema
@exp.staticImplements
class User extends exp.extends implements Implements<typeof exp> {}
const jsonifiedUser1 = user1.jsonify()
console.log(jsonifiedUser1)
console.log(dejsonifySchemable(User, jsonifiedUser1))
const inst = User.create({ id: 1n, name: "User" })
// console.log(inst.name)
const instEffect = User.createEffect({ id: 1n, name: "User" })
const jsonifiedUser = await inst.jsonifyPromise()
const dejsonifiedInst = await User.dejsonifyPromise(jsonifiedUser)
const UserWithPhone = extendSchemableClass(User, {
schema: ({ schema }) => schema.extend({
phone: z.string()
}),
defaultValues: defaultValues => defineDefaultValues({
...defaultValues,
phone: "+33600000000",
}),
})
UserWithPhone.defaultValues
// const AdminUserProto = User.extend()
// .schema({
// schema: s => s.extend({
// name: z.literal("Admin"),
// prout: z.string(),
// }),
// defaultValues: v => ({ ...v, name: "Admin" as const }),
// })
// .jsonifiable({
// jsonifySchema: (s, json) => json.extend({
// prout: s.shape.prout
// })
// })
// const user2 = newSchemable(UserWithPhone, { id: 1n, name: "User" })
// console.log(user2.jsonify())
// class AdminUser extends AdminUserProto.toClass() {}
// const subInst = await AdminUser.createPromise({ id: 2n, prout: "" })
// console.log(subInst)

View File

@@ -0,0 +1,16 @@
// import { trait } from "@thilawyn/traitify-ts"
// import { ZodSchemaClassExtender } from "../builders/ZodSchemaClassExtender"
// import { ZodSchemaClass } from "../shapes/ZodSchemaClass"
// export const ExtendableZodSchemaObject = trait
// .implement(Super => class ExtendableZodSchemaObject extends Super {
// static extend<
// Self extends ZodSchemaClass<any, any, any, any, any, any, any, any, any>,
// >(
// this: Self
// ) {
// return new ZodSchemaClassExtender(this, this)
// }
// })
// .build()

View File

@@ -0,0 +1,110 @@
import { ImplStatic, expression } from "@thilawyn/traitify-ts"
import { Class } from "type-fest"
import { JsonifiableObject } from "type-fest/source/jsonifiable"
import { z } from "zod"
import { parseZodSchemaEffect } from "../util"
import { ZodSchemaObject } from "./ZodSchemaObject"
export const JsonifiableZodSchemaObject = <
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
SchemaWithDefaultValuesT extends z.ZodRawShape,
SchemaWithDefaultValuesUnknownKeys extends z.UnknownKeysParam,
SchemaWithDefaultValuesCatchall extends z.ZodTypeAny,
Values extends object,
PartialValues extends Partial<Values>,
JsonifySchemaT extends z.ZodRawShape,
JsonifySchemaUnknownKeys extends z.UnknownKeysParam,
JsonifySchemaCatchall extends z.ZodTypeAny,
DejsonifySchemaT extends z.ZodRawShape,
DejsonifySchemaUnknownKeys extends z.UnknownKeysParam,
DejsonifySchemaCatchall extends z.ZodTypeAny,
JsonifiedValues extends JsonifiableObject,
>(
schema: z.ZodObject<SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, Values>,
schemaWithDefaultValues: z.ZodObject<SchemaWithDefaultValuesT, SchemaWithDefaultValuesUnknownKeys, SchemaWithDefaultValuesCatchall, Values, PartialValues>,
jsonifySchema: z.ZodObject<JsonifySchemaT, JsonifySchemaUnknownKeys, JsonifySchemaCatchall, JsonifiedValues, Values>,
dejsonifySchema: z.ZodObject<DejsonifySchemaT, DejsonifySchemaUnknownKeys, DejsonifySchemaCatchall, Values, JsonifiedValues>,
) => expression
.expresses(ZodSchemaObject(schema, schemaWithDefaultValues))
.build()
.subtrait()
.implement(Super => class JsonifiableZodSchemaObject extends Super {
declare ["constructor"]: typeof JsonifiableZodSchemaObject
static readonly jsonifySchema = jsonifySchema
static readonly dejsonifySchema = dejsonifySchema
jsonify(params?: Partial<z.ParseParams>) {
return this.constructor.jsonifySchema.parse(this, params)
}
jsonifyPromise(params?: Partial<z.ParseParams>) {
return this.constructor.jsonifySchema.parseAsync(this, params)
}
jsonifyEffect(params?: Partial<z.ParseParams>) {
return parseZodSchemaEffect(
this.constructor.jsonifySchema,
this,
params,
)
}
static dejsonify<
Instance extends Values
>(
this: (
Class<Instance, [values: Values]> &
ImplStatic<typeof JsonifiableZodSchemaObject>
),
values: JsonifiedValues,
params?: Partial<z.ParseParams>,
) {
return this
.transform(this.dejsonifySchema)
.parse(values, params)
}
static dejsonifyPromise<
Instance extends Values
>(
this: (
Class<Instance, [values: Values]> &
ImplStatic<typeof JsonifiableZodSchemaObject>
),
values: JsonifiedValues,
params?: Partial<z.ParseParams>,
) {
return this
.transform(this.dejsonifySchema)
.parseAsync(values, params)
}
static dejsonifyEffect<
Instance extends Values
>(
this: (
Class<Instance, [values: Values]> &
ImplStatic<typeof JsonifiableZodSchemaObject>
),
values: JsonifiedValues,
params?: Partial<z.ParseParams>,
) {
return parseZodSchemaEffect(
this.transform(this.dejsonifySchema),
values,
params,
)
}
})
.build()

View File

@@ -0,0 +1,23 @@
import { trait } from "@thilawyn/traitify-ts"
import { mapValues } from "lodash-es"
import { makeObservable, observable } from "mobx"
import { z } from "zod"
export const MobXObservableZodSchemaObject = trait
.staticAbstract(Super => class extends Super {
declare readonly schema: z.ZodObject<z.ZodRawShape, z.UnknownKeysParam, z.ZodTypeAny, object, object>
})
.implement(Super => class ObservableZodSchemaObject extends Super {
constructor(...args: any[]) {
super(...args)
makeObservable(this,
mapValues(
(this.constructor as typeof ObservableZodSchemaObject).schema.shape,
() => observable,
)
)
}
})
.build()

View File

@@ -0,0 +1,122 @@
import { ImplStatic, trait } from "@thilawyn/traitify-ts"
import { Class, HasRequiredKeys } from "type-fest"
import { z } from "zod"
import { parseZodSchemaEffect } from "../util"
type CreateArgs<Input extends object> = (
HasRequiredKeys<Input> extends true
? [values: Input, params?: Partial<z.ParseParams>]
: [] | [values: Input, params?: Partial<z.ParseParams>]
)
export const ZodSchemaObject = <
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
SchemaWithDefaultValuesT extends z.ZodRawShape,
SchemaWithDefaultValuesUnknownKeys extends z.UnknownKeysParam,
SchemaWithDefaultValuesCatchall extends z.ZodTypeAny,
Values extends object,
PartialValues extends Partial<Values>,
>(
schema: z.ZodObject<SchemaT, SchemaUnknownKeys, SchemaCatchall, Values, Values>,
schemaWithDefaultValues: z.ZodObject<SchemaWithDefaultValuesT, SchemaWithDefaultValuesUnknownKeys, SchemaWithDefaultValuesCatchall, Values, PartialValues>,
) => trait
.implement(Super => class ZodSchemaObject extends Super {
static readonly schema = schema
static readonly schemaWithDefaultValues = schemaWithDefaultValues
static transform<
Instance extends Values,
T extends z.ZodRawShape,
UnknownKeys extends z.UnknownKeysParam,
Catchall extends z.ZodTypeAny,
Output extends Values,
Input,
>(
this: Class<Instance, [values: Values]>,
schema: z.ZodObject<T, UnknownKeys, Catchall, Output, Input>,
) {
return schema.transform(values => new this(values))
}
static create<
Instance extends Values
>(
this: (
Class<Instance, [values: Values]> &
ImplStatic<typeof ZodSchemaObject>
),
...[values, params]: CreateArgs<PartialValues>
) {
return this
.transform(this.schemaWithDefaultValues)
.parse(values, params)
}
static createPromise<
Instance extends Values
>(
this: (
Class<Instance, [values: Values]> &
ImplStatic<typeof ZodSchemaObject>
),
...[values, params]: CreateArgs<PartialValues>
) {
return this
.transform(this.schemaWithDefaultValues)
.parseAsync(values, params)
}
static createEffect<
Instance extends Values
>(
this: (
Class<Instance, [values: Values]> &
ImplStatic<typeof ZodSchemaObject>
),
...[values, params]: CreateArgs<PartialValues>
) {
return parseZodSchemaEffect(
this.transform(this.schemaWithDefaultValues),
values,
params,
)
}
})
.build()
export type ZodSchemaObjectTrait<
SchemaT extends z.ZodRawShape,
SchemaUnknownKeys extends z.UnknownKeysParam,
SchemaCatchall extends z.ZodTypeAny,
SchemaWithDefaultValuesT extends z.ZodRawShape,
SchemaWithDefaultValuesUnknownKeys extends z.UnknownKeysParam,
SchemaWithDefaultValuesCatchall extends z.ZodTypeAny,
Values extends object,
PartialValues extends Partial<Values>,
> = (
ReturnType<
typeof ZodSchemaObject<
SchemaT,
SchemaUnknownKeys,
SchemaCatchall,
SchemaWithDefaultValuesT,
SchemaWithDefaultValuesUnknownKeys,
SchemaWithDefaultValuesCatchall,
Values,
PartialValues
>
>
)

View File

@@ -1,56 +0,0 @@
import { Effect, pipe } from "effect"
import { AbstractClass, Class as ConcreteClass } from "type-fest"
import { z } from "zod"
export function identity<T>(value: T) {
return value
}
export type ClassType = "AbstractClass" | "Class"
export type Class<
Type extends ClassType,
T,
Arguments extends unknown[] = any[],
> = (
Type extends "AbstractClass"
? AbstractClass<T, Arguments>
: Type extends "Class"
? ConcreteClass<T, Arguments>
: never
)
/**
* Represents the static members of a class.
* @template C - The class type.
*/
export type StaticMembers<C> = {
[Key in keyof C as Key extends "prototype" ? never : Key]: C[Key]
}
/**
* Parses a value using a ZodType schema wrapped in an Effect monad.
*
* @param schema - The ZodType schema to use for parsing.
* @param args - The arguments to pass to the `safeParseAsync` method of the schema.
* @returns An Effect monad representing the parsing result.
*/
export const parseZodTypeEffect = <
Output,
Input,
>(
schema: z.ZodType<Output, z.ZodTypeDef, Input>,
...args: Parameters<typeof schema.safeParseAsync>
) => pipe(
Effect.promise(() => schema.safeParseAsync(...args)),
Effect.flatMap(response =>
response.success
? Effect.succeed(response.data)
: Effect.fail(response.error)
),
)

101
src/util/class.ts Normal file
View File

@@ -0,0 +1,101 @@
import { AbstractClass, Class as ConcreteClass } from "type-fest"
/**
* Represents the possible types of a class.
*/
export type ClassType = "AbstractClass" | "Class"
/**
* Represents a class based on the specified type.
* @template Type - The type of the class ("AbstractClass" or "Class").
* @template T - The type parameter of the class.
* @template Arguments - The type of arguments the class constructor takes.
*/
export type Class<
Type extends ClassType,
T,
Arguments extends unknown[] = any[],
> = (
Type extends "AbstractClass"
? AbstractClass<T, Arguments>
: Type extends "Class"
? ConcreteClass<T, Arguments>
: never
)
/**
* Gets the type of a class (either "Class" or "AbstractClass").
* @template C - The class type to determine.
*/
export type GetClassType<C> = (
C extends ConcreteClass<any>
? "Class"
: C extends AbstractClass<any>
? "AbstractClass"
: never
)
/**
* Represents an array of instances corresponding to the provided classes.
* @template Classes - An array of classes extending AbstractClass.
*/
export type ClassesInstances<Classes extends readonly AbstractClass<any>[]> = (
Classes extends [infer Class, ...infer Rest]
? Class extends AbstractClass<any>
? Rest extends AbstractClass<any>[]
? [InstanceType<Class>, ...ClassesInstances<Rest>]
: never
: never
: []
)
/**
* Represents an intersection of instances of the provided classes.
* @template Classes - An array of classes extending AbstractClass.
*/
export type ClassesInstancesIntersection<Classes extends readonly AbstractClass<any>[]> = (
Classes extends [infer Class, ...infer Rest]
? Class extends AbstractClass<any>
? Rest extends AbstractClass<any>[]
? InstanceType<Class> & ClassesInstancesIntersection<Rest>
: never
: never
: {}
)
/**
* Represents the static members of a class.
* @template Class - A class extending AbstractClass.
*/
export type StaticMembers<Class extends AbstractClass<any>> = (
Omit<Class, "prototype">
)
/**
* Represents an array of static members corresponding to the provided classes.
* @template Classes - An array of classes extending AbstractClass.
*/
export type ClassesStaticMembers<Classes extends readonly AbstractClass<any>[]> = (
Classes extends [infer Class, ...infer Rest]
? Class extends AbstractClass<any>
? Rest extends AbstractClass<any>[]
? [StaticMembers<Class>, ...ClassesStaticMembers<Rest>]
: never
: never
: []
)
/**
* Represents an intersection of static members of the provided classes.
* @template Classes - An array of classes extending AbstractClass.
*/
export type ClassesStaticMembersIntersection<Classes extends readonly AbstractClass<any>[]> = (
Classes extends [infer Class, ...infer Rest]
? Class extends AbstractClass<any>
? Rest extends AbstractClass<any>[]
? StaticMembers<Class> & ClassesStaticMembersIntersection<Rest>
: never
: never
: {}
)

23
src/util/effect.ts Normal file
View File

@@ -0,0 +1,23 @@
import { Effect, pipe } from "effect"
import { z } from "zod"
/**
* Parses a value using a Zod schema wrapped in an Effect monad.
*
* @param schema - The Zod schema to use for parsing.
* @param args - The arguments to pass to the `safeParseAsync` method of the schema.
* @returns An Effect monad representing the parsing result.
*/
export const parseZodSchemaEffect = <Output, Input>(
schema: z.ZodType<Output, z.ZodTypeDef, Input>,
...args: Parameters<typeof schema.safeParseAsync>
) => pipe(
Effect.promise(() => schema.safeParseAsync(...args)),
Effect.flatMap(response =>
response.success
? Effect.succeed(response.data)
: Effect.fail(response.error)
),
)

55
src/util/extend.ts Normal file
View File

@@ -0,0 +1,55 @@
/**
* Represents the common keys between two types.
* @template A - The first type.
* @template B - The second type.
*/
export type CommonKeys<A, B> = Extract<keyof A, keyof B>
export type Extend<T extends readonly object[]> = (
T extends readonly [
infer Super,
infer Self,
...infer Rest extends readonly object[],
]
? Pick<Self, CommonKeys<Self, Super>> extends Pick<Super, CommonKeys<Self, Super>>
? Extend<readonly [
Omit<Super, CommonKeys<Self, Super>> & Self,
...Rest,
]>
: never
: T extends readonly [infer Self]
? Self
: {}
)
export type Override<T extends readonly object[]> = (
T extends readonly [
infer Super,
infer Self,
...infer Rest extends readonly object[],
]
? Override<readonly [
Omit<Super, CommonKeys<Self, Super>> & Self,
...Rest,
]>
: T extends readonly [infer Self]
? Self
: {}
)
/**
* Merges an inheritance tree defined by an array of types without allowing overrides.
* @template T - An array of types representing the inheritance tree.
*/
export type ExtendWithoutOverriding<T extends readonly any[]> = (
T extends [infer Super, infer Self, ...infer Rest]
? Pick<Self, CommonKeys<Self, Super>> extends Pick<Super, CommonKeys<Self, Super>>
? ExtendWithoutOverriding<[
Super & Self,
...Rest,
]>
: never
: T extends [infer Self]
? Self
: void
)

4
src/util/index.ts Normal file
View File

@@ -0,0 +1,4 @@
export * from "./class"
export * from "./effect"
export * from "./extend"
export * from "./misc"

3
src/util/misc.ts Normal file
View File

@@ -0,0 +1,3 @@
export function identity<T>(value: T) {
return value
}