import { IsAny, IsAnyOrUnknown, Or as LogicalOr } from "@re-do/utils" import { ParseConfig, typeDefProxy, ValidationErrorMessage, shallowCycleError, generateRequiredCycleError, createParser } from "./common.js" import { Root } from "../root.js" import { Fragment } from "./fragment.js" export namespace Resolution { export type Definition< TypeSet, Def extends keyof TypeSet & string = keyof TypeSet & string > = Def export type Validate< TypeName extends keyof TypeSet & string, Root, TypeSet > = TypeSet[TypeName] extends ValidationErrorMessage ? TypeSet[TypeName] : Root export type Parse< TypeName extends keyof TypeSet & string, TypeSet, Options extends ParseConfig > = IsAny extends true ? TypeSet : TypeSet[TypeName] extends ValidationErrorMessage ? unknown : TypeName extends keyof Options["seen"] ? Options["onCycle"] extends never ? ParseResolvedNonCyclicDefinition : ParseResolvedCyclicDefinition : ParseResolvedNonCyclicDefinition export type ParseResolvedCyclicDefinition< TypeName extends keyof TypeSet, TypeSet, Options extends ParseConfig > = Root.Parse< Options["onCycle"], Omit & { cyclic: TypeSet[TypeName] }, { onCycle: Options["deepOnCycle"] extends true ? Options["onCycle"] : never seen: {} onResolve: Options["onResolve"] deepOnCycle: Options["deepOnCycle"] } > export type ParseResolvedNonCyclicDefinition< TypeName extends keyof TypeSet, TypeSet, Options extends ParseConfig > = LogicalOr< Options["onResolve"] extends never ? true : false, TypeName extends "resolved" ? true : false > extends true ? Root.Parse< TypeSet[TypeName], TypeSet, Options & { seen: { [K in TypeName]: true } } > : Root.Parse< Options["onResolve"], Omit & { resolved: TypeSet[TypeName] }, Options & { seen: { [K in TypeName]: true } } > export const type = typeDefProxy as string export const parse = createParser( { type, parent: () => Fragment.parse, matches: (def, ctx) => def in ctx.typeSet, components: (def, ctx) => { /** * Keep track of definitions we've seen since last resolving to an object or built-in. * If we encounter the same definition twice, we're dealing with a shallow cyclic typeSet * like {user: "person", person: "user"}. **/ if (ctx.shallowSeen.includes(def)) { throw new Error( shallowCycleError({ def, ctx }) ) } return { resolve: () => Root.parse(ctx.typeSet[def], { ...ctx, seen: [...ctx.seen, def], shallowSeen: [...ctx.shallowSeen, def] }) } } }, { allows: ({ components: { resolve } }, valueType, opts) => resolve().allows(valueType, opts), generate: ({ components: { resolve }, ctx, def }, opts) => { if (ctx.seen.includes(def)) { if (opts.onRequiredCycle) { return opts.onRequiredCycle } throw new Error(generateRequiredCycleError({ def, ctx })) } return resolve().generate(opts) }, references: ({ def }, opts) => [def] } ) export const delegate = parse as any as string }