diff --git a/packages/derive-codec/src/install.ts b/packages/derive-codec/src/install.ts index 8dd38fac..aecad47c 100644 --- a/packages/derive-codec/src/install.ts +++ b/packages/derive-codec/src/install.ts @@ -6,25 +6,25 @@ import { bind } from './bind.js' void bind() declare module '@traversable/schema' { - interface t_LowerBound extends pipe> { } - interface t_never extends pipe { } - interface t_unknown extends pipe { } - interface t_void extends pipe { } - interface t_any extends pipe { } - interface t_null extends pipe { } - interface t_undefined extends pipe { } - interface t_symbol extends pipe { } - interface t_boolean extends pipe { } - interface t_integer extends pipe { } - interface t_bigint extends pipe { } - interface t_number extends pipe { } - interface t_string extends pipe { } - interface t_eq extends pipe> { } - interface t_optional extends pipe> { } - interface t_array extends pipe> { } - interface t_record extends pipe> { } - interface t_union extends pipe> { } - interface t_intersect extends pipe> { } - interface t_tuple extends pipe> { } - interface t_object extends pipe> { } + interface t_LowerBound extends pipe> {} + interface t_never extends pipe {} + interface t_unknown extends pipe {} + interface t_void extends pipe {} + interface t_any extends pipe {} + interface t_null extends pipe {} + interface t_undefined extends pipe {} + interface t_symbol extends pipe {} + interface t_boolean extends pipe {} + interface t_integer extends pipe {} + interface t_bigint extends pipe {} + interface t_number extends pipe {} + interface t_string extends pipe {} + interface t_eq extends pipe> {} + interface t_optional extends pipe> {} + interface t_array extends pipe> {} + interface t_record extends pipe> {} + interface t_union extends pipe> {} + interface t_intersect extends pipe> {} + interface t_tuple extends pipe> {} + interface t_object extends pipe> {} } diff --git a/packages/registry/src/either.ts b/packages/registry/src/either.ts new file mode 100644 index 00000000..7584191a --- /dev/null +++ b/packages/registry/src/either.ts @@ -0,0 +1,30 @@ +import type { Bind, TypeConstructor as F } from './hkt.js' + +export type Either = Left | Right +export type Any = Either +export interface Right extends Bind { _tag: 'Either::Right', right: T } +export interface Left extends Bind { _tag: 'Either::Left', left: T } + +export function isEither(x: unknown): x is Either { + return !!x && typeof x === 'object' && '_tag' in x && (x._tag === 'Either::Left' || x._tag === 'Either::Right') +} +export function isRight(x: Either): x is Right +export function isRight(x: unknown): x is Right +export function isRight(x: unknown) { return isEither(x) && x._tag === 'Either::Right' } +export function isLeft(x: Either): x is Left +export function isLeft(x: unknown): x is Left +export function isLeft(x: unknown) { return isEither(x) && x._tag === 'Either::Left' } +export function left(x: L): Either { return { _tag: 'Either::Left', left: x } } +export function right(x: R): Either { return { _tag: 'Either::Right', right: x } } +export function alt(x: Either, y: Either): Either { return isRight(x) ? x : y } + +export function fromPredicate(predicate: (x: S) => x is T): (x: S) => Either +export function fromPredicate(predicate: (x: S) => x is T, onFalse: (x: S) => E): (x: S) => Either +export function fromPredicate(predicate: (x: T) => boolean, onFalse: (x: T) => E): (x: T) => Either +export function fromPredicate(predicate: (x: T) => boolean, onFalse?: (x: T) => E) { + return (x: T) => predicate(x) ? right(x) : left(!onFalse ? x : onFalse(x)) +} + +export const either + : (x: Either, onLeft: (a: A) => T, onRight: (b: B) => T) => T + = (x, onLeft, onRight) => isLeft(x) ? onLeft(x.left) : onRight(x.right) diff --git a/packages/registry/src/exports.ts b/packages/registry/src/exports.ts index f3a4c8ab..34c8ced7 100644 --- a/packages/registry/src/exports.ts +++ b/packages/registry/src/exports.ts @@ -2,11 +2,21 @@ export type * from './satisfies.js' export type * from './types.js' export type { Match } from './types.js' +export * as Either from './either.js' +export type Either = import('./either.js').Either + +export * as Option from './option.js' +export type Option = import('./option.js').Option + export * from './globalThis.js' export * from './predicate.js' +export type { Homomorphism } from './function.js' export * as fn from './function.js' +export { fix, unfix } from './functor.js' export * as Print from './print.js' +export { hom } from './hom.js' + export { symbol_ as symbol } import * as symbol_ from './symbol.js' type symbol_ = typeof symbol_[keyof typeof symbol_] @@ -15,14 +25,14 @@ export { URI_ as URI } import * as URI_ from './uri.js' type URI_ = typeof URI_[keyof typeof URI_] -export * as Equal from './equals.js' export type Equal = import('./types.js').Equal +export * as Equal from './equals.js' export { NS, SCOPE } from './uri.js' export { VERSION } from './version.js' export type { TypeName } from './typeName.js' export { typeName } from './typeName.js' -export { get, has } from './has.js' +export { get, has, set } from './has.js' export { parseArgs } from './parseArgs.js' export { findPaths } from './path.js' export { diff --git a/packages/registry/src/function.ts b/packages/registry/src/function.ts index 099c4448..98ed58f7 100644 --- a/packages/registry/src/function.ts +++ b/packages/registry/src/function.ts @@ -1,5 +1,5 @@ -import type { Functor, Kind, HKT } from './types.js' -import type { FiniteArray, FiniteObject, NonFiniteArray, NonFiniteObject } from './satisfies.js' +import type { Algebra, Box, Boxed, F, Coalgebra, RAlgebra, IndexedAlgebra, Functor } from './types.js' +import type { EmptyObject, FiniteArray, FiniteObject, NonFiniteArray, NonFiniteObject } from './satisfies.js' import { Array_isArray, Object_create, Object_keys } from './globalThis.js' export const identity @@ -11,6 +11,44 @@ export const const_ : (x: T) => (y?: S) => T = (x) => () => x +/** + * ## {@link liftA2 `fn.liftA2`} + * + * @example + * import type { Box, Boxed, F } from '@traversable/registry' + * import { fn } from '@traversable/registry' + * + * declare const fx: Box + * declare const fy: Box + * + * const ap2 = liftA2(fx, fy) + * // const ap2: (f: (a: number, b: string) => C) => Array + * + * const xy = ap2((x, y) => ({ x, y })) + * // ^? const xy: Array<{ x: number, y: string }> + */ + +export type liftA2< + Fx extends Box.any, + Fy extends Box.any, + A extends Boxed, + B extends Boxed, + F = Box.from +> = never | { (f: (a: A, b: B) => C): Box } + +/** + * @internal + * + * TODO: need to figure out how to get `F.map` -- probably will need to + * have the user explicitly pass in the Functor they want to use + */ +declare function liftA2< + Fx extends Box.any, + Fy extends Box.any, + A extends Boxed, + B extends Boxed +>(fx: Fx, fy: Fy): liftA2 + /** * ## {@link ana `fn.ana`} * @@ -38,16 +76,11 @@ export const const_ * - {@link cata `fn.cata`} */ -export function ana(Functor: Functor): - (coalgebra: Functor.Coalgebra) - => (expr: S) - => Kind - -/// impl. -export function ana(F: Functor) { - return (coalgebra: Functor.Coalgebra) => { - return function loop(expr: T): Kind { - return F.map(loop)(coalgebra(expr)) +export function ana(F: Functor): (g: Coalgebra) => (expr: S) => Box +export function ana(F: Functor) { + return (g: Coalgebra) => { + return function loop(expr: T): Box { + return F.map(loop)(g(expr)) } } } @@ -61,82 +94,64 @@ export function ana(F: Functor) { * - the [Wikipedia page](https://en.wikipedia.org/wiki/Catamorphism) on catamorphisms * - {@link ana `ana`} */ -export function cata(Functor: Functor): - (algebra: Functor.Algebra) - => (term: S) - => T - -export function cata(F: Functor) { - return (algebra: Functor.Algebra) => { - return function loop(term: Kind): T { - return algebra(F.map(loop)(term)) +export function cata(F: Functor): (g: Algebra) => (x: S) => T +export function cata(F: Functor) { + return (g: Algebra) => { + return function loop(x: Box): T { + return g(F.map(loop)(x)) } } } -export function cataIx - (IxFunctor: Functor.Ix): - (algebra: Functor.IndexedAlgebra) - => (term: S, ix: Ix) - => T +export function cataIx(Ix: Functor.Ix): + (g: IndexedAlgebra) => (x: S, ix: Ix) => T export function cataIx - (IxFunctor: Functor.Ix, initialIndex: Ix): - (algebra: Functor.IndexedAlgebra) - => (term: S, ix?: Ix) + (Ix: Functor.Ix, initialIndex: Ix): + (g: IndexedAlgebra) + => (x: S, ix?: Ix) => T -export function cataIx(F: Functor.Ix, initialIndex?: Ix) { - return (g: Functor.IndexedAlgebra) => { - return function loop(x: Kind, ix: Ix): T { - return g(F.mapWithIndex(loop)(x, ix ?? initialIndex!), ix) +export function cataIx(Ix: Functor.Ix, initialIndex?: Ix) { + return (g: IndexedAlgebra) => { + return function loop(x: Box, ix: Ix): T { + return g(Ix.mapWithIndex(loop)(x, ix ?? initialIndex!), ix) } } } -export function hylo - (F: Functor): - ( - algebra: Functor.Algebra, - coalgebra: Functor.Coalgebra - ) => (s: S) - => T - -export function hylo(Functor: Functor) { +export function hylo(F: Functor): (f: Algebra, h: Coalgebra) => (s: S) => T +export function hylo(F: Functor) { return ( - algebra: Functor.Algebra, - coalgebra: Functor.Coalgebra + f: Algebra, + h: Coalgebra ) => (s: S) => { - const g = Functor.map(hylo(Functor)(algebra, coalgebra)) - return algebra(g(coalgebra(s))) + const g = F.map(hylo(F)(f, h)) + return f(g(h(s))) } } -export function para - (F: Functor): - (ralgebra: Functor.RAlgebra) - => (term: S) - => T - -export function para(F: Functor) { - return (ralgebra: Functor.RAlgebra) => { - function fanout(term: T): Kind { return [term, para(F)(ralgebra)(term)] } - return flow( - F.map(fanout), - ralgebra, - ) +export function para(F: Functor): (g: RAlgebra) => (term: S) => T +export function para(F: Functor) { + return (g: RAlgebra) => { + function fanout(x: T): Box { return [x, para(F)(g)(x)] } + return flow(F.map(fanout), g) } } +export const fanout + : (ab: (a: A) => B, ac: (a: A) => C) => (a: A) => [B, C] + = (ab, ac) => (a) => [ab(a), ac(a)] + export function paraIx - (F: Functor.Ix): + (F: Functor.Ix): (ralgebra: Functor.RAlgebra) => (term: S, ix: Ix) => T -export function paraIx(F: Functor.Ix) { +export function paraIx(F: Functor.Ix) { return (ralgebra: Functor.RAlgebra) => { - function fanout(term: T, ix: Ix): Kind { return [term, paraIx(F)(ralgebra)(term, ix)] } + function fanout(term: T, ix: Ix): Box { return [term, paraIx(F)(ralgebra)(term, ix)] } return flow( F.mapWithIndex(fanout), ralgebra, @@ -146,13 +161,26 @@ export function paraIx(F: Functor.Ix) { type MapFn> = (src: S[K], k: K, xs: S) => T type KeyOf = T extends readonly unknown[] ? number & keyof T : keyof T +export interface MapTo extends Box.any { [-1]: [this[0]] extends [MapTo[0] & infer T] ? { [K in keyof T]: S } : never } +export type Lift = unknown extends S ? Homomorphism : (xs: S) => { [K in keyof S]: T } +export type Map = Box, S> +export interface Homomorphism extends MapTo { + (x: R): Box + >(x: R): Box +} + + +type Top = [{} | null | undefined] extends [T] ? unknown : never +type Bottom = [T] extends [never] ? unknown : never -export function map(f: MapFn): Lift +export function map, T>(f: MapFn): Homomorphism +export function map(f: MapFn): (xs: S) => { [K in keyof S]: T } +export function map, T>(src: S, mapfn: (x: S[keyof S], ix: string, xs: S) => T): Map export function map, T>(src: S, mapfn: (x: S[number], ix: number, xs: S) => T): Map export function map, T>(src: S, mapfn: (x: S[keyof S], ix: keyof S, xs: S) => T): Map export function map, T>(src: S, mapfn: (x: S[number], ix: number, xs: S) => T): Map export function map, T>(src: S, mapfn: (x: S[keyof S], ix: string, xs: S) => T): Map -export function map(src: S, mapfn: (x: S[keyof S], ix: string, xs: S) => T): Map +export function map(src: S, mapfn: (x: S[keyof S], ix: keyof S, xs: S) => T): Map export function map( ...args: [mapfn: any] | [src: any, mapfn: any] ): {} { @@ -168,18 +196,6 @@ export function map( } } -export type Map = Kind, S> -export type Lift = unknown extends S ? Homomorphism : (xs: S) => { [K in keyof S]: T } - -export interface MapTo extends HKT { - [-1]: [this[0]] extends [MapTo[0] & infer T] ? { [K in keyof T]: S } : never -} - -export interface Homomorphism extends MapTo { - (x: R): Kind - >(x: R): Kind -} - export const exhaustive : <_ extends never = never>(..._: _[]) => _ = (..._) => { throw Error('Exhaustive match failed') } @@ -188,20 +204,18 @@ export const assertIsNotCalled : <_ extends void = void>(..._: _[]) => never = (..._) => { throw Error('Exhaustive match failed') } -export const fanout - : (ab: (a: A) => B, ac: (a: A) => C) => (a: A) => [B, C] - = (ab, ac) => (a) => [ab(a), ac(a)] - export function flow(ab: (...a: A) => B): (...a: A) => B export function flow(ab: (...a: A) => B, bc: (b: B) => C): (...a: A) => C export function flow(ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...a: A) => D export function flow(ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E): (...a: A) => E +export function flow(ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F): (...a: A) => E export function flow( ...args: | [ab: Function] | [ab: Function, bc: Function] | [ab: Function, bc: Function, cd: Function] | [ab: Function, bc: Function, cd: Function, de: Function] + | [ab: Function, bc: Function, cd: Function, de: Function, ef: Function] ) { switch (true) { default: return void 0 @@ -526,3 +540,14 @@ function pipe( } } } + +// export function apo(F: Bifunctor): (g: RCoalgebra) => (x: T) => Either> +// export function apo(F: Bifunctor) { +// return (g: RCoalgebra) => { +// function fanin(x: Either, T>): Fix +// function fanin(x: Either, T>) { return Either.isLeft(x) ? x : apo(F)(g)(x.right) } +// function loop(x: T): Fix +// function loop(x: T) { return F.map(loop)(fanin(g(x))) } +// return loop +// } +// } diff --git a/packages/registry/src/functor.ts b/packages/registry/src/functor.ts index 041c0eaf..090e8f02 100644 --- a/packages/registry/src/functor.ts +++ b/packages/registry/src/functor.ts @@ -1,16 +1,44 @@ -import type { Kind, HKT } from './hkt.js' +import type { Bind, Box, Boxed, Kind, HKT, HKT2 } from './hkt.js' +import type { Either } from './either.js' +import type { typeclass } from './symbol.js' -export type Algebra = never | { (term: Kind): T } -export type IndexedAlgebra = never | { (term: Kind, ix: Ix): T } -export type Coalgebra = never | { (expr: T): Kind } -export type RAlgebra = never | { (term: Kind.Product): T } -export type IndexedRAlgebra = never | { (term: Kind.Product, ix: Ix): T } +export class Fix { constructor(public readonly value: Box>) {} } +export function fix(value: Box>): Fix { return new Fix(value) } +export function unfix(term: Fix): Box> { return term.value } + +export type IndexedAlgebra = never | { (x: Box, ix: Ix): T } + +export type Algebra = never | { (x: Box): T } + +export type Coalgebra = never | { (x: T): Box } + +export type RAlgebra = never | { (x: Box, T>>): T } + +export type RCoalgebra = never | { (x: T): Box>> } + +export type IndexedRAlgebra = never | { (x: Kind.Product, ix: Ix): T } + +export type Product = never | [first: X, second: Y] + +export interface Sum extends HKT<[unknown, unknown]> { [-1]: Either> } + +export interface Profunctor { + dimap( + from: (s: S) => A, + to: (b: B) => T, + ): (F: Box) => Box +} + +export interface Bifunctor = HKT<[unknown, unknown]>> { + map(f: (s: S) => T): (F: Kind) => Kind + mapLeft(f: (d: D) => E): (F: Kind) => Kind +} /** * ## {@link Functor `Functor`} */ -export interface Functor> { - map(f: (s: S) => T): (F: Kind) => Kind +export interface Functor> extends Bind { + map(f: (s: S) => T): (F: Box) => Box } export declare namespace Functor { @@ -21,6 +49,8 @@ export declare namespace Functor { RAlgebra, IndexedRAlgebra, } + export type infer = T extends Functor ? Boxed : never + /** * ## {@link Ix `Functor.Ix`} * @@ -42,7 +72,7 @@ export declare namespace Functor { * 3. provide the index to the caller * 4. define its semantics, and communicate them clearly */ - export interface Ix> extends Functor { - mapWithIndex(f: (s: S, ix: Ix) => T): (F: Kind, ix: Ix) => Kind + export interface Ix> extends Functor { + mapWithIndex(f: (s: S, ix: Ix) => T): (F: Box, ix: Ix) => Box } } diff --git a/packages/registry/src/globalThis.ts b/packages/registry/src/globalThis.ts index baea4df8..259f4c64 100644 --- a/packages/registry/src/globalThis.ts +++ b/packages/registry/src/globalThis.ts @@ -37,6 +37,10 @@ export const Number_isInteger : (x: unknown) => x is number = globalThis.Number.isInteger +export const Number_parseInt + : (numeric: string, radix?: number) => number + = globalThis.Number.parseInt + /** * ## {@link Number_isNaN `Number_isNaN`} * @@ -64,7 +68,10 @@ export const Number_isNatural = (x: unknown): x is number => Number_isSafeIntege export const Object_assign = globalThis.Object.assign -export const Object_create = globalThis.Object.create +export const Object_create: { + (x: null): T + (x: T): T +} = globalThis.Object.create export const Object_entries = globalThis.Object.entries @@ -92,7 +99,7 @@ type SplitString : { [E in (Out[number]) as E[0]]: E[1] } export const Object_values - : (x: T) => T[keyof T][] + : (x: T) => T[(keyof T & (number | string))][] = globalThis.Object.values export const Object_hasOwn diff --git a/packages/registry/src/has.ts b/packages/registry/src/has.ts index 1163729d..ad79b6e4 100644 --- a/packages/registry/src/has.ts +++ b/packages/registry/src/has.ts @@ -1,3 +1,4 @@ +import { Object_assign } from '@traversable/registry/globalThis' import * as symbol from './symbol.js' /** @internal */ @@ -23,8 +24,8 @@ function hasOwn(u: unknown, key: keyof any): u is { [x: string]: unknown } { * sentinel-like to differentiate between the separate cases of * "path not found" and "value at path was undefined" */ -export function get(x: unknown, ks: (keyof any)[]) { - let out = x +export function get(x: T, ks: (keyof any)[]) { + let out: unknown = x let k: keyof any | undefined while ((k = ks.shift()) !== undefined) { if (hasOwn(out, k)) void (out = out[k]) @@ -34,6 +35,32 @@ export function get(x: unknown, ks: (keyof any)[]) { return out } +get.lazy = function get_lazy(...ks: (keyof any)[]) { return (x: T) => get(x, ks) } + +export function set(x: T, k: keyof any, v: unknown): T { + return !!x && (typeof x === 'object' || typeof x === 'function') + ? has(k)(x) && x[k] === v ? x + : Object_assign({}, x, { [k]: v }) + : x +} + +set.lazy = function set_lazy(k: keyof any): (v: V) => (x: T) => T { return (v) => (x) => set(x, k, v) } + +export function modify(x: T, k: K, f: (y: T[K]) => T[K]): T { + if (!has(k)(x)) { + return x + } else { + const prev = x[k] + const next = f(prev) + if (prev === next) return x + else return Object_assign(x, { [k]: next }) + } +} + +modify.lazy = function modify_lazy(k: K, f: (y: T[K]) => T[K]): (x: T) => T { + return (x) => modify(x, k, f) +} + export function fromPath(ks: (keyof any)[], seed: unknown) { let out: unknown = seed let k: keyof any | undefined diff --git a/packages/registry/src/hkt.ts b/packages/registry/src/hkt.ts index b628abff..83d2a2ce 100644 --- a/packages/registry/src/hkt.ts +++ b/packages/registry/src/hkt.ts @@ -1,56 +1,85 @@ import type { newtype } from './newtype.js' +import type { Either } from './either.js' +import type { Option } from './option.js' +import type { Tuple } from './tuple.js' +import { typeclass as kind } from './symbol.js' -export interface HKT extends newtype<{ [0]: I, [-1]: O }> { _applied?: unknown } +export interface HKT extends newtype<{ [0]: I, [-1]: O }> {} +export interface HKT2 extends newtype<{ [0]: { [0]: A, [1]: B }, [-1]: Z }> {} export type Kind = (F & { [0]: T })[-1] +export type Kind2 = (F & { [0]: { [0]: S, [1]: T } })[-1] +export type Bind = { [kind]?: T } -export declare namespace Kind { - export type Product = Kind, T>> - export type Coproduct = Kind, T>> - - export type infer = G extends - & { [0]: G[0 & keyof G]; _applied: G["_applied" & keyof G] } - & (infer F extends HKT) - ? F - : never +// @ts-expect-error +export type Box = (F & { [0]: T })[-1] +export type Boxed = Box.infer +export declare namespace Box { export { Any as any } } +export declare namespace Box { + type Any = Bind & Partial + // @ts-expect-error + type from> = _ extends Box.any ? _ : never + /** + * ## {@link bind `Box.bind`} + * + * Given a `Box` of type {@link F `F`}, instantiate a new `Box` of + * type {@link F `F`} bound to {@link T `T`}. + */ + type bind + // @ts-expect-error + = (Kind.infer & { [0]: T })[-1] + type infer = F extends Box.bind ? T : never + type of = Bind> } -export type Tuple = never - | [first: F, second: S] - -export type Either = never - | [tag: false, left: L] - | [tag: true, right: R] +export declare namespace Kind { export { Any as any, Box as lax } } +export declare namespace Kind { + type Product = Box, T>> + type Coproduct = Box, T>> + type infer = Exclude + type of = F extends Kind.apply ? T : never + type Any = Bind + // @ts-expect-error + type apply> = (_ & { [0]: T })[-1] +} -export interface Comparator { (left: T, right: T): number } -export interface Equal { (left: T, right: T): boolean; } -export interface Dictionary { [x: string]: T } +export type Identity = T -interface Record extends newtype> { } -interface Array extends newtype { } -interface ReadonlyArray extends newtype { } +export interface Comparator extends Bind { (left: T, right: T): number } +export interface Equal extends Bind { (left: T, right: T): boolean } +export interface Record extends Bind { [x: string]: T } +export interface Array extends newtype, Bind {} +export interface ReadonlyArray extends newtype, Bind {} +export interface Duplicate extends newtype<{ [0]: T, [1]: T }>, Bind { [Symbol.iterator](): Iterator } export declare namespace Type { export { + Array, Comparator, + Duplicate, + Either, Equal, - Dictionary, - Array, - Record, + Identity, + Option, ReadonlyArray, + Record, + Tuple, } } export interface Const extends HKT { [-1]: T } -export interface Identity extends HKT { [-1]: this[0] } - -export declare namespace TypeConstructor { export { Const, Identity } } -export declare namespace TypeConstructor { - interface Eq extends HKT { [-1]: Type.Equal } - interface Comparator extends HKT { [-1]: Type.Comparator } - interface Duplicate extends HKT { [-1]: [this[0], this[0]] } - interface Array extends HKT { [-1]: Type.Array } - interface ReadonlyArray extends HKT { [-1]: Type.ReadonlyArray } - interface Record extends HKT<[keyof any, unknown]> { [-1]: Type.Record } - interface Dictionary extends HKT { [-1]: Type.Dictionary } + +export type { F as TypeConstructor } +export declare namespace F { export { Const } } +export declare namespace F { + interface Array extends HKT, Bind> { [-1]: Type.Array } + interface Comparator extends HKT, Bind> { [-1]: Type.Comparator } + interface Duplicate extends HKT, Bind> { [-1]: Type.Duplicate } + interface Either extends HKT<[unknown, unknown]>, Bind> { [-1]: Type.Either } + interface Equal extends HKT, Bind> { [-1]: Type.Equal } + interface Identity extends HKT, Bind> { [-1]: Type.Identity } + interface Option extends HKT, Bind> { [-1]: Type.Option } + interface ReadonlyArray extends HKT, Bind> { [-1]: Type.ReadonlyArray } + interface Record extends HKT, Bind> { [-1]: Type.Record } + interface Tuple extends HKT<[unknown, unknown]>, Bind> { [-1]: Type.Tuple } } diff --git a/packages/registry/src/hom.ts b/packages/registry/src/hom.ts new file mode 100644 index 00000000..08476bda --- /dev/null +++ b/packages/registry/src/hom.ts @@ -0,0 +1,442 @@ +import type { HKT, Kind } from './hkt.js' + +const Functions = { + [0]: (ks: readonly [string]) => + (v0: any) => + ({ [ks[0]]: v0 }), + [1]: (ks: readonly [string, string]) => + (v0: any) => + (v1: any) => + ({ [ks[0]]: v0, [ks[1]]: v1 }), + [2]: (ks: readonly [string, string, string]) => + (v0: any) => + (v1: any) => + (v2: any) => + ({ [ks[0]]: v0, [ks[1]]: v1, [ks[2]]: v2 }), + [3]: + (ks: readonly [string, string, string, string]) => + (v0: any) => + (v1: any) => + (v2: any) => + (v3: any) => + ({ [ks[0]]: v0, [ks[1]]: v1, [ks[2]]: v2, [ks[3]]: v3 }), + [4]: + (ks: readonly [string, string, string, string, string]) => + (v0: any) => + (v1: any) => + (v2: any) => + (v3: any) => + (v4: any) => + ({ [ks[0]]: v0, [ks[1]]: v1, [ks[2]]: v2, [ks[3]]: v3, [ks[4]]: v4 }), + [-1]: + (ks: readonly string[]) => (...args: any[]) => { + const len = ks.length + let out: { [x: string]: any } = {} + for (let i = 0; i < len; i++) out[ks[i]] = args[i] + return out + } +} + +function curried(f: Function, n: number, acc: ReadonlyArray) { + return function (x: unknown) { + const combined = Array(acc.length + 1) + for (let i = 0; i < acc.length; i++) { + combined[i] = acc[i] + } + combined[acc.length] = x + return n === 0 ? f.apply(null, combined) : curried(f, n - 1, combined) + } +} + +/** @internal */ +const isIndexOf = (x: number, xs: unknown): x is -1 => !!xs && typeof xs === 'object' && x in xs + +export function hom(ks: readonly [...KS]): hom +export function hom(ks: readonly string[]) { + const len = ks.length - 1 + return isIndexOf(len, Functions) + ? Functions[len](ks) + : curried(Functions[-1](ks), len, []) +} + +export type FromEntries = never | { [E in T as E[0]]: E[1] } + +export type NonFinite = never | { (x: unknown): { (y: unknown): { [K in T]: any } } } + +export type hom< + KS extends string[], + Ix = KS['length'], + T extends + | Ix extends keyof Functions ? Functions[Ix] : never + = Ix extends keyof Functions ? Functions[Ix] : never +> = [T] extends [never] + ? NonFinite + : Kind + +export interface Args extends HKT { + [0x00]: [] + [0x01]: [...this[0x00], k01: string] + [0x02]: [...this[0x01], k02: string] + [0x03]: [...this[0x02], k03: string] + [0x04]: [...this[0x03], k04: string] + [0x05]: [...this[0x04], k05: string] + [0x06]: [...this[0x05], k06: string] + [0x07]: [...this[0x06], k07: string] + [0x08]: [...this[0x07], k08: string] + [0x09]: [...this[0x08], k09: string] + [0x0a]: [...this[0x09], k10: string] + [0x0b]: [...this[0x0a], k11: string] + [0x0c]: [...this[0x0b], k12: string] + [0x0d]: [...this[0x0c], k13: string] + [0x0e]: [...this[0x0d], k14: string] + [0x0f]: [...this[0x0e], k15: string] +} + +interface Functions { + [0x00]: Function00 + [0x01]: Function01 + [0x02]: Function02 + [0x03]: Function03 + [0x04]: Function04 + [0x05]: Function05 + [0x06]: Function06 + [0x07]: Function07 + [0x08]: Function08 + [0x09]: Function09 + [0x0a]: Function10 + [0x0b]: Function11 + [0x0c]: Function12 + [0x0d]: Function13 + [0x0e]: Function14 + [0x0f]: Function15 +} + +interface Function00 extends HKT { [-1]: () => {} } +interface Function01 extends HKT { [-1]: (a: A) => FromEntries<[this[0x00][0x00], A]> } +interface Function02 extends HKT { + [-1]: + (a: A) => + (b: B) => + FromEntries< + | [this[0x00][0x00], A] + | [this[0x00][0x01], B] + > +} + +interface Function03 extends HKT { + [-1]: + (a: A) => + (b: B) => + (c: C) => + FromEntries< + | [this[0x00][0x00], A] + | [this[0x00][0x01], B] + | [this[0x00][0x02], C] + > +} + +interface Function04 extends HKT { + [-1]: + (a: A) => + (b: B) => + (c: C) => + (d: D) => + FromEntries< + | [this[0x00][0x00], A] + | [this[0x00][0x01], B] + | [this[0x00][0x02], C] + | [this[0x00][0x03], D] + > +} + + +interface Function05 extends HKT { + [-1]: + (a: A) => + (b: B) => + (c: C) => + (d: D) => + (e: E) => + FromEntries< + | [this[0x00][0x00], A] + | [this[0x00][0x01], B] + | [this[0x00][0x02], C] + | [this[0x00][0x03], D] + | [this[0x00][0x04], E] + > +} + + +interface Function06 extends HKT { + [-1]: + (a: A) => + (b: B) => + (c: C) => + (d: D) => + (e: E) => + (f: F) => + FromEntries< + | [this[0x00][0x00], A] + | [this[0x00][0x01], B] + | [this[0x00][0x02], C] + | [this[0x00][0x03], D] + | [this[0x00][0x04], E] + | [this[0x00][0x05], F] + > +} + + +interface Function07 extends HKT { + [-1]: + (a: A) => + (b: B) => + (c: C) => + (d: D) => + (e: E) => + (f: F) => + (g: G) => + FromEntries< + | [this[0x00][0x00], A] + | [this[0x00][0x01], B] + | [this[0x00][0x02], C] + | [this[0x00][0x03], D] + | [this[0x00][0x04], E] + | [this[0x00][0x05], F] + | [this[0x00][0x06], G] + > +} + + +interface Function08 extends HKT { + [-1]: + (a: A) => + (b: B) => + (c: C) => + (d: D) => + (e: E) => + (f: F) => + (g: G) => + (h: H) => + FromEntries< + | [this[0x00][0x00], A] + | [this[0x00][0x01], B] + | [this[0x00][0x02], C] + | [this[0x00][0x03], D] + | [this[0x00][0x04], E] + | [this[0x00][0x05], F] + | [this[0x00][0x06], G] + | [this[0x00][0x07], H] + > +} + + +interface Function09 extends HKT { + [-1]: + (a: A) => + (b: B) => + (c: C) => + (d: D) => + (e: E) => + (f: F) => + (g: G) => + (h: H) => + (i: I) => + FromEntries< + | [this[0x00][0x00], A] + | [this[0x00][0x01], B] + | [this[0x00][0x02], C] + | [this[0x00][0x03], D] + | [this[0x00][0x04], E] + | [this[0x00][0x05], F] + | [this[0x00][0x06], G] + | [this[0x00][0x07], H] + | [this[0x00][0x08], I] + > +} + +interface Function10 extends HKT { + [-1]: + (a: A) => + (b: B) => + (c: C) => + (d: D) => + (e: E) => + (f: F) => + (g: G) => + (h: H) => + (i: I) => + (j: J) => + FromEntries< + | [this[0x00][0x00], A] + | [this[0x00][0x01], B] + | [this[0x00][0x02], C] + | [this[0x00][0x03], D] + | [this[0x00][0x04], E] + | [this[0x00][0x05], F] + | [this[0x00][0x06], G] + | [this[0x00][0x07], H] + | [this[0x00][0x08], I] + | [this[0x00][0x09], J] + > +} + + +interface Function11 extends HKT { + [-1]: + (a: A) => + (b: B) => + (c: C) => + (d: D) => + (e: E) => + (f: F) => + (g: G) => + (h: H) => + (i: I) => + (j: J) => + (k: K) => + FromEntries< + | [this[0x00][0x00], A] + | [this[0x00][0x01], B] + | [this[0x00][0x02], C] + | [this[0x00][0x03], D] + | [this[0x00][0x04], E] + | [this[0x00][0x05], F] + | [this[0x00][0x06], G] + | [this[0x00][0x07], H] + | [this[0x00][0x08], I] + | [this[0x00][0x09], J] + | [this[0x00][0x0a], K] + > +} + +interface Function12 extends HKT { + [-1]: + (a: A) => + (b: B) => + (c: C) => + (d: D) => + (e: E) => + (f: F) => + (g: G) => + (h: H) => + (i: I) => + (j: J) => + (k: K) => + (l: L) => + FromEntries< + | [this[0x00][0x00], A] + | [this[0x00][0x01], B] + | [this[0x00][0x02], C] + | [this[0x00][0x03], D] + | [this[0x00][0x04], E] + | [this[0x00][0x05], F] + | [this[0x00][0x06], G] + | [this[0x00][0x07], H] + | [this[0x00][0x08], I] + | [this[0x00][0x09], J] + | [this[0x00][0x0a], K] + | [this[0x00][0x0b], L] + > +} + +interface Function13 extends HKT { + [-1]: + (a: A) => + (b: B) => + (c: C) => + (d: D) => + (e: E) => + (f: F) => + (g: G) => + (h: H) => + (i: I) => + (j: J) => + (k: K) => + (l: L) => + (m: M) => + FromEntries< + | [this[0x00][0x00], A] + | [this[0x00][0x01], B] + | [this[0x00][0x02], C] + | [this[0x00][0x03], D] + | [this[0x00][0x04], E] + | [this[0x00][0x05], F] + | [this[0x00][0x06], G] + | [this[0x00][0x07], H] + | [this[0x00][0x08], I] + | [this[0x00][0x09], J] + | [this[0x00][0x0a], K] + | [this[0x00][0x0b], L] + | [this[0x00][0x0c], M] + > +} + +interface Function14 extends HKT { + [-1]: + (a: A) => + (b: B) => + (c: C) => + (d: D) => + (e: E) => + (f: F) => + (g: G) => + (h: H) => + (i: I) => + (j: J) => + (k: K) => + (l: L) => + (m: M) => + (n: N) => + FromEntries< + | [this[0x00][0x00], A] + | [this[0x00][0x01], B] + | [this[0x00][0x02], C] + | [this[0x00][0x03], D] + | [this[0x00][0x04], E] + | [this[0x00][0x05], F] + | [this[0x00][0x06], G] + | [this[0x00][0x07], H] + | [this[0x00][0x08], I] + | [this[0x00][0x09], J] + | [this[0x00][0x0a], K] + | [this[0x00][0x0b], L] + | [this[0x00][0x0c], M] + | [this[0x00][0x0d], N] + > +} + +interface Function15 extends HKT { + [-1]: + (a: A) => + (b: B) => + (c: C) => + (d: D) => + (e: E) => + (f: F) => + (g: G) => + (h: H) => + (i: I) => + (j: J) => + (k: K) => + (l: L) => + (m: M) => + (n: N) => + (o: O) => + FromEntries< + | [this[0x00][0x00], A] + | [this[0x00][0x01], B] + | [this[0x00][0x02], C] + | [this[0x00][0x03], D] + | [this[0x00][0x04], E] + | [this[0x00][0x05], F] + | [this[0x00][0x06], G] + | [this[0x00][0x07], H] + | [this[0x00][0x08], I] + | [this[0x00][0x09], J] + | [this[0x00][0x0a], K] + | [this[0x00][0x0b], L] + | [this[0x00][0x0c], M] + | [this[0x00][0x0d], N] + | [this[0x00][0x0e], O] + > +} diff --git a/packages/registry/src/index.ts b/packages/registry/src/index.ts index 704f5088..410a4bcb 100644 --- a/packages/registry/src/index.ts +++ b/packages/registry/src/index.ts @@ -1,2 +1 @@ export * from './exports.js' -export * as registry from './exports.js' diff --git a/packages/registry/src/newtype.ts b/packages/registry/src/newtype.ts index 2bc18d64..6ce9f4b0 100644 --- a/packages/registry/src/newtype.ts +++ b/packages/registry/src/newtype.ts @@ -3,12 +3,12 @@ import type * as symbol from './symbol.js' export type inline = never | T /** @ts-expect-error hush */ -export interface newtype extends inline { } +export interface newtype extends inline {} export interface TypeError - extends newtype<{ [K in Msg]: symbol.type_error }> { } + extends newtype<{ [K in Msg]: symbol.type_error }> {} export declare namespace TypeError { interface Unary - extends newtype<{ [K in Msg]: symbol.type_error } & T> { } + extends newtype<{ [K in Msg]: symbol.type_error } & T> {} } diff --git a/packages/registry/src/option.ts b/packages/registry/src/option.ts new file mode 100644 index 00000000..c97d0878 --- /dev/null +++ b/packages/registry/src/option.ts @@ -0,0 +1,41 @@ +import type { TypeConstructor as F, Bind } from './hkt.js' +import { Object_assign, Object_create } from './globalThis.js' + +export interface Some extends Bind { _tag: 'Option::Some', value: T } +export interface None extends Bind { _tag: 'Option::None' } +export type Option = Some | None + +const NONE = Object_create(null) +NONE._tag = 'Option::None' + +const SOME = Object_create>(null) +SOME._tag = 'Option::Some' + +export const isOption = (x: unknown): x is Option => + !!x && typeof x === 'object' && '_tag' in x && (x._tag === 'Option::Some' || x._tag === 'Option::None') +export function isSome(x: Option): x is Some +export function isSome(x: unknown): x is Some +export function isSome(x: unknown): x is Some { return isOption(x) && x._tag === 'Option::Some' } +export function isNone(x: Option): x is None +export function isNone(x: unknown): x is None +export function isNone(x: unknown): x is None { return isOption(x) && x._tag === 'Option::None' } + +export function some(x: T): Option { return Object_assign(Object_create>(null), SOME, { value: x }) } +export function none(): Option { return NONE } + +export function alt(x: Option, y: Option): Option { return isSome(x) ? x : y } + +export function map(f: (s: S) => T): (x: Option) => Option +export function map(f: (s: S) => T) { return (x: Option) => x._tag === 'Option::None' ? x : some(f(x.value)) } + +export function flatMap(f: (s: S) => Option): (x: Option) => Option +export function flatMap(f: (s: S) => Option) { return (x: Option) => x._tag === 'Option::None' ? x : f(x.value) } + +export function getOrElse(fallback: () => T): (x: Option) => T +export function getOrElse(fallback: () => T) { + return (x: Option) => x._tag === 'Option::Some' ? x.value : fallback() +} + +export function fromPredicate(predicate: (x: S) => x is T): (x: unknown) => Option +export function fromPredicate(predicate: (x: T) => boolean): (x: T) => Option +export function fromPredicate(predicate: (x: T) => boolean) { return (x: T) => predicate(x) ? some(x) : none() } diff --git a/packages/registry/src/satisfies.ts b/packages/registry/src/satisfies.ts index 87ce7c7c..389ff2bd 100644 --- a/packages/registry/src/satisfies.ts +++ b/packages/registry/src/satisfies.ts @@ -52,15 +52,26 @@ export type NonFiniteArray ? readonly [] | readonly unknown[] : never : never +export type NonFiniteArrayOf + = [T] extends [readonly any[]] + ? number extends T['length'] + ? readonly [] | readonly V[] + : never : never + export type NonFiniteObject = string extends keyof T ? Record : number extends keyof T ? Record : never +export type EmptyObject = [keyof T] extends [never] ? {} : never export type FiniteArray = [T] extends [readonly any[]] ? number extends T['length'] ? never : { [I in keyof T]: T[I] } : never export type FiniteObject = [T] extends [Record] ? string extends keyof T ? never : number extends keyof T ? never : { -readonly [K in keyof T]: T[K] } : never -export type FiniteArrayOf = [T] extends [readonly S[]] ? number extends T['length'] ? never : { [I in keyof T]: T[I] } : never -export type FiniteObjectOf = [T] extends [Record] ? string extends keyof T ? never : number extends keyof T ? never : Mut : never + +export type FiniteArrayOf = [T] extends [readonly V[]] ? number extends T['length'] ? never : { [I in keyof T]: T[I] } : never + +export type FiniteArrayOf_ = [T] extends [readonly V[]] ? number extends T['length'] ? never : [...V[]] : never + +export type FiniteObjectOf = [T] extends [Record] ? string extends keyof T ? never : number extends keyof T ? never : Mut : never export type OnlyAny = _ export type OnlyUnknown ? never : unknown> = [unknown] extends [T] ? _ : never diff --git a/packages/registry/src/symbol.ts b/packages/registry/src/symbol.ts index a10f7e95..dde9f2b6 100644 --- a/packages/registry/src/symbol.ts +++ b/packages/registry/src/symbol.ts @@ -15,9 +15,11 @@ export { symbol_invalid_value as invalid_value, symbol_cache_hit as cache_hit, symbol_never as never, + symbol_newtype as newtype, symbol_nonnullable as nonnullable, symbol_notfound as notfound, symbol_null as null, + symbol_nullable as nullable, symbol_number as number, symbol_object as object, symbol_optional as optional, @@ -31,6 +33,7 @@ export { symbol_typeclass as typeclass, symbol_unknown as unknown, symbol_undefined as undefined, + symbol_unspecified as unspecified, symbol_union as union, symbol_void as void, symbol_symbol as symbol, @@ -54,9 +57,11 @@ const symbol_intersect = Symbol.for(URI.intersect) const symbol_cache_hit = Symbol.for(URI.cache_hit) const symbol_invalid_value = Symbol.for(URI.invalid_value) const symbol_never = Symbol.for(URI.never) +const symbol_newtype = Symbol.for(URI.newtype) const symbol_nonnullable = Symbol.for(URI.nonnullable) const symbol_notfound = Symbol.for(URI.notfound) const symbol_null = Symbol.for(URI.null) +const symbol_nullable = Symbol.for(URI.nullable) const symbol_number = Symbol.for(URI.number) const symbol_object = Symbol.for(URI.object) const symbol_optional = Symbol.for(URI.optional) @@ -72,6 +77,7 @@ const symbol_typeclass = Symbol.for(URI.typeclass) const symbol_unknown = Symbol.for(URI.unknown) const symbol_undefined = Symbol.for(URI.undefined) const symbol_union = Symbol.for(URI.union) +const symbol_unspecified = Symbol.for(URI.unspecified) const symbol_void = Symbol.for(URI.void) type symbol_any = typeof symbol_any @@ -89,9 +95,11 @@ type symbol_invalid_value = typeof symbol_invalid_value type symbol_cache_hit = typeof symbol_cache_hit type symbol_has = typeof symbol_has type symbol_never = typeof symbol_never +type symbol_newtype = typeof symbol_newtype type symbol_nonnullable = typeof symbol_nonnullable type symbol_notfound = typeof symbol_notfound type symbol_null = typeof symbol_null +type symbol_nullable = typeof symbol_nullable type symbol_number = typeof symbol_number type symbol_object = typeof symbol_object type symbol_optional = typeof symbol_optional @@ -106,6 +114,7 @@ type symbol_type_error = typeof symbol_type_error type symbol_typeclass = typeof symbol_typeclass type symbol_unknown = typeof symbol_unknown type symbol_undefined = typeof symbol_undefined +type symbol_unspecified = typeof symbol_unspecified type symbol_union = typeof symbol_union type symbol_void = typeof symbol_void diff --git a/packages/registry/src/tuple.ts b/packages/registry/src/tuple.ts new file mode 100644 index 00000000..628275d6 --- /dev/null +++ b/packages/registry/src/tuple.ts @@ -0,0 +1,12 @@ +import type { Bind, HKT2 as HKT, Kind, TypeConstructor as F } from './hkt.js' +import type { Profunctor } from './functor.js' + +export interface Tuple extends Bind { [0]: F, [1]: S, [Symbol.iterator](): Iterator } + +export function first(x: Tuple): A { return x[0] } +export function second(x: Tuple): B { return x[1] } + +export interface Strong extends Profunctor { + first(F: Kind>): Kind, Tuple>> + second(F: Kind>): Kind, Tuple>> +} diff --git a/packages/registry/src/uri.ts b/packages/registry/src/uri.ts index bc0b6bb7..a216407b 100644 --- a/packages/registry/src/uri.ts +++ b/packages/registry/src/uri.ts @@ -29,13 +29,16 @@ export { URI_const as const, URI_has as has, URI_invalid_value as invalid_value, + URI_newtype as newtype, URI_nonnullable as nonnullable, URI_notfound as notfound, + URI_nullable as nullable, URI_tag as tag, URI_top as top, URI_type as type, URI_type_error as type_error, URI_typeclass as typeclass, + URI_unspecified as unspecified, } export const SCOPE = '@traversable/schema/URI' @@ -64,8 +67,12 @@ const URI_intersect = `${NS}intersect` as const type URI_intersect = typeof URI_intersect const URI_never = `${NS}never` as const type URI_never = typeof URI_never +const URI_newtype = `${NS}newtype` as const +type URI_newtype = typeof URI_newtype const URI_null = `${NS}null` as const type URI_null = typeof URI_null +const URI_nullable = `${NS}nullable` as const +type URI_nullable = typeof URI_nullable const URI_number = `${NS}number` as const type URI_number = typeof URI_number const URI_object = `${NS}object` as const @@ -116,3 +123,5 @@ const URI_type_error = `${NS}type_error` as const type URI_type_error = typeof URI_type_error const URI_typeclass = `${NS}typeclass` as const type URI_typeclass = typeof URI_typeclass +const URI_unspecified = `${NS}unspecified` as const +type URI_unspecified = typeof URI_unspecified diff --git a/packages/registry/test/function.test.ts b/packages/registry/test/function.test.ts index b0a9fea7..18af219f 100644 --- a/packages/registry/test/function.test.ts +++ b/packages/registry/test/function.test.ts @@ -1,5 +1,6 @@ import * as vi from 'vitest' -import { fn } from '@traversable/registry' +import type * as T from '@traversable/registry' +import { Either, fn } from '@traversable/registry' const readonly : (x: T) => { readonly [K in keyof T]: T[K] } @@ -27,3 +28,21 @@ vi.describe('〖⛳️〗‹‹‹ ❲@traverable/registry❳: fn', () => { vi.expectTypeOf(fn.map([Number(), Number(), Number()] as const, String)).toEqualTypeOf([String(), String(), String()] as const) }) }) + +const Bifunctor: T.Bifunctor = { + map(f) { return (x) => x._tag === 'Either::Left' ? x : { _tag: 'Either::Right', right: f(x.right) } }, + mapLeft(f) { return (x) => x._tag === 'Either::Right' ? x : { _tag: 'Either::Left', left: f(x.left) } } +} + +vi.describe('〖⛳️〗‹‹‹ ❲@traverable/registry❳: fn', () => { + vi.it('〖⛳️〗‹‹‹ ❲fn.map❳: typelevel tests', () => { + const unfold = fn.apo(Bifunctor) + + const sum = unfold((x) => { + return x > 0 ? Either.right(x - 1) : Either.left(x) + }) + + vi.assert.deepEqual(sum(10), Either.left(0)) + }) +}) + diff --git a/packages/registry/test/hom.test.ts b/packages/registry/test/hom.test.ts new file mode 100644 index 00000000..f9a91d71 --- /dev/null +++ b/packages/registry/test/hom.test.ts @@ -0,0 +1,46 @@ +import * as vi from 'vitest' +import { hom } from '@traversable/registry' + +vi.describe('〖⛳️〗‹‹‹ ❲@traverable/registry❳: fn', () => { + vi.it('〖⛳️〗‹‹‹ ❲hom❳: empty case', () => { + const actual = hom([])() + + vi.assert.deepEqual(actual, {}) + vi.expectTypeOf(actual).toEqualTypeOf<{}>() + }) + + vi.it('〖⛳️〗‹‹‹ ❲hom❳: standard use case', () => { + const actual = hom(['a', 'b', 'c'])(1 as const)(2 as const)(3 as const) + + vi.assert.deepEqual(actual, { a: 1, b: 2, c: 3 }) + vi.expectTypeOf(actual).toEqualTypeOf<{ a: 1, b: 2, c: 3 }>() + }) + + vi.it('〖⛳️〗‹‹‹ ❲hom❳: 5+ keys', () => { + const actual = hom([ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f' + ])(1 as const) + (2 as const) + (3 as const) + (4 as const) + (5 as const) + (6 as const) + + const expected = { + a: 1 as const, + b: 2 as const, + c: 3 as const, + d: 4 as const, + e: 5 as const, + f: 6 as const, + } + + vi.assert.deepEqual(actual, expected) + vi.expectTypeOf(actual).toEqualTypeOf() + }) +}) diff --git a/packages/registry/test/parse.test.ts b/packages/registry/test/parse.test.ts new file mode 100644 index 00000000..04031833 --- /dev/null +++ b/packages/registry/test/parse.test.ts @@ -0,0 +1,9 @@ +import * as vi from 'vitest' + +import { parseKey } from '@traversable/registry' + +vi.describe('〖⛳️〗‹‹‹ ❲@traverable/registry❳: merge', () => { + vi.it('〖⛳️〗‹‹‹ ❲parseKey❳', () => { + vi.expect(parseKey('`abc`')).toMatchInlineSnapshot(`"\`abc\`"`) + }) +}) \ No newline at end of file diff --git a/packages/schema-zod-adapter/package.json b/packages/schema-zod-adapter/package.json index 7d4fb48a..281c492e 100644 --- a/packages/schema-zod-adapter/package.json +++ b/packages/schema-zod-adapter/package.json @@ -46,8 +46,8 @@ "devDependencies": { "@traversable/json": "workspace:^", "@traversable/registry": "workspace:^", - "zod3": "catalog:", - "zod4": "catalog:" + "zod": "catalog:" + }, "peerDependencies": { "@traversable/json": "workspace:^", diff --git a/packages/schema-zod-adapter/src/__generated__/__manifest__.ts b/packages/schema-zod-adapter/src/__generated__/__manifest__.ts index ecca32b2..a5137d1c 100644 --- a/packages/schema-zod-adapter/src/__generated__/__manifest__.ts +++ b/packages/schema-zod-adapter/src/__generated__/__manifest__.ts @@ -42,8 +42,7 @@ export default { "devDependencies": { "@traversable/json": "workspace:^", "@traversable/registry": "workspace:^", - "zod3": "catalog:", - "zod4": "catalog:" + "zod": "catalog:" }, "peerDependencies": { "@traversable/json": "workspace:^", diff --git a/packages/schema-zod-adapter/src/algebra-v4.ts b/packages/schema-zod-adapter/src/algebra-v4.ts index 516b8d6f..7006ad03 100644 --- a/packages/schema-zod-adapter/src/algebra-v4.ts +++ b/packages/schema-zod-adapter/src/algebra-v4.ts @@ -1,7 +1,8 @@ -import { z } from 'zod4' -import { fn } from '@traversable/registry' +import { z } from 'zod/v4' +import { fn, Object_entries, Object_values } from '@traversable/registry' import * as F from './functor-v4.js' -import { tagged } from './typename-v4.js' +import { Sym, tagged } from './typename-v4.js' +import type { Path } from './paths-v4.js' /** @internal */ const readonly = (x: z.core.$ZodType) => tagged('readonly', x) ? x : z.readonly(x) @@ -12,6 +13,10 @@ const optional = (x: z.core.$ZodType) => tagged('optional', x) ? x : z.optional( /** @internal */ const nullable = (x: z.core.$ZodType) => tagged('nullable', x) ? x._zod.def.innerType : z.nullable(x) +/** @internal */ +const isAsyncFunction = (x: Function): boolean => + x.constructor.name === (async function async_function() {}).constructor.name + export const deepNullable = F.lift(nullable) export const deepPartial = F.lift((x: z.core.$ZodType) => !tagged('object', x) ? x : z.object(fn.map(x._zod.def.shape, optional))) @@ -29,3 +34,90 @@ export const deepReadonly = F.lift((x: z.core.$ZodType) => { : z.tuple(fn.map(x._zod.def.items, readonly), readonly(x._zod.def.rest)) } }) + +export const isAsync: F.Algebra = (x) => { + switch (true) { + default: return fn.exhaustive(x) + case F.isNullary(x): return false + case tagged('promise')(x): return true + case tagged('transform')(x): return isAsyncFunction(x._zod.def.transform) + case tagged('object')(x): return Object_values(x._zod.def.shape).some((v) => v) + case tagged('tuple')(x): return x._zod.def.items.some((v) => v) + case tagged('union')(x): return x._zod.def.options.some((v) => v) + case tagged('map')(x): return x._zod.def.keyType || x._zod.def.valueType + case tagged('intersection')(x): return x._zod.def.left || x._zod.def.right + case tagged('record')(x): return x._zod.def.keyType || x._zod.def.valueType + case tagged('pipe')(x): return x._zod.def.in || x._zod.def.out + case tagged('array')(x): return x._zod.def.element + case tagged('set')(x): return x._zod.def.valueType + case tagged('catch')(x): return x._zod.def.innerType + case tagged('default')(x): return x._zod.def.innerType + case tagged('prefault')(x): return x._zod.def.innerType + case tagged('optional')(x): return x._zod.def.innerType + case tagged('nonoptional')(x): return x._zod.def.innerType + case tagged('nullable')(x): return x._zod.def.innerType + case tagged('readonly')(x): return x._zod.def.innerType + case tagged('success')(x): return x._zod.def.innerType + case tagged('lazy')(x): return x._zod.def.getter() + } +} + +export const paths = F.fold((x) => { + switch (true) { + case tagged('transform')(x): { throw Error('todo: paths.transform') } + default: return fn.exhaustive(x) + // nullary + case tagged('any')(x): return [[[], x._zod.def.type]] + case tagged('bigint')(x): return [[[], x._zod.def.type]] + case tagged('boolean')(x): return [[[], x._zod.def.type]] + case tagged('date')(x): return [[[], x._zod.def.type]] + case tagged('enum')(x): return [[[], x._zod.def.type]] + case tagged('file')(x): return [[[], x._zod.def.type]] + case tagged('literal')(x): return [[[], x._zod.def.type]] + case tagged('nan')(x): return [[[], x._zod.def.type]] + case tagged('never')(x): return [[[], x._zod.def.type]] + case tagged('null')(x): return [[[], x._zod.def.type]] + case tagged('number')(x): return [[[], x._zod.def.type]] + case tagged('string')(x): return [[[], x._zod.def.type]] + case tagged('symbol')(x): return [[[], x._zod.def.type]] + case tagged('template_literal')(x): return [[[], x._zod.def.type]] + case tagged('undefined')(x): return [[[], x._zod.def.type]] + case tagged('unknown')(x): return [[[], x._zod.def.type]] + case tagged('void')(x): return [[[], x._zod.def.type]] + // unary + case tagged('array')(x): return [[[Sym.array, ...x._zod.def.element[0][0]], x._zod.def.element[0][1]]] + case tagged('catch')(x): return [[[Sym.catch, ...x._zod.def.innerType[0][0]], x._zod.def.innerType[0][1]]] + case tagged('default')(x): return [[[Sym.default, ...x._zod.def.innerType[0][0]], x._zod.def.innerType[0][1]]] + case tagged('lazy')(x): return [[[Sym.lazy, ...x._zod.def.getter()[0][0]], x._zod.def.getter()[0][1]]] + case tagged('nonoptional')(x): return [[[Sym.nonoptional, ...x._zod.def.innerType[0][0]], x._zod.def.innerType[0][1]]] + case tagged('nullable')(x): return [[[Sym.nullable, ...x._zod.def.innerType[0][0]], x._zod.def.innerType[0][1]]] + case tagged('optional')(x): return [[[Sym.optional, ...x._zod.def.innerType[0][0]], x._zod.def.innerType[0][1]]] + case tagged('prefault')(x): return [[[Sym.prefault, ...x._zod.def.innerType[0][0]], x._zod.def.innerType[0][1]]] + case tagged('promise')(x): return [[[Sym.promise, ...x._zod.def.innerType[0][0]], x._zod.def.innerType[0][1]]] + case tagged('readonly')(x): return [[[Sym.readonly, ...x._zod.def.innerType[0][0]], x._zod.def.innerType[0][1]]] + case tagged('set')(x): return [[[Sym.set, ...x._zod.def.valueType[0][0]], x._zod.def.valueType[0][1]]] + case tagged('success')(x): return [[[Sym.success, ...x._zod.def.innerType[0][0]], x._zod.def.innerType[0][1]]] + case tagged('intersection')(x): return [ + [[Sym.intersectionLeft, ...x._zod.def.left[0][0]], x._zod.def.left[0][1]], + [[Sym.intersectionRight, ...x._zod.def.right[0][0]], x._zod.def.right[0][1]], + ] + case tagged('map')(x): return [ + [[Sym.mapKey, ...x._zod.def.keyType[0][0]], x._zod.def.keyType[0][1]], + [[Sym.mapValue, ...x._zod.def.valueType[0][0]], x._zod.def.valueType[0][1]], + ] + case tagged('record')(x): return [ + [[Sym.recordKey, ...x._zod.def.keyType[0][0]], x._zod.def.keyType[0][1]], + [[Sym.recordValue, ...x._zod.def.valueType[0][0]], x._zod.def.valueType[0][1]], + ] + case tagged('pipe')(x): return [ + [[Sym.pipe, ...x._zod.def.in[0][0]], x._zod.def.in[0][1]], + [[Sym.pipe, ...x._zod.def.out[0][0]], x._zod.def.out[0][1]], + ] + case tagged('object')(x): return Object_entries(x._zod.def.shape) + .flatMap(([k, paths]) => paths.map(([path, leaf]) => [[Sym.object, k, ...path], leaf])) + case tagged('tuple')(x): return x._zod.def.items + .flatMap((paths, i) => paths.map(([path, leaf]) => [[Sym.tuple, i, ...path], leaf])) + case tagged('union')(x): return x._zod.def.options + .flatMap((paths, i) => paths.map(([path, leaf]) => [[Sym.union, i, ...path], leaf])) + } +}) diff --git a/packages/schema-zod-adapter/src/deep-nullable-v4.ts b/packages/schema-zod-adapter/src/deep-nullable-v4.ts index 26fa8f5d..98e10557 100644 --- a/packages/schema-zod-adapter/src/deep-nullable-v4.ts +++ b/packages/schema-zod-adapter/src/deep-nullable-v4.ts @@ -1,4 +1,4 @@ -import { z } from 'zod4' +import { z } from 'zod/v4' import type { newtype, Primitive } from '@traversable/registry' import { fn } from '@traversable/registry' import * as F from './functor-v4.js' diff --git a/packages/schema-zod-adapter/src/deep-partial-v4.ts b/packages/schema-zod-adapter/src/deep-partial-v4.ts index 3e0334a2..4b4c28b5 100644 --- a/packages/schema-zod-adapter/src/deep-partial-v4.ts +++ b/packages/schema-zod-adapter/src/deep-partial-v4.ts @@ -1,4 +1,4 @@ -import { z } from 'zod4' +import { z } from 'zod/v4' import type { newtype, Primitive } from '@traversable/registry' import { fn } from '@traversable/registry' import * as F from './functor-v4.js' diff --git a/packages/schema-zod-adapter/src/deep-readonly-v4.ts b/packages/schema-zod-adapter/src/deep-readonly-v4.ts index 79a980a6..c5e44aab 100644 --- a/packages/schema-zod-adapter/src/deep-readonly-v4.ts +++ b/packages/schema-zod-adapter/src/deep-readonly-v4.ts @@ -1,4 +1,4 @@ -import { z } from 'zod4' +import { z } from 'zod/v4' import type { newtype, Primitive } from '@traversable/registry' import { fn } from '@traversable/registry' import * as F from './functor-v4.js' diff --git a/packages/schema-zod-adapter/src/exports.ts b/packages/schema-zod-adapter/src/exports.ts index 062e69d4..4d4b6826 100644 --- a/packages/schema-zod-adapter/src/exports.ts +++ b/packages/schema-zod-adapter/src/exports.ts @@ -1,3 +1,5 @@ export * from './version-v3.js' export * as v3 from './v3.js' export * as v4 from './v4.js' +export type { RegisterDSL } from './make-lenses-v4.js' +export { registerDSL } from './make-lenses-v4.js' diff --git a/packages/schema-zod-adapter/src/functor-v3.ts b/packages/schema-zod-adapter/src/functor-v3.ts index 67ec1ab4..c90e3134 100644 --- a/packages/schema-zod-adapter/src/functor-v3.ts +++ b/packages/schema-zod-adapter/src/functor-v3.ts @@ -1,4 +1,4 @@ -import { z } from 'zod3' +import { z } from 'zod/v3' import type * as T from '@traversable/registry' import { fn, has, parseKey, Print } from '@traversable/registry' diff --git a/packages/schema-zod-adapter/src/functor-v4.ts b/packages/schema-zod-adapter/src/functor-v4.ts index 3d1f38f9..df67a8ee 100644 --- a/packages/schema-zod-adapter/src/functor-v4.ts +++ b/packages/schema-zod-adapter/src/functor-v4.ts @@ -1,9 +1,10 @@ -import { z } from 'zod4' +import { z } from 'zod/v4' import type * as T from '@traversable/registry' -import { fn } from '@traversable/registry' +import { fn, has } from '@traversable/registry' import type { Ctx } from './utils-v4.js' -import { Tag, tagged } from './typename-v4.js' +import type { AnyTypeName } from './typename-v4.js' +import { TypeName, tagged } from './typename-v4.js' export type Options = { initialIndex?: (string | number)[] @@ -23,139 +24,144 @@ type EnumEntries : T export declare namespace Z { - type lookup = Z.catalog[Tag[K]] - type catalog = { + type Lookup = Z.Catalog[TypeName[K]] + type Catalog = { // nullary - [Tag.any]: Z.Any - [Tag.bigint]: Z.BigInt - [Tag.boolean]: Z.Boolean - [Tag.date]: Z.Date - [Tag.nan]: Z.NaN - [Tag.never]: Z.Never - [Tag.null]: Z.Null - [Tag.number]: Z.Number - [Tag.string]: Z.String - [Tag.symbol]: Z.Symbol - [Tag.undefined]: Z.Undefined - [Tag.unknown]: Z.Unknown - [Tag.void]: Z.Void - [Tag.int]: Z.Integer, + [TypeName.any]: Z.Any + [TypeName.bigint]: Z.BigInt + [TypeName.boolean]: Z.Boolean + [TypeName.date]: Z.Date + [TypeName.nan]: Z.NaN + [TypeName.never]: Z.Never + [TypeName.null]: Z.Null + [TypeName.number]: Z.Number + [TypeName.string]: Z.String + [TypeName.symbol]: Z.Symbol + [TypeName.undefined]: Z.Undefined + [TypeName.unknown]: Z.Unknown + [TypeName.void]: Z.Void + [TypeName.int]: Z.Integer, // nullary-ish - [Tag.literal]: Z.Literal - [Tag.enum]: Z.Enum - [Tag.file]: Z.File - [Tag.template_literal]: Z.TemplateLiteral + [TypeName.literal]: Z.Literal + [TypeName.enum]: Z.Enum + [TypeName.file]: Z.File + [TypeName.template_literal]: Z.TemplateLiteral // unary - [Tag.array]: Z.Array - [Tag.catch]: Z.Catch - [Tag.default]: Z.Default - [Tag.lazy]: Z.Lazy - [Tag.map]: Z.Map - [Tag.nullable]: Z.Nullable - [Tag.object]: Z.Object - [Tag.optional]: Z.Optional - [Tag.readonly]: Z.Readonly - [Tag.record]: Z.Record - [Tag.set]: Z.Set - [Tag.tuple]: Z.Tuple - [Tag.union]: Z.Union - [Tag.intersection]: Z.Intersection - [Tag.pipe]: Z.Pipe - [Tag.custom]: Z.Custom - [Tag.nonoptional]: Z.NonOptional - [Tag.success]: Z.Success - [Tag.transform]: Z.Transform + [TypeName.array]: Z.Array + [TypeName.catch]: Z.Catch + [TypeName.default]: Z.Default + [TypeName.lazy]: Z.Lazy + [TypeName.map]: Z.Map + [TypeName.nullable]: Z.Nullable + [TypeName.object]: Z.Object + [TypeName.optional]: Z.Optional + [TypeName.prefault]: Z.Prefault + [TypeName.readonly]: Z.Readonly + [TypeName.record]: Z.Record + [TypeName.set]: Z.Set + [TypeName.tuple]: Z.Tuple + [TypeName.union]: Z.Union + [TypeName.intersection]: Z.Intersection + [TypeName.pipe]: Z.Pipe + [TypeName.custom]: Z.Custom + [TypeName.nonoptional]: Z.NonOptional + [TypeName.success]: Z.Success + [TypeName.transform]: Z.Transform /** @deprecated */ - [Tag.promise]: Z.Promise + [TypeName.promise]: Z.Promise } - type zodLookup = ZodCatalog[Tag[K]] + type ZodLookup = ZodCatalog[TypeName[K]] type ZodCatalog = { // nullary - [Tag.any]: z.ZodAny - [Tag.bigint]: z.ZodBigInt - [Tag.boolean]: z.ZodBoolean - [Tag.date]: z.ZodDate - [Tag.nan]: z.ZodNaN - [Tag.never]: z.ZodNever - [Tag.null]: z.ZodNull - [Tag.number]: z.ZodNumber - [Tag.string]: z.ZodString - [Tag.symbol]: z.ZodSymbol - [Tag.undefined]: z.ZodUndefined - [Tag.unknown]: z.ZodUnknown - [Tag.void]: z.ZodVoid - [Tag.int]: z.ZodNumber, + [TypeName.any]: z.ZodAny + [TypeName.bigint]: z.ZodBigInt + [TypeName.boolean]: z.ZodBoolean + [TypeName.date]: z.ZodDate + [TypeName.nan]: z.ZodNaN + [TypeName.never]: z.ZodNever + [TypeName.null]: z.ZodNull + [TypeName.number]: z.ZodNumber + [TypeName.string]: z.ZodString + [TypeName.symbol]: z.ZodSymbol + [TypeName.undefined]: z.ZodUndefined + [TypeName.unknown]: z.ZodUnknown + [TypeName.void]: z.ZodVoid + [TypeName.int]: z.ZodNumber, // nullary-ish - [Tag.literal]: z.ZodLiteral - [Tag.enum]: z.ZodEnum - [Tag.file]: z.ZodFile - [Tag.template_literal]: z.ZodTemplateLiteral + [TypeName.literal]: z.ZodLiteral + [TypeName.enum]: z.ZodEnum + [TypeName.file]: z.ZodFile + [TypeName.template_literal]: z.ZodTemplateLiteral // unary - [Tag.array]: z.ZodArray - [Tag.catch]: z.ZodCatch - [Tag.default]: z.ZodDefault - [Tag.lazy]: z.ZodLazy - [Tag.map]: z.ZodMap - [Tag.nullable]: z.ZodNullable - [Tag.object]: z.ZodObject<{ [x: string]: T }> - [Tag.optional]: z.ZodOptional - [Tag.readonly]: z.ZodReadonly - [Tag.record]: z.ZodRecord - [Tag.set]: z.ZodSet - [Tag.tuple]: z.ZodTuple<[T, ...T[]], T> - [Tag.union]: z.ZodUnion - [Tag.intersection]: z.ZodIntersection - [Tag.pipe]: z.ZodPipe - [Tag.custom]: z.ZodCustom - [Tag.nonoptional]: z.ZodNonOptional - [Tag.success]: z.ZodSuccess - [Tag.transform]: z.ZodTransform + [TypeName.array]: z.ZodArray + [TypeName.catch]: z.ZodCatch + [TypeName.default]: z.ZodDefault + [TypeName.lazy]: z.ZodLazy + [TypeName.map]: z.ZodMap + [TypeName.nullable]: z.ZodNullable + [TypeName.object]: z.ZodObject<{ [x: string]: T }> + [TypeName.optional]: z.ZodOptional + [TypeName.prefault]: z.ZodPrefault + [TypeName.readonly]: z.ZodReadonly + [TypeName.record]: z.ZodRecord + [TypeName.set]: z.ZodSet + [TypeName.tuple]: z.ZodTuple<[T, ...T[]], T> + [TypeName.union]: z.ZodUnion + [TypeName.intersection]: z.ZodIntersection + [TypeName.pipe]: z.ZodPipe + [TypeName.custom]: z.ZodCustom + [TypeName.nonoptional]: z.ZodNonOptional + [TypeName.success]: z.ZodSuccess + [TypeName.transform]: z.ZodTransform /** @deprecated */ - [Tag.promise]: z.ZodPromise + [TypeName.promise]: z.ZodPromise } - interface Never { _zod: { def: { type: Tag['never'] } } } - interface Any { _zod: { def: { type: Tag['any'] } } } - interface Unknown { _zod: { def: { type: Tag['unknown'] } } } - interface Undefined { _zod: { def: { type: Tag['undefined'] } } } - interface Null { _zod: { def: { type: Tag['null'] } } } - interface Void { _zod: { def: { type: Tag['void'] } } } - interface NaN { _zod: { def: { type: Tag['nan'] } } } - interface Symbol { _zod: { def: { type: Tag['symbol'] } } } - interface Boolean { _zod: { def: { type: Tag['boolean'] } } } - interface BigInt { _zod: { def: { type: Tag['bigint'] } } } - interface Number { _zod: { def: { type: Tag['number'], checks?: Number.Check[] } }, isInt: boolean } - interface Integer { _zod: { def: { type: Tag['int'], checks?: Integer.Check[] } } } - interface String extends String.Proto { _zod: { def: { type: Tag['string'] } } } - interface Date { _zod: { def: { type: Tag['date'] } } } - interface File { _zod: { def: { type: Tag['file'] } } } - - interface Enum { _zod: { def: { type: Tag['enum'], entries: EnumEntries } } } - interface Literal { _zod: { def: { type: Tag['literal'], values: N[] } } } - interface TemplateLiteral { _zod: { def: { type: Tag['template_literal'], parts: unknown[] } } } - - interface Optional { _zod: { def: { type: Tag['optional'], innerType: S } } } - interface Nullable { _zod: { def: { type: Tag['nullable'], innerType: S } } } - interface Array extends Omit { _zod: { def: { type: Tag['array'], element: S } } } - interface Set { _zod: { def: { type: Tag['set'], valueType: S } } } - interface Map { _zod: { def: { type: Tag['map'], keyType: S, valueType: S } } } - interface Readonly { _zod: { def: { type: Tag['readonly'], innerType: S } } } - interface Object extends Omit { _zod: { def: { type: Tag['object'], shape: { [x: string]: S }, catchall?: S } } } - interface Record { _zod: { def: { type: Tag['record'], keyType: S, valueType: S } } } - interface Tuple extends Omit { _zod: { def: { type: Tag['tuple'], items: [S, ...S[]], rest?: S } } } - interface Lazy { _zod: { def: { type: Tag['lazy'], getter(): S } } } - interface Intersection { _zod: { def: { type: Tag['intersection'], left: S, right: S } } } - interface Union { _zod: { def: { type: Tag['union'], options: readonly [S, S, ...S[]] } } } - interface Catch { _zod: { def: { type: Tag['catch'], innerType: S, catchValue(ctx: Ctx): unknown } } } - interface Custom { _zod: { def: { type: Tag['custom'] } } } - interface Default { _zod: { def: { type: Tag['default'], innerType: S, defaultValue: (ctx: Ctx) => unknown } } } - interface NonOptional { _zod: { def: { type: Tag['nonoptional'], innerType: S } } } - interface Pipe { _zod: { def: { type: Tag['pipe'], in: S, out: S } } } - interface Transform { _zod: { def: { type: Tag['transform'], transform: (x: unknown) => S } } } - interface Success { _zod: { def: { type: Tag['success'], innerType: S } } } + interface Proto { safeParse(x: unknown): { success: boolean } } + + interface Never extends Proto { _zod: { def: { type: TypeName['never'] } } } + interface Any extends Proto { _zod: { def: { type: TypeName['any'] } } } + interface Unknown extends Proto { _zod: { def: { type: TypeName['unknown'] } } } + interface Undefined extends Proto { _zod: { def: { type: TypeName['undefined'] } } } + interface Null extends Proto { _zod: { def: { type: TypeName['null'] } } } + interface Void extends Proto { _zod: { def: { type: TypeName['void'] } } } + interface NaN extends Proto { _zod: { def: { type: TypeName['nan'] } } } + interface Symbol extends Proto { _zod: { def: { type: TypeName['symbol'] } } } + interface Boolean extends Proto { _zod: { def: { type: TypeName['boolean'] } } } + interface BigInt extends Proto { _zod: { def: { type: TypeName['bigint'] } } } + interface Number extends Proto { _zod: { def: { type: TypeName['number'], checks?: Number.Check[] } }, isInt: boolean } + interface Integer extends Proto { _zod: { def: { type: TypeName['int'], checks?: Integer.Check[] } } } + interface String extends Proto, String.Proto { _zod: { def: { type: TypeName['string'] } } } + interface Date extends Proto { _zod: { def: { type: TypeName['date'] } } } + interface File extends Proto { _zod: { def: { type: TypeName['file'] } } } + + interface Enum extends Proto { _zod: { def: { type: TypeName['enum'], entries: EnumEntries } } } + interface Literal extends Proto { _zod: { def: { type: TypeName['literal'], values: N[] } } } + interface TemplateLiteral extends Proto { _zod: { def: { type: TypeName['template_literal'], parts: unknown[] } } } + + interface Optional extends Proto { _zod: { def: { type: TypeName['optional'], innerType: S } } } + interface Nullable extends Proto { _zod: { def: { type: TypeName['nullable'], innerType: S } } } + interface Array extends Omit { _zod: { def: { type: TypeName['array'], element: S } } } + interface Set extends Proto { _zod: { def: { type: TypeName['set'], valueType: S } } } + interface Map extends Proto { _zod: { def: { type: TypeName['map'], keyType: S, valueType: S } } } + interface Readonly extends Proto { _zod: { def: { type: TypeName['readonly'], innerType: S } } } + interface Object extends Proto, Omit { _zod: { def: { type: TypeName['object'], shape: { [x: string]: S }, catchall?: S } } } + interface Record extends Proto { _zod: { def: { type: TypeName['record'], keyType: S, valueType: S } } } + interface Tuple extends Proto, Omit { _zod: { def: { type: TypeName['tuple'], items: [S, ...S[]], rest?: S } } } + interface Lazy extends Proto { _zod: { def: { type: TypeName['lazy'], getter(): S } } } + interface Intersection extends Proto { _zod: { def: { type: TypeName['intersection'], left: S, right: S } } } + interface Union extends Proto { _zod: { def: { type: TypeName['union'], options: readonly [S, S, ...S[]] } } } + interface Catch extends Proto { _zod: { def: { type: TypeName['catch'], innerType: S, catchValue(ctx: Ctx): unknown } } } + interface Custom extends Proto { _zod: { def: { type: TypeName['custom'] } } } + interface Default extends Proto { _zod: { def: { type: TypeName['default'], innerType: S, defaultValue: (ctx: Ctx) => unknown } } } + interface Prefault extends Proto { _zod: { def: { type: TypeName['prefault'], innerType: S, defaultValue: (ctx: Ctx) => unknown } } } + interface NonOptional extends Proto { _zod: { def: { type: TypeName['nonoptional'], innerType: S } } } + interface Pipe extends Proto { _zod: { def: { type: TypeName['pipe'], in: S, out: S } } } + interface Transform extends Proto { _zod: { def: { type: TypeName['transform'], transform: (x: unknown) => S } } } + interface Success extends Proto { _zod: { def: { type: TypeName['success'], innerType: S } } } /** @deprecated */ - interface Promise { _zod: { def: { type: Tag['promise'], innerType: S } } } + interface Promise extends Proto { _zod: { def: { type: TypeName['promise'], innerType: S } } } /** * ## {@link Nullary `Z.Hole`} @@ -177,6 +183,7 @@ export declare namespace Z { | Z.Number | Z.String | Z.Date + | Z.File // | Z.Enum | Z.Literal @@ -212,28 +219,6 @@ export declare namespace Z { * * If you take a closer look at the type, you'll see what I mean: everywhere * where I would have made a recursive call has been replaced with {@link _ `_`}. - * - * Why do this? - * - * Well, for starters, it gives us a way to invert control. - * - * This part's important, because it mirrors what we're going to do at the value- - * level: factor out the recursion). We don't know, or care, what {@link _ `_`} - * will be -- all we care about is preserving the surrounding structure. - * - * That lets us get out of the way. Responsibilities are clear: the caller is - * responsible for writing the interpreter, and we're responsible for handling - * the recursion. - * - * Taking this approach is more ergonomic, but it's also mathematically rigorous, - * since it allows our Functor to be homomorphic (see the video below - * called "Constraints Liberate, Liberties Constrain" below). - * - * See also: - * - {@link Fixpoint `Z.Fixpoint`} - * - {@link Any `z.Any`} - * - A talk by Runar Bjarnason's called - * ["Constraints Liberate, Liberties Constrain"](https://www.youtube.com/watch?v=GqmsQeSzMdw) */ type Hole<_> = | Nullary @@ -245,6 +230,7 @@ export declare namespace Z { | Z.Map<_> | Z.Readonly<_> | Z.Object<_> + | Z.Prefault<_> | Z.Record<_> | Z.Tuple<_> | Z.Lazy<_> @@ -276,6 +262,7 @@ export declare namespace Z { type Fixpoint = | Nullary | Z.Catch + | Z.Prefault | Z.Optional | Z.Nullable | Z.Array @@ -331,6 +318,7 @@ export declare namespace Z { } export type Algebra = T.IndexedAlgebra<(string | number)[], Z.Free, T> +export type RCoalgebra = T.RCoalgebra export { In as in } function In(x: T): Z.Hole @@ -361,6 +349,7 @@ export const Functor: T.Functor = { case tagged('boolean')(x): return x case tagged('bigint')(x): return x case tagged('date')(x): return x + case tagged('file')(x): return x case tagged('number')(x): return x case tagged('string')(x): return x case tagged('enum')(x): return x @@ -376,6 +365,7 @@ export const Functor: T.Functor = { case tagged('catch')(x): return { ...x, _zod: { ...x._zod, def: { ...x._zod.def, innerType: g(x._zod.def.innerType) } } } case tagged('success')(x): return { ...x, _zod: { ...x._zod, def: { ...x._zod.def, innerType: g(x._zod.def.innerType) } } } case tagged('default')(x): return { ...x, _zod: { ...x._zod, def: { ...x._zod.def, innerType: g(x._zod.def.innerType) } } } + case tagged('prefault')(x): return { ...x, _zod: { ...x._zod, def: { ...x._zod.def, innerType: g(x._zod.def.innerType) } } } case tagged('readonly')(x): return { ...x, _zod: { ...x._zod, def: { ...x._zod.def, innerType: g(x._zod.def.innerType) } } } case tagged('nullable')(x): return { ...x, _zod: { ...x._zod, def: { ...x._zod.def, innerType: g(x._zod.def.innerType) } } } case tagged('lazy')(x): return { ...x, _zod: { ...x._zod, def: { ...x._zod.def, getter: () => g(x._zod.def.getter()) } } } @@ -433,6 +423,7 @@ export const IndexedFunctor: T.Functor.Ix<(string | number)[], Z.Free> = { case tagged('catch')(x): return { ...x, _zod: { ...x._zod, def: { ...x._zod.def, innerType: g(x._zod.def.innerType, ix) } } } case tagged('success')(x): return { ...x, _zod: { ...x._zod, def: { ...x._zod.def, innerType: g(x._zod.def.innerType, ix) } } } case tagged('default')(x): return { ...x, _zod: { ...x._zod, def: { ...x._zod.def, innerType: g(x._zod.def.innerType, ix) } } } + case tagged('prefault')(x): return { ...x, _zod: { ...x._zod, def: { ...x._zod.def, innerType: g(x._zod.def.innerType, ix) } } } case tagged('readonly')(x): return { ...x, _zod: { ...x._zod, def: { ...x._zod.def, innerType: g(x._zod.def.innerType, ix) } } } case tagged('nullable')(x): return { ...x, _zod: { ...x._zod, def: { ...x._zod.def, innerType: g(x._zod.def.innerType, ix) } } } case tagged('lazy')(x): return { ...x, _zod: { ...x._zod, def: { ...x._zod.def, getter: () => g(x._zod.def.getter(), ix) } } } @@ -462,7 +453,7 @@ export declare namespace Functor { } export const fold = fn.cataIx(IndexedFunctor, []) -export const foldWithContext = fn.para(Functor) +export const foldPara = fn.para(Functor) export type Any = | z.ZodAny @@ -496,3 +487,26 @@ export type Any = | z.ZodUndefined | z.ZodUnknown | z.ZodVoid + +export type NullaryTypeName = Z.Nullary['_zod']['def']['type'] +export const nullaryTypeNames = [ + 'any', + 'bigint', + 'boolean', + 'date', + 'enum', + 'literal', + 'nan', + 'never', + 'null', + 'number', + 'string', + 'symbol', + 'template_literal', + 'undefined', + 'unknown', + 'void', +] as const satisfies NullaryTypeName[] + +export const isNullaryTypeName = (x: unknown): x is NullaryTypeName => typeof x === 'string' && nullaryTypeNames.includes(x as never) +export const isNullary = (x: unknown): x is Z.Nullary => has('_zod', 'def', 'type', isNullaryTypeName)(x) diff --git a/packages/schema-zod-adapter/src/generator-options-v4.ts b/packages/schema-zod-adapter/src/generator-options-v4.ts index 938bf7e6..29368b1f 100644 --- a/packages/schema-zod-adapter/src/generator-options-v4.ts +++ b/packages/schema-zod-adapter/src/generator-options-v4.ts @@ -108,6 +108,7 @@ export type Constraints = { custom?: {} date?: {} default?: {} + prefault?: {} enum?: {} file?: {} int?: { min: undefined | number, max: undefined | number, multipleOf?: number } & fc.IntegerConstraints @@ -180,6 +181,7 @@ export const defaultConstraints = { custom: {}, date: {}, default: {}, + prefault: {}, enum: {}, file: {}, int: { @@ -273,6 +275,7 @@ export const paramsDefaults = { custom: {}, date: {}, default: {}, + prefault: {}, enum: {}, file: {}, int: { @@ -384,6 +387,7 @@ export function parseOptions(options: Options = defaults as never): Config custom = defaultConstraints.custom, date = defaultConstraints.date, default: default_ = defaultConstraints.default, + prefault = defaultConstraints.prefault, enum: enum_ = defaultConstraints.enum, file = defaultConstraints.file, int: { @@ -492,6 +496,7 @@ export function parseOptions(options: Options = defaults as never): Config custom, date, default: default_, + prefault, enum: enum_, file, int: { diff --git a/packages/schema-zod-adapter/src/generator-v4.ts b/packages/schema-zod-adapter/src/generator-v4.ts index 32c4f00f..baf538d8 100644 --- a/packages/schema-zod-adapter/src/generator-v4.ts +++ b/packages/schema-zod-adapter/src/generator-v4.ts @@ -1,8 +1,9 @@ -import { z } from 'zod4' +import { z } from 'zod/v4' import * as fc from 'fast-check' import type { newtype, inline } from '@traversable/registry' import { Array_isArray, + escape, fn, isKeyOf, isObject, @@ -16,6 +17,7 @@ import { Object_keys, Object_values, omit, + parseKey, pick, symbol, } from '@traversable/registry' @@ -25,7 +27,7 @@ import * as Config from './generator-options-v4.js' import * as Bounds from './bounds-v4.js' import type { Tag } from './seed-v4.js' import { byTag, bySeed, Seed, fold } from './seed-v4.js' -import type { TypeName } from './typename-v4.js' +import type { AnyTypeName } from './typename-v4.js' import type { ZodType } from './utils-v4.js' import { getRandomElementOf, @@ -71,7 +73,9 @@ const ValueMap = { const UnaryMap = { array: (tie) => fc.tuple(fc.constant(byTag.array), tie('*'), Bounds.array(fc.integer({ min: 0 }))), catch: (tie) => fc.tuple(fc.constant(byTag.catch), tie('*')), - custom: (tie) => fc.tuple(fc.constant(byTag.custom), tie('*')), default: (tie) => fc.tuple(fc.constant(byTag.default), tie('*')), + custom: (tie) => fc.tuple(fc.constant(byTag.custom), tie('*')), + default: (tie) => fc.tuple(fc.constant(byTag.default), tie('*')), + prefault: (tie) => fc.tuple(fc.constant(byTag.prefault), tie('*')), lazy: (tie) => fc.tuple(fc.constant(byTag.lazy), fc.func<[], unknown>(tie('*'))), nonoptional: (tie) => fc.tuple(fc.constant(byTag.nonoptional), tie('*')), nullable: (tie) => fc.tuple(fc.constant(byTag.nullable), tie('*')), @@ -210,7 +214,7 @@ const branchNames = [ 'object', 'record', 'tuple', -] as const satisfies TypeName[] +] as const satisfies AnyTypeName[] export interface Builder extends inline<{ [K in Tag]+?: fc.Arbitrary }> { root?: fc.Arbitrary @@ -234,6 +238,7 @@ export function Builder(base: Gen.Base) { (x) => omit(x, $.exclude), (x) => fn.map(x, (f, k) => f(tie, $[k as never])), ) + const nodes = pickAndSortNodes(Object_entries(builder) as [k: keyof SeedMap, unknown][])($) builder['*'] = fc.oneof($['*'], ...nodes.map((k) => builder[k])) const root = isKeyOf(builder, $.root) && builder[$.root] @@ -259,7 +264,7 @@ export function Builder(base: Gen.Base) { export declare namespace Gen { type Base = { [K in keyof T]: (tie: fc.LetrecLooselyTypedTie, constraints: $[K & keyof $]) => fc.Arbitrary } type Values = never | T[Exclude] - type InferArb = S extends fc.Arbitrary ? T : never + type InferArb> = S extends fc.Arbitrary ? unknown extends T ? onUnknown : T : never interface Builder extends newtype { ['*']: fc.Arbitrary>> } type BuildBuilder, Out extends {} = BuilderBase> = never | Builder type BuilderBase, $ extends ParseOptions = ParseOptions> = never | @@ -319,7 +324,7 @@ const enumValues selector: ([k]) => k, minLength: 1, } - ).map(Object_fromEntries) satisfies fc.Arbitrary + ).map((xs) => Object_fromEntries(xs)) satisfies fc.Arbitrary const pathName = fc.webUrl().map((webUrl) => new URL(webUrl).pathname) const ext = fc.string({ minLength: 2, maxLength: 3 }) @@ -354,6 +359,8 @@ const is = { } function templateLiteralNodeToPart(x: Seed.TemplateLiteral.Node): z.core.$TemplateLiteralPart { + if (typeof x === 'bigint') return `${x}n` + if (typeof x === 'string') return `"${x}"` if (isShowable(x)) return x else if (is.null(x)) return z.null() else if (is.undefined(x)) return z.undefined() @@ -420,7 +427,7 @@ const GeneratorByTag = { bigint: (x) => fc.bigInt(Bounds.bigintBoundsToBigIntConstraints(x[1])), number: (x) => fc.double(Bounds.numberBoundsToDoubleConstraints(x[1])), string: (x) => fc.string(Bounds.stringBoundsToStringConstraints(x[1])), - enum: (x) => fc.constantFrom(...Object_values(x[1])), + enum: (x) => fc.constantFrom(...Object_values(x[1]).map((v) => typeof v === 'string' ? `"${v}"` : v)), literal: (x) => fc.constant(x[1]), template_literal: (x, $) => generateStringFromRegExp(z.templateLiteral(templateParts(x))._zod.pattern, $), array: (x) => fc.array(x[1], Bounds.arrayBoundsToArrayConstraints(x[2])), @@ -437,9 +444,10 @@ const GeneratorByTag = { union: (x) => fc.oneof(...(x[1] || [fc.constant(void 0 as never)])), lazy: (x) => x[1](), default: (x) => x[1], + prefault: (x) => x[1], custom: (x) => x[1], pipe: (x) => x[1][1], - object: (x) => fc.record(Object.fromEntries(x[1])), + object: (x) => fc.record(Object.fromEntries(x[1].map(([k, v]) => [parseKey(k), v] satisfies [any, any]))), transform: (x) => x[1], intersection: (x) => fc.tuple(...x[1]).map(([x, y]) => intersect(x, y)), promise: () => PromiseSchemaIsUnsupported('GeneratorByTag'), @@ -550,6 +558,7 @@ const seedsThatPreventGeneratingValidData = [ 'never', 'nonoptional', 'pipe', + 'prefault', 'promise', ] satisfies SchemaGenerator.Options['exclude'] @@ -561,6 +570,7 @@ const seedsThatPreventGeneratingInvalidData = [ 'never', 'nonoptional', 'pipe', + 'prefault', 'promise', 'symbol', 'transform', @@ -732,6 +742,7 @@ export function seedToSchema(seed: Seed.F, options?: Config.Options) { case x[0] === byTag.success: return z.success(x[1]) case x[0] === byTag.catch: return z.catch(x[1], {}) case x[0] === byTag.default: return z._default(x[1], {}) + case x[0] === byTag.prefault: return z.prefault(x[1], {}) case x[0] === byTag.intersection: return z.intersection(...x[1]) case x[0] === byTag.map: return z.map(x[1][0], x[1][1]) case x[0] === byTag.record: return z.record(z.string(), x[1]) diff --git a/packages/schema-zod-adapter/src/get-v4.ts b/packages/schema-zod-adapter/src/get-v4.ts new file mode 100644 index 00000000..117fa826 --- /dev/null +++ b/packages/schema-zod-adapter/src/get-v4.ts @@ -0,0 +1,42 @@ +import { z } from 'zod/v4' +import { fn } from '@traversable/registry' + +export { makeLenses } from './lens-types-v4.js' +import * as Lens from './lens-v4.js' +type Lens = import('./lens-v4.js').Lens +import * as Prism from './prism-v4.js' +type Prism = import('./prism-v4.js').Prism + +export type Result = Success | Failure +export declare namespace Result { type Async = Promise> } +export interface Success { value: T } +export interface Failure { issues: ReadonlyArray } +export interface Standard { validate: (value: unknown) => Result | Result.Async } + +export const fromStd = { + v1: (std) => fn.flow(std.validate, (result) => 'value' in result) +} satisfies Record(std: Standard) => (value: unknown) => boolean> + +export type fromObject< + Z extends z.core.$ZodObject, + S extends + | Z['_zod']['def']['shape'] + = Z['_zod']['def']['shape'] +> = never | { [K in keyof S]: Lens } + +export function fromObject(type: Z): fromObject +export function fromObject(type: Z) { + return Lens.fromShape(fn.map(type._zod.def.shape, (s) => s._zod.output)) +} + +export type fromUnion< + Z extends z.core.$ZodUnion, + S extends + | Z['_zod']['def']['options'] + = Z['_zod']['def']['options'] +> = { [K in keyof S]: Prism } + +export function fromUnion(type: Z): fromUnion +export function fromUnion(type: Z) { + return Prism.fromPatterns(...fn.map(type._zod.def.options, fn.flow((s) => s['~standard'], fromStd.v1))) +} diff --git a/packages/schema-zod-adapter/src/instances-v4.ts b/packages/schema-zod-adapter/src/instances-v4.ts new file mode 100644 index 00000000..81162933 --- /dev/null +++ b/packages/schema-zod-adapter/src/instances-v4.ts @@ -0,0 +1,310 @@ +import type { Box, Functor, TypeConstructor as F, Type as T } from '@traversable/registry' +import { fn, hom, Object_assign, Object_create, Object_keys, Object_values, symbol } from '@traversable/registry' + +/** @internal */ +const coerce + : (x: S) => T + = fn.identity + +/** @internal */ +export const Const: (x: T) => Box, _> = coerce + +function FreeApply(Sg: Semigroup): Apply> +function FreeApply(Sg: Semigroup) { + const ConstApply = Object_create>>(null) + ConstApply.ap = function Const_Apply_ap(ff, x) { return Const(Sg.concat(ff, x)) } + ConstApply.map = function Const_Apply_map() { return fn.identity } + return ConstApply +} + +function FreeApplicative(M: Monoid): Applicative> { + const Const_applicative = Object_create>>(null) + Const_applicative.ap = Apply.free(M).ap + Const_applicative.map = function Const_map() { return fn.identity } + Const_applicative.of = function Const_of() { return Const(M.empty) } + return Const_applicative +} + + +export interface Monoid extends Semigroup { empty: T } +export namespace Monoid { + export const staticArray = Object_create>(null) + staticArray.empty = Array.of() + staticArray.concat = (x, y) => x.concat(y) + export const array = (): Monoid => staticArray +} + +export interface Pointed { of(x: T): Box } +export namespace Pointed { + export const identity = Object_create>(null) + identity.of = fn.identity + + export const array = Object_create>(null) + array.of = globalThis.Array.of +} + + +export declare namespace Apply { + type target = F extends T.Record<(...args: any) => infer T> ? T.Record : never +} + +export interface Apply extends Functor { + ap(fns: Box T>, x: Box): Box +} + +export function Apply() {} +Apply.free = FreeApply + +export namespace Apply { + export const identity = Object_create>(null) + ; (identity as any)[symbol.tag] = 'Apply.identity' + identity.ap = function Identity_apply(f, x) { return f(x) } + identity.map = function Identity_map(f) { return (x) => f(x) } + + export const array = Object_create>(null) + ; (array as any)[symbol.tag] = 'Apply.array' + array.ap = function Array_apply(fns, xs) { return fns.flatMap((f) => xs.map(f)) } + array.map = function Array_map(f) { return (xs) => xs.map(f) } + + export const record = Object_create>(null) + ; (record as any)[symbol.tag] = 'Apply.record' + record.map = function Record_map(f) { return fn.map(f) } + record.ap = function Record_apply(fns, xs) { + const ks = Object_keys(xs) + let out: Apply.target = Object_create(null) + let k, f, x + while ((k = ks.shift()) !== undefined) { + f = fns[k] + x = xs[k] + out[k] = f(x) + } + return out + } +} + +export { Functor_ as Functor } +namespace Functor_ { + export const identity = Object_create>(null) + identity.map = function Identity_map(f) { return f } + + export const array = Object_create>(null) + array.map = function Array_map(f) { return (xs) => xs.map(f) } + + export const record = Object_create>(null) + record.map = function Record_map(f) { return fn.map(f) } +} + +export interface Applicative extends Pointed, Functor, Apply {} + +export function Applicative() {} +Applicative.free = FreeApplicative + +export namespace Applicative { + export const identity = Object_create>(null) + Object_assign(identity, Pointed.identity) + Object_assign(identity, Functor_.identity) + Object_assign(identity, Apply.identity) + + export const array = Object_create>(null) + Object_assign(array, Pointed.array) + Object_assign(array, Functor_.array) + Object_assign(array, Apply.array) +} + +export interface Foldable { + reduce(xs: Box, init: T, g: (acc: T, x: S) => T): T + reduceRight(xs: Box, init: T, g: (acc: T, x: S) => T): T + foldMap(monoid: Monoid): (xs: Box, g: (x: S) => M) => M +} + +export namespace Foldable { + export const identity = Object_create>(null) + identity.reduce = function Identity_reduce(x, init, g) { return g(init, x) } + identity.reduceRight = function Identity_reduceRight(x, init, g) { return g(init, x) } + identity.foldMap = function Identity_foldMap() { return (x, g) => g(x) } + + export const array = Object_create>(null) + array.reduce = function Array_reduce(xs, init, g) { return xs.reduce(g, init) } + array.reduceRight = function Array_reduceRight(xs, init, g) { return xs.reduceRight(g, init) } + array.foldMap = function Array_foldMap(M) { return (xs, g) => xs.reduce((acc, x) => M.concat(acc, g(x)), M.empty) } + + export const record = Object_create>(null) + record.reduce = function Record_reduce(xs, init, g) { return Object_values(xs).reduce(g, init) } + record.reduceRight = function Record_reduceRight(xs, init, g) { return Object_values(xs).reduceRight(g, init) } + record.foldMap = function Record_foldMap(M) { return (xs, g) => Object_values(xs).reduce((acc, x) => M.concat(acc, g(x)), M.empty) } +} + + +export interface Traversable extends Functor, Foldable { + sequence(F: Applicative): (xs: Box>) => Box> + traverse(F: Applicative): (xs: Box, f: (x: A) => Box) => Box> +} + +export namespace Traversable { + export const identity = Object_create>(null) + Object_assign(identity, Applicative.identity) + Object_assign(identity, Foldable.identity) + identity.sequence = (F) => F.map(fn.identity) + identity.traverse = (F) => (x, f) => F.map(fn.identity)(f(x)) + + export const array = Object_create>(null) + Object_assign(array, Applicative.array) + Object_assign(array, Foldable.array) + array.sequence = function Array_sequence(F) { + return (xs) => xs.reduce( + (acc, cur) => F.ap( + fn.pipe(acc, F.map((ts: unknown[]) => (t) => [...ts, t])), + cur + ), + F.of(Array.of()), + ) + } + array.traverse = function Array_traverse(F) { + return (xs, f) => xs.reduce( + (acc, cur) => F.ap( + fn.pipe(acc, F.map((ts) => (t) => [...ts, t])), + f(cur), + ), + F.of(Array.of()) + ) + } + + export const record = Object_create>(null) + Object_assign(record, Functor_.record) + Object_assign(record, Foldable.record) + record.sequence = function Record_sequence(F) { + return (xs) => { + const ks = Object_keys(xs).sort((l, r) => (l).localeCompare(r)) + const tensor = hom(ks) + let out = F.map(tensor)(xs[ks[0]]) + for (let ix = 1, len = ks.length; ix < len; ix++) { + out = F.ap(out, xs[ks[ix]]) + } + return out + } + } + record.traverse = function Record_traverse(F) { + return (xs, f) => { + const ks = Object_keys(xs).sort((l, r) => (l).localeCompare(r)) + let out = F.of({}) + if (ks.length === 0) return out + else for (let ix = 1, len = ks.length; ix < len; ix++) { + const k = ks[ix] + out = F.ap( + fn.pipe( + out, + F.map((ys) => (y) => Object_assign({}, ys, { [k]: y })), + ), + f(xs[k]) + ) + } + return out + } + } +} + + +export interface Semigroup { concat(x: T, y: T): T } + +export declare namespace Semigroup { + /** + * ## {@link Semigroup.Traversable `Semigroup.Traversable`} + * + * Relaxes the constraint on `F` by requiring an {@link Apply `Apply`}, + * rather than an {@link Applicative `link Applicative`} instance. + * + * Note that there's no free lunch here -- you'll still need to provide a way + * to handle the empty case (this isn't a free Applicative). + * + * The "upside" of using {@link Semigroup.Traversable} is that it enables inversion + * of control at the call-site, rather than having the constructor baked into + * the Functor. + * + * The "downside" is that the user needs to specify it, and could possibly mess it + * up if they don't understand the semantics of the Functor they're working with. + * + * See also: + * - [`Data.Semigroup.Traversable`](https://hackage.haskell.org/package/semigroupoids-6.0.1/docs/Data-Semigroup-Traversable.html) + */ + export interface Traversable extends Functor, Foldable { + sequenceMaybe(F: Apply): (xs: Box>, of: () => Box) => Box> + traverseMaybe(F: Apply): (xs: Box, f: (x: A) => Box, of: () => Box) => Box> + } +} + +export namespace Semigroup { + export const staticArray = Object_create>(null) + staticArray.concat = function Semigroup_Array_concat(x, y) { return x.concat(y) } + export const array = (): Semigroup => staticArray + + export const staticRecord = Object_create>(null) + staticRecord.concat = function Semigroup_Record_concat(x, y) { return { ...x, ...y } } + export const record = (): Semigroup<{ [x: string]: T }> => staticRecord + + export namespace Traversable { + export const identity = Object_create>(null) + Object_assign(identity, Apply.identity) + Object_assign(identity, Foldable.identity) + identity.sequenceMaybe = function Identity_sequenceMaybe(F) { return F.map(fn.identity) } + identity.traverseMaybe = function Identity_traverseMaybe(F) { return (x, f) => F.map(fn.identity)(f(x)) } + + export const array = Object_create>(null) + Object_assign(array, Apply.array) + Object_assign(array, Foldable.array) + array.map = function Semigroup_Traversable_Array_map(f) { return (xs) => xs.map(f) } + array.sequenceMaybe = function Array_sequenceMaybe(F) { + return (xs, of) => xs.reduce( + (acc, cur) => F.ap( + fn.pipe(acc, F.map((ts: unknown[]) => (t) => [...ts, t])), + cur + ), + of() + ) + } + array.traverseMaybe = function Array_traverseMaybe(F) { + return (xs, f, of) => xs.reduce( + (acc, cur) => F.ap( + fn.pipe(acc, F.map((ts: unknown[]) => (t) => [...ts, t])), + f(cur), + ), + of() + ) + } + + export const record = Object_create>(null) + Object_assign(record, Apply.record) + Object_assign(record, Foldable.record) + record.map = function Semigroup_Traversable_Record_map(f) { return fn.map(f) } + record.sequenceMaybe = function Record_sequenceMaybe(F) { + return (xs, of) => { + const ks = Object_keys(xs).sort((l, r) => (l).localeCompare(r)) + const tensor = hom(ks) + let out = F.map(tensor)(xs[ks[0]]) + for (let ix = 1, len = ks.length; ix < len; ix++) { + out = F.ap(out, xs[ks[ix]]) + } + return out + } + } + + record.traverseMaybe = function Record_traverseMaybe(F) { + return (xs, f, of) => { + + const ks = Object_keys(xs).sort((l, r) => (l).localeCompare(r)) + let out = of() + if (ks.length === 0) return out + else for (let ix = 1, len = ks.length; ix < len; ix++) { + const k = ks[ix] + out = F.ap( + fn.pipe( + out, + F.map((ys) => (y) => Object_assign({}, ys, { [k]: y })), + ), + f(xs[k]) + ) + } + } + } + } + +} diff --git a/packages/schema-zod-adapter/src/iso-v4.ts b/packages/schema-zod-adapter/src/iso-v4.ts new file mode 100644 index 00000000..b08524e2 --- /dev/null +++ b/packages/schema-zod-adapter/src/iso-v4.ts @@ -0,0 +1,56 @@ +import type { HKT } from '@traversable/registry' +import { fn, has, Object_assign, Object_create, symbol } from '@traversable/registry' + +type Iso = import('./optics-v4.js').Iso +import type { Optic } from './proxy-v4.js' + +export type Any = Iso +export interface Box extends HKT<{ [0]: unknown, [1]: unknown }> { [-1]: Iso } +export type infer = SA extends Iso ? [S: S, A: A] : never +export type inferSource = SA extends Iso ? S : never +export type inferTarget = SA extends Iso ? T : never + +export const identity = Object_create>(null) +identity[symbol.tag] = 'Iso' +identity.decode = fn.identity +identity.encode = fn.identity + +export const declare + : () => Iso + = fn.const(identity) + +export function is(x: Optic.Any): x is Iso +export function is(x: unknown): x is Iso +export function is(x: unknown) { + return ( + has(symbol.tag, (x) => x === identity[symbol.tag])(x) || ( + has('decode', (x) => typeof x === 'function')(x) + && has('encode', (x) => typeof x === 'function')(x) + ) + ) +} + +export { Iso_new as new } +function Iso_new( + decode: (s: S) => T, + encode: (t: T) => S +): Iso { + return Object_assign( + Object_create(null), + identity, { + decode, + encode, + }) +} + +export function compose(sa: Iso, ab: Iso): Iso +export function compose(sa: Iso, ab: Iso) { + return Iso_new( + fn.flow(sa.decode, ab.decode), + fn.flow(ab.encode, sa.encode), + ) satisfies Iso +} + +export const fromIso + : (iso: Iso) => Iso + = fn.identity diff --git a/packages/schema-zod-adapter/src/json-v4.ts b/packages/schema-zod-adapter/src/json-v4.ts index 39ba6787..13e72487 100644 --- a/packages/schema-zod-adapter/src/json-v4.ts +++ b/packages/schema-zod-adapter/src/json-v4.ts @@ -1,4 +1,4 @@ -import { z as zod4 } from 'zod4' +import { z as zod4 } from 'zod/v4' import type * as T from '@traversable/registry' import { fn, Number_isSafeInteger, Number_isNaN, parseKey, Print } from '@traversable/registry' import { Json } from '@traversable/json' diff --git a/packages/schema-zod-adapter/src/lens-types-v4.ts b/packages/schema-zod-adapter/src/lens-types-v4.ts new file mode 100644 index 00000000..a5779de5 --- /dev/null +++ b/packages/schema-zod-adapter/src/lens-types-v4.ts @@ -0,0 +1,894 @@ +////////////////////////// +/// ****@ts-nocheck 🤫 /// +/** + * **Note:** + * + * If you are going to make edits to this file, __make sure__ you + * remove the `@ts-nocheck` directive (above) until you're done. + * + * This module cuts a lot of deals with the "type-level devil", as + * it were, to optimize for IDE performance. + * + * Without them, the deep queries into zod schemas (which in v4 are + * are even deeper than they were in v3) gets prohibitive almost + * immediately. + */ + + +import { z } from 'zod/v4' +import type { Box, Boxed, Force, Kind, HKT, newtype, TypeConstructor as Type, inline, Key, Showable } from '@traversable/registry' +import { Either, get, Option, set } from '@traversable/registry' +import * as Lens from './lens-v4.js' +type Lens = import('./lens-v4.js').Lens +import * as Prism from './prism-v4.js' +type Prism = import('./prism-v4.js').Prism + +export type At = Kind +export type Apply = Meta extends [infer F extends HKT, ...infer T] ? Apply> : Out + +export type ApplyFocus + = KS extends [infer K extends keyof any, ...infer KS] + ? K extends keyof T ? ApplyFocus + : K extends `|${infer I extends number & keyof T}|` ? ApplyFocus + + : K extends `::${infer H}` ? [T: T, H: H, KS: KS] + + : K extends Op[keyof Op] ? ApplyFocus : T, KS> + : ApplyFocus + : T['_output'] + + +export type OutputOf + = KS extends [infer K extends keyof any, ...infer KS] + ? K extends keyof T ? OutputOf + : K extends `|${infer I extends number & keyof T}|` ? OutputOf + + : K extends `::${infer H}` ? OutputOf } } }>, KS, Queue> + // OutputOf<{ _output: Extract> }, KS, Queue> + + : K extends Op[keyof Op] ? OutputOf : T, KS, [Operator[K], ...Queue]> + : OutputOf + : 0 extends Queue['length'] ? T['_output'] + : Apply + +export type Op = typeof Op +export declare const Op: { + optional: '?' + nonoptional: '!' + array: '[number]' + record: '[string]' + member: `|number|` + // index: `[${number}]` +} + +type Operator = { + [Op.optional]: Lazy.Optional + [Op.array]: Lazy.Array + [Op.record]: Lazy.Id + [Op.member]: Lazy.Maybe + // [x: typeof Op.index]: Lazy.Array +} + +export type Map = { + union: Lazy.UnionTag + optional: Lazy.OptionalTag + object: Lazy.ObjectTag + tuple: Lazy.TupleTag + array: Lazy.ArrayTag + record: Lazy.RecordTag + // intersection: Lazy.IntersectionTag +} + +export declare namespace Lazy { + type Array = Type.Array + type Id = Type.Identity + type Maybe = Type.Option + interface Optional extends HKT { [-1]: Option> } + interface OptionalIn extends HKT { [-1]: Exclude } + interface ObjectTag extends HKT<{ _zod: { def: { shape: unknown } } }> { [-1]: [this[0]['_zod']['def']['shape'], ['_zod', 'def', 'shape']] } + interface TupleTag extends HKT<{ _zod: { def: { items: unknown } } }> { [-1]: [this[0]['_zod']['def']['items'], ['_zod', 'def', 'items']] } + interface OptionalTag extends HKT<{ _zod: { def: { innerType: unknown } } }> { [-1]: [FocusOptional, ['_zod', 'def', 'innerType']] } + interface NonOptionalTag extends HKT<{ _zod: { def: { innerType: unknown } } }> { [-1]: [FocusNonOptional, ['_zod', 'def', 'innerType']] } + interface ArrayTag extends HKT<{ _zod: { def: { element: unknown } } }> { [-1]: [FocusArray, ['_zod', 'def', 'element']] } + interface RecordTag extends HKT { [-1]: [FocusRecord, ['valueType']] } + + type FocusOptional = { [Op.optional]: T } + type FocusNonOptional = { [Op.nonoptional]: T } + type FocusArray = { [Op.array]: T, [index: `[${number}]`]: T } + type FocusRecord + = string extends K ? { [Op.record]: V } + : number extends K ? { [Op.array]: V } + : { [P in K]: V } + + interface UnionTag extends HKT { + [-3]: this[0]['_zod']['def']['options'] + [-2]: this[-3][number]['_zod']['def']['shape'] + [-1]: [keyof this[-2]] extends [never] + ? [FocusUnion, ['_zod', 'def', 'options']] + : [FocusTaggedUnion, ['_zod', 'def', 'options', number,]] + } + type FocusUnion = never | { [K in Extract as `|${K}|`]: T[K] } + type FocusTaggedUnion = never | { [K in (T[keyof T]['_output']) as `::${K}`]: z.core.$ZodObject>, {}, {}> } +} + +const Schema = z.object({ + A: z.array( + z.union([ + z.object({ tag: z.literal('B'), b: z.string(), }), + z.object({ tag: z.literal('C'), c: z.union([z.number(), z.boolean()]), }), + ]) + ), +}) + +export type zs2 = Kind +// ^? + +// const xcd = makeLens(Schema, 'A', '[number]', '::B', 'b') +// ^? + + +type xs2 = Kind +// ^? +type ys2 = Kind +// ^? +type xs = Lazy.FocusUnion +// ^? +type ys = Lazy.FocusUnion +// ^? +type zs = Lazy.FocusUnion +// ^? +const xs = z.union([z.object({ a: z.literal(1) }), z.object({ b: z.literal(2) })]) +const ys = z.union([z.number(), z.string()]) +const zs = z.union([z.object({ tag: z.literal('A'), one: z.number() }), z.object({ tag: z.literal('B'), two: z.string() })]) + +export declare function makeLens(type: Z, ...keys: [never, ...never]): never + +export declare function makeLens< + Focus extends At, + K01 extends keyof A[0], + K02 extends keyof B[0], + K03 extends keyof C[0], + K04 extends keyof D[0], + K05 extends keyof E[0], + K06 extends keyof F[0], + K07 extends keyof G[0], + K08 extends keyof H[0], + K09 extends keyof I[0], + K10 extends keyof J[0], + K11 extends keyof K[0], + K12 extends keyof L[0], + K13 extends keyof M[0], + K14 extends keyof N[0], + K15 extends keyof O[0], + K16 extends keyof Focus[0], + Z extends z.ZodType, + A extends At, + B extends At, + C extends At, + D extends At, + E extends At, + F extends At, + G extends At, + H extends At, + I extends At, + J extends At, + K extends At, + L extends At, + M extends At, + N extends At, + O extends At, + Path = [ + ...A[1], K01, + ...B[1], K02, + ...C[1], K03, + ...D[1], K04, + ...E[1], K05, + ...F[1], K06, + ...G[1], K07, + ...H[1], K08, + ...I[1], K09, + ...J[1], K10, + ...K[1], K11, + ...L[1], K12, + ...M[1], K13, + ...N[1], K14, + ...O[1], K15, + ...Focus[1], K16, + ], + Out = OutputOf +>(type: Z, ...k: [K01, K02, K03, K04, K05, K06, K07, K08, K09, K10, K11, K12, K13, K14, K15, K16]): Lens, Out> + +export declare function makeLens< + Focus extends At, + K01 extends keyof A[0], + K02 extends keyof B[0], + K03 extends keyof C[0], + K04 extends keyof D[0], + K05 extends keyof E[0], + K06 extends keyof F[0], + K07 extends keyof G[0], + K08 extends keyof H[0], + K09 extends keyof I[0], + K10 extends keyof J[0], + K11 extends keyof K[0], + K12 extends keyof L[0], + K13 extends keyof M[0], + K14 extends keyof N[0], + K15 extends keyof Focus[0], + Z extends z.ZodType, + A extends At, + B extends At, + C extends At, + D extends At, + E extends At, + F extends At, + G extends At, + H extends At, + I extends At, + J extends At, + K extends At, + L extends At, + M extends At, + N extends At, + Path = [ + ...A[1], K01, + ...B[1], K02, + ...C[1], K03, + ...D[1], K04, + ...E[1], K05, + ...F[1], K06, + ...G[1], K07, + ...H[1], K08, + ...I[1], K09, + ...J[1], K10, + ...K[1], K11, + ...L[1], K12, + ...M[1], K13, + ...N[1], K14, + ...Focus[1], K15, + ], + Out = OutputOf +>(type: Z, ...k: [K01, K02, K03, K04, K05, K06, K07, K08, K09, K10, K11, K12, K13, K14, K15]): Lens, Out> + +export declare function makeLens< + Focus extends At, + K01 extends keyof A[0], + K02 extends keyof B[0], + K03 extends keyof C[0], + K04 extends keyof D[0], + K05 extends keyof E[0], + K06 extends keyof F[0], + K07 extends keyof G[0], + K08 extends keyof H[0], + K09 extends keyof I[0], + K10 extends keyof J[0], + K11 extends keyof K[0], + K12 extends keyof L[0], + K13 extends keyof M[0], + K14 extends keyof Focus[0], + Z extends z.ZodType, + A extends At, + B extends At, + C extends At, + D extends At, + E extends At, + F extends At, + G extends At, + H extends At, + I extends At, + J extends At, + K extends At, + L extends At, + M extends At, + Path = [ + ...A[1], K01, + ...B[1], K02, + ...C[1], K03, + ...D[1], K04, + ...E[1], K05, + ...F[1], K06, + ...G[1], K07, + ...H[1], K08, + ...I[1], K09, + ...J[1], K10, + ...K[1], K11, + ...L[1], K12, + ...M[1], K13, + ...Focus[1], K14, + ], + Out = OutputOf +>(type: Z, ...k: [K01, K02, K03, K04, K05, K06, K07, K08, K09, K10, K11, K12, K13, K14]): Lens, Out> + +export declare function makeLens< + Focus extends At, + K01 extends keyof A[0], + K02 extends keyof B[0], + K03 extends keyof C[0], + K04 extends keyof D[0], + K05 extends keyof E[0], + K06 extends keyof F[0], + K07 extends keyof G[0], + K08 extends keyof H[0], + K09 extends keyof I[0], + K10 extends keyof J[0], + K11 extends keyof K[0], + K12 extends keyof L[0], + K13 extends keyof Focus[0], + Z extends z.ZodType, + A extends At, + B extends At, + C extends At, + D extends At, + E extends At, + F extends At, + G extends At, + H extends At, + I extends At, + J extends At, + K extends At, + L extends At, + Path = [ + ...A[1], K01, + ...B[1], K02, + ...C[1], K03, + ...D[1], K04, + ...E[1], K05, + ...F[1], K06, + ...G[1], K07, + ...H[1], K08, + ...I[1], K09, + ...J[1], K10, + ...K[1], K11, + ...L[1], K12, + ...Focus[1], K13, + ], + Out = OutputOf +>(type: Z, ...k: [K01, K02, K03, K04, K05, K06, K07, K08, K09, K10, K11, K12, K13]): Lens, Out> + +export declare function makeLens< + Focus extends At, + K01 extends keyof A[0], + K02 extends keyof B[0], + K03 extends keyof C[0], + K04 extends keyof D[0], + K05 extends keyof E[0], + K06 extends keyof F[0], + K07 extends keyof G[0], + K08 extends keyof H[0], + K09 extends keyof I[0], + K10 extends keyof J[0], + K11 extends keyof K[0], + K12 extends keyof Focus[0], + Z extends z.ZodType, + A extends At, + B extends At, + C extends At, + D extends At, + E extends At, + F extends At, + G extends At, + H extends At, + I extends At, + J extends At, + K extends At, + Path = [ + ...A[1], K01, + ...B[1], K02, + ...C[1], K03, + ...D[1], K04, + ...E[1], K05, + ...F[1], K06, + ...G[1], K07, + ...H[1], K08, + ...I[1], K09, + ...J[1], K10, + ...K[1], K11, + ...Focus[1], K12, + ], + Out = OutputOf +>(type: Z, ...k: [K01, K02, K03, K04, K05, K06, K07, K08, K09, K10, K11, K12]): Lens, Out> + +export declare function makeLens< + Focus extends At, + K01 extends keyof A[0], + K02 extends keyof B[0], + K03 extends keyof C[0], + K04 extends keyof D[0], + K05 extends keyof E[0], + K06 extends keyof F[0], + K07 extends keyof G[0], + K08 extends keyof H[0], + K09 extends keyof I[0], + K10 extends keyof J[0], + K11 extends keyof Focus[0], + Z extends z.ZodType, + A extends At, + B extends At, + C extends At, + D extends At, + E extends At, + F extends At, + G extends At, + H extends At, + I extends At, + J extends At, + Path = [ + ...A[1], K01, + ...B[1], K02, + ...C[1], K03, + ...D[1], K04, + ...E[1], K05, + ...F[1], K06, + ...G[1], K07, + ...H[1], K08, + ...I[1], K09, + ...J[1], K10, + ...Focus[1], K11, + ], + Out = OutputOf +>(type: Z, ...k: [K01, K02, K03, K04, K05, K06, K07, K08, K09, K10, K11]): Lens, Out> + +export declare function makeLens< + Focus extends At, + K01 extends keyof A[0], + K02 extends keyof B[0], + K03 extends keyof C[0], + K04 extends keyof D[0], + K05 extends keyof E[0], + K06 extends keyof F[0], + K07 extends keyof G[0], + K08 extends keyof H[0], + K09 extends keyof I[0], + K10 extends keyof Focus[0], + Z extends z.ZodType, + A extends At, + B extends At, + C extends At, + D extends At, + E extends At, + F extends At, + G extends At, + H extends At, + I extends At, + Path = [ + ...A[1], K01, + ...B[1], K02, + ...C[1], K03, + ...D[1], K04, + ...E[1], K05, + ...F[1], K06, + ...G[1], K07, + ...H[1], K08, + ...I[1], K09, + ...Focus[1], K10, + ], + Out = OutputOf +>(type: Z, ...k: [K01, K02, K03, K04, K05, K06, K07, K08, K09, K10]): Lens, Out> + +export declare function makeLens< + Focus extends At, + K1 extends keyof A[0], + K2 extends keyof B[0], + K3 extends keyof C[0], + K4 extends keyof D[0], + K5 extends keyof E[0], + K6 extends keyof F[0], + K7 extends keyof G[0], + K8 extends keyof H[0], + K9 extends keyof Focus[0], + Z extends z.ZodType, + A extends At, + B extends At, + C extends At, + D extends At, + E extends At, + F extends At, + G extends At, + H extends At, + Path = [ + ...A[1], K1, + ...B[1], K2, + ...C[1], K3, + ...D[1], K4, + ...E[1], K5, + ...F[1], K6, + ...G[1], K7, + ...H[1], K8, + ...Focus[1], K9, + ], + Out = OutputOf +>(type: Z, ...k: [K1, K2, K3, K4, K5, K6, K7, K8, K9]): Lens, Out> + +export declare function makeLens< + Focus extends At, + K1 extends keyof A[0], + K2 extends keyof B[0], + K3 extends keyof C[0], + K4 extends keyof D[0], + K5 extends keyof E[0], + K6 extends keyof F[0], + K7 extends keyof G[0], + K8 extends keyof Focus[0], + Z extends z.ZodType, + A extends At, + B extends At, + C extends At, + D extends At, + E extends At, + F extends At, + G extends At, + Path = [ + ...A[1], K1, + ...B[1], K2, + ...C[1], K3, + ...D[1], K4, + ...E[1], K5, + ...F[1], K6, + ...G[1], K7, + ...Focus[1], K8, + ], + Out = OutputOf +>(type: Z, ...k: [K1, K2, K3, K4, K5, K6, K7, K8]): Lens, Out> + +export declare function makeLens< + Focus extends At, + K1 extends keyof A[0], + K2 extends keyof B[0], + K3 extends keyof C[0], + K4 extends keyof D[0], + K5 extends keyof E[0], + K6 extends keyof F[0], + K7 extends keyof Focus[0], + Z extends z.ZodType, + A extends At, + B extends At, + C extends At, + D extends At, + E extends At, + F extends At, + Path = [ + ...A[1], K1, + ...B[1], K2, + ...C[1], K3, + ...D[1], K4, + ...E[1], K5, + ...F[1], K6, + ...Focus[1], K7, + ], + Out = OutputOf +>(type: Z, ...k: [K1, K2, K3, K4, K5, K6, K7]): Lens, Out> + +export declare function makeLens< + Focus extends At, + K1 extends keyof A[0], + K2 extends keyof B[0], + K3 extends keyof C[0], + K4 extends keyof D[0], + K5 extends keyof E[0], + K6 extends keyof Focus[0], + Z extends z.ZodType, + A extends At, + B extends At, + C extends At, + D extends At, + E extends At, + Path = [ + ...A[1], K1, + ...B[1], K2, + ...C[1], K3, + ...D[1], K4, + ...E[1], K5, + ...Focus[1], K6, + ], + Out = OutputOf +>(type: Z, ...k: [K1, K2, K3, K4, K5, K6]): Lens, Out> + +export declare function makeLens< + Focus extends At, + K1 extends keyof A[0], + K2 extends keyof B[0], + K3 extends keyof C[0], + K4 extends keyof D[0], + K5 extends keyof Focus[0], + Z extends z.ZodType, + A extends At, + B extends At, + C extends At, + D extends At, + Path = [ + ...A[1], K1, + ...B[1], K2, + ...C[1], K3, + ...D[1], K4, + ...Focus[1], K5 + ], + Out = OutputOf +>(type: Z, ...k: [K1, K2, K3, K4, K5]): Lens, Out> + +export declare function makeLens< + Focus extends At, + K1 extends keyof A[0], + K2 extends keyof B[0], + K3 extends keyof C[0], + K4 extends keyof Focus[0], + Z extends z.ZodType, + A extends At, + B extends At, + C extends At, + Path = [ + ...A[1], K1, + ...B[1], K2, + ...C[1], K3, + ...Focus[1], K4 + ], + Out = OutputOf +>(type: Z, ...k: [K1, K2, K3, K4]): Lens, Out> + +export declare function makeLens< + Focus extends At, + K1 extends keyof A[0], + K2 extends keyof B[0], + K3 extends keyof Focus[0], + Z extends z.ZodType, + A extends At, + B extends At, + Path = [ + ...A[1], K1, + ...B[1], K2, + ...Focus[1], K3 + ], + Out = OutputOf +>(type: Z, ...k: [K1, K2, K3]): Lens, Out> + + +export declare function makeLens< + Focus extends At, + K1 extends keyof A[0], + K2 extends keyof Focus[0], + A extends At, + Z extends z.ZodType, + Path = [ + ...A[1], K1, + ...Focus[1], K2 + ], + Out = OutputOf +>(type: Z, ...k: [K1, K2]): Focus[0][K2] +// Lens, Out> + +export declare function makeLens< + Focus extends At, + K1 extends keyof Focus[0], + Z extends z.ZodType, + Path = [ + ...Focus[1], K1 + ], + Out = OutputOf +>(type: Z, ...k: [K1]): Lens, Out> + +export declare function makeLens(type: Z): Lens, z.infer> + + + + + + + + +// type Optional = Z.get.type extends 'optional' ? `${Key}?` : K + +type Optional = `${K}?` + +declare const Z: { + literal: Z.Literal + optional: Z.Optional + array: Z.Array + record: Z.Record + union: Z.Union + tuple: Z.Tuple + object: Z.Object +} + +declare namespace Z { + namespace get { + type output = T['_output'] + type zodOutput = T['_zod']['output'] + type type = T['_zod']['def']['type'] + type innerType = T['_zod']['def']['innerType'] + type element = T['_zod']['def']['element'] + type keyType = T['_zod']['def']['keyType'] + type valueType = T['_zod']['def']['valueType'] + type options = T['_zod']['def']['options'] + type shape = T['_zod']['def']['shape'] + type items = T['_zod']['def']['items'] + } + + type infer = T['_output' & keyof T] + interface Type { _output: T } + interface Indexed { [x: number]: Z.Typed } + interface Named { [x: string]: Z.Typed } + type Ordered = readonly Z.Typed[] + interface Infer< + T extends + | { _output: unknown } + = { _output: unknown } + > { _output: T['_output'] } + interface Typed< + T extends + | { _zod: { def: { type: unknown } } } + = { _zod: { def: { type: unknown } } } + > { _zod: { def: { type: T['_zod']['def']['type'] } } } + // + interface Literal< + T extends + | { _zod: { output: Showable } } + = { _zod: { output: Showable } } + > { _zod: { output: T['_zod']['output'] } } + interface Optional< + T extends + | { _zod: { def: { innerType: Z.Typed } } } + = { _zod: { def: { innerType: Z.Typed } } } + > { _zod: { def: { innerType: T['_zod']['def']['innerType'] } } } + interface Array< + T extends + | { _zod: { def: { element: Z.Typed } } } + = { _zod: { def: { element: Z.Typed } } } + > { _zod: { def: { element: T['_zod']['def']['element'] } } } + interface Record< + T extends + | { _zod: { def: { keyType: Z.Type, valueType: Z.Typed } } } + = { _zod: { def: { keyType: Z.Type, valueType: Z.Typed } } } + > { _zod: { def: { keyType: T['_zod']['def']['keyType'], valueType: T['_zod']['def']['valueType'] } } } + interface Union< + T extends + | { _zod: { def: { options: Z.Indexed } } } + = { _zod: { def: { options: Z.Indexed } } } + > { _zod: { def: { options: T['_zod']['def']['options'] } } } + interface Tuple< + T extends + | { _zod: { def: { items: Z.Ordered } } } + = { _zod: { def: { items: Z.Ordered } } } + > { _zod: { def: { items: T['_zod']['def']['items'] } } } + interface Object< + T extends + | { _zod: { def: { shape: Z.Named } } } + = { _zod: { def: { shape: Z.Named } } } + > { _zod: { def: { shape: T['_zod']['def']['shape'] } } } +} + +// interface ProxyNullary extends HKT<{ _output: unknown }> { [-1]: Z.get.output } +// interface ProxyLiteral extends HKT { [-1]: Z.get.zodOutput } +// interface ProxyOptional extends HKT { +// [-3]: this[0]['_zod']['def']['innerType'], +// [-2]: this[-3]['_zod']['def']['type'] +// [-1]: this[-2] +// // Kind & keyof ProxyMap], this[-2]> +// } +// interface ProxyArray extends HKT { [-1]: ProxyElement> } +// type ProxyElement = T +// interface ProxyRecord extends HKT { +// [-1]: [ProxyKey>, ProxyValue>] +// } +// type ProxyKey = T +// type ProxyValue = T +// interface ProxyUnion extends HKT { [-1]: ProxyMembers> } +// type ProxyMembers = { [I in keyof T]: T[I] } +// interface ProxyTuple extends HKT { [-1]: ProxyItems> } +// type ProxyItems = never | { [I in keyof T]: T[I] } +// interface ProxyObject extends HKT { [-1]: ProxyShape } +// type ProxyShape = never | { [P in keyof T as Optional]: typeOf } +// type ProxyMap = { +// never: ProxyNullary +// unknown: ProxyNullary +// any: ProxyNullary +// void: ProxyNullary +// undefined: ProxyNullary +// null: ProxyNullary +// boolean: ProxyNullary +// number: ProxyNullary +// string: ProxyNullary +// literal: ProxyLiteral +// optional: ProxyOptional +// array: ProxyArray +// record: ProxyRecord +// union: ProxyUnion +// tuple: ProxyTuple +// object: ProxyObject +// } + +type MakeProxy = typeOf + +type typeOf + = [T] extends [Z.Object] ? t.object + : [T] extends [Z.Optional] ? t.optional + : [T] extends [Z.Tuple] ? typeOfItems + : [T] extends [Z.Union] ? UnionType + : [T] extends [Z.Record] ? Record> + : [T] extends [Z.Array] ? t.array + : T['_output' & keyof T] + +declare namespace t { + export { t_object as object } +} +declare namespace t { + interface t_object extends newtype<{ [K in keyof S]: typeOf }> {} + interface union extends newtype<{ [I in keyof S]: typeOf }> {} + interface prism extends newtype<{ [E in T as E[0]]: { [K in keyof E[1]]: typeOf } }> {} + + interface optional { + ʔ: typeOf + ǃ: typeOf + } + interface array { + ↆ: typeOf + map(f: (x: typeOf) => T): T + } +} + + +type typeOfItems = { [I in Extract]: typeOf } + +type UnionType< + T extends Z.Indexed, + K extends keyof any = keyof T[number]['_zod']['def']['shape'], + Disc extends keyof any = T[number]['_zod']['def']['shape'][K]['_output'] +> + = [K] extends [never] ? never | t.union + : never | t.prism>] : never> +// { [P in Disc]: Extract> extends infer U ? { [K in keyof U]: typeOf } : never } +// : { [P in Disc]: Extract> extends infer U ? { [K in keyof U]: typeOf } : never } + + + +declare function makeProxy>(type: Z, selector: (proxy: P) => T): (data: Z.infer) => Force + +// type __3 = typeOfOptions<[z.ZodObject<{ a: z.ZodLiteral<'A'>, b: z.ZodString }>, z.ZodObject<{ a: z.ZodLiteral<'B'>, c: z.ZodArray }>]> +// type __4 = typeOfOptions<[z.ZodObject<{ a: z.ZodLiteral<'A'> }>, z.ZodObject<{ b: z.ZodLiteral<'B'> }>]> + +// const zsxs = makeProxy( +// z.union([ +// z.object({ +// tag: z.literal('A'), +// /** a stuff */ +// a: z.optional(z.array(z.string())), +// }), +// z.object({ +// tag: z.literal('B'), +// /** b stuff */ +// b: z.optional( +// z.object({ +// c: z.optional(z.null()), +// }) +// ) +// }), +// ]), +// (xs) => xs +// // .B.b.ʔ.c.ǃ +// ) + +// zsxs({ tag: 'A' }).A + +// const xyss = makeProxy( +// z.union([ +// z.object({ +// tag: z.literal('A'), +// a: z.tuple([z.literal(1)]), +// }), +// z.object({ +// tag: z.literal('B'), +// b: z.optional(z.boolean()) +// }), +// ]), +// ($) => $.B.b +// ) + +function prop(key: K): (s: S) => [S[K], () => S] +function prop(key: K) { + return (value: S) => [get(value, [key]), () => value] +} + +function match(predicate: (s: S) => s is A) { + return (value: S) => [ + Either.fromPredicate(predicate)(value), + () => value + ] satisfies [any, any] +} diff --git a/packages/schema-zod-adapter/src/lens-v4.ts b/packages/schema-zod-adapter/src/lens-v4.ts new file mode 100644 index 00000000..7dd3bc5f --- /dev/null +++ b/packages/schema-zod-adapter/src/lens-v4.ts @@ -0,0 +1,168 @@ +import type { HKT } from '@traversable/registry' +import type { Homomorphism } from '@traversable/registry' +import { fn, get, has, Object_assign, Object_create, Object_fromEntries, set, symbol } from '@traversable/registry' + +import type { Iso } from './optics-v4.js' +import type { Optic } from './proxy-v4.js' + +type Lens = import('./optics-v4.js').Lens + +export type Any = Lens +export type LowerBound = [Lens] extends [T] ? unknown : never + +export type { Infer as infer, Source as source, Target as target } +type Infer = SA extends Lens ? [S: S, A: A] : never +type Source = SA extends Lens ? S : never +type Target = SA extends Lens ? T : never +export type { F as Box } +interface F extends HKT<{ [0]: unknown, [1]: unknown }> { [-1]: Lens } +export type UnknownSource = unknown extends Source ? {} | null | undefined : never + +export const identity = Object_create(null) +identity[symbol.tag] = 'Lens' +identity.get = fn.identity +identity.set = fn.const(fn.identity) + +export const declare + : () => Lens + = fn.const(identity) + +export function is(x: Optic.Any): x is Lens +export function is(x: unknown): x is Lens +export function is(x: unknown) { + return ( + has(symbol.tag, (x) => x === identity[symbol.tag])(x) || ( + has('get', (x) => typeof x === 'function')(x) + && has('set', (x) => typeof x === 'function')(x) + ) + ) +} + +export { Lens_new as new } +function Lens_new( + get: (s: S) => A, + set: (a: A) => (s: S) => S +): Lens { + return Object_assign(Object_create(null), identity, { get, set }) +} + +export function compose(sa: Lens, ab: Lens): Lens { + return Lens_new( + (s) => ab.get(sa.get(s)), + (b) => (s) => sa.set(ab.set(b)(sa.get(s)))(s) + ) +} + +export const fromLens + : (lens: Lens) => Lens + = fn.identity + +export function fromIso(iso: Iso): Lens { + return Lens_new( + iso.decode, + fn.flow(iso.encode, fn.const) + ) +} + +export function prop>(lens: SA, k: K): Lens> +export function prop, A extends Target>(lens: SA, k: K): Lens +export function prop(k: K): () => Lens +export function prop(k: K): () => Lens +export function prop( + ...args: + | [k: keyof any] + | [lens: Any, k: keyof any] +): {} { + const [sa, k] = args.length === 1 ? [declare(), args[0]] : args + const lens = () => Lens_new( + (s) => sa.get(s)[k], + (a) => (s) => Object_assign( + Object_create(null), + s, + { [k]: a }, + ) + ) + return args.length === 1 ? lens : lens() +} + +export function fromProp(k: K): (lens: Lens) => Lens { + return (lens) => Lens_new( + (s) => lens.get(s)[k], + (a) => (s) => Object_assign( + Object_create<{ [P in K]: unknown }>(null), + s, + { [k]: a }, + ) + ) +} + +export function props(ks: readonly [K, K, ...K[]]): (lens: Lens) => Lens +export function props(ks: readonly K[]): (lens: Lens) => Lens +export function props(ks: readonly K[]): (lens: Lens) => Lens { + return (lens) => Lens_new( + (s) => { + const source = lens.get(s) + const target = Object_create<{ [P in K]: A[P] }>(null) + for (const k of ks) target[k] = source[k] + return target + }, + (next) => (s) => { + const prev = lens.get(s) + for (const k of ks) { + if (next[k] !== prev[k]) + return lens.set( + Object.assign( + Object_create(null), + prev, + next, + ) + )(s) + } + return s + } + ) +} + +export function homomorphic(lens: Lens): Homomorphism> +export function homomorphic(lens: Lens) { return fn.map(fn.const(lens)) } + +export type fromShape = never | { [K in keyof S]: Lens } +export type fromKeys = never | { [P in K]: Lens<{ [P in K]: V }, V> } +export type fromEntries = fromShape + +/** + * ## {@link fromShape `Lens.fromShape`} + * + * Given an arbitrary object, {@link fromShape `Lens.fromShape`} maps + * over the object's keys to create a new object whose values are + * lenses that focus from a structure of the same overall shape to + * that particular property. + * + * @example + * + */ +export function fromShape(shape: S): fromShape +export function fromShape(shape: {}) { return fn.map(shape, (_, k) => Lens_new(get.lazy(k), set.lazy(k))) } + +export const fromEntries + : (entries: [...T[]]) => fromEntries<[...T]> + = fn.flow( + Object_fromEntries, + fromShape, + ) as typeof fromEntries + +export const fromKey + : (k: K) => () => Lens + = ((k) => fn.pipe( + Lens_new(get.lazy(k), set.lazy(k)), + fn.const, + )) as typeof fromKey + +export function fromKeys(...keys: [...K[]]): () => fromKeys +export function fromKeys(...keys: [...K[]]) { + return fn.pipe( + fn.map(keys, (k) => [k, void 0] satisfies [keyof any, any]), + fromEntries, + fn.const, + ) +} diff --git a/packages/schema-zod-adapter/src/make-lenses-v4.ts b/packages/schema-zod-adapter/src/make-lenses-v4.ts new file mode 100644 index 00000000..7c9c1efb --- /dev/null +++ b/packages/schema-zod-adapter/src/make-lenses-v4.ts @@ -0,0 +1,486 @@ +// import type { z } from 'zod/v4' +import { z } from 'zod/v4' + +import type { Key, newtype, Showable } from '@traversable/registry' +import { Array_isArray, fn, has, Number_isSafeInteger, Number_parseInt, Object_assign, Object_create, Object_hasOwn, Object_keys, Object_values, symbol } from '@traversable/registry' +import * as Profunctor from './profunctor-optics-v4.js' +import { withDefault } from './with-default-v4.js' + +/** @internal */ + +export interface RegisterDSL {} +// /** TODO: set up module augmentation */ +// type Absorb = unknown extends S ? T : S +// export function registerDSL(dsl: T): { [K in keyof DSL]: Absorb } +// export function registerDSL>(dsl: T) { return setDSL(dsl) } +// function setDSL( +// DSL: { [K in keyof T]: T[K] } +// ): { [K in keyof DSL]: Absorb } +// function setDSL(DSL: { [K in keyof GlobalDSL]+?: string }) { +// fn.map(DSL, (v, k) => { GlobalDSL[k] = v as never }) +// return GlobalDSL +// } + + +type DSL = typeof DSL +const DSL = { + chainOptional: 'ʔ' as const, + coalesceOptional: 'ǃ' as const, + identity: 'Ⳇ' as const, + traverseArray: 'ᣔꓸꓸ' as const, + traverseRecord: 'ᣔꓸꓸ' as const, + traverseSet: 'ᣔꓸꓸ' as const, + unionPrefix: 'ꖛ' as const + // traverseRecord: 'ↆ' as const, + // traverseArray: 'ↆ' as const, +} + +type GlobalDSL = DSL +let GlobalDSL: { [K in keyof typeof DSL]: string } = { + chainOptional: DSL.chainOptional, + coalesceOptional: DSL.coalesceOptional, + identity: DSL.identity, + traverseArray: DSL.traverseArray, + traverseRecord: DSL.traverseRecord, + traverseSet: DSL.traverseSet, + unionPrefix: DSL.unionPrefix, +} satisfies { [K in keyof typeof DSL]: typeof DSL[K] } + +function getDSL() { return GlobalDSL } + +declare namespace Z { + type infer = T[0][symbol.type] + interface Type { _output: T } + interface Indexed { [x: number]: Z.Typed } + interface Named { [x: string]: Z.Typed } + type Objects = readonly Z.Object[] + type Ordered = readonly Z.Typed[] + interface Infer< + T extends + | { _output: unknown } + = { _output: unknown } + > { _output: T['_output'] } + interface Typed< + T extends + | { _zod: { def: { type: unknown } } } + = { _zod: { def: { type: unknown } } } + > { _zod: { def: { type: T['_zod']['def']['type'] } }, _output: unknown } + // + /** + * TODO: see if `z.set`'s valueType has been added to `_zod.def.valueType` in zod v3.25.0 + */ + interface Set { _zod: { def: { type: 'set' } }, _output: unknown } + interface Enum< + T extends + | { enum: { [x: string | number]: string | number }, output: string | number } + = { enum: { [x: string | number]: string | number }, output: string | number } + > { enum: T['enum'], output: string | number } + interface Literal< + T extends + | { _zod: { output: Showable } } + = { _zod: { output: Showable } } + > { _zod: { output: T['_zod']['output'] }, _output: unknown } + interface Optional< + T extends + | { _zod: { def: { innerType: Z.Typed } } } + = { _zod: { def: { innerType: Z.Typed } } } + > { _zod: { def: { innerType: T['_zod']['def']['innerType'] } }, _output: unknown } + interface Array< + T extends + | { _zod: { def: { element: Z.Typed } } } + = { _zod: { def: { element: Z.Typed } } } + > { _zod: { def: { element: T['_zod']['def']['element'] } }, _output: unknown[] } + interface Record< + T extends + | { keyType: unknown, valueType: unknown, _zod: { def: { type: 'record' } }, _output: unknown } + = { keyType: unknown, valueType: unknown, _zod: { def: { type: 'record' } }, _output: unknown } + > { keyType: T['keyType'], valueType: T['valueType'], _zod: { def: { type: 'record' } }, _output: unknown } + interface Map< + T extends + | { keyType: unknown, valueType: unknown, _output: unknown, _zod: { def: { type: 'map' } } } + = { keyType: unknown, valueType: unknown, _output: unknown, _zod: { def: { type: 'map' } } } + > { keyType: T['keyType'], valueType: T['valueType'], _zod: { def: { type: 'map' } }, _output: unknown } + interface Union< + T extends + | { _zod: { def: { options: Z.Indexed } } } + = { _zod: { def: { options: Z.Indexed } } } + > { _zod: { def: { options: T['_zod']['def']['options'] } }, _output: unknown } + interface Tuple< + T extends + | { _zod: { def: { items: Z.Ordered } } } + = { _zod: { def: { items: Z.Ordered } } } + > { _zod: { def: { items: T['_zod']['def']['items'] } }, _output: unknown } + interface Object< + T extends + | { _zod: { def: { shape: Z.Named } } } + = { _zod: { def: { shape: Z.Named } } } + > { _zod: { def: { shape: T['_zod']['def']['shape'] } }, _output: unknown } +} + +export interface Witness { (proxy: unknown): { [PATH_KEY]: string[] } } + +const PATH_KEY = ' ~path' as const + +function getFallback(root: unknown, ...path: string[]): any { + let HEAD: keyof any | undefined + let CURSOR: unknown = root + while ((HEAD = path.shift()) !== undefined) { + switch (true) { + case HEAD === GlobalDSL.identity: continue + case HEAD === GlobalDSL.chainOptional: continue + case HEAD === GlobalDSL.coalesceOptional: continue + case HEAD.startsWith(GlobalDSL.unionPrefix): { + const OP = Number_parseInt(HEAD.slice(GlobalDSL.unionPrefix.length)) + if (Array_isArray(CURSOR) && Number_isSafeInteger(OP) && OP in CURSOR) { + CURSOR = CURSOR[OP] + break + } else { + continue + } + } + case HEAD === GlobalDSL.traverseSet && CURSOR instanceof globalThis.Set: { + if (CURSOR.size === 0) break + else { CURSOR = [...CURSOR.values()][0]; continue } + } + case HEAD === GlobalDSL.traverseArray && Array_isArray(CURSOR): { + if (CURSOR.length === 0) break + else { CURSOR = CURSOR[0]; continue } + } + case HEAD === GlobalDSL.traverseRecord && !!CURSOR && typeof CURSOR === 'object': { + const values = Object_values(CURSOR) + if (values.length === 0) break + else { CURSOR = values[0]; continue } + } + case has(HEAD)(CURSOR): CURSOR = CURSOR[HEAD]; continue + default: { + throw Error + ('\ + \ + \ + [make-lenses-v4.ts]: \ + \ + \'getFallback\' encountered a value it did not know how to interpret: \ + ' + + String(HEAD) + + '\ + \ + ' + ) + } + } + } + return CURSOR +} + +export interface RevocableProxy { + proxy: T + revoke(): void +} + +export function createProxy>(type: T): RevocableProxy

+export function createProxy(type: T) { + let state = Array.of() + let proxy: RevocableProxy | undefined = undefined + let handler = Object_create>(null) + handler.get = (target, key) => { + proxy?.revoke?.() + proxy = globalThis.Proxy.revocable( + Object_assign(target, Object_assign(Object_create(null), { [PATH_KEY]: state })), + handler + ) + if (key === PATH_KEY) { + proxy.revoke() + return state + } else { + proxy.revoke() + proxy = globalThis.Proxy.revocable( + Object_assign(target, Object_assign(Object_create(null), { [PATH_KEY]: state })), + handler + ) + Reflect.set(state, state.length, key) + return proxy.proxy + } + } + return globalThis.Proxy.revocable(Object_create(null), handler) +} + +const Get = { + Identity: (op: Profunctor.Optic) => function IdentityGet(data: unknown) { return Profunctor.get(op, data) }, + Iso: (op: Profunctor.Optic) => function IdentityGet(data: unknown) { return Profunctor.get(op, data) }, + Lens: (op: Profunctor.Optic) => function LensGet(data: unknown) { return Profunctor.get(op, data) }, + Traversal: (op: Profunctor.Optic) => function TraversalGet(data: unknown) { return Profunctor.collect(op)(data) }, + Prism: (op: Profunctor.Optic) => function PrismGet(data: unknown) { return Profunctor.preview(op, data) }, +} + +const Set = { + Identity: (op: Profunctor.Optic) => function IdentitySet(update: unknown, data: unknown) { Profunctor.set(op, update, data) }, + Iso: (op: Profunctor.Optic) => function IdentitySet(update: unknown, data: unknown) { return Profunctor.set(op, update, data) }, + Lens: (op: Profunctor.Optic) => function LensSet(update: unknown, data: unknown) { return Profunctor.set(op, update, data) }, + Traversal: (op: Profunctor.Optic) => function TraversalSet(update: unknown, data: unknown) { return Profunctor.set(op, update, data) }, + Prism: (op: Profunctor.Optic) => function PrismSet(update: unknown, data: unknown) { return Profunctor.set(op, update, data) }, +} + +const Modify = { + Identity: (op: Profunctor.Optic) => function IdentityModify(fn: (x: unknown) => unknown, data: unknown) { return Profunctor.modify(op, fn, data) }, + Iso: (op: Profunctor.Optic) => function IdentityModify(fn: (x: unknown) => unknown, data: unknown) { return Profunctor.modify(op, fn, data) }, + Lens: (op: Profunctor.Optic) => function LensModify(fn: (x: unknown) => unknown, data: unknown) { return Profunctor.modify(op, fn, data) }, + Traversal: (op: Profunctor.Optic) => function TraversalModify(fn: (x: unknown) => unknown, data: unknown) { return Profunctor.modify(op, fn, data) }, + Prism: (op: Profunctor.Optic) => function PrismModify(fn: (x: any) => unknown, data: unknown) { return Profunctor.modify(op, fn, data) }, +} + +function intersectKeys(...objects: [...T]): (keyof T[number])[] +function intersectKeys(...objects: [...T]) { + const [x, ...xs] = objects + let out = Object_keys(x) + let $: T[number] | undefined = x + while (($ = xs.shift()) !== undefined) + out = out.filter((k) => Object_hasOwn($, k)) + return out +} + +const findDiscrinimant = (tagKey: K, tagValue: V) => has( + '_zod', 'def', 'shape', tagKey, '_zod', 'def', 'values', + (set): set is never => Array_isArray(set) && set.includes(tagValue) +) + + +function interpreter(type: T, ...path: string[]) { + const rootFallback = withDefault(type) + const fallback = getFallback(rootFallback, ...path) + const empty = [Profunctor.id] + let schemaCursor: unknown = type + const optics = path.reduce( + (acc, cmd, ix, cmds) => { + switch (true) { + case cmd === GlobalDSL.identity: return (acc.push(Profunctor.id), acc) + case cmd === GlobalDSL.chainOptional: { + if (has('_zod', 'def', 'innerType')(schemaCursor)) { + schemaCursor = schemaCursor._zod.def.innerType + } + return ( + acc.push(Profunctor.when((x) => x != null)), + acc + ) + } + case cmd === GlobalDSL.coalesceOptional: { + const prev = acc[acc.length - 1]?.[symbol.tag] + if (has('_zod', 'def', 'innerType')(schemaCursor)) { + schemaCursor = schemaCursor._zod.def.innerType + } + /** + * If we get the 'coalesceOptional' command and the previous optic was a Lens, + * replace it with 'Profunctor.propOr' so we can apply the default value + */ + if (prev === 'Lens') + acc.pop() && acc.push(Profunctor.propOr(fallback, cmds[ix - 1])) + else + acc.push(Profunctor.when((x) => x != null)) + return acc + } + case cmd === GlobalDSL.traverseSet: + case cmd === GlobalDSL.traverseArray: + case cmd === GlobalDSL.traverseRecord: { + if (has('_zod', 'def', 'valueType')(schemaCursor)) { schemaCursor = schemaCursor._zod.def.valueType } + else if (has('_zod', 'def', 'element')(schemaCursor)) { schemaCursor = schemaCursor._zod.def.element } + return (acc.push(Profunctor.traverse), acc) + } + case cmd.startsWith(GlobalDSL.unionPrefix): { + if (!has('options', Array.isArray)(schemaCursor)) throw Error('no has options') + else if (schemaCursor.options.length === 0) { + throw Error('TODO: union has zero elements') + } + + const RAW = cmd.slice(GlobalDSL.unionPrefix.length) + const OP = Number_parseInt(RAW) + + if (Number_isSafeInteger(OP)) { + const schema = schemaCursor.options?.[OP] + if (!schema) throw Error('could not find schema at index: ' + OP) + + const predicate = (x: unknown) => schema.safeParse(x).success + acc.push(Profunctor.when(predicate)) + schemaCursor = schemaCursor.options[OP] + return acc + } + + else { + if (!schemaCursor.options.every(has('_zod', 'def', 'shape'))) { + throw Error('not all objects') + } + const tags = intersectKeys(...schemaCursor.options.map((opt) => opt._zod.def.shape)) + + if (tags.length !== 1) { + throw Error('did not have exactly 1 tag: [' + tags.join(', ') + ']') + } + + const [tag] = tags + const schema = schemaCursor.options.find(findDiscrinimant(tag, RAW)) + if (!schema) throw Error('could not find schema with discriminant ' + tag + ' whose value is ' + RAW) + const predicate = (x: unknown) => (schema as any).safeParse(x).success + + acc.push(Profunctor.when(predicate)) + schemaCursor = schema + return acc + } + + + } + case typeof cmd === 'string': { + if (has('_zod', 'def', 'shape', (x): x is { [x: string]: unknown } => !!x && typeof x === 'object')(schemaCursor)) { + schemaCursor = schemaCursor._zod.def.shape[cmd] + } + return (acc.push(Profunctor.prop(cmd)), acc) + } + case typeof cmd === 'number': return (acc.push(Profunctor.prop(cmd)), acc) + case typeof cmd === 'symbol': throw Error('Illegal state') + default: return fn.exhaustive(cmd) + } + }, + empty + ) + + const optic = Profunctor.pipe(...optics) + const tag = optic[symbol.tag] + const get = Get[tag](optic) + const set = Set[tag](optic) + const modify = Modify[tag](optic) + + return { + get, + set, + modify, + type: tag, + fallback, + } satisfies SchemaLens, Z.infer> +} + +interface Proxy_object extends newtype<{ [K in keyof S]: Proxy }> { [symbol.type]: T } + +interface Proxy_tuple extends newtype<{ [I in Extract]: Proxy }> { [symbol.type]: T } + +interface Proxy_union extends newtype<{ [I in Extract as `${GlobalDSL['unionPrefix']}${I}`]: Proxy }> { [symbol.type]: T } + +const sas = makeLens( + z.union([ + z.object({ tag: z.literal('A') }), + z.object({ tag: z.literal('B') }), + z.object({ tag: z.literal('C') }) + ]), + (proxy) => proxy.ꖛA + // ^? +) + +sas.modify(x => x.tag, { tag: 'A' }) +// ^? + +interface Proxy_disjointUnion extends newtype<{ + [E in S as `${GlobalDSL['unionPrefix']}${Key}`]: { [K in keyof E[1]]: Proxy } +}> { + [symbol.type]: T +} + +interface Proxy_optional { + [DSL.chainOptional]: Proxy + [DSL.coalesceOptional]: Proxy]> + [symbol.type]: T +} + +interface Proxy_array { + [DSL.traverseArray]: Proxy + [symbol.type]: T +} + +type Proxy_record + = S[0] extends { enum: { [x: string | number]: string | number } } + ? never | Proxy_finiteRecord + : never | Proxy_nonfiniteRecord + +interface Proxy_nonfiniteRecord { + [DSL.traverseRecord]: Proxy + [symbol.type]: T +} + +interface Proxy_finiteRecord extends newtype< + { [K in S[0]['enum'][keyof S[0]['enum']]]: Proxy } +> { + [DSL.traverseRecord]: Proxy + [symbol.type]: T +} + +interface Proxy_primitive { [symbol.type]: T } + +interface Proxy_set { + [DSL.traverseSet]: Proxy + [symbol.type]: T +} + +type Proxy_sumType< + S extends [any], + T, + _ extends S[0] = S[0], + K extends keyof any = keyof _[number]['_zod']['def']['shape'], + Disc extends keyof any = _[number]['_zod']['def']['shape'][K]['_output'] +> = [K] extends [never] + ? never | Proxy.union<_, T> + : never | Proxy.disjointUnion< + Disc extends Disc + ? [Disc, Extract<_[number]['_zod']['def']['shape'], Record>] + : never, + T + > + +type Proxy + = [S] extends [Z.Object] ? Proxy_object + : [S] extends [Z.Optional] ? Proxy_optional + : [S] extends [Z.Tuple] ? Proxy_tuple + : [S] extends [Z.Union] ? Proxy_sumType<[S['_zod']['def']['options']], T[0]['_output']> + : [S] extends [Z.Array] ? Proxy_array + : [S] extends [Z.Record] ? Proxy_record<[S['keyType'], S['valueType']], T[0]['_output']> + : [S] extends [Z.Set] ? Proxy_set ? V : never, T[0]['_output']> + : Proxy_primitive + +export interface SchemaLens { + get(data: S): A + set(focus: A, data: S): S + modify(fn: (focus: A) => B, source: S): S + type: Profunctor.Optic.Type + fallback: withDefault +} + +export function makeLens< + Type extends z.ZodType, + Proxy extends Proxy.new, + Target, + Focus = Proxy +>( + type: Type, + selector: (proxy: Proxy) => Target +): SchemaLens, Z.infer> +// ): SchemaLens, Z.infer> + +export function makeLens(type: Type, selector: Witness) { + const { proxy, revoke } = createProxy(type) + const { [PATH_KEY]: path } = selector(proxy) + return ( + revoke(), + interpreter(type, ...path) + ) +} + +// makeLens.getDSL = getDSL +// makeLens.setDSL = setDSL + +export declare namespace Proxy { + export { + Proxy as new, + Proxy_array as array, + Proxy_disjointUnion as disjointUnion, + Proxy_object as object, + Proxy_optional as optional, + Proxy_primitive as primitive, + Proxy_record as record, + Proxy_sumType as sum, + Proxy_tuple as tuple, + Proxy_union as union, + } +} diff --git a/packages/schema-zod-adapter/src/optics-v4.ts b/packages/schema-zod-adapter/src/optics-v4.ts new file mode 100644 index 00000000..f1d59332 --- /dev/null +++ b/packages/schema-zod-adapter/src/optics-v4.ts @@ -0,0 +1,47 @@ +import type { Box } from '@traversable/registry' +import { fn, Object_assign, Object_create, Option, symbol } from '@traversable/registry' + +import type { Applicative, Apply } from './instances-v4.js' + +export * as Iso from './iso-v4.js' +export interface Iso { + [symbol.tag]: 'Iso' + decode(s: S): T + encode(t: T): S +} + +/** + * Law #1: calling `match` then `embed` is the same as doing nothing at all + * Law #2: `S` can be completely described by the combination of the focus of the prism and the prism itself + */ +export * as Prism from './prism-v4.js' +export interface Prism { + [symbol.tag]: 'Prism' + match(s: S): Option + embed(a: A): S +} + +export * as Lens from './lens-v4.js' +export interface Lens { + [symbol.tag]: 'Lens' + get(s: S): A + set(a: A): (s: S) => S +} + +export * as Optional from './optional-v4.js' +export interface Optional { + [symbol.tag]: 'Optional' + getOption(s: S): Option + set(a: A): (s: S) => S +} + +export * as Traversal from './traversal-v4.js' +export interface Traversal { + [symbol.tag]: 'Traversal' + modify(F: Applicative): (f: (a: A) => Box) => (s: S) => Box +} + +export interface TraversalWithPredicate { + [symbol.tag]: 'TraversalWithPredicate' + modifyWithFallback(F: Apply): (f: (a: A) => Box, fallback: Box) => (s: S) => Box +} diff --git a/packages/schema-zod-adapter/src/optional-v4.ts b/packages/schema-zod-adapter/src/optional-v4.ts new file mode 100644 index 00000000..51e8f526 --- /dev/null +++ b/packages/schema-zod-adapter/src/optional-v4.ts @@ -0,0 +1,169 @@ +import type { HKT } from '@traversable/registry' +import { fn, has, Object_assign, Object_create, Option, symbol } from '@traversable/registry' + +import type { Optic } from './proxy-v4.js' +import { + Lens, + Iso, + Prism, +} from './optics-v4.js' + +type Optional = import('./optics-v4.js').Optional + +export type Any = Optional +export type { Infer as infer, Source as source, Target as target } +type Infer = SA extends Optional ? [S: S, A: A] : never +type Source = SA extends Optional ? S : never +type Target = SA extends Optional ? T : never +export type { F as Box } +interface F extends HKT<{ [0]: unknown, [1]: unknown }> { [-1]: Optional } +export type UnknownSource = unknown extends Source ? {} | null | undefined : never + +export const identity = Object_create>(null) +identity[symbol.tag] = 'Optional' +identity.getOption = Option.some +identity.set = fn.const + +export { Optional_new as new } +function Optional_new( + getOption: Optional['getOption'], + set: Optional['set'] +): Optional { + return Object_assign(Object_create(null), identity, { getOption, set }) +} + + +export const declare + : () => Optional + = fn.const(identity) + +export function is(x: Optic.Any): x is Optional +export function is(x: unknown): x is Optional +export function is(x: unknown) { + return ( + has(symbol.tag, (x) => x === identity[symbol.tag])(x) || ( + has('getOption', (x) => typeof x === 'function')(x) + && has('set', (x) => typeof x === 'function')(x) + ) + ) +} + +export const fromOptional + : (optional: Optional) => Optional + = fn.identity + +export function fromIso(iso: Iso): Optional { + return Optional_new( + fn.flow(iso.decode, Option.some), + fn.flow(iso.encode, fn.const), + ) +} + +export function fromLens(lens: Lens): Optional { + return Optional_new( + fn.flow(lens.get, Option.some), + lens.set, + ) +} + + +export function joinLeft(prism: Prism): (lens: Lens) => Optional +export function joinLeft(prism: Prism) { + const ab = fromPrism(prism) + return (lens: Lens) => { + const sa = fromLens(lens) + return compose(sa, ab) as Optional + } +} + +export function joinRight(lens: Lens): (prism: Prism) => Optional +export function joinRight(lens: Lens) { + const ab = fromLens(lens) + return (prism: Prism) => { + const sa = fromPrism(prism) + return compose(sa, ab) as Optional + } +} + +export function join(LENS: Lens, PRISM: Prism): Optional +export function join(PRISM: Prism, LENS: Lens): Optional +export function join(SA: Prism | Lens, AB: Prism | Lens) { + if (SA[symbol.tag] === undefined || AB[symbol.tag] === undefined) + throw Error('Optional.join can\'t join without a tag') + else { + const sa = Lens.is(SA) ? fromLens(SA) : fromPrism(SA) + const ab = Lens.is(AB) ? fromLens(AB) : fromPrism(AB) + return compose(sa, ab) + } +} + +export function compose( + ...args: + | [ab: Optional] + | [sa: Optional, ab: Optional] +) { + if (args.length === 1) return (sa: Optional) => compose(sa, ...args) + else { + const [sa, ab] = args + return Optional_new( + fn.flow(sa.getOption, Option.flatMap(ab.getOption)), + (b) => modify(ab.set(b))(sa), + ) satisfies Optional + } +} + +export function composeLens(optional: Optional): (lens: Lens) => Optional +export function composeLens(sa: Optional) { + return (lens: Lens) => fn.pipe( + fromLens(lens), + (ab) => compose(sa, ab) + ) +} + + +export function prop>(optional: SA, k: K): Optional> +export function prop, A extends Target>(optional: SA, k: K): Optional +export function prop(k: K): Optional<{ [x: string]: unknown }, unknown> +export function prop(k: K): Optional<{ [x: number]: unknown }, unknown> +export function prop( + ...args: + | [sa: Any, k: keyof any] + | [k: keyof any] +): {} { + const [sa, k] = args.length === 1 ? [declare(), args[0]] : args + return fn.pipe( + Lens.declare(), + (lens) => Lens.prop(lens, k), + fromLens, + (ab) => compose(sa, ab) + ) +} + +export function fromPrism(prism: Prism): Optional { + return Optional_new( + prism.match, + (a) => Prism.set(a)(prism), + ) +} + +export function modify(f: (a: A) => B): (optional: Optional) => (s: S) => S +export function modify(f: (a: A) => B) { + return (sa: Optional) => { + const apply = modifyOption(f)(sa) + return (s: S) => fn.pipe( + apply(s), + Option.getOrElse(() => s), + ) satisfies S + } +} + +export function modifyOption(f: (a: A) => B): (optional: Optional) => (s: S) => Option +export function modifyOption(f: (a: A) => B) { + return (optional: Optional) => (s: S) => fn.pipe( + optional.getOption(s), + Option.map((a) => { + const x = f(a) + return x === a ? s : optional.set(x)(s) + }), + ) satisfies Option +} diff --git a/packages/schema-zod-adapter/src/paths-v4.ts b/packages/schema-zod-adapter/src/paths-v4.ts new file mode 100644 index 00000000..6743851c --- /dev/null +++ b/packages/schema-zod-adapter/src/paths-v4.ts @@ -0,0 +1,69 @@ +import { z } from 'zod/v4' +import type { Returns } from '@traversable/registry' +import * as F from './functor-v4.js' +import { NS, Sym } from './typename-v4.js' +import { paths as algebra } from './algebra-v4.js' + +export type Path = [(keyof any)[], z.ZodType['_zod']['def']['type']] + +export type Interpreter = + & { [K in keyof Sym]+?: Interpreter.Handler } + & { number?: Interpreter.Handler, string?: Interpreter.Handler, symbol?: Interpreter.Handler } + +export declare namespace Interpreter { + type Handler = (k: K, ix: number, prev: T[]) => T + type LeastUpperBound = { [x: string]: Interpreter.Handler } +} + +const rmNS = (k: symbol) => k.description?.startsWith(NS) ? k.description.slice(NS.length) : k.description +const rmNil = (x: T) => x == null ? [] : [x] + +function pathInterpreter(interpret: T, ...path: (keyof any)[]): (Returns & {})[] +function pathInterpreter(interpret: Interpreter.LeastUpperBound, ...path: (keyof any)[]): unknown[] { + let out = Array.of() + let ix = -1 + let head + while ((void ix++, head = path.shift()) !== undefined) { + let unprefixed = typeof head === 'symbol' ? rmNS(head) : null + if (typeof unprefixed === 'string' && unprefixed in interpret) out.push(...rmNil(interpret[unprefixed](head, ix, out))) + else if (typeof head === 'string') out.push(...rmNil('string' in interpret ? interpret.string(head, ix, out) : head)) + else if (typeof head === 'number') out.push(...rmNil('number' in interpret ? interpret.number(head, ix, out) : head)) + else if (typeof head === 'symbol') out.push(...rmNil('symbol' in interpret ? interpret.symbol(head, ix, out) : head)) + else { throw Error('Unmatched segment: ' + String(head)) } + } + return out +} + +function parseOptions(options?: paths.Options): paths.Config +function parseOptions({ + interpreter = defaults.interpreter, +}: paths.Options = defaults): paths.Config { + return { + interpreter, + } +} + +const defaults = { + interpreter: { + string: (k, ix) => `${ix === 0 ? '' : '.'}${k}`, + number: (k) => `[${k}]`, + symbol: () => null, + optional: () => `?`, + }, +} satisfies Required + +export declare namespace paths { + type Options = { + interpreter?: Interpreter + } + interface Config extends Required {} +} + +export function paths(type: T, options?: paths.Options) { + const $ = parseOptions(options) + return algebra(F.in(type)) + .map(([path, leaf]) => [pathInterpreter($.interpreter, ...path), leaf] satisfies [any, any]) + .map(([path]) => path) +} + +paths.defaults = defaults diff --git a/packages/schema-zod-adapter/src/prism-v4.ts b/packages/schema-zod-adapter/src/prism-v4.ts new file mode 100644 index 00000000..116a191c --- /dev/null +++ b/packages/schema-zod-adapter/src/prism-v4.ts @@ -0,0 +1,118 @@ +import type { HKT } from '@traversable/registry' +import { fn, has, Object_assign, Object_create, Option, symbol } from '@traversable/registry' + +import type { Optic } from './proxy-v4.js' +import type { Optional } from './optics-v4.js' +import { + Iso, + Lens, +} from './optics-v4.js' +type Prism = import('./optics-v4.js').Prism + +export type Any = Prism +export type { Infer as infer, Source as source, Target as target } +type Infer = SA extends Prism ? [S: S, A: A] : never +type Source = SA extends Prism ? S : never +type Target = SA extends Prism ? T : never +export type { F as Box } +interface F extends HKT<{ [0]: unknown, [1]: unknown }> { [-1]: Prism } +export type UnknownSource = unknown extends Source ? {} | null | undefined : never + +export type KeyOf = K & (T extends readonly any[] ? number : unknown) + +export const identity = Object_create(null) +identity[symbol.tag] = 'Prism' +identity.embed = fn.identity +identity.match = Option.some + +export const declare + : () => Prism + = fn.const(identity) + +export function is(x: Optic.Any): x is Prism +export function is(x: unknown): x is Prism +export function is(x: unknown) { + return ( + has(symbol.tag, (x) => x === identity[symbol.tag])(x) || ( + has('match', (x) => typeof x === 'function')(x) + && has('embed', (x) => typeof x === 'function')(x) + ) + ) +} + +export { Prism_new as new } +function Prism_new( + match: (s: S) => Option, + embed: (a: A) => S +): Prism { + return Object_assign(Object_create(null), identity, { match, embed }) +} + +export function compose(sa: Prism, ab: Prism): Prism +export function compose(sa: Prism, ab: Prism) { + return Prism_new( + fn.flow(sa.match, Option.flatMap(ab.match)), + fn.flow(ab.embed, sa.embed), + ) +} + +export const fromPrism + : (prism: Prism) => Prism + = fn.identity + +export function fromIso(iso: Iso): Prism { + return Prism_new( + fn.flow(iso.decode, Option.some), + iso.encode, + ) +} + +export function fromPredicate(predicate: (a: A) => a is B): Prism +export function fromPredicate(predicate: (a: A) => boolean): Prism +export function fromPredicate(predicate: (x: unknown) => boolean): Prism { + return Prism_new( + Option.fromPredicate(predicate), + fn.identity, + ) +} + +export function modify(f: (a: A) => B): (sa: Prism) => (s: S) => S +export function modify(f: (a: A) => B) { + return (sa: Prism) => { + const apply = modifyOption(f)(sa) + return (s: S) => + fn.pipe( + apply(s), + Option.getOrElse(() => s), + ) satisfies S + } +} + +export function modifyOption(f: (a: A) => B): (sa: Prism) => (s: S) => Option +export function modifyOption(f: (a: A) => B) { + return (sa: Prism) => (s: S) => fn.pipe( + sa.match(s), + Option.map( + fn.flow( + (a) => [a, f(a)] satisfies [any, any], + ([a, b]) => a === b ? s : sa.embed(b) + ) + ) + ) satisfies Option +} + +export const set + : (a: A) => ((sa: Prism) => (s: S) => S) + // (a) => modify(() => a) + = fn.flow(fn.const, modify) + +export type Sequence = never | { [K in keyof T]: Prism], T[K]> } + +export function sequence(patterns: { [K in keyof T]: (x: unknown) => x is T[K] }): Sequence +export function sequence(patterns: { [K in keyof T]: (x: T[K]) => boolean }): Sequence +export function sequence(patterns: { [x: number]: (x: unknown) => boolean }) { + return fn.map( + patterns, + (predicate) => Prism_new(Option.fromPredicate(predicate), fn.identity), + ) satisfies Sequence +} diff --git a/packages/schema-zod-adapter/src/profunctor-optics-v4.ts b/packages/schema-zod-adapter/src/profunctor-optics-v4.ts new file mode 100644 index 00000000..35bd67b4 --- /dev/null +++ b/packages/schema-zod-adapter/src/profunctor-optics-v4.ts @@ -0,0 +1,333 @@ +import { Either, fn, Number_isSafeInteger, Number_parseInt, Object_assign, Object_create, Object_entries, symbol } from '@traversable/registry' + +type UnaryFn = (x: any) => any +type BinaryFn = (x: any, y: any) => any +type Profunctor = any + + +export interface Optic { + [symbol.tag]: Optic.Type + _removable?: true + (P: Profunctor, optic: Optic): any +} + +export declare namespace Optic { + type Type = keyof Lattice +} + +export type Lattice = typeof Lattice + +export const Lattice = { + Identity: { + Identity: 'Identity', + Iso: 'Iso', + Lens: 'Lens', + Prism: 'Prism', + Traversal: 'Traversal', + }, + Iso: { + Identity: 'Iso', + Iso: 'Iso', + Lens: 'Lens', + Prism: 'Prism', + Traversal: 'Traversal', + }, + Lens: { + Identity: 'Lens', + Iso: 'Lens', + Lens: 'Lens', + Prism: 'Prism', + Traversal: 'Traversal', + }, + Prism: { + Identity: 'Prism', + Iso: 'Prism', + Lens: 'Prism', + Prism: 'Prism', + Traversal: 'Traversal', + }, + Traversal: { + Identity: 'Traversal', + Iso: 'Traversal', + Lens: 'Traversal', + Prism: 'Traversal', + Traversal: 'Traversal', + }, +} as const + +const either + : (mapLeft: (value: E) => T, mapRight: (value: S) => T, x: Either) => T + = (mapLeft, mapRight, x) => (x._tag === 'Either::Left' ? mapLeft(x.left) : mapRight(x.right)) + +export function Profunctor() {} +Profunctor.function = { + dimap(sa: UnaryFn, bt: UnaryFn, ab: UnaryFn) { return (x: any) => bt(ab(sa(x))) }, + first(f: UnaryFn) { return ([x, y]: [any, any]) => [f(x), y] }, + right(f: UnaryFn) { return (x: Either): Either => Either.isLeft(x) ? x : Either.right(f(x.right)) }, + wander(f: any) { return (xs: { [x: number]: unknown }) => fn.map(xs, f) }, +} + +Profunctor.const = (M: any) => ({ + dimap(f: UnaryFn, _g: UnaryFn, pure: UnaryFn) { return (x: any) => pure(f(x)) }, + first(pure: UnaryFn) { return ([x]: [any, any]) => pure(x) }, + right(pure: UnaryFn, hint?: string) { return (x: Either.Any) => Either.isLeft(x) ? M.empty(hint) : pure(x.right) }, + wander(pure: UnaryFn) { return (xs: any[]) => M.foldMap(pure, xs) }, +}) + +const UnknownMonoid = { + empty(hint?: string) { return hint === 'void' ? undefined : hint === 'array' ? [] : {} }, + foldMap(f: UnaryFn, xs: { [x: number]: unknown }) { + return xs instanceof Set + ? SetMonoid.foldMap(f, xs) + : Array.isArray(xs) + ? ArrayMonoid.foldMap(f, xs) + : Object_entries(xs).reduce( + (acc, [k, v]) => k in acc ? acc : { ...acc, [k]: f(v) }, + {} as { [x: number]: unknown } + ) + } +} + +const ArrayMonoid = { + empty() { return [] }, + foldMap(f: UnaryFn, xs: any[]) { + let acc = Array.of() + xs.forEach((x) => acc = acc.concat(f(x))) + return acc + }, +} + +const SetMonoid = { + empty() { return new Set() }, + foldMap(f: UnaryFn, xs: Set) { + let tmp = Array.of() + xs.forEach((x) => tmp = tmp.concat(f(x))) + return tmp + } +} + +const ObjectMonoid = { + empty() { return Object_create(null) }, + foldMap(f: UnaryFn, xs: { [x: string]: unknown }) { + return Object_entries(xs).reduce( + (acc, [k, v]) => k in acc ? acc : { ...acc, [k]: f(v) }, + {} as { [x: number]: unknown } + ) + } +} + +const FirstMonoid = { + empty() { return undefined }, + // empty() { return symbol.notfound }, + foldMap(f: UnaryFn, xs: any[]) { + for (let ix = 0, len = xs.length; ix < len; ix++) { + const x = f(xs[ix]) + if (!isMissing(x)) return x + } + return undefined + // return symbol.notfound + }, +} + +const FallbackSemigroup = (fallback: unknown) => ({ + empty() { return [fallback] }, + foldMap(f: UnaryFn, xs: any[]) { + if (Array.isArray(xs)) { + let acc = Array.of() + xs.forEach((x) => acc = acc.concat(f(isMissing(x) ? fallback : x))) + return acc + } + else { + Object_entries(xs).reduce( + (acc, [k, v]) => k in acc ? acc : { ...acc, [k]: f(isMissing(v) ? fallback : v) }, + {} as { [x: number]: unknown } + ) + } + }, +}) + +export function Monoid() {} +Monoid.any = UnknownMonoid +Monoid.array = ArrayMonoid +Monoid.fallback = FallbackSemigroup +Monoid.first = FirstMonoid +Monoid.object = ObjectMonoid +Monoid.set = SetMonoid + +interface OpticLike extends BinaryFn { [symbol.tag]?: unknown } + +function tag(tag: Tag, x: BinaryFn): Optic +function tag(tag: string, x: OpticLike): BinaryFn { + return ( + x[symbol.tag] = tag, + x + ) +} + +export const id = tag( + 'Identity', + (_P: Profunctor, optic: any) => optic +) + +export function lens(view: UnaryFn, update: UnaryFn): Optic { + return tag( + 'Lens', + (P: Profunctor, optic: Optic) => P.dimap( + (x: unknown) => [view(x), x], + update, + P.first(optic) + ) + ) +} + +export function prism(match: UnaryFn, embed: UnaryFn): Optic { + return tag( + 'Prism', + (P: Profunctor, optic: any) => P.dimap( + match, + (x: any) => either(fn.identity, embed, x), + P.right(optic, 'void') + ) + ) +} + +export const traverse = tag( + 'Traversal', + (P: Profunctor, optic: any): Optic => P.dimap( + fn.identity, + fn.identity, + P.wander(optic) + ), +) + +export function iso(there: UnaryFn, back: UnaryFn): Optic { + return tag( + 'Iso', + (P: Profunctor, optic: any): Optic => P.dimap( + there, + back, + optic + ) + ) +} + +export const modify = (optic: any, fn: UnaryFn, source: any): any => optic(Profunctor.function, fn)(source) + +export function set(optic: Optic, update: any, source: any): any +export function set(optic: BinaryFn, update: any, source: any): any +export function set(optic: any, update: any, source: any): any { + return optic(Profunctor.function, () => update)(source) +} + +export function get(optic: any, source: any): any { + return optic(Profunctor.const({}), fn.identity)(source) +} + +export const preview = (optic: any, source: any): any => optic(Profunctor.const(Monoid.first), fn.identity)(source) + +export const prop = (k: string | number): Optic => lens( + (source: any) => { + return source?.[k] + // return source?.[k] ?? symbol.notfound + }, + ([value, data]: [any, any]) => { + if (Array.isArray(data)) { + const index = typeof k === 'string' ? Number_parseInt(k) : k + if (!Number_isSafeInteger(index)) + throw Error(`'prop' optic received an array and a non-integer offset`) + else + return data.slice(0, index).concat(value).concat(data.slice(index + 1)) + } else { + return { + ...data, + [k]: value, + } + } + } +) + +const isMissing = (x: unknown) => x === undefined || x === symbol.notfound + +export const propOr = (fallback: unknown, key: string): Optic => lens( + (src: any) => isMissing(src?.[key]) ? fallback : src?.[key], + ([value, source]: [any, any]) => ({ ...source, [key]: value }) +) + + +export const pick = (keys: string[]): Optic => lens( + (source: any) => { + const value: any = {} + for (const key of keys) { + value[key] = source[key] + } + return value + }, + ([value, source]: [any, any]) => { + const result = { ...source } + for (const key of keys) { + delete result[key] + } + return Object_assign(result, value) + } +) + +export const nth = (n: number): Optic => lens( + (value) => value[n], + ([value, source]) => { + const result = source.slice() + result[n] = value + return result + } +) + +export const when = (pred: (x: any) => boolean): Optic => prism( + (x: any) => (pred(x) ? Either.right(x) : Either.left(x)), + fn.identity, +) + +export function collect(optic: any) { + return (source: any): any => optic( + Profunctor.const(Monoid.any), + (x: any) => { return [x] } + )(source) +} + +export const scavenge = (fallback: any, optic: any, source: any): any => optic(Profunctor.const(Monoid.fallback(fallback)), (x: any) => [x])(source) + +export function compose(optic1: Optic, optic2: Optic, optic3?: Optic): Optic { + switch (arguments.length) { + case 2: { + const next = (P: Profunctor, optic: Optic) => + optic1(P, optic2(P, optic)) + ; (next as any)[symbol.tag] = Lattice[optic1[symbol.tag]][optic2[symbol.tag]]! + ; (next as any)._removable = optic2._removable || false + return next as Optic + } + default: { + const tag1 = Lattice[optic1[symbol.tag]][optic2[symbol.tag]]! + const next = (P: Profunctor, optic: Optic) => + optic1(P, optic2(P, optic3!(P, optic))) + ; (next as any)[symbol.tag] = Lattice[tag1][optic3![symbol.tag]]! + ; (next as any)._removable = optic3!._removable || false + return next as Optic + } + } +} + +/** @internal */ +function compose1(optic1: any, optic2: any) { + const result = (P: Profunctor, optic: any) => optic1(P, optic2(P, optic)) + result[symbol.tag] = Lattice[optic1[symbol.tag] as Optic.Type][optic2[symbol.tag] as Optic.Type] + result._removable = optic2._removable || undefined + return result +} + +export function pipe( + ...args: T +): Optic { + const [first, ...rest] = args + const optic = typeof first === 'string' ? prop(first) : first + if (!rest.length) return optic as any + return compose1(optic, pipe(...(rest as any))) as any +} + diff --git a/packages/schema-zod-adapter/src/proxy-v4.ts b/packages/schema-zod-adapter/src/proxy-v4.ts new file mode 100644 index 00000000..867f24af --- /dev/null +++ b/packages/schema-zod-adapter/src/proxy-v4.ts @@ -0,0 +1,1782 @@ +import { z } from 'zod/v4' +import type { Box, Boxed, Force, Kind, HKT, newtype, TypeConstructor as Type, inline, Key, Showable } from '@traversable/registry' +import { Array_isArray, Either, fn, get, has, mut, Object_assign, Object_create, Object_entries, Object_fromEntries, Object_values, Option, pick, set, symbol } from '@traversable/registry' + +import * as F from './functor-v4.js' +import * as P from './paths-v4.js' +import { tagged, TypeName } from './typename-v4.js' +import type { Ctx } from './utils-v4.js' +import { Applicative, Semigroup, Traversable } from './instances-v4.js' +import { withDefault } from './with-default-v4.js' +import { toString } from './toString-v4.js' +import * as Pro from './profunctor-optics-v4.js' + +import * as _ from './optics-v4.js' + +/** @internal */ +const isKeyOf = (x: T, k: keyof any): k is keyof T => !!x && (typeof x === 'object' || typeof x === 'function') && k in x + +type InferSource + = SA extends + | _.Iso + | _.Lens + | _.Prism + | _.Optional + | _.Traversal + ? AbsorbNever> + : never + +type InferTarget + = SA extends + | _.Iso + | _.Lens + | _.Prism + | _.Optional + | _.Traversal + ? AbsorbNever> + : never + +type AbsorbUnknown = T extends T ? unknown extends T ? never : T : never +type AbsorbNever = [T] extends [never] ? unknown : T + +export function Optic() {} + +Optic.Type = { + // Traversal: _.Traversal.identity[symbol.tag], + Lens: _.Lens.identity[symbol.tag], + Prism: _.Prism.identity[symbol.tag], + Iso: _.Iso.identity[symbol.tag], + Optional: _.Optional.identity[symbol.tag], + TraversalWithPredicate: _.Traversal.identityWithPredicate[symbol.tag] +} + +export declare namespace Optic { + type Type = typeof Optic.Type[keyof typeof Optic.Type] + type Iso = typeof Optic.Type.Iso + type Prism = typeof Optic.Type.Prism + type Lens = typeof Optic.Type.Lens + type Optional = typeof Optic.Type.Optional + // type Traversal = typeof Optic.Type.Traversal + type TraversalWithPredicate = typeof Optic.Type.TraversalWithPredicate + + type Any = + | _.Iso + | _.Prism + | _.Lens + | _.Optional + | _.TraversalWithPredicate + + type AnyBox = + | _.Iso.Box + | _.Prism.Box + | _.Lens.Box + | _.Optional.Box + | _.Traversal.BoxWithFallback + + type BoxByTag = { + [Optic.Type.Iso]: _.Iso.Box + [Optic.Type.Prism]: _.Prism.Box + [Optic.Type.Lens]: _.Lens.Box + [Optic.Type.Optional]: _.Optional.Box + [Optic.Type.TraversalWithPredicate]: _.Traversal.BoxWithFallback + // [Optic.Type.Traversal]: _.Traversal.Box + } + + type LowerBound = [Optic.Any[symbol.tag]] extends [T[symbol.tag & keyof T]] ? Optic.Any : never + + namespace Descriptor { + type Predicate = { type: Optic.Type, tag: 'fromPredicate', predicate: (x: unknown) => boolean } + type Applicative = { type: Optic.Type, tag: 'applicative', applicative: 'array' | 'record' | 'set' | 'map' } + type Index = { type: Optic.Type, tag: 'index', index: number } + type Prop = { type: Optic.Type, tag: 'prop', prop: string } + type FromNullable = { type: Optic.Type, tag: 'fromNullable', nullable: unknown } + type Declaration = { type: Optic.Type, tag: 'declaration' } + } + + type Descriptor = + | Descriptor.Declaration + | Descriptor.FromNullable + | Descriptor.Prop + | Descriptor.Index + | Descriptor.Applicative + | Descriptor.Predicate + + type Path = [ + path: (keyof any)[], + leaf: T, + predicate: F.Z.Hole, + ] + + type SchemaPath = [ + path: (keyof any)[], + leaf: T, + predicate: z.ZodType, + ] +} + +export type Lattice = typeof Lattice +export const Lattice = { + [Optic.Type.Iso]: { + [Optic.Type.Iso]: Optic.Type.Iso, + [Optic.Type.Prism]: Optic.Type.Prism, + [Optic.Type.Lens]: Optic.Type.Lens, + [Optic.Type.Optional]: Optic.Type.Optional, + [Optic.Type.TraversalWithPredicate]: Optic.Type.TraversalWithPredicate, + // [Optic.Type.Traversal]: Optic.Type.Traversal, + }, + [Optic.Type.Prism]: { + [Optic.Type.Iso]: Optic.Type.Prism, + [Optic.Type.Prism]: Optic.Type.Prism, + [Optic.Type.Lens]: Optic.Type.Optional, + [Optic.Type.Optional]: Optic.Type.Optional, + [Optic.Type.TraversalWithPredicate]: Optic.Type.TraversalWithPredicate, + // [Optic.Type.Traversal]: Optic.Type.Traversal, + }, + [Optic.Type.Lens]: { + [Optic.Type.Iso]: Optic.Type.Lens, + [Optic.Type.Prism]: Optic.Type.Optional, + [Optic.Type.Lens]: Optic.Type.Lens, + [Optic.Type.Optional]: Optic.Type.Optional, + [Optic.Type.TraversalWithPredicate]: Optic.Type.TraversalWithPredicate, + // [Optic.Type.Traversal]: Optic.Type.Traversal, + }, + [Optic.Type.Optional]: { + [Optic.Type.Iso]: Optic.Type.Optional, + [Optic.Type.Prism]: Optic.Type.Optional, + [Optic.Type.Lens]: Optic.Type.Optional, + [Optic.Type.Optional]: Optic.Type.Optional, + [Optic.Type.TraversalWithPredicate]: Optic.Type.TraversalWithPredicate, + // [Optic.Type.Traversal]: Optic.Type.Traversal, + }, + [Optic.Type.TraversalWithPredicate]: { + [Optic.Type.Iso]: Optic.Type.TraversalWithPredicate, + [Optic.Type.Prism]: Optic.Type.TraversalWithPredicate, + [Optic.Type.Lens]: Optic.Type.TraversalWithPredicate, + [Optic.Type.Optional]: Optic.Type.TraversalWithPredicate, + [Optic.Type.TraversalWithPredicate]: Optic.Type.TraversalWithPredicate, + // [Optic.Type.Traversal]: Optic.Type.Traversal, + } +} + +const ConversionLattice = { + [Optic.Type.Iso]: { + [Optic.Type.Iso]: _.Iso.fromIso, + [Optic.Type.Prism]: _.Prism.fromIso, + [Optic.Type.Lens]: _.Lens.fromIso, + [Optic.Type.Optional]: _.Optional.fromIso, + // [Optic.Type.Traversal]: _.Traversal.fromIso, + [Optic.Type.TraversalWithPredicate]: _.Traversal.fromIsoWithFallback, + }, + [Optic.Type.Prism]: { + [Optic.Type.Prism]: _.Prism.fromPrism, + [Optic.Type.Iso]: _.Prism.fromPrism, + [Optic.Type.Lens]: _.Optional.fromPrism, + [Optic.Type.Optional]: _.Optional.fromPrism, + // [Optic.Type.Traversal]: _.Traversal.fromPrism, + [Optic.Type.TraversalWithPredicate]: _.Traversal.fromPrismWithFallback, + }, + [Optic.Type.Lens]: { + [Optic.Type.Lens]: _.Lens.fromLens, + [Optic.Type.Iso]: _.Lens.fromLens, + [Optic.Type.Prism]: _.Optional.fromLens, + [Optic.Type.Optional]: _.Optional.composeLens(_.Optional.declare()) as (lens: _.Lens) => _.Optional, + // [Optic.Type.Traversal]: _.Traversal.fromLens, + [Optic.Type.TraversalWithPredicate]: _.Traversal.fromLensWithFallback, + }, + [Optic.Type.Optional]: { + [Optic.Type.Optional]: _.Optional.fromOptional, + [Optic.Type.Iso]: _.Optional.fromOptional, + [Optic.Type.Prism]: _.Optional.fromOptional, + [Optic.Type.Lens]: _.Optional.fromOptional, + // [Optic.Type.Traversal]: _.Traversal.fromOptional, + [Optic.Type.TraversalWithPredicate]: _.Traversal.fromOptionalWithFallback, + }, + // [Optic.Type.Traversal]: { + // [Optic.Type.Traversal]: _.Traversal.fromTraversal, + // [Optic.Type.Iso]: _.Traversal.fromTraversal, + // [Optic.Type.Prism]: _.Traversal.fromTraversal, + // [Optic.Type.Lens]: _.Traversal.fromTraversal, + // [Optic.Type.Optional]: _.Traversal.fromTraversal, + // [Optic.Type.TraversalWithPredicate]: _.Traversal.toTraversalWithFallback, + // }, + [Optic.Type.TraversalWithPredicate]: { + [Optic.Type.TraversalWithPredicate]: _.Traversal.fromTraversalWithFallback, + [Optic.Type.Iso]: _.Traversal.fromTraversalWithFallback, + [Optic.Type.Prism]: _.Traversal.fromTraversalWithFallback, + [Optic.Type.Lens]: _.Traversal.fromTraversalWithFallback, + [Optic.Type.Optional]: _.Traversal.fromTraversalWithFallback, + // [Optic.Type.Traversal]: _.Traversal.fromTraversalWithFallback, + } +} + +const declare = { + Iso: _.Iso.declare, + Prism: _.Prism.declare, + Lens: _.Lens.declare, + Optional: _.Optional.declare, + // Traversal: _.Traversal.declare, + TraversalWithPredicate: _.Traversal.declareWithFallback +} satisfies { [K in Optic.Type]: unknown } + +export function declaration({ type }: Optic.Descriptor.Declaration & { fallback: unknown }): + | _.Iso + | _.Lens + | _.Prism + | _.Optional + // | _.Traversal + | _.TraversalWithPredicate { + return declare[type]() +} + +export function prop({ type, prop, fallback }: Optic.Descriptor.Prop & { fallback: unknown }): + | _.Lens<{ [x: string]: unknown }, unknown> + | _.Optional<{ [x: string]: unknown }, unknown> + // | _.Traversal<{ [x: string]: unknown }, unknown> + | _.TraversalWithPredicate<{ [x: string]: unknown }, unknown> { + return ConversionLattice.Lens[type](_.Lens.prop(prop)(), fallback) +} + +export function index({ type, index, fallback }: Optic.Descriptor.Index & { fallback: unknown }): + | _.Lens<{ [x: number]: unknown }, unknown> + | _.Optional<{ [x: number]: unknown }, unknown> + // | _.Traversal<{ [x: number]: unknown }, unknown> + | _.TraversalWithPredicate<{ [x: number]: unknown }, unknown> { + return ConversionLattice.Lens[type](_.Lens.prop(index)(), fallback) +} + +export function fromNullable({ type, nullable, fallback }: Optic.Descriptor.FromNullable & { fallback: unknown }): + | _.Prism + | _.Optional + // | _.Traversal + | _.TraversalWithPredicate { + return ConversionLattice.Prism[type](_.Prism.fromPredicate((x) => x !== nullable), fallback) +} + +export function fromPredicate({ type, predicate, fallback }: Optic.Descriptor.Predicate & { fallback: unknown }): + | _.Prism + | _.Optional + // | _.Traversal + | _.TraversalWithPredicate { + return ConversionLattice.Prism[type](_.Prism.fromPredicate(predicate), fallback) +} + +export function applicative({ type, applicative, fallback }: Optic.Descriptor.Applicative & { fallback: unknown }): _.TraversalWithPredicate { + if (!isKeyOf(Semigroup.Traversable, applicative)) throw Error('Unsupported applicative type: ' + applicative) + else { + const T: Semigroup.Traversable = Semigroup.Traversable[applicative] + const instance = _.Traversal.fromTraversableWithFallback(T)(fallback) + return ConversionLattice.TraversalWithPredicate[type](instance) + } +} + +export type CompositionLattice = typeof CompositionLattice +export const CompositionLattice = { + [Optic.Type.Iso]: { + [Optic.Type.Iso]: _.Iso.compose, + [Optic.Type.Prism]: (ISO: _.Iso, PRISM: _.Prism) => _.Prism.compose(_.Prism.fromIso(ISO), PRISM), + [Optic.Type.Lens]: (ISO: _.Iso, LENS: _.Lens) => _.Lens.compose(_.Lens.fromIso(ISO), LENS), + [Optic.Type.Optional]: (ISO: _.Iso, OPTIONAL: _.Optional) => _.Optional.compose(_.Optional.fromIso(ISO), OPTIONAL), + // [Optic.Type.Traversal]: (ISO: _.Iso, TRAVERSAL: _.Traversal) => _.Traversal.compose(_.Traversal.fromIso(ISO), TRAVERSAL), + [Optic.Type.TraversalWithPredicate]: ( + ISO: _.Iso, + TRAVERSAL_FB: _.TraversalWithPredicate, + fallback: A + ) => _.Traversal.composeWithFallback(_.Traversal.fromIsoWithFallback(ISO), TRAVERSAL_FB, fallback), + }, + [Optic.Type.Prism]: { + [Optic.Type.Prism]: _.Prism.compose, + [Optic.Type.Iso]: (PRISM: _.Prism, ISO: _.Iso) => _.Prism.compose(PRISM, _.Prism.fromIso(ISO)), + [Optic.Type.Lens]: (PRISM: _.Prism, LENS: _.Lens) => _.Optional.join(PRISM, LENS), + [Optic.Type.Optional]: (PRISM: _.Prism, OPTIONAL: _.Optional) => _.Optional.compose(_.Optional.fromPrism(PRISM), OPTIONAL), + // [Optic.Type.Traversal]: (PRISM: _.Prism, TRAVERSAL: _.Traversal) => _.Traversal.compose(_.Traversal.fromPrism(PRISM), TRAVERSAL), + [Optic.Type.TraversalWithPredicate]: ( + PRISM: _.Prism, + TRAVERSAL_FB: _.TraversalWithPredicate, + fallback: S + ) => _.Traversal.composeWithFallback(_.Traversal.fromPrismWithFallback(PRISM, fallback), TRAVERSAL_FB, fallback), + }, + [Optic.Type.Lens]: { + [Optic.Type.Lens]: _.Lens.compose, + [Optic.Type.Iso]: (LENS: _.Lens, ISO: _.Iso) => _.Lens.compose(LENS, _.Lens.fromIso(ISO)), + [Optic.Type.Prism]: (LENS: _.Lens, PRISM: _.Prism) => _.Optional.join(LENS, PRISM), + [Optic.Type.Optional]: (LENS: _.Lens, OPTIONAL: _.Optional) => _.Optional.compose(_.Optional.fromLens(LENS), OPTIONAL), + // [Optic.Type.Traversal]: (LENS: _.Lens, TRAVERSAL: _.Traversal) => _.Traversal.compose(_.Traversal.fromLens(LENS), TRAVERSAL), + [Optic.Type.TraversalWithPredicate]: ( + LENS: _.Lens, + TRAVERSAL_FB: _.TraversalWithPredicate, + fallback: A + ) => _.Traversal.composeWithFallback(_.Traversal.fromLensWithFallback(LENS, fallback), TRAVERSAL_FB, fallback), + }, + [Optic.Type.Optional]: { + [Optic.Type.Optional]: _.Optional.compose, + [Optic.Type.Iso]: (OPTIONAL: _.Optional, ISO: _.Iso) => _.Optional.compose(OPTIONAL, _.Optional.fromIso(ISO)), + [Optic.Type.Prism]: (OPTIONAL: _.Optional, PRISM: _.Prism) => _.Optional.compose(OPTIONAL, _.Optional.fromPrism(PRISM)), + [Optic.Type.Lens]: (OPTIONAL: _.Optional, LENS: _.Lens) => _.Optional.compose(OPTIONAL, _.Optional.fromLens(LENS)), + // [Optic.Type.Traversal]: (OPTIONAL: _.Optional, TRAVERSAL: _.Traversal) => _.Traversal.compose(_.Traversal.fromOptional(OPTIONAL), TRAVERSAL), + [Optic.Type.TraversalWithPredicate]: ( + OPTIONAL: _.Optional, + TRAVERSAL_FB: _.TraversalWithPredicate, + fallback: S + ) => _.Traversal.composeWithFallback(_.Traversal.fromOptionalWithFallback(OPTIONAL, fallback), TRAVERSAL_FB, fallback), + }, + // [Optic.Type.Traversal]: { + // // [Optic.Type.Traversal]: _.Traversal.compose, + // [Optic.Type.Iso]: (TRAVERSAL: _.Traversal, ISO: _.Iso) => _.Traversal.compose(TRAVERSAL, _.Traversal.fromIso(ISO)), + // [Optic.Type.Prism]: (TRAVERSAL: _.Traversal, PRISM: _.Prism) => _.Traversal.compose(TRAVERSAL, _.Traversal.fromPrism(PRISM)), + // [Optic.Type.Lens]: (TRAVERSAL: _.Traversal, LENS: _.Lens) => _.Traversal.compose(TRAVERSAL, _.Traversal.fromLens(LENS)), + // [Optic.Type.Optional]: (TRAVERSAL: _.Traversal, OPTIONAL: _.Optional) => _.Traversal.compose(TRAVERSAL, _.Traversal.fromOptional(OPTIONAL)), + // [Optic.Type.TraversalWithPredicate]: ( + // TRAVERSAL: _.Traversal, + // TRAVERSAL_FB: _.TraversalWithPredicate, + // fallback: A + // ) => _.Traversal.composeWithFallback(_.Traversal.toTraversalWithFallback(TRAVERSAL, fallback), TRAVERSAL_FB, fallback), + // }, + [Optic.Type.TraversalWithPredicate]: { + [Optic.Type.Iso]: ( + TRAVERSAL_FB: _.TraversalWithPredicate, + ISO: _.Iso, + fallback: A, + ) => _.Traversal.composeWithFallback(TRAVERSAL_FB, _.Traversal.fromIsoWithFallback(ISO), fallback), + [Optic.Type.Prism]: ( + TRAVERSAL_FB: _.TraversalWithPredicate, + PRISM: _.Prism, + fallback: A, + ) => _.Traversal.composeWithFallback(TRAVERSAL_FB, _.Traversal.fromPrismWithFallback(PRISM, fallback), fallback), + [Optic.Type.Lens]: ( + TRAVERSAL_FB: _.TraversalWithPredicate, + LENS: _.Lens, + fallback: B, + ) => _.Traversal.composeWithFallback(TRAVERSAL_FB, _.Traversal.fromLensWithFallback(LENS, fallback), fallback), + [Optic.Type.Optional]: ( + TRAVERSAL_FB: _.TraversalWithPredicate, + OPTIONAL: _.Optional, + fallback: A + ) => _.Traversal.composeWithFallback(TRAVERSAL_FB, _.Traversal.fromOptionalWithFallback(OPTIONAL, fallback), fallback), + // [Optic.Type.Traversal]: _.Traversal.compose, + [Optic.Type.TraversalWithPredicate]: _.Traversal.composeWithFallback, + } +} + +function compose< + Source extends Optic.Any, + Target extends Optic.Any, + S extends InferSource, + A extends InferTarget, + B extends InferTarget, + F extends Optic.BoxByTag[Lattice[Source[symbol.tag]][Target[symbol.tag]]] +>(sa: Source, ab: Target): Box + +function compose< + Source extends Optic.Any, + Target extends Optic.Any, +>(sa: Source, ab: Target): Optic.Any + +function compose(sa: Optic.Any, ab: Optic.Any): {} { + const f + : (sa: Optic.Any, ab: Optic.Any) => Optic.Any + = CompositionLattice[sa[symbol.tag]][ab[symbol.tag]] as never + return f(sa, ab) +} + +const Handler = { + Iso: fn.const(Optic.Type.Iso), + Prism: fn.const(Optic.Type.Prism), + Lens: fn.const(Optic.Type.Lens), + Optional: fn.const(Optic.Type.Optional), + // Traversal: fn.const(Optic.Type.Traversal), + TraversalWithPredicate: fn.const(Optic.Type.TraversalWithPredicate) +} satisfies { [K in Optic.Type]: P.Interpreter.Handler } + +const pathInterpreter = { + array: Handler.TraversalWithPredicate, + catch: Handler.Lens, + custom: Handler.TraversalWithPredicate, + default: Handler.Lens, + intersectionLeft: Handler.Iso, + intersectionRight: Handler.Iso, + lazy: Handler.Iso, + literal: Handler.Lens, + mapKey: Handler.TraversalWithPredicate, + mapValue: Handler.TraversalWithPredicate, + nonoptional: Handler.Lens, + null: Handler.Iso, + nullable: Handler.Prism, + number: Handler.Lens, + string: Handler.Lens, + object: Handler.Lens, + optional: Handler.Prism, + pipe: Handler.TraversalWithPredicate, + prefault: Handler.Lens, + promise: Handler.TraversalWithPredicate, + readonly: Handler.Iso, + recordKey: Handler.Lens, + recordValue: Handler.Lens, + set: Handler.TraversalWithPredicate, + success: Handler.Prism, + symbol: Handler.Lens, + transform: Handler.TraversalWithPredicate, + tuple: Handler.Iso, + union: Handler.Prism, +} satisfies { [K in keyof P.Interpreter]-?: P.Interpreter.Handler } + +declare const Z: { + literal: Z.Literal + optional: Z.Optional + array: Z.Array + record: Z.Record + union: Z.Union + tuple: Z.Tuple + object: Z.Object +} + +declare namespace Z { + // @ts-ignore + type infer = T['_output'] + interface Type { _output: T } + interface Indexed { [x: number]: Z.Typed } + interface Named { [x: string]: Z.Typed } + type Objects = readonly Z.Object[] + type Ordered = readonly Z.Typed[] + interface Infer< + T extends + | { _output: unknown } + = { _output: unknown } + > { _output: T['_output'] } + interface Typed< + T extends + | { _zod: { def: { type: unknown } } } + = { _zod: { def: { type: unknown } } } + > { _zod: { def: { type: T['_zod']['def']['type'] } } } + // + interface Literal< + T extends + | { _zod: { output: Showable } } + = { _zod: { output: Showable } } + > { _zod: { output: T['_zod']['output'] } } + interface Optional< + T extends + | { _zod: { def: { innerType: Z.Typed } } } + = { _zod: { def: { innerType: Z.Typed } } } + > { _zod: { def: { innerType: T['_zod']['def']['innerType'] } } } + interface Array< + T extends + | { _zod: { def: { element: Z.Typed } } } + = { _zod: { def: { element: Z.Typed } } } + > { _zod: { def: { element: T['_zod']['def']['element'] } } } + interface Record< + T extends + | { _zod: { def: { keyType: Z.Type, valueType: Z.Typed } } } + = { _zod: { def: { keyType: Z.Type, valueType: Z.Typed } } } + > { _zod: { def: { keyType: T['_zod']['def']['keyType'], valueType: T['_zod']['def']['valueType'] } } } + interface Union< + T extends + | { _zod: { def: { options: Z.Indexed } } } + = { _zod: { def: { options: Z.Indexed } } } + > { _zod: { def: { options: T['_zod']['def']['options'] } } } + interface Tuple< + T extends + | { _zod: { def: { items: Z.Ordered } } } + = { _zod: { def: { items: Z.Ordered } } } + > { _zod: { def: { items: T['_zod']['def']['items'] } } } + interface Object< + T extends + | { _zod: { def: { shape: Z.Named } } } + = { _zod: { def: { shape: Z.Named } } } + > { _zod: { def: { shape: T['_zod']['def']['shape'] } } } +} + +type MakeProxy = typeOf + +type typeOf + = [T] extends [Z.Object] ? t.object + : [T] extends [Z.Optional] ? t.optional + : [T] extends [Z.Tuple] ? typeOfItems + : [T] extends [Z.Union] ? UnionType + : [T] extends [Z.Array] ? t.array + : [T] extends [Z.Record] ? Record< + T['_zod']['def']['keyType']['_output'], + typeOf + > + : T['_output' & keyof T] + +declare namespace t { export { t_object as object } } +declare namespace t { + interface t_object extends newtype<{ [K in keyof S]: typeOf }> {} + interface union extends newtype<{ [I in keyof S]: typeOf }> {} + interface disjointUnion extends newtype<{ [E in T as E[0]]: { [K in keyof E[1]]: typeOf } }> {} + interface optional { + ʔ: typeOf + ǃ: typeOf + } + interface array { + ↆ: typeOf + map(f: (x: typeOf) => T): T + } +} + +type typeOfItems = { [I in Extract]: typeOf } + +type UnionType< + T extends Z.Indexed, + // @ts-expect-error + K extends keyof any = keyof T[number]['_zod']['def']['shape'], + // @ts-expect-error + Disc extends keyof any = T[number]['_zod']['def']['shape'][K]['_output'] +> = [K] extends [never] + ? never | t.union + : never | t.disjointUnion< + Disc extends Disc + ? [ + Disc, + Extract< + // @ts-expect-error + T[number]['_zod']['def']['shape'], + Record + > + ] + : never + > + +declare function makeProxy>( + type: Z, + selector: (proxy: P) => T +): (data: Z.infer) => Force + +type Cmd = + | ArrayDSL[keyof ArrayDSL] + | OptionalDSL[keyof OptionalDSL] + | IsoDSL[keyof IsoDSL] + | RecordDSL[keyof RecordDSL] + +type CMD = Cmd | (string & {}) + +type DSL = + | ArrayDSL + | OptionalDSL + | IsoDSL + | RecordDSL + +type ArrayDSL = typeof ArrayDSL +const ArrayDSL = { + each: 'ↆ', +} as const + +type RecordDSL = typeof RecordDSL +const RecordDSL = { + each: 'ↆ', +} as const + +type IsoDSL = typeof IsoDSL +const IsoDSL = { + id: 'Ɐ', + iso: 'ꘌ', +} as const + +type OptionalDSL = typeof OptionalDSL +const OptionalDSL = { + chain: 'ʔ', + filter: 'ǃ', +} as const + +const DSL = { + array: ArrayDSL, + record: RecordDSL, + optional: OptionalDSL, + iso: IsoDSL, +} as const + +declare namespace DSL { + namespace optional { + type chain = typeof DSL.optional.chain + type filter = typeof DSL.optional.filter + } + namespace array { + type each = typeof DSL.array.each + } + namespace record { + type each = typeof DSL.record.each + } +} + +const WitnessURI = Symbol.for('Witness') +export function createWitness(type: T): MakeProxy & { [WitnessURI]: (keyof any)[] } +export function createWitness() { + let state: (keyof any)[] = [] + const handler: ProxyHandler = { + get(target, key) { + if (key === WitnessURI) return state + else { + Reflect.set(state, state.length, key) + return new Proxy(Object_assign(target, { [WitnessURI]: state }), handler) + } + }, + } + return new Proxy({}, handler) +} + +declare namespace Witness { + namespace Kind { + interface Schema extends newtype { kind: 'schema' } + interface Proxy extends newtype { kind: 'proxy' } + } + namespace Proxy { + interface Array { + [DSL.array.each]: F.Z.Array + } + interface Optional { + [DSL.optional.chain]: F.Z.Optional + [DSL.optional.filter]: F.Z.Optional + } + } + interface Free extends HKT { [-1]: Witness.F } + type F = + | Kind.Proxy> + | Kind.Proxy> + | Kind.Schema + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + /** @deprecated */ + | Kind.Schema> + type Fixpoint = + | Kind.Proxy> + | Kind.Proxy> + | Kind.Schema + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + | Kind.Schema> + /** @deprecated */ + | Kind.Schema> +} + +function leastUpperBound(descriptors: Optic.Descriptor[], next: Optic.Type = Optic.Type.Iso): Optic.Type { + return descriptors.reduce((acc, { type: cur }) => Lattice[acc][cur], next) +} + +const schemaToPredicate + : >(type: Z) => (x: unknown) => boolean + = (type) => (x: unknown) => type.safeParse(x).success + +function findIndices(predicate: (x: T) => boolean): (xs: T[]) => number[] +function findIndices(predicate: (x: T) => boolean) { + return (xs: T[]) => xs.reduce((acc, x, ix) => predicate(x) ? [...acc, ix] : acc, Array.of()) +} + +const findSymbols = findIndices((x) => typeof x === 'symbol') + +function slicesFromIndices(xs: T[], indices: number[]) { + return indices.reduce( + (acc, _, i, all) => { + if (i === all.length - 1) return [...acc, xs] + else { + const next = all[i + 1] + return [...acc, xs.slice(0, next)] + } + }, + [Array.of()] + ) +} + +const emptyDescriptor = { type: Optic.Type.Iso, tag: 'declaration' } satisfies Optic.Descriptor + +const prefixEmptyDescriptor + : (schemaPath: Optic.SchemaPath) => Optic.SchemaPath + = ([path, descriptors, type]: Optic.SchemaPath) => { + return [path, [emptyDescriptor, ...descriptors], type] satisfies Optic.SchemaPath + } + +const KeyLookup = { + [symbol.object]: '', + [symbol.tuple]: '', + [symbol.array]: DSL.array.each, + [symbol.record]: DSL.record.each, + [symbol.optional]: DSL.optional.chain, +} as const satisfies Record + +function buildKey(path: (keyof any)[]): string { + return path.reduce( + (acc, cur) => { + const NEXT = isKeyOf(KeyLookup, cur) ? KeyLookup[cur] : String(cur) + const SEP = acc.length === 0 ? '' : NEXT.length === 0 ? '' : '.' + return `${acc}${SEP}${NEXT}` + }, + '' + ) +} + +function buildPath(path: (keyof any)[]): string[] { + return path.reduce( + (acc, cur) => { + const HEAD = isKeyOf(KeyLookup, cur) ? KeyLookup[cur] : String(cur) + const NEXT = acc.length === 0 ? HEAD.length === 0 ? [] : [HEAD] : HEAD.length === 0 ? [] : [HEAD] + return [...acc, ...NEXT] + }, + Array.of() + ) +} + +type Applied = { key: string, path: string[], optic: Optic.Any, fallback: unknown } + +/** + * TODO: + * 1. Parameterize DSL, so users can define their own + * 2. Checking that the path segment contains these particular properties is brittle. + * Experiment with ways to handle that better + */ +function getFallback(rootFallback: unknown, ...path: string[]): unknown { + console.log('rootFallback: ', rootFallback) + let HEAD: string | undefined + let CURSOR: unknown = rootFallback + while ((HEAD = path.shift()) !== undefined) { + switch (true) { + default: { + console.log('proxy-v4.getFallback encountered a value it did not know how to interpret, CURSOR: ', CURSOR) + throw Error('proxy-v4.getFallback encountered a value it did not know how to interpret: ' + HEAD) + } + case HEAD === DSL.iso.id: continue + case HEAD === DSL.optional.chain: continue + case HEAD === DSL.optional.filter: continue + case HEAD === DSL.array.each && Array_isArray(CURSOR): { + if (CURSOR.length === 0) break + else { CURSOR = CURSOR[0]; continue } + } + case HEAD === DSL.record.each && !!CURSOR && typeof CURSOR === 'object': { + const values = Object_values(CURSOR) + if (values.length === 0) break + else { CURSOR = values[0]; continue } + } + case has(HEAD)(CURSOR): CURSOR = CURSOR[HEAD]; continue + } + } + return CURSOR +} + +type IR = { + path: (keyof any)[] + optics: Optic.Descriptor[] +} + +const Get = { + Identity: (optic: any) => function IdentityGet(data: unknown) { return Pro.get(optic, data) }, + Lens: (optic: any) => function LensGet(data: unknown) { return Pro.get(optic, data) }, + Traversal: (optic: any) => function TraversalGet(data: unknown) { return Pro.collect(optic)(data) }, + Prism: (optic: any) => function PrismGet(data: unknown) { return Pro.preview(optic, data) }, +} + +const Set = { + Identity: (optic: any) => function IdentitySet(update: unknown, data: unknown) { return Pro.set(optic, update, data) }, + Lens: (optic: any) => function LensSet(update: unknown, data: unknown) { return Pro.set(optic, update, data) }, + Traversal: (optic: any) => function TraversalSet(update: unknown, data: unknown) { return Pro.set(optic, update, data) }, + Prism: (optic: any) => function PrismSet(update: unknown, data: unknown) { return Pro.set(optic, update, data) }, +} + +const Modify = { + Identity: (optic: any) => function IdentityModify(fn: (x: any) => unknown, data: unknown) { return Pro.modify(optic, fn, data) }, + Lens: (optic: any) => function LensModify(fn: (x: any) => unknown, data: unknown) { return Pro.modify(optic, fn, data) }, + Traversal: (optic: any) => function TraversalModify(fn: (x: any) => unknown, data: unknown) { return Pro.modify(optic, fn, data) }, + Prism: (optic: any) => function PrismModify(fn: (x: any) => unknown, data: unknown) { return Pro.modify(optic, fn, data) }, +} + +function interpretIR(rootFallback: unknown) { + return (x: IR) => { + const key = buildKey(x.path) + const path = buildPath(x.path) as CMD[] + const fallback = getFallback(rootFallback, ...path) + const init = [Pro.id] + + const optics = path.reduce((optics, cmd) => { + switch (true) { + default: return fn.exhaustive(cmd) + case cmd === 'ǃ': return [...optics, Pro.propOr(fallback, cmd)] + case cmd === 'ↆ': return [...optics, Pro.traverse] + case cmd === 'ʔ': return [...optics, Pro.when((x) => x != null)] + case cmd === 'Ɐ': return [...optics, Pro.id] + case typeof cmd === 'string': return [...optics, Pro.prop(cmd)] + } + }, init) + + const optic = Pro.pipe(...optics) + const get = (Get[optic[symbol.tag as never]] as Function)(optic) // TODO: type assertions + const set = (Set[optic[symbol.tag as never]] as Function)(optic) // TODO: type assertions + const modify = (Modify[optic[symbol.tag as never]] as Function)(optic) // TODO: type assertions + + return { + key, + path, + get, + set, + modify, + type: optic[symbol.tag], + fallback, + } + } +} + +const applyAll = (type: T) => (xs: { path: (string | number | symbol)[], optics: Optic.Descriptor[] }[]) => { + const fallback = withDefault(type) + const entries = xs.map( + fn.flow( + interpretIR(fallback), + ({ key, ...$ }) => [key, $] satisfies [any, any], + ) + ) + + return Object_fromEntries(entries) +} + +export type GeneratedLens = { + type: Pro.Optic.Type + get(data: Data): Focus + set(update: Focus, data: Data): Data + modify(fn: (x: Focus) => T, data: Data): Data +} + +export type buildLenses = { + [x: string]: GeneratedLens +} + +export function buildLenses>(type: Z): buildLenses +export function buildLenses(type: Z) { + return buildIntermediateRepresentation(type) + .map(applyAll(type)) + // might want to remove this later + .reduce((acc, cur) => Object_assign(Object_create(null), acc, cur)) +} + +export function buildIntermediateRepresentation(type: T) { + const schemaPaths = buildDescriptors(type).map(prefixEmptyDescriptor) + return schemaPaths.map(([path, descriptors]) => { + const indices = findSymbols(path) + const slices = slicesFromIndices(path, indices) + const firstPass = descriptors.map( + ({ type, ...descriptor }, i) => ({ + path: slices[i], + index: i, + optic: { + type: leastUpperBound(descriptors.slice(0, i), type), + ...descriptor, + } + }) + ) + const secondPass = firstPass.map(({ path, optic, index }) => ({ + path, + optics: firstPass.slice(0, index).map(({ optic }) => optic).concat(optic) + })) + return secondPass + }) +} + +export function buildDescriptors(type: T): Optic.SchemaPath[] +export function buildDescriptors>(type: T): {} { + return F.fold((x) => { + switch (true) { + default: return fn.exhaustive(x) + case tagged('void')(x): return [[[], [], x]] + case tagged('never')(x): return [[[], [], x]] + case tagged('unknown')(x): return [[[], [], x]] + case tagged('any')(x): return [[[], [], x]] + case tagged('undefined')(x): return [[[], [], x]] + case tagged('null')(x): return [[[], [], x]] + case tagged('nan')(x): return [[[], [], x]] + case tagged('boolean')(x): return [[[], [], x]] + case tagged('symbol')(x): return [[[], [], x]] + case tagged('int')(x): return [[[], [], x]] + case tagged('bigint')(x): return [[[], [], x]] + case tagged('number')(x): return [[[], [], x]] + case tagged('string')(x): return [[[], [], x]] + case tagged('date')(x): return [[[], [], x]] + case tagged('file')(x): return [[[], [], x]] + + case tagged('optional')(x): return fn.pipe( + x._zod.def.innerType, + (paths) => paths.map(([path, descriptors, schema]) => [ + [symbol.optional, ...path], + [ + { + type: Optic.Type.Prism, + tag: 'fromNullable', + nullable: undefined, + }, + ...descriptors, + ], + schema, + ] satisfies Optic.Path + ) + ) + + case tagged('nullable')(x): return fn.pipe( + x._zod.def.innerType, + (paths) => paths.map( + ([path, descriptors, schema]) => [ + [symbol.nullable, ...path], + [ + { + type: Optic.Type.Prism, + tag: 'fromNullable', + nullable: null, + }, + ...descriptors, + ], + schema, + ] satisfies Optic.Path + ) + ) + + case tagged('readonly')(x): return x._zod.def.innerType + + case tagged('array')(x): return fn.pipe( + x._zod.def.element, + (paths) => paths.map( + ([path, descriptors, schema]) => [ + [symbol.array, ...path], + [ + { + type: Optic.Type.TraversalWithPredicate, + tag: 'applicative', + applicative: 'array', + }, + ...descriptors, + ], + schema, + ] satisfies Optic.Path + ) + ) + + case tagged('record')(x): return fn.pipe( + x._zod.def.valueType, + (paths) => paths.map( + ([path, descriptors, schema]) => [ + [symbol.record, ...path], + [ + { + type: Optic.Type.TraversalWithPredicate, + tag: 'applicative', + applicative: 'record', + }, + ...descriptors, + ], + schema, + ] satisfies Optic.Path + ) + ) + + case tagged('tuple')(x): { + return fn.pipe( + x._zod.def.items, + (matrix) => ( + matrix.flatMap( + (paths, i) => ( + paths.map( + ([path, descriptors, schema]) => [ + [symbol.tuple, i, ...path], + [ + { + type: Optic.Type.Lens, + tag: 'index', + index: i, + }, + ...descriptors, + ], + schema, + ] satisfies Optic.Path + ) + ) + ) + ), + ) + } + + case tagged('object')(x): { + return fn.pipe( + Object_entries(x._zod.def.shape), + (matrix) => matrix.flatMap( + ([k, paths]) => paths.map( + ([path, descriptors, schema]) => [ + [symbol.object, k, ...path], + [ + { + type: Optic.Type.Lens, + tag: 'prop', + prop: k, + }, + ...descriptors, + ], + schema, + ] satisfies Optic.Path + ) + ) + ) + } + + case tagged('union')(x): { + return fn.pipe( + x._zod.def.options, + (matrix) => matrix.flatMap(( + paths, i) => paths.map( + ([path, descriptors, schema]) => [ + [symbol.union, i, ...path], + [ + { + type: Optic.Type.Prism, + tag: 'fromPredicate', + predicate: schemaToPredicate(schema), + }, + ...descriptors, + ], + schema, + ] satisfies Optic.Path + ) + ) + ) + } + + case tagged('enum')(x): { + return [[[], [], x]] satisfies Optic.Path[] + } + + case tagged('intersection')(x): + case tagged('map')(x): + case tagged('set')(x): + case tagged('nonoptional')(x): + case tagged('default')(x): + case tagged('prefault')(x): + case tagged('catch')(x): + case tagged('literal')(x): + case tagged('template_literal')(x): + case tagged('success')(x): + case tagged('custom')(x): + case tagged('lazy')(x): + case tagged('transform')(x): + case tagged('pipe')(x): + case tagged('promise')(x): throw Error('buildLens does not yet support schema type: ' + x._zod.def.type) + } + })(type) +} + +declare function makeProxy>(type: Z, selector: (proxy: P) => T): (data: Z.infer) => Force + +// type BoxByName = { +// [Optic.Type.Iso]: Iso.Box +// [Optic.Type.Prism]: Prism.Box +// [Optic.Type.Lens]: Lens.Box +// [Optic.Type.Optional]: Optional.Box +// [Optic.Type.Traversal]: Traversal.Box +// } + +// const declare = { +// Iso: Iso.declare, +// Prism: Prism.declare, +// Lens: Lens.declare, +// Optional: Optional.declare, +// Traversal: Traversal.declare, +// } satisfies { [K in Optic.Type]: unknown } + +// const IsoLattice = { +// Iso: Iso.fromIso, +// Prism: Prism.fromIso, +// Lens: Lens.fromIso, +// Optional: Optional.fromIso, +// Traversal: Traversal.fromIso, +// } + +// export function isoFrom(iso: Iso): Iso +// export function isoFrom(prism: Prism): Iso +// export function isoFrom(lens: Lens): Iso +// export function isoFrom(sa: Optional): Iso +// export function isoFrom(sa: Traversal): Iso +// export function isoFrom(sa: Iso | Prism | Lens | Optional | Traversal): Iso +// export function isoFrom(sa: Iso | Prism | Lens | Optional | Traversal) { +// if (!(sa[symbol.tag] in IsoLattice)) throw Error('isoFrom expected a value with a tag') +// else return IsoLattice[sa[symbol.tag]](sa as never) +// } + + +// const PrismLattice = { +// Iso: Prism.fromIso, +// Prism: Prism.fromPrism, +// Lens: Optional.join, +// Optional: Optional.fromPrism, +// Traversal: Traversal.fromPrism, +// } + +// export function prismFrom(iso: Iso): Prism +// export function prismFrom(prism: Prism): Prism +// export function prismFrom(lens: Lens): Prism +// export function prismFrom(sa: Optional): Prism +// export function prismFrom(sa: Traversal): Prism +// export function prismFrom(sa: Iso | Prism | Lens | Optional | Traversal): Prism +// export function prismFrom(sa: Iso | Prism | Lens | Optional | Traversal) { +// if (!(sa[symbol.tag] in PrismLattice)) throw Error('prismFrom expected a value with a tag') +// else return PrismLattice[sa[symbol.tag]](sa as never) +// } + +// const OptionalLattice = { +// Iso: Optional.fromIso, +// Prism: Optional.fromPrism, +// Lens: Optional.fromLens, +// Optional: Optional.fromOptional, +// Traversal: Traversal.fromOptional, +// } + +// export function optionalFrom(iso: Iso): Optional +// export function optionalFrom(prism: Prism): Optional +// export function optionalFrom(lens: Lens): Optional +// export function optionalFrom(sa: Optional): Optional +// export function optionalFrom(sa: Traversal): Optional +// export function optionalFrom(sa: Iso | Prism | Lens | Optional | Traversal): Optional +// export function optionalFrom(sa: Iso | Prism | Lens | Optional | Traversal) { +// if (!(sa[symbol.tag] in OptionalLattice)) throw Error('optionalFrom expected a value with a tag') +// else return OptionalLattice[sa[symbol.tag]](sa as never) +// } +// const LensLattice = { +// Iso: Lens.fromIso, +// Prism: Optional.join, +// Lens: Lens.fromLens, +// Optional: Optional.fromLens, +// Traversal: Traversal.fromLens, +// } + +// export function liftLens(iso: Iso): Lens +// export function liftLens(prism: Prism): Lens +// export function liftLens(lens: Lens): Lens +// export function liftLens(sa: Optional): Lens +// export function liftLens(sa: Traversal): Lens +// export function liftLens(sa: Iso | Prism | Lens | Optional | Traversal): Lens +// export function liftLens(sa: Iso | Prism | Lens | Optional | Traversal) { +// if (!(sa[symbol.tag] in LensLattice)) throw Error('lensFrom expected a value with a tag') +// else return LensLattice[sa[symbol.tag]](sa as never) +// } + +// type JoinLattice = { +// [Optic.Type.Iso]: [ +// typeof Optic.Type.Iso +// ] +// [Optic.Type.Prism]: [ +// typeof Optic.Type.Iso, +// typeof Optic.Type.Prism, +// ] +// [Optic.Type.Lens]: [ +// typeof Optic.Type.Iso, +// typeof Optic.Type.Lens, +// ] +// [Optic.Type.Optional]: [ +// typeof Optic.Type.Iso, +// typeof Optic.Type.Prism, +// typeof Optic.Type.Lens, +// ] +// [Optic.Type.Traversal]: [ +// typeof Optic.Type.Iso, +// typeof Optic.Type.Prism, +// typeof Optic.Type.Lens, +// typeof Optic.Type.Optional, +// typeof Optic.Type.Traversal, +// ] +// } + +// type MeetLattice = { +// [Optic.Type.Traversal]: [ +// typeof Optic.Type.Traversal, +// ] +// [Optic.Type.Optional]: [ +// typeof Optic.Type.Traversal, +// typeof Optic.Type.Optional, +// ] +// [Optic.Type.Lens]: [ +// typeof Optic.Type.Traversal, +// typeof Optic.Type.Optional, +// typeof Optic.Type.Lens, +// ] +// [Optic.Type.Prism]: [ +// typeof Optic.Type.Traversal, +// typeof Optic.Type.Optional, +// typeof Optic.Type.Prism, +// ] +// [Optic.Type.Iso]: [ +// typeof Optic.Type.Traversal, +// typeof Optic.Type.Optional, +// typeof Optic.Type.Lens, +// typeof Optic.Type.Prism, +// typeof Optic.Type.Iso, +// ] +// } + +// type IsoUpcast = JoinLattice[typeof Optic.Type.Iso][number] +// type IsoDowncast = MeetLattice[typeof Optic.Type.Iso][number] +// type PrismUpcast = JoinLattice[typeof Optic.Type.Prism][number] +// type PrismDowncast = MeetLattice[typeof Optic.Type.Prism][number] +// type LensUpcast = JoinLattice[typeof Optic.Type.Lens][number] +// type LensDowncast = MeetLattice[typeof Optic.Type.Lens][number] +// type OptionalUpcast = JoinLattice[typeof Optic.Type.Optional][number] +// type OptionalDowncast = MeetLattice[typeof Optic.Type.Optional][number] +// type TraveralUpcast = JoinLattice[typeof Optic.Type.Traversal][number] +// type TraveralDowncast = MeetLattice[typeof Optic.Type.Traversal][number] + +// type Join = typeof JoinLattice +// const JoinLattice = { +// [Optic.Type.Iso]: { +// from: { +// [Optic.Type.Iso]: Iso.fromIso, +// } +// }, +// [Optic.Type.Prism]: { +// from: { +// [Optic.Type.Iso]: Prism.fromIso, +// [Optic.Type.Prism]: Prism.fromPrism, +// } +// }, +// [Optic.Type.Lens]: { +// from: { +// [Optic.Type.Iso]: Lens.fromIso, +// [Optic.Type.Lens]: Lens.fromLens, +// } +// }, +// [Optic.Type.Optional]: { +// from: { +// [Optic.Type.Iso]: Optional.fromIso, +// [Optic.Type.Prism]: Optional.fromPrism, +// [Optic.Type.Lens]: Optional.fromLens, +// [Optic.Type.Optional]: Optional.fromOptional, +// } +// }, +// [Optic.Type.Traversal]: { +// from: { +// [Optic.Type.Iso]: Traversal.fromIso, +// [Optic.Type.Prism]: Traversal.fromPrism, +// [Optic.Type.Lens]: Traversal.fromLens, +// [Optic.Type.Optional]: Traversal.fromOptional, +// [Optic.Type.Traversal]: Traversal.fromTraversal, +// } +// } +// } + +// type Meet = typeof MeetLattice +// const MeetLattice = { +// [Optic.Type.Iso]: { +// to: { +// [Optic.Type.Iso]: Iso.fromIso, +// [Optic.Type.Prism]: Prism.fromIso, +// [Optic.Type.Lens]: Lens.fromIso, +// [Optic.Type.Optional]: Optional.fromIso, +// [Optic.Type.Traversal]: Traversal.fromIso, +// } +// }, +// [Optic.Type.Prism]: { +// to: { +// [Optic.Type.Prism]: Prism.fromPrism, +// [Optic.Type.Optional]: Optional.fromPrism, +// [Optic.Type.Traversal]: Traversal.fromPrism, +// } +// }, +// [Optic.Type.Lens]: { +// to: { +// [Optic.Type.Lens]: Lens.fromLens, +// [Optic.Type.Optional]: Optional.fromLens, +// [Optic.Type.Traversal]: Traversal.fromLens, +// } +// }, +// [Optic.Type.Optional]: { +// to: { +// [Optic.Type.Optional]: Optional.fromOptional, +// [Optic.Type.Traversal]: Traversal.fromOptional, +// } +// }, +// [Optic.Type.Traversal]: { +// to: { +// [Optic.Type.Traversal]: Traversal.fromTraversable, +// } +// } +// } + +// export type Lattice = typeof Lattice +// export const Lattice = { +// [Optic.Type.Iso]: { +// [Optic.Type.Iso]: Iso.compose, +// [Optic.Type.Prism]: (ISO: Iso, PRISM: Prism) => Prism.compose(Prism.fromIso(ISO), PRISM), +// [Optic.Type.Lens]: (ISO: Iso, LENS: Lens) => Lens.compose(Lens.fromIso(ISO), LENS), +// [Optic.Type.Optional]: (ISO: Iso, OPTIONAL: Optional) => Optional.compose(Optional.fromIso(ISO), OPTIONAL), +// [Optic.Type.Traversal]: (ISO: Iso, TRAVERSAL: Traversal) => Traversal.compose(Traversal.fromIso(ISO), TRAVERSAL), +// }, +// [Optic.Type.Prism]: { +// [Optic.Type.Prism]: Prism.compose, +// [Optic.Type.Iso]: (PRISM: Prism, ISO: Iso) => Prism.compose(PRISM, Prism.fromIso(ISO)), +// [Optic.Type.Lens]: (PRISM: Prism, LENS: Lens) => Optional.join(PRISM, LENS), +// [Optic.Type.Optional]: (PRISM: Prism, OPTIONAL: Optional) => Optional.compose(Optional.fromPrism(PRISM), OPTIONAL), +// [Optic.Type.Traversal]: (PRISM: Prism, TRAVERSAL: Traversal) => Traversal.compose(Traversal.fromPrism(PRISM), TRAVERSAL), +// }, +// [Optic.Type.Lens]: { +// [Optic.Type.Lens]: Lens.compose, +// [Optic.Type.Iso]: (LENS: Lens, ISO: Iso) => Lens.compose(LENS, Lens.fromIso(ISO)), +// [Optic.Type.Prism]: (LENS: Lens, PRISM: Prism) => Optional.join(LENS, PRISM), +// [Optic.Type.Optional]: (LENS: Lens, OPTIONAL: Optional) => Optional.compose(Optional.fromLens(LENS), OPTIONAL), +// [Optic.Type.Traversal]: (LENS: Lens, TRAVERSAL: Traversal) => Traversal.compose(Traversal.fromLens(LENS), TRAVERSAL), +// }, +// [Optic.Type.Optional]: { +// [Optic.Type.Optional]: Optional.compose, +// [Optic.Type.Iso]: (OPTIONAL: Optional, ISO: Iso) => Optional.compose(OPTIONAL, Optional.fromIso(ISO)), +// [Optic.Type.Prism]: (OPTIONAL: Optional, PRISM: Prism) => Optional.compose(OPTIONAL, Optional.fromPrism(PRISM)), +// [Optic.Type.Lens]: (OPTIONAL: Optional, LENS: Lens) => Optional.compose(OPTIONAL, Optional.fromLens(LENS)), +// [Optic.Type.Traversal]: (OPTIONAL: Optional, TRAVERSAL: Traversal) => Traversal.compose(Traversal.fromOptional(OPTIONAL), TRAVERSAL), +// }, +// [Optic.Type.Traversal]: { +// [Optic.Type.Traversal]: Traversal.compose, +// [Optic.Type.Iso]: (TRAVERSAL: Traversal, ISO: Iso) => Traversal.compose(TRAVERSAL, Traversal.fromIso(ISO)), +// [Optic.Type.Prism]: (TRAVERSAL: Traversal, PRISM: Prism) => Traversal.compose(TRAVERSAL, Traversal.fromPrism(PRISM)), +// [Optic.Type.Lens]: (TRAVERSAL: Traversal, LENS: Lens) => Traversal.compose(TRAVERSAL, Traversal.fromLens(LENS)), +// [Optic.Type.Optional]: (TRAVERSAL: Traversal, OPTIONAL: Optional) => Traversal.compose(TRAVERSAL, Traversal.fromOptional(OPTIONAL)), +// } +// } + +// function downcast(target: T): (source: S) => Join[T]['from'][S] +// function downcast(target: Optic.Type) { +// const lookup = JoinLattice[target].from +// return (source: keyof typeof lookup) => lookup[source] +// } + +// function upcast(source: S): (target: T) => Meet[S]['to'][T] +// function upcast(source: Optic.Type) { +// const lookup = MeetLattice[source].to +// return (target: keyof typeof lookup) => lookup[target] +// } + +/** + * @example + * [ + * [ + * [ + * Symbol(@traversable/schema/URI::union), + * 0, + * Symbol(@traversable/schema/URI::array), + * ], + * "z.number()", + * [ + * { + * "predicate": [Function], + * "tag": "predicate", + * "type": "Traversal", + * }, + * { + * "applicative": "array", + * "tag": "applicative", + * "type": "Traversal", + * }, + * { + * "tag": "none", + * "type": "Iso", + * }, + * ], + * ], + * [ + * [ + * Symbol(@traversable/schema/URI::union), + * 1, + * Symbol(@traversable/schema/URI::object), + * "a", + * Symbol(@traversable/schema/URI::optional), + * ], + * "z.string()", + * [ + * { + * "predicate": [Function], + * "tag": "predicate", + * "type": "Optional", + * }, + * { + * "prop": "a", + * "tag": "prop", + * "type": "Optional", + * }, + * { + * "nullable": undefined, + * "tag": "fromNullable", + * "type": "Prism", + * }, + * { + * "tag": "none", + * "type": "Iso", + * }, + * ], + * ], + * [ + * [ + * Symbol(@traversable/schema/URI::union), + * 2, + * ], + * "z.boolean()", + * [ + * { + * "predicate": [Function], + * "tag": "predicate", + * "type": "Prism", + * }, + * { + * "tag": "none", + * "type": "Iso", + * }, + * ], + * ], + * ] + */ + + +// export function fromPredicate< +// SA extends Optic.LowerBound, +// K extends keyof any, +// A extends { [P in K]: unknown } +// >( +// predicate: (x: unknown) => boolean, +// optic: SA +// ): +// | _.Prism<_.Prism.inferSource, A[K]> +// | _.Optional<_.Optional.inferSource, A[K]> +// | _.Traversal<_.Traversal.inferSource, A[K]> + +// export function fromPredicate(predicate: (x: unknown) => boolean, iso: _.Iso): _.Prism +// export function fromPredicate(predicate: (x: unknown) => boolean, prism: _.Prism): _.Prism +// export function fromPredicate(predicate: (x: unknown) => boolean, lens: _.Iso): _.Optional +// export function fromPredicate(predicate: (x: unknown) => boolean, optional: _.Optional): _.Optional +// export function fromPredicate(predicate: (x: unknown) => boolean, traversal: _.Traversal): _.Traversal +// export function fromPredicate(predicate: (x: unknown) => boolean): _.Prism | _.Optional | _.Traversal + +// export function fromPredicate( +// predicate: (x: unknown) => boolean, +// SA: Optic.Any = _.Prism.declare() as never +// ) { +// const AB = _.Prism.fromPredicate(predicate) +// switch (true) { +// default: return fn.exhaustive(SA) +// case _.Prism.is(SA): return _.Prism.compose(SA, AB) +// case _.Iso.is(SA): return _.Prism.compose(_.Prism.fromIso(SA), AB) +// case _.Lens.is(SA): return _.Optional.compose(_.Optional.fromLens(SA), _.Optional.fromPrism(AB)) +// case _.Optional.is(SA): return _.Optional.compose(SA, _.Optional.fromPrism(AB)) +// case _.Traversal.is(SA): return _.Traversal.compose(SA, _.Traversal.fromPrism(AB)) +// } +// } + +// export function prop< +// SA extends Optic.LowerBound, +// K extends keyof any, +// A extends { [P in K]: unknown } +// >( +// prop: K, +// optic: SA +// ): +// | _.Lens<_.Lens.inferSource, A[K]> +// | _.Optional<_.Optional.inferSource, A[K]> +// | _.Traversal<_.Traversal.inferSource, A[K]> + +// export function prop(prop: K, iso: _.Iso): _.Lens +// export function prop(prop: K, lens: _.Lens): _.Lens +// export function prop(prop: K, prism: _.Prism): _.Optional +// export function prop(prop: K, optional: _.Optional): _.Optional +// export function prop(prop: K, traversal: _.Traversal): _.Traversal +// export function prop(prop: K): _.Lens +// export function prop(prop: K, SA: Optic.Any = _.Lens.declare() as never) { +// const AK = _.Lens.prop(prop) +// switch (true) { +// default: return fn.exhaustive(SA) +// case _.Lens.is(SA): return _.Lens.compose(SA, AK) +// case _.Iso.is(SA): return _.Lens.compose(_.Lens.fromIso(SA), AK) +// case _.Prism.is(SA): return _.Optional.compose(_.Optional.fromPrism(SA), _.Optional.fromLens(AK)) +// case _.Optional.is(SA): return _.Optional.compose(SA, _.Optional.fromLens(AK)) +// case _.Traversal.is(SA): return _.Traversal.compose(SA, _.Traversal.fromLens(AK)) +// } +// } + +// const TraversalLattice = { +// Iso: _.Traversal.fromIso, +// Prism: _.Traversal.fromPrism, +// Lens: _.Traversal.fromLens, +// Optional: _.Traversal.fromOptional, +// Traversal: _.Traversal.fromTraversal, +// } + +// function traversalFrom(iso: _.Iso): _.Traversal +// function traversalFrom(prism: _.Prism): _.Traversal +// function traversalFrom(lens: _.Lens): _.Traversal +// function traversalFrom(sa: _.Optional): _.Traversal +// function traversalFrom(sa: _.Traversal): _.Traversal +// function traversalFrom(sa: _.Iso | _.Prism | _.Lens | _.Optional | _.Traversal): _.Traversal +// function traversalFrom(sa: _.Iso | _.Prism | _.Lens | _.Optional | _.Traversal) { +// if (!(sa[symbol.tag] in TraversalLattice)) throw Error('traversalFrom expected a value with a tag') +// else return TraversalLattice[sa[symbol.tag]](sa as never) +// } + +// const declarationGet = fn.identity +// const declarationSet = fn.const(fn.identity) + +// const propGet +// : (prop: string) => (object: unknown) => unknown +// = (prop) => (object) => { +// if (!has(prop)(object)) throw Error('proxy-v4.propGet: could not find property ' + prop + ' in object') +// else return object[prop] +// } + +// function chunkBy(predicate: (x: T) => boolean, ...xs: T[]): T[][] { +// let ix = 0 +// let x: T | undefined +// let chunk = Array.of() +// let prev = Array.of() +// let out = Array.of() +// while ((x = xs.shift()) !== undefined) { +// chunk.push(x) +// if (predicate(x)) { +// // out.push(chunk) +// ( +// out.push(prev.concat(chunk)), +// prev = chunk, +// chunk = Array.of() +// ) +// } +// } +// return out +// } + +// // const isArrayEachCmd = (x: unknown) => x === 'ↆ' +// // const isNotCmd = (x: unknown) => typeof x === 'string' +// // && !Object.values(DSL.array).includes(x as never) +// // && !Object.values(DSL.optional).includes(x as never) + +// // function example( +// // CMD: ['a', 'ↆ', 'b', 'ↆ', 'c'], +// // data: { +// // a: [ +// // { +// // b: [ +// // { c: 1 }, +// // { c?: -1 }, +// // { c: 2 } +// // ] +// // }, +// // { b?: [{ c?: -2 }] }, +// // { +// // b: [ +// // { c: 3 }, +// // { c: 4 }, +// // { c: undefined } +// // ] +// // } +// // ] +// // } +// // ) { +// // const chunks = chunkBy(isArrayEachCmd, ...CMD) +// // // let cursor: unknown = structuredClone(data) +// // // let paths = Array.of() + +// // // function go(cursor: unknown, cmds: CMD[], paths: (string | number)[][]): (string | number)[][] { +// // // const cmd = cmds[0] +// // // console.group('\n\ncalling go, CMD: ', cmd) +// // // console.debug('\ncursor: ', cursor) +// // // console.debug('\ncmds: ', cmds) +// // // console.debug('\npaths: ', paths) +// // // console.debug() +// // // console.groupEnd() + +// // // if (cmds.length === 0) { +// // // console.log('BOTTOM', paths) +// // // return paths +// // // } + +// // // else if (cmd === '.') return go(cursor, cmds.slice(1), paths) + +// // // else if (has(cmd)(cursor)) +// // // return go(cursor[cmd], cmds.slice(1), paths.length === 0 ? [[cmd]] : paths.map((path) => [...path, cmd])) + +// // // else if (cmd === 'ʔ') throw Error('Unimplemented: ' + cmd) + +// // // else if (cmd === 'ǃ') throw Error('Unimplemented: ' + cmd) + +// // // else if (cmd === 'ↆ' && Array_isArray(cursor)) +// // // return cursor.flatMap((x, i) => go(x, cmds.slice(1), paths.map((path) => [...path, i]))) + +// // // else +// // // return paths +// // // } + +// // // console.log('END: ', go(data, CMD, [])) + +// // /** +// // * ['a', 0, 'b', 0, 'c'] +// // * ['a', 0, 'b', 1, 'c'] +// // * ['a', 0, 'b', 2, 'c'] +// // * ['a', 1, 'b', 0, 'c'] (if it were to exist) +// // * ['a', 2, 'b', 0, 'c'] +// // * ['a', 2, 'b', 1, 'c'] +// // * ['a', 2, 'b', 2, 'c'] +// // * +// // * ['a', 0, 'b', 0, 'c'] +// // * ['a', 0, 'b', 1, 'c'] +// // * ['a', 0, 'b', 2, 'c'] +// // * ['a', 1, 'b', 0, 'c'] (if it were to exist) +// // * ['a', 2, 'b', 0, 'c'] +// // * ['a', 2, 'b', 1, 'c'] +// // * ['a', 2, 'b', 2, 'c'] +// // */ + +// // return [ +// // data?.a?.[0]?.b?.[0]?.c ?? symbol.notfound, +// // data?.a?.[0]?.b?.[1]?.c ?? symbol.notfound, +// // data?.a?.[0]?.b?.[2]?.c ?? symbol.notfound, +// // data?.a?.[1]?.b?.[0]?.c ?? symbol.notfound, +// // data?.a?.[2]?.b?.[0]?.c ?? symbol.notfound, +// // data?.a?.[2]?.b?.[1]?.c ?? symbol.notfound, +// // data?.a?.[2]?.b?.[2]?.c ?? symbol.notfound, +// // ] as const satisfies any[] +// // } + +// // const example_01 = example( +// // ['a', 'ↆ', 'b', 'ↆ', 'c'], +// // { +// // a: [ +// // { +// // b: [ +// // { c: 1 }, // [a, 0, b, 0, c] +// // {}, // [a, 0, b, 1, c] +// // { c: 2 } // [a, 0, b, 2, c] +// // ] +// // }, +// // {}, // [a, 1] +// // { +// // b: [ +// // { c: 3 }, // [a, 2, b, 0, c] +// // { c: 4 }, // [a, 2, b, 1, c] +// // { c: undefined } // [a, 2, b, 2, c] +// // ] +// // } +// // ] +// // } +// // ) + +// // console.log('\n\nexample:\n\n', example_01) + +// // for (let chunk of chunks) { +// // let accessor: unknown = cursor +// // let cmd1: CMD | undefined +// // while ((cmd1 = chunk.shift()) !== undefined) { +// // /** +// // * if (cmd === '?') ... +// // * if (cmd === '!') ... +// // */ +// // if (cmd1 === 'ↆ' && Array_isArray(accessor)) { +// // accessor.forEach((x, i) => { +// // let cmd2: CMD | undefined +// // while ((cmd2 = chunk.shift()) !== undefined) { +// // if (isNotCmd(cmd2) && has(cmd2)(x)) { +// // x = x[cmd2] +// // } +// // } +// // accessors.push(accessor) +// // }) +// // break +// // } +// // if (has(cmd1)(accessor)) { +// // accessor = accessor[cmd1] +// // } +// // else { +// // accessor = symbol.notfound +// // } +// // accessors.push(accessor) +// // } +// // } + +// function createGetter(fallback: unknown, ...path: CMD[]) { +// let cmd: CMD | undefined +// return function getter(data: unknown) { +// let cursor = data +// while ((cmd = path.shift()) !== undefined) { +// if (cmd === DSL.array.each && Array_isArray(cursor)) { +// // cursor = cursor.map((x) => ) +// } +// } +// } +// } + +// function chunksFromIndices_(xs: T[], indices: number[]) { +// return indices.reduce( +// (acc, prev, i, all) => { +// if (i === all.length - 1) return [...acc, xs.slice(i)] +// else { +// const next = all[i + 1] +// return [...acc, xs.slice(prev, next)] +// } +// }, +// [[] as T[]] +// ) +// } + +// function windowsFromIndices(xs: T[], indices: number[]) { +// return indices.reduce( +// (acc, prev, i, all) => { +// if (i === all.length - 1) return [...acc, [prev, xs.length - 1]] +// else { +// const next = all[i + 1] +// return [...acc, [prev, next]] +// } +// }, +// [[0, 0]] +// ) +// } + +// const bound = (bound: number, fallback: number) => (x: number) => x > bound ? x : fallback + +// const preprocess = F.fold((x) => { +// switch (true) { +// case tagged('array')(x): return { kind: 'proxy', [DSL.array.each]: x } +// case tagged('optional')(x): return { kind: 'proxy', [DSL.optional.chain]: x, [DSL.optional.filter]: x } +// default: return ( +// (x as typeof x & { kind: 'schema' }).kind = 'schema', +// (x as typeof x & { kind: 'schema' }) +// ) +// } +// }) + + + +// const applyEach = (rootFallback: unknown) => (x: { path: (keyof any)[], optics: Optic.Descriptor[] }) => { +// const key = buildKey(x.path) +// const path = buildPath(x.path) +// const fallback = getFallback(rootFallback, ...path) + +// return x.optics.reduce( +// ({ optic, ...$ }, descriptor_, i) => { +// const descriptor = { ...descriptor_, fallback } +// switch (descriptor.tag) { +// default: return fn.exhaustive(descriptor) +// case 'applicative': return { key, path, optic: compose(optic, applicative(descriptor)), fallback: [descriptor.fallback] } satisfies Applied +// case 'declaration': return { key, path, optic: compose(optic, declaration(descriptor)), fallback: descriptor.fallback } satisfies Applied +// case 'fromNullable': return { key, path, optic: compose(optic, fromNullable(descriptor)), fallback: descriptor.fallback } satisfies Applied +// case 'fromPredicate': return { key, path, optic: compose(optic, fromPredicate(descriptor)), fallback: descriptor.fallback } satisfies Applied +// case 'index': return { key, path, optic: compose(optic, index(descriptor)), fallback: descriptor.fallback } satisfies Applied +// case 'prop': return { key, path, optic: compose(optic, prop(descriptor)), fallback: descriptor.fallback } satisfies Applied +// } +// }, +// { key: '', path: Array.of(), optic: _.Iso.declare() as Optic.Any, fallback: rootFallback } satisfies Applied +// ) +// } diff --git a/packages/schema-zod-adapter/src/seed-v4.ts b/packages/schema-zod-adapter/src/seed-v4.ts index 44c23df7..68b5fa86 100644 --- a/packages/schema-zod-adapter/src/seed-v4.ts +++ b/packages/schema-zod-adapter/src/seed-v4.ts @@ -1,4 +1,4 @@ -import { z } from 'zod4' +import { z } from 'zod/v4' import type * as T from '@traversable/registry' import type { newtype } from '@traversable/registry' import { fn, Object_keys } from '@traversable/registry' @@ -37,6 +37,7 @@ export const byTag = { success: 4000 as const, catch: 5000 as const, default: 5500 as const, + prefault: 5600 as const, intersection: 6000 as const, map: 6500 as const, record: 7000 as const, @@ -94,6 +95,7 @@ export declare namespace Seed { | Seed.Success | Seed.Catch | Seed.Default + | Seed.Prefault | Seed.Map | Seed.Pipe | Seed.Custom @@ -168,6 +170,7 @@ export declare namespace Seed { interface Success extends newtype<[seed: byTag['success'], def: T]> {} interface Catch extends newtype<[seed: byTag['catch'], def: T]> {} interface Default extends newtype<[seed: byTag['default'], def: T]> {} + interface Prefault extends newtype<[seed: byTag['prefault'], def: T]> {} type UnaryMap = { array: Seed.Array record: Seed.Record @@ -182,6 +185,7 @@ export declare namespace Seed { success: Seed.Success catch: Seed.Catch default: Seed.Default + prefault: Seed.Prefault map: Seed.Map pipe: Seed.Pipe custom: Seed.Custom @@ -261,6 +265,7 @@ export const Functor: T.Functor> = { 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])] diff --git a/packages/schema-zod-adapter/src/toString-v4.ts b/packages/schema-zod-adapter/src/toString-v4.ts index 569be658..9e0763ab 100644 --- a/packages/schema-zod-adapter/src/toString-v4.ts +++ b/packages/schema-zod-adapter/src/toString-v4.ts @@ -1,4 +1,4 @@ -import { z } from 'zod4' +import { z } from 'zod/v4' import type { Showable } from '@traversable/registry' import { Array_isArray, fn, has, Number_isNatural, Object_entries, parseKey } from '@traversable/registry' import { Json } from '@traversable/json' @@ -179,7 +179,8 @@ export function toString(schema: z.ZodType, options?: toString.Options): string case tagged('union')(x): return `${z}.union([${x._zod.def.options.join(', ')}])` case tagged('lazy')(x): return `${z}.lazy(() => ${x._zod.def.getter()})` case tagged('pipe')(x): return `${x._zod.def.in}.pipe(${x._zod.def.out})` - case tagged('default')(x): return `${x._zod.def.innerType}.default(${serializeShort(x._zod.def.defaultValue(Ctx)!)})` + case tagged('default')(x): return `${x._zod.def.innerType}.default(${serializeShort(x._zod.def.defaultValue)})` + case tagged('prefault')(x): return `${x._zod.def.innerType}.prefault(${serializeShort(x._zod.def.defaultValue)})` case tagged('catch')(x): return `${x._zod.def.innerType}.catch(${serializeShort(x._zod.def.catchValue(Ctx)!)})` case tagged('template_literal')(x): return `${z}.templateLiteral([${foldTemplateParts(x._zod.def.parts as never)}])` case tagged('nonoptional')(x): return `${z}.nonoptional(${x._zod.def.innerType})` diff --git a/packages/schema-zod-adapter/src/traversal-v4.ts b/packages/schema-zod-adapter/src/traversal-v4.ts new file mode 100644 index 00000000..60935956 --- /dev/null +++ b/packages/schema-zod-adapter/src/traversal-v4.ts @@ -0,0 +1,307 @@ +import type { Box, HKT } from '@traversable/registry' +import { fn, has, Object_assign, Object_create, Option, symbol } from '@traversable/registry' + +import { Applicative, Apply, Const, Monoid, Traversable, Semigroup } from './instances-v4.js' + +import type { Optic } from './proxy-v4.js' +import { Iso, Lens, Optional, Prism } from './optics-v4.js' +import type { TraversalWithPredicate } from './optics-v4.js' +type Traversal = import('./optics-v4.js').Traversal + +export type Any = Traversal +export type AnyWithPredicate = TraversalWithPredicate +export type { Infer as infer, Source as source, Target as target } +type Infer = SA extends Traversal ? [S: S, A: A] : never +type Source = SA extends Traversal ? S : never +type Target = SA extends Traversal ? T : never +export type { + F as Box, + FwithFallback as BoxWithFallback, +} +interface F extends HKT<{ [0]: unknown, [1]: unknown }> { [-1]: Traversal } +interface FwithFallback extends HKT<{ [0]: unknown, [1]: unknown }> { [-1]: TraversalWithPredicate } +export type UnknownSource = unknown extends Source ? {} | null | undefined : never + +export const identity = Object_create>(null) +identity[symbol.tag] = 'Traversal' as const +identity.modify = fn.const(fn.identity) + +export const identityWithPredicate = Object_create>(null) +identityWithPredicate[symbol.tag] = 'TraversalWithPredicate' as const +identityWithPredicate.modifyWithFallback = fn.const(fn.identity) + +export function Traversal() {} + +Traversal.new = ((modify) => Object_assign( + Object_create(null), + identity, + { modify } +)) satisfies (modify: Traversal['modify']) => Traversal + +Traversal.newWithFallback = ((modifyWithFallback) => { + return Object_assign( + Object_create(null), + identityWithPredicate, + { modifyWithFallback } + ) +}) satisfies (modifyWithFallback: TraversalWithPredicate['modifyWithFallback']) => TraversalWithPredicate + +export const declare + : () => Traversal + = fn.const(identity) + +export const declareWithFallback + : () => TraversalWithPredicate + = fn.const(identityWithPredicate) + +export function is(x: Optic.Any): x is TraversalWithPredicate +export function is(x: unknown): x is TraversalWithPredicate +export function is(x: unknown) { + return ( + has(symbol.tag, (x) => x === identityWithPredicate[symbol.tag])(x) + // || ( has('modify', (x) => typeof x === 'function')(x)) + ) +} + +export function compose(sa: Traversal, ab: Traversal): Traversal +export function compose(ab: Traversal): (sa: Traversal) => Traversal +export function compose( + ...args: + | [ab: Traversal] + | [sa: Traversal, ab: Traversal] +): Traversal | ((sa: Traversal) => Traversal) { + if (args.length === 1) return (sa: Traversal) => compose(sa, ...args) + else { + const [sa, ab] = args + return Traversal.new((F) => (f) => sa.modify(F)(ab.modify(F)(f))) + } +} + +export function composeWithFallback(sa: TraversalWithPredicate, ab: TraversalWithPredicate, fallback: any): TraversalWithPredicate +export function composeWithFallback(ab: TraversalWithPredicate): (sa: TraversalWithPredicate, fallback: any) => TraversalWithPredicate +export function composeWithFallback( + ...args: + | [ab: TraversalWithPredicate] + | [sa: TraversalWithPredicate, ab: TraversalWithPredicate, fallback: any] +): TraversalWithPredicate | ((sa: TraversalWithPredicate, fallback: any) => TraversalWithPredicate) { + if (args.length === 1) return (sa: TraversalWithPredicate, fallback: any) => composeWithFallback(sa, args[0], fallback) + else { + const [sa, ab, fallback] = args + return Traversal.newWithFallback((F) => (f) => sa.modifyWithFallback(F)(ab.modifyWithFallback(F)(f, fallback), fallback)) + } +} + + +export const fromTraversal + : (traversal: Traversal) => Traversal + = fn.identity + +export const fromTraversalWithFallback + : (traversal: TraversalWithPredicate) => TraversalWithPredicate + = fn.identity + +export const toTraversalWithFallback + : (traversal: Traversal, fallback: B) => TraversalWithPredicate + = (traversal, fallback) => { + return Traversal.newWithFallback((F) => traversal.modify({ ...F, of() { return fallback } })) + } + +export function fromIso(iso: Iso): Traversal { + return Traversal.new((F) => (f) => (s) => fn.pipe( + f(iso.decode(s)), + F.map(iso.encode) + )) +} + +export function fromIsoWithFallback(iso: Iso): TraversalWithPredicate { + return Traversal.newWithFallback((F) => (f) => (s) => fn.pipe( + f(iso.decode(s)), + F.map(iso.encode) + )) +} + + +export function fromPrism(prism: Prism): Traversal { + return Traversal.new((F) => (f) => (s) => fn.pipe( + prism.match(s), + (option) => + Option.isNone(option) + ? F.of(s) + : fn.pipe( + f(option.value), + F.map((a) => Prism.set(a)(prism)(s)), + ) + )) +} + +export function fromPrismWithFallback(prism: Prism, fallback: A): TraversalWithPredicate { + return Traversal.newWithFallback((F) => (f) => (s) => fn.pipe( + prism.match(s), + (option) => + Option.isNone(option) + ? fallback + : fn.pipe( + f(option.value), + F.map((a) => Prism.set(a)(prism)(s)), + ) + )) +} + +export function fromLens(lens: Lens): Traversal { + return Traversal.new((F) => (f) => (s) => fn.pipe( + f(lens.get(s)), + F.map((a) => lens.set(a)(s)) + )) +} + +export function fromLensWithFallback(lens: Lens, fallback: B): TraversalWithPredicate { + return Traversal.newWithFallback((F) => (f) => (s) => fn.pipe( + f(lens.get(s)), + // f(lens.get(s) ?? fallback), + F.map((a) => lens.set(a)(s)) + )) +} + +export function prop, K extends keyof any>(traversal: SA, k: K): Traversal> +export function prop, A extends Target, K extends keyof A>(traversal: SA, k: K): Traversal +export function prop(k: K): Traversal<{ [x: string]: unknown }, unknown> +export function prop(k: K): Traversal<{ [x: number]: unknown }, unknown> +export function prop( + ...args: + | [k: keyof any] + | [sa: Any, k: keyof any] +) { + const [sa, k] = args.length === 1 ? [declare(), args[0]] : args + return fn.pipe( + Lens.declare(), + (lens) => Lens.prop(lens, k), + fromLens, + (ab) => compose(sa, ab) + ) +} + +export function propWithFallback, K extends keyof any>(traversal: SA, k: K, fallback: unknown): TraversalWithPredicate> +export function propWithFallback, A extends Target, K extends keyof A>(traversal: SA, k: K, fallback: unknown): TraversalWithPredicate +export function propWithFallback(k: K, fallback: unknown): TraversalWithPredicate<{ [x: string]: unknown }, unknown> +export function propWithFallback(k: K, fallback: unknown): TraversalWithPredicate<{ [x: number]: unknown }, unknown> +export function propWithFallback( + ...args: + | [k: keyof any, fallback: unknown] + | [sa: AnyWithPredicate, k: keyof any, fallback: unknown] +) { + const [sa, k, fallback] = args.length === 2 ? [declareWithFallback(), args[0], args[1]] : args + return fn.pipe( + Lens.declare(), + (lens) => Lens.prop(lens, k), + (lens) => fromLensWithFallback(lens, fallback), + (ab) => composeWithFallback(sa, ab, fallback) + ) +} + + +export function fromOptional(sa: Optional): Traversal { + return Traversal.new((F) => (f) => (s) => fn.pipe( + sa.getOption(s), + (option) => + Option.isNone(option) + ? F.of(s) + : fn.pipe( + f(option.value), + F.map((a) => sa.set(a)(s)) + ) + )) +} + +export function fromOptionalWithFallback(sa: Optional, fallback: A): TraversalWithPredicate { + return Traversal.newWithFallback((F) => (f) => (s) => fn.pipe( + sa.getOption(s), + (option) => + Option.isNone(option) + ? fallback + : fn.pipe( + f(option.value), + F.map((a) => sa.set(a)(s)) + ) + )) +} + +export function fromTraversable(T: Traversable): () => Traversal, A> { + return () => Traversal.new((F) => (f) => (s) => T.traverse(F)(s, f)) +} + +export function fromTraversableWithFallback(T: Semigroup.Traversable): (fallback: A) => TraversalWithPredicate, A> { + return (fallback) => Traversal.newWithFallback((F) => (f) => (s) => T.traverseMaybe(F)(s, f, () => fallback)) +} + +export function traverse(T: Traversable): (sta: Traversal>) => Traversal +export function traverse(T: Traversable) { + return (sta: Traversal>) => compose(sta, fromTraversable(T)()) satisfies Traversal +} + +// export function traverseWithFallback(T: Traversable): ( +// sta: TraversalWithPredicate>, +// predicate: (x: Box) => boolean, +// onFalse: Box +// ) => TraversalWithPredicate + +export function traverseWithFallback(T: Semigroup.Traversable) { + return ( + sta: TraversalWithPredicate>, + predicate: (x: Box) => boolean, + onFalse: Box + ): TraversalWithPredicate => { + // TODO: look into removing this type assertion vvvvvvvv + return composeWithFallback(sta, fromTraversableWithFallback(T)(onFalse as never), onFalse) + } +} + +export const array = traverse(Traversable.array) + +export const traverseArray: { + (sta: Traversal): Traversal + (sta: Traversal): Traversal +} = array + +export const record = traverse(Traversable.record) + +export const traverseRecord: { + (sta: Traversal): Traversal> + (sta: Traversal): Traversal +} = record + +export const modify + : (f: (a: A) => B) => (sa: Traversal) => ((s: S) => S) + = (f) => (sa) => sa.modify(Applicative.identity)(f) + + +export const modifyWithFallback + : (f: (a: A) => B, fallback: A) => (sa: TraversalWithPredicate) => ((s: S) => S) + = (f, fallback) => (sa) => sa.modifyWithFallback(Apply.identity)(f, fallback) + +export const set + : (a: A) => (sa: Traversal) => (s: S) => S + = fn.flow(fn.const, modify) + +export function foldMap(M: Monoid): (f: (a: A) => M) => (sa: Traversal) => (s: S) => M { + return (f) => (sa) => sa.modify(Applicative.free(M))(fn.flow(f, Const)) +} + +export function reduce(Sg: Semigroup): (f: (a: A) => M, fallback: M) => (sa: TraversalWithPredicate) => (s: S) => M { + return (f, fallback) => (sa) => sa.modifyWithFallback(Apply.free(Sg))(fn.flow(f, Const), fallback) +} + +export function getAll(s: S): (sa: Traversal) => readonly A[] { + return (sa: Traversal) => foldMap(Monoid.array())(Applicative.array.of)(sa)(s) +} + +export function getAllWithFallback(s: S, fallback: A): (sa: TraversalWithPredicate) => readonly A[] { + return (sa: TraversalWithPredicate) => reduce(Semigroup.array())(() => [], [])(sa)(s) +} + +export function get(sa: Traversal): (s: S) => readonly A[] { + return fn.flow(getAll, (f) => f(sa)) +} + +export function getWithFallback(sa: TraversalWithPredicate): (s: S, fallback: A) => readonly A[] { + return fn.flow(getAllWithFallback, (f) => f(sa)) +} diff --git a/packages/schema-zod-adapter/src/typename-v4.ts b/packages/schema-zod-adapter/src/typename-v4.ts index 93316775..a85c981b 100644 --- a/packages/schema-zod-adapter/src/typename-v4.ts +++ b/packages/schema-zod-adapter/src/typename-v4.ts @@ -1,9 +1,9 @@ -import { z } from 'zod4' +import { z } from 'zod/v4' import { has, Object_keys } from '@traversable/registry' import type { Z } from './functor-v4.js' -export type Typed = { _zod: { def: { type: T } } } +export type HasTypeName = { _zod: { def: { type: T } } } export { typeOf as typeof } /** @@ -12,129 +12,93 @@ export { typeOf as typeof } * Extract the type (previously called 'typeName') from a zod@4 schema. */ const typeOf - : (hasType: Typed) => T - = (x) => x._zod.def.type + : (hasType: HasTypeName) => T + = (x) => x._zod.def.type +const hasTypeName = (typeName: AnyTypeName) => has('_zod', 'def', 'type', (type): type is never => type === typeName) -export type AnyTag = Exclude -export type Tag = { [K in AnyTag]: K } -export const Tag = { - any: 'any', - array: 'array', - bigint: 'bigint', - boolean: 'boolean', - catch: 'catch', - custom: 'custom', - date: 'date', - default: 'default', - enum: 'enum', - file: 'file', - int: 'int', - intersection: 'intersection', - lazy: 'lazy', - literal: 'literal', - map: 'map', - nan: 'nan', - never: 'never', - nonoptional: 'nonoptional', - null: 'null', - nullable: 'nullable', - number: 'number', - object: 'object', - optional: 'optional', - pipe: 'pipe', - promise: 'promise', - readonly: 'readonly', - record: 'record', - set: 'set', - string: 'string', - success: 'success', - symbol: 'symbol', - template_literal: 'template_literal', - transform: 'transform', - tuple: 'tuple', - undefined: 'undefined', - union: 'union', - unknown: 'unknown', - void: 'void', -} satisfies Tag - -const hasTag = (tag: keyof Tag) => has('_zod', 'def', 'type', (x): x is never => x === tag) +export type AnyTypeName = Exclude +export type TypeName = { [K in AnyTypeName]: K } +export const TypeName = { + any: 'any', + array: 'array', + bigint: 'bigint', + boolean: 'boolean', + catch: 'catch', + custom: 'custom', + date: 'date', + default: 'default', + enum: 'enum', + file: 'file', + int: 'int', + intersection: 'intersection', + lazy: 'lazy', + literal: 'literal', + map: 'map', + nan: 'nan', + never: 'never', + nonoptional: 'nonoptional', + null: 'null', + nullable: 'nullable', + number: 'number', + object: 'object', + optional: 'optional', + pipe: 'pipe', + prefault: 'prefault', + promise: 'promise', + readonly: 'readonly', + record: 'record', + set: 'set', + string: 'string', + success: 'success', + symbol: 'symbol', + template_literal: 'template_literal', + transform: 'transform', + tuple: 'tuple', + undefined: 'undefined', + union: 'union', + unknown: 'unknown', + void: 'void', +} satisfies TypeName export const tagged: { - (tag: K): (x: unknown) => x is Z.lookup - (tag: K, x: unknown): x is Z.zodLookup -} - = ((tag: keyof Tag, x?: unknown): x is never => x === undefined ? hasTag(tag) as never : hasTag(tag)(x)) - -export type TypeName = z.ZodType['_zod']['def']['type'] -export const TypeName = { - /////////////////////////// - /// nullary /// - any: 'any', - bigint: 'bigint', - boolean: 'boolean', - date: 'date', - file: 'file', - int: 'int', - nan: 'nan', - never: 'never', - null: 'null', - number: 'number', - string: 'string', - symbol: 'symbol', - undefined: 'undefined', - unknown: 'unknown', - void: 'void', - /// nullary /// - /////////////////////////// - /// values /// - enum: 'enum', - literal: 'literal', - template_literal: 'template_literal', - /// values /// - /////////////////////////// - /// unary /// - array: 'array', - nonoptional: 'nonoptional', - nullable: 'nullable', - optional: 'optional', - readonly: 'readonly', - set: 'set', - success: 'success', - /// unary /// - /////////////////////////// - /// applicative /// - object: 'object', - tuple: 'tuple', - union: 'union', - /// applicative /// - /////////////////////////// - /// binary-like /// - catch: 'catch', - default: 'default', - /// binary-like /// - /////////////////////////// - /// binary /// - intersection: 'intersection', - map: 'map', - pipe: 'pipe', - record: 'record', - /// binary /// - /////////////////////////// - /// special cases /// - custom: 'custom', - lazy: 'lazy', - transform: 'transform', - /// special cases /// - /////////////////////////// - /// deprecated /// - /** @deprecated */ - interface: 'interface', - /** @deprecated */ - promise: 'promise', - /// deprecated /// - /////////////////////////// -} as const satisfies { [K in TypeName]: K } + (tag: K): (x: unknown) => x is Z.Lookup + (tag: K, x: unknown): x is Z.ZodLookup +} = ((tag: AnyTypeName, x?: unknown) => x === undefined ? hasTypeName(tag) : hasTypeName(tag)(x)) export const TypeNames = Object_keys(TypeName) + +export const NS = '@traversable/schema-zod-adapter::' as const + +export type Sym = typeof Sym +export const Sym = { + array: Symbol.for(`${NS}array`), + catch: Symbol.for(`${NS}catch`), + custom: Symbol.for(`${NS}custom`), + default: Symbol.for(`${NS}default`), + intersectionLeft: Symbol.for(`${NS}intersectionLeft`), + intersectionRight: Symbol.for(`${NS}intersectionRight`), + lazy: Symbol.for(`${NS}lazy`), + literal: Symbol.for(`${NS}literal`), + // map: Symbol.for(`${NS}map`), + mapKey: Symbol.for(`${NS}mapKey`), + mapValue: Symbol.for(`${NS}mapValue`), + nonoptional: Symbol.for(`${NS}nonoptional`), + null: Symbol.for(`${NS}null`), + nullable: Symbol.for(`${NS}nullable`), + object: Symbol.for(`${NS}object`), + optional: Symbol.for(`${NS}optional`), + pipe: Symbol.for(`${NS}pipe`), + prefault: Symbol.for(`${NS}prefault`), + promise: Symbol.for(`${NS}promise`), + readonly: Symbol.for(`${NS}readonly`), + // record: Symbol.for(`${NS}record`), + recordKey: Symbol.for(`${NS}recordKey`), + recordValue: Symbol.for(`${NS}recordValue`), + set: Symbol.for(`${NS}set`), + success: Symbol.for(`${NS}success`), + transform: Symbol.for(`${NS}transform`), + tuple: Symbol.for(`${NS}tuple`), + union: Symbol.for(`${NS}union`), +} + diff --git a/packages/schema-zod-adapter/src/utils-v4.ts b/packages/schema-zod-adapter/src/utils-v4.ts index 899d7a6d..94442d1a 100644 --- a/packages/schema-zod-adapter/src/utils-v4.ts +++ b/packages/schema-zod-adapter/src/utils-v4.ts @@ -1,4 +1,4 @@ -import { z } from 'zod4' +import { z } from 'zod/v4' import { RAISE_ISSUE_URL, VERSION, ZOD_CHANGELOG } from './version-v4.js' import { symbol } from '@traversable/registry' diff --git a/packages/schema-zod-adapter/src/v4.ts b/packages/schema-zod-adapter/src/v4.ts index aab8b0cc..53b70993 100644 --- a/packages/schema-zod-adapter/src/v4.ts +++ b/packages/schema-zod-adapter/src/v4.ts @@ -1,3 +1,25 @@ +////////////////////////////////////////////////////////// +/// TODO: move to '@traversable/registry' (start) /// +import type { Box } from '@traversable/registry' +export { + Applicative, + Apply, + Const, + Foldable, + Monoid, + Pointed, + Semigroup, + Traversable, +} from './instances-v4.js' + +export type { TraversalWithPredicate } from './optics-v4.js' +export { Iso, Lens, Prism, Optional, Traversal } from './optics-v4.js' +/// TODO: move to '@traversable/registry' (end) /// +///////////////////////////////////////////////////////// + +export * as Pro from './profunctor-optics-v4.js' +export { makeLens } from './make-lenses-v4.js' + export { RAISE_ISSUE_URL, VERSION, @@ -5,6 +27,15 @@ export { ZOD_VERSION, } from './version-v4.js' +export type { Optic } from './proxy-v4.js' +export { + buildDescriptors, + buildIntermediateRepresentation, + buildLenses, +} from './proxy-v4.js' + +export { withDefault } from './with-default-v4.js' + export type { Seed } from './seed-v4.js' export type { Z, Options as FunctorOptions } from './functor-v4.js' export { @@ -13,11 +44,14 @@ export { fold, functorDefaultOptions, } from './functor-v4.js' -export { Tag, typeof } from './typename-v4.js' +export { tagged, typeof } from './typename-v4.js' export { deepNullable } from './deep-nullable-v4.js' export { deepPartial } from './deep-partial-v4.js' export { deepReadonly } from './deep-readonly-v4.js' export { toString } from './toString-v4.js' +// export { makeLenses } from './get-v4.js' + +export { paths } from './paths-v4.js' export { toSchema as jsonToSchema, toExactSchema as jsonToExactSchema, @@ -39,4 +73,5 @@ export { seedToValidDataGenerator, seedToInvalidData, seedToInvalidDataGenerator, + SeedMap, } from './generator-v4.js' diff --git a/packages/schema-zod-adapter/src/version-v4.ts b/packages/schema-zod-adapter/src/version-v4.ts index eb3f773a..2b85f491 100644 --- a/packages/schema-zod-adapter/src/version-v4.ts +++ b/packages/schema-zod-adapter/src/version-v4.ts @@ -1,5 +1,5 @@ import pkgJson from './__generated__/__manifest__.js' -import zodPkgJson from 'zod4/package.json' with { type: 'json' } +import zodPkgJson from 'zod/package.json' with { type: 'json' } export const VERSION = `${pkgJson.name}@${pkgJson.version}` as const export type VERSION = typeof VERSION diff --git a/packages/schema-zod-adapter/src/with-default-v4.ts b/packages/schema-zod-adapter/src/with-default-v4.ts new file mode 100644 index 00000000..7bfdd19e --- /dev/null +++ b/packages/schema-zod-adapter/src/with-default-v4.ts @@ -0,0 +1,111 @@ +import { z } from 'zod/v4' +import type { Primitive } from '@traversable/registry' +import { fn, Object_assign, Object_fromEntries, Object_keys } from '@traversable/registry' + +import * as F from './functor-v4.js' +import { tagged } from './typename-v4.js' +import { Invariant } from './utils-v4.js' + +export type Fixpoint = + | undefined + | readonly Fixpoint[] + | { [x: string]: Fixpoint } + +export type Hole = + | undefined + | readonly T[] + | { [x: string]: T } + +export type Atom = + | globalThis.Date + | globalThis.RegExp + +export type Fallbacks = { [K in F.Z.Nullary['_zod']['def']['type']]+?: unknown } + +export type withDefault + = T extends Primitive | Atom ? T | Fallback + : T extends Set ? Set['return'] & {})>['value'] & {}, Fallback>> + : T extends Map ? Map< + withDefault<({} & ReturnType<{} & ReturnType['return']>['value'])[0], Fallback>, + withDefault<({} & ReturnType<{} & ReturnType['return']>['value'])[1], Fallback> + > + : { [K in keyof T]-?: withDefault } + +export function withDefault(type: T): withDefault> +export function withDefault(type: T, fallbacks: Leaves): withDefault, Leaves[keyof Leaves]> +export function withDefault>(type: T, fallbacks: Fallbacks = withDefault.defaults) { + return F.fold((x) => { + switch (true) { + default: return fn.exhaustive(x) + case tagged('enum')(x): return x._zod.def.entries ?? CATCH_ALL + case F.isNullary(x): return fallbacks[x._zod.def.type] ?? CATCH_ALL + case tagged('nonoptional')(x): return x._zod.def.innerType ?? CATCH_ALL + case tagged('nullable')(x): return x._zod.def.innerType ?? CATCH_ALL + case tagged('optional')(x): return x._zod.def.innerType ?? CATCH_ALL + case tagged('success')(x): return x._zod.def.innerType ?? CATCH_ALL + case tagged('readonly')(x): return x._zod.def.innerType ?? CATCH_ALL + case tagged('array')(x): return IS(x._zod.def.element) ? [x._zod.def.element] : Array.of() + case tagged('set')(x): return new Set(IS(x._zod.def.valueType) ? [x._zod.def.valueType] : []) + case tagged('map')(x): return new Map(ARE(x._zod.def.keyType, x._zod.def.valueType) ? [[x._zod.def.keyType, x._zod.def.valueType]] : []) + case tagged('catch')(x): return x._zod.def.innerType ?? x._zod.def.catchValue ?? CATCH_ALL + case tagged('default')(x): return x._zod.def.innerType ?? x._zod.def.defaultValue ?? CATCH_ALL + case tagged('prefault')(x): return x._zod.def.innerType ?? x._zod.def.defaultValue ?? CATCH_ALL + case tagged('union')(x): return x._zod.def.options.find(IS) ?? CATCH_ALL + case tagged('object')(x): return x._zod.def.shape + case tagged('tuple')(x): return [...x._zod.def.items, ...IS(x._zod.def.rest) ? [x._zod.def.rest] : []] + case tagged('custom')(x): return x._zod.def ?? CATCH_ALL + case tagged('lazy')(x): return x._zod.def.getter() ?? CATCH_ALL + case tagged('pipe')(x): return x._zod.def.out ?? x._zod.def.in + case tagged('transform')(x): return x._zod.def.transform(CATCH_ALL) + case tagged('intersection')(x): return Object_assign(x._zod.def.left ?? {}, x._zod.def.right ?? {}) + case tagged('record')(x): { + const keyType = x._zod.def.keyType + switch (true) { + default: return {} + case !!keyType && typeof keyType === 'object': { + return fn.pipe( + keyType, + Object_keys, + (keys) => fn.map(keys, (k) => [k, x._zod.def.valueType ?? CATCH_ALL]), + Object_fromEntries, + ) + } + } + } + /** @deprecated */ + case tagged('promise')(x): return Invariant.Unimplemented('promise', 'v4.withDeafult') + } + })(type) +} + +const IS = (x: unknown) => x != null +const ARE = (x: unknown, y: unknown) => x != null && y != null +const CATCH_ALL = undefined + +withDefault.defaults = { + any: undefined, + bigint: undefined, + boolean: undefined, + date: undefined, + enum: undefined, + file: undefined, + literal: undefined, + nan: undefined, + never: undefined, + null: undefined, + number: undefined, + string: undefined, + symbol: undefined, + template_literal: undefined, + undefined: undefined, + unknown: undefined, + void: undefined, +} satisfies Required + +export declare namespace withDefault { + export { + Fallbacks, + Fixpoint, + Hole, + } +} diff --git a/packages/schema-zod-adapter/test/deep-nullable-v4.test.ts b/packages/schema-zod-adapter/test/deep-nullable-v4.test.ts index 96e28907..ae745f8d 100644 --- a/packages/schema-zod-adapter/test/deep-nullable-v4.test.ts +++ b/packages/schema-zod-adapter/test/deep-nullable-v4.test.ts @@ -1,5 +1,5 @@ import * as vi from 'vitest' -import { z } from 'zod4' +import { z } from 'zod/v4' import { v4 } from '@traversable/schema-zod-adapter' vi.describe('〖️⛳️〗‹‹‹ ❲@traversable/schema-zod-adapter❳', () => { diff --git a/packages/schema-zod-adapter/test/deep-partial-v4.test.ts b/packages/schema-zod-adapter/test/deep-partial-v4.test.ts index 04fca7cb..7b1cb1e1 100644 --- a/packages/schema-zod-adapter/test/deep-partial-v4.test.ts +++ b/packages/schema-zod-adapter/test/deep-partial-v4.test.ts @@ -1,5 +1,5 @@ import * as vi from 'vitest' -import { z } from 'zod4' +import { z } from 'zod/v4' import { v4 } from '@traversable/schema-zod-adapter' vi.describe('〖️⛳️〗‹‹‹ ❲@traversable/schema-zod-adapter❳', () => { diff --git a/packages/schema-zod-adapter/test/deep-readonly-v4.test.ts b/packages/schema-zod-adapter/test/deep-readonly-v4.test.ts index 5ed0c4bc..a1e84b05 100644 --- a/packages/schema-zod-adapter/test/deep-readonly-v4.test.ts +++ b/packages/schema-zod-adapter/test/deep-readonly-v4.test.ts @@ -1,5 +1,5 @@ import * as vi from 'vitest' -import { z } from 'zod4' +import { z } from 'zod/v4' import { v4 } from '@traversable/schema-zod-adapter' vi.describe('〖️⛳️〗‹‹‹ ❲@traversable/schema-zod-adapter❳', () => { diff --git a/packages/schema-zod-adapter/test/functor-v3.test.ts b/packages/schema-zod-adapter/test/functor-v3.test.ts index 5f0ddeb1..262afebf 100644 --- a/packages/schema-zod-adapter/test/functor-v3.test.ts +++ b/packages/schema-zod-adapter/test/functor-v3.test.ts @@ -1,5 +1,5 @@ import * as vi from "vitest" -import { z } from "zod3" +import { z } from "zod/v3" import { v3 } from "@traversable/schema-zod-adapter" diff --git a/packages/schema-zod-adapter/test/generator-v4.test.ts b/packages/schema-zod-adapter/test/generator-v4.test.ts index 5763355c..2d9f7abb 100644 --- a/packages/schema-zod-adapter/test/generator-v4.test.ts +++ b/packages/schema-zod-adapter/test/generator-v4.test.ts @@ -1,6 +1,6 @@ import * as vi from 'vitest' import * as fc from 'fast-check' -import type { z } from 'zod4' +import type { z } from 'zod/v4' import { v4 } from '@traversable/schema-zod-adapter' const peek = (arbitrary: fc.Arbitrary) => fc.sample(arbitrary, 1)[0] diff --git a/packages/schema-zod-adapter/test/get-v4.test.ts b/packages/schema-zod-adapter/test/get-v4.test.ts new file mode 100644 index 00000000..8fa9628b --- /dev/null +++ b/packages/schema-zod-adapter/test/get-v4.test.ts @@ -0,0 +1,136 @@ +import * as vi from 'vitest' +import { v4 } from '@traversable/schema-zod-adapter' +import { z } from 'zod/v4' + +vi.describe('〖️⛳️〗‹‹‹ ❲@traversable/schema-zod-adapter❳', () => { + vi.it('〖️⛳️〗› ❲v4.get❳', () => { + vi.expect(v4.paths( + z.object({ + abc: z.boolean(), + def: z.string(), + })) + ).toMatchInlineSnapshot + (` + [ + [ + ".abc", + ], + [ + ".def", + ], + ] + `) + + /** + * @example + * + * 0 >>> + * ↆ + * 'abc' + * ↆ + * ⵁ + * + * ↆ + * 'def' + * ↆ + * '?' + * ↆ + * '?' + * ⵁ + * + * 0 + * ↆ + * 'ghi' + * ↆ + * '[number]' + * ↆ + * '[number]' + * ↆ + * ⵁ + * + * + * '?' | '[number]' + */ + + vi.expect(v4.paths( + z.tuple([ + z.object({ + abc: z.literal(1), + def: z.optional(z.literal(2)), + ghi: z.array(z.array(z.literal(3))), + }), + z.object({ + jkl: z.literal(4), + mno: z.intersection(z.boolean(), z.number()), + pqr: z.record(z.enum(['X', 'Y', 'Z']), z.unknown()) + }) + ]), { + interpreter: { + string: (k, ix) => `${ix === 0 ? '' : '.'}${k}`, + number: (k, ix, out) => `[${k}]`, + optional: () => `?`, + recordKey: (k) => `[string]`, + recordValue: (k) => `{}`, + array: () => `[]`, + union: () => `|`, + intersectionLeft: () => [`&`, 0], + intersectionRight: () => [`&`, 1], + tuple: () => null, + object: () => null, + } + }) + ).toMatchInlineSnapshot + (` + [ + [ + "[0]", + ".abc", + ], + [ + "[0]", + ".def", + "?", + "?", + ], + [ + "[0]", + ".ghi", + "[]", + "[]", + ], + [ + "[1]", + ".jkl", + ], + [ + "[1]", + ".mno", + [ + "&", + 0, + ], + ], + [ + "[1]", + ".mno", + [ + "&", + 1, + ], + ], + [ + "[1]", + ".pqr", + "[string]", + ], + [ + "[1]", + ".pqr", + "{}", + ], + ] + `) + + + }) +}) \ No newline at end of file diff --git a/packages/schema-zod-adapter/test/lenses-v4.test.ts b/packages/schema-zod-adapter/test/lenses-v4.test.ts new file mode 100644 index 00000000..00692327 --- /dev/null +++ b/packages/schema-zod-adapter/test/lenses-v4.test.ts @@ -0,0 +1,167 @@ +import * as vi from 'vitest' +import { v4 } from '@traversable/schema-zod-adapter' +import { fn } from '@traversable/registry' +import { z } from 'zod/v4' + + +vi.describe('〖⛳️〗‹‹‹ ❲@traversable/schema-zod-adapter❳', () => { + vi.it('〖⛳️〗› ❲v4.Lens❳', () => { + const lens_01 = fn.pipe( + v4.Lens.declare<{ x: number, y: number }>(), + v4.Lens.prop('x'), + ) + + vi.assert.equal(lens_01.get({ x: 1, y: 2 }), 1) + vi.assert.deepEqual(lens_01.set(3)({ x: 1, y: 2 }), { x: 3, y: 2 }) + }) +}) + +// declare let input_01: Parameters[0] +// declare let input_02: Parameters[0] +// declare let input_03: Parameters[0] +// declare let input_04: Parameters[0] + +// let schema_01 = z.object({ +// a: z.literal(0), +// b: z.object({ +// c: z.union([ +// z.optional( +// z.array( +// z.optional( +// z.object({ +// d: z.literal(1), +// e: z.literal(2), +// f: z.literal(3), +// g: z.object({ +// i: z.literal(4), +// j: z.object({ +// k: z.tuple([ +// z.tuple([ +// z.tuple([ +// z.tuple([ +// z.tuple([ +// z.tuple([ +// z.tuple([ +// z.literal(9000), +// ]) +// ]) +// ]) +// ]) +// ]) +// ]) +// ]) +// }), +// }) +// }) +// ) +// ) +// ), +// z.int32(), +// ]), +// i: z.literal(5), +// j: z.literal(6), +// }), +// k: z.literal(7), +// l: z.literal(8), +// }) + +// let ex_01 = v4.makeLenses(schema_01, 'b', 'c', '|0|', '?', '[number]', '?', 'g', 'j', 'k', '0', '0', '0', '0', '0', '0', '0') +// let _01 = ex_01.get(input_01) +// // ^? + + +// let schema_02 = z.record(z.enum(['A', 'B']), z.tuple([z.literal(1), z.optional(z.literal(2))])) +// let ex_02 = v4.makeLenses(schema_02, 'A', '1', '?') +// let _02 = ex_02.get(input_02) +// // ^? + +// let schema_03 = z.record(z.enum(['a', 'b', 'c']), z.tuple([z.literal(1), z.optional(z.literal(2))])) +// let ex_03 = v4.makeLenses(schema_03, 'a') +// let _03 = ex_03.get(input_03) +// // ^?Queue + +// // let schema_04 = z.intersection(z.object({ c: z.optional(z.literal(2)), d: z.literal(3) }), z.object({ e: z.literal(4), f: z.optional(z.literal(5)) })) +// // let ex_04 = v4.makeLenses(schema_04, '0') +// // let _04 = ex_04.get(input_04) +// // // ^? + +// let schema_05 = z.tuple([z.record(z.string(), z.number()), z.optional(z.string())]) +// let ex_05 = v4.makeLenses(schema_05, '0', '[string]') + +// vi.describe('〖️⛳️〗‹‹‹ ❲@traversable/schema-zod-adapter❳', () => { +// vi.it('〖️⛳️〗› ❲v4.get❳', () => { +// }) +// }) + +// /** +// * { +// * a: 0, +// * b: { +// * c: 9000, +// * i: 5, +// * j: 6, +// * }, +// * k: 7, +// * l: 8, +// * } +// * +// * { +// * a: 0, +// * b: { +// * c: undefined +// * i: 5, +// * j: 6, +// * }, +// * k: 7, +// * l: 8, +// * } +// * } +// * +// * { +// * a: 0, +// * b: { +// * c: [] +// * i: 5, +// * j: 6, +// * }, +// * k: 7, +// * l: 8, +// * } +// * } +// * +// * { +// * a: 0, +// * b: { +// * c: [undefined] // is this a real case? I think so, with a zod schema +// * i: 5, +// * j: 6, +// * }, +// * k: 7, +// * l: 8, +// * } +// * } +// * +// * { +// * a: 0, +// * b: { +// * c: [ +// * { +// * d: 1, +// * e: 2, +// * f: 3, +// * g: { +// * i: 4, +// * j: { +// * k: [[[9000]]] +// * } +// * } +// * } +// * ] // is this a real case? I think so, with a zod schema +// * i: 5, +// * j: 6, +// * }, +// * k: 7, +// * l: 8, +// * } +// * } +// */ diff --git a/packages/schema-zod-adapter/test/make-lenses-v4.test.ts b/packages/schema-zod-adapter/test/make-lenses-v4.test.ts new file mode 100644 index 00000000..08f48934 --- /dev/null +++ b/packages/schema-zod-adapter/test/make-lenses-v4.test.ts @@ -0,0 +1,6181 @@ +import * as vi from 'vitest' +import { v4, registerDSL } from '@traversable/schema-zod-adapter' +import { z } from 'zod/v4' + +vi.describe('〖⛳️〗‹‹‹ ❲@traversable/schema-zod-adapter❳', () => { + vi.it('〖⛳️〗› ❲v4.makeLens❳: types', () => { + v4.makeLens( + z.object({}), + (proxy) => proxy + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf>() + // ^? + }, + {} + ) + + v4.makeLens( + z.object({ ABC: z.optional(z.object({ DEF: z.optional(z.object({ GHI: z.optional(z.literal(1)) })) })) }), + (proxy) => proxy + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ ABC?: { DEF?: { GHI?: 1 } } }>() + // ^? + }, + {} + ) + + v4.makeLens( + z.object({ ABC: z.optional(z.object({ DEF: z.optional(z.object({ GHI: z.optional(z.literal(1)) })) })) }), + (proxy) => proxy.ABC + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ DEF?: { GHI?: 1 } } | undefined>() + // ^? + }, + {} + ) + + v4.makeLens( + z.object({ ABC: z.optional(z.object({ DEF: z.optional(z.object({ GHI: z.optional(z.literal(1)) })) })) }), + (proxy) => proxy.ABC.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ DEF?: { GHI?: 1 } }>() + // ^? + return focus + }, + {} + ) + + v4.makeLens( + z.object({ ABC: z.optional(z.object({ DEF: z.optional(z.object({ GHI: z.optional(z.literal(1)) })) })) }), + (proxy) => proxy.ABC.ʔ.DEF + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ GHI?: 1 } | undefined>() + // ^? + return focus + }, + {} + ) + + v4.makeLens( + z.object({ ABC: z.optional(z.object({ DEF: z.optional(z.object({ GHI: z.optional(z.literal(1)) })) })) }), + (proxy) => proxy.ABC.ʔ.DEF.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ GHI?: 1 }>() + // ^? + return focus + }, + {} + ) + + v4.makeLens( + z.object({ ABC: z.optional(z.object({ DEF: z.optional(z.object({ GHI: z.optional(z.literal(1)) })) })) }), + (proxy) => proxy.ABC.ʔ.DEF.ʔ.GHI + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<1 | undefined>() + // ^? + return focus + }, + {} + ) + + v4.makeLens( + z.object({ ABC: z.optional(z.object({ DEF: z.optional(z.object({ GHI: z.optional(z.literal(1)) })) })) }), + (proxy) => proxy.ABC.ʔ.DEF.ʔ.GHI.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<1>() + // ^? + return focus + }, + {} + ) + + v4.makeLens( + z.tuple([]), + (proxy) => proxy + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<[]>() + // ^? + }, + [] + ) + + v4.makeLens( + z.tuple([z.literal(1), z.tuple([z.literal(2), z.tuple([z.literal(3), z.tuple([z.literal(4)])])])]), + (proxy) => proxy + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<[1, [2, [3, [4]]]]>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([z.literal(1), z.tuple([z.literal(2), z.tuple([z.literal(3), z.tuple([z.literal(4)])])])]), + (proxy) => proxy[0] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<1>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([z.literal(1), z.tuple([z.literal(2), z.tuple([z.literal(3), z.tuple([z.literal(4)])])])]), + (proxy) => proxy[1] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<[2, [3, [4]]]>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([z.literal(1), z.tuple([z.literal(2), z.tuple([z.literal(3), z.tuple([z.literal(4)])])])]), + (proxy) => proxy[1][0] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<2>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([z.literal(1), z.tuple([z.literal(2), z.tuple([z.literal(3), z.tuple([z.literal(4)])])])]), + (proxy) => proxy[1][1] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<[3, [4]]>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([z.literal(1), z.tuple([z.literal(2), z.tuple([z.literal(3), z.tuple([z.literal(4)])])])]), + (proxy) => proxy[1][1][0] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<3>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([z.literal(1), z.tuple([z.literal(2), z.tuple([z.literal(3), z.tuple([z.literal(4)])])])]), + (proxy) => proxy[1][1][1] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<[4]>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([z.literal(1), z.tuple([z.literal(2), z.tuple([z.literal(3), z.tuple([z.literal(4)])])])]), + (proxy) => proxy[1][1][1][0] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<4>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional(z.tuple([z.optional(z.literal(4))])) + ]) + ) + ]) + )] + ), + (proxy) => proxy[0] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<1 | undefined>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional(z.tuple([z.optional(z.literal(4))])) + ]) + ) + ]) + )] + ), + (proxy) => proxy[1] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<[_?: 2, _?: [_?: 3, _?: [_?: 4]]] | undefined>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional(z.tuple([z.optional(z.literal(4))])) + ]) + ) + ]) + )] + ), + (proxy) => proxy[1].ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<[_?: 2, _?: [_?: 3, _?: [_?: 4]]]>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional(z.tuple([z.optional(z.literal(4))])) + ]) + ) + ]) + )] + ), + (proxy) => proxy[1].ʔ[0] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<2 | undefined>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional(z.tuple([z.optional(z.literal(4))])) + ]) + ) + ]) + )] + ), + (proxy) => proxy[1].ʔ[0].ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<2>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional(z.tuple([z.optional(z.literal(4))])) + ]) + ) + ]) + )] + ), + (proxy) => proxy[1].ʔ[1] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<[_?: 3, _?: [_?: 4]] | undefined>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional(z.tuple([z.optional(z.literal(4))])) + ]) + ) + ]) + )] + ), + (proxy) => proxy[1].ʔ[1].ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<[_?: 3, _?: [_?: 4]]>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional(z.tuple([z.optional(z.literal(4))])) + ]) + ) + ]) + )] + ), + (proxy) => proxy[1].ʔ[1].ʔ[0] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<3 | undefined>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional(z.tuple([z.optional(z.literal(4))])) + ]) + ) + ]) + )] + ), + (proxy) => proxy[1].ʔ[1].ʔ[0].ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<3>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional(z.tuple([z.optional(z.literal(4))])) + ]) + ) + ]) + )] + ), + (proxy) => proxy[1].ʔ[1].ʔ[1] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<[_?: 4] | undefined>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional(z.tuple([z.optional(z.literal(4))])) + ]) + ) + ]) + )] + ), + (proxy) => proxy[1].ʔ[1].ʔ[1].ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<[_?: 4]>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional(z.tuple([z.optional(z.literal(4))])) + ]) + ) + ]) + )] + ), + (proxy) => proxy[1].ʔ[1].ʔ[1].ʔ[0] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<4 | undefined>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional(z.tuple([z.optional(z.literal(4))])) + ]) + ) + ]) + )] + ), + (proxy) => proxy[1].ʔ[1].ʔ[1].ʔ[0].ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<4>() + // ^? + }, + [1, [2, [3, [4]]]] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.object({ + ABC: z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + }) + ) + ]) + ) + }) + ) + ]) + ) + }) + ) + ]), + (proxy) => proxy + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + // ^? + < + [ + _?: 1, + _?: { + ABC?: [ + _?: 2, + _?: { + DEF?: [ + _?: 3, + _?: { + GHI?: [_?: 4] + } + ] + } + ] + } + ] + >() + }, + [1, { ABC: [2, { DEF: [3, { GHI: [4] }] }] }] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.object({ + ABC: z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + }) + ) + ]) + ) + }) + ) + ]) + ) + }) + ) + ]), + (proxy) => proxy[1] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + // ^? + < + undefined | { + ABC?: [ + _?: 2, + _?: { + DEF?: [ + _?: 3, + _?: { + GHI?: [ + _?: 4 + ] + } + ] + } + ] + } + >() + }, + [1, { ABC: [2, { DEF: [3, { GHI: [4] }] }] }] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.object({ + ABC: z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + }) + ) + ]) + ) + }) + ) + ]) + ) + }) + ) + ]), + (proxy) => proxy[1].ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + // ^? + < + { + ABC?: [ + _?: 2, + _?: { + DEF?: [ + _?: 3, + _?: { + GHI?: [ + _?: 4 + ] + } + ] + } + ] + } + >() + }, + [1, { ABC: [2, { DEF: [3, { GHI: [4] }] }] }] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.object({ + ABC: z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + }) + ) + ]) + ) + }) + ) + ]) + ) + }) + ) + ]), + (proxy) => proxy[1].ʔ.ABC + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + // ^? + < + | undefined + | [ + _?: 2, + _?: { + DEF?: [ + _?: 3, + _?: { + GHI?: [ + _?: 4 + ] + } + ] + } + ] + >() + }, + [1, { ABC: [2, { DEF: [3, { GHI: [4] }] }] }] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.object({ + ABC: z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + }) + ) + ]) + ) + }) + ) + ]) + ) + }) + ) + ]), + (proxy) => proxy[1].ʔ.ABC.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + // ^? + < + [ + _?: 2, + _?: { + DEF?: [ + _?: 3, + _?: { + GHI?: [ + _?: 4 + ] + } + ] + } + ] + >() + }, + [1, { ABC: [2, { DEF: [3, { GHI: [4] }] }] }] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.object({ + ABC: z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + }) + ) + ]) + ) + }) + ) + ]) + ) + }) + ) + ]), + (proxy) => proxy[1].ʔ.ABC.ʔ[0] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<2 | undefined>() + // ^? + }, + [1, { ABC: [2, { DEF: [3, { GHI: [4] }] }] }] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.object({ + ABC: z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + }) + ) + ]) + ) + }) + ) + ]) + ) + }) + ) + ]), + (proxy) => proxy[1].ʔ.ABC.ʔ[1] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + // ^? + < + | undefined + | { + DEF?: [ + _?: 3, + _?: { + GHI?: [ + _?: 4 + ] + } + ] + } + >() + }, + [1, { ABC: [2, { DEF: [3, { GHI: [4] }] }] }] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.object({ + ABC: z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + }) + ) + ]) + ) + }) + ) + ]) + ) + }) + ) + ]), + (proxy) => proxy[1].ʔ.ABC.ʔ[1].ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + // ^? + < + { + DEF?: [ + _?: 3, + _?: { + GHI?: [ + _?: 4 + ] + } + ] + } + >() + }, + [1, { ABC: [2, { DEF: [3, { GHI: [4] }] }] }] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.object({ + ABC: z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + }) + ) + ]) + ) + }) + ) + ]) + ) + }) + ) + ]), + (proxy) => proxy[1].ʔ.ABC.ʔ[1].ʔ.DEF + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + // ^? + < + | undefined + | [ + _?: 3, + _?: { + GHI?: [ + _?: 4 + ] + } + ] + >() + }, + [1, { ABC: [2, { DEF: [3, { GHI: [4] }] }] }] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.object({ + ABC: z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + }) + ) + ]) + ) + }) + ) + ]) + ) + }) + ) + ]), + (proxy) => proxy[1].ʔ.ABC.ʔ[1].ʔ.DEF.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + // ^? + < + [ + _?: 3, + _?: { + GHI?: [ + _?: 4 + ] + } + ] + >() + }, + [1, { ABC: [2, { DEF: [3, { GHI: [4] }] }] }] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.object({ + ABC: z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + }) + ) + ]) + ) + }) + ) + ]) + ) + }) + ) + ]), + (proxy) => proxy[1].ʔ.ABC.ʔ[1].ʔ.DEF.ʔ[1] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + // ^? + < + | undefined + | { + GHI?: [ + _?: 4 + ] + } + >() + }, + [1, { ABC: [2, { DEF: [3, { GHI: [4] }] }] }] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.object({ + ABC: z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + }) + ) + ]) + ) + }) + ) + ]) + ) + }) + ) + ]), + (proxy) => proxy[1].ʔ.ABC.ʔ[1].ʔ.DEF.ʔ[1].ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ GHI?: [_?: 4] }>() + }, + [1, { ABC: [2, { DEF: [3, { GHI: [4] }] }] }] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.object({ + ABC: z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + }) + ) + ]) + ) + }) + ) + ]) + ) + }) + ) + ]), + (proxy) => proxy[1].ʔ.ABC.ʔ[1].ʔ.DEF.ʔ[1].ʔ.GHI + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + }, + [1, { ABC: [2, { DEF: [3, { GHI: [4] }] }] }] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.object({ + ABC: z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + }) + ) + ]) + ) + }) + ) + ]) + ) + }) + ) + ]), + (proxy) => proxy[1].ʔ.ABC.ʔ[1].ʔ.DEF.ʔ[1].ʔ.GHI.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<[_?: 4]>() + }, + [1, { ABC: [2, { DEF: [3, { GHI: [4] }] }] }] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.object({ + ABC: z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + }) + ) + ]) + ) + }) + ) + ]) + ) + }) + ) + ]), + (proxy) => proxy[1].ʔ.ABC.ʔ[1].ʔ.DEF.ʔ[1].ʔ.GHI.ʔ[0] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<4 | undefined>() + }, + [1, { ABC: [2, { DEF: [3, { GHI: [4] }] }] }] + ) + + v4.makeLens( + z.tuple([ + z.optional(z.literal(1)), + z.optional( + z.object({ + ABC: z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + }) + ) + ]) + ) + }) + ) + ]) + ) + }) + ) + ]), + (proxy) => proxy[1].ʔ.ABC.ʔ[1].ʔ.DEF.ʔ[1].ʔ.GHI.ʔ[0].ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<4>() + }, + [1, { ABC: [2, { DEF: [3, { GHI: [4] }] }] }] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + globalThis.Array< + | undefined + | [ + _?: { + ABC: globalThis.Array< + | undefined + | [ + _?: 2, + _?: { + DEF: globalThis.Array< + | undefined + | [ + _?: 3, + _?: { + GHI: globalThis.Array< + | undefined + | [_?: 4] + > + } + ] + > + } + ] + > + } + ] + > + >() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + | undefined + | [ + _?: { + ABC: globalThis.Array< + | undefined + | [ + _?: 2, + _?: { + DEF: globalThis.Array< + | undefined + | [ + _?: 3, + _?: { + GHI: globalThis.Array< + | undefined + | [_?: 4] + > + } + ] + > + } + ] + > + } + ] + >() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + [ + _?: { + ABC: globalThis.Array< + | undefined + | [ + _?: 2, + _?: { + DEF: globalThis.Array< + | undefined + | [ + _?: 3, + _?: { + GHI: globalThis.Array< + | undefined + | [_?: 4] + > + } + ] + > + } + ] + > + } + ] + >() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + | undefined + | { + ABC: globalThis.Array< + | undefined + | [ + _?: 2, + _?: { + DEF: globalThis.Array< + | undefined + | [ + _?: 3, + _?: { + GHI: globalThis.Array< + | undefined + | [_?: 4] + > + } + ] + > + } + ] + > + } + >() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + ABC: globalThis.Array< + | undefined + | [ + _?: 2, + _?: { + DEF: globalThis.Array< + | undefined + | [ + _?: 3, + _?: { + GHI: globalThis.Array< + | undefined + | [_?: 4] + > + } + ] + > + } + ] + > + } + >() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ.ABC.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + | undefined + | [ + _?: 2, + _?: { + DEF: globalThis.Array< + | undefined + | [ + _?: 3, + _?: { + GHI: globalThis.Array< + | undefined + | [_?: 4] + > + } + ] + > + } + ] + >() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ.ABC.ᣔꓸꓸ.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + [ + _?: 2, + _?: { + DEF: globalThis.Array< + | undefined + | [ + _?: 3, + _?: { + GHI: globalThis.Array< + | undefined + | [_?: 4] + > + } + ] + > + } + ] + >() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ.ABC.ᣔꓸꓸ.ʔ[1] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + | undefined + | { + DEF: globalThis.Array< + | undefined + | [ + _?: 3, + _?: { + GHI: globalThis.Array< + | undefined + | [_?: 4] + > + } + ] + > + } + >() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ.ABC.ᣔꓸꓸ.ʔ[1].ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + DEF: globalThis.Array< + | undefined + | [ + _?: 3, + _?: { + GHI: globalThis.Array< + | undefined + | [_?: 4] + > + } + ] + > + } + >() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ.ABC.ᣔꓸꓸ.ʔ[1].ʔ.DEF + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + globalThis.Array< + | undefined + | [ + _?: 3, + _?: { + GHI: globalThis.Array< + | undefined + | [_?: 4] + > + } + ] + > + >() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ.ABC.ᣔꓸꓸ.ʔ[1].ʔ.DEF.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + | undefined + | [ + _?: 3, + _?: { + GHI: globalThis.Array< + | undefined + | [_?: 4] + > + } + ] + >() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ.ABC.ᣔꓸꓸ.ʔ[1].ʔ.DEF.ᣔꓸꓸ.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + [ + _?: 3, + _?: { + GHI: globalThis.Array< + | undefined + | [_?: 4] + > + } + ] + >() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ.ABC.ᣔꓸꓸ.ʔ[1].ʔ.DEF.ᣔꓸꓸ.ʔ[1] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + | undefined + | { + GHI: globalThis.Array< + | undefined + | [_?: 4] + > + } + >() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ.ABC.ᣔꓸꓸ.ʔ[1].ʔ.DEF.ᣔꓸꓸ.ʔ[1].ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + GHI: globalThis.Array< + | undefined + | [_?: 4] + > + } + >() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ.ABC.ᣔꓸꓸ.ʔ[1].ʔ.DEF.ᣔꓸꓸ.ʔ[1].ʔ.GHI + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + globalThis.Array< + | undefined + | [_?: 4] + > + >() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ.ABC.ᣔꓸꓸ.ʔ[1].ʔ.DEF.ᣔꓸꓸ.ʔ[1].ʔ.GHI.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + | undefined + | [_?: 4] + >() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ.ABC.ᣔꓸꓸ.ʔ[1].ʔ.DEF.ᣔꓸꓸ.ʔ[1].ʔ.GHI.ᣔꓸꓸ.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<[_?: 4]>() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ.ABC.ᣔꓸꓸ.ʔ[1].ʔ.DEF.ᣔꓸꓸ.ʔ[1].ʔ.GHI.ᣔꓸꓸ.ʔ[0] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<4 | undefined>() + }, + [] + ) + + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ.ABC.ᣔꓸꓸ.ʔ[1].ʔ.DEF.ᣔꓸꓸ.ʔ[1].ʔ.GHI.ᣔꓸꓸ.ʔ[0].ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<4>() + }, + [] + ) + + }) + + vi.it('〖⛳️〗› ❲v4.makeLens❳: terms', () => { + + vi.expect( + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ.ABC.ᣔꓸꓸ.ʔ[1].ʔ.DEF.ᣔꓸꓸ.ʔ[1].ʔ.GHI.ᣔꓸꓸ.ʔ[0].ʔ + ).modify( + (focus) => focus * 100, + [ + [ + { + ABC: [ + [ + 2, + { + DEF: [ + [ + 3, + { GHI: [] }, + ], + [ + 3, + { GHI: [[4]] }, + ], + [ + 3, + { GHI: [[4], [4]] }, + ], + [ + 3, + { GHI: [[4], [4], [4]] }, + ], + ] + } + ], + [ + 2, + { + DEF: [ + [ + 3, + { GHI: [[4], [4], [4], [4]] }, + ], + [ + 3, + { GHI: [[4], [4], [4], [4], [4]] }, + ], + [ + 3, + { GHI: [[4], [4], [4], [4], [4], [4]] }, + ], + [ + 3, + { GHI: [[4], [4], [4], [4], [4], [4], [4]] }, + ], + ] + } + ] + ] + } + ], + [ + { + ABC: [ + [ + 2, + { + DEF: [ + [ + 3, + { GHI: [[4], [4], [4], [4], [4], [4], [4], [4]] }, + ], + [ + 3, + { GHI: [[4], [4], [4], [4], [4], [4], [4], [4], [4]] }, + ], + [ + 3, + { GHI: [[4], [4], [4], [4], [4], [4], [4], [4], [4], [4]] }, + ], + [ + 3, + { GHI: [[4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4]] }, + ], + ] + } + ], + [ + 2, + { + DEF: [ + [ + 3, + { GHI: [[4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4]] }, + ], + [ + 3, + { GHI: [[4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4]] }, + ], + [ + 3, + { GHI: [[4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4]] }, + ], + [ + 3, + { GHI: [[4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4]] }, + ], + ] + } + ] + ] + } + ] + ] + ) + ).toMatchInlineSnapshot + (` + [ + [ + { + "ABC": [ + [ + 2, + { + "DEF": [ + [ + 3, + { + "GHI": [], + }, + ], + [ + 3, + { + "GHI": [ + [ + 400, + ], + ], + }, + ], + [ + 3, + { + "GHI": [ + [ + 400, + ], + [ + 400, + ], + ], + }, + ], + [ + 3, + { + "GHI": [ + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + ], + }, + ], + ], + }, + ], + [ + 2, + { + "DEF": [ + [ + 3, + { + "GHI": [ + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + ], + }, + ], + [ + 3, + { + "GHI": [ + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + ], + }, + ], + [ + 3, + { + "GHI": [ + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + ], + }, + ], + [ + 3, + { + "GHI": [ + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + ], + }, + ], + ], + }, + ], + ], + }, + ], + [ + { + "ABC": [ + [ + 2, + { + "DEF": [ + [ + 3, + { + "GHI": [ + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + ], + }, + ], + [ + 3, + { + "GHI": [ + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + ], + }, + ], + [ + 3, + { + "GHI": [ + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + ], + }, + ], + [ + 3, + { + "GHI": [ + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + ], + }, + ], + ], + }, + ], + [ + 2, + { + "DEF": [ + [ + 3, + { + "GHI": [ + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + ], + }, + ], + [ + 3, + { + "GHI": [ + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + ], + }, + ], + [ + 3, + { + "GHI": [ + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + ], + }, + ], + [ + 3, + { + "GHI": [ + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + [ + 400, + ], + ], + }, + ], + ], + }, + ], + ], + }, + ], + ] + `) + + + vi.expect( + v4.makeLens( + z.array( + z.optional( + z.tuple([ + z.optional( + z.object({ + ABC: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(2)), + z.optional( + z.object({ + DEF: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(3)), + z.optional( + z.object({ + GHI: z.array( + z.optional( + z.tuple([ + z.optional(z.literal(4)), + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ) + }) + ) + ]) + ) + ), + (proxy) => proxy.ᣔꓸꓸ.ʔ[0].ʔ.ABC.ᣔꓸꓸ.ʔ[1].ʔ.DEF.ᣔꓸꓸ.ʔ[1].ʔ.GHI + ).modify( + (focus) => ({ RENAMED: focus.map((x) => (x?.[0] ?? 5) ** (x?.[0] ?? 5)) }), + [ + [ + { + ABC: [ + [ + 2, + { + DEF: [ + [ + 3, + { GHI: [] }, + ], + [ + 3, + { GHI: [[4]] }, + ], + [ + 3, + { GHI: [[], [4]] }, + ], + [ + 3, + { GHI: [[4], [], [4]] }, + ], + ] + } + ], + [ + 2, + { + DEF: [ + [ + 3, + { GHI: [[], [4], [], [4]] }, + ], + [ + 3, + { GHI: [[4], [], [4], [], [4]] }, + ], + [ + 3, + { GHI: [[], [4], [], [4], [], [4]] }, + ], + [ + 3, + { GHI: [[4], [], [4], [], [4], [], [4]] }, + ], + ] + } + ] + ] + } + ], + [ + { + ABC: [ + [ + 2, + { + DEF: [ + [ + 3, + { GHI: [[], [4], [], [4], [], [4], [], [4]] }, + ], + [ + 3, + { GHI: [[4], [], [4], [], [4], [], [4], [], [4]] }, + ], + [ + 3, + { GHI: [[], [4], [], [4], [], [4], [], [4], [], [4]] }, + ], + [ + 3, + { GHI: [[4], [], [4], [], [4], [], [4], [], [4], [], [4]] }, + ], + ] + } + ], + [ + 2, + { + DEF: [ + [ + 3, + { GHI: [[], [4], [], [4], [], [4], [], [4], [], [4], [], [4]] }, + ], + [ + 3, + { GHI: [[4], [], [4], [], [4], [], [4], [], [4], [], [4], [], [4]] }, + ], + [ + 3, + { GHI: [[], [4], [], [4], [], [4], [], [4], [], [4], [], [4], [], [4]] }, + ], + [ + 3, + { GHI: [[4], [], [4], [], [4], [], [4], [], [4], [], [4], [], [4], [], [4]] }, + ], + ] + } + ] + ] + } + ] + ] + ) + ).toMatchInlineSnapshot + (` + [ + [ + { + "ABC": [ + [ + 2, + { + "DEF": [ + [ + 3, + { + "GHI": { + "RENAMED": [], + }, + }, + ], + [ + 3, + { + "GHI": { + "RENAMED": [ + 256, + ], + }, + }, + ], + [ + 3, + { + "GHI": { + "RENAMED": [ + 3125, + 256, + ], + }, + }, + ], + [ + 3, + { + "GHI": { + "RENAMED": [ + 256, + 3125, + 256, + ], + }, + }, + ], + ], + }, + ], + [ + 2, + { + "DEF": [ + [ + 3, + { + "GHI": { + "RENAMED": [ + 3125, + 256, + 3125, + 256, + ], + }, + }, + ], + [ + 3, + { + "GHI": { + "RENAMED": [ + 256, + 3125, + 256, + 3125, + 256, + ], + }, + }, + ], + [ + 3, + { + "GHI": { + "RENAMED": [ + 3125, + 256, + 3125, + 256, + 3125, + 256, + ], + }, + }, + ], + [ + 3, + { + "GHI": { + "RENAMED": [ + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + ], + }, + }, + ], + ], + }, + ], + ], + }, + ], + [ + { + "ABC": [ + [ + 2, + { + "DEF": [ + [ + 3, + { + "GHI": { + "RENAMED": [ + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + ], + }, + }, + ], + [ + 3, + { + "GHI": { + "RENAMED": [ + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + ], + }, + }, + ], + [ + 3, + { + "GHI": { + "RENAMED": [ + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + ], + }, + }, + ], + [ + 3, + { + "GHI": { + "RENAMED": [ + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + ], + }, + }, + ], + ], + }, + ], + [ + 2, + { + "DEF": [ + [ + 3, + { + "GHI": { + "RENAMED": [ + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + ], + }, + }, + ], + [ + 3, + { + "GHI": { + "RENAMED": [ + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + ], + }, + }, + ], + [ + 3, + { + "GHI": { + "RENAMED": [ + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + ], + }, + }, + ], + [ + 3, + { + "GHI": { + "RENAMED": [ + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + 3125, + 256, + ], + }, + }, + ], + ], + }, + ], + ], + }, + ], + ] + `) + + const L_01 = v4.makeLens( + z.object({ + ABC: z.optional( + z.array( + z.object({ + DEF: z.boolean() + }) + ) + ) + }), + (proxy) => proxy.ABC.ʔ.ᣔꓸꓸ + ) + + vi.expect( + v4.makeLens( + z.object({ + ABC: z.optional( + z.array( + z.object({ + DEF: z.boolean() + }) + ) + ) + }), + (proxy) => proxy.ABC.ʔ.ᣔꓸꓸ + ).fallback + ).toMatchInlineSnapshot + (` + { + "DEF": undefined, + } + `) + + vi.expect( + v4.makeLens( + z.object({ + ABC: z.optional( + z.array( + z.object({ + DEF: z.boolean() + }) + ) + ) + }), + (proxy) => proxy.ABC.ʔ.ᣔꓸꓸ + ).get( + {} + ) + ).toMatchInlineSnapshot + (`undefined`) + + vi.expect( + v4.makeLens( + z.object({ + ABC: z.optional( + z.array( + z.object({ + DEF: z.boolean() + }) + ) + ) + }), + (proxy) => proxy.ABC.ʔ.ᣔꓸꓸ + ).get( + {} + ) + ).toMatchInlineSnapshot + (`undefined`) + + vi.expect( + v4.makeLens( + z.object({ + ABC: z.optional( + z.array( + z.object({ + DEF: z.boolean() + }) + ) + ) + }), + (proxy) => proxy.ABC.ʔ.ᣔꓸꓸ + ).set( + { DEF: false }, + { + ABC: [ + { DEF: true }, + { DEF: true } + ] + } + ) + ).toMatchInlineSnapshot + (` + { + "ABC": [ + { + "DEF": false, + }, + { + "DEF": false, + }, + ], + } + `) + + vi.expect( + v4.makeLens( + z.object({ + ABC: z.optional( + z.array( + z.object({ + DEF: z.boolean() + }) + ) + ) + }), + (proxy) => proxy.ABC.ʔ.ᣔꓸꓸ + ).modify( + (focus) => ({ + RENAMED: [ + focus.DEF, + !focus.DEF, + ] + }), + { + ABC: [ + { DEF: false }, + { DEF: false }, + { DEF: true }, + ] + } + ) + ).toMatchInlineSnapshot + (` + { + "ABC": [ + { + "RENAMED": [ + false, + true, + ], + }, + { + "RENAMED": [ + false, + true, + ], + }, + { + "RENAMED": [ + true, + false, + ], + }, + ], + } + `) + + vi.expect( + v4.makeLens( + z.array( + z.object({ + ABC: z.array( + z.object({ + DEF: z.number() + }) + ) + }), + ), + (proxy) => proxy.ᣔꓸꓸ.ABC.ᣔꓸꓸ.DEF + ).get( + [ + { ABC: [] }, + { + ABC: [ + { DEF: 1 } + ] + }, + { + ABC: [ + { DEF: 2 }, + { DEF: 3 }, + ] + } + ] + ) + ).toMatchInlineSnapshot + (` + [ + 1, + 2, + 3, + ] + `) + + type record = z.infer + const record = z.record( + z.enum({ 1: 'A', 2: 'B', 3: 'C' }), + z.object({ + ABC: z.array( + z.object({ + DEF: z.number() + }) + ) + }), + ) + + vi.expect( + v4.makeLens( + z.record( + z.enum(['A', 'B', 'C']), + z.object({ + ABC: z.array( + z.object({ + DEF: z.number() + }) + ) + }), + ), + (proxy) => proxy.ᣔꓸꓸ + ).modify( + (focus) => focus.ABC.map((x) => x.DEF), + { + A: { ABC: [] }, + B: { ABC: [{ DEF: 1 }, { DEF: 2 }] }, + C: { ABC: [{ DEF: 3 }, { DEF: 4 }, { DEF: 5 }] }, + } + ) + ).toMatchInlineSnapshot + (` + { + "A": [], + "B": [ + 1, + 2, + ], + "C": [ + 3, + 4, + 5, + ], + } + `) + + vi.expect( + v4.makeLens( + z.record( + z.enum(['A', 'B', 'C']), + z.object({ + ABC: z.array( + z.object({ + DEF: z.number() + }) + ) + }), + ), + (proxy) => proxy.C + ).modify( + (focus) => focus.ABC.filter((x) => x.DEF > 4).map((x) => x.DEF), + { + A: { ABC: [] }, + B: { ABC: [{ DEF: 1 }, { DEF: 2 }] }, + C: { ABC: [{ DEF: 3 }, { DEF: 4 }, { DEF: 5 }] }, + } + ) + ).toMatchInlineSnapshot + (` + { + "A": { + "ABC": [], + }, + "B": { + "ABC": [ + { + "DEF": 1, + }, + { + "DEF": 2, + }, + ], + }, + "C": [ + 5, + ], + } + `) + + vi.expect( + v4.makeLens( + z.object({ ABC: z.optional(z.array(z.object({ DEF: z.optional(z.array(z.object({ GHI: z.optional(z.array(z.number())) }))) }))) }), + (proxy) => proxy.ABC.ǃ + ).modify( + (proxy) => proxy, + { ABC: undefined } + ) + ).toMatchInlineSnapshot + (` + { + "ABC": [ + { + "DEF": [ + { + "GHI": [], + }, + ], + }, + ], + } + `) + + vi.expect( + v4.makeLens( + z.object({ ABC: z.optional(z.array(z.object({ DEF: z.optional(z.array(z.object({ GHI: z.optional(z.array(z.number())) }))) }))) }), + (proxy) => proxy.ABC.ʔ + ).modify( + (proxy) => proxy, + { ABC: undefined } + ) + ).toMatchInlineSnapshot + (` + { + "ABC": undefined, + } + `) + + vi.expect( + v4.makeLens( + z.object({ + ABC: z.optional( + z.array( + z.object({ + DEF: z.optional( + z.array( + z.object({ + GHI: z.optional( + z.array( + z.number() + ) + ) + }) + ) + ) + }) + ) + ) + }), + (proxy) => proxy.ABC.ʔ + ).set( + [], + { ABC: undefined }, + ) + ).toMatchInlineSnapshot + (` + { + "ABC": undefined, + } + `) + + vi.expect( + v4.makeLens( + z.object({ ABC: z.optional(z.array(z.object({ DEF: z.optional(z.array(z.object({ GHI: z.optional(z.array(z.number())) }))) }))) }), + (proxy) => proxy.ABC.ǃ + ).set( + [], + { ABC: undefined }, + ) + ).toMatchInlineSnapshot + (` + { + "ABC": [], + } + `) + + vi.expect( + v4.makeLens( + z.array(z.array(z.number())), + (proxy) => proxy.ᣔꓸꓸ + ).get( + [[1, 2, 3]] + ) + ) + .toMatchInlineSnapshot + (` + [ + [ + 1, + 2, + 3, + ], + ] + `) + + vi.expect( + v4.makeLens( + z.record(z.string(), z.array(z.number())), + (proxy) => proxy.ᣔꓸꓸ + ).get( + { a: [1, 2, 3] } + ) + ) + .toMatchInlineSnapshot + (` + { + "a": [ + [ + 1, + 2, + 3, + ], + ], + } + `) + + + vi.expect( + v4.makeLens( + z.set(z.array(z.number())), + (proxy) => proxy.ᣔꓸꓸ + ).get( + new Set([[1, 2, 3]]) + ) + ) + .toMatchInlineSnapshot + (` + [ + [ + 1, + 2, + 3, + ], + ] + `) + + vi.expect( + v4.makeLens( + z.union([z.string(), z.number()]), + (proxy) => proxy.ꖛ0 + ).get('hey') + ).toMatchInlineSnapshot + (`"hey"`) + + // vi.expect( + // v4.makeLens( + // z.union([z.string(), z.number()]), + // (proxy) => proxy.ꖛ0 + // ).get(0) + // ).toMatchInlineSnapshot + // (`undefined`) + + // vi.expect( + // v4.makeLens( + // z.array(z.union([z.string(), z.number()])), + // (proxy) => proxy.ᣔꓸꓸ.ꖛ0 + // ).get([0, 'ho', 1]) + // ).toMatchInlineSnapshot + // (` + // [ + // undefined, + // "ho", + // undefined, + // ] + // `) + + // vi.expect( + // v4.makeLens( + // z.array(z.union([z.string(), z.number()])), + // (proxy) => proxy.ᣔꓸꓸ.ꖛ1 + // ).get([0, 'ho', 1]) + // ).toMatchInlineSnapshot + // (` + // [ + // 0, + // undefined, + // 1, + // ] + // `) + + vi.expect( + v4.makeLens( + z.union([ + z.object({ + tag: z.literal('A'), + onA: z.boolean(), + }), + z.object({ + tag: z.literal('B'), + onB: z.string(), + }) + ]), + (proxy) => proxy.ꖛB + ).get({ tag: 'A', onA: true }) + ).toMatchInlineSnapshot + (`undefined`) + + vi.expect( + v4.makeLens( + z.union([ + z.object({ + tag: z.literal('A'), + onA: z.boolean(), + }), + z.object({ + tag: z.literal('B'), + onB: z.string(), + }) + ]), + (proxy) => proxy.ꖛB + ).get({ tag: 'B', onB: 'BBBB' }) + ).toMatchInlineSnapshot + (` + { + "onB": "BBBB", + "tag": "B", + } + `) + + const BIG_SCHEMA = z.object({ + A: z.optional( + z.union([ + z.literal(1), + z.array( + z.object({ + H: z.literal('two'), + I: z.union([ + z.number(), + z.object({ + J: z.optional(z.literal(false)), + }) + ]), + }) + ), + z.record( + z.enum(['x', 'y', 'z']), + z.optional( + z.union([ + z.boolean(), + z.number().int() + ]) + ) + ), + ]) + ), + B: z.optional( + z.array( + + + z.tuple([ + z.literal(7), + z.record( + z.number(), + z.union([ + z.object({ + discriminant: z.literal('circle'), + radius: z.number(), + }), + z.object({ + discriminant: z.literal('rectangle'), + width: z.number(), + length: z.number(), + }), + z.object({ + discrimnant: z.literal('square'), + length: z.number(), + }), + ]) + ) + ]), + ) + ), + C: z.optional(z.tuple([ + z.object({ + J: z.unknown(), + K: z.optional(z.string()), + }) + ])), + D: z.object({ + E: z.optional( + z.array( + z.object({ + F: z.number(), + G: z.union([ + z.object({ + tag: z.literal(7000), + L: z.optional(z.number().array()), + M: z.set(z.array(z.string())), + }), + z.object({ + tag: z.literal(8000), + }), + z.object({ + tag: z.literal(9000), + }), + ]) + }) + ) + ) + }) + }) + + const RESULT_000 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + D: { + E?: { + F: number + G: { + tag: 7000 + M: Set + L?: number[] | undefined + } | { + tag: 8000 + } | { + tag: 9000 + } + }[] | undefined + } + A?: 1 | { + H: "two" + I: number | { + J?: false | undefined + } + }[] | Record<"x" | "y" | "z", number | boolean | undefined> | undefined + B?: [7, Record][] | undefined + C?: [{ + J: unknown + K?: string | undefined + }] | undefined + } + >() + return [focus] + }, + { D: {} } + ) + + vi.expect(RESULT_000).toMatchInlineSnapshot + (` + [ + { + "D": {}, + }, + ] + `) + + const RESULT_001 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + 1 | { + H: "two" + I: number | { + J?: false | undefined + } + }[] | Record<"x" | "y" | "z", number | boolean | undefined> | undefined + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_002 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + 1 | { + H: "two" + I: number | { + J?: false | undefined + } + }[] | Record<"x" | "y" | "z", number | boolean | undefined> + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_003 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<1>() + return [focus] + }, + { D: {} } + ) + + const RESULT_004 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf< + { + H: "two" + I: number | { + J?: false | undefined + } + }[] + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_005 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + Record<"x" | "y" | "z", number | boolean | undefined> + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_006 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.x + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_007 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.x.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_008 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.x.ʔ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_009 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.x.ǃ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_010 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.x.ǃ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_012 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.x.ǃ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_013 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.y + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_014 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.y.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_015 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.y.ʔ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_017 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.y.ʔ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_018 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.y.ǃ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_019 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.y.ǃ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_020 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.y.ǃ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_021 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.z + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_022 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.z.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_023 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.z.ʔ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_024 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.z.ʔ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_025 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.z.ǃ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_026 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.z.ǃ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_027 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.z.ǃ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_028 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_029 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.ᣔꓸꓸ.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_030 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.ᣔꓸꓸ.ʔ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_031 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.ᣔꓸꓸ.ʔ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_032 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.ᣔꓸꓸ.ǃ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_033 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.ᣔꓸꓸ.ǃ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_034 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.ᣔꓸꓸ.ǃ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_035 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + 1 | { + H: "two" | undefined + I: number | { + J: false + } | undefined + }[] | { + x: number | boolean | undefined + y: number | boolean | undefined + z: number | boolean | undefined + } | undefined + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_037 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<1>() + return [focus] + }, + { D: {} } + ) + + const RESULT_038 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + H: "two" + I: number | { + J?: false | undefined + } + }[] + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_039 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf>() + return [focus] + }, + { D: {} } + ) + + const RESULT_040 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.x + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_041 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.x.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_042 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.x.ʔ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_044 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.x.ǃ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_045 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.x.ǃ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_047 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.x.ǃ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_048 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.y + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_049 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.y.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_050 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.y.ʔ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_051 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.y.ʔ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_052 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.y.ʔ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_054 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.y.ǃ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_055 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.y.ǃ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_056 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.y.ǃ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_057 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.z + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_058 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.z.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_059 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.z.ʔ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_060 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.z.ʔ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_061 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.z.ǃ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_062 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.z.ǃ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_063 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ʔ.ꖛ2.z.ǃ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_064 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_065 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.ᣔꓸꓸ.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_066 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.ᣔꓸꓸ.ʔ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_067 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.ᣔꓸꓸ.ʔ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_068 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.ᣔꓸꓸ.ǃ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_069 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.ᣔꓸꓸ.ǃ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_070 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.A.ǃ.ꖛ2.ᣔꓸꓸ.ǃ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_071 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + [7, Record][] | undefined + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_072 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ǃ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + [7 | undefined, { + [x: number]: { + discriminant: "circle" | undefined + radius: number | undefined + } | { + discriminant: "rectangle" | undefined + width: number | undefined + length: number | undefined + } | { + discrimnant: "square" | undefined + length: number | undefined + } + }][] + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_073 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ǃ.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf< + [7, Record] + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_07401 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ǃ.ᣔꓸꓸ[0] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<7>() + return [focus] + }, + { D: {} } + ) + + const RESULT_07402 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ǃ.ᣔꓸꓸ[1] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + Record + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_07403 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ǃ.ᣔꓸꓸ[1].ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ + discriminant: "circle" + radius: number + } | { + discriminant: "rectangle" + width: number + length: number + } | { + discrimnant: "square" + length: number + } + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_07404 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ǃ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ + discriminant: "circle" + radius: number + }>() + return [focus] + }, + { D: {} } + ) + + const RESULT_07405 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ǃ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ0.discriminant + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<'circle'>() + return [focus] + }, + { D: {} } + ) + + const RESULT_07406 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ǃ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ0.radius + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_07407 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ǃ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf< + { + discriminant: "rectangle" + width: number + length: number + } + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_07408 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ǃ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ1.discriminant + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<'rectangle'>() + return [focus] + }, + { D: {} } + ) + const RESULT_07409 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ǃ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ1.length + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_07410 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ǃ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ1.width + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_07411 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ǃ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ2 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + discrimnant: "square" + length: number + } + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_07412 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ǃ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ2.discrimnant + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<'square'>() + return [focus] + }, + { D: {} } + ) + + const RESULT_07413 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ǃ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ2.length + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_075 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + [7, Record][] + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_076 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ʔ.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + [7, Record] + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_077 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ʔ.ᣔꓸꓸ[0] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<7>() + return [focus] + }, + { D: {} } + ) + + const RESULT_079 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ʔ.ᣔꓸꓸ[1] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + Record + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_080 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ʔ.ᣔꓸꓸ[1].ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + discriminant: "circle" + radius: number + } | { + discriminant: "rectangle" + width: number + length: number + } | { + discrimnant: "square" + length: number + } + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_081 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ʔ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ0 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ discriminant: "circle"; radius: number }>() + return [focus] + }, + { D: {} } + ) + + const RESULT_082 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ʔ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ0.discriminant + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<'circle'>() + return [focus] + }, + { D: {} } + ) + + const RESULT_083 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ʔ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ0.radius + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_084 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ʔ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ1 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + discriminant: "rectangle" + width: number + length: number + } + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_085 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ʔ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ1.discriminant + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<'rectangle'>() + return [focus] + }, + { D: {} } + ) + const RESULT_074 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ʔ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ1.length + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_086 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ʔ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ1.width + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_087 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ʔ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ2 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ discrimnant: "square"; length: number }>() + return [focus] + }, + { D: {} } + ) + + const RESULT_088 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ʔ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ2.discrimnant + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<'square'>() + return [focus] + }, + { D: {} } + ) + + const RESULT_0890 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.B.ʔ.ᣔꓸꓸ[1].ᣔꓸꓸ.ꖛ2.length + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_089 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.C + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + [{ + J: unknown + K?: string | undefined + }] | undefined + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_090 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.C.ǃ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<[{ J: {}; K: string }]>() + return [focus] + }, + { D: {} } + ) + + const RESULT_091 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.C.ǃ[0] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ J: unknown; K?: string | undefined }>() + return [focus] + }, + { D: {} } + ) + + const RESULT_092 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.C.ǃ[0].J + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_093 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.C.ǃ[0].K + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_094 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.C.ǃ[0].K.ǃ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_095 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.C.ǃ[0].K.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_096 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.C.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<[{ J: unknown; K?: string | undefined }]>() + return [focus] + }, + { D: {} } + ) + + const RESULT_097 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.C.ʔ[0] + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ J: unknown; K?: string | undefined }>() + return [focus] + }, + { D: {} } + ) + + const RESULT_098 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.C.ʔ[0].J + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_099 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.C.ʔ[0].K + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_100 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.C.ʔ[0].K.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_101 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.C.ʔ[0].K.ǃ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_102 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + E?: { + F: number + G: { + tag: 7000 + M: Set + L?: number[] | undefined + } | { + tag: 8000 + } | { + tag: 9000 + } + }[] | undefined + } + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_103 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + F: number + G: { + tag: 7000 + M: Set + L?: number[] | undefined + } | { + tag: 8000 + } | { + tag: 9000 + } + }[] | undefined + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_104 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + F: number | undefined + G: { + tag: 7000 | undefined + M: Set + L: number[] + } | { + tag: 8000 | undefined + } | { + tag: 9000 | undefined + } + }[] + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_105 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + F: number + G: { + tag: 7000 + M: Set + L?: number[] | undefined + } | { + tag: 8000 + } | { + tag: 9000 + } + } + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_106 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ.F + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_107 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ.G + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + tag: 7000 + M: Set + L?: number[] | undefined + } | { + tag: 8000 + } | { + tag: 9000 + } + >() + return [focus] + }, + { D: {} } + ) + + /** + * TODO: typelevel bug + */ + const RESULT_108 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ.G.ꖛ7000 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ tag: 7000, L?: number[], M: Set }>() + return [focus] + }, + { D: {} } + ) + + const RESULT_109 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ.G.ꖛ7000.L + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_110 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ.G.ꖛ7000.L.ǃ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_111 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ.G.ꖛ7000.L.ǃ.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_112 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ.G.ꖛ7000.L.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_113 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ.G.ꖛ7000.L.ʔ.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_114 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ.G.ꖛ7000.M + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf>() + return [focus] + }, + { D: {} } + ) + + const RESULT_115 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ.G.ꖛ7000.M.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_116 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ.G.ꖛ7000.M.ᣔꓸꓸ.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_117 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ.G.ꖛ7000.tag + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<7000>() + return [focus] + }, + { D: {} } + ) + + /** + * TODO: bug + */ + const RESULT_118 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ.G.ꖛ8000 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ tag: 8000 }>() + return [focus] + }, + { D: {} } + ) + + const RESULT_119 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ.G.ꖛ8000.tag + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<8000>() + return [focus] + }, + { D: {} } + ) + + /** + * TODO: BUG + */ + const RESULT_120 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ.G.ꖛ9000 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ tag: 9000 }>() + return [focus] + }, + { D: {} } + ) + + const RESULT_121 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ǃ.ᣔꓸꓸ.G.ꖛ9000.tag + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<9000>() + return [focus] + }, + { D: {} } + ) + + const RESULT_122 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + F: number + G: { + tag: 7000 + M: Set + L?: number[] | undefined + } | { + tag: 8000 + } | { + tag: 9000 + } + }[] + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_123 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + F: number + G: { + tag: 7000 + M: Set + L?: number[] | undefined + } | { + tag: 8000 + } | { + tag: 9000 + } + } + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_124 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ.F + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_125 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ.G + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + tag: 7000 + M: Set + L?: number[] | undefined + } | { + tag: 8000 + } | { + tag: 9000 + } + >() + return [focus] + }, + { D: {} } + ) + + /** + * TODO: bug + */ + const RESULT_126 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ.G.ꖛ7000 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf + < + { + tag: 7000 + M: Set + L?: number[] | undefined + } + >() + return [focus] + }, + { D: {} } + ) + + const RESULT_127 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ.G.ꖛ7000.L + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_128 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ.G.ꖛ7000.L.ǃ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_129 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ.G.ꖛ7000.L.ǃ.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_130 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ.G.ꖛ7000.L.ʔ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_131 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ.G.ꖛ7000.L.ʔ.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_132 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ.G.ꖛ7000.M + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf>() + return [focus] + }, + { D: {} } + ) + + const RESULT_133 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ.G.ꖛ7000.M.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_134 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ.G.ꖛ7000.M.ᣔꓸꓸ.ᣔꓸꓸ + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf() + return [focus] + }, + { D: {} } + ) + + const RESULT_135 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ.G.ꖛ7000.tag + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<7000>() + return [focus] + }, + { D: {} } + ) + + /** + * TODO: bug + */ + const RESULT_136 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ.G.ꖛ8000 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ tag: 8000 }>() + return [focus] + }, + { D: {} } + ) + + const RESULT_137 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ.G.ꖛ8000.tag + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<8000>() + return [focus] + }, + { D: {} } + ) + + /** + * TODO: bug + */ + const RESULT_138 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ.G.ꖛ9000 + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<{ tag: 9000 }>() + return [focus] + }, + { D: {} } + ) + + const RESULT_139 = v4.makeLens( + BIG_SCHEMA, + (proxy) => proxy.D.E.ʔ.ᣔꓸꓸ.G.ꖛ9000.tag + ).modify( + (focus) => { + vi.expectTypeOf(focus).toEqualTypeOf<9000>() + return [focus] + }, + { D: {} } + ) + + }) +}) + diff --git a/packages/schema-zod-adapter/test/prism-v4.test.ts b/packages/schema-zod-adapter/test/prism-v4.test.ts new file mode 100644 index 00000000..d69bbead --- /dev/null +++ b/packages/schema-zod-adapter/test/prism-v4.test.ts @@ -0,0 +1,18 @@ +import * as vi from 'vitest' +import { v4 } from '@traversable/schema-zod-adapter' +import { fn } from '@traversable/registry' + +vi.describe('〖⛳️〗‹‹‹ ❲@traversable/schema-zod-adapter❳', () => { + vi.it('〖⛳️〗› ❲v4.Prism❳', () => { + type Leaf = { _tag: 'Leaf' } + type Node = { + _tag: 'Node' + value: number + left: Tree + right: Tree + } + + type Tree = Leaf | Node + + }) +}) diff --git a/packages/schema-zod-adapter/test/profunctor-optics-v4.test.ts b/packages/schema-zod-adapter/test/profunctor-optics-v4.test.ts new file mode 100644 index 00000000..9fc32519 --- /dev/null +++ b/packages/schema-zod-adapter/test/profunctor-optics-v4.test.ts @@ -0,0 +1,555 @@ +import * as vi from 'vitest' +import { v4 } from '@traversable/schema-zod-adapter' +import { fn } from '@traversable/registry' + +vi.describe('〖⛳️〗‹‹‹ ❲@traversable/schema-zod-adapter❳', () => { + vi.it('〖⛳️〗› ❲v4.❳', () => { + vi.expect(v4.Pro.set(v4.Pro.prop('foo'), 42, { foo: null })).toMatchInlineSnapshot + (` + { + "foo": 42, + } + `) + + vi.expect(v4.Pro.get(v4.Pro.pick(['foo', 'bar']), { foo: 'something', bar: 42, baz: true })).toMatchInlineSnapshot + (` + { + "bar": 42, + "foo": "something", + } + `) + + + vi.expect(v4.Pro.get(v4.Pro.pick([]), { foo: 'something', bar: 42, baz: true })).toMatchInlineSnapshot + (`{}`) + + vi.expect(v4.Pro.get(v4.Pro.propOr(0, 'abc'), { abc: undefined })).toMatchInlineSnapshot + (`0`) + + vi.expect(v4.Pro.get(v4.Pro.pipe(v4.Pro.prop('abc'), v4.Pro.propOr({ def: { ghi: 100 } }, 'def')), { abc: undefined })).toMatchInlineSnapshot + (` + { + "def": { + "ghi": 100, + }, + } + `) + + + vi.expect(v4.Pro.collect(v4.Pro.pipe(v4.Pro.traverse, v4.Pro.prop('foo'), v4.Pro.prop('bar')))([ + { foo: { bar: 1 } }, + { foo: { bar: 2 } }, + ])).toMatchInlineSnapshot + (` + [ + 1, + 2, + ] + `) + + + vi.expect(v4.Pro.scavenge({ foo: { bar: undefined } }, v4.Pro.pipe(v4.Pro.traverse, v4.Pro.prop('foo'), v4.Pro.prop('bar')), [ + { foo: { bar: 1 } }, + undefined, + ])).toMatchInlineSnapshot + (` + [ + 1, + Symbol(@traversable/schema/URI::notfound), + ] + `) + + + vi.expect(v4.Pro.scavenge({ foo: { bar: 2 } }, v4.Pro.pipe(v4.Pro.traverse, v4.Pro.prop('foo'), v4.Pro.prop('bar')), [ + { foo: { bar: 1 } }, + undefined, + ])).toMatchInlineSnapshot + (` + [ + 1, + 2, + ] + `) + + vi.expect(v4.Pro.scavenge({ foo: { bar: 2 } }, v4.Pro.pipe(v4.Pro.traverse, v4.Pro.prop('foo'), v4.Pro.prop('bar')), [ + { foo: { bar: 1 } }, + {}, + ])).toMatchInlineSnapshot + (` + [ + 1, + Symbol(@traversable/schema/URI::notfound), + ] + `) + + vi.expect( + v4.Pro.scavenge( + { d: -1 }, + v4.Pro.pipe(v4.Pro.prop('foo'), v4.Pro.traverse, v4.Pro.traverse, v4.Pro.prop('c')), + { foo: { a: [{ c: undefined }, { c: { d: 2 } }, {}], b: [{ c: { d: 4 } }, { c: { d: 5 } }, { c: { d: 6 } }] } }, + ) + ).toMatchInlineSnapshot + (`undefined`) + + + vi.expect( + v4.Pro.collect(v4.Pro.pipe(v4.Pro.prop('foo'), v4.Pro.traverse, v4.Pro.traverse, v4.Pro.prop('c')))( + { foo: { a: [{ c: 1 }, { c: 2 }, { c: 3 }], b: [{ c: 4 }, { c: 5 }, { c: 6 }] } }, + ) + ).toMatchInlineSnapshot + (` + { + "a": [ + 1, + 2, + 3, + ], + "b": [ + 4, + 5, + 6, + ], + } + `) + + vi.expect( + v4.Pro.collectObject( + v4.Pro.pipe( + v4.Pro.prop('F'), + v4.Pro.traverse, + v4.Pro.prop('G'), + ) + )({ + F: { + X: { + G: [] + } + } + }) + ).toMatchInlineSnapshot + (` + { + "X": [], + } + `) + + vi.expect( + v4.Pro.collectObject( + v4.Pro.pipe( + v4.Pro.prop('F'), + v4.Pro.traverse, + v4.Pro.prop('G'), + ) + )({ + F: { + ABC: { + G: [ + { one: 1 }, + { two: 2 }, + { three: 3 }, + ], + }, + DEF: { + G: [ + { four: 4 }, + { five: 5 }, + ] + }, + GHI: { + G: [], + }, + JKL: { + G: undefined + } + } + }) + ).toMatchInlineSnapshot + (` + { + "ABC": [ + { + "one": 1, + }, + { + "two": 2, + }, + { + "three": 3, + }, + ], + "DEF": [ + { + "four": 4, + }, + { + "five": 5, + }, + ], + "GHI": [], + "JKL": Symbol(@traversable/schema/URI::notfound), + } + `) + + + vi.expect( + v4.Pro.collectObject( + v4.Pro.pipe( + v4.Pro.prop('F'), + v4.Pro.traverse, + v4.Pro.prop('G'), + ) + )({ + F: { + X: { + G: [] + } + } + }) + ).toMatchInlineSnapshot + (` + { + "X": [], + } + `) + + vi.expect( + v4.Pro.collectObject( + v4.Pro.pipe( + v4.Pro.prop('F'), + v4.Pro.traverse, + v4.Pro.prop('G'), + ) + )({ + F: { + X: { + G: [] + } + } + }) + ).toMatchInlineSnapshot + (` + { + "X": [], + } + `) + + + + vi.expect( + v4.Pro.collectObject( + v4.Pro.pipe( + v4.Pro.prop('foo'), + v4.Pro.traverse, + v4.Pro.traverse, + v4.Pro.prop('c'), + ) + )({ + foo: { + a: [ + { c: 1 }, + { c: 2 }, + { c: 3 }, + ], + b: [ + { c: 4 }, + { c: 5 }, + { c: 6 }, + ] + } + }) + ).toMatchInlineSnapshot + (` + { + "a": [ + 1, + 2, + 3, + ], + "b": [ + 4, + 5, + 6, + ], + } + `) + + vi.expect( + v4.Pro.set( + v4.Pro.pipe( + v4.Pro.prop('foo'), + v4.Pro.traverse, + v4.Pro.traverse, + v4.Pro.prop('c'), + ), + 9000, + { + foo: { + a: [ + { c: 1 }, + { c: 2 }, + { c: 3 }, + ], + b: [ + { c: 4 }, + { c: 5 }, + { c: 6 } + ] + } + } + ) + ).toMatchInlineSnapshot + (` + { + "foo": { + "a": [ + { + "c": 9000, + }, + { + "c": 9000, + }, + { + "c": 9000, + }, + ], + "b": [ + { + "c": 9000, + }, + { + "c": 9000, + }, + { + "c": 9000, + }, + ], + }, + } + `) + + vi.expect( + v4.Pro.modify( + v4.Pro.pipe( + v4.Pro.prop('foo'), + v4.Pro.traverse, + v4.Pro.traverse, + v4.Pro.prop('c'), + ), + (x) => x + 1, + { + foo: { + a: [ + { c: 1 }, + { c: 2 }, + { c: 3 }, + ], + b: [ + { c: 4 }, + { c: 5 }, + { c: 6 }, + ] + } + } + ) + ).toMatchInlineSnapshot + (` + { + "foo": { + "a": [ + { + "c": 2, + }, + { + "c": 3, + }, + { + "c": 4, + }, + ], + "b": [ + { + "c": 5, + }, + { + "c": 6, + }, + { + "c": 7, + }, + ], + }, + } + `) + + vi.expect( + v4.Pro.collect( + v4.Pro.pipe( + v4.Pro.prop('foo'), + v4.Pro.traverse + ) + )({ + foo: [ + { + bar: [ + { baz: 1 }, + { baz: 2 }, + ] + }, + { + bar: [ + { baz: 3 }, + { baz: 4 }, + ] + } + ] + }) + ).toMatchInlineSnapshot + (` + [ + { + "bar": [ + { + "baz": 1, + }, + { + "baz": 2, + }, + ], + }, + { + "bar": [ + { + "baz": 3, + }, + { + "baz": 4, + }, + ], + }, + ] + `) + + vi.expect( + v4.Pro.collect( + v4.Pro.pipe( + v4.Pro.prop('foo'), + v4.Pro.traverse, + v4.Pro.prop('bar'), + ) + )({ + foo: [ + { + bar: [ + { baz: 1 }, + { baz: 2 } + ] + }, + { + bar: [ + { baz: 3 }, + { baz: 4 } + ] + } + ] + }) + ).toMatchInlineSnapshot + (` + [ + [ + { + "baz": 1, + }, + { + "baz": 2, + }, + ], + [ + { + "baz": 3, + }, + { + "baz": 4, + }, + ], + ] + `) + + vi.expect( + v4.Pro.collect( + v4.Pro.pipe( + v4.Pro.prop('foo'), + v4.Pro.traverse, + v4.Pro.prop('bar'), + v4.Pro.traverse, + ) + )({ + foo: [ + { + bar: [ + { baz: 1 }, + { baz: 2 }, + ] + }, + { + bar: [ + { baz: 3 }, + { baz: 4 }, + ] + } + ] + }) + ).toMatchInlineSnapshot + (` + [ + { + "baz": 1, + }, + { + "baz": 2, + }, + { + "baz": 3, + }, + { + "baz": 4, + }, + ] + `) + + vi.expect( + v4.Pro.collect( + v4.Pro.pipe( + v4.Pro.prop('foo'), + v4.Pro.traverse, + v4.Pro.prop('bar'), + v4.Pro.traverse, + v4.Pro.prop('baz'), + ) + )({ + foo: [ + { + bar: [ + { baz: 1 }, + { baz: 2 }, + ] + }, + { + bar: [ + { baz: 3 }, + { baz: 4 }, + ] + } + ] + }) + ).toMatchInlineSnapshot + (` + [ + 1, + 2, + 3, + 4, + ] + `) + + }) +}) diff --git a/packages/schema-zod-adapter/test/proxy-v4.test.ts b/packages/schema-zod-adapter/test/proxy-v4.test.ts new file mode 100644 index 00000000..39728065 --- /dev/null +++ b/packages/schema-zod-adapter/test/proxy-v4.test.ts @@ -0,0 +1,1087 @@ +import * as vi from 'vitest' +import { z } from 'zod/v4' + +import { v4 } from '@traversable/schema-zod-adapter' +import { fn } from '@traversable/registry' + +const toString = (schemaPaths: v4.Optic.SchemaPath[]) => schemaPaths.map(([path, descriptors, schema]) => [ + path, + descriptors, + v4.toString(schema), +] satisfies [any, any, any]) + +vi.describe('〖️⛳️〗‹‹‹ ❲@traversable/schema-zod-adapter❳', () => { + + vi.it('〖️⛳️〗› ❲v4.buildLenses❳ ', () => { + + type Type_00 = z.infer + const Type_00 = z.object({ + A: z.optional( + z.array( + z.object({ + B: z.optional( + z.array( + z.object({ + C: z.optional(z.number()), + D: z.string(), + }) + ) + ), + E: z.boolean(), + }) + ) + ), + F: z.unknown(), + }) + + const lenses_00 = v4.buildLenses(Type_00) + + const lens_000 = lenses_00['A'] + + vi.expect(lenses_00).toMatchInlineSnapshot + (` + { + "": { + "fallback": { + "A": [ + { + "B": [ + { + "C": undefined, + "D": undefined, + }, + ], + "E": undefined, + }, + ], + "F": undefined, + }, + "get": [Function], + "modify": [Function], + "path": [], + "set": [Function], + "type": "Identity", + }, + "A": { + "fallback": [ + { + "B": [ + { + "C": undefined, + "D": undefined, + }, + ], + "E": undefined, + }, + ], + "get": [Function], + "modify": [Function], + "path": [ + "A", + ], + "set": [Function], + "type": "Lens", + }, + "A.ʔ": { + "fallback": [ + { + "B": [ + { + "C": undefined, + "D": undefined, + }, + ], + "E": undefined, + }, + ], + "get": [Function], + "modify": [Function], + "path": [ + "A", + "ʔ", + ], + "set": [Function], + "type": "Prism", + }, + "A.ʔ.ↆ": { + "fallback": { + "B": [ + { + "C": undefined, + "D": undefined, + }, + ], + "E": undefined, + }, + "get": [Function], + "modify": [Function], + "path": [ + "A", + "ʔ", + "ↆ", + ], + "set": [Function], + "type": "Traversal", + }, + "A.ʔ.ↆ.B": { + "fallback": [ + { + "C": undefined, + "D": undefined, + }, + ], + "get": [Function], + "modify": [Function], + "path": [ + "A", + "ʔ", + "ↆ", + "B", + ], + "set": [Function], + "type": "Traversal", + }, + "A.ʔ.ↆ.B.ʔ": { + "fallback": [ + { + "C": undefined, + "D": undefined, + }, + ], + "get": [Function], + "modify": [Function], + "path": [ + "A", + "ʔ", + "ↆ", + "B", + "ʔ", + ], + "set": [Function], + "type": "Traversal", + }, + "A.ʔ.ↆ.B.ʔ.ↆ": { + "fallback": { + "C": undefined, + "D": undefined, + }, + "get": [Function], + "modify": [Function], + "path": [ + "A", + "ʔ", + "ↆ", + "B", + "ʔ", + "ↆ", + ], + "set": [Function], + "type": "Traversal", + }, + "A.ʔ.ↆ.B.ʔ.ↆ.C": { + "fallback": undefined, + "get": [Function], + "modify": [Function], + "path": [ + "A", + "ʔ", + "ↆ", + "B", + "ʔ", + "ↆ", + "C", + ], + "set": [Function], + "type": "Traversal", + }, + "A.ʔ.ↆ.B.ʔ.ↆ.C.ʔ": { + "fallback": undefined, + "get": [Function], + "modify": [Function], + "path": [ + "A", + "ʔ", + "ↆ", + "B", + "ʔ", + "ↆ", + "C", + "ʔ", + ], + "set": [Function], + "type": "Traversal", + }, + "A.ʔ.ↆ.B.ʔ.ↆ.D": { + "fallback": undefined, + "get": [Function], + "modify": [Function], + "path": [ + "A", + "ʔ", + "ↆ", + "B", + "ʔ", + "ↆ", + "D", + ], + "set": [Function], + "type": "Traversal", + }, + "A.ʔ.ↆ.E": { + "fallback": undefined, + "get": [Function], + "modify": [Function], + "path": [ + "A", + "ʔ", + "ↆ", + "E", + ], + "set": [Function], + "type": "Traversal", + }, + "F": { + "fallback": undefined, + "get": [Function], + "modify": [Function], + "path": [ + "F", + ], + "set": [Function], + "type": "Lens", + }, + } + `) + + + + vi.expect(lens_000.get({ F: 1, A: [{ B: [{ D: 'HEY' }], E: false }] })).toMatchInlineSnapshot + (` + [ + { + "B": [ + { + "D": "HEY", + }, + ], + "E": false, + }, + ] + `) + + // TODO: look into the behavior for `A` here + vi.expect(lenses_00['A.ʔ.ↆ.B'].set(1, { F: 1 })).toMatchInlineSnapshot + (` + { + "A": {}, + "F": 1, + } + `) + + vi.expect(lens_000.set(undefined, { F: 1, A: [{ B: [{ D: 'HEY' }], E: false }] })).toMatchInlineSnapshot + (` + { + "A": undefined, + "F": 1, + } + `) + + vi.expect(lens_000.modify((x: unknown) => ({ MODIFIED: x }), { F: 1, A: [{ B: [{ D: 'HEY' }], E: false }] })).toMatchInlineSnapshot + (` + { + "A": { + "MODIFIED": [ + { + "B": [ + { + "D": "HEY", + }, + ], + "E": false, + }, + ], + }, + "F": 1, + } + `) + + + + const lens_001 = lenses_00['A.ʔ'] + + vi.expect(lens_001.get({ F: 1, A: [{ B: [{ D: 'HEY' }], E: false }] })).toMatchInlineSnapshot + (` + [ + { + "B": [ + { + "D": "HEY", + }, + ], + "E": false, + }, + ] + `) + + vi.expect(lens_001.get({ F: 1, A: [{ B: [{ D: 'HEY' }], E: false }] })).toMatchInlineSnapshot + (` + [ + { + "B": [ + { + "D": "HEY", + }, + ], + "E": false, + }, + ] + `) + + vi.expect(lens_001.get({ F: 1 })).toMatchInlineSnapshot + (`Symbol(@traversable/schema/URI::notfound)`) + + + + + type Type_01 = z.infer + const Type_01 = z.object({ + a: z.number(), + b: z.optional(z.object({ c: z.boolean() })), + }) + + const lenses_01 = v4.buildLenses(Type_01) + + vi.expect(lenses_01).toMatchInlineSnapshot + (` + { + "": { + "fallback": { + "a": undefined, + "b": { + "c": undefined, + }, + }, + "get": [Function], + "modify": [Function], + "path": [], + "set": [Function], + "type": "Identity", + }, + "a": { + "fallback": undefined, + "get": [Function], + "modify": [Function], + "path": [ + "a", + ], + "set": [Function], + "type": "Lens", + }, + "b": { + "fallback": { + "c": undefined, + }, + "get": [Function], + "modify": [Function], + "path": [ + "b", + ], + "set": [Function], + "type": "Lens", + }, + "b.ʔ": { + "fallback": { + "c": undefined, + }, + "get": [Function], + "modify": [Function], + "path": [ + "b", + "ʔ", + ], + "set": [Function], + "type": "Prism", + }, + "b.ʔ.c": { + "fallback": undefined, + "get": [Function], + "modify": [Function], + "path": [ + "b", + "ʔ", + "c", + ], + "set": [Function], + "type": "Prism", + }, + } + `) + + vi.expect(lenses_01.a.get({ a: 0, b: { c: false } })).toMatchInlineSnapshot + (`0`) + vi.expect(lenses_01.a.get({ a: 0 })).toMatchInlineSnapshot + (`0`) + vi.expect(lenses_01.a.get({ a: 0, ...Math.random() > 1 && { b: { c: false } } })).toMatchInlineSnapshot + (`0`) + vi.expect(lenses_01.a.get({ a: 0, b: undefined })).toMatchInlineSnapshot + (`0`) + + vi.expect(lenses_01.b.get({ a: 0, b: { c: false } })).toMatchInlineSnapshot + (` + { + "c": false, + } + `) + vi.expect(lenses_01.b.get({ a: 0 })).toMatchInlineSnapshot + (`Symbol(@traversable/schema/URI::notfound)`) + vi.expect(lenses_01.b.get({ a: 0, ...Math.random() > 1 && { b: { c: false } } })).toMatchInlineSnapshot + (`Symbol(@traversable/schema/URI::notfound)`) + vi.expect(lenses_01.b.get({ a: 0, b: undefined })).toMatchInlineSnapshot + (`Symbol(@traversable/schema/URI::notfound)`) + + vi.expect(lenses_01['b.ʔ'].get({ a: 0, b: { c: false } })).toMatchInlineSnapshot + (` + { + "c": false, + } + `) + vi.expect(lenses_01['b.ʔ'].get({ a: 0 })).toMatchInlineSnapshot + (`Symbol(@traversable/schema/URI::notfound)`) + vi.expect(lenses_01['b.ʔ'].get({ a: 0, ...Math.random() > 1 && { b: { c: false } } })).toMatchInlineSnapshot + (`Symbol(@traversable/schema/URI::notfound)`) + vi.expect(lenses_01['b.ʔ'].get({ a: 0, b: undefined })).toMatchInlineSnapshot + (`Symbol(@traversable/schema/URI::notfound)`) + + vi.expect(lenses_01['b.ʔ.c'].get({ a: 0, b: { c: false } })).toMatchInlineSnapshot + (`false`) + vi.expect(lenses_01['b.ʔ.c'].get({ a: 0 })).toMatchInlineSnapshot + (`Symbol(@traversable/schema/URI::notfound)`) + vi.expect(lenses_01['b.ʔ.c'].get({ a: 0, ...Math.random() > 1 && { b: { c: false } } })).toMatchInlineSnapshot + (`Symbol(@traversable/schema/URI::notfound)`) + vi.expect(lenses_01['b.ʔ.c'].get({ a: 0, b: undefined })).toMatchInlineSnapshot + (`Symbol(@traversable/schema/URI::notfound)`) + + type Type_02 = z.infer + const Type_02 = z.array(z.object({ a: z.optional(z.number()) })) + + const lenses_02 = v4.buildLenses(Type_02) + const root_20 = lenses_02[''] + const trav_20 = lenses_02['ↆ'] + const trav_21 = lenses_02['ↆ.a'] + const trav_22 = lenses_02['ↆ.a.ʔ'] + + vi.expect(lenses_02).toMatchInlineSnapshot + (` + { + "": { + "fallback": [ + { + "a": undefined, + }, + ], + "get": [Function], + "modify": [Function], + "path": [], + "set": [Function], + "type": "Identity", + }, + "ↆ": { + "fallback": { + "a": undefined, + }, + "get": [Function], + "modify": [Function], + "path": [ + "ↆ", + ], + "set": [Function], + "type": "Traversal", + }, + "ↆ.a": { + "fallback": undefined, + "get": [Function], + "modify": [Function], + "path": [ + "ↆ", + "a", + ], + "set": [Function], + "type": "Traversal", + }, + "ↆ.a.ʔ": { + "fallback": undefined, + "get": [Function], + "modify": [Function], + "path": [ + "ↆ", + "a", + "ʔ", + ], + "set": [Function], + "type": "Traversal", + }, + } + `) + + type Type_03 = z.infer + const Type_03 = z.object({ + F: z.record( + z.enum(["X"]), + z.object({ G: z.array(z.string()) }) + ) + }) + + const lenses_03 = v4.buildLenses(Type_03) + + vi.expect(lenses_03['F.ↆ'].get({ F: { X: { G: [] } } })).toMatchInlineSnapshot + (` + { + "X": [ + { + "G": [], + }, + ], + } + `) + + vi.expect(lenses_03).toMatchInlineSnapshot + (` + { + "": { + "fallback": { + "F": { + "X": { + "G": [], + }, + }, + }, + "get": [Function], + "modify": [Function], + "path": [], + "set": [Function], + "type": "Identity", + }, + "F": { + "fallback": { + "X": { + "G": [], + }, + }, + "get": [Function], + "modify": [Function], + "path": [ + "F", + ], + "set": [Function], + "type": "Lens", + }, + "F.ↆ": { + "fallback": { + "G": [], + }, + "get": [Function], + "modify": [Function], + "path": [ + "F", + "ↆ", + ], + "set": [Function], + "type": "Traversal", + }, + "F.ↆ.G": { + "fallback": [], + "get": [Function], + "modify": [Function], + "path": [ + "F", + "ↆ", + "G", + ], + "set": [Function], + "type": "Traversal", + }, + "F.ↆ.G.ↆ": { + "fallback": [], + "get": [Function], + "modify": [Function], + "path": [ + "F", + "ↆ", + "G", + "ↆ", + ], + "set": [Function], + "type": "Traversal", + }, + } + `) + + }) + + vi.it('〖️⛳️〗› ❲v4.buildIntermediateRepresentation❳ ', () => { + vi.expect(v4.buildIntermediateRepresentation( + z.object({ + a: z.number(), + b: z.optional(z.object({ c: z.boolean() })), + }) + )).toMatchInlineSnapshot + (` + [ + [ + { + "optics": [ + { + "tag": "declaration", + "type": "Iso", + }, + ], + "path": [], + }, + { + "optics": [ + { + "tag": "declaration", + "type": "Iso", + }, + { + "prop": "a", + "tag": "prop", + "type": "Lens", + }, + ], + "path": [ + Symbol(@traversable/schema/URI::object), + "a", + ], + }, + ], + [ + { + "optics": [ + { + "tag": "declaration", + "type": "Iso", + }, + ], + "path": [], + }, + { + "optics": [ + { + "tag": "declaration", + "type": "Iso", + }, + { + "prop": "b", + "tag": "prop", + "type": "Lens", + }, + ], + "path": [ + Symbol(@traversable/schema/URI::object), + "b", + ], + }, + { + "optics": [ + { + "tag": "declaration", + "type": "Iso", + }, + { + "prop": "b", + "tag": "prop", + "type": "Lens", + }, + { + "nullable": undefined, + "tag": "fromNullable", + "type": "Optional", + }, + ], + "path": [ + Symbol(@traversable/schema/URI::object), + "b", + Symbol(@traversable/schema/URI::optional), + ], + }, + { + "optics": [ + { + "tag": "declaration", + "type": "Iso", + }, + { + "prop": "b", + "tag": "prop", + "type": "Lens", + }, + { + "nullable": undefined, + "tag": "fromNullable", + "type": "Optional", + }, + { + "prop": "c", + "tag": "prop", + "type": "Optional", + }, + ], + "path": [ + Symbol(@traversable/schema/URI::object), + "b", + Symbol(@traversable/schema/URI::optional), + Symbol(@traversable/schema/URI::object), + "c", + ], + }, + ], + ] + `) + }) +}) + +vi.describe("〖️⛳️〗‹‹‹ ❲@traversable/schema-zod-adapter❳", () => { + vi.it("〖️⛳️〗› ❲v4.buildDescriptors❳ ", () => { + vi.expect(toString(v4.buildDescriptors( + z.object({ a: z.number(), b: z.optional(z.string()) }) + ))).toMatchInlineSnapshot + (` + [ + [ + [ + Symbol(@traversable/schema/URI::object), + "a", + ], + [ + { + "prop": "a", + "tag": "prop", + "type": "Lens", + }, + ], + "z.number()", + ], + [ + [ + Symbol(@traversable/schema/URI::object), + "b", + Symbol(@traversable/schema/URI::optional), + ], + [ + { + "prop": "b", + "tag": "prop", + "type": "Lens", + }, + { + "nullable": undefined, + "tag": "fromNullable", + "type": "Prism", + }, + ], + "z.string()", + ], + ] + `) + + vi.expect(toString(v4.buildDescriptors( + z.tuple([ + z.number(), + z.optional( + z.tuple([ + z.optional( + z.string() + ) + ]) + ), + z.boolean() + ]) + ))).toMatchInlineSnapshot + (` + [ + [ + [ + Symbol(@traversable/schema/URI::tuple), + 0, + ], + [ + { + "index": 0, + "tag": "index", + "type": "Lens", + }, + ], + "z.number()", + ], + [ + [ + Symbol(@traversable/schema/URI::tuple), + 1, + Symbol(@traversable/schema/URI::optional), + Symbol(@traversable/schema/URI::tuple), + 0, + Symbol(@traversable/schema/URI::optional), + ], + [ + { + "index": 1, + "tag": "index", + "type": "Lens", + }, + { + "nullable": undefined, + "tag": "fromNullable", + "type": "Prism", + }, + { + "index": 0, + "tag": "index", + "type": "Lens", + }, + { + "nullable": undefined, + "tag": "fromNullable", + "type": "Prism", + }, + ], + "z.string()", + ], + [ + [ + Symbol(@traversable/schema/URI::tuple), + 2, + ], + [ + { + "index": 2, + "tag": "index", + "type": "Lens", + }, + ], + "z.boolean()", + ], + ] + `) + + vi.expect(toString( + v4.buildDescriptors( + z.object({ + a: z.number(), + b: z.optional(z.string()), + c: z.array( + z.object({ + d: z.object({ + e: z.boolean() + }) + }) + ) + }) + ) + )).toMatchInlineSnapshot + (` + [ + [ + [ + Symbol(@traversable/schema/URI::object), + "a", + ], + [ + { + "prop": "a", + "tag": "prop", + "type": "Lens", + }, + ], + "z.number()", + ], + [ + [ + Symbol(@traversable/schema/URI::object), + "b", + Symbol(@traversable/schema/URI::optional), + ], + [ + { + "prop": "b", + "tag": "prop", + "type": "Lens", + }, + { + "nullable": undefined, + "tag": "fromNullable", + "type": "Prism", + }, + ], + "z.string()", + ], + [ + [ + Symbol(@traversable/schema/URI::object), + "c", + Symbol(@traversable/schema/URI::array), + Symbol(@traversable/schema/URI::object), + "d", + Symbol(@traversable/schema/URI::object), + "e", + ], + [ + { + "prop": "c", + "tag": "prop", + "type": "Lens", + }, + { + "applicative": "array", + "tag": "applicative", + "type": "TraversalWithPredicate", + }, + { + "prop": "d", + "tag": "prop", + "type": "Lens", + }, + { + "prop": "e", + "tag": "prop", + "type": "Lens", + }, + ], + "z.boolean()", + ], + ] + `) + + vi.expect(toString( + v4.buildDescriptors( + z.union([ + z.number(), + z.string(), + z.boolean(), + ]) + ) + )).toMatchInlineSnapshot + (` + [ + [ + [ + Symbol(@traversable/schema/URI::union), + 0, + ], + [ + { + "predicate": [Function], + "tag": "fromPredicate", + "type": "Prism", + }, + ], + "z.number()", + ], + [ + [ + Symbol(@traversable/schema/URI::union), + 1, + ], + [ + { + "predicate": [Function], + "tag": "fromPredicate", + "type": "Prism", + }, + ], + "z.string()", + ], + [ + [ + Symbol(@traversable/schema/URI::union), + 2, + ], + [ + { + "predicate": [Function], + "tag": "fromPredicate", + "type": "Prism", + }, + ], + "z.boolean()", + ], + ] + `) + + vi.expect(toString( + v4.buildDescriptors( + z.union([ + z.array(z.number()), + z.object({ a: z.optional(z.string()) }), + z.boolean(), + ]) + ) + )).toMatchInlineSnapshot + (` + [ + [ + [ + Symbol(@traversable/schema/URI::union), + 0, + Symbol(@traversable/schema/URI::array), + ], + [ + { + "predicate": [Function], + "tag": "fromPredicate", + "type": "Prism", + }, + { + "applicative": "array", + "tag": "applicative", + "type": "TraversalWithPredicate", + }, + ], + "z.number()", + ], + [ + [ + Symbol(@traversable/schema/URI::union), + 1, + Symbol(@traversable/schema/URI::object), + "a", + Symbol(@traversable/schema/URI::optional), + ], + [ + { + "predicate": [Function], + "tag": "fromPredicate", + "type": "Prism", + }, + { + "prop": "a", + "tag": "prop", + "type": "Lens", + }, + { + "nullable": undefined, + "tag": "fromNullable", + "type": "Prism", + }, + ], + "z.string()", + ], + [ + [ + Symbol(@traversable/schema/URI::union), + 2, + ], + [ + { + "predicate": [Function], + "tag": "fromPredicate", + "type": "Prism", + }, + ], + "z.boolean()", + ], + ] + `) + + }) +}) diff --git a/packages/schema-zod-adapter/test/toString-v4.test.ts b/packages/schema-zod-adapter/test/toString-v4.test.ts index 9c858d74..f7d21dc8 100644 --- a/packages/schema-zod-adapter/test/toString-v4.test.ts +++ b/packages/schema-zod-adapter/test/toString-v4.test.ts @@ -1,7 +1,48 @@ import * as vi from "vitest" -import { z } from "zod4" +import { z } from "zod/v4" +import * as fc from 'fast-check' import { v4 } from "@traversable/schema-zod-adapter" +import { has } from '@traversable/registry' + +function schemaFromString(string: string): (lib: typeof z) => T +function schemaFromString(string: string) { + try { return globalThis.Function('z', 'return ' + string) } + catch (e) { throw e } +} + +function roundtrip(schema: T): T { return schemaFromString(v4.toString(schema))(z) } + +vi.describe.only("〖️⛳️〗‹‹‹ ❲@traversable/schema-zod-adapter❳: v4.toString", () => { + + vi.it.only("〖️⛳️〗› ❲z.brand❳ ", () => { + fc.assert(fc.property(v4.SeedGenerator({ file: {}, exclude: ['object', 'template_literal', 'promise', 'nonoptional', 'enum'] })['*'], (seed) => { + // let schema + const schema = v4.seedToSchema(seed) + let toAndFrom + try { + toAndFrom = roundtrip(schema) + vi.assert.equal(v4.toString(schema), v4.toString(toAndFrom)) + } catch (e) { + console.group('\r\n\n') + console.log('\n\nSCHEMA: ', v4.toString(schema)) + toAndFrom && console.log('\n\nROUNDTRIP: ', v4.toString(toAndFrom)) + console.groupEnd() + vi.assert.fail(has('message', (x) => typeof x === 'string')(e) ? e.message : JSON.stringify(e)) + } + }), { + endOnFailure: true, + numRuns: 10000, + // examples: [ + // [7500, [["`aCI/U`", [10]]]] + // ] + }) + + // const input = z.object({ a: z.boolean(), b: z.optional(z.number()) }) + // const output = roundtrip(input) + // vi.assert.equal(v4.toString(input), v4.toString(output)) + }) +}) vi.describe("〖️⛳️〗‹‹‹ ❲@traversable/schema-zod-adapter❳: v4.toString", () => { vi.it("〖️⛳️〗› ❲z.brand❳ ", () => { diff --git a/packages/schema-zod-adapter/test/traversal-v4.test.ts b/packages/schema-zod-adapter/test/traversal-v4.test.ts new file mode 100644 index 00000000..354789e6 --- /dev/null +++ b/packages/schema-zod-adapter/test/traversal-v4.test.ts @@ -0,0 +1,151 @@ +import * as vi from 'vitest' +import { v4 } from '@traversable/schema-zod-adapter' +import { fn } from '@traversable/registry' + +vi.describe('〖⛳️〗‹‹‹ ❲@traversable/schema-zod-adapter❳', () => { + vi.it('〖⛳️〗› ❲v4.Traversal❳', () => { + type Tweet = { text: string } + type Tweets = { tweets: Tweet[] } + + const traversal_01 = fn.pipe( + // v4.Lens.declare(), + v4.Lens.prop('tweets')(), + v4.Traversal.fromLens, + ) + + vi.expectTypeOf(traversal_01).toEqualTypeOf>() + + vi.expect( + fn.pipe( + // {}, + traversal_01, + v4.Traversal.getAll({ tweets: [] }), + ) + ).toMatchInlineSnapshot + (` + [ + [], + ] + `) + + vi.expect( + fn.pipe( + // {}, + traversal_01, + v4.Traversal.getAll({ tweets: [{ text: 'A' }, { text: 'B' }] }), + ) + ).toMatchInlineSnapshot + (` + [ + [ + { + "text": "A", + }, + { + "text": "B", + }, + ], + ] + `) + + const lens_02 = fn.pipe(traversal_01, v4.Traversal.modify((xs) => xs.map((x) => ({ ...x, text: x.text.concat('C') })))) + + vi.expect( + lens_02({ tweets: [{ text: 'A' }, { text: 'B' }] }) + ).toMatchInlineSnapshot + (` + { + "tweets": [ + { + "text": "AC", + }, + { + "text": "BC", + }, + ], + } + `) + }) + + vi.it('〖⛳️〗› ❲v4.Traversal❳: Traveral.fromOptionalWithFallback', () => { + const traversal_01 = v4.Traversal.fromOptionalWithFallback(v4.Optional.fromPrism(v4.Prism.fromPredicate((x) => x !== undefined)), 1 as {} | null) + vi.expect(v4.Traversal.getAllWithFallback(undefined, 1 as {} | null)(traversal_01)).toMatchInlineSnapshot + (`1`) + + const traversal_02 = v4.Traversal.fromOptionalWithFallback(v4.Optional.fromPrism(v4.Prism.fromPredicate((x) => x !== undefined)), { x: 2 }) + vi.expect(v4.Traversal.modifyWithFallback((x) => ({ x }), { x: 3 } as {} | null)(traversal_02)(1)).toMatchInlineSnapshot + (` + { + "x": 1, + } + `) + + vi.expect(v4.Traversal.modifyWithFallback((x) => ({ x }), { x: 3 } as {} | null)(traversal_02)(undefined)).toMatchInlineSnapshot + (` + { + "x": 2, + } + `) + }) + + vi.it('〖⛳️〗› ❲v4.Traversal❳: Traveral.getWithFallback', () => { + const traversal_01 = v4.Traversal.fromPrismWithFallback(v4.Prism.fromPredicate((x) => typeof x !== 'string'), ['fallback', 1]) + vi.expect(v4.Traversal.getWithFallback(traversal_01)('hey', ['fallback', 2])).toMatchInlineSnapshot + (` + [ + "fallback", + 1, + ] + `) + }) +}) + + +// const _x = fn.pipe( +// Lens.declare<{ x: number[], y: number[] }>(), +// Lens.prop('x'), +// Traversal.fromLens, +// ) + +// type Polymer = z.infer + +// // TODO: optional properties +// const Polymer = z.object({ +// isCircular: z.boolean(), +// isDoubleStranded: z.boolean(), +// sequence: z.array( +// z.union([ +// z.object({ tag: z.literal('C'), pos: z.number().int() }), +// z.object({ tag: z.literal('G'), pos: z.number().int() }), +// z.object({ tag: z.literal('A'), pos: z.number().int() }), +// z.object({ tag: z.literal('T'), pos: z.number().int() }), +// ]) +// ), +// }) + +// const sds = asTraversal( +// Lens.new( +// (x: { a: number, b: boolean }) => x.a, +// (a) => (x) => ({ ...x, a })) +// )({ +// X: { a: 1, b: false }, +// Y: { a: 2, b: true }, +// }) + +// const sx = Lens.fromShape({ +// a: 1 as const, +// b: 2 as const, +// c: 3 as const, +// }) + +// const sy = Lens.fromKeys( +// 'a', +// 'b', +// 'c', +// )<1 | 2 | 3>() + +// const sz = Lens.fromEntries([ +// ['a', 1 as const], +// ['b', 2 as const], +// ['c', 3 as const], +// ]) \ No newline at end of file diff --git a/packages/schema-zod-adapter/test/util-v4.test.ts b/packages/schema-zod-adapter/test/util-v4.test.ts index 2526cf4c..6b2cb525 100644 --- a/packages/schema-zod-adapter/test/util-v4.test.ts +++ b/packages/schema-zod-adapter/test/util-v4.test.ts @@ -1,6 +1,6 @@ import * as vi from 'vitest' import { v4 } from '@traversable/schema-zod-adapter' -import { z } from 'zod4' +import { z } from 'zod/v4' vi.describe('〖⛳️〗‹‹‹ ❲@traversable/schema-zod-adapter❳', () => { vi.it('〖⛳️〗› ❲v4.typeof❳', () => { diff --git a/packages/schema-zod-adapter/test/with-default-v4.test.ts b/packages/schema-zod-adapter/test/with-default-v4.test.ts new file mode 100644 index 00000000..813a1dc4 --- /dev/null +++ b/packages/schema-zod-adapter/test/with-default-v4.test.ts @@ -0,0 +1,68 @@ +import * as vi from 'vitest' +import { z } from 'zod/v4' +import { v4 } from '@traversable/schema-zod-adapter' + +vi.describe('〖️⛳️〗‹‹‹ ❲@traversable/schema-zod-adapter❳', () => { + vi.it("〖️⛳️〗› ❲v4.withDefault❳", () => { + + const schema = z.object({ + a: z.number(), + b: z.string().readonly(), + c: z.object({ + d: z.array( + z.object({ + e: z.number().max(1), + f: z.boolean() + }) + ).length(10) + }) + }) + + vi.expect(v4.withDefault(schema)).toMatchInlineSnapshot + (` + { + "a": undefined, + "b": undefined, + "c": { + "d": [ + { + "e": undefined, + "f": undefined, + }, + ], + }, + } + `) + + vi.expect(v4.withDefault( + schema, + { number: 0, string: '', boolean: false } + )).toMatchInlineSnapshot + (` + { + "a": 0, + "b": "", + "c": { + "d": [ + { + "e": 0, + "f": false, + }, + ], + }, + } + `) + + vi.expect(v4.withDefault( + z.object({ A: z.record(z.enum(['B', 'C']), z.boolean()) }), + )).toMatchInlineSnapshot + (` + { + "A": { + "B": undefined, + "C": undefined, + }, + } + `) + }) +}) diff --git a/packages/schema/package.json b/packages/schema/package.json index d88820a5..3e4ebfdf 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -49,8 +49,7 @@ "arktype": "catalog:", "lodash.isequal": "catalog:", "valibot": "catalog:", - "zod4": "catalog:", - "zod3": "catalog:" + "zod": "catalog:" }, "peerDependencies": { "@traversable/registry": "workspace:^" diff --git a/packages/schema/src/__generated__/__manifest__.ts b/packages/schema/src/__generated__/__manifest__.ts index 0044a2fe..fcee3707 100644 --- a/packages/schema/src/__generated__/__manifest__.ts +++ b/packages/schema/src/__generated__/__manifest__.ts @@ -45,8 +45,7 @@ export default { "arktype": "catalog:", "lodash.isequal": "catalog:", "valibot": "catalog:", - "zod4": "catalog:", - "zod3": "catalog:" + "zod": "catalog:" }, "peerDependencies": { "@traversable/registry": "workspace:^" diff --git a/packages/schema/src/utils.ts b/packages/schema/src/utils.ts index f6b97948..e099504d 100644 --- a/packages/schema/src/utils.ts +++ b/packages/schema/src/utils.ts @@ -1,6 +1,7 @@ import { __get as get_ } from '@traversable/registry' -import type * as t from './schema.js' +import * as t from './schema.js' +// import type * as t from './schema.js' export { get } diff --git a/packages/schema/test/_types/deep-object--no-baseline.bench.types.ts b/packages/schema/test/_types/deep-object--no-baseline.bench.types.ts index cb4683ac..eca89764 100644 --- a/packages/schema/test/_types/deep-object--no-baseline.bench.types.ts +++ b/packages/schema/test/_types/deep-object--no-baseline.bench.types.ts @@ -1,38 +1,38 @@ import { bench } from "@ark/attest" import { t as core } from "@traversable/schema" -import { z as zod3 } from "zod3" -import { z as zod4 } from "zod4" +import { z as zod3 } from "zod/v3" +import { z as zod4 } from "zod/v4" import { type as arktype } from "arktype" import { Type as typebox } from "@sinclair/typebox" -import * as valibot from "valibot" - -export declare let RESULTS: [ - { - libraryName: "@traversable/schema" - instantiations: 1293 - }, - { - libraryName: "zod@4" - instantiations: 3328 - }, - { - libraryName: "@sinclair/typebox" - instantiations: 14320 - }, - { - libraryName: "arktype" - instantiations: 16235 - }, - { - libraryName: "valibot" - instantiations: 40168 - }, - { - libraryName: "zod@3" - instantiations: 40197 - } -] - +import * as valibot from "valibot" + +export declare let RESULTS: [ + { + libraryName: "@traversable/schema" + instantiations: 1293 + }, + { + libraryName: "zod@4" + instantiations: 3328 + }, + { + libraryName: "@sinclair/typebox" + instantiations: 14320 + }, + { + libraryName: "arktype" + instantiations: 16235 + }, + { + libraryName: "valibot" + instantiations: 40168 + }, + { + libraryName: "zod@3" + instantiations: 40197 + } +] + bench.baseline(() => void {}) bench("@traversable/schema: deep object (no baseline)", () => @@ -61,7 +61,7 @@ bench("@traversable/schema: deep object (no baseline)", () => ), }), ).types - ([1293,"instantiations"]) + ([1293, "instantiations"]) bench("zod@4: deep object (no baseline)", () => zod4.object({ @@ -197,4 +197,4 @@ bench("zod@3: deep object (no baseline)", () => ), }), ).types - ([40197, "instantiations"]) + ([40197, "instantiations"]) diff --git a/packages/schema/test/_types/deep-object.bench.types.ts b/packages/schema/test/_types/deep-object.bench.types.ts index 7efa5e7b..303653ec 100644 --- a/packages/schema/test/_types/deep-object.bench.types.ts +++ b/packages/schema/test/_types/deep-object.bench.types.ts @@ -1,38 +1,38 @@ import { bench } from "@ark/attest" import { t } from "@traversable/schema" -import { z as zod3 } from "zod3" -import { z as zod4 } from "zod4" +import { z as zod3 } from "zod/v3" +import { z as zod4 } from "zod/v4" import { type as arktype } from "arktype" import { Type as typebox } from "@sinclair/typebox" -import * as valibot from "valibot" - -export declare let RESULTS: [ - { - libraryName: "@traversable/schema" - instantiations: 1274 - }, - { - libraryName: "zod@4" - instantiations: 3199 - }, - { - libraryName: "arktype" - instantiations: 12359 - }, - { - libraryName: "@sinclair/typebox" - instantiations: 14294 - }, - { - libraryName: "zod@3" - instantiations: 19292 - }, - { - libraryName: "valibot" - instantiations: 40067 - } -] - +import * as valibot from "valibot" + +export declare let RESULTS: [ + { + libraryName: "@traversable/schema" + instantiations: 1274 + }, + { + libraryName: "zod@4" + instantiations: 3199 + }, + { + libraryName: "arktype" + instantiations: 12359 + }, + { + libraryName: "@sinclair/typebox" + instantiations: 14294 + }, + { + libraryName: "zod@3" + instantiations: 19292 + }, + { + libraryName: "valibot" + instantiations: 40067 + } +] + bench.baseline(() => { t.tuple(t.string) zod3.tuple([zod3.string()]) @@ -203,4 +203,4 @@ bench("valibot: deep object", () => ), }), ).types - ([40067, "instantiations"]) + ([40067, "instantiations"]) diff --git a/packages/schema/test/_types/object--no-baseline.bench.types.ts b/packages/schema/test/_types/object--no-baseline.bench.types.ts index b7684ae4..63a0ed4a 100644 --- a/packages/schema/test/_types/object--no-baseline.bench.types.ts +++ b/packages/schema/test/_types/object--no-baseline.bench.types.ts @@ -1,38 +1,38 @@ import { bench } from "@ark/attest" import { t } from "@traversable/schema" -import { z as zod3 } from "zod3" -import { z as zod4 } from "zod4" +import { z as zod3 } from "zod/v3" +import { z as zod4 } from "zod/v4" import { type as arktype } from "arktype" import { Type as typebox } from "@sinclair/typebox" -import * as valibot from "valibot" - -export declare let RESULTS: [ - { - libraryName: "@traversable/schema" - instantiations: 75 - }, - { - libraryName: "@sinclair/typebox" - instantiations: 159 - }, - { - libraryName: "zod@4" - instantiations: 484 - }, - { - libraryName: "arktype" - instantiations: 5011 - }, - { - libraryName: "valibot" - instantiations: 15973 - }, - { - libraryName: "zod@3" - instantiations: 25146 - } -] - +import * as valibot from "valibot" + +export declare let RESULTS: [ + { + libraryName: "@traversable/schema" + instantiations: 75 + }, + { + libraryName: "@sinclair/typebox" + instantiations: 159 + }, + { + libraryName: "zod@4" + instantiations: 484 + }, + { + libraryName: "arktype" + instantiations: 5011 + }, + { + libraryName: "valibot" + instantiations: 15973 + }, + { + libraryName: "zod@3" + instantiations: 25146 + } +] + bench.baseline(() => void {}) bench("@traversable/schema: object (no baseline)", () => @@ -81,4 +81,4 @@ bench("valibot: object (no baseline)", () => b: valibot.optional(valibot.number()), }), ).types - ([15973, "instantiations"]) + ([15973, "instantiations"]) diff --git a/packages/schema/test/_types/object.bench.types.ts b/packages/schema/test/_types/object.bench.types.ts index 1933b23e..b28779e5 100644 --- a/packages/schema/test/_types/object.bench.types.ts +++ b/packages/schema/test/_types/object.bench.types.ts @@ -1,38 +1,38 @@ import { bench } from "@ark/attest" import { t } from "@traversable/schema" -import { z as zod3 } from "zod3" -import { z as zod4 } from "zod4" +import { z as zod3 } from "zod/v3" +import { z as zod4 } from "zod/v4" import { type as arktype } from "arktype" import { Type as typebox } from "@sinclair/typebox" -import * as valibot from "valibot" - -export declare let RESULTS: [ - { - libraryName: "@traversable/schema" - instantiations: 73 - }, - { - libraryName: "@sinclair/typebox" - instantiations: 150 - }, - { - libraryName: "zod@4" - instantiations: 356 - }, - { - libraryName: "arktype" - instantiations: 1663 - }, - { - libraryName: "zod@3" - instantiations: 4541 - }, - { - libraryName: "valibot" - instantiations: 16091 - } -] - +import * as valibot from "valibot" + +export declare let RESULTS: [ + { + libraryName: "@traversable/schema" + instantiations: 73 + }, + { + libraryName: "@sinclair/typebox" + instantiations: 150 + }, + { + libraryName: "zod@4" + instantiations: 356 + }, + { + libraryName: "arktype" + instantiations: 1663 + }, + { + libraryName: "zod@3" + instantiations: 4541 + }, + { + libraryName: "valibot" + instantiations: 16091 + } +] + bench.baseline(() => { t.tuple(t.string) zod3.tuple([zod3.string()]) @@ -87,4 +87,4 @@ bench("valibot: object", () => b: valibot.optional(valibot.number()), }), ).types - ([16091, "instantiations"]) + ([16091, "instantiations"]) diff --git a/packages/schema/test/schema.test.ts b/packages/schema/test/schema.test.ts index e2c08a15..9406629c 100644 --- a/packages/schema/test/schema.test.ts +++ b/packages/schema/test/schema.test.ts @@ -1,5 +1,5 @@ import * as vi from 'vitest' -import { z } from 'zod3' +import { z } from 'zod/v3' import { fc, test } from '@fast-check/vitest' import type { Functor, TypeError } from '@traversable/registry' diff --git a/packages/schema/test/to-zod-4.test.ts b/packages/schema/test/to-zod-4.test.ts index f2d44d3f..25f6da9d 100644 --- a/packages/schema/test/to-zod-4.test.ts +++ b/packages/schema/test/to-zod-4.test.ts @@ -3,7 +3,7 @@ import { fc, test } from '@fast-check/vitest' import { t, recurse } from '@traversable/schema' import { v4 } from '@traversable/schema-zod-adapter' -import { z } from 'zod4' +import { z } from 'zod/v4' import * as Seed from './seed.js' import * as Zod from './to-zod-4.js' import { SchemaGenerator, getErrorMessage, invalidDataToPaths } from './test-utils.js' diff --git a/packages/schema/test/to-zod-4.ts b/packages/schema/test/to-zod-4.ts index 0bb40b52..f31afba9 100644 --- a/packages/schema/test/to-zod-4.ts +++ b/packages/schema/test/to-zod-4.ts @@ -1,4 +1,4 @@ -import { z } from 'zod4' +import { z } from 'zod/v4' import type { Mut } from '@traversable/registry' import { escape, fn, JsonConstructor, Number_isFinite, Number_isNatural, Number_isSafeInteger, parseKey, URI } from '@traversable/registry' diff --git a/packages/schema/test/types/integer-with-maximum.bench.types.ts b/packages/schema/test/types/integer-with-maximum.bench.types.ts index 4c40c4f7..83f512f7 100644 --- a/packages/schema/test/types/integer-with-maximum.bench.types.ts +++ b/packages/schema/test/types/integer-with-maximum.bench.types.ts @@ -1,7 +1,7 @@ import { bench } from "@ark/attest" import { t } from "@traversable/schema" -import { z as zod3 } from "zod3" -import { z as zod4 } from "zod4" +import { z as zod3 } from "zod/v3" +import { z as zod4 } from "zod/v4" import { type as arktype } from "arktype" import { Type as typebox } from "@sinclair/typebox" import * as valibot from "valibot" @@ -38,29 +38,29 @@ bench.baseline(() => void {}) bench("@traversable/schema: integer with maximum", () => { t.integer.max(0) }).types - ([76,"instantiations"]) + ([76, "instantiations"]) bench("zod@3: integer with maximum", () => { zod3.number().int().max(0) }).types - ([1,"instantiations"]) + ([1, "instantiations"]) bench("zod@4: integer with maximum", () => { zod4.number().int().max(0) }).types - ([357,"instantiations"]) + ([357, "instantiations"]) bench("arktype: integer with maximum", () => { arktype('number.integer <= 0') }).types - ([5014,"instantiations"]) + ([5014, "instantiations"]) bench("typebox: integer with maximum", () => { typebox.Integer({ maximum: 0 }) }).types - ([9,"instantiations"]) + ([9, "instantiations"]) bench("valibot: integer with maximum", () => { valibot.pipe(valibot.number(), valibot.integer(), valibot.maxValue(0)) }).types - ([3017,"instantiations"]) + ([3017, "instantiations"]) diff --git a/packages/schema/test/types/integer-with-minimum.bench.types.ts b/packages/schema/test/types/integer-with-minimum.bench.types.ts index 89fc5c16..c3e90716 100644 --- a/packages/schema/test/types/integer-with-minimum.bench.types.ts +++ b/packages/schema/test/types/integer-with-minimum.bench.types.ts @@ -1,7 +1,7 @@ import { bench } from "@ark/attest" import { t } from "@traversable/schema" -import { z as zod3 } from "zod3" -import { z as zod4 } from "zod4" +import { z as zod3 } from "zod/v3" +import { z as zod4 } from "zod/v4" import { type as arktype } from "arktype" import { Type as typebox } from "@sinclair/typebox" import * as valibot from "valibot" @@ -38,29 +38,29 @@ bench.baseline(() => void {}) bench("@traversable/schema: integer with minimum", () => { t.integer.min(0) }).types - ([76,"instantiations"]) + ([76, "instantiations"]) bench("zod@3: integer with minimum", () => { zod3.number().int().min(0) }).types - ([1,"instantiations"]) + ([1, "instantiations"]) bench("zod@4: integer with minimum", () => { zod4.number().int().min(0) }).types - ([357,"instantiations"]) + ([357, "instantiations"]) bench("arktype: integer with minimum", () => { arktype('number.integer >= 0') }).types - ([5042,"instantiations"]) + ([5042, "instantiations"]) bench("typebox: integer with minimum", () => { typebox.Integer({ minimum: 0 }) }).types - ([9,"instantiations"]) + ([9, "instantiations"]) bench("valibot: integer with minimum", () => { valibot.pipe(valibot.number(), valibot.integer(), valibot.minValue(0)) }).types - ([3017,"instantiations"]) + ([3017, "instantiations"]) diff --git a/packages/schema/test/types/integer.bench.types.ts b/packages/schema/test/types/integer.bench.types.ts index d2fdc1b0..289eb0ab 100644 --- a/packages/schema/test/types/integer.bench.types.ts +++ b/packages/schema/test/types/integer.bench.types.ts @@ -1,7 +1,7 @@ import { bench } from "@ark/attest" import { t } from "@traversable/schema" -import { z as zod3 } from "zod3" -import { z as zod4 } from "zod4" +import { z as zod3 } from "zod/v3" +import { z as zod4 } from "zod/v4" import { type as arktype } from "arktype" import { Type as typebox } from "@sinclair/typebox" import * as valibot from "valibot" @@ -38,29 +38,29 @@ bench.baseline(() => void {}) bench("@traversable/schema: integer", () => { t.integer }).types - ([0,"instantiations"]) + ([0, "instantiations"]) bench("zod@3: integer", () => { zod3.number().int() }).types - ([0,"instantiations"]) + ([0, "instantiations"]) bench("zod@4: integer", () => { zod4.number().int() }).types - ([275,"instantiations"]) + ([275, "instantiations"]) bench("arktype: integer", () => { arktype.keywords.number.integer }).types - ([537,"instantiations"]) + ([537, "instantiations"]) bench("typebox: integer", () => { typebox.Integer() }).types - ([9,"instantiations"]) + ([9, "instantiations"]) bench("valibot: integer", () => { valibot.pipe(valibot.number(), valibot.integer()) }).types - ([2235,"instantiations"]) + ([2235, "instantiations"]) diff --git a/packages/schema/test/types/number-bounds.bench.types.ts b/packages/schema/test/types/number-bounds.bench.types.ts index 229564b7..44684bff 100644 --- a/packages/schema/test/types/number-bounds.bench.types.ts +++ b/packages/schema/test/types/number-bounds.bench.types.ts @@ -1,84 +1,84 @@ import { bench } from "@ark/attest" import { t } from "@traversable/schema" -import { z as zod3 } from "zod3" -import { z as zod4 } from "zod4" +import { z as zod3 } from "zod/v3" +import { z as zod4 } from "zod/v4" import { type as arktype } from "arktype" import { Type as typebox } from "@sinclair/typebox" -import * as valibot from "valibot" - -export declare let RESULTS: [ - { - libraryName: "@traversable/schema" - instantiations: 0 - }, - { - libraryName: "@traversable/schema" - instantiations: 7 - }, - { - libraryName: "@traversable/schema" - instantiations: 48 - }, - { - libraryName: "@traversable/schema" - instantiations: 48 - }, - { - libraryName: "@traversable/schema" - instantiations: 48 - }, - { - libraryName: "@traversable/schema" - instantiations: 48 - }, - { - libraryName: "@traversable/schema" - instantiations: 100 - }, - { - libraryName: "@traversable/schema" - instantiations: 111 - } -] - +import * as valibot from "valibot" + +export declare let RESULTS: [ + { + libraryName: "@traversable/schema" + instantiations: 0 + }, + { + libraryName: "@traversable/schema" + instantiations: 7 + }, + { + libraryName: "@traversable/schema" + instantiations: 48 + }, + { + libraryName: "@traversable/schema" + instantiations: 48 + }, + { + libraryName: "@traversable/schema" + instantiations: 48 + }, + { + libraryName: "@traversable/schema" + instantiations: 48 + }, + { + libraryName: "@traversable/schema" + instantiations: 100 + }, + { + libraryName: "@traversable/schema" + instantiations: 111 + } +] + bench.baseline(() => void {}) bench("@traversable/schema: number", () => { t.number }).types - ([0,"instantiations"]) + ([0, "instantiations"]) bench("@traversable/schema: number w/ min", () => { t.number.min(0) }).types - ([48,"instantiations"]) + ([48, "instantiations"]) bench("@traversable/schema: number w/ max", () => { t.number.max(0) }).types - ([48,"instantiations"]) + ([48, "instantiations"]) bench("@traversable/schema: number w/ lessThan", () => { t.number.lessThan(0) }).types - ([48,"instantiations"]) + ([48, "instantiations"]) bench("@traversable/schema: number w/ moreThan", () => { t.number.moreThan(0) }).types - ([48,"instantiations"]) + ([48, "instantiations"]) bench("@traversable/schema: number w/ between", () => { t.number.between(0, 1) }).types - ([7,"instantiations"]) + ([7, "instantiations"]) bench("@traversable/schema: number w/ min and lessThan", () => { t.number.min(0).lessThan(1) }).types - ([111,"instantiations"]) + ([111, "instantiations"]) bench("@traversable/schema: number w/ max and moreThan", () => { t.number.moreThan(0).max(1) }).types - ([100,"instantiations"]) + ([100, "instantiations"]) diff --git a/packages/schema/test/types/number-with-maximum.bench.types.ts b/packages/schema/test/types/number-with-maximum.bench.types.ts index a768175b..3b2e6fc4 100644 --- a/packages/schema/test/types/number-with-maximum.bench.types.ts +++ b/packages/schema/test/types/number-with-maximum.bench.types.ts @@ -1,7 +1,7 @@ import { bench } from "@ark/attest" import { t } from "@traversable/schema" -import { z as zod3 } from "zod3" -import { z as zod4 } from "zod4" +import { z as zod3 } from "zod/v3" +import { z as zod4 } from "zod/v4" import { type as arktype } from "arktype" import { Type as typebox } from "@sinclair/typebox" import * as valibot from "valibot" @@ -38,29 +38,29 @@ bench.baseline(() => void {}) bench("@traversable/schema: number with maximum", () => { t.number.max(0) }).types - ([48,"instantiations"]) + ([48, "instantiations"]) bench("zod@3: number with maximum", () => { zod3.number().max(0) }).types - ([1,"instantiations"]) + ([1, "instantiations"]) bench("zod@4: number with maximum", () => { zod4.number().max(0) }).types - ([285,"instantiations"]) + ([285, "instantiations"]) bench("arktype: number with maximum", () => { arktype('number <= 0') }).types - ([4853,"instantiations"]) + ([4853, "instantiations"]) bench("typebox: number with maximum", () => { typebox.Number({ maximum: 0 }) }).types - ([9,"instantiations"]) + ([9, "instantiations"]) bench("valibot: number with maximum", () => { valibot.pipe(valibot.number(), valibot.maxValue(0)) }).types - ([2263,"instantiations"]) + ([2263, "instantiations"]) diff --git a/packages/schema/test/types/number-with-minimum.bench.types.ts b/packages/schema/test/types/number-with-minimum.bench.types.ts index a3daf03c..26d56917 100644 --- a/packages/schema/test/types/number-with-minimum.bench.types.ts +++ b/packages/schema/test/types/number-with-minimum.bench.types.ts @@ -1,7 +1,7 @@ import { bench } from "@ark/attest" import { t } from "@traversable/schema" -import { z as zod3 } from "zod3" -import { z as zod4 } from "zod4" +import { z as zod3 } from "zod/v3" +import { z as zod4 } from "zod/v4" import { type as arktype } from "arktype" import { Type as typebox } from "@sinclair/typebox" import * as valibot from "valibot" @@ -38,29 +38,29 @@ bench.baseline(() => void {}) bench("@traversable/schema: number with minimum", () => { t.number.min(0) }).types - ([48,"instantiations"]) + ([48, "instantiations"]) bench("zod@3: number with minimum", () => { zod3.number().min(0) }).types - ([1,"instantiations"]) + ([1, "instantiations"]) bench("zod@4: number with minimum", () => { zod4.number().min(0) }).types - ([285,"instantiations"]) + ([285, "instantiations"]) bench("arktype: number with minimum", () => { arktype('number >= 0') }).types - ([4881,"instantiations"]) + ([4881, "instantiations"]) bench("typebox: number with minimum", () => { typebox.Number({ minimum: 0 }) }).types - ([9,"instantiations"]) + ([9, "instantiations"]) bench("valibot: number with minimum", () => { valibot.pipe(valibot.number(), valibot.minValue(0)) }).types - ([2263,"instantiations"]) + ([2263, "instantiations"]) diff --git a/packages/schema/test/types/number.bench.types.ts b/packages/schema/test/types/number.bench.types.ts index 505a45e6..1d7796a0 100644 --- a/packages/schema/test/types/number.bench.types.ts +++ b/packages/schema/test/types/number.bench.types.ts @@ -1,7 +1,7 @@ import { bench } from "@ark/attest" import { t } from "@traversable/schema" -import { z as zod3 } from "zod3" -import { z as zod4 } from "zod4" +import { z as zod3 } from "zod/v3" +import { z as zod4 } from "zod/v4" import { type as arktype } from "arktype" import { Type as typebox } from "@sinclair/typebox" import * as valibot from "valibot" @@ -38,29 +38,29 @@ bench.baseline(() => void {}) bench("@traversable/schema: number", () => { t.number }).types - ([0,"instantiations"]) + ([0, "instantiations"]) bench("zod@3: number", () => { zod3.number() }).types - ([0,"instantiations"]) + ([0, "instantiations"]) bench("zod@4: number", () => { zod4.number() }).types - ([0,"instantiations"]) + ([0, "instantiations"]) bench("arktype: number", () => { arktype.keywords.number }).types - ([475,"instantiations"]) + ([475, "instantiations"]) bench("typebox: number", () => { typebox.Number() }).types - ([9,"instantiations"]) + ([9, "instantiations"]) bench("valibot: number", () => { valibot.number() }).types - ([0,"instantiations"]) + ([0, "instantiations"]) diff --git a/packages/schema/test/types/string-with-maximum.bench.types.ts b/packages/schema/test/types/string-with-maximum.bench.types.ts index 1f051dc8..37a056b9 100644 --- a/packages/schema/test/types/string-with-maximum.bench.types.ts +++ b/packages/schema/test/types/string-with-maximum.bench.types.ts @@ -1,7 +1,7 @@ import { bench } from "@ark/attest" import { t } from "@traversable/schema" -import { z as zod3 } from "zod3" -import { z as zod4 } from "zod4" +import { z as zod3 } from "zod/v3" +import { z as zod4 } from "zod/v4" import { type as arktype } from "arktype" import { Type as typebox } from "@sinclair/typebox" import * as valibot from "valibot" @@ -38,29 +38,29 @@ bench.baseline(() => void {}) bench("@traversable/schema: string with maximum", () => { t.string.max(0) }).types - ([65,"instantiations"]) + ([65, "instantiations"]) bench("zod@3: string with maximum", () => { zod3.string().max(0) }).types - ([0,"instantiations"]) + ([0, "instantiations"]) bench("zod@4: string with maximum", () => { zod4.string().max(0) }).types - ([283,"instantiations"]) + ([283, "instantiations"]) bench("arktype: string with maximum", () => { arktype('string <= 0') }).types - ([4853,"instantiations"]) + ([4853, "instantiations"]) bench("typebox: string with maximum", () => { typebox.String({ maximum: 0 }) }).types - ([9,"instantiations"]) + ([9, "instantiations"]) bench("valibot: string with maximum", () => { valibot.pipe(valibot.string(), valibot.maxLength(0)) }).types - ([2262,"instantiations"]) + ([2262, "instantiations"]) diff --git a/packages/schema/test/types/string-with-minimum.bench.types.ts b/packages/schema/test/types/string-with-minimum.bench.types.ts index 867b50fd..8c34897d 100644 --- a/packages/schema/test/types/string-with-minimum.bench.types.ts +++ b/packages/schema/test/types/string-with-minimum.bench.types.ts @@ -1,7 +1,7 @@ import { bench } from "@ark/attest" import { t } from "@traversable/schema" -import { z as zod3 } from "zod3" -import { z as zod4 } from "zod4" +import { z as zod3 } from "zod/v3" +import { z as zod4 } from "zod/v4" import { type as arktype } from "arktype" import { Type as typebox } from "@sinclair/typebox" import * as valibot from "valibot" @@ -38,29 +38,29 @@ bench.baseline(() => void {}) bench("@traversable/schema: string with minimum", () => { t.string.min(0) }).types - ([65,"instantiations"]) + ([65, "instantiations"]) bench("zod@3: string with minimum", () => { zod3.string().min(0) }).types - ([0,"instantiations"]) + ([0, "instantiations"]) bench("zod@4: string with minimum", () => { zod4.string().min(0) }).types - ([283,"instantiations"]) + ([283, "instantiations"]) bench("arktype: string with minimum", () => { arktype('string >= 0') }).types - ([4881,"instantiations"]) + ([4881, "instantiations"]) bench("typebox: string with minimum", () => { typebox.String({ minimum: 0 }) }).types - ([9,"instantiations"]) + ([9, "instantiations"]) bench("valibot: string with minimum", () => { valibot.pipe(valibot.string(), valibot.minLength(0)) }).types - ([2262,"instantiations"]) + ([2262, "instantiations"]) diff --git a/packages/schema/test/types/string.bench.types.ts b/packages/schema/test/types/string.bench.types.ts index 88d95bc3..f647bae3 100644 --- a/packages/schema/test/types/string.bench.types.ts +++ b/packages/schema/test/types/string.bench.types.ts @@ -1,7 +1,7 @@ import { bench } from "@ark/attest" import { t } from "@traversable/schema" -import { z as zod3 } from "zod3" -import { z as zod4 } from "zod4" +import { z as zod3 } from "zod/v3" +import { z as zod4 } from "zod/v4" import { type as arktype } from "arktype" import { Type as typebox } from "@sinclair/typebox" import * as valibot from "valibot" @@ -38,29 +38,29 @@ bench.baseline(() => void {}) bench("@traversable/schema: string", () => { t.string }).types - ([0,"instantiations"]) + ([0, "instantiations"]) bench("zod@3: string", () => { zod3.string() }).types - ([0,"instantiations"]) + ([0, "instantiations"]) bench("zod@4: string", () => { zod4.string() }).types - ([0,"instantiations"]) + ([0, "instantiations"]) bench("arktype: string", () => { arktype.string }).types - ([175,"instantiations"]) + ([175, "instantiations"]) bench("typebox: string", () => { typebox.String() }).types - ([9,"instantiations"]) + ([9, "instantiations"]) bench("valibot: string", () => { valibot.string() }).types - ([0,"instantiations"]) + ([0, "instantiations"]) diff --git a/packages/schema/test/types/tuple--empty.bench.types.ts b/packages/schema/test/types/tuple--empty.bench.types.ts index bcd5f255..b140e82c 100644 --- a/packages/schema/test/types/tuple--empty.bench.types.ts +++ b/packages/schema/test/types/tuple--empty.bench.types.ts @@ -1,7 +1,7 @@ import { bench } from "@ark/attest" import { t } from "@traversable/schema" -import { z as zod3 } from "zod3" -import { z as zod4 } from "zod4" +import { z as zod3 } from "zod/v3" +import { z as zod4 } from "zod/v4" import { type as arktype } from "arktype" import { Type as typebox } from "@sinclair/typebox" import * as valibot from "valibot" @@ -38,29 +38,29 @@ bench.baseline(() => void {}) bench("@traversable/schema: tuple (empty)", () => { const x = t.tuple() }).types - ([287,"instantiations"]) + ([287, "instantiations"]) bench("zod@3: tuple (empty)", () => { const x = zod3.tuple([]) }).types - ([9,"instantiations"]) + ([9, "instantiations"]) bench("zod@4: tuple (empty)", () => { const x = zod4.tuple([]) }).types - ([21,"instantiations"]) + ([21, "instantiations"]) bench("arktype: tuple (empty)", () => { const x = arktype([]) }).types - ([4563,"instantiations"]) + ([4563, "instantiations"]) bench("typebox: tuple (empty)", () => { const x = typebox.Tuple([]) }).types - ([23,"instantiations"]) + ([23, "instantiations"]) bench("valibot: tuple (empty)", () => { const x = valibot.tuple([]) }).types - ([28,"instantiations"]) + ([28, "instantiations"]) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fcf357f9..318f08cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -75,12 +75,9 @@ catalogs: vitest: specifier: ^3.0.4 version: 3.1.1 - zod3: - specifier: npm:zod@3 - version: 3.24.2 - zod4: - specifier: npm:zod@4.0.0-beta.20250505T195954 - version: 4.0.0-beta.20250505T195954 + zod: + specifier: 3.25.0-beta.20250515T215617 + version: 3.25.0-beta.20250515T215617 importers: @@ -337,12 +334,9 @@ importers: valibot: specifier: 'catalog:' version: 1.0.0(typescript@5.8.3) - zod3: - specifier: 'catalog:' - version: zod@3.24.2 - zod4: + zod: specifier: 'catalog:' - version: zod@4.0.0-beta.20250505T195954 + version: 3.25.0-beta.20250515T215617 publishDirectory: dist packages/schema-compiler: @@ -440,9 +434,6 @@ importers: fast-check: specifier: 3 - 4 version: 3.23.2 - zod: - specifier: 3 - 4 - version: 3.24.2 devDependencies: '@traversable/json': specifier: workspace:^ @@ -450,12 +441,9 @@ importers: '@traversable/registry': specifier: workspace:^ version: link:../registry/dist - zod3: - specifier: 'catalog:' - version: zod@3.24.2 - zod4: + zod: specifier: 'catalog:' - version: zod@4.0.0-beta.20250505T195954 + version: 3.25.0-beta.20250515T215617 publishDirectory: dist packages: @@ -1430,9 +1418,6 @@ packages: '@web3-storage/multipart-parser@1.0.0': resolution: {integrity: sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==} - '@zod/core@0.11.6': - resolution: {integrity: sha512-03Bv82fFSfjDAvMfdHHdGSS6SOJs0iCcJlWJv1kJHRtoTT02hZpyip/2Lk6oo4l4FtjuwTrsEQTwg/LD8I7dJA==} - '@zxing/text-encoding@0.9.0': resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} @@ -3168,8 +3153,8 @@ packages: zod@3.24.2: resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} - zod@4.0.0-beta.20250505T195954: - resolution: {integrity: sha512-iB8WvxkobVIXMARvQu20fKvbS7mUTiYRpcD8OQV1xjRhxO0EEpYIRJBk6yfBzHAHEdOSDh3SxDITr5Eajr2vtg==} + zod@3.25.0-beta.20250515T215617: + resolution: {integrity: sha512-RjKlUKHkr00q8D3Qdg23TBDjv+GQsRH2YgpwGCY7YqpGdXLxtaZTDdr3hwq/lJrjhGc8Xir+YTEkmMEmu0+dvw==} snapshots: @@ -4267,8 +4252,6 @@ snapshots: '@web3-storage/multipart-parser@1.0.0': {} - '@zod/core@0.11.6': {} - '@zxing/text-encoding@0.9.0': optional: true @@ -5944,6 +5927,4 @@ snapshots: zod@3.24.2: {} - zod@4.0.0-beta.20250505T195954: - dependencies: - '@zod/core': 0.11.6 + zod@3.25.0-beta.20250515T215617: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3cf65cdc..d44c1977 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -25,7 +25,6 @@ catalog: '@sinclair/typebox': ^0.34.33 arktype: ^2.1.20 valibot: 1.0.0 - 'zod4': 'npm:zod@4.0.0-beta.20250505T195954' - 'zod3': 'npm:zod@3' + zod: 3.25.0-beta.20250515T215617 '@types/lodash.isequal': ^4.5.8 'lodash.isequal': ^4.5.0