├── .github ├── .keep └── workflows │ ├── build.yml │ └── nightly.yml ├── .gitignore ├── .vscode └── settings.json ├── design ├── concept.png ├── font.ttf ├── javascript.jpg ├── standard.jpg ├── typemap.blend ├── typemap.blend1 ├── typescript.jpg ├── valibot.jpg └── zod.jpg ├── example ├── index.ts └── prototypes │ └── effect.ts ├── hammer.mjs ├── license ├── package-lock.json ├── package.json ├── readme.md ├── src ├── compile │ ├── compile.ts │ ├── environment.ts │ ├── standard.ts │ └── validator.ts ├── guard.ts ├── index.ts ├── options.ts ├── static.ts ├── syntax │ ├── syntax-from-syntax.ts │ ├── syntax-from-typebox.ts │ ├── syntax-from-valibot.ts │ ├── syntax-from-zod.ts │ └── syntax.ts ├── tsconfig.json ├── typebox │ ├── typebox-from-syntax.ts │ ├── typebox-from-typebox.ts │ ├── typebox-from-valibot.ts │ ├── typebox-from-zod.ts │ └── typebox.ts ├── valibot │ ├── common.ts │ ├── valibot-from-syntax.ts │ ├── valibot-from-typebox.ts │ ├── valibot-from-valibot.ts │ ├── valibot-from-zod.ts │ └── valibot.ts └── zod │ ├── zod-from-syntax.ts │ ├── zod-from-typebox.ts │ ├── zod-from-valibot.ts │ ├── zod-from-zod.ts │ └── zod.ts ├── task ├── benchmark │ └── index.ts └── build │ ├── cjs │ ├── build.ts │ └── compile.ts │ ├── esm │ ├── build.ts │ ├── compile.ts │ └── convert-to-esm.ts │ ├── index.ts │ ├── notices │ └── remove-notices.ts │ └── package │ ├── build.ts │ ├── create-package-json-redirect.ts │ └── create-package-json.ts ├── test ├── assert.ts ├── compile.ts ├── index.ts ├── options.ts ├── parameters.ts ├── tsconfig.json ├── typebox-from-valibot.ts ├── typebox-from-zod.ts ├── valibot-from-typebox.ts └── zod-from-typebox.ts ├── tsconfig.json └── typemap.png /.github/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinclairzx81/typemap/631fab497a16871a7c15069b31e4ae6cda9077bd/.github/.keep -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push, pull_request] 3 | jobs: 4 | TypeBox-Adapter: 5 | runs-on: ${{ matrix.os }} 6 | strategy: 7 | matrix: 8 | node: [16.x, 18.x, 20.x] 9 | os: [ubuntu-latest, windows-latest, macOS-latest] 10 | steps: 11 | - uses: actions/checkout@v3 12 | - name: Install Node 13 | uses: actions/setup-node@v3 14 | with: 15 | node-version: ${{ matrix.node }} 16 | 17 | - name: Install Packages 18 | run: npm install 19 | 20 | - name: Build Library 21 | run: npm run build 22 | 23 | - name: Test Library 24 | run: npm run test -------------------------------------------------------------------------------- /.github/workflows/nightly.yml: -------------------------------------------------------------------------------- 1 | name: Build Nightly 2 | on: 3 | schedule: 4 | - cron: '0 18 * * *' # 6pm Daily 5 | jobs: 6 | TypeBox-Adapter-Nightly: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | node: [20.x] 11 | os: [ubuntu-latest] 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Install Node 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version: ${{ matrix.node }} 18 | 19 | - name: Install Packages 20 | run: npm install 21 | 22 | - name: Build Library 23 | run: npm run build -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | target -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "node_modules": true, 4 | "package-lock.json": true 5 | } 6 | } -------------------------------------------------------------------------------- /design/concept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinclairzx81/typemap/631fab497a16871a7c15069b31e4ae6cda9077bd/design/concept.png -------------------------------------------------------------------------------- /design/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinclairzx81/typemap/631fab497a16871a7c15069b31e4ae6cda9077bd/design/font.ttf -------------------------------------------------------------------------------- /design/javascript.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinclairzx81/typemap/631fab497a16871a7c15069b31e4ae6cda9077bd/design/javascript.jpg -------------------------------------------------------------------------------- /design/standard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinclairzx81/typemap/631fab497a16871a7c15069b31e4ae6cda9077bd/design/standard.jpg -------------------------------------------------------------------------------- /design/typemap.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinclairzx81/typemap/631fab497a16871a7c15069b31e4ae6cda9077bd/design/typemap.blend -------------------------------------------------------------------------------- /design/typemap.blend1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinclairzx81/typemap/631fab497a16871a7c15069b31e4ae6cda9077bd/design/typemap.blend1 -------------------------------------------------------------------------------- /design/typescript.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinclairzx81/typemap/631fab497a16871a7c15069b31e4ae6cda9077bd/design/typescript.jpg -------------------------------------------------------------------------------- /design/valibot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinclairzx81/typemap/631fab497a16871a7c15069b31e4ae6cda9077bd/design/valibot.jpg -------------------------------------------------------------------------------- /design/zod.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinclairzx81/typemap/631fab497a16871a7c15069b31e4ae6cda9077bd/design/zod.jpg -------------------------------------------------------------------------------- /example/index.ts: -------------------------------------------------------------------------------- 1 | import { TypeBox, Valibot, Zod, Syntax, Compile } from '@sinclair/typemap' 2 | 3 | // ------------------------------------------------------------------ 4 | // Syntax Types 5 | // ------------------------------------------------------------------ 6 | 7 | const S = `{ 8 | x: number, 9 | y: number, 10 | z: number 11 | }` 12 | 13 | // ------------------------------------------------------------------ 14 | // Runtime Types 15 | // ------------------------------------------------------------------ 16 | 17 | const T = TypeBox(S) // const T: TObject<{ ... }> 18 | 19 | const V = Valibot(S) // const V: ObjectSchema<{ ... }, ...> 20 | 21 | const Z = Zod(S) // const Z: ZodObject<{ ... }, ...> 22 | 23 | // ------------------------------------------------------------------ 24 | // Reverse Syntax 25 | // ------------------------------------------------------------------ 26 | 27 | const X = Syntax(Z) // const X: "{ x: number, y: number, z: number }" 28 | 29 | // ------------------------------------------------------------------ 30 | // Compile 31 | // ------------------------------------------------------------------ 32 | 33 | const C = Compile(X) // const C: Validator> 38 | 39 | // ------------------------------------------------------------------ 40 | // Validate | Standard 41 | // ------------------------------------------------------------------ 42 | 43 | const R = C['~standard'].validate(null) // const R: StandardSchemaV1.Result<{ 44 | // x: number; 45 | // y: number; 46 | // z: number; 47 | // }> -------------------------------------------------------------------------------- /example/prototypes/effect.ts: -------------------------------------------------------------------------------- 1 | // /*-------------------------------------------------------------------------- 2 | 3 | // @sinclair/typemap 4 | 5 | // The MIT License (MIT) 6 | 7 | // Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | // ---------------------------------------------------------------------------*/ 28 | 29 | // import { Schema as et } from '@effect/schema/Schema' 30 | // import { Schema as es } from '@effect/schema' 31 | // import * as ast from '@effect/schema/AST' 32 | // import * as tb from '@sinclair/typebox' 33 | 34 | // // ------------------------------------------------------------------ 35 | // // Effect Guard 36 | // // ------------------------------------------------------------------ 37 | // function IsBigInt(type: ast.Annotated) { 38 | // return type.annotations[ast.IdentifierAnnotationId] === 'bigint' 39 | // } 40 | // function IsDate(type: ast.Annotated) { 41 | // return type.annotations[ast.IdentifierAnnotationId] === 'Date' 42 | // } 43 | // function IsInt(type: ast.Annotated) { 44 | // return type.annotations[ast.IdentifierAnnotationId] === 'Int' 45 | // } 46 | // function IsNull(type: ast.Annotated) { 47 | // return type instanceof ast.Literal && tb.ValueGuard.IsNull(type.literal) 48 | // } 49 | // function IsOptional(type: ast.Annotated) { 50 | // return 'isOptional' in type && type.isOptional === true 51 | // } 52 | // function IsUint8Array(type: ast.Annotated) { 53 | // return type.annotations[ast.IdentifierAnnotationId] === 'Uint8Array' 54 | // } 55 | // function IsNumberFromString(type: ast.Annotated) { 56 | // return type.annotations[ast.IdentifierAnnotationId] === 'NumberFromString' 57 | // } 58 | // function IsArray(type: ast.Annotated): type is ast.TupleType { 59 | // return type instanceof ast.TupleType && type.rest.length > 0 && type.elements.length === 0 60 | // } 61 | // function IsTuple(type: ast.Annotated): type is ast.TupleType { 62 | // return type instanceof ast.TupleType && type.rest.length === 0 63 | // } 64 | // // ------------------------------------------------------------------ 65 | // // Any 66 | // // ------------------------------------------------------------------ 67 | // type TFromAny<_Type> = tb.TAny 68 | // function FromAny(_type: ast.AnyKeyword): tb.TSchema { 69 | // return tb.Any() 70 | // } 71 | // // ------------------------------------------------------------------ 72 | // // Array 73 | // // ------------------------------------------------------------------ 74 | // type TFromArray>> = Result 75 | // function FromArray(_type: ast.TupleType): tb.TSchema { 76 | // return tb.Array(FromType(_type.rest[0].type)) 77 | // } 78 | // // ------------------------------------------------------------------ 79 | // // BigInt 80 | // // ------------------------------------------------------------------ 81 | // type TFromBigInt<_Type> = tb.TBigInt 82 | // function FromBigInt(_type: ast.Annotated): tb.TSchema { 83 | // return tb.BigInt() 84 | // } 85 | // // ------------------------------------------------------------------ 86 | // // Boolean 87 | // // ------------------------------------------------------------------ 88 | // type TFromBoolean<_Type> = tb.TBoolean 89 | // function FromBoolean(_type: ast.BooleanKeyword): tb.TSchema { 90 | // return tb.Boolean() 91 | // } 92 | // // ------------------------------------------------------------------ 93 | // // Date 94 | // // ------------------------------------------------------------------ 95 | // type TFromDate<_Type> = tb.TDate 96 | // function FromDate(_type: ast.Annotated): tb.TSchema { 97 | // return tb.Date() 98 | // } 99 | // // ------------------------------------------------------------------ 100 | // // Integer 101 | // // ------------------------------------------------------------------ 102 | // type TFromInteger<_Type> = tb.TNumber 103 | // function FromInteger(_type: ast.Annotated): tb.TSchema { 104 | // return tb.Number({ multipleOf: 1 }) 105 | // } 106 | // // ------------------------------------------------------------------ 107 | // // Literal: Effect literal types may be union 108 | // // ------------------------------------------------------------------ 109 | // // prettier-ignore 110 | // type TFromLiteral = ( 111 | // Value extends [infer Left extends unknown, ...infer Right extends unknown[]] 112 | // ? ( 113 | // Left extends tb.TLiteralValue 114 | // ? TFromLiteral]> 115 | // : TFromLiteral 116 | // ) : tb.TUnionEvaluated 117 | // ) 118 | // function FromLiteral(type: ast.Literal): tb.TSchema { 119 | // return tb.KindGuard.IsLiteralValue(type.literal) ? tb.Literal(type.literal) : tb.Unknown() 120 | // } 121 | // // ------------------------------------------------------------------ 122 | // // FromNever 123 | // // ------------------------------------------------------------------ 124 | // function FromNever(_type: ast.NeverKeyword): tb.TSchema { 125 | // return tb.Never() 126 | // } 127 | // type TFromNever<_Type, Result extends tb.TSchema = tb.TNever> = Result 128 | // // ------------------------------------------------------------------ 129 | // // NullishOr 130 | // // ------------------------------------------------------------------ 131 | // type TFromNullishOr, tb.TNull, tb.TUndefined]>> = Result 132 | // // ------------------------------------------------------------------ 133 | // // NullOr 134 | // // ------------------------------------------------------------------ 135 | // type TFromNullOr, tb.TNull]>> = Result 136 | // // ------------------------------------------------------------------ 137 | // // Null 138 | // // ------------------------------------------------------------------ 139 | // type TFromNull<_Type> = tb.TNull 140 | // function FromNull(_type: ast.Annotated): tb.TSchema { 141 | // return tb.Null() 142 | // } 143 | // // ------------------------------------------------------------------ 144 | // // Number 145 | // // ------------------------------------------------------------------ 146 | // type TFromNumber<_Type> = tb.TNumber 147 | // function FromNumber(_type: ast.NumberKeyword): tb.TSchema { 148 | // return tb.Number() 149 | // } 150 | // // ------------------------------------------------------------------ 151 | // // Object 152 | // // ------------------------------------------------------------------ 153 | // type TFromObject<_Type> = tb.TObject 154 | // function FromObject(type: ast.ObjectKeyword): tb.TSchema { 155 | // return tb.Object({}) 156 | // } 157 | // // ------------------------------------------------------------------ 158 | // // Optional 159 | // // ------------------------------------------------------------------ 160 | // type TFromOptional, Result = tb.TOptional> = Result 161 | // function FromOptional(type: ast.Annotated): tb.TSchema { 162 | // return tb.Optional(FromType(type)) 163 | // } 164 | // // ------------------------------------------------------------------ 165 | // // Record 166 | // // ------------------------------------------------------------------ 167 | // type TFromRecord = tb.TRecordOrObject, TFromType> 168 | // // ------------------------------------------------------------------ 169 | // // SchemaClass: TypeLiteral 170 | // // ------------------------------------------------------------------ 171 | // type TFromSchemaClass> = Result 172 | // // ------------------------------------------------------------------ 173 | // // String 174 | // // ------------------------------------------------------------------ 175 | // type TFromString<_Type> = tb.TString 176 | // function FromString(_type: ast.Annotated): tb.TSchema { 177 | // return tb.String() 178 | // } 179 | // // ------------------------------------------------------------------ 180 | // // Struct 181 | // // ------------------------------------------------------------------ 182 | // // prettier-ignore 183 | // type TFromStruct, 184 | // Mapped extends tb.TProperties = { [Key in keyof Properties]: TFromType }, 185 | // Result = tb.TObject 186 | // > = Result 187 | // // prettier-ignore 188 | // function FromStruct(type: ast.TypeLiteral): tb.TSchema { 189 | // const properties = type.propertySignatures.reduce((result, property) => { 190 | // const mappedProperty = property.isOptional ? tb.Optional(FromType(property.type)) : FromType(property.type) 191 | // return { ...result, [property.name]: mappedProperty } 192 | // }, {} as tb.TProperties) as tb.TProperties 193 | // return tb.Object(properties) 194 | // } 195 | // // ------------------------------------------------------------------ 196 | // // Symbol 197 | // // ------------------------------------------------------------------ 198 | // type TFromSymbol<_Type> = tb.TSymbol 199 | // function FromSymbol(_type: ast.Annotated): tb.TSchema { 200 | // return tb.Symbol() 201 | // } 202 | // // ------------------------------------------------------------------ 203 | // // Tuple 204 | // // ------------------------------------------------------------------ 205 | // // prettier-ignore 206 | // type TFromTuple = ( 207 | // Types extends [infer Left extends unknown, ...infer Right extends unknown[]] 208 | // ? TFromTuple]> 209 | // : tb.TTuple 210 | // ) 211 | // function FromTuple(type: ast.TupleType): tb.TSchema { 212 | // return tb.Tuple(type.elements.map((type) => FromType(type.type))) 213 | // } 214 | // // ------------------------------------------------------------------ 215 | // // UndefinedOr 216 | // // ------------------------------------------------------------------ 217 | // type TFromUndefinedOr, tb.TUndefined]>> = Result 218 | // // ------------------------------------------------------------------ 219 | // // Undefined 220 | // // ------------------------------------------------------------------ 221 | // type TFromUndefined<_Type> = tb.TUndefined 222 | // function FromUndefined(_type: ast.Annotated): tb.TSchema { 223 | // return tb.Undefined() 224 | // } 225 | // // ------------------------------------------------------------------ 226 | // // Uint8Array 227 | // // ------------------------------------------------------------------ 228 | // type TFromUint8Array<_Type> = tb.TUint8Array 229 | // function FromUint8Array(_type: ast.Annotated): tb.TSchema { 230 | // return tb.Uint8Array() 231 | // } 232 | // // ------------------------------------------------------------------ 233 | // // Unknown 234 | // // ------------------------------------------------------------------ 235 | // type TFromUnknown<_Type> = tb.TUnknown 236 | // function FromUnknown(_type: ast.Annotated): tb.TSchema { 237 | // return tb.Unknown() 238 | // } 239 | // // ------------------------------------------------------------------ 240 | // // Union 241 | // // ------------------------------------------------------------------ 242 | // // prettier-ignore 243 | // type TFromUnion = ( 244 | // Variants extends [infer Left extends unknown, ...infer Right extends unknown[]] 245 | // ? TFromUnion]> 246 | // : tb.TUnionEvaluated 247 | // ) 248 | // function FromUnion(type: ast.Union): tb.TSchema { 249 | // return tb.Union(type.types.map((type) => FromType(type))) 250 | // } 251 | // // ------------------------------------------------------------------ 252 | // // Void 253 | // // ------------------------------------------------------------------ 254 | // type TFromVoid<_Type> = tb.TVoid 255 | // function FromVoid(_type: ast.VoidKeyword): tb.TSchema { 256 | // return tb.Void() 257 | // } 258 | // // ------------------------------------------------------------------ 259 | // // Type 260 | // // 261 | // // Note: Type differentition in Effect is quite challenging as the 262 | // // library doesn't provide discriminable types in all cases. An 263 | // // example would be Number and Integer where both are observed 264 | // // as Number. Unions also provide challenges for NullishOr and 265 | // // similar types. The order in which we resolve is important. 266 | // // ------------------------------------------------------------------ 267 | // // prettier-ignore 268 | // type TFromType = ( 269 | // Type extends es.optional ? TFromOptional : 270 | // Type extends es.Tuple ? TFromTuple : 271 | // Type extends es.Record$ ? TFromRecord : 272 | // Type extends es.Array$ ? TFromArray : 273 | // Type extends es.Date ? TFromDate : 274 | // Type extends es.Struct ? TFromStruct : 275 | // Type extends es.SchemaClass ? TFromSchemaClass : 276 | // Type extends es.Literal ? TFromLiteral : 277 | // Type extends es.Int ? TFromInteger : 278 | // Type extends es.BigInt ? TFromBigInt : 279 | // Type extends es.Boolean ? TFromBoolean : 280 | // Type extends es.Object ? TFromObject : 281 | // Type extends es.Never ? TFromNever : 282 | // Type extends es.Null ? TFromNull : 283 | // Type extends es.Number ? TFromNumber : 284 | // Type extends es.String ? TFromString : 285 | // Type extends es.Symbol ? TFromSymbol : 286 | // Type extends et ? TFromUint8Array : 287 | // Type extends es.Undefined ? TFromUndefined : 288 | // Type extends es.Void ? TFromVoid : 289 | // // Union-Like 290 | // Type extends es.UndefinedOr ? TFromUndefinedOr : 291 | // Type extends es.NullishOr ? TFromNullishOr : 292 | // Type extends es.NullOr ? TFromNullOr : 293 | // Type extends es.Union ? TFromUnion : 294 | // // Fallthrough 295 | // Type extends es.Unknown ? TFromUnknown : 296 | // Type extends es.Any ? TFromAny : 297 | // tb.TUnknown 298 | // ) 299 | // // prettier-ignore 300 | // function FromType(type: ast.Annotated): tb.TSchema { 301 | // const schema = ( 302 | // // Non-Differentiable 303 | // IsOptional(type) ? FromOptional(type) : 304 | // IsArray(type) ? FromArray(type) : 305 | // IsBigInt(type) ? FromBigInt(type) : 306 | // IsDate(type) ? FromDate(type) : 307 | // IsInt(type) ? FromInteger(type) : 308 | // IsNull(type) ? FromNull(type) : 309 | // IsTuple(type) ? FromTuple(type) : 310 | // IsUint8Array(type) ? FromUint8Array(type) : 311 | // // Differentiable 312 | // type instanceof ast.AnyKeyword ? FromAny(type) : 313 | // type instanceof ast.BooleanKeyword ? FromBoolean(type) : 314 | // type instanceof ast.Literal ? FromLiteral(type) : 315 | // type instanceof ast.NeverKeyword ? FromNever(type) : 316 | // type instanceof ast.NumberKeyword ? FromNumber(type) : 317 | // type instanceof ast.ObjectKeyword ? FromObject(type) : 318 | // type instanceof ast.StringKeyword ? FromString(type) : 319 | // type instanceof ast.SymbolKeyword ? FromSymbol(type) : 320 | // type instanceof ast.TypeLiteral ? FromStruct(type) : 321 | // type instanceof ast.UndefinedKeyword ? FromUndefined(type) : 322 | // type instanceof ast.UnknownKeyword ? FromUnknown(type) : 323 | // type instanceof ast.Union ? FromUnion(type) : 324 | // type instanceof ast.VoidKeyword ? FromVoid(type) : 325 | // tb.Unknown() 326 | // ) 327 | // return schema 328 | // } 329 | // // ------------------------------------------------------------------ 330 | // // Box 331 | // // ------------------------------------------------------------------ 332 | // /** Converts an Effect Type to a TypeBox Type */ 333 | // // prettier-ignore 334 | // export type TBox = ( 335 | // Type extends es.Any ? TFromType : undefined 336 | // ) 337 | // /** Converts an Effect Type to a TypeBox Type */ 338 | // // prettier-ignore 339 | // export function Box(type: Type): TBox { 340 | // return ( 341 | // es.isSchema(type) 342 | // ? FromType(type.ast) 343 | // : undefined 344 | // ) as never 345 | // } 346 | -------------------------------------------------------------------------------- /hammer.mjs: -------------------------------------------------------------------------------- 1 | import * as Build from './task/build' 2 | import * as Fs from 'node:fs' 3 | 4 | // ------------------------------------------------------------------ 5 | // Benchmark 6 | // ------------------------------------------------------------------ 7 | export async function benchmark(target = 'target/benchmark') { 8 | await shell(`hammer run task/benchmark/index.ts --dist ${target}`) 9 | } 10 | // ------------------------------------------------------------------ 11 | // Clean 12 | // ------------------------------------------------------------------ 13 | export async function clean() { 14 | await folder('target').delete() 15 | } 16 | // ------------------------------------------------------------------ 17 | // Start 18 | // ------------------------------------------------------------------ 19 | export async function start() { 20 | await shell('hammer run example/index.ts --dist target/example') 21 | } 22 | // ------------------------------------------------------------------------------- 23 | // Format 24 | // ------------------------------------------------------------------------------- 25 | export async function format() { 26 | await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write test src') 27 | } 28 | // ------------------------------------------------------------------ 29 | // Test 30 | // ------------------------------------------------------------------ 31 | export async function test(filter = '') { 32 | await shell(`hammer build ./test/index.ts --dist target/test --platform node`) 33 | await shell(`mocha target/test/index.js -g "${filter}"`) 34 | } 35 | // ------------------------------------------------------------------ 36 | // Build 37 | // ------------------------------------------------------------------ 38 | export async function build_check(target = 'target/build') { 39 | const { version } = JSON.parse(Fs.readFileSync('package.json', 'utf8')) 40 | await shell(`cd ${target} && attw sinclair-typemap-${version}.tgz`) 41 | } 42 | export async function build(target = 'target/build') { 43 | await test() 44 | await clean() 45 | await Promise.all([ 46 | Build.Package.build(target), 47 | Build.Esm.build(target), 48 | Build.Cjs.build(target), 49 | ]) 50 | await folder(target).add('readme.md') 51 | await folder(target).add('license') 52 | await shell(`cd ${target} && npm pack`) 53 | await build_check(target) 54 | } 55 | // ------------------------------------------------------------- 56 | // Publish 57 | // ------------------------------------------------------------- 58 | export async function publish(otp, target = 'target/build') { 59 | const { version } = JSON.parse(Fs.readFileSync('package.json', 'utf8')) 60 | if(version.includes('-dev')) throw Error(`package version should not include -dev specifier`) 61 | await shell(`cd ${target} && npm publish sinclair-typemap-${version}.tgz --access=public --otp ${otp}`) 62 | await shell(`git tag ${version}`) 63 | await shell(`git push origin ${version}`) 64 | } 65 | // ------------------------------------------------------------- 66 | // Publish-Dev 67 | // ------------------------------------------------------------- 68 | export async function publish_dev(otp, target = 'target/build') { 69 | const { version } = JSON.parse(Fs.readFileSync(`${target}/package.json`, 'utf8')) 70 | if(!version.includes('-dev')) throw Error(`development package version should include -dev specifier`) 71 | await shell(`cd ${target} && npm publish sinclair-typemap-${version}.tgz --access=public --otp ${otp} --tag dev`) 72 | } -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | @sinclair/typemap 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sinclair/typemap", 3 | "version": "0.10.0", 4 | "description": "Syntax Compiler and Translation System for Runtime Types", 5 | "author": "sinclairzx81", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/sinclairzx81/typemap" 10 | }, 11 | "scripts": { 12 | "benchmark": "hammer task benchmark", 13 | "clean": "hammer task clean", 14 | "start": "hammer task start", 15 | "format": "hammer task format", 16 | "test": "hammer task test", 17 | "build": "hammer task build", 18 | "publish": "hammer task publish" 19 | }, 20 | "peerDependencies": { 21 | "@sinclair/typebox": "^0.34.30", 22 | "valibot": "^1.0.0", 23 | "zod": "^3.24.1" 24 | }, 25 | "devDependencies": { 26 | "@arethetypeswrong/cli": "^0.17.2", 27 | "@sinclair/hammer": "^0.18.0", 28 | "@types/mocha": "^10.0.10", 29 | "@types/node": "^22.10.2", 30 | "mocha": "^11.0.1", 31 | "prettier": "^3.4.2", 32 | "typescript": "^5.7.2" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/compile/compile.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { TypeCompiler, TypeCheck } from '@sinclair/typebox/compiler' 30 | import { Value } from '@sinclair/typebox/value' 31 | import { IsEvalSupported } from './environment' 32 | import { Validator } from './validator' 33 | 34 | import { type TTypeBox, TypeBox } from '../typebox/typebox' 35 | import { type TSyntaxOptions } from '../options' 36 | import { type TParameter } from '../typebox/typebox' 37 | import * as t from '@sinclair/typebox' 38 | import * as g from '../guard' 39 | 40 | // ------------------------------------------------------------------ 41 | // CompileDynamic 42 | // ------------------------------------------------------------------ 43 | // prettier-ignore 44 | function CompileDynamic(type: Type, references: t.TSchema[] = []): TypeCheck { 45 | return new TypeCheck(type, references, value => Value.Check(type, references, value), TypeCompiler.Code(type, references)) 46 | } 47 | // ------------------------------------------------------------------ 48 | // ResolveTypeCheck 49 | // ------------------------------------------------------------------ 50 | function ResolveTypeCheck(type: Type): TypeCheck { 51 | return IsEvalSupported() ? TypeCompiler.Compile(type) : CompileDynamic(type) 52 | } 53 | // ------------------------------------------------------------------ 54 | // Compile 55 | // ------------------------------------------------------------------ 56 | /** Compiles a type for high performance validation */ 57 | // prettier-ignore 58 | type TCompile, 60 | Result = Validator 61 | > = Result 62 | 63 | /** Compiles a type for high performance validation */ 64 | export function Compile(parameter: Parameter, type: Type, options?: TSyntaxOptions): TCompile 65 | /** Compiles a type for high performance validation */ 66 | export function Compile(type: Type, options?: TSyntaxOptions): TCompile<{}, Type> 67 | /** Compiles a type for high performance validation */ 68 | export function Compile(type: Type, options?: TSyntaxOptions): TCompile<{}, Type> 69 | /** Compiles a type for high performance validation */ 70 | // prettier-ignore 71 | export function Compile(...args: any[]): never { 72 | const [parameter, type, options] = g.Signature(args) 73 | const schema = t.ValueGuard.IsString(type) ? TypeBox(parameter, type, options) : TypeBox(type) 74 | const check = ResolveTypeCheck(schema) 75 | return new Validator(check) as never 76 | } 77 | -------------------------------------------------------------------------------- /src/compile/environment.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | /** Cached: flag to indicate if the environment supports runtime evaluation */ 30 | let isEvalSupported: boolean | undefined = undefined 31 | 32 | // prettier-ignore 33 | function TryEval(): boolean { 34 | try { new Function('null')(); return true } catch { return false } 35 | } 36 | /** Tests if the environment supports eval */ 37 | // prettier-ignore 38 | export function IsEvalSupported(): boolean { 39 | if(isEvalSupported === undefined) { 40 | isEvalSupported = TryEval() 41 | } 42 | return isEvalSupported 43 | } 44 | -------------------------------------------------------------------------------- /src/compile/standard.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | MIT License 4 | 5 | Copyright (c) 2024 Colin McDonnell 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | ---------------------------------------------------------------------------*/ 26 | 27 | /** The Standard Schema interface. */ 28 | export interface StandardSchemaV1 { 29 | /** The Standard Schema properties. */ 30 | readonly '~standard': StandardSchemaV1.Props 31 | } 32 | export declare namespace StandardSchemaV1 { 33 | /** The Standard Schema properties interface. */ 34 | export interface Props { 35 | /** The version number of the standard. */ 36 | readonly version: 1 37 | /** The vendor name of the schema library. */ 38 | readonly vendor: string 39 | /** Validates unknown input values. */ 40 | readonly validate: (value: unknown) => Result | Promise> 41 | /** Inferred types associated with the schema. */ 42 | readonly types?: Types | undefined 43 | } 44 | /** The result interface of the validate function. */ 45 | export type Result = SuccessResult | FailureResult 46 | 47 | /** The result interface if validation succeeds. */ 48 | export interface SuccessResult { 49 | /** The typed output value. */ 50 | readonly value: Output 51 | /** The non-existent issues. */ 52 | readonly issues?: undefined 53 | } 54 | /** The result interface if validation fails. */ 55 | export interface FailureResult { 56 | /** The issues of failed validation. */ 57 | readonly issues: ReadonlyArray 58 | } 59 | /** The issue interface of the failure output. */ 60 | export interface Issue { 61 | /** The error message of the issue. */ 62 | readonly message: string 63 | /** The path of the issue, if any. */ 64 | readonly path?: ReadonlyArray | undefined 65 | } 66 | /** The path segment interface of the issue. */ 67 | export interface PathSegment { 68 | /** The key representing a path segment. */ 69 | readonly key: PropertyKey 70 | } 71 | /** The Standard Schema types interface. */ 72 | export interface Types { 73 | /** The input type of the schema. */ 74 | readonly input: Input 75 | /** The output type of the schema. */ 76 | readonly output: Output 77 | } 78 | /** Infers the input type of a Standard Schema. */ 79 | export type InferInput = NonNullable['input'] 80 | /** Infers the output type of a Standard Schema. */ 81 | export type InferOutput = NonNullable['output'] 82 | } 83 | -------------------------------------------------------------------------------- /src/compile/validator.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { TypeCheck, ValueErrorIterator } from '@sinclair/typebox/compiler' 30 | import { Value } from '@sinclair/typebox/value' 31 | import * as t from '@sinclair/typebox' 32 | import * as s from './standard' 33 | 34 | // ------------------------------------------------------------------ 35 | // StandardSchemaProps 36 | // ------------------------------------------------------------------ 37 | // prettier-ignore 38 | export class StandardSchemaProps, 39 | Input extends Static = Static, 40 | Output extends Static = Static 41 | > implements s.StandardSchemaV1.Props { 42 | private readonly __check: TypeCheck 43 | constructor(check: TypeCheck) { 44 | this.__check = check 45 | } 46 | // ---------------------------------------------------------------- 47 | // StandardSchemaV1.Props 48 | // ---------------------------------------------------------------- 49 | public get vendor(): '@sinclair/typemap' { 50 | return '@sinclair/typemap' 51 | } 52 | public get version(): 1 { 53 | return 1 54 | } 55 | public get types(): { input: Input; output: Output } { 56 | throw Error('types is a phantom property used for inference only.') 57 | } 58 | public validate(value: unknown): s.StandardSchemaV1.Result { 59 | return ( 60 | this.__check.Check(value) ? this.__createValue(value) : this.__createIssues(value) 61 | ) as never 62 | } 63 | // ---------------------------------------------------------------- 64 | // Internal 65 | // ---------------------------------------------------------------- 66 | private __createIssues(value: unknown) { 67 | const errors = [...Value.Errors(this.__check.Schema(), value)] 68 | const issues: s.StandardSchemaV1.Issue[] = errors.map((error) => ({ ...error, path: [error.path] })) 69 | return { issues } 70 | } 71 | private __createValue(value: unknown) { 72 | return { value } 73 | } 74 | } 75 | // ------------------------------------------------------------------ 76 | // Validator 77 | // ------------------------------------------------------------------ 78 | export class Validator implements s.StandardSchemaV1, t.Static> { 79 | private readonly __standard: StandardSchemaProps 80 | private readonly __check: TypeCheck 81 | constructor(check: TypeCheck) { 82 | this.__standard = new StandardSchemaProps(check) 83 | this.__check = check 84 | } 85 | /** Standard Schema Interface */ 86 | public get ['~standard'](): StandardSchemaProps { 87 | return this.__standard as never 88 | } 89 | /** Returns the code used by this validator. */ 90 | public Code(): string { 91 | return this.__check.Code() 92 | } 93 | /** Parses this value. Do not use this function for high throughput validation */ 94 | public Parse(value: unknown): t.StaticDecode { 95 | return Value.Parse(this.__check.Schema(), value) 96 | } 97 | /** Checks if this value matches the type */ 98 | public Check(value: unknown): value is t.Static { 99 | return this.__check.Check(value) 100 | } 101 | /** Returns errors for this value */ 102 | public Errors(value: unknown): ValueErrorIterator { 103 | return this.__check.Errors(value) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/guard.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import * as t from '@sinclair/typebox' 30 | import * as v from 'valibot' 31 | import * as z from 'zod' 32 | 33 | /** Structural Type for Syntax */ 34 | export type SyntaxType = string 35 | /** Structural Type for TypeBox */ 36 | export type TypeBoxType = t.TSchema 37 | /** Structural Type for Valibot */ 38 | export type ValibotType = v.BaseSchema> 39 | /** Structural Type for Zod */ 40 | export type ZodType = z.ZodTypeAny | z.ZodEffects 41 | 42 | // ------------------------------------------------------------------ 43 | // Syntax 44 | // ------------------------------------------------------------------ 45 | /** Returns true if the given value is a Syntax type */ 46 | export function IsSyntax(value: unknown): value is string { 47 | return t.ValueGuard.IsString(value) 48 | } 49 | // ------------------------------------------------------------------ 50 | // TypeBox 51 | // ------------------------------------------------------------------ 52 | /** Returns true if the given value is a TypeBox type */ 53 | export function IsTypeBox(type: unknown): type is t.TSchema { 54 | return t.KindGuard.IsSchema(type) 55 | } 56 | // ------------------------------------------------------------------ 57 | // Valibot 58 | // ------------------------------------------------------------------ 59 | /** Returns true if the given value is a Valibot type */ 60 | // prettier-ignore 61 | export function IsValibot(type: unknown): type is v.AnySchema { 62 | return ( 63 | t.ValueGuard.IsObject(type) && 64 | t.ValueGuard.HasPropertyKey(type, '~standard') && 65 | t.ValueGuard.IsObject(type['~standard']) && 66 | t.ValueGuard.HasPropertyKey(type['~standard'], 'vendor') && 67 | type['~standard'].vendor === 'valibot' 68 | ) 69 | } 70 | // ------------------------------------------------------------------ 71 | // Zod 72 | // ------------------------------------------------------------------ 73 | /** Returns true if the given value is a Zod type */ 74 | // prettier-ignore 75 | export function IsZod(type: unknown): type is z.ZodTypeAny { 76 | return ( 77 | t.ValueGuard.IsObject(type) && 78 | t.ValueGuard.HasPropertyKey(type, '~standard') && 79 | t.ValueGuard.IsObject(type['~standard']) && 80 | t.ValueGuard.HasPropertyKey(type['~standard'], 'vendor') && 81 | type['~standard'].vendor === 'zod' 82 | ) 83 | } 84 | // ------------------------------------------------------------------ 85 | // Signature 86 | // ------------------------------------------------------------------ 87 | // (parameter, syntax, options) 88 | function Signature1(args: any[]) { 89 | return args.length === 3 && t.ValueGuard.IsObject(args[0]) && t.ValueGuard.IsString(args[1]) && t.ValueGuard.IsObject(args[2]) 90 | } 91 | // (syntax, options) 92 | function Signature2(args: any[]) { 93 | return args.length === 2 && t.ValueGuard.IsString(args[0]) && t.ValueGuard.IsObject(args[1]) 94 | } 95 | // (parameter, options) 96 | function Signature3(args: any[]) { 97 | return args.length === 2 && t.ValueGuard.IsObject(args[0]) && t.ValueGuard.IsString(args[1]) 98 | } 99 | // (syntax | type) 100 | function Signature4(args: any[]) { 101 | return args.length === 1 && (t.ValueGuard.IsString(args[0]) || t.ValueGuard.IsObject(args[0])) 102 | } 103 | /** Resolve common mapping signature parameters */ 104 | // prettier-ignore 105 | export function Signature(args: any[]): [parameter: Record, type: string | object, options: object] { 106 | return ( 107 | Signature1(args) ? [args[0], args[1], args[2]] : 108 | Signature2(args) ? [{}, args[0], args[1]] : 109 | Signature3(args) ? [args[0], args[1], {}] : 110 | Signature4(args) ? [{}, args[0], {}] : 111 | [{}, 'never', {}] 112 | ) 113 | } 114 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | export { type TSyntaxOptions } from './options' 30 | 31 | // ------------------------------------------------------------------ 32 | // Static 33 | // ------------------------------------------------------------------ 34 | export { type Static } from './static' 35 | 36 | // ------------------------------------------------------------------ 37 | // Compile 38 | // ------------------------------------------------------------------ 39 | export * from './compile/compile' 40 | 41 | // ------------------------------------------------------------------ 42 | // Syntax 43 | // ------------------------------------------------------------------ 44 | export * from './syntax/syntax-from-syntax' 45 | export * from './syntax/syntax-from-typebox' 46 | export * from './syntax/syntax-from-valibot' 47 | export * from './syntax/syntax-from-zod' 48 | export { type TSyntax, Syntax } from './syntax/syntax' 49 | 50 | // ------------------------------------------------------------------ 51 | // TypeBox 52 | // ------------------------------------------------------------------ 53 | export * from './typebox/typebox-from-syntax' 54 | export * from './typebox/typebox-from-typebox' 55 | export * from './typebox/typebox-from-valibot' 56 | export * from './typebox/typebox-from-zod' 57 | export { type TTypeBox, TypeBox } from './typebox/typebox' 58 | 59 | // ------------------------------------------------------------------ 60 | // Valibot 61 | // ------------------------------------------------------------------ 62 | export * from './valibot/valibot-from-syntax' 63 | export * from './valibot/valibot-from-typebox' 64 | export * from './valibot/valibot-from-valibot' 65 | export * from './valibot/valibot-from-zod' 66 | export { type TValibot, Valibot } from './valibot/valibot' 67 | 68 | // ------------------------------------------------------------------ 69 | // Zod 70 | // ------------------------------------------------------------------ 71 | export * from './zod/zod-from-syntax' 72 | export * from './zod/zod-from-typebox' 73 | export * from './zod/zod-from-valibot' 74 | export * from './zod/zod-from-zod' 75 | export { type TZod, Zod } from './zod/zod' 76 | -------------------------------------------------------------------------------- /src/options.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import * as t from '@sinclair/typebox' 30 | 31 | /** Syntax Options and Constraints */ 32 | // prettier-ignore 33 | export type TSyntaxOptions = ( 34 | t.ObjectOptions 35 | & t.ArrayOptions 36 | & t.NumberOptions 37 | & t.IntegerOptions 38 | & t.StringOptions 39 | & t.DateOptions 40 | ) 41 | -------------------------------------------------------------------------------- /src/static.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { Validator } from './compile/validator' 30 | import * as s from '@sinclair/typebox/syntax' 31 | import * as t from '@sinclair/typebox' 32 | import * as v from 'valibot' 33 | import * as z from 'zod' 34 | 35 | type BaseSchema = v.BaseSchema> 36 | 37 | /** Statically infers a type */ 38 | // prettier-ignore 39 | export type Static = ( 40 | Type extends string ? s.TSyntax<{}, Type> : 41 | Type extends Validator ? t.Static : 42 | Type extends t.TSchema ? t.Static : 43 | Type extends BaseSchema ? v.InferInput : 44 | Type extends z.ZodTypeAny ? z.infer : 45 | never 46 | ) 47 | -------------------------------------------------------------------------------- /src/syntax/syntax-from-syntax.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | export type TSyntaxFromSyntax = Type 30 | 31 | export function SyntaxFromSyntax(type: Type): TSyntaxFromSyntax { 32 | return type 33 | } 34 | -------------------------------------------------------------------------------- /src/syntax/syntax-from-typebox.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import * as t from '@sinclair/typebox' 30 | 31 | // ------------------------------------------------------------------ 32 | // Characters 33 | // ------------------------------------------------------------------ 34 | type TEmptyString = '' 35 | 36 | type TAmpersand = '&' 37 | type TComma = ',' 38 | type TPipe = '|' 39 | 40 | const Ampersand = '&' 41 | const Comma = ',' 42 | const Pipe = '|' 43 | 44 | // ------------------------------------------------------------------ 45 | // Delmited 46 | // ------------------------------------------------------------------ 47 | // prettier-ignore 48 | type TFromDelimited = ( 49 | Values extends [infer Left extends string, ...infer Right extends string[]] 50 | ? Result extends TEmptyString 51 | ? TFromDelimited 52 | : TFromDelimited 53 | : Result 54 | ) 55 | function FromDelimited(values: string[], delimiter: string): string { 56 | return values.join(delimiter) 57 | } 58 | // ------------------------------------------------------------------ 59 | // Any 60 | // ------------------------------------------------------------------ 61 | type TFromAny<_Type extends t.TAny> = 'any' 62 | function FromAny(_type: t.TSchema): string { 63 | return 'any' 64 | } 65 | // ------------------------------------------------------------------ 66 | // Array 67 | // ------------------------------------------------------------------ 68 | // prettier-ignore 69 | type TFromArray}[]` 71 | > = Result 72 | function FromArray(type: t.TSchema): string { 73 | return `${FromType(type)}[]` 74 | } 75 | // ------------------------------------------------------------------ 76 | // BigInt 77 | // ------------------------------------------------------------------ 78 | type TFromBigInt<_Type extends t.TBigInt> = 'bigint' 79 | function FromBigInt(_type: t.TSchema): string { 80 | return 'bigint' 81 | } 82 | // ------------------------------------------------------------------ 83 | // Boolean 84 | // ------------------------------------------------------------------ 85 | type TFromBoolean<_Type extends t.TBoolean> = 'boolean' 86 | function FromBoolean(_type: t.TSchema): string { 87 | return 'boolean' 88 | } 89 | // ------------------------------------------------------------------ 90 | // Constructor 91 | // ------------------------------------------------------------------ 92 | // prettier-ignore 93 | type TFromConstructor, 95 | MappedInstanceType extends string = TFromType 96 | > = `new ${MappedParameters} => ${MappedInstanceType}` 97 | // prettier-ignore 98 | function FromConstructor(parameters: t.TSchema[], instanceType: t.TSchema): string { 99 | const mappedParameters = FromParameters(parameters) 100 | const mappedInstanceType = FromType(instanceType) 101 | return `new ${mappedParameters} => ${mappedInstanceType}` 102 | } 103 | // ------------------------------------------------------------------ 104 | // Function 105 | // ------------------------------------------------------------------ 106 | // prettier-ignore 107 | type TFromFunction, 109 | MappedReturnType extends string = TFromType 110 | > = `${MappedParameters} => ${MappedReturnType}` 111 | // prettier-ignore 112 | function FromFunction(parameters: t.TSchema[], returnType: t.TSchema): string { 113 | const mappedParameters = FromParameters(parameters) 114 | const mappedReturnType = FromType(returnType) 115 | return `${mappedParameters} => ${mappedReturnType}` 116 | } 117 | // ------------------------------------------------------------------ 118 | // Integer 119 | // ------------------------------------------------------------------ 120 | type TFromInteger<_Type extends t.TInteger> = 'integer' 121 | function FromInteger(_type: t.TSchema): string { 122 | return 'integer' 123 | } 124 | // ------------------------------------------------------------------ 125 | // Intersect 126 | // ------------------------------------------------------------------ 127 | // prettier-ignore 128 | type TFromIntersect = ( 129 | Types extends [infer Left extends t.TSchema, ...infer Right extends t.TSchema[]] 130 | ? TFromIntersect]> 131 | : `(${TFromDelimited})` 132 | ) 133 | function FromIntersect(types: t.TSchema[]): string { 134 | const result = types.map((type) => FromType(type)) 135 | return `(${FromDelimited(result, ` ${Ampersand} `)})` 136 | } 137 | // ------------------------------------------------------------------ 138 | // Literal 139 | // ------------------------------------------------------------------ 140 | // prettier-ignore 141 | type TFromLiteral = Result 144 | // prettier-ignore 145 | function FromLiteral(value: t.TLiteralValue): string { 146 | return t.ValueGuard.IsString(value) ? `"${value}"` : `${value}` 147 | } 148 | // ------------------------------------------------------------------ 149 | // Number 150 | // ------------------------------------------------------------------ 151 | type TFromNumber<_Type extends t.TNumber> = 'number' 152 | function FromNumber(_type: t.TSchema): string { 153 | return 'number' 154 | } 155 | // ------------------------------------------------------------------ 156 | // Null 157 | // ------------------------------------------------------------------ 158 | type TFromNull<_Type extends t.TNull> = 'null' 159 | function FromNull(_type: t.TSchema): string { 160 | return 'null' 161 | } 162 | // ------------------------------------------------------------------ 163 | // Object 164 | // ------------------------------------------------------------------ 165 | // prettier-ignore 166 | type TFromObject, 168 | Delimited extends string[] = TFromProperties, 169 | Result extends string = TFromDelimited 170 | > = `{ ${Result} }` 171 | function FromObject(properties: t.TProperties): string { 172 | const propertyKeys = globalThis.Object.getOwnPropertyNames(properties) 173 | const delimited = FromProperties(propertyKeys, properties) 174 | const result = FromDelimited(delimited, `${Comma} `) 175 | return `{ ${result} }` 176 | } 177 | // ------------------------------------------------------------------ 178 | // Parameters 179 | // ------------------------------------------------------------------ 180 | // prettier-ignore 181 | type TFromParameters = ( 182 | Parameters extends [infer Left extends t.TSchema, ...infer Right extends t.TSchema[]] 183 | ? TFromParameters, [...Result, `arg${Index}: ${TFromType}`]> 184 | : `(${TFromDelimited})` 185 | ) 186 | function FromParameters(parameters: t.TSchema[]): string { 187 | const result = parameters.map((parameter, index) => `arg${index}: ${FromType(parameter)}`) 188 | return `(${FromDelimited(result, `${Comma} `)})` 189 | } 190 | // ------------------------------------------------------------------ 191 | // Property 192 | // ------------------------------------------------------------------ 193 | // prettier-ignore 194 | type TFromProperty ? true : false, 196 | IsReadonly extends boolean = Type extends t.TReadonly ? true : false, 197 | Mapped extends string = TFromType, 198 | Result = ( 199 | [IsReadonly, IsOptional] extends [true, true] ? `readonly ${Key}?: ${Mapped}` : 200 | [IsReadonly, IsOptional] extends [false, true] ? `${Key}?: ${Mapped}` : 201 | [IsReadonly, IsOptional] extends [true, false] ? `readonly ${Key}: ${Mapped}` : 202 | `${Key}: ${Mapped}` 203 | ) 204 | > = Result 205 | // prettier-ignore 206 | function FromProperty(key: string, type: t.TSchema): string { 207 | const isOptional = t.KindGuard.IsOptional(type) 208 | const isReadonly = t.KindGuard.IsReadonly(type) 209 | const mapped = FromType(type) 210 | return ( 211 | isReadonly && isOptional ? `readonly ${key}?: ${mapped}` : 212 | !isReadonly && isOptional ? `${key}?: ${mapped}` : 213 | isReadonly && !isOptional ? `readonly ${key}: ${mapped}` : 214 | `${key}: ${mapped}` 215 | ) 216 | } 217 | // ------------------------------------------------------------------ 218 | // Properties 219 | // ------------------------------------------------------------------ 220 | // prettier-ignore 221 | type TFromProperties = ( 222 | PropertyKeys extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] 223 | ? (Left extends infer Key extends string 224 | ? (Key extends keyof Properties 225 | ? TFromProperties]> 226 | : TFromProperties 227 | ) : TFromProperties 228 | ) : Result 229 | ) 230 | // prettier-ignore 231 | function FromProperties(propertyKeys: PropertyKey[], properties: t.TProperties): string[] { 232 | return propertyKeys.reduce((result, left) => { 233 | const key = t.ValueGuard.IsString(left) || t.ValueGuard.IsNumber(left) || t.ValueGuard.IsBoolean(left) ? `${left}` : undefined 234 | return ( 235 | t.ValueGuard.IsString(key) 236 | ? (key in properties 237 | ? [...result, FromProperty(key, properties[key])] 238 | : result 239 | ): result 240 | ) 241 | }, [] as string[]) 242 | } 243 | // ------------------------------------------------------------------ 244 | // Promise 245 | // ------------------------------------------------------------------ 246 | type TFromPromise}>`> = Result 247 | function FromPromise(type: t.TSchema): string { 248 | return `Promise<${FromType(type)}>` 249 | } 250 | // ------------------------------------------------------------------ 251 | // String 252 | // ------------------------------------------------------------------ 253 | type TFromString<_Type extends t.TString> = 'string' 254 | function FromString(_type: t.TSchema): string { 255 | return 'string' 256 | } 257 | // ------------------------------------------------------------------ 258 | // Symbol 259 | // ------------------------------------------------------------------ 260 | type TFromSymbol<_Type extends t.TSymbol> = 'symbol' 261 | function FromSymbol(_type: t.TSchema): string { 262 | return 'symbol' 263 | } 264 | // ------------------------------------------------------------------ 265 | // Union 266 | // ------------------------------------------------------------------ 267 | // prettier-ignore 268 | type TFromTuple = ( 269 | Types extends [infer Left extends t.TSchema, ...infer Right extends t.TSchema[]] 270 | ? TFromTuple]> 271 | : `[${TFromDelimited}]` 272 | ) 273 | function FromTuple(types: t.TSchema[]): string { 274 | const result = types.map((type) => FromType(type)) 275 | return `[${FromDelimited(result, `${Comma} `)}]` 276 | } 277 | // ------------------------------------------------------------------ 278 | // Undefined 279 | // ------------------------------------------------------------------ 280 | type TFromUndefined<_Type extends t.TUndefined> = 'undefined' 281 | function FromUndefined(type: t.TSchema): string { 282 | return 'undefined' 283 | } 284 | // ------------------------------------------------------------------ 285 | // Union 286 | // ------------------------------------------------------------------ 287 | // prettier-ignore 288 | type TFromUnion = ( 289 | Types extends [infer Left extends t.TSchema, ...infer Right extends t.TSchema[]] 290 | ? TFromUnion]> 291 | : `(${TFromDelimited})` 292 | ) 293 | function FromUnion(types: t.TSchema[]): string { 294 | const result = types.map((type) => FromType(type)) 295 | return `(${FromDelimited(result, ` ${Pipe} `)})` 296 | } 297 | // ------------------------------------------------------------------ 298 | // Unknown 299 | // ------------------------------------------------------------------ 300 | type TFromUnknown = 'unknown' 301 | function FromUnknown(type: t.TSchema): string { 302 | return 'unknown' 303 | } 304 | // ------------------------------------------------------------------ 305 | // Void 306 | // ------------------------------------------------------------------ 307 | type TFromVoid = 'void' 308 | function FromVoid(type: t.TSchema): string { 309 | return 'void' 310 | } 311 | // ------------------------------------------------------------------ 312 | // FromType 313 | // ------------------------------------------------------------------ 314 | // prettier-ignore 315 | type TFromType = ( 316 | Type extends t.TAny ? TFromAny : 317 | Type extends t.TArray ? TFromArray : 318 | Type extends t.TBigInt ? TFromBigInt : 319 | Type extends t.TBoolean ? TFromBoolean : 320 | Type extends t.TConstructor ? TFromConstructor : 321 | Type extends t.TFunction ? TFromFunction : 322 | Type extends t.TInteger ? TFromInteger : 323 | Type extends t.Intersect ? TFromIntersect : 324 | Type extends t.TLiteral ? TFromLiteral : 325 | Type extends t.TNumber ? TFromNumber : 326 | Type extends t.TNull ? TFromNull : 327 | Type extends t.TObject ? TFromObject : 328 | Type extends t.TPromise ? TFromPromise : 329 | Type extends t.TString ? TFromString : 330 | Type extends t.TSymbol ? TFromSymbol : 331 | Type extends t.TTuple ? TFromTuple : 332 | Type extends t.TUndefined ? TFromUndefined : 333 | Type extends t.TUnion ? TFromUnion : 334 | Type extends t.TUnknown ? TFromUnknown : 335 | Type extends t.TVoid ? TFromVoid : 336 | 'never' 337 | ) 338 | // prettier-ignore 339 | function FromType(type: Type): TFromType { 340 | return ( 341 | t.KindGuard.IsAny(type) ? FromAny(type) : 342 | t.KindGuard.IsArray(type) ? FromArray(type.items) : 343 | t.KindGuard.IsBigInt(type) ? FromBigInt(type) : 344 | t.KindGuard.IsBoolean(type) ? FromBoolean(type) : 345 | t.KindGuard.IsConstructor(type) ? FromConstructor(type.parameters, type.returns) : 346 | t.KindGuard.IsFunction(type) ? FromFunction(type.parameters, type.returns) : 347 | t.KindGuard.IsInteger(type) ? FromInteger(type) : 348 | t.KindGuard.IsIntersect(type) ? FromIntersect(type.allOf) : 349 | t.KindGuard.IsLiteral(type) ? FromLiteral(type.const) : 350 | t.KindGuard.IsNumber(type) ? FromNumber(type) : 351 | t.KindGuard.IsNull(type) ? FromNull(type) : 352 | t.KindGuard.IsObject(type) ? FromObject(type.properties) : 353 | t.KindGuard.IsPromise(type) ? FromPromise(type.item) : 354 | t.KindGuard.IsString(type) ? FromString(type) : 355 | t.KindGuard.IsSymbol(type) ? FromSymbol(type) : 356 | t.KindGuard.IsTuple(type) ? FromTuple(type.items || []) : 357 | t.KindGuard.IsUndefined(type) ? FromUndefined(type) : 358 | t.KindGuard.IsUnion(type) ? FromUnion(type.anyOf) : 359 | t.KindGuard.IsUnknown(type) ? FromUnknown(type) : 360 | t.KindGuard.IsVoid(type) ? FromVoid(type) : 361 | 'never' 362 | ) as never 363 | } 364 | 365 | // ------------------------------------------------------------------ 366 | // SyntaxFromTypeBox 367 | // ------------------------------------------------------------------ 368 | // prettier-ignore 369 | export type TSyntaxFromTypeBox 371 | > = Result 372 | export function SyntaxFromTypeBox(type: t.TSchema): TSyntaxFromTypeBox { 373 | return FromType(type) as never 374 | } 375 | -------------------------------------------------------------------------------- /src/syntax/syntax-from-valibot.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { type TTypeBoxFromValibot, TypeBoxFromValibot } from '../typebox/typebox-from-valibot' 30 | import { type TSyntaxFromTypeBox, SyntaxFromTypeBox } from './syntax-from-typebox' 31 | 32 | import * as t from '@sinclair/typebox' 33 | import * as v from 'valibot' 34 | 35 | /** Creates Syntax from Valibot */ 36 | // prettier-ignore 37 | export type TSyntaxFromValibot, 38 | TypeBox extends t.TSchema = TTypeBoxFromValibot, 39 | Result extends string = TSyntaxFromTypeBox 40 | > = Result 41 | 42 | /** Creates Syntax from Valibot */ 43 | // prettier-ignore 44 | export function SyntaxFromValibot>(type: Type): TSyntaxFromValibot { 45 | const typebox = TypeBoxFromValibot(type) 46 | const result = SyntaxFromTypeBox(typebox) 47 | return result as never 48 | } 49 | -------------------------------------------------------------------------------- /src/syntax/syntax-from-zod.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { type TTypeBoxFromZod, TypeBoxFromZod } from '../typebox/typebox-from-zod' 30 | import { type TSyntaxFromTypeBox, SyntaxFromTypeBox } from './syntax-from-typebox' 31 | 32 | import * as t from '@sinclair/typebox' 33 | import * as z from 'zod' 34 | 35 | /** Creates Syntax from Zod */ 36 | // prettier-ignore 37 | export type TSyntaxFromZod, 38 | TypeBox extends t.TSchema = TTypeBoxFromZod, 39 | Result extends string = TSyntaxFromTypeBox 40 | > = Result 41 | 42 | /** Creates Syntax from Zod */ 43 | // prettier-ignore 44 | export function SyntaxFromZod>(type: Type): TSyntaxFromZod { 45 | const typebox = TypeBoxFromZod(type) 46 | const result = SyntaxFromTypeBox(typebox) 47 | return result as never 48 | } 49 | -------------------------------------------------------------------------------- /src/syntax/syntax.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { type TSyntaxFromSyntax, SyntaxFromSyntax } from './syntax-from-syntax' 30 | import { type TSyntaxFromTypeBox, SyntaxFromTypeBox } from './syntax-from-typebox' 31 | import { type TSyntaxFromValibot, SyntaxFromValibot } from './syntax-from-valibot' 32 | import { type TSyntaxFromZod, SyntaxFromZod } from './syntax-from-zod' 33 | import { type TSyntaxOptions } from '../options' 34 | import { type TParameter } from '../typebox/typebox' 35 | 36 | import * as g from '../guard' 37 | import * as z from 'zod' 38 | 39 | // ------------------------------------------------------------------ 40 | // Zod 41 | // ------------------------------------------------------------------ 42 | /** Creates Syntax by mapping from a remote Type */ 43 | // prettier-ignore 44 | export type TSyntax<_Parameter extends TParameter, Type extends object | string, Result extends string = ( 45 | Type extends g.SyntaxType ? TSyntaxFromSyntax : 46 | Type extends g.TypeBoxType ? TSyntaxFromTypeBox : 47 | Type extends g.ValibotType ? TSyntaxFromValibot : 48 | Type extends g.ZodType ? TSyntaxFromZod : 49 | 'never' 50 | )> = Result 51 | 52 | /** Creates Syntax by mapping from a remote Type */ 53 | export function Syntax(parameter: Parameter, type: Type, options?: TSyntaxOptions): TSyntax 54 | /** Creates Syntax by mapping from a remote Type */ 55 | export function Syntax(type: Type, options?: TSyntaxOptions): TSyntax<{}, Type> 56 | /** Creates Syntax by mapping from a remote Type */ 57 | export function Syntax(type: Type, options?: TSyntaxOptions): TSyntax<{}, Type> 58 | /** Creates Syntax by mapping from a remote Type */ 59 | // prettier-ignore 60 | export function Syntax(...args: any[]): never { 61 | const [_parameter, type, _options] = g.Signature(args) 62 | return ( 63 | g.IsSyntax(type) ? SyntaxFromSyntax(type) : 64 | g.IsTypeBox(type) ? SyntaxFromTypeBox(type) : 65 | g.IsValibot(type) ? SyntaxFromValibot(type) : 66 | g.IsZod(type) ? SyntaxFromZod(type) : 67 | z.never() 68 | ) as never 69 | } 70 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "files": ["index.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /src/typebox/typebox-from-syntax.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { TSyntax, Syntax } from '@sinclair/typebox/syntax' 30 | import * as t from '@sinclair/typebox' 31 | 32 | // ------------------------------------------------------------------ 33 | // TypeBoxFromSyntax 34 | // ------------------------------------------------------------------ 35 | 36 | // prettier-ignore 37 | export type TTypeBoxFromSyntax 39 | > = Result 40 | 41 | /** Creates a TypeBox Type From Syntax */ 42 | export function TypeBoxFromSyntax(context: Context, type: Type, options?: t.SchemaOptions): TTypeBoxFromSyntax { 43 | return Syntax(context, type, options) 44 | } 45 | -------------------------------------------------------------------------------- /src/typebox/typebox-from-typebox.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import * as t from '@sinclair/typebox' 30 | 31 | // ------------------------------------------------------------------ 32 | // TypeBoxFromTypeBox 33 | // ------------------------------------------------------------------ 34 | /** Creates a TypeBox type from TypeBox */ 35 | // prettier-ignore 36 | export type TTypeBoxFromTypeBox = Type 37 | 38 | /** Creates a TypeBox type from TypeBox */ 39 | // prettier-ignore 40 | export function TypeBoxFromTypeBox(type: Type): TTypeBoxFromTypeBox { 41 | return t.CloneType(type) 42 | } 43 | -------------------------------------------------------------------------------- /src/typebox/typebox.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { type TTypeBoxFromSyntax, TypeBoxFromSyntax } from './typebox-from-syntax' 30 | import { type TTypeBoxFromTypeBox, TypeBoxFromTypeBox } from './typebox-from-typebox' 31 | import { type TTypeBoxFromValibot, TypeBoxFromValibot } from './typebox-from-valibot' 32 | import { type TTypeBoxFromZod, TypeBoxFromZod } from './typebox-from-zod' 33 | import { type TSyntaxOptions } from '../options' 34 | 35 | import * as g from '../guard' 36 | import * as t from '@sinclair/typebox' 37 | 38 | // ------------------------------------------------------------------------------ 39 | // 40 | // TParameter: Shared 41 | // 42 | // TypeBox supports Type injection via a Context parameter. Because the Context 43 | // only accepts types of TSchema, we need to an intermediate structure to hold 44 | // the remote types such that they can be mapped prior to syntax parsing. 45 | // 46 | // ------------------------------------------------------------------------------- 47 | export type TParameter = Record 48 | 49 | // ------------------------------------------------------------------ 50 | // ContextFromParameter 51 | // ------------------------------------------------------------------ 52 | // prettier-ignore 53 | export type TContextFromParameter 56 | } 57 | > = Result 58 | // prettier-ignore 59 | export function ContextFromParameter(parameter: Parameter): TContextFromParameter { 60 | return globalThis.Object.getOwnPropertyNames(parameter).reduce((result, key) => { 61 | return { ...result, [key]: TypeBox(parameter[key] as never) } 62 | }, {} as t.TProperties) as never 63 | } 64 | 65 | // ------------------------------------------------------------------ 66 | // TypeBox 67 | // ------------------------------------------------------------------ 68 | /** Creates a TypeBox type by mapping from a remote Type */ 69 | // prettier-ignore 70 | export type TTypeBox, Type> : 72 | Type extends g.TypeBoxType ? TTypeBoxFromTypeBox : 73 | Type extends g.ValibotType ? TTypeBoxFromValibot : 74 | Type extends g.ZodType ? TTypeBoxFromZod : 75 | t.TNever 76 | )> = Result 77 | /** Creates a TypeBox type by mapping from a remote Type */ 78 | export function TypeBox(parameter: Parameter, type: Type, options?: TSyntaxOptions): TTypeBox 79 | /** Creates a TypeBox type by mapping from a remote Type */ 80 | export function TypeBox(type: Type, options?: TSyntaxOptions): TTypeBox<{}, Type> 81 | /** Creates a TypeBox type by mapping from a remote Type */ 82 | // prettier-ignore 83 | export function TypeBox(...args: any[]): never { 84 | const [parameter, type, options] = g.Signature(args) 85 | return ( 86 | g.IsSyntax(type) ? TypeBoxFromSyntax(ContextFromParameter(parameter), type, options) : 87 | g.IsTypeBox(type) ? TypeBoxFromTypeBox(type) : 88 | g.IsValibot(type) ? TypeBoxFromValibot(type) : 89 | g.IsZod(type) ? TypeBoxFromZod(type) : 90 | t.Never() 91 | ) as never 92 | } 93 | 94 | /** Creates a TypeBox type from Syntax or another Type */ 95 | export function Type(parameter: Parameter, type: Type, options?: TSyntaxOptions): TTypeBox 96 | /** Creates a TypeBox type from Syntax or another Type */ 97 | export function Type(type: Type, options?: TSyntaxOptions): TTypeBox<{}, Type> 98 | /** Creates a TypeBox type from Syntax or another Type */ 99 | // prettier-ignore 100 | export function Type(...args: any[]): never { 101 | return TypeBox.apply(null, args as never) as never 102 | } 103 | -------------------------------------------------------------------------------- /src/valibot/common.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import * as v from 'valibot' 30 | 31 | // Valibot really should consider providing default generic type parameters 32 | export type BaseConstraint = v.BaseValidation> | v.BaseMetadata | v.RegexAction 33 | export type BaseRecordKey = v.BaseSchema> 34 | export type BaseSchema = v.BaseSchema> 35 | export type BaseError = undefined // v.ErrorMessage 36 | -------------------------------------------------------------------------------- /src/valibot/valibot-from-syntax.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { TTypeBoxFromSyntax, TypeBoxFromSyntax } from '../typebox/typebox-from-syntax' 30 | import { ValibotFromTypeBox, TValibotFromTypeBox } from './valibot-from-typebox' 31 | 32 | import * as t from '@sinclair/typebox' 33 | import * as v from 'valibot' 34 | 35 | // ------------------------------------------------------------------ 36 | // ValibotFromSyntax 37 | // ------------------------------------------------------------------ 38 | /** Creates a Valibot type from Syntax */ 39 | // prettier-ignore 40 | export type TValibotFromSyntax, 42 | Result extends v.BaseSchema = TValibotFromTypeBox 43 | > = Result 44 | /** Creates a Valibot type from Syntax */ 45 | // prettier-ignore 46 | export function ValibotFromSyntax(context: Context, type: Type, options?: t.SchemaOptions): TValibotFromSyntax { 47 | const typebox = TypeBoxFromSyntax(context, type, options) 48 | const result = ValibotFromTypeBox(typebox) 49 | return result as never 50 | } 51 | -------------------------------------------------------------------------------- /src/valibot/valibot-from-valibot.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import * as v from 'valibot' 30 | 31 | // ------------------------------------------------------------------ 32 | // ValibotFromValibot 33 | // ------------------------------------------------------------------ 34 | /** Creates a Valibot type from Valibot */ 35 | // prettier-ignore 36 | export type TValibotFromValibot, 37 | Result extends v.BaseSchema = Type 38 | > = Result 39 | 40 | /** Creates a Valibot type from Valibot */ 41 | // prettier-ignore 42 | export function ValibotFromValibot>(type: Type): TValibotFromValibot { 43 | return type 44 | } 45 | -------------------------------------------------------------------------------- /src/valibot/valibot-from-zod.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { type TTypeBoxFromZod, TypeBoxFromZod } from '../typebox/typebox-from-zod' 30 | import { type TValibotFromTypeBox, ValibotFromTypeBox } from './valibot-from-typebox' 31 | 32 | import * as t from '@sinclair/typebox' 33 | import * as v from 'valibot' 34 | import * as z from 'zod' 35 | 36 | // ------------------------------------------------------------------ 37 | // ValibotFromZod 38 | // ------------------------------------------------------------------ 39 | /** Creates a Valibot type from Zod */ 40 | // prettier-ignore 41 | export type TValibotFromZod, 42 | TypeBox extends t.TSchema = TTypeBoxFromZod, 43 | Result extends v.BaseSchema = TValibotFromTypeBox 44 | > = Result 45 | /** Creates a Valibot type from Zod */ 46 | // prettier-ignore 47 | export function ValibotFromZod, 48 | Result extends v.BaseSchema = TValibotFromZod 49 | >(type: Type): Result { 50 | const schema = TypeBoxFromZod(type) 51 | const result = ValibotFromTypeBox(schema) 52 | return result as never 53 | } 54 | -------------------------------------------------------------------------------- /src/valibot/valibot.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { type TValibotFromSyntax, ValibotFromSyntax } from './valibot-from-syntax' 30 | import { type TValibotFromTypeBox, ValibotFromTypeBox } from './valibot-from-typebox' 31 | import { type TValibotFromValibot, ValibotFromValibot } from './valibot-from-valibot' 32 | import { type TValibotFromZod, ValibotFromZod } from './valibot-from-zod' 33 | import { type TSyntaxOptions } from '../options' 34 | 35 | import { type TParameter, type TContextFromParameter, ContextFromParameter } from '../typebox/typebox' 36 | 37 | import * as g from '../guard' 38 | import * as t from '@sinclair/typebox' 39 | import * as v from 'valibot' 40 | import * as c from './common' 41 | 42 | // ------------------------------------------------------------------ 43 | // Valibot 44 | // ------------------------------------------------------------------ 45 | /** Creates a Valibot type by mapping from a remote Type */ 46 | // prettier-ignore 47 | export type TValibot, Type> : 49 | Type extends t.TSchema ? TValibotFromTypeBox : 50 | Type extends g.ValibotType ? TValibotFromValibot : 51 | Type extends g.ZodType ? TValibotFromZod : 52 | v.NeverSchema 53 | )> = Result 54 | 55 | /** Creates a Valibot type by mapping from a remote Type */ 56 | export function Valibot(parameter: Parameter, type: Type, options?: TSyntaxOptions): TValibot 57 | /** Creates a Valibot type by mapping from a remote Type */ 58 | export function Valibot(type: Type, options?: TSyntaxOptions): TValibot<{}, Type> 59 | /** Creates a Valibot type by mapping from a remote Type */ 60 | export function Valibot(type: Type, options?: TSyntaxOptions): TValibot<{}, Type> 61 | /** Creates a Valibot type by mapping from a remote Type */ 62 | // prettier-ignore 63 | export function Valibot(...args: any[]): never { 64 | const [parameter, type, options] = g.Signature(args) 65 | return ( 66 | g.IsSyntax(type) ? ValibotFromSyntax(ContextFromParameter(parameter), type, options) : 67 | g.IsTypeBox(type) ? ValibotFromTypeBox(type) : 68 | g.IsValibot(type) ? ValibotFromValibot(type) : 69 | g.IsZod(type) ? ValibotFromZod(type as any) : 70 | v.never() 71 | ) as never 72 | } 73 | -------------------------------------------------------------------------------- /src/zod/zod-from-syntax.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { TTypeBoxFromSyntax, TypeBoxFromSyntax } from '../typebox/typebox-from-syntax' 30 | import { ZodFromTypeBox, TZodFromTypeBox } from './zod-from-typebox' 31 | import * as t from '@sinclair/typebox' 32 | import * as z from 'zod' 33 | 34 | // ------------------------------------------------------------------ 35 | // ZodFromSyntax 36 | // ------------------------------------------------------------------ 37 | /** Creates a Zod type from Syntax */ 38 | // prettier-ignore 39 | export type TZodFromSyntax, 41 | Result extends z.ZodTypeAny | z.ZodEffects = TZodFromTypeBox 42 | > = Result 43 | /** Creates a Zod type from Syntax */ 44 | export function ZodFromSyntax(context: Context, type: Type, options?: t.SchemaOptions): TZodFromSyntax { 45 | const typebox = TypeBoxFromSyntax(context, type, options) 46 | const result = ZodFromTypeBox(typebox) 47 | return result as never 48 | } 49 | -------------------------------------------------------------------------------- /src/zod/zod-from-valibot.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { type TTypeBoxFromValibot, TypeBoxFromValibot } from '../typebox/typebox-from-valibot' 30 | import { type TZodFromTypeBox, ZodFromTypeBox } from './zod-from-typebox' 31 | 32 | import * as t from '@sinclair/typebox' 33 | import * as v from 'valibot' 34 | import * as z from 'zod' 35 | 36 | /** Creates a Zod type from Valibot */ 37 | // prettier-ignore 38 | export type TZodFromValibot, 39 | TypeBox extends t.TSchema = TTypeBoxFromValibot, 40 | Result extends z.ZodTypeAny | z.ZodEffects = TZodFromTypeBox 41 | > = Result 42 | 43 | /** Creates a Zod type from Valibot */ 44 | // prettier-ignore 45 | export function ZodFromValibot>(type: Type): TZodFromValibot { 46 | const typebox = TypeBoxFromValibot(type) 47 | const result = ZodFromTypeBox(typebox) 48 | return result 49 | } 50 | -------------------------------------------------------------------------------- /src/zod/zod-from-zod.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import * as z from 'zod' 30 | 31 | /** Creates a Zod type from Zod */ 32 | // prettier-ignore 33 | export type TZodFromZod, 34 | Result = Type 35 | > = Result 36 | 37 | /** Creates a Zod type from Zod */ 38 | // prettier-ignore 39 | export function ZodFromZod>(type: Type): TZodFromZod { 40 | return type as never 41 | } 42 | -------------------------------------------------------------------------------- /src/zod/zod.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2024-2025 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { type TZodFromSyntax, ZodFromSyntax } from './zod-from-syntax' 30 | import { type TZodFromTypeBox, ZodFromTypeBox } from './zod-from-typebox' 31 | import { type TZodFromValibot, ZodFromValibot } from './zod-from-valibot' 32 | import { type TZodFromZod, ZodFromZod } from './zod-from-zod' 33 | import { type TSyntaxOptions } from '../options' 34 | 35 | import { type TParameter, type TContextFromParameter, ContextFromParameter } from '../typebox/typebox' 36 | 37 | import * as g from '../guard' 38 | import * as z from 'zod' 39 | 40 | // ------------------------------------------------------------------ 41 | // Zod 42 | // ------------------------------------------------------------------ 43 | /** Creates a Zod type by mapping from a remote Type */ 44 | // prettier-ignore 45 | export type TZod = ( 46 | Type extends g.SyntaxType ? TZodFromSyntax, Type> : 47 | Type extends g.TypeBoxType ? TZodFromTypeBox : 48 | Type extends g.ValibotType ? TZodFromValibot : 49 | Type extends g.ZodType ? TZodFromZod : 50 | z.ZodNever 51 | )> = Result 52 | 53 | /** Creates a Zod type by mapping from a remote Type */ 54 | export function Zod(parameter: Parameter, type: Type, options?: TSyntaxOptions): TZod 55 | /** Creates a Zod type by mapping from a remote Type */ 56 | export function Zod(type: Type, options?: TSyntaxOptions): TZod<{}, Type> 57 | /** Creates a Zod type by mapping from a remote Type */ 58 | export function Zod(type: Type, options?: TSyntaxOptions): TZod<{}, Type> 59 | /** Creates a Zod type by mapping from a remote Type */ 60 | // prettier-ignore 61 | export function Zod(...args: any[]): never { 62 | const [parameter, type, options] = g.Signature(args) 63 | return ( 64 | g.IsSyntax(type) ? ZodFromSyntax(ContextFromParameter(parameter), type, options) : 65 | g.IsTypeBox(type) ? ZodFromTypeBox(type) : 66 | g.IsValibot(type) ? ZodFromValibot(type) : 67 | g.IsZod(type) ? ZodFromZod(type) : 68 | z.never() 69 | ) as never 70 | } 71 | -------------------------------------------------------------------------------- /task/benchmark/index.ts: -------------------------------------------------------------------------------- 1 | import { Value } from '@sinclair/typebox/value' 2 | import { Compile, TypeBox } from '@sinclair/typemap' 3 | 4 | import * as v from 'valibot' 5 | import * as z from 'zod' 6 | 7 | // ------------------------------------------------------------------ 8 | // Benchmark 9 | // ------------------------------------------------------------------ 10 | function benchmark(library: string, using: string, callback: Function) { 11 | const [now, iterations] = [Date.now(), 10_000_000] 12 | for (let i = 0; i < iterations; i++) if (!callback()) throw Error('Invalid' + library + using) 13 | const elapsed = `${Date.now() - now} ms`.padEnd(8) 14 | return { library: library.padEnd(12), using: using.padEnd(16), iterations, elapsed } 15 | } 16 | // ------------------------------------------------------------------ 17 | // Zod 18 | // ------------------------------------------------------------------ 19 | function zod() { 20 | const T = z.object({ 21 | x: z.string(), 22 | y: z.number(), 23 | z: z.boolean(), 24 | }) 25 | return benchmark('zod', 'zod', () => T.safeParse({ x: 'hello', y: 42, z: true }).success) 26 | } 27 | function zod_using_value() { 28 | const T = TypeBox( 29 | z.object({ 30 | x: z.string(), 31 | y: z.number(), 32 | z: z.boolean(), 33 | }), 34 | ) 35 | return benchmark('zod', 'typebox:value', () => Value.Check(T, { x: 'hello', y: 42, z: true })) 36 | } 37 | function zod_using_compiler() { 38 | const T = Compile( 39 | z.object({ 40 | x: z.string(), 41 | y: z.number(), 42 | z: z.boolean(), 43 | }), 44 | ) 45 | return benchmark('zod', 'typebox:compile', () => T.Check({ x: 'hello', y: 42, z: true })) 46 | } 47 | // ------------------------------------------------------------------ 48 | // Valibot 49 | // ------------------------------------------------------------------ 50 | function valibot() { 51 | const T = v.object({ 52 | x: v.string(), 53 | y: v.number(), 54 | z: v.boolean(), 55 | }) 56 | return benchmark('valibot', 'valibot', () => v.safeParse(T, { x: 'hello', y: 42, z: true }).success) 57 | } 58 | function valibot_using_value() { 59 | const T = TypeBox( 60 | v.object({ 61 | x: v.string(), 62 | y: v.number(), 63 | z: v.boolean(), 64 | }), 65 | ) 66 | return benchmark('valibot', 'typebox:value', () => Value.Check(T, { x: 'hello', y: 42, z: true })) 67 | } 68 | function valibot_using_compiler() { 69 | const T = Compile( 70 | v.object({ 71 | x: v.string(), 72 | y: v.number(), 73 | z: v.boolean(), 74 | }), 75 | ) 76 | return benchmark('valibot', 'typebox:compile', () => T.Check({ x: 'hello', y: 42, z: true })) 77 | } 78 | 79 | console.log('running benchmark') 80 | console.table([valibot(), valibot_using_value(), valibot_using_compiler()]) 81 | console.table([zod(), zod_using_value(), zod_using_compiler()]) 82 | 83 | -------------------------------------------------------------------------------- /task/build/cjs/build.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2017-2024 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { removeNotices } from '../notices/remove-notices' 30 | import { compile } from './compile' 31 | 32 | /** Builds the CommonJS version of this package */ 33 | export async function build(target: string) { 34 | console.log('building...cjs') 35 | const buildTarget = `${target}/build/cjs` 36 | await compile(buildTarget) 37 | await removeNotices(buildTarget) 38 | } 39 | -------------------------------------------------------------------------------- /task/build/cjs/compile.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2017-2024 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | declare function shell(command: string): Promise 30 | 31 | // prettier-ignore 32 | export async function compile(target: string) { 33 | const options = [ 34 | `--outDir ${target}`, 35 | '--target ES2020', 36 | '--module CommonJS', 37 | '--declaration', 38 | ].join(' ') 39 | await shell(`tsc -p ./src/tsconfig.json ${options}`) 40 | } 41 | -------------------------------------------------------------------------------- /task/build/esm/build.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2017-2024 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { removeNotices } from '../notices/remove-notices' 30 | import { convertToEsm } from './convert-to-esm' 31 | import { compile } from './compile' 32 | 33 | /** Builds the ESM version of this package */ 34 | export async function build(target: string) { 35 | console.log('building...esm') 36 | const buildTarget = `${target}/build/esm` 37 | await compile(buildTarget) 38 | await convertToEsm(buildTarget) 39 | await removeNotices(buildTarget) 40 | } 41 | -------------------------------------------------------------------------------- /task/build/esm/compile.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2017-2024 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | declare function shell(command: string): Promise 30 | 31 | // prettier-ignore 32 | export async function compile(target: string) { 33 | const options = [ 34 | `--outDir ${target}`, 35 | '--target ES2020', 36 | '--module ESNext', 37 | '--declaration', 38 | ].join(' ') 39 | await shell(`tsc -p ./src/tsconfig.json ${options}`) 40 | } 41 | -------------------------------------------------------------------------------- /task/build/esm/convert-to-esm.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2017-2024 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import * as Path from 'node:path' 30 | import * as Fs from 'node:fs' 31 | 32 | // ------------------------------------------------------------------ 33 | // Specifier Rewrite 34 | // ------------------------------------------------------------------ 35 | function shouldSkipSpecifier(captured: string) { 36 | const specifier = captured.slice(1, captured.length - 1) 37 | return ( 38 | specifier.includes('.mjs') || // mitigate duplicate rewrite 39 | specifier.startsWith("@sinclair/parsebox") || 40 | specifier.startsWith("@sinclair/typebox") || 41 | specifier.startsWith("valibot") || 42 | specifier.startsWith("zod") 43 | ) 44 | } 45 | // prettier-ignore 46 | function replaceInlineImportSpecifiers(content: string): string { 47 | const pattern = /import\((.*?)\)/g 48 | while (true) { 49 | const match = pattern.exec(content) 50 | if (match === null) return content 51 | const captured = match[1] 52 | if(shouldSkipSpecifier(captured)) continue 53 | const specifier = captured.slice(1, captured.length - 1) 54 | content = content.replace(captured, `"${specifier}.mjs"`) 55 | } 56 | } 57 | // prettier-ignore 58 | function replaceExportSpecifiers(content: string): string { 59 | const pattern = /(export|import)(.*) from ('(.*)');/g 60 | while(true) { 61 | const match = pattern.exec(content) 62 | if(match === null) return content 63 | const captured = match[3] 64 | if(shouldSkipSpecifier(captured)) continue 65 | const specifier = captured.slice(1, captured.length - 1) 66 | content = content.replace(captured, `'${specifier}.mjs'`) 67 | } 68 | } 69 | function replaceSpecifiers(content: string): string { 70 | const pass1 = replaceExportSpecifiers(content) 71 | const pass2 = replaceInlineImportSpecifiers(pass1) 72 | return pass2 73 | } 74 | 75 | // ------------------------------------------------------------------ 76 | // ConvertToEsm 77 | // ------------------------------------------------------------------ 78 | // prettier-ignore 79 | function shouldProcess(sourcePath: string) { 80 | const extname = Path.extname(sourcePath) 81 | return ['.js', '.ts'].includes(extname) 82 | } 83 | // prettier-ignore 84 | function newExtension(extname: string) { 85 | return ( 86 | extname === '.js' ? '.mjs' : 87 | extname === '.ts' ? '.mts' : 88 | extname 89 | ) 90 | } 91 | // prettier-ignore 92 | function processFile(sourcePath: string) { 93 | if(!shouldProcess(sourcePath)) return 94 | const extname = Path.extname(sourcePath) 95 | const dirname = Path.dirname(sourcePath) 96 | const basename = Path.basename(sourcePath, extname) 97 | const new_extname = newExtension(extname) 98 | const sourceContent = Fs.readFileSync(sourcePath, 'utf-8') 99 | const targetContent = replaceSpecifiers(sourceContent) 100 | const targetPath = `${Path.join(dirname, basename)}${new_extname}` 101 | Fs.writeFileSync(sourcePath, targetContent) 102 | Fs.renameSync(sourcePath, targetPath) 103 | } 104 | // prettier-ignore 105 | function processSourcePath(sourcePath: string) { 106 | const stat = Fs.statSync(sourcePath) 107 | if(stat.isDirectory()) return readDirectory(sourcePath) 108 | if(stat.isFile()) return processFile(sourcePath) 109 | } 110 | // prettier-ignore 111 | function readDirectory(sourceDirectory: string) { 112 | for(const entry of Fs.readdirSync(sourceDirectory)) { 113 | const sourcePath = Path.join(sourceDirectory, entry) 114 | processSourcePath(sourcePath) 115 | } 116 | } 117 | /** Converts the JavaScript and TypeScript declaration modules in the given source directory to use .mjs extensions */ 118 | export function convertToEsm(sourceDirectory: string) { 119 | readDirectory(sourceDirectory) 120 | } 121 | -------------------------------------------------------------------------------- /task/build/index.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2017-2024 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | export * as Package from './package/build' 30 | export * as Esm from './esm/build' 31 | export * as Cjs from './cjs/build' 32 | -------------------------------------------------------------------------------- /task/build/notices/remove-notices.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2017-2024 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import * as Path from 'node:path' 30 | import * as Fs from 'node:fs' 31 | 32 | // ---------------------------------------------------------------------- 33 | // Remove Module Level MIT Notice on Package Distribution 34 | // 35 | // The MIT copyright notice the unnecessarily increases the distribution 36 | // size of the package, this code removes it. The MIT license is available 37 | // in the package root. 38 | // 39 | // ---------------------------------------------------------------------- 40 | // prettier-ignore 41 | function escape(content: string) { 42 | return content.split('').map((c) => `\\${c}`).join('') 43 | } 44 | // prettier-ignore 45 | function removeNotice(content: string): string { 46 | const open = escape('/*--------------------------------------------------------------------------') 47 | const close = escape('---------------------------------------------------------------------------*/') 48 | const critera = 'Permission is hereby granted, free of charge' 49 | const pattern = new RegExp(`${open}[\\s\\S]*?${close}`, 'gm') 50 | while (true) { 51 | const match = pattern.exec(content) 52 | if (match === null) return content.trimStart() 53 | if (!match[0].includes(critera)) continue 54 | content = content.replace(match[0], '') 55 | } 56 | } 57 | // ------------------------------------------------------------------ 58 | // Directory Enumeration 59 | // ------------------------------------------------------------------ 60 | // prettier-ignore 61 | function processFile(sourcePath: string) { 62 | const sourceContent = Fs.readFileSync(sourcePath, 'utf-8') 63 | const targetContent = removeNotice(sourceContent) 64 | Fs.writeFileSync(sourcePath, targetContent) 65 | } 66 | // prettier-ignore 67 | function processSourcePath(sourcePath: string) { 68 | const stat = Fs.statSync(sourcePath) 69 | if(stat.isDirectory()) return readDirectory(sourcePath) 70 | if(stat.isFile()) return processFile(sourcePath) 71 | } 72 | // prettier-ignore 73 | function readDirectory(sourceDirectory: string) { 74 | for(const entry of Fs.readdirSync(sourceDirectory)) { 75 | const sourcePath = Path.join(sourceDirectory, entry) 76 | processSourcePath(sourcePath) 77 | } 78 | } 79 | /** Removes the MIT copyright notices from each source file in the given directory */ 80 | export function removeNotices(sourceDirectory: string) { 81 | readDirectory(sourceDirectory) 82 | } 83 | -------------------------------------------------------------------------------- /task/build/package/build.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typebox 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2017-2024 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import { createPackageJsonRedirect } from './create-package-json-redirect' 30 | import { createPackageJson } from './create-package-json' 31 | 32 | /** Builds package.json and redirect directories */ 33 | export async function build(target: string) { 34 | console.log('building...package.json') 35 | const submodules = [] as string[] 36 | await createPackageJsonRedirect(target, submodules) 37 | await createPackageJson(target, submodules) 38 | } 39 | -------------------------------------------------------------------------------- /task/build/package/create-package-json-redirect.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typebox 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2017-2024 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import * as Fs from 'node:fs' 30 | 31 | // prettier-ignore 32 | function writeRedirect(target: string, submodule: string) { 33 | Fs.mkdirSync(`${target}/${submodule}`, { recursive: true }) 34 | Fs.writeFileSync(`${target}/${submodule}/package.json`,JSON.stringify({ 35 | main: `../build/cjs/${submodule}/index.js`, 36 | types: `../build/cjs/${submodule}/index.d.ts`, 37 | }, null, 2)) 38 | } 39 | // -------------------------------------------------------------------------------------------------------------------------- 40 | // Builds redirect directories for earlier versions of Node. Note that TypeScript will use these directories to 41 | // resolve types when tsconfig.json is configured for `moduleResolution: 'node'`. This approach is referred to as 42 | // `package-json-redirect` and enables correct type resolution in lieu of a correct end user configuration. 43 | // 44 | // https://github.com/andrewbranch/example-subpath-exports-ts-compat/tree/main/examples/node_modules/package-json-redirects 45 | // -------------------------------------------------------------------------------------------------------------------------- 46 | 47 | // prettier-ignore 48 | export function createPackageJsonRedirect(target: string, submodules: string[]) { 49 | submodules.forEach((submodule) => writeRedirect(target, submodule)) 50 | } 51 | -------------------------------------------------------------------------------- /task/build/package/create-package-json.ts: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | 3 | @sinclair/typemap 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2017-2024 Haydn Paterson (sinclair) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | ---------------------------------------------------------------------------*/ 28 | 29 | import * as Fs from 'node:fs' 30 | import * as Path from 'node:path' 31 | 32 | // prettier-ignore 33 | export function createPackageJson(target: string, submodules: string[]) { 34 | const content = JSON.stringify(resolvePackageJson(submodules), null, 2) 35 | const targetPath = Path.join(target, 'package.json') 36 | const targetDir = Path.dirname(targetPath) 37 | Fs.mkdirSync(targetDir, { recursive: true }) 38 | Fs.writeFileSync(targetPath, content, 'utf-8') 39 | } 40 | // prettier-ignore 41 | function resolvePackageJson(submodules: string[]) { 42 | return { 43 | ...resolveMetadata(), 44 | ...resolveExports(submodules) 45 | } 46 | } 47 | // prettier-ignore 48 | function resolveSubmoduleExports(submodule: string) { 49 | return { 50 | require: { 51 | types: `./build/cjs/${submodule}/index.d.ts`, 52 | default: `./build/cjs/${submodule}/index.js`, 53 | }, 54 | import: { 55 | types: `./build/esm/${submodule}/index.d.mts`, 56 | default: `./build/esm/${submodule}/index.mjs`, 57 | } 58 | } 59 | } 60 | // prettier-ignore 61 | function resolveExports(submodules: string[]) { 62 | const exports = submodules.reduce((acc, submodule) => { 63 | return { ...acc, [`./${submodule}`]: resolveSubmoduleExports(submodule) } 64 | }, { 65 | // ... and root module 66 | ".": { 67 | "require": { 68 | "types": "./build/cjs/index.d.ts", 69 | "default": "./build/cjs/index.js", 70 | 71 | }, 72 | "import": { 73 | "types": "./build/esm/index.d.mts", 74 | "default": "./build/esm/index.mjs", 75 | } 76 | } 77 | }) 78 | return { exports } 79 | } 80 | // prettier-ignore 81 | function resolveMetadata() { 82 | const packagePath = Path.join(process.cwd(), 'package.json') 83 | const packageJson = JSON.parse(Fs.readFileSync(packagePath, 'utf-8')) 84 | return { 85 | name: packageJson.name, 86 | version: packageJson.version, 87 | description: packageJson.description, 88 | keywords: packageJson.keywords, 89 | author: packageJson.author, 90 | license: packageJson.license, 91 | repository: packageJson.repository, 92 | // dependencies: packageJson.dependencies, 93 | peerDependencies: packageJson.peerDependencies, 94 | optionalDependencies: packageJson.optionalDependencies, 95 | // flagged by socket.dev if not present 96 | scripts: { test: 'echo test' }, 97 | types: "./build/cjs/index.d.ts", 98 | main: "./build/cjs/index.js", 99 | module: "./build/esm/index.mjs", 100 | // disable auto bundle strategy: see https://github.com/esm-dev/esm.sh#bundling-strategy 101 | 'esm.sh': { 'bundle': false }, 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /test/assert.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'node:assert' 2 | 3 | export namespace Assert { 4 | export function HasProperty(value: unknown, key: K): asserts value is Record { 5 | if (typeof value === 'object' && value !== null && key in value) return 6 | throw new Error(`Expected value to have property '${key as string}'`) 7 | } 8 | export function IsTrue(value: boolean): asserts value is true { 9 | return assert.strictEqual(value, true) 10 | } 11 | export function IsFalse(value: boolean): asserts value is false { 12 | return assert.strictEqual(value, false) 13 | } 14 | export function IsEqual(actual: unknown, expect: unknown) { 15 | if (actual instanceof Uint8Array && expect instanceof Uint8Array) { 16 | assert.equal(actual.length, expect.length) 17 | for (let i = 0; i < actual.length; i++) assert.equal(actual[i], expect[i]) 18 | } 19 | return assert.deepStrictEqual(actual, expect) 20 | } 21 | export function NotEqual(actual: unknown, expect: unknown) { 22 | return assert.notEqual(actual, expect) 23 | } 24 | /** Asserts a numeric value is within range of the expected */ 25 | export function InRange(value: number, expect: number, range: number) { 26 | if (Math.abs(value - expect) <= range) return 27 | throw Error('Expected value to be in range') 28 | } 29 | let nextIdOrdinal = 0 30 | export function NextId() { 31 | return `$id-${nextIdOrdinal++}` 32 | } 33 | export function Throws(callback: Function) { 34 | try { 35 | callback() 36 | } catch { 37 | return 38 | } 39 | throw Error('Expected throw') 40 | } 41 | export async function ThrowsAsync(callback: Function) { 42 | try { 43 | await callback() 44 | } catch { 45 | return 46 | } 47 | throw Error('Expected throw') 48 | } 49 | export function IsInstanceOf any>(value: any, constructor: T): asserts value is InstanceType { 50 | if (value instanceof constructor) return 51 | throw Error(`Value is not instance of ${constructor}`) 52 | } 53 | export function IsTypeOf(value: any, type: T) { 54 | if (typeof value === type) return 55 | throw Error(`Value is not typeof ${type}`) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/compile.ts: -------------------------------------------------------------------------------- 1 | import { Assert } from './assert' 2 | import { TypeBox, Valibot, Zod, Compile } from '@sinclair/typemap' 3 | 4 | describe('Compile', () => { 5 | // ---------------------------------------------------------------- 6 | // Validator 7 | // ---------------------------------------------------------------- 8 | it('Should compile Syntax', () => { 9 | const C = Compile('123') 10 | Assert.IsTrue(C.Check(123)) 11 | }) 12 | it('Should compile Syntax (Parameterized)', () => { 13 | const C = Compile({ T: TypeBox('123') }, 'T') 14 | Assert.IsTrue(C.Check(123)) 15 | }) 16 | it('Should compile TypeBox', () => { 17 | const C = Compile(TypeBox('123')) 18 | Assert.IsTrue(C.Check(123)) 19 | }) 20 | it('Should compile TypeBox (Parameterized)', () => { 21 | const C = Compile({ T: TypeBox('123') }, 'T') 22 | Assert.IsTrue(C.Check(123)) 23 | }) 24 | it('Should compile Valibot', () => { 25 | const C = Compile(Valibot('123')) 26 | Assert.IsTrue(C.Check(123)) 27 | }) 28 | it('Should compile Valibot (Parameterized)', () => { 29 | const C = Compile({ T: Valibot('123') }, 'T') 30 | Assert.IsTrue(C.Check(123)) 31 | }) 32 | it('Should compile Zod', () => { 33 | const C = Compile(Zod('123')) 34 | Assert.IsTrue(C.Check(123)) 35 | }) 36 | it('Should compile Zod (Parameterized)', () => { 37 | const C = Compile({ T: Zod('123') }, 'T') 38 | Assert.IsTrue(C.Check(123)) 39 | }) 40 | // ---------------------------------------------------------------- 41 | // Standard Schema 42 | // ---------------------------------------------------------------- 43 | it('Should validate via Standard Schema interface (Success)', () => { 44 | const C = Compile('string') 45 | const R = C['~standard'].validate('hello') 46 | // @ts-ignore 47 | Assert.IsEqual(R.value, 'hello') // Reference spec interface is broken here, It's not for me to fix. 48 | }) 49 | it('Should validate via Standard Schema interface (Failure)', () => { 50 | const C = Compile('string') 51 | const R = C['~standard'].validate(12345) 52 | Assert.IsTrue('issues' in R) 53 | Assert.IsTrue(R.issues!.length > 0) 54 | }) 55 | }) 56 | -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | import './compile' 2 | import './options' 3 | import './parameters' 4 | import './typebox-from-zod' 5 | import './typebox-from-valibot' 6 | import './valibot-from-typebox' 7 | import './zod-from-typebox' 8 | -------------------------------------------------------------------------------- /test/options.ts: -------------------------------------------------------------------------------- 1 | import { Assert } from './assert' 2 | import { TypeBox, Valibot, Zod } from '@sinclair/typemap' 3 | 4 | describe('SyntaxOptions', () => { 5 | it('Should map Options (Zod)', () => { 6 | const A = TypeBox('string', { minLength: 10 }) 7 | const B = Zod(A) 8 | const C = TypeBox(B) 9 | Assert.IsEqual(C.minLength, 10) 10 | }) 11 | it('Should map Options (Valibot)', () => { 12 | const A = TypeBox('string', { minLength: 10 }) 13 | const B = Valibot(A) 14 | const C = TypeBox(B) 15 | Assert.IsEqual(C.minLength, 10) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /test/parameters.ts: -------------------------------------------------------------------------------- 1 | import { Assert } from './assert' 2 | import { TypeBox, Valibot, Zod } from '@sinclair/typemap' 3 | import { KindGuard } from '@sinclair/typebox' 4 | 5 | describe('Parameters', () => { 6 | it('Should map Parameters (Zod)', () => { 7 | const A = TypeBox('string') 8 | const B = Zod({ A }, 'A') 9 | const C = TypeBox(B) 10 | Assert.IsTrue(KindGuard.IsString(C)) 11 | }) 12 | it('Should map Parameters (Valibot)', () => { 13 | const A = TypeBox('string') 14 | const B = Valibot({ A }, 'A') 15 | const C = TypeBox(B) 16 | Assert.IsTrue(KindGuard.IsString(C)) 17 | }) 18 | it('Should map Parameters With Constraints (Zod)', () => { 19 | const A = TypeBox('string', { minLength: 10 }) 20 | const B = Zod({ A }, 'A') 21 | const C = TypeBox(B) 22 | Assert.IsTrue(KindGuard.IsString(C)) 23 | Assert.IsEqual(C.minLength, 10) 24 | }) 25 | it('Should map Parameters With Constraints (Valibot)', () => { 26 | const A = TypeBox('string', { minLength: 10 }) 27 | const B = Valibot({ A }, 'A') 28 | const C = TypeBox(B) 29 | Assert.IsTrue(KindGuard.IsString(C)) 30 | Assert.IsEqual(C.minLength, 10) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "files": ["index.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /test/typebox-from-valibot.ts: -------------------------------------------------------------------------------- 1 | import { TypeBox } from '@sinclair/typemap' 2 | import { TypeGuard } from '@sinclair/typebox' 3 | import { Assert } from './assert' 4 | import * as t from '@sinclair/typebox' 5 | import * as v from 'valibot' 6 | 7 | describe('TypeBox from Valibot', () => { 8 | // ---------------------------------------------------------------- 9 | // Metadata 10 | // ---------------------------------------------------------------- 11 | it('Should map Description', () => { 12 | const T = TypeBox(v.pipe(v.number(), v.description('a number'))) 13 | Assert.IsEqual(T.description, 'a number') 14 | }) 15 | it('Should map Title', () => { 16 | const T = TypeBox(v.pipe(v.number(), v.title('a number'))) 17 | Assert.IsEqual(T.title, 'a number') 18 | }) 19 | it('Should map Metadata', () => { 20 | const T = TypeBox(v.pipe(v.number(), v.metadata({ x: 1, y: 2 }))) 21 | Assert.IsEqual(T.metadata.x, 1) 22 | Assert.IsEqual(T.metadata.y, 2) 23 | }) 24 | // ---------------------------------------------------------------- 25 | // Any 26 | // ---------------------------------------------------------------- 27 | it('Should map Any', () => { 28 | const T = TypeBox(v.any()) 29 | Assert.IsTrue(TypeGuard.IsAny(T)) 30 | }) 31 | // ---------------------------------------------------------------- 32 | // Array 33 | // ---------------------------------------------------------------- 34 | it('Should map Array', () => { 35 | const T = TypeBox(v.array(v.number())) 36 | Assert.IsTrue(TypeGuard.IsArray(T)) 37 | Assert.IsTrue(TypeGuard.IsNumber(T.items)) 38 | }) 39 | // ---------------------------------------------------------------- 40 | // BigInt 41 | // ---------------------------------------------------------------- 42 | it('Should map BigInt', () => { 43 | const T = TypeBox(v.bigint()) 44 | Assert.IsTrue(TypeGuard.IsBigInt(T)) 45 | }) 46 | // ---------------------------------------------------------------- 47 | // Date 48 | // ---------------------------------------------------------------- 49 | it('Should map Date', () => { 50 | const T = TypeBox(v.date()) 51 | Assert.IsTrue(TypeGuard.IsDate(T)) 52 | }) 53 | // ---------------------------------------------------------------- 54 | // Effects 55 | // ---------------------------------------------------------------- 56 | // it('Should map Effects (Transform)', () => { 57 | // const T = TypeBox(v.number().transform(x => x)) 58 | // Assert.IsTrue(TypeGuard.IsNumber(T)) 59 | // Assert.IsTrue(TypeGuard.IsTransform(T)) 60 | // }) 61 | // it('Should map Effects (Refine)', () => { 62 | // const T = TypeBox(v.number().refine(x => true)) 63 | // Assert.IsTrue(TypeGuard.IsNumber(T)) 64 | // Assert.IsTrue(TypeGuard.IsTransform(T)) 65 | // }) 66 | // ---------------------------------------------------------------- 67 | // Literal 68 | // ---------------------------------------------------------------- 69 | it('Should map Literal (Number)', () => { 70 | const T = TypeBox(v.literal(42)) 71 | Assert.IsTrue(TypeGuard.IsLiteral(T)) 72 | Assert.IsEqual(T.const, 42) 73 | }) 74 | it('Should map Literal (String)', () => { 75 | const T = TypeBox(v.literal('hello')) 76 | Assert.IsTrue(TypeGuard.IsLiteral(T)) 77 | Assert.IsEqual(T.const, 'hello') 78 | }) 79 | it('Should map Literal (Boolean)', () => { 80 | const T = TypeBox(v.literal(true)) 81 | Assert.IsTrue(TypeGuard.IsLiteral(T)) 82 | Assert.IsEqual(T.const, true) 83 | }) 84 | // ---------------------------------------------------------------- 85 | // Nullable 86 | // ---------------------------------------------------------------- 87 | it('Should map Nullable', () => { 88 | const T = TypeBox(v.nullable(v.number())) 89 | Assert.IsTrue(TypeGuard.IsUnion(T)) 90 | Assert.IsTrue(TypeGuard.IsNull(T.anyOf[0])) 91 | Assert.IsTrue(TypeGuard.IsNumber(T.anyOf[1])) 92 | }) 93 | // ---------------------------------------------------------------- 94 | // Object 95 | // ---------------------------------------------------------------- 96 | it('Should map Object', () => { 97 | const T = TypeBox( 98 | v.object({ 99 | x: v.number(), 100 | y: v.string(), 101 | }), 102 | ) 103 | Assert.IsTrue(TypeGuard.IsObject(T)) 104 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 105 | Assert.IsTrue(TypeGuard.IsString(T.properties.y)) 106 | }) 107 | it('Should map Object (Strict)', () => { 108 | const T = TypeBox( 109 | v.strictObject({ 110 | x: v.number(), 111 | y: v.string(), 112 | }), 113 | ) 114 | Assert.IsTrue(TypeGuard.IsObject(T)) 115 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 116 | Assert.IsTrue(TypeGuard.IsString(T.properties.y)) 117 | Assert.IsEqual(T.additionalProperties, false) 118 | }) 119 | // ---------------------------------------------------------------- 120 | // Optional 121 | // ---------------------------------------------------------------- 122 | it('Should map Optional', () => { 123 | const T = TypeBox( 124 | v.object({ 125 | x: v.optional(v.number()), 126 | y: v.optional(v.number()), 127 | }), 128 | ) 129 | Assert.IsTrue(TypeGuard.IsObject(T)) 130 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 131 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) 132 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 133 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) 134 | }) 135 | it('Should map Optional (Partial)', () => { 136 | const T = TypeBox( 137 | v.partial( 138 | v.object({ 139 | x: v.number(), 140 | y: v.number(), 141 | }), 142 | ), 143 | ) 144 | Assert.IsTrue(TypeGuard.IsObject(T)) 145 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 146 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) 147 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 148 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) 149 | }) 150 | // ---------------------------------------------------------------- 151 | // Promise 152 | // ---------------------------------------------------------------- 153 | it('Should map Promise', () => { 154 | const T = TypeBox(v.promise()) 155 | Assert.IsEqual(T[t.Kind], 'ValibotPromise') 156 | }) 157 | // ---------------------------------------------------------------- 158 | // Record 159 | // ---------------------------------------------------------------- 160 | it('Should map Record (String Key)', () => { 161 | const T = TypeBox(v.record(v.string(), v.number())) 162 | Assert.IsTrue(TypeGuard.IsRecord(T)) 163 | Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[t.PatternStringExact])) 164 | }) 165 | it('Should map Record (Finite Union)', () => { 166 | const T = TypeBox(v.record(v.union([v.literal('x'), v.literal('y')]), v.number())) 167 | Assert.IsTrue(TypeGuard.IsObject(T)) 168 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 169 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 170 | }) 171 | // ---------------------------------------------------------------- 172 | // Never 173 | // ---------------------------------------------------------------- 174 | it('Should map Never', () => { 175 | const T = TypeBox(v.never()) 176 | Assert.IsTrue(TypeGuard.IsNever(T)) 177 | }) 178 | // ---------------------------------------------------------------- 179 | // Null 180 | // ---------------------------------------------------------------- 181 | it('Should map Null', () => { 182 | const T = TypeBox(v.null()) 183 | Assert.IsTrue(TypeGuard.IsNull(T)) 184 | }) 185 | // ---------------------------------------------------------------- 186 | // Number 187 | // ---------------------------------------------------------------- 188 | it('Should map Number', () => { 189 | const T = TypeBox(v.number()) 190 | Assert.IsTrue(TypeGuard.IsNumber(T)) 191 | }) 192 | it('Should map Number (Integer)', () => { 193 | const T = TypeBox(v.pipe(v.number(), v.integer())) 194 | Assert.IsTrue(TypeGuard.IsNumber(T)) 195 | Assert.IsEqual(T.multipleOf, 1) 196 | }) 197 | it('Should map Number (Minimum)', () => { 198 | const T = TypeBox(v.pipe(v.number(), v.minValue(100))) 199 | Assert.IsTrue(TypeGuard.IsNumber(T)) 200 | Assert.IsEqual(T.minimum, 100) 201 | }) 202 | it('Should map Number (Maximum)', () => { 203 | const T = TypeBox(v.pipe(v.number(), v.maxValue(100))) 204 | Assert.IsTrue(TypeGuard.IsNumber(T)) 205 | Assert.IsEqual(T.maximum, 100) 206 | }) 207 | // ---------------------------------------------------------------- 208 | // String 209 | // ---------------------------------------------------------------- 210 | it('Should map String', () => { 211 | const T = TypeBox(v.string()) 212 | Assert.IsTrue(TypeGuard.IsString(T)) 213 | }) 214 | it('Should map String (Base64)', () => { 215 | const T = TypeBox(v.pipe(v.string(), v.base64())) 216 | Assert.IsTrue(TypeGuard.IsString(T)) 217 | Assert.IsEqual(T.format, 'base64') 218 | }) 219 | it('Should map String (Bic)', () => { 220 | const T = TypeBox(v.pipe(v.string(), v.bic())) 221 | Assert.IsTrue(TypeGuard.IsString(T)) 222 | Assert.IsEqual(T.format, 'bic') 223 | }) 224 | it('Should map String (CreditCard)', () => { 225 | const T = TypeBox(v.pipe(v.string(), v.creditCard())) 226 | Assert.IsTrue(TypeGuard.IsString(T)) 227 | Assert.IsEqual(T.format, 'credit_card') 228 | }) 229 | it('Should map String (Cuid2)', () => { 230 | const T = TypeBox(v.pipe(v.string(), v.cuid2())) 231 | Assert.IsTrue(TypeGuard.IsString(T)) 232 | Assert.IsEqual(T.format, 'cuid2') 233 | }) 234 | it('Should map String (Decimal)', () => { 235 | const T = TypeBox(v.pipe(v.string(), v.decimal())) 236 | Assert.IsTrue(TypeGuard.IsString(T)) 237 | Assert.IsEqual(T.format, 'decimal') 238 | }) 239 | it('Should map String (Digits)', () => { 240 | const T = TypeBox(v.pipe(v.string(), v.digits())) 241 | Assert.IsTrue(TypeGuard.IsString(T)) 242 | Assert.IsEqual(T.format, 'digits') 243 | }) 244 | it('Should map String (Email)', () => { 245 | const T = TypeBox(v.pipe(v.string(), v.email())) 246 | Assert.IsTrue(TypeGuard.IsString(T)) 247 | Assert.IsEqual(T.format, 'email') 248 | }) 249 | it('Should map String (Emoji)', () => { 250 | const T = TypeBox(v.pipe(v.string(), v.emoji())) 251 | Assert.IsTrue(TypeGuard.IsString(T)) 252 | Assert.IsEqual(T.format, 'emoji') 253 | }) 254 | it('Should map String (Empty)', () => { 255 | const T = TypeBox(v.pipe(v.string(), v.empty())) 256 | Assert.IsTrue(TypeGuard.IsString(T)) 257 | Assert.IsEqual(T.maxLength, 0) 258 | }) 259 | it('Should map String (EndsWith)', () => { 260 | const T = TypeBox(v.pipe(v.string(), v.endsWith('hello'))) 261 | Assert.IsTrue(TypeGuard.IsString(T)) 262 | Assert.IsEqual(T.pattern, 'hello$') 263 | }) 264 | it('Should map String (Includes)', () => { 265 | const T = TypeBox(v.pipe(v.string(), v.includes('hello'))) 266 | Assert.IsTrue(TypeGuard.IsString(T)) 267 | Assert.IsEqual(T.pattern, 'hello') 268 | }) 269 | it('Should map String (Ipv4)', () => { 270 | const T = TypeBox(v.pipe(v.string(), v.ipv4())) 271 | Assert.IsTrue(TypeGuard.IsString(T)) 272 | Assert.IsEqual(T.format, 'ipv4') 273 | }) 274 | it('Should map String (IpV6)', () => { 275 | const T = TypeBox(v.pipe(v.string(), v.ipv6())) 276 | Assert.IsTrue(TypeGuard.IsString(T)) 277 | Assert.IsEqual(T.format, 'ipv6') 278 | }) 279 | it('Should map String (Ip)', () => { 280 | const T = TypeBox(v.pipe(v.string(), v.ip())) 281 | Assert.IsTrue(TypeGuard.IsString(T)) 282 | Assert.IsEqual(T.format, 'ip') 283 | }) 284 | it('Should map String (IsoDate)', () => { 285 | const T = TypeBox(v.pipe(v.string(), v.isoDate())) 286 | Assert.IsTrue(TypeGuard.IsString(T)) 287 | Assert.IsEqual(T.format, 'iso_date') 288 | }) 289 | it('Should map String (IsoDateTime)', () => { 290 | const T = TypeBox(v.pipe(v.string(), v.isoDateTime())) 291 | Assert.IsTrue(TypeGuard.IsString(T)) 292 | Assert.IsEqual(T.format, 'iso_date_time') 293 | }) 294 | it('Should map String (IsoTime)', () => { 295 | const T = TypeBox(v.pipe(v.string(), v.isoTime())) 296 | Assert.IsTrue(TypeGuard.IsString(T)) 297 | Assert.IsEqual(T.format, 'iso_time') 298 | }) 299 | it('Should map String (IsoTimeSecond)', () => { 300 | const T = TypeBox(v.pipe(v.string(), v.isoTimeSecond())) 301 | Assert.IsTrue(TypeGuard.IsString(T)) 302 | Assert.IsEqual(T.format, 'iso_time_second') 303 | }) 304 | it('Should map String (IsoTimestamp)', () => { 305 | const T = TypeBox(v.pipe(v.string(), v.isoTimestamp())) 306 | Assert.IsTrue(TypeGuard.IsString(T)) 307 | Assert.IsEqual(T.format, 'iso_timestamp') 308 | }) 309 | it('Should map String (IsoWeek)', () => { 310 | const T = TypeBox(v.pipe(v.string(), v.isoWeek())) 311 | Assert.IsTrue(TypeGuard.IsString(T)) 312 | Assert.IsEqual(T.format, 'iso_week') 313 | }) 314 | it('Should map String (Length)', () => { 315 | const T = TypeBox(v.pipe(v.string(), v.length(100))) 316 | Assert.IsTrue(TypeGuard.IsString(T)) 317 | Assert.IsEqual(T.maxLength, 100) 318 | Assert.IsEqual(T.minLength, 100) 319 | }) 320 | it('Should map String (Mac48)', () => { 321 | const T = TypeBox(v.pipe(v.string(), v.mac48())) 322 | Assert.IsTrue(TypeGuard.IsString(T)) 323 | Assert.IsEqual(T.format, 'mac48') 324 | }) 325 | it('Should map String (Mac64)', () => { 326 | const T = TypeBox(v.pipe(v.string(), v.mac64())) 327 | Assert.IsTrue(TypeGuard.IsString(T)) 328 | Assert.IsEqual(T.format, 'mac64') 329 | }) 330 | it('Should map String (Mac)', () => { 331 | const T = TypeBox(v.pipe(v.string(), v.mac())) 332 | Assert.IsTrue(TypeGuard.IsString(T)) 333 | Assert.IsEqual(T.format, 'mac') 334 | }) 335 | it('Should map String (MaxLength)', () => { 336 | const T = TypeBox(v.pipe(v.string(), v.maxLength(100))) 337 | Assert.IsTrue(TypeGuard.IsString(T)) 338 | Assert.IsEqual(T.maxLength, 100) 339 | }) 340 | it('Should map String (MinLength)', () => { 341 | const T = TypeBox(v.pipe(v.string(), v.minLength(100))) 342 | Assert.IsTrue(TypeGuard.IsString(T)) 343 | Assert.IsEqual(T.minLength, 100) 344 | }) 345 | it('Should map String (Nanoid)', () => { 346 | const T = TypeBox(v.pipe(v.string(), v.nanoid())) 347 | Assert.IsTrue(TypeGuard.IsString(T)) 348 | Assert.IsEqual(T.format, 'nanoid') 349 | }) 350 | it('Should map String (Octal)', () => { 351 | const T = TypeBox(v.pipe(v.string(), v.octal())) 352 | Assert.IsTrue(TypeGuard.IsString(T)) 353 | Assert.IsEqual(T.format, 'octal') 354 | }) 355 | it('Should map String (RegExp)', () => { 356 | const T = TypeBox(v.pipe(v.string(), v.regex(/abc/))) 357 | Assert.IsTrue(TypeGuard.IsString(T)) 358 | Assert.IsEqual(T.pattern, 'abc') 359 | }) 360 | it('Should map String (StartsWith)', () => { 361 | const T = TypeBox(v.pipe(v.string(), v.startsWith('hello'))) 362 | Assert.IsTrue(TypeGuard.IsString(T)) 363 | Assert.IsEqual(T.pattern, '^hello') 364 | }) 365 | it('Should map String (Ulid)', () => { 366 | const T = TypeBox(v.pipe(v.string(), v.ulid())) 367 | Assert.IsTrue(TypeGuard.IsString(T)) 368 | Assert.IsEqual(T.format, 'ulid') 369 | }) 370 | it('Should map String (Url)', () => { 371 | const T = TypeBox(v.pipe(v.string(), v.url())) 372 | Assert.IsTrue(TypeGuard.IsString(T)) 373 | Assert.IsEqual(T.format, 'url') 374 | }) 375 | it('Should map String (Uuid)', () => { 376 | const T = TypeBox(v.pipe(v.string(), v.uuid())) 377 | Assert.IsTrue(TypeGuard.IsString(T)) 378 | Assert.IsEqual(T.format, 'uuid') 379 | }) 380 | // ---------------------------------------------------------------- 381 | // Symbol 382 | // ---------------------------------------------------------------- 383 | it('Should map Symbol', () => { 384 | const T = TypeBox(v.symbol()) 385 | Assert.IsTrue(TypeGuard.IsSymbol(T)) 386 | }) 387 | // ---------------------------------------------------------------- 388 | // Tuple 389 | // ---------------------------------------------------------------- 390 | it('Should map Tuple', () => { 391 | const T = TypeBox(v.tuple([v.number(), v.string()])) 392 | Assert.IsTrue(TypeGuard.IsTuple(T)) 393 | Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) 394 | Assert.IsTrue(TypeGuard.IsString(T.items![1])) 395 | }) 396 | // ---------------------------------------------------------------- 397 | // Undefined 398 | // ---------------------------------------------------------------- 399 | it('Should map Undefined', () => { 400 | const T = TypeBox(v.undefined()) 401 | Assert.IsTrue(TypeGuard.IsUndefined(T)) 402 | }) 403 | // ---------------------------------------------------------------- 404 | // Union 405 | // ---------------------------------------------------------------- 406 | it('Should map Union', () => { 407 | const T = TypeBox(v.union([v.string(), v.boolean()])) 408 | Assert.IsTrue(TypeGuard.IsUnion(T)) 409 | Assert.IsTrue(TypeGuard.IsString(T.anyOf[0])) 410 | Assert.IsTrue(TypeGuard.IsBoolean(T.anyOf[1])) 411 | }) 412 | // ---------------------------------------------------------------- 413 | // Unknown 414 | // ---------------------------------------------------------------- 415 | it('Should map Unknown', () => { 416 | const T = TypeBox(v.unknown()) 417 | Assert.IsTrue(TypeGuard.IsUnknown(T)) 418 | }) 419 | // ---------------------------------------------------------------- 420 | // Void 421 | // ---------------------------------------------------------------- 422 | it('Should map Void', () => { 423 | const T = TypeBox(v.void()) 424 | Assert.IsTrue(TypeGuard.IsVoid(T)) 425 | }) 426 | // ---------------------------------------------------------------- 427 | // Pipe 428 | // ---------------------------------------------------------------- 429 | it('Should map Pipe', () => { 430 | const S = v.pipe(v.literal('hello'), v.length(5)) 431 | const T = TypeBox(S) 432 | Assert.IsTrue(TypeGuard.IsLiteral(T)) 433 | Assert.IsEqual(T.const, 'hello') 434 | }) 435 | }) 436 | -------------------------------------------------------------------------------- /test/typebox-from-zod.ts: -------------------------------------------------------------------------------- 1 | import { TypeBox } from '@sinclair/typemap' 2 | import { TypeGuard } from '@sinclair/typebox' 3 | import { Assert } from './assert' 4 | import * as t from '@sinclair/typebox' 5 | import * as z from 'zod' 6 | 7 | describe('TypeBox From Zod', () => { 8 | // ---------------------------------------------------------------- 9 | // Metadata 10 | // ---------------------------------------------------------------- 11 | it('Should map Description', () => { 12 | const T = TypeBox(z.number().describe('a number')) 13 | Assert.IsEqual(T.description, 'a number') 14 | }) 15 | it('Should map Default', () => { 16 | const T = TypeBox(z.number().default(12345)) 17 | Assert.IsEqual(T.default, 12345) 18 | }) 19 | // ---------------------------------------------------------------- 20 | // Any 21 | // ---------------------------------------------------------------- 22 | it('Should map Any', () => { 23 | const T = TypeBox(z.any()) 24 | Assert.IsTrue(TypeGuard.IsAny(T)) 25 | }) 26 | // ---------------------------------------------------------------- 27 | // Array 28 | // ---------------------------------------------------------------- 29 | it('Should map Array', () => { 30 | const T = TypeBox(z.array(z.number())) 31 | Assert.IsTrue(TypeGuard.IsArray(T)) 32 | Assert.IsTrue(TypeGuard.IsNumber(T.items)) 33 | }) 34 | // ---------------------------------------------------------------- 35 | // BigInt 36 | // ---------------------------------------------------------------- 37 | it('Should map BigInt', () => { 38 | const T = TypeBox(z.bigint()) 39 | Assert.IsTrue(TypeGuard.IsBigInt(T)) 40 | }) 41 | // ---------------------------------------------------------------- 42 | // Date 43 | // ---------------------------------------------------------------- 44 | it('Should map Date', () => { 45 | const T = TypeBox(z.date()) 46 | Assert.IsTrue(TypeGuard.IsDate(T)) 47 | }) 48 | // ---------------------------------------------------------------- 49 | // DiscriminatedUnion 50 | // ---------------------------------------------------------------- 51 | it('Should map DiscriminatedUnion', () => { 52 | const A = z.object({ type: z.literal('A') }) 53 | const B = z.object({ type: z.literal('B') }) 54 | const C = z.object({ type: z.literal('C') }) 55 | const T = TypeBox(z.discriminatedUnion('type', [A, B, C])) 56 | Assert.IsTrue(TypeGuard.IsUnion(T)) 57 | Assert.IsEqual(T.discriminator, 'type') 58 | Assert.IsTrue(T.anyOf[0].properties.type.const === 'A') 59 | Assert.IsTrue(T.anyOf[1].properties.type.const === 'B') 60 | Assert.IsTrue(T.anyOf[2].properties.type.const === 'C') 61 | }) 62 | // ---------------------------------------------------------------- 63 | // Effects 64 | // ---------------------------------------------------------------- 65 | it('Should map Effects (Transform)', () => { 66 | const T = TypeBox(z.number().transform((x) => x)) 67 | Assert.IsTrue(TypeGuard.IsNumber(T)) 68 | Assert.IsTrue(TypeGuard.IsTransform(T)) 69 | }) 70 | it('Should map Effects (Refine)', () => { 71 | const T = TypeBox(z.number().refine((x) => true)) 72 | Assert.IsTrue(TypeGuard.IsNumber(T)) 73 | Assert.IsTrue(TypeGuard.IsTransform(T)) 74 | }) 75 | // ---------------------------------------------------------------- 76 | // Enum 77 | // ---------------------------------------------------------------- 78 | it('Should map Enum', () => { 79 | const T = TypeBox(z.enum(['a', 'b', 'c'])) 80 | Assert.IsTrue(TypeGuard.IsUnion(T)) 81 | Assert.IsEqual(T.anyOf[0].const, 'a') 82 | Assert.IsEqual(T.anyOf[1].const, 'b') 83 | Assert.IsEqual(T.anyOf[2].const, 'c') 84 | }) 85 | // ---------------------------------------------------------------- 86 | // Literal 87 | // ---------------------------------------------------------------- 88 | it('Should map Literal (Number)', () => { 89 | const T = TypeBox(z.literal(42)) 90 | Assert.IsTrue(TypeGuard.IsLiteral(T)) 91 | Assert.IsEqual(T.const, 42) 92 | }) 93 | it('Should map Literal (String)', () => { 94 | const T = TypeBox(z.literal('hello')) 95 | Assert.IsTrue(TypeGuard.IsLiteral(T)) 96 | Assert.IsEqual(T.const, 'hello') 97 | }) 98 | it('Should map Literal (Boolean)', () => { 99 | const T = TypeBox(z.literal(true)) 100 | Assert.IsTrue(TypeGuard.IsLiteral(T)) 101 | Assert.IsEqual(T.const, true) 102 | }) 103 | // ---------------------------------------------------------------- 104 | // Nullable 105 | // ---------------------------------------------------------------- 106 | it('Should map Nullable', () => { 107 | const T = TypeBox(z.number().nullable()) 108 | Assert.IsTrue(TypeGuard.IsUnion(T)) 109 | Assert.IsTrue(TypeGuard.IsNull(T.anyOf[0])) 110 | Assert.IsTrue(TypeGuard.IsNumber(T.anyOf[1])) 111 | }) 112 | // ---------------------------------------------------------------- 113 | // Object 114 | // ---------------------------------------------------------------- 115 | it('Should map Object', () => { 116 | const T = TypeBox( 117 | z.object({ 118 | x: z.number(), 119 | y: z.string(), 120 | }), 121 | ) 122 | Assert.IsTrue(TypeGuard.IsObject(T)) 123 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 124 | Assert.IsTrue(TypeGuard.IsString(T.properties.y)) 125 | }) 126 | it('Should map Object (Strict)', () => { 127 | const T = TypeBox( 128 | z 129 | .object({ 130 | x: z.number(), 131 | y: z.string(), 132 | }) 133 | .strict(), 134 | ) 135 | Assert.IsTrue(TypeGuard.IsObject(T)) 136 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 137 | Assert.IsTrue(TypeGuard.IsString(T.properties.y)) 138 | Assert.IsEqual(T.additionalProperties, false) 139 | }) 140 | // ---------------------------------------------------------------- 141 | // Optional 142 | // ---------------------------------------------------------------- 143 | it('Should map Optional', () => { 144 | const T = TypeBox( 145 | z.object({ 146 | x: z.number().optional(), 147 | y: z.number().optional(), 148 | }), 149 | ) 150 | Assert.IsTrue(TypeGuard.IsObject(T)) 151 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 152 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) 153 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 154 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) 155 | }) 156 | it('Should map Optional (Readonly)', () => { 157 | const T = TypeBox( 158 | z.object({ 159 | x: z.number().optional().readonly(), 160 | y: z.number().optional().readonly(), 161 | }), 162 | ) 163 | Assert.IsTrue(TypeGuard.IsObject(T)) 164 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 165 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) 166 | Assert.IsTrue(TypeGuard.IsReadonly(T.properties.x)) 167 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 168 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) 169 | Assert.IsTrue(TypeGuard.IsReadonly(T.properties.y)) 170 | }) 171 | it('Should map Optional (Partial)', () => { 172 | const T = TypeBox( 173 | z 174 | .object({ 175 | x: z.number(), 176 | y: z.number(), 177 | }) 178 | .partial(), 179 | ) 180 | Assert.IsTrue(TypeGuard.IsObject(T)) 181 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 182 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) 183 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 184 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) 185 | }) 186 | // ---------------------------------------------------------------- 187 | // Promise 188 | // ---------------------------------------------------------------- 189 | it('Should map Promise', () => { 190 | const T = TypeBox(z.promise(z.number())) 191 | Assert.IsTrue(TypeGuard.IsPromise(T)) 192 | Assert.IsTrue(TypeGuard.IsNumber(T.item)) 193 | }) 194 | // ---------------------------------------------------------------- 195 | // Readonly 196 | // ---------------------------------------------------------------- 197 | it('Should map Readonly', () => { 198 | const T = TypeBox( 199 | z.object({ 200 | x: z.number().readonly(), 201 | y: z.number().readonly(), 202 | }), 203 | ) 204 | Assert.IsTrue(TypeGuard.IsObject(T)) 205 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 206 | Assert.IsTrue(TypeGuard.IsReadonly(T.properties.x)) 207 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 208 | Assert.IsTrue(TypeGuard.IsReadonly(T.properties.y)) 209 | }) 210 | it('Should map Readonly (Optional)', () => { 211 | const T = TypeBox( 212 | z.object({ 213 | x: z.number().readonly().optional(), 214 | y: z.number().readonly().optional(), 215 | }), 216 | ) 217 | Assert.IsTrue(TypeGuard.IsObject(T)) 218 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 219 | Assert.IsTrue(TypeGuard.IsReadonly(T.properties.x)) 220 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) 221 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 222 | Assert.IsTrue(TypeGuard.IsReadonly(T.properties.y)) 223 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) 224 | }) 225 | // ---------------------------------------------------------------- 226 | // Record 227 | // ---------------------------------------------------------------- 228 | it('Should map Record (Key Implicit)', () => { 229 | const T = TypeBox(z.record(z.number())) 230 | Assert.IsTrue(TypeGuard.IsRecord(T)) 231 | Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[t.PatternStringExact])) 232 | }) 233 | it('Should map Record (Number Key)', () => { 234 | const T = TypeBox(z.record(z.number(), z.number())) 235 | Assert.IsTrue(TypeGuard.IsRecord(T)) 236 | Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[t.PatternNumberExact])) 237 | }) 238 | it('Should map Record (String Key)', () => { 239 | const T = TypeBox(z.record(z.string(), z.number())) 240 | Assert.IsTrue(TypeGuard.IsRecord(T)) 241 | Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[t.PatternStringExact])) 242 | }) 243 | it('Should map Record (Finite Union)', () => { 244 | const T = TypeBox(z.record(z.union([z.literal('x'), z.literal('y')]), z.number())) 245 | Assert.IsTrue(TypeGuard.IsObject(T)) 246 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 247 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 248 | }) 249 | // ---------------------------------------------------------------- 250 | // Never 251 | // ---------------------------------------------------------------- 252 | it('Should map Never', () => { 253 | const T = TypeBox(z.never()) 254 | Assert.IsTrue(TypeGuard.IsNever(T)) 255 | }) 256 | // ---------------------------------------------------------------- 257 | // Null 258 | // ---------------------------------------------------------------- 259 | it('Should map Null', () => { 260 | const T = TypeBox(z.null()) 261 | Assert.IsTrue(TypeGuard.IsNull(T)) 262 | }) 263 | // ---------------------------------------------------------------- 264 | // Number 265 | // ---------------------------------------------------------------- 266 | it('Should map Number', () => { 267 | const T = TypeBox(z.number()) 268 | Assert.IsTrue(TypeGuard.IsNumber(T)) 269 | }) 270 | it('Should map Number (Integer)', () => { 271 | const T = TypeBox(z.number().int()) 272 | Assert.IsTrue(TypeGuard.IsNumber(T)) 273 | Assert.IsEqual(T.multipleOf, 1) 274 | }) 275 | it('Should map Number (Minimum)', () => { 276 | const T = TypeBox(z.number().min(100)) 277 | Assert.IsTrue(TypeGuard.IsNumber(T)) 278 | Assert.IsEqual(T.minimum, 100) 279 | }) 280 | it('Should map Number (Maximum)', () => { 281 | const T = TypeBox(z.number().max(100)) 282 | Assert.IsTrue(TypeGuard.IsNumber(T)) 283 | Assert.IsEqual(T.maximum, 100) 284 | }) 285 | // ---------------------------------------------------------------- 286 | // String 287 | // ---------------------------------------------------------------- 288 | it('Should map String', () => { 289 | const T = TypeBox(z.string()) 290 | Assert.IsTrue(TypeGuard.IsString(T)) 291 | }) 292 | it('Should map String (Base64)', () => { 293 | const T = TypeBox(z.string().base64()) 294 | Assert.IsTrue(TypeGuard.IsString(T)) 295 | Assert.IsEqual(T.format, 'base64') 296 | }) 297 | it('Should map String (Base64Url)', () => { 298 | const T = TypeBox(z.string().base64url()) 299 | Assert.IsTrue(TypeGuard.IsString(T)) 300 | Assert.IsEqual(T.format, 'base64url') 301 | }) 302 | it('Should map String (Cidr V4)', () => { 303 | const T = TypeBox(z.string().cidr({ version: 'v4' })) 304 | Assert.IsTrue(TypeGuard.IsString(T)) 305 | Assert.IsEqual(T.format, 'cidrv4') 306 | }) 307 | it('Should map String (Cidr v6)', () => { 308 | const T = TypeBox(z.string().cidr({ version: 'v6' })) 309 | Assert.IsTrue(TypeGuard.IsString(T)) 310 | Assert.IsEqual(T.format, 'cidrv6') 311 | }) 312 | it('Should map String (Cidr)', () => { 313 | const T = TypeBox(z.string().cidr()) 314 | Assert.IsTrue(TypeGuard.IsString(T)) 315 | Assert.IsEqual(T.format, 'cidr') 316 | }) 317 | it('Should map String (Cuid)', () => { 318 | const T = TypeBox(z.string().cuid()) 319 | Assert.IsTrue(TypeGuard.IsString(T)) 320 | Assert.IsEqual(T.format, 'cuid') 321 | }) 322 | it('Should map String (Cuid2)', () => { 323 | const T = TypeBox(z.string().cuid2()) 324 | Assert.IsTrue(TypeGuard.IsString(T)) 325 | Assert.IsEqual(T.format, 'cuid2') 326 | }) 327 | it('Should map String (Ulid)', () => { 328 | const T = TypeBox(z.string().ulid()) 329 | Assert.IsTrue(TypeGuard.IsString(T)) 330 | Assert.IsEqual(T.format, 'ulid') 331 | }) 332 | it('Should map String (Email)', () => { 333 | const T = TypeBox(z.string().email()) 334 | Assert.IsTrue(TypeGuard.IsString(T)) 335 | Assert.IsEqual(T.format, 'email') 336 | }) 337 | it('Should map String (Emoji)', () => { 338 | const T = TypeBox(z.string().emoji()) 339 | Assert.IsTrue(TypeGuard.IsString(T)) 340 | Assert.IsEqual(T.format, 'emoji') 341 | }) 342 | it('Should map String (EndsWith)', () => { 343 | const T = TypeBox(z.string().endsWith('hello')) 344 | Assert.IsTrue(TypeGuard.IsString(T)) 345 | Assert.IsEqual(T.pattern, 'hello$') 346 | }) 347 | it('Should map String (Includes)', () => { 348 | const T = TypeBox(z.string().includes('hello')) 349 | Assert.IsTrue(TypeGuard.IsString(T)) 350 | Assert.IsEqual(T.pattern, 'hello') 351 | }) 352 | it('Should map String (IpV4)', () => { 353 | const T = TypeBox(z.string().ip({ version: 'v4' })) 354 | Assert.IsTrue(TypeGuard.IsString(T)) 355 | Assert.IsEqual(T.format, 'ipv4') 356 | }) 357 | it('Should map String (IpV6)', () => { 358 | const T = TypeBox(z.string().ip({ version: 'v6' })) 359 | Assert.IsTrue(TypeGuard.IsString(T)) 360 | Assert.IsEqual(T.format, 'ipv6') 361 | }) 362 | it('Should map String (Ip)', () => { 363 | const T = TypeBox(z.string().ip()) 364 | Assert.IsTrue(TypeGuard.IsString(T)) 365 | Assert.IsEqual(T.format, 'ip') 366 | }) 367 | it('Should map String (Jwt)', () => { 368 | const T = TypeBox(z.string().jwt()) 369 | Assert.IsTrue(TypeGuard.IsString(T)) 370 | Assert.IsEqual(T.format, 'jwt') 371 | }) 372 | it('Should map String (Length)', () => { 373 | const T = TypeBox(z.string().length(100)) 374 | Assert.IsTrue(TypeGuard.IsString(T)) 375 | Assert.IsEqual(T.minLength, 100) 376 | Assert.IsEqual(T.maxLength, 100) 377 | }) 378 | 379 | it('Should map String (Min)', () => { 380 | const T = TypeBox(z.string().min(100)) 381 | Assert.IsTrue(TypeGuard.IsString(T)) 382 | Assert.IsEqual(T.minLength, 100) 383 | }) 384 | it('Should map String (Max)', () => { 385 | const T = TypeBox(z.string().max(100)) 386 | Assert.IsTrue(TypeGuard.IsString(T)) 387 | Assert.IsEqual(T.maxLength, 100) 388 | }) 389 | it('Should map String (Nanoid)', () => { 390 | const T = TypeBox(z.string().nanoid()) 391 | Assert.IsTrue(TypeGuard.IsString(T)) 392 | Assert.IsEqual(T.format, 'nanoid') 393 | }) 394 | it('Should map String (RegExp)', () => { 395 | const T = TypeBox(z.string().regex(/abc/)) 396 | Assert.IsTrue(TypeGuard.IsString(T)) 397 | Assert.IsEqual(T.pattern, 'abc') 398 | }) 399 | it('Should map String (StartsWith)', () => { 400 | const T = TypeBox(z.string().startsWith('hello')) 401 | Assert.IsTrue(TypeGuard.IsString(T)) 402 | Assert.IsEqual(T.pattern, '^hello') 403 | }) 404 | it('Should map String (Time)', () => { 405 | const T = TypeBox(z.string().time()) 406 | Assert.IsTrue(TypeGuard.IsString(T)) 407 | Assert.IsEqual(T.format, 'time') 408 | }) 409 | it('Should map String (Ulid)', () => { 410 | const T = TypeBox(z.string().ulid()) 411 | Assert.IsTrue(TypeGuard.IsString(T)) 412 | Assert.IsEqual(T.format, 'ulid') 413 | }) 414 | it('Should map String (Url)', () => { 415 | const T = TypeBox(z.string().url()) 416 | Assert.IsTrue(TypeGuard.IsString(T)) 417 | Assert.IsEqual(T.format, 'url') 418 | }) 419 | it('Should map String (Uuid)', () => { 420 | const T = TypeBox(z.string().uuid()) 421 | Assert.IsTrue(TypeGuard.IsString(T)) 422 | Assert.IsEqual(T.format, 'uuid') 423 | }) 424 | // ---------------------------------------------------------------- 425 | // Symbol 426 | // ---------------------------------------------------------------- 427 | it('Should map Symbol', () => { 428 | const T = TypeBox(z.symbol()) 429 | Assert.IsTrue(TypeGuard.IsSymbol(T)) 430 | }) 431 | // ---------------------------------------------------------------- 432 | // Tuple 433 | // ---------------------------------------------------------------- 434 | it('Should map Tuple', () => { 435 | const T = TypeBox(z.tuple([z.number(), z.string()])) 436 | Assert.IsTrue(TypeGuard.IsTuple(T)) 437 | Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) 438 | Assert.IsTrue(TypeGuard.IsString(T.items![1])) 439 | }) 440 | // ---------------------------------------------------------------- 441 | // Undefined 442 | // ---------------------------------------------------------------- 443 | it('Should map Undefined', () => { 444 | const T = TypeBox(z.undefined()) 445 | Assert.IsTrue(TypeGuard.IsUndefined(T)) 446 | }) 447 | // ---------------------------------------------------------------- 448 | // Union 449 | // ---------------------------------------------------------------- 450 | it('Should map Union', () => { 451 | const T = TypeBox(z.union([z.string(), z.boolean()])) 452 | Assert.IsTrue(TypeGuard.IsUnion(T)) 453 | Assert.IsTrue(TypeGuard.IsString(T.anyOf[0])) 454 | Assert.IsTrue(TypeGuard.IsBoolean(T.anyOf[1])) 455 | }) 456 | // ---------------------------------------------------------------- 457 | // Unknown 458 | // ---------------------------------------------------------------- 459 | it('Should map Unknown', () => { 460 | const T = TypeBox(z.unknown()) 461 | Assert.IsTrue(TypeGuard.IsUnknown(T)) 462 | }) 463 | // ---------------------------------------------------------------- 464 | // Void 465 | // ---------------------------------------------------------------- 466 | it('Should map Void', () => { 467 | const T = TypeBox(z.void()) 468 | Assert.IsTrue(TypeGuard.IsVoid(T)) 469 | }) 470 | }) 471 | -------------------------------------------------------------------------------- /test/valibot-from-typebox.ts: -------------------------------------------------------------------------------- 1 | import { TypeBox, Valibot } from '@sinclair/typemap' 2 | import { TypeGuard } from '@sinclair/typebox' 3 | import { Assert } from './assert' 4 | import * as t from '@sinclair/typebox' 5 | import * as v from 'valibot' 6 | 7 | describe('Valibot from TypeBox', () => { 8 | // ---------------------------------------------------------------- 9 | // Metadata 10 | // ---------------------------------------------------------------- 11 | it('Should map Description', () => { 12 | const T = TypeBox(Valibot(t.Number({ description: 'a number' }))) 13 | Assert.IsEqual(T.description, 'a number') 14 | }) 15 | it('Should map Title', () => { 16 | const T = TypeBox(Valibot(t.Number({ title: 'a number' }))) 17 | Assert.IsEqual(T.title, 'a number') 18 | }) 19 | it('Should map Metadata', () => { 20 | const T = TypeBox(Valibot(t.Number({ metadata: { x: 1, y: 2 } }))) 21 | Assert.IsEqual(T.metadata.x, 1) 22 | Assert.IsEqual(T.metadata.y, 2) 23 | }) 24 | // ---------------------------------------------------------------- 25 | // Any 26 | // ---------------------------------------------------------------- 27 | it('Should map Any', () => { 28 | const T = TypeBox(Valibot(t.Any())) 29 | Assert.IsTrue(TypeGuard.IsAny(T)) 30 | }) 31 | // ---------------------------------------------------------------- 32 | // Array 33 | // ---------------------------------------------------------------- 34 | it('Should map Array', () => { 35 | const T = TypeBox(Valibot(t.Array(t.Number()))) 36 | Assert.IsTrue(TypeGuard.IsArray(T)) 37 | Assert.IsTrue(TypeGuard.IsNumber(T.items)) 38 | }) 39 | // ---------------------------------------------------------------- 40 | // BigInt 41 | // ---------------------------------------------------------------- 42 | it('Should map BigInt', () => { 43 | const T = TypeBox(Valibot(t.BigInt())) 44 | Assert.IsTrue(TypeGuard.IsBigInt(T)) 45 | }) 46 | // ---------------------------------------------------------------- 47 | // Date 48 | // ---------------------------------------------------------------- 49 | it('Should map Date', () => { 50 | const T = TypeBox(Valibot(t.Date())) 51 | Assert.IsTrue(TypeGuard.IsDate(T)) 52 | }) 53 | // ---------------------------------------------------------------- 54 | // Effects 55 | // ---------------------------------------------------------------- 56 | // it('Should map Effects (Transform)', () => { 57 | // const T = TypeBox(v.number().transform(x => x)) 58 | // Assert.IsTrue(TypeGuard.IsNumber(T)) 59 | // Assert.IsTrue(TypeGuard.IsTransform(T)) 60 | // }) 61 | // it('Should map Effects (Refine)', () => { 62 | // const T = TypeBox(v.number().refine(x => true)) 63 | // Assert.IsTrue(TypeGuard.IsNumber(T)) 64 | // Assert.IsTrue(TypeGuard.IsTransform(T)) 65 | // }) 66 | // ---------------------------------------------------------------- 67 | // Literal 68 | // ---------------------------------------------------------------- 69 | it('Should map Literal (Number)', () => { 70 | const T = TypeBox(Valibot(t.Literal(42))) 71 | Assert.IsTrue(TypeGuard.IsLiteral(T)) 72 | Assert.IsEqual(T.const, 42) 73 | }) 74 | it('Should map Literal (String)', () => { 75 | const T = TypeBox(Valibot(t.Literal('hello'))) 76 | Assert.IsTrue(TypeGuard.IsLiteral(T)) 77 | Assert.IsEqual(T.const, 'hello') 78 | }) 79 | it('Should map Literal (Boolean)', () => { 80 | const T = TypeBox(Valibot(t.Literal(true))) 81 | Assert.IsTrue(TypeGuard.IsLiteral(T)) 82 | Assert.IsEqual(T.const, true) 83 | }) 84 | // ---------------------------------------------------------------- 85 | // Nullable 86 | // ---------------------------------------------------------------- 87 | it('Should map Nullable', () => { 88 | const T = TypeBox(Valibot(t.Union([t.Null(), t.Number()]))) 89 | Assert.IsTrue(TypeGuard.IsUnion(T)) 90 | Assert.IsTrue(TypeGuard.IsNull(T.anyOf[0])) 91 | Assert.IsTrue(TypeGuard.IsNumber(T.anyOf[1])) 92 | }) 93 | // ---------------------------------------------------------------- 94 | // Object 95 | // ---------------------------------------------------------------- 96 | it('Should map Object', () => { 97 | const T = TypeBox( 98 | Valibot( 99 | t.Object({ 100 | x: t.Number(), 101 | y: t.String(), 102 | }), 103 | ), 104 | ) 105 | Assert.IsTrue(TypeGuard.IsObject(T)) 106 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 107 | Assert.IsTrue(TypeGuard.IsString(T.properties.y)) 108 | }) 109 | it('Should map Object (Strict)', () => { 110 | const T = TypeBox( 111 | Valibot( 112 | t.Object( 113 | { 114 | x: t.Number(), 115 | y: t.String(), 116 | }, 117 | { additionalProperties: false }, 118 | ), 119 | ), 120 | ) 121 | Assert.IsTrue(TypeGuard.IsObject(T)) 122 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 123 | Assert.IsTrue(TypeGuard.IsString(T.properties.y)) 124 | Assert.IsEqual(T.additionalProperties, false) 125 | }) 126 | // ---------------------------------------------------------------- 127 | // Optional 128 | // ---------------------------------------------------------------- 129 | it('Should map Optional', () => { 130 | const T = TypeBox( 131 | Valibot( 132 | t.Object({ 133 | x: t.Optional(t.Number()), 134 | y: t.Optional(t.Number()), 135 | }), 136 | ), 137 | ) 138 | Assert.IsTrue(TypeGuard.IsObject(T)) 139 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 140 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) 141 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 142 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) 143 | }) 144 | it('Should map Optional (Partial)', () => { 145 | const T = TypeBox( 146 | Valibot( 147 | t.Partial( 148 | t.Object({ 149 | x: t.Optional(t.Number()), 150 | y: t.Optional(t.Number()), 151 | }), 152 | ), 153 | ), 154 | ) 155 | Assert.IsTrue(TypeGuard.IsObject(T)) 156 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 157 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) 158 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 159 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) 160 | }) 161 | // ---------------------------------------------------------------- 162 | // Promise 163 | // ---------------------------------------------------------------- 164 | it('Should map Promise', () => { 165 | const T = TypeBox(Valibot(t.Promise(t.String()))) 166 | Assert.IsEqual(T[t.Kind], 'ValibotPromise') 167 | }) 168 | // ---------------------------------------------------------------- 169 | // Record 170 | // ---------------------------------------------------------------- 171 | it('Should map Record (String Key)', () => { 172 | const T = TypeBox(Valibot(t.Record(t.String(), t.Number()))) 173 | Assert.IsTrue(TypeGuard.IsRecord(T)) 174 | Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[t.PatternStringExact])) 175 | }) 176 | it('Should map Record (Finite Union)', () => { 177 | const T = TypeBox(Valibot(t.Record(t.Union([t.Literal('x'), t.Literal('y')]), t.Number()))) 178 | Assert.IsTrue(TypeGuard.IsObject(T)) 179 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 180 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 181 | }) 182 | // ---------------------------------------------------------------- 183 | // Never 184 | // ---------------------------------------------------------------- 185 | it('Should map Never', () => { 186 | const T = TypeBox(Valibot(t.Never())) 187 | Assert.IsTrue(TypeGuard.IsNever(T)) 188 | }) 189 | // ---------------------------------------------------------------- 190 | // Null 191 | // ---------------------------------------------------------------- 192 | it('Should map Null', () => { 193 | const T = TypeBox(Valibot(t.Null())) 194 | Assert.IsTrue(TypeGuard.IsNull(T)) 195 | }) 196 | // ---------------------------------------------------------------- 197 | // Number 198 | // ---------------------------------------------------------------- 199 | it('Should map Number', () => { 200 | const T = TypeBox(Valibot(t.Number())) 201 | Assert.IsTrue(TypeGuard.IsNumber(T)) 202 | }) 203 | it('Should map Number (Integer)', () => { 204 | const T = TypeBox(Valibot(t.Integer())) // remap as Number + Modulo 205 | Assert.IsTrue(TypeGuard.IsNumber(T)) 206 | Assert.IsEqual(T.multipleOf, 1) 207 | }) 208 | it('Should map Number (Minimum)', () => { 209 | const T = TypeBox(Valibot(t.Number({ minimum: 100 }))) 210 | Assert.IsTrue(TypeGuard.IsNumber(T)) 211 | Assert.IsEqual(T.minimum, 100) 212 | }) 213 | it('Should map Number (Maximum)', () => { 214 | const T = TypeBox(Valibot(t.Number({ maximum: 100 }))) 215 | Assert.IsTrue(TypeGuard.IsNumber(T)) 216 | Assert.IsEqual(T.maximum, 100) 217 | }) 218 | // ---------------------------------------------------------------- 219 | // String 220 | // ---------------------------------------------------------------- 221 | it('Should map String', () => { 222 | const T = TypeBox(Valibot(t.String())) 223 | Assert.IsTrue(TypeGuard.IsString(T)) 224 | }) 225 | it('Should map String (Base64)', () => { 226 | const T = TypeBox(Valibot(t.String({ format: 'base64' }))) 227 | Assert.IsTrue(TypeGuard.IsString(T)) 228 | Assert.IsEqual(T.format, 'base64') 229 | }) 230 | it('Should map String (Bic)', () => { 231 | const T = TypeBox(Valibot(t.String({ format: 'bic' }))) 232 | Assert.IsTrue(TypeGuard.IsString(T)) 233 | Assert.IsEqual(T.format, 'bic') 234 | }) 235 | it('Should map String (CreditCard)', () => { 236 | const T = TypeBox(Valibot(t.String({ format: 'credit_card' }))) 237 | Assert.IsTrue(TypeGuard.IsString(T)) 238 | Assert.IsEqual(T.format, 'credit_card') 239 | }) 240 | it('Should map String (Cuid2)', () => { 241 | const T = TypeBox(Valibot(t.String({ format: 'cuid2' }))) 242 | Assert.IsTrue(TypeGuard.IsString(T)) 243 | Assert.IsEqual(T.format, 'cuid2') 244 | }) 245 | it('Should map String (Decimal)', () => { 246 | const T = TypeBox(Valibot(t.String({ format: 'decimal' }))) 247 | Assert.IsTrue(TypeGuard.IsString(T)) 248 | Assert.IsEqual(T.format, 'decimal') 249 | }) 250 | it('Should map String (Digits)', () => { 251 | const T = TypeBox(Valibot(t.String({ format: 'digits' }))) 252 | Assert.IsTrue(TypeGuard.IsString(T)) 253 | Assert.IsEqual(T.format, 'digits') 254 | }) 255 | it('Should map String (Email)', () => { 256 | const T = TypeBox(Valibot(t.String({ format: 'email' }))) 257 | Assert.IsTrue(TypeGuard.IsString(T)) 258 | Assert.IsEqual(T.format, 'email') 259 | }) 260 | it('Should map String (Emoji)', () => { 261 | const T = TypeBox(Valibot(t.String({ format: 'emoji' }))) 262 | Assert.IsTrue(TypeGuard.IsString(T)) 263 | Assert.IsEqual(T.format, 'emoji') 264 | }) 265 | it('Should map String (EndsWith)', () => { 266 | const T = TypeBox(Valibot(t.String({ pattern: 'hello$' }))) 267 | Assert.IsTrue(TypeGuard.IsString(T)) 268 | Assert.IsEqual(T.pattern, 'hello$') 269 | }) 270 | it('Should map String (Includes)', () => { 271 | const T = TypeBox(Valibot(t.String({ pattern: 'hello' }))) 272 | Assert.IsTrue(TypeGuard.IsString(T)) 273 | Assert.IsEqual(T.pattern, 'hello') 274 | }) 275 | it('Should map String (Ipv4)', () => { 276 | const T = TypeBox(Valibot(t.String({ format: 'ipv4' }))) 277 | Assert.IsTrue(TypeGuard.IsString(T)) 278 | Assert.IsEqual(T.format, 'ipv4') 279 | }) 280 | it('Should map String (IpV6)', () => { 281 | const T = TypeBox(Valibot(t.String({ format: 'ipv6' }))) 282 | Assert.IsTrue(TypeGuard.IsString(T)) 283 | Assert.IsEqual(T.format, 'ipv6') 284 | }) 285 | it('Should map String (Ip)', () => { 286 | const T = TypeBox(Valibot(t.String({ format: 'ip' }))) 287 | Assert.IsTrue(TypeGuard.IsString(T)) 288 | Assert.IsEqual(T.format, 'ip') 289 | }) 290 | it('Should map String (IsoDate)', () => { 291 | const T = TypeBox(Valibot(t.String({ format: 'iso_date' }))) 292 | Assert.IsTrue(TypeGuard.IsString(T)) 293 | Assert.IsEqual(T.format, 'iso_date') 294 | }) 295 | it('Should map String (IsoDateTime)', () => { 296 | const T = TypeBox(Valibot(t.String({ format: 'iso_date_time' }))) 297 | Assert.IsTrue(TypeGuard.IsString(T)) 298 | Assert.IsEqual(T.format, 'iso_date_time') 299 | }) 300 | it('Should map String (IsoTime)', () => { 301 | const T = TypeBox(Valibot(t.String({ format: 'iso_time' }))) 302 | Assert.IsTrue(TypeGuard.IsString(T)) 303 | Assert.IsEqual(T.format, 'iso_time') 304 | }) 305 | it('Should map String (IsoTimeSecond)', () => { 306 | const T = TypeBox(Valibot(t.String({ format: 'iso_time_second' }))) 307 | Assert.IsTrue(TypeGuard.IsString(T)) 308 | Assert.IsEqual(T.format, 'iso_time_second') 309 | }) 310 | it('Should map String (IsoTimestamp)', () => { 311 | const T = TypeBox(Valibot(t.String({ format: 'iso_timestamp' }))) 312 | Assert.IsTrue(TypeGuard.IsString(T)) 313 | Assert.IsEqual(T.format, 'iso_timestamp') 314 | }) 315 | it('Should map String (IsoWeek)', () => { 316 | const T = TypeBox(Valibot(t.String({ format: 'iso_week' }))) 317 | Assert.IsTrue(TypeGuard.IsString(T)) 318 | Assert.IsEqual(T.format, 'iso_week') 319 | }) 320 | it('Should map String (Mac48)', () => { 321 | const T = TypeBox(Valibot(t.String({ format: 'mac48' }))) 322 | Assert.IsTrue(TypeGuard.IsString(T)) 323 | Assert.IsEqual(T.format, 'mac48') 324 | }) 325 | it('Should map String (Mac64)', () => { 326 | const T = TypeBox(Valibot(t.String({ format: 'mac64' }))) 327 | Assert.IsTrue(TypeGuard.IsString(T)) 328 | Assert.IsEqual(T.format, 'mac64') 329 | }) 330 | it('Should map String (Mac)', () => { 331 | const T = TypeBox(Valibot(t.String({ format: 'mac' }))) 332 | Assert.IsTrue(TypeGuard.IsString(T)) 333 | Assert.IsEqual(T.format, 'mac') 334 | }) 335 | it('Should map String (MaxLength)', () => { 336 | const T = TypeBox(Valibot(t.String({ maxLength: 100 }))) 337 | Assert.IsTrue(TypeGuard.IsString(T)) 338 | Assert.IsEqual(T.maxLength, 100) 339 | }) 340 | it('Should map String (MinLength)', () => { 341 | const T = TypeBox(Valibot(t.String({ minLength: 100 }))) 342 | Assert.IsTrue(TypeGuard.IsString(T)) 343 | Assert.IsEqual(T.minLength, 100) 344 | }) 345 | it('Should map String (Nanoid)', () => { 346 | const T = TypeBox(Valibot(t.String({ format: 'nanoid' }))) 347 | Assert.IsTrue(TypeGuard.IsString(T)) 348 | Assert.IsEqual(T.format, 'nanoid') 349 | }) 350 | it('Should map String (Octal)', () => { 351 | const T = TypeBox(Valibot(t.String({ format: 'octal' }))) 352 | Assert.IsTrue(TypeGuard.IsString(T)) 353 | Assert.IsEqual(T.format, 'octal') 354 | }) 355 | it('Should map String (RegExp)', () => { 356 | const T = TypeBox(Valibot(t.RegExp(/abc/))) 357 | Assert.IsTrue(TypeGuard.IsString(T)) 358 | Assert.IsEqual(T.pattern, 'abc') 359 | }) 360 | it('Should map String (StartsWith)', () => { 361 | const T = TypeBox(Valibot(t.String({ pattern: '^hello' }))) 362 | Assert.IsTrue(TypeGuard.IsString(T)) 363 | Assert.IsEqual(T.pattern, '^hello') 364 | }) 365 | it('Should map String (Ulid)', () => { 366 | const T = TypeBox(Valibot(t.String({ format: 'ulid' }))) 367 | Assert.IsTrue(TypeGuard.IsString(T)) 368 | Assert.IsEqual(T.format, 'ulid') 369 | }) 370 | it('Should map String (Url)', () => { 371 | const T = TypeBox(Valibot(t.String({ format: 'url' }))) 372 | Assert.IsTrue(TypeGuard.IsString(T)) 373 | Assert.IsEqual(T.format, 'url') 374 | }) 375 | it('Should map String (Uuid)', () => { 376 | const T = TypeBox(Valibot(t.String({ format: 'uuid' }))) 377 | Assert.IsTrue(TypeGuard.IsString(T)) 378 | Assert.IsEqual(T.format, 'uuid') 379 | }) 380 | // ---------------------------------------------------------------- 381 | // Symbol 382 | // ---------------------------------------------------------------- 383 | it('Should map Symbol', () => { 384 | const T = TypeBox(Valibot(t.Symbol())) 385 | Assert.IsTrue(TypeGuard.IsSymbol(T)) 386 | }) 387 | // ---------------------------------------------------------------- 388 | // Tuple 389 | // ---------------------------------------------------------------- 390 | it('Should map Tuple', () => { 391 | const T = TypeBox(Valibot(t.Tuple([t.Number(), t.String()]))) 392 | Assert.IsTrue(TypeGuard.IsTuple(T)) 393 | Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) 394 | Assert.IsTrue(TypeGuard.IsString(T.items![1])) 395 | }) 396 | // ---------------------------------------------------------------- 397 | // Undefined 398 | // ---------------------------------------------------------------- 399 | it('Should map Undefined', () => { 400 | const T = TypeBox(Valibot(t.Undefined())) 401 | Assert.IsTrue(TypeGuard.IsUndefined(T)) 402 | }) 403 | // ---------------------------------------------------------------- 404 | // Union 405 | // ---------------------------------------------------------------- 406 | it('Should map Union', () => { 407 | const T = TypeBox(Valibot(t.Union([t.String(), t.Boolean()]))) 408 | Assert.IsTrue(TypeGuard.IsUnion(T)) 409 | Assert.IsTrue(TypeGuard.IsString(T.anyOf[0])) 410 | Assert.IsTrue(TypeGuard.IsBoolean(T.anyOf[1])) 411 | }) 412 | // ---------------------------------------------------------------- 413 | // Unknown 414 | // ---------------------------------------------------------------- 415 | it('Should map Unknown', () => { 416 | const T = TypeBox(Valibot(t.Unknown())) 417 | Assert.IsTrue(TypeGuard.IsUnknown(T)) 418 | }) 419 | // ---------------------------------------------------------------- 420 | // Void 421 | // ---------------------------------------------------------------- 422 | it('Should map Void', () => { 423 | const T = TypeBox(Valibot(t.Void())) 424 | Assert.IsTrue(TypeGuard.IsVoid(T)) 425 | }) 426 | }) 427 | -------------------------------------------------------------------------------- /test/zod-from-typebox.ts: -------------------------------------------------------------------------------- 1 | import { TypeBox, Zod } from '@sinclair/typemap' 2 | import { TypeGuard } from '@sinclair/typebox' 3 | import { Assert } from './assert' 4 | import * as t from '@sinclair/typebox' 5 | import * as z from 'zod' 6 | 7 | describe('Zod From TypeBox', () => { 8 | // ---------------------------------------------------------------- 9 | // Metadata 10 | // ---------------------------------------------------------------- 11 | it('Should map Description', () => { 12 | const T = TypeBox(Zod(t.Number({ description: 'a number' }))) 13 | Assert.IsEqual(T.description, 'a number') 14 | }) 15 | it('Should map Default', () => { 16 | const T = TypeBox(Zod(t.Number({ default: 12345 }))) 17 | Assert.IsEqual(T.default, 12345) 18 | }) 19 | // ---------------------------------------------------------------- 20 | // Any 21 | // ---------------------------------------------------------------- 22 | it('Should map Any', () => { 23 | const T = TypeBox(Zod(t.Any())) 24 | Assert.IsTrue(TypeGuard.IsAny(T)) 25 | }) 26 | // ---------------------------------------------------------------- 27 | // Array 28 | // ---------------------------------------------------------------- 29 | it('Should map Array', () => { 30 | const T = TypeBox(Zod(t.Array(t.Number()))) 31 | Assert.IsTrue(TypeGuard.IsArray(T)) 32 | Assert.IsTrue(TypeGuard.IsNumber(T.items)) 33 | }) 34 | // ---------------------------------------------------------------- 35 | // BigInt 36 | // ---------------------------------------------------------------- 37 | it('Should map BigInt', () => { 38 | const T = TypeBox(Zod(t.BigInt())) 39 | Assert.IsTrue(TypeGuard.IsBigInt(T)) 40 | }) 41 | // ---------------------------------------------------------------- 42 | // Date 43 | // ---------------------------------------------------------------- 44 | it('Should map Date', () => { 45 | const T = TypeBox(Zod(t.Date())) 46 | Assert.IsTrue(TypeGuard.IsDate(T)) 47 | }) 48 | // ---------------------------------------------------------------- 49 | // Effects 50 | // ---------------------------------------------------------------- 51 | // it('Should map Effects (Transform)', () => { 52 | // const T = TypeBox(z.number().transform((x) => x)) 53 | // Assert.IsTrue(TypeGuard.IsNumber(T)) 54 | // Assert.IsTrue(TypeGuard.IsTransform(T)) 55 | // }) 56 | // it('Should map Effects (Refine)', () => { 57 | // const T = TypeBox(z.number().refine((x) => true)) 58 | // Assert.IsTrue(TypeGuard.IsNumber(T)) 59 | // Assert.IsTrue(TypeGuard.IsTransform(T)) 60 | // }) 61 | // ---------------------------------------------------------------- 62 | // Literal 63 | // ---------------------------------------------------------------- 64 | it('Should map Literal (Number)', () => { 65 | const T = TypeBox(Zod(t.Literal(42))) 66 | Assert.IsTrue(TypeGuard.IsLiteral(T)) 67 | Assert.IsEqual(T.const, 42) 68 | }) 69 | it('Should map Literal (String)', () => { 70 | const T = TypeBox(Zod(t.Literal('hello'))) 71 | Assert.IsTrue(TypeGuard.IsLiteral(T)) 72 | Assert.IsEqual(T.const, 'hello') 73 | }) 74 | it('Should map Literal (Boolean)', () => { 75 | const T = TypeBox(Zod(t.Literal(true))) 76 | Assert.IsTrue(TypeGuard.IsLiteral(T)) 77 | Assert.IsEqual(T.const, true) 78 | }) 79 | // ---------------------------------------------------------------- 80 | // Object 81 | // ---------------------------------------------------------------- 82 | it('Should map Object', () => { 83 | const T = TypeBox( 84 | Zod( 85 | t.Object({ 86 | x: t.Number(), 87 | y: t.String(), 88 | }), 89 | ), 90 | ) 91 | Assert.IsTrue(TypeGuard.IsObject(T)) 92 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 93 | Assert.IsTrue(TypeGuard.IsString(T.properties.y)) 94 | }) 95 | it('Should map Object (Strict)', () => { 96 | const T = TypeBox( 97 | Zod( 98 | t.Object( 99 | { 100 | x: t.Number(), 101 | y: t.String(), 102 | }, 103 | { additionalProperties: false }, 104 | ), 105 | ), 106 | ) 107 | Assert.IsTrue(TypeGuard.IsObject(T)) 108 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 109 | Assert.IsTrue(TypeGuard.IsString(T.properties.y)) 110 | Assert.IsEqual(T.additionalProperties, false) 111 | }) 112 | // ---------------------------------------------------------------- 113 | // Optional 114 | // ---------------------------------------------------------------- 115 | it('Should map Optional', () => { 116 | const T = TypeBox( 117 | Zod( 118 | t.Object({ 119 | x: t.Optional(t.Number()), 120 | y: t.Optional(t.Number()), 121 | }), 122 | ), 123 | ) 124 | Assert.IsTrue(TypeGuard.IsObject(T)) 125 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 126 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) 127 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 128 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) 129 | }) 130 | it('Should map Optional (Readonly)', () => { 131 | const T = TypeBox( 132 | Zod( 133 | t.Object({ 134 | x: t.ReadonlyOptional(t.Number()), 135 | y: t.ReadonlyOptional(t.Number()), 136 | }), 137 | ), 138 | ) 139 | Assert.IsTrue(TypeGuard.IsObject(T)) 140 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 141 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) 142 | Assert.IsFalse(TypeGuard.IsReadonly(T.properties.x)) // Cannot Map for Readonly in Zod 143 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 144 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) 145 | Assert.IsFalse(TypeGuard.IsReadonly(T.properties.y)) // Cannot Map for Readonly in Zod 146 | }) 147 | it('Should map Optional (Partial)', () => { 148 | const T = TypeBox( 149 | Zod( 150 | t.Partial( 151 | t.Object({ 152 | x: t.Number(), 153 | y: t.Number(), 154 | }), 155 | ), 156 | ), 157 | ) 158 | Assert.IsTrue(TypeGuard.IsObject(T)) 159 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 160 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) 161 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 162 | Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) 163 | }) 164 | // ---------------------------------------------------------------- 165 | // Promise 166 | // ---------------------------------------------------------------- 167 | it('Should map Promise', () => { 168 | const T = TypeBox(Zod(t.Promise(t.Number()))) 169 | Assert.IsTrue(TypeGuard.IsPromise(T)) 170 | Assert.IsTrue(TypeGuard.IsNumber(T.item)) 171 | }) 172 | // ---------------------------------------------------------------- 173 | // Readonly 174 | // ---------------------------------------------------------------- 175 | it('Should map Readonly', () => { 176 | const T = TypeBox( 177 | Zod( 178 | t.Object({ 179 | x: t.Readonly(t.Number()), 180 | y: t.Readonly(t.Number()), 181 | }), 182 | ), 183 | ) 184 | Assert.IsTrue(TypeGuard.IsObject(T)) 185 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 186 | Assert.IsFalse(TypeGuard.IsReadonly(T.properties.x)) 187 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 188 | Assert.IsFalse(TypeGuard.IsReadonly(T.properties.y)) 189 | }) 190 | // ---------------------------------------------------------------- 191 | // Record 192 | // ---------------------------------------------------------------- 193 | it('Should map Record (Number Key)', () => { 194 | const T = TypeBox(Zod(t.Record(t.Number(), t.Number()))) 195 | Assert.IsTrue(TypeGuard.IsRecord(T)) 196 | Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[t.PatternNumberExact])) 197 | }) 198 | it('Should map Record (String Key)', () => { 199 | const T = TypeBox(Zod(t.Record(t.String(), t.Number()))) 200 | Assert.IsTrue(TypeGuard.IsRecord(T)) 201 | Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties[t.PatternStringExact])) 202 | }) 203 | it('Should map Record (Finite Union)', () => { 204 | const T = TypeBox(Zod(t.Record(t.Union([t.Literal('x'), t.Literal('y')]), t.Number()))) 205 | Assert.IsTrue(TypeGuard.IsObject(T)) 206 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) 207 | Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) 208 | }) 209 | // ---------------------------------------------------------------- 210 | // Never 211 | // ---------------------------------------------------------------- 212 | it('Should map Never', () => { 213 | const T = TypeBox(Zod(t.Never())) 214 | Assert.IsTrue(TypeGuard.IsNever(T)) 215 | }) 216 | // ---------------------------------------------------------------- 217 | // Null 218 | // ---------------------------------------------------------------- 219 | it('Should map Null', () => { 220 | const T = TypeBox(Zod(t.Null())) 221 | Assert.IsTrue(TypeGuard.IsNull(T)) 222 | }) 223 | // ---------------------------------------------------------------- 224 | // Number 225 | // ---------------------------------------------------------------- 226 | it('Should map Number', () => { 227 | const T = TypeBox(Zod(t.Number())) 228 | Assert.IsTrue(TypeGuard.IsNumber(T)) 229 | }) 230 | it('Should map Number (Integer)', () => { 231 | const T = TypeBox(Zod(t.Integer())) 232 | Assert.IsTrue(TypeGuard.IsNumber(T)) 233 | Assert.IsEqual(T.multipleOf, 1) 234 | }) 235 | it('Should map Number (Minimum)', () => { 236 | const T = TypeBox(Zod(t.Number({ minimum: 100 }))) 237 | Assert.IsTrue(TypeGuard.IsNumber(T)) 238 | Assert.IsEqual(T.minimum, 100) 239 | }) 240 | it('Should map Number (Maximum)', () => { 241 | const T = TypeBox(Zod(t.Number({ maximum: 100 }))) 242 | Assert.IsTrue(TypeGuard.IsNumber(T)) 243 | Assert.IsEqual(T.maximum, 100) 244 | }) 245 | // ---------------------------------------------------------------- 246 | // String 247 | // ---------------------------------------------------------------- 248 | it('Should map String', () => { 249 | const T = TypeBox(Zod(t.String())) 250 | Assert.IsTrue(TypeGuard.IsString(T)) 251 | }) 252 | it('Should map String (Base64)', () => { 253 | const T = TypeBox(Zod(t.String({ format: 'base64' }))) 254 | Assert.IsTrue(TypeGuard.IsString(T)) 255 | Assert.IsEqual(T.format, 'base64') 256 | }) 257 | it('Should map String (Base64Url)', () => { 258 | const T = TypeBox(Zod(t.String({ format: 'base64url' }))) 259 | Assert.IsTrue(TypeGuard.IsString(T)) 260 | Assert.IsEqual(T.format, 'base64url') 261 | }) 262 | it('Should map String (Cidr V4)', () => { 263 | const T = TypeBox(Zod(t.String({ format: 'cidrv4' }))) 264 | Assert.IsTrue(TypeGuard.IsString(T)) 265 | Assert.IsEqual(T.format, 'cidrv4') 266 | }) 267 | it('Should map String (Cidr v6)', () => { 268 | const T = TypeBox(Zod(t.String({ format: 'cidrv6' }))) 269 | Assert.IsTrue(TypeGuard.IsString(T)) 270 | Assert.IsEqual(T.format, 'cidrv6') 271 | }) 272 | it('Should map String (Cidr)', () => { 273 | const T = TypeBox(Zod(t.String({ format: 'cidr' }))) 274 | Assert.IsTrue(TypeGuard.IsString(T)) 275 | Assert.IsEqual(T.format, 'cidr') 276 | }) 277 | it('Should map String (Cuid)', () => { 278 | const T = TypeBox(Zod(t.String({ format: 'cuid' }))) 279 | Assert.IsTrue(TypeGuard.IsString(T)) 280 | Assert.IsEqual(T.format, 'cuid') 281 | }) 282 | it('Should map String (Cuid2)', () => { 283 | const T = TypeBox(Zod(t.String({ format: 'cuid2' }))) 284 | Assert.IsTrue(TypeGuard.IsString(T)) 285 | Assert.IsEqual(T.format, 'cuid2') 286 | }) 287 | it('Should map String (Ulid)', () => { 288 | const T = TypeBox(Zod(t.String({ format: 'ulid' }))) 289 | Assert.IsTrue(TypeGuard.IsString(T)) 290 | Assert.IsEqual(T.format, 'ulid') 291 | }) 292 | it('Should map String (Email)', () => { 293 | const T = TypeBox(Zod(t.String({ format: 'email' }))) 294 | Assert.IsTrue(TypeGuard.IsString(T)) 295 | Assert.IsEqual(T.format, 'email') 296 | }) 297 | it('Should map String (Emoji)', () => { 298 | const T = TypeBox(Zod(t.String({ format: 'emoji' }))) 299 | Assert.IsTrue(TypeGuard.IsString(T)) 300 | Assert.IsEqual(T.format, 'emoji') 301 | }) 302 | it('Should map String (EndsWith)', () => { 303 | const T = TypeBox(Zod(t.String({ pattern: 'hello$' }))) 304 | Assert.IsTrue(TypeGuard.IsString(T)) 305 | Assert.IsEqual(T.pattern, 'hello$') 306 | }) 307 | it('Should map String (Includes)', () => { 308 | const T = TypeBox(Zod(t.String({ pattern: 'hello' }))) 309 | Assert.IsTrue(TypeGuard.IsString(T)) 310 | Assert.IsEqual(T.pattern, 'hello') 311 | }) 312 | it('Should map String (IpV4)', () => { 313 | const T = TypeBox(Zod(t.String({ format: 'ipv4' }))) 314 | Assert.IsTrue(TypeGuard.IsString(T)) 315 | Assert.IsEqual(T.format, 'ipv4') 316 | }) 317 | it('Should map String (IpV6)', () => { 318 | const T = TypeBox(Zod(t.String({ format: 'ipv6' }))) 319 | Assert.IsTrue(TypeGuard.IsString(T)) 320 | Assert.IsEqual(T.format, 'ipv6') 321 | }) 322 | it('Should map String (Ip)', () => { 323 | const T = TypeBox(Zod(t.String({ format: 'ip' }))) 324 | Assert.IsTrue(TypeGuard.IsString(T)) 325 | Assert.IsEqual(T.format, 'ip') 326 | }) 327 | it('Should map String (Jwt)', () => { 328 | const T = TypeBox(Zod(t.String({ format: 'jwt' }))) 329 | Assert.IsTrue(TypeGuard.IsString(T)) 330 | Assert.IsEqual(T.format, 'jwt') 331 | }) 332 | it('Should map String (Length)', () => { 333 | const T = TypeBox(Zod(t.String({ minLength: 100, maxLength: 100 }))) 334 | Assert.IsTrue(TypeGuard.IsString(T)) 335 | Assert.IsEqual(T.minLength, 100) 336 | Assert.IsEqual(T.maxLength, 100) 337 | }) 338 | it('Should map String (Min)', () => { 339 | const T = TypeBox(Zod(t.String({ minLength: 100 }))) 340 | Assert.IsTrue(TypeGuard.IsString(T)) 341 | Assert.IsEqual(T.minLength, 100) 342 | }) 343 | it('Should map String (Max)', () => { 344 | const T = TypeBox(Zod(t.String({ maxLength: 100 }))) 345 | Assert.IsTrue(TypeGuard.IsString(T)) 346 | Assert.IsEqual(T.maxLength, 100) 347 | }) 348 | it('Should map String (Nanoid)', () => { 349 | const T = TypeBox(Zod(t.String({ format: 'nanoid' }))) 350 | Assert.IsTrue(TypeGuard.IsString(T)) 351 | Assert.IsEqual(T.format, 'nanoid') 352 | }) 353 | it('Should map String (RegExp)', () => { 354 | const T = TypeBox(Zod(t.RegExp(/abc/))) 355 | Assert.IsTrue(TypeGuard.IsString(T)) 356 | Assert.IsEqual(T.pattern, 'abc') 357 | }) 358 | it('Should map String (StartsWith)', () => { 359 | const T = TypeBox(Zod(t.String({ pattern: '^hello' }))) 360 | Assert.IsTrue(TypeGuard.IsString(T)) 361 | Assert.IsEqual(T.pattern, '^hello') 362 | }) 363 | it('Should map String (Time)', () => { 364 | const T = TypeBox(Zod(t.String({ format: 'time' }))) 365 | Assert.IsTrue(TypeGuard.IsString(T)) 366 | Assert.IsEqual(T.format, 'time') 367 | }) 368 | it('Should map String (Ulid)', () => { 369 | const T = TypeBox(Zod(t.String({ format: 'ulid' }))) 370 | Assert.IsTrue(TypeGuard.IsString(T)) 371 | Assert.IsEqual(T.format, 'ulid') 372 | }) 373 | it('Should map String (Url)', () => { 374 | const T = TypeBox(Zod(t.String({ format: 'url' }))) 375 | Assert.IsTrue(TypeGuard.IsString(T)) 376 | Assert.IsEqual(T.format, 'url') 377 | }) 378 | it('Should map String (Uuid)', () => { 379 | const T = TypeBox(Zod(t.String({ format: 'uuid' }))) 380 | Assert.IsTrue(TypeGuard.IsString(T)) 381 | Assert.IsEqual(T.format, 'uuid') 382 | }) 383 | // ---------------------------------------------------------------- 384 | // Symbol 385 | // ---------------------------------------------------------------- 386 | it('Should map Symbol', () => { 387 | const T = TypeBox(Zod(t.Symbol())) 388 | Assert.IsTrue(TypeGuard.IsSymbol(T)) 389 | }) 390 | // ---------------------------------------------------------------- 391 | // Tuple 392 | // ---------------------------------------------------------------- 393 | it('Should map Tuple', () => { 394 | const T = TypeBox(Zod(t.Tuple([t.Number(), t.String()]))) 395 | Assert.IsTrue(TypeGuard.IsTuple(T)) 396 | Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) 397 | Assert.IsTrue(TypeGuard.IsString(T.items![1])) 398 | }) 399 | // ---------------------------------------------------------------- 400 | // Undefined 401 | // ---------------------------------------------------------------- 402 | it('Should map Undefined', () => { 403 | const T = TypeBox(Zod(t.Undefined())) 404 | Assert.IsTrue(TypeGuard.IsUndefined(T)) 405 | }) 406 | // ---------------------------------------------------------------- 407 | // Union 408 | // ---------------------------------------------------------------- 409 | it('Should map Union', () => { 410 | const T = TypeBox(Zod(t.Union([t.String(), t.Boolean()]))) 411 | Assert.IsTrue(TypeGuard.IsUnion(T)) 412 | Assert.IsTrue(TypeGuard.IsString(T.anyOf[0])) 413 | Assert.IsTrue(TypeGuard.IsBoolean(T.anyOf[1])) 414 | }) 415 | // ---------------------------------------------------------------- 416 | // Unknown 417 | // ---------------------------------------------------------------- 418 | it('Should map Unknown', () => { 419 | const T = TypeBox(Zod(t.Unknown())) 420 | Assert.IsTrue(TypeGuard.IsUnknown(T)) 421 | }) 422 | // ---------------------------------------------------------------- 423 | // Void 424 | // ---------------------------------------------------------------- 425 | it('Should map Void', () => { 426 | const T = TypeBox(Zod(t.Void())) 427 | Assert.IsTrue(TypeGuard.IsVoid(T)) 428 | }) 429 | }) 430 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "ES2020", 5 | "moduleResolution": "Node", 6 | "baseUrl": ".", 7 | "paths": { 8 | "@sinclair/typemap": ["src/index.ts"] 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /typemap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinclairzx81/typemap/631fab497a16871a7c15069b31e4ae6cda9077bd/typemap.png --------------------------------------------------------------------------------