@ls-stack/utils 3.47.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.
@@ -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 partialEqual(target, sub) {
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"]);
@@ -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 };
@@ -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 };
@@ -3,6 +3,7 @@ import {
3
3
  } from "./chunk-JQFUKJU5.js";
4
4
 
5
5
  // src/partialEqual.ts
6
+ import { err, ok } from "t-result";
6
7
  var has = Object.prototype.hasOwnProperty;
7
8
  function createComparison(type) {
8
9
  return { "~sc": type };
@@ -302,7 +303,437 @@ function executeComparison(target, comparison) {
302
303
  return false;
303
304
  }
304
305
  }
305
- function partialEqual(target, sub) {
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
+ }
316
+ }
317
+ return result;
318
+ }
319
+ function addError(context, message, received, expected) {
320
+ const error = {
321
+ path: formatPath(context.path),
322
+ message,
323
+ received
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
+ );
410
+ }
411
+ break;
412
+ case "numIsGreaterThan":
413
+ if (typeof target !== "number" || target <= value) {
414
+ addError(
415
+ context,
416
+ `Expected number greater than ${value}`,
417
+ target
418
+ );
419
+ }
420
+ break;
421
+ case "numIsGreaterThanOrEqual":
422
+ if (typeof target !== "number" || target < value) {
423
+ addError(
424
+ context,
425
+ `Expected number greater than or equal to ${value}`,
426
+ target
427
+ );
428
+ }
429
+ break;
430
+ case "numIsLessThan":
431
+ if (typeof target !== "number" || target >= value) {
432
+ addError(
433
+ context,
434
+ `Expected number less than ${value}`,
435
+ target
436
+ );
437
+ }
438
+ break;
439
+ case "numIsLessThanOrEqual":
440
+ if (typeof target !== "number" || target > value) {
441
+ addError(
442
+ context,
443
+ `Expected number less than or equal to ${value}`,
444
+ target
445
+ );
446
+ }
447
+ break;
448
+ case "numIsInRange":
449
+ if (typeof target !== "number" || target < value[0] || target > value[1]) {
450
+ addError(
451
+ context,
452
+ `Expected number in range [${value[0]}, ${value[1]}]`,
453
+ target
454
+ );
455
+ }
456
+ break;
457
+ case "jsonStringHasPartial":
458
+ if (typeof target !== "string") {
459
+ addError(context, `Expected JSON string`, target);
460
+ } else {
461
+ try {
462
+ const parsed = JSON.parse(target);
463
+ partialEqualWithErrorCollection(parsed, value, context);
464
+ } catch {
465
+ addError(
466
+ context,
467
+ `Expected valid JSON string`,
468
+ target
469
+ );
470
+ }
471
+ }
472
+ break;
473
+ case "deepEqual":
474
+ if (!deepEqual(target, value)) {
475
+ addError(context, `Expected deep equal`, target, value);
476
+ }
477
+ break;
478
+ case "partialEqual":
479
+ partialEqualWithErrorCollection(target, value, context);
480
+ break;
481
+ case "custom":
482
+ if (!value(target)) {
483
+ addError(context, `Custom validation failed`, target, "valid value");
484
+ }
485
+ break;
486
+ case "keyNotBePresent":
487
+ addError(context, `Key should not be present`, target);
488
+ break;
489
+ case "any": {
490
+ for (const comp of value) {
491
+ const subContext = {
492
+ errors: [],
493
+ path: [...context.path]
494
+ };
495
+ executeComparisonWithErrorCollection(target, comp, subContext);
496
+ if (subContext.errors.length === 0) {
497
+ return;
498
+ }
499
+ }
500
+ addError(
501
+ context,
502
+ `None of the alternative comparisons matched`,
503
+ target
504
+ );
505
+ break;
506
+ }
507
+ case "all":
508
+ for (const comp of value) {
509
+ executeComparisonWithErrorCollection(target, comp, context);
510
+ }
511
+ break;
512
+ case "not": {
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
+ );
524
+ }
525
+ break;
526
+ }
527
+ case "withNoExtraKeys":
528
+ case "withDeepNoExtraKeys":
529
+ case "noExtraDefinedKeys":
530
+ case "deepNoExtraDefinedKeys":
531
+ addError(
532
+ context,
533
+ `Complex validation not supported in error collection mode yet`,
534
+ target
535
+ );
536
+ break;
537
+ }
538
+ }
539
+ function executeComparisonWithKeyContextAndErrorCollection(target, comp, keyExists, context) {
540
+ const [type, value] = comp;
541
+ if (type === "keyNotBePresent") {
542
+ if (keyExists) {
543
+ addError(context, `Key should not be present`, target);
544
+ }
545
+ return;
546
+ }
547
+ if (type === "any") {
548
+ for (const childComp of value) {
549
+ const subContext = {
550
+ errors: [],
551
+ path: [...context.path]
552
+ };
553
+ executeComparisonWithKeyContextAndErrorCollection(
554
+ target,
555
+ childComp,
556
+ keyExists,
557
+ subContext
558
+ );
559
+ if (subContext.errors.length === 0) {
560
+ return;
561
+ }
562
+ }
563
+ addError(
564
+ context,
565
+ `None of the alternative comparisons matched`,
566
+ target,
567
+ "any alternative match"
568
+ );
569
+ return;
570
+ }
571
+ if (type === "not") {
572
+ const subContext = {
573
+ errors: [],
574
+ path: [...context.path]
575
+ };
576
+ executeComparisonWithKeyContextAndErrorCollection(
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
+ );
589
+ }
590
+ return;
591
+ }
592
+ executeComparisonWithErrorCollection(target, comp, context);
593
+ }
594
+ function partialEqualWithErrorCollection(target, sub, context) {
595
+ if (sub === target) return;
596
+ if (sub && typeof sub === "object" && "~sc" in sub) {
597
+ executeComparisonWithErrorCollection(target, sub["~sc"], context);
598
+ return;
599
+ }
600
+ if (sub && target && sub.constructor === target.constructor) {
601
+ const ctor = sub.constructor;
602
+ if (ctor === Date) {
603
+ if (sub.getTime() !== target.getTime()) {
604
+ addError(context, `Date mismatch`, target, sub);
605
+ }
606
+ return;
607
+ }
608
+ if (ctor === RegExp) {
609
+ if (sub.toString() !== target.toString()) {
610
+ addError(context, `RegExp mismatch`, target, sub);
611
+ }
612
+ return;
613
+ }
614
+ if (ctor === Array) {
615
+ if (sub.length > target.length) {
616
+ addError(
617
+ context,
618
+ `Array too short: expected at least ${sub.length} elements, got ${target.length}`,
619
+ target,
620
+ sub
621
+ );
622
+ return;
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;
630
+ }
631
+ if (ctor === Set) {
632
+ if (sub.size > target.size) {
633
+ addError(
634
+ context,
635
+ `Set too small: expected at least ${sub.size} elements, got ${target.size}`,
636
+ target,
637
+ sub
638
+ );
639
+ return;
640
+ }
641
+ for (const value of sub) {
642
+ let found = false;
643
+ if (value && typeof value === "object") {
644
+ found = !!find(target, value);
645
+ } else {
646
+ found = target.has(value);
647
+ }
648
+ if (!found) {
649
+ addError(context, `Set missing value`, target, value);
650
+ }
651
+ }
652
+ return;
653
+ }
654
+ if (ctor === Map) {
655
+ if (sub.size > target.size) {
656
+ addError(
657
+ context,
658
+ `Map too small: expected at least ${sub.size} entries, got ${target.size}`,
659
+ target,
660
+ sub
661
+ );
662
+ return;
663
+ }
664
+ for (const [key, value] of sub) {
665
+ let targetKey = key;
666
+ if (key && typeof key === "object") {
667
+ targetKey = find(target, key);
668
+ if (!targetKey) {
669
+ addError(context, `Map missing key`, target, key);
670
+ continue;
671
+ }
672
+ }
673
+ if (!target.has(targetKey)) {
674
+ addError(context, `Map missing key`, target, key);
675
+ continue;
676
+ }
677
+ context.path.push(`[${key}]`);
678
+ partialEqualWithErrorCollection(target.get(targetKey), value, context);
679
+ context.path.pop();
680
+ }
681
+ return;
682
+ }
683
+ if (!ctor || typeof sub === "object") {
684
+ for (const key in sub) {
685
+ if (has.call(sub, key)) {
686
+ const subValue = sub[key];
687
+ context.path.push(key);
688
+ if (subValue && typeof subValue === "object" && "~sc" in subValue && subValue["~sc"][0] === "keyNotBePresent") {
689
+ if (has.call(target, key)) {
690
+ addError(
691
+ context,
692
+ `Key should not be present`,
693
+ target[key],
694
+ "key not present"
695
+ );
696
+ }
697
+ } else if (subValue && typeof subValue === "object" && "~sc" in subValue && subValue["~sc"][0] === "any") {
698
+ const targetHasKey = has.call(target, key);
699
+ const targetValue = targetHasKey ? target[key] : void 0;
700
+ executeComparisonWithKeyContextAndErrorCollection(
701
+ targetValue,
702
+ subValue["~sc"],
703
+ targetHasKey,
704
+ context
705
+ );
706
+ } else {
707
+ if (!has.call(target, key)) {
708
+ addError(context, `Missing property`, void 0, subValue);
709
+ } else {
710
+ partialEqualWithErrorCollection(target[key], subValue, context);
711
+ }
712
+ }
713
+ context.path.pop();
714
+ }
715
+ }
716
+ return;
717
+ }
718
+ }
719
+ if (sub !== sub && target !== target) {
720
+ return;
721
+ }
722
+ addError(context, `Value mismatch`, target, sub);
723
+ }
724
+ function partialEqual(target, sub, returnErrors) {
725
+ if (returnErrors === true) {
726
+ const context = {
727
+ errors: [],
728
+ path: []
729
+ };
730
+ partialEqualWithErrorCollection(target, sub, context);
731
+ if (context.errors.length === 0) {
732
+ return ok(void 0);
733
+ } else {
734
+ return err(context.errors);
735
+ }
736
+ }
306
737
  if (sub === target) return true;
307
738
  if (sub && typeof sub === "object" && "~sc" in sub) {
308
739
  return executeComparison(target, sub["~sc"]);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ls-stack/utils",
3
3
  "description": "Universal TypeScript utilities for browser and Node.js",
4
- "version": "3.47.0",
4
+ "version": "3.48.0",
5
5
  "license": "MIT",
6
6
  "files": [
7
7
  "dist",