cmpstr 3.2.1 → 3.2.2

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.
Files changed (150) hide show
  1. package/README.md +24 -18
  2. package/dist/CmpStr.esm.js +485 -220
  3. package/dist/CmpStr.esm.min.js +2 -3
  4. package/dist/CmpStr.umd.js +484 -220
  5. package/dist/CmpStr.umd.min.js +2 -3
  6. package/dist/cjs/CmpStr.cjs +57 -36
  7. package/dist/cjs/CmpStrAsync.cjs +30 -24
  8. package/dist/cjs/index.cjs +1 -2
  9. package/dist/cjs/metric/Cosine.cjs +1 -2
  10. package/dist/cjs/metric/DamerauLevenshtein.cjs +1 -2
  11. package/dist/cjs/metric/DiceSorensen.cjs +1 -2
  12. package/dist/cjs/metric/Hamming.cjs +5 -4
  13. package/dist/cjs/metric/Jaccard.cjs +1 -2
  14. package/dist/cjs/metric/JaroWinkler.cjs +1 -2
  15. package/dist/cjs/metric/LCS.cjs +1 -2
  16. package/dist/cjs/metric/Levenshtein.cjs +1 -2
  17. package/dist/cjs/metric/Metric.cjs +57 -38
  18. package/dist/cjs/metric/NeedlemanWunsch.cjs +1 -2
  19. package/dist/cjs/metric/QGram.cjs +1 -2
  20. package/dist/cjs/metric/SmithWaterman.cjs +1 -2
  21. package/dist/cjs/phonetic/Caverphone.cjs +1 -2
  22. package/dist/cjs/phonetic/Cologne.cjs +1 -2
  23. package/dist/cjs/phonetic/Metaphone.cjs +1 -2
  24. package/dist/cjs/phonetic/Phonetic.cjs +55 -35
  25. package/dist/cjs/phonetic/Soundex.cjs +1 -2
  26. package/dist/cjs/root.cjs +3 -2
  27. package/dist/cjs/utils/DeepMerge.cjs +10 -5
  28. package/dist/cjs/utils/DiffChecker.cjs +1 -2
  29. package/dist/cjs/utils/Errors.cjs +103 -0
  30. package/dist/cjs/utils/Filter.cjs +52 -27
  31. package/dist/cjs/utils/HashTable.cjs +1 -2
  32. package/dist/cjs/utils/Normalizer.cjs +54 -34
  33. package/dist/cjs/utils/Pool.cjs +42 -18
  34. package/dist/cjs/utils/Profiler.cjs +1 -2
  35. package/dist/cjs/utils/Registry.cjs +46 -22
  36. package/dist/cjs/utils/StructuredData.cjs +13 -5
  37. package/dist/cjs/utils/TextAnalyzer.cjs +1 -2
  38. package/dist/esm/CmpStr.mjs +55 -32
  39. package/dist/esm/CmpStrAsync.mjs +26 -20
  40. package/dist/esm/index.mjs +1 -2
  41. package/dist/esm/metric/Cosine.mjs +1 -2
  42. package/dist/esm/metric/DamerauLevenshtein.mjs +1 -2
  43. package/dist/esm/metric/DiceSorensen.mjs +1 -2
  44. package/dist/esm/metric/Hamming.mjs +5 -4
  45. package/dist/esm/metric/Jaccard.mjs +1 -2
  46. package/dist/esm/metric/JaroWinkler.mjs +1 -2
  47. package/dist/esm/metric/LCS.mjs +1 -2
  48. package/dist/esm/metric/Levenshtein.mjs +1 -2
  49. package/dist/esm/metric/Metric.mjs +59 -38
  50. package/dist/esm/metric/NeedlemanWunsch.mjs +1 -2
  51. package/dist/esm/metric/QGram.mjs +1 -2
  52. package/dist/esm/metric/SmithWaterman.mjs +1 -2
  53. package/dist/esm/phonetic/Caverphone.mjs +1 -2
  54. package/dist/esm/phonetic/Cologne.mjs +1 -2
  55. package/dist/esm/phonetic/Metaphone.mjs +1 -2
  56. package/dist/esm/phonetic/Phonetic.mjs +55 -35
  57. package/dist/esm/phonetic/Soundex.mjs +1 -2
  58. package/dist/esm/root.mjs +3 -2
  59. package/dist/esm/utils/DeepMerge.mjs +10 -5
  60. package/dist/esm/utils/DiffChecker.mjs +1 -2
  61. package/dist/esm/utils/Errors.mjs +103 -0
  62. package/dist/esm/utils/Filter.mjs +52 -27
  63. package/dist/esm/utils/HashTable.mjs +1 -2
  64. package/dist/esm/utils/Normalizer.mjs +54 -34
  65. package/dist/esm/utils/Pool.mjs +38 -18
  66. package/dist/esm/utils/Profiler.mjs +1 -2
  67. package/dist/esm/utils/Registry.mjs +46 -22
  68. package/dist/esm/utils/StructuredData.mjs +13 -5
  69. package/dist/esm/utils/TextAnalyzer.mjs +1 -2
  70. package/dist/types/CmpStr.d.ts +5 -1
  71. package/dist/types/CmpStrAsync.d.ts +1 -0
  72. package/dist/types/index.d.ts +3 -2
  73. package/dist/types/metric/Metric.d.ts +6 -5
  74. package/dist/types/phonetic/Phonetic.d.ts +3 -1
  75. package/dist/types/root.d.ts +2 -1
  76. package/dist/types/utils/DeepMerge.d.ts +1 -1
  77. package/dist/types/utils/Errors.d.ts +137 -0
  78. package/dist/types/utils/Filter.d.ts +4 -0
  79. package/dist/types/utils/Normalizer.d.ts +3 -0
  80. package/dist/types/utils/Pool.d.ts +2 -0
  81. package/dist/types/utils/Registry.d.ts +3 -3
  82. package/dist/types/utils/StructuredData.d.ts +3 -1
  83. package/dist/types/utils/Types.d.ts +26 -0
  84. package/package.json +7 -7
  85. package/dist/CmpStr.esm.js.map +0 -1
  86. package/dist/CmpStr.esm.min.js.map +0 -1
  87. package/dist/CmpStr.umd.js.map +0 -1
  88. package/dist/CmpStr.umd.min.js.map +0 -1
  89. package/dist/cjs/CmpStr.cjs.map +0 -1
  90. package/dist/cjs/CmpStrAsync.cjs.map +0 -1
  91. package/dist/cjs/index.cjs.map +0 -1
  92. package/dist/cjs/metric/Cosine.cjs.map +0 -1
  93. package/dist/cjs/metric/DamerauLevenshtein.cjs.map +0 -1
  94. package/dist/cjs/metric/DiceSorensen.cjs.map +0 -1
  95. package/dist/cjs/metric/Hamming.cjs.map +0 -1
  96. package/dist/cjs/metric/Jaccard.cjs.map +0 -1
  97. package/dist/cjs/metric/JaroWinkler.cjs.map +0 -1
  98. package/dist/cjs/metric/LCS.cjs.map +0 -1
  99. package/dist/cjs/metric/Levenshtein.cjs.map +0 -1
  100. package/dist/cjs/metric/Metric.cjs.map +0 -1
  101. package/dist/cjs/metric/NeedlemanWunsch.cjs.map +0 -1
  102. package/dist/cjs/metric/QGram.cjs.map +0 -1
  103. package/dist/cjs/metric/SmithWaterman.cjs.map +0 -1
  104. package/dist/cjs/phonetic/Caverphone.cjs.map +0 -1
  105. package/dist/cjs/phonetic/Cologne.cjs.map +0 -1
  106. package/dist/cjs/phonetic/Metaphone.cjs.map +0 -1
  107. package/dist/cjs/phonetic/Phonetic.cjs.map +0 -1
  108. package/dist/cjs/phonetic/Soundex.cjs.map +0 -1
  109. package/dist/cjs/root.cjs.map +0 -1
  110. package/dist/cjs/utils/DeepMerge.cjs.map +0 -1
  111. package/dist/cjs/utils/DiffChecker.cjs.map +0 -1
  112. package/dist/cjs/utils/Filter.cjs.map +0 -1
  113. package/dist/cjs/utils/HashTable.cjs.map +0 -1
  114. package/dist/cjs/utils/Normalizer.cjs.map +0 -1
  115. package/dist/cjs/utils/Pool.cjs.map +0 -1
  116. package/dist/cjs/utils/Profiler.cjs.map +0 -1
  117. package/dist/cjs/utils/Registry.cjs.map +0 -1
  118. package/dist/cjs/utils/StructuredData.cjs.map +0 -1
  119. package/dist/cjs/utils/TextAnalyzer.cjs.map +0 -1
  120. package/dist/esm/CmpStr.mjs.map +0 -1
  121. package/dist/esm/CmpStrAsync.mjs.map +0 -1
  122. package/dist/esm/index.mjs.map +0 -1
  123. package/dist/esm/metric/Cosine.mjs.map +0 -1
  124. package/dist/esm/metric/DamerauLevenshtein.mjs.map +0 -1
  125. package/dist/esm/metric/DiceSorensen.mjs.map +0 -1
  126. package/dist/esm/metric/Hamming.mjs.map +0 -1
  127. package/dist/esm/metric/Jaccard.mjs.map +0 -1
  128. package/dist/esm/metric/JaroWinkler.mjs.map +0 -1
  129. package/dist/esm/metric/LCS.mjs.map +0 -1
  130. package/dist/esm/metric/Levenshtein.mjs.map +0 -1
  131. package/dist/esm/metric/Metric.mjs.map +0 -1
  132. package/dist/esm/metric/NeedlemanWunsch.mjs.map +0 -1
  133. package/dist/esm/metric/QGram.mjs.map +0 -1
  134. package/dist/esm/metric/SmithWaterman.mjs.map +0 -1
  135. package/dist/esm/phonetic/Caverphone.mjs.map +0 -1
  136. package/dist/esm/phonetic/Cologne.mjs.map +0 -1
  137. package/dist/esm/phonetic/Metaphone.mjs.map +0 -1
  138. package/dist/esm/phonetic/Phonetic.mjs.map +0 -1
  139. package/dist/esm/phonetic/Soundex.mjs.map +0 -1
  140. package/dist/esm/root.mjs.map +0 -1
  141. package/dist/esm/utils/DeepMerge.mjs.map +0 -1
  142. package/dist/esm/utils/DiffChecker.mjs.map +0 -1
  143. package/dist/esm/utils/Filter.mjs.map +0 -1
  144. package/dist/esm/utils/HashTable.mjs.map +0 -1
  145. package/dist/esm/utils/Normalizer.mjs.map +0 -1
  146. package/dist/esm/utils/Pool.mjs.map +0 -1
  147. package/dist/esm/utils/Profiler.mjs.map +0 -1
  148. package/dist/esm/utils/Registry.mjs.map +0 -1
  149. package/dist/esm/utils/StructuredData.mjs.map +0 -1
  150. package/dist/esm/utils/TextAnalyzer.mjs.map +0 -1
