index.js 9.8 KB

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