From 019066bb9c80cf056ef7fa9176c1208e681c18b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 17 Jan 2024 20:47:13 +0100 Subject: [PATCH] 0.1.1 (#2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Julien Valverdé Reviewed-on: https://git.jvalver.de/Thilawyn/schemable-class/pulls/2 --- bun.lockb | Bin 154055 -> 160294 bytes package.json | 18 +- rollup.config.ts | 1 + src/SchemableClass.ts | 122 ++++------ src/defineDefaultValues.ts | 8 + src/extendSchemableClass.ts | 95 ++++++++ src/index.ts | 2 + src/jsonifiable/JsonifiableSchemableClass.ts | 124 +++++----- src/jsonifiable/dejsonifySchemable.ts | 230 +++++++++++++++--- .../makeJsonifiableSchemableClass.ts | 112 ++++++--- src/jsonifiable/schema/bigint.ts | 38 +-- src/jsonifiable/schema/date.ts | 38 +-- src/jsonifiable/schema/decimal.ts | 42 ++-- src/jsonifiable/schema/schemable.ts | 152 ++++++++++-- src/legacy/SchemableClass.ts | 90 +++++++ src/legacy/index.ts | 3 + .../jsonifiable/JsonifiableSchemableClass.ts | 66 +++++ src/legacy/jsonifiable/dejsonifySchemable.ts | 47 ++++ src/legacy/jsonifiable/index.ts | 4 + .../makeJsonifiableSchemableClass.ts | 98 ++++++++ src/legacy/jsonifiable/schema/bigint.ts | 18 ++ src/legacy/jsonifiable/schema/date.ts | 18 ++ src/legacy/jsonifiable/schema/decimal.ts | 19 ++ src/legacy/jsonifiable/schema/index.ts | 4 + src/legacy/jsonifiable/schema/schemable.ts | 25 ++ src/legacy/makeSchemableClass.ts | 49 ++++ src/legacy/newSchemable.ts | 77 ++++++ src/legacy/tests.ts | 64 +++++ src/legacy/util.ts | 82 +++++++ src/makeSchemableClass.ts | 115 +++++---- src/newSchemable.ts | 166 ++++++++----- src/observable/index.ts | 1 + .../makeSchemableClassObservable.ts | 29 +++ src/tests.ts | 101 ++++---- src/util.ts | 68 ++---- 35 files changed, 1661 insertions(+), 465 deletions(-) create mode 100644 src/defineDefaultValues.ts create mode 100644 src/extendSchemableClass.ts create mode 100644 src/legacy/SchemableClass.ts create mode 100644 src/legacy/index.ts create mode 100644 src/legacy/jsonifiable/JsonifiableSchemableClass.ts create mode 100644 src/legacy/jsonifiable/dejsonifySchemable.ts create mode 100644 src/legacy/jsonifiable/index.ts create mode 100644 src/legacy/jsonifiable/makeJsonifiableSchemableClass.ts create mode 100644 src/legacy/jsonifiable/schema/bigint.ts create mode 100644 src/legacy/jsonifiable/schema/date.ts create mode 100644 src/legacy/jsonifiable/schema/decimal.ts create mode 100644 src/legacy/jsonifiable/schema/index.ts create mode 100644 src/legacy/jsonifiable/schema/schemable.ts create mode 100644 src/legacy/makeSchemableClass.ts create mode 100644 src/legacy/newSchemable.ts create mode 100644 src/legacy/tests.ts create mode 100644 src/legacy/util.ts create mode 100644 src/observable/index.ts create mode 100644 src/observable/makeSchemableClassObservable.ts diff --git a/bun.lockb b/bun.lockb index 4e23eb8b4364adf2258b86fdc025f2982746ab68..a8be37ec9a258a1b8ffb32b87f1705fd70b7789f 100755 GIT binary patch delta 31440 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⪙Yq#~*a_?awgKCLJ-|U= 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 delta 28722 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 = { - 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 + SchemaT extends z.ZodRawShape, + SchemaUnknownKeys extends z.UnknownKeysParam, + SchemaCatchall extends z.ZodTypeAny, + + Values extends {}, + DefaultValues extends Partial, + + Type extends ClassType = "AbstractClass" > = ( Class< - SchemableObject<$Config>, - SchemableClassConstructorParams<$Config> + Type, + + { + readonly schema: z.ZodObject< + SchemaT, + SchemaUnknownKeys, + SchemaCatchall, + Values, + Values + > + + readonly defaultValues: DefaultValues + } & Values, + + Parameters<(values: Values) => never> > & { - readonly $schemableConfig: $Config - readonly schema: $Config["schema"] - readonly schemaWithDefaultValues: $Config["schemaWithDefaultValues"] + readonly schema: z.ZodObject< + SchemaT, + SchemaUnknownKeys, + SchemaCatchall, + Values, + Values + > + + readonly defaultValues: DefaultValues } ) -/** - * 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"] -) +export type SchemableClassInput< + Values extends {}, + DefaultValues extends Partial, +> = { + [Key in Exclude]: Values[Key] +} & { + [Key in keyof DefaultValues]?: Key extends keyof Values + ? Values[Key] + : never +} diff --git a/src/defineDefaultValues.ts b/src/defineDefaultValues.ts new file mode 100644 index 0000000..634d83a --- /dev/null +++ b/src/defineDefaultValues.ts @@ -0,0 +1,8 @@ +import { Opaque } from "type-fest" + + +export type DefinedDefaultValuesTag = "@thilawyn/schemable-class/DefinedDefaultValues" + +export function defineDefaultValues(values: T) { + return values as Opaque +} diff --git a/src/extendSchemableClass.ts b/src/extendSchemableClass.ts new file mode 100644 index 0000000..bb392e1 --- /dev/null +++ b/src/extendSchemableClass.ts @@ -0,0 +1,95 @@ +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, + + SchemaT extends z.ZodRawShape, + SchemaUnknownKeys extends z.UnknownKeysParam, + SchemaCatchall extends z.ZodTypeAny, + SchemaValues extends ExtendSchemaValues, + + DefaultValues extends Partial, +>( + 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 + }, +) { + type Class = ( + C extends ConcreteClass + ? ConcreteClass + : AbstractClass + ) + + 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, "schema" | "defaultValues" | keyof ExtendSchemaValues> & + { + readonly schema: z.ZodObject, + readonly defaultValues: DefaultValues, + } & + SchemaValues, + + Parameters<(values: SchemaValues) => void> + > & + + Omit, "schema" | "defaultValues"> & + { + readonly schema: z.ZodObject, + readonly defaultValues: DefaultValues, + } + ) +} diff --git a/src/index.ts b/src/index.ts index 17d8b56..4b4c248 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,5 @@ export * from "./SchemableClass" +export * from "./defineDefaultValues" +export * from "./extendSchemableClass" export * from "./makeSchemableClass" export * from "./newSchemable" diff --git a/src/jsonifiable/JsonifiableSchemableClass.ts b/src/jsonifiable/JsonifiableSchemableClass.ts index c0dc3c0..da145d6 100644 --- a/src/jsonifiable/JsonifiableSchemableClass.ts +++ b/src/jsonifiable/JsonifiableSchemableClass.ts @@ -1,66 +1,80 @@ 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 - > -} +import { SchemableClass } from ".." +import { Class, ClassType } from "../util" export type JsonifiableSchemableClass< - $Config extends JsonifiableSchemableConfig + SchemaT extends z.ZodRawShape, + SchemaUnknownKeys extends z.UnknownKeysParam, + SchemaCatchall extends z.ZodTypeAny, + + Values extends {}, + DefaultValues extends Partial, + + 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< - JsonifiableSchemableObject<$Config>, - SchemableClassConstructorParams<$Config["$schemableConfig"]> + Type, + + { + readonly jsonifySchema: z.ZodObject< + JsonifySchemaT, + JsonifySchemaUnknownKeys, + JsonifySchemaCatchall, + JsonifiedValues, + Values + > + + readonly dejsonifySchema: z.ZodObject< + DejsonifySchemaT, + DejsonifySchemaUnknownKeys, + DejsonifySchemaCatchall, + Values, + JsonifiedValues + > + + jsonify(): JsonifiedValues + jsonifyPromise(): Promise + jsonifyEffect(): Effect.Effect, JsonifiedValues> + }, + + any[] > & { - readonly $jsonifiableSchemableConfig: $Config - readonly jsonifySchema: $Config["jsonifySchema"] - readonly dejsonifySchema: $Config["dejsonifySchema"] + readonly jsonifySchema: z.ZodObject< + JsonifySchemaT, + JsonifySchemaUnknownKeys, + JsonifySchemaCatchall, + JsonifiedValues, + Values + > + + readonly dejsonifySchema: z.ZodObject< + DejsonifySchemaT, + DejsonifySchemaUnknownKeys, + DejsonifySchemaCatchall, + Values, + JsonifiedValues + > } ) - -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, $Config["jsonifiedValues"]> -} diff --git a/src/jsonifiable/dejsonifySchemable.ts b/src/jsonifiable/dejsonifySchemable.ts index 35ceafb..b8e7f80 100644 --- a/src/jsonifiable/dejsonifySchemable.ts +++ b/src/jsonifiable/dejsonifySchemable.ts @@ -1,47 +1,213 @@ import { Effect, pipe } from "effect" +import { JsonifiableObject } from "type-fest/source/jsonifiable" import { z } from "zod" -import { JsonifiableSchemableClass, JsonifiableSchemableConfig } from "." +import { JsonifiableSchemableClass } from "." import { parseZodTypeEffect } from "../util" -export const dejsonifySchemable = < - C extends JsonifiableSchemableClass<$Config>, - $Config extends JsonifiableSchemableConfig, +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, + + 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<$Config>, - values: $Config["jsonifiedValues"], + class_: C | JsonifiableSchemableClass< + SchemaT, + SchemaUnknownKeys, + SchemaCatchall, + + Values, + DefaultValues, + + JsonifySchemaT, + JsonifySchemaUnknownKeys, + JsonifySchemaCatchall, + + DejsonifySchemaT, + DejsonifySchemaUnknownKeys, + DejsonifySchemaCatchall, + + JsonifiedValues, + + "Class" + >, + + values: JsonifiedValues, params?: Partial, -) => - new class_(class_.dejsonifySchema.parse(values, params)) as InstanceType +) { + return new class_( + class_.dejsonifySchema.parse(values, params) + ) as InstanceType +} -export const dejsonifySchemablePromise = async < - C extends JsonifiableSchemableClass<$Config>, - $Config extends JsonifiableSchemableConfig, +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, + + 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<$Config>, - values: $Config["jsonifiedValues"], + class_: C | JsonifiableSchemableClass< + SchemaT, + SchemaUnknownKeys, + SchemaCatchall, + + Values, + DefaultValues, + + JsonifySchemaT, + JsonifySchemaUnknownKeys, + JsonifySchemaCatchall, + + DejsonifySchemaT, + DejsonifySchemaUnknownKeys, + DejsonifySchemaCatchall, + + JsonifiedValues, + + "Class" + >, + + values: JsonifiedValues, params?: Partial, -) => - new class_(await class_.dejsonifySchema.parseAsync(values, params)) as InstanceType +) { + return new class_( + await class_.dejsonifySchema.parseAsync(values, params) + ) as InstanceType +} -export const dejsonifySchemableEffect = < - C extends JsonifiableSchemableClass<$Config>, - $Config extends JsonifiableSchemableConfig, +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, + + 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<$Config>, - values: $Config["jsonifiedValues"], - params?: Partial, -) => pipe( - parseZodTypeEffect< - z.output, - z.input - >( - class_.dejsonifySchema, - values, - params, - ), + class_: C | JsonifiableSchemableClass< + SchemaT, + SchemaUnknownKeys, + SchemaCatchall, - Effect.map(values => new class_(values) as InstanceType), -) + Values, + DefaultValues, + + JsonifySchemaT, + JsonifySchemaUnknownKeys, + JsonifySchemaCatchall, + + DejsonifySchemaT, + DejsonifySchemaUnknownKeys, + DejsonifySchemaCatchall, + + JsonifiedValues, + + "Class" + >, + + values: JsonifiedValues, + params?: Partial, +) { + return pipe( + parseZodTypeEffect(class_.dejsonifySchema, values, params), + Effect.map(values => new class_(values) as InstanceType), + ) +} diff --git a/src/jsonifiable/makeJsonifiableSchemableClass.ts b/src/jsonifiable/makeJsonifiableSchemableClass.ts index a956b0b..691a3f2 100644 --- a/src/jsonifiable/makeJsonifiableSchemableClass.ts +++ b/src/jsonifiable/makeJsonifiableSchemableClass.ts @@ -1,16 +1,26 @@ -import { Class } from "type-fest" +import { Effect } from "effect" +import { AbstractClass, Class as ConcreteClass } from "type-fest" import { JsonifiableObject } from "type-fest/source/jsonifiable" import { z } from "zod" -import { JsonifiableSchemableClass, JsonifiableSchemableConfig, JsonifiableSchemableObject } from "." -import { SchemableClass, SchemableConfig } from ".." +import { SchemableClass } from ".." import { StaticMembers, parseZodTypeEffect } from "../util" export function makeJsonifiableSchemableClass< - C extends SchemableClass<$SchemableConfig>, - $SchemableConfig extends SchemableConfig, + C extends SchemableClass< + SchemaT, + SchemaUnknownKeys, + SchemaCatchall, + Values, + DefaultValues + >, - JsonifiedValues extends JsonifiableObject, + SchemaT extends z.ZodRawShape, + SchemaUnknownKeys extends z.UnknownKeysParam, + SchemaCatchall extends z.ZodTypeAny, + + Values extends {}, + DefaultValues extends Partial, JsonifySchemaT extends z.ZodRawShape, JsonifySchemaUnknownKeys extends z.UnknownKeysParam, @@ -19,59 +29,77 @@ export function makeJsonifiableSchemableClass< DejsonifySchemaT extends z.ZodRawShape, DejsonifySchemaUnknownKeys extends z.UnknownKeysParam, DejsonifySchemaCatchall extends z.ZodTypeAny, + + JsonifiedValues extends JsonifiableObject, >( - class_: C | SchemableClass<$SchemableConfig>, + extend: C | SchemableClass< + SchemaT, + SchemaUnknownKeys, + SchemaCatchall, + Values, + DefaultValues + >, props: { jsonifySchema: (props: { - schema: $SchemableConfig["schema"] - s: $SchemableConfig["schema"]["shape"] + schema: z.ZodObject< + SchemaT, + SchemaUnknownKeys, + SchemaCatchall, + Values, + Values + > + + shape: SchemaT }) => z.ZodObject< JsonifySchemaT, JsonifySchemaUnknownKeys, JsonifySchemaCatchall, JsonifiedValues, - $SchemableConfig["values"] + Values > dejsonifySchema: (props: { - schema: $SchemableConfig["schema"] - s: $SchemableConfig["schema"]["shape"] + schema: z.ZodObject< + SchemaT, + SchemaUnknownKeys, + SchemaCatchall, + Values, + Values + > + + shape: SchemaT }) => z.ZodObject< DejsonifySchemaT, DejsonifySchemaUnknownKeys, DejsonifySchemaCatchall, - $SchemableConfig["values"], + Values, JsonifiedValues > }, ) { + type Class = ( + C extends ConcreteClass + ? ConcreteClass + : AbstractClass + ) const jsonifySchema = props.jsonifySchema({ - schema: class_.schema, - s: class_.schema.shape, + schema: extend.schema, + shape: extend.schema.shape, }) const dejsonifySchema = props.dejsonifySchema({ - schema: class_.schema, - s: class_.schema.shape, + schema: extend.schema, + shape: extend.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 + return class extends extend { + static readonly jsonifySchema = jsonifySchema + readonly jsonifySchema = jsonifySchema - 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 + static readonly dejsonifySchema = dejsonifySchema + readonly dejsonifySchema = dejsonifySchema jsonify() { return this.jsonifySchema.parse(this) @@ -84,15 +112,23 @@ export function makeJsonifiableSchemableClass< jsonifyEffect() { return parseZodTypeEffect(this.jsonifySchema, this) } - } satisfies JsonifiableSchemableClass - - return jsonifiableClass as unknown as ( + } as unknown as ( Class< - InstanceType & JsonifiableSchemableObject, + InstanceType & { + readonly jsonifySchema: z.ZodObject, + readonly dejsonifySchema: z.ZodObject, + + jsonify(): JsonifiedValues + jsonifyPromise(): Promise + jsonifyEffect(): Effect.Effect, JsonifiedValues> + }, + ConstructorParameters > & - StaticMembers & - StaticMembers> - ) + StaticMembers & { + readonly jsonifySchema: z.ZodObject, + readonly dejsonifySchema: z.ZodObject, + } + ) } diff --git a/src/jsonifiable/schema/bigint.ts b/src/jsonifiable/schema/bigint.ts index b0f0093..76a605d 100644 --- a/src/jsonifiable/schema/bigint.ts +++ b/src/jsonifiable/schema/bigint.ts @@ -1,18 +1,28 @@ +import { Opaque } from "type-fest" import { z } from "zod" +import { identity } from "../../util" -export const jsonifyBigIntSchema = (schema: S) => - schema.transform(v => v.toString()) +export type JsonifiedBigInt = Opaque -export const dejsonifyBigIntSchema = (schema: S) => - z - .string() - .transform(v => { - try { - return BigInt(v) - } - catch (e) { - return v - } - }) - .pipe(schema) + +export function jsonifyBigIntSchema(schema: S) { + return schema.transform(v => v.toString() as JsonifiedBigInt) +} + +export function dejsonifyBigIntSchema(schema: S) { + return z + .custom(identity) + .pipe(z + .string() + .transform(v => { + try { + return BigInt(v) + } + catch (e) { + return v + } + }) + .pipe(schema) + ) +} diff --git a/src/jsonifiable/schema/date.ts b/src/jsonifiable/schema/date.ts index b5ac677..c70b2ed 100644 --- a/src/jsonifiable/schema/date.ts +++ b/src/jsonifiable/schema/date.ts @@ -1,18 +1,28 @@ +import { Opaque } from "type-fest" import { z } from "zod" +import { identity } from "../../util" -export const jsonifyDateSchema = (schema: S) => - schema.transform(v => v.toString()) +export type JsonifiedDate = Opaque -export const dejsonifyDateSchema = (schema: S) => - z - .string() - .transform(v => { - try { - return new Date(v) - } - catch (e) { - return v - } - }) - .pipe(schema) + +export function jsonifyDateSchema(schema: S) { + return schema.transform(v => v.toString() as JsonifiedDate) +} + +export function dejsonifyDateSchema(schema: S) { + return z + .custom(identity) + .pipe(z + .string() + .transform(v => { + try { + return new Date(v) + } + catch (e) { + return v + } + }) + .pipe(schema) + ) +} diff --git a/src/jsonifiable/schema/decimal.ts b/src/jsonifiable/schema/decimal.ts index 58c492d..be387ee 100644 --- a/src/jsonifiable/schema/decimal.ts +++ b/src/jsonifiable/schema/decimal.ts @@ -1,19 +1,33 @@ import { Decimal } from "decimal.js" +import { Opaque } from "type-fest" import { z } from "zod" +import { identity } from "../../util" -export const jsonifyDecimalSchema = >(schema: S) => - schema.transform(v => v.toJSON()) +export type JsonifiedDecimal = Opaque -export const dejsonifyDecimalSchema = >(schema: S) => - z - .string() - .transform(v => { - try { - return new Decimal(v) - } - catch (e) { - return v - } - }) - .pipe(schema) + +export function jsonifyDecimalSchema< + S extends z.ZodType +>(schema: S) { + return schema.transform(v => v.toJSON() as JsonifiedDecimal) +} + +export function dejsonifyDecimalSchema< + S extends z.ZodType +>(schema: S) { + return z + .custom(identity) + .pipe(z + .string() + .transform(v => { + try { + return new Decimal(v) + } + catch (e) { + return v + } + }) + .pipe(schema) + ) +} diff --git a/src/jsonifiable/schema/schemable.ts b/src/jsonifiable/schema/schemable.ts index c6e2a2d..b0a72bc 100644 --- a/src/jsonifiable/schema/schemable.ts +++ b/src/jsonifiable/schema/schemable.ts @@ -1,25 +1,139 @@ +import { JsonifiableObject } from "type-fest/source/jsonifiable" import { z } from "zod" -import { JsonifiableSchemableClass, JsonifiableSchemableConfig } from ".." +import { JsonifiableSchemableClass } 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, z.ZodTypeDef, InstanceType>, +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, + + 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, z.ZodTypeDef, InstanceType>, >( - class_: C | JsonifiableSchemableClass<$Config>, - schema: S, -) => - schema.pipe(class_.jsonifySchema) + class_: C | JsonifiableSchemableClass< + SchemaT, + SchemaUnknownKeys, + SchemaCatchall, -// 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, z.ZodTypeDef, InstanceType>, + 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, + + 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, z.ZodTypeDef, InstanceType>, >( - class_: C | JsonifiableSchemableClass<$Config>, - schema: S, -) => - class_.dejsonifySchema.transform(v => new class_(v)).pipe(schema) + 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) +} diff --git a/src/legacy/SchemableClass.ts b/src/legacy/SchemableClass.ts new file mode 100644 index 0000000..f85216c --- /dev/null +++ b/src/legacy/SchemableClass.ts @@ -0,0 +1,90 @@ +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"] +) diff --git a/src/legacy/index.ts b/src/legacy/index.ts new file mode 100644 index 0000000..17d8b56 --- /dev/null +++ b/src/legacy/index.ts @@ -0,0 +1,3 @@ +export * from "./SchemableClass" +export * from "./makeSchemableClass" +export * from "./newSchemable" diff --git a/src/legacy/jsonifiable/JsonifiableSchemableClass.ts b/src/legacy/jsonifiable/JsonifiableSchemableClass.ts new file mode 100644 index 0000000..c0dc3c0 --- /dev/null +++ b/src/legacy/jsonifiable/JsonifiableSchemableClass.ts @@ -0,0 +1,66 @@ +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, $Config["jsonifiedValues"]> +} diff --git a/src/legacy/jsonifiable/dejsonifySchemable.ts b/src/legacy/jsonifiable/dejsonifySchemable.ts new file mode 100644 index 0000000..35ceafb --- /dev/null +++ b/src/legacy/jsonifiable/dejsonifySchemable.ts @@ -0,0 +1,47 @@ +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, +) => + new class_(class_.dejsonifySchema.parse(values, params)) as InstanceType + + +export const dejsonifySchemablePromise = async < + C extends JsonifiableSchemableClass<$Config>, + $Config extends JsonifiableSchemableConfig, +>( + class_: C | JsonifiableSchemableClass<$Config>, + values: $Config["jsonifiedValues"], + params?: Partial, +) => + new class_(await class_.dejsonifySchema.parseAsync(values, params)) as InstanceType + + +export const dejsonifySchemableEffect = < + C extends JsonifiableSchemableClass<$Config>, + $Config extends JsonifiableSchemableConfig, +>( + class_: C | JsonifiableSchemableClass<$Config>, + values: $Config["jsonifiedValues"], + params?: Partial, +) => pipe( + parseZodTypeEffect< + z.output, + z.input + >( + class_.dejsonifySchema, + values, + params, + ), + + Effect.map(values => new class_(values) as InstanceType), +) diff --git a/src/legacy/jsonifiable/index.ts b/src/legacy/jsonifiable/index.ts new file mode 100644 index 0000000..74e7d23 --- /dev/null +++ b/src/legacy/jsonifiable/index.ts @@ -0,0 +1,4 @@ +export * from "./JsonifiableSchemableClass" +export * from "./dejsonifySchemable" +export * from "./makeJsonifiableSchemableClass" +export * from "./schema" diff --git a/src/legacy/jsonifiable/makeJsonifiableSchemableClass.ts b/src/legacy/jsonifiable/makeJsonifiableSchemableClass.ts new file mode 100644 index 0000000..a956b0b --- /dev/null +++ b/src/legacy/jsonifiable/makeJsonifiableSchemableClass.ts @@ -0,0 +1,98 @@ +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 + + return jsonifiableClass as unknown as ( + Class< + InstanceType & JsonifiableSchemableObject, + ConstructorParameters + > & + StaticMembers & + StaticMembers> + ) + +} diff --git a/src/legacy/jsonifiable/schema/bigint.ts b/src/legacy/jsonifiable/schema/bigint.ts new file mode 100644 index 0000000..b0f0093 --- /dev/null +++ b/src/legacy/jsonifiable/schema/bigint.ts @@ -0,0 +1,18 @@ +import { z } from "zod" + + +export const jsonifyBigIntSchema = (schema: S) => + schema.transform(v => v.toString()) + +export const dejsonifyBigIntSchema = (schema: S) => + z + .string() + .transform(v => { + try { + return BigInt(v) + } + catch (e) { + return v + } + }) + .pipe(schema) diff --git a/src/legacy/jsonifiable/schema/date.ts b/src/legacy/jsonifiable/schema/date.ts new file mode 100644 index 0000000..b5ac677 --- /dev/null +++ b/src/legacy/jsonifiable/schema/date.ts @@ -0,0 +1,18 @@ +import { z } from "zod" + + +export const jsonifyDateSchema = (schema: S) => + schema.transform(v => v.toString()) + +export const dejsonifyDateSchema = (schema: S) => + z + .string() + .transform(v => { + try { + return new Date(v) + } + catch (e) { + return v + } + }) + .pipe(schema) diff --git a/src/legacy/jsonifiable/schema/decimal.ts b/src/legacy/jsonifiable/schema/decimal.ts new file mode 100644 index 0000000..58c492d --- /dev/null +++ b/src/legacy/jsonifiable/schema/decimal.ts @@ -0,0 +1,19 @@ +import { Decimal } from "decimal.js" +import { z } from "zod" + + +export const jsonifyDecimalSchema = >(schema: S) => + schema.transform(v => v.toJSON()) + +export const dejsonifyDecimalSchema = >(schema: S) => + z + .string() + .transform(v => { + try { + return new Decimal(v) + } + catch (e) { + return v + } + }) + .pipe(schema) diff --git a/src/legacy/jsonifiable/schema/index.ts b/src/legacy/jsonifiable/schema/index.ts new file mode 100644 index 0000000..08ecef8 --- /dev/null +++ b/src/legacy/jsonifiable/schema/index.ts @@ -0,0 +1,4 @@ +export * from "./bigint" +export * from "./date" +export * from "./decimal" +export * from "./schemable" diff --git a/src/legacy/jsonifiable/schema/schemable.ts b/src/legacy/jsonifiable/schema/schemable.ts new file mode 100644 index 0000000..c6e2a2d --- /dev/null +++ b/src/legacy/jsonifiable/schema/schemable.ts @@ -0,0 +1,25 @@ +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, z.ZodTypeDef, InstanceType>, +>( + 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, z.ZodTypeDef, InstanceType>, +>( + class_: C | JsonifiableSchemableClass<$Config>, + schema: S, +) => + class_.dejsonifySchema.transform(v => new class_(v)).pipe(schema) diff --git a/src/legacy/makeSchemableClass.ts b/src/legacy/makeSchemableClass.ts new file mode 100644 index 0000000..88bcf72 --- /dev/null +++ b/src/legacy/makeSchemableClass.ts @@ -0,0 +1,49 @@ +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, + input: undefined as unknown as z.input, + 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) { + Object.assign(this, data) + } + } as SchemableClass + +} diff --git a/src/legacy/newSchemable.ts b/src/legacy/newSchemable.ts new file mode 100644 index 0000000..da0be36 --- /dev/null +++ b/src/legacy/newSchemable.ts @@ -0,0 +1,77 @@ +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] + +type NewSchemableArgs = + HasRequiredKeys 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 + + +/** + * 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 + + +/** + * 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, + z.input + >( + class_.schemaWithDefaultValues, + values || {}, + params, + ), + + Effect.map(values => new class_(values) as InstanceType), +) diff --git a/src/legacy/tests.ts b/src/legacy/tests.ts new file mode 100644 index 0000000..c19daf7 --- /dev/null +++ b/src/legacy/tests.ts @@ -0,0 +1,64 @@ +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) diff --git a/src/legacy/util.ts b/src/legacy/util.ts new file mode 100644 index 0000000..2cf7b08 --- /dev/null +++ b/src/legacy/util.ts @@ -0,0 +1,82 @@ +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 = { + [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 + ? 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, + ...args: Parameters +) => pipe( + Effect.promise(() => schema.safeParseAsync(...args)), + + Effect.flatMap(response => + response.success + ? Effect.succeed(response.data) + : Effect.fail(response.error) + ), +) diff --git a/src/makeSchemableClass.ts b/src/makeSchemableClass.ts index 88bcf72..0345eb7 100644 --- a/src/makeSchemableClass.ts +++ b/src/makeSchemableClass.ts @@ -1,49 +1,82 @@ +import { AbstractClass, Class as ConcreteClass, Opaque } from "type-fest" import { z } from "zod" -import { SchemableClass, SchemableConfig } from "." -import { zodObjectRemoveDefaults } from "./util" +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, +>( + extend: C, + + { schema, defaultValues }: { + schema: z.ZodObject + defaultValues: Opaque + }, +) { + type Class = ( + C extends ConcreteClass + ? ConcreteClass + : AbstractClass + ) + + return class extends (extend as unknown as ConcreteClass) { + 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 & + { + readonly schema: z.ZodObject, + readonly defaultValues: DefaultValues, + } & + Values, + + Parameters<(values: Values) => void> + > & + + StaticMembers & + { + readonly schema: z.ZodObject, + readonly defaultValues: DefaultValues, + } + ) +} export function makeSchemableClass< - SchemaWithDefaultValuesT extends z.ZodRawShape, - SchemaWithDefaultValuesUnknownKeys extends z.UnknownKeysParam, - SchemaWithDefaultValuesCatchall extends z.ZodTypeAny, - SchemaWithDefaultValuesOutput extends SchemaWithDefaultValuesInput, // TODO: apply "StripSchemaInputDefaults"? - SchemaWithDefaultValuesInput extends {}, + SchemaT extends z.ZodRawShape, + SchemaUnknownKeys extends z.UnknownKeysParam, + SchemaCatchall extends z.ZodTypeAny, + + Values extends {}, + DefaultValues extends Partial, >( - { - schema: schemaWithDefaultValues - }: { - schema: z.ZodObject< - SchemaWithDefaultValuesT, - SchemaWithDefaultValuesUnknownKeys, - SchemaWithDefaultValuesCatchall, - SchemaWithDefaultValuesOutput, - SchemaWithDefaultValuesInput - > + props: { + schema: z.ZodObject + defaultValues: Opaque } ) { - - const schema = zodObjectRemoveDefaults(schemaWithDefaultValues) - - const $schemableConfig = { - values: undefined as unknown as z.output, - input: undefined as unknown as z.input, - 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) { - Object.assign(this, data) - } - } as SchemableClass - + return makeSchemableClassFrom(Object, props) } diff --git a/src/newSchemable.ts b/src/newSchemable.ts index da0be36..98a5498 100644 --- a/src/newSchemable.ts +++ b/src/newSchemable.ts @@ -1,77 +1,127 @@ import { Effect, pipe } from "effect" import { HasRequiredKeys } from "type-fest" import { z } from "zod" -import { SchemableClass, SchemableConfig } from "." +import { SchemableClass, SchemableClassInput } from "." import { parseZodTypeEffect } from "./util" -type ParamsArgs = [] | [Partial] +type ParamsArgs = [] | [params: Partial] type NewSchemableArgs = HasRequiredKeys extends true - ? [Input, ...ParamsArgs] - : [] | [Input, ...ParamsArgs] + ? [values: Input, ...args: ParamsArgs] + : [] | [values: Input, ...args: 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, +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, >( - class_: C | SchemableClass<$Config>, - ...[values, params]: NewSchemableArgs<$Config["input"]> -) => - new class_(class_.schemaWithDefaultValues.parse(values || {}, params)) as InstanceType + class_: C | SchemableClass< + SchemaT, + SchemaUnknownKeys, + SchemaCatchall, + Values, + DefaultValues, + "Class" + >, + + ...[values, params]: NewSchemableArgs< + SchemableClassInput + > +) { + return new class_( + class_.schema.parse({ ...class_.defaultValues, ...values }, params) + ) as InstanceType +} -/** - * 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, +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, >( - class_: C | SchemableClass<$Config>, - ...[values, params]: NewSchemableArgs<$Config["input"]> -) => - new class_(await class_.schemaWithDefaultValues.parseAsync(values || {}, params)) as InstanceType + class_: C | SchemableClass< + SchemaT, + SchemaUnknownKeys, + SchemaCatchall, + Values, + DefaultValues, + "Class" + >, + + ...[values, params]: NewSchemableArgs< + SchemableClassInput + > +) { + return new class_( + await class_.schema.parseAsync({ ...class_.defaultValues, ...values }, params) + ) as InstanceType +} -/** - * 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, +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, >( - class_: C | SchemableClass<$Config>, - ...[values, params]: NewSchemableArgs<$Config["input"]> -) => pipe( - parseZodTypeEffect< - z.output, - z.input - >( - class_.schemaWithDefaultValues, - values || {}, - params, - ), + class_: C | SchemableClass< + SchemaT, + SchemaUnknownKeys, + SchemaCatchall, + Values, + DefaultValues, + "Class" + >, - Effect.map(values => new class_(values) as InstanceType), -) + ...[values, params]: NewSchemableArgs< + SchemableClassInput + > +) { + return pipe( + parseZodTypeEffect( + class_.schema, + { ...class_.defaultValues, ...values }, + params, + ), + + Effect.map(values => new class_(values) as InstanceType), + ) +} diff --git a/src/observable/index.ts b/src/observable/index.ts new file mode 100644 index 0000000..6f219a5 --- /dev/null +++ b/src/observable/index.ts @@ -0,0 +1 @@ +export * from "./makeSchemableClassObservable" diff --git a/src/observable/makeSchemableClassObservable.ts b/src/observable/makeSchemableClassObservable.ts new file mode 100644 index 0000000..7b5fcb7 --- /dev/null +++ b/src/observable/makeSchemableClassObservable.ts @@ -0,0 +1,29 @@ +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 extends z.ZodRawShape, + SchemaUnknownKeys extends z.UnknownKeysParam, + SchemaCatchall extends z.ZodTypeAny, + + Values extends {}, + DefaultValues extends Partial, +>( + extend: C | SchemableClass +) { + return class extends (extend as AbstractConstructor) { + constructor(...args: any[]) { + super(...args) + + makeObservable(this, + mapValues(this.schema.shape, () => observable) + ) + } + } as unknown as C +} diff --git a/src/tests.ts b/src/tests.ts index c19daf7..6015710 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -1,64 +1,63 @@ +import { pipeInto } from "ts-functional-pipe" import { z } from "zod" -import { makeSchemableClass, newSchemable } from "." -import { dejsonifyBigIntSchema, dejsonifySchemable, dejsonifySchemableSchema, jsonifyBigIntSchema, jsonifySchemableSchema, makeJsonifiableSchemableClass } from "./jsonifiable" +import { defineDefaultValues, extendSchemableClass, makeSchemableClass, newSchemable } from "." +import { dejsonifyBigIntSchema, dejsonifySchemable, jsonifyBigIntSchema, makeJsonifiableSchemableClass } from "./jsonifiable" +import { makeSchemableClassObservable } from "./observable" -const GroupSchema = z.object({ - /** Group ID */ - id: z.bigint(), +const UserLevel = z.enum(["User", "Admin"]) - /** Group name */ - name: z.string(), -}) -const GroupSchemableObject = makeSchemableClass({ schema: GroupSchema }) +class User extends pipeInto( + makeSchemableClass({ + schema: z.object({ + id: z.bigint(), + name: z.string(), + level: UserLevel, + }), -const GroupJsonifiableSchemableObject = makeJsonifiableSchemableClass(GroupSchemableObject, { - jsonifySchema: ({ schema, s }) => schema.extend({ - id: jsonifyBigIntSchema(s.id) + defaultValues: defineDefaultValues({ + level: "User" as const + }), }), - dejsonifySchema: ({ schema, s }) => schema.extend({ - id: dejsonifyBigIntSchema(s.id) + v => makeSchemableClassObservable(v), + + v => makeJsonifiableSchemableClass(v, { + jsonifySchema: ({ schema, shape }) => schema.extend({ + id: jsonifyBigIntSchema(shape.id) + }), + + dejsonifySchema: ({ schema, shape }) => schema.extend({ + id: dejsonifyBigIntSchema(shape.id) + }), + }), +) {} + +User.schema + + +const user1 = newSchemable(User, { id: 1n, name: "User" }) +user1.schema + +const jsonifiedUser1 = user1.jsonify() +console.log(jsonifiedUser1) +console.log(dejsonifySchemable(User, jsonifiedUser1)) + + +const UserWithPhone = extendSchemableClass(User, { + schema: ({ schema }) => schema.extend({ + phone: z.string() + }), + + defaultValues: defaultValues => defineDefaultValues({ + ...defaultValues, + phone: "+33600000000", }), }) -class Group extends GroupJsonifiableSchemableObject {} +UserWithPhone.defaultValues -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) +// const user2 = newSchemable(UserWithPhone, { id: 1n, name: "User" }) +// console.log(user2.jsonify()) diff --git a/src/util.ts b/src/util.ts index 2cf7b08..a1c11d4 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,8 +1,28 @@ import { Effect, pipe } from "effect" -import { mapValues } from "lodash-es" +import { AbstractClass, Class as ConcreteClass } from "type-fest" import { z } from "zod" +export function identity(value: T) { + return value +} + + +export type ClassType = "AbstractClass" | "Class" + +export type Class< + Type extends ClassType, + T, + Arguments extends unknown[] = any[], +> = ( + Type extends "AbstractClass" + ? AbstractClass + : Type extends "Class" + ? ConcreteClass + : never +) + + /** * Represents the static members of a class. * @template C - The class type. @@ -12,52 +32,6 @@ export type StaticMembers = { } -/** - * 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 - ? 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. *