@@ -1,9 +1,113 @@
1
1
  /**
2
- * CmpStr v3.2.1 build-3439ccb-260130
2
+ * CmpStr v3.2.2 build-bb61120-260311
3
3
  * This is a lightweight, fast and well performing library for calculating string similarity.
4
4
  * (c) 2023-2026 Paul Köhler @komed3 / MIT License
5
5
  * Visit https://github.com/komed3/cmpstr and https://npmjs.org/package/cmpstr
6
6
  */
7
+ class CmpStrError extends Error {
8
+ code;
9
+ meta;
10
+ cause;
11
+ when = new Date().toISOString();
12
+ constructor(code, message, meta, cause) {
13
+ super(message);
14
+ this.name = this.constructor.name;
15
+ this.code = code;
16
+ this.meta = meta;
17
+ this.cause = cause;
18
+ if (typeof Error.captureStackTrace === 'function') {
19
+ Error.captureStackTrace(this, this.constructor);
20
+ }
21
+ }
22
+ toJSON() {
23
+ return {
24
+ name: this.name,
25
+ code: this.code,
26
+ message: this.message,
27
+ meta: this.meta,
28
+ when: this.when,
29
+ cause:
30
+ this.cause instanceof Error
31
+ ? {
32
+ name: this.cause.name,
33
+ message: this.cause.message,
34
+ stack: this.cause.stack
35
+ }
36
+ : this.cause
37
+ };
38
+ }
39
+ toString(stack = false) {
40
+ const parts = [`${this.name} [${this.code}]`, this.message];
41
+ if (this.meta && Object.keys(this.meta).length) {
42
+ try {
43
+ parts.push(JSON.stringify(this.meta));
44
+ } catch {}
45
+ }
46
+ return (
47
+ parts.join(' - ') +
48
+ (stack && this.stack ? `\nStack Trace:\n${this.stack}` : '')
49
+ );
50
+ }
51
+ }
52
+ class CmpStrValidationError extends CmpStrError {
53
+ constructor(message, meta, cause) {
54
+ super('E_VALIDATION', message, meta, cause);
55
+ }
56
+ }
57
+ class CmpStrNotFoundError extends CmpStrError {
58
+ constructor(message, meta, cause) {
59
+ super('E_NOT_FOUND', message, meta, cause);
60
+ }
61
+ }
62
+ class CmpStrUsageError extends CmpStrError {
63
+ constructor(message, meta, cause) {
64
+ super('E_USAGE', message, meta, cause);
65
+ }
66
+ }
67
+ class CmpStrInternalError extends CmpStrError {
68
+ constructor(message, meta, cause) {
69
+ super('E_INTERNAL', message, meta, cause);
70
+ }
71
+ }
72
+ class ErrorUtil {
73
+ static assert(condition, message, meta) {
74
+ if (!condition) throw new CmpStrUsageError(message, meta);
75
+ }
76
+ static create(err, message, meta) {
77
+ if (err instanceof CmpStrError) throw err;
78
+ throw new CmpStrInternalError(message, meta, err);
79
+ }
80
+ static format(err) {
81
+ if (err instanceof CmpStrError) return err.toString();
82
+ if (err instanceof Error) return `${err.name}: ${err.message}`;
83
+ return String(err);
84
+ }
85
+ static wrap(fn, message, meta) {
86
+ try {
87
+ return fn();
88
+ } catch (err) {
89
+ throw new CmpStrInternalError(message, meta, err);
90
+ }
91
+ }
92
+ static async wrapAsync(fn, message, meta) {
93
+ try {
94
+ return await fn();
95
+ } catch (err) {
96
+ throw new CmpStrInternalError(message, meta, err);
97
+ }
98
+ }
99
+ }
100
+
101
+ var Errors = /*#__PURE__*/ Object.freeze({
102
+ __proto__: null,
103
+ CmpStrError: CmpStrError,
104
+ CmpStrInternalError: CmpStrInternalError,
105
+ CmpStrNotFoundError: CmpStrNotFoundError,
106
+ CmpStrUsageError: CmpStrUsageError,
107
+ CmpStrValidationError: CmpStrValidationError,
108
+ ErrorUtil: ErrorUtil
109
+ });
110
+
7
111
  const BRACKET_PATTERN = /\[(\d+)]/g;
