@kklab/fortress-validator-plugin-json-schema 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ import { default as Ajv, ValidateFunction } from 'ajv';
2
+ declare const ajv: Ajv;
3
+ declare const schemaCache: Map<string, ValidateFunction<unknown>>;
4
+ export { ajv, schemaCache, };
@@ -0,0 +1,3 @@
1
+ import { Plugin } from '@fortress-validator/types';
2
+ declare const plugin: Plugin;
3
+ export default plugin;
package/dist/index.js ADDED
@@ -0,0 +1,369 @@
1
+ import p from "ajv";
2
+ const l = {
3
+ jsonSchema: (r) => `The ${r} field must be a valid JSON Schema.`
4
+ }, u = {
5
+ jsonSchema: () => "此欄位必須是有效的 JSON Schema"
6
+ }, b = {
7
+ en: l,
8
+ "zh-TW": u
9
+ }, $ = (r) => r == null || r === "" || Array.isArray(r) && r.length < 1, k = new p(), n = /* @__PURE__ */ new Map(), h = (r, t) => {
10
+ if (t)
11
+ for (const e of t) {
12
+ let a;
13
+ switch (e.keyword) {
14
+ case "additionalItems":
15
+ case "items": {
16
+ const s = e.params.limit;
17
+ a = `must NOT have more than ${s} item${s != 1 ? "s" : ""}`;
18
+ break;
19
+ }
20
+ case "additionalProperties": {
21
+ a = "must NOT have additional properties";
22
+ break;
23
+ }
24
+ case "anyOf": {
25
+ a = 'must match a schema in "anyOf"';
26
+ break;
27
+ }
28
+ case "const": {
29
+ a = "must be equal to constant";
30
+ break;
31
+ }
32
+ case "contains": {
33
+ a = "must contain a valid item";
34
+ break;
35
+ }
36
+ case "dependencies":
37
+ case "dependentRequired": {
38
+ a = `must have propert${e.params.depsCount == 1 ? "y" : "ies"} ${e.params.deps} when property ${e.params.property} is present`;
39
+ break;
40
+ }
41
+ case "discriminator": {
42
+ switch (e.params.error) {
43
+ case "tag": {
44
+ a = `tag "${e.params.tag}" must be string`;
45
+ break;
46
+ }
47
+ case "mapping": {
48
+ a = `value of tag "${e.params.tag}" must be in "oneOf"`;
49
+ break;
50
+ }
51
+ default:
52
+ a = `must pass "${e.keyword}" keyword validation`;
53
+ }
54
+ break;
55
+ }
56
+ case "enum": {
57
+ a = "must be equal to one of the allowed values";
58
+ break;
59
+ }
60
+ case "false schema": {
61
+ a = "boolean schema is false";
62
+ break;
63
+ }
64
+ case "format": {
65
+ a = `must match format "${e.params.format}"`;
66
+ break;
67
+ }
68
+ case "formatMaximum":
69
+ case "formatExclusiveMaximum": {
70
+ a = `must be ${e.params.comparison} ${e.params.limit}`;
71
+ break;
72
+ }
73
+ case "formatMinimum":
74
+ case "formatExclusiveMinimum": {
75
+ a = `must be ${e.params.comparison} ${e.params.limit}`;
76
+ break;
77
+ }
78
+ case "if": {
79
+ a = `must match "${e.params.failingKeyword}" schema`;
80
+ break;
81
+ }
82
+ case "maximum":
83
+ case "exclusiveMaximum": {
84
+ a = `must be ${e.params.comparison} ${e.params.limit}`;
85
+ break;
86
+ }
87
+ case "maxItems": {
88
+ const s = e.params.limit;
89
+ a = `must NOT have more than ${s} item${s != 1 ? "s" : ""}`;
90
+ break;
91
+ }
92
+ case "maxLength": {
93
+ const s = e.params.limit;
94
+ a = `must NOT be longer than ${s} character${s != 1 ? "s" : ""}`;
95
+ break;
96
+ }
97
+ case "maxProperties": {
98
+ const s = e.params.limit;
99
+ a = `must NOT have more than ${s} propert${s == 1 ? "y" : "ies"}`;
100
+ break;
101
+ }
102
+ case "minimum":
103
+ case "exclusiveMinimum": {
104
+ a = `must be ${e.params.comparison} ${e.params.limit}`;
105
+ break;
106
+ }
107
+ case "minItems": {
108
+ const s = e.params.limit;
109
+ a = `must NOT have less than ${s} item${s != 1 ? "s" : ""}`;
110
+ break;
111
+ }
112
+ case "minLength": {
113
+ const s = e.params.limit;
114
+ a = `must NOT be shorter than ${s} character${s != 1 ? "s" : ""}`;
115
+ break;
116
+ }
117
+ case "minProperties": {
118
+ const s = e.params.limit;
119
+ a = `must NOT have less than ${s} propert${s == 1 ? "y" : "ies"}`;
120
+ break;
121
+ }
122
+ case "multipleOf": {
123
+ a = `must be a multiple of ${e.params.multipleOf}`;
124
+ break;
125
+ }
126
+ case "not": {
127
+ a = 'must NOT be valid according to schema in "not"';
128
+ break;
129
+ }
130
+ case "oneOf": {
131
+ a = 'must match exactly one schema in "oneOf"';
132
+ break;
133
+ }
134
+ case "pattern": {
135
+ a = `must match pattern "${e.params.pattern}"`;
136
+ break;
137
+ }
138
+ case "patternRequired": {
139
+ a = `must have property matching pattern "${e.params.missingPattern}"`;
140
+ break;
141
+ }
142
+ case "propertyNames": {
143
+ a = "property name is invalid";
144
+ break;
145
+ }
146
+ case "required": {
147
+ a = `must have required property "${e.params.missingProperty}"`;
148
+ break;
149
+ }
150
+ case "type": {
151
+ a = `must be ${e.params.type}`;
152
+ break;
153
+ }
154
+ case "unevaluatedItems": {
155
+ const s = e.params.len;
156
+ a = `must NOT have more than ${s} item${s != 1 ? "s" : ""}`;
157
+ break;
158
+ }
159
+ case "unevaluatedProperties": {
160
+ a = "must NOT have unevaluated properties";
161
+ break;
162
+ }
163
+ case "uniqueItems": {
164
+ a = `must NOT have duplicate items (items #${e.params.j} and #${e.params.i} are identical)`;
165
+ break;
166
+ }
167
+ default:
168
+ a = `must pass "${e.keyword}" keyword validation`;
169
+ }
170
+ e.message = e.instancePath ? `The property at path "${e.instancePath}" ${a}.` : `The ${r.toLowerCase()} field ${a}.`;
171
+ }
172
+ }, d = (r, t) => {
173
+ if (t)
174
+ for (const e of t) {
175
+ let a;
176
+ switch (e.keyword) {
177
+ case "additionalItems":
178
+ case "items": {
179
+ a = `不能超過 ${e.params.limit} 個元素`;
180
+ break;
181
+ }
182
+ case "additionalProperties": {
183
+ a = "不能有額外的屬性";
184
+ break;
185
+ }
186
+ case "anyOf": {
187
+ a = '必須符合 "anyOf" 指定的模式';
188
+ break;
189
+ }
190
+ case "const": {
191
+ a = "必須等於常數";
192
+ break;
193
+ }
194
+ case "contains": {
195
+ a = "必須包含一個有效元素";
196
+ break;
197
+ }
198
+ case "dependencies":
199
+ case "dependentRequired": {
200
+ a = `當 "${e.params.property}" 屬性存在時,必須有 "${e.params.deps}" 屬性`;
201
+ break;
202
+ }
203
+ case "discriminator": {
204
+ switch (e.params.error) {
205
+ case "tag": {
206
+ a = `標籤 "${e.params.tag}" 的類型必須是字串`;
207
+ break;
208
+ }
209
+ case "mapping": {
210
+ a = `標籤 "${e.params.tag}" 必須在 "oneOf" 其中之一`;
211
+ break;
212
+ }
213
+ default:
214
+ a = `必須通過 "${e.keyword}" 關鍵詞檢驗`;
215
+ }
216
+ break;
217
+ }
218
+ case "enum": {
219
+ a = "必須是指定的其中一個值";
220
+ break;
221
+ }
222
+ case "false schema": {
223
+ a = "布林模式不正確";
224
+ break;
225
+ }
226
+ case "format": {
227
+ a = `必須要符合 ${e.params.format} 格式`;
228
+ break;
229
+ }
230
+ case "formatMaximum":
231
+ case "formatExclusiveMaximum": {
232
+ a = `必須是 ${e.params.comparison} ${e.params.limit}`;
233
+ break;
234
+ }
235
+ case "formatMinimum":
236
+ case "formatExclusiveMinimum": {
237
+ a = `必須是 ${e.params.comparison} ${e.params.limit}`;
238
+ break;
239
+ }
240
+ case "if": {
241
+ a = `必須符合 "${e.params.failingKeyword}" 模式`;
242
+ break;
243
+ }
244
+ case "maximum":
245
+ case "exclusiveMaximum": {
246
+ a = `必須要 ${e.params.comparison} ${e.params.limit}`;
247
+ break;
248
+ }
249
+ case "maxItems": {
250
+ a = `不能多於 ${e.params.limit} 個`;
251
+ break;
252
+ }
253
+ case "maxLength": {
254
+ a = `不能多於 ${e.params.limit} 個字元`;
255
+ break;
256
+ }
257
+ case "maxProperties": {
258
+ a = `不能多於 ${e.params.limit} 個屬性`;
259
+ break;
260
+ }
261
+ case "minimum":
262
+ case "exclusiveMinimum": {
263
+ a = `必須要 ${e.params.comparison} ${e.params.limit}`;
264
+ break;
265
+ }
266
+ case "minItems": {
267
+ a = `不能少於 ${e.params.limit} 個`;
268
+ break;
269
+ }
270
+ case "minLength": {
271
+ a = `不能少於 ${e.params.limit} 個字元`;
272
+ break;
273
+ }
274
+ case "minProperties": {
275
+ a = `不能少於 ${e.params.limit} 個屬性`;
276
+ break;
277
+ }
278
+ case "multipleOf": {
279
+ a = `必須是 ${e.params.multipleOf} 的整數倍`;
280
+ break;
281
+ }
282
+ case "not": {
283
+ a = '必須不符合 "not" 中的模式';
284
+ break;
285
+ }
286
+ case "oneOf": {
287
+ a = '只能符合一個 "oneOf" 中的模式';
288
+ break;
289
+ }
290
+ case "pattern": {
291
+ a = `必須符合 "${e.params.pattern}" 模式`;
292
+ break;
293
+ }
294
+ case "patternRequired": {
295
+ a = `必須有符合 "${e.params.missingPattern}" 模式的屬性`;
296
+ break;
297
+ }
298
+ case "propertyNames": {
299
+ a = "必須是有效的属性名稱";
300
+ break;
301
+ }
302
+ case "required": {
303
+ a = `必須有 "${e.params.missingProperty}" 屬性`;
304
+ break;
305
+ }
306
+ case "type": {
307
+ a = `必須是 ${e.params.type} 類型`;
308
+ break;
309
+ }
310
+ case "unevaluatedItems": {
311
+ a = `不能超過 ${e.params.len} 個元素`;
312
+ break;
313
+ }
314
+ case "unevaluatedProperties": {
315
+ a = "不能有未驗證的屬性";
316
+ break;
317
+ }
318
+ case "uniqueItems": {
319
+ a = `不能有重複的元素(第 ${e.params.j} 項和第 ${e.params.i} 項是重複的)`;
320
+ break;
321
+ }
322
+ default:
323
+ a = `必須通過 "${e.keyword}" 關鍵詞檢驗`;
324
+ }
325
+ e.message = e.instancePath ? `路徑為 "${e.instancePath}" 的屬性${a}` : `此欄位${a}`;
326
+ }
327
+ }, c = {
328
+ en: h,
329
+ "zh-TW": d
330
+ }, f = ({ locale: r, field: t, schema: e }) => {
331
+ const a = y(e);
332
+ return (s) => {
333
+ if ($(s))
334
+ return !1;
335
+ if (typeof s == "string")
336
+ try {
337
+ s = JSON.parse(s);
338
+ } catch (i) {
339
+ return console.warn(i), !1;
340
+ }
341
+ if (a(s))
342
+ return !0;
343
+ const { errors: m } = a;
344
+ if (m && m.length > 0) {
345
+ (c[r] || c.en)(t, m);
346
+ const [o] = m;
347
+ return o.message || !1;
348
+ }
349
+ return !1;
350
+ };
351
+ }, y = (r) => {
352
+ const t = JSON.stringify(r), e = n.get(t);
353
+ if (e)
354
+ return e;
355
+ try {
356
+ const a = k.compile(r);
357
+ return n.set(t, a), a;
358
+ } catch (a) {
359
+ throw new Error(`Invalid schema: ${a.message.replace("schema is invalid: ", "")}`);
360
+ }
361
+ }, v = {
362
+ jsonSchema: f
363
+ }, O = {
364
+ locales: b,
365
+ rules: v
366
+ };
367
+ export {
368
+ O as default
369
+ };
@@ -0,0 +1 @@
1
+ (function(m,i){typeof exports=="object"&&typeof module<"u"?module.exports=i(require("ajv")):typeof define=="function"&&define.amd?define(["ajv"],i):(m=typeof globalThis<"u"?globalThis:m||self,m.FortressPluginJSONSchema=i(m.ajv))})(this,function(m){"use strict";const l={en:{jsonSchema:r=>`The ${r} field must be a valid JSON Schema.`},"zh-TW":{jsonSchema:()=>"此欄位必須是有效的 JSON Schema"}},u=r=>r==null||r===""||Array.isArray(r)&&r.length<1,$=new m,c=new Map,o={en:(r,t)=>{if(t)for(const e of t){let a;switch(e.keyword){case"additionalItems":case"items":{const s=e.params.limit;a=`must NOT have more than ${s} item${s!=1?"s":""}`;break}case"additionalProperties":{a="must NOT have additional properties";break}case"anyOf":{a='must match a schema in "anyOf"';break}case"const":{a="must be equal to constant";break}case"contains":{a="must contain a valid item";break}case"dependencies":case"dependentRequired":{a=`must have propert${e.params.depsCount==1?"y":"ies"} ${e.params.deps} when property ${e.params.property} is present`;break}case"discriminator":{switch(e.params.error){case"tag":{a=`tag "${e.params.tag}" must be string`;break}case"mapping":{a=`value of tag "${e.params.tag}" must be in "oneOf"`;break}default:a=`must pass "${e.keyword}" keyword validation`}break}case"enum":{a="must be equal to one of the allowed values";break}case"false schema":{a="boolean schema is false";break}case"format":{a=`must match format "${e.params.format}"`;break}case"formatMaximum":case"formatExclusiveMaximum":{a=`must be ${e.params.comparison} ${e.params.limit}`;break}case"formatMinimum":case"formatExclusiveMinimum":{a=`must be ${e.params.comparison} ${e.params.limit}`;break}case"if":{a=`must match "${e.params.failingKeyword}" schema`;break}case"maximum":case"exclusiveMaximum":{a=`must be ${e.params.comparison} ${e.params.limit}`;break}case"maxItems":{const s=e.params.limit;a=`must NOT have more than ${s} item${s!=1?"s":""}`;break}case"maxLength":{const s=e.params.limit;a=`must NOT be longer than ${s} character${s!=1?"s":""}`;break}case"maxProperties":{const s=e.params.limit;a=`must NOT have more than ${s} propert${s==1?"y":"ies"}`;break}case"minimum":case"exclusiveMinimum":{a=`must be ${e.params.comparison} ${e.params.limit}`;break}case"minItems":{const s=e.params.limit;a=`must NOT have less than ${s} item${s!=1?"s":""}`;break}case"minLength":{const s=e.params.limit;a=`must NOT be shorter than ${s} character${s!=1?"s":""}`;break}case"minProperties":{const s=e.params.limit;a=`must NOT have less than ${s} propert${s==1?"y":"ies"}`;break}case"multipleOf":{a=`must be a multiple of ${e.params.multipleOf}`;break}case"not":{a='must NOT be valid according to schema in "not"';break}case"oneOf":{a='must match exactly one schema in "oneOf"';break}case"pattern":{a=`must match pattern "${e.params.pattern}"`;break}case"patternRequired":{a=`must have property matching pattern "${e.params.missingPattern}"`;break}case"propertyNames":{a="property name is invalid";break}case"required":{a=`must have required property "${e.params.missingProperty}"`;break}case"type":{a=`must be ${e.params.type}`;break}case"unevaluatedItems":{const s=e.params.len;a=`must NOT have more than ${s} item${s!=1?"s":""}`;break}case"unevaluatedProperties":{a="must NOT have unevaluated properties";break}case"uniqueItems":{a=`must NOT have duplicate items (items #${e.params.j} and #${e.params.i} are identical)`;break}default:a=`must pass "${e.keyword}" keyword validation`}e.message=e.instancePath?`The property at path "${e.instancePath}" ${a}.`:`The ${r.toLowerCase()} field ${a}.`}},"zh-TW":(r,t)=>{if(t)for(const e of t){let a;switch(e.keyword){case"additionalItems":case"items":{a=`不能超過 ${e.params.limit} 個元素`;break}case"additionalProperties":{a="不能有額外的屬性";break}case"anyOf":{a='必須符合 "anyOf" 指定的模式';break}case"const":{a="必須等於常數";break}case"contains":{a="必須包含一個有效元素";break}case"dependencies":case"dependentRequired":{a=`當 "${e.params.property}" 屬性存在時,必須有 "${e.params.deps}" 屬性`;break}case"discriminator":{switch(e.params.error){case"tag":{a=`標籤 "${e.params.tag}" 的類型必須是字串`;break}case"mapping":{a=`標籤 "${e.params.tag}" 必須在 "oneOf" 其中之一`;break}default:a=`必須通過 "${e.keyword}" 關鍵詞檢驗`}break}case"enum":{a="必須是指定的其中一個值";break}case"false schema":{a="布林模式不正確";break}case"format":{a=`必須要符合 ${e.params.format} 格式`;break}case"formatMaximum":case"formatExclusiveMaximum":{a=`必須是 ${e.params.comparison} ${e.params.limit}`;break}case"formatMinimum":case"formatExclusiveMinimum":{a=`必須是 ${e.params.comparison} ${e.params.limit}`;break}case"if":{a=`必須符合 "${e.params.failingKeyword}" 模式`;break}case"maximum":case"exclusiveMaximum":{a=`必須要 ${e.params.comparison} ${e.params.limit}`;break}case"maxItems":{a=`不能多於 ${e.params.limit} 個`;break}case"maxLength":{a=`不能多於 ${e.params.limit} 個字元`;break}case"maxProperties":{a=`不能多於 ${e.params.limit} 個屬性`;break}case"minimum":case"exclusiveMinimum":{a=`必須要 ${e.params.comparison} ${e.params.limit}`;break}case"minItems":{a=`不能少於 ${e.params.limit} 個`;break}case"minLength":{a=`不能少於 ${e.params.limit} 個字元`;break}case"minProperties":{a=`不能少於 ${e.params.limit} 個屬性`;break}case"multipleOf":{a=`必須是 ${e.params.multipleOf} 的整數倍`;break}case"not":{a='必須不符合 "not" 中的模式';break}case"oneOf":{a='只能符合一個 "oneOf" 中的模式';break}case"pattern":{a=`必須符合 "${e.params.pattern}" 模式`;break}case"patternRequired":{a=`必須有符合 "${e.params.missingPattern}" 模式的屬性`;break}case"propertyNames":{a="必須是有效的属性名稱";break}case"required":{a=`必須有 "${e.params.missingProperty}" 屬性`;break}case"type":{a=`必須是 ${e.params.type} 類型`;break}case"unevaluatedItems":{a=`不能超過 ${e.params.len} 個元素`;break}case"unevaluatedProperties":{a="不能有未驗證的屬性";break}case"uniqueItems":{a=`不能有重複的元素(第 ${e.params.j} 項和第 ${e.params.i} 項是重複的)`;break}default:a=`必須通過 "${e.keyword}" 關鍵詞檢驗`}e.message=e.instancePath?`路徑為 "${e.instancePath}" 的屬性${a}`:`此欄位${a}`}}},b=({locale:r,field:t,schema:e})=>{const a=d(e);return s=>{if(u(s))return!1;if(typeof s=="string")try{s=JSON.parse(s)}catch(p){return console.warn(p),!1}if(a(s))return!0;const{errors:n}=a;if(n&&n.length>0){(o[r]||o.en)(t,n);const[h]=n;return h.message||!1}return!1}},d=r=>{const t=JSON.stringify(r),e=c.get(t);if(e)return e;try{const a=$.compile(r);return c.set(t,a),a}catch(a){throw new Error(`Invalid schema: ${a.message.replace("schema is invalid: ","")}`)}};return{locales:l,rules:{jsonSchema:b}}});
@@ -0,0 +1,3 @@
1
+ import { Messages } from '@fortress-validator/types';
2
+ declare const en: Messages;
3
+ export default en;
@@ -0,0 +1,3 @@
1
+ import { Locales } from '@fortress-validator/types';
2
+ declare const locales: Locales;
3
+ export default locales;
@@ -0,0 +1,3 @@
1
+ import { Messages } from '@fortress-validator/types';
2
+ declare const en: Messages;
3
+ export default en;
@@ -0,0 +1,3 @@
1
+ import { default as Localizer } from '../types/Localizer';
2
+ declare const localizer: Localizer;
3
+ export default localizer;
@@ -0,0 +1,3 @@
1
+ import { default as Localizers } from '../types/Localizers';
2
+ declare const localizers: Localizers;
3
+ export default localizers;
@@ -0,0 +1,3 @@
1
+ import { default as Localizer } from '../types/Localizer';
2
+ declare const localizer: Localizer;
3
+ export default localizer;
@@ -0,0 +1,3 @@
1
+ import { Rules } from '@fortress-validator/types';
2
+ declare const rules: Rules;
3
+ export default rules;
@@ -0,0 +1,8 @@
1
+ import { Rule, RuleArguments } from '@fortress-validator/types';
2
+ export interface JSONSchemaRuleArguments extends RuleArguments {
3
+ locale: string;
4
+ field: string;
5
+ schema: Record<string, unknown>;
6
+ }
7
+ declare const jsonSchema: Rule<JSONSchemaRuleArguments>;
8
+ export default jsonSchema;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { ErrorObject } from 'ajv';
2
+ interface Localizer {
3
+ (field: string, errors: ErrorObject[]): void;
4
+ }
5
+ export default Localizer;
@@ -0,0 +1,5 @@
1
+ import { default as Localizer } from './Localizer';
2
+ interface Localizers {
3
+ [key: string]: Localizer;
4
+ }
5
+ export default Localizers;
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@kklab/fortress-validator-plugin-json-schema",
3
+ "private": false,
4
+ "version": "1.0.3",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc -p ./tsconfig.build.json && vite build",
9
+ "preview": "vite preview",
10
+ "lint": "eslint lib",
11
+ "test": "npm run test:unit -- --run",
12
+ "test:unit": "vitest",
13
+ "release": "npm run test && npm run build && npm publish --access public"
14
+ },
15
+ "dependencies": {
16
+ "@fortress-validator/utils": "^1.0.0"
17
+ },
18
+ "peerDependencies": {
19
+ "ajv": "^8.17.1"
20
+ },
21
+ "devDependencies": {
22
+ "@eslint/js": "^9.7.0",
23
+ "@fortress-validator/types": "^1.0.0",
24
+ "@stylistic/eslint-plugin": "^2.9.0",
25
+ "@types/eslint__js": "^8.42.3",
26
+ "@types/node": "^20.14.12",
27
+ "eslint": "^8.57.0",
28
+ "globals": "^15.8.0",
29
+ "typescript": "^5.0.2",
30
+ "typescript-eslint": "^7.17.0",
31
+ "vite": "^4.4.5",
32
+ "vite-plugin-dts": "^4.2.3",
33
+ "vitest": "^2.1.2"
34
+ },
35
+ "main": "dist/index.js",
36
+ "types": "dist/index.d.ts",
37
+ "files": [
38
+ "dist"
39
+ ],
40
+ "exports": {
41
+ ".": {
42
+ "import": "./dist/index.js",
43
+ "require": "./dist/index.umd.js"
44
+ }
45
+ },
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://gitlab.com/kklab-com/vd/0-1-lab/chemi/fortress-validator-plugin-json-schema"
49
+ },
50
+ "keywords": [
51
+ "form",
52
+ "validation",
53
+ "validator"
54
+ ]
55
+ }