@aeriajs/validation 0.0.177 → 0.0.179
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 +108 -101
- package/package.json +7 -11
- package/dist/index.mjs +0 -2
- package/dist/validate.mjs +0 -377
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
|
}));
|
|
@@ -116,25 +112,40 @@ const validateProperty = (what, property, options = {}) => {
|
|
|
116
112
|
if ('type' in property) {
|
|
117
113
|
switch (property.type) {
|
|
118
114
|
case 'string': {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
115
|
+
switch (property.format) {
|
|
116
|
+
case 'objectid': {
|
|
117
|
+
if (!options.objectIdConstructor) {
|
|
118
|
+
throw new Error;
|
|
119
|
+
}
|
|
120
|
+
if (!(what instanceof options.objectIdConstructor)) {
|
|
121
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.StringConstraint, {
|
|
122
|
+
expected: 'objectid',
|
|
123
|
+
got: 'invalid_objectid',
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
default: {
|
|
129
|
+
if (typeof what !== 'string') {
|
|
130
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
131
|
+
expected: expectedType,
|
|
132
|
+
got: actualType,
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
135
|
+
if ((typeof property.minLength === 'number' && property.minLength > what.length)
|
|
136
|
+
|| (typeof property.maxLength === 'number' && property.maxLength < what.length)) {
|
|
137
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.StringConstraint, {
|
|
138
|
+
expected: 'string',
|
|
139
|
+
got: 'invalid_string',
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
132
143
|
}
|
|
133
144
|
break;
|
|
134
145
|
}
|
|
135
146
|
case 'integer': {
|
|
136
147
|
if (!Number.isInteger(what)) {
|
|
137
|
-
return
|
|
148
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.NumericConstraint, {
|
|
138
149
|
expected: 'integer',
|
|
139
150
|
got: 'invalid_number',
|
|
140
151
|
}));
|
|
@@ -142,7 +153,7 @@ const validateProperty = (what, property, options = {}) => {
|
|
|
142
153
|
}
|
|
143
154
|
case 'number': {
|
|
144
155
|
if (typeof what !== 'number') {
|
|
145
|
-
return
|
|
156
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
146
157
|
expected: expectedType,
|
|
147
158
|
got: actualType,
|
|
148
159
|
}));
|
|
@@ -151,7 +162,7 @@ const validateProperty = (what, property, options = {}) => {
|
|
|
151
162
|
|| (typeof property.minimum === 'number' && property.minimum > what)
|
|
152
163
|
|| (typeof property.exclusiveMaximum === 'number' && property.exclusiveMaximum <= what)
|
|
153
164
|
|| (typeof property.exclusiveMinimum === 'number' && property.exclusiveMinimum >= what)) {
|
|
154
|
-
return
|
|
165
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.NumericConstraint, {
|
|
155
166
|
expected: 'number',
|
|
156
167
|
got: 'invalid_number',
|
|
157
168
|
}));
|
|
@@ -160,39 +171,39 @@ const validateProperty = (what, property, options = {}) => {
|
|
|
160
171
|
}
|
|
161
172
|
case 'object': {
|
|
162
173
|
if ('properties' in property) {
|
|
163
|
-
return
|
|
174
|
+
return validate(what, property, options);
|
|
164
175
|
}
|
|
165
176
|
switch (typeof property.additionalProperties) {
|
|
166
|
-
case 'object': return
|
|
167
|
-
case 'boolean': return
|
|
177
|
+
case 'object': return validate(what, property.additionalProperties, options);
|
|
178
|
+
case 'boolean': return Result.result(what);
|
|
168
179
|
}
|
|
169
180
|
}
|
|
170
181
|
case 'array': {
|
|
171
182
|
if (!Array.isArray(what)) {
|
|
172
|
-
return
|
|
183
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
173
184
|
expected: expectedType,
|
|
174
185
|
got: actualType,
|
|
175
186
|
}));
|
|
176
187
|
}
|
|
177
188
|
if (property.minItems) {
|
|
178
189
|
if (what.length < property.minItems) {
|
|
179
|
-
return
|
|
190
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.MoreItemsExpected));
|
|
180
191
|
}
|
|
181
192
|
}
|
|
182
193
|
if (property.maxItems) {
|
|
183
194
|
if (what.length > property.maxItems) {
|
|
184
|
-
return
|
|
195
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.LessItemsExpected));
|
|
185
196
|
}
|
|
186
197
|
}
|
|
187
198
|
let i = 0;
|
|
188
199
|
for (const elem of what) {
|
|
189
|
-
const { error } =
|
|
200
|
+
const { error } = validateProperty(elem, property.items, options);
|
|
190
201
|
if (error) {
|
|
191
202
|
if ('code' in error) {
|
|
192
203
|
continue;
|
|
193
204
|
}
|
|
194
205
|
error.index = i;
|
|
195
|
-
return
|
|
206
|
+
return Result.error(error);
|
|
196
207
|
}
|
|
197
208
|
i++;
|
|
198
209
|
}
|
|
@@ -201,7 +212,7 @@ const validateProperty = (what, property, options = {}) => {
|
|
|
201
212
|
}
|
|
202
213
|
else if ('enum' in property) {
|
|
203
214
|
if (!property.enum.includes(what)) {
|
|
204
|
-
return
|
|
215
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.ExtraneousElement, {
|
|
205
216
|
expected: property.enum,
|
|
206
217
|
got: what,
|
|
207
218
|
}));
|
|
@@ -209,26 +220,25 @@ const validateProperty = (what, property, options = {}) => {
|
|
|
209
220
|
}
|
|
210
221
|
else if ('const' in property) {
|
|
211
222
|
if (what !== property.const) {
|
|
212
|
-
return
|
|
223
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
213
224
|
expected: property.const,
|
|
214
225
|
got: what,
|
|
215
226
|
}));
|
|
216
227
|
}
|
|
217
228
|
}
|
|
218
|
-
return
|
|
229
|
+
return Result.result(what);
|
|
219
230
|
};
|
|
220
|
-
|
|
221
|
-
const validateRefs = async (what, property, options = {}) => {
|
|
231
|
+
export const validateRefs = async (what, property, options = {}) => {
|
|
222
232
|
if ('$ref' in property) {
|
|
223
233
|
if (options.checkObjectIds) {
|
|
224
234
|
if (!options.context || !options.objectIdConstructor) {
|
|
225
235
|
throw new Error;
|
|
226
236
|
}
|
|
227
237
|
if (what === null) {
|
|
228
|
-
return
|
|
238
|
+
return Result.result({});
|
|
229
239
|
}
|
|
230
240
|
if (!isValidObjectId(String(what))) {
|
|
231
|
-
return
|
|
241
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
232
242
|
expected: 'objectid',
|
|
233
243
|
got: typeof what,
|
|
234
244
|
}));
|
|
@@ -240,7 +250,7 @@ const validateRefs = async (what, property, options = {}) => {
|
|
|
240
250
|
{
|
|
241
251
|
_id: new options.objectIdConstructor(what),
|
|
242
252
|
},
|
|
243
|
-
|
|
253
|
+
convertConditionToQuery(property.constraints),
|
|
244
254
|
],
|
|
245
255
|
};
|
|
246
256
|
}
|
|
@@ -255,69 +265,71 @@ const validateRefs = async (what, property, options = {}) => {
|
|
|
255
265
|
},
|
|
256
266
|
});
|
|
257
267
|
if (!exists) {
|
|
258
|
-
return
|
|
268
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.ReferenceConstraint, {
|
|
259
269
|
expected: 'objectid',
|
|
260
270
|
got: 'invalid_objectid',
|
|
261
271
|
}));
|
|
262
272
|
}
|
|
263
|
-
return
|
|
273
|
+
return Result.result({});
|
|
264
274
|
}
|
|
265
275
|
let description;
|
|
266
276
|
if (options.descriptions) {
|
|
267
277
|
description = options.descriptions[property.$ref];
|
|
268
278
|
}
|
|
269
279
|
else {
|
|
270
|
-
const collection = await
|
|
280
|
+
const collection = await getCollection(property.$ref);
|
|
271
281
|
if (!collection) {
|
|
272
282
|
throw new Error;
|
|
273
283
|
}
|
|
274
284
|
description = collection.description;
|
|
275
285
|
}
|
|
276
286
|
if (typeof what !== 'object') {
|
|
277
|
-
return
|
|
287
|
+
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
278
288
|
expected: 'object',
|
|
279
289
|
got: typeof what,
|
|
280
290
|
}));
|
|
281
291
|
}
|
|
282
|
-
return
|
|
292
|
+
return validate(what, description, options);
|
|
283
293
|
}
|
|
284
294
|
else if ('items' in property) {
|
|
285
295
|
if (!Array.isArray(what)) {
|
|
286
296
|
throw new Error;
|
|
287
297
|
}
|
|
288
298
|
for (const elem of what) {
|
|
289
|
-
const { error } = await
|
|
299
|
+
const { error } = await validateRefs(elem, property.items, options);
|
|
290
300
|
if (error) {
|
|
291
|
-
return
|
|
301
|
+
return Result.error(error);
|
|
292
302
|
}
|
|
293
303
|
}
|
|
294
304
|
}
|
|
295
305
|
else if ('properties' in property) {
|
|
296
306
|
const details = {};
|
|
297
307
|
for (const propName in what) {
|
|
298
|
-
|
|
308
|
+
if (!(propName in property.properties)) {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
const { error } = await validateRefs(what[propName], property.properties[propName], options);
|
|
299
312
|
if (error) {
|
|
300
313
|
details[propName] = error;
|
|
301
314
|
}
|
|
302
315
|
}
|
|
303
316
|
if (Object.keys(details).length > 0) {
|
|
304
|
-
return
|
|
305
|
-
code:
|
|
317
|
+
return Result.error(makeValidationError({
|
|
318
|
+
code: ValidationErrorCode.InvalidProperties,
|
|
306
319
|
details,
|
|
307
320
|
}));
|
|
308
321
|
}
|
|
309
322
|
}
|
|
310
|
-
return
|
|
323
|
+
return Result.result({});
|
|
311
324
|
};
|
|
312
|
-
|
|
313
|
-
const validateWholeness = (what, schema) => {
|
|
325
|
+
export const validateWholeness = (what, schema) => {
|
|
314
326
|
const required = schema.required
|
|
315
327
|
? schema.required
|
|
316
328
|
: Object.keys(schema.properties);
|
|
317
|
-
const missingProps =
|
|
329
|
+
const missingProps = getMissingProperties(what, schema, required);
|
|
318
330
|
if (missingProps.length > 0) {
|
|
319
|
-
return
|
|
320
|
-
code:
|
|
331
|
+
return makeValidationError({
|
|
332
|
+
code: ValidationErrorCode.MissingProperties,
|
|
321
333
|
details: Object.fromEntries(missingProps.map((error) => [
|
|
322
334
|
error,
|
|
323
335
|
{
|
|
@@ -327,38 +339,37 @@ const validateWholeness = (what, schema) => {
|
|
|
327
339
|
});
|
|
328
340
|
}
|
|
329
341
|
};
|
|
330
|
-
|
|
331
|
-
const validate = (what, schema, options = {}) => {
|
|
342
|
+
export const validate = (what, schema, options = {}) => {
|
|
332
343
|
if (what === undefined) {
|
|
333
|
-
return
|
|
334
|
-
code:
|
|
344
|
+
return Result.error(makeValidationError({
|
|
345
|
+
code: ValidationErrorCode.EmptyTarget,
|
|
335
346
|
details: {},
|
|
336
347
|
}));
|
|
337
348
|
}
|
|
338
349
|
if (!('properties' in schema)) {
|
|
339
|
-
const { error } =
|
|
350
|
+
const { error } = validateProperty(what, schema);
|
|
340
351
|
if (error) {
|
|
341
|
-
return
|
|
352
|
+
return Result.error(error);
|
|
342
353
|
}
|
|
343
|
-
return
|
|
354
|
+
return Result.result(what);
|
|
344
355
|
}
|
|
345
|
-
const wholenessError =
|
|
356
|
+
const wholenessError = validateWholeness(what, schema);
|
|
346
357
|
if (wholenessError) {
|
|
347
358
|
if (options.throwOnError) {
|
|
348
|
-
throw new TypeError(
|
|
359
|
+
throw new TypeError(ValidationErrorCode.MissingProperties);
|
|
349
360
|
}
|
|
350
|
-
return
|
|
361
|
+
return Result.error(wholenessError);
|
|
351
362
|
}
|
|
352
363
|
const details = {};
|
|
353
364
|
const resultCopy = {};
|
|
354
365
|
for (const propName in what) {
|
|
355
|
-
const { error, result: parsed } =
|
|
366
|
+
const { error, result: parsed } = validateProperty(what[propName], schema.properties[propName], {
|
|
356
367
|
...options,
|
|
357
368
|
parentProperty: schema,
|
|
358
369
|
});
|
|
359
370
|
if (error) {
|
|
360
371
|
if (options.throwOnError) {
|
|
361
|
-
throw new TypeError(
|
|
372
|
+
throw new TypeError(ValidationErrorCode.InvalidProperties);
|
|
362
373
|
}
|
|
363
374
|
details[propName] = error;
|
|
364
375
|
continue;
|
|
@@ -368,36 +379,32 @@ const validate = (what, schema, options = {}) => {
|
|
|
368
379
|
}
|
|
369
380
|
}
|
|
370
381
|
if (Object.keys(details).length > 0) {
|
|
371
|
-
return
|
|
372
|
-
code:
|
|
382
|
+
return Result.error(makeValidationError({
|
|
383
|
+
code: ValidationErrorCode.InvalidProperties,
|
|
373
384
|
details,
|
|
374
385
|
}));
|
|
375
386
|
}
|
|
376
|
-
return
|
|
387
|
+
return Result.result(resultCopy);
|
|
377
388
|
};
|
|
378
|
-
|
|
379
|
-
const
|
|
380
|
-
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);
|
|
381
391
|
if (refsError) {
|
|
382
|
-
return
|
|
392
|
+
return Result.error(refsError);
|
|
383
393
|
}
|
|
384
|
-
return
|
|
394
|
+
return validate(what, schema, options);
|
|
385
395
|
};
|
|
386
|
-
|
|
387
|
-
const
|
|
388
|
-
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);
|
|
389
398
|
if (refsError) {
|
|
390
|
-
return
|
|
399
|
+
return Result.error(refsError);
|
|
391
400
|
}
|
|
392
|
-
return
|
|
401
|
+
return validateProperty(what, property, options);
|
|
393
402
|
};
|
|
394
|
-
|
|
395
|
-
const validator = (schema, options = {}) => {
|
|
403
|
+
export const validator = (schema, options = {}) => {
|
|
396
404
|
return [
|
|
397
405
|
{},
|
|
398
406
|
(what) => {
|
|
399
|
-
return
|
|
407
|
+
return validate(what, schema, options);
|
|
400
408
|
},
|
|
401
409
|
];
|
|
402
410
|
};
|
|
403
|
-
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.179",
|
|
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.164",
|
|
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,377 +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
|
-
if (typeof what !== "string") {
|
|
112
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
113
|
-
expected: expectedType,
|
|
114
|
-
got: actualType
|
|
115
|
-
}));
|
|
116
|
-
}
|
|
117
|
-
if (property.format === "objectid" && !isValidObjectId(what) || typeof property.minLength === "number" && property.minLength > what.length || typeof property.maxLength === "number" && property.maxLength < what.length) {
|
|
118
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.StringConstraint, {
|
|
119
|
-
expected: "string",
|
|
120
|
-
got: "invalid_string"
|
|
121
|
-
}));
|
|
122
|
-
}
|
|
123
|
-
break;
|
|
124
|
-
}
|
|
125
|
-
case "integer": {
|
|
126
|
-
if (!Number.isInteger(what)) {
|
|
127
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.NumericConstraint, {
|
|
128
|
-
expected: "integer",
|
|
129
|
-
got: "invalid_number"
|
|
130
|
-
}));
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
case "number": {
|
|
134
|
-
if (typeof what !== "number") {
|
|
135
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
136
|
-
expected: expectedType,
|
|
137
|
-
got: actualType
|
|
138
|
-
}));
|
|
139
|
-
}
|
|
140
|
-
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) {
|
|
141
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.NumericConstraint, {
|
|
142
|
-
expected: "number",
|
|
143
|
-
got: "invalid_number"
|
|
144
|
-
}));
|
|
145
|
-
}
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
case "object": {
|
|
149
|
-
if ("properties" in property) {
|
|
150
|
-
return validate(what, property, options);
|
|
151
|
-
}
|
|
152
|
-
switch (typeof property.additionalProperties) {
|
|
153
|
-
case "object":
|
|
154
|
-
return validate(what, property.additionalProperties, options);
|
|
155
|
-
case "boolean":
|
|
156
|
-
return Result.result(what);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
case "array": {
|
|
160
|
-
if (!Array.isArray(what)) {
|
|
161
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
162
|
-
expected: expectedType,
|
|
163
|
-
got: actualType
|
|
164
|
-
}));
|
|
165
|
-
}
|
|
166
|
-
if (property.minItems) {
|
|
167
|
-
if (what.length < property.minItems) {
|
|
168
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.MoreItemsExpected));
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
if (property.maxItems) {
|
|
172
|
-
if (what.length > property.maxItems) {
|
|
173
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.LessItemsExpected));
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
let i = 0;
|
|
177
|
-
for (const elem of what) {
|
|
178
|
-
const { error } = validateProperty(elem, property.items, options);
|
|
179
|
-
if (error) {
|
|
180
|
-
if ("code" in error) {
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
error.index = i;
|
|
184
|
-
return Result.error(error);
|
|
185
|
-
}
|
|
186
|
-
i++;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
} else if ("enum" in property) {
|
|
191
|
-
if (!property.enum.includes(what)) {
|
|
192
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.ExtraneousElement, {
|
|
193
|
-
expected: property.enum,
|
|
194
|
-
got: what
|
|
195
|
-
}));
|
|
196
|
-
}
|
|
197
|
-
} else if ("const" in property) {
|
|
198
|
-
if (what !== property.const) {
|
|
199
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
200
|
-
expected: property.const,
|
|
201
|
-
got: what
|
|
202
|
-
}));
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return Result.result(what);
|
|
206
|
-
};
|
|
207
|
-
export const validateRefs = async (what, property, options = {}) => {
|
|
208
|
-
if ("$ref" in property) {
|
|
209
|
-
if (options.checkObjectIds) {
|
|
210
|
-
if (!options.context || !options.objectIdConstructor) {
|
|
211
|
-
throw new Error();
|
|
212
|
-
}
|
|
213
|
-
if (what === null) {
|
|
214
|
-
return Result.result({});
|
|
215
|
-
}
|
|
216
|
-
if (!isValidObjectId(String(what))) {
|
|
217
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
218
|
-
expected: "objectid",
|
|
219
|
-
got: typeof what
|
|
220
|
-
}));
|
|
221
|
-
}
|
|
222
|
-
let query;
|
|
223
|
-
if (property.constraints) {
|
|
224
|
-
query = {
|
|
225
|
-
$and: [
|
|
226
|
-
{
|
|
227
|
-
_id: new options.objectIdConstructor(what)
|
|
228
|
-
},
|
|
229
|
-
convertConditionToQuery(property.constraints)
|
|
230
|
-
]
|
|
231
|
-
};
|
|
232
|
-
} else {
|
|
233
|
-
query = {
|
|
234
|
-
_id: new options.objectIdConstructor(what)
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
const exists = await options.context.collections[property.$ref].model.findOne(query, {
|
|
238
|
-
projection: {
|
|
239
|
-
_id: 1
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
if (!exists) {
|
|
243
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.ReferenceConstraint, {
|
|
244
|
-
expected: "objectid",
|
|
245
|
-
got: "invalid_objectid"
|
|
246
|
-
}));
|
|
247
|
-
}
|
|
248
|
-
return Result.result({});
|
|
249
|
-
}
|
|
250
|
-
let description;
|
|
251
|
-
if (options.descriptions) {
|
|
252
|
-
description = options.descriptions[property.$ref];
|
|
253
|
-
} else {
|
|
254
|
-
const collection = await getCollection(property.$ref);
|
|
255
|
-
if (!collection) {
|
|
256
|
-
throw new Error();
|
|
257
|
-
}
|
|
258
|
-
description = collection.description;
|
|
259
|
-
}
|
|
260
|
-
if (typeof what !== "object") {
|
|
261
|
-
return Result.error(makePropertyError(PropertyValidationErrorCode.Unmatching, {
|
|
262
|
-
expected: "object",
|
|
263
|
-
got: typeof what
|
|
264
|
-
}));
|
|
265
|
-
}
|
|
266
|
-
return validate(what, description, options);
|
|
267
|
-
} else if ("items" in property) {
|
|
268
|
-
if (!Array.isArray(what)) {
|
|
269
|
-
throw new Error();
|
|
270
|
-
}
|
|
271
|
-
for (const elem of what) {
|
|
272
|
-
const { error } = await validateRefs(elem, property.items, options);
|
|
273
|
-
if (error) {
|
|
274
|
-
return Result.error(error);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
} else if ("properties" in property) {
|
|
278
|
-
const details = {};
|
|
279
|
-
for (const propName in what) {
|
|
280
|
-
const { error } = await validateRefs(what[propName], property.properties[propName], options);
|
|
281
|
-
if (error) {
|
|
282
|
-
details[propName] = error;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
if (Object.keys(details).length > 0) {
|
|
286
|
-
return Result.error(makeValidationError({
|
|
287
|
-
code: ValidationErrorCode.InvalidProperties,
|
|
288
|
-
details
|
|
289
|
-
}));
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
return Result.result({});
|
|
293
|
-
};
|
|
294
|
-
export const validateWholeness = (what, schema) => {
|
|
295
|
-
const required = schema.required ? schema.required : Object.keys(schema.properties);
|
|
296
|
-
const missingProps = getMissingProperties(what, schema, required);
|
|
297
|
-
if (missingProps.length > 0) {
|
|
298
|
-
return makeValidationError({
|
|
299
|
-
code: ValidationErrorCode.MissingProperties,
|
|
300
|
-
details: Object.fromEntries(missingProps.map((error) => [
|
|
301
|
-
error,
|
|
302
|
-
{
|
|
303
|
-
type: "missing"
|
|
304
|
-
}
|
|
305
|
-
]))
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
export const validate = (what, schema, options = {}) => {
|
|
310
|
-
if (what === void 0) {
|
|
311
|
-
return Result.error(makeValidationError({
|
|
312
|
-
code: ValidationErrorCode.EmptyTarget,
|
|
313
|
-
details: {}
|
|
314
|
-
}));
|
|
315
|
-
}
|
|
316
|
-
if (!("properties" in schema)) {
|
|
317
|
-
const { error } = validateProperty(what, schema);
|
|
318
|
-
if (error) {
|
|
319
|
-
return Result.error(error);
|
|
320
|
-
}
|
|
321
|
-
return Result.result(what);
|
|
322
|
-
}
|
|
323
|
-
const wholenessError = validateWholeness(what, schema);
|
|
324
|
-
if (wholenessError) {
|
|
325
|
-
if (options.throwOnError) {
|
|
326
|
-
throw new TypeError(ValidationErrorCode.MissingProperties);
|
|
327
|
-
}
|
|
328
|
-
return Result.error(wholenessError);
|
|
329
|
-
}
|
|
330
|
-
const details = {};
|
|
331
|
-
const resultCopy = {};
|
|
332
|
-
for (const propName in what) {
|
|
333
|
-
const { error, result: parsed } = validateProperty(what[propName], schema.properties[propName], {
|
|
334
|
-
...options,
|
|
335
|
-
parentProperty: schema
|
|
336
|
-
});
|
|
337
|
-
if (error) {
|
|
338
|
-
if (options.throwOnError) {
|
|
339
|
-
throw new TypeError(ValidationErrorCode.InvalidProperties);
|
|
340
|
-
}
|
|
341
|
-
details[propName] = error;
|
|
342
|
-
continue;
|
|
343
|
-
}
|
|
344
|
-
if (parsed !== void 0) {
|
|
345
|
-
resultCopy[propName] = parsed;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
if (Object.keys(details).length > 0) {
|
|
349
|
-
return Result.error(makeValidationError({
|
|
350
|
-
code: ValidationErrorCode.InvalidProperties,
|
|
351
|
-
details
|
|
352
|
-
}));
|
|
353
|
-
}
|
|
354
|
-
return Result.result(resultCopy);
|
|
355
|
-
};
|
|
356
|
-
export const validateWithRefs = async (what, schema, options = {}) => {
|
|
357
|
-
const { error: refsError } = await validateRefs(what, schema, options);
|
|
358
|
-
if (refsError) {
|
|
359
|
-
return Result.error(refsError);
|
|
360
|
-
}
|
|
361
|
-
return validate(what, schema, options);
|
|
362
|
-
};
|
|
363
|
-
export const validatePropertyWithRefs = async (what, property, options = {}) => {
|
|
364
|
-
const { error: refsError } = await validateRefs(what, property, options);
|
|
365
|
-
if (refsError) {
|
|
366
|
-
return Result.error(refsError);
|
|
367
|
-
}
|
|
368
|
-
return validateProperty(what, property, options);
|
|
369
|
-
};
|
|
370
|
-
export const validator = (schema, options = {}) => {
|
|
371
|
-
return [
|
|
372
|
-
{},
|
|
373
|
-
(what) => {
|
|
374
|
-
return validate(what, schema, options);
|
|
375
|
-
}
|
|
376
|
-
];
|
|
377
|
-
};
|