normul
Version:
Normul is a tiny TypeScript/JavaScript library for data normalization and transformation
104 lines • 3.66 kB
JavaScript
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