diff --git a/example/prototypes/from-schema.ts b/example/prototypes/from-schema.ts new file mode 100644 index 0000000..94a4716 --- /dev/null +++ b/example/prototypes/from-schema.ts @@ -0,0 +1,237 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/prototypes + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Type from '@sinclair/typebox' + +// ------------------------------------------------------------------ +// Schematics +// ------------------------------------------------------------------ +const IsExact = (value: unknown, expect: unknown) => value === expect +const IsSValue = (value: unknown): value is SValue => Type.ValueGuard.IsString(value) || Type.ValueGuard.IsNumber(value) || Type.ValueGuard.IsBoolean(value) +const IsSEnum = (value: unknown): value is SEnum => Type.ValueGuard.IsObject(value) && Type.ValueGuard.IsArray(value.enum) && value.enum.every((value) => IsSValue(value)) +const IsSAllOf = (value: unknown): value is SAllOf => Type.ValueGuard.IsObject(value) && Type.ValueGuard.IsArray(value.allOf) +const IsSAnyOf = (value: unknown): value is SAnyOf => Type.ValueGuard.IsObject(value) && Type.ValueGuard.IsArray(value.anyOf) +const IsSOneOf = (value: unknown): value is SOneOf => Type.ValueGuard.IsObject(value) && Type.ValueGuard.IsArray(value.oneOf) +const IsSTuple = (value: unknown): value is STuple => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'array') && Type.ValueGuard.IsArray(value.items) +const IsSArray = (value: unknown): value is SArray => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'array') && !Type.ValueGuard.IsArray(value.items) && Type.ValueGuard.IsObject(value.items) +const IsSConst = (value: unknown): value is SConst => Type.ValueGuard.IsObject(value) && Type.ValueGuard.IsObject(value['const']) +const IsSString = (value: unknown): value is SString => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'string') +const IsSNumber = (value: unknown): value is SNumber => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'number') +const IsSInteger = (value: unknown): value is SInteger => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'integer') +const IsSBoolean = (value: unknown): value is SBoolean => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'boolean') +const IsSNull = (value: unknown): value is SBoolean => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'null') +const IsSProperties = (value: unknown): value is SProperties => Type.ValueGuard.IsObject(value) +// prettier-ignore +const IsSObject = (value: unknown): value is SObject => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'object') && IsSProperties(value.properties) && (value.required === undefined || Type.ValueGuard.IsArray(value.required) && value.required.every((value: unknown) => Type.ValueGuard.IsString(value))) +type SValue = string | number | boolean +type SEnum = Readonly<{ enum: readonly SValue[] }> +type SAllOf = Readonly<{ allOf: readonly unknown[] }> +type SAnyOf = Readonly<{ anyOf: readonly unknown[] }> +type SOneOf = Readonly<{ oneOf: readonly unknown[] }> +type SProperties = Record +type SObject = Readonly<{ type: 'object'; properties: SProperties; required?: readonly string[] }> +type STuple = Readonly<{ type: 'array'; items: readonly unknown[] }> +type SArray = Readonly<{ type: 'array'; items: unknown }> +type SConst = Readonly<{ const: SValue }> +type SString = Readonly<{ type: 'string' }> +type SNumber = Readonly<{ type: 'number' }> +type SInteger = Readonly<{ type: 'integer' }> +type SBoolean = Readonly<{ type: 'boolean' }> +type SNull = Readonly<{ type: 'null' }> +// ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRest = ( + T extends readonly [infer L extends unknown, ...infer R extends unknown[]] + ? TFromSchema extends infer S extends Type.TSchema + ? TFromRest + : TFromRest + : Acc +) +function FromRest(T: T): TFromRest { + return T.map((L) => FromSchema(L)) as never +} +// ------------------------------------------------------------------ +// FromEnumRest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromEnumRest = ( + T extends readonly [infer L extends SValue, ...infer R extends SValue[]] + ? TFromEnumRest]> + : Acc +) +function FromEnumRest(T: T): TFromEnumRest { + return T.map((L) => Type.Literal(L)) as never +} +// ------------------------------------------------------------------ +// AllOf +// ------------------------------------------------------------------ +// prettier-ignore +type TFromAllOf = ( + TFromRest extends infer Rest extends Type.TSchema[] + ? Type.TIntersectEvaluated + : Type.TNever +) +function FromAllOf(T: T): TFromAllOf { + return Type.IntersectEvaluated(FromRest(T.allOf), T) +} +// ------------------------------------------------------------------ +// AnyOf +// ------------------------------------------------------------------ +// prettier-ignore +type TFromAnyOf = ( + TFromRest extends infer Rest extends Type.TSchema[] + ? Type.TUnionEvaluated + : Type.TNever +) +function FromAnyOf(T: T): TFromAnyOf { + return Type.UnionEvaluated(FromRest(T.anyOf), T) +} +// ------------------------------------------------------------------ +// OneOf +// ------------------------------------------------------------------ +// prettier-ignore +type TFromOneOf = ( + TFromRest extends infer Rest extends Type.TSchema[] + ? Type.TUnionEvaluated + : Type.TNever +) +function FromOneOf(T: T): TFromOneOf { + return Type.UnionEvaluated(FromRest(T.oneOf), T) +} +// ------------------------------------------------------------------ +// Enum +// ------------------------------------------------------------------ +// prettier-ignore +type TFromEnum = ( + TFromEnumRest extends infer Elements extends Type.TSchema[] + ? Type.TUnionEvaluated + : Type.TNever +) +function FromEnum(T: T): TFromEnum { + return Type.UnionEvaluated(FromEnumRest(T.enum)) +} +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTuple = ( + TFromRest extends infer Elements extends Type.TSchema[] + ? Type.TTuple + : Type.TTuple<[]> +) +// prettier-ignore +function FromTuple(T: T): TFromTuple { + return Type.Tuple(FromRest(T.items), T) as never +} +// ------------------------------------------------------------------ +// Array +// ------------------------------------------------------------------ +// prettier-ignore +type TFromArray = ( + TFromSchema extends infer Items extends Type.TSchema + ? Type.TArray + : Type.TArray +) +// prettier-ignore +function FromArray(T: T): TFromArray { + return Type.Array(FromSchema(T.items), T) as never +} +// ------------------------------------------------------------------ +// Const +// ------------------------------------------------------------------ +// prettier-ignore +type TFromConst = ( + Type.Ensure> +) +function FromConst(T: T) { + return Type.Literal(T.const, T) +} +// ------------------------------------------------------------------ +// Object +// ------------------------------------------------------------------ +type TFromPropertiesIsOptional = unknown extends R ? true : K extends R ? false : true +// prettier-ignore +type TFromProperties = Type.Evaluate<{ + -readonly [K in keyof T]: TFromPropertiesIsOptional extends true + ? Type.TOptional> + : TFromSchema +}> +// prettier-ignore +type TFromObject = ( + TFromProperties[number]> extends infer Properties extends Type.TProperties + ? Type.TObject + : Type.TObject<{}> +) +function FromObject(T: T): TFromObject { + const properties = globalThis.Object.getOwnPropertyNames(T.properties).reduce((Acc, K) => { + return { ...Acc, [K]: T.required && T.required.includes(K) ? FromSchema(T.properties[K]) : Type.Optional(FromSchema(T.properties[K])) } + }, {} as Type.TProperties) + return Type.Object(properties, T) as never +} +// ------------------------------------------------------------------ +// FromSchema +// ------------------------------------------------------------------ +// prettier-ignore +export type TFromSchema = ( + T extends SAllOf ? TFromAllOf : + T extends SAnyOf ? TFromAnyOf : + T extends SOneOf ? TFromOneOf : + T extends SEnum ? TFromEnum : + T extends SObject ? TFromObject : + T extends STuple ? TFromTuple : + T extends SArray ? TFromArray : + T extends SConst ? TFromConst : + T extends SString ? Type.TString : + T extends SNumber ? Type.TNumber : + T extends SInteger ? Type.TInteger : + T extends SBoolean ? Type.TBoolean : + T extends SNull ? Type.TNull : + Type.TUnknown +) +/** Parses a TypeBox type from raw JsonSchema */ +export function FromSchema(T: T): TFromSchema { + // prettier-ignore + return ( + IsSAllOf(T) ? FromAllOf(T) : + IsSAnyOf(T) ? FromAnyOf(T) : + IsSOneOf(T) ? FromOneOf(T) : + IsSEnum(T) ? FromEnum(T) : + IsSObject(T) ? FromObject(T) : + IsSTuple(T) ? FromTuple(T) : + IsSArray(T) ? FromArray(T) : + IsSConst(T) ? FromConst(T) : + IsSString(T) ? Type.String(T) : + IsSNumber(T) ? Type.Number(T) : + IsSInteger(T) ? Type.Integer(T) : + IsSBoolean(T) ? Type.Boolean(T) : + IsSNull(T) ? Type.Null(T) : + Type.Unknown(T || {}) + ) as never +} diff --git a/example/prototypes/index.ts b/example/prototypes/index.ts index c001126..c0039fc 100644 --- a/example/prototypes/index.ts +++ b/example/prototypes/index.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export * from './evaluate' +export * from './from-schema' export * from './partial-deep' export * from './union-enum' export * from './union-oneof'