@ls-stack/utils 3.48.0 → 3.49.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/dist/partialEqual.cjs +551 -695
- package/dist/partialEqual.d.cts +38 -5
- package/dist/partialEqual.d.ts +38 -5
- package/dist/partialEqual.js +550 -634
- package/package.json +1 -1
package/dist/partialEqual.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-
|
|
2
|
+
exhaustiveCheck
|
|
3
|
+
} from "./chunk-C2SVCIWE.js";
|
|
4
|
+
import "./chunk-JF2MDHOJ.js";
|
|
4
5
|
|
|
5
6
|
// src/partialEqual.ts
|
|
6
7
|
import { err, ok } from "t-result";
|
|
@@ -42,8 +43,24 @@ var match = {
|
|
|
42
43
|
partialEqual: (value) => createComparison(["partialEqual", value]),
|
|
43
44
|
custom: (isEqual) => createComparison(["custom", isEqual]),
|
|
44
45
|
keyNotBePresent: createComparison(["keyNotBePresent", null]),
|
|
45
|
-
any: (...
|
|
46
|
-
|
|
46
|
+
any: (...values) => createComparison([
|
|
47
|
+
"any",
|
|
48
|
+
values.map((v) => {
|
|
49
|
+
if (isComparison(v)) return v["~sc"];
|
|
50
|
+
if (typeof v === "object" && v !== null)
|
|
51
|
+
return ["partialEqual", v];
|
|
52
|
+
return ["deepEqual", v];
|
|
53
|
+
})
|
|
54
|
+
]),
|
|
55
|
+
all: (...values) => createComparison([
|
|
56
|
+
"all",
|
|
57
|
+
values.map((v) => {
|
|
58
|
+
if (isComparison(v)) return v["~sc"];
|
|
59
|
+
if (typeof v === "object" && v !== null)
|
|
60
|
+
return ["partialEqual", v];
|
|
61
|
+
return ["deepEqual", v];
|
|
62
|
+
})
|
|
63
|
+
]),
|
|
47
64
|
not: {
|
|
48
65
|
hasType: {
|
|
49
66
|
string: createComparison(["not", ["hasType", "string"]]),
|
|
@@ -74,741 +91,640 @@ var match = {
|
|
|
74
91
|
equal: (value) => createComparison(["not", ["deepEqual", value]]),
|
|
75
92
|
partialEqual: (value) => createComparison(["not", ["partialEqual", value]]),
|
|
76
93
|
custom: (value) => createComparison(["not", ["custom", value]]),
|
|
77
|
-
any: (...
|
|
78
|
-
|
|
94
|
+
any: (...values) => createComparison([
|
|
95
|
+
"not",
|
|
96
|
+
[
|
|
97
|
+
"any",
|
|
98
|
+
values.map((v) => {
|
|
99
|
+
if (isComparison(v)) return v["~sc"];
|
|
100
|
+
if (typeof v === "object" && v !== null)
|
|
101
|
+
return ["partialEqual", v];
|
|
102
|
+
return ["deepEqual", v];
|
|
103
|
+
})
|
|
104
|
+
]
|
|
105
|
+
]),
|
|
106
|
+
all: (...values) => createComparison([
|
|
107
|
+
"not",
|
|
108
|
+
[
|
|
109
|
+
"all",
|
|
110
|
+
values.map((v) => {
|
|
111
|
+
if (isComparison(v)) return v["~sc"];
|
|
112
|
+
if (typeof v === "object" && v !== null)
|
|
113
|
+
return ["partialEqual", v];
|
|
114
|
+
return ["deepEqual", v];
|
|
115
|
+
})
|
|
116
|
+
]
|
|
117
|
+
]),
|
|
79
118
|
noExtraKeys: (partialShape) => createComparison(["not", ["withNoExtraKeys", partialShape]]),
|
|
80
119
|
deepNoExtraKeys: (partialShape) => createComparison(["not", ["withDeepNoExtraKeys", partialShape]]),
|
|
81
120
|
noExtraDefinedKeys: (partialShape) => createComparison(["not", ["noExtraDefinedKeys", partialShape]]),
|
|
82
121
|
deepNoExtraDefinedKeys: (partialShape) => createComparison(["not", ["deepNoExtraDefinedKeys", partialShape]])
|
|
83
122
|
}
|
|
84
123
|
};
|
|
85
|
-
function
|
|
86
|
-
|
|
87
|
-
if (partialEqual(key, tar)) return key;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
function executeComparisonWithKeyContext(target, comp, keyExists) {
|
|
91
|
-
const [type, value] = comp;
|
|
92
|
-
if (type === "keyNotBePresent") {
|
|
93
|
-
return !keyExists;
|
|
94
|
-
}
|
|
95
|
-
if (type === "any") {
|
|
96
|
-
for (const childComp of value) {
|
|
97
|
-
if (executeComparisonWithKeyContext(target, childComp, keyExists)) {
|
|
98
|
-
return true;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
if (type === "not") {
|
|
104
|
-
return !executeComparisonWithKeyContext(target, value, keyExists);
|
|
105
|
-
}
|
|
106
|
-
return executeComparison(target, comp);
|
|
124
|
+
function isComparison(value) {
|
|
125
|
+
return value && typeof value === "object" && "~sc" in value;
|
|
107
126
|
}
|
|
108
|
-
function executeComparison(target, comparison) {
|
|
127
|
+
function executeComparison(target, comparison, context) {
|
|
109
128
|
const [type, value] = comparison;
|
|
110
129
|
switch (type) {
|
|
111
|
-
case "hasType":
|
|
112
|
-
switch (value) {
|
|
113
|
-
case "string":
|
|
114
|
-
return typeof target === "string";
|
|
115
|
-
case "number":
|
|
116
|
-
return typeof target === "number";
|
|
117
|
-
case "boolean":
|
|
118
|
-
return typeof target === "boolean";
|
|
119
|
-
case "function":
|
|
120
|
-
return typeof target === "function";
|
|
121
|
-
case "array":
|
|
122
|
-
return Array.isArray(target);
|
|
123
|
-
case "object":
|
|
124
|
-
return typeof target === "object" && target !== null && !Array.isArray(target);
|
|
125
|
-
default:
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
case "isInstanceOf":
|
|
129
|
-
return target instanceof value;
|
|
130
130
|
case "strStartsWith":
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
case "strMatchesRegex":
|
|
137
|
-
return typeof target === "string" && value.test(target);
|
|
138
|
-
case "numIsGreaterThan":
|
|
139
|
-
return typeof target === "number" && target > value;
|
|
140
|
-
case "numIsGreaterThanOrEqual":
|
|
141
|
-
return typeof target === "number" && target >= value;
|
|
142
|
-
case "numIsLessThan":
|
|
143
|
-
return typeof target === "number" && target < value;
|
|
144
|
-
case "numIsLessThanOrEqual":
|
|
145
|
-
return typeof target === "number" && target <= value;
|
|
146
|
-
case "numIsInRange":
|
|
147
|
-
return typeof target === "number" && target >= value[0] && target <= value[1];
|
|
148
|
-
case "jsonStringHasPartial":
|
|
149
|
-
if (typeof target !== "string") return false;
|
|
150
|
-
try {
|
|
151
|
-
const parsed = JSON.parse(target);
|
|
152
|
-
return partialEqual(parsed, value);
|
|
153
|
-
} catch {
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
case "deepEqual":
|
|
157
|
-
return deepEqual(target, value);
|
|
158
|
-
case "partialEqual":
|
|
159
|
-
return partialEqual(target, value);
|
|
160
|
-
case "custom":
|
|
161
|
-
return value(target);
|
|
162
|
-
case "keyNotBePresent":
|
|
163
|
-
return false;
|
|
164
|
-
case "any":
|
|
165
|
-
for (const comp of value) {
|
|
166
|
-
if (executeComparison(target, comp)) {
|
|
167
|
-
return true;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
return false;
|
|
171
|
-
case "all":
|
|
172
|
-
for (const comp of value) {
|
|
173
|
-
if (!executeComparison(target, comp)) {
|
|
174
|
-
return false;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return true;
|
|
178
|
-
case "not":
|
|
179
|
-
return !executeComparison(target, value);
|
|
180
|
-
case "withNoExtraKeys":
|
|
181
|
-
if (typeof target !== "object" || target === null || Array.isArray(target)) {
|
|
131
|
+
if (typeof target !== "string") {
|
|
132
|
+
addError(context, {
|
|
133
|
+
message: `Expected string starting with "${value}"`,
|
|
134
|
+
received: target
|
|
135
|
+
});
|
|
182
136
|
return false;
|
|
183
137
|
}
|
|
184
|
-
if (
|
|
138
|
+
if (!target.startsWith(value)) {
|
|
139
|
+
addError(context, {
|
|
140
|
+
message: `Expected string starting with "${value}"`,
|
|
141
|
+
received: target
|
|
142
|
+
});
|
|
185
143
|
return false;
|
|
186
144
|
}
|
|
187
|
-
for (const key in target) {
|
|
188
|
-
if (has.call(target, key) && !has.call(value, key)) {
|
|
189
|
-
return false;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
for (const key in value) {
|
|
193
|
-
if (has.call(value, key)) {
|
|
194
|
-
if (!has.call(target, key)) {
|
|
195
|
-
return false;
|
|
196
|
-
}
|
|
197
|
-
if (!partialEqual(target[key], value[key])) {
|
|
198
|
-
return false;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
145
|
return true;
|
|
203
|
-
case "
|
|
204
|
-
if (typeof target !== "
|
|
146
|
+
case "strEndsWith":
|
|
147
|
+
if (typeof target !== "string") {
|
|
148
|
+
addError(context, {
|
|
149
|
+
message: `Expected string ending with "${value}"`,
|
|
150
|
+
received: target
|
|
151
|
+
});
|
|
205
152
|
return false;
|
|
206
153
|
}
|
|
207
|
-
if (
|
|
154
|
+
if (!target.endsWith(value)) {
|
|
155
|
+
addError(context, {
|
|
156
|
+
message: `Expected string ending with "${value}"`,
|
|
157
|
+
received: target
|
|
158
|
+
});
|
|
208
159
|
return false;
|
|
209
160
|
}
|
|
210
|
-
for (const key in target) {
|
|
211
|
-
if (has.call(target, key) && !has.call(value, key)) {
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
for (const key in value) {
|
|
216
|
-
if (has.call(value, key)) {
|
|
217
|
-
if (!has.call(target, key)) {
|
|
218
|
-
return false;
|
|
219
|
-
}
|
|
220
|
-
const targetValue = target[key];
|
|
221
|
-
const partialValue = value[key];
|
|
222
|
-
if (partialValue && typeof partialValue === "object" && "~sc" in partialValue && partialValue["~sc"][0] === "withDeepNoExtraKeys") {
|
|
223
|
-
if (!executeComparison(targetValue, partialValue["~sc"])) {
|
|
224
|
-
return false;
|
|
225
|
-
}
|
|
226
|
-
} else if (partialValue && typeof partialValue === "object" && !Array.isArray(partialValue) && partialValue.constructor === Object && targetValue && typeof targetValue === "object" && !Array.isArray(targetValue) && targetValue.constructor === Object) {
|
|
227
|
-
if (!executeComparison(targetValue, [
|
|
228
|
-
"withDeepNoExtraKeys",
|
|
229
|
-
partialValue
|
|
230
|
-
])) {
|
|
231
|
-
return false;
|
|
232
|
-
}
|
|
233
|
-
} else {
|
|
234
|
-
if (!partialEqual(targetValue, partialValue)) {
|
|
235
|
-
return false;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
161
|
return true;
|
|
241
|
-
case "
|
|
242
|
-
if (typeof target !== "
|
|
162
|
+
case "strContains":
|
|
163
|
+
if (typeof target !== "string") {
|
|
164
|
+
addError(context, {
|
|
165
|
+
message: `Expected string containing "${value}"`,
|
|
166
|
+
received: target
|
|
167
|
+
});
|
|
243
168
|
return false;
|
|
244
169
|
}
|
|
245
|
-
if (
|
|
170
|
+
if (!target.includes(value)) {
|
|
171
|
+
addError(context, {
|
|
172
|
+
message: `Expected string containing "${value}"`,
|
|
173
|
+
received: target
|
|
174
|
+
});
|
|
246
175
|
return false;
|
|
247
176
|
}
|
|
248
|
-
for (const key in target) {
|
|
249
|
-
if (has.call(target, key) && target[key] !== void 0 && !has.call(value, key)) {
|
|
250
|
-
return false;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
for (const key in value) {
|
|
254
|
-
if (has.call(value, key)) {
|
|
255
|
-
if (!has.call(target, key)) {
|
|
256
|
-
return false;
|
|
257
|
-
}
|
|
258
|
-
if (!partialEqual(target[key], value[key])) {
|
|
259
|
-
return false;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
177
|
return true;
|
|
264
|
-
case "
|
|
265
|
-
if (typeof target !== "
|
|
178
|
+
case "strMatchesRegex":
|
|
179
|
+
if (typeof target !== "string") {
|
|
180
|
+
addError(context, {
|
|
181
|
+
message: `Expected string matching regex ${value}`,
|
|
182
|
+
received: target
|
|
183
|
+
});
|
|
266
184
|
return false;
|
|
267
185
|
}
|
|
268
|
-
if (
|
|
186
|
+
if (!value.test(target)) {
|
|
187
|
+
addError(context, {
|
|
188
|
+
message: `Expected string matching regex ${value}`,
|
|
189
|
+
received: target
|
|
190
|
+
});
|
|
269
191
|
return false;
|
|
270
192
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
193
|
+
return true;
|
|
194
|
+
case "hasType": {
|
|
195
|
+
let actualType;
|
|
196
|
+
if (value === "array") {
|
|
197
|
+
actualType = Array.isArray(target) ? "array" : typeof target;
|
|
198
|
+
} else if (value === "object") {
|
|
199
|
+
if (target === null || Array.isArray(target)) {
|
|
200
|
+
actualType = "not-object";
|
|
201
|
+
} else {
|
|
202
|
+
actualType = typeof target;
|
|
274
203
|
}
|
|
204
|
+
} else {
|
|
205
|
+
actualType = typeof target;
|
|
275
206
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const partialValue = value[key];
|
|
283
|
-
if (partialValue && typeof partialValue === "object" && "~sc" in partialValue && partialValue["~sc"][0] === "deepNoExtraDefinedKeys") {
|
|
284
|
-
if (!executeComparison(targetValue, partialValue["~sc"])) {
|
|
285
|
-
return false;
|
|
286
|
-
}
|
|
287
|
-
} else if (partialValue && typeof partialValue === "object" && !Array.isArray(partialValue) && partialValue.constructor === Object && targetValue && typeof targetValue === "object" && !Array.isArray(targetValue) && targetValue.constructor === Object) {
|
|
288
|
-
if (!executeComparison(targetValue, [
|
|
289
|
-
"deepNoExtraDefinedKeys",
|
|
290
|
-
partialValue
|
|
291
|
-
])) {
|
|
292
|
-
return false;
|
|
293
|
-
}
|
|
294
|
-
} else {
|
|
295
|
-
if (!partialEqual(targetValue, partialValue)) {
|
|
296
|
-
return false;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
207
|
+
if (actualType !== value) {
|
|
208
|
+
addError(context, {
|
|
209
|
+
message: `Expected type ${value}`,
|
|
210
|
+
received: target
|
|
211
|
+
});
|
|
212
|
+
return false;
|
|
300
213
|
}
|
|
301
214
|
return true;
|
|
302
|
-
default:
|
|
303
|
-
return false;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
function formatPath(path) {
|
|
307
|
-
if (path.length === 0) return "";
|
|
308
|
-
let result = path[0] || "";
|
|
309
|
-
for (let i = 1; i < path.length; i++) {
|
|
310
|
-
const segment = path[i];
|
|
311
|
-
if (segment && segment.startsWith("[") && segment.endsWith("]")) {
|
|
312
|
-
result += segment;
|
|
313
|
-
} else if (segment) {
|
|
314
|
-
result += `.${segment}`;
|
|
315
215
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
};
|
|
325
|
-
if (expected !== void 0) {
|
|
326
|
-
error.expected = expected;
|
|
327
|
-
}
|
|
328
|
-
context.errors.push(error);
|
|
329
|
-
}
|
|
330
|
-
function executeComparisonWithErrorCollection(target, comparison, context) {
|
|
331
|
-
const [type, value] = comparison;
|
|
332
|
-
switch (type) {
|
|
333
|
-
case "hasType":
|
|
334
|
-
switch (value) {
|
|
335
|
-
case "string":
|
|
336
|
-
if (typeof target !== "string") {
|
|
337
|
-
addError(context, `Expected type string`, target);
|
|
338
|
-
}
|
|
339
|
-
break;
|
|
340
|
-
case "number":
|
|
341
|
-
if (typeof target !== "number") {
|
|
342
|
-
addError(context, `Expected type number`, target);
|
|
343
|
-
}
|
|
344
|
-
break;
|
|
345
|
-
case "boolean":
|
|
346
|
-
if (typeof target !== "boolean") {
|
|
347
|
-
addError(context, `Expected type boolean`, target);
|
|
348
|
-
}
|
|
349
|
-
break;
|
|
350
|
-
case "function":
|
|
351
|
-
if (typeof target !== "function") {
|
|
352
|
-
addError(context, `Expected type function`, target);
|
|
353
|
-
}
|
|
354
|
-
break;
|
|
355
|
-
case "array":
|
|
356
|
-
if (!Array.isArray(target)) {
|
|
357
|
-
addError(context, `Expected array`, target);
|
|
358
|
-
}
|
|
359
|
-
break;
|
|
360
|
-
case "object":
|
|
361
|
-
if (typeof target !== "object" || target === null || Array.isArray(target)) {
|
|
362
|
-
addError(context, `Expected object`, target);
|
|
363
|
-
}
|
|
364
|
-
break;
|
|
365
|
-
}
|
|
366
|
-
break;
|
|
367
|
-
case "isInstanceOf":
|
|
368
|
-
if (!(target instanceof value)) {
|
|
369
|
-
addError(
|
|
370
|
-
context,
|
|
371
|
-
`Expected instance of ${value.name}`,
|
|
372
|
-
target
|
|
373
|
-
);
|
|
374
|
-
}
|
|
375
|
-
break;
|
|
376
|
-
case "strStartsWith":
|
|
377
|
-
if (typeof target !== "string" || !target.startsWith(value)) {
|
|
378
|
-
addError(
|
|
379
|
-
context,
|
|
380
|
-
`Expected string starting with "${value}"`,
|
|
381
|
-
target
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
break;
|
|
385
|
-
case "strEndsWith":
|
|
386
|
-
if (typeof target !== "string" || !target.endsWith(value)) {
|
|
387
|
-
addError(
|
|
388
|
-
context,
|
|
389
|
-
`Expected string ending with "${value}"`,
|
|
390
|
-
target
|
|
391
|
-
);
|
|
392
|
-
}
|
|
393
|
-
break;
|
|
394
|
-
case "strContains":
|
|
395
|
-
if (typeof target !== "string" || !target.includes(value)) {
|
|
396
|
-
addError(
|
|
397
|
-
context,
|
|
398
|
-
`Expected string containing "${value}"`,
|
|
399
|
-
target
|
|
400
|
-
);
|
|
401
|
-
}
|
|
402
|
-
break;
|
|
403
|
-
case "strMatchesRegex":
|
|
404
|
-
if (typeof target !== "string" || !value.test(target)) {
|
|
405
|
-
addError(
|
|
406
|
-
context,
|
|
407
|
-
`Expected string matching regex ${value}`,
|
|
408
|
-
target
|
|
409
|
-
);
|
|
216
|
+
case "deepEqual":
|
|
217
|
+
if (!deepEqual(target, value)) {
|
|
218
|
+
addError(context, {
|
|
219
|
+
message: "Values are not deeply equal",
|
|
220
|
+
received: target,
|
|
221
|
+
expected: value
|
|
222
|
+
});
|
|
223
|
+
return false;
|
|
410
224
|
}
|
|
411
|
-
|
|
225
|
+
return true;
|
|
412
226
|
case "numIsGreaterThan":
|
|
413
227
|
if (typeof target !== "number" || target <= value) {
|
|
414
|
-
addError(
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
228
|
+
addError(context, {
|
|
229
|
+
message: `Expected number greater than ${value}`,
|
|
230
|
+
received: target
|
|
231
|
+
});
|
|
232
|
+
return false;
|
|
419
233
|
}
|
|
420
|
-
|
|
234
|
+
return true;
|
|
421
235
|
case "numIsGreaterThanOrEqual":
|
|
422
236
|
if (typeof target !== "number" || target < value) {
|
|
423
|
-
addError(
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
237
|
+
addError(context, {
|
|
238
|
+
message: `Expected number greater than or equal to ${value}`,
|
|
239
|
+
received: target
|
|
240
|
+
});
|
|
241
|
+
return false;
|
|
428
242
|
}
|
|
429
|
-
|
|
243
|
+
return true;
|
|
430
244
|
case "numIsLessThan":
|
|
431
245
|
if (typeof target !== "number" || target >= value) {
|
|
432
|
-
addError(
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
246
|
+
addError(context, {
|
|
247
|
+
message: `Expected number less than ${value}`,
|
|
248
|
+
received: target
|
|
249
|
+
});
|
|
250
|
+
return false;
|
|
437
251
|
}
|
|
438
|
-
|
|
252
|
+
return true;
|
|
439
253
|
case "numIsLessThanOrEqual":
|
|
440
254
|
if (typeof target !== "number" || target > value) {
|
|
441
|
-
addError(
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
255
|
+
addError(context, {
|
|
256
|
+
message: `Expected number less than or equal to ${value}`,
|
|
257
|
+
received: target
|
|
258
|
+
});
|
|
259
|
+
return false;
|
|
446
260
|
}
|
|
447
|
-
|
|
261
|
+
return true;
|
|
448
262
|
case "numIsInRange":
|
|
449
263
|
if (typeof target !== "number" || target < value[0] || target > value[1]) {
|
|
450
|
-
addError(
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
264
|
+
addError(context, {
|
|
265
|
+
message: `Expected number in range [${value[0]}, ${value[1]}]`,
|
|
266
|
+
received: target
|
|
267
|
+
});
|
|
268
|
+
return false;
|
|
455
269
|
}
|
|
456
|
-
|
|
270
|
+
return true;
|
|
457
271
|
case "jsonStringHasPartial":
|
|
458
272
|
if (typeof target !== "string") {
|
|
459
|
-
addError(context,
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
} catch {
|
|
465
|
-
addError(
|
|
466
|
-
context,
|
|
467
|
-
`Expected valid JSON string`,
|
|
468
|
-
target
|
|
469
|
-
);
|
|
470
|
-
}
|
|
273
|
+
addError(context, {
|
|
274
|
+
message: "Expected JSON string",
|
|
275
|
+
received: target
|
|
276
|
+
});
|
|
277
|
+
return false;
|
|
471
278
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
279
|
+
try {
|
|
280
|
+
const parsed = JSON.parse(target);
|
|
281
|
+
if (!partialEqualInternal(parsed, value, context)) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
} catch {
|
|
285
|
+
addError(context, {
|
|
286
|
+
message: "Expected valid JSON string",
|
|
287
|
+
received: target
|
|
288
|
+
});
|
|
289
|
+
return false;
|
|
476
290
|
}
|
|
477
|
-
|
|
291
|
+
return true;
|
|
478
292
|
case "partialEqual":
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
if (
|
|
483
|
-
addError(context,
|
|
293
|
+
return partialEqualInternal(target, value, context);
|
|
294
|
+
case "custom": {
|
|
295
|
+
const result = value(target);
|
|
296
|
+
if (result !== true) {
|
|
297
|
+
addError(context, {
|
|
298
|
+
message: `Custom validation failed ${typeof result === "object" ? `: ${result.error}` : ""}`,
|
|
299
|
+
received: target
|
|
300
|
+
});
|
|
301
|
+
return false;
|
|
484
302
|
}
|
|
485
|
-
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
case "isInstanceOf":
|
|
306
|
+
if (!(target instanceof value)) {
|
|
307
|
+
addError(context, {
|
|
308
|
+
message: `Expected instance of ${value.name}`,
|
|
309
|
+
received: target
|
|
310
|
+
});
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
return true;
|
|
486
314
|
case "keyNotBePresent":
|
|
487
|
-
addError(context,
|
|
488
|
-
|
|
315
|
+
addError(context, {
|
|
316
|
+
message: "This property should not be present",
|
|
317
|
+
received: target
|
|
318
|
+
});
|
|
319
|
+
return false;
|
|
320
|
+
case "not": {
|
|
321
|
+
const tempContext = {
|
|
322
|
+
errors: [],
|
|
323
|
+
path: context.path
|
|
324
|
+
};
|
|
325
|
+
const result = executeComparison(target, value, tempContext);
|
|
326
|
+
if (result) {
|
|
327
|
+
addError(context, {
|
|
328
|
+
message: "Expected negated condition to fail",
|
|
329
|
+
received: target,
|
|
330
|
+
expected: { "not match": value }
|
|
331
|
+
});
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
489
336
|
case "any": {
|
|
490
|
-
for (const
|
|
491
|
-
const
|
|
337
|
+
for (const subComparison of value) {
|
|
338
|
+
const anyTempContext = {
|
|
492
339
|
errors: [],
|
|
493
|
-
path:
|
|
340
|
+
path: context.path
|
|
494
341
|
};
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
return;
|
|
342
|
+
if (executeComparison(target, subComparison, anyTempContext)) {
|
|
343
|
+
return true;
|
|
498
344
|
}
|
|
499
345
|
}
|
|
500
|
-
addError(
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
346
|
+
addError(context, {
|
|
347
|
+
message: "None of the alternative comparisons matched",
|
|
348
|
+
received: target,
|
|
349
|
+
expected: {
|
|
350
|
+
matchAny: value
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
return false;
|
|
506
354
|
}
|
|
507
|
-
case "all":
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
const subContext = {
|
|
514
|
-
errors: [],
|
|
515
|
-
path: [...context.path]
|
|
516
|
-
};
|
|
517
|
-
executeComparisonWithErrorCollection(target, value, subContext);
|
|
518
|
-
if (subContext.errors.length === 0) {
|
|
519
|
-
addError(
|
|
520
|
-
context,
|
|
521
|
-
`Expected negated condition to fail`,
|
|
522
|
-
target
|
|
523
|
-
);
|
|
355
|
+
case "all": {
|
|
356
|
+
let allMatch = true;
|
|
357
|
+
for (const subComparison of value) {
|
|
358
|
+
if (!executeComparison(target, subComparison, context)) {
|
|
359
|
+
allMatch = false;
|
|
360
|
+
}
|
|
524
361
|
}
|
|
525
|
-
|
|
362
|
+
return allMatch;
|
|
526
363
|
}
|
|
527
364
|
case "withNoExtraKeys":
|
|
365
|
+
return checkNoExtraKeys(target, value, context, false);
|
|
528
366
|
case "withDeepNoExtraKeys":
|
|
367
|
+
return checkNoExtraKeys(target, value, context, true);
|
|
529
368
|
case "noExtraDefinedKeys":
|
|
369
|
+
return checkNoExtraDefinedKeys(target, value, context, false);
|
|
530
370
|
case "deepNoExtraDefinedKeys":
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
target
|
|
535
|
-
);
|
|
536
|
-
break;
|
|
371
|
+
return checkNoExtraDefinedKeys(target, value, context, true);
|
|
372
|
+
default:
|
|
373
|
+
throw exhaustiveCheck(type);
|
|
537
374
|
}
|
|
538
375
|
}
|
|
539
|
-
function
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
376
|
+
function formatPath(path) {
|
|
377
|
+
if (path.length === 0) return "";
|
|
378
|
+
let result = path[0] || "";
|
|
379
|
+
for (let i = 1; i < path.length; i++) {
|
|
380
|
+
const segment = path[i];
|
|
381
|
+
if (segment && segment.startsWith("[") && segment.endsWith("]")) {
|
|
382
|
+
result += segment;
|
|
383
|
+
} else if (segment) {
|
|
384
|
+
if (result) {
|
|
385
|
+
result += `.${segment}`;
|
|
386
|
+
} else {
|
|
387
|
+
result += segment;
|
|
388
|
+
}
|
|
544
389
|
}
|
|
545
|
-
return;
|
|
546
390
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
function addError(context, error) {
|
|
394
|
+
context.errors.push({
|
|
395
|
+
path: formatPath(context.path),
|
|
396
|
+
...error
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
function deepEqual(a, b) {
|
|
400
|
+
if (a === b) return true;
|
|
401
|
+
if (Number.isNaN(a) && Number.isNaN(b)) return true;
|
|
402
|
+
if (a === null || b === null) return false;
|
|
403
|
+
if (typeof a !== typeof b) return false;
|
|
404
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
405
|
+
if (a.length !== b.length) return false;
|
|
406
|
+
for (let i = 0; i < a.length; i++) {
|
|
407
|
+
if (!deepEqual(a[i], b[i])) return false;
|
|
562
408
|
}
|
|
563
|
-
|
|
564
|
-
context,
|
|
565
|
-
`None of the alternative comparisons matched`,
|
|
566
|
-
target,
|
|
567
|
-
"any alternative match"
|
|
568
|
-
);
|
|
569
|
-
return;
|
|
409
|
+
return true;
|
|
570
410
|
}
|
|
571
|
-
if (
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
target,
|
|
578
|
-
value,
|
|
579
|
-
keyExists,
|
|
580
|
-
subContext
|
|
581
|
-
);
|
|
582
|
-
if (subContext.errors.length === 0) {
|
|
583
|
-
addError(
|
|
584
|
-
context,
|
|
585
|
-
`Expected negated condition to fail`,
|
|
586
|
-
target,
|
|
587
|
-
"negated condition"
|
|
588
|
-
);
|
|
411
|
+
if (typeof a === "object") {
|
|
412
|
+
const keysA = Object.keys(a);
|
|
413
|
+
const keysB = Object.keys(b);
|
|
414
|
+
if (keysA.length !== keysB.length) return false;
|
|
415
|
+
for (const key of keysA) {
|
|
416
|
+
if (!has.call(b, key) || !deepEqual(a[key], b[key])) return false;
|
|
589
417
|
}
|
|
590
|
-
return;
|
|
418
|
+
return true;
|
|
591
419
|
}
|
|
592
|
-
|
|
420
|
+
return false;
|
|
593
421
|
}
|
|
594
|
-
function
|
|
595
|
-
if (sub
|
|
596
|
-
|
|
597
|
-
executeComparisonWithErrorCollection(target, sub["~sc"], context);
|
|
598
|
-
return;
|
|
422
|
+
function partialEqualInternal(target, sub, context) {
|
|
423
|
+
if (isComparison(sub)) {
|
|
424
|
+
return executeComparison(target, sub["~sc"], context);
|
|
599
425
|
}
|
|
600
|
-
if (sub &&
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
426
|
+
if (isComparison(sub) && sub["~sc"][0] === "keyNotBePresent") {
|
|
427
|
+
addError(context, {
|
|
428
|
+
message: "Key should not be present",
|
|
429
|
+
received: target,
|
|
430
|
+
expected: "key not present"
|
|
431
|
+
});
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
if (target === sub) return true;
|
|
435
|
+
if (Number.isNaN(target) && Number.isNaN(sub)) return true;
|
|
436
|
+
if (target === null || sub === null || target === void 0 || sub === void 0) {
|
|
437
|
+
if (target !== sub) {
|
|
438
|
+
addError(context, {
|
|
439
|
+
message: "Value mismatch",
|
|
440
|
+
received: target,
|
|
441
|
+
expected: sub
|
|
442
|
+
});
|
|
443
|
+
return false;
|
|
607
444
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
if (target instanceof Date && sub instanceof Date) {
|
|
448
|
+
if (target.getTime() !== sub.getTime()) {
|
|
449
|
+
addError(context, {
|
|
450
|
+
message: "Date mismatch",
|
|
451
|
+
received: target,
|
|
452
|
+
expected: sub
|
|
453
|
+
});
|
|
454
|
+
return false;
|
|
613
455
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
for (let i = 0; i < sub.length; i++) {
|
|
625
|
-
context.path.push(`[${i}]`);
|
|
626
|
-
partialEqualWithErrorCollection(target[i], sub[i], context);
|
|
627
|
-
context.path.pop();
|
|
628
|
-
}
|
|
629
|
-
return;
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
if (target instanceof RegExp && sub instanceof RegExp) {
|
|
459
|
+
if (target.source !== sub.source || target.flags !== sub.flags) {
|
|
460
|
+
addError(context, {
|
|
461
|
+
message: "RegExp mismatch",
|
|
462
|
+
received: target,
|
|
463
|
+
expected: sub
|
|
464
|
+
});
|
|
465
|
+
return false;
|
|
630
466
|
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
467
|
+
return true;
|
|
468
|
+
}
|
|
469
|
+
if (target instanceof Set && sub instanceof Set) {
|
|
470
|
+
if (sub.size > target.size) {
|
|
471
|
+
addError(context, {
|
|
472
|
+
message: "Set too small",
|
|
473
|
+
received: target,
|
|
474
|
+
expected: sub
|
|
475
|
+
});
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
for (const subValue of sub) {
|
|
479
|
+
let found = false;
|
|
480
|
+
for (const targetValue of target) {
|
|
481
|
+
const tempContext = {
|
|
482
|
+
errors: [],
|
|
483
|
+
path: context.path
|
|
484
|
+
};
|
|
485
|
+
if (partialEqualInternal(targetValue, subValue, tempContext)) {
|
|
486
|
+
found = true;
|
|
487
|
+
break;
|
|
650
488
|
}
|
|
651
489
|
}
|
|
652
|
-
|
|
490
|
+
if (!found) {
|
|
491
|
+
addError(context, {
|
|
492
|
+
message: "Set element not found",
|
|
493
|
+
received: target,
|
|
494
|
+
expected: sub
|
|
495
|
+
});
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
653
498
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
499
|
+
return true;
|
|
500
|
+
}
|
|
501
|
+
if (target instanceof Map && sub instanceof Map) {
|
|
502
|
+
if (sub.size > target.size) {
|
|
503
|
+
addError(context, {
|
|
504
|
+
message: "Map too small",
|
|
505
|
+
received: target,
|
|
506
|
+
expected: sub
|
|
507
|
+
});
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
for (const [subKey, subValue] of sub) {
|
|
511
|
+
let found = false;
|
|
512
|
+
for (const [targetKey, targetValue] of target) {
|
|
513
|
+
const tempContextKey = {
|
|
514
|
+
errors: [],
|
|
515
|
+
path: context.path
|
|
516
|
+
};
|
|
517
|
+
const tempContextValue = {
|
|
518
|
+
errors: [],
|
|
519
|
+
path: context.path
|
|
520
|
+
};
|
|
521
|
+
if (partialEqualInternal(targetKey, subKey, tempContextKey) && partialEqualInternal(targetValue, subValue, tempContextValue)) {
|
|
522
|
+
found = true;
|
|
523
|
+
break;
|
|
676
524
|
}
|
|
677
|
-
context.path.push(`[${key}]`);
|
|
678
|
-
partialEqualWithErrorCollection(target.get(targetKey), value, context);
|
|
679
|
-
context.path.pop();
|
|
680
525
|
}
|
|
681
|
-
|
|
526
|
+
if (!found) {
|
|
527
|
+
addError(context, {
|
|
528
|
+
message: "Map entry not found",
|
|
529
|
+
received: target,
|
|
530
|
+
expected: sub
|
|
531
|
+
});
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
682
534
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
535
|
+
return true;
|
|
536
|
+
}
|
|
537
|
+
if (typeof target !== typeof sub) {
|
|
538
|
+
addError(context, {
|
|
539
|
+
message: "Value mismatch",
|
|
540
|
+
received: target,
|
|
541
|
+
expected: sub
|
|
542
|
+
});
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
if (Array.isArray(sub)) {
|
|
546
|
+
if (!Array.isArray(target)) {
|
|
547
|
+
addError(context, {
|
|
548
|
+
message: "Expected array",
|
|
549
|
+
received: target,
|
|
550
|
+
expected: sub
|
|
551
|
+
});
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
if (target.length < sub.length) {
|
|
555
|
+
addError(context, {
|
|
556
|
+
message: `Array too short: expected at least ${sub.length} elements, got ${target.length}`,
|
|
557
|
+
received: target,
|
|
558
|
+
expected: sub
|
|
559
|
+
});
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
let allMatch = true;
|
|
563
|
+
for (let i = 0; i < sub.length; i++) {
|
|
564
|
+
const oldPath = context.path;
|
|
565
|
+
context.path = [...oldPath, `[${i}]`];
|
|
566
|
+
const result = partialEqualInternal(target[i], sub[i], context);
|
|
567
|
+
context.path = oldPath;
|
|
568
|
+
if (!result) allMatch = false;
|
|
569
|
+
}
|
|
570
|
+
return allMatch;
|
|
571
|
+
}
|
|
572
|
+
if (typeof sub === "object") {
|
|
573
|
+
if (typeof target !== "object" || Array.isArray(target)) {
|
|
574
|
+
addError(context, {
|
|
575
|
+
message: "Expected object",
|
|
576
|
+
received: target,
|
|
577
|
+
expected: sub
|
|
578
|
+
});
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
let allMatch = true;
|
|
582
|
+
for (const key of Object.keys(sub)) {
|
|
583
|
+
if (isComparison(sub[key]) && sub[key]["~sc"][0] === "keyNotBePresent") {
|
|
584
|
+
if (has.call(target, key)) {
|
|
585
|
+
const oldPath2 = context.path;
|
|
586
|
+
context.path = [...oldPath2, key];
|
|
587
|
+
addError(context, {
|
|
588
|
+
message: "Key should not be present",
|
|
589
|
+
received: target[key],
|
|
590
|
+
expected: "key not present"
|
|
591
|
+
});
|
|
592
|
+
context.path = oldPath2;
|
|
593
|
+
allMatch = false;
|
|
594
|
+
}
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
if (!has.call(target, key)) {
|
|
598
|
+
if (isComparison(sub[key])) {
|
|
599
|
+
const comparison = sub[key]["~sc"];
|
|
600
|
+
if (comparison[0] === "any") {
|
|
601
|
+
const anyComparisons = comparison[1];
|
|
602
|
+
const hasKeyNotBePresent = anyComparisons.some(
|
|
603
|
+
(comp) => comp[0] === "keyNotBePresent"
|
|
705
604
|
);
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
addError(context, `Missing property`, void 0, subValue);
|
|
709
|
-
} else {
|
|
710
|
-
partialEqualWithErrorCollection(target[key], subValue, context);
|
|
605
|
+
if (hasKeyNotBePresent) {
|
|
606
|
+
continue;
|
|
711
607
|
}
|
|
712
608
|
}
|
|
713
|
-
context.path.pop();
|
|
714
609
|
}
|
|
715
|
-
|
|
716
|
-
|
|
610
|
+
const oldPath2 = context.path;
|
|
611
|
+
context.path = [...oldPath2, key];
|
|
612
|
+
addError(context, {
|
|
613
|
+
message: "Missing property",
|
|
614
|
+
received: void 0,
|
|
615
|
+
expected: sub[key]
|
|
616
|
+
});
|
|
617
|
+
context.path = oldPath2;
|
|
618
|
+
allMatch = false;
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
const oldPath = context.path;
|
|
622
|
+
context.path = [...oldPath, key];
|
|
623
|
+
const result = partialEqualInternal(target[key], sub[key], context);
|
|
624
|
+
context.path = oldPath;
|
|
625
|
+
if (!result) allMatch = false;
|
|
717
626
|
}
|
|
627
|
+
return allMatch;
|
|
718
628
|
}
|
|
719
|
-
if (
|
|
720
|
-
|
|
629
|
+
if (target !== sub) {
|
|
630
|
+
addError(context, {
|
|
631
|
+
message: "Value mismatch",
|
|
632
|
+
received: target,
|
|
633
|
+
expected: sub
|
|
634
|
+
});
|
|
635
|
+
return false;
|
|
721
636
|
}
|
|
722
|
-
|
|
637
|
+
return true;
|
|
723
638
|
}
|
|
724
|
-
function
|
|
725
|
-
if (
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
};
|
|
730
|
-
|
|
731
|
-
if (context.errors.length === 0) {
|
|
732
|
-
return ok(void 0);
|
|
733
|
-
} else {
|
|
734
|
-
return err(context.errors);
|
|
735
|
-
}
|
|
639
|
+
function checkNoExtraKeys(target, partialShape, context, deep) {
|
|
640
|
+
if (typeof target !== "object" || target === null || Array.isArray(target)) {
|
|
641
|
+
addError(context, {
|
|
642
|
+
message: "Expected object for key validation",
|
|
643
|
+
received: target
|
|
644
|
+
});
|
|
645
|
+
return false;
|
|
736
646
|
}
|
|
737
|
-
if (
|
|
738
|
-
|
|
739
|
-
return executeComparison(target, sub["~sc"]);
|
|
647
|
+
if (!partialEqualInternal(target, partialShape, context)) {
|
|
648
|
+
return false;
|
|
740
649
|
}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
if (
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
for (let i = 0; i < sub.length; i++) {
|
|
752
|
-
if (!partialEqual(target[i], sub[i])) return false;
|
|
753
|
-
}
|
|
754
|
-
return true;
|
|
650
|
+
const allowedKeys = new Set(Object.keys(partialShape));
|
|
651
|
+
for (const key of Object.keys(target)) {
|
|
652
|
+
if (!allowedKeys.has(key)) {
|
|
653
|
+
const oldPath = context.path;
|
|
654
|
+
context.path = [...oldPath, key];
|
|
655
|
+
addError(context, {
|
|
656
|
+
message: `Extra key "${key}" not expected`
|
|
657
|
+
});
|
|
658
|
+
context.path = oldPath;
|
|
659
|
+
return false;
|
|
755
660
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
661
|
+
}
|
|
662
|
+
if (deep) {
|
|
663
|
+
for (const key of Object.keys(partialShape)) {
|
|
664
|
+
if (typeof partialShape[key] === "object" && partialShape[key] !== null && !Array.isArray(partialShape[key]) && !isComparison(partialShape[key])) {
|
|
665
|
+
const oldPath = context.path;
|
|
666
|
+
context.path = [...oldPath, key];
|
|
667
|
+
const result = checkNoExtraKeys(
|
|
668
|
+
target[key],
|
|
669
|
+
partialShape[key],
|
|
670
|
+
context,
|
|
671
|
+
true
|
|
672
|
+
);
|
|
673
|
+
context.path = oldPath;
|
|
674
|
+
if (!result) return false;
|
|
766
675
|
}
|
|
767
|
-
return true;
|
|
768
676
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
677
|
+
}
|
|
678
|
+
return true;
|
|
679
|
+
}
|
|
680
|
+
function checkNoExtraDefinedKeys(target, partialShape, context, deep) {
|
|
681
|
+
if (typeof target !== "object" || target === null || Array.isArray(target)) {
|
|
682
|
+
addError(context, {
|
|
683
|
+
message: "Expected object for key validation",
|
|
684
|
+
received: target
|
|
685
|
+
});
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
if (!partialEqualInternal(target, partialShape, context)) {
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
const allowedKeys = new Set(Object.keys(partialShape));
|
|
692
|
+
for (const key of Object.keys(target)) {
|
|
693
|
+
if (!allowedKeys.has(key) && target[key] !== void 0) {
|
|
694
|
+
const oldPath = context.path;
|
|
695
|
+
context.path = [...oldPath, key];
|
|
696
|
+
addError(context, {
|
|
697
|
+
message: `Extra defined key "${key}" not expected`
|
|
698
|
+
});
|
|
699
|
+
context.path = oldPath;
|
|
700
|
+
return false;
|
|
782
701
|
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
targetHasKey
|
|
798
|
-
)) {
|
|
799
|
-
return false;
|
|
800
|
-
}
|
|
801
|
-
} else {
|
|
802
|
-
if (!has.call(target, key) || !partialEqual(target[key], subValue)) {
|
|
803
|
-
return false;
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
}
|
|
702
|
+
}
|
|
703
|
+
if (deep) {
|
|
704
|
+
for (const key of Object.keys(partialShape)) {
|
|
705
|
+
if (typeof partialShape[key] === "object" && partialShape[key] !== null && !Array.isArray(partialShape[key]) && !isComparison(partialShape[key])) {
|
|
706
|
+
const oldPath = context.path;
|
|
707
|
+
context.path = [...oldPath, key];
|
|
708
|
+
const result = checkNoExtraDefinedKeys(
|
|
709
|
+
target[key],
|
|
710
|
+
partialShape[key],
|
|
711
|
+
context,
|
|
712
|
+
true
|
|
713
|
+
);
|
|
714
|
+
context.path = oldPath;
|
|
715
|
+
if (!result) return false;
|
|
807
716
|
}
|
|
808
|
-
return true;
|
|
809
717
|
}
|
|
810
718
|
}
|
|
811
|
-
return
|
|
719
|
+
return true;
|
|
720
|
+
}
|
|
721
|
+
function partialEqual(target, sub, returnErrors) {
|
|
722
|
+
const context = { errors: [], path: [] };
|
|
723
|
+
const result = partialEqualInternal(target, sub, context);
|
|
724
|
+
if (returnErrors) {
|
|
725
|
+
return result ? ok(void 0) : err(context.errors);
|
|
726
|
+
}
|
|
727
|
+
return result;
|
|
812
728
|
}
|
|
813
729
|
export {
|
|
814
730
|
match,
|