158 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			158 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| "use strict";
 | |
| 
 | |
| Object.defineProperty(exports, "__esModule", {
 | |
|   value: true
 | |
| });
 | |
| exports.default = void 0;
 | |
| /** @typedef {import("ajv").default} Ajv */
 | |
| /** @typedef {import("ajv").Code} Code */
 | |
| /** @typedef {import("ajv").Name} Name */
 | |
| /** @typedef {import("ajv").KeywordErrorDefinition} KeywordErrorDefinition */
 | |
| 
 | |
| /**
 | |
|  * @param {Ajv} ajv
 | |
|  * @returns {Ajv}
 | |
|  */
 | |
| function addLimitKeyword(ajv) {
 | |
|   // eslint-disable-next-line global-require
 | |
|   const {
 | |
|     _,
 | |
|     str,
 | |
|     KeywordCxt,
 | |
|     nil,
 | |
|     Name
 | |
|   } = require("ajv");
 | |
| 
 | |
|   /**
 | |
|    * @param {Code | Name} x
 | |
|    * @returns {Code | Name}
 | |
|    */
 | |
|   function par(x) {
 | |
|     return x instanceof Name ? x : _`(${x})`;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * @param {Code} op
 | |
|    * @returns {function(Code, Code): Code}
 | |
|    */
 | |
|   function mappend(op) {
 | |
|     return (x, y) => x === nil ? y : y === nil ? x : _`${par(x)} ${op} ${par(y)}`;
 | |
|   }
 | |
|   const orCode = mappend(_`||`);
 | |
| 
 | |
|   // boolean OR (||) expression with the passed arguments
 | |
|   /**
 | |
|    * @param {...Code} args
 | |
|    * @returns {Code}
 | |
|    */
 | |
|   function or(...args) {
 | |
|     return args.reduce(orCode);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * @param {string | number} key
 | |
|    * @returns {Code}
 | |
|    */
 | |
|   function getProperty(key) {
 | |
|     return _`[${key}]`;
 | |
|   }
 | |
|   const keywords = {
 | |
|     formatMaximum: {
 | |
|       okStr: "<=",
 | |
|       ok: _`<=`,
 | |
|       fail: _`>`
 | |
|     },
 | |
|     formatMinimum: {
 | |
|       okStr: ">=",
 | |
|       ok: _`>=`,
 | |
|       fail: _`<`
 | |
|     },
 | |
|     formatExclusiveMaximum: {
 | |
|       okStr: "<",
 | |
|       ok: _`<`,
 | |
|       fail: _`>=`
 | |
|     },
 | |
|     formatExclusiveMinimum: {
 | |
|       okStr: ">",
 | |
|       ok: _`>`,
 | |
|       fail: _`<=`
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   /** @type {KeywordErrorDefinition} */
 | |
|   const error = {
 | |
|     message: ({
 | |
|       keyword,
 | |
|       schemaCode
 | |
|     }) => str`should be ${keywords[(/** @type {keyof typeof keywords} */keyword)].okStr} ${schemaCode}`,
 | |
|     params: ({
 | |
|       keyword,
 | |
|       schemaCode
 | |
|     }) => _`{comparison: ${keywords[(/** @type {keyof typeof keywords} */keyword)].okStr}, limit: ${schemaCode}}`
 | |
|   };
 | |
|   for (const keyword of Object.keys(keywords)) {
 | |
|     ajv.addKeyword({
 | |
|       keyword,
 | |
|       type: "string",
 | |
|       schemaType: keyword.startsWith("formatExclusive") ? ["string", "boolean"] : ["string", "number"],
 | |
|       $data: true,
 | |
|       error,
 | |
|       code(cxt) {
 | |
|         const {
 | |
|           gen,
 | |
|           data,
 | |
|           schemaCode,
 | |
|           keyword,
 | |
|           it
 | |
|         } = cxt;
 | |
|         const {
 | |
|           opts,
 | |
|           self
 | |
|         } = it;
 | |
|         if (!opts.validateFormats) return;
 | |
|         const fCxt = new KeywordCxt(it, /** @type {any} */
 | |
|         self.RULES.all.format.definition, "format");
 | |
| 
 | |
|         /**
 | |
|          * @param {Name} fmt
 | |
|          * @returns {Code}
 | |
|          */
 | |
|         function compareCode(fmt) {
 | |
|           return _`${fmt}.compare(${data}, ${schemaCode}) ${keywords[(/** @type {keyof typeof keywords} */keyword)].fail} 0`;
 | |
|         }
 | |
|         function validate$DataFormat() {
 | |
|           const fmts = gen.scopeValue("formats", {
 | |
|             ref: self.formats,
 | |
|             code: opts.code.formats
 | |
|           });
 | |
|           const fmt = gen.const("fmt", _`${fmts}[${fCxt.schemaCode}]`);
 | |
|           cxt.fail$data(or(_`typeof ${fmt} != "object"`, _`${fmt} instanceof RegExp`, _`typeof ${fmt}.compare != "function"`, compareCode(fmt)));
 | |
|         }
 | |
|         function validateFormat() {
 | |
|           const format = fCxt.schema;
 | |
|           const fmtDef = self.formats[format];
 | |
|           if (!fmtDef || fmtDef === true) {
 | |
|             return;
 | |
|           }
 | |
|           if (typeof fmtDef !== "object" || fmtDef instanceof RegExp || typeof fmtDef.compare !== "function") {
 | |
|             throw new Error(`"${keyword}": format "${format}" does not define "compare" function`);
 | |
|           }
 | |
|           const fmt = gen.scopeValue("formats", {
 | |
|             key: format,
 | |
|             ref: fmtDef,
 | |
|             code: opts.code.formats ? _`${opts.code.formats}${getProperty(format)}` : undefined
 | |
|           });
 | |
|           cxt.fail$data(compareCode(fmt));
 | |
|         }
 | |
|         if (fCxt.$data) {
 | |
|           validate$DataFormat();
 | |
|         } else {
 | |
|           validateFormat();
 | |
|         }
 | |
|       },
 | |
|       dependencies: ["format"]
 | |
|     });
 | |
|   }
 | |
|   return ajv;
 | |
| }
 | |
| var _default = exports.default = addLimitKeyword; |