juniper
Version:
ESM JSON Schema builder for static Typescript inference.
260 lines (259 loc) • 8.83 kB
JavaScript
import { maxInt } from '../lib/constants.js';
import { AbstractSchema } from '../lib/schema.js';
import { mergeAllOf } from '../lib/utils.js';
import { MergeSchema } from './merge.js';
import { NeverSchema } from './never.js';
const dependentRequiredSym = Symbol('dependentRequired');
const dependentSchemasSym = Symbol('dependentSchemas');
const patternPropertiesSym = Symbol('patternProperties');
const ignoreUnevaluatedProperties = Symbol('ignoreUnevaluatedProperties');
const trueSchema = new MergeSchema();
const falseSchema = new NeverSchema();
export class ObjectSchema extends AbstractSchema {
#additionalProperties;
#dependentRequired;
#dependentSchemas;
#patternProperties;
#properties;
#maxProperties;
#minProperties;
#required;
#unevaluatedProperties;
schemaType = 'object';
constructor(options = {}){
super(options);
this.#additionalProperties = options.additionalProperties ?? null;
this.#maxProperties = options.maxProperties ?? Number.POSITIVE_INFINITY;
this.#minProperties = options.minProperties ?? 0;
this.#properties = {};
if (options.properties) {
for (const [key, val] of Object.entries(options.properties)){
if (val === true) {
this.#properties[key] = trueSchema;
} else if (val === false) {
this.#properties[key] = falseSchema;
} else {
this.#properties[key] = val;
}
}
}
this.#required = options.required ?? [];
this.#unevaluatedProperties = options.unevaluatedProperties ?? ignoreUnevaluatedProperties;
this.#dependentRequired = options[dependentRequiredSym] ?? {};
this.#dependentSchemas = options[dependentSchemasSym] ?? {};
this.#patternProperties = options[patternPropertiesSym] ?? {};
}
static create(options) {
return new ObjectSchema(options);
}
properties(...properties) {
return this.clone({
properties: {
...this.#properties,
...properties[0]
}
});
}
maxProperties(maxProperties) {
return this.clone({
maxProperties
});
}
minProperties(minProperties) {
return this.clone({
minProperties
});
}
required(required) {
return this.clone({
required: [
...this.#required,
...[
required
].flat()
]
});
}
additionalProperties(additionalProperties) {
return this.clone({
additionalProperties
});
}
patternProperties(pattern, schema) {
let patternSchema;
if (schema === true) {
patternSchema = trueSchema;
} else if (schema === false) {
patternSchema = falseSchema;
} else {
patternSchema = schema;
}
return this.clone({
[patternPropertiesSym]: {
...this.#patternProperties,
[pattern]: patternSchema
}
});
}
dependentRequired(key, dependents) {
return this.clone({
[dependentRequiredSym]: {
...this.#dependentRequired,
[key]: [
...dependents
]
}
});
}
dependentSchemas(key, schema) {
return this.clone({
[dependentSchemasSym]: {
...this.#dependentSchemas,
[key]: schema
}
});
}
unevaluatedProperties(unevaluatedProperties) {
return this.clone({
unevaluatedProperties
});
}
getCloneParams() {
return {
...super.getCloneParams(),
additionalProperties: this.#additionalProperties,
minProperties: this.#minProperties,
maxProperties: this.#maxProperties,
properties: {
...this.#properties
},
required: [
...this.#required
],
unevaluatedProperties: this.#unevaluatedProperties,
[dependentRequiredSym]: {
...this.#dependentRequired
},
[dependentSchemasSym]: {
...this.#dependentSchemas
},
[patternPropertiesSym]: {
...this.#patternProperties
}
};
}
static getDefaultValues(params) {
return {
...super.getDefaultValues(params),
minProperties: 0,
maxProperties: maxInt
};
}
toSchema(params) {
const base = super.toSchema(params);
if (this.#additionalProperties !== null) {
base.additionalProperties = typeof this.#additionalProperties === 'boolean' ? this.#additionalProperties : ObjectSchema.getSchema(this.#additionalProperties, params);
}
if (this.#minProperties > 0) {
base.minProperties = this.#minProperties;
}
if (this.#maxProperties < Number.POSITIVE_INFINITY) {
base.maxProperties = this.#maxProperties;
}
const schemaToProperty = (schema)=>{
if (schema === trueSchema) {
return true;
}
if (schema === falseSchema) {
return false;
}
return ObjectSchema.getSchema(schema, params);
};
const propertyEntries = Object.entries(this.#properties);
if (propertyEntries.length > 0) {
const properties = {};
for (const [key, val] of propertyEntries){
properties[key] = schemaToProperty(val);
}
base.properties = properties;
}
if (this.#required.length > 0) {
base.required = [
...new Set(this.#required)
];
}
const dependentRequiredEntries = Object.entries(this.#dependentRequired);
const compositionParams = {
...params,
composition: {
type: this.schemaType,
nullable: false
}
};
const dependentSchemasEntries = Object.entries(this.#dependentSchemas).map(([key, schema])=>[
key,
schema.getChildSchema(compositionParams)
]);
if (params.openApi30) {
const [anyOf, ...anyOfs] = [
...dependentRequiredEntries.map(([key, dependent])=>({
anyOf: [
{
not: {
required: [
key
]
}
},
{
required: dependent
}
]
})),
...dependentSchemasEntries.map(([key, dependent])=>({
anyOf: [
{
not: {
required: [
key
]
}
},
dependent
]
}))
];
if (anyOf) {
if (base.anyOf) {
anyOfs.unshift(anyOf);
} else {
Object.assign(base, anyOf);
}
mergeAllOf(base, anyOfs);
}
} else {
if (dependentRequiredEntries.length > 0) {
base.dependentRequired = this.#dependentRequired;
}
if (dependentSchemasEntries.length > 0) {
base.dependentSchemas = {};
for (const [key, schema] of dependentSchemasEntries){
base.dependentSchemas[key] = schema;
}
}
if (this.#unevaluatedProperties !== ignoreUnevaluatedProperties) {
base.unevaluatedProperties = this.#unevaluatedProperties;
}
const patternEntries = Object.entries(this.#patternProperties);
if (patternEntries.length > 0) {
const patternProperties = {};
for (const [key, val] of patternEntries){
patternProperties[key] = schemaToProperty(val);
}
base.patternProperties = patternProperties;
}
}
return base;
}
}
//# sourceMappingURL=object.js.map