|
| 1 | +/*-------------------------------------------------------------------------- |
| 2 | +
|
| 3 | +@sinclair/typebox/prototypes |
| 4 | +
|
| 5 | +The MIT License (MIT) |
| 6 | +
|
| 7 | +Copyright (c) 2017-2024 Haydn Paterson (sinclair) <[email protected]> |
| 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 Types from '@sinclair/typebox' |
| 30 | + |
| 31 | +// ------------------------------------------------------------------ |
| 32 | +// Mapping: Functions and Type |
| 33 | +// ------------------------------------------------------------------ |
| 34 | +export type TMappingFunction = (schema: Types.TSchema) => Types.TSchema |
| 35 | + |
| 36 | +export interface TMappingType { |
| 37 | + input: unknown |
| 38 | + output: unknown |
| 39 | +} |
| 40 | +// ------------------------------------------------------------------ |
| 41 | +// Record Parameters |
| 42 | +// ------------------------------------------------------------------ |
| 43 | +function GetRecordPattern(record: Types.TRecord): string { |
| 44 | + return globalThis.Object.getOwnPropertyNames(record.patternProperties)[0] |
| 45 | +} |
| 46 | +function GetRecordKey(record: Types.TRecord): Types.TSchema { |
| 47 | + const pattern = GetRecordPattern(record) |
| 48 | + return ( |
| 49 | + pattern === Types.PatternStringExact ? Types.String() : |
| 50 | + pattern === Types.PatternNumberExact ? Types.Number() : |
| 51 | + pattern === Types.PatternBooleanExact ? Types.Boolean() : |
| 52 | + Types.String({ pattern }) |
| 53 | + ) |
| 54 | +} |
| 55 | +function GetRecordValue(record: Types.TRecord): Types.TSchema { |
| 56 | + return record.patternProperties[GetRecordPattern(record)] |
| 57 | +} |
| 58 | +// ------------------------------------------------------------------ |
| 59 | +// Traversal |
| 60 | +// ------------------------------------------------------------------ |
| 61 | +// prettier-ignore |
| 62 | +type TApply<Type extends Types.TSchema, Func extends TMappingType, |
| 63 | + Mapped = (Func & { input: Type })['output'], |
| 64 | + Result = Mapped extends Types.TSchema ? Mapped : never |
| 65 | +> = Result |
| 66 | +// prettier-ignore |
| 67 | +type TFromProperties<Properties extends Types.TProperties, Func extends TMappingType, Result extends Types.TProperties = { |
| 68 | + [Key in keyof Properties]: TRecursiveMap<Properties[Key], Func> |
| 69 | +}> = Result |
| 70 | +function FromProperties(properties: Types.TProperties, func: TMappingFunction): Types.TProperties { |
| 71 | + return globalThis.Object.getOwnPropertyNames(properties).reduce((result, key) => { |
| 72 | + return {...result, [key]: RecursiveMap(properties[key], func) } |
| 73 | + }, {}) |
| 74 | +} |
| 75 | +// prettier-ignore |
| 76 | +type TFromRest<Types extends Types.TSchema[], Func extends TMappingType, Result extends Types.TSchema[] = []> = ( |
| 77 | + Types extends [infer Left extends Types.TSchema, ...infer Right extends Types.TSchema[]] |
| 78 | + ? TFromRest<Right, Func, [...Result, TRecursiveMap<Left, Func>]> |
| 79 | + : Result |
| 80 | +) |
| 81 | +function FromRest(types: Types.TSchema[], func: TMappingFunction): Types.TSchema[] { |
| 82 | + return types.map(type => RecursiveMap(type, func)) |
| 83 | +} |
| 84 | +// prettier-ignore |
| 85 | +type TFromType<Type extends Types.TSchema, Func extends TMappingType, Result extends Types.TSchema = ( |
| 86 | + TApply<Type, Func> |
| 87 | +)> = Result |
| 88 | +function FromType(type: Types.TSchema, func: TMappingFunction): Types.TSchema { |
| 89 | + return func(type) |
| 90 | +} |
| 91 | +// ------------------------------------------------------------------ |
| 92 | +// TRecursiveMap<Type, Mapping> |
| 93 | +// ------------------------------------------------------------------ |
| 94 | +/** `[Prototype]` Applies a deep recursive map across the given type and sub types. */ |
| 95 | +// prettier-ignore |
| 96 | +export type TRecursiveMap<Type extends Types.TSchema, Func extends TMappingType, |
| 97 | + // Maps the Exterior Type |
| 98 | + Exterior extends Types.TSchema = TFromType<Type, Func>, |
| 99 | + // Maps the Interior Parameterized Types |
| 100 | + Interior extends Types.TSchema = ( |
| 101 | + Exterior extends Types.TConstructor<infer Parameters extends Types.TSchema[], infer ReturnType extends Types.TSchema> ? Types.TConstructor<TFromRest<Parameters, Func>, TFromType<ReturnType, Func>> : |
| 102 | + Exterior extends Types.TFunction<infer Parameters extends Types.TSchema[], infer ReturnType extends Types.TSchema> ? Types.TFunction<TFromRest<Parameters, Func>, TFromType<ReturnType, Func>> : |
| 103 | + Exterior extends Types.TIntersect<infer Types extends Types.TSchema[]> ? Types.TIntersect<TFromRest<Types, Func>> : |
| 104 | + Exterior extends Types.TUnion<infer Types extends Types.TSchema[]> ? Types.TUnion<TFromRest<Types, Func>> : |
| 105 | + Exterior extends Types.TTuple<infer Types extends Types.TSchema[]> ? Types.TTuple<TFromRest<Types, Func>> : |
| 106 | + Exterior extends Types.TArray<infer Type extends Types.TSchema> ? Types.TArray<TFromType<Type, Func>>: |
| 107 | + Exterior extends Types.TAsyncIterator<infer Type extends Types.TSchema> ? Types.TAsyncIterator<TFromType<Type, Func>> : |
| 108 | + Exterior extends Types.TIterator<infer Type extends Types.TSchema> ? Types.TIterator<TFromType<Type, Func>> : |
| 109 | + Exterior extends Types.TPromise<infer Type extends Types.TSchema> ? Types.TPromise<TFromType<Type, Func>> : |
| 110 | + Exterior extends Types.TObject<infer Properties extends Types.TProperties> ? Types.TObject<TFromProperties<Properties, Func>> : |
| 111 | + Exterior extends Types.TRecord<infer Key extends Types.TSchema, infer Value extends Types.TSchema> ? Types.TRecordOrObject<TFromType<Key, Func>, TFromType<Value, Func>> : |
| 112 | + Exterior |
| 113 | + ), |
| 114 | + // Modifiers Derived from Exterior Type Mapping |
| 115 | + IsOptional extends number = Exterior extends Types.TOptional<Types.TSchema> ? 1 : 0, |
| 116 | + IsReadonly extends number = Exterior extends Types.TReadonly<Types.TSchema> ? 1 : 0, |
| 117 | + Result extends Types.TSchema = ( |
| 118 | + [IsReadonly, IsOptional] extends [1, 1] ? Types.TReadonlyOptional<Interior> : |
| 119 | + [IsReadonly, IsOptional] extends [0, 1] ? Types.TOptional<Interior> : |
| 120 | + [IsReadonly, IsOptional] extends [1, 0] ? Types.TReadonly<Interior> : |
| 121 | + Interior |
| 122 | + ) |
| 123 | +> = Result |
| 124 | +/** `[Prototype]` Applies a deep recursive map across the given type and sub types. */ |
| 125 | +// prettier-ignore |
| 126 | +export function RecursiveMap(type: Types.TSchema, func: TMappingFunction): Types.TSchema { |
| 127 | + // Maps the Exterior Type |
| 128 | + const exterior = Types.CloneType(FromType(type, func), type) |
| 129 | + // Maps the Interior Parameterized Types |
| 130 | + const interior = ( |
| 131 | + Types.KindGuard.IsConstructor(type) ? Types.Constructor(FromRest(type.parameters, func), FromType(type.returns, func), exterior) : |
| 132 | + Types.KindGuard.IsFunction(type) ? Types.Function(FromRest(type.parameters, func), FromType(type.returns, func), exterior) : |
| 133 | + Types.KindGuard.IsIntersect(type) ? Types.Intersect(FromRest(type.allOf, func), exterior) : |
| 134 | + Types.KindGuard.IsUnion(type) ? Types.Union(FromRest(type.anyOf, func), exterior) : |
| 135 | + Types.KindGuard.IsTuple(type) ? Types.Tuple(FromRest(type.items || [], func), exterior) : |
| 136 | + Types.KindGuard.IsArray(type) ? Types.Array(FromType(type.items, func), exterior) : |
| 137 | + Types.KindGuard.IsAsyncIterator(type) ? Types.AsyncIterator(FromType(type.items, func), exterior) : |
| 138 | + Types.KindGuard.IsIterator(type) ? Types.Iterator(FromType(type.items, func), exterior) : |
| 139 | + Types.KindGuard.IsPromise(type) ? Types.Promise(FromType(type.items, func), exterior) : |
| 140 | + Types.KindGuard.IsObject(type) ? Types.Object(FromProperties(type.properties, func), exterior) : |
| 141 | + Types.KindGuard.IsRecord(type) ? Types.Record(FromType(GetRecordKey(type), func), FromType(GetRecordValue(type), func), exterior) : |
| 142 | + Types.CloneType(exterior, exterior) |
| 143 | + ) |
| 144 | + // Modifiers Derived from Exterior Type Mapping |
| 145 | + const isOptional = Types.KindGuard.IsOptional(exterior) |
| 146 | + const isReadonly = Types.KindGuard.IsOptional(exterior) |
| 147 | + return ( |
| 148 | + isOptional && isReadonly ? Types.ReadonlyOptional(interior) : |
| 149 | + isOptional ? Types.Optional(interior) : |
| 150 | + isReadonly ? Types.Readonly(interior) : |
| 151 | + interior |
| 152 | + ) |
| 153 | +} |
| 154 | + |
| 155 | + |
| 156 | + |
0 commit comments