@serum-enterprises/schema 2.0.1-beta.0
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.
- package/LICENSE +21 -0
- package/README.md +65 -0
- package/build/Schema.js +667 -0
- package/package.json +33 -0
- package/src/Schema.ts +887 -0
- package/tsconfig.json +30 -0
- package/types/Schema.d.ts +99 -0
package/build/Schema.js
ADDED
@@ -0,0 +1,667 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
15
|
+
}) : function(o, v) {
|
16
|
+
o["default"] = v;
|
17
|
+
});
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
19
|
+
var ownKeys = function(o) {
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
21
|
+
var ar = [];
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
23
|
+
return ar;
|
24
|
+
};
|
25
|
+
return ownKeys(o);
|
26
|
+
};
|
27
|
+
return function (mod) {
|
28
|
+
if (mod && mod.__esModule) return mod;
|
29
|
+
var result = {};
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
31
|
+
__setModuleDefault(result, mod);
|
32
|
+
return result;
|
33
|
+
};
|
34
|
+
})();
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
36
|
+
exports.Schema = exports.ValidationError = exports.SchemaError = void 0;
|
37
|
+
const JSON = __importStar(require("@serum-enterprises/json"));
|
38
|
+
const result_1 = require("@serum-enterprises/result");
|
39
|
+
class SchemaError extends Error {
|
40
|
+
}
|
41
|
+
exports.SchemaError = SchemaError;
|
42
|
+
class ValidationError extends Error {
|
43
|
+
}
|
44
|
+
exports.ValidationError = ValidationError;
|
45
|
+
class Schema {
|
46
|
+
static get Any() {
|
47
|
+
return new AnyValidator();
|
48
|
+
}
|
49
|
+
static get Boolean() {
|
50
|
+
return new BooleanValidator();
|
51
|
+
}
|
52
|
+
static get Number() {
|
53
|
+
return new NumberValidator();
|
54
|
+
}
|
55
|
+
static get String() {
|
56
|
+
return new StringValidator();
|
57
|
+
}
|
58
|
+
static get Array() {
|
59
|
+
return new ArrayValidator();
|
60
|
+
}
|
61
|
+
static get Object() {
|
62
|
+
return new ObjectValidator();
|
63
|
+
}
|
64
|
+
static get Or() {
|
65
|
+
return new OrValidator();
|
66
|
+
}
|
67
|
+
static get And() {
|
68
|
+
return new AndValidator();
|
69
|
+
}
|
70
|
+
static get defaultRegistry() {
|
71
|
+
return new Map([
|
72
|
+
['any', AnyValidator],
|
73
|
+
['boolean', BooleanValidator],
|
74
|
+
['number', NumberValidator],
|
75
|
+
['string', StringValidator],
|
76
|
+
['array', ArrayValidator],
|
77
|
+
['object', ObjectValidator],
|
78
|
+
['or', OrValidator],
|
79
|
+
['and', AndValidator]
|
80
|
+
]);
|
81
|
+
}
|
82
|
+
static fromJSON(schema, path = "schema", registry = Schema.defaultRegistry) {
|
83
|
+
if (!JSON.isObject(schema))
|
84
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path} to be an Object`));
|
85
|
+
if (!JSON.isString(schema['type']))
|
86
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.type to be a String`));
|
87
|
+
if (!registry.has(schema['type']))
|
88
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.type to be a registered Validator`));
|
89
|
+
return registry.get(schema['type']).fromJSON(schema, path, registry);
|
90
|
+
}
|
91
|
+
}
|
92
|
+
exports.Schema = Schema;
|
93
|
+
class AnyValidator extends Schema {
|
94
|
+
static fromJSON(schema, path = "schema") {
|
95
|
+
const validator = new AnyValidator();
|
96
|
+
if (!JSON.isObject(schema))
|
97
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path} to be an Object`));
|
98
|
+
return result_1.Result.Ok(validator);
|
99
|
+
}
|
100
|
+
constructor() {
|
101
|
+
super();
|
102
|
+
}
|
103
|
+
validate(data, path = 'data') {
|
104
|
+
if (!JSON.isJSON(data))
|
105
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be JSON`));
|
106
|
+
return result_1.Result.Ok(void 0);
|
107
|
+
}
|
108
|
+
toJSON() {
|
109
|
+
return {
|
110
|
+
type: 'any'
|
111
|
+
};
|
112
|
+
}
|
113
|
+
}
|
114
|
+
class BooleanValidator extends Schema {
|
115
|
+
static fromJSON(schema, path = "schema") {
|
116
|
+
const validator = new BooleanValidator();
|
117
|
+
if (!JSON.isObject(schema))
|
118
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path} to be an Object`));
|
119
|
+
if ('nullable' in schema) {
|
120
|
+
if (!JSON.isBoolean(schema['nullable']))
|
121
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.nullable to be a Boolean`));
|
122
|
+
validator.nullable(schema['nullable']);
|
123
|
+
}
|
124
|
+
if ('equals' in schema) {
|
125
|
+
if (!JSON.isBoolean(schema['equals']))
|
126
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.equals to be a Boolean`));
|
127
|
+
validator.equals(schema['equals']);
|
128
|
+
}
|
129
|
+
return result_1.Result.Ok(validator);
|
130
|
+
}
|
131
|
+
#nullable;
|
132
|
+
#equals;
|
133
|
+
constructor() {
|
134
|
+
super();
|
135
|
+
this.#nullable = { flag: false };
|
136
|
+
this.#equals = { flag: false, value: false };
|
137
|
+
}
|
138
|
+
nullable(flag) {
|
139
|
+
this.#nullable = { flag };
|
140
|
+
return this;
|
141
|
+
}
|
142
|
+
equals(value) {
|
143
|
+
this.#equals = { flag: true, value };
|
144
|
+
return this;
|
145
|
+
}
|
146
|
+
validate(data, path = 'data') {
|
147
|
+
if (JSON.isBoolean(data)) {
|
148
|
+
if (this.#equals.flag && this.#equals.value !== data)
|
149
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be ${this.#equals.value}${this.#nullable.flag ? '' : ' or Null'}`));
|
150
|
+
return result_1.Result.Ok(void 0);
|
151
|
+
}
|
152
|
+
if (this.#nullable.flag && JSON.isNull(data))
|
153
|
+
return result_1.Result.Ok(void 0);
|
154
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be a Boolean${this.#nullable.flag ? '' : ' or Null'}`));
|
155
|
+
}
|
156
|
+
toJSON() {
|
157
|
+
const schema = {
|
158
|
+
type: 'boolean'
|
159
|
+
};
|
160
|
+
if (this.#nullable.flag)
|
161
|
+
schema['nullable'] = this.#nullable.flag;
|
162
|
+
if (this.#equals.flag)
|
163
|
+
schema['equals'] = this.#equals.value;
|
164
|
+
return schema;
|
165
|
+
}
|
166
|
+
}
|
167
|
+
class NumberValidator extends Schema {
|
168
|
+
static fromJSON(schema, path = "schema") {
|
169
|
+
const validator = new NumberValidator();
|
170
|
+
if (!JSON.isObject(schema))
|
171
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path} to be an Object`));
|
172
|
+
if ('nullable' in schema) {
|
173
|
+
if (!JSON.isBoolean(schema['nullable']))
|
174
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.nullable to be a Boolean`));
|
175
|
+
validator.nullable(schema['nullable']);
|
176
|
+
}
|
177
|
+
if ('equals' in schema) {
|
178
|
+
if (!JSON.isNumber(schema['equals']))
|
179
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.equals to be a Number`));
|
180
|
+
validator.equals(schema['equals']);
|
181
|
+
}
|
182
|
+
if ('integer' in schema) {
|
183
|
+
if (!JSON.isBoolean(schema['integer']))
|
184
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.integer to be a Boolean`));
|
185
|
+
validator.integer(schema['integer']);
|
186
|
+
}
|
187
|
+
if ('min' in schema) {
|
188
|
+
if (!JSON.isNumber(schema['min']))
|
189
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.min to be a Number`));
|
190
|
+
validator.min(schema['min']);
|
191
|
+
}
|
192
|
+
if ('max' in schema) {
|
193
|
+
if (!JSON.isNumber(schema['max']))
|
194
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.max to be a Number`));
|
195
|
+
validator.max(schema['max']);
|
196
|
+
}
|
197
|
+
return result_1.Result.Ok(validator);
|
198
|
+
}
|
199
|
+
#nullable;
|
200
|
+
#equals;
|
201
|
+
#integer;
|
202
|
+
#min;
|
203
|
+
#max;
|
204
|
+
constructor() {
|
205
|
+
super();
|
206
|
+
this.#nullable = { flag: false };
|
207
|
+
this.#equals = { flag: false, value: 0 };
|
208
|
+
this.#integer = { flag: false };
|
209
|
+
this.#min = { flag: false, value: 0 };
|
210
|
+
this.#max = { flag: false, value: 0 };
|
211
|
+
}
|
212
|
+
nullable(flag) {
|
213
|
+
this.#nullable = { flag };
|
214
|
+
return this;
|
215
|
+
}
|
216
|
+
equals(value) {
|
217
|
+
this.#equals = { flag: true, value };
|
218
|
+
return this;
|
219
|
+
}
|
220
|
+
integer(flag = true) {
|
221
|
+
this.#integer = { flag };
|
222
|
+
return this;
|
223
|
+
}
|
224
|
+
min(value) {
|
225
|
+
this.#min = { flag: true, value };
|
226
|
+
return this;
|
227
|
+
}
|
228
|
+
max(value) {
|
229
|
+
this.#max = { flag: true, value };
|
230
|
+
return this;
|
231
|
+
}
|
232
|
+
validate(data, path = 'data') {
|
233
|
+
if (JSON.isNumber(data)) {
|
234
|
+
if (this.#equals.flag && this.#equals.value !== data)
|
235
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be ${this.#equals.value}`));
|
236
|
+
if (this.#integer.flag && !Number.isInteger(data))
|
237
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be an Integer`));
|
238
|
+
if (this.#min.flag && this.#min.value > data)
|
239
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be at least ${this.#min.value}`));
|
240
|
+
if (this.#max.flag && this.#max.value < data)
|
241
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be at most ${this.#max.value}`));
|
242
|
+
return result_1.Result.Ok(void 0);
|
243
|
+
}
|
244
|
+
if (this.#nullable.flag && JSON.isNull(data))
|
245
|
+
return result_1.Result.Ok(void 0);
|
246
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be a Number${this.#nullable.flag ? '' : ' or Null'}`));
|
247
|
+
}
|
248
|
+
toJSON() {
|
249
|
+
const schema = {
|
250
|
+
type: 'number'
|
251
|
+
};
|
252
|
+
if (this.#nullable.flag)
|
253
|
+
schema['nullable'] = this.#nullable.flag;
|
254
|
+
if (this.#equals.flag)
|
255
|
+
schema['equals'] = this.#equals.value;
|
256
|
+
if (this.#integer.flag)
|
257
|
+
schema['integer'] = this.#integer.flag;
|
258
|
+
if (this.#min.flag)
|
259
|
+
schema['min'] = this.#min.value;
|
260
|
+
if (this.#max.flag)
|
261
|
+
schema['max'] = this.#max.value;
|
262
|
+
return schema;
|
263
|
+
}
|
264
|
+
}
|
265
|
+
class StringValidator extends Schema {
|
266
|
+
static fromJSON(schema, path = "schema") {
|
267
|
+
const validator = new StringValidator();
|
268
|
+
if (!JSON.isObject(schema))
|
269
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path} to be an Object`));
|
270
|
+
if ('nullable' in schema) {
|
271
|
+
if (!JSON.isBoolean(schema['nullable']))
|
272
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.nullable to be a Boolean`));
|
273
|
+
validator.nullable(schema['nullable']);
|
274
|
+
}
|
275
|
+
if ('equals' in schema) {
|
276
|
+
if (!JSON.isString(schema['equals']))
|
277
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.equals to be a String`));
|
278
|
+
validator.equals(schema['equals']);
|
279
|
+
}
|
280
|
+
if ('min' in schema) {
|
281
|
+
if (!JSON.isNumber(schema['min']))
|
282
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.min to be a Number`));
|
283
|
+
validator.min(schema['min']);
|
284
|
+
}
|
285
|
+
if ('max' in schema) {
|
286
|
+
if (!JSON.isNumber(schema['max']))
|
287
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.max to be a Number`));
|
288
|
+
validator.max(schema['max']);
|
289
|
+
}
|
290
|
+
return result_1.Result.Ok(validator);
|
291
|
+
}
|
292
|
+
#nullable;
|
293
|
+
#equals;
|
294
|
+
#min;
|
295
|
+
#max;
|
296
|
+
constructor() {
|
297
|
+
super();
|
298
|
+
this.#nullable = { flag: false };
|
299
|
+
this.#equals = { flag: false, value: "" };
|
300
|
+
this.#min = { flag: false, value: 0 };
|
301
|
+
this.#max = { flag: false, value: 0 };
|
302
|
+
}
|
303
|
+
nullable(flag) {
|
304
|
+
this.#nullable = { flag };
|
305
|
+
return this;
|
306
|
+
}
|
307
|
+
equals(value) {
|
308
|
+
this.#equals = { flag: true, value };
|
309
|
+
return this;
|
310
|
+
}
|
311
|
+
min(value) {
|
312
|
+
this.#min = { flag: true, value };
|
313
|
+
return this;
|
314
|
+
}
|
315
|
+
max(value) {
|
316
|
+
this.#max = { flag: true, value };
|
317
|
+
return this;
|
318
|
+
}
|
319
|
+
validate(data, path = 'data') {
|
320
|
+
if (JSON.isString(data)) {
|
321
|
+
if (this.#equals.flag && this.#equals.value !== data)
|
322
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be ${this.#equals.value}`));
|
323
|
+
if (this.#min.flag && this.#min.value > data.length)
|
324
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be at least ${this.#min.value} characters long`));
|
325
|
+
if (this.#max.flag && this.#max.value < data.length)
|
326
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be at most ${this.#max.value} characters long`));
|
327
|
+
return result_1.Result.Ok(void 0);
|
328
|
+
}
|
329
|
+
if (this.#nullable.flag && JSON.isNull(data))
|
330
|
+
return result_1.Result.Ok(void 0);
|
331
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be a String${this.#nullable.flag ? '' : ' or Null'}`));
|
332
|
+
}
|
333
|
+
toJSON() {
|
334
|
+
const schema = {
|
335
|
+
type: 'string'
|
336
|
+
};
|
337
|
+
if (this.#nullable.flag)
|
338
|
+
schema['nullable'] = this.#nullable.flag;
|
339
|
+
if (this.#equals.flag)
|
340
|
+
schema['equals'] = this.#equals.value;
|
341
|
+
if (this.#min.flag)
|
342
|
+
schema['min'] = this.#min.value;
|
343
|
+
if (this.#max.flag)
|
344
|
+
schema['max'] = this.#max.value;
|
345
|
+
return schema;
|
346
|
+
}
|
347
|
+
}
|
348
|
+
class ArrayValidator extends Schema {
|
349
|
+
static fromJSON(schema, path = "schema", registry = Schema.defaultRegistry) {
|
350
|
+
const validator = new ArrayValidator();
|
351
|
+
if (!JSON.isObject(schema))
|
352
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path} to be an Object`));
|
353
|
+
if ('nullable' in schema) {
|
354
|
+
if (!JSON.isBoolean(schema['nullable']))
|
355
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.nullable to be a Boolean`));
|
356
|
+
validator.nullable(schema['nullable']);
|
357
|
+
}
|
358
|
+
if ('min' in schema) {
|
359
|
+
if (!JSON.isNumber(schema['min']))
|
360
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.min to be a Number`));
|
361
|
+
validator.min(schema['min']);
|
362
|
+
}
|
363
|
+
if ('max' in schema) {
|
364
|
+
if (!JSON.isNumber(schema['max']))
|
365
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.max to be a Number`));
|
366
|
+
validator.max(schema['max']);
|
367
|
+
}
|
368
|
+
if ('every' in schema) {
|
369
|
+
if (!JSON.isObject(schema['every']))
|
370
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.every to be an Object`));
|
371
|
+
const itemValidator = Schema.fromJSON(schema['every'], `${path}.every`, registry);
|
372
|
+
if (itemValidator.isOk())
|
373
|
+
validator.every(itemValidator.value);
|
374
|
+
if (itemValidator.isErr())
|
375
|
+
return itemValidator;
|
376
|
+
}
|
377
|
+
if ('tuple' in schema) {
|
378
|
+
if (!JSON.isArray(schema['tuple']))
|
379
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.tuple to be an Array`));
|
380
|
+
const validatorResults = [{}, {}];
|
381
|
+
for (let i = 0; i < schema['tuple'].length; i++) {
|
382
|
+
const value = schema['tuple'][i];
|
383
|
+
if (!JSON.isObject(value))
|
384
|
+
validatorResults[1][i] = new SchemaError(`Expected ${path}.tuple[${i}] to be an Object`);
|
385
|
+
Schema.fromJSON(value, `${path}.tuple[${i}]`, registry).match(value => { validatorResults[0][i] = value; }, error => { validatorResults[1][i] = error; });
|
386
|
+
}
|
387
|
+
if (Object.keys(validatorResults[1]).length > 0)
|
388
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.tuple to be an Array where every Element is a valid Schema`, { cause: validatorResults[1] }));
|
389
|
+
validator.tuple(Object.entries(validatorResults[0]).map(([_, value]) => value));
|
390
|
+
}
|
391
|
+
return result_1.Result.Ok(validator);
|
392
|
+
}
|
393
|
+
#nullable;
|
394
|
+
#every;
|
395
|
+
#min;
|
396
|
+
#max;
|
397
|
+
#tuple;
|
398
|
+
constructor() {
|
399
|
+
super();
|
400
|
+
this.#nullable = { flag: false };
|
401
|
+
this.#every = { flag: false, value: new AnyValidator() };
|
402
|
+
this.#min = { flag: false, value: 0 };
|
403
|
+
this.#max = { flag: false, value: 0 };
|
404
|
+
this.#tuple = { flag: false, value: [] };
|
405
|
+
}
|
406
|
+
nullable(flag = true) {
|
407
|
+
this.#nullable = { flag };
|
408
|
+
return this;
|
409
|
+
}
|
410
|
+
min(value) {
|
411
|
+
this.#min = { flag: true, value };
|
412
|
+
return this;
|
413
|
+
}
|
414
|
+
max(value) {
|
415
|
+
this.#max = { flag: true, value };
|
416
|
+
return this;
|
417
|
+
}
|
418
|
+
every(validator) {
|
419
|
+
this.#every = { flag: true, value: validator };
|
420
|
+
return this;
|
421
|
+
}
|
422
|
+
tuple(validators) {
|
423
|
+
this.#tuple = { flag: true, value: validators };
|
424
|
+
return this;
|
425
|
+
}
|
426
|
+
validate(data, path = 'data') {
|
427
|
+
if (JSON.isArray(data)) {
|
428
|
+
if (this.#min.flag && this.#min.value > data.length)
|
429
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be at least ${this.#min.value} Elements long`));
|
430
|
+
if (this.#max.flag && this.#max.value < data.length)
|
431
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be at most ${this.#max.value} Elements long`));
|
432
|
+
if (this.#every.flag) {
|
433
|
+
const errors = data
|
434
|
+
.map((value, index) => this.#every.value.validate(value, `${path}[${index}]`))
|
435
|
+
.filter(value => value.isErr())
|
436
|
+
.map(value => value.error);
|
437
|
+
if (errors.length > 0)
|
438
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be an Array where every Element matches the item Validator`, { cause: errors }));
|
439
|
+
}
|
440
|
+
if (this.#tuple.flag) {
|
441
|
+
if (this.#tuple.value.length !== data.length)
|
442
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to have exactly ${this.#tuple.value.length} Elements`));
|
443
|
+
const errors = this.#tuple.value
|
444
|
+
.map((validator, index) => validator.validate(data[index], `${path}[${index}]`))
|
445
|
+
.filter(value => value.isErr())
|
446
|
+
.map(value => value.error);
|
447
|
+
if (errors.length > 0)
|
448
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be a Tuple where every Element matches its respective Validator`, { cause: errors }));
|
449
|
+
}
|
450
|
+
return result_1.Result.Ok(void 0);
|
451
|
+
}
|
452
|
+
if (this.#nullable.flag && JSON.isNull(data))
|
453
|
+
return result_1.Result.Ok(void 0);
|
454
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be an Array${this.#nullable.flag ? '' : ' or Null'}`));
|
455
|
+
}
|
456
|
+
toJSON() {
|
457
|
+
const schema = {
|
458
|
+
type: 'array'
|
459
|
+
};
|
460
|
+
if (this.#nullable.flag)
|
461
|
+
schema['nullable'] = this.#nullable.flag;
|
462
|
+
if (this.#min.flag)
|
463
|
+
schema['min'] = this.#min.value;
|
464
|
+
if (this.#max.flag)
|
465
|
+
schema['max'] = this.#max.value;
|
466
|
+
if (this.#every.flag)
|
467
|
+
schema['every'] = this.#every.value.toJSON();
|
468
|
+
if (this.#tuple.flag)
|
469
|
+
schema['tuple'] = this.#tuple.value.map(validator => validator.toJSON());
|
470
|
+
return schema;
|
471
|
+
}
|
472
|
+
}
|
473
|
+
class ObjectValidator extends Schema {
|
474
|
+
static fromJSON(schema, path = "schema", registry = Schema.defaultRegistry) {
|
475
|
+
const validator = new ObjectValidator();
|
476
|
+
if (!JSON.isObject(schema))
|
477
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path} to be an Object`));
|
478
|
+
if ('nullable' in schema) {
|
479
|
+
if (!JSON.isBoolean(schema['nullable']))
|
480
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.nullable to be a Boolean`));
|
481
|
+
validator.nullable(schema['nullable']);
|
482
|
+
}
|
483
|
+
if ('schema' in schema) {
|
484
|
+
if (!JSON.isObject(schema['schema']))
|
485
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.schema to be an Object`));
|
486
|
+
const validatorResults = [{}, {}];
|
487
|
+
for (let [key, value] of Object.entries(schema['schema'])) {
|
488
|
+
if (!JSON.isObject(value))
|
489
|
+
validatorResults[1][key] = new SchemaError(`Expected ${path}.schema.${key} to be an Object`);
|
490
|
+
Schema.fromJSON(value, path, registry).match(value => { validatorResults[0][key] = value; }, error => { validatorResults[1][key] = error; });
|
491
|
+
}
|
492
|
+
if (Object.keys(validatorResults[1]).length > 0)
|
493
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.schema to be an Object where every Property is a valid Schema`, { cause: validatorResults[1] }));
|
494
|
+
validator.schema(validatorResults[0]);
|
495
|
+
}
|
496
|
+
if ('inclusive' in schema) {
|
497
|
+
if (!JSON.isBoolean(schema['inclusive']))
|
498
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.inclusive to be a Boolean`));
|
499
|
+
validator.inclusive(schema['inclusive']);
|
500
|
+
}
|
501
|
+
return result_1.Result.Ok(validator);
|
502
|
+
}
|
503
|
+
#nullable;
|
504
|
+
#schema;
|
505
|
+
#inclusive;
|
506
|
+
constructor() {
|
507
|
+
super();
|
508
|
+
this.#nullable = { flag: false };
|
509
|
+
this.#schema = { flag: false, value: {} };
|
510
|
+
this.#inclusive = { flag: false };
|
511
|
+
}
|
512
|
+
nullable(flag = true) {
|
513
|
+
this.#nullable = { flag };
|
514
|
+
return this;
|
515
|
+
}
|
516
|
+
inclusive(flag = true) {
|
517
|
+
this.#inclusive = { flag };
|
518
|
+
return this;
|
519
|
+
}
|
520
|
+
schema(value, flag = true) {
|
521
|
+
this.#schema = { flag, value };
|
522
|
+
return this;
|
523
|
+
}
|
524
|
+
validate(data, path = 'data') {
|
525
|
+
if (JSON.isObject(data)) {
|
526
|
+
if (this.#schema.flag) {
|
527
|
+
const errors = {};
|
528
|
+
for (let [key, validator] of Object.entries(this.#schema.value)) {
|
529
|
+
const result = validator.validate(data[key], `${path}.${key}`);
|
530
|
+
if (result.isErr())
|
531
|
+
errors[key] = result.error;
|
532
|
+
}
|
533
|
+
if (Object.keys(errors).length > 0)
|
534
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be an Object where every Property matches the Schema Constraint`, { cause: errors }));
|
535
|
+
// If inclusive is not set and the Object has more Properties than the Schema, return an Error
|
536
|
+
if (!this.#inclusive.flag && Object.keys(data).length !== Object.keys(this.#schema.value).length) {
|
537
|
+
const schemaKeys = Object.keys(this.#schema.value);
|
538
|
+
const errors = Object.keys(data).filter(key => !schemaKeys.includes(key))
|
539
|
+
.map(key => new ValidationError(`Expected ${path}.${key} not to exist on this Schema`));
|
540
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to have only the Properties defined in the Schema`, { cause: errors }));
|
541
|
+
}
|
542
|
+
}
|
543
|
+
return result_1.Result.Ok(void 0);
|
544
|
+
}
|
545
|
+
if (this.#nullable.flag && JSON.isNull(data))
|
546
|
+
return result_1.Result.Ok(void 0);
|
547
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be an Object${this.#nullable.flag ? '' : ' or Null'}`));
|
548
|
+
}
|
549
|
+
toJSON() {
|
550
|
+
const schema = {
|
551
|
+
type: 'object'
|
552
|
+
};
|
553
|
+
if (this.#nullable.flag)
|
554
|
+
schema['nullable'] = this.#nullable.flag;
|
555
|
+
if (this.#schema.flag)
|
556
|
+
schema['schema'] = Object.fromEntries(Object.entries(this.#schema.value).map(([key, value]) => [key, value.toJSON()]));
|
557
|
+
if (this.#inclusive.flag)
|
558
|
+
schema['inclusive'] = this.#inclusive.flag;
|
559
|
+
return schema;
|
560
|
+
}
|
561
|
+
}
|
562
|
+
class OrValidator extends Schema {
|
563
|
+
static fromJSON(schema, path = "schema", registry = Schema.defaultRegistry) {
|
564
|
+
const validator = new OrValidator();
|
565
|
+
if (!JSON.isObject(schema))
|
566
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path} to be an Object`));
|
567
|
+
if ('oneOf' in schema) {
|
568
|
+
if (!JSON.isArray(schema['oneOf']))
|
569
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.oneOf to be an Array`));
|
570
|
+
const validatorResults = schema['oneOf']
|
571
|
+
.map(value => Schema.fromJSON(value, `${path}.oneOf`, registry))
|
572
|
+
.reduce((acc, result) => {
|
573
|
+
return result.match(value => [[...acc[0], value], acc[1]], error => [acc[0], [...acc[1], error]]);
|
574
|
+
}, [[], []]);
|
575
|
+
if (validatorResults[1].length > 0)
|
576
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.oneOf to be an Array where every Element is a valid Schema`, { cause: validatorResults[1] }));
|
577
|
+
validator.oneOf(validatorResults[0]);
|
578
|
+
}
|
579
|
+
return result_1.Result.Ok(validator);
|
580
|
+
}
|
581
|
+
#oneOf;
|
582
|
+
constructor() {
|
583
|
+
super();
|
584
|
+
this.#oneOf = { flag: false, value: [] };
|
585
|
+
}
|
586
|
+
oneOf(validators) {
|
587
|
+
this.#oneOf = { flag: true, value: validators };
|
588
|
+
return this;
|
589
|
+
}
|
590
|
+
validate(data, path = 'data') {
|
591
|
+
if (!JSON.isJSON(data))
|
592
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be JSON`));
|
593
|
+
if (this.#oneOf.flag) {
|
594
|
+
let errors = this.#oneOf.value
|
595
|
+
.map(validator => validator.validate(data, path))
|
596
|
+
.reduce((acc, value) => {
|
597
|
+
return value.match(_ => acc, error => [...acc, error]);
|
598
|
+
}, []);
|
599
|
+
if (errors.length === this.#oneOf.value.length)
|
600
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to match at least one of the OneOf Validators`, { cause: errors }));
|
601
|
+
return result_1.Result.Ok(void 0);
|
602
|
+
}
|
603
|
+
return result_1.Result.Ok(void 0);
|
604
|
+
}
|
605
|
+
toJSON() {
|
606
|
+
const schema = {
|
607
|
+
type: 'or'
|
608
|
+
};
|
609
|
+
if (this.#oneOf.flag)
|
610
|
+
schema['oneOf'] = this.#oneOf.value.map(validator => validator.toJSON());
|
611
|
+
return schema;
|
612
|
+
}
|
613
|
+
}
|
614
|
+
class AndValidator extends Schema {
|
615
|
+
static fromJSON(schema, path = "schema", registry = Schema.defaultRegistry) {
|
616
|
+
const validator = new AndValidator();
|
617
|
+
if (!JSON.isObject(schema))
|
618
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path} to be an Object`));
|
619
|
+
if ('allOf' in schema) {
|
620
|
+
if (!JSON.isArray(schema['allOf']))
|
621
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.allOf to be an Array`));
|
622
|
+
const validatorResults = [[], []];
|
623
|
+
for (let i = 0; i < schema['allOf'].length; i++) {
|
624
|
+
const value = schema['allOf'][i];
|
625
|
+
if (!JSON.isObject(value))
|
626
|
+
validatorResults[1][i] = new SchemaError(`Expected ${path}.allOf[${i}] to be an Object`);
|
627
|
+
else
|
628
|
+
Schema.fromJSON(value, `${path}.allOf[${i}]`, registry).match(value => { validatorResults[0].push(value); }, error => { validatorResults[1].push(error); });
|
629
|
+
}
|
630
|
+
if (validatorResults[1].length > 0)
|
631
|
+
return result_1.Result.Err(new SchemaError(`Expected ${path}.allOf to be an Array where every Element is a valid Schema`, { cause: validatorResults[1] }));
|
632
|
+
validator.allOf(Object.entries(validatorResults[0]).map(([_, value]) => value));
|
633
|
+
}
|
634
|
+
return result_1.Result.Ok(validator);
|
635
|
+
}
|
636
|
+
#allOf;
|
637
|
+
constructor() {
|
638
|
+
super();
|
639
|
+
this.#allOf = { flag: false, value: [] };
|
640
|
+
}
|
641
|
+
allOf(validators) {
|
642
|
+
this.#allOf = { flag: true, value: validators };
|
643
|
+
return this;
|
644
|
+
}
|
645
|
+
validate(data, path) {
|
646
|
+
if (!JSON.isJSON(data))
|
647
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to be JSON`));
|
648
|
+
if (this.#allOf.flag) {
|
649
|
+
const errors = this.#allOf.value
|
650
|
+
.map(validator => validator.validate(data, path))
|
651
|
+
.reduce((acc, value) => {
|
652
|
+
return value.match(_ => acc, error => [...acc, error]);
|
653
|
+
}, []);
|
654
|
+
if (errors.length > 0)
|
655
|
+
return result_1.Result.Err(new ValidationError(`Expected ${path} to match all of the AllOf Validators`, { cause: errors }));
|
656
|
+
}
|
657
|
+
return result_1.Result.Ok(void 0);
|
658
|
+
}
|
659
|
+
toJSON() {
|
660
|
+
const schema = {
|
661
|
+
type: 'and'
|
662
|
+
};
|
663
|
+
if (this.#allOf.flag)
|
664
|
+
schema['allOf'] = this.#allOf.value.map(validator => validator.toJSON());
|
665
|
+
return schema;
|
666
|
+
}
|
667
|
+
}
|