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