167 lines
4.3 KiB
JavaScript
167 lines
4.3 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 ajv
|
||
|
|
* @returns {Ajv} ajv with limit keyword
|
||
|
|
*/
|
||
|
|
function addLimitKeyword(ajv) {
|
||
|
|
const {
|
||
|
|
_,
|
||
|
|
str,
|
||
|
|
KeywordCxt,
|
||
|
|
nil,
|
||
|
|
Name
|
||
|
|
} = require("ajv");
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param {Code | Name} nameOrCode name or code
|
||
|
|
* @returns {Code | Name} name or code
|
||
|
|
*/
|
||
|
|
function par(nameOrCode) {
|
||
|
|
return nameOrCode instanceof Name ? nameOrCode : _`(${nameOrCode})`;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param {Code} op op
|
||
|
|
* @returns {(xValue: Code, yValue: Code) => Code} code
|
||
|
|
*/
|
||
|
|
function mappend(op) {
|
||
|
|
return (xValue, yValue) => xValue === nil ? yValue : yValue === nil ? xValue : _`${par(xValue)} ${op} ${par(yValue)}`;
|
||
|
|
}
|
||
|
|
const orCode = mappend(_`||`);
|
||
|
|
|
||
|
|
// boolean OR (||) expression with the passed arguments
|
||
|
|
/**
|
||
|
|
* @param {...Code} args args
|
||
|
|
* @returns {Code} code
|
||
|
|
*/
|
||
|
|
function or(...args) {
|
||
|
|
return args.reduce(orCode);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param {string | number} key key
|
||
|
|
* @returns {Code} property
|
||
|
|
*/
|
||
|
|
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,
|
||
|
|
// eslint-disable-next-line jsdoc/no-restricted-syntax
|
||
|
|
/** @type {any} */
|
||
|
|
self.RULES.all.format.definition, "format");
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param {Name} fmt fmt
|
||
|
|
* @returns {Code} code
|
||
|
|
*/
|
||
|
|
function compareCode(fmt) {
|
||
|
|
return _`${fmt}.compare(${data}, ${schemaCode}) ${keywords[(/** @type {keyof typeof keywords} */keyword)].fail} 0`;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @returns {void}
|
||
|
|
*/
|
||
|
|
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)));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @returns {void}
|
||
|
|
*/
|
||
|
|
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;
|