UNPKG

normul

Version:

Normul is a tiny TypeScript/JavaScript library for data normalization and transformation

104 lines 3.66 kB
import { Schema } from '../Schema.js'; import { ObjectSchema } from './ObjectSchema.js'; export class UnionSchema extends Schema { schemas; constructor(schemas) { super(); this.schemas = schemas; } _normalize(input, ctx) { const find = (input) => { const candidates = []; for (const schema of this.schemas) { const innerCtx = { issues: [], path: [], }; const data = this.invokeNormalize(schema, input, innerCtx); if (innerCtx.issues.filter(issue => issue.level !== 'info').length === 0) { return { data, }; } candidates.push({ data, ctx: innerCtx, }); } let maxDepth = 0; for (const candidate of candidates) { maxDepth = Math.max(maxDepth, candidate.ctx.path.length); } let remaining = candidates; for (let level = 0; level <= maxDepth; level++) { const counts = remaining.map((candidate) => { return candidate.ctx.issues.filter((issue) => { return (issue.level !== 'info' && issue.path.length === level); }).length; }); const minCount = Math.min(...counts); remaining = remaining.filter((_, i) => counts[i] === minCount); if (remaining.length === 1) { this.makeIssue({ ctx, message: 'Found the most suitable schema in union', level: 'info', }); return remaining[0]; } } this.makeIssue({ ctx, message: 'Multiple most suitable schemas found in union, taking the first one', level: 'warn', }); return remaining[0]; }; const candidate = find(input); if (candidate.ctx) { ctx.issues.push(...candidate.ctx.issues.map(issue => ({ ...issue, path: [...ctx.path, ...issue.path], }))); } return candidate.data; } cloneArgs() { return [this.schemas]; } } export class DiscriminatedUnionSchema extends UnionSchema { discriminator; schemas; constructor(discriminator, schemas) { super(schemas); this.discriminator = discriminator; this.schemas = schemas; } _normalize(input, ctx) { for (const schema of this.schemas) { if (schema instanceof ObjectSchema) { const pickedSchema = schema.pick([this.discriminator]); const innerCtx = { issues: [], path: [], }; this.invokeNormalize(pickedSchema, input, innerCtx); if (innerCtx.issues.filter(issue => issue.level !== 'info').length === 0) { return this.invokeNormalize(schema, input, ctx); } } } this.makeIssue({ ctx, message: 'No matching schema found in discriminated union, falling back to regular union', level: 'warn', }); return super._normalize(input, ctx); } cloneArgs() { return [this.discriminator, this.schemas]; } } //# sourceMappingURL=UnionSchema.js.map