8
112
  const PATH_CACHE = new Map();
9
113
  function parse(p) {
@@ -39,15 +143,19 @@ function set(t, path, value) {
39
143
  if (path === '') return value;
40
144
  const keys = parse(path);
41
145
  if (t !== undefined && (typeof t !== 'object' || t === null))
42
- throw Error(`Cannot set property <${keys[0]}> of <${JSON.stringify(t)}>`);
146
+ throw new CmpStrUsageError(
147
+ `Cannot set property <${keys[0]}> of <${JSON.stringify(t)}>`,
148
+ { path: keys[0], target: t }
149
+ );
43
150
  const root = t ?? (typeof keys[0] === 'number' ? [] : Object.create(null));
44
151
  let cur = root;
45
152
  for (let i = 0; i < keys.length - 1; i++) {
46
153
  const k = keys[i];
47
154
  let n = cur[k];
48
155
  if (n != null && typeof n !== 'object')
49
- throw Error(
50
- `Cannot set property <${keys[i + 1]}> of <${JSON.stringify(n)}>`
156
+ throw new CmpStrUsageError(
157
+ `Cannot set property <${keys[i + 1]}> of <${JSON.stringify(n)}>`,
158
+ { path: keys.slice(0, i + 2), value: n }
51
159
  );
52
160
  if (n == null)
53
161
  n = cur[k] = typeof keys[i + 1] === 'number' ? [] : Object.create(null);
@@ -411,30 +519,42 @@ class Filter {
411
519
  static filters = new Map();
412
520
  static pipeline = new Map();
413
521
  static getPipeline(hook) {
414
- const cached = Filter.pipeline.get(hook);
415
- if (cached) return cached;
416
- const filter = Filter.filters.get(hook);
417
- if (!filter) return (s) => s;
418
- const pipeline = Array.from(filter.values())
419
- .filter((f) => f.active)
420
- .sort((a, b) => a.priority - b.priority)
421
- .map((f) => f.fn);
422
- const fn = (input) => pipeline.reduce((v, f) => f(v), input);
423
- Filter.pipeline.set(hook, fn);
424
- return fn;
522
+ return ErrorUtil.wrap(
523
+ () => {
524
+ const cached = Filter.pipeline.get(hook);
525
+ if (cached) return cached;
526
+ const filter = Filter.filters.get(hook);
527
+ if (!filter) return (s) => s;
528
+ const pipeline = Array.from(filter.values())
529
+ .filter((f) => f.active)
530
+ .sort((a, b) => a.priority - b.priority)
531
+ .map((f) => f.fn);
532
+ const fn = (input) => pipeline.reduce((v, f) => f(v), input);
533
+ Filter.pipeline.set(hook, fn);
534
+ return fn;
535
+ },
536
+ `Error compiling filter pipeline for hook <${hook}>`,
537
+ { hook }
538
+ );
425
539
  }
426
540
  static has(hook, id) {
427
541
  return !!Filter.filters.get(hook)?.has(id);
428
542
  }
429
543
  static add(hook, id, fn, opt = {}) {
430
- const { priority = 10, active = true, overrideable = true } = opt;
431
- const filter = Filter.filters.get(hook) ?? new Map();
432
- const index = filter.get(id);
433
- if (index && !index.overrideable) return false;
434
- filter.set(id, { id, fn, priority, active, overrideable });
435
- Filter.filters.set(hook, filter);
436
- Filter.pipeline.delete(hook);
437
- return true;
544
+ return ErrorUtil.wrap(
545
+ () => {
546
+ const { priority = 10, active = true, overrideable = true } = opt;
547
+ const filter = Filter.filters.get(hook) ?? new Map();
548
+ const index = filter.get(id);
549
+ if (index && !index.overrideable) return false;
550
+ filter.set(id, { id, fn, priority, active, overrideable });
551
+ Filter.filters.set(hook, filter);
552
+ Filter.pipeline.delete(hook);
553
+ return true;
554
+ },
555
+ `Error adding filter <${id}> to hook <${hook}>`,
556
+ { hook, id, opt }
557
+ );
438
558
  }
439
559
  static remove(hook, id) {
440
560
  Filter.pipeline.delete(hook);
@@ -459,14 +579,26 @@ class Filter {
459
579
  return out;
460
580
  }
461
581
  static apply(hook, input) {
462
- const fn = Filter.getPipeline(hook);
463
- return Array.isArray(input) ? input.map(fn) : fn(input);
582
+ return ErrorUtil.wrap(
583
+ () => {
584
+ const fn = Filter.getPipeline(hook);
585
+ return Array.isArray(input) ? input.map(fn) : fn(input);
586
+ },
587
+ `Error applying filters for hook <${hook}>`,
588
+ { hook, input }
589
+ );
464
590
  }
465
591
  static async applyAsync(hook, input) {
466
- const fn = Filter.getPipeline(hook);
467
- return Array.isArray(input)
468
- ? Promise.all(input.map(fn))
469
- : Promise.resolve(fn(input));
592
+ return ErrorUtil.wrapAsync(
593
+ async () => {
594
+ const fn = Filter.getPipeline(hook);
595
+ return Array.isArray(input)
596
+ ? Promise.all(input.map(fn))
597
+ : Promise.resolve(fn(input));
598
+ },
599
+ `Error applying filters for hook <${hook}>`,
600
+ { hook, input }
601
+ );
470
602
  }
471
603
  static clear(hook) {
472
604
  Filter.pipeline.clear();
@@ -554,42 +686,62 @@ class Normalizer {
554
686
  return Array.from(new Set(flags)).sort().join('');
555
687
  }
556
688
  static getPipeline(flags) {
557
- if (Normalizer.pipeline.has(flags)) return Normalizer.pipeline.get(flags);
558
- const { REGEX } = Normalizer;
559
- const steps = [
560
- ['d', (s) => s.normalize('NFD')],
561
- ['i', (s) => s.toLowerCase()],
562
- ['k', (s) => s.replace(REGEX.nonLetters, '')],
563
- ['n', (s) => s.replace(REGEX.nonNumbers, '')],
564
- ['r', (s) => s.replace(REGEX.doubleChars, '$1')],
565
- ['s', (s) => s.replace(REGEX.specialChars, '')],
566
- ['t', (s) => s.trim()],
567
- ['u', (s) => s.normalize('NFC')],
568
- ['w', (s) => s.replace(REGEX.whitespace, ' ')],
569
- ['x', (s) => s.normalize('NFKC')]
570
- ];
571
- const pipeline = steps
572
- .filter(([f]) => flags.includes(f))
573
- .map(([, fn]) => fn);
574
- const fn = (s) => pipeline.reduce((v, f) => f(v), s);
575
- Normalizer.pipeline.set(flags, fn);
576
- return fn;
689
+ return ErrorUtil.wrap(
690
+ () => {
691
+ if (Normalizer.pipeline.has(flags))
692
+ return Normalizer.pipeline.get(flags);
693
+ const { REGEX } = Normalizer;
694
+ const steps = [
695
+ ['d', (s) => s.normalize('NFD')],
696
+ ['i', (s) => s.toLowerCase()],
697
+ ['k', (s) => s.replace(REGEX.nonLetters, '')],
698
+ ['n', (s) => s.replace(REGEX.nonNumbers, '')],
699
+ ['r', (s) => s.replace(REGEX.doubleChars, '$1')],
700
+ ['s', (s) => s.replace(REGEX.specialChars, '')],
701
+ ['t', (s) => s.trim()],
702
+ ['u', (s) => s.normalize('NFC')],
703
+ ['w', (s) => s.replace(REGEX.whitespace, ' ')],
704
+ ['x', (s) => s.normalize('NFKC')]
705
+ ];
706
+ const pipeline = steps
707
+ .filter(([f]) => flags.includes(f))
708
+ .map(([, fn]) => fn);
709
+ const fn = (s) => pipeline.reduce((v, f) => f(v), s);
710
+ Normalizer.pipeline.set(flags, fn);
711
+ return fn;
712
+ },
713
+ `Failed to create normalization pipeline for flags: ${flags}`,
714
+ { flags }
715
+ );
577
716
  }
578
717
  static normalize(input, flags) {
579
- if (!flags || typeof flags !== 'string' || !input) return input;
580
- flags = this.canonicalFlags(flags);
581
- if (Array.isArray(input))
582
- return input.map((s) => Normalizer.normalize(s, flags));
583
- const key = Normalizer.cache.key(flags, [input]);
584
- if (key && Normalizer.cache.has(key)) return Normalizer.cache.get(key);
585
- const res = Normalizer.getPipeline(flags)(input);
586
- if (key) Normalizer.cache.set(key, res);
587
- return res;
718
+ return ErrorUtil.wrap(
719
+ () => {
720
+ if (!flags || typeof flags !== 'string' || !input) return input;
721
+ flags = this.canonicalFlags(flags);
722
+ if (Array.isArray(input))
723
+ return input.map((s) => Normalizer.normalize(s, flags));
724
+ const key = Normalizer.cache.key(flags, [input]);
725
+ if (key && Normalizer.cache.has(key)) return Normalizer.cache.get(key);
726
+ const res = Normalizer.getPipeline(flags)(input);
727
+ if (key) Normalizer.cache.set(key, res);
728
+ return res;
729
+ },
730
+ `Failed to normalize input with flags: ${flags}`,
731
+ { input, flags }
732
+ );
588
733
  }
589
734
  static async normalizeAsync(input, flags) {
590
- return await (Array.isArray(input)
591
- ? Promise.all(input.map((s) => Normalizer.normalize(s, flags)))
592
- : Promise.resolve(Normalizer.normalize(input, flags)));
735
+ return await ErrorUtil.wrapAsync(
736
+ async () => {
737
+ if (!flags || typeof flags !== 'string' || !input) return input;
738
+ return await (Array.isArray(input)
739
+ ? Promise.all(input.map((s) => Normalizer.normalize(s, flags)))
740
+ : Promise.resolve(Normalizer.normalize(input, flags)));
741
+ },
742
+ `Failed to asynchronously normalize input with flags: ${flags}`,
743
+ { input, flags }
744
+ );
593
745
  }
594
746
  static clear() {
595
747
  Normalizer.pipeline.clear();
@@ -679,19 +831,34 @@ class Profiler {
679
831
  const registry = Object.create(null);
680
832
  const factory = Object.create(null);
681
833
  function Registry(reg, ctor) {
682
- if (reg in registry || reg in factory)
683
- throw new Error(
684
- `Registry <${reg}> already exists / overwriting is forbidden`
685
- );
834
+ ErrorUtil.assert(
835
+ !(reg in registry || reg in factory),
836
+ `Registry <${reg}> already exists / overwriting is forbidden`,
837
+ { registry: reg }
838
+ );
686
839
  const classes = Object.create(null);
687
840
  const service = Object.freeze({
688
841
  add(name, cls, update = false) {
689
- if (!(cls.prototype instanceof ctor))
690
- throw new TypeError(`Class must extend <${reg}>`);
691
- if (!update && name in classes)
692
- throw new Error(
693
- `Entry <${name}> already exists / use <update=true> to overwrite`
694
- );
842
+ ErrorUtil.assert(
843
+ typeof name === 'string' && name.length > 0,
844
+ `Class name must be a non-empty string`,
845
+ { registry: reg, name }
846
+ );
847
+ ErrorUtil.assert(
848
+ typeof cls === 'function',
849
+ `Class must be a constructor function`,
850
+ { registry: reg, class: cls }
851
+ );
852
+ ErrorUtil.assert(
853
+ cls.prototype instanceof ctor,
854
+ `Class must extend <${reg}>`,
855
+ { registry: reg, class: cls }
856
+ );
857
+ ErrorUtil.assert(
858
+ update || !(name in classes),
859
+ `Class <${name}> already exists / use <update=true> to overwrite`,
860
+ { registry: reg, name }
861
+ );
695
862
  classes[name] = cls;
696
863
  },
697
864
  remove(name) {
@@ -704,8 +871,16 @@ function Registry(reg, ctor) {
704
871
  return Object.keys(classes);
705
872
  },
706
873
  get(name) {
707
- if (!(name in classes))
708
- throw new Error(`Class <${name}> not registered for <${reg}>`);
874
+ ErrorUtil.assert(
875
+ typeof name === 'string' && name.length > 0,
876
+ `Class name must be a non-empty string`,
877
+ { registry: reg, name }
878
+ );
879
+ ErrorUtil.assert(
880
+ name in classes,
881
+ `Class <${name}> not registered for <${reg}>`,
882
+ { registry: reg, name }
883
+ );
709
884
  return classes[name];
710
885
  }
711
886
  });
@@ -715,18 +890,18 @@ function Registry(reg, ctor) {
715
890
  }
716
891
  function resolveCls(reg, cls) {
717
892
  if (!(reg in registry))
718
- throw new ReferenceError(`Registry <${reg}> does not exist`);
893
+ throw new CmpStrNotFoundError(`Registry <${reg}> does not exist`, {
894
+ registry: reg
895
+ });
719
896
  return typeof cls === 'string' ? registry[reg]?.get(cls) : cls;
720
897
  }
721
898
  function createFromRegistry(reg, cls, ...args) {
722
899
  cls = resolveCls(reg, cls);
723
- try {
724
- return new cls(...args);
725
- } catch (err) {
726
- throw new Error(`Cannot instantiate class <${cls.name ?? cls}>`, {
727
- cause: err
728
- });
729
- }
900
+ return ErrorUtil.wrap(
901
+ () => new cls(...args),
902
+ `Failed to create instance of class <${cls.name ?? cls}> from registry <${reg}>`,
903
+ { registry: reg, class: cls, args }
904
+ );
730
905
  }
731
906
 
732
907
  class RingPool {
@@ -737,22 +912,37 @@ class RingPool {
737
912
  this.maxSize = maxSize;
738
913
  }
739
914
  acquire(minSize, allowOversize) {
740
- const len = this.buffers.length;
741
- for (let i = 0; i < len; i++) {
742
- const idx = (this.pointer + i) & (len - 1);
743
- const item = this.buffers[idx];
744
- if (item.size >= minSize && (allowOversize || item.size === minSize)) {
745
- this.pointer = (idx + 1) & (len - 1);
746
- return item;
747
- }
748
- }
749
- return null;
915
+ return ErrorUtil.wrap(
916
+ () => {
917
+ const len = this.buffers.length;
918
+ for (let i = 0; i < len; i++) {
919
+ const idx = (this.pointer + i) & (len - 1);
920
+ const item = this.buffers[idx];
921
+ if (
922
+ item.size >= minSize &&
923
+ (allowOversize || item.size === minSize)
924
+ ) {
925
+ this.pointer = (idx + 1) & (len - 1);
926
+ return item;
927
+ }
928
+ }
929
+ return null;
930
+ },
931
+ `Failed to acquire buffer of size >= ${minSize} from pool`,
932
+ { minSize, allowOversize }
933
+ );
750
934
  }
751
935
  release(item) {
752
- if (this.buffers.length < this.maxSize)
753
- return void [this.buffers.push(item)];
754
- this.buffers[this.pointer] = item;
755
- this.pointer = (this.pointer + 1) % this.maxSize;
936
+ ErrorUtil.wrap(
937
+ () => {
938
+ if (this.buffers.length < this.maxSize)
939
+ return void [this.buffers.push(item)];
940
+ this.buffers[this.pointer] = item;
941
+ this.pointer = (this.pointer + 1) % this.maxSize;
942
+ },
943
+ `Failed to release buffer back to pool`,
944
+ { item }
945
+ );
756
946
  }
757
947
  clear() {
758
948
  this.buffers = [];
@@ -805,6 +995,8 @@ class Pool {
805
995
  }
806
996
  static acquire(type, size) {
807
997
  const CONFIG = this.CONFIG[type];
998
+ if (!CONFIG)
999
+ throw new CmpStrUsageError(`Unsupported pool type <${type}>`, { type });
808
1000
  if (size > CONFIG.maxItemSize) return this.allocate(type, size);
809
1001
  const item = this.POOLS[type].acquire(size, CONFIG.allowOversize);
810
1002
  if (item)
@@ -815,8 +1007,10 @@ class Pool {
815
1007
  return sizes.map((size) => this.acquire(type, size));
816
1008
  }
817
1009
  static release(type, buffer, size) {
818
- if (size <= this.CONFIG[type].maxItemSize)
819
- this.POOLS[type].release({ buffer, size });
1010
+ const CONFIG = this.CONFIG[type];
1011
+ if (!CONFIG)
1012
+ throw new CmpStrUsageError(`Unsupported pool type <${type}>`, { type });
1013
+ if (size <= CONFIG.maxItemSize) this.POOLS[type].release({ buffer, size });
820
1014
  }
821
1015
  }
822
1016
 
@@ -867,7 +1061,7 @@ class StructuredData {
867
1061
  raw: r.raw
868
1062
  }));
869
1063
  else
870
- throw new TypeError(
1064
+ throw new CmpStrValidationError(
871
1065
  'Unsupported result format for StructuredData normalization.'
872
1066
  );
873
1067
  return normalized.map((r, idx) => ({ ...r, __idx: idx }));
@@ -925,10 +1119,18 @@ class StructuredData {
925
1119
  );
926
1120
  }
927
1121
  performLookup(fn, extractedStrings, opt) {
928
- return this.finalizeLookup(fn(), extractedStrings, opt);
1122
+ return ErrorUtil.wrap(
1123
+ () => this.finalizeLookup(fn(), extractedStrings, opt),
1124
+ 'StructuredData lookup failed',
1125
+ { key: this.key }
1126
+ );
929
1127
  }
930
1128
  async performLookupAsync(fn, extractedStrings, opt) {
931
- return this.finalizeLookup(await fn(), extractedStrings, opt);
1129
+ return await ErrorUtil.wrapAsync(
1130
+ async () => this.finalizeLookup(await fn(), extractedStrings, opt),
1131
+ 'StructuredData async lookup failed',
1132
+ { key: this.key }
1133
+ );
932
1134
  }
933
1135
  lookup(fn, query, opt) {
934
1136
  const b = this.extract();
@@ -1181,8 +1383,11 @@ class Metric {
1181
1383
  this.metric = metric;
1182
1384
  this.a = Array.isArray(a) ? a : [a];
1183
1385
  this.b = Array.isArray(b) ? b : [b];
1184
- if (this.a.length === 0 || this.b.length === 0)
1185
- throw new Error(`Inputs <a> and <b> must not be empty`);
1386
+ ErrorUtil.assert(
1387
+ this.a.length > 0 && this.b.length > 0,
1388
+ `Inputs <a> and <b> must not be empty`,
1389
+ { a: this.a, b: this.b }
1390
+ );
1186
1391
  this.options = opt;
1187
1392
  this.optKey = Hasher.fastFNV1a(
1188
1393
  JSON.stringify(opt, Object.keys(opt).sort())
@@ -1195,37 +1400,46 @@ class Metric {
1195
1400
  return undefined;
1196
1401
  }
1197
1402
  compute(a, b, m, n, maxLen) {
1198
- throw new Error(`Method compute() must be overridden in a subclass`);
1403
+ throw new CmpStrInternalError(
1404
+ `Method compute() must be overridden in a subclass`
1405
+ );
1199
1406
  }
1200
1407
  runSingle(i, j) {
1201
- let a = String(this.a[i]),
1202
- A = a;
1203
- let b = String(this.b[j]),
1204
- B = b;
1205
- let m = A.length,
1206
- n = B.length;
1207
- let result = this.preCompute(A, B, m, n);
1208
- if (!result) {
1209
- result = profiler$2.run(() => {
1210
- if (this.symmetric) [A, B, m, n] = Metric.swap(A, B, m, n);
1211
- const key =
1212
- Metric.cache.key(this.metric, [A, B], this.symmetric) + this.optKey;
1213
- return (
1214
- Metric.cache.get(key || '') ??
1215
- (() => {
1216
- const res = this.compute(A, B, m, n, Math.max(m, n));
1217
- if (key) Metric.cache.set(key, res);
1218
- return res;
1219
- })()
1220
- );
1221
- });
1222
- }
1223
- return {
1224
- metric: this.metric,
1225
- a: this.origA[i] ?? a,
1226
- b: this.origB[j] ?? b,
1227
- ...result
1228
- };
1408
+ return ErrorUtil.wrap(
1409
+ () => {
1410
+ let a = String(this.a[i]),
1411
+ A = a;
1412
+ let b = String(this.b[j]),
1413
+ B = b;
1414
+ let m = A.length,
1415
+ n = B.length;
1416
+ let result = this.preCompute(A, B, m, n);
1417
+ if (!result) {
1418
+ result = profiler$2.run(() => {
1419
+ if (this.symmetric) [A, B, m, n] = Metric.swap(A, B, m, n);
1420
+ const key =
1421
+ Metric.cache.key(this.metric, [A, B], this.symmetric) +
1422
+ this.optKey;
1423
+ return (
1424
+ Metric.cache.get(key || '') ??
1425
+ (() => {
1426
+ const res = this.compute(A, B, m, n, Math.max(m, n));
1427
+ if (key) Metric.cache.set(key, res);
1428
+ return res;
1429
+ })()
1430
+ );
1431
+ });
1432
+ }
1433
+ return {
1434
+ metric: this.metric,
1435
+ a: this.origA[i] ?? a,
1436
+ b: this.origB[j] ?? b,
1437
+ ...result
1438
+ };
1439
+ },
1440
+ `Failed to compute metric for inputs at indices a[${i}] and b[${j}]`,
1441
+ { i, j }
1442
+ );
1229
1443
  }
1230
1444
  async runSingleAsync(i, j) {
1231
1445
  return Promise.resolve(this.runSingle(i, j));
@@ -1267,7 +1481,10 @@ class Metric {
1267
1481
  ? true
1268
1482
  : !safe &&
1269
1483
  (() => {
1270
- throw new Error(`Mode <pairwise> requires arrays of equal length`);
1484
+ throw new CmpStrUsageError(
1485
+ `Mode <pairwise> requires arrays of equal length`,
1486
+ { a: this.a, b: this.b }
1487
+ );
1271
1488
  })();
1272
1489
  }
1273
1490
  isSymmetrical = () => this.symmetric;
@@ -1291,7 +1508,7 @@ class Metric {
1291
1508
  if (this.isPairwise()) this.runPairwise();
1292
1509
  break;
1293
1510
  default:
1294
- throw new Error(`Unsupported mode <${mode}>`);
1511
+ throw new CmpStrInternalError(`Unsupported mode <${mode}>`);
1295
1512
  }
1296
1513
  }
1297
1514
  async runAsync(mode, clear = true) {
@@ -1312,13 +1529,15 @@ class Metric {
1312
1529
  if (this.isPairwise()) await this.runPairwiseAsync();
1313
1530
  break;
1314
1531
  default:
1315
- throw new Error(`Unsupported async mode <${mode}>`);
1532
+ throw new CmpStrInternalError(`Unsupported async mode <${mode}>`);
1316
1533
  }
1317
1534
  }
1318
1535
  getMetricName = () => this.metric;
1319
1536
  getResults() {
1320
- if (this.results === undefined)
1321
- throw new Error(`run() must be called before getResult()`);
1537
+ ErrorUtil.assert(
1538
+ this.results !== undefined,
1539
+ `run() must be called before getResults()`
1540
+ );
1322
1541
  return this.results;
1323
1542
  }
1324
1543
  }
@@ -1446,9 +1665,10 @@ class HammingDistance extends Metric {
1446
1665
  if (n < maxLen) b = b.padEnd(maxLen, this.options.pad);
1447
1666
  m = n = maxLen;
1448
1667
  } else
1449
- throw new Error(
1668
+ throw new CmpStrUsageError(
1450
1669
  `Strings must be of equal length for Hamming Distance, a=${m} and b=${n} given, ` +
1451
- `use option.pad for automatic adjustment`
1670
+ `use option.pad for automatic adjustment`,
1671
+ { a: m, b: n }
1452
1672
  );
1453
1673
  }
1454
1674
  let dist = 0;
@@ -1721,10 +1941,17 @@ class Phonetic {
1721
1941
  constructor(algo, opt = {}) {
1722
1942
  const defaults = this.constructor.default ?? {};
1723
1943
  const mapId = opt.map ?? defaults.map;
1724
- if (!mapId) throw new Error(`No mapping specified for phonetic algorithm`);
1944
+ if (!mapId)
1945
+ throw new CmpStrNotFoundError(
1946
+ `No mapping specified for phonetic algorithm`,
1947
+ { algo }
1948
+ );
1725
1949
  const map = PhoneticMappingRegistry.get(algo, mapId);
1726
1950
  if (map === undefined)
1727
- throw new Error(`Requested mapping <${mapId}> is not declared`);
1951
+ throw new CmpStrNotFoundError(
1952
+ `Requested mapping <${mapId}> is not declared`,
1953
+ { algo, mapId }
1954
+ );
1728
1955
  this.options = merge(merge(defaults, map.options ?? {}), opt);
1729
1956
  this.optKey = Hasher.fastFNV1a(
1730
1957
  JSON.stringify(this.options, Object.keys(this.options).sort())
@@ -1814,35 +2041,47 @@ class Phonetic {
1814
2041
  return code;
1815
2042
  }
1816
2043
  loop(words) {
1817
- const index = [];
1818
- for (const word of words) {
1819
- const key = Phonetic.cache.key(this.algo, [word]) + this.optKey;
1820
- const code =
1821
- Phonetic.cache.get(key || '') ??
1822
- (() => {
1823
- const res = this.encode(word);
1824
- if (key) Phonetic.cache.set(key, res);
1825
- return res;
1826
- })();
1827
- if (code && code.length) index.push(this.equalLen(code));
1828
- }
1829
- return index;
2044
+ return ErrorUtil.wrap(
2045
+ () => {
2046
+ const index = [];
2047
+ for (const word of words) {
2048
+ const key = Phonetic.cache.key(this.algo, [word]) + this.optKey;
2049
+ const code =
2050
+ Phonetic.cache.get(key || '') ??
2051
+ (() => {
2052
+ const res = this.encode(word);
2053
+ if (key) Phonetic.cache.set(key, res);
2054
+ return res;
2055
+ })();
2056
+ if (code && code.length) index.push(this.equalLen(code));
2057
+ }
2058
+ return index;
2059
+ },
2060
+ `Failed to generate phonetic index`,
2061
+ { algo: this.algo, words }
2062
+ );
1830
2063
  }
1831
2064
  async loopAsync(words) {
1832
- const index = [];
1833
- for (const word of words) {
1834
- const key = Phonetic.cache.key(this.algo, [word]) + this.optKey;
1835
- const code = await Promise.resolve(
1836
- Phonetic.cache.get(key || '') ??
1837
- (() => {
1838
- const res = this.encode(word);
1839
- if (key) Phonetic.cache.set(key, res);
1840
- return res;
1841
- })()
1842
- );
1843
- if (code && code.length) index.push(this.equalLen(code));
1844
- }
1845
- return index;
2065
+ return ErrorUtil.wrapAsync(
2066
+ async () => {
2067
+ const index = [];
2068
+ for (const word of words) {
2069
+ const key = Phonetic.cache.key(this.algo, [word]) + this.optKey;
2070
+ const code = await Promise.resolve(
2071
+ Phonetic.cache.get(key || '') ??
2072
+ (() => {
2073
+ const res = this.encode(word);
2074
+ if (key) Phonetic.cache.set(key, res);
2075
+ return res;
2076
+ })()
2077
+ );
2078
+ if (code && code.length) index.push(this.equalLen(code));
2079
+ }
2080
+ return index;
2081
+ },
2082
+ `Failed to generate phonetic index asynchronously`,
2083
+ { algo: this.algo, words }
2084
+ );
1846
2085
  }
1847
2086
  getAlgoName = () => this.algo;
1848
2087
  getIndex(input) {
@@ -1867,10 +2106,11 @@ const PhoneticMappingRegistry = (() => {
1867
2106
  return Object.freeze({
1868
2107
  add(algo, id, map, update = false) {
1869
2108
  const mappings = maps(algo);
1870
- if (!update && id in mappings)
1871
- throw new Error(
1872
- `Entry <${id}> already exists / use <update=true> to overwrite`
1873
- );
2109
+ ErrorUtil.assert(
2110
+ !(!id || id in mappings) || update,
2111
+ `Entry <${id}> already exists / use <update=true> to overwrite`,
2112
+ { algo, id }
2113
+ );
1874
2114
  mappings[id] = map;
1875
2115
  },
1876
2116
  remove(algo, id) {
@@ -2290,20 +2530,22 @@ class CmpStr {
2290
2530
  switch (cond) {
2291
2531
  case 'metric':
2292
2532
  if (!CmpStr.metric.has(test))
2293
- throw new Error(
2533
+ throw new CmpStrNotFoundError(
2294
2534
  `CmpStr <metric> must be set, call .setMetric(), ` +
2295
- `use CmpStr.metric.list() for available metrics`
2535
+ `use CmpStr.metric.list() for available metrics`,
2536
+ { metric: test }
2296
2537
  );
2297
2538
  break;
2298
2539
  case 'phonetic':
2299
2540
  if (!CmpStr.phonetic.has(test))
2300
- throw new Error(
2541
+ throw new CmpStrNotFoundError(
2301
2542
  `CmpStr <phonetic> must be set, call .setPhonetic(), ` +
2302
- `use CmpStr.phonetic.list() for available phonetic algorithms`
2543
+ `use CmpStr.phonetic.list() for available phonetic algorithms`,
2544
+ { phonetic: test }
2303
2545
  );
2304
2546
  break;
2305
2547
  default:
2306
- throw new Error(`Cmpstr condition <${cond}> unknown`);
2548
+ throw new CmpStrInternalError(`Cmpstr condition <${cond}> unknown`);
2307
2549
  }
2308
2550
  }
2309
2551
  assertMany(...cond) {
@@ -2342,31 +2584,42 @@ class CmpStr {
2342
2584
  return StructuredData.create(data, key);
2343
2585
  }
2344
2586
  compute(a, b, opt, mode, raw, skip) {
2345
- const resolved = this.resolveOptions(opt);
2346
- this.assert('metric', resolved.metric);
2347
- const A = skip ? a : this.prepare(a, resolved);
2348
- const B = skip ? b : this.prepare(b, resolved);
2349
- if (
2350
- resolved.safeEmpty &&
2351
- ((Array.isArray(A) && A.length === 0) ||
2352
- (Array.isArray(B) && B.length === 0) ||
2353
- A === '' ||
2354
- B === '')
2355
- ) {
2356
- return [];
2357
- }
2358
- const metric = factory['metric'](resolved.metric, A, B, resolved.opt);
2359
- if (resolved.output !== 'prep') metric.setOriginal(a, b);
2360
- metric.run(mode);
2361
- const result = this.postProcess(metric.getResults(), resolved);
2362
- return this.output(result, raw ?? resolved.raw);
2587
+ return ErrorUtil.wrap(
2588
+ () => {
2589
+ const resolved = this.resolveOptions(opt);
2590
+ this.assert('metric', resolved.metric);
2591
+ const A = skip ? a : this.prepare(a, resolved);
2592
+ const B = skip ? b : this.prepare(b, resolved);
2593
+ if (
2594
+ resolved.safeEmpty &&
2595
+ ((Array.isArray(A) && A.length === 0) ||
2596
+ (Array.isArray(B) && B.length === 0) ||
2597
+ A === '' ||
2598
+ B === '')
2599
+ ) {
2600
+ return [];
2601
+ }
2602
+ const metric = factory['metric'](resolved.metric, A, B, resolved.opt);
2603
+ if (resolved.output !== 'prep') metric.setOriginal(a, b);
2604
+ metric.run(mode);
2605
+ const result = this.postProcess(metric.getResults(), resolved);
2606
+ return this.output(result, raw ?? resolved.raw);
2607
+ },
2608
+ `Failed to compute metric <${opt?.metric ?? this.options.metric}> for the given inputs`,
2609
+ { a, b, options: opt }
2610
+ );
2363
2611
  }
2364
2612
  output(result, raw) {
2365
- return (raw ?? this.options.raw)
2366
- ? result
2367
- : Array.isArray(result)
2368
- ? result.map((r) => ({ source: r.a, target: r.b, match: r.res }))
2369
- : { source: result.a, target: result.b, match: result.res };
2613
+ return ErrorUtil.wrap(
2614
+ () =>
2615
+ (raw ?? this.options.raw)
2616
+ ? result
2617
+ : Array.isArray(result)
2618
+ ? result.map((r) => ({ source: r.a, target: r.b, match: r.res }))
2619
+ : { source: result.a, target: result.b, match: result.res },
2620
+ `Failed to resolve output format for the metric result`,
2621
+ { result, raw }
2622
+ );
2370
2623
  }
2371
2624
  clone = () => Object.assign(Object.create(Object.getPrototypeOf(this)), this);
2372
2625
  reset() {
@@ -2382,8 +2635,14 @@ class CmpStr {
2382
2635
  return this;
2383
2636
  }
2384
2637
  setSerializedOptions(opt) {
2385
- this.options = JSON.parse(opt);
2386
- return this;
2638
+ return ErrorUtil.wrap(
2639
+ () => {
2640
+ this.options = JSON.parse(opt);
2641
+ return this;
2642
+ },
2643
+ `Failed to parse serialized options, invalid JSON string`,
2644
+ { opt }
2645
+ );
2387
2646
  }
2388
2647
  setOption(path, value) {
2389
2648
  set(this.options, path, value);
@@ -2526,24 +2785,30 @@ class CmpStrAsync extends CmpStr {
2526
2785
  : phonetic.getIndexAsync(input).then((r) => r.join(delimiter));
2527
2786
  }
2528
2787
  async computeAsync(a, b, opt, mode, raw, skip) {
2529
- const resolved = this.resolveOptions(opt);
2530
- this.assert('metric', resolved.metric);
2531
- const A = skip ? a : await this.prepareAsync(a, resolved);
2532
- const B = skip ? b : await this.prepareAsync(b, resolved);
2533
- if (
2534
- resolved.safeEmpty &&
2535
- ((Array.isArray(A) && A.length === 0) ||
2536
- (Array.isArray(B) && B.length === 0) ||
2537
- A === '' ||
2538
- B === '')
2539
- ) {
2540
- return [];
2541
- }
2542
- const metric = factory['metric'](resolved.metric, A, B, resolved.opt);
2543
- if (resolved.output !== 'prep') metric.setOriginal(a, b);
2544
- await metric.runAsync(mode);
2545
- const result = this.postProcess(metric.getResults(), resolved);
2546
- return this.output(result, raw ?? resolved.raw);
2788
+ return ErrorUtil.wrapAsync(
2789
+ async () => {
2790
+ const resolved = this.resolveOptions(opt);
2791
+ this.assert('metric', resolved.metric);
2792
+ const A = skip ? a : await this.prepareAsync(a, resolved);
2793
+ const B = skip ? b : await this.prepareAsync(b, resolved);
2794
+ if (
2795
+ resolved.safeEmpty &&
2796
+ ((Array.isArray(A) && A.length === 0) ||
2797
+ (Array.isArray(B) && B.length === 0) ||
2798
+ A === '' ||
2799
+ B === '')
2800
+ ) {
2801
+ return [];
2802
+ }
2803
+ const metric = factory['metric'](resolved.metric, A, B, resolved.opt);
2804
+ if (resolved.output !== 'prep') metric.setOriginal(a, b);
2805
+ await metric.runAsync(mode);
2806
+ const result = this.postProcess(metric.getResults(), resolved);
2807
+ return this.output(result, raw ?? resolved.raw);
2808
+ },
2809
+ `Failed to compute metric <${opt?.metric ?? this.options.metric}> for the given inputs`,
2810
+ { a, b, opt }
2811
+ );
2547
2812
  }
2548
2813
  async testAsync(a, b, opt) {
2549
2814
  return this.computeAsync(a, b, opt, 'single');
@@ -2645,6 +2910,7 @@ class CmpStrAsync extends CmpStr {
2645
2910
  export {
2646
2911
  CmpStr,
2647
2912
  CmpStrAsync,
2913
+ Errors as CmpStrError,
2648
2914
  DeepMerge,
2649
2915
  DiffChecker,
2650
2916
  Filter,
@@ -2661,4 +2927,3 @@ export {
2661
2927
  StructuredData,
2662
2928
  TextAnalyzer
2663
2929
  };
2664
- //# sourceMappingURL=CmpStr.esm.js.map