diff --git a/design/typebox-adapter.blend b/design/typebox-adapter.blend deleted file mode 100644 index 9a5a821..0000000 Binary files a/design/typebox-adapter.blend and /dev/null differ diff --git a/design/typemap.blend b/design/typemap.blend new file mode 100644 index 0000000..cf51136 Binary files /dev/null and b/design/typemap.blend differ diff --git a/design/typemap.blend1 b/design/typemap.blend1 new file mode 100644 index 0000000..58ea741 Binary files /dev/null and b/design/typemap.blend1 differ diff --git a/design/typescript.jpg b/design/typescript.jpg new file mode 100644 index 0000000..f08a6ed Binary files /dev/null and b/design/typescript.jpg differ diff --git a/example/index.ts b/example/index.ts index a4205cd..5b771d1 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,23 +1,25 @@ -import { Box } from '@sinclair/typebox-adapter' -import * as v from 'valibot' -import * as z from 'zod' +import { TypeBox, Zod, Valibot } from '@sinclair/typemap' -// Valibot to TypeBox (Runtime) +// const T: TObject<{ ... }> -const V = Box( - v.object({ - x: v.number(), - y: v.number(), - z: v.number(), - }), -) +const T = TypeBox(`{ + x: number, + y: number, + z: number +}`) -// Zod to TypeBox (Static) +// const V: ObjectSchema<{ ... }> -const Z = Box( - z.object({ - a: z.string(), - b: z.string(), - c: z.string(), - }), -) \ No newline at end of file +const V = Valibot(`{ + x: number, + y: number, + z: number +}`) + +// const Z: ZodObject<{ ... }> + +const Z = Zod(`{ + x: number, + y: number, + z: number +}`) diff --git a/example/prototypes/effect.ts b/example/prototypes/effect.ts index 887f500..f65d5da 100644 --- a/example/prototypes/effect.ts +++ b/example/prototypes/effect.ts @@ -1,345 +1,345 @@ -/*-------------------------------------------------------------------------- +// /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +// @sinclair/typemap -The MIT License (MIT) +// The MIT License (MIT) -Copyright (c) 2024 Haydn Paterson (sinclair) +// Copyright (c) 2024 Haydn Paterson (sinclair) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. ----------------------------------------------------------------------------*/ +// ---------------------------------------------------------------------------*/ -import { Schema as et } from '@effect/schema/Schema' -import { Schema as es } from '@effect/schema' -import * as ast from '@effect/schema/AST' -import * as tb from '@sinclair/typebox' +// import { Schema as et } from '@effect/schema/Schema' +// import { Schema as es } from '@effect/schema' +// import * as ast from '@effect/schema/AST' +// import * as tb from '@sinclair/typebox' -// ------------------------------------------------------------------ -// Effect Guard -// ------------------------------------------------------------------ -function IsBigInt(type: ast.Annotated) { - return type.annotations[ast.IdentifierAnnotationId] === 'bigint' -} -function IsDate(type: ast.Annotated) { - return type.annotations[ast.IdentifierAnnotationId] === 'Date' -} -function IsInt(type: ast.Annotated) { - return type.annotations[ast.IdentifierAnnotationId] === 'Int' -} -function IsNull(type: ast.Annotated) { - return type instanceof ast.Literal && tb.ValueGuard.IsNull(type.literal) -} -function IsOptional(type: ast.Annotated) { - return 'isOptional' in type && type.isOptional === true -} -function IsUint8Array(type: ast.Annotated) { - return type.annotations[ast.IdentifierAnnotationId] === 'Uint8Array' -} -function IsNumberFromString(type: ast.Annotated) { - return type.annotations[ast.IdentifierAnnotationId] === 'NumberFromString' -} -function IsArray(type: ast.Annotated): type is ast.TupleType { - return type instanceof ast.TupleType && type.rest.length > 0 && type.elements.length === 0 -} -function IsTuple(type: ast.Annotated): type is ast.TupleType { - return type instanceof ast.TupleType && type.rest.length === 0 -} -// ------------------------------------------------------------------ -// Any -// ------------------------------------------------------------------ -type TFromAny<_Type> = tb.TAny -function FromAny(_type: ast.AnyKeyword): tb.TSchema { - return tb.Any() -} -// ------------------------------------------------------------------ -// Array -// ------------------------------------------------------------------ -type TFromArray>> = Result -function FromArray(_type: ast.TupleType): tb.TSchema { - return tb.Array(FromType(_type.rest[0].type)) -} -// ------------------------------------------------------------------ -// BigInt -// ------------------------------------------------------------------ -type TFromBigInt<_Type> = tb.TBigInt -function FromBigInt(_type: ast.Annotated): tb.TSchema { - return tb.BigInt() -} -// ------------------------------------------------------------------ -// Boolean -// ------------------------------------------------------------------ -type TFromBoolean<_Type> = tb.TBoolean -function FromBoolean(_type: ast.BooleanKeyword): tb.TSchema { - return tb.Boolean() -} -// ------------------------------------------------------------------ -// Date -// ------------------------------------------------------------------ -type TFromDate<_Type> = tb.TDate -function FromDate(_type: ast.Annotated): tb.TSchema { - return tb.Date() -} -// ------------------------------------------------------------------ -// Integer -// ------------------------------------------------------------------ -type TFromInteger<_Type> = tb.TNumber -function FromInteger(_type: ast.Annotated): tb.TSchema { - return tb.Number({ multipleOf: 1 }) -} -// ------------------------------------------------------------------ -// Literal: Effect literal types may be union -// ------------------------------------------------------------------ -// prettier-ignore -type TFromLiteral = ( - Value extends [infer Left extends unknown, ...infer Right extends unknown[]] - ? ( - Left extends tb.TLiteralValue - ? TFromLiteral]> - : TFromLiteral - ) : tb.TUnionEvaluated -) -function FromLiteral(type: ast.Literal): tb.TSchema { - return tb.KindGuard.IsLiteralValue(type.literal) ? tb.Literal(type.literal) : tb.Unknown() -} -// ------------------------------------------------------------------ -// FromNever -// ------------------------------------------------------------------ -function FromNever(_type: ast.NeverKeyword): tb.TSchema { - return tb.Never() -} -type TFromNever<_Type, Result extends tb.TSchema = tb.TNever> = Result -// ------------------------------------------------------------------ -// NullishOr -// ------------------------------------------------------------------ -type TFromNullishOr, tb.TNull, tb.TUndefined]>> = Result -// ------------------------------------------------------------------ -// NullOr -// ------------------------------------------------------------------ -type TFromNullOr, tb.TNull]>> = Result -// ------------------------------------------------------------------ -// Null -// ------------------------------------------------------------------ -type TFromNull<_Type> = tb.TNull -function FromNull(_type: ast.Annotated): tb.TSchema { - return tb.Null() -} -// ------------------------------------------------------------------ -// Number -// ------------------------------------------------------------------ -type TFromNumber<_Type> = tb.TNumber -function FromNumber(_type: ast.NumberKeyword): tb.TSchema { - return tb.Number() -} -// ------------------------------------------------------------------ -// Object -// ------------------------------------------------------------------ -type TFromObject<_Type> = tb.TObject -function FromObject(type: ast.ObjectKeyword): tb.TSchema { - return tb.Object({}) -} -// ------------------------------------------------------------------ -// Optional -// ------------------------------------------------------------------ -type TFromOptional, Result = tb.TOptional> = Result -function FromOptional(type: ast.Annotated): tb.TSchema { - return tb.Optional(FromType(type)) -} -// ------------------------------------------------------------------ -// Record -// ------------------------------------------------------------------ -type TFromRecord = tb.TRecordOrObject, TFromType> -// ------------------------------------------------------------------ -// SchemaClass: TypeLiteral -// ------------------------------------------------------------------ -type TFromSchemaClass> = Result -// ------------------------------------------------------------------ -// String -// ------------------------------------------------------------------ -type TFromString<_Type> = tb.TString -function FromString(_type: ast.Annotated): tb.TSchema { - return tb.String() -} -// ------------------------------------------------------------------ -// Struct -// ------------------------------------------------------------------ -// prettier-ignore -type TFromStruct, - Mapped extends tb.TProperties = { [Key in keyof Properties]: TFromType }, - Result = tb.TObject -> = Result -// prettier-ignore -function FromStruct(type: ast.TypeLiteral): tb.TSchema { - const properties = type.propertySignatures.reduce((result, property) => { - const mappedProperty = property.isOptional ? tb.Optional(FromType(property.type)) : FromType(property.type) - return { ...result, [property.name]: mappedProperty } - }, {} as tb.TProperties) as tb.TProperties - return tb.Object(properties) -} -// ------------------------------------------------------------------ -// Symbol -// ------------------------------------------------------------------ -type TFromSymbol<_Type> = tb.TSymbol -function FromSymbol(_type: ast.Annotated): tb.TSchema { - return tb.Symbol() -} -// ------------------------------------------------------------------ -// Tuple -// ------------------------------------------------------------------ -// prettier-ignore -type TFromTuple = ( - Types extends [infer Left extends unknown, ...infer Right extends unknown[]] - ? TFromTuple]> - : tb.TTuple -) -function FromTuple(type: ast.TupleType): tb.TSchema { - return tb.Tuple(type.elements.map((type) => FromType(type.type))) -} -// ------------------------------------------------------------------ -// UndefinedOr -// ------------------------------------------------------------------ -type TFromUndefinedOr, tb.TUndefined]>> = Result -// ------------------------------------------------------------------ -// Undefined -// ------------------------------------------------------------------ -type TFromUndefined<_Type> = tb.TUndefined -function FromUndefined(_type: ast.Annotated): tb.TSchema { - return tb.Undefined() -} -// ------------------------------------------------------------------ -// Uint8Array -// ------------------------------------------------------------------ -type TFromUint8Array<_Type> = tb.TUint8Array -function FromUint8Array(_type: ast.Annotated): tb.TSchema { - return tb.Uint8Array() -} -// ------------------------------------------------------------------ -// Unknown -// ------------------------------------------------------------------ -type TFromUnknown<_Type> = tb.TUnknown -function FromUnknown(_type: ast.Annotated): tb.TSchema { - return tb.Unknown() -} -// ------------------------------------------------------------------ -// Union -// ------------------------------------------------------------------ -// prettier-ignore -type TFromUnion = ( - Variants extends [infer Left extends unknown, ...infer Right extends unknown[]] - ? TFromUnion]> - : tb.TUnionEvaluated -) -function FromUnion(type: ast.Union): tb.TSchema { - return tb.Union(type.types.map((type) => FromType(type))) -} -// ------------------------------------------------------------------ -// Void -// ------------------------------------------------------------------ -type TFromVoid<_Type> = tb.TVoid -function FromVoid(_type: ast.VoidKeyword): tb.TSchema { - return tb.Void() -} -// ------------------------------------------------------------------ -// Type -// -// Note: Type differentition in Effect is quite challenging as the -// library doesn't provide discriminable types in all cases. An -// example would be Number and Integer where both are observed -// as Number. Unions also provide challenges for NullishOr and -// similar types. The order in which we resolve is important. -// ------------------------------------------------------------------ -// prettier-ignore -type TFromType = ( - Type extends es.optional ? TFromOptional : - Type extends es.Tuple ? TFromTuple : - Type extends es.Record$ ? TFromRecord : - Type extends es.Array$ ? TFromArray : - Type extends es.Date ? TFromDate : - Type extends es.Struct ? TFromStruct : - Type extends es.SchemaClass ? TFromSchemaClass : - Type extends es.Literal ? TFromLiteral : - Type extends es.Int ? TFromInteger : - Type extends es.BigInt ? TFromBigInt : - Type extends es.Boolean ? TFromBoolean : - Type extends es.Object ? TFromObject : - Type extends es.Never ? TFromNever : - Type extends es.Null ? TFromNull : - Type extends es.Number ? TFromNumber : - Type extends es.String ? TFromString : - Type extends es.Symbol ? TFromSymbol : - Type extends et ? TFromUint8Array : - Type extends es.Undefined ? TFromUndefined : - Type extends es.Void ? TFromVoid : - // Union-Like - Type extends es.UndefinedOr ? TFromUndefinedOr : - Type extends es.NullishOr ? TFromNullishOr : - Type extends es.NullOr ? TFromNullOr : - Type extends es.Union ? TFromUnion : - // Fallthrough - Type extends es.Unknown ? TFromUnknown : - Type extends es.Any ? TFromAny : - tb.TUnknown -) -// prettier-ignore -function FromType(type: ast.Annotated): tb.TSchema { - const schema = ( - // Non-Differentiable - IsOptional(type) ? FromOptional(type) : - IsArray(type) ? FromArray(type) : - IsBigInt(type) ? FromBigInt(type) : - IsDate(type) ? FromDate(type) : - IsInt(type) ? FromInteger(type) : - IsNull(type) ? FromNull(type) : - IsTuple(type) ? FromTuple(type) : - IsUint8Array(type) ? FromUint8Array(type) : - // Differentiable - type instanceof ast.AnyKeyword ? FromAny(type) : - type instanceof ast.BooleanKeyword ? FromBoolean(type) : - type instanceof ast.Literal ? FromLiteral(type) : - type instanceof ast.NeverKeyword ? FromNever(type) : - type instanceof ast.NumberKeyword ? FromNumber(type) : - type instanceof ast.ObjectKeyword ? FromObject(type) : - type instanceof ast.StringKeyword ? FromString(type) : - type instanceof ast.SymbolKeyword ? FromSymbol(type) : - type instanceof ast.TypeLiteral ? FromStruct(type) : - type instanceof ast.UndefinedKeyword ? FromUndefined(type) : - type instanceof ast.UnknownKeyword ? FromUnknown(type) : - type instanceof ast.Union ? FromUnion(type) : - type instanceof ast.VoidKeyword ? FromVoid(type) : - tb.Unknown() - ) - return schema -} -// ------------------------------------------------------------------ -// Box -// ------------------------------------------------------------------ -/** Converts an Effect Type to a TypeBox Type */ -// prettier-ignore -export type TBox = ( - Type extends es.Any ? TFromType : undefined -) -/** Converts an Effect Type to a TypeBox Type */ -// prettier-ignore -export function Box(type: Type): TBox { - return ( - es.isSchema(type) - ? FromType(type.ast) - : undefined - ) as never -} +// // ------------------------------------------------------------------ +// // Effect Guard +// // ------------------------------------------------------------------ +// function IsBigInt(type: ast.Annotated) { +// return type.annotations[ast.IdentifierAnnotationId] === 'bigint' +// } +// function IsDate(type: ast.Annotated) { +// return type.annotations[ast.IdentifierAnnotationId] === 'Date' +// } +// function IsInt(type: ast.Annotated) { +// return type.annotations[ast.IdentifierAnnotationId] === 'Int' +// } +// function IsNull(type: ast.Annotated) { +// return type instanceof ast.Literal && tb.ValueGuard.IsNull(type.literal) +// } +// function IsOptional(type: ast.Annotated) { +// return 'isOptional' in type && type.isOptional === true +// } +// function IsUint8Array(type: ast.Annotated) { +// return type.annotations[ast.IdentifierAnnotationId] === 'Uint8Array' +// } +// function IsNumberFromString(type: ast.Annotated) { +// return type.annotations[ast.IdentifierAnnotationId] === 'NumberFromString' +// } +// function IsArray(type: ast.Annotated): type is ast.TupleType { +// return type instanceof ast.TupleType && type.rest.length > 0 && type.elements.length === 0 +// } +// function IsTuple(type: ast.Annotated): type is ast.TupleType { +// return type instanceof ast.TupleType && type.rest.length === 0 +// } +// // ------------------------------------------------------------------ +// // Any +// // ------------------------------------------------------------------ +// type TFromAny<_Type> = tb.TAny +// function FromAny(_type: ast.AnyKeyword): tb.TSchema { +// return tb.Any() +// } +// // ------------------------------------------------------------------ +// // Array +// // ------------------------------------------------------------------ +// type TFromArray>> = Result +// function FromArray(_type: ast.TupleType): tb.TSchema { +// return tb.Array(FromType(_type.rest[0].type)) +// } +// // ------------------------------------------------------------------ +// // BigInt +// // ------------------------------------------------------------------ +// type TFromBigInt<_Type> = tb.TBigInt +// function FromBigInt(_type: ast.Annotated): tb.TSchema { +// return tb.BigInt() +// } +// // ------------------------------------------------------------------ +// // Boolean +// // ------------------------------------------------------------------ +// type TFromBoolean<_Type> = tb.TBoolean +// function FromBoolean(_type: ast.BooleanKeyword): tb.TSchema { +// return tb.Boolean() +// } +// // ------------------------------------------------------------------ +// // Date +// // ------------------------------------------------------------------ +// type TFromDate<_Type> = tb.TDate +// function FromDate(_type: ast.Annotated): tb.TSchema { +// return tb.Date() +// } +// // ------------------------------------------------------------------ +// // Integer +// // ------------------------------------------------------------------ +// type TFromInteger<_Type> = tb.TNumber +// function FromInteger(_type: ast.Annotated): tb.TSchema { +// return tb.Number({ multipleOf: 1 }) +// } +// // ------------------------------------------------------------------ +// // Literal: Effect literal types may be union +// // ------------------------------------------------------------------ +// // prettier-ignore +// type TFromLiteral = ( +// Value extends [infer Left extends unknown, ...infer Right extends unknown[]] +// ? ( +// Left extends tb.TLiteralValue +// ? TFromLiteral]> +// : TFromLiteral +// ) : tb.TUnionEvaluated +// ) +// function FromLiteral(type: ast.Literal): tb.TSchema { +// return tb.KindGuard.IsLiteralValue(type.literal) ? tb.Literal(type.literal) : tb.Unknown() +// } +// // ------------------------------------------------------------------ +// // FromNever +// // ------------------------------------------------------------------ +// function FromNever(_type: ast.NeverKeyword): tb.TSchema { +// return tb.Never() +// } +// type TFromNever<_Type, Result extends tb.TSchema = tb.TNever> = Result +// // ------------------------------------------------------------------ +// // NullishOr +// // ------------------------------------------------------------------ +// type TFromNullishOr, tb.TNull, tb.TUndefined]>> = Result +// // ------------------------------------------------------------------ +// // NullOr +// // ------------------------------------------------------------------ +// type TFromNullOr, tb.TNull]>> = Result +// // ------------------------------------------------------------------ +// // Null +// // ------------------------------------------------------------------ +// type TFromNull<_Type> = tb.TNull +// function FromNull(_type: ast.Annotated): tb.TSchema { +// return tb.Null() +// } +// // ------------------------------------------------------------------ +// // Number +// // ------------------------------------------------------------------ +// type TFromNumber<_Type> = tb.TNumber +// function FromNumber(_type: ast.NumberKeyword): tb.TSchema { +// return tb.Number() +// } +// // ------------------------------------------------------------------ +// // Object +// // ------------------------------------------------------------------ +// type TFromObject<_Type> = tb.TObject +// function FromObject(type: ast.ObjectKeyword): tb.TSchema { +// return tb.Object({}) +// } +// // ------------------------------------------------------------------ +// // Optional +// // ------------------------------------------------------------------ +// type TFromOptional, Result = tb.TOptional> = Result +// function FromOptional(type: ast.Annotated): tb.TSchema { +// return tb.Optional(FromType(type)) +// } +// // ------------------------------------------------------------------ +// // Record +// // ------------------------------------------------------------------ +// type TFromRecord = tb.TRecordOrObject, TFromType> +// // ------------------------------------------------------------------ +// // SchemaClass: TypeLiteral +// // ------------------------------------------------------------------ +// type TFromSchemaClass> = Result +// // ------------------------------------------------------------------ +// // String +// // ------------------------------------------------------------------ +// type TFromString<_Type> = tb.TString +// function FromString(_type: ast.Annotated): tb.TSchema { +// return tb.String() +// } +// // ------------------------------------------------------------------ +// // Struct +// // ------------------------------------------------------------------ +// // prettier-ignore +// type TFromStruct, +// Mapped extends tb.TProperties = { [Key in keyof Properties]: TFromType }, +// Result = tb.TObject +// > = Result +// // prettier-ignore +// function FromStruct(type: ast.TypeLiteral): tb.TSchema { +// const properties = type.propertySignatures.reduce((result, property) => { +// const mappedProperty = property.isOptional ? tb.Optional(FromType(property.type)) : FromType(property.type) +// return { ...result, [property.name]: mappedProperty } +// }, {} as tb.TProperties) as tb.TProperties +// return tb.Object(properties) +// } +// // ------------------------------------------------------------------ +// // Symbol +// // ------------------------------------------------------------------ +// type TFromSymbol<_Type> = tb.TSymbol +// function FromSymbol(_type: ast.Annotated): tb.TSchema { +// return tb.Symbol() +// } +// // ------------------------------------------------------------------ +// // Tuple +// // ------------------------------------------------------------------ +// // prettier-ignore +// type TFromTuple = ( +// Types extends [infer Left extends unknown, ...infer Right extends unknown[]] +// ? TFromTuple]> +// : tb.TTuple +// ) +// function FromTuple(type: ast.TupleType): tb.TSchema { +// return tb.Tuple(type.elements.map((type) => FromType(type.type))) +// } +// // ------------------------------------------------------------------ +// // UndefinedOr +// // ------------------------------------------------------------------ +// type TFromUndefinedOr, tb.TUndefined]>> = Result +// // ------------------------------------------------------------------ +// // Undefined +// // ------------------------------------------------------------------ +// type TFromUndefined<_Type> = tb.TUndefined +// function FromUndefined(_type: ast.Annotated): tb.TSchema { +// return tb.Undefined() +// } +// // ------------------------------------------------------------------ +// // Uint8Array +// // ------------------------------------------------------------------ +// type TFromUint8Array<_Type> = tb.TUint8Array +// function FromUint8Array(_type: ast.Annotated): tb.TSchema { +// return tb.Uint8Array() +// } +// // ------------------------------------------------------------------ +// // Unknown +// // ------------------------------------------------------------------ +// type TFromUnknown<_Type> = tb.TUnknown +// function FromUnknown(_type: ast.Annotated): tb.TSchema { +// return tb.Unknown() +// } +// // ------------------------------------------------------------------ +// // Union +// // ------------------------------------------------------------------ +// // prettier-ignore +// type TFromUnion = ( +// Variants extends [infer Left extends unknown, ...infer Right extends unknown[]] +// ? TFromUnion]> +// : tb.TUnionEvaluated +// ) +// function FromUnion(type: ast.Union): tb.TSchema { +// return tb.Union(type.types.map((type) => FromType(type))) +// } +// // ------------------------------------------------------------------ +// // Void +// // ------------------------------------------------------------------ +// type TFromVoid<_Type> = tb.TVoid +// function FromVoid(_type: ast.VoidKeyword): tb.TSchema { +// return tb.Void() +// } +// // ------------------------------------------------------------------ +// // Type +// // +// // Note: Type differentition in Effect is quite challenging as the +// // library doesn't provide discriminable types in all cases. An +// // example would be Number and Integer where both are observed +// // as Number. Unions also provide challenges for NullishOr and +// // similar types. The order in which we resolve is important. +// // ------------------------------------------------------------------ +// // prettier-ignore +// type TFromType = ( +// Type extends es.optional ? TFromOptional : +// Type extends es.Tuple ? TFromTuple : +// Type extends es.Record$ ? TFromRecord : +// Type extends es.Array$ ? TFromArray : +// Type extends es.Date ? TFromDate : +// Type extends es.Struct ? TFromStruct : +// Type extends es.SchemaClass ? TFromSchemaClass : +// Type extends es.Literal ? TFromLiteral : +// Type extends es.Int ? TFromInteger : +// Type extends es.BigInt ? TFromBigInt : +// Type extends es.Boolean ? TFromBoolean : +// Type extends es.Object ? TFromObject : +// Type extends es.Never ? TFromNever : +// Type extends es.Null ? TFromNull : +// Type extends es.Number ? TFromNumber : +// Type extends es.String ? TFromString : +// Type extends es.Symbol ? TFromSymbol : +// Type extends et ? TFromUint8Array : +// Type extends es.Undefined ? TFromUndefined : +// Type extends es.Void ? TFromVoid : +// // Union-Like +// Type extends es.UndefinedOr ? TFromUndefinedOr : +// Type extends es.NullishOr ? TFromNullishOr : +// Type extends es.NullOr ? TFromNullOr : +// Type extends es.Union ? TFromUnion : +// // Fallthrough +// Type extends es.Unknown ? TFromUnknown : +// Type extends es.Any ? TFromAny : +// tb.TUnknown +// ) +// // prettier-ignore +// function FromType(type: ast.Annotated): tb.TSchema { +// const schema = ( +// // Non-Differentiable +// IsOptional(type) ? FromOptional(type) : +// IsArray(type) ? FromArray(type) : +// IsBigInt(type) ? FromBigInt(type) : +// IsDate(type) ? FromDate(type) : +// IsInt(type) ? FromInteger(type) : +// IsNull(type) ? FromNull(type) : +// IsTuple(type) ? FromTuple(type) : +// IsUint8Array(type) ? FromUint8Array(type) : +// // Differentiable +// type instanceof ast.AnyKeyword ? FromAny(type) : +// type instanceof ast.BooleanKeyword ? FromBoolean(type) : +// type instanceof ast.Literal ? FromLiteral(type) : +// type instanceof ast.NeverKeyword ? FromNever(type) : +// type instanceof ast.NumberKeyword ? FromNumber(type) : +// type instanceof ast.ObjectKeyword ? FromObject(type) : +// type instanceof ast.StringKeyword ? FromString(type) : +// type instanceof ast.SymbolKeyword ? FromSymbol(type) : +// type instanceof ast.TypeLiteral ? FromStruct(type) : +// type instanceof ast.UndefinedKeyword ? FromUndefined(type) : +// type instanceof ast.UnknownKeyword ? FromUnknown(type) : +// type instanceof ast.Union ? FromUnion(type) : +// type instanceof ast.VoidKeyword ? FromVoid(type) : +// tb.Unknown() +// ) +// return schema +// } +// // ------------------------------------------------------------------ +// // Box +// // ------------------------------------------------------------------ +// /** Converts an Effect Type to a TypeBox Type */ +// // prettier-ignore +// export type TBox = ( +// Type extends es.Any ? TFromType : undefined +// ) +// /** Converts an Effect Type to a TypeBox Type */ +// // prettier-ignore +// export function Box(type: Type): TBox { +// return ( +// es.isSchema(type) +// ? FromType(type.ast) +// : undefined +// ) as never +// } diff --git a/example/prototypes/index.ts b/example/prototypes/index.ts deleted file mode 100644 index 887b7f0..0000000 --- a/example/prototypes/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as Effect from './effect' \ No newline at end of file diff --git a/hammer.mjs b/hammer.mjs index d61ea01..824fc27 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -19,14 +19,12 @@ export async function clean() { export async function start() { await shell('hammer run example/index.ts --dist target/example') } - // ------------------------------------------------------------------------------- // Format // ------------------------------------------------------------------------------- export async function format() { await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write test src example/index.ts') } - // ------------------------------------------------------------------ // Test // ------------------------------------------------------------------ @@ -39,7 +37,7 @@ export async function test(filter = '') { // ------------------------------------------------------------------ export async function build_check(target = 'target/build') { const { version } = JSON.parse(Fs.readFileSync('package.json', 'utf8')) - await shell(`cd ${target} && attw sinclair-typebox-adapter-${version}.tgz`) + await shell(`cd ${target} && attw sinclair-typemap-${version}.tgz`) } export async function build(target = 'target/build') { await test() @@ -60,7 +58,7 @@ export async function build(target = 'target/build') { export async function publish(otp, target = 'target/build') { const { version } = JSON.parse(Fs.readFileSync('package.json', 'utf8')) if(version.includes('-dev')) throw Error(`package version should not include -dev specifier`) - await shell(`cd ${target} && npm publish sinclair-typebox-adapter-${version}.tgz --access=public --otp ${otp}`) + await shell(`cd ${target} && npm publish sinclair-typemap-${version}.tgz --access=public --otp ${otp}`) await shell(`git tag ${version}`) await shell(`git push origin ${version}`) } @@ -70,5 +68,5 @@ export async function publish(otp, target = 'target/build') { export async function publish_dev(otp, target = 'target/build') { const { version } = JSON.parse(Fs.readFileSync(`${target}/package.json`, 'utf8')) if(!version.includes('-dev')) throw Error(`development package version should include -dev specifier`) - await shell(`cd ${target} && npm publish sinclair-typebox-adapter-${version}.tgz --access=public --otp ${otp} --tag dev`) + await shell(`cd ${target} && npm publish sinclair-typemap-${version}.tgz --access=public --otp ${otp} --tag dev`) } \ No newline at end of file diff --git a/license b/license index 6a4960c..81365c2 100644 --- a/license +++ b/license @@ -1,4 +1,4 @@ -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) diff --git a/package-lock.json b/package-lock.json index 3f36592..7ba8abc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,15 @@ { - "name": "@sinclair/typebox-adapter", - "version": "0.9.1", + "name": "@sinclair/typemap", + "version": "0.8.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "@sinclair/typebox-adapter", - "version": "0.9.1", + "name": "@sinclair/typemap", + "version": "0.8.0", "license": "MIT", - "dependencies": { - "valibot": "^1.0.0-beta.13", - "zod": "^3.24.1" - }, "devDependencies": { "@arethetypeswrong/cli": "^0.17.2", - "@effect/schema": "^0.75.5", "@sinclair/hammer": "^0.18.0", "@types/mocha": "^10.0.10", "@types/node": "^22.10.2", @@ -22,12 +17,10 @@ "prettier": "^3.4.2", "typescript": "^5.7.2" }, - "optionalDependencies": { + "peerDependencies": { + "@sinclair/typebox": "^0.34.14", "valibot": "^1.0.0-beta.13", "zod": "^3.24.1" - }, - "peerDependencies": { - "@sinclair/typebox": "^0.34.14" } }, "node_modules/@andrewbranch/untar.js": { @@ -98,18 +91,6 @@ "node": ">=0.1.90" } }, - "node_modules/@effect/schema": { - "version": "0.75.5", - "resolved": "https://registry.npmjs.org/@effect/schema/-/schema-0.75.5.tgz", - "integrity": "sha512-TQInulTVCuF+9EIbJpyLP6dvxbQJMphrnRqgexm/Ze39rSjfhJuufF7XvU3SxTgg3HnL7B/kpORTJbHhlE6thw==", - "dev": true, - "dependencies": { - "fast-check": "^3.21.0" - }, - "peerDependencies": { - "effect": "^3.9.2" - } - }, "node_modules/@esbuild/linux-loong64": { "version": "0.15.7", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.7.tgz", @@ -213,9 +194,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "22.10.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", - "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", + "version": "22.10.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.9.tgz", + "integrity": "sha512-Ir6hwgsKyNESl/gLOcEz3krR4CBGgliDqBQ2ma4wIhEx0w+xnoeTq3tdrNw15kU3SxogDjOgv9sqdtLW8mIHaw==", "dev": true, "dependencies": { "undici-types": "~6.20.0" @@ -579,16 +560,6 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "node_modules/effect": { - "version": "3.12.6", - "resolved": "https://registry.npmjs.org/effect/-/effect-3.12.6.tgz", - "integrity": "sha512-4gNxRpXduuvVv03528sPLGglkCAX9szGBUA6oIG3YL+6ap82JmbVp4OIa+xTurype+H9b4/I4M2ubxDxJJ01Og==", - "dev": true, - "peer": true, - "dependencies": { - "fast-check": "^3.23.1" - } - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -990,28 +961,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fast-check": { - "version": "3.23.2", - "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", - "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "dependencies": { - "pure-rand": "^6.1.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", @@ -1377,9 +1326,9 @@ } }, "node_modules/mocha": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.0.1.tgz", - "integrity": "sha512-+3GkODfsDG71KSCQhc4IekSW+ItCK/kiez1Z28ksWvYhKXV/syxMlerR/sC7whDp7IyreZ4YxceMLdTs5hQE8A==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", + "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==", "dev": true, "dependencies": { "ansi-colors": "^4.1.3", @@ -1399,8 +1348,8 @@ "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" }, "bin": { @@ -1411,6 +1360,41 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/mocha/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -1426,6 +1410,41 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1594,22 +1613,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -1936,10 +1939,10 @@ } }, "node_modules/valibot": { - "version": "1.0.0-beta.13", - "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.0.0-beta.13.tgz", - "integrity": "sha512-WCAqfG126/nadCrK36lOgVHrYWeWJfxb52PYE48gqg/8clLTy9sWjE6v/W43cVtgR+rSt30J1IAswk6ovT48pQ==", - "optional": true, + "version": "1.0.0-beta.14", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.0.0-beta.14.tgz", + "integrity": "sha512-tLyV2rE5QL6U29MFy3xt4AqMrn+/HErcp2ZThASnQvPMwfSozjV1uBGKIGiegtZIGjinJqn0SlBdannf18wENA==", + "peer": true, "peerDependencies": { "typescript": ">=5" }, @@ -2098,12 +2101,12 @@ } }, "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-unparser": { @@ -2121,6 +2124,15 @@ "node": ">=10" } }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -2137,7 +2149,7 @@ "version": "3.24.1", "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", - "optional": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -2195,15 +2207,6 @@ "dev": true, "optional": true }, - "@effect/schema": { - "version": "0.75.5", - "resolved": "https://registry.npmjs.org/@effect/schema/-/schema-0.75.5.tgz", - "integrity": "sha512-TQInulTVCuF+9EIbJpyLP6dvxbQJMphrnRqgexm/Ze39rSjfhJuufF7XvU3SxTgg3HnL7B/kpORTJbHhlE6thw==", - "dev": true, - "requires": { - "fast-check": "^3.21.0" - } - }, "@esbuild/linux-loong64": { "version": "0.15.7", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.7.tgz", @@ -2279,9 +2282,9 @@ "dev": true }, "@types/node": { - "version": "22.10.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", - "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", + "version": "22.10.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.9.tgz", + "integrity": "sha512-Ir6hwgsKyNESl/gLOcEz3krR4CBGgliDqBQ2ma4wIhEx0w+xnoeTq3tdrNw15kU3SxogDjOgv9sqdtLW8mIHaw==", "dev": true, "requires": { "undici-types": "~6.20.0" @@ -2541,16 +2544,6 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "effect": { - "version": "3.12.6", - "resolved": "https://registry.npmjs.org/effect/-/effect-3.12.6.tgz", - "integrity": "sha512-4gNxRpXduuvVv03528sPLGglkCAX9szGBUA6oIG3YL+6ap82JmbVp4OIa+xTurype+H9b4/I4M2ubxDxJJ01Og==", - "dev": true, - "peer": true, - "requires": { - "fast-check": "^3.23.1" - } - }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -2750,15 +2743,6 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, - "fast-check": { - "version": "3.23.2", - "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", - "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", - "dev": true, - "requires": { - "pure-rand": "^6.1.0" - } - }, "fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", @@ -3008,9 +2992,9 @@ "dev": true }, "mocha": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.0.1.tgz", - "integrity": "sha512-+3GkODfsDG71KSCQhc4IekSW+ItCK/kiez1Z28ksWvYhKXV/syxMlerR/sC7whDp7IyreZ4YxceMLdTs5hQE8A==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", + "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==", "dev": true, "requires": { "ansi-colors": "^4.1.3", @@ -3030,11 +3014,37 @@ "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" }, "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -3043,6 +3053,32 @@ "requires": { "has-flag": "^4.0.0" } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } } } }, @@ -3168,12 +3204,6 @@ "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "dev": true }, - "pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true - }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -3402,10 +3432,10 @@ "dev": true }, "valibot": { - "version": "1.0.0-beta.13", - "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.0.0-beta.13.tgz", - "integrity": "sha512-WCAqfG126/nadCrK36lOgVHrYWeWJfxb52PYE48gqg/8clLTy9sWjE6v/W43cVtgR+rSt30J1IAswk6ovT48pQ==", - "optional": true, + "version": "1.0.0-beta.14", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.0.0-beta.14.tgz", + "integrity": "sha512-tLyV2rE5QL6U29MFy3xt4AqMrn+/HErcp2ZThASnQvPMwfSozjV1uBGKIGiegtZIGjinJqn0SlBdannf18wENA==", + "peer": true, "requires": {} }, "validate-npm-package-name": { @@ -3512,12 +3542,20 @@ "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" + }, + "dependencies": { + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + } } }, "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true }, "yargs-unparser": { @@ -3542,7 +3580,7 @@ "version": "3.24.1", "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", - "optional": true + "peer": true } } } diff --git a/package.json b/package.json index a2f9deb..a229daf 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { - "name": "@sinclair/typebox-adapter", - "version": "0.9.1", - "description": "Integrate Valibot and Zod with TypeBox", + "name": "@sinclair/typemap", + "version": "0.8.0", + "description": "Unified Syntax Frontend and Type Remapping System for TypeBox, Valibot and Zod", "author": "sinclairzx81", "license": "MIT", "repository": { "type": "git", - "url": "https://github.com/sinclairzx81/typebox-adapter" + "url": "https://github.com/sinclairzx81/typemap" }, "scripts": { "benchmark": "hammer task benchmark", @@ -18,15 +18,12 @@ "publish": "hammer task publish" }, "peerDependencies": { - "@sinclair/typebox": "^0.34.14" - }, - "optionalDependencies": { + "@sinclair/typebox": "^0.34.14", "valibot": "^1.0.0-beta.13", "zod": "^3.24.1" }, "devDependencies": { "@arethetypeswrong/cli": "^0.17.2", - "@effect/schema": "^0.75.5", "@sinclair/hammer": "^0.18.0", "@types/mocha": "^10.0.10", "@types/node": "^22.10.2", diff --git a/readme.md b/readme.md index 6ca8a24..a7ad5aa 100644 --- a/readme.md +++ b/readme.md @@ -1,10 +1,10 @@
-

