diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e22d27..d2516c3 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # @brainylab/resolver-validators +## 0.8.5 + +### Patch Changes + +- [`f9d8661`](https://github.com/brainylab/resolver-validators/commit/f9d86616340db6dc776d837b40a7e78dc65f22f4) Thanks [@andrefelipeschulle](https://github.com/andrefelipeschulle)! - fix build typebox-deprecated + +## 0.8.4 + +### Patch Changes + +- [`03a372b`](https://github.com/brainylab/resolver-validators/commit/03a372bb7b6287453965d559367cc6e82b34025e) Thanks [@andrefelipeschulle](https://github.com/andrefelipeschulle)! - add deprecated version from typebox, compatible from elysia + ## 0.8.3 ### Patch Changes diff --git a/clean-package.config.json b/clean-package.config.json index 097e4e8..098b5ed 100755 --- a/clean-package.config.json +++ b/clean-package.config.json @@ -15,6 +15,11 @@ "import": "./dist/resolvers/typebox.js", "default": "./dist/resolvers/typebox.js" }, + "./typebox-deprecated": { + "types": "./dist/resolvers/typebox-deprecated.d.ts", + "import": "./dist/resolvers/typebox-deprecated.js", + "default": "./dist/resolvers/typebox-deprecated.js" + }, "./zod": { "types": "./dist/resolvers/zod.d.ts", "import": "./dist/resolvers/zod.js", diff --git a/package.json b/package.json index 3032e9e..1b25556 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@brainylab/resolver-validators", - "version": "0.8.3", + "version": "0.8.5", "description": "", "keywords": [], "bugs": { @@ -20,10 +20,7 @@ ], "type": "module", "scripts": { - "build": "pnpm build:core && pnpm build:resolvers:typebox && pnpm build:resolvers:zod", - "build:core": "pnpm tsup ./src/index.ts --clean", - "build:resolvers:typebox": "pnpm tsup ./src/resolvers/typebox.ts --out-dir dist/resolvers --clean", - "build:resolvers:zod": "pnpm tsup ./src/resolvers/zod.ts --out-dir dist/resolvers", + "build": "pnpm tsup --clean", "commit": "pnpm commit:add && pnpm commit:detail && pnpm commit:push", "commit:add": "git add .", "commit:detail": "cz", @@ -45,6 +42,7 @@ "@changesets/cli": "2.29.8", "@changesets/get-release-plan": "4.0.14", "@changesets/types": "6.1.0", + "@sinclair/typebox": "0.34.41", "@types/node": "^22.7.4", "@vitest/coverage-v8": "2.1.2", "@vitest/ui": "2.1.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c1e0f31..bf00447 100755 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: '@changesets/types': specifier: 6.1.0 version: 6.1.0 + '@sinclair/typebox': + specifier: 0.34.41 + version: 0.34.41 '@types/node': specifier: ^22.7.4 version: 22.7.4 @@ -833,6 +836,9 @@ packages: cpu: [x64] os: [win32] + '@sinclair/typebox@0.34.41': + resolution: {integrity: sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==} + '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} @@ -2390,6 +2396,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.53.3': optional: true + '@sinclair/typebox@0.34.41': {} + '@standard-schema/spec@1.0.0': {} '@types/chai@5.2.3': diff --git a/src/resolvers/typebox-deprecated.spec.ts b/src/resolvers/typebox-deprecated.spec.ts new file mode 100644 index 0000000..1308253 --- /dev/null +++ b/src/resolvers/typebox-deprecated.spec.ts @@ -0,0 +1,41 @@ +import { Type } from "@sinclair/typebox"; +import { describe, expect, it } from "vitest"; + +import { rv } from "../index"; +import { resolver } from "./typebox-deprecated"; + +describe("TypeBox Deprecated Resolver", () => { + it("resolver core schema to typebox validator", () => { + const coreSchema = rv.object({ + name: rv.string({ description: "description test" }), + age: rv.optional(rv.number()), + isActive: rv.boolean(), + description: rv.nullable(rv.string()), + other: rv.object({ + name: rv.string(), + age: rv.number(), + }), + hobbies: rv.array(rv.string()), + cities: rv.tuple([rv.string(), rv.number()]), + date: rv.or(rv.date(), rv.string()), + }); + + const typeBoxSchema = Type.Object({ + name: Type.String({ description: "description test" }), + age: Type.Optional(Type.Number()), + isActive: Type.Boolean(), + description: Type.Null(Type.String()), + other: Type.Object({ + name: Type.String(), + age: Type.Number(), + }), + hobbies: Type.Array(Type.String()), + cities: Type.Tuple([Type.String(), Type.Number()]), + date: Type.Union([Type.Date(), Type.String()]), + }); + + const resolvedTypeBox = resolver(coreSchema); + + expect(resolvedTypeBox).toEqual(typeBoxSchema); + }); +}); diff --git a/src/resolvers/typebox-deprecated.ts b/src/resolvers/typebox-deprecated.ts new file mode 100644 index 0000000..dc96e85 --- /dev/null +++ b/src/resolvers/typebox-deprecated.ts @@ -0,0 +1,205 @@ +import { + type NumberOptions, + type StringOptions, + type TArray, + type TBoolean, + type TDate, + type TNull, + type TNumber, + type TObject, + type TSchema, + type TString, + type TTuple, + type TUnion, + Type, +} from "@sinclair/typebox"; + +import { isObject } from "@/utils"; + +import type { + RVNumberParams, + RVParams, + RVSchema, + RVStringParams, + RVTypes, +} from "@/types"; + +// import { TBOptional } from "./optional"; +// import { TBRequired } from "./required"; + +function TBString(params?: RVStringParams): TString { + const typeBoxParams: StringOptions = {}; + + const keys: { [key in keyof RVStringParams]: string } = { + min: "minLength", + max: "maxLength", + format: "format", + pattern: "pattern", + description: "description", + }; + + if (params) { + for (const key in keys) { + const mappedKey = keys[key as keyof typeof keys] as keyof RVStringParams; + if (params[key as keyof RVStringParams] !== undefined) { + typeBoxParams[mappedKey] = params[key as keyof RVStringParams]; + } + } + } + + return Type.String(typeBoxParams); +} + +function TBTuple(schemas: TSchema[]): TTuple { + return Type.Tuple(schemas); +} + +function TBUnion(schemas: TSchema[]): TUnion { + return Type.Union(schemas); +} + +function TBArray(schema: TArray): TArray { + return Type.Array(schema); +} + +function TBBoolean(): TBoolean { + return Type.Boolean(); +} + +function TBDate(): TDate { + return Type.Date(); +} + +function TBNullable(schema: TSchema): TNull { + return Type.Null(schema); +} + +function TBOptional(schema: TSchema): TSchema { + return Type.Optional(schema as unknown as TSchema); +} + +function TBNumber(params?: RVNumberParams): TNumber { + const typeBoxParams: NumberOptions = {}; + + const keys: { [key in keyof RVNumberParams]: string } = { + min: "minLength", + max: "maxLength", + description: "description", + }; + + if (params) { + for (const key in keys) { + const mappedKey = keys[key as keyof typeof keys] as keyof RVNumberParams; + if (params[key as keyof RVNumberParams] !== undefined) { + typeBoxParams[mappedKey] = params[key as keyof RVNumberParams]; + } + } + } + + return Type.Number(typeBoxParams); +} + +function TBObject(schema: TObject): TObject { + return Type.Object(schema); +} + +type PrimitiveSchema = { + type: RVTypes; + schema?: RVSchema; + schemas?: RVSchema[]; + params?: RVParams; + optional?: boolean; +}; + +type ResolverObjectSchema = { + type: "object"; + schema: { [key: string]: PrimitiveSchema }; +}; + +function resolverPrimitiveSchema( + options: PrimitiveSchema, +): TSchema | undefined { + if (options.type === "string") { + return TBString(options.params); + } + + if (options.type === "number") { + return TBNumber(options.params); + } + + if (options.type === "boolean") { + return TBBoolean(); + } + + if (options.type === "date") { + return TBDate(); + } + + if (options.type === "array") { + return TBArray( + resolverPrimitiveSchema(options.schema as PrimitiveSchema) as TArray, + ); + } + + if (options.type === "tuple") { + const tuplePrimitiveResolved = options.schemas?.map((schema) => + resolverPrimitiveSchema(schema as PrimitiveSchema), + ); + return TBTuple(tuplePrimitiveResolved as TSchema[]); + } + + if (options.type === "object") { + return resolverObjectSchema( + options as unknown as ResolverObjectSchema, + ) as TObject; + } + + if (options.type === "optional") { + return TBOptional( + resolverPrimitiveSchema(options.schema as PrimitiveSchema) as TSchema, + ); + } + + if (options.type === "nullable") { + return TBNullable( + resolverPrimitiveSchema(options.schema as PrimitiveSchema) as TSchema, + ); + } + + if (options.type === "or") { + return TBUnion( + options.schemas?.map( + (item) => resolverPrimitiveSchema(item as PrimitiveSchema) as TSchema, + ) as TSchema[], + ); + } +} + +function resolverObjectSchema(schema: ResolverObjectSchema) { + if (schema.type === "object") { + const resolvedTypeBox = {} as TObject; + + const primitiveSchema = schema.schema; + + for (const key in primitiveSchema) { + const value = primitiveSchema[key]; + + if (value.type === "optional") { + resolvedTypeBox[key] = resolverPrimitiveSchema(value); + continue; + } + + resolvedTypeBox[key] = resolverPrimitiveSchema(value); + } + + return TBObject(resolvedTypeBox); + } +} + +export function resolver(schema: RVSchema) { + if (isObject(schema)) { + return resolverObjectSchema(schema as ResolverObjectSchema); + } + + return resolverPrimitiveSchema(schema as PrimitiveSchema); +} diff --git a/tsup.config.ts b/tsup.config.ts index d4f187d..5c9876f 100755 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -1,11 +1,11 @@ import { defineConfig } from "tsup"; export default defineConfig({ - entry: ["!./src/**/*.spec.ts"], + entry: ["src", "!./src/**/*.spec.ts"], target: "es2022", format: ["esm"], splitting: false, - bundle: true, + bundle: false, dts: true, - external: ["typebox", "zod"], + external: ["typebox", "zod", "@sinclair/typebox"], });