index.js 8.9 KB

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