@ls-stack/utils 3.46.0 → 3.48.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/cache.cjs +82 -4
- package/dist/cache.d.cts +143 -0
- package/dist/cache.d.ts +143 -0
- package/dist/cache.js +1 -1
- package/dist/{chunk-OIAGLRII.js → chunk-AULH7VMS.js} +82 -4
- package/dist/chunk-PUKVXYYL.js +52 -0
- package/dist/deepReplaceValues.cjs +87 -0
- package/dist/deepReplaceValues.d.cts +27 -0
- package/dist/deepReplaceValues.d.ts +27 -0
- package/dist/deepReplaceValues.js +7 -0
- package/dist/matchPath.cjs +8 -2
- package/dist/matchPath.js +1 -1
- package/dist/partialEqual.cjs +432 -1
- package/dist/partialEqual.d.cts +9 -0
- package/dist/partialEqual.d.ts +9 -0
- package/dist/partialEqual.js +432 -1
- package/dist/testUtils.cjs +46 -41
- package/dist/testUtils.js +6 -43
- package/package.json +5 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isPlainObject
|
|
3
|
+
} from "./chunk-JF2MDHOJ.js";
|
|
4
|
+
|
|
5
|
+
// src/deepReplaceValues.ts
|
|
6
|
+
function applyValueReplacements(value, replaceValues, visited, currentPath) {
|
|
7
|
+
function processValue(val, path) {
|
|
8
|
+
const replacement = replaceValues(val, path);
|
|
9
|
+
if (replacement !== false) {
|
|
10
|
+
return replacement.newValue;
|
|
11
|
+
}
|
|
12
|
+
if (Array.isArray(val)) {
|
|
13
|
+
if (visited.has(val)) {
|
|
14
|
+
throw new Error("Circular reference detected in array");
|
|
15
|
+
}
|
|
16
|
+
visited.add(val);
|
|
17
|
+
try {
|
|
18
|
+
return val.map((item, index) => {
|
|
19
|
+
const itemPath = path ? `${path}[${index}]` : `[${index}]`;
|
|
20
|
+
return processValue(item, itemPath);
|
|
21
|
+
});
|
|
22
|
+
} finally {
|
|
23
|
+
visited.delete(val);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (isPlainObject(val)) {
|
|
27
|
+
if (visited.has(val)) {
|
|
28
|
+
throw new Error("Circular reference detected in object");
|
|
29
|
+
}
|
|
30
|
+
visited.add(val);
|
|
31
|
+
try {
|
|
32
|
+
const result = {};
|
|
33
|
+
for (const [key, itemValue] of Object.entries(val)) {
|
|
34
|
+
const itemPath = path ? `${path}.${key}` : key;
|
|
35
|
+
result[key] = processValue(itemValue, itemPath);
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
} finally {
|
|
39
|
+
visited.delete(val);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return val;
|
|
43
|
+
}
|
|
44
|
+
return processValue(value, currentPath);
|
|
45
|
+
}
|
|
46
|
+
function deepReplaceValues(value, replaceValues) {
|
|
47
|
+
return applyValueReplacements(value, replaceValues, /* @__PURE__ */ new Set(), "");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export {
|
|
51
|
+
deepReplaceValues
|
|
52
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/deepReplaceValues.ts
|
|
21
|
+
var deepReplaceValues_exports = {};
|
|
22
|
+
__export(deepReplaceValues_exports, {
|
|
23
|
+
deepReplaceValues: () => deepReplaceValues
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(deepReplaceValues_exports);
|
|
26
|
+
|
|
27
|
+
// src/typeGuards.ts
|
|
28
|
+
function isPlainObject(value) {
|
|
29
|
+
if (!value || typeof value !== "object") return false;
|
|
30
|
+
const proto = Object.getPrototypeOf(value);
|
|
31
|
+
if (proto === null) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
const Ctor = Object.hasOwnProperty.call(proto, "constructor") && proto.constructor;
|
|
35
|
+
if (Ctor === Object) return true;
|
|
36
|
+
const objectCtorString = Object.prototype.constructor.toString();
|
|
37
|
+
return typeof Ctor == "function" && Function.toString.call(Ctor) === objectCtorString;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/deepReplaceValues.ts
|
|
41
|
+
function applyValueReplacements(value, replaceValues, visited, currentPath) {
|
|
42
|
+
function processValue(val, path) {
|
|
43
|
+
const replacement = replaceValues(val, path);
|
|
44
|
+
if (replacement !== false) {
|
|
45
|
+
return replacement.newValue;
|
|
46
|
+
}
|
|
47
|
+
if (Array.isArray(val)) {
|
|
48
|
+
if (visited.has(val)) {
|
|
49
|
+
throw new Error("Circular reference detected in array");
|
|
50
|
+
}
|
|
51
|
+
visited.add(val);
|
|
52
|
+
try {
|
|
53
|
+
return val.map((item, index) => {
|
|
54
|
+
const itemPath = path ? `${path}[${index}]` : `[${index}]`;
|
|
55
|
+
return processValue(item, itemPath);
|
|
56
|
+
});
|
|
57
|
+
} finally {
|
|
58
|
+
visited.delete(val);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (isPlainObject(val)) {
|
|
62
|
+
if (visited.has(val)) {
|
|
63
|
+
throw new Error("Circular reference detected in object");
|
|
64
|
+
}
|
|
65
|
+
visited.add(val);
|
|
66
|
+
try {
|
|
67
|
+
const result = {};
|
|
68
|
+
for (const [key, itemValue] of Object.entries(val)) {
|
|
69
|
+
const itemPath = path ? `${path}.${key}` : key;
|
|
70
|
+
result[key] = processValue(itemValue, itemPath);
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
} finally {
|
|
74
|
+
visited.delete(val);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return val;
|
|
78
|
+
}
|
|
79
|
+
return processValue(value, currentPath);
|
|
80
|
+
}
|
|
81
|
+
function deepReplaceValues(value, replaceValues) {
|
|
82
|
+
return applyValueReplacements(value, replaceValues, /* @__PURE__ */ new Set(), "");
|
|
83
|
+
}
|
|
84
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
85
|
+
0 && (module.exports = {
|
|
86
|
+
deepReplaceValues
|
|
87
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursively traverses an object or array and allows conditional replacement of values
|
|
3
|
+
* based on a provided callback function. The callback receives each value and its path
|
|
4
|
+
* within the data structure.
|
|
5
|
+
*
|
|
6
|
+
* @param value - The input value to process (object, array, or primitive)
|
|
7
|
+
* @param replaceValues - Callback function that receives each value and its path.
|
|
8
|
+
* Return `false` to keep the original value, or `{ newValue: unknown }` to replace it.
|
|
9
|
+
* The path uses dot notation for objects (e.g., "user.name") and bracket notation for arrays (e.g., "items[0]")
|
|
10
|
+
* @returns A new structure with replaced values. The original structure is not modified.
|
|
11
|
+
* @throws Error if circular references are detected
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const data = { user: { id: 1, name: "Alice" }, scores: [85, 92] };
|
|
15
|
+
* const result = deepReplaceValues(data, (value, path) => {
|
|
16
|
+
* if (typeof value === "number") {
|
|
17
|
+
* return { newValue: value * 2 };
|
|
18
|
+
* }
|
|
19
|
+
* return false;
|
|
20
|
+
* });
|
|
21
|
+
* // Result: { user: { id: 2, name: "Alice" }, scores: [170, 184] }
|
|
22
|
+
*/
|
|
23
|
+
declare function deepReplaceValues<T, R = T>(value: T, replaceValues: (value: unknown, path: string) => false | {
|
|
24
|
+
newValue: unknown;
|
|
25
|
+
}): R;
|
|
26
|
+
|
|
27
|
+
export { deepReplaceValues };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursively traverses an object or array and allows conditional replacement of values
|
|
3
|
+
* based on a provided callback function. The callback receives each value and its path
|
|
4
|
+
* within the data structure.
|
|
5
|
+
*
|
|
6
|
+
* @param value - The input value to process (object, array, or primitive)
|
|
7
|
+
* @param replaceValues - Callback function that receives each value and its path.
|
|
8
|
+
* Return `false` to keep the original value, or `{ newValue: unknown }` to replace it.
|
|
9
|
+
* The path uses dot notation for objects (e.g., "user.name") and bracket notation for arrays (e.g., "items[0]")
|
|
10
|
+
* @returns A new structure with replaced values. The original structure is not modified.
|
|
11
|
+
* @throws Error if circular references are detected
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const data = { user: { id: 1, name: "Alice" }, scores: [85, 92] };
|
|
15
|
+
* const result = deepReplaceValues(data, (value, path) => {
|
|
16
|
+
* if (typeof value === "number") {
|
|
17
|
+
* return { newValue: value * 2 };
|
|
18
|
+
* }
|
|
19
|
+
* return false;
|
|
20
|
+
* });
|
|
21
|
+
* // Result: { user: { id: 2, name: "Alice" }, scores: [170, 184] }
|
|
22
|
+
*/
|
|
23
|
+
declare function deepReplaceValues<T, R = T>(value: T, replaceValues: (value: unknown, path: string) => false | {
|
|
24
|
+
newValue: unknown;
|
|
25
|
+
}): R;
|
|
26
|
+
|
|
27
|
+
export { deepReplaceValues };
|
package/dist/matchPath.cjs
CHANGED
|
@@ -39,12 +39,18 @@ function fastCache({ maxCacheSize = 1e3 } = {}) {
|
|
|
39
39
|
}
|
|
40
40
|
function getOrInsert(cacheKey, val) {
|
|
41
41
|
if (!cache2.has(cacheKey)) {
|
|
42
|
-
|
|
42
|
+
const value = val();
|
|
43
|
+
cache2.set(cacheKey, value);
|
|
43
44
|
trimCache();
|
|
45
|
+
return cache2.get(cacheKey) ?? value;
|
|
44
46
|
}
|
|
45
47
|
return cache2.get(cacheKey);
|
|
46
48
|
}
|
|
47
|
-
return {
|
|
49
|
+
return {
|
|
50
|
+
getOrInsert,
|
|
51
|
+
/** Clears all cached values */
|
|
52
|
+
clear: () => cache2.clear()
|
|
53
|
+
};
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
// src/matchPath.ts
|
package/dist/matchPath.js
CHANGED
package/dist/partialEqual.cjs
CHANGED
|
@@ -24,6 +24,7 @@ __export(partialEqual_exports, {
|
|
|
24
24
|
partialEqual: () => partialEqual
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(partialEqual_exports);
|
|
27
|
+
var import_t_result = require("t-result");
|
|
27
28
|
|
|
28
29
|
// src/deepEqual.ts
|
|
29
30
|
var has = Object.prototype.hasOwnProperty;
|
|
@@ -389,7 +390,437 @@ function executeComparison(target, comparison) {
|
|
|
389
390
|
return false;
|
|
390
391
|
}
|
|
391
392
|
}
|
|
392
|
-
function
|
|
393
|
+
function formatPath(path) {
|
|
394
|
+
if (path.length === 0) return "";
|
|
395
|
+
let result = path[0] || "";
|
|
396
|
+
for (let i = 1; i < path.length; i++) {
|
|
397
|
+
const segment = path[i];
|
|
398
|
+
if (segment && segment.startsWith("[") && segment.endsWith("]")) {
|
|
399
|
+
result += segment;
|
|
400
|
+
} else if (segment) {
|
|
401
|
+
result += `.${segment}`;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return result;
|
|
405
|
+
}
|
|
406
|
+
function addError(context, message, received, expected) {
|
|
407
|
+
const error = {
|
|
408
|
+
path: formatPath(context.path),
|
|
409
|
+
message,
|
|
410
|
+
received
|
|
411
|
+
};
|
|
412
|
+
if (expected !== void 0) {
|
|
413
|
+
error.expected = expected;
|
|
414
|
+
}
|
|
415
|
+
context.errors.push(error);
|
|
416
|
+
}
|
|
417
|
+
function executeComparisonWithErrorCollection(target, comparison, context) {
|
|
418
|
+
const [type, value] = comparison;
|
|
419
|
+
switch (type) {
|
|
420
|
+
case "hasType":
|
|
421
|
+
switch (value) {
|
|
422
|
+
case "string":
|
|
423
|
+
if (typeof target !== "string") {
|
|
424
|
+
addError(context, `Expected type string`, target);
|
|
425
|
+
}
|
|
426
|
+
break;
|
|
427
|
+
case "number":
|
|
428
|
+
if (typeof target !== "number") {
|
|
429
|
+
addError(context, `Expected type number`, target);
|
|
430
|
+
}
|
|
431
|
+
break;
|
|
432
|
+
case "boolean":
|
|
433
|
+
if (typeof target !== "boolean") {
|
|
434
|
+
addError(context, `Expected type boolean`, target);
|
|
435
|
+
}
|
|
436
|
+
break;
|
|
437
|
+
case "function":
|
|
438
|
+
if (typeof target !== "function") {
|
|
439
|
+
addError(context, `Expected type function`, target);
|
|
440
|
+
}
|
|
441
|
+
break;
|
|
442
|
+
case "array":
|
|
443
|
+
if (!Array.isArray(target)) {
|
|
444
|
+
addError(context, `Expected array`, target);
|
|
445
|
+
}
|
|
446
|
+
break;
|
|
447
|
+
case "object":
|
|
448
|
+
if (typeof target !== "object" || target === null || Array.isArray(target)) {
|
|
449
|
+
addError(context, `Expected object`, target);
|
|
450
|
+
}
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
break;
|
|
454
|
+
case "isInstanceOf":
|
|
455
|
+
if (!(target instanceof value)) {
|
|
456
|
+
addError(
|
|
457
|
+
context,
|
|
458
|
+
`Expected instance of ${value.name}`,
|
|
459
|
+
target
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
break;
|
|
463
|
+
case "strStartsWith":
|
|
464
|
+
if (typeof target !== "string" || !target.startsWith(value)) {
|
|
465
|
+
addError(
|
|
466
|
+
context,
|
|
467
|
+
`Expected string starting with "${value}"`,
|
|
468
|
+
target
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
break;
|
|
472
|
+
case "strEndsWith":
|
|
473
|
+
if (typeof target !== "string" || !target.endsWith(value)) {
|
|
474
|
+
addError(
|
|
475
|
+
context,
|
|
476
|
+
`Expected string ending with "${value}"`,
|
|
477
|
+
target
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
break;
|
|
481
|
+
case "strContains":
|
|
482
|
+
if (typeof target !== "string" || !target.includes(value)) {
|
|
483
|
+
addError(
|
|
484
|
+
context,
|
|
485
|
+
`Expected string containing "${value}"`,
|
|
486
|
+
target
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
break;
|
|
490
|
+
case "strMatchesRegex":
|
|
491
|
+
if (typeof target !== "string" || !value.test(target)) {
|
|
492
|
+
addError(
|
|
493
|
+
context,
|
|
494
|
+
`Expected string matching regex ${value}`,
|
|
495
|
+
target
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
break;
|
|
499
|
+
case "numIsGreaterThan":
|
|
500
|
+
if (typeof target !== "number" || target <= value) {
|
|
501
|
+
addError(
|
|
502
|
+
context,
|
|
503
|
+
`Expected number greater than ${value}`,
|
|
504
|
+
target
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
break;
|
|
508
|
+
case "numIsGreaterThanOrEqual":
|
|
509
|
+
if (typeof target !== "number" || target < value) {
|
|
510
|
+
addError(
|
|
511
|
+
context,
|
|
512
|
+
`Expected number greater than or equal to ${value}`,
|
|
513
|
+
target
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
break;
|
|
517
|
+
case "numIsLessThan":
|
|
518
|
+
if (typeof target !== "number" || target >= value) {
|
|
519
|
+
addError(
|
|
520
|
+
context,
|
|
521
|
+
`Expected number less than ${value}`,
|
|
522
|
+
target
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
break;
|
|
526
|
+
case "numIsLessThanOrEqual":
|
|
527
|
+
if (typeof target !== "number" || target > value) {
|
|
528
|
+
addError(
|
|
529
|
+
context,
|
|
530
|
+
`Expected number less than or equal to ${value}`,
|
|
531
|
+
target
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
break;
|
|
535
|
+
case "numIsInRange":
|
|
536
|
+
if (typeof target !== "number" || target < value[0] || target > value[1]) {
|
|
537
|
+
addError(
|
|
538
|
+
context,
|
|
539
|
+
`Expected number in range [${value[0]}, ${value[1]}]`,
|
|
540
|
+
target
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
break;
|
|
544
|
+
case "jsonStringHasPartial":
|
|
545
|
+
if (typeof target !== "string") {
|
|
546
|
+
addError(context, `Expected JSON string`, target);
|
|
547
|
+
} else {
|
|
548
|
+
try {
|
|
549
|
+
const parsed = JSON.parse(target);
|
|
550
|
+
partialEqualWithErrorCollection(parsed, value, context);
|
|
551
|
+
} catch {
|
|
552
|
+
addError(
|
|
553
|
+
context,
|
|
554
|
+
`Expected valid JSON string`,
|
|
555
|
+
target
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
break;
|
|
560
|
+
case "deepEqual":
|
|
561
|
+
if (!deepEqual(target, value)) {
|
|
562
|
+
addError(context, `Expected deep equal`, target, value);
|
|
563
|
+
}
|
|
564
|
+
break;
|
|
565
|
+
case "partialEqual":
|
|
566
|
+
partialEqualWithErrorCollection(target, value, context);
|
|
567
|
+
break;
|
|
568
|
+
case "custom":
|
|
569
|
+
if (!value(target)) {
|
|
570
|
+
addError(context, `Custom validation failed`, target, "valid value");
|
|
571
|
+
}
|
|
572
|
+
break;
|
|
573
|
+
case "keyNotBePresent":
|
|
574
|
+
addError(context, `Key should not be present`, target);
|
|
575
|
+
break;
|
|
576
|
+
case "any": {
|
|
577
|
+
for (const comp of value) {
|
|
578
|
+
const subContext = {
|
|
579
|
+
errors: [],
|
|
580
|
+
path: [...context.path]
|
|
581
|
+
};
|
|
582
|
+
executeComparisonWithErrorCollection(target, comp, subContext);
|
|
583
|
+
if (subContext.errors.length === 0) {
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
addError(
|
|
588
|
+
context,
|
|
589
|
+
`None of the alternative comparisons matched`,
|
|
590
|
+
target
|
|
591
|
+
);
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
case "all":
|
|
595
|
+
for (const comp of value) {
|
|
596
|
+
executeComparisonWithErrorCollection(target, comp, context);
|
|
597
|
+
}
|
|
598
|
+
break;
|
|
599
|
+
case "not": {
|
|
600
|
+
const subContext = {
|
|
601
|
+
errors: [],
|
|
602
|
+
path: [...context.path]
|
|
603
|
+
};
|
|
604
|
+
executeComparisonWithErrorCollection(target, value, subContext);
|
|
605
|
+
if (subContext.errors.length === 0) {
|
|
606
|
+
addError(
|
|
607
|
+
context,
|
|
608
|
+
`Expected negated condition to fail`,
|
|
609
|
+
target
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
break;
|
|
613
|
+
}
|
|
614
|
+
case "withNoExtraKeys":
|
|
615
|
+
case "withDeepNoExtraKeys":
|
|
616
|
+
case "noExtraDefinedKeys":
|
|
617
|
+
case "deepNoExtraDefinedKeys":
|
|
618
|
+
addError(
|
|
619
|
+
context,
|
|
620
|
+
`Complex validation not supported in error collection mode yet`,
|
|
621
|
+
target
|
|
622
|
+
);
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
function executeComparisonWithKeyContextAndErrorCollection(target, comp, keyExists, context) {
|
|
627
|
+
const [type, value] = comp;
|
|
628
|
+
if (type === "keyNotBePresent") {
|
|
629
|
+
if (keyExists) {
|
|
630
|
+
addError(context, `Key should not be present`, target);
|
|
631
|
+
}
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
if (type === "any") {
|
|
635
|
+
for (const childComp of value) {
|
|
636
|
+
const subContext = {
|
|
637
|
+
errors: [],
|
|
638
|
+
path: [...context.path]
|
|
639
|
+
};
|
|
640
|
+
executeComparisonWithKeyContextAndErrorCollection(
|
|
641
|
+
target,
|
|
642
|
+
childComp,
|
|
643
|
+
keyExists,
|
|
644
|
+
subContext
|
|
645
|
+
);
|
|
646
|
+
if (subContext.errors.length === 0) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
addError(
|
|
651
|
+
context,
|
|
652
|
+
`None of the alternative comparisons matched`,
|
|
653
|
+
target,
|
|
654
|
+
"any alternative match"
|
|
655
|
+
);
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
if (type === "not") {
|
|
659
|
+
const subContext = {
|
|
660
|
+
errors: [],
|
|
661
|
+
path: [...context.path]
|
|
662
|
+
};
|
|
663
|
+
executeComparisonWithKeyContextAndErrorCollection(
|
|
664
|
+
target,
|
|
665
|
+
value,
|
|
666
|
+
keyExists,
|
|
667
|
+
subContext
|
|
668
|
+
);
|
|
669
|
+
if (subContext.errors.length === 0) {
|
|
670
|
+
addError(
|
|
671
|
+
context,
|
|
672
|
+
`Expected negated condition to fail`,
|
|
673
|
+
target,
|
|
674
|
+
"negated condition"
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
executeComparisonWithErrorCollection(target, comp, context);
|
|
680
|
+
}
|
|
681
|
+
function partialEqualWithErrorCollection(target, sub, context) {
|
|
682
|
+
if (sub === target) return;
|
|
683
|
+
if (sub && typeof sub === "object" && "~sc" in sub) {
|
|
684
|
+
executeComparisonWithErrorCollection(target, sub["~sc"], context);
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
if (sub && target && sub.constructor === target.constructor) {
|
|
688
|
+
const ctor = sub.constructor;
|
|
689
|
+
if (ctor === Date) {
|
|
690
|
+
if (sub.getTime() !== target.getTime()) {
|
|
691
|
+
addError(context, `Date mismatch`, target, sub);
|
|
692
|
+
}
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
if (ctor === RegExp) {
|
|
696
|
+
if (sub.toString() !== target.toString()) {
|
|
697
|
+
addError(context, `RegExp mismatch`, target, sub);
|
|
698
|
+
}
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
if (ctor === Array) {
|
|
702
|
+
if (sub.length > target.length) {
|
|
703
|
+
addError(
|
|
704
|
+
context,
|
|
705
|
+
`Array too short: expected at least ${sub.length} elements, got ${target.length}`,
|
|
706
|
+
target,
|
|
707
|
+
sub
|
|
708
|
+
);
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
for (let i = 0; i < sub.length; i++) {
|
|
712
|
+
context.path.push(`[${i}]`);
|
|
713
|
+
partialEqualWithErrorCollection(target[i], sub[i], context);
|
|
714
|
+
context.path.pop();
|
|
715
|
+
}
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
if (ctor === Set) {
|
|
719
|
+
if (sub.size > target.size) {
|
|
720
|
+
addError(
|
|
721
|
+
context,
|
|
722
|
+
`Set too small: expected at least ${sub.size} elements, got ${target.size}`,
|
|
723
|
+
target,
|
|
724
|
+
sub
|
|
725
|
+
);
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
for (const value of sub) {
|
|
729
|
+
let found = false;
|
|
730
|
+
if (value && typeof value === "object") {
|
|
731
|
+
found = !!find2(target, value);
|
|
732
|
+
} else {
|
|
733
|
+
found = target.has(value);
|
|
734
|
+
}
|
|
735
|
+
if (!found) {
|
|
736
|
+
addError(context, `Set missing value`, target, value);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
if (ctor === Map) {
|
|
742
|
+
if (sub.size > target.size) {
|
|
743
|
+
addError(
|
|
744
|
+
context,
|
|
745
|
+
`Map too small: expected at least ${sub.size} entries, got ${target.size}`,
|
|
746
|
+
target,
|
|
747
|
+
sub
|
|
748
|
+
);
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
for (const [key, value] of sub) {
|
|
752
|
+
let targetKey = key;
|
|
753
|
+
if (key && typeof key === "object") {
|
|
754
|
+
targetKey = find2(target, key);
|
|
755
|
+
if (!targetKey) {
|
|
756
|
+
addError(context, `Map missing key`, target, key);
|
|
757
|
+
continue;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
if (!target.has(targetKey)) {
|
|
761
|
+
addError(context, `Map missing key`, target, key);
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
context.path.push(`[${key}]`);
|
|
765
|
+
partialEqualWithErrorCollection(target.get(targetKey), value, context);
|
|
766
|
+
context.path.pop();
|
|
767
|
+
}
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
if (!ctor || typeof sub === "object") {
|
|
771
|
+
for (const key in sub) {
|
|
772
|
+
if (has2.call(sub, key)) {
|
|
773
|
+
const subValue = sub[key];
|
|
774
|
+
context.path.push(key);
|
|
775
|
+
if (subValue && typeof subValue === "object" && "~sc" in subValue && subValue["~sc"][0] === "keyNotBePresent") {
|
|
776
|
+
if (has2.call(target, key)) {
|
|
777
|
+
addError(
|
|
778
|
+
context,
|
|
779
|
+
`Key should not be present`,
|
|
780
|
+
target[key],
|
|
781
|
+
"key not present"
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
} else if (subValue && typeof subValue === "object" && "~sc" in subValue && subValue["~sc"][0] === "any") {
|
|
785
|
+
const targetHasKey = has2.call(target, key);
|
|
786
|
+
const targetValue = targetHasKey ? target[key] : void 0;
|
|
787
|
+
executeComparisonWithKeyContextAndErrorCollection(
|
|
788
|
+
targetValue,
|
|
789
|
+
subValue["~sc"],
|
|
790
|
+
targetHasKey,
|
|
791
|
+
context
|
|
792
|
+
);
|
|
793
|
+
} else {
|
|
794
|
+
if (!has2.call(target, key)) {
|
|
795
|
+
addError(context, `Missing property`, void 0, subValue);
|
|
796
|
+
} else {
|
|
797
|
+
partialEqualWithErrorCollection(target[key], subValue, context);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
context.path.pop();
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
if (sub !== sub && target !== target) {
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
addError(context, `Value mismatch`, target, sub);
|
|
810
|
+
}
|
|
811
|
+
function partialEqual(target, sub, returnErrors) {
|
|
812
|
+
if (returnErrors === true) {
|
|
813
|
+
const context = {
|
|
814
|
+
errors: [],
|
|
815
|
+
path: []
|
|
816
|
+
};
|
|
817
|
+
partialEqualWithErrorCollection(target, sub, context);
|
|
818
|
+
if (context.errors.length === 0) {
|
|
819
|
+
return (0, import_t_result.ok)(void 0);
|
|
820
|
+
} else {
|
|
821
|
+
return (0, import_t_result.err)(context.errors);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
393
824
|
if (sub === target) return true;
|
|
394
825
|
if (sub && typeof sub === "object" && "~sc" in sub) {
|
|
395
826
|
return executeComparison(target, sub["~sc"]);
|
package/dist/partialEqual.d.cts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Result } from 't-result';
|
|
2
|
+
|
|
1
3
|
type ComparisonsType = [type: 'strStartsWith', value: string] | [type: 'strEndsWith', value: string] | [
|
|
2
4
|
type: 'hasType',
|
|
3
5
|
value: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'function'
|
|
@@ -46,6 +48,13 @@ type Match = BaseMatch & {
|
|
|
46
48
|
not: BaseMatch;
|
|
47
49
|
};
|
|
48
50
|
declare const match: Match;
|
|
51
|
+
type PartialError = {
|
|
52
|
+
path: string;
|
|
53
|
+
message: string;
|
|
54
|
+
received: any;
|
|
55
|
+
expected?: any;
|
|
56
|
+
};
|
|
57
|
+
declare function partialEqual(target: any, sub: any, returnErrors: true): Result<void, PartialError[]>;
|
|
49
58
|
declare function partialEqual(target: any, sub: any): boolean;
|
|
50
59
|
|
|
51
60
|
export { match, partialEqual };
|