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,6 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
definePropertyOrThrow,
|
|
3
|
+
isCompiledSchema
|
|
4
|
+
} from "../utils/main-utils";
|
|
5
|
+
import type {
|
|
6
|
+
KeywordFunction,
|
|
7
|
+
Result,
|
|
8
|
+
ValidateFunction,
|
|
9
|
+
ValidateSubschemaFunction
|
|
10
|
+
} from "../index";
|
|
11
|
+
import type { DefineErrorFunction } from "../utils/main-utils";
|
|
4
12
|
import { hasChanged } from "../utils/has-changed";
|
|
5
13
|
|
|
6
14
|
type BranchEntry =
|
|
@@ -40,7 +48,7 @@ function getBranchEntries(schema: any, key: "allOf" | "anyOf" | "oneOf") {
|
|
|
40
48
|
entries.push(toBranchEntry(source[i]));
|
|
41
49
|
}
|
|
42
50
|
|
|
43
|
-
|
|
51
|
+
definePropertyOrThrow(schema, cacheKey, {
|
|
44
52
|
value: entries,
|
|
45
53
|
enumerable: false,
|
|
46
54
|
configurable: false,
|
|
@@ -50,6 +58,192 @@ function getBranchEntries(schema: any, key: "allOf" | "anyOf" | "oneOf") {
|
|
|
50
58
|
return entries;
|
|
51
59
|
}
|
|
52
60
|
|
|
61
|
+
type CombinatorKey = "allOf" | "anyOf" | "oneOf";
|
|
62
|
+
type DefaultMutation = { target: Record<string, any>; key: string; value: any };
|
|
63
|
+
type TransactionHooks = {
|
|
64
|
+
savepoint: () => number;
|
|
65
|
+
rollback: (savepoint: number) => void;
|
|
66
|
+
capture: (savepoint: number) => DefaultMutation[];
|
|
67
|
+
restore: (mutations: DefaultMutation[]) => void;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
function evaluateAllOf(
|
|
71
|
+
branches: BranchEntry[],
|
|
72
|
+
data: any,
|
|
73
|
+
defineError: DefineErrorFunction
|
|
74
|
+
): Result {
|
|
75
|
+
for (let i = 0; i < branches.length; i++) {
|
|
76
|
+
const branch = branches[i];
|
|
77
|
+
if (branch.kind === "validate") {
|
|
78
|
+
const error = branch.validate(data);
|
|
79
|
+
if (error) {
|
|
80
|
+
return defineError("Value is not valid", { cause: error, data });
|
|
81
|
+
}
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (branch.kind === "alwaysValid") {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (branch.kind === "alwaysInvalid" || data !== branch.value) {
|
|
88
|
+
return defineError("Value is not valid", { data });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function evaluateAnyOf(
|
|
94
|
+
branches: BranchEntry[],
|
|
95
|
+
data: any,
|
|
96
|
+
defineError: DefineErrorFunction
|
|
97
|
+
): Result {
|
|
98
|
+
for (let i = 0; i < branches.length; i++) {
|
|
99
|
+
const branch = branches[i];
|
|
100
|
+
if (branch.kind === "validate") {
|
|
101
|
+
if (!branch.validate(data)) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (branch.kind === "alwaysValid") {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (branch.kind === "literal" && data === branch.value) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return defineError("Value is not valid", { data });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function evaluateOneOf(
|
|
117
|
+
branches: BranchEntry[],
|
|
118
|
+
data: any,
|
|
119
|
+
defineError: DefineErrorFunction
|
|
120
|
+
): { error: Result; winnerIndex: number } {
|
|
121
|
+
let validCount = 0;
|
|
122
|
+
let winnerIndex = -1;
|
|
123
|
+
for (let i = 0; i < branches.length; i++) {
|
|
124
|
+
const branch = branches[i];
|
|
125
|
+
let isValid = false;
|
|
126
|
+
if (branch.kind === "validate") {
|
|
127
|
+
isValid = !branch.validate(data);
|
|
128
|
+
} else if (branch.kind === "alwaysValid") {
|
|
129
|
+
isValid = true;
|
|
130
|
+
} else if (branch.kind === "literal") {
|
|
131
|
+
isValid = data === branch.value;
|
|
132
|
+
}
|
|
133
|
+
if (isValid) {
|
|
134
|
+
validCount++;
|
|
135
|
+
winnerIndex = i;
|
|
136
|
+
if (validCount > 1) {
|
|
137
|
+
return {
|
|
138
|
+
error: defineError("Value is not valid", { data }),
|
|
139
|
+
winnerIndex: -1
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
error:
|
|
146
|
+
validCount === 1
|
|
147
|
+
? undefined
|
|
148
|
+
: defineError("Value is not valid", { data }),
|
|
149
|
+
winnerIndex: validCount === 1 ? winnerIndex : -1
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function createCombinatorValidator(
|
|
154
|
+
key: CombinatorKey,
|
|
155
|
+
schema: any,
|
|
156
|
+
defineError: DefineErrorFunction,
|
|
157
|
+
validateSubschema?: ValidateSubschemaFunction,
|
|
158
|
+
transactions?: TransactionHooks
|
|
159
|
+
): ValidateFunction {
|
|
160
|
+
const sourceBranches = getBranchEntries(schema, key);
|
|
161
|
+
const branches = validateSubschema
|
|
162
|
+
? sourceBranches.map((branch, index): BranchEntry =>
|
|
163
|
+
branch.kind === "validate"
|
|
164
|
+
? {
|
|
165
|
+
kind: "validate",
|
|
166
|
+
validate: (data) => validateSubschema(schema[key][index], data)
|
|
167
|
+
}
|
|
168
|
+
: branch
|
|
169
|
+
)
|
|
170
|
+
: sourceBranches;
|
|
171
|
+
|
|
172
|
+
if (!transactions) {
|
|
173
|
+
if (key === "allOf") {
|
|
174
|
+
return (data) => evaluateAllOf(branches, data, defineError);
|
|
175
|
+
}
|
|
176
|
+
if (key === "anyOf") {
|
|
177
|
+
return (data) => evaluateAnyOf(branches, data, defineError);
|
|
178
|
+
}
|
|
179
|
+
return (data) => evaluateOneOf(branches, data, defineError).error;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (key === "allOf") {
|
|
183
|
+
return (data) => {
|
|
184
|
+
const savepoint = transactions.savepoint();
|
|
185
|
+
try {
|
|
186
|
+
const error = evaluateAllOf(branches, data, defineError);
|
|
187
|
+
if (error) {
|
|
188
|
+
transactions.rollback(savepoint);
|
|
189
|
+
}
|
|
190
|
+
return error;
|
|
191
|
+
} catch (error) {
|
|
192
|
+
transactions.rollback(savepoint);
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (key === "anyOf") {
|
|
199
|
+
return (data) => evaluateAnyOf(branches, data, defineError);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return (data) => {
|
|
203
|
+
const savepoint = transactions.savepoint();
|
|
204
|
+
const defaultsByBranch: DefaultMutation[][] = [];
|
|
205
|
+
const isolatedBranches = branches.map((branch, index): BranchEntry =>
|
|
206
|
+
branch.kind === "validate"
|
|
207
|
+
? {
|
|
208
|
+
kind: "validate",
|
|
209
|
+
validate: (value) => {
|
|
210
|
+
const branchSavepoint = transactions.savepoint();
|
|
211
|
+
const error = branch.validate(value);
|
|
212
|
+
if (!error) {
|
|
213
|
+
defaultsByBranch[index] = transactions.capture(branchSavepoint);
|
|
214
|
+
}
|
|
215
|
+
return error;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
: branch
|
|
219
|
+
);
|
|
220
|
+
try {
|
|
221
|
+
const result = evaluateOneOf(isolatedBranches, data, defineError);
|
|
222
|
+
if (result.error) {
|
|
223
|
+
transactions.rollback(savepoint);
|
|
224
|
+
return result.error;
|
|
225
|
+
}
|
|
226
|
+
transactions.restore(defaultsByBranch[result.winnerIndex] || []);
|
|
227
|
+
return;
|
|
228
|
+
} catch (error) {
|
|
229
|
+
transactions.rollback(savepoint);
|
|
230
|
+
throw error;
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function prepareCombinatorEntries(schema: any) {
|
|
236
|
+
if (Array.isArray(schema.allOf)) {
|
|
237
|
+
getBranchEntries(schema, "allOf");
|
|
238
|
+
}
|
|
239
|
+
if (Array.isArray(schema.anyOf)) {
|
|
240
|
+
getBranchEntries(schema, "anyOf");
|
|
241
|
+
}
|
|
242
|
+
if (Array.isArray(schema.oneOf)) {
|
|
243
|
+
getBranchEntries(schema, "oneOf");
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
53
247
|
export const OtherKeywords: Record<string, KeywordFunction> = {
|
|
54
248
|
enum(schema, data, defineError) {
|
|
55
249
|
let enumCache = (schema as any)._enumCache as
|
|
@@ -71,7 +265,7 @@ export const OtherKeywords: Record<string, KeywordFunction> = {
|
|
|
71
265
|
}
|
|
72
266
|
|
|
73
267
|
enumCache = { primitiveSet, objectValues };
|
|
74
|
-
|
|
268
|
+
definePropertyOrThrow(schema, "_enumCache", {
|
|
75
269
|
value: enumCache,
|
|
76
270
|
enumerable: false,
|
|
77
271
|
configurable: false,
|
|
@@ -100,175 +294,15 @@ export const OtherKeywords: Record<string, KeywordFunction> = {
|
|
|
100
294
|
},
|
|
101
295
|
|
|
102
296
|
allOf(schema, data, defineError) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (branches.length === 1) {
|
|
106
|
-
const onlyBranch = branches[0];
|
|
107
|
-
|
|
108
|
-
if (onlyBranch.kind === "validate") {
|
|
109
|
-
const error = onlyBranch.validate(data);
|
|
110
|
-
if (error) {
|
|
111
|
-
return defineError("Value is not valid", { cause: error, data });
|
|
112
|
-
}
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (onlyBranch.kind === "alwaysValid") {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (onlyBranch.kind === "alwaysInvalid") {
|
|
121
|
-
return defineError("Value is not valid", { data });
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (data !== onlyBranch.value) {
|
|
125
|
-
return defineError("Value is not valid", { data });
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
for (let i = 0; i < branches.length; i++) {
|
|
132
|
-
const branch = branches[i];
|
|
133
|
-
|
|
134
|
-
if (branch.kind === "validate") {
|
|
135
|
-
const error = branch.validate(data);
|
|
136
|
-
if (error) {
|
|
137
|
-
return defineError("Value is not valid", { cause: error, data });
|
|
138
|
-
}
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (branch.kind === "alwaysValid") {
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (branch.kind === "alwaysInvalid") {
|
|
147
|
-
return defineError("Value is not valid", { data });
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (data !== branch.value) {
|
|
151
|
-
return defineError("Value is not valid", { data });
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return;
|
|
297
|
+
return createCombinatorValidator("allOf", schema, defineError)(data);
|
|
156
298
|
},
|
|
157
299
|
|
|
158
300
|
anyOf(schema, data, defineError) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (branches.length === 1) {
|
|
162
|
-
const onlyBranch = branches[0];
|
|
163
|
-
|
|
164
|
-
if (onlyBranch.kind === "validate") {
|
|
165
|
-
const error = onlyBranch.validate(data);
|
|
166
|
-
if (!error) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
return defineError("Value is not valid", { data });
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (onlyBranch.kind === "alwaysValid") {
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (onlyBranch.kind === "alwaysInvalid") {
|
|
177
|
-
return defineError("Value is not valid", { data });
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (data === onlyBranch.value) {
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return defineError("Value is not valid", { data });
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
for (let i = 0; i < branches.length; i++) {
|
|
188
|
-
const branch = branches[i];
|
|
189
|
-
|
|
190
|
-
if (branch.kind === "validate") {
|
|
191
|
-
const error = branch.validate(data);
|
|
192
|
-
if (!error) {
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (branch.kind === "alwaysValid") {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (branch.kind === "alwaysInvalid") {
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (data === branch.value) {
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return defineError("Value is not valid", { data });
|
|
301
|
+
return createCombinatorValidator("anyOf", schema, defineError)(data);
|
|
212
302
|
},
|
|
213
303
|
|
|
214
304
|
oneOf(schema, data, defineError) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (branches.length === 1) {
|
|
218
|
-
const onlyBranch = branches[0];
|
|
219
|
-
|
|
220
|
-
if (onlyBranch.kind === "validate") {
|
|
221
|
-
const error = onlyBranch.validate(data);
|
|
222
|
-
if (!error) {
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
return defineError("Value is not valid", { data });
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
if (onlyBranch.kind === "alwaysValid") {
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (onlyBranch.kind === "alwaysInvalid") {
|
|
233
|
-
return defineError("Value is not valid", { data });
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (data === onlyBranch.value) {
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return defineError("Value is not valid", { data });
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
let validCount = 0;
|
|
244
|
-
|
|
245
|
-
for (let i = 0; i < branches.length; i++) {
|
|
246
|
-
const branch = branches[i];
|
|
247
|
-
let isValid = false;
|
|
248
|
-
|
|
249
|
-
if (branch.kind === "validate") {
|
|
250
|
-
isValid = !branch.validate(data);
|
|
251
|
-
} else if (branch.kind === "alwaysValid") {
|
|
252
|
-
isValid = true;
|
|
253
|
-
} else if (branch.kind === "alwaysInvalid") {
|
|
254
|
-
isValid = false;
|
|
255
|
-
} else {
|
|
256
|
-
isValid = data === branch.value;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (isValid) {
|
|
260
|
-
validCount++;
|
|
261
|
-
if (validCount > 1) {
|
|
262
|
-
return defineError("Value is not valid", { data });
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if (validCount === 1) {
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return defineError("Value is not valid", { data });
|
|
305
|
+
return createCombinatorValidator("oneOf", schema, defineError)(data);
|
|
272
306
|
},
|
|
273
307
|
|
|
274
308
|
const(schema, data, defineError) {
|
|
@@ -352,32 +386,11 @@ export const OtherKeywords: Record<string, KeywordFunction> = {
|
|
|
352
386
|
return defineError("Value is not valid", { data });
|
|
353
387
|
},
|
|
354
388
|
|
|
355
|
-
$ref(schema, data, defineError
|
|
356
|
-
if (schema._resolvedRef) {
|
|
357
|
-
if (schema.$validate !== schema._resolvedRef) {
|
|
358
|
-
schema.$validate = schema._resolvedRef;
|
|
359
|
-
}
|
|
360
|
-
|
|
389
|
+
$ref(schema, data, defineError) {
|
|
390
|
+
if (typeof schema._resolvedRef === "function") {
|
|
361
391
|
return schema._resolvedRef(data);
|
|
362
392
|
}
|
|
363
393
|
|
|
364
|
-
|
|
365
|
-
let targetSchema = instance.getSchemaRef(refPath);
|
|
366
|
-
|
|
367
|
-
if (!targetSchema) {
|
|
368
|
-
targetSchema = instance.getSchemaById(refPath);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
if (!targetSchema) {
|
|
372
|
-
return defineError(`Missing reference: ${refPath}`);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
if (!targetSchema.$validate) {
|
|
376
|
-
return;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
schema._resolvedRef = targetSchema.$validate;
|
|
380
|
-
schema.$validate = schema._resolvedRef;
|
|
381
|
-
return schema._resolvedRef(data);
|
|
394
|
+
return defineError(`Missing reference: ${schema.$ref}`);
|
|
382
395
|
}
|
|
383
396
|
};
|
|
@@ -1,12 +1,46 @@
|
|
|
1
1
|
import { FormatFunction, KeywordFunction } from "../index";
|
|
2
2
|
import { compilePatternMatcher } from "../utils/pattern-matcher";
|
|
3
|
+
import { definePropertyOrThrow } from "../utils/main-utils";
|
|
3
4
|
|
|
4
5
|
const PATTERN_MATCH_CACHE_LIMIT = 512;
|
|
5
6
|
const FORMAT_RESULT_CACHE_LIMIT = 512;
|
|
6
7
|
|
|
8
|
+
function hasAtLeastCodePoints(value: string, limit: number): boolean {
|
|
9
|
+
let count = 0;
|
|
10
|
+
for (let index = 0; index < value.length; index++) {
|
|
11
|
+
const unit = value.charCodeAt(index);
|
|
12
|
+
if (
|
|
13
|
+
unit >= 0xd800 &&
|
|
14
|
+
unit <= 0xdbff &&
|
|
15
|
+
index + 1 < value.length
|
|
16
|
+
) {
|
|
17
|
+
const nextUnit = value.charCodeAt(index + 1);
|
|
18
|
+
if (nextUnit >= 0xdc00 && nextUnit <= 0xdfff) {
|
|
19
|
+
index++;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
count++;
|
|
24
|
+
if (count >= limit) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return count >= limit;
|
|
30
|
+
}
|
|
31
|
+
|
|
7
32
|
export const StringKeywords: Record<string, KeywordFunction> = {
|
|
8
33
|
minLength(schema, data, defineError) {
|
|
9
|
-
if (typeof data !== "string"
|
|
34
|
+
if (typeof data !== "string") {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const units = data.length;
|
|
39
|
+
const limit = schema.minLength;
|
|
40
|
+
if (units < limit) {
|
|
41
|
+
return defineError("Value is shorter than the minimum length", { data });
|
|
42
|
+
}
|
|
43
|
+
if (units - limit >= limit || hasAtLeastCodePoints(data, limit)) {
|
|
10
44
|
return;
|
|
11
45
|
}
|
|
12
46
|
|
|
@@ -14,11 +48,20 @@ export const StringKeywords: Record<string, KeywordFunction> = {
|
|
|
14
48
|
},
|
|
15
49
|
|
|
16
50
|
maxLength(schema, data, defineError) {
|
|
17
|
-
if (typeof data !== "string"
|
|
51
|
+
if (typeof data !== "string") {
|
|
18
52
|
return;
|
|
19
53
|
}
|
|
20
54
|
|
|
21
|
-
|
|
55
|
+
const units = data.length;
|
|
56
|
+
const limit = schema.maxLength;
|
|
57
|
+
if (units <= limit) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (units - limit > limit || hasAtLeastCodePoints(data, limit + 1)) {
|
|
61
|
+
return defineError("Value is longer than the maximum length", { data });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return;
|
|
22
65
|
},
|
|
23
66
|
|
|
24
67
|
pattern(schema, data, defineError) {
|
|
@@ -42,7 +85,7 @@ export const StringKeywords: Record<string, KeywordFunction> = {
|
|
|
42
85
|
? (value: string) => compiled.test(value)
|
|
43
86
|
: compiled;
|
|
44
87
|
|
|
45
|
-
|
|
88
|
+
definePropertyOrThrow(schema, "_patternMatch", {
|
|
46
89
|
value: patternMatch,
|
|
47
90
|
enumerable: false,
|
|
48
91
|
configurable: false,
|
|
@@ -58,7 +101,7 @@ export const StringKeywords: Record<string, KeywordFunction> = {
|
|
|
58
101
|
|
|
59
102
|
if (!patternMatchCache) {
|
|
60
103
|
patternMatchCache = new Map<string, boolean>();
|
|
61
|
-
|
|
104
|
+
definePropertyOrThrow(schema, "_patternMatchCache", {
|
|
62
105
|
value: patternMatchCache,
|
|
63
106
|
enumerable: false,
|
|
64
107
|
configurable: false,
|
|
@@ -104,7 +147,7 @@ export const StringKeywords: Record<string, KeywordFunction> = {
|
|
|
104
147
|
|
|
105
148
|
if (formatValidate === undefined) {
|
|
106
149
|
formatValidate = instance.getFormat(schema.format);
|
|
107
|
-
|
|
150
|
+
definePropertyOrThrow(schema, "_formatValidate", {
|
|
108
151
|
value: formatValidate,
|
|
109
152
|
enumerable: false,
|
|
110
153
|
configurable: false,
|
|
@@ -122,7 +165,7 @@ export const StringKeywords: Record<string, KeywordFunction> = {
|
|
|
122
165
|
formatValidate
|
|
123
166
|
);
|
|
124
167
|
|
|
125
|
-
|
|
168
|
+
definePropertyOrThrow(schema, "_formatResultCacheEnabled", {
|
|
126
169
|
value: formatResultCacheEnabled,
|
|
127
170
|
enumerable: false,
|
|
128
171
|
configurable: false,
|
|
@@ -140,7 +183,7 @@ export const StringKeywords: Record<string, KeywordFunction> = {
|
|
|
140
183
|
|
|
141
184
|
if (!formatResultCache) {
|
|
142
185
|
formatResultCache = new Map<string, boolean>();
|
|
143
|
-
|
|
186
|
+
definePropertyOrThrow(schema, "_formatResultCache", {
|
|
144
187
|
value: formatResultCache,
|
|
145
188
|
enumerable: false,
|
|
146
189
|
configurable: false,
|
package/lib/types.ts
CHANGED
|
@@ -11,10 +11,10 @@ export const Types: Record<string, TypeFunction | false> = {
|
|
|
11
11
|
return typeof data === "string";
|
|
12
12
|
},
|
|
13
13
|
number(data) {
|
|
14
|
-
return typeof data === "number";
|
|
14
|
+
return typeof data === "number" && Number.isFinite(data);
|
|
15
15
|
},
|
|
16
16
|
integer(data) {
|
|
17
|
-
return typeof data === "number" && data % 1 === 0;
|
|
17
|
+
return typeof data === "number" && Number.isFinite(data) && data % 1 === 0;
|
|
18
18
|
},
|
|
19
19
|
boolean(data) {
|
|
20
20
|
return typeof data === "boolean";
|
package/lib/utils/deep-freeze.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { definePropertyOrThrow } from "./main-utils";
|
|
2
|
+
|
|
1
3
|
export function deepFreeze(
|
|
2
4
|
obj: any,
|
|
3
5
|
freezeClassInstances: boolean = false,
|
|
@@ -26,7 +28,7 @@ export function deepFreeze(
|
|
|
26
28
|
|
|
27
29
|
// If the object is an instance of a class (not a plain object or array) we need to freeze the prototype
|
|
28
30
|
if (freezeClassInstances) {
|
|
29
|
-
const proto =
|
|
31
|
+
const proto = Reflect.getPrototypeOf(obj);
|
|
30
32
|
if (proto && proto !== Object.prototype) {
|
|
31
33
|
deepFreeze(proto, freezeClassInstances, seen);
|
|
32
34
|
}
|
|
@@ -43,7 +45,7 @@ function isPlainObject(value: any): boolean {
|
|
|
43
45
|
return false;
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
const proto =
|
|
48
|
+
const proto = Reflect.getPrototypeOf(value);
|
|
47
49
|
return proto === Object.prototype || proto === null;
|
|
48
50
|
}
|
|
49
51
|
|
|
@@ -168,7 +170,7 @@ export function deepCloneUnfreeze<T>(
|
|
|
168
170
|
seen.set(source, clone);
|
|
169
171
|
return clone;
|
|
170
172
|
}
|
|
171
|
-
clone = Object.create(
|
|
173
|
+
clone = Object.create(Reflect.getPrototypeOf(source));
|
|
172
174
|
seen.set(source, clone);
|
|
173
175
|
break;
|
|
174
176
|
}
|
|
@@ -201,7 +203,7 @@ export function deepCloneUnfreeze<T>(
|
|
|
201
203
|
seen
|
|
202
204
|
);
|
|
203
205
|
}
|
|
204
|
-
|
|
206
|
+
definePropertyOrThrow(clone, key, descriptor);
|
|
205
207
|
}
|
|
206
208
|
|
|
207
209
|
return clone;
|