schema-shield 1.0.5 → 1.1.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/README.md +400 -1050
- package/dist/formats.d.ts.map +1 -1
- package/dist/index.d.ts +33 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1247 -392
- package/dist/index.min.js +2 -1
- package/dist/index.min.js.map +1 -1
- package/dist/index.mjs +1247 -392
- package/dist/keywords/array-keywords.d.ts.map +1 -1
- package/dist/keywords/number-keywords.d.ts.map +1 -1
- package/dist/keywords/object-keywords.d.ts +7 -1
- package/dist/keywords/object-keywords.d.ts.map +1 -1
- package/dist/keywords/other-keywords.d.ts +17 -1
- package/dist/keywords/other-keywords.d.ts.map +1 -1
- package/dist/keywords/string-keywords.d.ts.map +1 -1
- package/dist/utils/deep-freeze.d.ts.map +1 -1
- package/dist/utils/main-utils.d.ts +7 -4
- package/dist/utils/main-utils.d.ts.map +1 -1
- package/lib/formats.ts +36 -10
- package/lib/index.ts +1157 -155
- package/lib/keywords/array-keywords.ts +18 -3
- package/lib/keywords/number-keywords.ts +19 -5
- package/lib/keywords/object-keywords.ts +59 -61
- package/lib/keywords/other-keywords.ts +205 -192
- package/lib/keywords/string-keywords.ts +51 -8
- package/lib/types.ts +2 -2
- package/lib/utils/deep-freeze.ts +6 -4
- package/lib/utils/main-utils.ts +91 -47
- package/package.json +8 -10
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
definePropertyOrThrow,
|
|
3
|
+
isCompiledSchema
|
|
4
|
+
} from "../utils/main-utils";
|
|
2
5
|
|
|
3
6
|
import { KeywordFunction } from "../index";
|
|
4
7
|
import { hasChanged } from "../utils/has-changed";
|
|
@@ -210,7 +213,7 @@ export const ArrayKeywords: Record<string, KeywordFunction> = {
|
|
|
210
213
|
let tupleLength = (schema as any)._tupleItemsLength as number | undefined;
|
|
211
214
|
if (tupleLength === undefined) {
|
|
212
215
|
tupleLength = schema.items.length;
|
|
213
|
-
|
|
216
|
+
definePropertyOrThrow(schema, "_tupleItemsLength", {
|
|
214
217
|
value: tupleLength,
|
|
215
218
|
enumerable: false,
|
|
216
219
|
configurable: false,
|
|
@@ -296,7 +299,9 @@ export const ArrayKeywords: Record<string, KeywordFunction> = {
|
|
|
296
299
|
return;
|
|
297
300
|
}
|
|
298
301
|
|
|
299
|
-
|
|
302
|
+
let hasFirstPrimitive = false;
|
|
303
|
+
let firstPrimitive: any;
|
|
304
|
+
let primitiveSeen: Set<any> | undefined;
|
|
300
305
|
let primitiveArraySignatures: Set<string> | undefined;
|
|
301
306
|
let arrayBuckets: Map<string, any[]> | undefined;
|
|
302
307
|
let objectBuckets: Map<string, any[]> | undefined;
|
|
@@ -305,6 +310,16 @@ export const ArrayKeywords: Record<string, KeywordFunction> = {
|
|
|
305
310
|
const item = data[i];
|
|
306
311
|
|
|
307
312
|
if (isUniquePrimitive(item)) {
|
|
313
|
+
if (!hasFirstPrimitive) {
|
|
314
|
+
hasFirstPrimitive = true;
|
|
315
|
+
firstPrimitive = item;
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (!primitiveSeen) {
|
|
320
|
+
primitiveSeen = new Set<any>([firstPrimitive]);
|
|
321
|
+
}
|
|
322
|
+
|
|
308
323
|
if (primitiveSeen.has(item)) {
|
|
309
324
|
return defineError("Array items are not unique", { data: item });
|
|
310
325
|
}
|
|
@@ -6,6 +6,9 @@ export const NumberKeywords: Record<string, KeywordFunction> = {
|
|
|
6
6
|
if (typeof data !== "number") {
|
|
7
7
|
return;
|
|
8
8
|
}
|
|
9
|
+
if (!Number.isFinite(data)) {
|
|
10
|
+
return defineError("Value must be finite", { data });
|
|
11
|
+
}
|
|
9
12
|
|
|
10
13
|
let min = schema.minimum;
|
|
11
14
|
if (typeof schema.exclusiveMinimum === "number") {
|
|
@@ -25,6 +28,9 @@ export const NumberKeywords: Record<string, KeywordFunction> = {
|
|
|
25
28
|
if (typeof data !== "number") {
|
|
26
29
|
return;
|
|
27
30
|
}
|
|
31
|
+
if (!Number.isFinite(data)) {
|
|
32
|
+
return defineError("Value must be finite", { data });
|
|
33
|
+
}
|
|
28
34
|
|
|
29
35
|
let max = schema.maximum;
|
|
30
36
|
if (typeof schema.exclusiveMaximum === "number") {
|
|
@@ -45,13 +51,21 @@ export const NumberKeywords: Record<string, KeywordFunction> = {
|
|
|
45
51
|
return;
|
|
46
52
|
}
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
if (
|
|
55
|
+
!Number.isFinite(data) ||
|
|
56
|
+
!Number.isFinite(schema.multipleOf) ||
|
|
57
|
+
schema.multipleOf <= 0
|
|
58
|
+
) {
|
|
59
|
+
return defineError("Value must use a finite positive multipleOf", {
|
|
60
|
+
data
|
|
61
|
+
});
|
|
52
62
|
}
|
|
53
63
|
|
|
54
|
-
|
|
64
|
+
const quotient = data / schema.multipleOf;
|
|
65
|
+
const valid = Number.isFinite(quotient)
|
|
66
|
+
? areCloseEnough(quotient, Math.round(quotient))
|
|
67
|
+
: data % schema.multipleOf === 0;
|
|
68
|
+
if (!valid) {
|
|
55
69
|
return defineError("Value is not a multiple of the multipleOf", { data });
|
|
56
70
|
}
|
|
57
71
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
definePropertyOrThrow,
|
|
3
|
+
hasOwn,
|
|
4
|
+
isCompiledSchema
|
|
5
|
+
} from "../utils/main-utils";
|
|
2
6
|
|
|
3
|
-
import { KeywordFunction } from "../index";
|
|
7
|
+
import type { KeywordFunction, SchemaShield } from "../index";
|
|
4
8
|
import { deepCloneUnfreeze } from "../utils/deep-freeze";
|
|
5
9
|
import { compilePatternMatcher } from "../utils/pattern-matcher";
|
|
6
10
|
|
|
@@ -11,6 +15,43 @@ type PatternPropertyEntry = {
|
|
|
11
15
|
match: (key: string) => boolean;
|
|
12
16
|
};
|
|
13
17
|
|
|
18
|
+
interface ApplyPropertyDefaultsFunction {
|
|
19
|
+
(schema: Record<string, any>, data: any, instance: SchemaShield): void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function createApplyPropertyDefaults(
|
|
23
|
+
replaceEmpty: boolean
|
|
24
|
+
): ApplyPropertyDefaultsFunction {
|
|
25
|
+
return function applyPropertyDefaults(schema, data, instance) {
|
|
26
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const defaultKeys = (schema as any)._defaultKeys as string[];
|
|
31
|
+
for (let i = 0; i < defaultKeys.length; i++) {
|
|
32
|
+
const key = defaultKeys[i];
|
|
33
|
+
const hasOwnValue = hasOwn(data, key);
|
|
34
|
+
const value = hasOwnValue ? data[key] : undefined;
|
|
35
|
+
if (
|
|
36
|
+
hasOwnValue &&
|
|
37
|
+
value !== undefined &&
|
|
38
|
+
(!replaceEmpty || (value !== null && value !== ""))
|
|
39
|
+
) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
instance.setDefault(
|
|
44
|
+
data,
|
|
45
|
+
key,
|
|
46
|
+
deepCloneUnfreeze(schema.properties[key].default)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const applyPropertyDefaults = createApplyPropertyDefaults(false);
|
|
53
|
+
export const applyEmptyPropertyDefaults = createApplyPropertyDefaults(true);
|
|
54
|
+
|
|
14
55
|
function getPatternPropertyEntries(schema: Record<string, any>) {
|
|
15
56
|
let entries = (schema as any)._patternPropertyEntries as
|
|
16
57
|
| PatternPropertyEntry[]
|
|
@@ -45,7 +86,7 @@ function getPatternPropertyEntries(schema: Record<string, any>) {
|
|
|
45
86
|
};
|
|
46
87
|
}
|
|
47
88
|
|
|
48
|
-
|
|
89
|
+
definePropertyOrThrow(schema, "_patternPropertyEntries", {
|
|
49
90
|
value: entries,
|
|
50
91
|
enumerable: false,
|
|
51
92
|
configurable: false,
|
|
@@ -71,7 +112,7 @@ function getPatternKeyMatchIndexes(
|
|
|
71
112
|
}
|
|
72
113
|
} else {
|
|
73
114
|
cache = new Map<string, number[]>();
|
|
74
|
-
|
|
115
|
+
definePropertyOrThrow(schema, "_patternKeyMatchIndexCache", {
|
|
75
116
|
value: cache,
|
|
76
117
|
enumerable: false,
|
|
77
118
|
configurable: false,
|
|
@@ -102,7 +143,7 @@ export const ObjectKeywords: Record<string, KeywordFunction | false> = {
|
|
|
102
143
|
|
|
103
144
|
for (let i = 0; i < schema.required.length; i++) {
|
|
104
145
|
const key = schema.required[i];
|
|
105
|
-
if (!
|
|
146
|
+
if (!hasOwn(data, key)) {
|
|
106
147
|
return defineError("Required property is missing", {
|
|
107
148
|
item: key,
|
|
108
149
|
data: data[key]
|
|
@@ -113,61 +154,18 @@ export const ObjectKeywords: Record<string, KeywordFunction | false> = {
|
|
|
113
154
|
return;
|
|
114
155
|
},
|
|
115
156
|
|
|
116
|
-
properties(schema, data, defineError) {
|
|
157
|
+
properties(schema, data, defineError, instance) {
|
|
117
158
|
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
118
159
|
return;
|
|
119
160
|
}
|
|
120
161
|
|
|
121
|
-
|
|
122
|
-
if (!propKeys) {
|
|
123
|
-
propKeys = Object.keys(schema.properties || {});
|
|
124
|
-
Object.defineProperty(schema, "_propKeys", {
|
|
125
|
-
value: propKeys,
|
|
126
|
-
enumerable: false,
|
|
127
|
-
configurable: false,
|
|
128
|
-
writable: false
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
let requiredSet = (schema as any)._requiredSet as
|
|
133
|
-
| Set<string>
|
|
134
|
-
| null
|
|
135
|
-
| undefined;
|
|
136
|
-
if (requiredSet === undefined) {
|
|
137
|
-
requiredSet = Array.isArray(schema.required)
|
|
138
|
-
? new Set<string>(schema.required)
|
|
139
|
-
: null;
|
|
140
|
-
Object.defineProperty(schema, "_requiredSet", {
|
|
141
|
-
value: requiredSet,
|
|
142
|
-
enumerable: false,
|
|
143
|
-
configurable: false,
|
|
144
|
-
writable: false
|
|
145
|
-
});
|
|
146
|
-
}
|
|
162
|
+
const propKeys = (schema as any)._propKeys as string[];
|
|
147
163
|
|
|
148
164
|
for (let i = 0; i < propKeys.length; i++) {
|
|
149
165
|
const key = propKeys[i];
|
|
150
166
|
const schemaProp = schema.properties[key];
|
|
151
167
|
|
|
152
|
-
if (!
|
|
153
|
-
if (
|
|
154
|
-
requiredSet &&
|
|
155
|
-
requiredSet.has(key) &&
|
|
156
|
-
schemaProp &&
|
|
157
|
-
typeof schemaProp === "object" &&
|
|
158
|
-
!Array.isArray(schemaProp) &&
|
|
159
|
-
"default" in schemaProp
|
|
160
|
-
) {
|
|
161
|
-
const error = (schemaProp as any).$validate(schemaProp.default);
|
|
162
|
-
if (error) {
|
|
163
|
-
return defineError("Default property is invalid", {
|
|
164
|
-
item: key,
|
|
165
|
-
cause: error,
|
|
166
|
-
data: schemaProp.default
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
data[key] = deepCloneUnfreeze(schemaProp.default);
|
|
170
|
-
}
|
|
168
|
+
if (!hasOwn(data, key)) {
|
|
171
169
|
continue;
|
|
172
170
|
}
|
|
173
171
|
|
|
@@ -207,7 +205,7 @@ export const ObjectKeywords: Record<string, KeywordFunction | false> = {
|
|
|
207
205
|
}
|
|
208
206
|
|
|
209
207
|
for (const key in data) {
|
|
210
|
-
if (!
|
|
208
|
+
if (!hasOwn(data, key)) {
|
|
211
209
|
continue;
|
|
212
210
|
}
|
|
213
211
|
|
|
@@ -229,7 +227,7 @@ export const ObjectKeywords: Record<string, KeywordFunction | false> = {
|
|
|
229
227
|
|
|
230
228
|
let count = 0;
|
|
231
229
|
for (const key in data) {
|
|
232
|
-
if (!
|
|
230
|
+
if (!hasOwn(data, key)) {
|
|
233
231
|
continue;
|
|
234
232
|
}
|
|
235
233
|
count++;
|
|
@@ -248,7 +246,7 @@ export const ObjectKeywords: Record<string, KeywordFunction | false> = {
|
|
|
248
246
|
|
|
249
247
|
let count = 0;
|
|
250
248
|
for (const key in data) {
|
|
251
|
-
if (!
|
|
249
|
+
if (!hasOwn(data, key)) {
|
|
252
250
|
continue;
|
|
253
251
|
}
|
|
254
252
|
count++;
|
|
@@ -273,7 +271,7 @@ export const ObjectKeywords: Record<string, KeywordFunction | false> = {
|
|
|
273
271
|
apValidate = isCompiledSchema(schema.additionalProperties)
|
|
274
272
|
? schema.additionalProperties.$validate
|
|
275
273
|
: null;
|
|
276
|
-
|
|
274
|
+
definePropertyOrThrow(schema, "_apValidate", {
|
|
277
275
|
value: apValidate,
|
|
278
276
|
enumerable: false,
|
|
279
277
|
configurable: false,
|
|
@@ -284,13 +282,13 @@ export const ObjectKeywords: Record<string, KeywordFunction | false> = {
|
|
|
284
282
|
const patternEntries = getPatternPropertyEntries(schema);
|
|
285
283
|
|
|
286
284
|
for (const key in data) {
|
|
287
|
-
if (!
|
|
285
|
+
if (!hasOwn(data, key)) {
|
|
288
286
|
continue;
|
|
289
287
|
}
|
|
290
288
|
|
|
291
289
|
if (
|
|
292
290
|
schema.properties &&
|
|
293
|
-
|
|
291
|
+
hasOwn(schema.properties, key)
|
|
294
292
|
) {
|
|
295
293
|
continue;
|
|
296
294
|
}
|
|
@@ -333,7 +331,7 @@ export const ObjectKeywords: Record<string, KeywordFunction | false> = {
|
|
|
333
331
|
}
|
|
334
332
|
|
|
335
333
|
for (const key in data) {
|
|
336
|
-
if (!
|
|
334
|
+
if (!hasOwn(data, key)) {
|
|
337
335
|
continue;
|
|
338
336
|
}
|
|
339
337
|
|
|
@@ -342,7 +340,7 @@ export const ObjectKeywords: Record<string, KeywordFunction | false> = {
|
|
|
342
340
|
if (matchingIndexes.length === 0) {
|
|
343
341
|
if (
|
|
344
342
|
schema.additionalProperties === false &&
|
|
345
|
-
!(schema.properties &&
|
|
343
|
+
!(schema.properties && hasOwn(schema.properties, key))
|
|
346
344
|
) {
|
|
347
345
|
return defineError("Additional properties are not allowed", {
|
|
348
346
|
item: key,
|
|
@@ -392,7 +390,7 @@ export const ObjectKeywords: Record<string, KeywordFunction | false> = {
|
|
|
392
390
|
if (typeof pn === "boolean") {
|
|
393
391
|
if (pn === false) {
|
|
394
392
|
for (const key in data) {
|
|
395
|
-
if (
|
|
393
|
+
if (hasOwn(data, key)) {
|
|
396
394
|
return defineError("Properties are not allowed", { data });
|
|
397
395
|
}
|
|
398
396
|
}
|
|
@@ -407,7 +405,7 @@ export const ObjectKeywords: Record<string, KeywordFunction | false> = {
|
|
|
407
405
|
}
|
|
408
406
|
|
|
409
407
|
for (const key in data) {
|
|
410
|
-
if (!
|
|
408
|
+
if (!hasOwn(data, key)) {
|
|
411
409
|
continue;
|
|
412
410
|
}
|
|
413
411
|
const error = validate(key);
|