From 0817f85f5d84d7e4b175bee92d2c741cd2cc5f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 5 Jan 2024 00:39:32 +0100 Subject: [PATCH] 0.1.0 (#1) 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/1 --- .drone.jsonnet | 110 ++++++++++++++++++ .gitignore | 1 - .npmrc | 1 + README.md | 4 +- bun.lockb | Bin 0 -> 154055 bytes bunfig.toml | 2 + package.json | 59 ++++++++++ rollup.config.ts | 43 +++++++ src/SchemableClass.ts | 90 ++++++++++++++ src/index.ts | 3 + src/jsonifiable/JsonifiableSchemableClass.ts | 66 +++++++++++ src/jsonifiable/dejsonifySchemable.ts | 47 ++++++++ src/jsonifiable/index.ts | 4 + .../makeJsonifiableSchemableClass.ts | 98 ++++++++++++++++ src/jsonifiable/schema/bigint.ts | 18 +++ src/jsonifiable/schema/date.ts | 18 +++ src/jsonifiable/schema/decimal.ts | 19 +++ src/jsonifiable/schema/index.ts | 4 + src/jsonifiable/schema/schemable.ts | 25 ++++ src/makeSchemableClass.ts | 49 ++++++++ src/newSchemable.ts | 77 ++++++++++++ src/tests.ts | 64 ++++++++++ src/util.ts | 82 +++++++++++++ tsconfig.json | 24 ++++ 24 files changed, 905 insertions(+), 3 deletions(-) create mode 100644 .drone.jsonnet create mode 100644 .npmrc create mode 100755 bun.lockb create mode 100644 bunfig.toml create mode 100644 package.json create mode 100644 rollup.config.ts create mode 100644 src/SchemableClass.ts create mode 100644 src/index.ts create mode 100644 src/jsonifiable/JsonifiableSchemableClass.ts create mode 100644 src/jsonifiable/dejsonifySchemable.ts create mode 100644 src/jsonifiable/index.ts create mode 100644 src/jsonifiable/makeJsonifiableSchemableClass.ts create mode 100644 src/jsonifiable/schema/bigint.ts create mode 100644 src/jsonifiable/schema/date.ts create mode 100644 src/jsonifiable/schema/decimal.ts create mode 100644 src/jsonifiable/schema/index.ts create mode 100644 src/jsonifiable/schema/schemable.ts create mode 100644 src/makeSchemableClass.ts create mode 100644 src/newSchemable.ts create mode 100644 src/tests.ts create mode 100644 src/util.ts create mode 100644 tsconfig.json diff --git a/.drone.jsonnet b/.drone.jsonnet new file mode 100644 index 0000000..fefec26 --- /dev/null +++ b/.drone.jsonnet @@ -0,0 +1,110 @@ +local bun_image = "oven/bun:1"; +local node_image = "node:20"; + + +local install_step = { + name: "install", + image: bun_image, + commands: ["bun install --frozen-lockfile"], +}; + +local lint_step = { + name: "lint", + image: bun_image, + commands: ["bun lint:tsc"], +}; + +local build_step = { + name: "build", + image: bun_image, + commands: ["bun run build"], +}; + +local pack_step = { + name: "pack", + image: node_image, + commands: ["npm pack --dry-run"], +}; + +local publish_step = { + name: "publish", + image: node_image, + + environment: { + NPM_TOKEN: { from_secret: "npm_token" } + }, + + commands: [ + "npm set @thilawyn:registry https://git.jvalver.de/api/packages/thilawyn/npm/", + "npm config set -- //git.jvalver.de/api/packages/thilawyn/npm/:_authToken $NPM_TOKEN", + "npm publish", + ], +}; + + +[ + // Lint the whole project when not in master, not in a PR nor on a tag + { + kind: "pipeline", + type: "docker", + name: "lint", + + trigger: { + ref: { + exclude: [ + "refs/heads/master", + "refs/pull/**", + "refs/tags/**", + ] + } + }, + + steps: [ + install_step, + lint_step, + ], + }, + + // Build the package without publishing for pull requests + { + kind: "pipeline", + type: "docker", + name: "build", + + trigger: { + ref: { + include: ["refs/pull/**"] + } + }, + + steps: [ + install_step, + lint_step, + build_step, + pack_step, + ], + }, + + // Build and publish the package for master and tags + { + kind: "pipeline", + type: "docker", + name: "build-publish", + + trigger: { + ref: { + include: [ + "refs/heads/master", + "refs/tags/**", + ] + } + }, + + steps: [ + install_step, + lint_step, + build_step, + publish_step, + ], + }, +] diff --git a/.gitignore b/.gitignore index ceaea36..745264f 100644 --- a/.gitignore +++ b/.gitignore @@ -129,4 +129,3 @@ dist .yarn/build-state.yml .yarn/install-state.gz .pnp.* - diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..93f962e --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +@thilawyn:registry=https://git.jvalver.de/api/packages/thilawyn/npm/ diff --git a/README.md b/README.md index b731fa9..adb9b58 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# schemable-classes +# schemable-class -Create TypeScript classes out of Zod schemas \ No newline at end of file +Create TypeScript classes out of Zod schemas diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..4e23eb8b4364adf2258b86fdc025f2982746ab68 GIT binary patch literal 154055 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 z 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 + 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/index.ts", "."), + createBundleConfig("src/jsonifiable/index.ts", "./jsonifiable"), +] diff --git a/src/SchemableClass.ts b/src/SchemableClass.ts new file mode 100644 index 0000000..f85216c --- /dev/null +++ b/src/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/index.ts b/src/index.ts new file mode 100644 index 0000000..17d8b56 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,3 @@ +export * from "./SchemableClass" +export * from "./makeSchemableClass" +export * from "./newSchemable" diff --git a/src/jsonifiable/JsonifiableSchemableClass.ts b/src/jsonifiable/JsonifiableSchemableClass.ts new file mode 100644 index 0000000..c0dc3c0 --- /dev/null +++ b/src/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/jsonifiable/dejsonifySchemable.ts b/src/jsonifiable/dejsonifySchemable.ts new file mode 100644 index 0000000..35ceafb --- /dev/null +++ b/src/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/jsonifiable/index.ts b/src/jsonifiable/index.ts new file mode 100644 index 0000000..74e7d23 --- /dev/null +++ b/src/jsonifiable/index.ts @@ -0,0 +1,4 @@ +export * from "./JsonifiableSchemableClass" +export * from "./dejsonifySchemable" +export * from "./makeJsonifiableSchemableClass" +export * from "./schema" diff --git a/src/jsonifiable/makeJsonifiableSchemableClass.ts b/src/jsonifiable/makeJsonifiableSchemableClass.ts new file mode 100644 index 0000000..a956b0b --- /dev/null +++ b/src/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/jsonifiable/schema/bigint.ts b/src/jsonifiable/schema/bigint.ts new file mode 100644 index 0000000..b0f0093 --- /dev/null +++ b/src/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/jsonifiable/schema/date.ts b/src/jsonifiable/schema/date.ts new file mode 100644 index 0000000..b5ac677 --- /dev/null +++ b/src/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/jsonifiable/schema/decimal.ts b/src/jsonifiable/schema/decimal.ts new file mode 100644 index 0000000..58c492d --- /dev/null +++ b/src/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/jsonifiable/schema/index.ts b/src/jsonifiable/schema/index.ts new file mode 100644 index 0000000..08ecef8 --- /dev/null +++ b/src/jsonifiable/schema/index.ts @@ -0,0 +1,4 @@ +export * from "./bigint" +export * from "./date" +export * from "./decimal" +export * from "./schemable" diff --git a/src/jsonifiable/schema/schemable.ts b/src/jsonifiable/schema/schemable.ts new file mode 100644 index 0000000..c6e2a2d --- /dev/null +++ b/src/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/makeSchemableClass.ts b/src/makeSchemableClass.ts new file mode 100644 index 0000000..88bcf72 --- /dev/null +++ b/src/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/newSchemable.ts b/src/newSchemable.ts new file mode 100644 index 0000000..da0be36 --- /dev/null +++ b/src/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/tests.ts b/src/tests.ts new file mode 100644 index 0000000..c19daf7 --- /dev/null +++ b/src/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/util.ts b/src/util.ts new file mode 100644 index 0000000..2cf7b08 --- /dev/null +++ b/src/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/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5470793 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "bundler", + "moduleDetection": "force", + // "allowImportingTsExtensions": true, + // "noEmit": true, + "declaration": true, + "composite": true, + "strict": true, + "downlevelIteration": true, + "skipLibCheck": true, + "jsx": "react-jsx", + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "allowJs": true, + "types": [ + "bun-types" + ] + }, + "include": ["src"] +}