You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					242 lines
				
				9.7 KiB
			
		
		
			
		
	
	
					242 lines
				
				9.7 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								"use strict";
							 | 
						||
| 
								 | 
							
								Object.defineProperty(exports, "__esModule", { value: true });
							 | 
						||
| 
								 | 
							
								exports.resolveSchema = exports.getCompilingSchema = exports.resolveRef = exports.compileSchema = exports.SchemaEnv = void 0;
							 | 
						||
| 
								 | 
							
								const codegen_1 = require("./codegen");
							 | 
						||
| 
								 | 
							
								const validation_error_1 = require("../runtime/validation_error");
							 | 
						||
| 
								 | 
							
								const names_1 = require("./names");
							 | 
						||
| 
								 | 
							
								const resolve_1 = require("./resolve");
							 | 
						||
| 
								 | 
							
								const util_1 = require("./util");
							 | 
						||
| 
								 | 
							
								const validate_1 = require("./validate");
							 | 
						||
| 
								 | 
							
								class SchemaEnv {
							 | 
						||
| 
								 | 
							
								    constructor(env) {
							 | 
						||
| 
								 | 
							
								        var _a;
							 | 
						||
| 
								 | 
							
								        this.refs = {};
							 | 
						||
| 
								 | 
							
								        this.dynamicAnchors = {};
							 | 
						||
| 
								 | 
							
								        let schema;
							 | 
						||
| 
								 | 
							
								        if (typeof env.schema == "object")
							 | 
						||
| 
								 | 
							
								            schema = env.schema;
							 | 
						||
| 
								 | 
							
								        this.schema = env.schema;
							 | 
						||
| 
								 | 
							
								        this.schemaId = env.schemaId;
							 | 
						||
| 
								 | 
							
								        this.root = env.root || this;
							 | 
						||
| 
								 | 
							
								        this.baseId = (_a = env.baseId) !== null && _a !== void 0 ? _a : (0, resolve_1.normalizeId)(schema === null || schema === void 0 ? void 0 : schema[env.schemaId || "$id"]);
							 | 
						||
| 
								 | 
							
								        this.schemaPath = env.schemaPath;
							 | 
						||
| 
								 | 
							
								        this.localRefs = env.localRefs;
							 | 
						||
| 
								 | 
							
								        this.meta = env.meta;
							 | 
						||
| 
								 | 
							
								        this.$async = schema === null || schema === void 0 ? void 0 : schema.$async;
							 | 
						||
| 
								 | 
							
								        this.refs = {};
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								exports.SchemaEnv = SchemaEnv;
							 | 
						||
| 
								 | 
							
								// let codeSize = 0
							 | 
						||
| 
								 | 
							
								// let nodeCount = 0
							 | 
						||
| 
								 | 
							
								// Compiles schema in SchemaEnv
							 | 
						||
| 
								 | 
							
								function compileSchema(sch) {
							 | 
						||
| 
								 | 
							
								    // TODO refactor - remove compilations
							 | 
						||
| 
								 | 
							
								    const _sch = getCompilingSchema.call(this, sch);
							 | 
						||
| 
								 | 
							
								    if (_sch)
							 | 
						||
| 
								 | 
							
								        return _sch;
							 | 
						||
| 
								 | 
							
								    const rootId = (0, resolve_1.getFullPath)(this.opts.uriResolver, sch.root.baseId); // TODO if getFullPath removed 1 tests fails
							 | 
						||
| 
								 | 
							
								    const { es5, lines } = this.opts.code;
							 | 
						||
| 
								 | 
							
								    const { ownProperties } = this.opts;
							 | 
						||
| 
								 | 
							
								    const gen = new codegen_1.CodeGen(this.scope, { es5, lines, ownProperties });
							 | 
						||
| 
								 | 
							
								    let _ValidationError;
							 | 
						||
| 
								 | 
							
								    if (sch.$async) {
							 | 
						||
| 
								 | 
							
								        _ValidationError = gen.scopeValue("Error", {
							 | 
						||
| 
								 | 
							
								            ref: validation_error_1.default,
							 | 
						||
| 
								 | 
							
								            code: (0, codegen_1._) `require("ajv/dist/runtime/validation_error").default`,
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    const validateName = gen.scopeName("validate");
							 | 
						||
| 
								 | 
							
								    sch.validateName = validateName;
							 | 
						||
| 
								 | 
							
								    const schemaCxt = {
							 | 
						||
| 
								 | 
							
								        gen,
							 | 
						||
| 
								 | 
							
								        allErrors: this.opts.allErrors,
							 | 
						||
| 
								 | 
							
								        data: names_1.default.data,
							 | 
						||
| 
								 | 
							
								        parentData: names_1.default.parentData,
							 | 
						||
| 
								 | 
							
								        parentDataProperty: names_1.default.parentDataProperty,
							 | 
						||
| 
								 | 
							
								        dataNames: [names_1.default.data],
							 | 
						||
| 
								 | 
							
								        dataPathArr: [codegen_1.nil],
							 | 
						||
| 
								 | 
							
								        dataLevel: 0,
							 | 
						||
| 
								 | 
							
								        dataTypes: [],
							 | 
						||
| 
								 | 
							
								        definedProperties: new Set(),
							 | 
						||
| 
								 | 
							
								        topSchemaRef: gen.scopeValue("schema", this.opts.code.source === true
							 | 
						||
| 
								 | 
							
								            ? { ref: sch.schema, code: (0, codegen_1.stringify)(sch.schema) }
							 | 
						||
| 
								 | 
							
								            : { ref: sch.schema }),
							 | 
						||
| 
								 | 
							
								        validateName,
							 | 
						||
| 
								 | 
							
								        ValidationError: _ValidationError,
							 | 
						||
| 
								 | 
							
								        schema: sch.schema,
							 | 
						||
| 
								 | 
							
								        schemaEnv: sch,
							 | 
						||
| 
								 | 
							
								        rootId,
							 | 
						||
| 
								 | 
							
								        baseId: sch.baseId || rootId,
							 | 
						||
| 
								 | 
							
								        schemaPath: codegen_1.nil,
							 | 
						||
| 
								 | 
							
								        errSchemaPath: sch.schemaPath || (this.opts.jtd ? "" : "#"),
							 | 
						||
| 
								 | 
							
								        errorPath: (0, codegen_1._) `""`,
							 | 
						||
| 
								 | 
							
								        opts: this.opts,
							 | 
						||
| 
								 | 
							
								        self: this,
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    let sourceCode;
							 | 
						||
| 
								 | 
							
								    try {
							 | 
						||
| 
								 | 
							
								        this._compilations.add(sch);
							 | 
						||
| 
								 | 
							
								        (0, validate_1.validateFunctionCode)(schemaCxt);
							 | 
						||
| 
								 | 
							
								        gen.optimize(this.opts.code.optimize);
							 | 
						||
| 
								 | 
							
								        // gen.optimize(1)
							 | 
						||
| 
								 | 
							
								        const validateCode = gen.toString();
							 | 
						||
| 
								 | 
							
								        sourceCode = `${gen.scopeRefs(names_1.default.scope)}return ${validateCode}`;
							 | 
						||
| 
								 | 
							
								        // console.log((codeSize += sourceCode.length), (nodeCount += gen.nodeCount))
							 | 
						||
| 
								 | 
							
								        if (this.opts.code.process)
							 | 
						||
| 
								 | 
							
								            sourceCode = this.opts.code.process(sourceCode, sch);
							 | 
						||
| 
								 | 
							
								        // console.log("\n\n\n *** \n", sourceCode)
							 | 
						||
| 
								 | 
							
								        const makeValidate = new Function(`${names_1.default.self}`, `${names_1.default.scope}`, sourceCode);
							 | 
						||
| 
								 | 
							
								        const validate = makeValidate(this, this.scope.get());
							 | 
						||
| 
								 | 
							
								        this.scope.value(validateName, { ref: validate });
							 | 
						||
| 
								 | 
							
								        validate.errors = null;
							 | 
						||
| 
								 | 
							
								        validate.schema = sch.schema;
							 | 
						||
| 
								 | 
							
								        validate.schemaEnv = sch;
							 | 
						||
| 
								 | 
							
								        if (sch.$async)
							 | 
						||
| 
								 | 
							
								            validate.$async = true;
							 | 
						||
| 
								 | 
							
								        if (this.opts.code.source === true) {
							 | 
						||
| 
								 | 
							
								            validate.source = { validateName, validateCode, scopeValues: gen._values };
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (this.opts.unevaluated) {
							 | 
						||
| 
								 | 
							
								            const { props, items } = schemaCxt;
							 | 
						||
| 
								 | 
							
								            validate.evaluated = {
							 | 
						||
| 
								 | 
							
								                props: props instanceof codegen_1.Name ? undefined : props,
							 | 
						||
| 
								 | 
							
								                items: items instanceof codegen_1.Name ? undefined : items,
							 | 
						||
| 
								 | 
							
								                dynamicProps: props instanceof codegen_1.Name,
							 | 
						||
| 
								 | 
							
								                dynamicItems: items instanceof codegen_1.Name,
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								            if (validate.source)
							 | 
						||
| 
								 | 
							
								                validate.source.evaluated = (0, codegen_1.stringify)(validate.evaluated);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        sch.validate = validate;
							 | 
						||
| 
								 | 
							
								        return sch;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    catch (e) {
							 | 
						||
| 
								 | 
							
								        delete sch.validate;
							 | 
						||
| 
								 | 
							
								        delete sch.validateName;
							 | 
						||
| 
								 | 
							
								        if (sourceCode)
							 | 
						||
| 
								 | 
							
								            this.logger.error("Error compiling schema, function code:", sourceCode);
							 | 
						||
| 
								 | 
							
								        // console.log("\n\n\n *** \n", sourceCode, this.opts)
							 | 
						||
| 
								 | 
							
								        throw e;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    finally {
							 | 
						||
| 
								 | 
							
								        this._compilations.delete(sch);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								exports.compileSchema = compileSchema;
							 | 
						||
| 
								 | 
							
								function resolveRef(root, baseId, ref) {
							 | 
						||
| 
								 | 
							
								    var _a;
							 | 
						||
| 
								 | 
							
								    ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref);
							 | 
						||
| 
								 | 
							
								    const schOrFunc = root.refs[ref];
							 | 
						||
| 
								 | 
							
								    if (schOrFunc)
							 | 
						||
| 
								 | 
							
								        return schOrFunc;
							 | 
						||
| 
								 | 
							
								    let _sch = resolve.call(this, root, ref);
							 | 
						||
| 
								 | 
							
								    if (_sch === undefined) {
							 | 
						||
| 
								 | 
							
								        const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref]; // TODO maybe localRefs should hold SchemaEnv
							 | 
						||
| 
								 | 
							
								        const { schemaId } = this.opts;
							 | 
						||
| 
								 | 
							
								        if (schema)
							 | 
						||
| 
								 | 
							
								            _sch = new SchemaEnv({ schema, schemaId, root, baseId });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (_sch === undefined)
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    return (root.refs[ref] = inlineOrCompile.call(this, _sch));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								exports.resolveRef = resolveRef;
							 | 
						||
| 
								 | 
							
								function inlineOrCompile(sch) {
							 | 
						||
| 
								 | 
							
								    if ((0, resolve_1.inlineRef)(sch.schema, this.opts.inlineRefs))
							 | 
						||
| 
								 | 
							
								        return sch.schema;
							 | 
						||
| 
								 | 
							
								    return sch.validate ? sch : compileSchema.call(this, sch);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Index of schema compilation in the currently compiled list
							 | 
						||
| 
								 | 
							
								function getCompilingSchema(schEnv) {
							 | 
						||
| 
								 | 
							
								    for (const sch of this._compilations) {
							 | 
						||
| 
								 | 
							
								        if (sameSchemaEnv(sch, schEnv))
							 | 
						||
| 
								 | 
							
								            return sch;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								exports.getCompilingSchema = getCompilingSchema;
							 | 
						||
| 
								 | 
							
								function sameSchemaEnv(s1, s2) {
							 | 
						||
| 
								 | 
							
								    return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// resolve and compile the references ($ref)
							 | 
						||
| 
								 | 
							
								// TODO returns AnySchemaObject (if the schema can be inlined) or validation function
							 | 
						||
| 
								 | 
							
								function resolve(root, // information about the root schema for the current schema
							 | 
						||
| 
								 | 
							
								ref // reference to resolve
							 | 
						||
| 
								 | 
							
								) {
							 | 
						||
| 
								 | 
							
								    let sch;
							 | 
						||
| 
								 | 
							
								    while (typeof (sch = this.refs[ref]) == "string")
							 | 
						||
| 
								 | 
							
								        ref = sch;
							 | 
						||
| 
								 | 
							
								    return sch || this.schemas[ref] || resolveSchema.call(this, root, ref);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								// Resolve schema, its root and baseId
							 | 
						||
| 
								 | 
							
								function resolveSchema(root, // root object with properties schema, refs TODO below SchemaEnv is assigned to it
							 | 
						||
| 
								 | 
							
								ref // reference to resolve
							 | 
						||
| 
								 | 
							
								) {
							 | 
						||
| 
								 | 
							
								    const p = this.opts.uriResolver.parse(ref);
							 | 
						||
| 
								 | 
							
								    const refPath = (0, resolve_1._getFullPath)(this.opts.uriResolver, p);
							 | 
						||
| 
								 | 
							
								    let baseId = (0, resolve_1.getFullPath)(this.opts.uriResolver, root.baseId, undefined);
							 | 
						||
| 
								 | 
							
								    // TODO `Object.keys(root.schema).length > 0` should not be needed - but removing breaks 2 tests
							 | 
						||
| 
								 | 
							
								    if (Object.keys(root.schema).length > 0 && refPath === baseId) {
							 | 
						||
| 
								 | 
							
								        return getJsonPointer.call(this, p, root);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    const id = (0, resolve_1.normalizeId)(refPath);
							 | 
						||
| 
								 | 
							
								    const schOrRef = this.refs[id] || this.schemas[id];
							 | 
						||
| 
								 | 
							
								    if (typeof schOrRef == "string") {
							 | 
						||
| 
								 | 
							
								        const sch = resolveSchema.call(this, root, schOrRef);
							 | 
						||
| 
								 | 
							
								        if (typeof (sch === null || sch === void 0 ? void 0 : sch.schema) !== "object")
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        return getJsonPointer.call(this, p, sch);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (typeof (schOrRef === null || schOrRef === void 0 ? void 0 : schOrRef.schema) !== "object")
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    if (!schOrRef.validate)
							 | 
						||
| 
								 | 
							
								        compileSchema.call(this, schOrRef);
							 | 
						||
| 
								 | 
							
								    if (id === (0, resolve_1.normalizeId)(ref)) {
							 | 
						||
| 
								 | 
							
								        const { schema } = schOrRef;
							 | 
						||
| 
								 | 
							
								        const { schemaId } = this.opts;
							 | 
						||
| 
								 | 
							
								        const schId = schema[schemaId];
							 | 
						||
| 
								 | 
							
								        if (schId)
							 | 
						||
| 
								 | 
							
								            baseId = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, schId);
							 | 
						||
| 
								 | 
							
								        return new SchemaEnv({ schema, schemaId, root, baseId });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return getJsonPointer.call(this, p, schOrRef);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								exports.resolveSchema = resolveSchema;
							 | 
						||
| 
								 | 
							
								const PREVENT_SCOPE_CHANGE = new Set([
							 | 
						||
| 
								 | 
							
								    "properties",
							 | 
						||
| 
								 | 
							
								    "patternProperties",
							 | 
						||
| 
								 | 
							
								    "enum",
							 | 
						||
| 
								 | 
							
								    "dependencies",
							 | 
						||
| 
								 | 
							
								    "definitions",
							 | 
						||
| 
								 | 
							
								]);
							 | 
						||
| 
								 | 
							
								function getJsonPointer(parsedRef, { baseId, schema, root }) {
							 | 
						||
| 
								 | 
							
								    var _a;
							 | 
						||
| 
								 | 
							
								    if (((_a = parsedRef.fragment) === null || _a === void 0 ? void 0 : _a[0]) !== "/")
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    for (const part of parsedRef.fragment.slice(1).split("/")) {
							 | 
						||
| 
								 | 
							
								        if (typeof schema === "boolean")
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        const partSchema = schema[(0, util_1.unescapeFragment)(part)];
							 | 
						||
| 
								 | 
							
								        if (partSchema === undefined)
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        schema = partSchema;
							 | 
						||
| 
								 | 
							
								        // TODO PREVENT_SCOPE_CHANGE could be defined in keyword def?
							 | 
						||
| 
								 | 
							
								        const schId = typeof schema === "object" && schema[this.opts.schemaId];
							 | 
						||
| 
								 | 
							
								        if (!PREVENT_SCOPE_CHANGE.has(part) && schId) {
							 | 
						||
| 
								 | 
							
								            baseId = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, schId);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    let env;
							 | 
						||
| 
								 | 
							
								    if (typeof schema != "boolean" && schema.$ref && !(0, util_1.schemaHasRulesButRef)(schema, this.RULES)) {
							 | 
						||
| 
								 | 
							
								        const $ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, schema.$ref);
							 | 
						||
| 
								 | 
							
								        env = resolveSchema.call(this, root, $ref);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // even though resolution failed we need to return SchemaEnv to throw exception
							 | 
						||
| 
								 | 
							
								    // so that compileAsync loads missing schema.
							 | 
						||
| 
								 | 
							
								    const { schemaId } = this.opts;
							 | 
						||
| 
								 | 
							
								    env = env || new SchemaEnv({ schema, schemaId, root, baseId });
							 | 
						||
| 
								 | 
							
								    if (env.schema !== env.root.schema)
							 | 
						||
| 
								 | 
							
								        return env;
							 | 
						||
| 
								 | 
							
								    return undefined;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								//# sourceMappingURL=index.js.map
							 |