UNPKG

9.69 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.resolveSchema = exports.getCompilingSchema = exports.resolveRef = exports.compileSchema = exports.SchemaEnv = void 0;
4const codegen_1 = require("./codegen");
5const validation_error_1 = require("../runtime/validation_error");
6const names_1 = require("./names");
7const resolve_1 = require("./resolve");
8const util_1 = require("./util");
9const validate_1 = require("./validate");
10const URI = require("uri-js");
11class 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}
30exports.SchemaEnv = SchemaEnv;
31// let codeSize = 0
32// let nodeCount = 0
33// Compiles schema in SchemaEnv
34function 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}
127exports.compileSchema = compileSchema;
128function 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}
145exports.resolveRef = resolveRef;
146function 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
152function getCompilingSchema(schEnv) {
153 for (const sch of this._compilations) {
154 if (sameSchemaEnv(sch, schEnv))
155 return sch;
156 }
157}
158exports.getCompilingSchema = getCompilingSchema;
159function 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
164function resolve(root, // information about the root schema for the current schema
165ref // 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
173function resolveSchema(root, // root object with properties schema, refs TODO below SchemaEnv is assigned to it
174ref // 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}
205exports.resolveSchema = resolveSchema;
206const PREVENT_SCOPE_CHANGE = new Set([
207 "properties",
208 "patternProperties",
209 "enum",
210 "dependencies",
211 "definitions",
212]);
213function 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
\No newline at end of file