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
						
					
					
				| "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
 |