diff --git a/.drone.jsonnet b/.drone.jsonnet index fefec26..ecbbc49 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -16,8 +16,8 @@ local lint_step = { local build_step = { name: "build", - image: bun_image, - commands: ["bun run build"], + image: node_image, + commands: ["npm run build"], }; local pack_step = { diff --git a/bun.lockb b/bun.lockb index 6409dc5..422b3ef 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index fbd80fd..4c43ba9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@thilawyn/zod-schema-class", - "version": "0.1.2", + "version": "0.1.3", "type": "module", "publishConfig": { "registry": "https://git.jvalver.de/api/packages/thilawyn/npm/" @@ -8,11 +8,12 @@ "files": [ "./dist" ], + "types": "./dist/lib.d.ts", "exports": { ".": { "import": { - "types": "./dist/lib.d.mts", - "default": "./dist/lib.mjs" + "types": "./dist/lib.d.ts", + "default": "./dist/lib.js" }, "require": { "types": "./dist/lib.d.cts", @@ -21,42 +22,45 @@ }, "./schema": { "import": { - "types": "./dist/schema.d.mts", - "default": "./dist/schema.mjs" + "types": "./dist/schema/lib.d.ts", + "default": "./dist/schema/lib.js" }, "require": { - "types": "./dist/schema.d.cts", - "default": "./dist/schema.cjs" + "types": "./dist/schema/lib.d.cts", + "default": "./dist/schema/lib.cjs" } } }, "scripts": { - "build": "rollup -c rollup.config.ts", + "build": "tsup", "lint:tsc": "tsc --noEmit", "clean:cache": "rm -f tsconfig.tsbuildinfo", "clean:dist": "rm -rf dist", "clean:node": "rm -rf node_modules" }, "dependencies": { - "@thilawyn/traitify-ts": "^0.1.15", - "decimal.js": "^10.4.3", - "effect": "^2.4.3", + "@thilawyn/traitify-ts": "^0.1.19", "lodash-es": "^4.17.21", - "mobx": "^6.12.0", - "type-fest": "^4.12.0", + "type-fest": "^4.14.0", "zod": "^3.22.4" }, + "peerDependencies": { + "mobx": "^6.12.1", + "decimal.js": "^10.4.3", + "effect": "^2.4.11" + }, + "peerDependenciesMeta": { + "mobx": { "optional": true }, + "decimal.js": { "optional": true }, + "effect": { "optional": true } + }, "devDependencies": { - "@rollup/plugin-node-resolve": "^15.2.3", "@types/lodash-es": "^4.17.12", "bun-types": "latest", - "npm-check-updates": "^16.14.15", + "npm-check-updates": "^16.14.17", "npm-sort": "^0.0.4", - "rollup": "^4.12.1", - "rollup-plugin-cleanup": "^3.2.1", - "rollup-plugin-ts": "^3.4.5", - "ts-functional-pipe": "^3.1.2", + "tsup": "^8.0.2", "tsx": "^4.7.1", - "typescript": "^5.4.2" + "typescript": "^5.4.3" } } diff --git a/rollup.config.ts b/rollup.config.ts deleted file mode 100644 index 566206a..0000000 --- a/rollup.config.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { nodeResolve } from "@rollup/plugin-node-resolve" -import { defineConfig } from "rollup" -import cleanup from "rollup-plugin-cleanup" -import ts from "rollup-plugin-ts" -import pkg from "./package.json" assert { type: "json" } - - -export const createBundleConfig = ( - input: string, - name: keyof typeof pkg.exports, -) => - defineConfig({ - input, - - output: [ - { - file: pkg.exports[name].import.default, - format: "esm", - }, - { - file: pkg.exports[name].require.default, - format: "cjs", - }, - ], - - external: id => !/^[./]/.test(id), - - plugins: [ - nodeResolve(), - ts(), - - cleanup({ - comments: "jsdoc", - extensions: ["ts"], - }), - ], - }) - - -export default [ - createBundleConfig("src/lib.ts", "."), - createBundleConfig("src/schema/lib.ts", "./schema"), -] diff --git a/src/ExtendZodSchemaClass.ts b/src/ExtendZodSchemaClass.ts new file mode 100644 index 0000000..e65ee3d --- /dev/null +++ b/src/ExtendZodSchemaClass.ts @@ -0,0 +1,56 @@ +import { TraitInstance, TraitStaticMembers, expression } from "@thilawyn/traitify-ts" +import { AbstractClass } from "type-fest" +import { z } from "zod" +import { ZodSchemaObject, ZodSchemaObjectTrait } from "./traits/ZodSchemaObject" +import { Extend, StaticMembers } from "./util" + + +export function ExtendZodSchemaClass< + Self extends ( + AbstractClass< + Values & TraitInstance>, + [values: Values] + > & + TraitStaticMembers> + ), + + T extends z.ZodRawShape, + Catchall extends z.ZodTypeAny, + Values extends object, + PartialValues extends Partial, + + ExtendedT extends z.ZodRawShape, + ExtendedCatchall extends z.ZodTypeAny, + ExtendedValues extends Values, + ExtendedPartialValues extends Partial, +>( + class_: Self | ( + AbstractClass< + Values & TraitInstance>, + [values: Values] + > & + TraitStaticMembers> + ), + + schemaWithDefaults: ( + schemaWithDefaults: z.ZodObject + ) => z.ZodObject, +) { + return expression + .extends( + class_ as ( + AbstractClass< + Omit< + Extend<[InstanceType, ExtendedValues]>, + keyof TraitInstance> + >, + [values: ExtendedValues] + > & + Omit< + StaticMembers, + keyof TraitStaticMembers> + > + ) + ) + .expresses(ZodSchemaObject(schemaWithDefaults((class_ as Self).schemaWithDefaults))) +} diff --git a/src/JsonifiedZodSchemaClass.ts b/src/JsonifiedZodSchemaClass.ts new file mode 100644 index 0000000..289a66b --- /dev/null +++ b/src/JsonifiedZodSchemaClass.ts @@ -0,0 +1,42 @@ +import { expression } from "@thilawyn/traitify-ts" +import { Class, Jsonifiable } from "type-fest" +import { z } from "zod" +import { JsonifiedZodSchemaObject, OfClass, OfClassInstance } from "./traits/JsonifiedZodSchemaObject" + + +export function JsonifiedZodSchemaClass< + Of extends OfClass, + OfInstance extends OfClassInstance, + T extends z.ZodRawShape, + Catchall extends z.ZodTypeAny, + Values extends object, + PartialValues extends Partial, + + JsonifyT extends z.ZodRawShape, + JsonifyCatchall extends z.ZodTypeAny, + DejsonifyT extends z.ZodRawShape, + DejsonifyCatchall extends z.ZodTypeAny, + JsonifiedValues extends object & Jsonifiable, +>( + of: Of | OfClass, + + props: { + jsonifySchema: ( + schema: typeof of.schema + ) => z.ZodObject, + + dejsonifySchema: ( + schema: typeof of.schema + ) => z.ZodObject, + }, +) { + return expression + .extends( + class JsonifiedZodSchemaObjectConstructor { + constructor(values: JsonifiedValues) { + Object.assign(this, values) + } + } as Class + ) + .expresses(JsonifiedZodSchemaObject(of as Of, props)) +} diff --git a/src/ZodSchemaClass.ts b/src/ZodSchemaClass.ts new file mode 100644 index 0000000..1b4b9a1 --- /dev/null +++ b/src/ZodSchemaClass.ts @@ -0,0 +1,24 @@ +import { expression } from "@thilawyn/traitify-ts" +import { Class } from "type-fest" +import { z } from "zod" +import { ZodSchemaObject } from "./traits/ZodSchemaObject" + + +export function ZodSchemaClass< + T extends z.ZodRawShape, + Catchall extends z.ZodTypeAny, + Values extends object, + PartialValues extends Partial, +>( + schemaWithDefaults: z.ZodObject +) { + return expression + .extends( + class ZodSchemaObjectConstructor { + constructor(values: Values) { + Object.assign(this, values) + } + } as Class + ) + .expresses(ZodSchemaObject(schemaWithDefaults)) +} diff --git a/src/builders/ZodSchemaClassBuilder.ts b/src/builders/ZodSchemaClassBuilder.ts deleted file mode 100644 index a16ed6f..0000000 --- a/src/builders/ZodSchemaClassBuilder.ts +++ /dev/null @@ -1,133 +0,0 @@ -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, - const Traits extends readonly Trait[], - Schemas extends object, -> { - declare ["constructor"]: typeof ZodSchemaClassBuilder - - constructor( - protected readonly expression: TraitExpressionBuilder, - 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, - >( - this: ZodSchemaClassBuilder, - - props: { - schema: z.ZodObject - - schemaWithDefaultValues: ( - schema: z.ZodObject - ) => z.ZodObject - }, - ) { - 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) - .expresses(ZodSchemaObject(schema, schemaWithDefaultValues)), - - { schema, schemaWithDefaultValues } as const, - ) - } - - jsonifiable< - S extends { - readonly schema: z.ZodObject, - readonly schemaWithDefaultValues: z.ZodObject, - 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, - - 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, - readonly schemaWithDefaultValues: z.ZodObject, - jsonifySchema?: never - dejsonifySchema?: never - }>, - - props: { - jsonifySchema: ( - schema: z.ZodObject - ) => z.ZodObject - - dejsonifySchema: ( - schema: z.ZodObject - ) => z.ZodObject - }, - ) { - 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, {}) diff --git a/src/builders/ZodSchemaClassExtender.ts b/src/builders/ZodSchemaClassExtender.ts deleted file mode 100644 index d78a3c4..0000000 --- a/src/builders/ZodSchemaClassExtender.ts +++ /dev/null @@ -1,196 +0,0 @@ -// 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, -// Subclass extends AbstractClass, -// > { -// declare ["constructor"]: typeof ZodSchemaClassExtender - -// constructor( -// readonly superclass: Superclass, -// readonly subclass: Subclass, -// ) {} - - -// schema< -// Super extends ZodSchemaAbstractClass, - -// SuperSchemaT extends z.ZodRawShape, -// SuperSchemaUnknownKeys extends z.UnknownKeysParam, -// SuperSchemaCatchall extends z.ZodTypeAny, - -// SuperValues extends object, -// SuperDefaultValues extends Partial, - -// SchemaT extends z.ZodRawShape, -// SchemaUnknownKeys extends z.UnknownKeysParam, -// SchemaCatchall extends z.ZodTypeAny, - -// Values extends SuperValues, -// DefaultValues extends Partial, -// >( -// this: ZodSchemaClassExtender< -// Super | ZodSchemaAbstractClass, -// any -// >, - -// props: { -// schema: (schema: Super["schema"]) => z.ZodObject -// 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) { -// static readonly schema = schema -// static readonly defaultValues = defaultValues -// } - -// return new this.constructor( -// this.superclass as Super, - -// Schemas as unknown as AbstractClass< -// Simplify< -// Extend<[InstanceType, Values]> -// >, - -// [values: Values] -// > & -// Simplify< -// Override<[ -// StaticMembers, -// StaticMembers, -// ]> -// > -// ) -// } - -// 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, - -// SelfSchemaT extends z.ZodRawShape, -// SelfSchemaUnknownKeys extends z.UnknownKeysParam, -// SelfSchemaCatchall extends z.ZodTypeAny, - -// SelfValues extends object, -// SelfDefaultValues extends Partial, - -// /* 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 -// >, - -// props: { -// jsonifySchema: ( -// schema: Self["schema"], -// jsonifySchema: Super["jsonifySchema"], -// ) => z.ZodObject - -// dejsonifySchema: ( -// schema: Self["schema"], -// dejsonifySchema: Super["dejsonifySchema"], -// ) => z.ZodObject -// }, -// ) { -// 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) { -// 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, JsonifiableSchemas]> -// >, - -// ConstructorParameters< -// ZodSchemaAbstractClass -// > -// > & -// Simplify< -// Override<[ -// StaticMembers, -// StaticMembers, -// ]> -// >, -// ) -// } - - -// toClass() { -// return this.subclass -// } - -// toExpressionBuilder() { -// return expression.extends(this.subclass) -// } -// } diff --git a/src/lib.ts b/src/lib.ts index ef4a60b..b7d4fe2 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -1,4 +1,6 @@ -export { ZodSchemaClassBuilder, zodSchemaClass } from "./builders/ZodSchemaClassBuilder" -export { JsonifiableZodSchemaObject } from "./traits/JsonifiableZodSchemaObject" +export { ExtendZodSchemaClass } from "./ExtendZodSchemaClass" +export { JsonifiedZodSchemaClass } from "./JsonifiedZodSchemaClass" +export { ZodSchemaClass } from "./ZodSchemaClass" +export { JsonifiedZodSchemaObject } from "./traits/JsonifiedZodSchemaObject" export { MobXObservableZodSchemaObject } from "./traits/MobXObservableZodSchemaObject" export { ZodSchemaObject, ZodSchemaObjectTrait } from "./traits/ZodSchemaObject" diff --git a/src/schema/bigint.ts b/src/schema/bigint.ts new file mode 100644 index 0000000..079f598 --- /dev/null +++ b/src/schema/bigint.ts @@ -0,0 +1,19 @@ +import { z } from "zod" + + +type JsonifiedBigIntBrand = "@thilawyn/zod-schema-class/JsonifiedBigInt" + + +export function jsonify>(schema: S) { + return schema + .transform(v => v.toString()) + .brand() +} + +export function dejsonify>(schema: S) { + return z + .custom>() + .pipe(z.string()) + .pipe(z.coerce.bigint()) + .pipe(schema) +} diff --git a/src/schema/date.ts b/src/schema/date.ts new file mode 100644 index 0000000..287121e --- /dev/null +++ b/src/schema/date.ts @@ -0,0 +1,19 @@ +import { z } from "zod" + + +type JsonifiedDateBrand = "@thilawyn/zod-schema-class/JsonifiedDate" + + +export function jsonify>(schema: S) { + return schema + .transform(v => v.toString()) + .brand() +} + +export function dejsonify>(schema: S) { + return z + .custom>() + .pipe(z.string()) + .pipe(z.coerce.date()) + .pipe(schema) +} diff --git a/src/schema/decimal.ts b/src/schema/decimal.ts new file mode 100644 index 0000000..5f946b7 --- /dev/null +++ b/src/schema/decimal.ts @@ -0,0 +1,36 @@ +import Decimal from "decimal.js" +import { z } from "zod" + + +type JsonifiedDecimalBrand = "@thilawyn/zod-schema-class/JsonifiedDecimal" + + +export function decimal() { + return z.custom(v => Decimal.isDecimal(v)) +} + +export module decimal { + export function jsonifyDecimalSchema>(schema: S) { + return schema + .transform(v => v.toJSON()) + .brand() + } + + export function dejsonifyDecimalSchema>(schema: S) { + return z + .custom>() + .pipe( + z + .string() + .transform(v => { + try { + return new Decimal(v) + } + catch (e) { + return v + } + }) + ) + .pipe(schema) + } +} diff --git a/src/schema/effect/index.ts b/src/schema/effect/index.ts index 904b057..720a9ef 100644 --- a/src/schema/effect/index.ts +++ b/src/schema/effect/index.ts @@ -1,6 +1 @@ -import { option } from "./option" - - -export const effect = { - option, -} as const +export { option } from "./option" diff --git a/src/schema/effect/option.ts b/src/schema/effect/option.ts index 53b6abf..8908d0c 100644 --- a/src/schema/effect/option.ts +++ b/src/schema/effect/option.ts @@ -3,19 +3,144 @@ import { identity } from "lodash-es" import { z } from "zod" -export const option = { - option: (schema: S) => - z.union([option.some(schema), option.none(schema)]), +type JsonifiedEffectOptionSomeBrand = "@thilawyn/zod-schema-class/JsonifiedEffectOptionSome" - some: (schema: S) => z - .custom>>(v => Option.isOption(v) && Option.isSome(v), "Not an Option") - .pipe(z.object({ value: schema }).passthrough()) - .transform>>(identity), - none: (_schema?: S) => - z.custom - : unknown - >>(v => Option.isOption(v) && Option.isNone(v), "Not an Option"), -} as const +export function option(schema: InnerS) { + return z.union([option.some(schema), option.none(schema)]) +} + +export module option { + export type Option = ReturnType> + + function getSomeSchema(schema: Option): some.Some { + return schema.options[0] + } + function getNoneSchema(schema: Option): none.None { + return schema.options[1] + } + + + export function jsonify< + InnerS extends z.ZodTypeAny, + JsonifiedInnerS extends z.ZodTypeAny = InnerS, + >( + schema: Option, + jsonifySchema: (schema: InnerS) => JsonifiedInnerS = identity, + ) { + return z.union([ + some.jsonify(getSomeSchema(schema), jsonifySchema), + none.jsonify(getNoneSchema(schema), jsonifySchema), + ]) + } + + export function dejsonify< + InnerS extends z.ZodTypeAny, + DejsonifiedInnerS extends z.ZodTypeAny = InnerS, + >( + schema: Option, + dejsonifySchema: (schema: InnerS) => DejsonifiedInnerS = identity, + ) { + return z.union([ + some.dejsonify(getSomeSchema(schema), dejsonifySchema), + none.dejsonify(getNoneSchema(schema), dejsonifySchema), + ]) + } + + + export function some< + InnerS extends z.ZodTypeAny + >( + schema: InnerS + ) { + return z + .custom>>( + v => Option.isOption(v) && Option.isSome(v), + "Not an Option.Some", + ) + .pipe(z.object({ value: schema }).passthrough()) + .transform(v => Option.some>(v.value)) + } + + export module some { + export type Some = ReturnType> + + function getInnerSchema(schema: Some): InnerS { + return schema._def.schema._def.out.shape.value + } + + + export function jsonify< + InnerS extends z.ZodTypeAny, + JsonifiedInnerS extends z.ZodTypeAny = InnerS, + >( + schema: Some, + jsonifySchema: (schema: InnerS) => JsonifiedInnerS = identity, + ) { + return schema + .transform(v => Option.getOrThrow(v)) + .pipe(jsonifySchema(getInnerSchema(schema))) + .brand() + } + + export function dejsonify< + InnerS extends z.ZodTypeAny, + DejsonifiedInnerS extends z.ZodTypeAny = InnerS, + >( + schema: Some, + dejsonifySchema: (schema: InnerS) => DejsonifiedInnerS = identity, + ) { + return z + .custom & z.BRAND>() + .pipe(dejsonifySchema(getInnerSchema(schema))) + .transform(v => Option.some>(v)) + .pipe(schema) + } + } + + + export function none< + InnerS extends z.ZodTypeAny | unknown = unknown + >( + _schema?: InnerS + ) { + return z.custom< + Option.None< + InnerS extends z.ZodTypeAny + ? z.output + : unknown + > + >( + v => Option.isOption(v) && Option.isNone(v), + "Not an Option.None", + ) + } + + export module none { + export type None = ReturnType> + + + export function jsonify< + InnerS extends z.ZodTypeAny, + JsonifiedInnerS extends z.ZodTypeAny = InnerS, + >( + schema: None, + _jsonifySchema?: (schema: InnerS) => JsonifiedInnerS, + ) { + return schema.transform(() => null) + } + + export function dejsonify< + InnerS extends z.ZodTypeAny, + DejsonifiedInnerS extends z.ZodTypeAny = InnerS, + >( + schema: None, + _dejsonifySchema?: (schema: InnerS) => DejsonifiedInnerS, + ) { + return z + .null() + .transform(() => Option.none>()) + .pipe(schema) + } + } +} diff --git a/src/schema/jsonifiable/bigint.ts b/src/schema/jsonifiable/bigint.ts deleted file mode 100644 index b6d02d0..0000000 --- a/src/schema/jsonifiable/bigint.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { identity } from "lodash-es" -import { Opaque } from "type-fest" -import { z } from "zod" - - -export type JsonifiedBigInt = Opaque - - -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/schema/jsonifiable/date.ts b/src/schema/jsonifiable/date.ts deleted file mode 100644 index e147a5d..0000000 --- a/src/schema/jsonifiable/date.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { identity } from "lodash-es" -import { Opaque } from "type-fest" -import { z } from "zod" - - -export type JsonifiedDate = Opaque - - -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/schema/jsonifiable/decimal.ts b/src/schema/jsonifiable/decimal.ts deleted file mode 100644 index ca87662..0000000 --- a/src/schema/jsonifiable/decimal.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Decimal } from "decimal.js" -import { identity } from "lodash-es" -import { Opaque } from "type-fest" -import { z } from "zod" - - -export type JsonifiedDecimal = Opaque - - -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/schema/jsonifiable/index.ts b/src/schema/jsonifiable/index.ts deleted file mode 100644 index 6bff7ae..0000000 --- a/src/schema/jsonifiable/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -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 diff --git a/src/schema/lib.ts b/src/schema/lib.ts index 9339106..1208dd9 100644 --- a/src/schema/lib.ts +++ b/src/schema/lib.ts @@ -1,2 +1,12 @@ -export * from "./effect" -export * from "./jsonifiable" +import * as bigint from "./bigint" +import * as date from "./date" +import { decimal } from "./decimal" +import * as effect from "./effect" + + +export const s = { + bigint, + date, + decimal, + effect, +} as const diff --git a/src/shapes/JsonifiableZodSchemaClass.ts b/src/shapes/JsonifiableZodSchemaClass.ts deleted file mode 100644 index 2f35971..0000000 --- a/src/shapes/JsonifiableZodSchemaClass.ts +++ /dev/null @@ -1,78 +0,0 @@ -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 & - 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 & - 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 - readonly dejsonifySchema: z.ZodObject -} diff --git a/src/shapes/ZodSchemaClass.ts b/src/shapes/ZodSchemaClass.ts deleted file mode 100644 index 680c59a..0000000 --- a/src/shapes/ZodSchemaClass.ts +++ /dev/null @@ -1,55 +0,0 @@ -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, -> = ( - Class & - ZodSchemas -) - -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, -> = ( - AbstractClass & - ZodSchemas -) - -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, -> = { - readonly schema: z.ZodObject - readonly schemaWithDefaultValues: z.ZodObject -} diff --git a/src/tests.ts b/src/tests.ts index dff7e44..d73750e 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -1,64 +1,60 @@ import { Implements } from "@thilawyn/traitify-ts" +import { Option } from "effect" import { z } from "zod" -import { zodSchemaClass } from "./builders/ZodSchemaClassBuilder" -import { dejsonify, jsonify } from "./schema/jsonifiable" +import { ExtendZodSchemaClass } from "./ExtendZodSchemaClass" +import { JsonifiedZodSchemaClass } from "./JsonifiedZodSchemaClass" +import { ZodSchemaClass } from "./ZodSchemaClass" +import { s } from "./schema/lib" import { MobXObservableZodSchemaObject } from "./traits/MobXObservableZodSchemaObject" -const exp = zodSchemaClass - .schema({ - schema: z.object({ - /** User ID */ - id: z.bigint(), +const userExp = ZodSchemaClass( + z.object({ + /** User ID */ + id: s.effect.option(z.bigint()).default(Option.none()), - /** Username */ - name: z.string(), - }), + /** Username */ + name: z.string(), - schemaWithDefaultValues: s => s.extend({ - id: s.shape.id.default(-1n), - }), + /** Role */ + role: z.enum(["User", "Moderator", "Admin"]).default("User"), }) - .jsonifiable({ - jsonifySchema: s => s.extend({ - id: jsonify.bigint(s.shape.id) - }), - - dejsonifySchema: s => s.extend({ - id: dejsonify.bigint(s.shape.id) - }), - }) - .toExpression() +) .expresses(MobXObservableZodSchemaObject) .build() +@userExp.staticImplements +class User extends userExp.extends implements Implements {} -@exp.staticImplements -class User extends exp.extends implements Implements {} - -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 userInst = User.create({ id: Option.some(1n), name: "User" }) +const userInstEffect = User.createEffect({ id: Option.some(1n), name: "User" }) -// 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 jsonifiedUserExp = JsonifiedZodSchemaClass(User, { + jsonifySchema: schema => schema.extend({ + id: s.effect.option.jsonify(schema.shape.id, s.bigint.jsonify) + }), + + dejsonifySchema: schema => schema.extend({ + id: s.effect.option.dejsonify(schema.shape.id, s.bigint.dejsonify) + }), +}).build() + +@jsonifiedUserExp.staticImplements +class JsonifiedUser extends jsonifiedUserExp.extends implements Implements {} + +const jsonifiedUserInst = JsonifiedUser.jsonify(userInst) +// console.log(JSON.stringify(jsonifiedUserInst)) +console.log(jsonifiedUserInst.dejsonify().id) -// class AdminUser extends AdminUserProto.toClass() {} -// const subInst = await AdminUser.createPromise({ id: 2n, prout: "" }) -// console.log(subInst) +const adminUserExp = ExtendZodSchemaClass(User, + schema => schema.extend({ + role: z.literal("Admin").default("Admin") + }) +).build() + +@adminUserExp.staticImplements +class AdminUser extends adminUserExp.extends implements Implements {} + +const admin = AdminUser.create({ id: Option.some(2n), name: "Admin" }) diff --git a/src/tests2.ts b/src/tests2.ts new file mode 100644 index 0000000..b61c281 --- /dev/null +++ b/src/tests2.ts @@ -0,0 +1,20 @@ +import { Option } from "effect" +import { z } from "zod" +import { s } from "./schema/lib" + + +const schema = z.object({ + id: s.effect.option(z.bigint()) +}) + +const jsonifySchema = schema.extend({ + id: s.effect.option.jsonify(schema.shape.id, s.bigint.jsonify) +}) + +const dejsonifySchema = schema.extend({ + id: s.effect.option.dejsonify(schema.shape.id, s.bigint.dejsonify) +}) + + +const jsonified = jsonifySchema.parse({ id: Option.some(1n) }) +console.log(dejsonifySchema.parse(jsonified)) diff --git a/src/traits/ExtendableZodSchemaObject.ts b/src/traits/ExtendableZodSchemaObject.ts index 0fc0891..d8d271b 100644 --- a/src/traits/ExtendableZodSchemaObject.ts +++ b/src/traits/ExtendableZodSchemaObject.ts @@ -1,16 +1,51 @@ -// import { trait } from "@thilawyn/traitify-ts" -// import { ZodSchemaClassExtender } from "../builders/ZodSchemaClassExtender" -// import { ZodSchemaClass } from "../shapes/ZodSchemaClass" +import { ImplStatic, expression, implStaticThis } from "@thilawyn/traitify-ts" +import { AbstractClass } from "type-fest" +import { z } from "zod" +import { Extend, StaticMembers } from "../util" +import { ZodSchemaObject } from "./ZodSchemaObject" -// export const ExtendableZodSchemaObject = trait -// .implement(Super => class ExtendableZodSchemaObject extends Super { -// static extend< -// Self extends ZodSchemaClass, -// >( -// this: Self -// ) { -// return new ZodSchemaClassExtender(this, this) -// } -// }) -// .build() +export const ExtendableZodSchemaObject = < + T extends z.ZodRawShape, + Catchall extends z.ZodTypeAny, + Values extends object, + PartialValues extends Partial, +>( + schemaWithDefaults: z.ZodObject, +) => expression + .expresses(ZodSchemaObject(schemaWithDefaults)) + .build() + .subtrait() + .implement(Super => class ExtendableZodSchemaObjectImpl extends Super { + static extend< + Self extends AbstractClass, + + ExtendedT extends z.ZodRawShape, + ExtendedCatchall extends z.ZodTypeAny, + ExtendedValues extends Values, + ExtendedPartialValues extends Partial, + >( + this: Self, + + schemaWithDefaults: ( + schemaWithDefaults: z.ZodObject + ) => z.ZodObject, + ) { + const t = implStaticThis(ExtendableZodSchemaObjectImpl, this) + return expression + .extends( + t as unknown as ( + AbstractClass< + Omit< + Extend<[InstanceType, ExtendedValues]>, + keyof ExtendableZodSchemaObjectImpl + >, + [values: ExtendedValues] + > & + Omit, keyof ImplStatic> + ) + ) + .expresses(ExtendableZodSchemaObject(schemaWithDefaults(t.schemaWithDefaults))) + } + }) + .build() diff --git a/src/traits/JsonifiableZodSchemaObject.ts b/src/traits/JsonifiableZodSchemaObject.ts deleted file mode 100644 index c747a8b..0000000 --- a/src/traits/JsonifiableZodSchemaObject.ts +++ /dev/null @@ -1,110 +0,0 @@ -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, - - 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, - schemaWithDefaultValues: z.ZodObject, - jsonifySchema: z.ZodObject, - dejsonifySchema: z.ZodObject, -) => 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) { - return this.constructor.jsonifySchema.parse(this, params) - } - - jsonifyPromise(params?: Partial) { - return this.constructor.jsonifySchema.parseAsync(this, params) - } - - jsonifyEffect(params?: Partial) { - return parseZodSchemaEffect( - this.constructor.jsonifySchema, - this, - params, - ) - } - - - static dejsonify< - Instance extends Values - >( - this: ( - Class & - ImplStatic - ), - values: JsonifiedValues, - params?: Partial, - ) { - return this - .transform(this.dejsonifySchema) - .parse(values, params) - } - - static dejsonifyPromise< - Instance extends Values - >( - this: ( - Class & - ImplStatic - ), - values: JsonifiedValues, - params?: Partial, - ) { - return this - .transform(this.dejsonifySchema) - .parseAsync(values, params) - } - - static dejsonifyEffect< - Instance extends Values - >( - this: ( - Class & - ImplStatic - ), - values: JsonifiedValues, - params?: Partial, - ) { - return parseZodSchemaEffect( - this.transform(this.dejsonifySchema), - values, - params, - ) - } - }) - .build() diff --git a/src/traits/JsonifiedZodSchemaObject.ts b/src/traits/JsonifiedZodSchemaObject.ts new file mode 100644 index 0000000..34ee1c3 --- /dev/null +++ b/src/traits/JsonifiedZodSchemaObject.ts @@ -0,0 +1,174 @@ +import { TraitInstance, TraitStaticMembers, implStaticInstantiableThis, trait } from "@thilawyn/traitify-ts" +import { Class, Jsonifiable } from "type-fest" +import { z } from "zod" +import { parseZodSchemaEffect } from "../util" +import { ZodSchemaObjectTrait } from "./ZodSchemaObject" + + +export type OfClass< + Instance extends OfClassInstance, + + T extends z.ZodRawShape, + Catchall extends z.ZodTypeAny, + Values extends object, + PartialValues extends Partial, +> = ( + Class & + TraitStaticMembers> +) + +export type OfClassInstance< + T extends z.ZodRawShape, + Catchall extends z.ZodTypeAny, + Values extends object, + PartialValues extends Partial, +> = ( + Values & TraitInstance> +) + + +export const JsonifiedZodSchemaObject = < + Of extends OfClass, + OfInstance extends OfClassInstance, + T extends z.ZodRawShape, + Catchall extends z.ZodTypeAny, + Values extends object, + PartialValues extends Partial, + + JsonifyT extends z.ZodRawShape, + JsonifyCatchall extends z.ZodTypeAny, + DejsonifyT extends z.ZodRawShape, + DejsonifyCatchall extends z.ZodTypeAny, + JsonifiedValues extends object & Jsonifiable, +>( + of: Of | OfClass, + + props: { + jsonifySchema: ( + schema: typeof of.schema + ) => z.ZodObject, + + dejsonifySchema: ( + schema: typeof of.schema + ) => z.ZodObject, + }, +) => trait + .implement(Super => class JsonifiedZodSchemaObjectImpl extends Super { + declare ["constructor"]: typeof JsonifiedZodSchemaObjectImpl + + static readonly of = of as Of + static readonly jsonifySchema = props.jsonifySchema(of.schema) + static readonly dejsonifySchema = props.dejsonifySchema(of.schema) + + + static pipeSchemaIntoInstance< + Instance extends JsonifiedValues, + + SchemaT extends z.ZodRawShape, + SchemaUnknownKeys extends z.UnknownKeysParam, + SchemaCatchall extends z.ZodTypeAny, + SchemaOutput extends JsonifiedValues, + SchemaInput, + >( + this: Class, + schema: z.ZodObject, + ) { + return schema.transform(values => new this(values)) + } + + + static jsonify< + Instance extends JsonifiedValues + >( + this: Class, + values: Values, + params?: Partial, + ) { + const t = implStaticInstantiableThis(JsonifiedZodSchemaObjectImpl, this) + + return t + .pipeSchemaIntoInstance(t.jsonifySchema) + .parse(values, params) + } + + static jsonifyPromise< + Instance extends JsonifiedValues + >( + this: Class, + values: Values, + params?: Partial, + ) { + const t = implStaticInstantiableThis(JsonifiedZodSchemaObjectImpl, this) + + return t + .pipeSchemaIntoInstance(t.jsonifySchema) + .parseAsync(values, params) + } + + static jsonifyEffect< + Instance extends JsonifiedValues + >( + this: Class, + values: Values, + params?: Partial, + ) { + const t = implStaticInstantiableThis(JsonifiedZodSchemaObjectImpl, this) + + return parseZodSchemaEffect( + t.pipeSchemaIntoInstance(t.jsonifySchema), + values, + params, + ) + } + + + static dejsonify( + values: JsonifiedValues, + params?: Partial, + ) { + return this.of + .pipeSchemaIntoInstance(this.dejsonifySchema) + .parse(values, params) + } + + static dejsonifyPromise( + values: JsonifiedValues, + params?: Partial, + ) { + return this.of + .pipeSchemaIntoInstance(this.dejsonifySchema) + .parseAsync(values, params) + } + + static dejsonifyEffect( + values: JsonifiedValues, + params?: Partial, + ) { + return parseZodSchemaEffect( + this.of.pipeSchemaIntoInstance(this.dejsonifySchema), + values, + params, + ) + } + + dejsonify(params?: Partial) { + return this.constructor.of.pipeSchemaIntoInstance( + this.constructor.dejsonifySchema + ).parse(this, params) + } + + dejsonifyPromise(params?: Partial) { + return this.constructor.of.pipeSchemaIntoInstance( + this.constructor.dejsonifySchema + ).parseAsync(this, params) + } + + dejsonifyEffect(params?: Partial) { + return parseZodSchemaEffect( + this.constructor.of.pipeSchemaIntoInstance(this.constructor.dejsonifySchema), + this, + params, + ) + } + }) + .build() diff --git a/src/traits/MobXObservableZodSchemaObject.ts b/src/traits/MobXObservableZodSchemaObject.ts index c9913da..9c3a409 100644 --- a/src/traits/MobXObservableZodSchemaObject.ts +++ b/src/traits/MobXObservableZodSchemaObject.ts @@ -8,13 +8,15 @@ export const MobXObservableZodSchemaObject = trait .staticAbstract(Super => class extends Super { declare readonly schema: z.ZodObject }) - .implement(Super => class ObservableZodSchemaObject extends Super { + .implement(Super => class MobXObservableZodSchemaObjectImpl extends Super { + declare ["constructor"]: typeof MobXObservableZodSchemaObjectImpl + constructor(...args: any[]) { super(...args) makeObservable(this, mapValues( - (this.constructor as typeof ObservableZodSchemaObject).schema.shape, + this.constructor.schema.shape, () => observable, ) ) diff --git a/src/traits/ZodSchemaObject.ts b/src/traits/ZodSchemaObject.ts index e866fdf..2efca03 100644 --- a/src/traits/ZodSchemaObject.ts +++ b/src/traits/ZodSchemaObject.ts @@ -1,7 +1,7 @@ -import { ImplStatic, trait } from "@thilawyn/traitify-ts" +import { implStaticInstantiableThis, trait } from "@thilawyn/traitify-ts" import { Class, HasRequiredKeys } from "type-fest" import { z } from "zod" -import { parseZodSchemaEffect } from "../util" +import { parseZodSchemaEffect, stripZodObjectDefaults } from "../util" type CreateArgs = ( @@ -12,111 +12,104 @@ type CreateArgs = ( 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, + T extends z.ZodRawShape, + Catchall extends z.ZodTypeAny, + Values extends object, + PartialValues extends Partial, >( - schema: z.ZodObject, - schemaWithDefaultValues: z.ZodObject, + schemaWithDefaults: z.ZodObject, ) => trait - .implement(Super => class ZodSchemaObject extends Super { - static readonly schema = schema - static readonly schemaWithDefaultValues = schemaWithDefaultValues + .implement(Super => { + class ZodSchemaObjectImpl extends Super { + static readonly schema = stripZodObjectDefaults(schemaWithDefaults) + static readonly schemaWithDefaults = schemaWithDefaults - static transform< - Instance extends Values, - T extends z.ZodRawShape, - UnknownKeys extends z.UnknownKeysParam, - Catchall extends z.ZodTypeAny, - Output extends Values, - Input, - >( - this: Class, - schema: z.ZodObject, - ) { - return schema.transform(values => new this(values)) + static pipeSchemaIntoInstance< + Instance extends Values, + + SchemaT extends z.ZodRawShape, + SchemaUnknownKeys extends z.UnknownKeysParam, + SchemaCatchall extends z.ZodTypeAny, + SchemaOutput extends Values, + SchemaInput, + >( + this: Class, + schema: z.ZodObject, + ) { + return schema.transform(values => new this(values)) + } + + static pipeInstanceIntoSchema< + Self extends Class, + + SchemaT extends z.ZodRawShape, + SchemaUnknownKeys extends z.UnknownKeysParam, + SchemaCatchall extends z.ZodTypeAny, + SchemaOutput, + SchemaInput extends Values, + >( + this: Self, + schema: z.ZodObject, + ) { + return z.instanceof(this).pipe(schema) + } + + + static create< + Instance extends Values + >( + this: Class, + ...[values, params]: CreateArgs + ) { + const t = implStaticInstantiableThis(ZodSchemaObjectImpl, this) + + return t + .pipeSchemaIntoInstance(t.schemaWithDefaults) + .parse(values, params) + } + + static createPromise< + Instance extends Values + >( + this: Class, + ...[values, params]: CreateArgs + ) { + const t = implStaticInstantiableThis(ZodSchemaObjectImpl, this) + + return t + .pipeSchemaIntoInstance(t.schemaWithDefaults) + .parseAsync(values, params) + } + + static createEffect< + Instance extends Values + >( + this: Class, + ...[values, params]: CreateArgs + ) { + const t = implStaticInstantiableThis(ZodSchemaObjectImpl, this) + + return parseZodSchemaEffect( + t.pipeSchemaIntoInstance(t.schemaWithDefaults), + values, + params, + ) + } } - - static create< - Instance extends Values - >( - this: ( - Class & - ImplStatic - ), - ...[values, params]: CreateArgs - ) { - return this - .transform(this.schemaWithDefaultValues) - .parse(values, params) - } - - static createPromise< - Instance extends Values - >( - this: ( - Class & - ImplStatic - ), - ...[values, params]: CreateArgs - ) { - return this - .transform(this.schemaWithDefaultValues) - .parseAsync(values, params) - } - - static createEffect< - Instance extends Values - >( - this: ( - Class & - ImplStatic - ), - ...[values, params]: CreateArgs - ) { - return parseZodSchemaEffect( - this.transform(this.schemaWithDefaultValues), - values, - params, - ) - } + return ZodSchemaObjectImpl }) .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, + T extends z.ZodRawShape, + Catchall extends z.ZodTypeAny, + Values extends object, + PartialValues extends Partial, > = ( ReturnType< - typeof ZodSchemaObject< - SchemaT, - SchemaUnknownKeys, - SchemaCatchall, - - SchemaWithDefaultValuesT, - SchemaWithDefaultValuesUnknownKeys, - SchemaWithDefaultValuesCatchall, - - Values, - PartialValues - > + typeof ZodSchemaObject > ) diff --git a/src/util/class.ts b/src/util/class.ts index 616f709..a228590 100644 --- a/src/util/class.ts +++ b/src/util/class.ts @@ -1,69 +1,6 @@ -import { AbstractClass, Class as ConcreteClass } from "type-fest" +import { AbstractClass } 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 - : Type extends "Class" - ? ConcreteClass - : never -) - -/** - * Gets the type of a class (either "Class" or "AbstractClass"). - * @template C - The class type to determine. - */ -export type GetClassType = ( - C extends ConcreteClass - ? "Class" - : C extends AbstractClass - ? "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 [infer Class, ...infer Rest] - ? Class extends AbstractClass - ? Rest extends AbstractClass[] - ? [InstanceType, ...ClassesInstances] - : never - : never - : [] -) - -/** - * Represents an intersection of instances of the provided classes. - * @template Classes - An array of classes extending AbstractClass. - */ -export type ClassesInstancesIntersection[]> = ( - Classes extends [infer Class, ...infer Rest] - ? Class extends AbstractClass - ? Rest extends AbstractClass[] - ? InstanceType & ClassesInstancesIntersection - : never - : never - : {} -) - /** * Represents the static members of a class. * @template Class - A class extending AbstractClass. @@ -71,31 +8,3 @@ export type ClassesInstancesIntersection> = ( Omit ) - -/** - * Represents an array of static members corresponding to the provided classes. - * @template Classes - An array of classes extending AbstractClass. - */ -export type ClassesStaticMembers[]> = ( - Classes extends [infer Class, ...infer Rest] - ? Class extends AbstractClass - ? Rest extends AbstractClass[] - ? [StaticMembers, ...ClassesStaticMembers] - : 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 [infer Class, ...infer Rest] - ? Class extends AbstractClass - ? Rest extends AbstractClass[] - ? StaticMembers & ClassesStaticMembersIntersection - : never - : never - : {} -) diff --git a/src/util/index.ts b/src/util/index.ts index 7bc5968..bdefda8 100644 --- a/src/util/index.ts +++ b/src/util/index.ts @@ -1,4 +1,4 @@ export * from "./class" export * from "./effect" export * from "./extend" -export * from "./misc" +export * from "./zod" diff --git a/src/util/misc.ts b/src/util/misc.ts deleted file mode 100644 index f381be2..0000000 --- a/src/util/misc.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function identity(value: T) { - return value -} diff --git a/src/util/zod.ts b/src/util/zod.ts new file mode 100644 index 0000000..efcf26c --- /dev/null +++ b/src/util/zod.ts @@ -0,0 +1,25 @@ +import { mapValues } from "lodash-es" +import { z } from "zod" + + +export function stripZodObjectDefaults< + T extends z.ZodRawShape, + UnknownKeys extends z.UnknownKeysParam, + Catchall extends z.ZodTypeAny, + Output, + Input, +>( + schema: z.ZodObject +) { + return schema.extend( + mapValues(schema.shape, v => + v instanceof z.ZodDefault + ? v.removeDefault() + : v + ) as { + [K in keyof T]: T[K] extends z.ZodDefault + ? ReturnType + : T[K] + } + ) +} diff --git a/tsconfig.json b/tsconfig.json index 5470793..c8b2ea9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,7 @@ // "allowImportingTsExtensions": true, // "noEmit": true, "declaration": true, - "composite": true, + // "composite": true, "strict": true, "downlevelIteration": true, "skipLibCheck": true, diff --git a/tsup.config.ts b/tsup.config.ts new file mode 100644 index 0000000..ed2a957 --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "tsup" + + +export default defineConfig({ + entry: ["src/lib.ts", "src/schema/lib.ts"], + format: ["esm", "cjs"], + dts: true, + splitting: false, + sourcemap: true, + clean: true, +})