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