import type { ParseContext, SchemaNode } from "../types.js"; import { BaseSchema } from "./base.js"; import { ISSUE_CODES } from "../issue-codes.js"; import { describeType } from "../util.js"; export class UnionSchema< T extends BaseSchema[] = BaseSchema[], > extends BaseSchema { private _variants: T; constructor(variants: [...T]) { super(); this._variants = variants as T; } _validate(input: unknown, ctx: ParseContext): unknown { for (const variant of this._variants) { const innerCtx: ParseContext = { path: [...ctx.path], issues: [], // Propagate recursion depth and circular-reference tracking so the // depth guard is not reset (and bypassed) at each union boundary. definitions: ctx.definitions, seen: ctx.seen, depth: ctx.depth, sensitiveMode: ctx.sensitiveMode, sensitiveTransform: ctx.sensitiveTransform, sensitiveCache: ctx.sensitiveCache, }; const result = variant._runPipeline(input, innerCtx); if (innerCtx.issues.length === 0) { return result; } } const variantKinds = this._variants.map((v) => v._toNode().kind); ctx.issues.push({ code: ISSUE_CODES.INVALID_UNION, message: `Input did not match any variant of the union`, path: [...ctx.path], expected: variantKinds.join(" | "), received: describeType(input), }); return undefined; } _children(): BaseSchema[] { return this._variants; } _toNode(): SchemaNode { const node = { kind: "union" as const, variants: this._variants.map((v) => v._toNode()), }; this._addDefault(node as unknown as SchemaNode); return node as unknown as SchemaNode; } }