import type { Schema, Infer } from '../base.ts'; import { createIssue, failure, createParseMethods, SCHEMA_KIND } from '../base.ts'; import { optional } from '../utils/optional.ts'; import { nullable } from '../utils/nullable.ts'; // eslint-disable-next-line @typescript-eslint/no-explicit-any type InferUnion[]> = Infer; /** * Schema for union types (one of multiple possible schemas). * Validates against each schema until one succeeds. * * @template T - Array of schema types * * @example * ```typescript * const idSchema = s.union(s.string(), s.number()); * idSchema.parse('abc123'); // 'abc123' * idSchema.parse(123); // 123 * * const roleSchema = s.union( * s.literal('admin'), * s.literal('user'), * s.literal('guest') * ); * ``` */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export class UnionSchema[]> implements Schema, InferUnion> { readonly [SCHEMA_KIND] = 'UnionSchema'; description?: string; private parseMethods = createParseMethods>(); constructor(private schemas: T) {} readonly '~standard' = { version: 1 as const, vendor: 'agentuity', validate: (value: unknown) => { const allIssues: ReturnType[] = []; for (const schema of this.schemas) { const result = schema['~standard'].validate(value); // Only support synchronous validation for now if (result instanceof Promise) { throw new Error('Async validation not supported'); } if (!result.issues) { // eslint-disable-next-line @typescript-eslint/no-explicit-any return result as any; } allIssues.push(...result.issues); } return failure([ createIssue( `Value did not match any of the union types (${allIssues.length} validation errors)` ), ]); }, types: undefined as unknown as { input: InferUnion; output: InferUnion }, }; describe(description: string): this { this.description = description; return this; } optional() { return optional(this); } nullable() { return nullable(this); } parse = this.parseMethods.parse; safeParse = this.parseMethods.safeParse; } /** * Create a union schema (one of multiple possible types). * * @param schemas - Variable number of schemas to union * * @example * ```typescript * const idSchema = s.union(s.string(), s.number()); * * const roleSchema = s.union( * s.literal('admin'), * s.literal('user'), * s.literal('guest') * ); * ``` */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export function union[]>(...schemas: T): UnionSchema { return new UnionSchema(schemas); }