@aeriajs/validation 0.0.178 → 0.0.180
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/dist/index.js +1 -17
- package/dist/validate.js +83 -91
- package/package.json +7 -11
- package/dist/index.mjs +0 -2
- package/dist/validate.mjs +0 -393
package/dist/index.js
CHANGED
|
@@ -1,17 +1 @@
|
|
|
1
|
-
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./validate.js"), exports);
|
|
1
|
+
export * from './validate.js';
|
package/dist/validate.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const common_1 = require("@aeriajs/common");
|
|
5
|
-
const types_1 = require("@aeriajs/types");
|
|
6
|
-
const entrypoint_1 = require("@aeriajs/entrypoint");
|
|
1
|
+
import { convertConditionToQuery, getMissingProperties } from '@aeriajs/common';
|
|
2
|
+
import { Result, ValidationErrorCode, PropertyValidationErrorCode } from '@aeriajs/types';
|
|
3
|
+
import { getCollection } from '@aeriajs/entrypoint';
|
|
7
4
|
const isValidObjectId = (what) => {
|
|
8
5
|
return /^[0-9a-f]{24}$/.test(what);
|
|
9
6
|
};
|
|
@@ -38,28 +35,27 @@ const makePropertyError = (type, details) => {
|
|
|
38
35
|
details,
|
|
39
36
|
};
|
|
40
37
|
};
|
|
41
|
-
const makeValidationError = (error) => {
|
|
38
|
+
export const makeValidationError = (error) => {
|
|
42
39
|
return error;
|
|
43
40
|
};
|
|
44
|
-
|
|
45
|
-
const validateProperty = (what, property, options = {}) => {
|
|
41
|
+
export const validateProperty = (what, property, options = {}) => {
|
|
46
42
|
if (!property) {
|
|
47
43
|
if (options.parentProperty && 'additionalProperties' in options.parentProperty) {
|
|
48
44
|
if (options.parentProperty.additionalProperties === true) {
|
|
49
|
-
return
|
|
45
|
+
return Result.result(what);
|
|
50
46
|
}
|
|
51
|
-
return
|
|
47
|
+
return validateProperty(what, options.parentProperty.additionalProperties);
|
|
52
48
|
}
|
|
53
49
|
if (options.tolerateExtraneous) {
|
|
54
|
-
return
|
|
50
|
+
return Result.result(undefined);
|
|
55
51
|
}
|
|
56
|
-
return
|
|
52
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.Extraneous));
|
|
57
53
|
}
|
|
58
54
|
if (what === null || what === undefined) {
|
|
59
|
-
return
|
|
55
|
+
return Result.result(what);
|
|
60
56
|
}
|
|
61
57
|
if ('getter' in property) {
|
|
62
|
-
return
|
|
58
|
+
return Result.result(undefined);
|
|
63
59
|
}
|
|
64
60
|
const expectedType = getPropertyType(property);
|
|
65
61
|
const actualType = Array.isArray(what)
|
|
@@ -68,15 +64,15 @@ const validateProperty = (what, property, options = {}) => {
|
|
|
68
64
|
if (actualType !== expectedType
|
|
69
65
|
&& !(actualType === 'number' && expectedType === 'integer')) {
|
|
70
66
|
if (expectedType === 'datetime' && what instanceof Date) {
|
|
71
|
-
return
|
|
67
|
+
return Result.result(what);
|
|
72
68
|
}
|
|
73
69
|
if ('$ref' in property) {
|
|
74
70
|
switch (typeof what) {
|
|
75
71
|
case 'string': {
|
|
76
72
|
if (isValidObjectId(what)) {
|
|
77
|
-
return
|
|
73
|
+
return Result.result(what);
|
|
78
74
|
}
|
|
79
|
-
return
|
|
75
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
80
76
|
expected: expectedType,
|
|
81
77
|
got: actualType,
|
|
82
78
|
}));
|
|
@@ -87,28 +83,28 @@ const validateProperty = (what, property, options = {}) => {
|
|
|
87
83
|
if (expectedType === 'number' && typeof what === 'string') {
|
|
88
84
|
const coerced = parseFloat(what);
|
|
89
85
|
if (!isNaN(coerced)) {
|
|
90
|
-
return
|
|
86
|
+
return Result.result(coerced);
|
|
91
87
|
}
|
|
92
88
|
}
|
|
93
89
|
if (expectedType === 'integer' && typeof what === 'string') {
|
|
94
90
|
const coerced = parseInt(what);
|
|
95
91
|
if (!isNaN(coerced)) {
|
|
96
|
-
return
|
|
92
|
+
return Result.result(coerced);
|
|
97
93
|
}
|
|
98
94
|
}
|
|
99
95
|
if (expectedType === 'string' && typeof what === 'number') {
|
|
100
|
-
return
|
|
96
|
+
return Result.result(String(what));
|
|
101
97
|
}
|
|
102
98
|
if (expectedType === 'datetime' && typeof what === 'string') {
|
|
103
|
-
return
|
|
99
|
+
return Result.result(new Date(what));
|
|
104
100
|
}
|
|
105
101
|
}
|
|
106
102
|
if ((options.coerce || options.coerceObjectIds) && options.objectIdConstructor) {
|
|
107
103
|
if (('$ref' in property || 'format' in property && property.format === 'objectid') && typeof what === 'string') {
|
|
108
|
-
return
|
|
104
|
+
return Result.result(new options.objectIdConstructor(what));
|
|
109
105
|
}
|
|
110
106
|
}
|
|
111
|
-
return
|
|
107
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
112
108
|
expected: expectedType,
|
|
113
109
|
got: actualType,
|
|
114
110
|
}));
|
|
@@ -122,7 +118,7 @@ const validateProperty = (what, property, options = {}) => {
|
|
|
122
118
|
throw new Error;
|
|
123
119
|
}
|
|
124
120
|
if (!(what instanceof options.objectIdConstructor)) {
|
|
125
|
-
return
|
|
121
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.StringConstraint, {
|
|
126
122
|
expected: 'objectid',
|
|
127
123
|
got: 'invalid_objectid',
|
|
128
124
|
}));
|
|
@@ -131,14 +127,14 @@ const validateProperty = (what, property, options = {}) => {
|
|
|
131
127
|
}
|
|
132
128
|
default: {
|
|
133
129
|
if (typeof what !== 'string') {
|
|
134
|
-
return
|
|
130
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
135
131
|
expected: expectedType,
|
|
136
132
|
got: actualType,
|
|
137
133
|
}));
|
|
138
134
|
}
|
|
139
135
|
if ((typeof property.minLength === 'number' && property.minLength > what.length)
|
|
140
136
|
|| (typeof property.maxLength === 'number' && property.maxLength < what.length)) {
|
|
141
|
-
return
|
|
137
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.StringConstraint, {
|
|
142
138
|
expected: 'string',
|
|
143
139
|
got: 'invalid_string',
|
|
144
140
|
}));
|
|
@@ -149,7 +145,7 @@ const validateProperty = (what, property, options = {}) => {
|
|
|
149
145
|
}
|
|
150
146
|
case 'integer': {
|
|
151
147
|
if (!Number.isInteger(what)) {
|
|
152
|
-
return
|
|
148
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.NumericConstraint, {
|
|
153
149
|
expected: 'integer',
|
|
154
150
|
got: 'invalid_number',
|
|
155
151
|
}));
|
|
@@ -157,7 +153,7 @@ const validateProperty = (what, property, options = {}) => {
|
|
|
157
153
|
}
|
|
158
154
|
case 'number': {
|
|
159
155
|
if (typeof what !== 'number') {
|
|
160
|
-
return
|
|
156
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
161
157
|
expected: expectedType,
|
|
162
158
|
got: actualType,
|
|
163
159
|
}));
|
|
@@ -166,7 +162,7 @@ const validateProperty = (what, property, options = {}) => {
|
|
|
166
162
|
|| (typeof property.minimum === 'number' && property.minimum > what)
|
|
167
163
|
|| (typeof property.exclusiveMaximum === 'number' && property.exclusiveMaximum <= what)
|
|
168
164
|
|| (typeof property.exclusiveMinimum === 'number' && property.exclusiveMinimum >= what)) {
|
|
169
|
-
return
|
|
165
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.NumericConstraint, {
|
|
170
166
|
expected: 'number',
|
|
171
167
|
got: 'invalid_number',
|
|
172
168
|
}));
|
|
@@ -175,39 +171,39 @@ const validateProperty = (what, property, options = {}) => {
|
|
|
175
171
|
}
|
|
176
172
|
case 'object': {
|
|
177
173
|
if ('properties' in property) {
|
|
178
|
-
return
|
|
174
|
+
return validate(what, property, options);
|
|
179
175
|
}
|
|
180
176
|
switch (typeof property.additionalProperties) {
|
|
181
|
-
case 'object': return
|
|
182
|
-
case 'boolean': return
|
|
177
|
+
case 'object': return validate(what, property.additionalProperties, options);
|
|
178
|
+
case 'boolean': return Result.result(what);
|
|
183
179
|
}
|
|
184
180
|
}
|
|
185
181
|
case 'array': {
|
|
186
182
|
if (!Array.isArray(what)) {
|
|
187
|
-
return
|
|
183
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
188
184
|
expected: expectedType,
|
|
189
185
|
got: actualType,
|
|
190
186
|
}));
|
|
191
187
|
}
|
|
192
188
|
if (property.minItems) {
|
|
193
189
|
if (what.length < property.minItems) {
|
|
194
|
-
return
|
|
190
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.MoreItemsExpected));
|
|
195
191
|
}
|
|
196
192
|
}
|
|
197
193
|
if (property.maxItems) {
|
|
198
194
|
if (what.length > property.maxItems) {
|
|
199
|
-
return
|
|
195
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.LessItemsExpected));
|
|
200
196
|
}
|
|
201
197
|
}
|
|
202
198
|
let i = 0;
|
|
203
199
|
for (const elem of what) {
|
|
204
|
-
const { error } =
|
|
200
|
+
const { error } = validateProperty(elem, property.items, options);
|
|
205
201
|
if (error) {
|
|
206
202
|
if ('code' in error) {
|
|
207
203
|
continue;
|
|
208
204
|
}
|
|
209
205
|
error.index = i;
|
|
210
|
-
return
|
|
206
|
+
return Result.error(error);
|
|
211
207
|
}
|
|
212
208
|
i++;
|
|
213
209
|
}
|
|
@@ -216,7 +212,7 @@ const validateProperty = (what, property, options = {}) => {
|
|
|
216
212
|
}
|
|
217
213
|
else if ('enum' in property) {
|
|
218
214
|
if (!property.enum.includes(what)) {
|
|
219
|
-
return
|
|
215
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.ExtraneousElement, {
|
|
220
216
|
expected: property.enum,
|
|
221
217
|
got: what,
|
|
222
218
|
}));
|
|
@@ -224,26 +220,25 @@ const validateProperty = (what, property, options = {}) => {
|
|
|
224
220
|
}
|
|
225
221
|
else if ('const' in property) {
|
|
226
222
|
if (what !== property.const) {
|
|
227
|
-
return
|
|
223
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
228
224
|
expected: property.const,
|
|
229
225
|
got: what,
|
|
230
226
|
}));
|
|
231
227
|
}
|
|
232
228
|
}
|
|
233
|
-
return
|
|
229
|
+
return Result.result(what);
|
|
234
230
|
};
|
|
235
|
-
|
|
236
|
-
const validateRefs = async (what, property, options = {}) => {
|
|
231
|
+
export const validateRefs = async (what, property, options = {}) => {
|
|
237
232
|
if ('$ref' in property) {
|
|
238
233
|
if (options.checkObjectIds) {
|
|
239
234
|
if (!options.context || !options.objectIdConstructor) {
|
|
240
235
|
throw new Error;
|
|
241
236
|
}
|
|
242
237
|
if (what === null) {
|
|
243
|
-
return
|
|
238
|
+
return Result.result({});
|
|
244
239
|
}
|
|
245
240
|
if (!isValidObjectId(String(what))) {
|
|
246
|
-
return
|
|
241
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
247
242
|
expected: 'objectid',
|
|
248
243
|
got: typeof what,
|
|
249
244
|
}));
|
|
@@ -255,7 +250,7 @@ const validateRefs = async (what, property, options = {}) => {
|
|
|
255
250
|
{
|
|
256
251
|
_id: new options.objectIdConstructor(what),
|
|
257
252
|
},
|
|
258
|
-
|
|
253
|
+
convertConditionToQuery(property.constraints),
|
|
259
254
|
],
|
|
260
255
|
};
|
|
261
256
|
}
|
|
@@ -270,69 +265,71 @@ const validateRefs = async (what, property, options = {}) => {
|
|
|
270
265
|
},
|
|
271
266
|
});
|
|
272
267
|
if (!exists) {
|
|
273
|
-
return
|
|
268
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.ReferenceConstraint, {
|
|
274
269
|
expected: 'objectid',
|
|
275
270
|
got: 'invalid_objectid',
|
|
276
271
|
}));
|
|
277
272
|
}
|
|
278
|
-
return
|
|
273
|
+
return Result.result({});
|
|
279
274
|
}
|
|
280
275
|
let description;
|
|
281
276
|
if (options.descriptions) {
|
|
282
277
|
description = options.descriptions[property.$ref];
|
|
283
278
|
}
|
|
284
279
|
else {
|
|
285
|
-
const collection = await
|
|
280
|
+
const collection = await getCollection(property.$ref);
|
|
286
281
|
if (!collection) {
|
|
287
282
|
throw new Error;
|
|
288
283
|
}
|
|
289
284
|
description = collection.description;
|
|
290
285
|
}
|
|
291
286
|
if (typeof what !== 'object') {
|
|
292
|
-
return
|
|
287
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
293
288
|
expected: 'object',
|
|
294
289
|
got: typeof what,
|
|
295
290
|
}));
|
|
296
291
|
}
|
|
297
|
-
return
|
|
292
|
+
return validate(what, description, options);
|
|
298
293
|
}
|
|
299
294
|
else if ('items' in property) {
|
|
300
295
|
if (!Array.isArray(what)) {
|
|
301
296
|
throw new Error;
|
|
302
297
|
}
|
|
303
298
|
for (const elem of what) {
|
|
304
|
-
const { error } = await
|
|
299
|
+
const { error } = await validateRefs(elem, property.items, options);
|
|
305
300
|
if (error) {
|
|
306
|
-
return
|
|
301
|
+
return Result.error(error);
|
|
307
302
|
}
|
|
308
303
|
}
|
|
309
304
|
}
|
|
310
305
|
else if ('properties' in property) {
|
|
311
306
|
const details = {};
|
|
312
307
|
for (const propName in what) {
|
|
313
|
-
|
|
308
|
+
if (!(propName in property.properties)) {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
const { error } = await validateRefs(what[propName], property.properties[propName], options);
|
|
314
312
|
if (error) {
|
|
315
313
|
details[propName] = error;
|
|
316
314
|
}
|
|
317
315
|
}
|
|
318
316
|
if (Object.keys(details).length > 0) {
|
|
319
|
-
return
|
|
320
|
-
code:
|
|
317
|
+
return Result.error(makeValidationError({
|
|
318
|
+
code: ValidationErrorCode.InvalidProperties,
|
|
321
319
|
details,
|
|
322
320
|
}));
|
|
323
321
|
}
|
|
324
322
|
}
|
|
325
|
-
return
|
|
323
|
+
return Result.result({});
|
|
326
324
|
};
|
|
327
|
-
|
|
328
|
-
const validateWholeness = (what, schema) => {
|
|
325
|
+
export const validateWholeness = (what, schema) => {
|
|
329
326
|
const required = schema.required
|
|
330
327
|
? schema.required
|
|
331
328
|
: Object.keys(schema.properties);
|
|
332
|
-
const missingProps =
|
|
329
|
+
const missingProps = getMissingProperties(what, schema, required);
|
|
333
330
|
if (missingProps.length > 0) {
|
|
334
|
-
return
|
|
335
|
-
code:
|
|
331
|
+
return makeValidationError({
|
|
332
|
+
code: ValidationErrorCode.MissingProperties,
|
|
336
333
|
details: Object.fromEntries(missingProps.map((error) => [
|
|
337
334
|
error,
|
|
338
335
|
{
|
|
@@ -342,38 +339,37 @@ const validateWholeness = (what, schema) => {
|
|
|
342
339
|
});
|
|
343
340
|
}
|
|
344
341
|
};
|
|
345
|
-
|
|
346
|
-
const validate = (what, schema, options = {}) => {
|
|
342
|
+
export const validate = (what, schema, options = {}) => {
|
|
347
343
|
if (what === undefined) {
|
|
348
|
-
return
|
|
349
|
-
code:
|
|
344
|
+
return Result.error(makeValidationError({
|
|
345
|
+
code: ValidationErrorCode.EmptyTarget,
|
|
350
346
|
details: {},
|
|
351
347
|
}));
|
|
352
348
|
}
|
|
353
349
|
if (!('properties' in schema)) {
|
|
354
|
-
const { error } =
|
|
350
|
+
const { error } = validateProperty(what, schema);
|
|
355
351
|
if (error) {
|
|
356
|
-
return
|
|
352
|
+
return Result.error(error);
|
|
357
353
|
}
|
|
358
|
-
return
|
|
354
|
+
return Result.result(what);
|
|
359
355
|
}
|
|
360
|
-
const wholenessError =
|
|
356
|
+
const wholenessError = validateWholeness(what, schema);
|
|
361
357
|
if (wholenessError) {
|
|
362
358
|
if (options.throwOnError) {
|
|
363
|
-
throw new TypeError(
|
|
359
|
+
throw new TypeError(ValidationErrorCode.MissingProperties);
|
|
364
360
|
}
|
|
365
|
-
return
|
|
361
|
+
return Result.error(wholenessError);
|
|
366
362
|
}
|
|
367
363
|
const details = {};
|
|
368
364
|
const resultCopy = {};
|
|
369
365
|
for (const propName in what) {
|
|
370
|
-
const { error, result: parsed } =
|
|
366
|
+
const { error, result: parsed } = validateProperty(what[propName], schema.properties[propName], {
|
|
371
367
|
...options,
|
|
372
368
|
parentProperty: schema,
|
|
373
369
|
});
|
|
374
370
|
if (error) {
|
|
375
371
|
if (options.throwOnError) {
|
|
376
|
-
throw new TypeError(
|
|
372
|
+
throw new TypeError(ValidationErrorCode.InvalidProperties);
|
|
377
373
|
}
|
|
378
374
|
details[propName] = error;
|
|
379
375
|
continue;
|
|
@@ -383,36 +379,32 @@ const validate = (what, schema, options = {}) => {
|
|
|
383
379
|
}
|
|
384
380
|
}
|
|
385
381
|
if (Object.keys(details).length > 0) {
|
|
386
|
-
return
|
|
387
|
-
code:
|
|
382
|
+
return Result.error(makeValidationError({
|
|
383
|
+
code: ValidationErrorCode.InvalidProperties,
|
|
388
384
|
details,
|
|
389
385
|
}));
|
|
390
386
|
}
|
|
391
|
-
return
|
|
387
|
+
return Result.result(resultCopy);
|
|
392
388
|
};
|
|
393
|
-
|
|
394
|
-
const
|
|
395
|
-
const { error: refsError } = await (0, exports.validateRefs)(what, schema, options);
|
|
389
|
+
export const validateWithRefs = async (what, schema, options = {}) => {
|
|
390
|
+
const { error: refsError } = await validateRefs(what, schema, options);
|
|
396
391
|
if (refsError) {
|
|
397
|
-
return
|
|
392
|
+
return Result.error(refsError);
|
|
398
393
|
}
|
|
399
|
-
return
|
|
394
|
+
return validate(what, schema, options);
|
|
400
395
|
};
|
|
401
|
-
|
|
402
|
-
const
|
|
403
|
-
const { error: refsError } = await (0, exports.validateRefs)(what, property, options);
|
|
396
|
+
export const validatePropertyWithRefs = async (what, property, options = {}) => {
|
|
397
|
+
const { error: refsError } = await validateRefs(what, property, options);
|
|
404
398
|
if (refsError) {
|
|
405
|
-
return
|
|
399
|
+
return Result.error(refsError);
|
|
406
400
|
}
|
|
407
|
-
return
|
|
401
|
+
return validateProperty(what, property, options);
|
|
408
402
|
};
|
|
409
|
-
|
|
410
|
-
const validator = (schema, options = {}) => {
|
|
403
|
+
export const validator = (schema, options = {}) => {
|
|
411
404
|
return [
|
|
412
405
|
{},
|
|
413
406
|
(what) => {
|
|
414
|
-
return
|
|
407
|
+
return validate(what, schema, options);
|
|
415
408
|
},
|
|
416
409
|
];
|
|
417
410
|
};
|
|
418
|
-
exports.validator = validator;
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aeriajs/validation",
|
|
3
|
-
"
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.180",
|
|
4
5
|
"description": "## Installation",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
|
-
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"publishConfig": {
|
|
9
9
|
"access": "public"
|
|
@@ -17,8 +17,7 @@
|
|
|
17
17
|
"exports": {
|
|
18
18
|
".": {
|
|
19
19
|
"types": "./dist/index.d.ts",
|
|
20
|
-
"
|
|
21
|
-
"require": "./dist/index.js"
|
|
20
|
+
"default": "./dist/index.js"
|
|
22
21
|
}
|
|
23
22
|
},
|
|
24
23
|
"devDependencies": {
|
|
@@ -28,9 +27,9 @@
|
|
|
28
27
|
"mongodb": "^6.17.0"
|
|
29
28
|
},
|
|
30
29
|
"peerDependencies": {
|
|
31
|
-
"@aeriajs/common": "^0.0.
|
|
32
|
-
"@aeriajs/entrypoint": "^0.0.
|
|
33
|
-
"@aeriajs/types": "^0.0.
|
|
30
|
+
"@aeriajs/common": "^0.0.158",
|
|
31
|
+
"@aeriajs/entrypoint": "^0.0.165",
|
|
32
|
+
"@aeriajs/types": "^0.0.136",
|
|
34
33
|
"mongodb": "^6.17.0"
|
|
35
34
|
},
|
|
36
35
|
"scripts": {
|
|
@@ -38,9 +37,6 @@
|
|
|
38
37
|
"test:typecheck": "tsc -p tsconfig.test.json",
|
|
39
38
|
"lint": "eslint .",
|
|
40
39
|
"lint:fix": "eslint . --fix",
|
|
41
|
-
"build": "
|
|
42
|
-
"build:cjs": "tsc",
|
|
43
|
-
"build:esm": "esbuild './src/**/*.ts' --outdir=dist --out-extension:.js=.mjs && pnpm build:esm-transform",
|
|
44
|
-
"build:esm-transform": "pnpm -w esm-transform $PWD/dist"
|
|
40
|
+
"build": "tsc"
|
|
45
41
|
}
|
|
46
42
|
}
|
package/dist/index.mjs
DELETED
package/dist/validate.mjs
DELETED
|
@@ -1,393 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { convertConditionToQuery, getMissingProperties } from "@aeriajs/common";
|
|
3
|
-
import { Result, ValidationErrorCode, PropertyValidationErrorCode } from "@aeriajs/types";
|
|
4
|
-
import { getCollection } from "@aeriajs/entrypoint";
|
|
5
|
-
const isValidObjectId = (what) => {
|
|
6
|
-
return /^[0-9a-f]{24}$/.test(what);
|
|
7
|
-
};
|
|
8
|
-
const getPropertyType = (property) => {
|
|
9
|
-
if ("type" in property) {
|
|
10
|
-
if ("format" in property && property.format) {
|
|
11
|
-
switch (property.format) {
|
|
12
|
-
case "date":
|
|
13
|
-
case "date-time":
|
|
14
|
-
return "datetime";
|
|
15
|
-
case "objectid":
|
|
16
|
-
return "object";
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return property.type;
|
|
20
|
-
}
|
|
21
|
-
if ("enum" in property) {
|
|
22
|
-
return typeof property.enum[0];
|
|
23
|
-
}
|
|
24
|
-
if ("const" in property) {
|
|
25
|
-
return typeof property.const;
|
|
26
|
-
}
|
|
27
|
-
if ("properties" in property || "additionalProperties" in property || "$ref" in property) {
|
|
28
|
-
return "object";
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
const makePropertyError = (type, details) => {
|
|
32
|
-
return {
|
|
33
|
-
type,
|
|
34
|
-
details
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
export const makeValidationError = (error) => {
|
|
38
|
-
return error;
|
|
39
|
-
};
|
|
40
|
-
export const validateProperty = (what, property, options = {}) => {
|
|
41
|
-
if (!property) {
|
|
42
|
-
if (options.parentProperty && "additionalProperties" in options.parentProperty) {
|
|
43
|
-
if (options.parentProperty.additionalProperties === true) {
|
|
44
|
-
return Result.result(what);
|
|
45
|
-
}
|
|
46
|
-
return validateProperty(what, options.parentProperty.additionalProperties);
|
|
47
|
-
}
|
|
48
|
-
if (options.tolerateExtraneous) {
|
|
49
|
-
return Result.result(void 0);
|
|
50
|
-
}
|
|
51
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.Extraneous));
|
|
52
|
-
}
|
|
53
|
-
if (what === null || what === void 0) {
|
|
54
|
-
return Result.result(what);
|
|
55
|
-
}
|
|
56
|
-
if ("getter" in property) {
|
|
57
|
-
return Result.result(void 0);
|
|
58
|
-
}
|
|
59
|
-
const expectedType = getPropertyType(property);
|
|
60
|
-
const actualType = Array.isArray(what) ? "array" : typeof what;
|
|
61
|
-
if (actualType !== expectedType && !(actualType === "number" && expectedType === "integer")) {
|
|
62
|
-
if (expectedType === "datetime" && what instanceof Date) {
|
|
63
|
-
return Result.result(what);
|
|
64
|
-
}
|
|
65
|
-
if ("$ref" in property) {
|
|
66
|
-
switch (typeof what) {
|
|
67
|
-
case "string": {
|
|
68
|
-
if (isValidObjectId(what)) {
|
|
69
|
-
return Result.result(what);
|
|
70
|
-
}
|
|
71
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
72
|
-
expected: expectedType,
|
|
73
|
-
got: actualType
|
|
74
|
-
}));
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
if (options.coerce) {
|
|
79
|
-
if (expectedType === "number" && typeof what === "string") {
|
|
80
|
-
const coerced = parseFloat(what);
|
|
81
|
-
if (!isNaN(coerced)) {
|
|
82
|
-
return Result.result(coerced);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
if (expectedType === "integer" && typeof what === "string") {
|
|
86
|
-
const coerced = parseInt(what);
|
|
87
|
-
if (!isNaN(coerced)) {
|
|
88
|
-
return Result.result(coerced);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
if (expectedType === "string" && typeof what === "number") {
|
|
92
|
-
return Result.result(String(what));
|
|
93
|
-
}
|
|
94
|
-
if (expectedType === "datetime" && typeof what === "string") {
|
|
95
|
-
return Result.result(new Date(what));
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
if ((options.coerce || options.coerceObjectIds) && options.objectIdConstructor) {
|
|
99
|
-
if (("$ref" in property || "format" in property && property.format === "objectid") && typeof what === "string") {
|
|
100
|
-
return Result.result(new options.objectIdConstructor(what));
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
104
|
-
expected: expectedType,
|
|
105
|
-
got: actualType
|
|
106
|
-
}));
|
|
107
|
-
}
|
|
108
|
-
if ("type" in property) {
|
|
109
|
-
switch (property.type) {
|
|
110
|
-
case "string": {
|
|
111
|
-
switch (property.format) {
|
|
112
|
-
case "objectid": {
|
|
113
|
-
if (!options.objectIdConstructor) {
|
|
114
|
-
throw new Error();
|
|
115
|
-
}
|
|
116
|
-
if (!(what instanceof options.objectIdConstructor)) {
|
|
117
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.StringConstraint, {
|
|
118
|
-
expected: "objectid",
|
|
119
|
-
got: "invalid_objectid"
|
|
120
|
-
}));
|
|
121
|
-
}
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
|
-
default: {
|
|
125
|
-
if (typeof what !== "string") {
|
|
126
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
127
|
-
expected: expectedType,
|
|
128
|
-
got: actualType
|
|
129
|
-
}));
|
|
130
|
-
}
|
|
131
|
-
if (typeof property.minLength === "number" && property.minLength > what.length || typeof property.maxLength === "number" && property.maxLength < what.length) {
|
|
132
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.StringConstraint, {
|
|
133
|
-
expected: "string",
|
|
134
|
-
got: "invalid_string"
|
|
135
|
-
}));
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
case "integer": {
|
|
142
|
-
if (!Number.isInteger(what)) {
|
|
143
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.NumericConstraint, {
|
|
144
|
-
expected: "integer",
|
|
145
|
-
got: "invalid_number"
|
|
146
|
-
}));
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
case "number": {
|
|
150
|
-
if (typeof what !== "number") {
|
|
151
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
152
|
-
expected: expectedType,
|
|
153
|
-
got: actualType
|
|
154
|
-
}));
|
|
155
|
-
}
|
|
156
|
-
if (typeof property.maximum === "number" && property.maximum < what || typeof property.minimum === "number" && property.minimum > what || typeof property.exclusiveMaximum === "number" && property.exclusiveMaximum <= what || typeof property.exclusiveMinimum === "number" && property.exclusiveMinimum >= what) {
|
|
157
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.NumericConstraint, {
|
|
158
|
-
expected: "number",
|
|
159
|
-
got: "invalid_number"
|
|
160
|
-
}));
|
|
161
|
-
}
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
case "object": {
|
|
165
|
-
if ("properties" in property) {
|
|
166
|
-
return validate(what, property, options);
|
|
167
|
-
}
|
|
168
|
-
switch (typeof property.additionalProperties) {
|
|
169
|
-
case "object":
|
|
170
|
-
return validate(what, property.additionalProperties, options);
|
|
171
|
-
case "boolean":
|
|
172
|
-
return Result.result(what);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
case "array": {
|
|
176
|
-
if (!Array.isArray(what)) {
|
|
177
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
178
|
-
expected: expectedType,
|
|
179
|
-
got: actualType
|
|
180
|
-
}));
|
|
181
|
-
}
|
|
182
|
-
if (property.minItems) {
|
|
183
|
-
if (what.length < property.minItems) {
|
|
184
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.MoreItemsExpected));
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
if (property.maxItems) {
|
|
188
|
-
if (what.length > property.maxItems) {
|
|
189
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.LessItemsExpected));
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
let i = 0;
|
|
193
|
-
for (const elem of what) {
|
|
194
|
-
const { error } = validateProperty(elem, property.items, options);
|
|
195
|
-
if (error) {
|
|
196
|
-
if ("code" in error) {
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
error.index = i;
|
|
200
|
-
return Result.error(error);
|
|
201
|
-
}
|
|
202
|
-
i++;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
} else if ("enum" in property) {
|
|
207
|
-
if (!property.enum.includes(what)) {
|
|
208
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.ExtraneousElement, {
|
|
209
|
-
expected: property.enum,
|
|
210
|
-
got: what
|
|
211
|
-
}));
|
|
212
|
-
}
|
|
213
|
-
} else if ("const" in property) {
|
|
214
|
-
if (what !== property.const) {
|
|
215
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
216
|
-
expected: property.const,
|
|
217
|
-
got: what
|
|
218
|
-
}));
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
return Result.result(what);
|
|
222
|
-
};
|
|
223
|
-
export const validateRefs = async (what, property, options = {}) => {
|
|
224
|
-
if ("$ref" in property) {
|
|
225
|
-
if (options.checkObjectIds) {
|
|
226
|
-
if (!options.context || !options.objectIdConstructor) {
|
|
227
|
-
throw new Error();
|
|
228
|
-
}
|
|
229
|
-
if (what === null) {
|
|
230
|
-
return Result.result({});
|
|
231
|
-
}
|
|
232
|
-
if (!isValidObjectId(String(what))) {
|
|
233
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
234
|
-
expected: "objectid",
|
|
235
|
-
got: typeof what
|
|
236
|
-
}));
|
|
237
|
-
}
|
|
238
|
-
let query;
|
|
239
|
-
if (property.constraints) {
|
|
240
|
-
query = {
|
|
241
|
-
$and: [
|
|
242
|
-
{
|
|
243
|
-
_id: new options.objectIdConstructor(what)
|
|
244
|
-
},
|
|
245
|
-
convertConditionToQuery(property.constraints)
|
|
246
|
-
]
|
|
247
|
-
};
|
|
248
|
-
} else {
|
|
249
|
-
query = {
|
|
250
|
-
_id: new options.objectIdConstructor(what)
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
const exists = await options.context.collections[property.$ref].model.findOne(query, {
|
|
254
|
-
projection: {
|
|
255
|
-
_id: 1
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
if (!exists) {
|
|
259
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.ReferenceConstraint, {
|
|
260
|
-
expected: "objectid",
|
|
261
|
-
got: "invalid_objectid"
|
|
262
|
-
}));
|
|
263
|
-
}
|
|
264
|
-
return Result.result({});
|
|
265
|
-
}
|
|
266
|
-
let description;
|
|
267
|
-
if (options.descriptions) {
|
|
268
|
-
description = options.descriptions[property.$ref];
|
|
269
|
-
} else {
|
|
270
|
-
const collection = await getCollection(property.$ref);
|
|
271
|
-
if (!collection) {
|
|
272
|
-
throw new Error();
|
|
273
|
-
}
|
|
274
|
-
description = collection.description;
|
|
275
|
-
}
|
|
276
|
-
if (typeof what !== "object") {
|
|
277
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
278
|
-
expected: "object",
|
|
279
|
-
got: typeof what
|
|
280
|
-
}));
|
|
281
|
-
}
|
|
282
|
-
return validate(what, description, options);
|
|
283
|
-
} else if ("items" in property) {
|
|
284
|
-
if (!Array.isArray(what)) {
|
|
285
|
-
throw new Error();
|
|
286
|
-
}
|
|
287
|
-
for (const elem of what) {
|
|
288
|
-
const { error } = await validateRefs(elem, property.items, options);
|
|
289
|
-
if (error) {
|
|
290
|
-
return Result.error(error);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
} else if ("properties" in property) {
|
|
294
|
-
const details = {};
|
|
295
|
-
for (const propName in what) {
|
|
296
|
-
const { error } = await validateRefs(what[propName], property.properties[propName], options);
|
|
297
|
-
if (error) {
|
|
298
|
-
details[propName] = error;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
if (Object.keys(details).length > 0) {
|
|
302
|
-
return Result.error(makeValidationError({
|
|
303
|
-
code: ValidationErrorCode.InvalidProperties,
|
|
304
|
-
details
|
|
305
|
-
}));
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
return Result.result({});
|
|
309
|
-
};
|
|
310
|
-
export const validateWholeness = (what, schema) => {
|
|
311
|
-
const required = schema.required ? schema.required : Object.keys(schema.properties);
|
|
312
|
-
const missingProps = getMissingProperties(what, schema, required);
|
|
313
|
-
if (missingProps.length > 0) {
|
|
314
|
-
return makeValidationError({
|
|
315
|
-
code: ValidationErrorCode.MissingProperties,
|
|
316
|
-
details: Object.fromEntries(missingProps.map((error) => [
|
|
317
|
-
error,
|
|
318
|
-
{
|
|
319
|
-
type: "missing"
|
|
320
|
-
}
|
|
321
|
-
]))
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
|
-
export const validate = (what, schema, options = {}) => {
|
|
326
|
-
if (what === void 0) {
|
|
327
|
-
return Result.error(makeValidationError({
|
|
328
|
-
code: ValidationErrorCode.EmptyTarget,
|
|
329
|
-
details: {}
|
|
330
|
-
}));
|
|
331
|
-
}
|
|
332
|
-
if (!("properties" in schema)) {
|
|
333
|
-
const { error } = validateProperty(what, schema);
|
|
334
|
-
if (error) {
|
|
335
|
-
return Result.error(error);
|
|
336
|
-
}
|
|
337
|
-
return Result.result(what);
|
|
338
|
-
}
|
|
339
|
-
const wholenessError = validateWholeness(what, schema);
|
|
340
|
-
if (wholenessError) {
|
|
341
|
-
if (options.throwOnError) {
|
|
342
|
-
throw new TypeError(ValidationErrorCode.MissingProperties);
|
|
343
|
-
}
|
|
344
|
-
return Result.error(wholenessError);
|
|
345
|
-
}
|
|
346
|
-
const details = {};
|
|
347
|
-
const resultCopy = {};
|
|
348
|
-
for (const propName in what) {
|
|
349
|
-
const { error, result: parsed } = validateProperty(what[propName], schema.properties[propName], {
|
|
350
|
-
...options,
|
|
351
|
-
parentProperty: schema
|
|
352
|
-
});
|
|
353
|
-
if (error) {
|
|
354
|
-
if (options.throwOnError) {
|
|
355
|
-
throw new TypeError(ValidationErrorCode.InvalidProperties);
|
|
356
|
-
}
|
|
357
|
-
details[propName] = error;
|
|
358
|
-
continue;
|
|
359
|
-
}
|
|
360
|
-
if (parsed !== void 0) {
|
|
361
|
-
resultCopy[propName] = parsed;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
if (Object.keys(details).length > 0) {
|
|
365
|
-
return Result.error(makeValidationError({
|
|
366
|
-
code: ValidationErrorCode.InvalidProperties,
|
|
367
|
-
details
|
|
368
|
-
}));
|
|
369
|
-
}
|
|
370
|
-
return Result.result(resultCopy);
|
|
371
|
-
};
|
|
372
|
-
export const validateWithRefs = async (what, schema, options = {}) => {
|
|
373
|
-
const { error: refsError } = await validateRefs(what, schema, options);
|
|
374
|
-
if (refsError) {
|
|
375
|
-
return Result.error(refsError);
|
|
376
|
-
}
|
|
377
|
-
return validate(what, schema, options);
|
|
378
|
-
};
|
|
379
|
-
export const validatePropertyWithRefs = async (what, property, options = {}) => {
|
|
380
|
-
const { error: refsError } = await validateRefs(what, property, options);
|
|
381
|
-
if (refsError) {
|
|
382
|
-
return Result.error(refsError);
|
|
383
|
-
}
|
|
384
|
-
return validateProperty(what, property, options);
|
|
385
|
-
};
|
|
386
|
-
export const validator = (schema, options = {}) => {
|
|
387
|
-
return [
|
|
388
|
-
{},
|
|
389
|
-
(what) => {
|
|
390
|
-
return validate(what, schema, options);
|
|
391
|
-
}
|
|
392
|
-
];
|
|
393
|
-
};
|