UNPKG

juniper

Version:

ESM JSON Schema builder for static Typescript inference.

260 lines (259 loc) 8.83 kB
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