import type * as T from '@traversable/registry' import type { Integer, Label, Natural as NaturalNumber, SchemaOptions as Options, TypeError, Unknown, } from '@traversable/registry' import { Array_isArray, applyOptions, escape, fn, getConfig, has, Math_max, Math_min, Number_isFinite, Number_isNatural, Number_isSafeInteger, Object_assign, Object_entries, Object_keys, parseArgs, parseKey, symbol, URI, } from '@traversable/registry' import type { Guard, Predicate as AnyPredicate, Typeguard, ValidateTuple, } from './types.js' import { conjunctiveIdentity, is as guard } from './predicates.js' import { boundedArray, boundedBigInt, boundedInteger, boundedNumber, boundedString, } from './bounded.js' export const isPredicate : (got: unknown) => got is { (): boolean; (x: S): x is T } = (got: unknown): got is never => typeof got === 'function' export type Source = T extends (_: infer S) => unknown ? S : unknown export type Target = never | S extends (_: any) => _ is infer T ? T : S export type Inline = never | of> export type Predicate = AnyPredicate | Schema export type Force = never | { -readonly [K in keyof T]: T[K] } export type Optional = never | string extends K ? string : K extends K ? S[K] extends bottom | optional ? K : never : never export type FirstOptionalItem = S extends readonly [infer H, ...infer T] ? optional extends H ? Offset['length'] : FirstOptionalItem : never export type Required = never | string extends K ? string : K extends K ? S[K] extends bottom | optional ? never : K : never export type Entry = [Schema] extends [S] ? Schema : S extends { def: unknown } ? S : S extends Guard ? of : S extends globalThis.BooleanConstructor ? nonnullable : S extends (() => infer _ extends boolean) ? BoolLookup[`${_}`] : S export type BoolLookup = never | { true: top false: bottom boolean: unknown_ } export type IntersectType = Todo extends readonly [infer H, ...infer T] ? IntersectType : Out export type TupleType = never | optional extends T[number & keyof T] ? T extends readonly [infer Head, ...infer Tail] ? [Head] extends [optional] ? Label< { [ix in keyof Out]: Out[ix]['_type' & keyof Out[ix]] }, { [ix in keyof T]: T[ix]['_type' & keyof T[ix]] } > : TupleType : never : { [ix in keyof T]: T[ix]['_type' & keyof T[ix]] } export type typeOf< T extends { _type?: unknown }, _ extends | T['_type'] = T['_type'] > = never | _ export interface LowerBound { readonly _type?: T (got: Unknown): got is T (got: unknown): got is T tag?: string def?: unknown } export interface Schema { readonly _type?: Fn['_type'] (got: Unknown): got is Fn['_type'] (got: unknown): got is Fn['_type'] tag?: Fn['tag'] def?: Fn['def'] } export type Type = F export type Unary = | eq | ref | array | record | optional | union | intersect | tuple | object_<{ [x: string]: Unary }> export type F = | Leaf | eq | ref | array | record | optional | union | intersect | tuple | object_<{ [x: string]: T }> export type Fixpoint = Leaf | Unary export interface Free extends T.HKT { [-1]: F } /** @internal */ const childToString = (x: unknown) => has('toString', (x) => typeof x === 'function')(x) ? x.toString() as string : 't.unknown' /** * @internal * TODO: move to registry? */ const serialize = (json: unknown) => { const go = (x: unknown): string => { switch (true) { default: return childToString(x) case x == null: return String(x) case x === true: return 'true' case x === false: return 'false' case typeof x === 'number': return `${x}` case typeof x === 'string': return `"${escape(x)}"` case typeof x === 'bigint': return `${x}n` case typeof x === 'symbol': return String(x) case Array_isArray(x): return x.length === 0 ? '[]' : `[${x.map(go).join(', ')}]` case !!x && typeof x === 'object': { const xs = Object_entries(x).map(([k, v]) => `${parseKey(k)}: ${go(v)}`) return xs.length === 0 ? `{}` : `{ ${xs.join(', ')} }` } } } return go(json) } export interface of { readonly _type: Target (got: this['_type'] | Unknown): got is this['_type'] (got: unknown): got is this['_type'] tag: URI.inline def: S } export function of(typeguard: S): Entry export function of(typeguard: S): of> export function of(typeguard: (Guard) & { tag?: URI.inline, def?: Guard }) { typeguard.def = typeguard return Object_assign(typeguard, of.prototype) } export namespace of { export let prototype = { tag: URI.inline } export type type> = never | T export function def(guard: T): of /* v8 ignore next 6 */ export function def(guard: T) { function InlineSchema(got: unknown) { return guard(got) } InlineSchema.tag = URI.inline InlineSchema.def = guard return InlineSchema } } export interface top { readonly _type: unknown, tag: URI.top, def: unknown } export interface bottom { readonly _type: never, tag: URI.bottom, def: never } export interface invalid<_Err> extends TypeError<''>, never_ {} export { void_ as void } interface void_ { readonly _type: void (got: this['_type'] | Unknown): got is void (got: unknown): got is void tag: URI.void def: void } const void_ = function VoidSchema(got: unknown) { return got === void 0 } void_.tag = URI.void void_.def = void 0 void_.toString = () => `t.void` export { never_ as never } interface never_ { readonly _type: never (got: unknown): got is never tag: URI.never def: never } const never_ = function NeverSchema(got: unknown) { return false } never_.tag = URI.never never_.def = void 0 as never never_.toString = () => `t.never` export { unknown_ as unknown } interface unknown_ { readonly _type: unknown (got: unknown): got is unknown tag: URI.unknown def: unknown } const unknown_ = function UnknownSchema(got: unknown) { return true } unknown_.tag = URI.unknown unknown_.def = void 0 unknown_.toString = () => 't.unknown' export { any_ as any } interface any_ { readonly _type: any (got: unknown): got is any tag: URI.any def: any } const any_ = function AnySchema(got: unknown) { return true } any_.tag = URI.any any_.def = void 0 any_.toString = () => 't.any' export { null_ as null } interface null_ { readonly _type: null (got: null | Unknown): got is null (got: unknown): got is null tag: URI.null def: this['_type'] } const null_ = function NullSchema(got: unknown) { return got === null } null_.tag = URI.null null_.def = null null_.toString = () => 't.null' export { undefined_ as undefined } interface undefined_ { readonly _type: undefined (got: undefined | Unknown): got is undefined (got: unknown): got is undefined tag: URI.undefined def: undefined } const undefined_ = function UndefinedSchema(got: unknown) { return got === undefined } undefined_.tag = URI.undefined undefined_.def = void 0 undefined_.toString = () => 't.undefined' export { symbol_ as symbol } interface symbol_ { readonly _type: symbol (got: symbol | Unknown): got is symbol (got: unknown): got is symbol tag: URI.symbol def: symbol } const symbol_ = function SymbolSchema(got: unknown) { return typeof got === 'symbol' } symbol_.tag = URI.symbol symbol_.def = Symbol() symbol_.toString = () => 't.symbol' export { boolean_ as boolean } interface boolean_ { readonly _type: boolean (got: boolean | Unknown): got is boolean (got: unknown): got is boolean tag: URI.boolean def: boolean } const boolean_ = function BooleanSchema(got: unknown) { return got === true || got === false } boolean_.tag = URI.boolean boolean_.def = false boolean_.toString = () => 't.boolean' export { integer } interface integer extends integer.methods { readonly _type: number (got: number | Unknown): got is number (got: unknown): got is number tag: URI.integer def: this['_type'] minimum?: number maximum?: number } declare namespace integer { type Min = [Self] extends [{ maximum: number }] ? integer.between<[min: X, max: Self['maximum']]> : integer.min type Max = [Self] extends [{ minimum: number }] ? integer.between<[min: Self['minimum'], max: X]> : integer.max interface methods { min>(minimum: Min): integer.Min max>(maximum: Max): integer.Max between, Max extends Integer>(minimum: Min, maximum: Max): integer.between<[min: Min, max: Max]> } interface min extends integer { minimum: Min } interface max extends integer { maximum: Max } interface between extends integer { minimum: Bounds[0], maximum: Bounds[1] } } const integer = function IntegerSchema(got: unknown) { return Number_isSafeInteger(got) } integer.tag = URI.integer integer.def = 0 integer.toString = () => 't.integer' integer.min = function integer_min(minimum) { const { maximum } = this const STR = Number_isSafeInteger(maximum) ? `t.integer.between(${minimum}, ${maximum})` : `t.integer.min(${minimum})` return Object_assign(boundedInteger({ minimum, maximum }), this, { minimum, toString() { return STR } }) } integer.max = function integer_max(maximum) { const { minimum } = this const STR = Number_isSafeInteger(minimum) ? `t.integer.between(${minimum}, ${maximum})` : `t.integer.max(${maximum})` return Object_assign(boundedInteger({ minimum, maximum }), this, { maximum, toString() { return STR } }) } integer.between = function integerBetween(min, max) { const minimum = Math_min(min, max) const maximum = Math_max(min, max) const STR = `t.integer.between(${minimum}, ${maximum})` return Object_assign( boundedInteger({ minimum, maximum }), this, { minimum, maximum, toString() { return STR } }, ) } export { bigint_ as bigint } interface bigint_ extends bigint_.methods { readonly _type: bigint (got: bigint | Unknown): got is bigint (got: unknown): got is bigint tag: URI.bigint def: this['_type'] minimum?: bigint maximum?: bigint } declare namespace bigint_ { type Min = [Self] extends [{ maximum: bigint }] ? bigint_.between<[min: X, max: Self['maximum']]> : bigint_.min type Max = [Self] extends [{ minimum: bigint }] ? bigint_.between<[min: Self['minimum'], max: X]> : bigint_.max interface methods extends Typeguard { min(minimum: Min): bigint_.Min max(maximum: Max): bigint_.Max between(minimum: Min, maximum: Max): bigint_.between<[min: Min, max: Max]> } interface min extends bigint_ { minimum: Min } interface max extends bigint_ { maximum: Max } interface between extends bigint_ { minimum: Bounds[0], maximum: Bounds[1] } } const bigint_ = function BigIntSchema(got: unknown) { return typeof got === 'bigint' } bigint_.tag = URI.bigint bigint_.def = 0n bigint_.toString = () => 't.bigint' bigint_.min = function bigint_min(minimum) { const { maximum } = this const STR = typeof maximum === 'bigint' ? `t.bigint.between(${minimum}n, ${maximum}n)` : `t.bigint.min(${minimum}n)` return Object_assign(boundedBigInt({ minimum, maximum }), this, { minimum, toString() { return STR } }) } bigint_.max = function bigint_max(maximum) { const { minimum } = this const STR = typeof minimum === 'bigint' ? `t.bigint.between(${minimum}n, ${maximum}n)` : `t.bigint.max(${maximum}n)` return Object_assign(boundedBigInt({ minimum, maximum }), this, { maximum, toString() { return STR } }) } bigint_.between = function bigint_between(min, max) { const minimum = (max < min ? max : min) const maximum = (max < min ? min : max) const STR = `t.bigint.between(${minimum}n, ${maximum}n)` return Object_assign(boundedBigInt({ minimum, maximum }), this, { minimum, maximum, toString() { return STR } }) } export { number_ as number } interface number_ extends number_.methods { readonly _type: number (got: number | Unknown): got is number (got: unknown): got is number tag: URI.number def: number minimum?: number maximum?: number exclusiveMinimum?: number exclusiveMaximum?: number } declare namespace number_ { interface methods { min(minimum: Min): number_.Min max(maximum: Max): number_.Max moreThan(moreThan: Min): ExclusiveMin lessThan(lessThan: Max): ExclusiveMax between(minimum: Min, maximum: Max): number_.between<[min: Min, max: Max]> } type Min = [Self] extends [{ exclusiveMaximum: number }] ? number_.minStrictMax<[min: X, lessThan: Self['exclusiveMaximum']]> : [Self] extends [{ maximum: number }] ? number_.between<[min: X, max: Self['maximum']]> : number_.min type Max = [Self] extends [{ exclusiveMinimum: number }] ? number_.maxStrictMin<[moreThan: Self['exclusiveMinimum'], max: X]> : [Self] extends [{ minimum: number }] ? number_.between<[min: Self['minimum'], max: X]> : number_.max type ExclusiveMin = [Self] extends [{ exclusiveMaximum: number }] ? number_.strictlyBetween<[moreThan: X, lessThan: Self['exclusiveMaximum']]> : [Self] extends [{ maximum: number }] ? number_.maxStrictMin<[moreThan: X, max: Self['maximum']]> : number_.moreThan type ExclusiveMax = [Self] extends [{ exclusiveMinimum: number }] ? number_.strictlyBetween<[moreThan: Self['exclusiveMinimum'], lessThan: X]> : [Self] extends [{ minimum: number }] ? number_.minStrictMax<[min: Self['minimum'], lessThan: X]> : number_.lessThan interface min extends number_ { minimum: Min } interface max extends number_ { maximum: Max } interface moreThan extends number_ { exclusiveMinimum: Min } interface lessThan extends number_ { exclusiveMaximum: Max } interface between extends number_ { minimum: Bounds[0], maximum: Bounds[1] } interface minStrictMax extends number_ { minimum: Bounds[0], exclusiveMaximum: Bounds[1] } interface maxStrictMin extends number_ { maximum: Bounds[1], exclusiveMinimum: Bounds[0] } interface strictlyBetween extends number_ { exclusiveMinimum: Bounds[0], exclusiveMaximum: Bounds[1] } } const number_ = function NumberSchema(got: unknown) { return Number_isFinite(got) } number_.tag = URI.number number_.def = 0 number_.toString = () => 't.number' number_.min = function number_min(minimum) { const { maximum, exclusiveMaximum } = this const PREV = Number_isFinite(exclusiveMaximum) ? `.lessThan(${exclusiveMaximum})` : `` const STR = Number_isFinite(maximum) ? `t.number.between(${minimum}, ${maximum})` : `t.number${PREV}.min(${minimum})` return Object_assign(boundedNumber({ minimum, maximum, exclusiveMaximum }), this, { minimum, toString() { return STR } }) } number_.max = function number_max(maximum) { const { minimum, exclusiveMinimum } = this const PREV = Number_isFinite(exclusiveMinimum) ? `.moreThan(${exclusiveMinimum})` : `` const STR = Number_isFinite(minimum) ? `t.number.between(${minimum}, ${maximum})` : `t.number${PREV}.max(${maximum})` return Object_assign(boundedNumber({ maximum, minimum, exclusiveMinimum }), this, { maximum, toString() { return STR } }) } number_.moreThan = function number_moreThan(exclusiveMinimum) { const { maximum, exclusiveMaximum } = this const PREV = Number_isFinite(maximum) ? `.max(${maximum})` : Number_isFinite(exclusiveMaximum) ? `.lessThan(${exclusiveMaximum})` : `` const STR = `t.number${PREV}.moreThan(${exclusiveMinimum})` return Object_assign(boundedNumber({ exclusiveMinimum, maximum, exclusiveMaximum }), this, { exclusiveMinimum, toString() { return STR } }) } number_.lessThan = function number_lessThan(exclusiveMaximum) { const { minimum, exclusiveMinimum } = this const PREV = Number_isFinite(minimum) ? `.min(${minimum})` : Number_isFinite(exclusiveMinimum) ? `.moreThan(${exclusiveMinimum})` : `` const STR = `t.number${PREV}.lessThan(${exclusiveMaximum})` return Object_assign(boundedNumber({ exclusiveMaximum, minimum, exclusiveMinimum }), this, { exclusiveMaximum, toString() { return STR } }) } number_.between = function number_between(min, max) { const minimum = Math_min(min, max) const maximum = Math_max(min, max) const STR = `t.number.between(${minimum}, ${maximum})` return Object_assign(boundedNumber({ minimum, maximum }), this, { minimum, maximum, toString() { return STR } }) } export { string_ as string } interface string_ extends string_.methods { _type: string (got: string | Unknown): got is string (got: unknown): got is string tag: URI.string def: this['_type'] minLength?: number maxLength?: number } declare namespace string_ { interface methods { min>(minLength: Min): string_.Min max>(maxLength: Max): string_.Max between< Min extends NaturalNumber, Max extends NaturalNumber >( minLength: Min, maxLength: Max ): string_.between<[min: Min, max: Max]> } type Min = [Self] extends [{ maxLength: number }] ? string_.between<[min: Min, max: Self['maxLength']]> : string_.min type Max = [Self] extends [{ minLength: number }] ? string_.between<[min: Self['minLength'], max: Max]> : string_.max interface min extends string_ { minLength: Min } interface max extends string_ { maxLength: Max } interface between extends string_ { minLength: Bounds[0], maxLength: Bounds[1] } } const string_ = function StringSchema(got: unknown) { return typeof got === 'string' } string_.tag = URI.string string_.def = '' string_.toString = () => 't.string' string_.min = function string_min(minLength) { const { maxLength } = this const STR = Number_isNatural(maxLength) ? `t.string.between(${Math_min(minLength, maxLength)}, ${Math_max(maxLength, minLength)})` : `t.string.min(${minLength})` return Object_assign(boundedString({ minimum: minLength, maximum: maxLength }), this, { minLength, toString() { return STR } }) } string_.max = function string_max(maxLength) { const { minLength } = this const STR = Number_isNatural(minLength) ? `t.string.between(${Math_min(minLength, maxLength)}, ${Math_max(maxLength, minLength)})` : `t.string.max(${maxLength})` return Object_assign(boundedString({ minimum: minLength, maximum: maxLength }), this, { maxLength, toString() { return STR } }) } string_.between = function string_between(min, max) { const minLength = Math_min(min, max) const maxLength = Math_max(min, max) const STR = `t.string.between(${minLength}, ${maxLength})` return Object_assign(boundedString({ minimum: minLength, maximum: maxLength }), this, { minLength, maxLength, toString() { return STR } }) } export { nonnullable } interface nonnullable extends Typeguard<{}> { readonly _type: {} (got: {} | Unknown): got is {} (got: unknown): got is {} tag: URI.nonnullable def: {} } const nonnullable = function NonNullableSchema(got: unknown) { return got != null } nonnullable.tag = URI.nonnullable nonnullable.def = {} nonnullable.toString = () => 't.nonnullable' export function eq>(value: V, options?: Options): eq> export function eq(value: V, options?: Options): eq export function eq(value: V, options?: Options): eq { return eq.def(value, options) } export interface eq { readonly _type: V (got: V | Unknown): got is V (got: unknown): got is V tag: URI.eq def: V } export namespace eq { export let prototype = { tag: URI.eq } export function def(value: T, options?: Options): eq /* v8 ignore next 1 */ export function def(x: T, $?: Options) { const options = applyOptions($) const eqGuard = isPredicate(x) ? x : (y: unknown) => options.eq.equalsFn(x, y) const STR = `t.eq(${serialize(x)})` function EqSchema(got: unknown) { return eqGuard(got) } EqSchema.def = x return Object_assign(EqSchema, { toString() { return STR } }, eq.prototype) } } export function optional(schema: S): optional export function optional(schema: S): optional> export function optional(schema: S): optional { return optional.def(schema) } export interface optional { readonly _type: undefined | S['_type' & keyof S] (got: this['_type'] | Unknown): got is this['_type'] (got: unknown): got is this['_type'] tag: URI.optional def: S [symbol.optional]: number } export namespace optional { export let prototype = { tag: URI.optional } export type type = never | T export function def(x: T): optional export function def(x: T) { const optionalGuard = isPredicate(x) ? guard.optional(x) : conjunctiveIdentity function OptionalSchema(got: unknown) { return optionalGuard(got) } const STR = `t.optional(${childToString(x)})` OptionalSchema.tag = URI.optional OptionalSchema.def = x OptionalSchema[symbol.optional] = 1 return Object_assign(OptionalSchema, { toString() { return STR } }, optional.prototype) } export const is : (got: unknown) => got is optional /* v8 ignore next 1 */ = has('tag', eq(URI.optional)) as never } export interface ref { readonly _type: S['_type' & keyof S] (got: this['_type'] | Unknown): got is this['_type'] (got: Unknown): got is this['_type'] tag: URI.ref id: Id def: S toString(): Id } export function ref(schema: S, id: Id): ref { return ref.def(schema, id) } export namespace ref { export let prototype = { tag: URI.ref } export function def(schema: S, id: Id): ref export function def(schema: S, id: Id) { const predicate = has('got', (x): x is (_: unknown) => _ is unknown => typeof x === 'function')(schema) ? schema.got : (_: unknown): _ is unknown => true function RefSchema(got: unknown): got is unknown { return predicate(got) } RefSchema.def = schema RefSchema.toString = () => id RefSchema.id = id return Object_assign(RefSchema, prototype) } } export function array(schema: S, readonly: 'readonly'): ReadonlyArray export function array(schema: S): array export function array(schema: S): array> export function array(schema: S): array { return array.def(schema) } export interface array extends array.methods { readonly _type: S['_type' & keyof S][] (got: this['_type'] | Unknown): got is this['_type'] (got: unknown): got is this['_type'] tag: URI.array def: S minLength?: number maxLength?: number } export declare namespace array { interface methods { min>(minLength: Min): array.Min max>(maxLength: Max): array.Max between< Min extends NaturalNumber, Max extends NaturalNumber >( minLength: Min, maxLength: Max ): array.between<[min: Min, max: Max], S> } type Min = [Self] extends [{ maxLength: number }] ? array.between<[min: Min, max: Self['maxLength']], Self['def' & keyof Self]> : array.min type Max = [Self] extends [{ minLength: number }] ? array.between<[min: Self['minLength'], max: Max], Self['def' & keyof Self]> : array.max interface min extends array { minLength: Min } interface max extends array { maxLength: Max } interface between extends array { minLength: Bounds[0], maxLength: Bounds[1] } type type = never | S['_type' & keyof S][] } export namespace array { export let prototype = { tag: URI.array } as array export function def(x: S, prev?: array): array export function def(x: S, prev?: unknown): array export function def(x: S, prev?: array): array /* v8 ignore next 1 */ export function def(x: S, prev?: unknown): {} { const predicate = isPredicate(x) ? x : conjunctiveIdentity function ArraySchema(got: unknown) { return Array_isArray(got) && got.every(predicate) } const CHILD_STR = childToString(x) const STR = `t.array(${CHILD_STR})` ArraySchema.min = function array_min(minLength: number) { const { maxLength } = this const STR = Number_isNatural(maxLength) ? `t.array(${CHILD_STR}).between(${minLength}, ${maxLength})` : `t.array(${CHILD_STR}).min(${minLength})` return Object_assign(boundedArray({ minimum: minLength, maximum: maxLength }, predicate), this, { minLength, toString() { return STR } }) } ArraySchema.max = function array_max(maxLength: number) { const { minLength } = this const STR = Number_isNatural(minLength) ? `t.array(${CHILD_STR}).between(${minLength}, ${maxLength})` : `t.array(${CHILD_STR}).max(${maxLength})` return Object_assign(boundedArray({ minimum: minLength, maximum: maxLength }, predicate), this, { maxLength, toString() { return STR } }) } ArraySchema.between = function array_between(min: number, max: number) { const minLength = Math_min(min, max) const maxLength = Math_max(min, max) const STR = `t.array(${CHILD_STR}).between(${minLength}, ${maxLength})` return Object_assign(boundedArray({ minimum: minLength, maximum: maxLength }, predicate), this, { minLength, maxLength, toString() { return STR } }) } ArraySchema.def = x ArraySchema._type = void 0 as never if (has('minLength', Number_isNatural)(prev)) ArraySchema.minLength = prev.minLength if (has('maxLength', Number_isNatural)(prev)) ArraySchema.maxLength = prev.maxLength return Object.assign(ArraySchema, { toString() { return STR } }, array.prototype) } } export const readonlyArray: { (schema: S, readonly: 'readonly'): ReadonlyArray (schema: S): ReadonlyArray> } = array export interface ReadonlyArray { readonly _type: readonly S['_type' & keyof S][] (got: this['_type'] | Unknown): got is this['_type'] (got: unknown): got is this['_type'] tag: URI.array def: S } export function record(schema: S): record export function record(schema: S): record> export function record(schema: S) { return record.def(schema) } export interface record { readonly _type: Record (got: this['_type'] | Unknown): got is this['_type'] (got: unknown): got is this['_type'] tag: URI.record def: S } export namespace record { export let prototype = { tag: URI.record } as record export type type> = never | T export function def(x: T): record /* v8 ignore next 1 */ export function def(x: T) { const recordGuard = isPredicate(x) ? guard.record(x) : guard.anyObject const STR = `t.record(${childToString(x)})` function RecordGuard(got: unknown) { return recordGuard(got) } RecordGuard.def = x return Object.assign(RecordGuard, { toString() { return STR } }, record.prototype) } } export function union(...schemas: S): union export function union }>(...schemas: S): union export function union(...schemas: S): {} { return union.def(schemas) } export interface union { readonly _type: S[number & keyof S]['_type' & keyof S[number & keyof S]] (got: this['_type'] | Unknown): got is this['_type'] (got: unknown): got is this['_type'] tag: URI.union def: S } export namespace union { export let prototype = { tag: URI.union } as union export type type = never | T export function def(xs: T): union /* v8 ignore next 1 */ export function def(xs: T) { const anyOf = xs.every(isPredicate) ? guard.union(xs) : guard.unknown const STR = `t.union(${xs.map(childToString).join(', ')})` function UnionSchema(got: unknown) { return anyOf(got) } UnionSchema.def = xs return Object_assign(UnionSchema, { toString() { return STR } }, union.prototype) } } export function intersect(...schemas: S): intersect export function intersect }>(...schemas: S): intersect export function intersect(...schemas: S) { return intersect.def(schemas) } export interface intersect { _type: IntersectType (got: this['_type'] | Unknown): got is this['_type'] (got: unknown): got is this['_type'] tag: URI.intersect def: S } export namespace intersect { export let prototype = { tag: URI.intersect } as intersect export type type> = never | T export function def(xs: readonly [...T]): intersect /* v8 ignore next 1 */ export function def(xs: readonly [...T]) { const allOf = xs.every(isPredicate) ? guard.intersect(xs) : guard.unknown const STR = `t.intersect(${xs.map(childToString).join(', ')})` function IntersectSchema(got: unknown) { return allOf(got) } IntersectSchema.def = xs return Object_assign(IntersectSchema, { toString() { return STR } }, intersect.prototype) } } export { tuple } function tuple }>(...schemas: tuple.validate): tuple, T>> function tuple(...schemas: tuple.validate): tuple, S>> function tuple }>(...args: [...schemas: tuple.validate, options: Options]): tuple, T>> function tuple(...args: [...schemas: tuple.validate, options: Options]): tuple, S>> function tuple }>(...schemas: tuple.validate): tuple, T>> function tuple(...schemas: tuple.validate): tuple, S>> function tuple(...args: | [...S] | [...S, Options]) { return tuple.def(...parseArgs(getConfig().schema, args)) } interface tuple { readonly _type: TupleType (got: this['_type'] | Unknown): got is this['_type'] (got: unknown): got is this['_type'] tag: URI.tuple def: S opt: FirstOptionalItem } namespace tuple { export let prototype = { tag: URI.tuple } as tuple export function def(xs: readonly [...T], $?: Options, opt_?: number): tuple /* v8 ignore next 1 */ export function def(xs: readonly [...T], $: Options = getConfig().schema, opt_?: number) { const opt = opt_ || xs.findIndex(optional.is) const options = { ...$, minLength: $.optionalTreatment === 'treatUndefinedAndOptionalAsTheSame' ? -1 : xs.findIndex(optional.is) } satisfies tuple.InternalOptions const tupleGuard = guard.tuple(options)(xs as never) const STR = `t.tuple(${xs.map(childToString).join(', ')})` function TupleSchema(got: unknown) { return tupleGuard(got) } TupleSchema.def = xs TupleSchema.opt = opt return Object_assign(TupleSchema, { toString() { return STR } }, tuple.prototype) } } declare namespace tuple { type type = never | TupleType type validate = ValidateTuple> type from = TypeError extends V[number] ? { [I in keyof V]: V[I] extends TypeError ? invalid> : V[I] } : T type InternalOptions = { minLength?: number } } export { object_ as object } function object_< S extends { [x: string]: Schema }, T extends { [K in keyof S]: Entry } >(schemas: S, options?: Options): object_ function object_< S extends { [x: string]: Predicate }, T extends { [K in keyof S]: Entry } >(schemas: S, options?: Options): object_ // function object_(schemas: S, options?: Options) { return object_.def(schemas, options) } interface object_ { readonly _type: object_.type (got: this['_type'] | Unknown): got is this['_type'] (got: unknown): got is this['_type'] tag: URI.object def: S opt: Optional[] req: Required[] } namespace object_ { export let prototype = { tag: URI.object } as object_ export type Opt = symbol.optional extends keyof S[K] ? never : K export type Req = symbol.optional extends keyof S[K] ? K : never export type type = Force< & { [K in keyof S as Opt]-?: S[K]['_type' & keyof S[K]] } & { [K in keyof S as Req]+?: S[K]['_type' & keyof S[K]] } > export function def(xs: T, $?: Options, opt?: string[]): object_ export function def(xs: T, $?: Options, opt?: string[]): object_ /* v8 ignore next 1 */ export function def( xs: T, $: Options = getConfig().schema, opt_?: string[] ): {} { const keys = Object_keys(xs) const opt = Array_isArray(opt_) ? opt_ : keys.filter((k) => optional.is(xs[k])) const req = keys.filter((k) => !optional.is(xs[k])) const STR = keys.length === 0 ? 't.object({})' : `t.object({ ${keys.map((k) => `${parseKey(k)}: ${childToString(xs[k])}`).join(', ')} })` const objectGuard = guard.object(xs, applyOptions($)) function ObjectSchema(got: unknown) { return objectGuard(got) } ObjectSchema.def = xs ObjectSchema.opt = opt ObjectSchema.req = req return Object_assign(ObjectSchema, { toString() { return STR } }, object_.prototype) } } export type Leaf = typeof leaves[number] export type LeafTag = Leaf['tag'] export type Nullary = typeof nullaries[number] export type NullaryTag = Nullary['tag'] export type Boundable = typeof boundables[number] export type BoundableTag = Boundable['tag'] export type Tag = typeof tags[number] export type TypeName = T.TypeName export type UnaryTag = typeof unaryTags[number] export type UnaryTypeName = T.TypeName const hasTag = has('tag', (tag) => typeof tag === 'string') export const nullaries = [unknown_, never_, any_, void_, undefined_, null_, symbol_, boolean_] export const nullaryTags = nullaries.map((x) => x.tag) export const isNullaryTag = (got: unknown): got is NullaryTag => nullaryTags.includes(got as never) export const isNullary = (got: unknown): got is Nullary => hasTag(got) && nullaryTags.includes(got.tag as never) export const boundables = [integer, bigint_, number_, string_] export const boundableTags = boundables.map((x) => x.tag) export const isBoundableTag = (got: unknown): got is BoundableTag => boundableTags.includes(got as never) export const isBoundable = (got: unknown): got is Boundable => hasTag(got) && boundableTags.includes(got.tag as never) export const leaves = [...nullaries, ...boundables] export const leafTags = leaves.map((leaf) => leaf.tag) export const isLeaf = (got: unknown): got is Leaf => hasTag(got) && leafTags.includes(got.tag as never) export const unaryTags = [URI.optional, URI.eq, URI.ref, URI.array, URI.record, URI.tuple, URI.union, URI.intersect, URI.object] as const export const tags = [...leafTags, ...unaryTags] export const isUnary = (got: unknown): got is Unary => hasTag(got) && unaryTags.includes(got.tag as never) export type AnyCoreSchema = | Schema | Nullary | Boundable export const isCore = (got: unknown): got is Schema => hasTag(got) && tags.includes(got.tag as never) export const defaultIndex = { path: Array.of(), depth: 0 } satisfies globalThis.Required export type Index = { path: (keyof any)[], depth: number } export declare namespace Functor { export { Index } } export const Functor: T.Functor = { map(f) { return (x) => { switch (true) { default: return fn.exhaustive(x) case isLeaf(x): return x case x.tag === URI.enum as never: return x as never case x.tag === URI.eq: return eq.def(x.def as never) as never case x.tag === URI.ref: return ref.def(f(x.def), x.id) case x.tag === URI.array: return array.def(f(x.def), x) case x.tag === URI.record: return record.def(f(x.def)) case x.tag === URI.optional: return optional.def(f(x.def)) case x.tag === URI.tuple: return tuple.def(fn.map(x.def, f)) case x.tag === URI.object: return object_.def(fn.map(x.def, f), {}, x.opt) case x.tag === URI.union: return union.def(fn.map(x.def, f)) case x.tag === URI.intersect: return intersect.def(fn.map(x.def, f)) } } } } export type IndexedFunctor = T.Functor.Ix export const IndexedFunctor: IndexedFunctor = { ...Functor, mapWithIndex(f) { return (x, ix) => { switch (true) { default: return fn.exhaustive(x) case isLeaf(x): return x case x.tag === URI.enum as never: return x as never case x.tag === URI.eq: return eq.def(x.def as never) as never case x.tag === URI.ref: return ref.def(f(x.def, { ...ix, path: [...ix.path, symbol.ref] }, x), x.id) case x.tag === URI.array: return array.def(f(x.def, { ...ix, path: [...ix.path, symbol.array], depth: ix.depth + 1 }, x), x) case x.tag === URI.record: return record.def(f(x.def, { ...ix, path: [...ix.path, symbol.record], depth: ix.depth + 1 }, x)) case x.tag === URI.optional: return optional.def(f(x.def, { ...ix, path: [...ix.path, symbol.optional], depth: ix.depth + 1 }, x)) case x.tag === URI.tuple: return tuple.def(fn.map(x.def, (y, iy) => f(y, { ...ix, path: [...ix.path, iy], depth: ix.depth + 1 }, x)), x.opt) case x.tag === URI.object: return object_.def(fn.map(x.def, (y, iy) => f(y, { ...ix, path: [...ix.path, iy], depth: ix.depth + 1 }, x)), {}, x.opt) case x.tag === URI.union: return union.def(fn.map(x.def, (y, iy) => f(y, { ...ix, path: [...ix.path, symbol.union, iy], depth: ix.depth + 1 }, x))) case x.tag === URI.intersect: return intersect.def(fn.map(x.def, (y, iy) => f(y, { ...ix, path: [...ix.path, symbol.intersect, iy], depth: ix.depth + 1 }, x))) } } } } export const unfold = fn.ana(Functor) export const fold = fn.catamorphism(IndexedFunctor, { depth: 0, path: [] }) export const foldWithIndex = fn.cataIx(IndexedFunctor)