TypeBox Adapter

+

TypeMap

-

Integrate Valibot and Zod with TypeBox

+

Unified Syntax Frontend and Type Remapping System for TypeBox, Valibot and Zod

- +

@@ -19,84 +19,186 @@ ## Install ```bash -$ npm install @sinclair/typebox-adapter --save +$ npm install @sinclair/typemap --save ``` ## Example -TypeBox Adapter converts Valibot and Zod Types into TypeBox compatible schematics +Use syntax to construct types for TypeBox, Valibot and Zod ... -[TypeScript Example](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgIQgDzgXzgMyhEOAcgAEBnYAOwGMAbAQ2CgHoYBPMAUwCN0BaegBN6YGJyhEAUKEixEcAMox6MYNSy58hUhRoMmrDj3RSZ0eACo49MnABumgsTv1awXjFPhzcKzbgAXo7aARCCUpLMzHAAaq7uEPAwEHAAKkaoaJKS7FyxcAC8isqq1AA8uZwQOLEAfNnUEJRk8DGFKOgAFHYAdBDcAFac1DCdSHATk1PTMzNRcI3Nre0IkhNoAFz2PZQAriDc4p0AlAA0sxeXVxPzE5VbRP1DI0Sna3BsW717B0dn1wDAbc4FBOABHXZMTiCLYAbSIaFexDYSKIASIAF03hMAl8dvtDlAToCSVdgWB8FxYMBOGQtqtMMdjqSWay4MD1vS4PdiD9CUQsNi2cKSRyPlyeUQ+eIBZghSKFZcxbj5JLpRIsO9FdrpsDMFqdYb5vrItEAFphbkpdJcTLZSpwM3tJQqNQVIzVR31SSLFqO9qZToBPqDYajcaG9nRX3wM1bVIAeVDIzKqwm9C2wZaUCoAHMTudI0roun40oc5Rc0LuJmetm8wWi8XJjW0uW80LqLX65XiU2LsCu22YBXc5JGcz+8LjbUgA) +```typescript +import { TypeBox, Zod, Valibot } from '@sinclair/typemap' + +// const T: TObject<{ ... }> + +const T = TypeBox(`{ + x: number, + y: number, + z: number +}`) + +// const V: ObjectSchema<{ ... }> + +const V = Valibot(`{ + x: number, + y: number, + z: number +}`) + +// const Z: ZodObject<{ ... }> + +const Z = Zod(`{ + x: number, + y: number, + z: number +}`) +``` + +... or structurally remap types from one library to another + +```typescript +import { TypeBox, Valibot, Zod } from '@sinclair/typemap' + +// Syntax > Zod > Valibot > TypeBox + +const T = TypeBox(Valibot(Zod(`{ + x: number, + y: number, + z: number +}`))) +``` + +... or compile types for high performance runtime type checking ```typescript -import { Box } from '@sinclair/typebox-adapter' - -import * as v from 'valibot' -import * as z from 'zod' - -// Valibot to TypeBox (Runtime) - -const V = Box(v.object({ // const V = { - x: v.number(), // type: 'object', - y: v.number(), // required: ['x', 'y', 'z'], - z: v.number() // properties: { -})) // x: { type: 'number' }, - // y: { type: 'number' }, - // z: { type: 'number' } - // } - // } - -// Zod to TypeBox (Static) - -const Z = Box(z.object({ // const Z: TObject<{ - a: z.string(), // a: TString, - b: z.string(), // b: TString, - c: z.string() // c: TString -})) // }> +import { Compile } from '@sinclair/typemap' + +import z from 'zod' + +const T = z.object({ // const T: z.ZodObject<{ + x: z.number(), // x: z.ZodNumber, + y: z.number(), // y: z.ZodNumber, + z: z.number(), // z: z.ZodNumber, +}) // }> + +const C = Compile(T) // const C: Validator> + +const R = C.Check({ // const R: boolean - High Performance Checking! + x: 1, + y: 2, + z: 3 +}) ``` + + ## Overview -TypeBox Adapter converts Zod and Valibot types into TypeBox schematics (Json Schema). It performs a deep structural remapping of the types provided by these libraries into TypeScript-aligned Json Schema, enabling integration with industry-standard validators like Ajv and OpenAPI-related technologies, while also facilitating interoperability and acceleration via the TypeBox validation infrastructure. +TypeMap is an type mapping library developed for TypeBox, Valibot and Zod. It enables quick compatibility between each library by structurally remapping type representations from one library to another. In addition,TypeMap offers a uniform syntax for type construction as well as high-performance runtime type checking for Valibot and Zod via the TypeBox compiler infrastructure. + +TypeMap is designed to be a simple tool to enable Valibot and Zod to integrate with TypeBox and Json Schema validation infrastructure. It is also written to allow TypeBox to integrate with systems leveraging Valibot and Zod for validation. The frontend syntax provided by TypeMap seeks to explore a uniform API surface for runtime type libraries. -License MIT +License: MIT ## Contents - [Install](#Install) - [Overview](#Overview) -- [Usage](#Usage) +- [Libraries](#Libraries) + - [TypeBox](#TypeBox) - [Valibot](#Valibot) - [Zod](#Zod) +- [Static](#Static) +- [Compile](#Compile) - [Benchmark](#Benchmark) - [Contribute](#Contribute) -## Usage +## Libraries -TypeBox Adapter provides a singular Box function to transform Valibot and Zod types into TypeBox schematics. The top-level export is capable of transforming both Valibot and Zod, but you should use the appropriate submodule depending on which library you are using. +TypeMap exports mapping functions named after the library they map for. Each function can accept a type from any other library, where the function will attempt to map the type or return a `never` representation if a mapping is not possible. -### Valibot +### TypeBox -Use the `/valibot` submodule if you only have Valibot installed. Refer to the Valibot [documentation](https://valibot.dev/) for more information on this type library. +Use the TypeBox function to map the parameter into a TypeBox type ```typescript -import { Box } from '@sinclair/typebox-adapter/valibot' // Transform Valibot Only +import { TypeBox } from '@sinclair/typemap' + +const A = TypeBox(t.Number()) // const A: TNumber (TypeBox) +const B = TypeBox(v.string()) // const B: TString (Valibot) +const C = TypeBox(z.boolean()) // const C: TBoolean (Zod) +const D = TypeBox('string[]') // const D: TArray (Syntax) +``` + +### Valibot -import * as v from 'valibot' +Use the Valibot function to map the parameter into a Valibot type + +```typescript +import { Valibot } from '@sinclair/typemap' -const T = Box(v.string()) // const T = { type: 'string' } +const A = Valibot(t.Number()) // const A: v.NumberSchema (TypeBox) +const B = Valibot(v.string()) // const B: v.StringSchema (Valibot) +const C = Valibot(z.boolean()) // const C: v.BooleanSchema (Zod) +const D = Valibot('string[]') // const D: v.ArraySchema<...> (Syntax) ``` ### Zod -Use the `/zod` submodule if you only have Zod installed. Refer to the Zod [documentation](https://zod.dev/) for more information on this type library. +Use the Zod function to map the parameter into a Zod type + +```typescript +import { Zod } from '@sinclair/typemap' + +const A = Zod(t.Number()) // const A: z.ZodNumber (TypeBox) +const B = Zod(v.string()) // const B: z.ZodString (Valibot) +const C = Zod(z.boolean()) // const C: z.ZodBoolean (Zod) +const D = Zod('string[]') // const D: z.ZodArray<...> (Syntax) +``` + +## Static + +TypeMap can statically infer for TypeBox, Valibot, Zod and Syntax with the `Static` type. + +```typescript +import { type Static } from '@sinclair/typemap' + +const T = t.Number() // TypeBox +const V = v.string() // Valibot +const Z = z.boolean() // Zod +const S = 'string[]' // Syntax + +type T = Static // number +type V = Static // string +type Z = Static // boolean +type S = Static // string[] +``` + +## Compile + +TypeMap offers JIT compilation of TypeBox, Valibot, Zod and Syntax using the `Compile` function. This function will internally use the TypeBox TypeCompiler for high performance checking. This function will also gracefully degrade to dynamic checking if the runtime does not support JavaScript evaluation. + +The `Compile` function returns a validator object that implements the [standard-schema](https://github.com/standard-schema/standard-schema) interface. ```typescript -import { Box } from '@sinclair/typebox-adapter/zod' // Transform Zod Only +import { Compile } from '@sinclair/typemap' + +// Pass TypeBox, Valibot, Zod or Syntax to JIT Compile the type. +const V = Compile(`{ + x: number + y: number, + z: number +}`) -import * as z from 'zod' +// TypeMap Interface +const R1 = V.Check({ x: 1, y: 2, z: 3 }) -const T = Box(z.string()) // const T = { type: 'string' } +// Standard Schema Interface +const R2 = V['~standard'].validate({ x: 1, y: 2, z: 3 }) ``` ## Benchmark @@ -136,5 +238,5 @@ For community benchmarks, refer to the [runtime-type-benchmarks](https://github. ## Contribute -This project is open to community contributions. Please ensure you submit an open issue before creating a pull request. TypeBox and its associated projects encourage open community discussion before accepting new features. +This project is open to community contributions. Please ensure you submit an open issue before creating a pull request. TypeMap encourages open community discussion before accepting new features. diff --git a/src/compile/compile.ts b/src/compile/compile.ts new file mode 100644 index 0000000..3ada75a --- /dev/null +++ b/src/compile/compile.ts @@ -0,0 +1,120 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typemap + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TypeCompiler, TypeCheck, ValueErrorIterator } from '@sinclair/typebox/compiler' +import { Value } from '@sinclair/typebox/value' +import { TypeBox, TTypeBox } from '../typebox/typebox' +import { StandardSchemaV1 } from './standard' +import { IsEvalSupported } from './environment' +import * as t from '@sinclair/typebox' + +// ------------------------------------------------------------------ +// StandardSchemaProps +// ------------------------------------------------------------------ +export class StandardSchemaProps implements StandardSchemaV1.Props> { + readonly #check: TypeCheck + constructor(check: TypeCheck) { + this.#check = check + } + // ---------------------------------------------------------------- + // StandardSchemaV1.Props> + // ---------------------------------------------------------------- + public get vendor(): '@sinclair/typemap' { + return '@sinclair/typemap' + } + public get version(): 1 { + return 1 + } + public get types(): { input: Type; output: t.Static } { + return { input: this.#check.Schema(), output: null } + } + public validate(value: unknown): StandardSchemaV1.Result> { + return this.#check.Check(value) ? this.#createValue(value) : this.#createIssues(value) + } + // ---------------------------------------------------------------- + // Internal + // ---------------------------------------------------------------- + #createIssues(value: unknown) { + const errors = [...Value.Errors(this.#check.Schema(), value)] + const issues: StandardSchemaV1.Issue[] = errors.map((error) => ({ ...error, path: [error.path] })) + return { issues } + } + #createValue(value: unknown) { + return { value } + } +} +// ------------------------------------------------------------------ +// Validator +// ------------------------------------------------------------------ +export class Validator implements StandardSchemaV1> { + readonly #standard: StandardSchemaProps + readonly #check: TypeCheck + constructor(check: TypeCheck) { + this.#standard = new StandardSchemaProps(check) + this.#check = check + } + /** Standard Schema Interface */ + public get ['~standard'](): StandardSchemaProps { + return this.#standard + } + /** Parses this value. Do not use this function for high throughput validation */ + public Parse(value: unknown): t.StaticDecode { + return Value.Parse(this.#check.Schema(), value) + } + /** Checks if this value matches the type */ + public Check(value: unknown): value is t.Static { + return this.#check.Check(value) + } + /** Returns errors for this value */ + public Errors(value: unknown): ValueErrorIterator { + return this.#check.Errors(value) + } +} +// ------------------------------------------------------------------ +// CompileDynamic +// ------------------------------------------------------------------ +// prettier-ignore +function CompileDynamic(type: Type, references: t.TSchema[] = []): TypeCheck { + return new TypeCheck(type, references, value => Value.Check(type, references, value), TypeCompiler.Code(type, references)) +} +// ------------------------------------------------------------------ +// Compile +// ------------------------------------------------------------------ +/** Compiles a type for high performance validation */ +// prettier-ignore +type TCompile, + Result extends Validator = Validator +> = Result +/** Compiles a type for high performance validation */ +// prettier-ignore +export function Compile(type: Type): TCompile { + const schema = TypeBox(type) + const check = IsEvalSupported() ? TypeCompiler.Compile(schema) : CompileDynamic(schema) + return new Validator(check) +} diff --git a/src/zod/index.ts b/src/compile/environment.ts similarity index 72% rename from src/zod/index.ts rename to src/compile/environment.ts index a89712c..5c5f934 100644 --- a/src/zod/index.ts +++ b/src/compile/environment.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) @@ -26,4 +26,18 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './box' +/** Cached: flag to indicate if the environment supports runtime evaluation */ +let isEvalSupported: boolean | undefined = undefined + +// prettier-ignore +function TryEval(): boolean { + try { new Function('null')(); return true } catch { return false } +} +/** Tests if the environment supports eval */ +// prettier-ignore +export function IsEvalSupported(): boolean { + if(isEvalSupported === undefined) { + isEvalSupported = TryEval() + } + return isEvalSupported +} diff --git a/src/compile/standard.ts b/src/compile/standard.ts new file mode 100644 index 0000000..ed96946 --- /dev/null +++ b/src/compile/standard.ts @@ -0,0 +1,82 @@ +/*-------------------------------------------------------------------------- + +MIT License + +Copyright (c) 2024 Colin McDonnell + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +---------------------------------------------------------------------------*/ + +/** The Standard Schema interface. */ +export interface StandardSchemaV1 { + /** The Standard Schema properties. */ + readonly '~standard': StandardSchemaV1.Props +} +export declare namespace StandardSchemaV1 { + /** The Standard Schema properties interface. */ + export interface Props { + /** The version number of the standard. */ + readonly version: 1 + /** The vendor name of the schema library. */ + readonly vendor: string + /** Validates unknown input values. */ + readonly validate: (value: unknown) => Result | Promise> + /** Inferred types associated with the schema. */ + readonly types?: Types | undefined + } + /** The result interface of the validate function. */ + export type Result = SuccessResult | FailureResult + + /** The result interface if validation succeeds. */ + export interface SuccessResult { + /** The typed output value. */ + readonly value: Output + /** The non-existent issues. */ + readonly issues?: undefined + } + /** The result interface if validation fails. */ + export interface FailureResult { + /** The issues of failed validation. */ + readonly issues: ReadonlyArray + } + /** The issue interface of the failure output. */ + export interface Issue { + /** The error message of the issue. */ + readonly message: string + /** The path of the issue, if any. */ + readonly path?: ReadonlyArray | undefined + } + /** The path segment interface of the issue. */ + export interface PathSegment { + /** The key representing a path segment. */ + readonly key: PropertyKey + } + /** The Standard Schema types interface. */ + export interface Types { + /** The input type of the schema. */ + readonly input: Input + /** The output type of the schema. */ + readonly output: Output + } + /** Infers the input type of a Standard Schema. */ + export type InferInput = NonNullable['input'] + /** Infers the output type of a Standard Schema. */ + export type InferOutput = NonNullable['output'] +} diff --git a/src/guard.ts b/src/guard.ts new file mode 100644 index 0000000..bd5432a --- /dev/null +++ b/src/guard.ts @@ -0,0 +1,85 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typemap + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as t from '@sinclair/typebox' +import * as v from 'valibot' +import * as z from 'zod' + +// ------------------------------------------------------------------ +// Syntax +// ------------------------------------------------------------------ +/** Returns true if the given value is a Syntax type */ +export type TIsSyntax = Type extends string ? true : false +/** Returns true if the given value is a Syntax type */ +export function IsSyntax(type: unknown): type is string { + return t.ValueGuard.IsString(type) +} +// ------------------------------------------------------------------ +// TypeBox +// ------------------------------------------------------------------ +/** Returns true if the given value is a TypeBox type */ +export type TIsTypeBox = Type extends t.TSchema ? true : false +/** Returns true if the given value is a TypeBox type */ +export function IsTypeBox(type: unknown): type is t.TSchema { + return t.KindGuard.IsSchema(type) +} +// ------------------------------------------------------------------ +// Valibot +// ------------------------------------------------------------------ +/** Returns true if the given value is a Valibot type */ +// prettier-ignore +export type TIsValibot = ( + Type extends v.BaseSchema> + ? Type extends { '~standard': { vendor: 'valibot' } } + ? true + : false + : false +) +/** Returns true if the given value is a Valibot type */ +// prettier-ignore +export function IsValibot(type: unknown): type is v.AnySchema { + return ( + t.ValueGuard.IsObject(type) && + t.ValueGuard.HasPropertyKey(type, '~standard') && + t.ValueGuard.IsObject(type['~standard']) && + t.ValueGuard.HasPropertyKey(type['~standard'], 'vendor') && + type['~standard'].vendor === 'valibot' + ) +} +// ------------------------------------------------------------------ +// Zod +// ------------------------------------------------------------------ +// prettier-ignore +/** Returns true if the given value is a Zod type */ +export type TIsZod = ( + Type extends z.ZodTypeAny ? true : false +) +/** Returns true if the given value is a Zod type */ +export function IsZod(type: unknown): type is z.ZodTypeAny { + return t.ValueGuard.IsObject(type) && t.ValueGuard.HasPropertyKey(type, '~standard') && t.ValueGuard.IsObject(type['~standard']) && t.ValueGuard.HasPropertyKey(type['~standard'], 'vendor') && type['~standard'].vendor === 'zod' +} diff --git a/src/index.ts b/src/index.ts index a89712c..9d18a76 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) @@ -26,4 +26,8 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './box' +export { type Static } from './static' +export * from './compile/compile' +export * from './typebox/typebox' +export * from './valibot/valibot' +export * from './zod/zod' diff --git a/src/static.ts b/src/static.ts new file mode 100644 index 0000000..51412c7 --- /dev/null +++ b/src/static.ts @@ -0,0 +1,44 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typemap + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as s from '@sinclair/typebox/syntax' +import * as t from '@sinclair/typebox' +import * as v from 'valibot' +import * as z from 'zod' + +type BaseSchema = v.BaseSchema> + +/** Statically infers a type */ +// prettier-ignore +export type Static = ( + Type extends string ? s.StaticParseAsType<{}, Type> : + Type extends t.TSchema ? t.Static : + Type extends BaseSchema ? v.InferInput : + Type extends z.ZodTypeAny ? z.infer : + never +) diff --git a/src/typebox/typebox-from-syntax.ts b/src/typebox/typebox-from-syntax.ts new file mode 100644 index 0000000..a115292 --- /dev/null +++ b/src/typebox/typebox-from-syntax.ts @@ -0,0 +1,44 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typemap + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { StaticParseAsSchema, Parse } from '@sinclair/typebox/syntax' +import * as t from '@sinclair/typebox' + +// prettier-ignore +export type TTypeBoxFromSyntax< + Type extends string | object, + Parsed = Type extends string ? StaticParseAsSchema<{}, Type> : t.TNever, + Result extends t.TSchema = Parsed extends t.TSchema ? Parsed : t.TNever +> = Result + +// prettier-ignore +export function TypeBoxFromSyntax(type: Type): TTypeBoxFromSyntax { + const parsed = t.ValueGuard.IsString(type) ? Parse(type) : t.Never() + const result = t.KindGuard.IsSchema(parsed) ? parsed : t.Never() + return result as never +} diff --git a/src/typebox/index.ts b/src/typebox/typebox-from-typebox.ts similarity index 75% rename from src/typebox/index.ts rename to src/typebox/typebox-from-typebox.ts index a89712c..e06d011 100644 --- a/src/typebox/index.ts +++ b/src/typebox/typebox-from-typebox.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) @@ -26,4 +26,13 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './box' +import * as t from '@sinclair/typebox' + +// prettier-ignore +export type TTypeBoxFromTypeBox = ( + Type extends t.TSchema ? Type : t.TNever +) +// prettier-ignore +export function TypeBoxFromTypeBox = TTypeBoxFromTypeBox>(type: Type): Result { + return (t.KindGuard.IsSchema(type) ? type : t.Never()) as never +} diff --git a/src/valibot/box.ts b/src/typebox/typebox-from-valibot.ts similarity index 63% rename from src/valibot/box.ts rename to src/typebox/typebox-from-valibot.ts index 8c22d2a..f562795 100644 --- a/src/valibot/box.ts +++ b/src/typebox/typebox-from-valibot.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) @@ -26,14 +26,15 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import * as tb from '@sinclair/typebox' +import * as t from '@sinclair/typebox' import * as v from 'valibot' +import * as Guard from '../guard' // ------------------------------------------------------------------ // Options // ------------------------------------------------------------------ function IsSchemaWithPipe(type: BaseSchema): type is v.SchemaWithPipe<[BaseSchema, ...BaseValidation[]]> { - return tb.ValueGuard.IsObject(type) && tb.ValueGuard.HasPropertyKey(type, 'pipe') && tb.ValueGuard.IsArray(type.pipe) + return t.ValueGuard.IsObject(type) && t.ValueGuard.HasPropertyKey(type, 'pipe') && t.ValueGuard.IsArray(type.pipe) } // prettier-ignore function Options(type: BaseSchema) { @@ -42,19 +43,19 @@ function Options(type: BaseSchema) { return { ...options, ...( action.type === 'args' ? {} : - action.type === 'base64' ? { format: 'valibot:base64' } : - action.type === 'bic' ? { format: 'valibot:bic' } : + action.type === 'base64' ? { format: 'base64' } : + action.type === 'bic' ? { format: 'bic' } : action.type === 'brand' ? {} : action.type === 'bytes' ? {} : action.type === 'check' ? {} : action.type === 'check_items' ? {} : - action.type === 'credit_card' ? { format: 'valibot:credit_card' } : - action.type === 'cuid2' ? { format: 'valibot:cuid2' } : - action.type === 'decimal' ? { format: 'valibot:decimal' } : + action.type === 'credit_card' ? { format: 'credit_card' } : + action.type === 'cuid2' ? { format: 'cuid2' } : + action.type === 'decimal' ? { format: 'decimal' } : action.type === 'description' ? { description: action.description } : - action.type === 'digits' ? { format: 'valibot:digits' } : - action.type === 'email' ? { format: 'valibot:email' } : - action.type === 'emoji' ? { format: 'valibot:emoji' } : + action.type === 'digits' ? { format: 'digits' } : + action.type === 'email' ? { format: 'email' } : + action.type === 'emoji' ? { format: 'emoji' } : action.type === 'empty' ? ( type.type === 'array' ? { maxItems: 0 } : type.type === 'string' ? { maxLength: 0 } : @@ -71,26 +72,26 @@ function Options(type: BaseSchema) { action.type === 'hex_color' ? {} : action.type === 'imei' ? {} : action.type === 'includes' ? ( - type.type === 'array' ? { contains: tb.Literal(action.requirement) } : + type.type === 'array' ? { contains: t.Literal(action.requirement) } : type.type === 'string' ? { pattern: action.requirement } : {}) : action.type === 'integer' ? { multipleOf: 1 } : - action.type === 'ip' ? { format: 'valibot:ip' } : - action.type === 'ipv4' ? { format: 'valibot:ipv4' } : - action.type === 'ipv6' ? { format: 'valibot:ipv6' } : - action.type === 'iso_date' ? { format: 'valibot:iso_date' } : - action.type === 'iso_date_time' ? { format: 'valibot:iso_date_time' } : - action.type === 'iso_time' ? { format: 'valibot:iso_time' } : - action.type === 'iso_time_second' ? { format: 'valibot:iso_time_second' } : - action.type === 'iso_timestamp' ? { format: 'valibot:iso_timestamp' } : - action.type === 'iso_week' ? { format: 'valibot:iso_week' } : + action.type === 'ip' ? { format: 'ip' } : + action.type === 'ipv4' ? { format: 'ipv4' } : + action.type === 'ipv6' ? { format: 'ipv6' } : + action.type === 'iso_date' ? { format: 'iso_date' } : + action.type === 'iso_date_time' ? { format: 'iso_date_time' } : + action.type === 'iso_time' ? { format: 'iso_time' } : + action.type === 'iso_time_second' ? { format: 'iso_time_second' } : + action.type === 'iso_timestamp' ? { format: 'iso_timestamp' } : + action.type === 'iso_week' ? { format: 'iso_week' } : action.type === 'length' ? ( type.type === 'string' ? { minLength: action.requirement, maxLength: action.requirement } : type.type === 'array' ? { minItems: action.requirement, maxItems: action.requirement } : {}) : - action.type === 'mac' ? { format: 'valibot:mac' } : - action.type === 'mac48' ? { format: 'valibot:mac48' } : - action.type === 'mac64' ? { format: 'valibot:mac64' } : + action.type === 'mac' ? { format: 'mac' } : + action.type === 'mac48' ? { format: 'mac48' } : + action.type === 'mac64' ? { format: 'mac64' } : action.type === 'map_items' ? {} : action.type === 'max_bytes' ? {} : action.type === 'max_graphemes' ? {} : @@ -101,7 +102,7 @@ function Options(type: BaseSchema) { action.type === 'max_size' ? {} : action.type === 'max_value' ? { maximum: action.requirement } : action.type === 'max_words' ? {} : - action.type === 'metadata' ? { ...action.metadata } : + action.type === 'metadata' ? { metadata: action.metadata } : action.type === 'mime_type' ? {} : action.type === 'min_bytes' ? {} : action.type === 'min_graphemes' ? {} : @@ -113,7 +114,7 @@ function Options(type: BaseSchema) { action.type === 'min_value' ? { minimum: action.requirement } : action.type === 'min_words' ? {} : action.type === 'multiple_of' ? { multipleOf: action.requirement } : - action.type === 'nanoid' ? { format: 'valibot:nanoid' } : + action.type === 'nanoid' ? { format: 'nanoid' } : action.type === 'non_empty' ? {} : action.type === 'normalize' ? {} : action.type === 'not_bytes' ? {} : @@ -122,7 +123,7 @@ function Options(type: BaseSchema) { action.type === 'not_size' ? {} : action.type === 'not_value' ? {} : action.type === 'not_words' ? {} : - action.type === 'octal' ? { format: 'valibot:octal' } : + action.type === 'octal' ? { format: 'octal' } : action.type === 'partial_check' ? {} : action.type === 'raw_check' ? {} : action.type === 'raw_transform' ? {} : @@ -144,9 +145,9 @@ function Options(type: BaseSchema) { action.type === 'trim' ? {} : action.type === 'trim_end' ? {} : action.type === 'trim_start' ? {} : - action.type === 'ulid' ? { format: 'valibot:ulid' } : - action.type === 'url' ? { format: 'valibot:url' } : - action.type === 'uuid' ? { format: 'valibot:uuid' } : + action.type === 'ulid' ? { format: 'ulid' } : + action.type === 'url' ? { format: 'url' } : + action.type === 'uuid' ? { format: 'uuid' } : action.type === 'value' ? {} : action.type === 'words' ? {} : {}) @@ -156,31 +157,31 @@ function Options(type: BaseSchema) { // ------------------------------------------------------------------ // Formats // ------------------------------------------------------------------ -tb.FormatRegistry.Set('valibot:base64', (value) => v.safeParse(v.pipe(v.string(), v.base64()), value).success) -tb.FormatRegistry.Set('valibot:bic', (value) => v.safeParse(v.pipe(v.string(), v.bic()), value).success) -tb.FormatRegistry.Set('valibot:credit_card', (value) => v.safeParse(v.pipe(v.string(), v.creditCard()), value).success) -tb.FormatRegistry.Set('valibot:cuid2', (value) => v.safeParse(v.pipe(v.string(), v.cuid2()), value).success) -tb.FormatRegistry.Set('valibot:decimal', (value) => v.safeParse(v.pipe(v.string(), v.decimal()), value).success) -tb.FormatRegistry.Set('valibot:digits', (value) => v.safeParse(v.pipe(v.string(), v.digits()), value).success) -tb.FormatRegistry.Set('valibot:email', (value) => v.safeParse(v.pipe(v.string(), v.email()), value).success) -tb.FormatRegistry.Set('valibot:emoji', (value) => v.safeParse(v.pipe(v.string(), v.emoji()), value).success) -tb.FormatRegistry.Set('valibot:ip', (value) => v.safeParse(v.pipe(v.string(), v.ip()), value).success) -tb.FormatRegistry.Set('valibot:ipv4', (value) => v.safeParse(v.pipe(v.string(), v.ipv4()), value).success) -tb.FormatRegistry.Set('valibot:ipv6', (value) => v.safeParse(v.pipe(v.string(), v.ipv6()), value).success) -tb.FormatRegistry.Set('valibot:iso_date', (value) => v.safeParse(v.pipe(v.string(), v.isoDate()), value).success) -tb.FormatRegistry.Set('valibot:iso_date_time', (value) => v.safeParse(v.pipe(v.string(), v.isoDateTime()), value).success) -tb.FormatRegistry.Set('valibot:iso_time', (value) => v.safeParse(v.pipe(v.string(), v.isoTime()), value).success) -tb.FormatRegistry.Set('valibot:iso_time_second', (value) => v.safeParse(v.pipe(v.string(), v.isoTimeSecond()), value).success) -tb.FormatRegistry.Set('valibot:iso_timestamp', (value) => v.safeParse(v.pipe(v.string(), v.isoTimestamp()), value).success) -tb.FormatRegistry.Set('valibot:iso_week', (value) => v.safeParse(v.pipe(v.string(), v.isoWeek()), value).success) -tb.FormatRegistry.Set('valibot:mac', (value) => v.safeParse(v.pipe(v.string(), v.mac()), value).success) -tb.FormatRegistry.Set('valibot:mac48', (value) => v.safeParse(v.pipe(v.string(), v.mac48()), value).success) -tb.FormatRegistry.Set('valibot:mac64', (value) => v.safeParse(v.pipe(v.string(), v.mac64()), value).success) -tb.FormatRegistry.Set('valibot:nanoid', (value) => v.safeParse(v.pipe(v.string(), v.nanoid()), value).success) -tb.FormatRegistry.Set('valibot:octal', (value) => v.safeParse(v.pipe(v.string(), v.octal()), value).success) -tb.FormatRegistry.Set('valibot:ulid', (value) => v.safeParse(v.pipe(v.string(), v.ulid()), value).success) -tb.FormatRegistry.Set('valibot:url', (value) => v.safeParse(v.pipe(v.string(), v.url()), value).success) -tb.FormatRegistry.Set('valibot:uuid', (value) => v.safeParse(v.pipe(v.string(), v.uuid()), value).success) +t.FormatRegistry.Set('base64', (value) => v.safeParse(v.pipe(v.string(), v.base64()), value).success) +t.FormatRegistry.Set('bic', (value) => v.safeParse(v.pipe(v.string(), v.bic()), value).success) +t.FormatRegistry.Set('credit_card', (value) => v.safeParse(v.pipe(v.string(), v.creditCard()), value).success) +t.FormatRegistry.Set('cuid2', (value) => v.safeParse(v.pipe(v.string(), v.cuid2()), value).success) +t.FormatRegistry.Set('decimal', (value) => v.safeParse(v.pipe(v.string(), v.decimal()), value).success) +t.FormatRegistry.Set('digits', (value) => v.safeParse(v.pipe(v.string(), v.digits()), value).success) +t.FormatRegistry.Set('email', (value) => v.safeParse(v.pipe(v.string(), v.email()), value).success) +t.FormatRegistry.Set('emoji', (value) => v.safeParse(v.pipe(v.string(), v.emoji()), value).success) +t.FormatRegistry.Set('ip', (value) => v.safeParse(v.pipe(v.string(), v.ip()), value).success) +t.FormatRegistry.Set('ipv4', (value) => v.safeParse(v.pipe(v.string(), v.ipv4()), value).success) +t.FormatRegistry.Set('ipv6', (value) => v.safeParse(v.pipe(v.string(), v.ipv6()), value).success) +t.FormatRegistry.Set('iso_date', (value) => v.safeParse(v.pipe(v.string(), v.isoDate()), value).success) +t.FormatRegistry.Set('iso_date_time', (value) => v.safeParse(v.pipe(v.string(), v.isoDateTime()), value).success) +t.FormatRegistry.Set('iso_time', (value) => v.safeParse(v.pipe(v.string(), v.isoTime()), value).success) +t.FormatRegistry.Set('iso_time_second', (value) => v.safeParse(v.pipe(v.string(), v.isoTimeSecond()), value).success) +t.FormatRegistry.Set('iso_timestamp', (value) => v.safeParse(v.pipe(v.string(), v.isoTimestamp()), value).success) +t.FormatRegistry.Set('iso_week', (value) => v.safeParse(v.pipe(v.string(), v.isoWeek()), value).success) +t.FormatRegistry.Set('mac', (value) => v.safeParse(v.pipe(v.string(), v.mac()), value).success) +t.FormatRegistry.Set('mac48', (value) => v.safeParse(v.pipe(v.string(), v.mac48()), value).success) +t.FormatRegistry.Set('mac64', (value) => v.safeParse(v.pipe(v.string(), v.mac64()), value).success) +t.FormatRegistry.Set('nanoid', (value) => v.safeParse(v.pipe(v.string(), v.nanoid()), value).success) +t.FormatRegistry.Set('octal', (value) => v.safeParse(v.pipe(v.string(), v.octal()), value).success) +t.FormatRegistry.Set('ulid', (value) => v.safeParse(v.pipe(v.string(), v.ulid()), value).success) +t.FormatRegistry.Set('url', (value) => v.safeParse(v.pipe(v.string(), v.url()), value).success) +t.FormatRegistry.Set('uuid', (value) => v.safeParse(v.pipe(v.string(), v.uuid()), value).success) // ------------------------------------------------------------------ // Schema @@ -191,156 +192,156 @@ type BaseRecordKey = v.BaseSchema = tb.Ensure -function FromAny(type: BaseSchema): tb.TSchema { - return tb.Any(Options(type)) +type TFromAny<_Type extends v.AnySchema> = t.Ensure +function FromAny(type: BaseSchema): t.TSchema { + return t.Any(Options(type)) } // ------------------------------------------------------------------ // Array // ------------------------------------------------------------------ -type TFromArray = tb.Ensure>> -function FromArray(type: BaseSchema): tb.TSchema { - return tb.Array(FromType((type as v.ArraySchema).item), Options(type)) +type TFromArray = t.Ensure>> +function FromArray(type: BaseSchema): t.TSchema { + return t.Array(FromType((type as v.ArraySchema).item), Options(type)) } // ------------------------------------------------------------------ // BigInt // ------------------------------------------------------------------ -type TFromBigInt<_Type extends v.BigintSchema> = tb.Ensure -function FromBigInt(type: BaseSchema): tb.TSchema { - return tb.BigInt(Options(type)) +type TFromBigInt<_Type extends v.BigintSchema> = t.Ensure +function FromBigInt(type: BaseSchema): t.TSchema { + return t.BigInt(Options(type)) } // ------------------------------------------------------------------ // Blob // ------------------------------------------------------------------ -tb.TypeRegistry.Set('ValibotBlob', (schema, value) => { +t.TypeRegistry.Set('ValibotBlob', (schema, value) => { return v.safeParse(schema.schema, value).success }) -interface TBlob = v.BlobSchema> extends tb.TSchema { - [tb.Kind]: 'ValibotBlob' +interface TBlob = v.BlobSchema> extends t.TSchema { + [t.Kind]: 'ValibotBlob' static: v.InferOutput type: Type } -function _Blob(type: v.BlobSchema, options?: tb.SchemaOptions): TBlob { - return tb.CreateType({ [tb.Kind]: 'ValibotBlob', type }, options) as never +function _Blob(type: v.BlobSchema, options?: t.SchemaOptions): TBlob { + return t.CreateType({ [t.Kind]: 'ValibotBlob', type }, options) as never } -type TFromBlob> = tb.Ensure> -function FromBlob(type: BaseSchema): tb.TSchema { +type TFromBlob> = t.Ensure> +function FromBlob(type: BaseSchema): t.TSchema { return _Blob(type as v.BlobSchema, Options(type)) } // ------------------------------------------------------------------ // Boolean // ------------------------------------------------------------------ -type TFromBoolean<_Type extends v.BooleanSchema> = tb.TBoolean -function FromBoolean(type: BaseSchema): tb.TSchema { - return tb.Boolean(Options(type)) +type TFromBoolean<_Type extends v.BooleanSchema> = t.TBoolean +function FromBoolean(type: BaseSchema): t.TSchema { + return t.Boolean(Options(type)) } // ------------------------------------------------------------------ // Custom // ------------------------------------------------------------------ -tb.TypeRegistry.Set('ValibotCustom', (schema, value) => v.safeParse(schema.schema, value).success) -export interface TCustom = v.CustomSchema> extends tb.TSchema { - [tb.Kind]: 'ValibotCustom' +t.TypeRegistry.Set('ValibotCustom', (schema, value) => v.safeParse(schema.schema, value).success) +export interface TCustom = v.CustomSchema> extends t.TSchema { + [t.Kind]: 'ValibotCustom' static: v.InferOutput type: Type } -function Custom>(type: Type, options?: tb.SchemaOptions): TCustom { - return tb.CreateType({ [tb.Kind]: 'ValibotCustom', type }, options) as never +function Custom>(type: Type, options?: t.SchemaOptions): TCustom { + return t.CreateType({ [t.Kind]: 'ValibotCustom', type }, options) as never } -type TFromCustom> = tb.Ensure> -function FromCustom(type: BaseSchema): tb.TSchema { +type TFromCustom> = t.Ensure> +function FromCustom(type: BaseSchema): t.TSchema { return Custom(type as v.CustomSchema, Options(type)) } // ------------------------------------------------------------------ // Date // ------------------------------------------------------------------ -type TFromDate<_Type extends v.DateSchema> = tb.TDate -function FromDate(type: BaseSchema): tb.TSchema { - return tb.Date(Options(type)) +type TFromDate<_Type extends v.DateSchema> = t.TDate +function FromDate(type: BaseSchema): t.TSchema { + return t.Date(Options(type)) } // ------------------------------------------------------------------ // Enum // ------------------------------------------------------------------ -tb.TypeRegistry.Set('ValibotEnum', (schema, value) => { +t.TypeRegistry.Set('ValibotEnum', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TValibotEnum = v.EnumSchema> extends tb.TSchema { - [tb.Kind]: 'ValibotEnum' +export interface TValibotEnum = v.EnumSchema> extends t.TSchema { + [t.Kind]: 'ValibotEnum' static: v.InferOutput type: Type } -function ValibotEnum>(type: Type, options?: tb.SchemaOptions): TValibotEnum { - return tb.CreateType({ [tb.Kind]: 'ValibotEnum', type }, options) as never +function ValibotEnum>(type: Type, options?: t.SchemaOptions): TValibotEnum { + return t.CreateType({ [t.Kind]: 'ValibotEnum', type }, options) as never } type TFromEnum> = TValibotEnum -function FromEnum(type: Type): tb.TSchema { +function FromEnum(type: Type): t.TSchema { return ValibotEnum(type as never as v.EnumSchema, Options(type)) } // ------------------------------------------------------------------ // File // ------------------------------------------------------------------ -tb.TypeRegistry.Set('ValibotFile', (schema, value) => { +t.TypeRegistry.Set('ValibotFile', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TFile = v.FileSchema> extends tb.TSchema { - [tb.Kind]: 'ValibotFile' +export interface TFile = v.FileSchema> extends t.TSchema { + [t.Kind]: 'ValibotFile' static: v.InferOutput type: Type } -function _File(type: v.FileSchema, options?: tb.SchemaOptions): TFile { - return tb.CreateType({ [tb.Kind]: 'ValibotFile', type }, options) as never +function _File(type: v.FileSchema, options?: t.SchemaOptions): TFile { + return t.CreateType({ [t.Kind]: 'ValibotFile', type }, options) as never } -type TFromFile> = tb.Ensure> -function FromFile(type: BaseSchema): tb.TSchema { +type TFromFile> = t.Ensure> +function FromFile(type: BaseSchema): t.TSchema { return _File(type as v.FileSchema, Options(type)) } // ------------------------------------------------------------------ // Function // ------------------------------------------------------------------ -tb.TypeRegistry.Set('ValibotFunction', (schema, value) => { +t.TypeRegistry.Set('ValibotFunction', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TFunction = v.FunctionSchema> extends tb.TSchema { - [tb.Kind]: 'ValibotFunction' +export interface TFunction = v.FunctionSchema> extends t.TSchema { + [t.Kind]: 'ValibotFunction' static: v.InferOutput type: Type } -function _Function>(type: Type, options?: tb.SchemaOptions): TFunction { - return tb.CreateType({ [tb.Kind]: 'ValibotFunction', type }, options) as never +function _Function>(type: Type, options?: t.SchemaOptions): TFunction { + return t.CreateType({ [t.Kind]: 'ValibotFunction', type }, options) as never } -type TFromFunction> = tb.Ensure> -function FromFunction(type: BaseSchema): tb.TSchema { +type TFromFunction> = t.Ensure> +function FromFunction(type: BaseSchema): t.TSchema { return _Function(type as v.FunctionSchema, Options(type)) } // ------------------------------------------------------------------ // Instance // ------------------------------------------------------------------ -tb.TypeRegistry.Set('ValibotInstance', (schema, value) => { +t.TypeRegistry.Set('ValibotInstance', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TInstance = v.InstanceSchema> extends tb.TSchema { - [tb.Kind]: 'ValibotInstance' +export interface TInstance = v.InstanceSchema> extends t.TSchema { + [t.Kind]: 'ValibotInstance' static: v.InferOutput type: Type } -function Instance>(type: Type, options?: tb.SchemaOptions): TInstance { - return tb.CreateType({ [tb.Kind]: 'ValibotInstance', type }, options) as never +function Instance>(type: Type, options?: t.SchemaOptions): TInstance { + return t.CreateType({ [t.Kind]: 'ValibotInstance', type }, options) as never } -type TFromInstance> = tb.Ensure> -function FromInstance(type: BaseSchema): tb.TSchema { +type TFromInstance> = t.Ensure> +function FromInstance(type: BaseSchema): t.TSchema { return Instance(type as v.InstanceSchema, Options(type)) } // ------------------------------------------------------------------ // Intersect // ------------------------------------------------------------------ // prettier-ignore -type TFromIntersect = ( +type TFromIntersect = ( Type extends [infer Left extends BaseSchema, ...infer Right extends BaseSchema[]] ? TFromIntersect]> - : tb.TIntersect + : t.TIntersect ) -function FromIntersect(type: BaseSchema): tb.TSchema { +function FromIntersect(type: BaseSchema): t.TSchema { const intersect = type as v.IntersectSchema - return tb.Intersect( + return t.Intersect( intersect.options.map((option) => FromType(option)), Options(type), ) @@ -348,268 +349,268 @@ function FromIntersect(type: BaseSchema): tb.TSchema { // ------------------------------------------------------------------ // Literal // ------------------------------------------------------------------ -type TFromLiteral = tb.Ensure> -function FromLiteral(type: BaseSchema): tb.TSchema { - const literal = type as v.LiteralSchema - return tb.Literal(literal.literal, Options(type)) +type TFromLiteral = t.Ensure> +function FromLiteral(type: BaseSchema): t.TSchema { + const literal = type as v.LiteralSchema + return t.Literal(literal.literal, Options(type)) } // ------------------------------------------------------------------ // LooseObject // ------------------------------------------------------------------ -type TFromLooseObject = tb.Ensure< - tb.TObject<{ +type TFromLooseObject = t.Ensure< + t.TObject<{ -readonly [Key in keyof Properties]: TFromType }> > -function FromLooseObject(type: BaseSchema): tb.TSchema { +function FromLooseObject(type: BaseSchema): t.TSchema { const object = type as v.LooseObjectSchema const keys = globalThis.Object.getOwnPropertyNames(object.entries) - return tb.Object( + return t.Object( keys.reduce((properties, key) => { return { ...properties, [key]: FromType(object.entries[key]) } - }, {} as tb.TProperties), + }, {} as t.TProperties), Options(type), ) } // ------------------------------------------------------------------ // LooseTuple // ------------------------------------------------------------------ -tb.TypeRegistry.Set('ValibotLooseTuple', (schema, value) => { +t.TypeRegistry.Set('ValibotLooseTuple', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TLooseTuple = v.LooseTupleSchema> extends tb.TSchema { - [tb.Kind]: 'ValibotLooseTuple' +export interface TLooseTuple = v.LooseTupleSchema> extends t.TSchema { + [t.Kind]: 'ValibotLooseTuple' static: v.InferOutput type: Type } -function LooseTuple>(type: Type, schema?: tb.SchemaOptions): TLooseTuple { - return tb.CreateType({ [tb.Kind]: 'ValibotLooseTuple', type }) as never +function LooseTuple>(type: Type, schema?: t.SchemaOptions): TLooseTuple { + return t.CreateType({ [t.Kind]: 'ValibotLooseTuple', type }) as never } -type TFromLooseTuple> = tb.Ensure> -function FromLooseTuple(type: BaseSchema): tb.TSchema { +type TFromLooseTuple> = t.Ensure> +function FromLooseTuple(type: BaseSchema): t.TSchema { return LooseTuple(type as v.LooseTupleSchema, Options(type)) } // ------------------------------------------------------------------ // Map // ------------------------------------------------------------------ -tb.TypeRegistry.Set('ValibotMap', (schema, value) => { +t.TypeRegistry.Set('ValibotMap', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TMap = v.MapSchema> extends tb.TSchema { - [tb.Kind]: 'ValibotMap' +export interface TMap = v.MapSchema> extends t.TSchema { + [t.Kind]: 'ValibotMap' static: v.InferOutput type: Type } -function _Map>(type: Type, options?: tb.SchemaOptions): TMap { - return tb.CreateType({ [tb.Kind]: 'ValibotMap', type }) as never +function _Map>(type: Type, options?: t.SchemaOptions): TMap { + return t.CreateType({ [t.Kind]: 'ValibotMap', type }) as never } -type TFromMap> = tb.Ensure> -function FromMap(type: BaseSchema): tb.TSchema { +type TFromMap> = t.Ensure> +function FromMap(type: BaseSchema): t.TSchema { return _Map(type as v.MapSchema, Options(type)) } // ------------------------------------------------------------------ // NaN // ------------------------------------------------------------------ -tb.TypeRegistry.Set('ValibotNaN', (schema, value) => { +t.TypeRegistry.Set('ValibotNaN', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TNaN = v.NanSchema> extends tb.TSchema { - [tb.Kind]: 'ValibotNaN' +export interface TNaN = v.NanSchema> extends t.TSchema { + [t.Kind]: 'ValibotNaN' static: v.InferOutput type: Type } -function _NaN>(type: Type, options?: tb.SchemaOptions): TNaN { - return tb.CreateType({ [tb.Kind]: 'ValibotNaN', type }, options) as never +function _NaN>(type: Type, options?: t.SchemaOptions): TNaN { + return t.CreateType({ [t.Kind]: 'ValibotNaN', type }, options) as never } -type TFromNaN> = tb.Ensure> -function FromNaN(type: BaseSchema): tb.TSchema { +type TFromNaN> = t.Ensure> +function FromNaN(type: BaseSchema): t.TSchema { return _NaN(type as v.NanSchema, Options(type)) } // ------------------------------------------------------------------ // Never // ------------------------------------------------------------------ -type TFromNever<_Type extends v.NeverSchema> = tb.TNever -function FromNever(type: BaseSchema): tb.TSchema { - return tb.Never(Options(type)) +type TFromNever<_Type extends v.NeverSchema> = t.TNever +function FromNever(type: BaseSchema): t.TSchema { + return t.Never(Options(type)) } // ------------------------------------------------------------------ // NonNullable // ------------------------------------------------------------------ -type TFromNonNullable = tb.TExclude, tb.TNull> -function FromNonNullable(type: BaseSchema): tb.TSchema { +type TFromNonNullable = t.TExclude, t.TNull> +function FromNonNullable(type: BaseSchema): t.TSchema { const non_nullable = type as v.NonNullableSchema - return tb.Exclude(FromType(non_nullable.wrapped), tb.Null(), Options(type)) + return t.Exclude(FromType(non_nullable.wrapped), t.Null(), Options(type)) } // ------------------------------------------------------------------ // NonNullish // ------------------------------------------------------------------ -type TFromNonNullish = tb.TExclude, tb.TUnion<[tb.TNull, tb.TUndefined]>> -function FromNonNullish(type: BaseSchema): tb.TSchema { +type TFromNonNullish = t.TExclude, t.TUnion<[t.TNull, t.TUndefined]>> +function FromNonNullish(type: BaseSchema): t.TSchema { const non_nullish = type as v.NonNullishSchema - return tb.Exclude(FromType(non_nullish.wrapped), tb.Union([tb.Null(), tb.Undefined()]), Options(type)) + return t.Exclude(FromType(non_nullish.wrapped), t.Union([t.Null(), t.Undefined()]), Options(type)) } // ------------------------------------------------------------------ // NonOptional // ------------------------------------------------------------------ -type TFromNonOptional = TFromType> = tb.TOptionalWithFlag -function FromNonOptional(type: BaseSchema): tb.TSchema { +type TFromNonOptional = TFromType> = t.TOptionalWithFlag +function FromNonOptional(type: BaseSchema): t.TSchema { const non_optional = type as v.NonOptionalSchema - return tb.Optional(FromType(non_optional.wrapped), false) + return t.Optional(FromType(non_optional.wrapped), false) } // ------------------------------------------------------------------ // Null // ------------------------------------------------------------------ -type TFromNull<_Type extends v.NullSchema> = tb.TNull +type TFromNull<_Type extends v.NullSchema> = t.TNull function FromNull(type: BaseSchema) { - return tb.Null(Options(type)) + return t.Null(Options(type)) } // ------------------------------------------------------------------ // Nullable // ------------------------------------------------------------------ -type TFromNullable = tb.TUnion<[TFromType, tb.TNull]> +type TFromNullable = t.TUnion<[TFromType, t.TNull]> function FromNullable(type: BaseSchema) { const nullable = type as v.NullableSchema - return tb.Union([tb.Null(), FromType(nullable.wrapped)], Options(type)) + return t.Union([t.Null(), FromType(nullable.wrapped)], Options(type)) } // ------------------------------------------------------------------ // Nullish // ------------------------------------------------------------------ -type TFromNullish = tb.TUnion<[TFromType, tb.TNull, tb.TUndefined]> +type TFromNullish = t.TUnion<[TFromType, t.TNull, t.TUndefined]> function FromNullish(type: BaseSchema) { const nullish = type as v.NullishSchema - return tb.Union([FromType(nullish.wrapped), tb.Null(), tb.Undefined()], Options(type)) + return t.Union([FromType(nullish.wrapped), t.Null(), t.Undefined()], Options(type)) } // ------------------------------------------------------------------ // Number // ------------------------------------------------------------------ -type TFromNumber<_Type extends v.NumberSchema> = tb.TNumber -function FromNumber(type: BaseSchema): tb.TSchema { - return tb.Number(Options(type)) +type TFromNumber<_Type extends v.NumberSchema> = t.TNumber +function FromNumber(type: BaseSchema): t.TSchema { + return t.Number(Options(type)) } // ------------------------------------------------------------------ // Object // ------------------------------------------------------------------ -type TFromObject = tb.Ensure< - tb.TObject<{ +type TFromObject = t.Ensure< + t.TObject<{ -readonly [Key in keyof Properties]: TFromType }> > -function FromObject(type: BaseSchema): tb.TSchema { +function FromObject(type: BaseSchema): t.TSchema { const object = type as v.ObjectSchema const keys = globalThis.Object.getOwnPropertyNames(object.entries) - return tb.Object( + return t.Object( keys.reduce((properties, key) => { return { ...properties, [key]: FromType(object.entries[key]) } - }, {} as tb.TProperties), + }, {} as t.TProperties), Options(type), ) } // ------------------------------------------------------------------ // ObjectWithRest // ------------------------------------------------------------------ -type TFromObjectWithRest = tb.Ensure< - tb.TObject<{ +type TFromObjectWithRest = t.Ensure< + t.TObject<{ -readonly [Key in keyof Properties]: TFromType }> > -function FromObjectWithRest(type: BaseSchema): tb.TSchema { +function FromObjectWithRest(type: BaseSchema): t.TSchema { const object = type as v.ObjectWithRestSchema const keys = globalThis.Object.getOwnPropertyNames(object.entries) - return tb.Object( + return t.Object( keys.reduce((properties, key) => { return { ...properties, [key]: FromType(object.entries[key]) } - }, {} as tb.TProperties), + }, {} as t.TProperties), { ...Options(type), additionalProperties: FromType(object.rest) }, ) } // ------------------------------------------------------------------ // Optional // ------------------------------------------------------------------ -type TFromOptional = TFromType> = tb.TOptionalWithFlag -function FromOptional(type: BaseSchema): tb.TSchema { +type TFromOptional = TFromType> = t.TOptionalWithFlag +function FromOptional(type: BaseSchema): t.TSchema { const optional = type as v.OptionalSchema - return tb.Optional(FromType(optional.wrapped)) + return t.Optional(FromType(optional.wrapped)) } // ------------------------------------------------------------------ // PickList // ------------------------------------------------------------------ type PickListOption = string | number | bigint // prettier-ignore -type TFromPickList = ( +type TFromPickList = ( Options extends [infer Left extends PickListOption, ...infer Right extends PickListOption[]] ? ( - Left extends tb.TLiteralValue - ? TFromPickList]> + Left extends t.TLiteralValue + ? TFromPickList]> : TFromPickList ) - : tb.TUnion + : t.TUnion ) -function FromPickList(type: BaseSchema): tb.TSchema { +function FromPickList(type: BaseSchema): t.TSchema { const picklist = type as v.PicklistSchema - return tb.Union( - picklist.options.map((option) => tb.Literal(option as tb.TLiteralValue)), + return t.Union( + picklist.options.map((option) => t.Literal(option as t.TLiteralValue)), Options(type), ) } // ------------------------------------------------------------------ // Promise // ------------------------------------------------------------------ -tb.TypeRegistry.Set('ValibotPromise', (schema, value) => { +t.TypeRegistry.Set('ValibotPromise', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TPromise = v.PromiseSchema> extends tb.TSchema { - [tb.Kind]: 'ValibotPromise' +export interface TPromise = v.PromiseSchema> extends t.TSchema { + [t.Kind]: 'ValibotPromise' static: v.InferOutput type: Type } -function _Promise>(type: Type, options?: tb.SchemaOptions): TPromise { - return tb.CreateType({ [tb.Kind]: 'ValibotPromise', type }, options) as never +function _Promise>(type: Type, options?: t.SchemaOptions): TPromise { + return t.CreateType({ [t.Kind]: 'ValibotPromise', type }, options) as never } -type TFromPromise> = tb.Ensure> -function FromPromise(type: BaseSchema): tb.TSchema { +type TFromPromise> = t.Ensure> +function FromPromise(type: BaseSchema): t.TSchema { return _Promise(type as v.PromiseSchema, Options(type)) } // ------------------------------------------------------------------ // Record // ------------------------------------------------------------------ -type TFromRecord = tb.Ensure, TFromType>> +type TFromRecord = t.Ensure, TFromType>> function FromRecord(type: BaseSchema) { const record = type as v.RecordSchema - return tb.Record(FromType(record.key), FromType(record.value), Options(type)) + return t.Record(FromType(record.key), FromType(record.value), Options(type)) } // ------------------------------------------------------------------ // Set // ------------------------------------------------------------------ -tb.TypeRegistry.Set('ValibotSet', (schema, value) => { +t.TypeRegistry.Set('ValibotSet', (schema, value) => { return v.safeParse(schema.schema, value).success }) -export interface TSet = v.SetSchema> extends tb.TSchema { - [tb.Kind]: 'ValibotSet' +export interface TSet = v.SetSchema> extends t.TSchema { + [t.Kind]: 'ValibotSet' static: v.InferOutput extends infer Result ? Result : never type: Type } -function Set>(type: Type, options?: tb.SchemaOptions): TSet { - return tb.CreateType({ [tb.Kind]: 'ValibotSet', type }, options) as never +function Set>(type: Type, options?: t.SchemaOptions): TSet { + return t.CreateType({ [t.Kind]: 'ValibotSet', type }, options) as never } -type TFromSet> = tb.Ensure> -function FromSet(type: BaseSchema): tb.TSchema { +type TFromSet> = t.Ensure> +function FromSet(type: BaseSchema): t.TSchema { return Set(type as v.SetSchema) } // ------------------------------------------------------------------ // StrictObject // ------------------------------------------------------------------ -type TFromStrictObject = tb.Ensure< - tb.TObject<{ +type TFromStrictObject = t.Ensure< + t.TObject<{ -readonly [Key in keyof Properties]: TFromType }> > -function FromStrictObject(type: BaseSchema): tb.TSchema { +function FromStrictObject(type: BaseSchema): t.TSchema { const object = type as v.StrictObjectSchema const keys = globalThis.Object.getOwnPropertyNames(object.entries) - return tb.Object( + return t.Object( keys.reduce((properties, key) => { return { ...properties, [key]: FromType(object.entries[key]) } - }, {} as tb.TProperties), + }, {} as t.TProperties), { ...Options(type), additionalProperties: false }, ) } @@ -617,121 +618,121 @@ function FromStrictObject(type: BaseSchema): tb.TSchema { // StrictTuple // ------------------------------------------------------------------ // prettier-ignore -type TFromStrictTuple = ( +type TFromStrictTuple = ( Type extends [infer Left extends BaseSchema, ...infer Right extends BaseSchema[]] ? TFromTuple]> - : tb.TTuple + : t.TTuple ) -function FromStrictTuple(type: BaseSchema): tb.TSchema { +function FromStrictTuple(type: BaseSchema): t.TSchema { const tuple = type as v.StrictTupleSchema const items = globalThis.Array.isArray(tuple.items) ? tuple.items.map((item) => FromType(item)) : [] - return tb.Tuple(items, Options(type)) + return t.Tuple(items, Options(type)) } // ------------------------------------------------------------------ // String // ------------------------------------------------------------------ -type TFromString<_Type extends v.StringSchema> = tb.TString -function FromString(type: BaseSchema): tb.TSchema { - return tb.String(Options(type)) +type TFromString<_Type extends v.StringSchema> = t.TString +function FromString(type: BaseSchema): t.TSchema { + return t.String(Options(type)) } // ------------------------------------------------------------------ // Symbol // ------------------------------------------------------------------ -type TFromSymbol<_Type extends v.SymbolSchema> = tb.TSymbol -function FromSymbol(type: BaseSchema): tb.TSchema { - return tb.Symbol(Options(type)) +type TFromSymbol<_Type extends v.SymbolSchema> = t.TSymbol +function FromSymbol(type: BaseSchema): t.TSchema { + return t.Symbol(Options(type)) } // ------------------------------------------------------------------ // Tuple // ------------------------------------------------------------------ // prettier-ignore -type TFromTuple = ( +type TFromTuple = ( Type extends [infer Left extends BaseSchema, ...infer Right extends BaseSchema[]] ? TFromTuple]> - : tb.TTuple + : t.TTuple ) -function FromTuple(type: BaseSchema): tb.TSchema { +function FromTuple(type: BaseSchema): t.TSchema { const tuple = type as v.TupleSchema const items = globalThis.Array.isArray(tuple.items) ? tuple.items.map((item) => FromType(item)) : [] - return tb.Tuple(items, Options(type)) + return t.Tuple(items, Options(type)) } // ------------------------------------------------------------------ // TupleWithRest // ------------------------------------------------------------------ -tb.TypeRegistry.Set('ValibotTupleWithRest', (schema, value) => { +t.TypeRegistry.Set('ValibotTupleWithRest', (schema, value) => { return v.safeParse(schema.schema, value).success }) -interface TTupleWithRest = v.TupleWithRestSchema> extends tb.TSchema { - [tb.Kind]: 'ValibotTupleWithRest' +interface TTupleWithRest = v.TupleWithRestSchema> extends t.TSchema { + [t.Kind]: 'ValibotTupleWithRest' static: v.InferOutput type: Type } -function TupleWithRest>(type: Type, options?: tb.SchemaOptions): TTupleWithRest { - return tb.CreateType({ [tb.Kind]: 'ValibotTupleWithRest', type }, Options(type)) as never +function TupleWithRest>(type: Type, options?: t.SchemaOptions): TTupleWithRest { + return t.CreateType({ [t.Kind]: 'ValibotTupleWithRest', type }, Options(type)) as never } -type TFromTupleWithRest> = tb.Ensure> -function FromTupleWithRest(type: BaseSchema): tb.TSchema { +type TFromTupleWithRest> = t.Ensure> +function FromTupleWithRest(type: BaseSchema): t.TSchema { return TupleWithRest(type as v.TupleWithRestSchema, Options(type)) } // ------------------------------------------------------------------ // Undefined // ------------------------------------------------------------------ -type TFromUndefined<_Type extends v.UndefinedSchema> = tb.TUndefined -function FromUndefined(type: BaseSchema): tb.TSchema { - return tb.Undefined(Options(type)) +type TFromUndefined<_Type extends v.UndefinedSchema> = t.TUndefined +function FromUndefined(type: BaseSchema): t.TSchema { + return t.Undefined(Options(type)) } // ------------------------------------------------------------------ // Undefinable // ------------------------------------------------------------------ -type TFromUndefinedable = tb.TUnion<[TFromType, tb.TUndefined]> -function FromUndefinedable(type: BaseSchema): tb.TSchema { +type TFromUndefinedable = t.TUnion<[TFromType, t.TUndefined]> +function FromUndefinedable(type: BaseSchema): t.TSchema { const undefinedable = type as v.UndefinedableSchema - return tb.Union([FromType(undefinedable.wrapped), tb.Undefined()], Options(type)) + return t.Union([FromType(undefinedable.wrapped), t.Undefined()], Options(type)) } // ------------------------------------------------------------------ // Union // ------------------------------------------------------------------ // prettier-ignore -type TFromUnion = ( +type TFromUnion = ( Type extends [infer Left extends BaseSchema, ...infer Right extends BaseSchema[]] ? TFromUnion]> - : tb.TUnion + : t.TUnion ) -function FromUnion(type: BaseSchema): tb.TSchema { +function FromUnion(type: BaseSchema): t.TSchema { const variants = (type as v.UnionSchema).options.map((option) => FromType(option)) - return tb.Union(variants, Options(type)) + return t.Union(variants, Options(type)) } // ------------------------------------------------------------------ // Unknown // ------------------------------------------------------------------ -type TFromUnknown<_Type extends v.UnknownSchema> = tb.TUnknown -function FromUnknown(type: BaseSchema): tb.TSchema { - return tb.Unknown(Options(type)) +type TFromUnknown<_Type extends v.UnknownSchema> = t.TUnknown +function FromUnknown(type: BaseSchema): t.TSchema { + return t.Unknown(Options(type)) } // ------------------------------------------------------------------ // Variant // ------------------------------------------------------------------ -tb.TypeRegistry.Set('ValibotVariant', (schema, value) => { +t.TypeRegistry.Set('ValibotVariant', (schema, value) => { return v.safeParse(schema.schema, value).success }) -interface TVariant, any> = v.VariantSchema, any>> extends tb.TSchema { - [tb.Kind]: 'ValibotVariant' +interface TVariant, any> = v.VariantSchema, any>> extends t.TSchema { + [t.Kind]: 'ValibotVariant' static: v.InferOutput type: Type } function Variant, any>>(type: Type): TVariant { - return tb.CreateType({ [tb.Kind]: 'ValibotVariant', type }, Options(type)) as never + return t.CreateType({ [t.Kind]: 'ValibotVariant', type }, Options(type)) as never } -type TFromVariant, any>> = tb.Ensure> -function FromVariant(type: BaseSchema): tb.TSchema { +type TFromVariant, any>> = t.Ensure> +function FromVariant(type: BaseSchema): t.TSchema { return Variant(type as v.VariantSchema, any>) } // ------------------------------------------------------------------ // Void // ------------------------------------------------------------------ -type TFromVoid<_Type extends v.VoidSchema> = tb.TVoid -function FromVoid(type: BaseSchema): tb.TSchema { - return tb.Void(Options(type)) +type TFromVoid<_Type extends v.VoidSchema> = t.TVoid +function FromVoid(type: BaseSchema): t.TSchema { + return t.Void(Options(type)) } // ------------------------------------------------------------------ // Type @@ -753,7 +754,7 @@ export type TFromType = ( Type extends v.FunctionSchema ? TFromFunction : Type extends v.InstanceSchema ? TFromInstance : Type extends v.IntersectSchema ? TFromIntersect : - Type extends v.LiteralSchema ? TFromLiteral : + Type extends v.LiteralSchema ? TFromLiteral : Type extends v.LooseObjectSchema ? TFromLooseObject : Type extends v.LooseTupleSchema ? TFromLooseTuple : Type extends v.MapSchema ? TFromMap : @@ -785,7 +786,7 @@ export type TFromType = ( Type extends v.UnknownSchema ? TFromUnknown : Type extends v.VariantSchema, any> ? TFromVariant : Type extends v.VoidSchema ? TFromVoid : - tb.TNever + t.TNever ) // prettier-ignore export function FromType(type: Type): TFromType { @@ -834,35 +835,18 @@ export function FromType(type: Type): TFromType { type.type === 'union' ? FromUnion(type) : type.type === 'variant' ? FromVariant(type) : type.type === 'void' ? FromVoid(type) : - tb.Never() + t.Never() ) as never } // ------------------------------------------------------------------ -// IsValibot +// TypeBoxFromValibot // ------------------------------------------------------------------ // prettier-ignore -export function IsValibot(type: unknown): type is BaseSchema { - return ( - tb.ValueGuard.IsObject(type) && - tb.ValueGuard.HasPropertyKey(type, '~standard') && - tb.ValueGuard.IsObject(type['~standard']) && - tb.ValueGuard.HasPropertyKey(type['~standard'], 'vendor') && - type['~standard'].vendor === 'valibot' - ) -} -// ------------------------------------------------------------------ -// Box -// ------------------------------------------------------------------ -/** Converts a Valibot Type to a TypeBox Type */ -// prettier-ignore -export type TBox = ( - Type extends BaseSchema - ? Type extends { '~standard': { vendor: 'valibot' } } - ? TFromType - : undefined - : undefined +export type TTypeBoxFromValibot = ( + Type extends BaseSchema ? TFromType : t.TNever ) /** Converts a Valibot Type to a TypeBox Type */ -export function Box = TBox>(type: Type): Result { - return (IsValibot(type) ? FromType(type) : undefined) as never +// prettier-ignore +export function TypeBoxFromValibot = TTypeBoxFromValibot>(type: Type): Result { + return (Guard.IsValibot(type) ? FromType(type) : t.Never()) as never } diff --git a/src/zod/box.ts b/src/typebox/typebox-from-zod.ts similarity index 60% rename from src/zod/box.ts rename to src/typebox/typebox-from-zod.ts index 3c87177..eda6a1d 100644 --- a/src/zod/box.ts +++ b/src/typebox/typebox-from-zod.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) @@ -26,106 +26,106 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import * as tb from '@sinclair/typebox' +import * as t from '@sinclair/typebox' import * as z from 'zod' +import * as Guard from '../guard' // ------------------------------------------------------------------ // Options // ------------------------------------------------------------------ -function Options(type: z.ZodTypeAny): tb.SchemaOptions { - const description = tb.ValueGuard.IsUndefined(type.description) ? {} : { description: type.description } +function Options(type: z.ZodTypeAny): t.SchemaOptions { + const description = t.ValueGuard.IsUndefined(type.description) ? {} : { description: type.description } return { ...description } } // ------------------------------------------------------------------ // Formats // ------------------------------------------------------------------ const check = (type: z.ZodTypeAny, value: unknown) => type.safeParse(value).success -tb.FormatRegistry.Set('zod:base64', (value) => check(z.string().base64(), value)) -tb.FormatRegistry.Set('zod:base64url', (value) => check(z.string().base64url(), value)) -tb.FormatRegistry.Set('zod:cidrv4', (value) => check(z.string().cidr({ version: 'v4' }), value)) -tb.FormatRegistry.Set('zod:cidrv6', (value) => check(z.string().cidr({ version: 'v6' }), value)) -tb.FormatRegistry.Set('zod:cidr', (value) => check(z.string().cidr(), value)) -tb.FormatRegistry.Set('zod:cuid', (value) => check(z.string().cuid(), value)) -tb.FormatRegistry.Set('zod:cuid2', (value) => check(z.string().cuid2(), value)) -tb.FormatRegistry.Set('zod:ulid', (value) => check(z.string().ulid(), value)) -tb.FormatRegistry.Set('zod:email', (value) => check(z.string().email(), value)) -tb.FormatRegistry.Set('zod:emoji', (value) => check(z.string().emoji(), value)) -tb.FormatRegistry.Set('zod:ipv4', (value) => check(z.string().ip({ version: 'v4' }), value)) -tb.FormatRegistry.Set('zod:ipv6', (value) => check(z.string().ip({ version: 'v6' }), value)) -tb.FormatRegistry.Set('zod:ip', (value) => check(z.string().ip(), value)) -tb.FormatRegistry.Set('zod:ipv6Cidr', (value) => check(z.string().cidr({ version: 'v6' }), value)) -tb.FormatRegistry.Set('zod:nanoid', (value) => check(z.string().nanoid(), value)) -tb.FormatRegistry.Set('zod:jwt', (value) => check(z.string().jwt(), value)) -tb.FormatRegistry.Set('zod:date', (value) => check(z.string().date(), value)) -tb.FormatRegistry.Set('zod:datetime', (value) => check(z.string().datetime(), value)) -tb.FormatRegistry.Set('zod:duration', (value) => check(z.string().duration(), value)) -tb.FormatRegistry.Set('zod:time', (value) => check(z.string().time(), value)) -tb.FormatRegistry.Set('zod:url', (value) => check(z.string().url(), value)) -tb.FormatRegistry.Set('zod:uuid', (value) => check(z.string().uuid(), value)) +t.FormatRegistry.Set('base64', (value) => check(z.string().base64(), value)) +t.FormatRegistry.Set('base64url', (value) => check(z.string().base64url(), value)) +t.FormatRegistry.Set('cidrv4', (value) => check(z.string().cidr({ version: 'v4' }), value)) +t.FormatRegistry.Set('cidrv6', (value) => check(z.string().cidr({ version: 'v6' }), value)) +t.FormatRegistry.Set('cidr', (value) => check(z.string().cidr(), value)) +t.FormatRegistry.Set('cuid', (value) => check(z.string().cuid(), value)) +t.FormatRegistry.Set('cuid2', (value) => check(z.string().cuid2(), value)) +t.FormatRegistry.Set('date', (value) => check(z.string().date(), value)) +t.FormatRegistry.Set('datetime', (value) => check(z.string().datetime(), value)) +t.FormatRegistry.Set('duration', (value) => check(z.string().duration(), value)) +t.FormatRegistry.Set('email', (value) => check(z.string().email(), value)) +t.FormatRegistry.Set('emoji', (value) => check(z.string().emoji(), value)) +t.FormatRegistry.Set('ipv4', (value) => check(z.string().ip({ version: 'v4' }), value)) +t.FormatRegistry.Set('ipv6', (value) => check(z.string().ip({ version: 'v6' }), value)) +t.FormatRegistry.Set('ip', (value) => check(z.string().ip(), value)) +t.FormatRegistry.Set('jwt', (value) => check(z.string().jwt(), value)) +t.FormatRegistry.Set('nanoid', (value) => check(z.string().nanoid(), value)) +t.FormatRegistry.Set('time', (value) => check(z.string().time(), value)) +t.FormatRegistry.Set('ulid', (value) => check(z.string().ulid(), value)) +t.FormatRegistry.Set('url', (value) => check(z.string().url(), value)) +t.FormatRegistry.Set('uuid', (value) => check(z.string().uuid(), value)) // ------------------------------------------------------------------ // Any // ------------------------------------------------------------------ -type TFromAny = tb.TAny -function FromAny(_def: Def) { - return tb.Any() +type TFromAny = t.TAny +function FromAny(_def: Def): t.TSchema { + return t.Any() } // ------------------------------------------------------------------ // Array // ------------------------------------------------------------------ -type TFromArray = tb.Ensure>> -function FromArray(def: Def): tb.TSchema { +type TFromArray = t.Ensure>> +function FromArray(def: Def): t.TSchema { const minItems = def.minLength === null ? {} : { minItems: def.minLength.value } const maxItems = def.maxLength === null ? {} : { minItems: def.maxLength.value } const options = { ...minItems, ...maxItems } - return tb.Array(FromType(def.type), options) + return t.Array(FromType(def.type), options) } // ------------------------------------------------------------------ // BigInt // ------------------------------------------------------------------ -type TFromBigInt = tb.TBigInt -function FromBigInt(def: Def) { - return tb.BigInt() +type TFromBigInt = t.TBigInt +function FromBigInt(def: Def): t.TSchema { + return t.BigInt() } // ------------------------------------------------------------------ // Boolean // ------------------------------------------------------------------ -type TFromBoolean = tb.TBoolean -function FromBoolean(def: Def) { - return tb.Boolean() +type TFromBoolean = t.TBoolean +function FromBoolean(def: Def): t.TSchema { + return t.Boolean() } // ------------------------------------------------------------------ // Date // ------------------------------------------------------------------ -type TFromDate = tb.TDate -function FromDate(def: Def) { - return tb.Date() +type TFromDate = t.TDate +function FromDate(def: Def): t.TSchema { + return t.Date() } // ------------------------------------------------------------------ // Default // ------------------------------------------------------------------ type TFromDefault = TFromType -function FromDefault(def: Def): tb.TSchema { - return tb.CloneType(FromType(def.innerType), { default: def.defaultValue() }) +function FromDefault(def: Def): t.TSchema { + return t.CloneType(FromType(def.innerType), { default: def.defaultValue() }) } // ------------------------------------------------------------------ // DiscriminatedUnion // ------------------------------------------------------------------ // prettier-ignore -type TFromDiscriminatedUnion[], Result extends tb.TSchema[] = []> = ( +type TFromDiscriminatedUnion[], Result extends t.TSchema[] = []> = ( Types extends [infer Left extends z.ZodObject, ...infer Right extends z.ZodObject[]] ? TFromDiscriminatedUnion]> - : tb.TUnion + : t.TUnion ) -function FromDiscriminatedUnion[]>>(def: Def): tb.TSchema { +function FromDiscriminatedUnion[]>>(def: Def): t.TSchema { const types = def.options.map((type) => FromType(type)) - return tb.Union(types, { discriminator: def.discriminator }) + return t.Union(types, { discriminator: def.discriminator }) } // ------------------------------------------------------------------ // Effects // ------------------------------------------------------------------ -type TFromEffects = tb.Ensure, Output>> -function FromEffects>(type: Type): tb.TSchema { - return tb +type TFromEffects = t.Ensure, Output>> +function FromEffects>(type: Type): t.TSchema { + return t .Transform(FromType(type._def.schema)) .Decode((value) => type.parse(value)) .Encode((_) => { @@ -133,69 +133,77 @@ function FromEffects>(type: Typ }) } // ------------------------------------------------------------------ +// Enum +// ------------------------------------------------------------------ +type TFromEnum = Variants extends [infer Left extends string, ...infer Right extends string[]] ? TFromEnum]> : t.TUnion +function FromEnum(def: Def): t.TSchema { + const variants = def.values.map((value) => t.Literal(value)) + return t.Union(variants) +} +// ------------------------------------------------------------------ // Literal // ------------------------------------------------------------------ -type TFromLiteral = tb.Ensure : tb.TNever> -function FromLiteral(def: Def) { - return tb.Literal(def.value as tb.TLiteralValue) +type TFromLiteral = t.Ensure : t.TNever> +function FromLiteral(def: Def): t.TSchema { + return t.Literal(def.value as t.TLiteralValue) } // ------------------------------------------------------------------ // Intersect // ------------------------------------------------------------------ // prettier-ignore -type TFromIntersect = ( +type TFromIntersect = ( Types extends [infer Left extends z.ZodTypeAny, ...infer Right extends z.ZodTypeAny[]] ? TFromIntersect]> - : tb.Ensure> + : t.Ensure> ) -function FromIntersect(type: Type): tb.TSchema { - return tb.Intersect([FromType(type.left), FromType(type.right)]) +function FromIntersect(type: Type): t.TSchema { + return t.Intersect([FromType(type.left), FromType(type.right)]) } // ------------------------------------------------------------------ // Object // ------------------------------------------------------------------ -type TFromObject = tb.Ensure< - tb.TObject<{ +type TFromObject = t.Ensure< + t.TObject<{ [Key in keyof Properties]: TFromType }> > -function FromObject, Shape extends z.ZodRawShape>(def: Def, shape: Shape): tb.TSchema { +function FromObject, Shape extends z.ZodRawShape>(def: Def, shape: Shape): t.TSchema { const additionalProperties = def.unknownKeys === 'strict' ? { additionalProperties: false } : {} const options = { ...additionalProperties } - return tb.Object( + return t.Object( globalThis.Object.keys(shape).reduce((properties: any, key: any) => { return { ...properties, [key]: FromType(shape[key]) } - }, {} as tb.TProperties) as never, + }, {} as t.TProperties) as never, options, ) } // ------------------------------------------------------------------ // Optional // ------------------------------------------------------------------ -type TFromOptional>> = Result -function FromOptional(def: Def): tb.TSchema { - return tb.Optional(FromType(def.innerType)) +type TFromOptional>> = Result +function FromOptional(def: Def): t.TSchema { + return t.Optional(FromType(def.innerType)) } // ------------------------------------------------------------------ // Promise // ------------------------------------------------------------------ -type TFromPromise = tb.Ensure>> -function FromPromise(def: Def): tb.TSchema { - return tb.Promise(FromType(def.type)) +type TFromPromise = t.Ensure>> +function FromPromise(def: Def): t.TSchema { + return t.Promise(FromType(def.type)) } // ------------------------------------------------------------------ // Nullable // ------------------------------------------------------------------ -type TFromNullable = tb.Ensure]>> -function FromNullable(def: Def): tb.TSchema { - return tb.Union([tb.Null(), FromType(def.innerType)]) +type TFromNullable = t.Ensure]>> +function FromNullable(def: Def): t.TSchema { + return t.Union([t.Null(), FromType(def.innerType)]) } // ------------------------------------------------------------------ // Number // ------------------------------------------------------------------ -type TFromNumber = tb.TNumber +type TFromNumber = t.TNumber // prettier-ignore -function FromNumber(def: Def) { +function FromNumber(def: Def): t.TSchema { const options = def.checks.reduce((options, check) => { return { ...options, ... ( check.kind === 'int' ? { multipleOf: 1 } : @@ -205,124 +213,124 @@ function FromNumber(def: Def) { {} )} }, {}) - return tb.Number(options) + return t.Number(options) } // ------------------------------------------------------------------ // Never // ------------------------------------------------------------------ -type TFromNever = tb.TNever -function FromNever(def: Def) { - return tb.Never() +type TFromNever = t.TNever +function FromNever(def: Def): t.TSchema { + return t.Never() } // ------------------------------------------------------------------ // Null // ------------------------------------------------------------------ -type TFromNull = tb.TNull -function FromNull(def: Def) { - return tb.Null() +type TFromNull = t.TNull +function FromNull(def: Def): t.TSchema { + return t.Null() } // ------------------------------------------------------------------ // Readonly // ------------------------------------------------------------------ -type TFromReadonly>> = Result -function FromReadonly(def: Def): tb.TSchema { - return tb.Readonly(FromType(def.innerType)) +type TFromReadonly>> = Result +function FromReadonly(def: Def): t.TSchema { + return t.Readonly(FromType(def.innerType)) } // ------------------------------------------------------------------ // Record // ------------------------------------------------------------------ -type TFromRecord = tb.Ensure, TFromType>> -function FromRecord(def: Def): tb.TSchema { - return tb.Record(FromType(def.keyType), FromType(def.valueType)) +type TFromRecord = t.Ensure, TFromType>> +function FromRecord(def: Def): t.TSchema { + return t.Record(FromType(def.keyType), FromType(def.valueType)) } // ------------------------------------------------------------------ // String // ------------------------------------------------------------------ -type TFromString = tb.TString +type TFromString = t.TString // prettier-ignore -function FromString(def: Def) { +function FromString(def: Def): t.TSchema { const options = def.checks.reduce((options, check) => { return { ...options, ...( - check.kind === 'base64' ? { format: 'zod:base64' } : - check.kind === 'base64url' ? { format: 'zod:base64url' } : - check.kind === 'cidr' ? { format: check.version === 'v4' ? 'zod:cidrv4' : check.version === 'v6' ? 'zod:cidrv6' : 'zod:cidr' } : - check.kind === 'cuid' ? { format: 'zod:cuid' } : - check.kind === 'cuid2' ? { format: 'zod:cuid2' } : - check.kind === 'date' ? { format: 'zod:date' } : - check.kind === 'datetime' ? { format: 'zod:datetime' } : - check.kind === 'duration' ? { format: 'zod:duration' } : - check.kind === 'email' ? { format: 'zod:email' } : - check.kind === 'emoji' ? { format: 'zod:emoji' } : + check.kind === 'base64' ? { format: 'base64' } : + check.kind === 'base64url' ? { format: 'base64url' } : + check.kind === 'cidr' ? { format: check.version === 'v4' ? 'cidrv4' : check.version === 'v6' ? 'cidrv6' : 'cidr' } : + check.kind === 'cuid' ? { format: 'cuid' } : + check.kind === 'cuid2' ? { format: 'cuid2' } : + check.kind === 'date' ? { format: 'date' } : + check.kind === 'datetime' ? { format: 'datetime' } : + check.kind === 'duration' ? { format: 'duration' } : + check.kind === 'email' ? { format: 'email' } : + check.kind === 'emoji' ? { format: 'emoji' } : check.kind === 'endsWith' ? { pattern: `${check.value}$` } : check.kind === 'includes' ? { pattern: check.value } : - check.kind === 'ip' ? { format: check.version === 'v4' ? 'zod:ipv4' : check.version === 'v6' ? 'zod:ipv6' : 'zod:ip' } : - check.kind === 'jwt' ? { format: 'zod:jwt' } : + check.kind === 'ip' ? { format: check.version === 'v4' ? 'ipv4' : check.version === 'v6' ? 'ipv6' : 'ip' } : + check.kind === 'jwt' ? { format: 'jwt' } : check.kind === 'length' ? { minLength: check.value, maxLength: check.value } : check.kind === 'min' ? { minLength: check.value } : check.kind === 'max' ? { maxLength: check.value } : - check.kind === 'nanoid' ? { format: 'zod:nanoid' } : + check.kind === 'nanoid' ? { format: 'nanoid' } : check.kind === 'regex' ? { pattern: check.regex.source } : check.kind === 'startsWith' ? { pattern: `^${check.value}` } : - check.kind === 'time' ? { format: 'zod:time' } : - check.kind === 'ulid' ? { format: 'zod:ulid' } : - check.kind === 'url' ? { format: 'zod:url' } : - check.kind === 'uuid' ? { format: 'zod:uuid' } : + check.kind === 'time' ? { format: 'time' } : + check.kind === 'ulid' ? { format: 'ulid' } : + check.kind === 'url' ? { format: 'url' } : + check.kind === 'uuid' ? { format: 'uuid' } : {} )} }, {}) - return tb.String(options) + return t.String(options) } // ------------------------------------------------------------------ // Symbol // ------------------------------------------------------------------ -type TFromSymbol = tb.TSymbol -function FromSymbol(def: Def) { - return tb.Symbol() +type TFromSymbol = t.TSymbol +function FromSymbol(def: Def): t.TSchema { + return t.Symbol() } // ------------------------------------------------------------------ // Tuple // ------------------------------------------------------------------ // prettier-ignore -type TFromTuple = ( +type TFromTuple = ( Types extends [infer Left extends z.ZodTypeAny, ...infer Right extends z.ZodTypeAny[]] ? TFromTuple]> - : tb.TTuple + : t.TTuple ) -function FromTuple(def: Def): tb.TSchema { - return tb.Tuple(def.items.map((item) => FromType(item))) +function FromTuple(def: Def): t.TSchema { + return t.Tuple(def.items.map((item) => FromType(item))) } // ------------------------------------------------------------------ // Undefined // ------------------------------------------------------------------ -type TFromUndefined = tb.TUndefined -function FromUndefined(def: Def) { - return tb.Undefined() +type TFromUndefined = t.TUndefined +function FromUndefined(def: Def): t.TSchema { + return t.Undefined() } // ------------------------------------------------------------------ // Union // ------------------------------------------------------------------ // prettier-ignore -type TFromUnion = ( +type TFromUnion = ( Types extends [infer Left extends z.ZodTypeAny, ...infer Right extends z.ZodTypeAny[]] ? TFromUnion]> - : tb.TUnion + : t.TUnion ) -function FromUnion(def: Def): tb.TSchema { - return tb.Union(def.options.map((item) => FromType(item))) +function FromUnion(def: Def): t.TSchema { + return t.Union(def.options.map((item) => FromType(item))) } // ------------------------------------------------------------------ // Unknown // ------------------------------------------------------------------ -type TFromUnknown = tb.TUnknown -function FromUnknown(def: Def) { - return tb.Unknown() +type TFromUnknown = t.TUnknown +function FromUnknown(def: Def): t.TSchema { + return t.Unknown() } // ------------------------------------------------------------------ // Void // ------------------------------------------------------------------ -type TFromVoid = tb.TVoid -function FromVoid(def: Def) { - return tb.Void() +type TFromVoid = t.TVoid +function FromVoid(def: Def): t.TSchema { + return t.Void() } // ------------------------------------------------------------------ // Type @@ -337,6 +345,7 @@ type TFromType = ( Type extends z.ZodDefault ? TFromDefault : Type extends z.ZodDiscriminatedUnion ? TFromDiscriminatedUnion : Type extends z.ZodEffects ? TFromEffects : + Type extends z.ZodEnum ? TFromEnum : Type extends z.ZodLiteral ? TFromLiteral : Type extends z.ZodNullable ? TFromNullable : Type extends z.ZodObject ? TFromObject : @@ -349,17 +358,17 @@ type TFromType = ( Type extends z.ZodNull ? TFromNull : Type extends z.ZodString ? TFromString : Type extends z.ZodSymbol ? TFromSymbol : - Type extends z.ZodTuple ? TFromTuple> : + Type extends z.ZodTuple ? TFromTuple> : Type extends z.ZodUndefined ? TFromUndefined : - Type extends z.ZodUnion ? TFromUnion> : + Type extends z.ZodUnion ? TFromUnion> : Type extends z.ZodUnknown ? TFromUnknown : Type extends z.ZodVoid ? TFromVoid : // Intersection (Ensure Last Due to Zod Differentiation Issue) Type extends z.ZodIntersection ? TFromIntersect<[Left, Right]> : - tb.TNever + t.TNever ) // prettier-ignore -function FromType(type: Type): tb.TSchema { +function FromType(type: Type): t.TSchema { const schema = ( type instanceof z.ZodAny ? FromAny(type._def) : type instanceof z.ZodArray ? FromArray(type._def) : @@ -369,6 +378,7 @@ function FromType(type: Type): tb.TSchema { type instanceof z.ZodDefault ? FromDefault(type._def) : type instanceof z.ZodDiscriminatedUnion ? FromDiscriminatedUnion(type._def) : type instanceof z.ZodEffects ? FromEffects(type) : + type instanceof z.ZodEnum ? FromEnum(type._def) : type instanceof z.ZodLiteral ? FromLiteral(type._def) : type instanceof z.ZodNullable ? FromNullable(type._def) : type instanceof z.ZodObject ? FromObject(type._def, type.shape) : @@ -388,16 +398,19 @@ function FromType(type: Type): tb.TSchema { type instanceof z.ZodVoid ? FromVoid(type._def) : // Intersection (Ensure Last Due to Zod Differentiation Issue) type instanceof z.ZodIntersection ? FromIntersect(type._def) : - tb.Never() - ) as tb.TSchema - return tb.CreateType(schema, Options(type)) as tb.TSchema + t.Never() + ) as t.TSchema + return t.CreateType(schema, Options(type)) as t.TSchema } // ------------------------------------------------------------------ -// Box +// TypeBoxFromZod // ------------------------------------------------------------------ -/** Converts a Zod Type to a TypeBox Type */ -export type TBox = Type extends z.ZodType ? TFromType : undefined -/** Converts a Zod Type to a TypeBox Type */ -export function Box = TBox>(type: Type): Result { - return (type instanceof z.ZodType ? FromType(type) : undefined) as never +// prettier-ignore +export type TTypeBoxFromZod = ( + Type extends z.ZodType ? TFromType : t.TNever +) + +// prettier-ignore +export function TypeBoxFromZod = TTypeBoxFromZod>(type: Type): Result { + return (type instanceof z.ZodType ? FromType(type) : t.Never()) as never } diff --git a/src/typebox/typebox.ts b/src/typebox/typebox.ts new file mode 100644 index 0000000..fe8ee84 --- /dev/null +++ b/src/typebox/typebox.ts @@ -0,0 +1,63 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typemap + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TTypeBoxFromSyntax, TypeBoxFromSyntax } from './typebox-from-syntax' +import { TTypeBoxFromTypeBox, TypeBoxFromTypeBox } from './typebox-from-typebox' +import { TTypeBoxFromValibot, TypeBoxFromValibot } from './typebox-from-valibot' +import { TTypeBoxFromZod, TypeBoxFromZod } from './typebox-from-zod' +import * as Guard from '../guard' +import * as t from '@sinclair/typebox' + +/** Creates a TypeBox type from Syntax or another Type */ +// prettier-ignore +export type TTypeBox = ( + Guard.TIsSyntax extends true ? TTypeBoxFromSyntax : + Guard.TIsTypeBox extends true ? TTypeBoxFromTypeBox : + Guard.TIsValibot extends true ? TTypeBoxFromValibot : + Guard.TIsZod extends true ? TTypeBoxFromZod : + t.TNever +) +/** Creates a TypeBox type from Syntax or another Type */ +// prettier-ignore +export function TypeBox(type: Type): TTypeBox { + return ( + Guard.IsSyntax(type) ? TypeBoxFromSyntax(type) : + Guard.IsTypeBox(type) ? TypeBoxFromTypeBox(type) : + Guard.IsValibot(type) ? TypeBoxFromValibot(type) : + Guard.IsZod(type) ? TypeBoxFromZod(type) : + t.Never() + ) as never +} + +/** + * Creates a TypeBox type from Syntax or another Type + * @deprecated Use TypeBox() export instead + */ +export function Box(type: Type): TTypeBox { + return TypeBox(type) +} diff --git a/src/valibot/index.ts b/src/valibot/common.ts similarity index 73% rename from src/valibot/index.ts rename to src/valibot/common.ts index a89712c..b34f3cc 100644 --- a/src/valibot/index.ts +++ b/src/valibot/common.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) @@ -26,4 +26,10 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './box' +import * as v from 'valibot' + +// Valibot really should consider providing default generic type parameters +export type BaseConstraint = v.BaseValidation> | v.BaseMetadata | v.RegexAction +export type BaseRecordKey = v.BaseSchema> +export type BaseSchema = v.BaseSchema> +export type BaseError = v.ErrorMessage diff --git a/src/valibot/valibot-from-syntax.ts b/src/valibot/valibot-from-syntax.ts new file mode 100644 index 0000000..eec3a1d --- /dev/null +++ b/src/valibot/valibot-from-syntax.ts @@ -0,0 +1,46 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typemap + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TTypeBoxFromSyntax, TypeBoxFromSyntax } from '../typebox/typebox-from-syntax' +import { ValibotFromTypeBox, TValibotFromTypeBox } from './valibot-from-typebox' + +import * as t from '@sinclair/typebox' +import * as c from './common' + +// prettier-ignore +export type TValibotFromSyntax, + Result extends c.BaseSchema = TValibotFromTypeBox +> = Result + +// prettier-ignore +export function ValibotFromSyntax(type: Type): TValibotFromSyntax { + const schema = TypeBoxFromSyntax(type) + const result = ValibotFromTypeBox(schema) + return result +} diff --git a/src/valibot/valibot-from-typebox.ts b/src/valibot/valibot-from-typebox.ts new file mode 100644 index 0000000..755b020 --- /dev/null +++ b/src/valibot/valibot-from-typebox.ts @@ -0,0 +1,394 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typemap + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as t from '@sinclair/typebox' +import * as v from 'valibot' +import * as c from './common' + +// ------------------------------------------------------------------ +// Constraints +// ------------------------------------------------------------------ +function CreateConstraints(type: t.TSchema, initial: c.BaseConstraint[] = []): c.BaseConstraint[] { + const constraints: c.BaseConstraint[] = [] + if (t.ValueGuard.IsString(type.description)) constraints.push(v.description(type.description!)) + if (t.ValueGuard.IsString(type.title)) constraints.push(v.title(type.title!)) + if (t.ValueGuard.IsObject(type.metadata)) constraints.push(v.metadata(type.metadata!)) + return [...initial, ...constraints] +} +function CreateType(type: c.BaseSchema, constraints: c.BaseConstraint[] = []) { + return constraints.length === 0 ? type : v.pipe(type, ...constraints) +} +// ------------------------------------------------------------------ +// Any +// ------------------------------------------------------------------ +type TFromAny = Result +function FromAny(type: t.TAny): c.BaseSchema { + return CreateType(v.any(), CreateConstraints(type)) +} +// ------------------------------------------------------------------ +// Array +// ------------------------------------------------------------------ +type TFromArray, c.BaseError>> = Result +function FromArray(type: t.TArray): c.BaseSchema { + const { minItems, maxItems /* minContains, maxContains, contains */ } = type + const constraints = CreateConstraints(type) + if (t.ValueGuard.IsNumber(minItems)) constraints.push(v.minLength(minItems)) + if (t.ValueGuard.IsNumber(maxItems)) constraints.push(v.maxLength(maxItems)) + const mapped = v.array(FromType(type.items)) + return CreateType(mapped, constraints) +} +// ------------------------------------------------------------------ +// BigInt +// ------------------------------------------------------------------ +type TFromBigInt> = Result +function FromBigInt(type: t.TBigInt): c.BaseSchema { + return CreateType(v.bigint(), CreateConstraints(type)) +} +// ------------------------------------------------------------------ +// Boolean +// ------------------------------------------------------------------ +type TFromBoolean> = Result +function FromBoolean(type: t.TBoolean): c.BaseSchema { + return CreateType(v.boolean(), CreateConstraints(type)) +} +// ------------------------------------------------------------------ +// Date +// ------------------------------------------------------------------ +type TFromDate> = Result +function FromDate(type: t.TDate): c.BaseSchema { + return CreateType(v.date(), CreateConstraints(type)) +} +// ------------------------------------------------------------------ +// Function +// ------------------------------------------------------------------ +// prettier-ignore +type TFromFunction = ( + v.FunctionSchema +) +function FromFunction(type: t.TFunction): c.BaseSchema { + return CreateType(v.function(), CreateConstraints(type)) +} +// ------------------------------------------------------------------ +// Integer +// ------------------------------------------------------------------ +type TFromInteger = v.NumberSchema +function FromInteger(type: t.TInteger): c.BaseSchema { + const { exclusiveMaximum, exclusiveMinimum, minimum, maximum, multipleOf } = type + const constraints = CreateConstraints(type, [v.integer()]) + if (t.ValueGuard.IsNumber(exclusiveMinimum)) constraints.push(v.minValue(exclusiveMinimum + 1)) + if (t.ValueGuard.IsNumber(exclusiveMaximum)) constraints.push(v.minValue(exclusiveMaximum - 1)) + if (t.ValueGuard.IsNumber(maximum)) constraints.push(v.maxValue(maximum)) + if (t.ValueGuard.IsNumber(minimum)) constraints.push(v.minValue(minimum)) + if (t.ValueGuard.IsNumber(multipleOf)) constraints.push(v.multipleOf(multipleOf)) + return CreateType(v.number(), constraints) +} +// ------------------------------------------------------------------ +// Intersect +// ------------------------------------------------------------------ +type TFromIntersect = Types extends [infer Left extends t.TSchema, ...infer Right extends t.TSchema[]] + ? TFromIntersect]> + : v.IntersectSchema +function FromIntersect(type: t.TIntersect): c.BaseSchema { + const schemas = type.allOf.map((schema) => FromType(schema)) + return CreateType(v.intersect(schemas), CreateConstraints(type)) +} +// ------------------------------------------------------------------ +// Literal +// ------------------------------------------------------------------ +type TFromLiteral = v.LiteralSchema +function FromLiteral(type: t.TLiteral): c.BaseSchema { + return CreateType(v.literal(type.const), CreateConstraints(type)) +} +// ------------------------------------------------------------------ +// Object +// ------------------------------------------------------------------ +// prettier-ignore +type TFromObject + }, c.BaseError> +> = Result +function FromObject(type: t.TObject): c.BaseSchema { + const { additionalProperties } = type + const constraints = CreateConstraints(type) + const properties = globalThis.Object.getOwnPropertyNames(type.properties).reduce((result, key) => ({ ...result, [key]: FromType(type.properties[key]) }), {}) + return additionalProperties === false + ? CreateType(v.strictObject(properties), constraints) // facade + : CreateType(v.object(properties), constraints) +} +// ------------------------------------------------------------------ +// Promise +// ------------------------------------------------------------------ +type TFromPromise<_Type extends t.TSchema, Result = v.PromiseSchema> = Result +function FromPromise(type: t.TPromise): c.BaseSchema { + return CreateType(v.promise(), CreateConstraints(type)) +} +// ------------------------------------------------------------------ +// Record +// ------------------------------------------------------------------ +type TFromRegExp> = Result +function FromRegExp(type: t.TRegExp): c.BaseSchema { + const { minLength, maxLength } = type + const constraints = CreateConstraints(type, [v.regex(new RegExp(type.source, type.flags))]) + if (t.ValueGuard.IsNumber(maxLength)) constraints.push(v.maxLength(maxLength)) + if (t.ValueGuard.IsNumber(minLength)) constraints.push(v.minLength(minLength)) + return CreateType(v.string(), constraints) +} +// ------------------------------------------------------------------ +// Record +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRecord = ( + TFromType extends infer MappedKey extends c.BaseRecordKey + ? v.RecordSchema, c.BaseError> + : v.RecordSchema, TFromType, c.BaseError> +) +// prettier-ignore +function FromRecord(type: t.TRecord): c.BaseSchema { + const constraints = CreateConstraints(type) + const pattern = globalThis.Object.getOwnPropertyNames(type.patternProperties)[0] + const value = FromType(type.patternProperties[pattern]) + return ( + pattern === t.PatternStringExact + ? CreateType(v.record(v.string(), value), constraints) + : CreateType(v.record(v.pipe(v.string(), v.regex(new RegExp(pattern))), value), constraints) + ) +} +// ------------------------------------------------------------------ +// Optional +// ------------------------------------------------------------------ +type TFromOptional, c.BaseError>> = Result +function FromOptional(type: t.TOptional): c.BaseSchema { + return v.optional(FromType(t.Optional(type, false))) +} +// ------------------------------------------------------------------ +// Readonly +// ------------------------------------------------------------------ +type TFromReadonly> = Result +function FromReadonly(type: t.TReadonly): c.BaseSchema { + return FromType(t.Readonly(type, false)) +} +// ------------------------------------------------------------------ +// Never +// ------------------------------------------------------------------ +type TFromNever> = Result +function FromNever(type: t.TNever): c.BaseSchema { + return CreateType(v.never(), CreateConstraints(type)) +} +// ------------------------------------------------------------------ +// Null +// ------------------------------------------------------------------ +type TFromNull> = Result +function FromNull(type: t.TNull): c.BaseSchema { + return CreateType(v.null(), CreateConstraints(type)) +} +// ------------------------------------------------------------------ +// Number +// ------------------------------------------------------------------ +type TFromNumber> = Result +function FromNumber(type: t.TNumber): c.BaseSchema { + const { exclusiveMaximum, exclusiveMinimum, minimum, maximum, multipleOf } = type + const constraints = CreateConstraints(type) + if (t.ValueGuard.IsNumber(exclusiveMinimum)) constraints.push(v.minValue(exclusiveMinimum + 1)) + if (t.ValueGuard.IsNumber(exclusiveMaximum)) constraints.push(v.minValue(exclusiveMaximum - 1)) + if (t.ValueGuard.IsNumber(maximum)) constraints.push(v.maxValue(maximum)) + if (t.ValueGuard.IsNumber(minimum)) constraints.push(v.minValue(minimum)) + if (t.ValueGuard.IsNumber(multipleOf)) constraints.push(v.multipleOf(multipleOf)) + return CreateType(v.number(), constraints) +} +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +type TFromString> = Result +// prettier-ignore +function FromString(type: t.TString): c.BaseSchema { + const { minLength, maxLength, pattern, format } = type + const constraints = CreateConstraints(type) + if (t.ValueGuard.IsNumber(maxLength)) constraints.push(v.maxLength(maxLength)) + if (t.ValueGuard.IsNumber(minLength)) constraints.push(v.minLength(minLength)) + if (t.ValueGuard.IsString(pattern)) constraints.push(v.regex(new RegExp(pattern))) + if (t.ValueGuard.IsString(format)) constraints.push(...( + format === 'base64' ? [v.base64()] : + format === 'bic' ? [v.bic()] : + format === 'credit_card' ? [v.creditCard()] : + format === 'cuid2' ? [v.cuid2()] : + format === 'decimal' ? [v.decimal()] : + format === 'digits' ? [v.digits()] : + format === 'email' ? [v.email()] : + format === 'emoji' ? [v.emoji()] : + format === 'ip' ? [v.ip()] : + format === 'ipv4' ? [v.ipv4()] : + format === 'ipv6' ? [v.ipv6()] : + format === 'iso_date' ? [v.isoDate()] : + format === 'iso_date_time' ? [v.isoDateTime()] : + format === 'iso_time' ? [v.isoTime()] : + format === 'iso_time_second' ? [v.isoTimeSecond()] : + format === 'iso_timestamp' ? [v.isoTimestamp()] : + format === 'iso_week' ? [v.isoWeek()] : + format === 'mac' ? [v.mac()] : + format === 'mac48' ? [v.mac48()] : + format === 'mac64' ? [v.mac64()] : + format === 'nanoid' ? [v.nanoid()] : + format === 'octal' ? [v.octal()] : + format === 'ulid' ? [v.ulid()] : + format === 'url' ? [v.url()] : + format === 'uuid' ? [v.uuid()] : + [])) + return CreateType(v.string(), constraints) +} +// ------------------------------------------------------------------ +// Symbol +// ------------------------------------------------------------------ +type TFromSymbol> = Result +function FromSymbol(type: t.TSymbol): c.BaseSchema { + return CreateType(v.symbol(), CreateConstraints(type)) +} +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTuple> = ( + v.TupleSchema +) +function FromTuple(type: t.TTuple): c.BaseSchema { + const mapped = FromTypes(type.items || []) as [] | [c.BaseSchema, ...c.BaseSchema[]] + return CreateType(v.tuple(mapped), CreateConstraints(type)) +} +// ------------------------------------------------------------------ +// Undefined +// ------------------------------------------------------------------ +type TFromUndefined> = Result +function FromUndefined(type: t.TUndefined): c.BaseSchema { + return CreateType(v.undefined(), CreateConstraints(type)) +} +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +type TFromUnion, Result = v.UnionSchema> = ( + Result +) +function FromUnion(type: t.TUnion): c.BaseSchema { + const mapped = FromTypes(type.anyOf) as [c.BaseSchema, c.BaseSchema, ...c.BaseSchema[]] + return CreateType(v.union(mapped), CreateConstraints(type)) +} +// ------------------------------------------------------------------ +// TUnknown +// ------------------------------------------------------------------ +type TFromUnknown = Result +function FromUnknown(type: t.TUnknown): c.BaseSchema { + return CreateType(v.unknown(), CreateConstraints(type)) +} +// ------------------------------------------------------------------ +// Void +// ------------------------------------------------------------------ +type TFromVoid> = Result +function FromVoid(type: t.TVoid): c.BaseSchema { + return CreateType(v.void(), CreateConstraints(type)) +} +// ------------------------------------------------------------------ +// Types +// ------------------------------------------------------------------ +type TFromTypes = Types extends [infer Left extends t.TSchema, ...infer Right extends t.TSchema[]] ? TFromTypes]> : Result +function FromTypes(types: t.TSchema[]): c.BaseSchema[] { + return types.map((type) => FromType(type)) +} +// ------------------------------------------------------------------ +// Type +// ------------------------------------------------------------------ +// prettier-ignore +type TFromType = ( + Type extends t.TReadonly ? TFromReadonly : + Type extends t.TOptional ? TFromOptional : + Type extends t.TAny ? TFromAny : + Type extends t.TArray ? TFromArray : + Type extends t.TBigInt ? TFromBigInt : + Type extends t.TBoolean ? TFromBoolean : + Type extends t.TDate ? TFromDate : + Type extends t.TFunction ? TFromFunction : + Type extends t.TInteger ? TFromInteger : + Type extends t.TIntersect ? TFromIntersect : + Type extends t.TLiteral ? TFromLiteral : + Type extends t.TNull ? TFromNull : + Type extends t.TNever ? TFromNever : + Type extends t.TNumber ? TFromNumber : + Type extends t.TObject ? TFromObject : + Type extends t.TPromise ? TFromPromise : + Type extends t.TRecord ? TFromRecord : + Type extends t.TRegExp ? TFromRegExp : + Type extends t.TString ? TFromString : + Type extends t.TSymbol ? TFromSymbol : + Type extends t.TTuple ? TFromTuple : + Type extends t.TUndefined ? TFromUndefined : + Type extends t.TUnion ? TFromUnion : + Type extends t.TUnknown ? TFromUnknown : + Type extends t.TVoid ? TFromVoid : + v.NeverSchema +) +// prettier-ignore +function FromType(type: t.TSchema): c.BaseSchema { + return ( + t.KindGuard.IsReadonly(type) ? FromReadonly(type) : + t.KindGuard.IsOptional(type) ? FromOptional(type) : + t.KindGuard.IsAny(type) ? FromAny(type) : + t.KindGuard.IsArray(type) ? FromArray(type) : + t.KindGuard.IsBigInt(type) ? FromBigInt(type) : + t.KindGuard.IsBoolean(type) ? FromBoolean(type) : + t.KindGuard.IsDate(type) ? FromDate(type) : + t.KindGuard.IsFunction(type) ? FromFunction(type) : + t.KindGuard.IsInteger(type) ? FromInteger(type) : + t.KindGuard.IsIntersect(type) ? FromIntersect(type) : + t.KindGuard.IsLiteral(type) ? FromLiteral(type) : + t.KindGuard.IsNever(type) ? FromNever(type) : + t.KindGuard.IsNull(type) ? FromNull(type) : + t.KindGuard.IsNumber(type) ? FromNumber(type) : + t.KindGuard.IsObject(type) ? FromObject(type) : + t.KindGuard.IsPromise(type) ? FromPromise(type) : + t.KindGuard.IsRegExp(type) ? FromRegExp(type) : + t.KindGuard.IsRecord(type) ? FromRecord(type) : + t.KindGuard.IsString(type) ? FromString(type) : + t.KindGuard.IsSymbol(type) ? FromSymbol(type) : + t.KindGuard.IsTuple(type) ? FromTuple(type) : + t.KindGuard.IsUndefined(type) ? FromUndefined(type) : + t.KindGuard.IsUnion(type) ? FromUnion(type) : + t.KindGuard.IsUnknown(type) ? FromUnknown(type) : + t.KindGuard.IsVoid(type) ? FromVoid(type) : + v.never() + ) +} +// ------------------------------------------------------------------ +// ValibotFromTypeBox +// ------------------------------------------------------------------ +// prettier-ignore +export type TValibotFromTypeBox = ( + Type extends t.TSchema ? TFromType : v.NeverSchema +) +// prettier-ignore +export function ValibotFromTypeBox(type: Type): TValibotFromTypeBox { + return (t.KindGuard.IsSchema(type) ? FromType(type) : v.never()) as never +} diff --git a/src/valibot/valibot-from-valibot.ts b/src/valibot/valibot-from-valibot.ts new file mode 100644 index 0000000..9e1eb24 --- /dev/null +++ b/src/valibot/valibot-from-valibot.ts @@ -0,0 +1,40 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typemap + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Guard from '../guard' +import * as v from 'valibot' +import * as c from './common' + +// prettier-ignore +export type TValibotFromValibot +> = Result +// prettier-ignore +export function ValibotFromValibot(type: Type): TValibotFromValibot { + return (Guard.IsValibot(type) ? type : undefined) as never +} diff --git a/src/valibot/valibot-from-zod.ts b/src/valibot/valibot-from-zod.ts new file mode 100644 index 0000000..7f44417 --- /dev/null +++ b/src/valibot/valibot-from-zod.ts @@ -0,0 +1,46 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typemap + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { type TTypeBoxFromZod, TypeBoxFromZod } from '../typebox/typebox-from-zod' +import { type TValibotFromTypeBox, ValibotFromTypeBox } from './valibot-from-typebox' +import * as t from '@sinclair/typebox' +import * as v from 'valibot' + +// prettier-ignore +export type TValibotFromZod< + Type extends object | string, + Schema extends t.TSchema = TTypeBoxFromZod, + Result extends v.BaseSchema = TValibotFromTypeBox +> = Result + +// prettier-ignore +export function ValibotFromZod(type: Type): TValibotFromZod { + const schema = TypeBoxFromZod(type) + const result = ValibotFromTypeBox(schema) + return result +} diff --git a/src/valibot/valibot.ts b/src/valibot/valibot.ts new file mode 100644 index 0000000..59d6209 --- /dev/null +++ b/src/valibot/valibot.ts @@ -0,0 +1,56 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typemap + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { type TValibotFromSyntax, ValibotFromSyntax } from './valibot-from-syntax' +import { type TValibotFromTypeBox, ValibotFromTypeBox } from './valibot-from-typebox' +import { type TValibotFromValibot, ValibotFromValibot } from './valibot-from-valibot' +import { type TValibotFromZod, ValibotFromZod } from './valibot-from-zod' +import * as Guard from '../guard' +import * as v from 'valibot' +import * as c from './common' + +/** Creates a Valibot type from Syntax or another Type */ +// prettier-ignore +export type TValibot = ( + Guard.TIsSyntax extends true ? TValibotFromSyntax : + Guard.TIsTypeBox extends true ? TValibotFromTypeBox : + Guard.TIsValibot extends true ? TValibotFromValibot : + Guard.TIsZod extends true ? TValibotFromZod : + v.NeverSchema +) +/** Creates a Valibot type from Syntax or another Type */ +// prettier-ignore +export function Valibot>(type: Type): Result { + return ( + Guard.IsSyntax(type) ? ValibotFromSyntax(type) : + Guard.IsTypeBox(type) ? ValibotFromTypeBox(type) : + Guard.IsValibot(type) ? ValibotFromValibot(type) : + Guard.IsZod(type) ? ValibotFromZod(type as any) : + v.never() + ) as never +} diff --git a/src/zod/zod-from-syntax.ts b/src/zod/zod-from-syntax.ts new file mode 100644 index 0000000..7af48fd --- /dev/null +++ b/src/zod/zod-from-syntax.ts @@ -0,0 +1,45 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typemap + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TTypeBoxFromSyntax, TypeBoxFromSyntax } from '../typebox/typebox-from-syntax' +import { ZodFromTypeBox, TZodFromTypeBox } from './zod-from-typebox' +import * as t from '@sinclair/typebox' +import * as z from 'zod' + +// prettier-ignore +export type TZodFromSyntax, + Result extends z.ZodTypeAny | z.ZodEffects = TZodFromTypeBox +> = Result + +// prettier-ignore +export function ZodFromSyntax(type: Type): TZodFromSyntax { + const schema = TypeBoxFromSyntax(type) + const result = ZodFromTypeBox(schema) + return result +} diff --git a/src/zod/zod-from-typebox.ts b/src/zod/zod-from-typebox.ts new file mode 100644 index 0000000..93e735c --- /dev/null +++ b/src/zod/zod-from-typebox.ts @@ -0,0 +1,389 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typemap + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as t from '@sinclair/typebox' +import * as z from 'zod' + +// ------------------------------------------------------------------ +// Constraint +// ------------------------------------------------------------------ +type TConstraint = (input: Input) => Output + +// ------------------------------------------------------------------ +// Any +// ------------------------------------------------------------------ +type TFromAny = Result +function FromAny(_type: t.TAny): z.ZodTypeAny { + return z.any() +} +// ------------------------------------------------------------------ +// Array +// ------------------------------------------------------------------ +type TFromArray>> = Result +function FromArray(type: t.TArray): z.ZodTypeAny { + const constraints: TConstraint>[] = [] + const { minItems, maxItems /* minContains, maxContains, contains */ } = type + if (t.ValueGuard.IsNumber(minItems)) constraints.push((input) => input.min(minItems)) + if (t.ValueGuard.IsNumber(maxItems)) constraints.push((input) => input.max(maxItems)) + const mapped = z.array(FromType(type.items)) + return constraints.reduce((type, constraint) => constraint(type), mapped) +} +// ------------------------------------------------------------------ +// BigInt +// ------------------------------------------------------------------ +type TFromBigInt = Result +function FromBigInt(_type: t.TBigInt): z.ZodTypeAny { + return z.bigint() +} +// ------------------------------------------------------------------ +// Boolean +// ------------------------------------------------------------------ +type TFromBoolean = Result +function FromBoolean(_type: t.TBoolean): z.ZodTypeAny { + return z.boolean() +} +// ------------------------------------------------------------------ +// Date +// ------------------------------------------------------------------ +type TFromDate = Result +function FromDate(_type: t.TDate): z.ZodTypeAny { + return z.date() +} +// ------------------------------------------------------------------ +// Function +// ------------------------------------------------------------------ +// prettier-ignore +type TFromFunction +> = ( + MappedParameters extends [z.ZodTypeAny, ...z.ZodTypeAny[]] | [] + ? z.ZodFunction, TFromType> + : z.ZodNever +) +function FromFunction(type: t.TFunction): z.ZodTypeAny { + const mappedParameters = FromTypes(type.parameters) as [] | [z.ZodTypeAny, ...z.ZodTypeAny[]] + return z.function(z.tuple(mappedParameters), FromType(type.returns)) +} +// ------------------------------------------------------------------ +// Integer +// ------------------------------------------------------------------ +type TFromInteger = Result +function FromInteger(type: t.TInteger): z.ZodTypeAny { + const { exclusiveMaximum, exclusiveMinimum, minimum, maximum, multipleOf } = type + const constraints: TConstraint[] = [(value) => value.int()] + if (t.ValueGuard.IsNumber(exclusiveMinimum)) constraints.push((input) => input.min(exclusiveMinimum + 1)) + if (t.ValueGuard.IsNumber(exclusiveMaximum)) constraints.push((input) => input.min(exclusiveMaximum - 1)) + if (t.ValueGuard.IsNumber(maximum)) constraints.push((input) => input.max(maximum)) + if (t.ValueGuard.IsNumber(minimum)) constraints.push((input) => input.min(minimum)) + if (t.ValueGuard.IsNumber(multipleOf)) constraints.push((input) => input.multipleOf(multipleOf)) + return constraints.reduce((input, constraint) => constraint(input), z.number()) +} +// ------------------------------------------------------------------ +// Intersect +// ------------------------------------------------------------------ +type TFromIntersect = Types extends [infer Left extends t.TSchema, ...infer Right extends t.TSchema[]] + ? TFromIntersect, Result>> + : Result +function FromIntersect(type: t.TIntersect): z.ZodTypeAny { + return type.allOf.reduce((result, left) => { + return z.intersection(FromType(left), result) as never + }, z.unknown()) as never +} +// ------------------------------------------------------------------ +// Literal +// ------------------------------------------------------------------ +type TFromLiteral> = Result +function FromLiteral(type: t.TLiteral): z.ZodTypeAny { + return z.literal(type.const) +} +// ------------------------------------------------------------------ +// Object +// ------------------------------------------------------------------ +type TFromObject< + Properties extends t.TProperties, + Result = z.ZodObject<{ + [Key in keyof Properties]: TFromType + }>, +> = Result +// prettier-ignore +function FromObject(type: t.TObject): z.ZodTypeAny { + const constraints: TConstraint>[] = [] + const { additionalProperties } = type + if (additionalProperties === false) constraints.push((input) => input.strict()) + if (t.KindGuard.IsSchema(additionalProperties)) constraints.push((input) => input.catchall(FromType(additionalProperties))) + const properties = globalThis.Object.getOwnPropertyNames(type.properties).reduce((result, key) => ({ ...result, [key]: FromType(type.properties[key]) }), {}) + return constraints.reduce((type, constraint) => constraint(type), z.object(properties)) +} +// ------------------------------------------------------------------ +// Promise +// ------------------------------------------------------------------ +type TFromPromise>> = Result +function FromPromise(type: t.TPromise): z.ZodTypeAny { + return z.promise(FromType(type.item)) +} +// ------------------------------------------------------------------ +// Record +// ------------------------------------------------------------------ +type TFromRegExp = Result +function FromRegExp(type: t.TRegExp): z.ZodTypeAny { + const constraints: TConstraint[] = [(input) => input.regex(new RegExp(type.source), type.flags)] + const { minLength, maxLength } = type + if (t.ValueGuard.IsNumber(maxLength)) constraints.push((input) => input.max(maxLength)) + if (t.ValueGuard.IsNumber(minLength)) constraints.push((input) => input.min(minLength)) + return constraints.reduce((type, constraint) => constraint(type), z.string()) +} +// ------------------------------------------------------------------ +// Record +// ------------------------------------------------------------------ +type TFromRecord = TFromType extends infer ZodKey extends z.KeySchema ? z.ZodRecord> : z.ZodNever +// prettier-ignore +function FromRecord(type: t.TRecord): z.ZodTypeAny { + const pattern = globalThis.Object.getOwnPropertyNames(type.patternProperties)[0] + const value = FromType(type.patternProperties[pattern]) + return ( + pattern === t.PatternBooleanExact ? z.record(z.boolean(), value) : + pattern === t.PatternNumberExact ? z.record(z.number(), value) : + pattern === t.PatternStringExact ? z.record(z.string(), value) : + z.record(z.string().regex(new RegExp(pattern)), value) + ) +} +// ------------------------------------------------------------------ +// Optional +// ------------------------------------------------------------------ +type TFromOptional>> = Result +function FromOptional(type: t.TOptional): z.ZodTypeAny { + return z.optional(FromType(t.Optional(type, false))) +} +// ------------------------------------------------------------------ +// Readonly +// ------------------------------------------------------------------ +type TFromReadonly>> = Result +function FromReadonly(type: t.TReadonly): z.ZodTypeAny { + return FromType(t.Readonly(type, false)) +} +// ------------------------------------------------------------------ +// Never +// ------------------------------------------------------------------ +type TFromNever = Result +function FromNever(type: t.TNever): z.ZodTypeAny { + return z.never() +} +// ------------------------------------------------------------------ +// Never +// ------------------------------------------------------------------ +type TFromNull = Result +function FromNull(_type: t.TNull): z.ZodTypeAny { + return z.null() +} +// ------------------------------------------------------------------ +// Number +// ------------------------------------------------------------------ +type TFromNumber = Result +function FromNumber(type: t.TNumber): z.ZodTypeAny { + const { exclusiveMaximum, exclusiveMinimum, minimum, maximum, multipleOf } = type + const constraints: TConstraint[] = [] + if (t.ValueGuard.IsNumber(exclusiveMinimum)) constraints.push((input) => input.min(exclusiveMinimum + 1)) + if (t.ValueGuard.IsNumber(exclusiveMaximum)) constraints.push((input) => input.min(exclusiveMaximum - 1)) + if (t.ValueGuard.IsNumber(maximum)) constraints.push((input) => input.max(maximum)) + if (t.ValueGuard.IsNumber(minimum)) constraints.push((input) => input.min(minimum)) + if (t.ValueGuard.IsNumber(multipleOf)) constraints.push((input) => input.multipleOf(multipleOf)) + return constraints.reduce((input, constraint) => constraint(input), z.number()) +} +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +type TFromString = Result +// prettier-ignore +function FromString(type: t.TString): z.ZodTypeAny { + const constraints: TConstraint[] = [] + const { minLength, maxLength, pattern, format } = type + if (t.ValueGuard.IsNumber(maxLength)) constraints.push((input) => input.max(maxLength)) + if (t.ValueGuard.IsNumber(minLength)) constraints.push((input) => input.min(minLength)) + if (t.ValueGuard.IsString(pattern)) constraints.push((input) => input.regex(new RegExp(pattern))) + if (t.ValueGuard.IsString(format)) + constraints.push((input) => + format === 'base64' ? input.base64() : + format === 'base64url' ? input.base64url() : + format === 'cidrv4' ? input.cidr({ version: 'v4' }) : + format === 'cidrv6' ? input.cidr({ version: 'v6' }) : + format === 'cidr' ? input.cidr() : + format === 'cuid' ? input.cuid() : + format === 'cuid2' ? input.cuid2() : + format === 'date' ? input.date() : + format === 'datetime' ? input.datetime() : + format === 'duration' ? input.duration() : + format === 'email' ? input.email() : + format === 'emoji' ? input.emoji() : + format === 'ipv4' ? input.ip({ version: 'v4' }) : + format === 'ipv6' ? input.ip({ version: 'v6' }) : + format === 'ip' ? input.ip() : + format === 'jwt' ? input.jwt() : + format === 'nanoid' ? input.nanoid() : + format === 'time' ? input.time() : + format === 'ulid' ? input.ulid() : + format === 'url' ? input.url() : + format === 'uuid' ? input.uuid() : + input, + ) + return constraints.reduce((type, constraint) => constraint(type), z.string()) +} +// ------------------------------------------------------------------ +// Symbol +// ------------------------------------------------------------------ +type TFromSymbol = Result +function FromSymbol(_type: t.TSymbol): z.ZodTypeAny { + return z.symbol() +} +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTuple> = ( + Mapped extends [z.ZodTypeAny, ...z.ZodTypeAny[]] | [] + ? z.ZodTuple + : z.ZodNever +) +function FromTuple(type: t.TTuple): z.ZodTypeAny { + const mapped = FromTypes(type.items || []) as [] | [z.ZodTypeAny, ...z.ZodTypeAny[]] + return z.tuple(mapped) +} +// ------------------------------------------------------------------ +// Undefined +// ------------------------------------------------------------------ +type TFromUndefined = Result +function FromUndefined(_type: t.TUndefined): z.ZodTypeAny { + return z.undefined() +} +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +type TFromUnion> = ( + Mapped extends z.ZodUnionOptions ? z.ZodUnion : z.ZodNever +) +function FromUnion(_type: t.TUnion): z.ZodTypeAny { + const mapped = FromTypes(_type.anyOf) as [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]] + return mapped.length >= 1 ? z.union(mapped) : z.never() +} +// ------------------------------------------------------------------ +// TUnknown +// ------------------------------------------------------------------ +type TFromUnknown = Result +function FromUnknown(_type: t.TUnknown): z.ZodTypeAny { + return z.unknown() +} +// ------------------------------------------------------------------ +// Void +// ------------------------------------------------------------------ +type TFromVoid = Result +function FromVoid(_type: t.TVoid): z.ZodTypeAny { + return z.void() +} +// ------------------------------------------------------------------ +// Types +// ------------------------------------------------------------------ +type TFromTypes = Types extends [infer Left extends t.TSchema, ...infer Right extends t.TSchema[]] ? TFromTypes]> : Result +function FromTypes(types: t.TSchema[]): z.ZodTypeAny[] { + return types.map((type) => FromType(type)) +} +// ------------------------------------------------------------------ +// Type +// ------------------------------------------------------------------ +// prettier-ignore +type TFromType = ( + Type extends t.TReadonly ? TFromReadonly : + Type extends t.TOptional ? TFromOptional : + Type extends t.TAny ? TFromAny : + Type extends t.TArray ? TFromArray : + Type extends t.TBigInt ? TFromBigInt : + Type extends t.TBoolean ? TFromBoolean : + Type extends t.TDate ? TFromDate : + Type extends t.TFunction ? TFromFunction : + Type extends t.TInteger ? TFromInteger : + Type extends t.TIntersect ? TFromIntersect : + Type extends t.TLiteral ? TFromLiteral : + Type extends t.TNever ? TFromNever : + Type extends t.TNull ? TFromNull : + Type extends t.TNumber ? TFromNumber : + Type extends t.TObject ? TFromObject : + Type extends t.TPromise ? TFromPromise : + Type extends t.TRecord ? TFromRecord : + Type extends t.TRegExp ? TFromRegExp : + Type extends t.TString ? TFromString : + Type extends t.TSymbol ? TFromSymbol : + Type extends t.TTuple ? TFromTuple : + Type extends t.TUndefined ? TFromUndefined : + Type extends t.TUnion ? TFromUnion : + Type extends t.TUnknown ? TFromUnknown : + Type extends t.TVoid ? TFromVoid : + z.ZodNever +) +// prettier-ignore +function FromType(type: t.TSchema): z.ZodTypeAny { + const constraints: TConstraint[] = [] + if(!t.ValueGuard.IsUndefined(type.description)) constraints.push(input => input.describe(type.description!)) + if(!t.ValueGuard.IsUndefined(type.default)) constraints.push(input => input.default(type.default)) + return constraints.reduce((type, constraint) => constraint(type), ( + t.KindGuard.IsReadonly(type) ? FromReadonly(type) : + t.KindGuard.IsOptional(type) ? FromOptional(type) : + t.KindGuard.IsAny(type) ? FromAny(type) : + t.KindGuard.IsArray(type) ? FromArray(type) : + t.KindGuard.IsBigInt(type) ? FromBigInt(type) : + t.KindGuard.IsBoolean(type) ? FromBoolean(type) : + t.KindGuard.IsDate(type) ? FromDate(type) : + t.KindGuard.IsFunction(type) ? FromFunction(type) : + t.KindGuard.IsInteger(type) ? FromInteger(type) : + t.KindGuard.IsIntersect(type) ? FromIntersect(type) : + t.KindGuard.IsLiteral(type) ? FromLiteral(type) : + t.KindGuard.IsNever(type) ? FromNever(type) : + t.KindGuard.IsNull(type) ? FromNull(type) : + t.KindGuard.IsNumber(type) ? FromNumber(type) : + t.KindGuard.IsObject(type) ? FromObject(type) : + t.KindGuard.IsPromise(type) ? FromPromise(type) : + t.KindGuard.IsRegExp(type) ? FromRegExp(type) : + t.KindGuard.IsRecord(type) ? FromRecord(type) : + t.KindGuard.IsString(type) ? FromString(type) : + t.KindGuard.IsSymbol(type) ? FromSymbol(type) : + t.KindGuard.IsTuple(type) ? FromTuple(type) : + t.KindGuard.IsUndefined(type) ? FromUndefined(type) : + t.KindGuard.IsUnion(type) ? FromUnion(type) : + t.KindGuard.IsUnknown(type) ? FromUnknown(type) : + t.KindGuard.IsVoid(type) ? FromVoid(type) : + z.never() + )) +} +// ------------------------------------------------------------------ +// ZodFromTypeBox +// ------------------------------------------------------------------ +// prettier-ignore +export type TZodFromTypeBox = ( + Type extends t.TSchema ? TFromType : z.ZodNever +) +export function ZodFromTypeBox(type: Type): TZodFromTypeBox { + return (t.KindGuard.IsSchema(type) ? FromType(type) : z.never()) as never +} diff --git a/src/zod/zod-from-valibot.ts b/src/zod/zod-from-valibot.ts new file mode 100644 index 0000000..c1e9c2b --- /dev/null +++ b/src/zod/zod-from-valibot.ts @@ -0,0 +1,45 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typemap + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { type TTypeBoxFromValibot, TypeBoxFromValibot } from '../typebox/typebox-from-valibot' +import { type TZodFromTypeBox, ZodFromTypeBox } from './zod-from-typebox' +import * as t from '@sinclair/typebox' +import * as z from 'zod' + +// prettier-ignore +export type TZodFromValibot, + Result extends z.ZodTypeAny | z.ZodEffects = TZodFromTypeBox +> = Result + +// prettier-ignore +export function ZodFromValibot(type: Type): TZodFromValibot { + const schema = TypeBoxFromValibot(type) + const result = ZodFromTypeBox(schema) + return result +} diff --git a/src/typebox/box.ts b/src/zod/zod-from-zod.ts similarity index 73% rename from src/typebox/box.ts rename to src/zod/zod-from-zod.ts index d931335..1b69867 100644 --- a/src/typebox/box.ts +++ b/src/zod/zod-from-zod.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) @@ -26,11 +26,21 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { TSchema, KindGuard } from '@sinclair/typebox' +import * as Guard from '../guard' +import * as z from 'zod' -/** Converts a TypeBox Type to a TypeBox Type */ -export type TBox = Type extends TSchema ? Type : undefined -/** Converts a TypeBox Type to a TypeBox Type */ -export function Box = TBox>(type: Type): Result { - return (KindGuard.IsSchema(type) ? type : undefined) as never +type BaseType = z.ZodTypeAny | z.ZodEffects + +// prettier-ignore +export type TZodFromZod = Result + +// prettier-ignore +export function ZodFromZod(type: Type): TZodFromZod { + return (Guard.IsZod(type) ? type : z.never()) as never } diff --git a/src/box.ts b/src/zod/zod.ts similarity index 54% rename from src/box.ts rename to src/zod/zod.ts index a1c6cef..074ae1b 100644 --- a/src/box.ts +++ b/src/zod/zod.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) @@ -26,33 +26,31 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { TSchema, KindGuard, Unknown, type TUnknown } from '@sinclair/typebox' -import * as TypeBox from './typebox/index' -import * as Valibot from './valibot/index' -import * as Zod from './zod/index' +import { type TZodFromSyntax, ZodFromSyntax } from './zod-from-syntax' +import { type TZodFromTypeBox, ZodFromTypeBox } from './zod-from-typebox' +import { type TZodFromValibot, ZodFromValibot } from './zod-from-valibot' +import { type TZodFromZod, ZodFromZod } from './zod-from-zod' +import * as Guard from '../guard' +import * as z from 'zod' -/** Converts a Zod, Valibot or TypeBox Type to a TypeBox Type */ +/** Creates a Zod type from Syntax or another Type */ // prettier-ignore -export type TBox = ( - TypeBox.TBox extends infer Schema extends TSchema ? Schema : - Valibot.TBox extends infer Schema extends TSchema ? Schema : - Zod.TBox extends infer Schema extends TSchema ? Schema : - TUnknown +export type TZod = ( + Guard.TIsSyntax extends true ? TZodFromSyntax : + Guard.TIsTypeBox extends true ? TZodFromTypeBox : + Guard.TIsValibot extends true ? TZodFromValibot : + Guard.TIsZod extends true ? TZodFromZod : + z.ZodNever ) -/** Converts a Zod, Valibot or TypeBox Type to a TypeBox Type */ + +/** Creates a Zod type from Syntax or another Type */ // prettier-ignore -export function Box(type: Type): TBox { - { - const result = TypeBox.Box(type) - if(KindGuard.IsSchema(result)) return result as never - } - { - const result = Valibot.Box(type) - if(KindGuard.IsSchema(result)) return result as never - } - { - const result = Zod.Box(type) - if(KindGuard.IsSchema(result)) return result as never - } - return Unknown() as never +export function Zod>(type: Type): Result { + return ( + Guard.IsSyntax(type) ? ZodFromSyntax(type) : + Guard.IsTypeBox(type) ? ZodFromTypeBox(type) : + Guard.IsValibot(type) ? ZodFromValibot(type) : + Guard.IsZod(type) ? ZodFromZod(type) : + z.never() + ) as never } diff --git a/task/benchmark/index.ts b/task/benchmark/index.ts index 7583aa2..f071ea0 100644 --- a/task/benchmark/index.ts +++ b/task/benchmark/index.ts @@ -1,6 +1,5 @@ -import { TypeCompiler } from '@sinclair/typebox/compiler' import { Value } from '@sinclair/typebox/value' -import { Box } from '@sinclair/typebox-adapter' +import { Compile, TypeBox } from '@sinclair/typemap' import * as v from 'valibot' import * as z from 'zod' @@ -26,7 +25,7 @@ function zod() { return benchmark('zod', 'zod', () => T.safeParse({ x: 'hello', y: 42, z: true }).success) } function zod_using_value() { - const T = Box( + const T = TypeBox( z.object({ x: z.string(), y: z.number(), @@ -36,14 +35,12 @@ function zod_using_value() { return benchmark('zod', 'typebox:value', () => Value.Check(T, { x: 'hello', y: 42, z: true })) } function zod_using_compiler() { - const T = TypeCompiler.Compile( - Box( - z.object({ - x: z.string(), - y: z.number(), - z: z.boolean(), - }), - ), + const T = Compile( + z.object({ + x: z.string(), + y: z.number(), + z: z.boolean(), + }), ) return benchmark('zod', 'typebox:compile', () => T.Check({ x: 'hello', y: 42, z: true })) } @@ -59,7 +56,7 @@ function valibot() { return benchmark('valibot', 'valibot', () => v.safeParse(T, { x: 'hello', y: 42, z: true }).success) } function valibot_using_value() { - const T = Box( + const T = TypeBox( v.object({ x: v.string(), y: v.number(), @@ -69,14 +66,12 @@ function valibot_using_value() { return benchmark('valibot', 'typebox:value', () => Value.Check(T, { x: 'hello', y: 42, z: true })) } function valibot_using_compiler() { - const T = TypeCompiler.Compile( - Box( - v.object({ - x: v.string(), - y: v.number(), - z: v.boolean(), - }), - ), + const T = Compile( + v.object({ + x: v.string(), + y: v.number(), + z: v.boolean(), + }), ) return benchmark('valibot', 'typebox:compile', () => T.Check({ x: 'hello', y: 42, z: true })) } diff --git a/task/build/cjs/build.ts b/task/build/cjs/build.ts index 6903a9a..332d9fa 100644 --- a/task/build/cjs/build.ts +++ b/task/build/cjs/build.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) diff --git a/task/build/cjs/compile.ts b/task/build/cjs/compile.ts index de4fbef..d6a5e73 100644 --- a/task/build/cjs/compile.ts +++ b/task/build/cjs/compile.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) diff --git a/task/build/esm/build.ts b/task/build/esm/build.ts index 2ea97b7..f5581a7 100644 --- a/task/build/esm/build.ts +++ b/task/build/esm/build.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) diff --git a/task/build/esm/compile.ts b/task/build/esm/compile.ts index c47d1a6..e899c61 100644 --- a/task/build/esm/compile.ts +++ b/task/build/esm/compile.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) diff --git a/task/build/esm/convert-to-esm.ts b/task/build/esm/convert-to-esm.ts index 943f070..b766bd5 100644 --- a/task/build/esm/convert-to-esm.ts +++ b/task/build/esm/convert-to-esm.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) diff --git a/task/build/index.ts b/task/build/index.ts index 189238a..7299941 100644 --- a/task/build/index.ts +++ b/task/build/index.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) diff --git a/task/build/notices/remove-notices.ts b/task/build/notices/remove-notices.ts index 5d3c020..d8b0355 100644 --- a/task/build/notices/remove-notices.ts +++ b/task/build/notices/remove-notices.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) diff --git a/task/build/package/build.ts b/task/build/package/build.ts index 1ddc400..4f6e0e9 100644 --- a/task/build/package/build.ts +++ b/task/build/package/build.ts @@ -32,7 +32,7 @@ import { createPackageJson } from './create-package-json' /** Builds package.json and redirect directories */ export async function build(target: string) { console.log('building...package.json') - const submodules = ['typebox', 'valibot', 'zod'] + const submodules = [] as string[] await createPackageJsonRedirect(target, submodules) await createPackageJson(target, submodules) } diff --git a/task/build/package/create-package-json.ts b/task/build/package/create-package-json.ts index 1eb02ac..ddb61c6 100644 --- a/task/build/package/create-package-json.ts +++ b/task/build/package/create-package-json.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox-adapter +@sinclair/typemap The MIT License (MIT) diff --git a/test/index.ts b/test/index.ts index 8bc91a9..aea66b0 100644 --- a/test/index.ts +++ b/test/index.ts @@ -1,2 +1,4 @@ -import './zod' -import './valibot' +import './typebox-from-zod' +import './typebox-from-valibot' +import './valibot-from-typebox' +import './zod-from-typebox' diff --git a/test/valibot.ts b/test/typebox-from-valibot.ts similarity index 69% rename from test/valibot.ts rename to test/typebox-from-valibot.ts index b0e3052..aedd8eb 100644 --- a/test/valibot.ts +++ b/test/typebox-from-valibot.ts @@ -1,38 +1,38 @@ -import { Box } from '@sinclair/typebox-adapter' -import * as Types from '@sinclair/typebox' +import { TypeBox } from '@sinclair/typemap' import { TypeGuard } from '@sinclair/typebox' import { Assert } from './assert' +import * as t from '@sinclair/typebox' import * as v from 'valibot' -describe('Valibot', () => { +describe('TypeBox from Valibot', () => { // ---------------------------------------------------------------- // Metadata // ---------------------------------------------------------------- it('Should map Description', () => { - const T = Box(v.pipe(v.number(), v.description('a number'))) + const T = TypeBox(v.pipe(v.number(), v.description('a number'))) Assert.IsEqual(T.description, 'a number') }) it('Should map Title', () => { - const T = Box(v.pipe(v.number(), v.title('a number'))) + const T = TypeBox(v.pipe(v.number(), v.title('a number'))) Assert.IsEqual(T.title, 'a number') }) it('Should map Metadata', () => { - const T = Box(v.pipe(v.number(), v.metadata({ x: 1, y: 2 }))) - Assert.IsEqual(T.x, 1) - Assert.IsEqual(T.y, 2) + const T = TypeBox(v.pipe(v.number(), v.metadata({ x: 1, y: 2 }))) + Assert.IsEqual(T.metadata.x, 1) + Assert.IsEqual(T.metadata.y, 2) }) // ---------------------------------------------------------------- // Any // ---------------------------------------------------------------- it('Should map Any', () => { - const T = Box(v.any()) + const T = TypeBox(v.any()) Assert.IsTrue(TypeGuard.IsAny(T)) }) // ---------------------------------------------------------------- // Array // ---------------------------------------------------------------- it('Should map Array', () => { - const T = Box(v.array(v.number())) + const T = TypeBox(v.array(v.number())) Assert.IsTrue(TypeGuard.IsArray(T)) Assert.IsTrue(TypeGuard.IsNumber(T.items)) }) @@ -40,26 +40,26 @@ describe('Valibot', () => { // BigInt // ---------------------------------------------------------------- it('Should map BigInt', () => { - const T = Box(v.bigint()) + const T = TypeBox(v.bigint()) Assert.IsTrue(TypeGuard.IsBigInt(T)) }) // ---------------------------------------------------------------- // Date // ---------------------------------------------------------------- it('Should map Date', () => { - const T = Box(v.date()) + const T = TypeBox(v.date()) Assert.IsTrue(TypeGuard.IsDate(T)) }) // ---------------------------------------------------------------- // Effects // ---------------------------------------------------------------- // it('Should map Effects (Transform)', () => { - // const T = Box(v.number().transform(x => x)) + // const T = TypeBox(v.number().transform(x => x)) // Assert.IsTrue(TypeGuard.IsNumber(T)) // Assert.IsTrue(TypeGuard.IsTransform(T)) // }) // it('Should map Effects (Refine)', () => { - // const T = Box(v.number().refine(x => true)) + // const T = TypeBox(v.number().refine(x => true)) // Assert.IsTrue(TypeGuard.IsNumber(T)) // Assert.IsTrue(TypeGuard.IsTransform(T)) // }) @@ -67,17 +67,17 @@ describe('Valibot', () => { // Literal // ---------------------------------------------------------------- it('Should map Literal (Number)', () => { - const T = Box(v.literal(42)) + const T = TypeBox(v.literal(42)) Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 42) }) it('Should map Literal (String)', () => { - const T = Box(v.literal('hello')) + const T = TypeBox(v.literal('hello')) Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 'hello') }) it('Should map Literal (Boolean)', () => { - const T = Box(v.literal(true)) + const T = TypeBox(v.literal(true)) Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, true) }) @@ -85,7 +85,7 @@ describe('Valibot', () => { // Nullable // ---------------------------------------------------------------- it('Should map Nullable', () => { - const T = Box(v.nullable(v.number())) + const T = TypeBox(v.nullable(v.number())) Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsTrue(TypeGuard.IsNull(T.anyOf[0])) Assert.IsTrue(TypeGuard.IsNumber(T.anyOf[1])) @@ -94,7 +94,7 @@ describe('Valibot', () => { // Object // ---------------------------------------------------------------- it('Should map Object', () => { - const T = Box( + const T = TypeBox( v.object({ x: v.number(), y: v.string(), @@ -105,7 +105,7 @@ describe('Valibot', () => { Assert.IsTrue(TypeGuard.IsString(T.properties.y)) }) it('Should map Object (Strict)', () => { - const T = Box( + const T = TypeBox( v.strictObject({ x: v.number(), y: v.string(), @@ -120,7 +120,7 @@ describe('Valibot', () => { // Optional // ---------------------------------------------------------------- it('Should map Optional', () => { - const T = Box( + const T = TypeBox( v.object({ x: v.optional(v.number()), y: v.optional(v.number()), @@ -133,7 +133,7 @@ describe('Valibot', () => { Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) }) it('Should map Optional (Partial)', () => { - const T = Box( + const T = TypeBox( v.partial( v.object({ x: v.number(), @@ -151,19 +151,19 @@ describe('Valibot', () => { // Promise // ---------------------------------------------------------------- it('Should map Promise', () => { - const T = Box(v.promise()) - Assert.IsEqual(T[Types.Kind], 'ValibotPromise') + const T = TypeBox(v.promise()) + Assert.IsEqual(T[t.Kind], 'ValibotPromise') }) // ---------------------------------------------------------------- // Record // ---------------------------------------------------------------- it('Should map Record (String Key)', () => { - const T = Box(v.record(v.string(), v.number())) + const T = TypeBox(v.record(v.string(), v.number())) Assert.IsTrue(TypeGuard.IsRecord(T)) - Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[Types.PatternStringExact])) + Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[t.PatternStringExact])) }) it('Should map Record (Finite Union)', () => { - const T = Box(v.record(v.union([v.literal('x'), v.literal('y')]), v.number())) + const T = TypeBox(v.record(v.union([v.literal('x'), v.literal('y')]), v.number())) Assert.IsTrue(TypeGuard.IsObject(T)) Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) @@ -172,35 +172,35 @@ describe('Valibot', () => { // Never // ---------------------------------------------------------------- it('Should map Never', () => { - const T = Box(v.never()) + const T = TypeBox(v.never()) Assert.IsTrue(TypeGuard.IsNever(T)) }) // ---------------------------------------------------------------- // Null // ---------------------------------------------------------------- it('Should map Null', () => { - const T = Box(v.null()) + const T = TypeBox(v.null()) Assert.IsTrue(TypeGuard.IsNull(T)) }) // ---------------------------------------------------------------- // Number // ---------------------------------------------------------------- it('Should map Number', () => { - const T = Box(v.number()) + const T = TypeBox(v.number()) Assert.IsTrue(TypeGuard.IsNumber(T)) }) it('Should map Number (Integer)', () => { - const T = Box(v.pipe(v.number(), v.integer())) + const T = TypeBox(v.pipe(v.number(), v.integer())) Assert.IsTrue(TypeGuard.IsNumber(T)) Assert.IsEqual(T.multipleOf, 1) }) it('Should map Number (Minimum)', () => { - const T = Box(v.pipe(v.number(), v.minValue(100))) + const T = TypeBox(v.pipe(v.number(), v.minValue(100))) Assert.IsTrue(TypeGuard.IsNumber(T)) Assert.IsEqual(T.minimum, 100) }) it('Should map Number (Maximum)', () => { - const T = Box(v.pipe(v.number(), v.maxValue(100))) + const T = TypeBox(v.pipe(v.number(), v.maxValue(100))) Assert.IsTrue(TypeGuard.IsNumber(T)) Assert.IsEqual(T.maximum, 100) }) @@ -208,194 +208,203 @@ describe('Valibot', () => { // String // ---------------------------------------------------------------- it('Should map String', () => { - const T = Box(v.string()) + const T = TypeBox(v.string()) Assert.IsTrue(TypeGuard.IsString(T)) }) it('Should map String (Base64)', () => { - const T = Box(v.pipe(v.string(), v.base64())) + const T = TypeBox(v.pipe(v.string(), v.base64())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:base64') + Assert.IsEqual(T.format, 'base64') }) it('Should map String (Bic)', () => { - const T = Box(v.pipe(v.string(), v.bic())) + const T = TypeBox(v.pipe(v.string(), v.bic())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:bic') + Assert.IsEqual(T.format, 'bic') }) it('Should map String (CreditCard)', () => { - const T = Box(v.pipe(v.string(), v.creditCard())) + const T = TypeBox(v.pipe(v.string(), v.creditCard())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:credit_card') + Assert.IsEqual(T.format, 'credit_card') }) it('Should map String (Cuid2)', () => { - const T = Box(v.pipe(v.string(), v.cuid2())) + const T = TypeBox(v.pipe(v.string(), v.cuid2())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:cuid2') + Assert.IsEqual(T.format, 'cuid2') }) it('Should map String (Decimal)', () => { - const T = Box(v.pipe(v.string(), v.decimal())) + const T = TypeBox(v.pipe(v.string(), v.decimal())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:decimal') + Assert.IsEqual(T.format, 'decimal') }) it('Should map String (Digits)', () => { - const T = Box(v.pipe(v.string(), v.digits())) + const T = TypeBox(v.pipe(v.string(), v.digits())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:digits') + Assert.IsEqual(T.format, 'digits') }) it('Should map String (Email)', () => { - const T = Box(v.pipe(v.string(), v.email())) + const T = TypeBox(v.pipe(v.string(), v.email())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:email') + Assert.IsEqual(T.format, 'email') }) it('Should map String (Emoji)', () => { - const T = Box(v.pipe(v.string(), v.emoji())) + const T = TypeBox(v.pipe(v.string(), v.emoji())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:emoji') + Assert.IsEqual(T.format, 'emoji') }) it('Should map String (Empty)', () => { - const T = Box(v.pipe(v.string(), v.empty())) + const T = TypeBox(v.pipe(v.string(), v.empty())) Assert.IsTrue(TypeGuard.IsString(T)) Assert.IsEqual(T.maxLength, 0) }) it('Should map String (EndsWith)', () => { - const T = Box(v.pipe(v.string(), v.endsWith('hello'))) + const T = TypeBox(v.pipe(v.string(), v.endsWith('hello'))) Assert.IsTrue(TypeGuard.IsString(T)) Assert.IsEqual(T.pattern, 'hello$') }) it('Should map String (Includes)', () => { - const T = Box(v.pipe(v.string(), v.includes('hello'))) + const T = TypeBox(v.pipe(v.string(), v.includes('hello'))) Assert.IsTrue(TypeGuard.IsString(T)) Assert.IsEqual(T.pattern, 'hello') }) it('Should map String (Ipv4)', () => { - const T = Box(v.pipe(v.string(), v.ipv4())) + const T = TypeBox(v.pipe(v.string(), v.ipv4())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:ipv4') + Assert.IsEqual(T.format, 'ipv4') }) it('Should map String (IpV6)', () => { - const T = Box(v.pipe(v.string(), v.ipv6())) + const T = TypeBox(v.pipe(v.string(), v.ipv6())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:ipv6') + Assert.IsEqual(T.format, 'ipv6') }) it('Should map String (Ip)', () => { - const T = Box(v.pipe(v.string(), v.ip())) + const T = TypeBox(v.pipe(v.string(), v.ip())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:ip') + Assert.IsEqual(T.format, 'ip') }) it('Should map String (IsoDate)', () => { - const T = Box(v.pipe(v.string(), v.isoDate())) + const T = TypeBox(v.pipe(v.string(), v.isoDate())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:iso_date') + Assert.IsEqual(T.format, 'iso_date') }) it('Should map String (IsoDateTime)', () => { - const T = Box(v.pipe(v.string(), v.isoDateTime())) + const T = TypeBox(v.pipe(v.string(), v.isoDateTime())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:iso_date_time') + Assert.IsEqual(T.format, 'iso_date_time') }) it('Should map String (IsoTime)', () => { - const T = Box(v.pipe(v.string(), v.isoTime())) + const T = TypeBox(v.pipe(v.string(), v.isoTime())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:iso_time') + Assert.IsEqual(T.format, 'iso_time') }) it('Should map String (IsoTimeSecond)', () => { - const T = Box(v.pipe(v.string(), v.isoTimeSecond())) + const T = TypeBox(v.pipe(v.string(), v.isoTimeSecond())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:iso_time_second') + Assert.IsEqual(T.format, 'iso_time_second') }) it('Should map String (IsoTimestamp)', () => { - const T = Box(v.pipe(v.string(), v.isoTimestamp())) + const T = TypeBox(v.pipe(v.string(), v.isoTimestamp())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:iso_timestamp') + Assert.IsEqual(T.format, 'iso_timestamp') }) it('Should map String (IsoWeek)', () => { - const T = Box(v.pipe(v.string(), v.isoWeek())) + const T = TypeBox(v.pipe(v.string(), v.isoWeek())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:iso_week') + Assert.IsEqual(T.format, 'iso_week') }) it('Should map String (Length)', () => { - const T = Box(v.pipe(v.string(), v.length(100))) + const T = TypeBox(v.pipe(v.string(), v.length(100))) Assert.IsTrue(TypeGuard.IsString(T)) Assert.IsEqual(T.maxLength, 100) Assert.IsEqual(T.minLength, 100) }) it('Should map String (Mac48)', () => { - const T = Box(v.pipe(v.string(), v.mac48())) + const T = TypeBox(v.pipe(v.string(), v.mac48())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:mac48') + Assert.IsEqual(T.format, 'mac48') }) it('Should map String (Mac64)', () => { - const T = Box(v.pipe(v.string(), v.mac64())) + const T = TypeBox(v.pipe(v.string(), v.mac64())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:mac64') + Assert.IsEqual(T.format, 'mac64') }) it('Should map String (Mac)', () => { - const T = Box(v.pipe(v.string(), v.mac())) + const T = TypeBox(v.pipe(v.string(), v.mac())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:mac') + Assert.IsEqual(T.format, 'mac') }) it('Should map String (MaxLength)', () => { - const T = Box(v.pipe(v.string(), v.maxLength(100))) + const T = TypeBox(v.pipe(v.string(), v.maxLength(100))) Assert.IsTrue(TypeGuard.IsString(T)) Assert.IsEqual(T.maxLength, 100) }) it('Should map String (MinLength)', () => { - const T = Box(v.pipe(v.string(), v.minLength(100))) + const T = TypeBox(v.pipe(v.string(), v.minLength(100))) Assert.IsTrue(TypeGuard.IsString(T)) Assert.IsEqual(T.minLength, 100) }) it('Should map String (Nanoid)', () => { - const T = Box(v.pipe(v.string(), v.nanoid())) + const T = TypeBox(v.pipe(v.string(), v.nanoid())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:nanoid') + Assert.IsEqual(T.format, 'nanoid') }) it('Should map String (Octal)', () => { - const T = Box(v.pipe(v.string(), v.octal())) + const T = TypeBox(v.pipe(v.string(), v.octal())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:octal') + Assert.IsEqual(T.format, 'octal') }) it('Should map String (RegExp)', () => { - const T = Box(v.pipe(v.string(), v.regex(/abc/))) + const T = TypeBox(v.pipe(v.string(), v.regex(/abc/))) Assert.IsTrue(TypeGuard.IsString(T)) Assert.IsEqual(T.pattern, 'abc') }) it('Should map String (StartsWith)', () => { - const T = Box(v.pipe(v.string(), v.startsWith('hello'))) + const T = TypeBox(v.pipe(v.string(), v.startsWith('hello'))) Assert.IsTrue(TypeGuard.IsString(T)) Assert.IsEqual(T.pattern, '^hello') }) it('Should map String (Ulid)', () => { - const T = Box(v.pipe(v.string(), v.ulid())) + const T = TypeBox(v.pipe(v.string(), v.ulid())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:ulid') + Assert.IsEqual(T.format, 'ulid') }) it('Should map String (Url)', () => { - const T = Box(v.pipe(v.string(), v.url())) + const T = TypeBox(v.pipe(v.string(), v.url())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:url') + Assert.IsEqual(T.format, 'url') }) it('Should map String (Uuid)', () => { - const T = Box(v.pipe(v.string(), v.uuid())) + const T = TypeBox(v.pipe(v.string(), v.uuid())) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'valibot:uuid') + Assert.IsEqual(T.format, 'uuid') }) // ---------------------------------------------------------------- // Symbol // ---------------------------------------------------------------- it('Should map Symbol', () => { - const T = Box(v.symbol()) + const T = TypeBox(v.symbol()) Assert.IsTrue(TypeGuard.IsSymbol(T)) }) // ---------------------------------------------------------------- + // Tuple + // ---------------------------------------------------------------- + it('Should map Tuple', () => { + const T = TypeBox(v.tuple([v.number(), v.string()])) + Assert.IsTrue(TypeGuard.IsTuple(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) + Assert.IsTrue(TypeGuard.IsString(T.items![1])) + }) + // ---------------------------------------------------------------- // Undefined // ---------------------------------------------------------------- it('Should map Undefined', () => { - const T = Box(v.undefined()) + const T = TypeBox(v.undefined()) Assert.IsTrue(TypeGuard.IsUndefined(T)) }) // ---------------------------------------------------------------- // Union // ---------------------------------------------------------------- it('Should map Union', () => { - const T = Box(v.union([v.string(), v.boolean()])) + const T = TypeBox(v.union([v.string(), v.boolean()])) Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsTrue(TypeGuard.IsString(T.anyOf[0])) Assert.IsTrue(TypeGuard.IsBoolean(T.anyOf[1])) @@ -404,14 +413,14 @@ describe('Valibot', () => { // Unknown // ---------------------------------------------------------------- it('Should map Unknown', () => { - const T = Box(v.unknown()) + const T = TypeBox(v.unknown()) Assert.IsTrue(TypeGuard.IsUnknown(T)) }) // ---------------------------------------------------------------- // Void // ---------------------------------------------------------------- it('Should map Void', () => { - const T = Box(v.void()) + const T = TypeBox(v.void()) Assert.IsTrue(TypeGuard.IsVoid(T)) }) }) diff --git a/test/zod.ts b/test/typebox-from-zod.ts similarity index 75% rename from test/zod.ts rename to test/typebox-from-zod.ts index e00ec2b..ff76ec3 100644 --- a/test/zod.ts +++ b/test/typebox-from-zod.ts @@ -1,33 +1,33 @@ -import { Box } from '@sinclair/typebox-adapter' -import * as Types from '@sinclair/typebox' +import { TypeBox } from '@sinclair/typemap' import { TypeGuard } from '@sinclair/typebox' import { Assert } from './assert' +import * as t from '@sinclair/typebox' import * as z from 'zod' -describe('Zod', () => { +describe('TypeBox From Zod', () => { // ---------------------------------------------------------------- // Metadata // ---------------------------------------------------------------- it('Should map Description', () => { - const T = Box(z.number().describe('a number')) + const T = TypeBox(z.number().describe('a number')) Assert.IsEqual(T.description, 'a number') }) it('Should map Default', () => { - const T = Box(z.number().default(12345)) + const T = TypeBox(z.number().default(12345)) Assert.IsEqual(T.default, 12345) }) // ---------------------------------------------------------------- // Any // ---------------------------------------------------------------- it('Should map Any', () => { - const T = Box(z.any()) + const T = TypeBox(z.any()) Assert.IsTrue(TypeGuard.IsAny(T)) }) // ---------------------------------------------------------------- // Array // ---------------------------------------------------------------- it('Should map Array', () => { - const T = Box(z.array(z.number())) + const T = TypeBox(z.array(z.number())) Assert.IsTrue(TypeGuard.IsArray(T)) Assert.IsTrue(TypeGuard.IsNumber(T.items)) }) @@ -35,14 +35,14 @@ describe('Zod', () => { // BigInt // ---------------------------------------------------------------- it('Should map BigInt', () => { - const T = Box(z.bigint()) + const T = TypeBox(z.bigint()) Assert.IsTrue(TypeGuard.IsBigInt(T)) }) // ---------------------------------------------------------------- // Date // ---------------------------------------------------------------- it('Should map Date', () => { - const T = Box(z.date()) + const T = TypeBox(z.date()) Assert.IsTrue(TypeGuard.IsDate(T)) }) // ---------------------------------------------------------------- @@ -52,7 +52,7 @@ describe('Zod', () => { const A = z.object({ type: z.literal('A') }) const B = z.object({ type: z.literal('B') }) const C = z.object({ type: z.literal('C') }) - const T = Box(z.discriminatedUnion('type', [A, B, C])) + const T = TypeBox(z.discriminatedUnion('type', [A, B, C])) Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsEqual(T.discriminator, 'type') Assert.IsTrue(T.anyOf[0].properties.type.const === 'A') @@ -63,30 +63,40 @@ describe('Zod', () => { // Effects // ---------------------------------------------------------------- it('Should map Effects (Transform)', () => { - const T = Box(z.number().transform((x) => x)) + const T = TypeBox(z.number().transform((x) => x)) Assert.IsTrue(TypeGuard.IsNumber(T)) Assert.IsTrue(TypeGuard.IsTransform(T)) }) it('Should map Effects (Refine)', () => { - const T = Box(z.number().refine((x) => true)) + const T = TypeBox(z.number().refine((x) => true)) Assert.IsTrue(TypeGuard.IsNumber(T)) Assert.IsTrue(TypeGuard.IsTransform(T)) }) // ---------------------------------------------------------------- + // Enum + // ---------------------------------------------------------------- + it('Should map Enum', () => { + const T = TypeBox(z.enum(['a', 'b', 'c'])) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'a') + Assert.IsEqual(T.anyOf[1].const, 'b') + Assert.IsEqual(T.anyOf[2].const, 'c') + }) + // ---------------------------------------------------------------- // Literal // ---------------------------------------------------------------- it('Should map Literal (Number)', () => { - const T = Box(z.literal(42)) + const T = TypeBox(z.literal(42)) Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 42) }) it('Should map Literal (String)', () => { - const T = Box(z.literal('hello')) + const T = TypeBox(z.literal('hello')) Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 'hello') }) it('Should map Literal (Boolean)', () => { - const T = Box(z.literal(true)) + const T = TypeBox(z.literal(true)) Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, true) }) @@ -94,7 +104,7 @@ describe('Zod', () => { // Nullable // ---------------------------------------------------------------- it('Should map Nullable', () => { - const T = Box(z.number().nullable()) + const T = TypeBox(z.number().nullable()) Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsTrue(TypeGuard.IsNull(T.anyOf[0])) Assert.IsTrue(TypeGuard.IsNumber(T.anyOf[1])) @@ -103,7 +113,7 @@ describe('Zod', () => { // Object // ---------------------------------------------------------------- it('Should map Object', () => { - const T = Box( + const T = TypeBox( z.object({ x: z.number(), y: z.string(), @@ -114,7 +124,7 @@ describe('Zod', () => { Assert.IsTrue(TypeGuard.IsString(T.properties.y)) }) it('Should map Object (Strict)', () => { - const T = Box( + const T = TypeBox( z .object({ x: z.number(), @@ -131,7 +141,7 @@ describe('Zod', () => { // Optional // ---------------------------------------------------------------- it('Should map Optional', () => { - const T = Box( + const T = TypeBox( z.object({ x: z.number().optional(), y: z.number().optional(), @@ -144,7 +154,7 @@ describe('Zod', () => { Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) }) it('Should map Optional (Readonly)', () => { - const T = Box( + const T = TypeBox( z.object({ x: z.number().optional().readonly(), y: z.number().optional().readonly(), @@ -159,7 +169,7 @@ describe('Zod', () => { Assert.IsTrue(TypeGuard.IsReadonly(T.properties.y)) }) it('Should map Optional (Partial)', () => { - const T = Box( + const T = TypeBox( z .object({ x: z.number(), @@ -177,7 +187,7 @@ describe('Zod', () => { // Promise // ---------------------------------------------------------------- it('Should map Promise', () => { - const T = Box(z.promise(z.number())) + const T = TypeBox(z.promise(z.number())) Assert.IsTrue(TypeGuard.IsPromise(T)) Assert.IsTrue(TypeGuard.IsNumber(T.item)) }) @@ -185,7 +195,7 @@ describe('Zod', () => { // Readonly // ---------------------------------------------------------------- it('Should map Readonly', () => { - const T = Box( + const T = TypeBox( z.object({ x: z.number().readonly(), y: z.number().readonly(), @@ -198,7 +208,7 @@ describe('Zod', () => { Assert.IsTrue(TypeGuard.IsReadonly(T.properties.y)) }) it('Should map Readonly (Optional)', () => { - const T = Box( + const T = TypeBox( z.object({ x: z.number().readonly().optional(), y: z.number().readonly().optional(), @@ -216,22 +226,22 @@ describe('Zod', () => { // Record // ---------------------------------------------------------------- it('Should map Record (Key Implicit)', () => { - const T = Box(z.record(z.number())) + const T = TypeBox(z.record(z.number())) Assert.IsTrue(TypeGuard.IsRecord(T)) - Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[Types.PatternStringExact])) + Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[t.PatternStringExact])) }) it('Should map Record (Number Key)', () => { - const T = Box(z.record(z.number(), z.number())) + const T = TypeBox(z.record(z.number(), z.number())) Assert.IsTrue(TypeGuard.IsRecord(T)) - Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[Types.PatternNumberExact])) + Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[t.PatternNumberExact])) }) it('Should map Record (String Key)', () => { - const T = Box(z.record(z.string(), z.number())) + const T = TypeBox(z.record(z.string(), z.number())) Assert.IsTrue(TypeGuard.IsRecord(T)) - Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[Types.PatternStringExact])) + Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[t.PatternStringExact])) }) it('Should map Record (Finite Union)', () => { - const T = Box(z.record(z.union([z.literal('x'), z.literal('y')]), z.number())) + const T = TypeBox(z.record(z.union([z.literal('x'), z.literal('y')]), z.number())) Assert.IsTrue(TypeGuard.IsObject(T)) Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) @@ -240,35 +250,35 @@ describe('Zod', () => { // Never // ---------------------------------------------------------------- it('Should map Never', () => { - const T = Box(z.never()) + const T = TypeBox(z.never()) Assert.IsTrue(TypeGuard.IsNever(T)) }) // ---------------------------------------------------------------- // Null // ---------------------------------------------------------------- it('Should map Null', () => { - const T = Box(z.null()) + const T = TypeBox(z.null()) Assert.IsTrue(TypeGuard.IsNull(T)) }) // ---------------------------------------------------------------- // Number // ---------------------------------------------------------------- it('Should map Number', () => { - const T = Box(z.number()) + const T = TypeBox(z.number()) Assert.IsTrue(TypeGuard.IsNumber(T)) }) it('Should map Number (Integer)', () => { - const T = Box(z.number().int()) + const T = TypeBox(z.number().int()) Assert.IsTrue(TypeGuard.IsNumber(T)) Assert.IsEqual(T.multipleOf, 1) }) it('Should map Number (Minimum)', () => { - const T = Box(z.number().min(100)) + const T = TypeBox(z.number().min(100)) Assert.IsTrue(TypeGuard.IsNumber(T)) Assert.IsEqual(T.minimum, 100) }) it('Should map Number (Maximum)', () => { - const T = Box(z.number().max(100)) + const T = TypeBox(z.number().max(100)) Assert.IsTrue(TypeGuard.IsNumber(T)) Assert.IsEqual(T.maximum, 100) }) @@ -276,153 +286,153 @@ describe('Zod', () => { // String // ---------------------------------------------------------------- it('Should map String', () => { - const T = Box(z.string()) + const T = TypeBox(z.string()) Assert.IsTrue(TypeGuard.IsString(T)) }) it('Should map String (Base64)', () => { - const T = Box(z.string().base64()) + const T = TypeBox(z.string().base64()) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:base64') + Assert.IsEqual(T.format, 'base64') }) it('Should map String (Base64Url)', () => { - const T = Box(z.string().base64url()) + const T = TypeBox(z.string().base64url()) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:base64url') + Assert.IsEqual(T.format, 'base64url') }) it('Should map String (Cidr V4)', () => { - const T = Box(z.string().cidr({ version: 'v4' })) + const T = TypeBox(z.string().cidr({ version: 'v4' })) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:cidrv4') + Assert.IsEqual(T.format, 'cidrv4') }) it('Should map String (Cidr v6)', () => { - const T = Box(z.string().cidr({ version: 'v6' })) + const T = TypeBox(z.string().cidr({ version: 'v6' })) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:cidrv6') + Assert.IsEqual(T.format, 'cidrv6') }) it('Should map String (Cidr)', () => { - const T = Box(z.string().cidr()) + const T = TypeBox(z.string().cidr()) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:cidr') + Assert.IsEqual(T.format, 'cidr') }) it('Should map String (Cuid)', () => { - const T = Box(z.string().cuid()) + const T = TypeBox(z.string().cuid()) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:cuid') + Assert.IsEqual(T.format, 'cuid') }) it('Should map String (Cuid2)', () => { - const T = Box(z.string().cuid2()) + const T = TypeBox(z.string().cuid2()) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:cuid2') + Assert.IsEqual(T.format, 'cuid2') }) it('Should map String (Ulid)', () => { - const T = Box(z.string().ulid()) + const T = TypeBox(z.string().ulid()) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:ulid') + Assert.IsEqual(T.format, 'ulid') }) it('Should map String (Email)', () => { - const T = Box(z.string().email()) + const T = TypeBox(z.string().email()) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:email') + Assert.IsEqual(T.format, 'email') }) it('Should map String (Emoji)', () => { - const T = Box(z.string().emoji()) + const T = TypeBox(z.string().emoji()) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:emoji') + Assert.IsEqual(T.format, 'emoji') }) it('Should map String (EndsWith)', () => { - const T = Box(z.string().endsWith('hello')) + const T = TypeBox(z.string().endsWith('hello')) Assert.IsTrue(TypeGuard.IsString(T)) Assert.IsEqual(T.pattern, 'hello$') }) it('Should map String (Includes)', () => { - const T = Box(z.string().includes('hello')) + const T = TypeBox(z.string().includes('hello')) Assert.IsTrue(TypeGuard.IsString(T)) Assert.IsEqual(T.pattern, 'hello') }) it('Should map String (IpV4)', () => { - const T = Box(z.string().ip({ version: 'v4' })) + const T = TypeBox(z.string().ip({ version: 'v4' })) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:ipv4') + Assert.IsEqual(T.format, 'ipv4') }) it('Should map String (IpV6)', () => { - const T = Box(z.string().ip({ version: 'v6' })) + const T = TypeBox(z.string().ip({ version: 'v6' })) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:ipv6') + Assert.IsEqual(T.format, 'ipv6') }) it('Should map String (Ip)', () => { - const T = Box(z.string().ip()) + const T = TypeBox(z.string().ip()) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:ip') + Assert.IsEqual(T.format, 'ip') }) it('Should map String (Jwt)', () => { - const T = Box(z.string().jwt()) + const T = TypeBox(z.string().jwt()) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:jwt') + Assert.IsEqual(T.format, 'jwt') }) it('Should map String (Length)', () => { - const T = Box(z.string().length(100)) + const T = TypeBox(z.string().length(100)) Assert.IsTrue(TypeGuard.IsString(T)) Assert.IsEqual(T.minLength, 100) Assert.IsEqual(T.maxLength, 100) }) it('Should map String (Min)', () => { - const T = Box(z.string().min(100)) + const T = TypeBox(z.string().min(100)) Assert.IsTrue(TypeGuard.IsString(T)) Assert.IsEqual(T.minLength, 100) }) it('Should map String (Max)', () => { - const T = Box(z.string().max(100)) + const T = TypeBox(z.string().max(100)) Assert.IsTrue(TypeGuard.IsString(T)) Assert.IsEqual(T.maxLength, 100) }) it('Should map String (Nanoid)', () => { - const T = Box(z.string().nanoid()) + const T = TypeBox(z.string().nanoid()) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:nanoid') + Assert.IsEqual(T.format, 'nanoid') }) it('Should map String (RegExp)', () => { - const T = Box(z.string().regex(/abc/)) + const T = TypeBox(z.string().regex(/abc/)) Assert.IsTrue(TypeGuard.IsString(T)) Assert.IsEqual(T.pattern, 'abc') }) it('Should map String (StartsWith)', () => { - const T = Box(z.string().startsWith('hello')) + const T = TypeBox(z.string().startsWith('hello')) Assert.IsTrue(TypeGuard.IsString(T)) Assert.IsEqual(T.pattern, '^hello') }) it('Should map String (Time)', () => { - const T = Box(z.string().time()) + const T = TypeBox(z.string().time()) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:time') + Assert.IsEqual(T.format, 'time') }) it('Should map String (Ulid)', () => { - const T = Box(z.string().ulid()) + const T = TypeBox(z.string().ulid()) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:ulid') + Assert.IsEqual(T.format, 'ulid') }) it('Should map String (Url)', () => { - const T = Box(z.string().url()) + const T = TypeBox(z.string().url()) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:url') + Assert.IsEqual(T.format, 'url') }) it('Should map String (Uuid)', () => { - const T = Box(z.string().uuid()) + const T = TypeBox(z.string().uuid()) Assert.IsTrue(TypeGuard.IsString(T)) - Assert.IsEqual(T.format, 'zod:uuid') + Assert.IsEqual(T.format, 'uuid') }) // ---------------------------------------------------------------- // Symbol // ---------------------------------------------------------------- it('Should map Symbol', () => { - const T = Box(z.symbol()) + const T = TypeBox(z.symbol()) Assert.IsTrue(TypeGuard.IsSymbol(T)) }) // ---------------------------------------------------------------- // Tuple // ---------------------------------------------------------------- it('Should map Tuple', () => { - const T = Box(z.tuple([z.number(), z.string()])) + const T = TypeBox(z.tuple([z.number(), z.string()])) Assert.IsTrue(TypeGuard.IsTuple(T)) Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) Assert.IsTrue(TypeGuard.IsString(T.items![1])) @@ -431,14 +441,14 @@ describe('Zod', () => { // Undefined // ---------------------------------------------------------------- it('Should map Undefined', () => { - const T = Box(z.undefined()) + const T = TypeBox(z.undefined()) Assert.IsTrue(TypeGuard.IsUndefined(T)) }) // ---------------------------------------------------------------- // Union // ---------------------------------------------------------------- it('Should map Union', () => { - const T = Box(z.union([z.string(), z.boolean()])) + const T = TypeBox(z.union([z.string(), z.boolean()])) Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsTrue(TypeGuard.IsString(T.anyOf[0])) Assert.IsTrue(TypeGuard.IsBoolean(T.anyOf[1])) @@ -447,14 +457,14 @@ describe('Zod', () => { // Unknown // ---------------------------------------------------------------- it('Should map Unknown', () => { - const T = Box(z.unknown()) + const T = TypeBox(z.unknown()) Assert.IsTrue(TypeGuard.IsUnknown(T)) }) // ---------------------------------------------------------------- // Void // ---------------------------------------------------------------- it('Should map Void', () => { - const T = Box(z.void()) + const T = TypeBox(z.void()) Assert.IsTrue(TypeGuard.IsVoid(T)) }) }) diff --git a/test/valibot-from-typebox.ts b/test/valibot-from-typebox.ts new file mode 100644 index 0000000..5b009e1 --- /dev/null +++ b/test/valibot-from-typebox.ts @@ -0,0 +1,426 @@ +import { TypeBox, Valibot } from '@sinclair/typemap' +import { TypeGuard } from '@sinclair/typebox' +import { Assert } from './assert' +import * as t from '@sinclair/typebox' +import * as v from 'valibot' + +describe('Valibot from TypeBox', () => { + // ---------------------------------------------------------------- + // Metadata + // ---------------------------------------------------------------- + it('Should map Description', () => { + const T = TypeBox(Valibot(t.Number({ description: 'a number' }))) + Assert.IsEqual(T.description, 'a number') + }) + it('Should map Title', () => { + const T = TypeBox(Valibot(t.Number({ title: 'a number' }))) + Assert.IsEqual(T.title, 'a number') + }) + it('Should map Metadata', () => { + const T = TypeBox(Valibot(t.Number({ metadata: { x: 1, y: 2 } }))) + Assert.IsEqual(T.metadata.x, 1) + Assert.IsEqual(T.metadata.y, 2) + }) + // ---------------------------------------------------------------- + // Any + // ---------------------------------------------------------------- + it('Should map Any', () => { + const T = TypeBox(Valibot(t.Any())) + Assert.IsTrue(TypeGuard.IsAny(T)) + }) + // ---------------------------------------------------------------- + // Array + // ---------------------------------------------------------------- + it('Should map Array', () => { + const T = TypeBox(Valibot(t.Array(t.Number()))) + Assert.IsTrue(TypeGuard.IsArray(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items)) + }) + // ---------------------------------------------------------------- + // BigInt + // ---------------------------------------------------------------- + it('Should map BigInt', () => { + const T = TypeBox(Valibot(t.BigInt())) + Assert.IsTrue(TypeGuard.IsBigInt(T)) + }) + // ---------------------------------------------------------------- + // Date + // ---------------------------------------------------------------- + it('Should map Date', () => { + const T = TypeBox(Valibot(t.Date())) + Assert.IsTrue(TypeGuard.IsDate(T)) + }) + // ---------------------------------------------------------------- + // Effects + // ---------------------------------------------------------------- + // it('Should map Effects (Transform)', () => { + // const T = TypeBox(v.number().transform(x => x)) + // Assert.IsTrue(TypeGuard.IsNumber(T)) + // Assert.IsTrue(TypeGuard.IsTransform(T)) + // }) + // it('Should map Effects (Refine)', () => { + // const T = TypeBox(v.number().refine(x => true)) + // Assert.IsTrue(TypeGuard.IsNumber(T)) + // Assert.IsTrue(TypeGuard.IsTransform(T)) + // }) + // ---------------------------------------------------------------- + // Literal + // ---------------------------------------------------------------- + it('Should map Literal (Number)', () => { + const T = TypeBox(Valibot(t.Literal(42))) + Assert.IsTrue(TypeGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 42) + }) + it('Should map Literal (String)', () => { + const T = TypeBox(Valibot(t.Literal('hello'))) + Assert.IsTrue(TypeGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 'hello') + }) + it('Should map Literal (Boolean)', () => { + const T = TypeBox(Valibot(t.Literal(true))) + Assert.IsTrue(TypeGuard.IsLiteral(T)) + Assert.IsEqual(T.const, true) + }) + // ---------------------------------------------------------------- + // Nullable + // ---------------------------------------------------------------- + it('Should map Nullable', () => { + const T = TypeBox(Valibot(t.Union([t.Null(), t.Number()]))) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(TypeGuard.IsNull(T.anyOf[0])) + Assert.IsTrue(TypeGuard.IsNumber(T.anyOf[1])) + }) + // ---------------------------------------------------------------- + // Object + // ---------------------------------------------------------------- + it('Should map Object', () => { + const T = TypeBox( + Valibot( + t.Object({ + x: t.Number(), + y: t.String(), + }), + ), + ) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsString(T.properties.y)) + }) + it('Should map Object (Strict)', () => { + const T = TypeBox( + Valibot( + t.Object( + { + x: t.Number(), + y: t.String(), + }, + { additionalProperties: false }, + ), + ), + ) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsString(T.properties.y)) + Assert.IsEqual(T.additionalProperties, false) + }) + // ---------------------------------------------------------------- + // Optional + // ---------------------------------------------------------------- + it('Should map Optional', () => { + const T = TypeBox( + Valibot( + t.Object({ + x: t.Optional(t.Number()), + y: t.Optional(t.Number()), + }), + ), + ) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) + }) + it('Should map Optional (Partial)', () => { + const T = TypeBox( + Valibot( + t.Partial( + t.Object({ + x: t.Optional(t.Number()), + y: t.Optional(t.Number()), + }), + ), + ), + ) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) + }) + // ---------------------------------------------------------------- + // Promise + // ---------------------------------------------------------------- + it('Should map Promise', () => { + const T = TypeBox(Valibot(t.Promise(t.String()))) + Assert.IsEqual(T[t.Kind], 'ValibotPromise') + }) + // ---------------------------------------------------------------- + // Record + // ---------------------------------------------------------------- + it('Should map Record (String Key)', () => { + const T = TypeBox(Valibot(t.Record(t.String(), t.Number()))) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[t.PatternStringExact])) + }) + it('Should map Record (Finite Union)', () => { + const T = TypeBox(Valibot(t.Record(t.Union([t.Literal('x'), t.Literal('y')]), t.Number()))) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + }) + // ---------------------------------------------------------------- + // Never + // ---------------------------------------------------------------- + it('Should map Never', () => { + const T = TypeBox(Valibot(t.Never())) + Assert.IsTrue(TypeGuard.IsNever(T)) + }) + // ---------------------------------------------------------------- + // Null + // ---------------------------------------------------------------- + it('Should map Null', () => { + const T = TypeBox(Valibot(t.Null())) + Assert.IsTrue(TypeGuard.IsNull(T)) + }) + // ---------------------------------------------------------------- + // Number + // ---------------------------------------------------------------- + it('Should map Number', () => { + const T = TypeBox(Valibot(t.Number())) + Assert.IsTrue(TypeGuard.IsNumber(T)) + }) + it('Should map Number (Integer)', () => { + const T = TypeBox(Valibot(t.Integer())) // remap as Number + Modulo + Assert.IsTrue(TypeGuard.IsNumber(T)) + Assert.IsEqual(T.multipleOf, 1) + }) + it('Should map Number (Minimum)', () => { + const T = TypeBox(Valibot(t.Number({ minimum: 100 }))) + Assert.IsTrue(TypeGuard.IsNumber(T)) + Assert.IsEqual(T.minimum, 100) + }) + it('Should map Number (Maximum)', () => { + const T = TypeBox(Valibot(t.Number({ maximum: 100 }))) + Assert.IsTrue(TypeGuard.IsNumber(T)) + Assert.IsEqual(T.maximum, 100) + }) + // ---------------------------------------------------------------- + // String + // ---------------------------------------------------------------- + it('Should map String', () => { + const T = TypeBox(Valibot(t.String())) + Assert.IsTrue(TypeGuard.IsString(T)) + }) + it('Should map String (Base64)', () => { + const T = TypeBox(Valibot(t.String({ format: 'base64' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'base64') + }) + it('Should map String (Bic)', () => { + const T = TypeBox(Valibot(t.String({ format: 'bic' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'bic') + }) + it('Should map String (CreditCard)', () => { + const T = TypeBox(Valibot(t.String({ format: 'credit_card' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'credit_card') + }) + it('Should map String (Cuid2)', () => { + const T = TypeBox(Valibot(t.String({ format: 'cuid2' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'cuid2') + }) + it('Should map String (Decimal)', () => { + const T = TypeBox(Valibot(t.String({ format: 'decimal' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'decimal') + }) + it('Should map String (Digits)', () => { + const T = TypeBox(Valibot(t.String({ format: 'digits' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'digits') + }) + it('Should map String (Email)', () => { + const T = TypeBox(Valibot(t.String({ format: 'email' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'email') + }) + it('Should map String (Emoji)', () => { + const T = TypeBox(Valibot(t.String({ format: 'emoji' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'emoji') + }) + it('Should map String (EndsWith)', () => { + const T = TypeBox(Valibot(t.String({ pattern: 'hello$' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.pattern, 'hello$') + }) + it('Should map String (Includes)', () => { + const T = TypeBox(Valibot(t.String({ pattern: 'hello' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.pattern, 'hello') + }) + it('Should map String (Ipv4)', () => { + const T = TypeBox(Valibot(t.String({ format: 'ipv4' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'ipv4') + }) + it('Should map String (IpV6)', () => { + const T = TypeBox(Valibot(t.String({ format: 'ipv6' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'ipv6') + }) + it('Should map String (Ip)', () => { + const T = TypeBox(Valibot(t.String({ format: 'ip' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'ip') + }) + it('Should map String (IsoDate)', () => { + const T = TypeBox(Valibot(t.String({ format: 'iso_date' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'iso_date') + }) + it('Should map String (IsoDateTime)', () => { + const T = TypeBox(Valibot(t.String({ format: 'iso_date_time' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'iso_date_time') + }) + it('Should map String (IsoTime)', () => { + const T = TypeBox(Valibot(t.String({ format: 'iso_time' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'iso_time') + }) + it('Should map String (IsoTimeSecond)', () => { + const T = TypeBox(Valibot(t.String({ format: 'iso_time_second' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'iso_time_second') + }) + it('Should map String (IsoTimestamp)', () => { + const T = TypeBox(Valibot(t.String({ format: 'iso_timestamp' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'iso_timestamp') + }) + it('Should map String (IsoWeek)', () => { + const T = TypeBox(Valibot(t.String({ format: 'iso_week' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'iso_week') + }) + it('Should map String (Mac48)', () => { + const T = TypeBox(Valibot(t.String({ format: 'mac48' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'mac48') + }) + it('Should map String (Mac64)', () => { + const T = TypeBox(Valibot(t.String({ format: 'mac64' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'mac64') + }) + it('Should map String (Mac)', () => { + const T = TypeBox(Valibot(t.String({ format: 'mac' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'mac') + }) + it('Should map String (MaxLength)', () => { + const T = TypeBox(Valibot(t.String({ maxLength: 100 }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.maxLength, 100) + }) + it('Should map String (MinLength)', () => { + const T = TypeBox(Valibot(t.String({ minLength: 100 }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.minLength, 100) + }) + it('Should map String (Nanoid)', () => { + const T = TypeBox(Valibot(t.String({ format: 'nanoid' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'nanoid') + }) + it('Should map String (Octal)', () => { + const T = TypeBox(Valibot(t.String({ format: 'octal' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'octal') + }) + it('Should map String (RegExp)', () => { + const T = TypeBox(Valibot(t.RegExp(/abc/))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.pattern, 'abc') + }) + it('Should map String (StartsWith)', () => { + const T = TypeBox(Valibot(t.String({ pattern: '^hello' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.pattern, '^hello') + }) + it('Should map String (Ulid)', () => { + const T = TypeBox(Valibot(t.String({ format: 'ulid' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'ulid') + }) + it('Should map String (Url)', () => { + const T = TypeBox(Valibot(t.String({ format: 'url' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'url') + }) + it('Should map String (Uuid)', () => { + const T = TypeBox(Valibot(t.String({ format: 'uuid' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'uuid') + }) + // ---------------------------------------------------------------- + // Symbol + // ---------------------------------------------------------------- + it('Should map Symbol', () => { + const T = TypeBox(Valibot(t.Symbol())) + Assert.IsTrue(TypeGuard.IsSymbol(T)) + }) + // ---------------------------------------------------------------- + // Tuple + // ---------------------------------------------------------------- + it('Should map Tuple', () => { + const T = TypeBox(Valibot(t.Tuple([t.Number(), t.String()]))) + Assert.IsTrue(TypeGuard.IsTuple(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) + Assert.IsTrue(TypeGuard.IsString(T.items![1])) + }) + // ---------------------------------------------------------------- + // Undefined + // ---------------------------------------------------------------- + it('Should map Undefined', () => { + const T = TypeBox(Valibot(t.Undefined())) + Assert.IsTrue(TypeGuard.IsUndefined(T)) + }) + // ---------------------------------------------------------------- + // Union + // ---------------------------------------------------------------- + it('Should map Union', () => { + const T = TypeBox(Valibot(t.Union([t.String(), t.Boolean()]))) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(TypeGuard.IsString(T.anyOf[0])) + Assert.IsTrue(TypeGuard.IsBoolean(T.anyOf[1])) + }) + // ---------------------------------------------------------------- + // Unknown + // ---------------------------------------------------------------- + it('Should map Unknown', () => { + const T = TypeBox(Valibot(t.Unknown())) + Assert.IsTrue(TypeGuard.IsUnknown(T)) + }) + // ---------------------------------------------------------------- + // Void + // ---------------------------------------------------------------- + it('Should map Void', () => { + const T = TypeBox(Valibot(t.Void())) + Assert.IsTrue(TypeGuard.IsVoid(T)) + }) +}) diff --git a/test/zod-from-typebox.ts b/test/zod-from-typebox.ts new file mode 100644 index 0000000..39f8a94 --- /dev/null +++ b/test/zod-from-typebox.ts @@ -0,0 +1,429 @@ +import { TypeBox, Zod } from '@sinclair/typemap' +import { TypeGuard } from '@sinclair/typebox' +import { Assert } from './assert' +import * as t from '@sinclair/typebox' +import * as z from 'zod' + +describe('Zod From TypeBox', () => { + // ---------------------------------------------------------------- + // Metadata + // ---------------------------------------------------------------- + it('Should map Description', () => { + const T = TypeBox(Zod(t.Number({ description: 'a number' }))) + Assert.IsEqual(T.description, 'a number') + }) + it('Should map Default', () => { + const T = TypeBox(Zod(t.Number({ default: 12345 }))) + Assert.IsEqual(T.default, 12345) + }) + // ---------------------------------------------------------------- + // Any + // ---------------------------------------------------------------- + it('Should map Any', () => { + const T = TypeBox(Zod(t.Any())) + Assert.IsTrue(TypeGuard.IsAny(T)) + }) + // ---------------------------------------------------------------- + // Array + // ---------------------------------------------------------------- + it('Should map Array', () => { + const T = TypeBox(Zod(t.Array(t.Number()))) + Assert.IsTrue(TypeGuard.IsArray(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items)) + }) + // ---------------------------------------------------------------- + // BigInt + // ---------------------------------------------------------------- + it('Should map BigInt', () => { + const T = TypeBox(Zod(t.BigInt())) + Assert.IsTrue(TypeGuard.IsBigInt(T)) + }) + // ---------------------------------------------------------------- + // Date + // ---------------------------------------------------------------- + it('Should map Date', () => { + const T = TypeBox(Zod(t.Date())) + Assert.IsTrue(TypeGuard.IsDate(T)) + }) + // ---------------------------------------------------------------- + // Effects + // ---------------------------------------------------------------- + // it('Should map Effects (Transform)', () => { + // const T = TypeBox(z.number().transform((x) => x)) + // Assert.IsTrue(TypeGuard.IsNumber(T)) + // Assert.IsTrue(TypeGuard.IsTransform(T)) + // }) + // it('Should map Effects (Refine)', () => { + // const T = TypeBox(z.number().refine((x) => true)) + // Assert.IsTrue(TypeGuard.IsNumber(T)) + // Assert.IsTrue(TypeGuard.IsTransform(T)) + // }) + // ---------------------------------------------------------------- + // Literal + // ---------------------------------------------------------------- + it('Should map Literal (Number)', () => { + const T = TypeBox(Zod(t.Literal(42))) + Assert.IsTrue(TypeGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 42) + }) + it('Should map Literal (String)', () => { + const T = TypeBox(Zod(t.Literal('hello'))) + Assert.IsTrue(TypeGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 'hello') + }) + it('Should map Literal (Boolean)', () => { + const T = TypeBox(Zod(t.Literal(true))) + Assert.IsTrue(TypeGuard.IsLiteral(T)) + Assert.IsEqual(T.const, true) + }) + // ---------------------------------------------------------------- + // Object + // ---------------------------------------------------------------- + it('Should map Object', () => { + const T = TypeBox( + Zod( + t.Object({ + x: t.Number(), + y: t.String(), + }), + ), + ) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsString(T.properties.y)) + }) + it('Should map Object (Strict)', () => { + const T = TypeBox( + Zod( + t.Object( + { + x: t.Number(), + y: t.String(), + }, + { additionalProperties: false }, + ), + ), + ) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsString(T.properties.y)) + Assert.IsEqual(T.additionalProperties, false) + }) + // ---------------------------------------------------------------- + // Optional + // ---------------------------------------------------------------- + it('Should map Optional', () => { + const T = TypeBox( + Zod( + t.Object({ + x: t.Optional(t.Number()), + y: t.Optional(t.Number()), + }), + ), + ) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) + }) + it('Should map Optional (Readonly)', () => { + const T = TypeBox( + Zod( + t.Object({ + x: t.ReadonlyOptional(t.Number()), + y: t.ReadonlyOptional(t.Number()), + }), + ), + ) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) + Assert.IsFalse(TypeGuard.IsReadonly(T.properties.x)) // Cannot Map for Readonly in Zod + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) + Assert.IsFalse(TypeGuard.IsReadonly(T.properties.y)) // Cannot Map for Readonly in Zod + }) + it('Should map Optional (Partial)', () => { + const T = TypeBox( + Zod( + t.Partial( + t.Object({ + x: t.Number(), + y: t.Number(), + }), + ), + ), + ) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) + }) + // ---------------------------------------------------------------- + // Promise + // ---------------------------------------------------------------- + it('Should map Promise', () => { + const T = TypeBox(Zod(t.Promise(t.Number()))) + Assert.IsTrue(TypeGuard.IsPromise(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.item)) + }) + // ---------------------------------------------------------------- + // Readonly + // ---------------------------------------------------------------- + it('Should map Readonly', () => { + const T = TypeBox( + Zod( + t.Object({ + x: t.Readonly(t.Number()), + y: t.Readonly(t.Number()), + }), + ), + ) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsFalse(TypeGuard.IsReadonly(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsFalse(TypeGuard.IsReadonly(T.properties.y)) + }) + // ---------------------------------------------------------------- + // Record + // ---------------------------------------------------------------- + it('Should map Record (Number Key)', () => { + const T = TypeBox(Zod(t.Record(t.Number(), t.Number()))) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[t.PatternNumberExact])) + }) + it('Should map Record (String Key)', () => { + const T = TypeBox(Zod(t.Record(t.String(), t.Number()))) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[t.PatternStringExact])) + }) + it('Should map Record (Finite Union)', () => { + const T = TypeBox(Zod(t.Record(t.Union([t.Literal('x'), t.Literal('y')]), t.Number()))) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + }) + // ---------------------------------------------------------------- + // Never + // ---------------------------------------------------------------- + it('Should map Never', () => { + const T = TypeBox(Zod(t.Never())) + Assert.IsTrue(TypeGuard.IsNever(T)) + }) + // ---------------------------------------------------------------- + // Null + // ---------------------------------------------------------------- + it('Should map Null', () => { + const T = TypeBox(Zod(t.Null())) + Assert.IsTrue(TypeGuard.IsNull(T)) + }) + // ---------------------------------------------------------------- + // Number + // ---------------------------------------------------------------- + it('Should map Number', () => { + const T = TypeBox(Zod(t.Number())) + Assert.IsTrue(TypeGuard.IsNumber(T)) + }) + it('Should map Number (Integer)', () => { + const T = TypeBox(Zod(t.Integer())) + Assert.IsTrue(TypeGuard.IsNumber(T)) + Assert.IsEqual(T.multipleOf, 1) + }) + it('Should map Number (Minimum)', () => { + const T = TypeBox(Zod(t.Number({ minimum: 100 }))) + Assert.IsTrue(TypeGuard.IsNumber(T)) + Assert.IsEqual(T.minimum, 100) + }) + it('Should map Number (Maximum)', () => { + const T = TypeBox(Zod(t.Number({ maximum: 100 }))) + Assert.IsTrue(TypeGuard.IsNumber(T)) + Assert.IsEqual(T.maximum, 100) + }) + // ---------------------------------------------------------------- + // String + // ---------------------------------------------------------------- + it('Should map String', () => { + const T = TypeBox(Zod(t.String())) + Assert.IsTrue(TypeGuard.IsString(T)) + }) + it('Should map String (Base64)', () => { + const T = TypeBox(Zod(t.String({ format: 'base64' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'base64') + }) + it('Should map String (Base64Url)', () => { + const T = TypeBox(Zod(t.String({ format: 'base64url' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'base64url') + }) + it('Should map String (Cidr V4)', () => { + const T = TypeBox(Zod(t.String({ format: 'cidrv4' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'cidrv4') + }) + it('Should map String (Cidr v6)', () => { + const T = TypeBox(Zod(t.String({ format: 'cidrv6' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'cidrv6') + }) + it('Should map String (Cidr)', () => { + const T = TypeBox(Zod(t.String({ format: 'cidr' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'cidr') + }) + it('Should map String (Cuid)', () => { + const T = TypeBox(Zod(t.String({ format: 'cuid' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'cuid') + }) + it('Should map String (Cuid2)', () => { + const T = TypeBox(Zod(t.String({ format: 'cuid2' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'cuid2') + }) + it('Should map String (Ulid)', () => { + const T = TypeBox(Zod(t.String({ format: 'ulid' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'ulid') + }) + it('Should map String (Email)', () => { + const T = TypeBox(Zod(t.String({ format: 'email' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'email') + }) + it('Should map String (Emoji)', () => { + const T = TypeBox(Zod(t.String({ format: 'emoji' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'emoji') + }) + it('Should map String (EndsWith)', () => { + const T = TypeBox(Zod(t.String({ pattern: 'hello$' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.pattern, 'hello$') + }) + it('Should map String (Includes)', () => { + const T = TypeBox(Zod(t.String({ pattern: 'hello' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.pattern, 'hello') + }) + it('Should map String (IpV4)', () => { + const T = TypeBox(Zod(t.String({ format: 'ipv4' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'ipv4') + }) + it('Should map String (IpV6)', () => { + const T = TypeBox(Zod(t.String({ format: 'ipv6' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'ipv6') + }) + it('Should map String (Ip)', () => { + const T = TypeBox(Zod(t.String({ format: 'ip' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'ip') + }) + it('Should map String (Jwt)', () => { + const T = TypeBox(Zod(t.String({ format: 'jwt' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'jwt') + }) + it('Should map String (Length)', () => { + const T = TypeBox(Zod(t.String({ minLength: 100, maxLength: 100 }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.minLength, 100) + Assert.IsEqual(T.maxLength, 100) + }) + it('Should map String (Min)', () => { + const T = TypeBox(Zod(t.String({ minLength: 100 }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.minLength, 100) + }) + it('Should map String (Max)', () => { + const T = TypeBox(Zod(t.String({ maxLength: 100 }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.maxLength, 100) + }) + it('Should map String (Nanoid)', () => { + const T = TypeBox(Zod(t.String({ format: 'nanoid' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'nanoid') + }) + it('Should map String (RegExp)', () => { + const T = TypeBox(Zod(t.RegExp(/abc/))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.pattern, 'abc') + }) + it('Should map String (StartsWith)', () => { + const T = TypeBox(Zod(t.String({ pattern: '^hello' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.pattern, '^hello') + }) + it('Should map String (Time)', () => { + const T = TypeBox(Zod(t.String({ format: 'time' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'time') + }) + it('Should map String (Ulid)', () => { + const T = TypeBox(Zod(t.String({ format: 'ulid' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'ulid') + }) + it('Should map String (Url)', () => { + const T = TypeBox(Zod(t.String({ format: 'url' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'url') + }) + it('Should map String (Uuid)', () => { + const T = TypeBox(Zod(t.String({ format: 'uuid' }))) + Assert.IsTrue(TypeGuard.IsString(T)) + Assert.IsEqual(T.format, 'uuid') + }) + // ---------------------------------------------------------------- + // Symbol + // ---------------------------------------------------------------- + it('Should map Symbol', () => { + const T = TypeBox(Zod(t.Symbol())) + Assert.IsTrue(TypeGuard.IsSymbol(T)) + }) + // ---------------------------------------------------------------- + // Tuple + // ---------------------------------------------------------------- + it('Should map Tuple', () => { + const T = TypeBox(Zod(t.Tuple([t.Number(), t.String()]))) + Assert.IsTrue(TypeGuard.IsTuple(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) + Assert.IsTrue(TypeGuard.IsString(T.items![1])) + }) + // ---------------------------------------------------------------- + // Undefined + // ---------------------------------------------------------------- + it('Should map Undefined', () => { + const T = TypeBox(Zod(t.Undefined())) + Assert.IsTrue(TypeGuard.IsUndefined(T)) + }) + // ---------------------------------------------------------------- + // Union + // ---------------------------------------------------------------- + it('Should map Union', () => { + const T = TypeBox(Zod(t.Union([t.String(), t.Boolean()]))) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(TypeGuard.IsString(T.anyOf[0])) + Assert.IsTrue(TypeGuard.IsBoolean(T.anyOf[1])) + }) + // ---------------------------------------------------------------- + // Unknown + // ---------------------------------------------------------------- + it('Should map Unknown', () => { + const T = TypeBox(Zod(t.Unknown())) + Assert.IsTrue(TypeGuard.IsUnknown(T)) + }) + // ---------------------------------------------------------------- + // Void + // ---------------------------------------------------------------- + it('Should map Void', () => { + const T = TypeBox(Zod(t.Void())) + Assert.IsTrue(TypeGuard.IsVoid(T)) + }) +}) diff --git a/tsconfig.json b/tsconfig.json index 6d0789e..ac0dc92 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,10 +5,7 @@ "moduleResolution": "Node", "baseUrl": ".", "paths": { - "@sinclair/typebox-adapter/typebox": ["src/typebox/index.ts"], - "@sinclair/typebox-adapter/valibot": ["src/valibot/index.ts"], - "@sinclair/typebox-adapter/zod": ["src/zod/index.ts"], - "@sinclair/typebox-adapter": ["src/index.ts"] + "@sinclair/typemap": ["src/index.ts"] } } } \ No newline at end of file diff --git a/typebox-adapter.png b/typebox-adapter.png deleted file mode 100644 index ac6f42e..0000000 Binary files a/typebox-adapter.png and /dev/null differ diff --git a/typemap.png b/typemap.png new file mode 100644 index 0000000..843b674 Binary files /dev/null and b/typemap.png differ