[6a3a178] | 1 | "use strict";
|
---|
| 2 | Object.defineProperty(exports, "__esModule", { value: true });
|
---|
| 3 | exports.resolveSchema = exports.getCompilingSchema = exports.resolveRef = exports.compileSchema = exports.SchemaEnv = void 0;
|
---|
| 4 | const codegen_1 = require("./codegen");
|
---|
| 5 | const validation_error_1 = require("../runtime/validation_error");
|
---|
| 6 | const names_1 = require("./names");
|
---|
| 7 | const resolve_1 = require("./resolve");
|
---|
| 8 | const util_1 = require("./util");
|
---|
| 9 | const validate_1 = require("./validate");
|
---|
| 10 | const URI = require("uri-js");
|
---|
| 11 | class SchemaEnv {
|
---|
| 12 | constructor(env) {
|
---|
| 13 | var _a;
|
---|
| 14 | this.refs = {};
|
---|
| 15 | this.dynamicAnchors = {};
|
---|
| 16 | let schema;
|
---|
| 17 | if (typeof env.schema == "object")
|
---|
| 18 | schema = env.schema;
|
---|
| 19 | this.schema = env.schema;
|
---|
| 20 | this.schemaId = env.schemaId;
|
---|
| 21 | this.root = env.root || this;
|
---|
| 22 | this.baseId = (_a = env.baseId) !== null && _a !== void 0 ? _a : resolve_1.normalizeId(schema === null || schema === void 0 ? void 0 : schema[env.schemaId || "$id"]);
|
---|
| 23 | this.schemaPath = env.schemaPath;
|
---|
| 24 | this.localRefs = env.localRefs;
|
---|
| 25 | this.meta = env.meta;
|
---|
| 26 | this.$async = schema === null || schema === void 0 ? void 0 : schema.$async;
|
---|
| 27 | this.refs = {};
|
---|
| 28 | }
|
---|
| 29 | }
|
---|
| 30 | exports.SchemaEnv = SchemaEnv;
|
---|
| 31 | // let codeSize = 0
|
---|
| 32 | // let nodeCount = 0
|
---|
| 33 | // Compiles schema in SchemaEnv
|
---|
| 34 | function compileSchema(sch) {
|
---|
| 35 | // TODO refactor - remove compilations
|
---|
| 36 | const _sch = getCompilingSchema.call(this, sch);
|
---|
| 37 | if (_sch)
|
---|
| 38 | return _sch;
|
---|
| 39 | const rootId = resolve_1.getFullPath(sch.root.baseId); // TODO if getFullPath removed 1 tests fails
|
---|
| 40 | const { es5, lines } = this.opts.code;
|
---|
| 41 | const { ownProperties } = this.opts;
|
---|
| 42 | const gen = new codegen_1.CodeGen(this.scope, { es5, lines, ownProperties });
|
---|
| 43 | let _ValidationError;
|
---|
| 44 | if (sch.$async) {
|
---|
| 45 | _ValidationError = gen.scopeValue("Error", {
|
---|
| 46 | ref: validation_error_1.default,
|
---|
| 47 | code: codegen_1._ `require("ajv/dist/runtime/validation_error").default`,
|
---|
| 48 | });
|
---|
| 49 | }
|
---|
| 50 | const validateName = gen.scopeName("validate");
|
---|
| 51 | sch.validateName = validateName;
|
---|
| 52 | const schemaCxt = {
|
---|
| 53 | gen,
|
---|
| 54 | allErrors: this.opts.allErrors,
|
---|
| 55 | data: names_1.default.data,
|
---|
| 56 | parentData: names_1.default.parentData,
|
---|
| 57 | parentDataProperty: names_1.default.parentDataProperty,
|
---|
| 58 | dataNames: [names_1.default.data],
|
---|
| 59 | dataPathArr: [codegen_1.nil],
|
---|
| 60 | dataLevel: 0,
|
---|
| 61 | dataTypes: [],
|
---|
| 62 | definedProperties: new Set(),
|
---|
| 63 | topSchemaRef: gen.scopeValue("schema", this.opts.code.source === true
|
---|
| 64 | ? { ref: sch.schema, code: codegen_1.stringify(sch.schema) }
|
---|
| 65 | : { ref: sch.schema }),
|
---|
| 66 | validateName,
|
---|
| 67 | ValidationError: _ValidationError,
|
---|
| 68 | schema: sch.schema,
|
---|
| 69 | schemaEnv: sch,
|
---|
| 70 | rootId,
|
---|
| 71 | baseId: sch.baseId || rootId,
|
---|
| 72 | schemaPath: codegen_1.nil,
|
---|
| 73 | errSchemaPath: sch.schemaPath || (this.opts.jtd ? "" : "#"),
|
---|
| 74 | errorPath: codegen_1._ `""`,
|
---|
| 75 | opts: this.opts,
|
---|
| 76 | self: this,
|
---|
| 77 | };
|
---|
| 78 | let sourceCode;
|
---|
| 79 | try {
|
---|
| 80 | this._compilations.add(sch);
|
---|
| 81 | validate_1.validateFunctionCode(schemaCxt);
|
---|
| 82 | gen.optimize(this.opts.code.optimize);
|
---|
| 83 | // gen.optimize(1)
|
---|
| 84 | const validateCode = gen.toString();
|
---|
| 85 | sourceCode = `${gen.scopeRefs(names_1.default.scope)}return ${validateCode}`;
|
---|
| 86 | // console.log((codeSize += sourceCode.length), (nodeCount += gen.nodeCount))
|
---|
| 87 | if (this.opts.code.process)
|
---|
| 88 | sourceCode = this.opts.code.process(sourceCode, sch);
|
---|
| 89 | // console.log("\n\n\n *** \n", sourceCode)
|
---|
| 90 | const makeValidate = new Function(`${names_1.default.self}`, `${names_1.default.scope}`, sourceCode);
|
---|
| 91 | const validate = makeValidate(this, this.scope.get());
|
---|
| 92 | this.scope.value(validateName, { ref: validate });
|
---|
| 93 | validate.errors = null;
|
---|
| 94 | validate.schema = sch.schema;
|
---|
| 95 | validate.schemaEnv = sch;
|
---|
| 96 | if (sch.$async)
|
---|
| 97 | validate.$async = true;
|
---|
| 98 | if (this.opts.code.source === true) {
|
---|
| 99 | validate.source = { validateName, validateCode, scopeValues: gen._values };
|
---|
| 100 | }
|
---|
| 101 | if (this.opts.unevaluated) {
|
---|
| 102 | const { props, items } = schemaCxt;
|
---|
| 103 | validate.evaluated = {
|
---|
| 104 | props: props instanceof codegen_1.Name ? undefined : props,
|
---|
| 105 | items: items instanceof codegen_1.Name ? undefined : items,
|
---|
| 106 | dynamicProps: props instanceof codegen_1.Name,
|
---|
| 107 | dynamicItems: items instanceof codegen_1.Name,
|
---|
| 108 | };
|
---|
| 109 | if (validate.source)
|
---|
| 110 | validate.source.evaluated = codegen_1.stringify(validate.evaluated);
|
---|
| 111 | }
|
---|
| 112 | sch.validate = validate;
|
---|
| 113 | return sch;
|
---|
| 114 | }
|
---|
| 115 | catch (e) {
|
---|
| 116 | delete sch.validate;
|
---|
| 117 | delete sch.validateName;
|
---|
| 118 | if (sourceCode)
|
---|
| 119 | this.logger.error("Error compiling schema, function code:", sourceCode);
|
---|
| 120 | // console.log("\n\n\n *** \n", sourceCode, this.opts)
|
---|
| 121 | throw e;
|
---|
| 122 | }
|
---|
| 123 | finally {
|
---|
| 124 | this._compilations.delete(sch);
|
---|
| 125 | }
|
---|
| 126 | }
|
---|
| 127 | exports.compileSchema = compileSchema;
|
---|
| 128 | function resolveRef(root, baseId, ref) {
|
---|
| 129 | var _a;
|
---|
| 130 | ref = resolve_1.resolveUrl(baseId, ref);
|
---|
| 131 | const schOrFunc = root.refs[ref];
|
---|
| 132 | if (schOrFunc)
|
---|
| 133 | return schOrFunc;
|
---|
| 134 | let _sch = resolve.call(this, root, ref);
|
---|
| 135 | if (_sch === undefined) {
|
---|
| 136 | const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref]; // TODO maybe localRefs should hold SchemaEnv
|
---|
| 137 | const { schemaId } = this.opts;
|
---|
| 138 | if (schema)
|
---|
| 139 | _sch = new SchemaEnv({ schema, schemaId, root, baseId });
|
---|
| 140 | }
|
---|
| 141 | if (_sch === undefined)
|
---|
| 142 | return;
|
---|
| 143 | return (root.refs[ref] = inlineOrCompile.call(this, _sch));
|
---|
| 144 | }
|
---|
| 145 | exports.resolveRef = resolveRef;
|
---|
| 146 | function inlineOrCompile(sch) {
|
---|
| 147 | if (resolve_1.inlineRef(sch.schema, this.opts.inlineRefs))
|
---|
| 148 | return sch.schema;
|
---|
| 149 | return sch.validate ? sch : compileSchema.call(this, sch);
|
---|
| 150 | }
|
---|
| 151 | // Index of schema compilation in the currently compiled list
|
---|
| 152 | function getCompilingSchema(schEnv) {
|
---|
| 153 | for (const sch of this._compilations) {
|
---|
| 154 | if (sameSchemaEnv(sch, schEnv))
|
---|
| 155 | return sch;
|
---|
| 156 | }
|
---|
| 157 | }
|
---|
| 158 | exports.getCompilingSchema = getCompilingSchema;
|
---|
| 159 | function sameSchemaEnv(s1, s2) {
|
---|
| 160 | return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
---|
| 161 | }
|
---|
| 162 | // resolve and compile the references ($ref)
|
---|
| 163 | // TODO returns AnySchemaObject (if the schema can be inlined) or validation function
|
---|
| 164 | function resolve(root, // information about the root schema for the current schema
|
---|
| 165 | ref // reference to resolve
|
---|
| 166 | ) {
|
---|
| 167 | let sch;
|
---|
| 168 | while (typeof (sch = this.refs[ref]) == "string")
|
---|
| 169 | ref = sch;
|
---|
| 170 | return sch || this.schemas[ref] || resolveSchema.call(this, root, ref);
|
---|
| 171 | }
|
---|
| 172 | // Resolve schema, its root and baseId
|
---|
| 173 | function resolveSchema(root, // root object with properties schema, refs TODO below SchemaEnv is assigned to it
|
---|
| 174 | ref // reference to resolve
|
---|
| 175 | ) {
|
---|
| 176 | const p = URI.parse(ref);
|
---|
| 177 | const refPath = resolve_1._getFullPath(p);
|
---|
| 178 | let baseId = resolve_1.getFullPath(root.baseId);
|
---|
| 179 | // TODO `Object.keys(root.schema).length > 0` should not be needed - but removing breaks 2 tests
|
---|
| 180 | if (Object.keys(root.schema).length > 0 && refPath === baseId) {
|
---|
| 181 | return getJsonPointer.call(this, p, root);
|
---|
| 182 | }
|
---|
| 183 | const id = resolve_1.normalizeId(refPath);
|
---|
| 184 | const schOrRef = this.refs[id] || this.schemas[id];
|
---|
| 185 | if (typeof schOrRef == "string") {
|
---|
| 186 | const sch = resolveSchema.call(this, root, schOrRef);
|
---|
| 187 | if (typeof (sch === null || sch === void 0 ? void 0 : sch.schema) !== "object")
|
---|
| 188 | return;
|
---|
| 189 | return getJsonPointer.call(this, p, sch);
|
---|
| 190 | }
|
---|
| 191 | if (typeof (schOrRef === null || schOrRef === void 0 ? void 0 : schOrRef.schema) !== "object")
|
---|
| 192 | return;
|
---|
| 193 | if (!schOrRef.validate)
|
---|
| 194 | compileSchema.call(this, schOrRef);
|
---|
| 195 | if (id === resolve_1.normalizeId(ref)) {
|
---|
| 196 | const { schema } = schOrRef;
|
---|
| 197 | const { schemaId } = this.opts;
|
---|
| 198 | const schId = schema[schemaId];
|
---|
| 199 | if (schId)
|
---|
| 200 | baseId = resolve_1.resolveUrl(baseId, schId);
|
---|
| 201 | return new SchemaEnv({ schema, schemaId, root, baseId });
|
---|
| 202 | }
|
---|
| 203 | return getJsonPointer.call(this, p, schOrRef);
|
---|
| 204 | }
|
---|
| 205 | exports.resolveSchema = resolveSchema;
|
---|
| 206 | const PREVENT_SCOPE_CHANGE = new Set([
|
---|
| 207 | "properties",
|
---|
| 208 | "patternProperties",
|
---|
| 209 | "enum",
|
---|
| 210 | "dependencies",
|
---|
| 211 | "definitions",
|
---|
| 212 | ]);
|
---|
| 213 | function getJsonPointer(parsedRef, { baseId, schema, root }) {
|
---|
| 214 | var _a;
|
---|
| 215 | if (((_a = parsedRef.fragment) === null || _a === void 0 ? void 0 : _a[0]) !== "/")
|
---|
| 216 | return;
|
---|
| 217 | for (const part of parsedRef.fragment.slice(1).split("/")) {
|
---|
| 218 | if (typeof schema == "boolean")
|
---|
| 219 | return;
|
---|
| 220 | schema = schema[util_1.unescapeFragment(part)];
|
---|
| 221 | if (schema === undefined)
|
---|
| 222 | return;
|
---|
| 223 | // TODO PREVENT_SCOPE_CHANGE could be defined in keyword def?
|
---|
| 224 | const schId = typeof schema == "object" && schema[this.opts.schemaId];
|
---|
| 225 | if (!PREVENT_SCOPE_CHANGE.has(part) && schId) {
|
---|
| 226 | baseId = resolve_1.resolveUrl(baseId, schId);
|
---|
| 227 | }
|
---|
| 228 | }
|
---|
| 229 | let env;
|
---|
| 230 | if (typeof schema != "boolean" && schema.$ref && !util_1.schemaHasRulesButRef(schema, this.RULES)) {
|
---|
| 231 | const $ref = resolve_1.resolveUrl(baseId, schema.$ref);
|
---|
| 232 | env = resolveSchema.call(this, root, $ref);
|
---|
| 233 | }
|
---|
| 234 | // even though resolution failed we need to return SchemaEnv to throw exception
|
---|
| 235 | // so that compileAsync loads missing schema.
|
---|
| 236 | const { schemaId } = this.opts;
|
---|
| 237 | env = env || new SchemaEnv({ schema, schemaId, root, baseId });
|
---|
| 238 | if (env.schema !== env.root.schema)
|
---|
| 239 | return env;
|
---|
| 240 | return undefined;
|
---|
| 241 | }
|
---|
| 242 | //# sourceMappingURL=index.js.map |
---|