json-schema-describes-subset
Version:
Tools for static JSON schema analysis, including functions to determine if one schema describes a subset of another or if a schema describes the empty set or to convert a schema to its disjunctive normal form (DNF).
109 lines • 4.04 kB
JavaScript
import isString from 'lodash/isString.js';
import { AtomicSchemaObject, AllOfSchema, NotSchema, } from '../atomic-schema/index.js';
import { TypeAtomicSchema } from './type.js';
import { negateIntegerMaximum, negateIntegerMinimum, } from '../utils/negate-integer-min-max/index.js';
import { getUniqueNegatedConstSchemasFromNegatedAtomicSchemasByConstructor } from './negated-const-helpers.js';
export class PatternAtomicSchema extends AtomicSchemaObject {
pattern;
constructor(pattern) {
super();
this.pattern = pattern;
}
negate() {
return new AllOfSchema([
new NotSchema(this),
/* actually redundant, but makes checking for contradictions easier */
new TypeAtomicSchema('string'),
]);
}
toJSONSchema() {
return { pattern: this.pattern };
}
}
export class MinLengthAtomicSchema extends AtomicSchemaObject {
minLength;
constructor(minLength) {
super();
this.minLength = minLength;
}
negate() {
return new AllOfSchema([
new MaxLengthAtomicSchema(negateIntegerMinimum(this.minLength)),
new TypeAtomicSchema('string'),
]);
}
toJSONSchema() {
return { minLength: this.minLength };
}
}
export class MaxLengthAtomicSchema extends AtomicSchemaObject {
maxLength;
constructor(maxLength) {
super();
this.maxLength = maxLength;
}
negate() {
return new AllOfSchema([
new MinLengthAtomicSchema(negateIntegerMaximum(this.maxLength)),
new TypeAtomicSchema('string'),
]);
}
toJSONSchema() {
return { maxLength: this.maxLength };
}
}
export const stringExtraction = {
extract: ({ schema }) => {
const schemas = [];
if (typeof schema.pattern === 'string') {
schemas.push(new PatternAtomicSchema(schema.pattern));
}
if (typeof schema.minLength === 'number') {
schemas.push(new MinLengthAtomicSchema(schema.minLength));
}
if (typeof schema.maxLength === 'number') {
schemas.push(new MaxLengthAtomicSchema(schema.maxLength));
}
if (schemas.length === 0) {
return true;
}
return new AllOfSchema(schemas);
},
};
export const stringSimplification = {
appliesToJSONSchemaType: 'string',
mergeableKeywords: ['minLength', 'maxLength'],
simplify({ schemaDescribesEmptySet, atomicSchemasByConstructor, negatedAtomicSchemasByConstructor, validateConst, }) {
const minLength = Math.ceil(Math.max(0, ...atomicSchemasByConstructor
.get(MinLengthAtomicSchema)
.map(({ minLength }) => minLength)));
const maxLength = Math.floor(Math.min(...atomicSchemasByConstructor
.get(MaxLengthAtomicSchema)
.map(({ maxLength }) => maxLength)));
if (minLength > maxLength) {
return false;
}
if (maxLength <= 0) {
return validateConst('');
}
const patternAtomicSchemas = atomicSchemasByConstructor.get(PatternAtomicSchema);
const negatedPatternAtomicSchemas = negatedAtomicSchemasByConstructor.get(PatternAtomicSchema);
for (const { pattern } of patternAtomicSchemas) {
if (negatedPatternAtomicSchemas.some((notSchema) => pattern === notSchema.pattern)) {
return false;
}
}
return {
...(minLength > 0 ? { minLength } : {}),
...(maxLength < Infinity ? { maxLength } : {}),
allOf: [
...patternAtomicSchemas.map(({ pattern }) => ({ pattern })),
...negatedPatternAtomicSchemas.map(({ pattern }) => ({
not: { pattern },
})),
...getUniqueNegatedConstSchemasFromNegatedAtomicSchemasByConstructor(schemaDescribesEmptySet, negatedAtomicSchemasByConstructor, isString),
],
};
},
};
//# sourceMappingURL=string.js.map