import { z } from 'zod' import type * as T from '@traversable/registry' import { fn, Object_keys } from '@traversable/registry' import * as Bounds from './generator-bounds.js' import { PromiseSchemaIsUnsupported } from './utils.js' import type { AnyTypeName } from './typename.js' export type Tag = byTag[keyof byTag] export type byTag = typeof byTag export const byTag = { any: 10, boolean: 15, date: 20, file: 25, nan: 30, never: 35, null: 40, symbol: 45, undefined: 50, unknown: 55, void: 60, int: 100, bigint: 150, number: 200, string: 250, enum: 500, literal: 550, template_literal: 600, array: 1000, nonoptional: 1500, nullable: 2000, optional: 2500, readonly: 3000, set: 3500, success: 4000, catch: 5000, default: 5500, prefault: 5600, intersection: 6000, map: 6500, record: 7000, object: 7500, tuple: 8000, union: 8500, pipe: 9000, custom: 9500, transform: 10_000, lazy: 10_500, /** @deprecated */ promise: 100_000, } as const satisfies Record export function invert>(x: T): { [K in keyof T as T[K]]: K } export function invert(x: Record) { return Object_keys(x).reduce((acc, k) => (acc[x[k]] = k, acc), {} as typeof x) } export type bySeed = typeof bySeed export const bySeed = invert(byTag) // ^? export type Seed = & Seed.TerminalMap & Seed.BoundableMap & Seed.ValueMap & Seed.UnaryMap export declare namespace Seed { type Fixpoint = [ tag: Tag, children?: unknown, bounds?: Seed.Array[2] ] type F = | Seed.Nullary | Seed.Unary type Nullary = | Seed.Terminal | Seed.Boundable | Seed.Value type Unary = | Seed.Array | Seed.Record | Seed.Object | Seed.Tuple | Seed.Union | Seed.NonOptional | Seed.Optional | Seed.Nullable | Seed.Readonly | Seed.Set | Seed.Success | Seed.Catch | Seed.Default | Seed.Prefault | Seed.Map | Seed.Pipe | Seed.Custom | Seed.Transform | Seed.Lazy | Seed.Intersection | Seed.Promise interface Free extends T.HKT { [-1]: Seed.F } //////////////// /// nullary type Any = [any: byTag['any']] type Boolean = [boolean: byTag['boolean']] type Date = [date: byTag['date']] type File = [file: byTag['file']] type NaN = [NaN: byTag['nan']] type Never = [never: byTag['never']] type Null = [null: byTag['null']] type Symbol = [symbol: byTag['symbol']] type Undefined = [undefined: byTag['undefined']] type Unknown = [unknown: byTag['unknown']] type Void = [void: byTag['void']] type Terminal = TerminalMap[keyof TerminalMap] type TerminalMap = { any: Any boolean: Boolean date: Date file: File nan: NaN never: Never null: Null symbol: Symbol undefined: Undefined unknown: Unknown void: Void } //////////////// /// boundable type Integer = [int: byTag['int'], bounds?: Bounds.int] type BigInt = [bigint: byTag['bigint'], bounds?: Bounds.bigint] type Number = [number: byTag['number'], bounds?: Bounds.number] type String = [string: byTag['string'], bounds?: Bounds.string] type Boundable = BoundableMap[keyof BoundableMap] type BoundableMap = { int: Integer bigint: BigInt number: Number string: String } //////////////// /// value type Enum = [enum_: byTag['enum'], value: { [x: string]: number | string }] type Literal = [literal: byTag['literal'], value: z.core.util.Literal] type TemplateLiteral = [templateLiteral: byTag['template_literal'], value: TemplateLiteral.Node[]] namespace TemplateLiteral { type Node = | T.Showable | Seed.Boolean | Seed.Null | Seed.Undefined | Seed.Integer | Seed.Number | Seed.BigInt | Seed.String | Seed.Literal | Seed.Nullable | Seed.Optional } type Value = ValueMap[keyof ValueMap] type ValueMap = { enum: Enum literal: Literal template_literal: TemplateLiteral } //////////////// /// unary type Array = [array: byTag['array'], element: T, bounds?: Bounds.array] type NonOptional = [nonOptional: byTag['nonoptional'], innerType: T] type Optional = [optional: byTag['optional'], innerType: T] type Nullable = [nullable: byTag['nullable'], innerType: T] type Readonly = [readonly: byTag['readonly'], innerType: T] type Set = [set: byTag['set'], valueType: T] type Success = [success: byTag['success'], innerType: T] type Catch = [catch_: byTag['catch'], innerType: T] type Default = [default_: byTag['default'], innerType: T] type Prefault = [prefault: byTag['prefault'], innerType: T] type UnaryMap = { array: Seed.Array record: Seed.Record object: Seed.Object tuple: Seed.Tuple union: Seed.Union nonoptional: Seed.NonOptional optional: Seed.Optional nullable: Seed.Nullable readonly: Seed.Readonly set: Seed.Set success: Seed.Success catch: Seed.Catch default: Seed.Default prefault: Seed.Prefault map: Seed.Map pipe: Seed.Pipe custom: Seed.Custom transform: Seed.Transform lazy: Seed.Lazy intersection: Seed.Intersection promise: Seed.Promise } type Composite = | Seed.Array | Seed.Record | Seed.Object | Seed.Tuple type fromComposite = { [byTag.array]: unknown[] [byTag.record]: globalThis.Record [byTag.object]: { [x: string]: unknown } [byTag.tuple]: unknown[] } type schemaFromComposite = { [byTag.array]: z.ZodArray [byTag.record]: z.ZodRecord [byTag.object]: z.ZodObject [byTag.tuple]: z.ZodTuple } //////////////// /// composite type Object = [object: byTag['object'], shape: [K: string, V: T][]] type Union = [union: byTag['union'], members: T[]] type Tuple = [tuple: byTag['tuple'], items: T[]] //////////////// /// binary type Map = [map: byTag['map'], def: [keyType: T, valueType: T]] type Record = [record: byTag['record'], valueType: T] type Intersection = [intersection: byTag['intersection'], def: [left: T, right: T]] //////////////// /// special type Pipe = [pipe: byTag['pipe'], def: [in_: T, out: T]] type Custom = [custom: byTag['custom'], def: T] type Transform = [transform: byTag['transform'], def: T] type Lazy = [lazy: byTag['lazy'], getter: () => T] //////////////// /// deprecated /** @deprecated */ type Promise = [promise: byTag['promise'], innerType: T] } export const Functor: T.Functor.Ix> = { map(f) { return (x) => { switch (true) { default: return x case x[0] === byTag.any: return x case x[0] === byTag.boolean: return x case x[0] === byTag.date: return x case x[0] === byTag.file: return x case x[0] === byTag.nan: return x case x[0] === byTag.never: return x case x[0] === byTag.null: return x case x[0] === byTag.symbol: return x case x[0] === byTag.undefined: return x case x[0] === byTag.unknown: return x case x[0] === byTag.void: return x case x[0] === byTag.int: return x case x[0] === byTag.bigint: return x case x[0] === byTag.number: return x case x[0] === byTag.string: return x case x[0] === byTag.enum: return x case x[0] === byTag.literal: return x case x[0] === byTag.template_literal: return x case x[0] === byTag.array: return [x[0], f(x[1]), x[2]] case x[0] === byTag.nonoptional: return [x[0], f(x[1])] case x[0] === byTag.nullable: return [x[0], f(x[1])] case x[0] === byTag.optional: return [x[0], f(x[1])] case x[0] === byTag.readonly: return [x[0], f(x[1])] case x[0] === byTag.set: return [x[0], f(x[1])] case x[0] === byTag.success: return [x[0], f(x[1])] case x[0] === byTag.catch: return [x[0], f(x[1])] case x[0] === byTag.default: return [x[0], f(x[1])] case x[0] === byTag.prefault: return [x[0], f(x[1])] case x[0] === byTag.intersection: return [x[0], [f(x[1][0]), f(x[1][1])]] case x[0] === byTag.map: return [x[0], [f(x[1][0]), f(x[1][1])]] case x[0] === byTag.record: return [x[0], f(x[1])] case x[0] === byTag.object: return [x[0], x[1].map(([k, v]) => [k, f(v)] satisfies [any, any])] case x[0] === byTag.tuple: return [x[0], x[1].map(f)] case x[0] === byTag.union: return [x[0], x[1].map(f)] case x[0] === byTag.pipe: return [x[0], [f(x[1][0]), f(x[1][1])]] case x[0] === byTag.custom: return [x[0], f(x[1])] case x[0] === byTag.transform: return [x[0], f(x[1])] case x[0] === byTag.lazy: return [x[0], () => f(x[1]())] case x[0] === byTag.promise: { return PromiseSchemaIsUnsupported('Functor') } } } }, mapWithIndex(f) { return (x, isProperty) => { switch (true) { default: return x case x[0] === byTag.any: return x case x[0] === byTag.boolean: return x case x[0] === byTag.date: return x case x[0] === byTag.file: return x case x[0] === byTag.nan: return x case x[0] === byTag.never: return x case x[0] === byTag.null: return x case x[0] === byTag.symbol: return x case x[0] === byTag.undefined: return x case x[0] === byTag.unknown: return x case x[0] === byTag.void: return x case x[0] === byTag.int: return x case x[0] === byTag.bigint: return x case x[0] === byTag.number: return x case x[0] === byTag.string: return x case x[0] === byTag.enum: return x case x[0] === byTag.literal: return x case x[0] === byTag.template_literal: return x case x[0] === byTag.array: return [x[0], f(x[1], false, x), x[2]] case x[0] === byTag.nonoptional: return [x[0], f(x[1], false, x)] case x[0] === byTag.nullable: return [x[0], f(x[1], false, x)] case x[0] === byTag.optional: return [x[0], f(x[1], isProperty, x)] case x[0] === byTag.readonly: return [x[0], f(x[1], false, x)] case x[0] === byTag.set: return [x[0], f(x[1], false, x)] case x[0] === byTag.success: return [x[0], f(x[1], false, x)] case x[0] === byTag.catch: return [x[0], f(x[1], false, x)] case x[0] === byTag.default: return [x[0], f(x[1], false, x)] case x[0] === byTag.prefault: return [x[0], f(x[1], false, x)] case x[0] === byTag.intersection: return [x[0], [f(x[1][0], false, x), f(x[1][1], false, x)]] case x[0] === byTag.map: return [x[0], [f(x[1][0], false, x), f(x[1][1], false, x)]] case x[0] === byTag.record: return [x[0], f(x[1], false, x)] case x[0] === byTag.tuple: return [x[0], x[1].map((_) => f(_, false, x))] case x[0] === byTag.union: return [x[0], x[1].map((_) => f(_, false, x))] case x[0] === byTag.pipe: return [x[0], [f(x[1][0], false, x), f(x[1][1], false, x)]] case x[0] === byTag.custom: return [x[0], f(x[1], false, x)] case x[0] === byTag.transform: return [x[0], f(x[1], false, x)] case x[0] === byTag.lazy: return [x[0], () => f(x[1](), false, x)] case x[0] === byTag.object: return [x[0], x[1].map(([k, v]) => [k, f(v, true, x)] satisfies [any, any])] case x[0] === byTag.promise: return PromiseSchemaIsUnsupported('Functor') } } } } export const fold = fn.catamorphism(Functor, false)