Publish
This commit is contained in:
73
src/value/assert/assert.ts
Normal file
73
src/value/assert/assert.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { Errors, ValueErrorIterator, ValueError } from '../../errors/index'
|
||||
import { TypeBoxError } from '../../type/error/error'
|
||||
import { TSchema } from '../../type/schema/index'
|
||||
import { Static } from '../../type/static/index'
|
||||
import { Check } from '../check/check'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// AssertError
|
||||
// ------------------------------------------------------------------
|
||||
export class AssertError extends TypeBoxError {
|
||||
readonly #iterator: ValueErrorIterator
|
||||
error: ValueError | undefined
|
||||
constructor(iterator: ValueErrorIterator) {
|
||||
const error = iterator.First()
|
||||
super(error === undefined ? 'Invalid Value' : error.message)
|
||||
this.#iterator = iterator
|
||||
this.error = error
|
||||
}
|
||||
/** Returns an iterator for each error in this value. */
|
||||
public Errors(): ValueErrorIterator {
|
||||
return new ValueErrorIterator(this.#Iterator())
|
||||
}
|
||||
*#Iterator(): IterableIterator<ValueError> {
|
||||
if (this.error) yield this.error
|
||||
yield* this.#iterator
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// AssertValue
|
||||
// ------------------------------------------------------------------
|
||||
function AssertValue(schema: TSchema, references: TSchema[], value: unknown): unknown {
|
||||
if (Check(schema, references, value)) return
|
||||
throw new AssertError(Errors(schema, references, value))
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Assert
|
||||
// ------------------------------------------------------------------
|
||||
/** Asserts a value matches the given type or throws an `AssertError` if invalid */
|
||||
export function Assert<T extends TSchema>(schema: T, references: TSchema[], value: unknown): asserts value is Static<T>
|
||||
/** Asserts a value matches the given type or throws an `AssertError` if invalid */
|
||||
export function Assert<T extends TSchema>(schema: T, value: unknown): asserts value is Static<T>
|
||||
/** Asserts a value matches the given type or throws an `AssertError` if invalid */
|
||||
export function Assert(...args: any[]): unknown {
|
||||
return args.length === 3 ? AssertValue(args[0], args[1], args[2]) : AssertValue(args[0], [], args[1])
|
||||
}
|
||||
29
src/value/assert/index.ts
Normal file
29
src/value/assert/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './assert'
|
||||
266
src/value/cast/cast.ts
Normal file
266
src/value/cast/cast.ts
Normal file
@@ -0,0 +1,266 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { IsObject, IsArray, IsString, IsNumber, IsNull } from '../guard/index'
|
||||
import { TypeBoxError } from '../../type/error/index'
|
||||
import { Kind } from '../../type/symbols/index'
|
||||
import { Create } from '../create/index'
|
||||
import { Check } from '../check/index'
|
||||
import { Clone } from '../clone/index'
|
||||
import { Deref, Pushref } from '../deref/index'
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
import type { Static } from '../../type/static/index'
|
||||
import type { TArray } from '../../type/array/index'
|
||||
import type { TConstructor } from '../../type/constructor/index'
|
||||
import type { TImport } from '../../type/module/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TObject } from '../../type/object/index'
|
||||
import type { TRecord } from '../../type/record/index'
|
||||
import type { TRef } from '../../type/ref/index'
|
||||
import type { TThis } from '../../type/recursive/index'
|
||||
import type { TTuple } from '../../type/tuple/index'
|
||||
import type { TUnion } from '../../type/union/index'
|
||||
import type { TNever } from '../../type/never/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Errors
|
||||
// ------------------------------------------------------------------
|
||||
export class ValueCastError extends TypeBoxError {
|
||||
constructor(public readonly schema: TSchema, message: string) {
|
||||
super(message)
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// The following logic assigns a score to a schema based on how well
|
||||
// it matches a given value. For object types, the score is calculated
|
||||
// by evaluating each property of the value against the schema's
|
||||
// properties. To avoid bias towards objects with many properties,
|
||||
// each property contributes equally to the total score. Properties
|
||||
// that exactly match literal values receive the highest possible
|
||||
// score, as literals are often used as discriminators in union types.
|
||||
// ------------------------------------------------------------------
|
||||
function ScoreUnion(schema: TSchema, references: TSchema[], value: any): number {
|
||||
if (schema[Kind] === 'Object' && typeof value === 'object' && !IsNull(value)) {
|
||||
const object = schema as TObject
|
||||
const keys = Object.getOwnPropertyNames(value)
|
||||
const entries = Object.entries(object.properties)
|
||||
return entries.reduce((acc, [key, schema]) => {
|
||||
const literal = schema[Kind] === 'Literal' && schema.const === value[key] ? 100 : 0
|
||||
const checks = Check(schema, references, value[key]) ? 10 : 0
|
||||
const exists = keys.includes(key) ? 1 : 0
|
||||
return acc + (literal + checks + exists)
|
||||
}, 0)
|
||||
} else if (schema[Kind] === 'Union') {
|
||||
const schemas = schema.anyOf.map((schema: TSchema) => Deref(schema, references))
|
||||
const scores = schemas.map((schema: TSchema) => ScoreUnion(schema, references, value))
|
||||
return Math.max(...scores)
|
||||
} else {
|
||||
return Check(schema, references, value) ? 1 : 0
|
||||
}
|
||||
}
|
||||
function SelectUnion(union: TUnion, references: TSchema[], value: any): TSchema {
|
||||
const schemas = union.anyOf.map((schema) => Deref(schema, references))
|
||||
let [select, best] = [schemas[0], 0]
|
||||
for (const schema of schemas) {
|
||||
const score = ScoreUnion(schema, references, value)
|
||||
if (score > best) {
|
||||
select = schema
|
||||
best = score
|
||||
}
|
||||
}
|
||||
return select
|
||||
}
|
||||
function CastUnion(union: TUnion, references: TSchema[], value: any) {
|
||||
if ('default' in union) {
|
||||
return typeof value === 'function' ? union.default : Clone(union.default)
|
||||
} else {
|
||||
const schema = SelectUnion(union, references, value)
|
||||
return Cast(schema, references, value)
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Default
|
||||
// ------------------------------------------------------------------
|
||||
function DefaultClone(schema: TSchema, references: TSchema[], value: any): any {
|
||||
return Check(schema, references, value) ? Clone(value) : Create(schema, references)
|
||||
}
|
||||
function Default(schema: TSchema, references: TSchema[], value: any): any {
|
||||
return Check(schema, references, value) ? value : Create(schema, references)
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Cast
|
||||
// ------------------------------------------------------------------
|
||||
function FromArray(schema: TArray, references: TSchema[], value: any): any {
|
||||
if (Check(schema, references, value)) return Clone(value)
|
||||
const created = IsArray(value) ? Clone(value) : Create(schema, references)
|
||||
const minimum = IsNumber(schema.minItems) && created.length < schema.minItems ? [...created, ...Array.from({ length: schema.minItems - created.length }, () => null)] : created
|
||||
const maximum = IsNumber(schema.maxItems) && minimum.length > schema.maxItems ? minimum.slice(0, schema.maxItems) : minimum
|
||||
const casted = maximum.map((value: unknown) => Visit(schema.items, references, value))
|
||||
if (schema.uniqueItems !== true) return casted
|
||||
const unique = [...new Set(casted)]
|
||||
if (!Check(schema, references, unique)) throw new ValueCastError(schema, 'Array cast produced invalid data due to uniqueItems constraint')
|
||||
return unique
|
||||
}
|
||||
function FromConstructor(schema: TConstructor, references: TSchema[], value: any): any {
|
||||
if (Check(schema, references, value)) return Create(schema, references)
|
||||
const required = new Set(schema.returns.required || [])
|
||||
const result = function () {}
|
||||
for (const [key, property] of Object.entries(schema.returns.properties)) {
|
||||
if (!required.has(key) && value.prototype[key] === undefined) continue
|
||||
result.prototype[key] = Visit(property as TSchema, references, value.prototype[key])
|
||||
}
|
||||
return result
|
||||
}
|
||||
function FromImport(schema: TImport, references: TSchema[], value: unknown): boolean {
|
||||
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
return Visit(target, [...references, ...definitions], value)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Intersect
|
||||
// ------------------------------------------------------------------
|
||||
function IntersectAssign(correct: unknown, value: unknown): unknown {
|
||||
// trust correct on mismatch | value on non-object
|
||||
if ((IsObject(correct) && !IsObject(value)) || (!IsObject(correct) && IsObject(value))) return correct
|
||||
if (!IsObject(correct) || !IsObject(value)) return value
|
||||
return globalThis.Object.getOwnPropertyNames(correct).reduce((result, key) => {
|
||||
const property = key in value ? IntersectAssign(correct[key], value[key]) : correct[key]
|
||||
return { ...result, [key]: property }
|
||||
}, {})
|
||||
}
|
||||
function FromIntersect(schema: TIntersect, references: TSchema[], value: any): any {
|
||||
if (Check(schema, references, value)) return value
|
||||
const correct = Create(schema, references)
|
||||
const assigned = IntersectAssign(correct, value)
|
||||
return Check(schema, references, assigned) ? assigned : correct
|
||||
}
|
||||
function FromNever(schema: TNever, references: TSchema[], value: any): any {
|
||||
throw new ValueCastError(schema, 'Never types cannot be cast')
|
||||
}
|
||||
function FromObject(schema: TObject, references: TSchema[], value: any): any {
|
||||
if (Check(schema, references, value)) return value
|
||||
if (value === null || typeof value !== 'object') return Create(schema, references)
|
||||
const required = new Set(schema.required || [])
|
||||
const result = {} as Record<string, any>
|
||||
for (const [key, property] of Object.entries(schema.properties)) {
|
||||
if (!required.has(key) && value[key] === undefined) continue
|
||||
result[key] = Visit(property, references, value[key])
|
||||
}
|
||||
// additional schema properties
|
||||
if (typeof schema.additionalProperties === 'object') {
|
||||
const propertyNames = Object.getOwnPropertyNames(schema.properties)
|
||||
for (const propertyName of Object.getOwnPropertyNames(value)) {
|
||||
if (propertyNames.includes(propertyName)) continue
|
||||
result[propertyName] = Visit(schema.additionalProperties, references, value[propertyName])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
function FromRecord(schema: TRecord, references: TSchema[], value: any): any {
|
||||
if (Check(schema, references, value)) return Clone(value)
|
||||
if (value === null || typeof value !== 'object' || Array.isArray(value) || value instanceof Date) return Create(schema, references)
|
||||
const subschemaPropertyName = Object.getOwnPropertyNames(schema.patternProperties)[0]
|
||||
const subschema = schema.patternProperties[subschemaPropertyName]
|
||||
const result = {} as Record<string, any>
|
||||
for (const [propKey, propValue] of Object.entries(value)) {
|
||||
result[propKey] = Visit(subschema, references, propValue)
|
||||
}
|
||||
return result
|
||||
}
|
||||
function FromRef(schema: TRef, references: TSchema[], value: any): any {
|
||||
return Visit(Deref(schema, references), references, value)
|
||||
}
|
||||
function FromThis(schema: TThis, references: TSchema[], value: any): any {
|
||||
return Visit(Deref(schema, references), references, value)
|
||||
}
|
||||
function FromTuple(schema: TTuple, references: TSchema[], value: any): any {
|
||||
if (Check(schema, references, value)) return Clone(value)
|
||||
if (!IsArray(value)) return Create(schema, references)
|
||||
if (schema.items === undefined) return []
|
||||
return schema.items.map((schema, index) => Visit(schema, references, value[index]))
|
||||
}
|
||||
function FromUnion(schema: TUnion, references: TSchema[], value: any): any {
|
||||
return Check(schema, references, value) ? Clone(value) : CastUnion(schema, references, value)
|
||||
}
|
||||
function Visit(schema: TSchema, references: TSchema[], value: any): any {
|
||||
const references_ = IsString(schema.$id) ? Pushref(schema, references) : references
|
||||
const schema_ = schema as any
|
||||
switch (schema[Kind]) {
|
||||
// --------------------------------------------------------------
|
||||
// Structural
|
||||
// --------------------------------------------------------------
|
||||
case 'Array':
|
||||
return FromArray(schema_, references_, value)
|
||||
case 'Constructor':
|
||||
return FromConstructor(schema_, references_, value)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_, value)
|
||||
case 'Intersect':
|
||||
return FromIntersect(schema_, references_, value)
|
||||
case 'Never':
|
||||
return FromNever(schema_, references_, value)
|
||||
case 'Object':
|
||||
return FromObject(schema_, references_, value)
|
||||
case 'Record':
|
||||
return FromRecord(schema_, references_, value)
|
||||
case 'Ref':
|
||||
return FromRef(schema_, references_, value)
|
||||
case 'This':
|
||||
return FromThis(schema_, references_, value)
|
||||
case 'Tuple':
|
||||
return FromTuple(schema_, references_, value)
|
||||
case 'Union':
|
||||
return FromUnion(schema_, references_, value)
|
||||
// --------------------------------------------------------------
|
||||
// DefaultClone
|
||||
// --------------------------------------------------------------
|
||||
case 'Date':
|
||||
case 'Symbol':
|
||||
case 'Uint8Array':
|
||||
return DefaultClone(schema, references, value)
|
||||
// --------------------------------------------------------------
|
||||
// Default
|
||||
// --------------------------------------------------------------
|
||||
default:
|
||||
return Default(schema_, references_, value)
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Cast
|
||||
// ------------------------------------------------------------------
|
||||
/** Casts a value into a given type and references. The return value will retain as much information of the original value as possible. */
|
||||
export function Cast<T extends TSchema>(schema: T, references: TSchema[], value: unknown): Static<T>
|
||||
/** Casts a value into a given type. The return value will retain as much information of the original value as possible. */
|
||||
export function Cast<T extends TSchema>(schema: T, value: unknown): Static<T>
|
||||
/** Casts a value into a given type. The return value will retain as much information of the original value as possible. */
|
||||
export function Cast(...args: any[]) {
|
||||
return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1])
|
||||
}
|
||||
29
src/value/cast/index.ts
Normal file
29
src/value/cast/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './cast'
|
||||
513
src/value/check/check.ts
Normal file
513
src/value/check/check.ts
Normal file
@@ -0,0 +1,513 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { TypeSystemPolicy } from '../../system/index'
|
||||
import { Deref, Pushref } from '../deref/index'
|
||||
import { Hash } from '../hash/index'
|
||||
import { Kind } from '../../type/symbols/index'
|
||||
import { KeyOfPattern } from '../../type/keyof/index'
|
||||
import { ExtendsUndefinedCheck } from '../../type/extends/index'
|
||||
import { TypeRegistry, FormatRegistry } from '../../type/registry/index'
|
||||
import { TypeBoxError } from '../../type/error/index'
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
|
||||
import type { TAny } from '../../type/any/index'
|
||||
import type { TArgument } from '../../type/argument/index'
|
||||
import type { TArray } from '../../type/array/index'
|
||||
import type { TAsyncIterator } from '../../type/async-iterator/index'
|
||||
import type { TBigInt } from '../../type/bigint/index'
|
||||
import type { TBoolean } from '../../type/boolean/index'
|
||||
import type { TDate } from '../../type/date/index'
|
||||
import type { TConstructor } from '../../type/constructor/index'
|
||||
import type { TFunction } from '../../type/function/index'
|
||||
import type { TInteger } from '../../type/integer/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TIterator } from '../../type/iterator/index'
|
||||
import type { TLiteral } from '../../type/literal/index'
|
||||
import type { TImport } from '../../type/module/index'
|
||||
import { Never, type TNever } from '../../type/never/index'
|
||||
import type { TNot } from '../../type/not/index'
|
||||
import type { TNull } from '../../type/null/index'
|
||||
import type { TNumber } from '../../type/number/index'
|
||||
import type { TObject } from '../../type/object/index'
|
||||
import type { TPromise } from '../../type/promise/index'
|
||||
import type { TRecord } from '../../type/record/index'
|
||||
import type { TRef } from '../../type/ref/index'
|
||||
import type { TRegExp } from '../../type/regexp/index'
|
||||
import type { TTemplateLiteral } from '../../type/template-literal/index'
|
||||
import type { TThis } from '../../type/recursive/index'
|
||||
import type { TTuple } from '../../type/tuple/index'
|
||||
import type { TUnion } from '../../type/union/index'
|
||||
import type { TUnknown } from '../../type/unknown/index'
|
||||
import type { Static } from '../../type/static/index'
|
||||
import type { TString } from '../../type/string/index'
|
||||
import type { TSymbol } from '../../type/symbol/index'
|
||||
import type { TUndefined } from '../../type/undefined/index'
|
||||
import type { TUint8Array } from '../../type/uint8array/index'
|
||||
import type { TVoid } from '../../type/void/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ValueGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsArray, IsUint8Array, IsDate, IsPromise, IsFunction, IsAsyncIterator, IsIterator, IsBoolean, IsNumber, IsBigInt, IsString, IsSymbol, IsInteger, IsNull, IsUndefined } from '../guard/index'
|
||||
// ------------------------------------------------------------------
|
||||
// KindGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsSchema } from '../../type/guard/kind'
|
||||
// ------------------------------------------------------------------
|
||||
// Errors
|
||||
// ------------------------------------------------------------------
|
||||
export class ValueCheckUnknownTypeError extends TypeBoxError {
|
||||
constructor(public readonly schema: TSchema) {
|
||||
super(`Unknown type`)
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// TypeGuards
|
||||
// ------------------------------------------------------------------
|
||||
function IsAnyOrUnknown(schema: TSchema) {
|
||||
return schema[Kind] === 'Any' || schema[Kind] === 'Unknown'
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Guards
|
||||
// ------------------------------------------------------------------
|
||||
function IsDefined<T>(value: unknown): value is T {
|
||||
return value !== undefined
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Types
|
||||
// ------------------------------------------------------------------
|
||||
function FromAny(schema: TAny, references: TSchema[], value: any): boolean {
|
||||
return true
|
||||
}
|
||||
function FromArgument(schema: TArgument, references: TSchema[], value: any): boolean {
|
||||
return true
|
||||
}
|
||||
function FromArray(schema: TArray, references: TSchema[], value: any): boolean {
|
||||
if (!IsArray(value)) return false
|
||||
if (IsDefined<number>(schema.minItems) && !(value.length >= schema.minItems)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.maxItems) && !(value.length <= schema.maxItems)) {
|
||||
return false
|
||||
}
|
||||
if (!value.every((value) => Visit(schema.items, references, value))) {
|
||||
return false
|
||||
}
|
||||
// prettier-ignore
|
||||
if (schema.uniqueItems === true && !((function() { const set = new Set(); for(const element of value) { const hashed = Hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())) {
|
||||
return false
|
||||
}
|
||||
// contains
|
||||
if (!(IsDefined(schema.contains) || IsNumber(schema.minContains) || IsNumber(schema.maxContains))) {
|
||||
return true // exit
|
||||
}
|
||||
const containsSchema = IsDefined<TSchema>(schema.contains) ? schema.contains : Never()
|
||||
const containsCount = value.reduce((acc: number, value) => (Visit(containsSchema, references, value) ? acc + 1 : acc), 0)
|
||||
if (containsCount === 0) {
|
||||
return false
|
||||
}
|
||||
if (IsNumber(schema.minContains) && containsCount < schema.minContains) {
|
||||
return false
|
||||
}
|
||||
if (IsNumber(schema.maxContains) && containsCount > schema.maxContains) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
function FromAsyncIterator(schema: TAsyncIterator, references: TSchema[], value: any): boolean {
|
||||
return IsAsyncIterator(value)
|
||||
}
|
||||
function FromBigInt(schema: TBigInt, references: TSchema[], value: any): boolean {
|
||||
if (!IsBigInt(value)) return false
|
||||
if (IsDefined<bigint>(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<bigint>(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<bigint>(schema.maximum) && !(value <= schema.maximum)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<bigint>(schema.minimum) && !(value >= schema.minimum)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<bigint>(schema.multipleOf) && !(value % schema.multipleOf === BigInt(0))) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
function FromBoolean(schema: TBoolean, references: TSchema[], value: any): boolean {
|
||||
return IsBoolean(value)
|
||||
}
|
||||
function FromConstructor(schema: TConstructor, references: TSchema[], value: any): boolean {
|
||||
return Visit(schema.returns, references, value.prototype)
|
||||
}
|
||||
function FromDate(schema: TDate, references: TSchema[], value: any): boolean {
|
||||
if (!IsDate(value)) return false
|
||||
if (IsDefined<number>(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.minimumTimestamp) && !(value.getTime() >= schema.minimumTimestamp)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.multipleOfTimestamp) && !(value.getTime() % schema.multipleOfTimestamp === 0)) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
function FromFunction(schema: TFunction, references: TSchema[], value: any): boolean {
|
||||
return IsFunction(value)
|
||||
}
|
||||
function FromImport(schema: TImport, references: TSchema[], value: any): boolean {
|
||||
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
return Visit(target, [...references, ...definitions], value)
|
||||
}
|
||||
function FromInteger(schema: TInteger, references: TSchema[], value: any): boolean {
|
||||
if (!IsInteger(value)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.maximum) && !(value <= schema.maximum)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.minimum) && !(value >= schema.minimum)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.multipleOf) && !(value % schema.multipleOf === 0)) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
function FromIntersect(schema: TIntersect, references: TSchema[], value: any): boolean {
|
||||
const check1 = schema.allOf.every((schema) => Visit(schema, references, value))
|
||||
if (schema.unevaluatedProperties === false) {
|
||||
const keyPattern = new RegExp(KeyOfPattern(schema))
|
||||
const check2 = Object.getOwnPropertyNames(value).every((key) => keyPattern.test(key))
|
||||
return check1 && check2
|
||||
} else if (IsSchema(schema.unevaluatedProperties)) {
|
||||
const keyCheck = new RegExp(KeyOfPattern(schema))
|
||||
const check2 = Object.getOwnPropertyNames(value).every((key) => keyCheck.test(key) || Visit(schema.unevaluatedProperties as TSchema, references, value[key]))
|
||||
return check1 && check2
|
||||
} else {
|
||||
return check1
|
||||
}
|
||||
}
|
||||
function FromIterator(schema: TIterator, references: TSchema[], value: any): boolean {
|
||||
return IsIterator(value)
|
||||
}
|
||||
function FromLiteral(schema: TLiteral, references: TSchema[], value: any): boolean {
|
||||
return value === schema.const
|
||||
}
|
||||
function FromNever(schema: TNever, references: TSchema[], value: any): boolean {
|
||||
return false
|
||||
}
|
||||
function FromNot(schema: TNot, references: TSchema[], value: any): boolean {
|
||||
return !Visit(schema.not, references, value)
|
||||
}
|
||||
function FromNull(schema: TNull, references: TSchema[], value: any): boolean {
|
||||
return IsNull(value)
|
||||
}
|
||||
function FromNumber(schema: TNumber, references: TSchema[], value: any): boolean {
|
||||
if (!TypeSystemPolicy.IsNumberLike(value)) return false
|
||||
if (IsDefined<number>(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.minimum) && !(value >= schema.minimum)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.maximum) && !(value <= schema.maximum)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.multipleOf) && !(value % schema.multipleOf === 0)) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
function FromObject(schema: TObject, references: TSchema[], value: any): boolean {
|
||||
if (!TypeSystemPolicy.IsObjectLike(value)) return false
|
||||
if (IsDefined<number>(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.maxProperties) && !(Object.getOwnPropertyNames(value).length <= schema.maxProperties)) {
|
||||
return false
|
||||
}
|
||||
const knownKeys = Object.getOwnPropertyNames(schema.properties)
|
||||
for (const knownKey of knownKeys) {
|
||||
const property = schema.properties[knownKey]
|
||||
if (schema.required && schema.required.includes(knownKey)) {
|
||||
if (!Visit(property, references, value[knownKey])) {
|
||||
return false
|
||||
}
|
||||
if ((ExtendsUndefinedCheck(property) || IsAnyOrUnknown(property)) && !(knownKey in value)) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if (TypeSystemPolicy.IsExactOptionalProperty(value, knownKey) && !Visit(property, references, value[knownKey])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
if (schema.additionalProperties === false) {
|
||||
const valueKeys = Object.getOwnPropertyNames(value)
|
||||
// optimization: value is valid if schemaKey length matches the valueKey length
|
||||
if (schema.required && schema.required.length === knownKeys.length && valueKeys.length === knownKeys.length) {
|
||||
return true
|
||||
} else {
|
||||
return valueKeys.every((valueKey) => knownKeys.includes(valueKey))
|
||||
}
|
||||
} else if (typeof schema.additionalProperties === 'object') {
|
||||
const valueKeys = Object.getOwnPropertyNames(value)
|
||||
return valueKeys.every((key) => knownKeys.includes(key) || Visit(schema.additionalProperties as TSchema, references, value[key]))
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
function FromPromise(schema: TPromise, references: TSchema[], value: any): boolean {
|
||||
return IsPromise(value)
|
||||
}
|
||||
function FromRecord(schema: TRecord, references: TSchema[], value: any): boolean {
|
||||
if (!TypeSystemPolicy.IsRecordLike(value)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.maxProperties) && !(Object.getOwnPropertyNames(value).length <= schema.maxProperties)) {
|
||||
return false
|
||||
}
|
||||
const [patternKey, patternSchema] = Object.entries(schema.patternProperties)[0]
|
||||
const regex = new RegExp(patternKey)
|
||||
// prettier-ignore
|
||||
const check1 = Object.entries(value).every(([key, value]) => {
|
||||
return (regex.test(key)) ? Visit(patternSchema, references, value) : true
|
||||
})
|
||||
// prettier-ignore
|
||||
const check2 = typeof schema.additionalProperties === 'object' ? Object.entries(value).every(([key, value]) => {
|
||||
return (!regex.test(key)) ? Visit(schema.additionalProperties as TSchema, references, value) : true
|
||||
}) : true
|
||||
const check3 =
|
||||
schema.additionalProperties === false
|
||||
? Object.getOwnPropertyNames(value).every((key) => {
|
||||
return regex.test(key)
|
||||
})
|
||||
: true
|
||||
return check1 && check2 && check3
|
||||
}
|
||||
function FromRef(schema: TRef, references: TSchema[], value: any): boolean {
|
||||
return Visit(Deref(schema, references), references, value)
|
||||
}
|
||||
function FromRegExp(schema: TRegExp, references: TSchema[], value: any): boolean {
|
||||
const regex = new RegExp(schema.source, schema.flags)
|
||||
if (IsDefined<number>(schema.minLength)) {
|
||||
if (!(value.length >= schema.minLength)) return false
|
||||
}
|
||||
if (IsDefined<number>(schema.maxLength)) {
|
||||
if (!(value.length <= schema.maxLength)) return false
|
||||
}
|
||||
return regex.test(value)
|
||||
}
|
||||
function FromString(schema: TString, references: TSchema[], value: any): boolean {
|
||||
if (!IsString(value)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.minLength)) {
|
||||
if (!(value.length >= schema.minLength)) return false
|
||||
}
|
||||
if (IsDefined<number>(schema.maxLength)) {
|
||||
if (!(value.length <= schema.maxLength)) return false
|
||||
}
|
||||
if (IsDefined<string>(schema.pattern)) {
|
||||
const regex = new RegExp(schema.pattern)
|
||||
if (!regex.test(value)) return false
|
||||
}
|
||||
if (IsDefined<string>(schema.format)) {
|
||||
if (!FormatRegistry.Has(schema.format)) return false
|
||||
const func = FormatRegistry.Get(schema.format)!
|
||||
return func(value)
|
||||
}
|
||||
return true
|
||||
}
|
||||
function FromSymbol(schema: TSymbol, references: TSchema[], value: any): boolean {
|
||||
return IsSymbol(value)
|
||||
}
|
||||
function FromTemplateLiteral(schema: TTemplateLiteral, references: TSchema[], value: any): boolean {
|
||||
return IsString(value) && new RegExp(schema.pattern).test(value)
|
||||
}
|
||||
function FromThis(schema: TThis, references: TSchema[], value: any): boolean {
|
||||
return Visit(Deref(schema, references), references, value)
|
||||
}
|
||||
function FromTuple(schema: TTuple<any[]>, references: TSchema[], value: any): boolean {
|
||||
if (!IsArray(value)) {
|
||||
return false
|
||||
}
|
||||
if (schema.items === undefined && !(value.length === 0)) {
|
||||
return false
|
||||
}
|
||||
if (!(value.length === schema.maxItems)) {
|
||||
return false
|
||||
}
|
||||
if (!schema.items) {
|
||||
return true
|
||||
}
|
||||
for (let i = 0; i < schema.items.length; i++) {
|
||||
if (!Visit(schema.items[i], references, value[i])) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
function FromUndefined(schema: TUndefined, references: TSchema[], value: any): boolean {
|
||||
return IsUndefined(value)
|
||||
}
|
||||
function FromUnion(schema: TUnion, references: TSchema[], value: any): boolean {
|
||||
return schema.anyOf.some((inner) => Visit(inner, references, value))
|
||||
}
|
||||
function FromUint8Array(schema: TUint8Array, references: TSchema[], value: any): boolean {
|
||||
if (!IsUint8Array(value)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) {
|
||||
return false
|
||||
}
|
||||
if (IsDefined<number>(schema.minByteLength) && !(value.length >= schema.minByteLength)) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
function FromUnknown(schema: TUnknown, references: TSchema[], value: any): boolean {
|
||||
return true
|
||||
}
|
||||
function FromVoid(schema: TVoid, references: TSchema[], value: any): boolean {
|
||||
return TypeSystemPolicy.IsVoidLike(value)
|
||||
}
|
||||
function FromKind(schema: TSchema, references: TSchema[], value: unknown): boolean {
|
||||
if (!TypeRegistry.Has(schema[Kind])) return false
|
||||
const func = TypeRegistry.Get(schema[Kind])!
|
||||
return func(schema, value)
|
||||
}
|
||||
function Visit<T extends TSchema>(schema: T, references: TSchema[], value: any): boolean {
|
||||
const references_ = IsDefined<string>(schema.$id) ? Pushref(schema, references) : references
|
||||
const schema_ = schema as any
|
||||
switch (schema_[Kind]) {
|
||||
case 'Any':
|
||||
return FromAny(schema_, references_, value)
|
||||
case 'Argument':
|
||||
return FromArgument(schema_, references_, value)
|
||||
case 'Array':
|
||||
return FromArray(schema_, references_, value)
|
||||
case 'AsyncIterator':
|
||||
return FromAsyncIterator(schema_, references_, value)
|
||||
case 'BigInt':
|
||||
return FromBigInt(schema_, references_, value)
|
||||
case 'Boolean':
|
||||
return FromBoolean(schema_, references_, value)
|
||||
case 'Constructor':
|
||||
return FromConstructor(schema_, references_, value)
|
||||
case 'Date':
|
||||
return FromDate(schema_, references_, value)
|
||||
case 'Function':
|
||||
return FromFunction(schema_, references_, value)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_, value)
|
||||
case 'Integer':
|
||||
return FromInteger(schema_, references_, value)
|
||||
case 'Intersect':
|
||||
return FromIntersect(schema_, references_, value)
|
||||
case 'Iterator':
|
||||
return FromIterator(schema_, references_, value)
|
||||
case 'Literal':
|
||||
return FromLiteral(schema_, references_, value)
|
||||
case 'Never':
|
||||
return FromNever(schema_, references_, value)
|
||||
case 'Not':
|
||||
return FromNot(schema_, references_, value)
|
||||
case 'Null':
|
||||
return FromNull(schema_, references_, value)
|
||||
case 'Number':
|
||||
return FromNumber(schema_, references_, value)
|
||||
case 'Object':
|
||||
return FromObject(schema_, references_, value)
|
||||
case 'Promise':
|
||||
return FromPromise(schema_, references_, value)
|
||||
case 'Record':
|
||||
return FromRecord(schema_, references_, value)
|
||||
case 'Ref':
|
||||
return FromRef(schema_, references_, value)
|
||||
case 'RegExp':
|
||||
return FromRegExp(schema_, references_, value)
|
||||
case 'String':
|
||||
return FromString(schema_, references_, value)
|
||||
case 'Symbol':
|
||||
return FromSymbol(schema_, references_, value)
|
||||
case 'TemplateLiteral':
|
||||
return FromTemplateLiteral(schema_, references_, value)
|
||||
case 'This':
|
||||
return FromThis(schema_, references_, value)
|
||||
case 'Tuple':
|
||||
return FromTuple(schema_, references_, value)
|
||||
case 'Undefined':
|
||||
return FromUndefined(schema_, references_, value)
|
||||
case 'Union':
|
||||
return FromUnion(schema_, references_, value)
|
||||
case 'Uint8Array':
|
||||
return FromUint8Array(schema_, references_, value)
|
||||
case 'Unknown':
|
||||
return FromUnknown(schema_, references_, value)
|
||||
case 'Void':
|
||||
return FromVoid(schema_, references_, value)
|
||||
default:
|
||||
if (!TypeRegistry.Has(schema_[Kind])) throw new ValueCheckUnknownTypeError(schema_)
|
||||
return FromKind(schema_, references_, value)
|
||||
}
|
||||
}
|
||||
// --------------------------------------------------------------------------
|
||||
// Check
|
||||
// --------------------------------------------------------------------------
|
||||
/** Returns true if the value matches the given type. */
|
||||
export function Check<T extends TSchema>(schema: T, references: TSchema[], value: unknown): value is Static<T>
|
||||
/** Returns true if the value matches the given type. */
|
||||
export function Check<T extends TSchema>(schema: T, value: unknown): value is Static<T>
|
||||
/** Returns true if the value matches the given type. */
|
||||
export function Check(...args: any[]) {
|
||||
return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1])
|
||||
}
|
||||
29
src/value/check/index.ts
Normal file
29
src/value/check/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './check'
|
||||
194
src/value/clean/clean.ts
Normal file
194
src/value/clean/clean.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { KeyOfPropertyKeys } from '../../type/keyof/index'
|
||||
import { Check } from '../check/index'
|
||||
import { Clone } from '../clone/index'
|
||||
import { Deref, Pushref } from '../deref/index'
|
||||
import { Kind } from '../../type/symbols/index'
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
import type { TArray } from '../../type/array/index'
|
||||
import type { TImport } from '../../type/module/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TObject } from '../../type/object/index'
|
||||
import type { TRecord } from '../../type/record/index'
|
||||
import type { TRef } from '../../type/ref/index'
|
||||
import type { TThis } from '../../type/recursive/index'
|
||||
import type { TTuple } from '../../type/tuple/index'
|
||||
import type { TUnion } from '../../type/union/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ValueGuard
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
import {
|
||||
HasPropertyKey,
|
||||
IsString,
|
||||
IsObject,
|
||||
IsArray,
|
||||
IsUndefined
|
||||
} from '../guard/index'
|
||||
// ------------------------------------------------------------------
|
||||
// TypeGuard
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
import {
|
||||
IsKind
|
||||
} from '../../type/guard/kind'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// IsCheckable
|
||||
// ------------------------------------------------------------------
|
||||
function IsCheckable(schema: unknown): boolean {
|
||||
return IsKind(schema) && schema[Kind] !== 'Unsafe'
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Types
|
||||
// ------------------------------------------------------------------
|
||||
function FromArray(schema: TArray, references: TSchema[], value: unknown): any {
|
||||
if (!IsArray(value)) return value
|
||||
return value.map((value) => Visit(schema.items, references, value))
|
||||
}
|
||||
function FromImport(schema: TImport, references: TSchema[], value: unknown): any {
|
||||
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
return Visit(target, [...references, ...definitions], value)
|
||||
}
|
||||
function FromIntersect(schema: TIntersect, references: TSchema[], value: unknown): any {
|
||||
const unevaluatedProperties = schema.unevaluatedProperties as TSchema
|
||||
const intersections = schema.allOf.map((schema) => Visit(schema, references, Clone(value)))
|
||||
const composite = intersections.reduce((acc: any, value: any) => (IsObject(value) ? { ...acc, ...value } : value), {})
|
||||
if (!IsObject(value) || !IsObject(composite) || !IsKind(unevaluatedProperties)) return composite
|
||||
const knownkeys = KeyOfPropertyKeys(schema) as string[]
|
||||
for (const key of Object.getOwnPropertyNames(value)) {
|
||||
if (knownkeys.includes(key)) continue
|
||||
if (Check(unevaluatedProperties, references, value[key])) {
|
||||
composite[key] = Visit(unevaluatedProperties, references, value[key])
|
||||
}
|
||||
}
|
||||
return composite
|
||||
}
|
||||
function FromObject(schema: TObject, references: TSchema[], value: unknown): any {
|
||||
if (!IsObject(value) || IsArray(value)) return value // Check IsArray for AllowArrayObject configuration
|
||||
const additionalProperties = schema.additionalProperties as TSchema
|
||||
for (const key of Object.getOwnPropertyNames(value)) {
|
||||
if (HasPropertyKey(schema.properties, key)) {
|
||||
value[key] = Visit(schema.properties[key], references, value[key])
|
||||
continue
|
||||
}
|
||||
if (IsKind(additionalProperties) && Check(additionalProperties, references, value[key])) {
|
||||
value[key] = Visit(additionalProperties, references, value[key])
|
||||
continue
|
||||
}
|
||||
delete value[key]
|
||||
}
|
||||
return value
|
||||
}
|
||||
function FromRecord(schema: TRecord, references: TSchema[], value: unknown): any {
|
||||
if (!IsObject(value)) return value
|
||||
const additionalProperties = schema.additionalProperties as TSchema
|
||||
const propertyKeys = Object.getOwnPropertyNames(value)
|
||||
const [propertyKey, propertySchema] = Object.entries(schema.patternProperties)[0]
|
||||
const propertyKeyTest = new RegExp(propertyKey)
|
||||
for (const key of propertyKeys) {
|
||||
if (propertyKeyTest.test(key)) {
|
||||
value[key] = Visit(propertySchema, references, value[key])
|
||||
continue
|
||||
}
|
||||
if (IsKind(additionalProperties) && Check(additionalProperties, references, value[key])) {
|
||||
value[key] = Visit(additionalProperties, references, value[key])
|
||||
continue
|
||||
}
|
||||
delete value[key]
|
||||
}
|
||||
return value
|
||||
}
|
||||
function FromRef(schema: TRef, references: TSchema[], value: unknown): any {
|
||||
return Visit(Deref(schema, references), references, value)
|
||||
}
|
||||
function FromThis(schema: TThis, references: TSchema[], value: unknown): any {
|
||||
return Visit(Deref(schema, references), references, value)
|
||||
}
|
||||
function FromTuple(schema: TTuple, references: TSchema[], value: unknown): any {
|
||||
if (!IsArray(value)) return value
|
||||
if (IsUndefined(schema.items)) return []
|
||||
const length = Math.min(value.length, schema.items.length)
|
||||
for (let i = 0; i < length; i++) {
|
||||
value[i] = Visit(schema.items[i], references, value[i])
|
||||
}
|
||||
// prettier-ignore
|
||||
return value.length > length
|
||||
? value.slice(0, length)
|
||||
: value
|
||||
}
|
||||
function FromUnion(schema: TUnion, references: TSchema[], value: unknown): any {
|
||||
for (const inner of schema.anyOf) {
|
||||
if (IsCheckable(inner) && Check(inner, references, value)) {
|
||||
return Visit(inner, references, value)
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
function Visit(schema: TSchema, references: TSchema[], value: unknown): unknown {
|
||||
const references_ = IsString(schema.$id) ? Pushref(schema, references) : references
|
||||
const schema_ = schema as any
|
||||
switch (schema_[Kind]) {
|
||||
case 'Array':
|
||||
return FromArray(schema_, references_, value)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_, value)
|
||||
case 'Intersect':
|
||||
return FromIntersect(schema_, references_, value)
|
||||
case 'Object':
|
||||
return FromObject(schema_, references_, value)
|
||||
case 'Record':
|
||||
return FromRecord(schema_, references_, value)
|
||||
case 'Ref':
|
||||
return FromRef(schema_, references_, value)
|
||||
case 'This':
|
||||
return FromThis(schema_, references_, value)
|
||||
case 'Tuple':
|
||||
return FromTuple(schema_, references_, value)
|
||||
case 'Union':
|
||||
return FromUnion(schema_, references_, value)
|
||||
default:
|
||||
return value
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Clean
|
||||
// ------------------------------------------------------------------
|
||||
/** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */
|
||||
export function Clean(schema: TSchema, references: TSchema[], value: unknown): unknown
|
||||
/** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */
|
||||
export function Clean(schema: TSchema, value: unknown): unknown
|
||||
/** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */
|
||||
export function Clean(...args: any[]) {
|
||||
return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1])
|
||||
}
|
||||
29
src/value/clean/index.ts
Normal file
29
src/value/clean/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './clean'
|
||||
79
src/value/clone/clone.ts
Normal file
79
src/value/clone/clone.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import type { ObjectType as FromObject, ArrayType as FromArray, TypedArrayType, ValueType } from '../guard/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ValueGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsArray, IsDate, IsMap, IsSet, IsObject, IsTypedArray, IsValueType } from '../guard/index'
|
||||
// ------------------------------------------------------------------
|
||||
// Clonable
|
||||
// ------------------------------------------------------------------
|
||||
function FromObject(value: FromObject): any {
|
||||
const Acc = {} as Record<PropertyKey, unknown>
|
||||
for (const key of Object.getOwnPropertyNames(value)) {
|
||||
Acc[key] = Clone(value[key])
|
||||
}
|
||||
for (const key of Object.getOwnPropertySymbols(value)) {
|
||||
Acc[key] = Clone(value[key])
|
||||
}
|
||||
return Acc
|
||||
}
|
||||
function FromArray(value: FromArray): any {
|
||||
return value.map((element: any) => Clone(element))
|
||||
}
|
||||
function FromTypedArray(value: TypedArrayType): any {
|
||||
return value.slice()
|
||||
}
|
||||
function FromMap(value: Map<unknown, unknown>): any {
|
||||
return new Map(Clone([...value.entries()]))
|
||||
}
|
||||
function FromSet(value: Set<unknown>): any {
|
||||
return new Set(Clone([...value.entries()]))
|
||||
}
|
||||
function FromDate(value: Date): any {
|
||||
return new Date(value.toISOString())
|
||||
}
|
||||
function FromValue(value: ValueType): any {
|
||||
return value
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Clone
|
||||
// ------------------------------------------------------------------
|
||||
/** Returns a clone of the given value */
|
||||
export function Clone<T extends unknown>(value: T): T {
|
||||
if (IsArray(value)) return FromArray(value)
|
||||
if (IsDate(value)) return FromDate(value)
|
||||
if (IsTypedArray(value)) return FromTypedArray(value)
|
||||
if (IsMap(value)) return FromMap(value)
|
||||
if (IsSet(value)) return FromSet(value)
|
||||
if (IsObject(value)) return FromObject(value)
|
||||
if (IsValueType(value)) return FromValue(value)
|
||||
throw new Error('ValueClone: Unable to clone value')
|
||||
}
|
||||
29
src/value/clone/index.ts
Normal file
29
src/value/clone/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './clone'
|
||||
318
src/value/convert/convert.ts
Normal file
318
src/value/convert/convert.ts
Normal file
@@ -0,0 +1,318 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { Clone } from '../clone/index'
|
||||
import { Check } from '../check/index'
|
||||
import { Deref, Pushref } from '../deref/index'
|
||||
import { Kind } from '../../type/symbols/index'
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
import type { TArray } from '../../type/array/index'
|
||||
import type { TBigInt } from '../../type/bigint/index'
|
||||
import type { TBoolean } from '../../type/boolean/index'
|
||||
import type { TDate } from '../../type/date/index'
|
||||
import type { TInteger } from '../../type/integer/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TImport } from '../../type/module/index'
|
||||
import type { TLiteral } from '../../type/literal/index'
|
||||
import type { TNull } from '../../type/null/index'
|
||||
import type { TNumber } from '../../type/number/index'
|
||||
import type { TObject } from '../../type/object/index'
|
||||
import type { TRecord } from '../../type/record/index'
|
||||
import type { TRef } from '../../type/ref/index'
|
||||
import type { TThis } from '../../type/recursive/index'
|
||||
import type { TTuple } from '../../type/tuple/index'
|
||||
import type { TUnion } from '../../type/union/index'
|
||||
import type { TString } from '../../type/string/index'
|
||||
import type { TSymbol } from '../../type/symbol/index'
|
||||
import type { TUndefined } from '../../type/undefined/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ValueGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsArray, IsObject, IsDate, IsUndefined, IsString, IsNumber, IsBoolean, IsBigInt, IsSymbol, HasPropertyKey } from '../guard/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Conversions
|
||||
// ------------------------------------------------------------------
|
||||
function IsStringNumeric(value: unknown): value is string {
|
||||
return IsString(value) && !isNaN(value as any) && !isNaN(parseFloat(value))
|
||||
}
|
||||
function IsValueToString(value: unknown): value is { toString: () => string } {
|
||||
return IsBigInt(value) || IsBoolean(value) || IsNumber(value)
|
||||
}
|
||||
function IsValueTrue(value: unknown): value is true {
|
||||
return value === true || (IsNumber(value) && value === 1) || (IsBigInt(value) && value === BigInt('1')) || (IsString(value) && (value.toLowerCase() === 'true' || value === '1'))
|
||||
}
|
||||
function IsValueFalse(value: unknown): value is false {
|
||||
return value === false || (IsNumber(value) && (value === 0 || Object.is(value, -0))) || (IsBigInt(value) && value === BigInt('0')) || (IsString(value) && (value.toLowerCase() === 'false' || value === '0' || value === '-0'))
|
||||
}
|
||||
function IsTimeStringWithTimeZone(value: unknown): value is string {
|
||||
return IsString(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value)
|
||||
}
|
||||
function IsTimeStringWithoutTimeZone(value: unknown): value is string {
|
||||
return IsString(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value)
|
||||
}
|
||||
function IsDateTimeStringWithTimeZone(value: unknown): value is string {
|
||||
return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value)
|
||||
}
|
||||
function IsDateTimeStringWithoutTimeZone(value: unknown): value is string {
|
||||
return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value)
|
||||
}
|
||||
function IsDateString(value: unknown): value is string {
|
||||
return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\d$/i.test(value)
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Convert
|
||||
// ------------------------------------------------------------------
|
||||
function TryConvertLiteralString(value: unknown, target: string) {
|
||||
const conversion = TryConvertString(value)
|
||||
return conversion === target ? conversion : value
|
||||
}
|
||||
function TryConvertLiteralNumber(value: unknown, target: number) {
|
||||
const conversion = TryConvertNumber(value)
|
||||
return conversion === target ? conversion : value
|
||||
}
|
||||
function TryConvertLiteralBoolean(value: unknown, target: boolean) {
|
||||
const conversion = TryConvertBoolean(value)
|
||||
return conversion === target ? conversion : value
|
||||
}
|
||||
// prettier-ignore
|
||||
function TryConvertLiteral(schema: TLiteral, value: unknown) {
|
||||
return (
|
||||
IsString(schema.const) ? TryConvertLiteralString(value, schema.const) :
|
||||
IsNumber(schema.const) ? TryConvertLiteralNumber(value, schema.const) :
|
||||
IsBoolean(schema.const) ? TryConvertLiteralBoolean(value, schema.const) :
|
||||
value
|
||||
)
|
||||
}
|
||||
function TryConvertBoolean(value: unknown) {
|
||||
return IsValueTrue(value) ? true : IsValueFalse(value) ? false : value
|
||||
}
|
||||
function TryConvertBigInt(value: unknown) {
|
||||
const truncateInteger = (value: string) => value.split('.')[0]
|
||||
return IsStringNumeric(value) ? BigInt(truncateInteger(value)) : IsNumber(value) ? BigInt(Math.trunc(value)) : IsValueFalse(value) ? BigInt(0) : IsValueTrue(value) ? BigInt(1) : value
|
||||
}
|
||||
function TryConvertString(value: unknown) {
|
||||
return IsSymbol(value) && value.description !== undefined ? value.description.toString() : IsValueToString(value) ? value.toString() : value
|
||||
}
|
||||
function TryConvertNumber(value: unknown) {
|
||||
return IsStringNumeric(value) ? parseFloat(value) : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value
|
||||
}
|
||||
function TryConvertInteger(value: unknown) {
|
||||
return IsStringNumeric(value) ? parseInt(value) : IsNumber(value) ? Math.trunc(value) : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value
|
||||
}
|
||||
function TryConvertNull(value: unknown) {
|
||||
return IsString(value) && value.toLowerCase() === 'null' ? null : value
|
||||
}
|
||||
function TryConvertUndefined(value: unknown) {
|
||||
return IsString(value) && value === 'undefined' ? undefined : value
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// note: this function may return an invalid dates for the regex
|
||||
// tests above. Invalid dates will however be checked during the
|
||||
// casting function and will return a epoch date if invalid.
|
||||
// Consider better string parsing for the iso dates in future
|
||||
// revisions.
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
function TryConvertDate(value: unknown) {
|
||||
return (
|
||||
IsDate(value) ? value :
|
||||
IsNumber(value) ? new Date(value) :
|
||||
IsValueTrue(value) ? new Date(1) :
|
||||
IsValueFalse(value) ? new Date(0) :
|
||||
IsStringNumeric(value) ? new Date(parseInt(value)) :
|
||||
IsTimeStringWithoutTimeZone(value) ? new Date(`1970-01-01T${value}.000Z`) :
|
||||
IsTimeStringWithTimeZone(value) ? new Date(`1970-01-01T${value}`) :
|
||||
IsDateTimeStringWithoutTimeZone(value) ? new Date(`${value}.000Z`) :
|
||||
IsDateTimeStringWithTimeZone(value) ? new Date(value) :
|
||||
IsDateString(value) ? new Date(`${value}T00:00:00.000Z`) :
|
||||
value
|
||||
)
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Default
|
||||
// ------------------------------------------------------------------
|
||||
function Default(value: unknown): unknown {
|
||||
return value
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Convert
|
||||
// ------------------------------------------------------------------
|
||||
function FromArray(schema: TArray, references: TSchema[], value: any): any {
|
||||
const elements = IsArray(value) ? value : [value]
|
||||
return elements.map((element) => Visit(schema.items, references, element))
|
||||
}
|
||||
function FromBigInt(schema: TBigInt, references: TSchema[], value: any): unknown {
|
||||
return TryConvertBigInt(value)
|
||||
}
|
||||
function FromBoolean(schema: TBoolean, references: TSchema[], value: any): unknown {
|
||||
return TryConvertBoolean(value)
|
||||
}
|
||||
function FromDate(schema: TDate, references: TSchema[], value: any): unknown {
|
||||
return TryConvertDate(value)
|
||||
}
|
||||
function FromImport(schema: TImport, references: TSchema[], value: unknown): unknown {
|
||||
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
return Visit(target, [...references, ...definitions], value)
|
||||
}
|
||||
function FromInteger(schema: TInteger, references: TSchema[], value: any): unknown {
|
||||
return TryConvertInteger(value)
|
||||
}
|
||||
function FromIntersect(schema: TIntersect, references: TSchema[], value: any): unknown {
|
||||
return schema.allOf.reduce((value, schema) => Visit(schema, references, value), value)
|
||||
}
|
||||
function FromLiteral(schema: TLiteral, references: TSchema[], value: any): unknown {
|
||||
return TryConvertLiteral(schema, value)
|
||||
}
|
||||
function FromNull(schema: TNull, references: TSchema[], value: any): unknown {
|
||||
return TryConvertNull(value)
|
||||
}
|
||||
function FromNumber(schema: TNumber, references: TSchema[], value: any): unknown {
|
||||
return TryConvertNumber(value)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromObject(schema: TObject, references: TSchema[], value: any): unknown {
|
||||
if(!IsObject(value) || IsArray(value)) return value
|
||||
for(const propertyKey of Object.getOwnPropertyNames(schema.properties)) {
|
||||
if(!HasPropertyKey(value, propertyKey)) continue
|
||||
value[propertyKey] = Visit(schema.properties[propertyKey], references, value[propertyKey])
|
||||
}
|
||||
return value
|
||||
}
|
||||
function FromRecord(schema: TRecord, references: TSchema[], value: any): unknown {
|
||||
const isConvertable = IsObject(value) && !IsArray(value)
|
||||
if (!isConvertable) return value
|
||||
const propertyKey = Object.getOwnPropertyNames(schema.patternProperties)[0]
|
||||
const property = schema.patternProperties[propertyKey]
|
||||
for (const [propKey, propValue] of Object.entries(value)) {
|
||||
value[propKey] = Visit(property, references, propValue)
|
||||
}
|
||||
return value
|
||||
}
|
||||
function FromRef(schema: TRef, references: TSchema[], value: any): unknown {
|
||||
return Visit(Deref(schema, references), references, value)
|
||||
}
|
||||
function FromString(schema: TString, references: TSchema[], value: any): unknown {
|
||||
return TryConvertString(value)
|
||||
}
|
||||
function FromSymbol(schema: TSymbol, references: TSchema[], value: any): unknown {
|
||||
return IsString(value) || IsNumber(value) ? Symbol(value) : value
|
||||
}
|
||||
function FromThis(schema: TThis, references: TSchema[], value: any): unknown {
|
||||
return Visit(Deref(schema, references), references, value)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromTuple(schema: TTuple, references: TSchema[], value: any): unknown {
|
||||
const isConvertable = IsArray(value) && !IsUndefined(schema.items)
|
||||
if(!isConvertable) return value
|
||||
return value.map((value, index) => {
|
||||
return (index < schema.items!.length)
|
||||
? Visit(schema.items![index], references, value)
|
||||
: value
|
||||
})
|
||||
}
|
||||
function FromUndefined(schema: TUndefined, references: TSchema[], value: any): unknown {
|
||||
return TryConvertUndefined(value)
|
||||
}
|
||||
function FromUnion(schema: TUnion, references: TSchema[], value: any): unknown {
|
||||
// Check if original value already matches one of the union variants
|
||||
for (const subschema of schema.anyOf) {
|
||||
if (Check(subschema, references, value)) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt conversion for each variant
|
||||
for (const subschema of schema.anyOf) {
|
||||
const converted = Visit(subschema, references, Clone(value))
|
||||
if (!Check(subschema, references, converted)) continue
|
||||
return converted
|
||||
}
|
||||
return value
|
||||
}
|
||||
function Visit(schema: TSchema, references: TSchema[], value: any): unknown {
|
||||
const references_ = Pushref(schema, references)
|
||||
const schema_ = schema as any
|
||||
switch (schema[Kind]) {
|
||||
case 'Array':
|
||||
return FromArray(schema_, references_, value)
|
||||
case 'BigInt':
|
||||
return FromBigInt(schema_, references_, value)
|
||||
case 'Boolean':
|
||||
return FromBoolean(schema_, references_, value)
|
||||
case 'Date':
|
||||
return FromDate(schema_, references_, value)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_, value)
|
||||
case 'Integer':
|
||||
return FromInteger(schema_, references_, value)
|
||||
case 'Intersect':
|
||||
return FromIntersect(schema_, references_, value)
|
||||
case 'Literal':
|
||||
return FromLiteral(schema_, references_, value)
|
||||
case 'Null':
|
||||
return FromNull(schema_, references_, value)
|
||||
case 'Number':
|
||||
return FromNumber(schema_, references_, value)
|
||||
case 'Object':
|
||||
return FromObject(schema_, references_, value)
|
||||
case 'Record':
|
||||
return FromRecord(schema_, references_, value)
|
||||
case 'Ref':
|
||||
return FromRef(schema_, references_, value)
|
||||
case 'String':
|
||||
return FromString(schema_, references_, value)
|
||||
case 'Symbol':
|
||||
return FromSymbol(schema_, references_, value)
|
||||
case 'This':
|
||||
return FromThis(schema_, references_, value)
|
||||
case 'Tuple':
|
||||
return FromTuple(schema_, references_, value)
|
||||
case 'Undefined':
|
||||
return FromUndefined(schema_, references_, value)
|
||||
case 'Union':
|
||||
return FromUnion(schema_, references_, value)
|
||||
default:
|
||||
return Default(value)
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Convert
|
||||
// ------------------------------------------------------------------
|
||||
/** `[Mutable]` Converts any type mismatched values to their target type if a reasonable conversion is possible. */
|
||||
export function Convert(schema: TSchema, references: TSchema[], value: unknown): unknown
|
||||
/** `[Mutable]` Converts any type mismatched values to their target type if a reasonable conversion is possible. */
|
||||
export function Convert(schema: TSchema, value: unknown): unknown
|
||||
/** `[Mutable]` Converts any type mismatched values to their target type if a reasonable conversion is possible. */
|
||||
// prettier-ignore
|
||||
export function Convert(...args: any[]) {
|
||||
return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1])
|
||||
}
|
||||
29
src/value/convert/index.ts
Normal file
29
src/value/convert/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './convert'
|
||||
488
src/value/create/create.ts
Normal file
488
src/value/create/create.ts
Normal file
@@ -0,0 +1,488 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { HasPropertyKey } from '../guard/index'
|
||||
import { Check } from '../check/index'
|
||||
import { Clone } from '../clone/index'
|
||||
import { Deref, Pushref } from '../deref/index'
|
||||
import { TemplateLiteralGenerate, IsTemplateLiteralFinite } from '../../type/template-literal/index'
|
||||
import { PatternStringExact, PatternNumberExact } from '../../type/patterns/index'
|
||||
import { TypeRegistry } from '../../type/registry/index'
|
||||
import { Kind } from '../../type/symbols/index'
|
||||
import { TypeBoxError } from '../../type/error/index'
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
import type { TAsyncIterator } from '../../type/async-iterator/index'
|
||||
import type { TAny } from '../../type/any/index'
|
||||
import type { TArray } from '../../type/array/index'
|
||||
import type { TBigInt } from '../../type/bigint/index'
|
||||
import type { TBoolean } from '../../type/boolean/index'
|
||||
import type { TDate } from '../../type/date/index'
|
||||
import type { TConstructor } from '../../type/constructor/index'
|
||||
import type { TFunction } from '../../type/function/index'
|
||||
import type { TImport } from '../../type/module/index'
|
||||
import type { TInteger } from '../../type/integer/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TIterator } from '../../type/iterator/index'
|
||||
import type { TLiteral } from '../../type/literal/index'
|
||||
import type { TNever } from '../../type/never/index'
|
||||
import type { TNot } from '../../type/not/index'
|
||||
import type { TNull } from '../../type/null/index'
|
||||
import type { TNumber } from '../../type/number/index'
|
||||
import type { TObject } from '../../type/object/index'
|
||||
import type { TPromise } from '../../type/promise/index'
|
||||
import type { TRecord } from '../../type/record/index'
|
||||
import type { TRef } from '../../type/ref/index'
|
||||
import type { TRegExp } from '../../type/regexp/index'
|
||||
import type { TTemplateLiteral } from '../../type/template-literal/index'
|
||||
import type { TThis } from '../../type/recursive/index'
|
||||
import type { TTuple } from '../../type/tuple/index'
|
||||
import type { TUnion } from '../../type/union/index'
|
||||
import type { TUnknown } from '../../type/unknown/index'
|
||||
import type { Static } from '../../type/static/index'
|
||||
import type { TString } from '../../type/string/index'
|
||||
import type { TSymbol } from '../../type/symbol/index'
|
||||
import type { TUndefined } from '../../type/undefined/index'
|
||||
import type { TUint8Array } from '../../type/uint8array/index'
|
||||
import type { TVoid } from '../../type/void/index'
|
||||
|
||||
import { IsFunction } from '../guard/guard'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Errors
|
||||
// ------------------------------------------------------------------
|
||||
export class ValueCreateError extends TypeBoxError {
|
||||
constructor(public readonly schema: TSchema, message: string) {
|
||||
super(message)
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Default
|
||||
// ------------------------------------------------------------------
|
||||
function FromDefault(value: unknown) {
|
||||
return IsFunction(value) ? value() : Clone(value)
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Create
|
||||
// ------------------------------------------------------------------
|
||||
function FromAny(schema: TAny, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
function FromArgument(schema: TAny, references: TSchema[]): any {
|
||||
return {}
|
||||
}
|
||||
function FromArray(schema: TArray, references: TSchema[]): any {
|
||||
if (schema.uniqueItems === true && !HasPropertyKey(schema, 'default')) {
|
||||
throw new ValueCreateError(schema, 'Array with the uniqueItems constraint requires a default value')
|
||||
} else if ('contains' in schema && !HasPropertyKey(schema, 'default')) {
|
||||
throw new ValueCreateError(schema, 'Array with the contains constraint requires a default value')
|
||||
} else if ('default' in schema) {
|
||||
return FromDefault(schema.default)
|
||||
} else if (schema.minItems !== undefined) {
|
||||
return Array.from({ length: schema.minItems }).map((item) => {
|
||||
return Visit(schema.items, references)
|
||||
})
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
function FromAsyncIterator(schema: TAsyncIterator, references: TSchema[]) {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
return (async function* () {})()
|
||||
}
|
||||
}
|
||||
function FromBigInt(schema: TBigInt, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
return BigInt(0)
|
||||
}
|
||||
}
|
||||
function FromBoolean(schema: TBoolean, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
function FromConstructor(schema: TConstructor, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
const value = Visit(schema.returns, references) as any
|
||||
if (typeof value === 'object' && !Array.isArray(value)) {
|
||||
return class {
|
||||
constructor() {
|
||||
for (const [key, val] of Object.entries(value)) {
|
||||
const self = this as any
|
||||
self[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return class {}
|
||||
}
|
||||
}
|
||||
}
|
||||
function FromDate(schema: TDate, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else if (schema.minimumTimestamp !== undefined) {
|
||||
return new Date(schema.minimumTimestamp)
|
||||
} else {
|
||||
return new Date()
|
||||
}
|
||||
}
|
||||
function FromFunction(schema: TFunction, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
return () => Visit(schema.returns, references)
|
||||
}
|
||||
}
|
||||
function FromImport(schema: TImport, references: TSchema[]): any {
|
||||
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
return Visit(target, [...references, ...definitions])
|
||||
}
|
||||
function FromInteger(schema: TInteger, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else if (schema.minimum !== undefined) {
|
||||
return schema.minimum
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
function FromIntersect(schema: TIntersect, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
// --------------------------------------------------------------
|
||||
// Note: The best we can do here is attempt to instance each
|
||||
// sub type and apply through object assign. For non-object
|
||||
// sub types, we just escape the assignment and just return
|
||||
// the value. In the latter case, this is typically going to
|
||||
// be a consequence of an illogical intersection.
|
||||
// --------------------------------------------------------------
|
||||
const value = schema.allOf.reduce((acc, schema) => {
|
||||
const next = Visit(schema, references) as any
|
||||
return typeof next === 'object' ? { ...acc, ...next } : next
|
||||
}, {})
|
||||
if (!Check(schema, references, value)) throw new ValueCreateError(schema, 'Intersect produced invalid value. Consider using a default value.')
|
||||
return value
|
||||
}
|
||||
}
|
||||
function FromIterator(schema: TIterator, references: TSchema[]) {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
return (function* () {})()
|
||||
}
|
||||
}
|
||||
function FromLiteral(schema: TLiteral, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
return schema.const
|
||||
}
|
||||
}
|
||||
function FromNever(schema: TNever, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
throw new ValueCreateError(schema, 'Never types cannot be created. Consider using a default value.')
|
||||
}
|
||||
}
|
||||
function FromNot(schema: TNot, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
throw new ValueCreateError(schema, 'Not types must have a default value')
|
||||
}
|
||||
}
|
||||
function FromNull(schema: TNull, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
function FromNumber(schema: TNumber, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else if (schema.minimum !== undefined) {
|
||||
return schema.minimum
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
function FromObject(schema: TObject, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
const required = new Set(schema.required)
|
||||
const Acc = {} as Record<PropertyKey, unknown>
|
||||
for (const [key, subschema] of Object.entries(schema.properties)) {
|
||||
if (!required.has(key)) continue
|
||||
Acc[key] = Visit(subschema, references)
|
||||
}
|
||||
return Acc
|
||||
}
|
||||
}
|
||||
function FromPromise(schema: TPromise, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
return Promise.resolve(Visit(schema.item, references))
|
||||
}
|
||||
}
|
||||
function FromRecord(schema: TRecord, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
function FromRef(schema: TRef, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
return Visit(Deref(schema, references), references)
|
||||
}
|
||||
}
|
||||
function FromRegExp(schema: TRegExp, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
throw new ValueCreateError(schema, 'RegExp types cannot be created. Consider using a default value.')
|
||||
}
|
||||
}
|
||||
function FromString(schema: TString, references: TSchema[]): any {
|
||||
if (schema.pattern !== undefined) {
|
||||
if (!HasPropertyKey(schema, 'default')) {
|
||||
throw new ValueCreateError(schema, 'String types with patterns must specify a default value')
|
||||
} else {
|
||||
return FromDefault(schema.default)
|
||||
}
|
||||
} else if (schema.format !== undefined) {
|
||||
if (!HasPropertyKey(schema, 'default')) {
|
||||
throw new ValueCreateError(schema, 'String types with formats must specify a default value')
|
||||
} else {
|
||||
return FromDefault(schema.default)
|
||||
}
|
||||
} else {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else if (schema.minLength !== undefined) {
|
||||
// prettier-ignore
|
||||
return Array.from({ length: schema.minLength }).map(() => ' ').join('')
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
function FromSymbol(schema: TSymbol, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else if ('value' in schema) {
|
||||
return Symbol.for(schema.value)
|
||||
} else {
|
||||
return Symbol()
|
||||
}
|
||||
}
|
||||
function FromTemplateLiteral(schema: TTemplateLiteral, references: TSchema[]) {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
}
|
||||
if (!IsTemplateLiteralFinite(schema)) throw new ValueCreateError(schema, 'Can only create template literals that produce a finite variants. Consider using a default value.')
|
||||
const generated = TemplateLiteralGenerate(schema) as string[]
|
||||
return generated[0]
|
||||
}
|
||||
function FromThis(schema: TThis, references: TSchema[]): any {
|
||||
if (recursiveDepth++ > recursiveMaxDepth) throw new ValueCreateError(schema, 'Cannot create recursive type as it appears possibly infinite. Consider using a default.')
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
return Visit(Deref(schema, references), references)
|
||||
}
|
||||
}
|
||||
function FromTuple(schema: TTuple, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
}
|
||||
if (schema.items === undefined) {
|
||||
return []
|
||||
} else {
|
||||
return Array.from({ length: schema.minItems }).map((_, index) => Visit((schema.items as any[])[index], references))
|
||||
}
|
||||
}
|
||||
function FromUndefined(schema: TUndefined, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
function FromUnion(schema: TUnion, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else if (schema.anyOf.length === 0) {
|
||||
throw new Error('ValueCreate.Union: Cannot create Union with zero variants')
|
||||
} else {
|
||||
return Visit(schema.anyOf[0], references)
|
||||
}
|
||||
}
|
||||
function FromUint8Array(schema: TUint8Array, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else if (schema.minByteLength !== undefined) {
|
||||
return new Uint8Array(schema.minByteLength)
|
||||
} else {
|
||||
return new Uint8Array(0)
|
||||
}
|
||||
}
|
||||
function FromUnknown(schema: TUnknown, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
function FromVoid(schema: TVoid, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
return void 0
|
||||
}
|
||||
}
|
||||
function FromKind(schema: TSchema, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
} else {
|
||||
throw new Error('User defined types must specify a default value')
|
||||
}
|
||||
}
|
||||
function Visit(schema: TSchema, references: TSchema[]): unknown {
|
||||
const references_ = Pushref(schema, references)
|
||||
const schema_ = schema as any
|
||||
switch (schema_[Kind]) {
|
||||
case 'Any':
|
||||
return FromAny(schema_, references_)
|
||||
case 'Argument':
|
||||
return FromArgument(schema_, references_)
|
||||
case 'Array':
|
||||
return FromArray(schema_, references_)
|
||||
case 'AsyncIterator':
|
||||
return FromAsyncIterator(schema_, references_)
|
||||
case 'BigInt':
|
||||
return FromBigInt(schema_, references_)
|
||||
case 'Boolean':
|
||||
return FromBoolean(schema_, references_)
|
||||
case 'Constructor':
|
||||
return FromConstructor(schema_, references_)
|
||||
case 'Date':
|
||||
return FromDate(schema_, references_)
|
||||
case 'Function':
|
||||
return FromFunction(schema_, references_)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_)
|
||||
case 'Integer':
|
||||
return FromInteger(schema_, references_)
|
||||
case 'Intersect':
|
||||
return FromIntersect(schema_, references_)
|
||||
case 'Iterator':
|
||||
return FromIterator(schema_, references_)
|
||||
case 'Literal':
|
||||
return FromLiteral(schema_, references_)
|
||||
case 'Never':
|
||||
return FromNever(schema_, references_)
|
||||
case 'Not':
|
||||
return FromNot(schema_, references_)
|
||||
case 'Null':
|
||||
return FromNull(schema_, references_)
|
||||
case 'Number':
|
||||
return FromNumber(schema_, references_)
|
||||
case 'Object':
|
||||
return FromObject(schema_, references_)
|
||||
case 'Promise':
|
||||
return FromPromise(schema_, references_)
|
||||
case 'Record':
|
||||
return FromRecord(schema_, references_)
|
||||
case 'Ref':
|
||||
return FromRef(schema_, references_)
|
||||
case 'RegExp':
|
||||
return FromRegExp(schema_, references_)
|
||||
case 'String':
|
||||
return FromString(schema_, references_)
|
||||
case 'Symbol':
|
||||
return FromSymbol(schema_, references_)
|
||||
case 'TemplateLiteral':
|
||||
return FromTemplateLiteral(schema_, references_)
|
||||
case 'This':
|
||||
return FromThis(schema_, references_)
|
||||
case 'Tuple':
|
||||
return FromTuple(schema_, references_)
|
||||
case 'Undefined':
|
||||
return FromUndefined(schema_, references_)
|
||||
case 'Union':
|
||||
return FromUnion(schema_, references_)
|
||||
case 'Uint8Array':
|
||||
return FromUint8Array(schema_, references_)
|
||||
case 'Unknown':
|
||||
return FromUnknown(schema_, references_)
|
||||
case 'Void':
|
||||
return FromVoid(schema_, references_)
|
||||
default:
|
||||
if (!TypeRegistry.Has(schema_[Kind])) throw new ValueCreateError(schema_, 'Unknown type')
|
||||
return FromKind(schema_, references_)
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// State
|
||||
// ------------------------------------------------------------------
|
||||
const recursiveMaxDepth = 512
|
||||
let recursiveDepth = 0
|
||||
// ------------------------------------------------------------------
|
||||
// Create
|
||||
// ------------------------------------------------------------------
|
||||
/** Creates a value from the given schema and references */
|
||||
export function Create<T extends TSchema>(schema: T, references: TSchema[]): Static<T>
|
||||
/** Creates a value from the given schema */
|
||||
export function Create<T extends TSchema>(schema: T): Static<T>
|
||||
/** Creates a value from the given schema */
|
||||
export function Create(...args: any[]) {
|
||||
recursiveDepth = 0
|
||||
return args.length === 2 ? Visit(args[0], args[1]) : Visit(args[0], [])
|
||||
}
|
||||
29
src/value/create/index.ts
Normal file
29
src/value/create/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './create'
|
||||
45
src/value/decode/decode.ts
Normal file
45
src/value/decode/decode.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { HasTransform, TransformDecode, TransformDecodeCheckError } from '../transform/index'
|
||||
import { Check } from '../check/index'
|
||||
import { Errors } from '../../errors/index'
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
import type { StaticDecode } from '../../type/static/index'
|
||||
|
||||
/** Decodes a value or throws if error */
|
||||
export function Decode<T extends TSchema, Static = StaticDecode<T>, Result extends Static = Static>(schema: T, references: TSchema[], value: unknown): Result
|
||||
/** Decodes a value or throws if error */
|
||||
export function Decode<T extends TSchema, Static = StaticDecode<T>, Result extends Static = Static>(schema: T, value: unknown): Result
|
||||
/** Decodes a value or throws if error */
|
||||
export function Decode(...args: any[]): any {
|
||||
const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]]
|
||||
if (!Check(schema, references, value)) throw new TransformDecodeCheckError(schema, value, Errors(schema, references, value).First()!)
|
||||
return HasTransform(schema, references) ? TransformDecode(schema, references, value) : value
|
||||
}
|
||||
29
src/value/decode/index.ts
Normal file
29
src/value/decode/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './decode'
|
||||
208
src/value/default/default.ts
Normal file
208
src/value/default/default.ts
Normal file
@@ -0,0 +1,208 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { Check } from '../check/index'
|
||||
import { Clone } from '../clone/index'
|
||||
import { Deref, Pushref } from '../deref/index'
|
||||
import { Kind } from '../../type/symbols/index'
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
import type { TArray } from '../../type/array/index'
|
||||
import type { TImport } from '../../type/module/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TObject } from '../../type/object/index'
|
||||
import type { TRecord } from '../../type/record/index'
|
||||
import type { TRef } from '../../type/ref/index'
|
||||
import type { TThis } from '../../type/recursive/index'
|
||||
import type { TTuple } from '../../type/tuple/index'
|
||||
import type { TUnion } from '../../type/union/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ValueGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsArray, IsDate, IsFunction, IsObject, IsUndefined, HasPropertyKey } from '../guard/index'
|
||||
// ------------------------------------------------------------------
|
||||
// TypeGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsKind } from '../../type/guard/kind'
|
||||
// ------------------------------------------------------------------
|
||||
// ValueOrDefault
|
||||
// ------------------------------------------------------------------
|
||||
function ValueOrDefault(schema: TSchema, value: unknown): unknown {
|
||||
const defaultValue = HasPropertyKey(schema, 'default') ? schema.default : undefined
|
||||
const clone = IsFunction(defaultValue) ? defaultValue() : Clone(defaultValue)
|
||||
return IsUndefined(value) ? clone : IsObject(value) && IsObject(clone) ? Object.assign(clone, value) : value
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// HasDefaultProperty
|
||||
// ------------------------------------------------------------------
|
||||
function HasDefaultProperty(schema: unknown): schema is TSchema {
|
||||
return IsKind(schema) && 'default' in schema
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Types
|
||||
// ------------------------------------------------------------------
|
||||
function FromArray(schema: TArray, references: TSchema[], value: unknown): any {
|
||||
// if the value is an array, we attempt to initialize it's elements
|
||||
if (IsArray(value)) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
value[i] = Visit(schema.items, references, value[i])
|
||||
}
|
||||
return value
|
||||
}
|
||||
// ... otherwise use default initialization
|
||||
const defaulted = ValueOrDefault(schema, value)
|
||||
if (!IsArray(defaulted)) return defaulted
|
||||
for (let i = 0; i < defaulted.length; i++) {
|
||||
defaulted[i] = Visit(schema.items, references, defaulted[i])
|
||||
}
|
||||
return defaulted
|
||||
}
|
||||
function FromDate(schema: TArray, references: TSchema[], value: unknown): any {
|
||||
// special case intercept for dates
|
||||
return IsDate(value) ? value : ValueOrDefault(schema, value)
|
||||
}
|
||||
function FromImport(schema: TImport, references: TSchema[], value: unknown): any {
|
||||
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
return Visit(target, [...references, ...definitions], value)
|
||||
}
|
||||
function FromIntersect(schema: TIntersect, references: TSchema[], value: unknown): any {
|
||||
const defaulted = ValueOrDefault(schema, value)
|
||||
return schema.allOf.reduce((acc, schema) => {
|
||||
const next = Visit(schema, references, defaulted)
|
||||
return IsObject(next) ? { ...acc, ...next } : next
|
||||
}, {})
|
||||
}
|
||||
function FromObject(schema: TObject, references: TSchema[], value: unknown): any {
|
||||
const defaulted = ValueOrDefault(schema, value)
|
||||
// return defaulted
|
||||
if (!IsObject(defaulted)) return defaulted
|
||||
const knownPropertyKeys = Object.getOwnPropertyNames(schema.properties)
|
||||
// properties
|
||||
for (const key of knownPropertyKeys) {
|
||||
// note: we need to traverse into the object and test if the return value
|
||||
// yielded a non undefined result. Here we interpret an undefined result as
|
||||
// a non assignable property and continue.
|
||||
const propertyValue = Visit(schema.properties[key], references, defaulted[key])
|
||||
if (IsUndefined(propertyValue)) continue
|
||||
defaulted[key] = Visit(schema.properties[key], references, defaulted[key])
|
||||
}
|
||||
// return if not additional properties
|
||||
if (!HasDefaultProperty(schema.additionalProperties)) return defaulted
|
||||
// additional properties
|
||||
for (const key of Object.getOwnPropertyNames(defaulted)) {
|
||||
if (knownPropertyKeys.includes(key)) continue
|
||||
defaulted[key] = Visit(schema.additionalProperties, references, defaulted[key])
|
||||
}
|
||||
return defaulted
|
||||
}
|
||||
function FromRecord(schema: TRecord, references: TSchema[], value: unknown): any {
|
||||
const defaulted = ValueOrDefault(schema, value)
|
||||
if (!IsObject(defaulted)) return defaulted
|
||||
const additionalPropertiesSchema = schema.additionalProperties as TSchema
|
||||
const [propertyKeyPattern, propertySchema] = Object.entries(schema.patternProperties)[0]
|
||||
const knownPropertyKey = new RegExp(propertyKeyPattern)
|
||||
// properties
|
||||
for (const key of Object.getOwnPropertyNames(defaulted)) {
|
||||
if (!(knownPropertyKey.test(key) && HasDefaultProperty(propertySchema))) continue
|
||||
defaulted[key] = Visit(propertySchema, references, defaulted[key])
|
||||
}
|
||||
// return if not additional properties
|
||||
if (!HasDefaultProperty(additionalPropertiesSchema)) return defaulted
|
||||
// additional properties
|
||||
for (const key of Object.getOwnPropertyNames(defaulted)) {
|
||||
if (knownPropertyKey.test(key)) continue
|
||||
defaulted[key] = Visit(additionalPropertiesSchema, references, defaulted[key])
|
||||
}
|
||||
return defaulted
|
||||
}
|
||||
function FromRef(schema: TRef, references: TSchema[], value: unknown): any {
|
||||
return Visit(Deref(schema, references), references, ValueOrDefault(schema, value))
|
||||
}
|
||||
function FromThis(schema: TThis, references: TSchema[], value: unknown): any {
|
||||
return Visit(Deref(schema, references), references, value)
|
||||
}
|
||||
function FromTuple(schema: TTuple, references: TSchema[], value: unknown): any {
|
||||
const defaulted = ValueOrDefault(schema, value)
|
||||
if (!IsArray(defaulted) || IsUndefined(schema.items)) return defaulted
|
||||
const [items, max] = [schema.items!, Math.max(schema.items!.length, defaulted.length)]
|
||||
for (let i = 0; i < max; i++) {
|
||||
if (i < items.length) defaulted[i] = Visit(items[i], references, defaulted[i])
|
||||
}
|
||||
return defaulted
|
||||
}
|
||||
function FromUnion(schema: TUnion, references: TSchema[], value: unknown): any {
|
||||
const defaulted = ValueOrDefault(schema, value)
|
||||
for (const inner of schema.anyOf) {
|
||||
const result = Visit(inner, references, Clone(defaulted))
|
||||
if (Check(inner, references, result)) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return defaulted
|
||||
}
|
||||
function Visit(schema: TSchema, references: TSchema[], value: unknown): any {
|
||||
const references_ = Pushref(schema, references)
|
||||
const schema_ = schema as any
|
||||
switch (schema_[Kind]) {
|
||||
case 'Array':
|
||||
return FromArray(schema_, references_, value)
|
||||
case 'Date':
|
||||
return FromDate(schema_, references_, value)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_, value)
|
||||
case 'Intersect':
|
||||
return FromIntersect(schema_, references_, value)
|
||||
case 'Object':
|
||||
return FromObject(schema_, references_, value)
|
||||
case 'Record':
|
||||
return FromRecord(schema_, references_, value)
|
||||
case 'Ref':
|
||||
return FromRef(schema_, references_, value)
|
||||
case 'This':
|
||||
return FromThis(schema_, references_, value)
|
||||
case 'Tuple':
|
||||
return FromTuple(schema_, references_, value)
|
||||
case 'Union':
|
||||
return FromUnion(schema_, references_, value)
|
||||
default:
|
||||
return ValueOrDefault(schema_, value)
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Default
|
||||
// ------------------------------------------------------------------
|
||||
/** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */
|
||||
export function Default(schema: TSchema, references: TSchema[], value: unknown): unknown
|
||||
/** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */
|
||||
export function Default(schema: TSchema, value: unknown): unknown
|
||||
/** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */
|
||||
export function Default(...args: any[]) {
|
||||
return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1])
|
||||
}
|
||||
29
src/value/default/index.ts
Normal file
29
src/value/default/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './default'
|
||||
215
src/value/delta/delta.ts
Normal file
215
src/value/delta/delta.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { HasPropertyKey, IsStandardObject, IsArray, IsTypedArray, IsValueType } from '../guard/index'
|
||||
import type { ObjectType, ArrayType, TypedArrayType, ValueType } from '../guard/index'
|
||||
import type { Static } from '../../type/static/index'
|
||||
import { ValuePointer } from '../pointer/index'
|
||||
import { Clone } from '../clone/index'
|
||||
import { Equal } from '../equal/equal'
|
||||
import { TypeBoxError } from '../../type/error/index'
|
||||
|
||||
import { Literal, type TLiteral } from '../../type/literal/index'
|
||||
import { Object, type TObject } from '../../type/object/index'
|
||||
import { String, type TString } from '../../type/string/index'
|
||||
import { Unknown, type TUnknown } from '../../type/unknown/index'
|
||||
import { Union, type TUnion } from '../../type/union/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Commands
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
// Note: A TypeScript 5.4.2 compiler regression resulted in the type
|
||||
// import paths being generated incorrectly. We can resolve this by
|
||||
// explicitly importing the correct TSchema types above. Note also
|
||||
// that the left-side annotations are optional, but since the types
|
||||
// are imported we might as well use them. We should check this
|
||||
// regression in future. The regression occured between TypeScript
|
||||
// versions 5.3.3 -> 5.4.2.
|
||||
|
||||
export type Insert = Static<typeof Insert>
|
||||
export const Insert: TObject<{
|
||||
type: TLiteral<'insert'>
|
||||
path: TString
|
||||
value: TUnknown
|
||||
}> = Object({
|
||||
type: Literal('insert'),
|
||||
path: String(),
|
||||
value: Unknown(),
|
||||
})
|
||||
export type Update = Static<typeof Update>
|
||||
export const Update: TObject<{
|
||||
type: TLiteral<'update'>
|
||||
path: TString
|
||||
value: TUnknown
|
||||
}> = Object({
|
||||
type: Literal('update'),
|
||||
path: String(),
|
||||
value: Unknown(),
|
||||
})
|
||||
export type Delete = Static<typeof Delete>
|
||||
export const Delete: TObject<{
|
||||
type: TLiteral<'delete'>
|
||||
path: TString
|
||||
}> = Object({
|
||||
type: Literal('delete'),
|
||||
path: String(),
|
||||
})
|
||||
export type Edit = Static<typeof Edit>
|
||||
export const Edit: TUnion<[typeof Insert, typeof Update, typeof Delete]> = Union([Insert, Update, Delete])
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Errors
|
||||
// ------------------------------------------------------------------
|
||||
export class ValueDiffError extends TypeBoxError {
|
||||
constructor(public readonly value: unknown, message: string) {
|
||||
super(message)
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Command Factory
|
||||
// ------------------------------------------------------------------
|
||||
function CreateUpdate(path: string, value: unknown): Edit {
|
||||
return { type: 'update', path, value }
|
||||
}
|
||||
function CreateInsert(path: string, value: unknown): Edit {
|
||||
return { type: 'insert', path, value }
|
||||
}
|
||||
function CreateDelete(path: string): Edit {
|
||||
return { type: 'delete', path }
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// AssertDiffable
|
||||
// ------------------------------------------------------------------
|
||||
function AssertDiffable(value: unknown): asserts value is Record<string | number, unknown> {
|
||||
if (globalThis.Object.getOwnPropertySymbols(value).length > 0) throw new ValueDiffError(value, 'Cannot diff objects with symbols')
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Diffing Generators
|
||||
// ------------------------------------------------------------------
|
||||
function* ObjectType(path: string, current: ObjectType, next: unknown): IterableIterator<Edit> {
|
||||
AssertDiffable(current)
|
||||
AssertDiffable(next)
|
||||
if (!IsStandardObject(next)) return yield CreateUpdate(path, next)
|
||||
const currentKeys = globalThis.Object.getOwnPropertyNames(current)
|
||||
const nextKeys = globalThis.Object.getOwnPropertyNames(next)
|
||||
// ----------------------------------------------------------------
|
||||
// inserts
|
||||
// ----------------------------------------------------------------
|
||||
for (const key of nextKeys) {
|
||||
if (HasPropertyKey(current, key)) continue
|
||||
yield CreateInsert(`${path}/${key}`, next[key])
|
||||
}
|
||||
// ----------------------------------------------------------------
|
||||
// updates
|
||||
// ----------------------------------------------------------------
|
||||
for (const key of currentKeys) {
|
||||
if (!HasPropertyKey(next, key)) continue
|
||||
if (Equal(current, next)) continue
|
||||
yield* Visit(`${path}/${key}`, current[key], next[key])
|
||||
}
|
||||
// ----------------------------------------------------------------
|
||||
// deletes
|
||||
// ----------------------------------------------------------------
|
||||
for (const key of currentKeys) {
|
||||
if (HasPropertyKey(next, key)) continue
|
||||
yield CreateDelete(`${path}/${key}`)
|
||||
}
|
||||
}
|
||||
function* ArrayType(path: string, current: ArrayType, next: unknown): IterableIterator<Edit> {
|
||||
if (!IsArray(next)) return yield CreateUpdate(path, next)
|
||||
for (let i = 0; i < Math.min(current.length, next.length); i++) {
|
||||
yield* Visit(`${path}/${i}`, current[i], next[i])
|
||||
}
|
||||
for (let i = 0; i < next.length; i++) {
|
||||
if (i < current.length) continue
|
||||
yield CreateInsert(`${path}/${i}`, next[i])
|
||||
}
|
||||
for (let i = current.length - 1; i >= 0; i--) {
|
||||
if (i < next.length) continue
|
||||
yield CreateDelete(`${path}/${i}`)
|
||||
}
|
||||
}
|
||||
function* TypedArrayType(path: string, current: TypedArrayType, next: unknown): IterableIterator<Edit> {
|
||||
if (!IsTypedArray(next) || current.length !== next.length || globalThis.Object.getPrototypeOf(current).constructor.name !== globalThis.Object.getPrototypeOf(next).constructor.name) return yield CreateUpdate(path, next)
|
||||
for (let i = 0; i < Math.min(current.length, next.length); i++) {
|
||||
yield* Visit(`${path}/${i}`, current[i], next[i])
|
||||
}
|
||||
}
|
||||
function* ValueType(path: string, current: ValueType, next: unknown): IterableIterator<Edit> {
|
||||
if (current === next) return
|
||||
yield CreateUpdate(path, next)
|
||||
}
|
||||
function* Visit(path: string, current: unknown, next: unknown): IterableIterator<Edit> {
|
||||
if (IsStandardObject(current)) return yield* ObjectType(path, current, next)
|
||||
if (IsArray(current)) return yield* ArrayType(path, current, next)
|
||||
if (IsTypedArray(current)) return yield* TypedArrayType(path, current, next)
|
||||
if (IsValueType(current)) return yield* ValueType(path, current, next)
|
||||
throw new ValueDiffError(current, 'Unable to diff value')
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Diff
|
||||
// ------------------------------------------------------------------
|
||||
export function Diff(current: unknown, next: unknown): Edit[] {
|
||||
return [...Visit('', current, next)]
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Patch
|
||||
// ------------------------------------------------------------------
|
||||
function IsRootUpdate(edits: Edit[]): edits is [Update] {
|
||||
return edits.length > 0 && edits[0].path === '' && edits[0].type === 'update'
|
||||
}
|
||||
function IsIdentity(edits: Edit[]) {
|
||||
return edits.length === 0
|
||||
}
|
||||
export function Patch<T = any>(current: unknown, edits: Edit[]): T {
|
||||
if (IsRootUpdate(edits)) {
|
||||
return Clone(edits[0].value) as T
|
||||
}
|
||||
if (IsIdentity(edits)) {
|
||||
return Clone(current) as T
|
||||
}
|
||||
const clone = Clone(current)
|
||||
for (const edit of edits) {
|
||||
switch (edit.type) {
|
||||
case 'insert': {
|
||||
ValuePointer.Set(clone, edit.path, edit.value)
|
||||
break
|
||||
}
|
||||
case 'update': {
|
||||
ValuePointer.Set(clone, edit.path, edit.value)
|
||||
break
|
||||
}
|
||||
case 'delete': {
|
||||
ValuePointer.Delete(clone, edit.path)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return clone as T
|
||||
}
|
||||
29
src/value/delta/index.ts
Normal file
29
src/value/delta/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './delta'
|
||||
59
src/value/deref/deref.ts
Normal file
59
src/value/deref/deref.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
import type { TRef } from '../../type/ref/index'
|
||||
import type { TThis } from '../../type/recursive/index'
|
||||
import { TypeBoxError } from '../../type/error/index'
|
||||
import { Kind } from '../../type/symbols/index'
|
||||
import { IsString } from '../guard/guard'
|
||||
export class TypeDereferenceError extends TypeBoxError {
|
||||
constructor(public readonly schema: TRef | TThis) {
|
||||
super(`Unable to dereference schema with $id '${schema.$ref}'`)
|
||||
}
|
||||
}
|
||||
function Resolve(schema: TThis | TRef, references: TSchema[]): TSchema {
|
||||
const target = references.find((target) => target.$id === schema.$ref)
|
||||
if (target === undefined) throw new TypeDereferenceError(schema)
|
||||
return Deref(target, references)
|
||||
}
|
||||
|
||||
/** `[Internal]` Pushes a schema onto references if the schema has an $id and does not exist on references */
|
||||
export function Pushref(schema: TSchema, references: TSchema[]): TSchema[] {
|
||||
if (!IsString(schema.$id) || references.some((target) => target.$id === schema.$id)) return references
|
||||
references.push(schema)
|
||||
return references
|
||||
}
|
||||
|
||||
/** `[Internal]` Dereferences a schema from the references array or throws if not found */
|
||||
export function Deref(schema: TSchema, references: TSchema[]): TSchema {
|
||||
// prettier-ignore
|
||||
return (schema[Kind] === 'This' || schema[Kind] === 'Ref')
|
||||
? Resolve(schema as never, references)
|
||||
: schema
|
||||
}
|
||||
29
src/value/deref/index.ts
Normal file
29
src/value/deref/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './deref'
|
||||
46
src/value/encode/encode.ts
Normal file
46
src/value/encode/encode.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { HasTransform, TransformEncode, TransformEncodeCheckError } from '../transform/index'
|
||||
import { Check } from '../check/index'
|
||||
import { Errors } from '../../errors/index'
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
import type { StaticEncode } from '../../type/static/index'
|
||||
|
||||
/** Encodes a value or throws if error */
|
||||
export function Encode<T extends TSchema, Static = StaticEncode<T>, Result extends Static = Static>(schema: T, references: TSchema[], value: unknown): Result
|
||||
/** Encodes a value or throws if error */
|
||||
export function Encode<T extends TSchema, Static = StaticEncode<T>, Result extends Static = Static>(schema: T, value: unknown): Result
|
||||
/** Encodes a value or throws if error */
|
||||
export function Encode(...args: any[]): any {
|
||||
const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]]
|
||||
const encoded = HasTransform(schema, references) ? TransformEncode(schema, references, value) : value
|
||||
if (!Check(schema, references, encoded)) throw new TransformEncodeCheckError(schema, encoded, Errors(schema, references, encoded).First()!)
|
||||
return encoded
|
||||
}
|
||||
29
src/value/encode/index.ts
Normal file
29
src/value/encode/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './encode'
|
||||
67
src/value/equal/equal.ts
Normal file
67
src/value/equal/equal.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { IsObject, IsDate, IsArray, IsTypedArray, IsValueType } from '../guard/index'
|
||||
import type { ObjectType, ArrayType, TypedArrayType, ValueType } from '../guard/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Equality Checks
|
||||
// ------------------------------------------------------------------
|
||||
function ObjectType(left: ObjectType, right: unknown): boolean {
|
||||
if (!IsObject(right)) return false
|
||||
const leftKeys = [...Object.keys(left), ...Object.getOwnPropertySymbols(left)]
|
||||
const rightKeys = [...Object.keys(right), ...Object.getOwnPropertySymbols(right)]
|
||||
if (leftKeys.length !== rightKeys.length) return false
|
||||
return leftKeys.every((key) => Equal(left[key], right[key]))
|
||||
}
|
||||
function DateType(left: Date, right: unknown): any {
|
||||
return IsDate(right) && left.getTime() === right.getTime()
|
||||
}
|
||||
function ArrayType(left: ArrayType, right: unknown): any {
|
||||
if (!IsArray(right) || left.length !== right.length) return false
|
||||
return left.every((value, index) => Equal(value, right[index]))
|
||||
}
|
||||
function TypedArrayType(left: TypedArrayType, right: unknown): any {
|
||||
if (!IsTypedArray(right) || left.length !== right.length || Object.getPrototypeOf(left).constructor.name !== Object.getPrototypeOf(right).constructor.name) return false
|
||||
return left.every((value, index) => Equal(value, right[index]))
|
||||
}
|
||||
function ValueType(left: ValueType, right: unknown): any {
|
||||
return left === right
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Equal
|
||||
// ------------------------------------------------------------------
|
||||
/** Returns true if the left value deep-equals the right */
|
||||
export function Equal<T>(left: T, right: unknown): right is T {
|
||||
if (IsDate(left)) return DateType(left, right)
|
||||
if (IsTypedArray(left)) return TypedArrayType(left, right)
|
||||
if (IsArray(left)) return ArrayType(left, right)
|
||||
if (IsObject(left)) return ObjectType(left, right)
|
||||
if (IsValueType(left)) return ValueType(left, right)
|
||||
throw new Error('ValueEquals: Unable to compare value')
|
||||
}
|
||||
29
src/value/equal/index.ts
Normal file
29
src/value/equal/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './equal'
|
||||
207
src/value/guard/guard.ts
Normal file
207
src/value/guard/guard.ts
Normal file
@@ -0,0 +1,207 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Types
|
||||
// ------------------------------------------------------------------
|
||||
export type ObjectType = Record<PropertyKey, unknown>
|
||||
export type ArrayType = unknown[]
|
||||
export type ValueType = null | undefined | symbol | bigint | number | boolean | string
|
||||
// prettier-ignore
|
||||
export type TypedArrayType =
|
||||
| Int8Array
|
||||
| Uint8Array
|
||||
| Uint8ClampedArray
|
||||
| Int16Array
|
||||
| Uint16Array
|
||||
| Int32Array
|
||||
| Uint32Array
|
||||
| Float32Array
|
||||
| Float64Array
|
||||
| BigInt64Array
|
||||
| BigUint64Array
|
||||
// --------------------------------------------------------------------------
|
||||
// Iterators
|
||||
// --------------------------------------------------------------------------
|
||||
/** Returns true if this value is an async iterator */
|
||||
export function IsAsyncIterator(value: unknown): value is AsyncIterableIterator<any> {
|
||||
return IsObject(value) && globalThis.Symbol.asyncIterator in value
|
||||
}
|
||||
/** Returns true if this value is an iterator */
|
||||
export function IsIterator(value: unknown): value is IterableIterator<any> {
|
||||
return IsObject(value) && globalThis.Symbol.iterator in value
|
||||
}
|
||||
// --------------------------------------------------------------------------
|
||||
// Object Instances
|
||||
// --------------------------------------------------------------------------
|
||||
/** Returns true if this value is not an instance of a class */
|
||||
export function IsStandardObject(value: unknown): value is ObjectType {
|
||||
return IsObject(value) && (globalThis.Object.getPrototypeOf(value) === Object.prototype || globalThis.Object.getPrototypeOf(value) === null)
|
||||
}
|
||||
/** Returns true if this value is an instance of a class */
|
||||
export function IsInstanceObject(value: unknown): value is ObjectType {
|
||||
return IsObject(value) && !IsArray(value) && IsFunction(value.constructor) && value.constructor.name !== 'Object'
|
||||
}
|
||||
// --------------------------------------------------------------------------
|
||||
// JavaScript
|
||||
// --------------------------------------------------------------------------
|
||||
/** Returns true if this value is a Promise */
|
||||
export function IsPromise(value: unknown): value is Promise<unknown> {
|
||||
return value instanceof globalThis.Promise
|
||||
}
|
||||
/** Returns true if this value is a Date */
|
||||
export function IsDate(value: unknown): value is Date {
|
||||
return value instanceof Date && globalThis.Number.isFinite(value.getTime())
|
||||
}
|
||||
/** Returns true if this value is an instance of Map<K, T> */
|
||||
export function IsMap(value: unknown): value is Map<unknown, unknown> {
|
||||
return value instanceof globalThis.Map
|
||||
}
|
||||
/** Returns true if this value is an instance of Set<T> */
|
||||
export function IsSet(value: unknown): value is Set<unknown> {
|
||||
return value instanceof globalThis.Set
|
||||
}
|
||||
/** Returns true if this value is RegExp */
|
||||
export function IsRegExp(value: unknown): value is RegExp {
|
||||
return value instanceof globalThis.RegExp
|
||||
}
|
||||
/** Returns true if this value is a typed array */
|
||||
export function IsTypedArray(value: unknown): value is TypedArrayType {
|
||||
return globalThis.ArrayBuffer.isView(value)
|
||||
}
|
||||
/** Returns true if the value is a Int8Array */
|
||||
export function IsInt8Array(value: unknown): value is Int8Array {
|
||||
return value instanceof globalThis.Int8Array
|
||||
}
|
||||
/** Returns true if the value is a Uint8Array */
|
||||
export function IsUint8Array(value: unknown): value is Uint8Array {
|
||||
return value instanceof globalThis.Uint8Array
|
||||
}
|
||||
/** Returns true if the value is a Uint8ClampedArray */
|
||||
export function IsUint8ClampedArray(value: unknown): value is Uint8ClampedArray {
|
||||
return value instanceof globalThis.Uint8ClampedArray
|
||||
}
|
||||
/** Returns true if the value is a Int16Array */
|
||||
export function IsInt16Array(value: unknown): value is Int16Array {
|
||||
return value instanceof globalThis.Int16Array
|
||||
}
|
||||
/** Returns true if the value is a Uint16Array */
|
||||
export function IsUint16Array(value: unknown): value is Uint16Array {
|
||||
return value instanceof globalThis.Uint16Array
|
||||
}
|
||||
/** Returns true if the value is a Int32Array */
|
||||
export function IsInt32Array(value: unknown): value is Int32Array {
|
||||
return value instanceof globalThis.Int32Array
|
||||
}
|
||||
/** Returns true if the value is a Uint32Array */
|
||||
export function IsUint32Array(value: unknown): value is Uint32Array {
|
||||
return value instanceof globalThis.Uint32Array
|
||||
}
|
||||
/** Returns true if the value is a Float32Array */
|
||||
export function IsFloat32Array(value: unknown): value is Float32Array {
|
||||
return value instanceof globalThis.Float32Array
|
||||
}
|
||||
/** Returns true if the value is a Float64Array */
|
||||
export function IsFloat64Array(value: unknown): value is Float64Array {
|
||||
return value instanceof globalThis.Float64Array
|
||||
}
|
||||
/** Returns true if the value is a BigInt64Array */
|
||||
export function IsBigInt64Array(value: unknown): value is BigInt64Array {
|
||||
return value instanceof globalThis.BigInt64Array
|
||||
}
|
||||
/** Returns true if the value is a BigUint64Array */
|
||||
export function IsBigUint64Array(value: unknown): value is BigUint64Array {
|
||||
return value instanceof globalThis.BigUint64Array
|
||||
}
|
||||
// --------------------------------------------------------------------------
|
||||
// PropertyKey
|
||||
// --------------------------------------------------------------------------
|
||||
/** Returns true if this value has this property key */
|
||||
export function HasPropertyKey<K extends PropertyKey>(value: Record<any, unknown>, key: K): value is Record<PropertyKey, unknown> & { [_ in K]: unknown } {
|
||||
return key in value
|
||||
}
|
||||
// --------------------------------------------------------------------------
|
||||
// Standard
|
||||
// --------------------------------------------------------------------------
|
||||
/** Returns true of this value is an object type */
|
||||
export function IsObject(value: unknown): value is ObjectType {
|
||||
return value !== null && typeof value === 'object'
|
||||
}
|
||||
/** Returns true if this value is an array, but not a typed array */
|
||||
export function IsArray(value: unknown): value is ArrayType {
|
||||
return globalThis.Array.isArray(value) && !globalThis.ArrayBuffer.isView(value)
|
||||
}
|
||||
/** Returns true if this value is an undefined */
|
||||
export function IsUndefined(value: unknown): value is undefined {
|
||||
return value === undefined
|
||||
}
|
||||
/** Returns true if this value is an null */
|
||||
export function IsNull(value: unknown): value is null {
|
||||
return value === null
|
||||
}
|
||||
/** Returns true if this value is an boolean */
|
||||
export function IsBoolean(value: unknown): value is boolean {
|
||||
return typeof value === 'boolean'
|
||||
}
|
||||
/** Returns true if this value is an number */
|
||||
export function IsNumber(value: unknown): value is number {
|
||||
return typeof value === 'number'
|
||||
}
|
||||
/** Returns true if this value is an integer */
|
||||
export function IsInteger(value: unknown): value is number {
|
||||
return globalThis.Number.isInteger(value)
|
||||
}
|
||||
/** Returns true if this value is bigint */
|
||||
export function IsBigInt(value: unknown): value is bigint {
|
||||
return typeof value === 'bigint'
|
||||
}
|
||||
/** Returns true if this value is string */
|
||||
export function IsString(value: unknown): value is string {
|
||||
return typeof value === 'string'
|
||||
}
|
||||
/** Returns true if this value is a function */
|
||||
export function IsFunction(value: unknown): value is Function {
|
||||
return typeof value === 'function'
|
||||
}
|
||||
/** Returns true if this value is a symbol */
|
||||
export function IsSymbol(value: unknown): value is symbol {
|
||||
return typeof value === 'symbol'
|
||||
}
|
||||
/** Returns true if this value is a value type such as number, string, boolean */
|
||||
export function IsValueType(value: unknown): value is ValueType {
|
||||
// prettier-ignore
|
||||
return (
|
||||
IsBigInt(value) ||
|
||||
IsBoolean(value) ||
|
||||
IsNull(value) ||
|
||||
IsNumber(value) ||
|
||||
IsString(value) ||
|
||||
IsSymbol(value) ||
|
||||
IsUndefined(value)
|
||||
)
|
||||
}
|
||||
29
src/value/guard/index.ts
Normal file
29
src/value/guard/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './guard'
|
||||
162
src/value/hash/hash.ts
Normal file
162
src/value/hash/hash.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { IsArray, IsBoolean, IsBigInt, IsDate, IsNull, IsNumber, IsObject, IsString, IsSymbol, IsUint8Array, IsUndefined } from '../guard/index'
|
||||
import { TypeBoxError } from '../../type/error/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Errors
|
||||
// ------------------------------------------------------------------
|
||||
export class ValueHashError extends TypeBoxError {
|
||||
constructor(public readonly value: unknown) {
|
||||
super(`Unable to hash value`)
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// ByteMarker
|
||||
// ------------------------------------------------------------------
|
||||
enum ByteMarker {
|
||||
Undefined,
|
||||
Null,
|
||||
Boolean,
|
||||
Number,
|
||||
String,
|
||||
Object,
|
||||
Array,
|
||||
Date,
|
||||
Uint8Array,
|
||||
Symbol,
|
||||
BigInt,
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// State
|
||||
// ------------------------------------------------------------------
|
||||
let Accumulator = BigInt('14695981039346656037')
|
||||
const [Prime, Size] = [BigInt('1099511628211'), BigInt('18446744073709551616' /* 2 ^ 64 */)]
|
||||
const Bytes = Array.from({ length: 256 }).map((_, i) => BigInt(i))
|
||||
const F64 = new Float64Array(1)
|
||||
const F64In = new DataView(F64.buffer)
|
||||
const F64Out = new Uint8Array(F64.buffer)
|
||||
// ------------------------------------------------------------------
|
||||
// NumberToBytes
|
||||
// ------------------------------------------------------------------
|
||||
function* NumberToBytes(value: number): IterableIterator<number> {
|
||||
const byteCount = value === 0 ? 1 : Math.ceil(Math.floor(Math.log2(value) + 1) / 8)
|
||||
for (let i = 0; i < byteCount; i++) {
|
||||
yield (value >> (8 * (byteCount - 1 - i))) & 0xff
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Hashing Functions
|
||||
// ------------------------------------------------------------------
|
||||
function ArrayType(value: Array<unknown>) {
|
||||
FNV1A64(ByteMarker.Array)
|
||||
for (const item of value) {
|
||||
Visit(item)
|
||||
}
|
||||
}
|
||||
function BooleanType(value: boolean) {
|
||||
FNV1A64(ByteMarker.Boolean)
|
||||
FNV1A64(value ? 1 : 0)
|
||||
}
|
||||
function BigIntType(value: bigint) {
|
||||
FNV1A64(ByteMarker.BigInt)
|
||||
F64In.setBigInt64(0, value)
|
||||
for (const byte of F64Out) {
|
||||
FNV1A64(byte)
|
||||
}
|
||||
}
|
||||
function DateType(value: Date) {
|
||||
FNV1A64(ByteMarker.Date)
|
||||
Visit(value.getTime())
|
||||
}
|
||||
function NullType(value: null) {
|
||||
FNV1A64(ByteMarker.Null)
|
||||
}
|
||||
function NumberType(value: number) {
|
||||
FNV1A64(ByteMarker.Number)
|
||||
F64In.setFloat64(0, value)
|
||||
for (const byte of F64Out) {
|
||||
FNV1A64(byte)
|
||||
}
|
||||
}
|
||||
function ObjectType(value: Record<string, unknown>) {
|
||||
FNV1A64(ByteMarker.Object)
|
||||
for (const key of globalThis.Object.getOwnPropertyNames(value).sort()) {
|
||||
Visit(key)
|
||||
Visit(value[key])
|
||||
}
|
||||
}
|
||||
function StringType(value: string) {
|
||||
FNV1A64(ByteMarker.String)
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
for (const byte of NumberToBytes(value.charCodeAt(i))) {
|
||||
FNV1A64(byte)
|
||||
}
|
||||
}
|
||||
}
|
||||
function SymbolType(value: symbol) {
|
||||
FNV1A64(ByteMarker.Symbol)
|
||||
Visit(value.description)
|
||||
}
|
||||
function Uint8ArrayType(value: Uint8Array) {
|
||||
FNV1A64(ByteMarker.Uint8Array)
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
FNV1A64(value[i])
|
||||
}
|
||||
}
|
||||
function UndefinedType(value: undefined) {
|
||||
return FNV1A64(ByteMarker.Undefined)
|
||||
}
|
||||
function Visit(value: any) {
|
||||
if (IsArray(value)) return ArrayType(value)
|
||||
if (IsBoolean(value)) return BooleanType(value)
|
||||
if (IsBigInt(value)) return BigIntType(value)
|
||||
if (IsDate(value)) return DateType(value)
|
||||
if (IsNull(value)) return NullType(value)
|
||||
if (IsNumber(value)) return NumberType(value)
|
||||
if (IsObject(value)) return ObjectType(value)
|
||||
if (IsString(value)) return StringType(value)
|
||||
if (IsSymbol(value)) return SymbolType(value)
|
||||
if (IsUint8Array(value)) return Uint8ArrayType(value)
|
||||
if (IsUndefined(value)) return UndefinedType(value)
|
||||
throw new ValueHashError(value)
|
||||
}
|
||||
function FNV1A64(byte: number) {
|
||||
Accumulator = Accumulator ^ Bytes[byte]
|
||||
Accumulator = (Accumulator * Prime) % Size
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Hash
|
||||
// ------------------------------------------------------------------
|
||||
/** Creates a FNV1A-64 non cryptographic hash of the given value */
|
||||
export function Hash(value: unknown) {
|
||||
Accumulator = BigInt('14695981039346656037')
|
||||
Visit(value)
|
||||
return Accumulator
|
||||
}
|
||||
29
src/value/hash/index.ts
Normal file
29
src/value/hash/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './hash'
|
||||
60
src/value/index.ts
Normal file
60
src/value/index.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Errors (re-export)
|
||||
// ------------------------------------------------------------------
|
||||
export { ValueError, ValueErrorType, ValueErrorIterator } from '../errors/index'
|
||||
// ------------------------------------------------------------------
|
||||
// Guards
|
||||
// ------------------------------------------------------------------
|
||||
export * from './guard/index'
|
||||
// ------------------------------------------------------------------
|
||||
// Operators
|
||||
// ------------------------------------------------------------------
|
||||
export * from './assert/index'
|
||||
export * from './cast/index'
|
||||
export * from './check/index'
|
||||
export * from './clean/index'
|
||||
export * from './clone/index'
|
||||
export * from './convert/index'
|
||||
export * from './create/index'
|
||||
export * from './decode/index'
|
||||
export * from './default/index'
|
||||
export * from './delta/index'
|
||||
export * from './encode/index'
|
||||
export * from './equal/index'
|
||||
export * from './hash/index'
|
||||
export * from './mutate/index'
|
||||
export * from './parse/index'
|
||||
export * from './pointer/index'
|
||||
export * from './transform/index'
|
||||
// ------------------------------------------------------------------
|
||||
// Namespace
|
||||
// ------------------------------------------------------------------
|
||||
export { Value } from './value/index'
|
||||
29
src/value/mutate/index.ts
Normal file
29
src/value/mutate/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './mutate'
|
||||
123
src/value/mutate/mutate.ts
Normal file
123
src/value/mutate/mutate.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { IsObject, IsArray, IsTypedArray, IsValueType, type TypedArrayType } from '../guard/index'
|
||||
import { ValuePointer } from '../pointer/index'
|
||||
import { Clone } from '../clone/index'
|
||||
import { TypeBoxError } from '../../type/error/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// IsStandardObject
|
||||
// ------------------------------------------------------------------
|
||||
function IsStandardObject(value: unknown): value is Record<PropertyKey, unknown> {
|
||||
return IsObject(value) && !IsArray(value)
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Errors
|
||||
// ------------------------------------------------------------------
|
||||
export class ValueMutateError extends TypeBoxError {
|
||||
constructor(message: string) {
|
||||
super(message)
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Mutators
|
||||
// ------------------------------------------------------------------
|
||||
export type Mutable = { [key: string]: unknown } | unknown[]
|
||||
function ObjectType(root: Mutable, path: string, current: unknown, next: Record<string, unknown>) {
|
||||
if (!IsStandardObject(current)) {
|
||||
ValuePointer.Set(root, path, Clone(next))
|
||||
} else {
|
||||
const currentKeys = Object.getOwnPropertyNames(current)
|
||||
const nextKeys = Object.getOwnPropertyNames(next)
|
||||
for (const currentKey of currentKeys) {
|
||||
if (!nextKeys.includes(currentKey)) {
|
||||
delete current[currentKey]
|
||||
}
|
||||
}
|
||||
for (const nextKey of nextKeys) {
|
||||
if (!currentKeys.includes(nextKey)) {
|
||||
current[nextKey] = null
|
||||
}
|
||||
}
|
||||
for (const nextKey of nextKeys) {
|
||||
Visit(root, `${path}/${nextKey}`, current[nextKey], next[nextKey])
|
||||
}
|
||||
}
|
||||
}
|
||||
function ArrayType(root: Mutable, path: string, current: unknown, next: unknown[]) {
|
||||
if (!IsArray(current)) {
|
||||
ValuePointer.Set(root, path, Clone(next))
|
||||
} else {
|
||||
for (let index = 0; index < next.length; index++) {
|
||||
Visit(root, `${path}/${index}`, current[index], next[index])
|
||||
}
|
||||
current.splice(next.length)
|
||||
}
|
||||
}
|
||||
function TypedArrayType(root: Mutable, path: string, current: unknown, next: TypedArrayType) {
|
||||
if (IsTypedArray(current) && current.length === next.length) {
|
||||
for (let i = 0; i < current.length; i++) {
|
||||
current[i] = next[i]
|
||||
}
|
||||
} else {
|
||||
ValuePointer.Set(root, path, Clone(next))
|
||||
}
|
||||
}
|
||||
function ValueType(root: Mutable, path: string, current: unknown, next: unknown) {
|
||||
if (current === next) return
|
||||
ValuePointer.Set(root, path, next)
|
||||
}
|
||||
function Visit(root: Mutable, path: string, current: unknown, next: unknown) {
|
||||
if (IsArray(next)) return ArrayType(root, path, current, next)
|
||||
if (IsTypedArray(next)) return TypedArrayType(root, path, current, next)
|
||||
if (IsStandardObject(next)) return ObjectType(root, path, current, next)
|
||||
if (IsValueType(next)) return ValueType(root, path, current, next)
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// IsNonMutableValue
|
||||
// ------------------------------------------------------------------
|
||||
function IsNonMutableValue(value: unknown): value is Mutable {
|
||||
return IsTypedArray(value) || IsValueType(value)
|
||||
}
|
||||
function IsMismatchedValue(current: unknown, next: unknown) {
|
||||
// prettier-ignore
|
||||
return (
|
||||
(IsStandardObject(current) && IsArray(next)) ||
|
||||
(IsArray(current) && IsStandardObject(next))
|
||||
)
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Mutate
|
||||
// ------------------------------------------------------------------
|
||||
/** `[Mutable]` Performs a deep mutable value assignment while retaining internal references */
|
||||
export function Mutate(current: Mutable, next: Mutable): void {
|
||||
if (IsNonMutableValue(current) || IsNonMutableValue(next)) throw new ValueMutateError('Only object and array types can be mutated at the root level')
|
||||
if (IsMismatchedValue(current, next)) throw new ValueMutateError('Cannot assign due type mismatch of assignable values')
|
||||
Visit(current, '', current, next)
|
||||
}
|
||||
29
src/value/parse/index.ts
Normal file
29
src/value/parse/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './parse'
|
||||
130
src/value/parse/parse.ts
Normal file
130
src/value/parse/parse.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { TypeBoxError } from '../../type/error/index'
|
||||
import { TransformDecode, TransformEncode, HasTransform } from '../transform/index'
|
||||
import { TSchema } from '../../type/schema/index'
|
||||
import { StaticDecode } from '../../type/static/index'
|
||||
import { Assert } from '../assert/index'
|
||||
import { Cast } from '../cast/index'
|
||||
import { Clean } from '../clean/index'
|
||||
import { Clone } from '../clone/index'
|
||||
import { Convert } from '../convert/index'
|
||||
import { Default } from '../default/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Guards
|
||||
// ------------------------------------------------------------------
|
||||
import { IsArray, IsUndefined } from '../guard/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Error
|
||||
// ------------------------------------------------------------------
|
||||
export class ParseError extends TypeBoxError {
|
||||
constructor(message: string) {
|
||||
super(message)
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ParseRegistry
|
||||
// ------------------------------------------------------------------
|
||||
export type TParseOperation = 'Assert' | 'Cast' | 'Clean' | 'Clone' | 'Convert' | 'Decode' | 'Default' | 'Encode' | ({} & string)
|
||||
export type TParseFunction = (type: TSchema, references: TSchema[], value: unknown) => unknown
|
||||
|
||||
// prettier-ignore
|
||||
export namespace ParseRegistry {
|
||||
const registry = new Map<string, TParseFunction>([
|
||||
['Assert', (type, references, value: unknown) => { Assert(type, references, value); return value }],
|
||||
['Cast', (type, references, value: unknown) => Cast(type, references, value)],
|
||||
['Clean', (type, references, value: unknown) => Clean(type, references, value)],
|
||||
['Clone', (_type, _references, value: unknown) => Clone(value)],
|
||||
['Convert', (type, references, value: unknown) => Convert(type, references, value)],
|
||||
['Decode', (type, references, value: unknown) => (HasTransform(type, references) ? TransformDecode(type, references, value) : value)],
|
||||
['Default', (type, references, value: unknown) => Default(type, references, value)],
|
||||
['Encode', (type, references, value: unknown) => (HasTransform(type, references) ? TransformEncode(type, references, value) : value)],
|
||||
])
|
||||
// Deletes an entry from the registry
|
||||
export function Delete(key: string): void {
|
||||
registry.delete(key)
|
||||
}
|
||||
// Sets an entry in the registry
|
||||
export function Set(key: string, callback: TParseFunction): void {
|
||||
registry.set(key, callback)
|
||||
}
|
||||
// Gets an entry in the registry
|
||||
export function Get(key: string): TParseFunction | undefined {
|
||||
return registry.get(key)
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Default Parse Pipeline
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
export const ParseDefault = [
|
||||
'Clone',
|
||||
'Clean',
|
||||
'Default',
|
||||
'Convert',
|
||||
'Assert',
|
||||
'Decode'
|
||||
] as const
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ParseValue
|
||||
// ------------------------------------------------------------------
|
||||
function ParseValue<Type extends TSchema, Result extends StaticDecode<Type> = StaticDecode<Type>>(operations: TParseOperation[], type: Type, references: TSchema[], value: unknown): Result {
|
||||
return operations.reduce((value, operationKey) => {
|
||||
const operation = ParseRegistry.Get(operationKey)
|
||||
if (IsUndefined(operation)) throw new ParseError(`Unable to find Parse operation '${operationKey}'`)
|
||||
return operation(type, references, value)
|
||||
}, value) as Result
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Parse
|
||||
// ------------------------------------------------------------------
|
||||
/** Parses a value using the default parse pipeline. Will throws an `AssertError` if invalid. */
|
||||
export function Parse<Type extends TSchema, Output = StaticDecode<Type>, Result extends Output = Output>(schema: Type, references: TSchema[], value: unknown): Result
|
||||
/** Parses a value using the default parse pipeline. Will throws an `AssertError` if invalid. */
|
||||
export function Parse<Type extends TSchema, Output = StaticDecode<Type>, Result extends Output = Output>(schema: Type, value: unknown): Result
|
||||
/** Parses a value using the specified operations. */
|
||||
export function Parse<Type extends TSchema>(operations: TParseOperation[], schema: Type, references: TSchema[], value: unknown): unknown
|
||||
/** Parses a value using the specified operations. */
|
||||
export function Parse<Type extends TSchema>(operations: TParseOperation[], schema: Type, value: unknown): unknown
|
||||
/** Parses a value */
|
||||
export function Parse(...args: any[]): unknown {
|
||||
// prettier-ignore
|
||||
const [operations, schema, references, value] = (
|
||||
args.length === 4 ? [args[0], args[1], args[2], args[3]] :
|
||||
args.length === 3 ? IsArray(args[0]) ? [args[0], args[1], [], args[2]] : [ParseDefault, args[0], args[1], args[2]] :
|
||||
args.length === 2 ? [ParseDefault, args[0], [], args[1]] :
|
||||
(() => { throw new ParseError('Invalid Arguments') })()
|
||||
)
|
||||
return ParseValue(operations, schema, references, value)
|
||||
}
|
||||
29
src/value/pointer/index.ts
Normal file
29
src/value/pointer/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * as ValuePointer from './pointer'
|
||||
127
src/value/pointer/pointer.ts
Normal file
127
src/value/pointer/pointer.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { TypeBoxError } from '../../type/error/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Errors
|
||||
// ------------------------------------------------------------------
|
||||
export class ValuePointerRootSetError extends TypeBoxError {
|
||||
constructor(public readonly value: unknown, public readonly path: string, public readonly update: unknown) {
|
||||
super('Cannot set root value')
|
||||
}
|
||||
}
|
||||
export class ValuePointerRootDeleteError extends TypeBoxError {
|
||||
constructor(public readonly value: unknown, public readonly path: string) {
|
||||
super('Cannot delete root value')
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// ValuePointer
|
||||
// ------------------------------------------------------------------
|
||||
/** Provides functionality to update values through RFC6901 string pointers */
|
||||
// prettier-ignore
|
||||
function Escape(component: string) {
|
||||
return component.indexOf('~') === -1 ? component : component.replace(/~1/g, '/').replace(/~0/g, '~')
|
||||
}
|
||||
/** Formats the given pointer into navigable key components */
|
||||
// prettier-ignore
|
||||
export function* Format(pointer: string): IterableIterator<string> {
|
||||
if (pointer === '') return
|
||||
let [start, end] = [0, 0]
|
||||
for (let i = 0; i < pointer.length; i++) {
|
||||
const char = pointer.charAt(i)
|
||||
if (char === '/') {
|
||||
if (i === 0) {
|
||||
start = i + 1
|
||||
} else {
|
||||
end = i
|
||||
yield Escape(pointer.slice(start, end))
|
||||
start = i + 1
|
||||
}
|
||||
} else {
|
||||
end = i
|
||||
}
|
||||
}
|
||||
yield Escape(pointer.slice(start))
|
||||
}
|
||||
/** Sets the value at the given pointer. If the value at the pointer does not exist it is created */
|
||||
// prettier-ignore
|
||||
export function Set(value: any, pointer: string, update: unknown): void {
|
||||
if (pointer === '') throw new ValuePointerRootSetError(value, pointer, update)
|
||||
let [owner, next, key] = [null as any, value, '']
|
||||
for (const component of Format(pointer)) {
|
||||
if (next[component] === undefined) next[component] = {}
|
||||
owner = next
|
||||
next = next[component]
|
||||
key = component
|
||||
}
|
||||
owner[key] = update
|
||||
}
|
||||
/** Deletes a value at the given pointer */
|
||||
// prettier-ignore
|
||||
export function Delete(value: any, pointer: string): void {
|
||||
if (pointer === '') throw new ValuePointerRootDeleteError(value, pointer)
|
||||
let [owner, next, key] = [null as any, value as any, '']
|
||||
for (const component of Format(pointer)) {
|
||||
if (next[component] === undefined || next[component] === null) return
|
||||
owner = next
|
||||
next = next[component]
|
||||
key = component
|
||||
}
|
||||
if (Array.isArray(owner)) {
|
||||
const index = parseInt(key)
|
||||
owner.splice(index, 1)
|
||||
} else {
|
||||
delete owner[key]
|
||||
}
|
||||
}
|
||||
/** Returns true if a value exists at the given pointer */
|
||||
// prettier-ignore
|
||||
export function Has(value: any, pointer: string): boolean {
|
||||
if (pointer === '') return true
|
||||
let [owner, next, key] = [null as any, value as any, '']
|
||||
for (const component of Format(pointer)) {
|
||||
if (next[component] === undefined) return false
|
||||
owner = next
|
||||
next = next[component]
|
||||
key = component
|
||||
}
|
||||
return Object.getOwnPropertyNames(owner).includes(key)
|
||||
}
|
||||
/** Gets the value at the given pointer */
|
||||
// prettier-ignore
|
||||
export function Get(value: any, pointer: string): any {
|
||||
if (pointer === '') return value
|
||||
let current = value
|
||||
for (const component of Format(pointer)) {
|
||||
if (current[component] === undefined) return undefined
|
||||
current = current[component]
|
||||
}
|
||||
return current
|
||||
}
|
||||
242
src/value/transform/decode.ts
Normal file
242
src/value/transform/decode.ts
Normal file
@@ -0,0 +1,242 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { TypeSystemPolicy } from '../../system/policy'
|
||||
import { Kind, TransformKind } from '../../type/symbols/index'
|
||||
import { TypeBoxError } from '../../type/error/index'
|
||||
import { ValueError } from '../../errors/index'
|
||||
import { KeyOfPropertyKeys, KeyOfPropertyEntries } from '../../type/keyof/index'
|
||||
import { Deref, Pushref } from '../deref/index'
|
||||
import { Check } from '../check/index'
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
import type { TArray } from '../../type/array/index'
|
||||
import type { TImport } from '../../type/module/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TNot } from '../../type/not/index'
|
||||
import type { TObject } from '../../type/object/index'
|
||||
import type { TRecord } from '../../type/record/index'
|
||||
import type { TRef } from '../../type/ref/index'
|
||||
import type { TThis } from '../../type/recursive/index'
|
||||
import type { TTuple } from '../../type/tuple/index'
|
||||
import type { TUnion } from '../../type/union/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ValueGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { HasPropertyKey, IsObject, IsArray, IsValueType, IsUndefined as IsUndefinedValue } from '../guard/index'
|
||||
// ------------------------------------------------------------------
|
||||
// KindGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsTransform, IsSchema, IsUndefined } from '../../type/guard/kind'
|
||||
// ------------------------------------------------------------------
|
||||
// Errors
|
||||
// ------------------------------------------------------------------
|
||||
// thrown externally
|
||||
// prettier-ignore
|
||||
export class TransformDecodeCheckError extends TypeBoxError {
|
||||
constructor(
|
||||
public readonly schema: TSchema,
|
||||
public readonly value: unknown,
|
||||
public readonly error: ValueError
|
||||
) {
|
||||
super(`Unable to decode value as it does not match the expected schema`)
|
||||
}
|
||||
}
|
||||
// prettier-ignore
|
||||
export class TransformDecodeError extends TypeBoxError {
|
||||
constructor(
|
||||
public readonly schema: TSchema,
|
||||
public readonly path: string,
|
||||
public readonly value: unknown,
|
||||
public readonly error: Error,
|
||||
) {
|
||||
super(error instanceof Error ? error.message : 'Unknown error')
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Decode
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
function Default(schema: TSchema, path: string, value: any): unknown {
|
||||
try {
|
||||
return IsTransform(schema) ? schema[TransformKind].Decode(value) : value
|
||||
} catch (error) {
|
||||
throw new TransformDecodeError(schema, path, value, error as Error)
|
||||
}
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromArray(schema: TArray, references: TSchema[], path: string, value: any): unknown {
|
||||
return (IsArray(value))
|
||||
? Default(schema, path, value.map((value: any, index) => Visit(schema.items, references, `${path}/${index}`, value)))
|
||||
: Default(schema, path, value)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any): unknown {
|
||||
if (!IsObject(value) || IsValueType(value)) return Default(schema, path, value)
|
||||
const knownEntries = KeyOfPropertyEntries(schema)
|
||||
const knownKeys = knownEntries.map(entry => entry[0])
|
||||
const knownProperties = { ...value } as Record<PropertyKey, unknown>
|
||||
for(const [knownKey, knownSchema] of knownEntries) if(knownKey in knownProperties) {
|
||||
knownProperties[knownKey] = Visit(knownSchema, references, `${path}/${knownKey}`, knownProperties[knownKey])
|
||||
}
|
||||
if (!IsTransform(schema.unevaluatedProperties)) {
|
||||
return Default(schema, path, knownProperties)
|
||||
}
|
||||
const unknownKeys = Object.getOwnPropertyNames(knownProperties)
|
||||
const unevaluatedProperties = schema.unevaluatedProperties as TSchema
|
||||
const unknownProperties = { ...knownProperties } as Record<PropertyKey, unknown>
|
||||
for(const key of unknownKeys) if(!knownKeys.includes(key)) {
|
||||
unknownProperties[key] = Default(unevaluatedProperties, `${path}/${key}`, unknownProperties[key])
|
||||
}
|
||||
return Default(schema, path, unknownProperties)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromImport(schema: TImport, references: TSchema[], path: string, value: unknown): unknown {
|
||||
const additional = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
const result = Visit(target, [...references, ...additional], path, value)
|
||||
return Default(schema, path, result)
|
||||
}
|
||||
function FromNot(schema: TNot, references: TSchema[], path: string, value: any): unknown {
|
||||
return Default(schema, path, Visit(schema.not, references, path, value))
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromObject(schema: TObject, references: TSchema[], path: string, value: any): unknown {
|
||||
if (!IsObject(value)) return Default(schema, path, value)
|
||||
const knownKeys = KeyOfPropertyKeys(schema) as string[]
|
||||
const knownProperties = { ...value } as Record<PropertyKey, unknown>
|
||||
for(const key of knownKeys) {
|
||||
if(!HasPropertyKey(knownProperties, key)) continue
|
||||
// if the property value is undefined, but the target is not, nor does it satisfy exact optional
|
||||
// property policy, then we need to continue. This is a special case for optional property handling
|
||||
// where a transforms wrapped in a optional modifiers should not run.
|
||||
if(IsUndefinedValue(knownProperties[key]) && (
|
||||
!IsUndefined(schema.properties[key]) ||
|
||||
TypeSystemPolicy.IsExactOptionalProperty(knownProperties, key)
|
||||
)) continue
|
||||
// decode property
|
||||
knownProperties[key] = Visit(schema.properties[key], references, `${path}/${key}`, knownProperties[key])
|
||||
}
|
||||
if (!IsSchema(schema.additionalProperties)) {
|
||||
return Default(schema, path, knownProperties)
|
||||
}
|
||||
const unknownKeys = Object.getOwnPropertyNames(knownProperties)
|
||||
const additionalProperties = schema.additionalProperties as TSchema
|
||||
const unknownProperties = { ...knownProperties } as Record<PropertyKey, unknown>
|
||||
for(const key of unknownKeys) if(!knownKeys.includes(key)) {
|
||||
unknownProperties[key] = Default(additionalProperties, `${path}/${key}`, unknownProperties[key])
|
||||
}
|
||||
return Default(schema, path, unknownProperties)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromRecord(schema: TRecord, references: TSchema[], path: string, value: any): unknown {
|
||||
if (!IsObject(value)) return Default(schema, path, value)
|
||||
const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0]
|
||||
const knownKeys = new RegExp(pattern)
|
||||
const knownProperties = { ...value } as Record<PropertyKey, unknown>
|
||||
for(const key of Object.getOwnPropertyNames(value)) if(knownKeys.test(key)) {
|
||||
knownProperties[key] = Visit(schema.patternProperties[pattern], references, `${path}/${key}`, knownProperties[key])
|
||||
}
|
||||
if (!IsSchema(schema.additionalProperties)) {
|
||||
return Default(schema, path, knownProperties)
|
||||
}
|
||||
const unknownKeys = Object.getOwnPropertyNames(knownProperties)
|
||||
const additionalProperties = schema.additionalProperties as TSchema
|
||||
const unknownProperties = {...knownProperties} as Record<PropertyKey, unknown>
|
||||
for(const key of unknownKeys) if(!knownKeys.test(key)) {
|
||||
unknownProperties[key] = Default(additionalProperties, `${path}/${key}`, unknownProperties[key])
|
||||
}
|
||||
return Default(schema, path, unknownProperties)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromRef(schema: TRef, references: TSchema[], path: string, value: any): unknown {
|
||||
const target = Deref(schema, references)
|
||||
return Default(schema, path, Visit(target, references, path, value))
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromThis(schema: TThis, references: TSchema[], path: string, value: any): unknown {
|
||||
const target = Deref(schema, references)
|
||||
return Default(schema, path, Visit(target, references, path, value))
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromTuple(schema: TTuple, references: TSchema[], path: string, value: any): unknown {
|
||||
return (IsArray(value) && IsArray(schema.items))
|
||||
? Default(schema, path, schema.items.map((schema, index) => Visit(schema, references, `${path}/${index}`, value[index])))
|
||||
: Default(schema, path, value)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromUnion(schema: TUnion, references: TSchema[], path: string, value: any): unknown {
|
||||
for (const subschema of schema.anyOf) {
|
||||
if (!Check(subschema, references, value)) continue
|
||||
// note: ensure interior is decoded first
|
||||
const decoded = Visit(subschema, references, path, value)
|
||||
return Default(schema, path, decoded)
|
||||
}
|
||||
return Default(schema, path, value)
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
function Visit(schema: TSchema, references: TSchema[], path: string, value: any): any {
|
||||
const references_ = Pushref(schema, references)
|
||||
const schema_ = schema as any
|
||||
switch (schema[Kind]) {
|
||||
case 'Array':
|
||||
return FromArray(schema_, references_, path, value)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_, path, value)
|
||||
case 'Intersect':
|
||||
return FromIntersect(schema_, references_, path, value)
|
||||
case 'Not':
|
||||
return FromNot(schema_, references_, path, value)
|
||||
case 'Object':
|
||||
return FromObject(schema_, references_, path, value)
|
||||
case 'Record':
|
||||
return FromRecord(schema_, references_, path, value)
|
||||
case 'Ref':
|
||||
return FromRef(schema_, references_, path, value)
|
||||
case 'Symbol':
|
||||
return Default(schema_, path, value)
|
||||
case 'This':
|
||||
return FromThis(schema_, references_, path, value)
|
||||
case 'Tuple':
|
||||
return FromTuple(schema_, references_, path, value)
|
||||
case 'Union':
|
||||
return FromUnion(schema_, references_, path, value)
|
||||
default:
|
||||
return Default(schema_, path, value)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* `[Internal]` Decodes the value and returns the result. This function requires that
|
||||
* the caller `Check` the value before use. Passing unchecked values may result in
|
||||
* undefined behavior. Refer to the `Value.Decode()` for implementation details.
|
||||
*/
|
||||
export function TransformDecode(schema: TSchema, references: TSchema[], value: unknown): unknown {
|
||||
return Visit(schema, references, '', value)
|
||||
}
|
||||
251
src/value/transform/encode.ts
Normal file
251
src/value/transform/encode.ts
Normal file
@@ -0,0 +1,251 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { TypeSystemPolicy } from '../../system/policy'
|
||||
import { Kind, TransformKind } from '../../type/symbols/index'
|
||||
import { TypeBoxError } from '../../type/error/index'
|
||||
import { ValueError } from '../../errors/index'
|
||||
import { KeyOfPropertyKeys, KeyOfPropertyEntries } from '../../type/keyof/index'
|
||||
import { Deref, Pushref } from '../deref/index'
|
||||
import { Check } from '../check/index'
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
import type { TArray } from '../../type/array/index'
|
||||
import type { TImport } from '../../type/module/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TNot } from '../../type/not/index'
|
||||
import type { TObject } from '../../type/object/index'
|
||||
import type { TRecord } from '../../type/record/index'
|
||||
import type { TRef } from '../../type/ref/index'
|
||||
import type { TThis } from '../../type/recursive/index'
|
||||
import type { TTuple } from '../../type/tuple/index'
|
||||
import type { TUnion } from '../../type/union/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ValueGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { HasPropertyKey, IsObject, IsArray, IsValueType, IsUndefined as IsUndefinedValue } from '../guard/index'
|
||||
// ------------------------------------------------------------------
|
||||
// KindGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsTransform, IsSchema, IsUndefined } from '../../type/guard/kind'
|
||||
// ------------------------------------------------------------------
|
||||
// Errors
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
export class TransformEncodeCheckError extends TypeBoxError {
|
||||
constructor(
|
||||
public readonly schema: TSchema,
|
||||
public readonly value: unknown,
|
||||
public readonly error: ValueError
|
||||
) {
|
||||
super(`The encoded value does not match the expected schema`)
|
||||
}
|
||||
}
|
||||
// prettier-ignore
|
||||
export class TransformEncodeError extends TypeBoxError {
|
||||
constructor(
|
||||
public readonly schema: TSchema,
|
||||
public readonly path: string,
|
||||
public readonly value: unknown,
|
||||
public readonly error: Error,
|
||||
) {
|
||||
super(`${error instanceof Error ? error.message : 'Unknown error'}`)
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Encode
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
function Default(schema: TSchema, path: string, value: any) {
|
||||
try {
|
||||
return IsTransform(schema) ? schema[TransformKind].Encode(value) : value
|
||||
} catch (error) {
|
||||
throw new TransformEncodeError(schema, path, value, error as Error)
|
||||
}
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromArray(schema: TArray, references: TSchema[], path: string, value: any): any {
|
||||
const defaulted = Default(schema, path, value)
|
||||
return IsArray(defaulted)
|
||||
? defaulted.map((value: any, index) => Visit(schema.items, references, `${path}/${index}`, value))
|
||||
: defaulted
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromImport(schema: TImport, references: TSchema[], path: string, value: unknown): unknown {
|
||||
const additional = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
const result = Default(schema, path, value)
|
||||
return Visit(target, [...references, ...additional], path, result)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any) {
|
||||
const defaulted = Default(schema, path, value)
|
||||
if (!IsObject(value) || IsValueType(value)) return defaulted
|
||||
const knownEntries = KeyOfPropertyEntries(schema)
|
||||
const knownKeys = knownEntries.map(entry => entry[0])
|
||||
const knownProperties = { ...defaulted } as Record<PropertyKey, unknown>
|
||||
for(const [knownKey, knownSchema] of knownEntries) if(knownKey in knownProperties) {
|
||||
knownProperties[knownKey] = Visit(knownSchema, references, `${path}/${knownKey}`, knownProperties[knownKey])
|
||||
}
|
||||
if (!IsTransform(schema.unevaluatedProperties)) {
|
||||
return knownProperties
|
||||
}
|
||||
const unknownKeys = Object.getOwnPropertyNames(knownProperties)
|
||||
const unevaluatedProperties = schema.unevaluatedProperties as TSchema
|
||||
const properties = { ...knownProperties } as Record<PropertyKey, unknown>
|
||||
for(const key of unknownKeys) if(!knownKeys.includes(key)) {
|
||||
properties[key] = Default(unevaluatedProperties, `${path}/${key}`, properties[key])
|
||||
}
|
||||
return properties
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromNot(schema: TNot, references: TSchema[], path: string, value: any) {
|
||||
return Default(schema.not, path, Default(schema, path, value))
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromObject(schema: TObject, references: TSchema[], path: string, value: any) {
|
||||
const defaulted = Default(schema, path, value)
|
||||
if (!IsObject(defaulted)) return defaulted
|
||||
const knownKeys = KeyOfPropertyKeys(schema) as string[]
|
||||
const knownProperties = { ...defaulted } as Record<PropertyKey, unknown>
|
||||
for(const key of knownKeys) {
|
||||
if(!HasPropertyKey(knownProperties, key)) continue
|
||||
// if the property value is undefined, but the target is not, nor does it satisfy exact optional
|
||||
// property policy, then we need to continue. This is a special case for optional property handling
|
||||
// where a transforms wrapped in a optional modifiers should not run.
|
||||
if(IsUndefinedValue(knownProperties[key]) && (
|
||||
!IsUndefined(schema.properties[key]) ||
|
||||
TypeSystemPolicy.IsExactOptionalProperty(knownProperties, key)
|
||||
)) continue
|
||||
// encode property
|
||||
knownProperties[key] = Visit(schema.properties[key], references, `${path}/${key}`, knownProperties[key])
|
||||
}
|
||||
if (!IsSchema(schema.additionalProperties)) {
|
||||
return knownProperties
|
||||
}
|
||||
const unknownKeys = Object.getOwnPropertyNames(knownProperties)
|
||||
const additionalProperties = schema.additionalProperties as TSchema
|
||||
const properties = { ...knownProperties } as Record<PropertyKey, unknown>
|
||||
for(const key of unknownKeys) if(!knownKeys.includes(key)) {
|
||||
properties[key] = Default(additionalProperties, `${path}/${key}`, properties[key])
|
||||
}
|
||||
return properties
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromRecord(schema: TRecord, references: TSchema[], path: string, value: any) {
|
||||
const defaulted = Default(schema, path, value) as Record<PropertyKey, unknown>
|
||||
if (!IsObject(value)) return defaulted
|
||||
const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0]
|
||||
const knownKeys = new RegExp(pattern)
|
||||
const knownProperties = {...defaulted } as Record<PropertyKey, unknown>
|
||||
for(const key of Object.getOwnPropertyNames(value)) if(knownKeys.test(key)) {
|
||||
knownProperties[key] = Visit(schema.patternProperties[pattern], references, `${path}/${key}`, knownProperties[key])
|
||||
}
|
||||
if (!IsSchema(schema.additionalProperties)) {
|
||||
return knownProperties
|
||||
}
|
||||
const unknownKeys = Object.getOwnPropertyNames(knownProperties)
|
||||
const additionalProperties = schema.additionalProperties as TSchema
|
||||
const properties = { ...knownProperties } as Record<PropertyKey, unknown>
|
||||
for(const key of unknownKeys) if(!knownKeys.test(key)) {
|
||||
properties[key] = Default(additionalProperties, `${path}/${key}`, properties[key])
|
||||
}
|
||||
return properties
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromRef(schema: TRef, references: TSchema[], path: string, value: any) {
|
||||
const target = Deref(schema, references)
|
||||
const resolved = Visit(target, references, path, value)
|
||||
return Default(schema, path, resolved)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromThis(schema: TThis, references: TSchema[], path: string, value: any) {
|
||||
const target = Deref(schema, references)
|
||||
const resolved = Visit(target, references, path, value)
|
||||
return Default(schema, path, resolved)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromTuple(schema: TTuple, references: TSchema[], path: string, value: any) {
|
||||
const value1 = Default(schema, path, value)
|
||||
return IsArray(schema.items) ? schema.items.map((schema, index) => Visit(schema, references, `${path}/${index}`, value1[index])) : []
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromUnion(schema: TUnion, references: TSchema[], path: string, value: any) {
|
||||
// test value against union variants
|
||||
for (const subschema of schema.anyOf) {
|
||||
if (!Check(subschema, references, value)) continue
|
||||
const value1 = Visit(subschema, references, path, value)
|
||||
return Default(schema, path, value1)
|
||||
}
|
||||
// test transformed value against union variants
|
||||
for (const subschema of schema.anyOf) {
|
||||
const value1 = Visit(subschema, references, path, value)
|
||||
if (!Check(schema, references, value1)) continue
|
||||
return Default(schema, path, value1)
|
||||
}
|
||||
return Default(schema, path, value)
|
||||
}
|
||||
// prettier-ignore
|
||||
function Visit(schema: TSchema, references: TSchema[], path: string, value: any): any {
|
||||
const references_ = Pushref(schema, references)
|
||||
const schema_ = schema as any
|
||||
switch (schema[Kind]) {
|
||||
case 'Array':
|
||||
return FromArray(schema_, references_, path, value)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_, path, value)
|
||||
case 'Intersect':
|
||||
return FromIntersect(schema_, references_, path, value)
|
||||
case 'Not':
|
||||
return FromNot(schema_, references_, path, value)
|
||||
case 'Object':
|
||||
return FromObject(schema_, references_, path, value)
|
||||
case 'Record':
|
||||
return FromRecord(schema_, references_, path, value)
|
||||
case 'Ref':
|
||||
return FromRef(schema_, references_, path, value)
|
||||
case 'This':
|
||||
return FromThis(schema_, references_, path, value)
|
||||
case 'Tuple':
|
||||
return FromTuple(schema_, references_, path, value)
|
||||
case 'Union':
|
||||
return FromUnion(schema_, references_, path, value)
|
||||
default:
|
||||
return Default(schema_, path, value)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* `[Internal]` Encodes the value and returns the result. This function expects the
|
||||
* caller to pass a statically checked value. This function does not check the encoded
|
||||
* result, meaning the result should be passed to `Check` before use. Refer to the
|
||||
* `Value.Encode()` function for implementation details.
|
||||
*/
|
||||
export function TransformEncode(schema: TSchema, references: TSchema[], value: unknown): unknown {
|
||||
return Visit(schema, references, '', value)
|
||||
}
|
||||
176
src/value/transform/has.ts
Normal file
176
src/value/transform/has.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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 { Deref, Pushref } from '../deref/index'
|
||||
import { Kind } from '../../type/symbols/index'
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
import type { TArray } from '../../type/array/index'
|
||||
import type { TAsyncIterator } from '../../type/async-iterator/index'
|
||||
import type { TConstructor } from '../../type/constructor/index'
|
||||
import type { TFunction } from '../../type/function/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TIterator } from '../../type/iterator/index'
|
||||
import type { TImport } from '../../type/module/index'
|
||||
import type { TNot } from '../../type/not/index'
|
||||
import type { TObject } from '../../type/object/index'
|
||||
import type { TPromise } from '../../type/promise/index'
|
||||
import type { TRecord } from '../../type/record/index'
|
||||
import type { TRef } from '../../type/ref/index'
|
||||
import type { TThis } from '../../type/recursive/index'
|
||||
import type { TTuple } from '../../type/tuple/index'
|
||||
import type { TUnion } from '../../type/union/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// KindGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsTransform, IsSchema } from '../../type/guard/kind'
|
||||
// ------------------------------------------------------------------
|
||||
// ValueGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsUndefined } from '../guard/index'
|
||||
|
||||
// prettier-ignore
|
||||
function FromArray(schema: TArray, references: TSchema[]): boolean {
|
||||
return IsTransform(schema) || Visit(schema.items, references)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromAsyncIterator(schema: TAsyncIterator, references: TSchema[]): boolean {
|
||||
return IsTransform(schema) || Visit(schema.items, references)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromConstructor(schema: TConstructor, references: TSchema[]) {
|
||||
return IsTransform(schema) || Visit(schema.returns, references) || schema.parameters.some((schema) => Visit(schema, references))
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromFunction(schema: TFunction, references: TSchema[]) {
|
||||
return IsTransform(schema) || Visit(schema.returns, references) || schema.parameters.some((schema) => Visit(schema, references))
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromIntersect(schema: TIntersect, references: TSchema[]) {
|
||||
return IsTransform(schema) || IsTransform(schema.unevaluatedProperties) || schema.allOf.some((schema) => Visit(schema, references))
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromImport(schema: TImport, references: TSchema[]) {
|
||||
const additional = globalThis.Object.getOwnPropertyNames(schema.$defs).reduce((result, key) => [...result, schema.$defs[key as never]], [] as TSchema[])
|
||||
const target = schema.$defs[schema.$ref]
|
||||
return IsTransform(schema) || Visit(target, [...additional, ...references])
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromIterator(schema: TIterator, references: TSchema[]) {
|
||||
return IsTransform(schema) || Visit(schema.items, references)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromNot(schema: TNot, references: TSchema[]) {
|
||||
return IsTransform(schema) || Visit(schema.not, references)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromObject(schema: TObject, references: TSchema[]) {
|
||||
return (
|
||||
IsTransform(schema) ||
|
||||
Object.values(schema.properties).some((schema) => Visit(schema, references)) ||
|
||||
(
|
||||
IsSchema(schema.additionalProperties) && Visit(schema.additionalProperties, references)
|
||||
)
|
||||
)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromPromise(schema: TPromise, references: TSchema[]) {
|
||||
return IsTransform(schema) || Visit(schema.item, references)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromRecord(schema: TRecord, references: TSchema[]) {
|
||||
const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0]
|
||||
const property = schema.patternProperties[pattern]
|
||||
return IsTransform(schema) || Visit(property, references) || (IsSchema(schema.additionalProperties) && IsTransform(schema.additionalProperties))
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromRef(schema: TRef, references: TSchema[]) {
|
||||
if (IsTransform(schema)) return true
|
||||
return Visit(Deref(schema, references), references)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromThis(schema: TThis, references: TSchema[]) {
|
||||
if (IsTransform(schema)) return true
|
||||
return Visit(Deref(schema, references), references)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromTuple(schema: TTuple, references: TSchema[]) {
|
||||
return IsTransform(schema) || (!IsUndefined(schema.items) && schema.items.some((schema) => Visit(schema, references)))
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromUnion(schema: TUnion, references: TSchema[]) {
|
||||
return IsTransform(schema) || schema.anyOf.some((schema) => Visit(schema, references))
|
||||
}
|
||||
// prettier-ignore
|
||||
function Visit(schema: TSchema, references: TSchema[]): boolean {
|
||||
const references_ = Pushref(schema, references)
|
||||
const schema_ = schema as any
|
||||
if (schema.$id && visited.has(schema.$id)) return false
|
||||
if (schema.$id) visited.add(schema.$id)
|
||||
switch (schema[Kind]) {
|
||||
case 'Array':
|
||||
return FromArray(schema_, references_)
|
||||
case 'AsyncIterator':
|
||||
return FromAsyncIterator(schema_, references_)
|
||||
case 'Constructor':
|
||||
return FromConstructor(schema_, references_)
|
||||
case 'Function':
|
||||
return FromFunction(schema_, references_)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_)
|
||||
case 'Intersect':
|
||||
return FromIntersect(schema_, references_)
|
||||
case 'Iterator':
|
||||
return FromIterator(schema_, references_)
|
||||
case 'Not':
|
||||
return FromNot(schema_, references_)
|
||||
case 'Object':
|
||||
return FromObject(schema_, references_)
|
||||
case 'Promise':
|
||||
return FromPromise(schema_, references_)
|
||||
case 'Record':
|
||||
return FromRecord(schema_, references_)
|
||||
case 'Ref':
|
||||
return FromRef(schema_, references_)
|
||||
case 'This':
|
||||
return FromThis(schema_, references_)
|
||||
case 'Tuple':
|
||||
return FromTuple(schema_, references_)
|
||||
case 'Union':
|
||||
return FromUnion(schema_, references_)
|
||||
default:
|
||||
return IsTransform(schema)
|
||||
}
|
||||
}
|
||||
const visited = new Set<string>()
|
||||
/** Returns true if this schema contains a transform codec */
|
||||
export function HasTransform(schema: TSchema, references: TSchema[]): boolean {
|
||||
visited.clear()
|
||||
return Visit(schema, references)
|
||||
}
|
||||
31
src/value/transform/index.ts
Normal file
31
src/value/transform/index.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './decode'
|
||||
export * from './encode'
|
||||
export * from './has'
|
||||
29
src/value/value/index.ts
Normal file
29
src/value/value/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * as Value from './value'
|
||||
45
src/value/value/value.ts
Normal file
45
src/value/value/value.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/value
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export { Errors, ValueErrorIterator } from '../../errors/index'
|
||||
|
||||
export { Assert } from '../assert/index'
|
||||
export { Cast } from '../cast/index'
|
||||
export { Check } from '../check/index'
|
||||
export { Clean } from '../clean/index'
|
||||
export { Clone } from '../clone/index'
|
||||
export { Convert } from '../convert/index'
|
||||
export { Create } from '../create/index'
|
||||
export { Decode } from '../decode/index'
|
||||
export { Default } from '../default/index'
|
||||
export { Diff, Patch, Edit } from '../delta/index'
|
||||
export { Encode } from '../encode/index'
|
||||
export { Equal } from '../equal/index'
|
||||
export { Hash } from '../hash/index'
|
||||
export { Mutate, type Mutable } from '../mutate/index'
|
||||
export { Parse } from '../parse/index'
|
||||
Reference in New Issue
Block a user