limit.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. /** @typedef {import("ajv").default} Ajv */
  7. /** @typedef {import("ajv").Code} Code */
  8. /** @typedef {import("ajv").Name} Name */
  9. /** @typedef {import("ajv").KeywordErrorDefinition} KeywordErrorDefinition */
  10. /**
  11. * @param {Ajv} ajv
  12. * @returns {Ajv}
  13. */
  14. function addLimitKeyword(ajv) {
  15. // eslint-disable-next-line global-require
  16. const {
  17. _,
  18. str,
  19. KeywordCxt,
  20. nil,
  21. Name
  22. } = require("ajv");
  23. /**
  24. * @param {Code | Name} x
  25. * @returns {Code | Name}
  26. */
  27. function par(x) {
  28. return x instanceof Name ? x : _`(${x})`;
  29. }
  30. /**
  31. * @param {Code} op
  32. * @returns {function(Code, Code): Code}
  33. */
  34. function mappend(op) {
  35. return (x, y) => x === nil ? y : y === nil ? x : _`${par(x)} ${op} ${par(y)}`;
  36. }
  37. const orCode = mappend(_`||`);
  38. // boolean OR (||) expression with the passed arguments
  39. /**
  40. * @param {...Code} args
  41. * @returns {Code}
  42. */
  43. function or(...args) {
  44. return args.reduce(orCode);
  45. }
  46. /**
  47. * @param {string | number} key
  48. * @returns {Code}
  49. */
  50. function getProperty(key) {
  51. return _`[${key}]`;
  52. }
  53. const keywords = {
  54. formatMaximum: {
  55. okStr: "<=",
  56. ok: _`<=`,
  57. fail: _`>`
  58. },
  59. formatMinimum: {
  60. okStr: ">=",
  61. ok: _`>=`,
  62. fail: _`<`
  63. },
  64. formatExclusiveMaximum: {
  65. okStr: "<",
  66. ok: _`<`,
  67. fail: _`>=`
  68. },
  69. formatExclusiveMinimum: {
  70. okStr: ">",
  71. ok: _`>`,
  72. fail: _`<=`
  73. }
  74. };
  75. /** @type {KeywordErrorDefinition} */
  76. const error = {
  77. message: ({
  78. keyword,
  79. schemaCode
  80. }) => str`should be ${keywords[(/** @type {keyof typeof keywords} */keyword)].okStr} ${schemaCode}`,
  81. params: ({
  82. keyword,
  83. schemaCode
  84. }) => _`{comparison: ${keywords[(/** @type {keyof typeof keywords} */keyword)].okStr}, limit: ${schemaCode}}`
  85. };
  86. for (const keyword of Object.keys(keywords)) {
  87. ajv.addKeyword({
  88. keyword,
  89. type: "string",
  90. schemaType: keyword.startsWith("formatExclusive") ? ["string", "boolean"] : ["string", "number"],
  91. $data: true,
  92. error,
  93. code(cxt) {
  94. const {
  95. gen,
  96. data,
  97. schemaCode,
  98. keyword,
  99. it
  100. } = cxt;
  101. const {
  102. opts,
  103. self
  104. } = it;
  105. if (!opts.validateFormats) return;
  106. const fCxt = new KeywordCxt(it, /** @type {any} */
  107. self.RULES.all.format.definition, "format");
  108. /**
  109. * @param {Name} fmt
  110. * @returns {Code}
  111. */
  112. function compareCode(fmt) {
  113. return _`${fmt}.compare(${data}, ${schemaCode}) ${keywords[(/** @type {keyof typeof keywords} */keyword)].fail} 0`;
  114. }
  115. function validate$DataFormat() {
  116. const fmts = gen.scopeValue("formats", {
  117. ref: self.formats,
  118. code: opts.code.formats
  119. });
  120. const fmt = gen.const("fmt", _`${fmts}[${fCxt.schemaCode}]`);
  121. cxt.fail$data(or(_`typeof ${fmt} != "object"`, _`${fmt} instanceof RegExp`, _`typeof ${fmt}.compare != "function"`, compareCode(fmt)));
  122. }
  123. function validateFormat() {
  124. const format = fCxt.schema;
  125. const fmtDef = self.formats[format];
  126. if (!fmtDef || fmtDef === true) {
  127. return;
  128. }
  129. if (typeof fmtDef !== "object" || fmtDef instanceof RegExp || typeof fmtDef.compare !== "function") {
  130. throw new Error(`"${keyword}": format "${format}" does not define "compare" function`);
  131. }
  132. const fmt = gen.scopeValue("formats", {
  133. key: format,
  134. ref: fmtDef,
  135. code: opts.code.formats ? _`${opts.code.formats}${getProperty(format)}` : undefined
  136. });
  137. cxt.fail$data(compareCode(fmt));
  138. }
  139. if (fCxt.$data) {
  140. validate$DataFormat();
  141. } else {
  142. validateFormat();
  143. }
  144. },
  145. dependencies: ["format"]
  146. });
  147. }
  148. return ajv;
  149. }
  150. var _default = exports.default = addLimitKeyword;