adorn-api 1.0.34 → 1.0.36
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/package.json +2 -2
- package/dist/core/validation.d.ts +0 -10
- package/dist/core/validation.js +0 -533
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adorn-api",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.36",
|
|
4
4
|
"description": "Decorator-first web framework with OpenAPI 3.1 schema generation.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"express": "^4.19.2",
|
|
18
|
-
"metal-orm": "^1.0.
|
|
18
|
+
"metal-orm": "^1.0.104"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@electric-sql/pglite": "^0.3.15",
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { SchemaSource } from "./schema";
|
|
2
|
-
import { ValidationError } from "./validation-errors";
|
|
3
|
-
/**
|
|
4
|
-
* Validates a value against a schema source.
|
|
5
|
-
* @param value Value to validate
|
|
6
|
-
* @param schema Schema source (schema node or DTO constructor)
|
|
7
|
-
* @param path Optional field path for error reporting
|
|
8
|
-
* @returns Array of validation errors
|
|
9
|
-
*/
|
|
10
|
-
export declare function validate(value: unknown, schema: SchemaSource, path?: string): ValidationError[];
|
package/dist/core/validation.js
DELETED
|
@@ -1,533 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validate = validate;
|
|
4
|
-
const metadata_1 = require("./metadata");
|
|
5
|
-
/**
|
|
6
|
-
* Validates a value against a schema source.
|
|
7
|
-
* @param value Value to validate
|
|
8
|
-
* @param schema Schema source (schema node or DTO constructor)
|
|
9
|
-
* @param path Optional field path for error reporting
|
|
10
|
-
* @returns Array of validation errors
|
|
11
|
-
*/
|
|
12
|
-
function validate(value, schema, path = "") {
|
|
13
|
-
if (typeof schema === "function") {
|
|
14
|
-
return validateDto(value, schema, path);
|
|
15
|
-
}
|
|
16
|
-
return validateSchemaNode(value, schema, path);
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Validates a value against a DTO schema.
|
|
20
|
-
* @param value Value to validate
|
|
21
|
-
* @param dto DTO constructor
|
|
22
|
-
* @param path Optional field path for error reporting
|
|
23
|
-
* @returns Array of validation errors
|
|
24
|
-
*/
|
|
25
|
-
function validateDto(value, dto, path) {
|
|
26
|
-
const dtoMeta = (0, metadata_1.getDtoMeta)(dto);
|
|
27
|
-
if (!dtoMeta) {
|
|
28
|
-
throw new Error(`DTO "${dto.name}" is missing @Dto decorator.`);
|
|
29
|
-
}
|
|
30
|
-
const errors = [];
|
|
31
|
-
// Check if value is an object
|
|
32
|
-
if (value === null || value === undefined) {
|
|
33
|
-
errors.push({
|
|
34
|
-
field: path,
|
|
35
|
-
message: "must be an object",
|
|
36
|
-
value
|
|
37
|
-
});
|
|
38
|
-
return errors;
|
|
39
|
-
}
|
|
40
|
-
if (typeof value !== "object") {
|
|
41
|
-
errors.push({
|
|
42
|
-
field: path,
|
|
43
|
-
message: "must be an object",
|
|
44
|
-
value
|
|
45
|
-
});
|
|
46
|
-
return errors;
|
|
47
|
-
}
|
|
48
|
-
// Validate each field
|
|
49
|
-
for (const [fieldName, fieldMeta] of Object.entries(dtoMeta.fields)) {
|
|
50
|
-
const fieldPath = path ? `${path}.${fieldName}` : fieldName;
|
|
51
|
-
const fieldValue = value[fieldName];
|
|
52
|
-
const isOptional = fieldMeta.optional ?? fieldMeta.schema.optional ?? false;
|
|
53
|
-
// Check required field
|
|
54
|
-
if (!isOptional && fieldValue === undefined) {
|
|
55
|
-
errors.push({
|
|
56
|
-
field: fieldPath,
|
|
57
|
-
message: "is required",
|
|
58
|
-
value: fieldValue
|
|
59
|
-
});
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
// Validate field value if present
|
|
63
|
-
if (fieldValue !== undefined && fieldValue !== null) {
|
|
64
|
-
const fieldErrors = validateSchemaNode(fieldValue, fieldMeta.schema, fieldPath);
|
|
65
|
-
errors.push(...fieldErrors);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
// Check additional properties
|
|
69
|
-
if (!dtoMeta.additionalProperties) {
|
|
70
|
-
const valueKeys = Object.keys(value);
|
|
71
|
-
const dtoKeys = Object.keys(dtoMeta.fields);
|
|
72
|
-
const additionalKeys = valueKeys.filter(key => !dtoKeys.includes(key));
|
|
73
|
-
for (const key of additionalKeys) {
|
|
74
|
-
errors.push({
|
|
75
|
-
field: path ? `${path}.${key}` : key,
|
|
76
|
-
message: "is not a valid field",
|
|
77
|
-
value: value[key]
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return errors;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Validates a value against a schema node.
|
|
85
|
-
* @param value Value to validate
|
|
86
|
-
* @param schema Schema node to validate against
|
|
87
|
-
* @param path Optional field path for error reporting
|
|
88
|
-
* @returns Array of validation errors
|
|
89
|
-
*/
|
|
90
|
-
function validateSchemaNode(value, schema, path) {
|
|
91
|
-
const errors = [];
|
|
92
|
-
// Handle optional fields
|
|
93
|
-
if (schema.optional && value === undefined) {
|
|
94
|
-
return errors;
|
|
95
|
-
}
|
|
96
|
-
// Handle nullable fields
|
|
97
|
-
if (schema.nullable && value === null) {
|
|
98
|
-
return errors;
|
|
99
|
-
}
|
|
100
|
-
// Validate based on schema kind
|
|
101
|
-
switch (schema.kind) {
|
|
102
|
-
case "string":
|
|
103
|
-
errors.push(...validateString(value, schema, path));
|
|
104
|
-
break;
|
|
105
|
-
case "number":
|
|
106
|
-
case "integer":
|
|
107
|
-
errors.push(...validateNumber(value, schema, path));
|
|
108
|
-
break;
|
|
109
|
-
case "boolean":
|
|
110
|
-
errors.push(...validateBoolean(value, schema, path));
|
|
111
|
-
break;
|
|
112
|
-
case "array":
|
|
113
|
-
errors.push(...validateArray(value, schema, path));
|
|
114
|
-
break;
|
|
115
|
-
case "object":
|
|
116
|
-
errors.push(...validateObject(value, schema, path));
|
|
117
|
-
break;
|
|
118
|
-
case "enum":
|
|
119
|
-
errors.push(...validateEnum(value, schema, path));
|
|
120
|
-
break;
|
|
121
|
-
case "literal":
|
|
122
|
-
errors.push(...validateLiteral(value, schema, path));
|
|
123
|
-
break;
|
|
124
|
-
case "union":
|
|
125
|
-
errors.push(...validateUnion(value, schema, path));
|
|
126
|
-
break;
|
|
127
|
-
case "record":
|
|
128
|
-
errors.push(...validateRecord(value, schema, path));
|
|
129
|
-
break;
|
|
130
|
-
case "ref":
|
|
131
|
-
errors.push(...validate(value, schema.dto, path));
|
|
132
|
-
break;
|
|
133
|
-
case "any":
|
|
134
|
-
// Any type accepts all values
|
|
135
|
-
break;
|
|
136
|
-
case "null":
|
|
137
|
-
errors.push(...validateNull(value, schema, path));
|
|
138
|
-
break;
|
|
139
|
-
case "file":
|
|
140
|
-
// File validation is handled separately
|
|
141
|
-
break;
|
|
142
|
-
default:
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
return errors;
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Validates a string value.
|
|
149
|
-
*/
|
|
150
|
-
function validateString(value, schema, path) {
|
|
151
|
-
const errors = [];
|
|
152
|
-
if (typeof value !== "string") {
|
|
153
|
-
errors.push({
|
|
154
|
-
field: path,
|
|
155
|
-
message: "must be a string",
|
|
156
|
-
value
|
|
157
|
-
});
|
|
158
|
-
return errors;
|
|
159
|
-
}
|
|
160
|
-
if (schema.minLength !== undefined && value.length < schema.minLength) {
|
|
161
|
-
errors.push({
|
|
162
|
-
field: path,
|
|
163
|
-
message: `must be at least ${schema.minLength} characters long`,
|
|
164
|
-
value
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
if (schema.maxLength !== undefined && value.length > schema.maxLength) {
|
|
168
|
-
errors.push({
|
|
169
|
-
field: path,
|
|
170
|
-
message: `must be at most ${schema.maxLength} characters long`,
|
|
171
|
-
value
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
if (schema.pattern && !new RegExp(schema.pattern).test(value)) {
|
|
175
|
-
errors.push({
|
|
176
|
-
field: path,
|
|
177
|
-
message: `must match pattern ${schema.pattern}`,
|
|
178
|
-
value
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
if (schema.format) {
|
|
182
|
-
const formatErrors = validateFormat(value, schema.format, path);
|
|
183
|
-
errors.push(...formatErrors);
|
|
184
|
-
}
|
|
185
|
-
return errors;
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Validates string format.
|
|
189
|
-
*/
|
|
190
|
-
function validateFormat(value, format, path) {
|
|
191
|
-
const errors = [];
|
|
192
|
-
switch (format) {
|
|
193
|
-
case "uuid":
|
|
194
|
-
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
195
|
-
if (!uuidPattern.test(value)) {
|
|
196
|
-
errors.push({
|
|
197
|
-
field: path,
|
|
198
|
-
message: "must be a valid UUID",
|
|
199
|
-
value
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
break;
|
|
203
|
-
case "date-time":
|
|
204
|
-
const date = new Date(value);
|
|
205
|
-
if (isNaN(date.getTime())) {
|
|
206
|
-
errors.push({
|
|
207
|
-
field: path,
|
|
208
|
-
message: "must be a valid date-time",
|
|
209
|
-
value
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
break;
|
|
213
|
-
}
|
|
214
|
-
return errors;
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Validates a number value.
|
|
218
|
-
*/
|
|
219
|
-
function validateNumber(value, schema, path) {
|
|
220
|
-
const errors = [];
|
|
221
|
-
if (typeof value !== "number") {
|
|
222
|
-
errors.push({
|
|
223
|
-
field: path,
|
|
224
|
-
message: `must be a ${schema.kind}`,
|
|
225
|
-
value
|
|
226
|
-
});
|
|
227
|
-
return errors;
|
|
228
|
-
}
|
|
229
|
-
if (schema.kind === "integer" && !Number.isInteger(value)) {
|
|
230
|
-
errors.push({
|
|
231
|
-
field: path,
|
|
232
|
-
message: "must be an integer",
|
|
233
|
-
value
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
if (schema.minimum !== undefined && value < schema.minimum) {
|
|
237
|
-
errors.push({
|
|
238
|
-
field: path,
|
|
239
|
-
message: `must be at least ${schema.minimum}`,
|
|
240
|
-
value
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
if (schema.maximum !== undefined && value > schema.maximum) {
|
|
244
|
-
errors.push({
|
|
245
|
-
field: path,
|
|
246
|
-
message: `must be at most ${schema.maximum}`,
|
|
247
|
-
value
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
if (schema.exclusiveMinimum !== undefined && value <= schema.exclusiveMinimum) {
|
|
251
|
-
errors.push({
|
|
252
|
-
field: path,
|
|
253
|
-
message: `must be greater than ${schema.exclusiveMinimum}`,
|
|
254
|
-
value
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
if (schema.exclusiveMaximum !== undefined && value >= schema.exclusiveMaximum) {
|
|
258
|
-
errors.push({
|
|
259
|
-
field: path,
|
|
260
|
-
message: `must be less than ${schema.exclusiveMaximum}`,
|
|
261
|
-
value
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
if (schema.multipleOf !== undefined && value % schema.multipleOf !== 0) {
|
|
265
|
-
errors.push({
|
|
266
|
-
field: path,
|
|
267
|
-
message: `must be a multiple of ${schema.multipleOf}`,
|
|
268
|
-
value
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
return errors;
|
|
272
|
-
}
|
|
273
|
-
/**
|
|
274
|
-
* Validates a boolean value.
|
|
275
|
-
*/
|
|
276
|
-
function validateBoolean(value, schema, path) {
|
|
277
|
-
const errors = [];
|
|
278
|
-
if (typeof value !== "boolean") {
|
|
279
|
-
errors.push({
|
|
280
|
-
field: path,
|
|
281
|
-
message: "must be a boolean",
|
|
282
|
-
value
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
return errors;
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* Validates an array value.
|
|
289
|
-
*/
|
|
290
|
-
function validateArray(value, schema, path) {
|
|
291
|
-
const errors = [];
|
|
292
|
-
if (!Array.isArray(value)) {
|
|
293
|
-
errors.push({
|
|
294
|
-
field: path,
|
|
295
|
-
message: "must be an array",
|
|
296
|
-
value
|
|
297
|
-
});
|
|
298
|
-
return errors;
|
|
299
|
-
}
|
|
300
|
-
if (schema.minItems !== undefined && value.length < schema.minItems) {
|
|
301
|
-
errors.push({
|
|
302
|
-
field: path,
|
|
303
|
-
message: `must have at least ${schema.minItems} items`,
|
|
304
|
-
value
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
if (schema.maxItems !== undefined && value.length > schema.maxItems) {
|
|
308
|
-
errors.push({
|
|
309
|
-
field: path,
|
|
310
|
-
message: `must have at most ${schema.maxItems} items`,
|
|
311
|
-
value
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
if (schema.uniqueItems && hasDuplicates(value)) {
|
|
315
|
-
errors.push({
|
|
316
|
-
field: path,
|
|
317
|
-
message: "must contain unique items",
|
|
318
|
-
value
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
// Validate each item
|
|
322
|
-
for (let i = 0; i < value.length; i++) {
|
|
323
|
-
const itemErrors = validateSchemaNode(value[i], schema.items, `${path}[${i}]`);
|
|
324
|
-
errors.push(...itemErrors);
|
|
325
|
-
}
|
|
326
|
-
return errors;
|
|
327
|
-
}
|
|
328
|
-
function hasDuplicates(arr) {
|
|
329
|
-
const seen = new Set();
|
|
330
|
-
for (const item of arr) {
|
|
331
|
-
const serialized = JSON.stringify(item);
|
|
332
|
-
if (seen.has(serialized)) {
|
|
333
|
-
return true;
|
|
334
|
-
}
|
|
335
|
-
seen.add(serialized);
|
|
336
|
-
}
|
|
337
|
-
return false;
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* Validates an object value.
|
|
341
|
-
*/
|
|
342
|
-
function validateObject(value, schema, path) {
|
|
343
|
-
const errors = [];
|
|
344
|
-
if (value === null || value === undefined) {
|
|
345
|
-
errors.push({
|
|
346
|
-
field: path,
|
|
347
|
-
message: "must be an object",
|
|
348
|
-
value
|
|
349
|
-
});
|
|
350
|
-
return errors;
|
|
351
|
-
}
|
|
352
|
-
if (typeof value !== "object") {
|
|
353
|
-
errors.push({
|
|
354
|
-
field: path,
|
|
355
|
-
message: "must be an object",
|
|
356
|
-
value
|
|
357
|
-
});
|
|
358
|
-
return errors;
|
|
359
|
-
}
|
|
360
|
-
if (schema.properties) {
|
|
361
|
-
// Validate properties
|
|
362
|
-
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
363
|
-
const propPath = path ? `${path}.${propName}` : propName;
|
|
364
|
-
const propValue = value[propName];
|
|
365
|
-
const isRequired = schema.required?.includes(propName) ?? false;
|
|
366
|
-
if (isRequired && propValue === undefined) {
|
|
367
|
-
errors.push({
|
|
368
|
-
field: propPath,
|
|
369
|
-
message: "is required",
|
|
370
|
-
value: propValue
|
|
371
|
-
});
|
|
372
|
-
continue;
|
|
373
|
-
}
|
|
374
|
-
if (propValue !== undefined && propValue !== null) {
|
|
375
|
-
const propErrors = validateSchemaNode(propValue, propSchema, propPath);
|
|
376
|
-
errors.push(...propErrors);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
// Validate additional properties
|
|
381
|
-
if (schema.additionalProperties === false) {
|
|
382
|
-
const valueKeys = Object.keys(value);
|
|
383
|
-
const schemaKeys = schema.properties ? Object.keys(schema.properties) : [];
|
|
384
|
-
const additionalKeys = valueKeys.filter(key => !schemaKeys.includes(key));
|
|
385
|
-
for (const key of additionalKeys) {
|
|
386
|
-
errors.push({
|
|
387
|
-
field: path ? `${path}.${key}` : key,
|
|
388
|
-
message: "is not a valid field",
|
|
389
|
-
value: value[key]
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
else if (typeof schema.additionalProperties === "object") {
|
|
394
|
-
const valueKeys = Object.keys(value);
|
|
395
|
-
const schemaKeys = schema.properties ? Object.keys(schema.properties) : [];
|
|
396
|
-
const additionalKeys = valueKeys.filter(key => !schemaKeys.includes(key));
|
|
397
|
-
for (const key of additionalKeys) {
|
|
398
|
-
const additionalPath = path ? `${path}.${key}` : key;
|
|
399
|
-
const additionalValue = value[key];
|
|
400
|
-
const additionalErrors = validateSchemaNode(additionalValue, schema.additionalProperties, additionalPath);
|
|
401
|
-
errors.push(...additionalErrors);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
if (schema.minProperties !== undefined && Object.keys(value).length < schema.minProperties) {
|
|
405
|
-
errors.push({
|
|
406
|
-
field: path,
|
|
407
|
-
message: `must have at least ${schema.minProperties} properties`,
|
|
408
|
-
value
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
if (schema.maxProperties !== undefined && Object.keys(value).length > schema.maxProperties) {
|
|
412
|
-
errors.push({
|
|
413
|
-
field: path,
|
|
414
|
-
message: `must have at most ${schema.maxProperties} properties`,
|
|
415
|
-
value
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
return errors;
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* Validates an enum value.
|
|
422
|
-
*/
|
|
423
|
-
function validateEnum(value, schema, path) {
|
|
424
|
-
const errors = [];
|
|
425
|
-
if (!schema.values.some(v => isEqual(v, value))) {
|
|
426
|
-
errors.push({
|
|
427
|
-
field: path,
|
|
428
|
-
message: `must be one of: ${schema.values.map(v => JSON.stringify(v)).join(", ")}`,
|
|
429
|
-
value
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
return errors;
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Validates a literal value.
|
|
436
|
-
*/
|
|
437
|
-
function validateLiteral(value, schema, path) {
|
|
438
|
-
const errors = [];
|
|
439
|
-
if (!isEqual(schema.value, value)) {
|
|
440
|
-
errors.push({
|
|
441
|
-
field: path,
|
|
442
|
-
message: `must be ${JSON.stringify(schema.value)}`,
|
|
443
|
-
value
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
return errors;
|
|
447
|
-
}
|
|
448
|
-
/**
|
|
449
|
-
* Validates a union value.
|
|
450
|
-
*/
|
|
451
|
-
function validateUnion(value, schema, path) {
|
|
452
|
-
// For union types, value must validate against at least one schema
|
|
453
|
-
const validSchemas = schema.anyOf.filter(s => {
|
|
454
|
-
const errors = validateSchemaNode(value, s, path);
|
|
455
|
-
return errors.length === 0;
|
|
456
|
-
});
|
|
457
|
-
if (validSchemas.length === 0) {
|
|
458
|
-
return [
|
|
459
|
-
{
|
|
460
|
-
field: path,
|
|
461
|
-
message: "must match one of the allowed types",
|
|
462
|
-
value
|
|
463
|
-
}
|
|
464
|
-
];
|
|
465
|
-
}
|
|
466
|
-
return [];
|
|
467
|
-
}
|
|
468
|
-
/**
|
|
469
|
-
* Validates a record value.
|
|
470
|
-
*/
|
|
471
|
-
function validateRecord(value, schema, path) {
|
|
472
|
-
const errors = [];
|
|
473
|
-
if (value === null || value === undefined) {
|
|
474
|
-
errors.push({
|
|
475
|
-
field: path,
|
|
476
|
-
message: "must be an object",
|
|
477
|
-
value
|
|
478
|
-
});
|
|
479
|
-
return errors;
|
|
480
|
-
}
|
|
481
|
-
if (typeof value !== "object") {
|
|
482
|
-
errors.push({
|
|
483
|
-
field: path,
|
|
484
|
-
message: "must be an object",
|
|
485
|
-
value
|
|
486
|
-
});
|
|
487
|
-
return errors;
|
|
488
|
-
}
|
|
489
|
-
// Validate each value in the record
|
|
490
|
-
for (const [key, recordValue] of Object.entries(value)) {
|
|
491
|
-
const recordPath = path ? `${path}.${key}` : key;
|
|
492
|
-
const recordErrors = validateSchemaNode(recordValue, schema.values, recordPath);
|
|
493
|
-
errors.push(...recordErrors);
|
|
494
|
-
}
|
|
495
|
-
return errors;
|
|
496
|
-
}
|
|
497
|
-
/**
|
|
498
|
-
* Validates a null value.
|
|
499
|
-
*/
|
|
500
|
-
function validateNull(value, schema, path) {
|
|
501
|
-
const errors = [];
|
|
502
|
-
if (value !== null) {
|
|
503
|
-
errors.push({
|
|
504
|
-
field: path,
|
|
505
|
-
message: "must be null",
|
|
506
|
-
value
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
return errors;
|
|
510
|
-
}
|
|
511
|
-
/**
|
|
512
|
-
* Deep equality check for validation.
|
|
513
|
-
*/
|
|
514
|
-
function isEqual(a, b) {
|
|
515
|
-
if (a === b)
|
|
516
|
-
return true;
|
|
517
|
-
if (typeof a !== typeof b)
|
|
518
|
-
return false;
|
|
519
|
-
if (typeof a === "object") {
|
|
520
|
-
if (a === null || b === null)
|
|
521
|
-
return false;
|
|
522
|
-
const aKeys = Object.keys(a);
|
|
523
|
-
const bKeys = Object.keys(b);
|
|
524
|
-
if (aKeys.length !== bKeys.length)
|
|
525
|
-
return false;
|
|
526
|
-
for (const key of aKeys) {
|
|
527
|
-
if (!isEqual(a[key], b[key]))
|
|
528
|
-
return false;
|
|
529
|
-
}
|
|
530
|
-
return true;
|
|
531
|
-
}
|
|
532
|
-
return false;
|
|
533
|
-
}
|