cmpstr 3.2.0 → 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 (171) hide show
  1. package/README.md +26 -18
  2. package/dist/CmpStr.esm.js +490 -220
  3. package/dist/CmpStr.esm.min.js +2 -3
  4. package/dist/CmpStr.umd.js +489 -220
  5. package/dist/CmpStr.umd.min.js +2 -3
  6. package/dist/cjs/CmpStr.cjs +58 -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 +56 -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 +56 -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 +56 -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 +12 -6
  71. package/dist/types/CmpStrAsync.d.ts +6 -4
  72. package/dist/types/index.d.ts +3 -2
  73. package/dist/types/metric/Cosine.d.ts +2 -1
  74. package/dist/types/metric/DamerauLevenshtein.d.ts +2 -1
  75. package/dist/types/metric/DiceSorensen.d.ts +2 -1
  76. package/dist/types/metric/Hamming.d.ts +2 -1
  77. package/dist/types/metric/Jaccard.d.ts +2 -1
  78. package/dist/types/metric/JaroWinkler.d.ts +2 -1
  79. package/dist/types/metric/LCS.d.ts +2 -1
  80. package/dist/types/metric/Levenshtein.d.ts +2 -1
  81. package/dist/types/metric/Metric.d.ts +7 -5
  82. package/dist/types/metric/NeedlemanWunsch.d.ts +2 -1
  83. package/dist/types/metric/QGram.d.ts +2 -1
  84. package/dist/types/metric/SmithWaterman.d.ts +2 -1
  85. package/dist/types/metric/index.d.ts +1 -0
  86. package/dist/types/phonetic/Caverphone.d.ts +2 -1
  87. package/dist/types/phonetic/Cologne.d.ts +2 -1
  88. package/dist/types/phonetic/Metaphone.d.ts +2 -1
  89. package/dist/types/phonetic/Phonetic.d.ts +4 -1
  90. package/dist/types/phonetic/Soundex.d.ts +2 -1
  91. package/dist/types/phonetic/index.d.ts +1 -0
  92. package/dist/types/root.d.ts +2 -1
  93. package/dist/types/utils/DeepMerge.d.ts +3 -2
  94. package/dist/types/utils/DiffChecker.d.ts +2 -1
  95. package/dist/types/utils/Errors.d.ts +137 -0
  96. package/dist/types/utils/Filter.d.ts +33 -22
  97. package/dist/types/utils/HashTable.d.ts +2 -1
  98. package/dist/types/utils/Normalizer.d.ts +5 -1
  99. package/dist/types/utils/Pool.d.ts +4 -1
  100. package/dist/types/utils/Profiler.d.ts +3 -2
  101. package/dist/types/utils/Registry.d.ts +5 -4
  102. package/dist/types/utils/StructuredData.d.ts +5 -2
  103. package/dist/types/utils/TextAnalyzer.d.ts +2 -1
  104. package/dist/types/utils/Types.d.ts +34 -2
  105. package/package.json +10 -7
  106. package/dist/CmpStr.esm.js.map +0 -1
  107. package/dist/CmpStr.esm.min.js.map +0 -1
  108. package/dist/CmpStr.umd.js.map +0 -1
  109. package/dist/CmpStr.umd.min.js.map +0 -1
  110. package/dist/cjs/CmpStr.cjs.map +0 -1
  111. package/dist/cjs/CmpStrAsync.cjs.map +0 -1
  112. package/dist/cjs/index.cjs.map +0 -1
  113. package/dist/cjs/metric/Cosine.cjs.map +0 -1
  114. package/dist/cjs/metric/DamerauLevenshtein.cjs.map +0 -1
  115. package/dist/cjs/metric/DiceSorensen.cjs.map +0 -1
  116. package/dist/cjs/metric/Hamming.cjs.map +0 -1
  117. package/dist/cjs/metric/Jaccard.cjs.map +0 -1
  118. package/dist/cjs/metric/JaroWinkler.cjs.map +0 -1
  119. package/dist/cjs/metric/LCS.cjs.map +0 -1
  120. package/dist/cjs/metric/Levenshtein.cjs.map +0 -1
  121. package/dist/cjs/metric/Metric.cjs.map +0 -1
  122. package/dist/cjs/metric/NeedlemanWunsch.cjs.map +0 -1
  123. package/dist/cjs/metric/QGram.cjs.map +0 -1
  124. package/dist/cjs/metric/SmithWaterman.cjs.map +0 -1
  125. package/dist/cjs/phonetic/Caverphone.cjs.map +0 -1
  126. package/dist/cjs/phonetic/Cologne.cjs.map +0 -1
  127. package/dist/cjs/phonetic/Metaphone.cjs.map +0 -1
  128. package/dist/cjs/phonetic/Phonetic.cjs.map +0 -1
  129. package/dist/cjs/phonetic/Soundex.cjs.map +0 -1
  130. package/dist/cjs/root.cjs.map +0 -1
  131. package/dist/cjs/utils/DeepMerge.cjs.map +0 -1
  132. package/dist/cjs/utils/DiffChecker.cjs.map +0 -1
  133. package/dist/cjs/utils/Filter.cjs.map +0 -1
  134. package/dist/cjs/utils/HashTable.cjs.map +0 -1
  135. package/dist/cjs/utils/Normalizer.cjs.map +0 -1
  136. package/dist/cjs/utils/Pool.cjs.map +0 -1
  137. package/dist/cjs/utils/Profiler.cjs.map +0 -1
  138. package/dist/cjs/utils/Registry.cjs.map +0 -1
  139. package/dist/cjs/utils/StructuredData.cjs.map +0 -1
  140. package/dist/cjs/utils/TextAnalyzer.cjs.map +0 -1
  141. package/dist/esm/CmpStr.mjs.map +0 -1
  142. package/dist/esm/CmpStrAsync.mjs.map +0 -1
  143. package/dist/esm/index.mjs.map +0 -1
  144. package/dist/esm/metric/Cosine.mjs.map +0 -1
  145. package/dist/esm/metric/DamerauLevenshtein.mjs.map +0 -1
  146. package/dist/esm/metric/DiceSorensen.mjs.map +0 -1
  147. package/dist/esm/metric/Hamming.mjs.map +0 -1
  148. package/dist/esm/metric/Jaccard.mjs.map +0 -1
  149. package/dist/esm/metric/JaroWinkler.mjs.map +0 -1
  150. package/dist/esm/metric/LCS.mjs.map +0 -1
  151. package/dist/esm/metric/Levenshtein.mjs.map +0 -1
  152. package/dist/esm/metric/Metric.mjs.map +0 -1
  153. package/dist/esm/metric/NeedlemanWunsch.mjs.map +0 -1
  154. package/dist/esm/metric/QGram.mjs.map +0 -1
  155. package/dist/esm/metric/SmithWaterman.mjs.map +0 -1
  156. package/dist/esm/phonetic/Caverphone.mjs.map +0 -1
  157. package/dist/esm/phonetic/Cologne.mjs.map +0 -1
  158. package/dist/esm/phonetic/Metaphone.mjs.map +0 -1
  159. package/dist/esm/phonetic/Phonetic.mjs.map +0 -1
  160. package/dist/esm/phonetic/Soundex.mjs.map +0 -1
  161. package/dist/esm/root.mjs.map +0 -1
  162. package/dist/esm/utils/DeepMerge.mjs.map +0 -1
  163. package/dist/esm/utils/DiffChecker.mjs.map +0 -1
  164. package/dist/esm/utils/Filter.mjs.map +0 -1
  165. package/dist/esm/utils/HashTable.mjs.map +0 -1
  166. package/dist/esm/utils/Normalizer.mjs.map +0 -1
  167. package/dist/esm/utils/Pool.mjs.map +0 -1
  168. package/dist/esm/utils/Profiler.mjs.map +0 -1
  169. package/dist/esm/utils/Registry.mjs.map +0 -1
  170. package/dist/esm/utils/StructuredData.mjs.map +0 -1
  171. package/dist/esm/utils/TextAnalyzer.mjs.map +0 -1
@@ -1,9 +1,113 @@
1
1
  /**
2
- * CmpStr v3.2.0 build-6929b12-260122
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,19 +579,35 @@ 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) {
604
+ Filter.pipeline.clear();
472
605
  if (hook) Filter.filters.delete(hook);
473
606
  else Filter.filters.clear();
474
607
  }
608
+ static clearPipeline() {
609
+ Filter.pipeline.clear();
610
+ }
475
611
  }
476
612
 
477
613
  class Hasher {
@@ -550,42 +686,62 @@ class Normalizer {
550
686
  return Array.from(new Set(flags)).sort().join('');
551
687
  }
552
688
  static getPipeline(flags) {
553
- if (Normalizer.pipeline.has(flags)) return Normalizer.pipeline.get(flags);
554
- const { REGEX } = Normalizer;
555
- const steps = [
556
- ['d', (s) => s.normalize('NFD')],
557
- ['i', (s) => s.toLowerCase()],
558
- ['k', (s) => s.replace(REGEX.nonLetters, '')],
559
- ['n', (s) => s.replace(REGEX.nonNumbers, '')],
560
- ['r', (s) => s.replace(REGEX.doubleChars, '$1')],
561
- ['s', (s) => s.replace(REGEX.specialChars, '')],
562
- ['t', (s) => s.trim()],
563
- ['u', (s) => s.normalize('NFC')],
564
- ['w', (s) => s.replace(REGEX.whitespace, ' ')],
565
- ['x', (s) => s.normalize('NFKC')]
566
- ];
567
- const pipeline = steps
568
- .filter(([f]) => flags.includes(f))
569
- .map(([, fn]) => fn);
570
- const fn = (s) => pipeline.reduce((v, f) => f(v), s);
571
- Normalizer.pipeline.set(flags, fn);
572
- 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
+ );
573
716
  }
574
717
  static normalize(input, flags) {
575
- if (!flags || typeof flags !== 'string' || !input) return input;
576
- flags = this.canonicalFlags(flags);
577
- if (Array.isArray(input))
578
- return input.map((s) => Normalizer.normalize(s, flags));
579
- const key = Normalizer.cache.key(flags, [input]);
580
- if (key && Normalizer.cache.has(key)) return Normalizer.cache.get(key);
581
- const res = Normalizer.getPipeline(flags)(input);
582
- if (key) Normalizer.cache.set(key, res);
583
- 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
+ );
584
733
  }
585
734
  static async normalizeAsync(input, flags) {
586
- return await (Array.isArray(input)
587
- ? Promise.all(input.map((s) => Normalizer.normalize(s, flags)))
588
- : 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
+ );
589
745
  }
590
746
  static clear() {
591
747
  Normalizer.pipeline.clear();
@@ -675,19 +831,34 @@ class Profiler {
675
831
  const registry = Object.create(null);
676
832
  const factory = Object.create(null);
677
833
  function Registry(reg, ctor) {
678
- if (reg in registry || reg in factory)
679
- throw new Error(
680
- `Registry <${reg}> already exists / overwriting is forbidden`
681
- );
834
+ ErrorUtil.assert(
835
+ !(reg in registry || reg in factory),
836
+ `Registry <${reg}> already exists / overwriting is forbidden`,
837
+ { registry: reg }
838
+ );
682
839
  const classes = Object.create(null);
683
840
  const service = Object.freeze({
684
841
  add(name, cls, update = false) {
685
- if (!(cls.prototype instanceof ctor))
686
- throw new TypeError(`Class must extend <${reg}>`);
687
- if (!update && name in classes)
688
- throw new Error(
689
- `Entry <${name}> already exists / use <update=true> to overwrite`
690
- );
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
+ );
691
862
  classes[name] = cls;
692
863
  },
693
864
  remove(name) {
@@ -700,8 +871,16 @@ function Registry(reg, ctor) {
700
871
  return Object.keys(classes);
701
872
  },
702
873
  get(name) {
703
- if (!(name in classes))
704
- 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
+ );
705
884
  return classes[name];
706
885
  }
707
886
  });
@@ -711,18 +890,18 @@ function Registry(reg, ctor) {
711
890
  }
712
891
  function resolveCls(reg, cls) {
713
892
  if (!(reg in registry))
714
- throw new ReferenceError(`Registry <${reg}> does not exist`);
893
+ throw new CmpStrNotFoundError(`Registry <${reg}> does not exist`, {
894
+ registry: reg
895
+ });
715
896
  return typeof cls === 'string' ? registry[reg]?.get(cls) : cls;
716
897
  }
717
898
  function createFromRegistry(reg, cls, ...args) {
718
899
  cls = resolveCls(reg, cls);
719
- try {
720
- return new cls(...args);
721
- } catch (err) {
722
- throw new Error(`Cannot instantiate class <${cls.name ?? cls}>`, {
723
- cause: err
724
- });
725
- }
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
+ );
726
905
  }
727
906
 
728
907
  class RingPool {
@@ -733,22 +912,37 @@ class RingPool {
733
912
  this.maxSize = maxSize;
734
913
  }
735
914
  acquire(minSize, allowOversize) {
736
- const len = this.buffers.length;
737
- for (let i = 0; i < len; i++) {
738
- const idx = (this.pointer + i) & (len - 1);
739
- const item = this.buffers[idx];
740
- if (item.size >= minSize && (allowOversize || item.size === minSize)) {
741
- this.pointer = (idx + 1) & (len - 1);
742
- return item;
743
- }
744
- }
745
- 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
+ );
746
934
  }
747
935
  release(item) {
748
- if (this.buffers.length < this.maxSize)
749
- return void [this.buffers.push(item)];
750
- this.buffers[this.pointer] = item;
751
- 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
+ );
752
946
  }
753
947
  clear() {
754
948
  this.buffers = [];
@@ -801,6 +995,8 @@ class Pool {
801
995
  }
802
996
  static acquire(type, size) {
803
997
  const CONFIG = this.CONFIG[type];
998
+ if (!CONFIG)
999
+ throw new CmpStrUsageError(`Unsupported pool type <${type}>`, { type });
804
1000
  if (size > CONFIG.maxItemSize) return this.allocate(type, size);
805
1001
  const item = this.POOLS[type].acquire(size, CONFIG.allowOversize);
806
1002
  if (item)
@@ -811,8 +1007,10 @@ class Pool {
811
1007
  return sizes.map((size) => this.acquire(type, size));
812
1008
  }
813
1009
  static release(type, buffer, size) {
814
- if (size <= this.CONFIG[type].maxItemSize)
815
- 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 });
816
1014
  }
817
1015
  }
818
1016
 
@@ -863,7 +1061,7 @@ class StructuredData {
863
1061
  raw: r.raw
864
1062
  }));
865
1063
  else
866
- throw new TypeError(
1064
+ throw new CmpStrValidationError(
867
1065
  'Unsupported result format for StructuredData normalization.'
868
1066
  );
869
1067
  return normalized.map((r, idx) => ({ ...r, __idx: idx }));
@@ -921,10 +1119,18 @@ class StructuredData {
921
1119
  );
922
1120
  }
923
1121
  performLookup(fn, extractedStrings, opt) {
924
- 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
+ );
925
1127
  }
926
1128
  async performLookupAsync(fn, extractedStrings, opt) {
927
- 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
+ );
928
1134
  }
929
1135
  lookup(fn, query, opt) {
930
1136
  const b = this.extract();
@@ -1177,8 +1383,11 @@ class Metric {
1177
1383
  this.metric = metric;
1178
1384
  this.a = Array.isArray(a) ? a : [a];
1179
1385
  this.b = Array.isArray(b) ? b : [b];
1180
- if (this.a.length === 0 || this.b.length === 0)
1181
- 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
+ );
1182
1391
  this.options = opt;
1183
1392
  this.optKey = Hasher.fastFNV1a(
1184
1393
  JSON.stringify(opt, Object.keys(opt).sort())
@@ -1191,37 +1400,46 @@ class Metric {
1191
1400
  return undefined;
1192
1401
  }
1193
1402
  compute(a, b, m, n, maxLen) {
1194
- 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
+ );
1195
1406
  }
1196
1407
  runSingle(i, j) {
1197
- let a = String(this.a[i]),
1198
- A = a;
1199
- let b = String(this.b[j]),
1200
- B = b;
1201
- let m = A.length,
1202
- n = B.length;
1203
- let result = this.preCompute(A, B, m, n);
1204
- if (!result) {
1205
- result = profiler$2.run(() => {
1206
- if (this.symmetric) [A, B, m, n] = Metric.swap(A, B, m, n);
1207
- const key =
1208
- Metric.cache.key(this.metric, [A, B], this.symmetric) + this.optKey;
1209
- return (
1210
- Metric.cache.get(key || '') ??
1211
- (() => {
1212
- const res = this.compute(A, B, m, n, Math.max(m, n));
1213
- if (key) Metric.cache.set(key, res);
1214
- return res;
1215
- })()
1216
- );
1217
- });
1218
- }
1219
- return {
1220
- metric: this.metric,
1221
- a: this.origA[i] ?? a,
1222
- b: this.origB[j] ?? b,
1223
- ...result
1224
- };
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
+ );
1225
1443
  }
1226
1444
  async runSingleAsync(i, j) {
1227
1445
  return Promise.resolve(this.runSingle(i, j));
@@ -1263,7 +1481,10 @@ class Metric {
1263
1481
  ? true
1264
1482
  : !safe &&
1265
1483
  (() => {
1266
- 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
+ );
1267
1488
  })();
1268
1489
  }
1269
1490
  isSymmetrical = () => this.symmetric;
@@ -1287,7 +1508,7 @@ class Metric {
1287
1508
  if (this.isPairwise()) this.runPairwise();
1288
1509
  break;
1289
1510
  default:
1290
- throw new Error(`Unsupported mode <${mode}>`);
1511
+ throw new CmpStrInternalError(`Unsupported mode <${mode}>`);
1291
1512
  }
1292
1513
  }
1293
1514
  async runAsync(mode, clear = true) {
@@ -1308,13 +1529,15 @@ class Metric {
1308
1529
  if (this.isPairwise()) await this.runPairwiseAsync();
1309
1530
  break;
1310
1531
  default:
1311
- throw new Error(`Unsupported async mode <${mode}>`);
1532
+ throw new CmpStrInternalError(`Unsupported async mode <${mode}>`);
1312
1533
  }
1313
1534
  }
1314
1535
  getMetricName = () => this.metric;
1315
1536
  getResults() {
1316
- if (this.results === undefined)
1317
- throw new Error(`run() must be called before getResult()`);
1537
+ ErrorUtil.assert(
1538
+ this.results !== undefined,
1539
+ `run() must be called before getResults()`
1540
+ );
1318
1541
  return this.results;
1319
1542
  }
1320
1543
  }
@@ -1442,9 +1665,10 @@ class HammingDistance extends Metric {
1442
1665
  if (n < maxLen) b = b.padEnd(maxLen, this.options.pad);
1443
1666
  m = n = maxLen;
1444
1667
  } else
1445
- throw new Error(
1668
+ throw new CmpStrUsageError(
1446
1669
  `Strings must be of equal length for Hamming Distance, a=${m} and b=${n} given, ` +
1447
- `use option.pad for automatic adjustment`
1670
+ `use option.pad for automatic adjustment`,
1671
+ { a: m, b: n }
1448
1672
  );
1449
1673
  }
1450
1674
  let dist = 0;
@@ -1717,10 +1941,17 @@ class Phonetic {
1717
1941
  constructor(algo, opt = {}) {
1718
1942
  const defaults = this.constructor.default ?? {};
1719
1943
  const mapId = opt.map ?? defaults.map;
1720
- 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
+ );
1721
1949
  const map = PhoneticMappingRegistry.get(algo, mapId);
1722
1950
  if (map === undefined)
1723
- throw new Error(`Requested mapping <${mapId}> is not declared`);
1951
+ throw new CmpStrNotFoundError(
1952
+ `Requested mapping <${mapId}> is not declared`,
1953
+ { algo, mapId }
1954
+ );
1724
1955
  this.options = merge(merge(defaults, map.options ?? {}), opt);
1725
1956
  this.optKey = Hasher.fastFNV1a(
1726
1957
  JSON.stringify(this.options, Object.keys(this.options).sort())
@@ -1810,35 +2041,47 @@ class Phonetic {
1810
2041
  return code;
1811
2042
  }
1812
2043
  loop(words) {
1813
- const index = [];
1814
- for (const word of words) {
1815
- const key = Phonetic.cache.key(this.algo, [word]) + this.optKey;
1816
- const code =
1817
- Phonetic.cache.get(key || '') ??
1818
- (() => {
1819
- const res = this.encode(word);
1820
- if (key) Phonetic.cache.set(key, res);
1821
- return res;
1822
- })();
1823
- if (code && code.length) index.push(this.equalLen(code));
1824
- }
1825
- 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
+ );
1826
2063
  }
1827
2064
  async loopAsync(words) {
1828
- const index = [];
1829
- for (const word of words) {
1830
- const key = Phonetic.cache.key(this.algo, [word]) + this.optKey;
1831
- const code = await Promise.resolve(
1832
- Phonetic.cache.get(key || '') ??
1833
- (() => {
1834
- const res = this.encode(word);
1835
- if (key) Phonetic.cache.set(key, res);
1836
- return res;
1837
- })()
1838
- );
1839
- if (code && code.length) index.push(this.equalLen(code));
1840
- }
1841
- 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
+ );
1842
2085
  }
1843
2086
  getAlgoName = () => this.algo;
1844
2087
  getIndex(input) {
@@ -1863,10 +2106,11 @@ const PhoneticMappingRegistry = (() => {
1863
2106
  return Object.freeze({
1864
2107
  add(algo, id, map, update = false) {
1865
2108
  const mappings = maps(algo);
1866
- if (!update && id in mappings)
1867
- throw new Error(
1868
- `Entry <${id}> already exists / use <update=true> to overwrite`
1869
- );
2109
+ ErrorUtil.assert(
2110
+ !(!id || id in mappings) || update,
2111
+ `Entry <${id}> already exists / use <update=true> to overwrite`,
2112
+ { algo, id }
2113
+ );
1870
2114
  mappings[id] = map;
1871
2115
  },
1872
2116
  remove(algo, id) {
@@ -2266,6 +2510,7 @@ class CmpStr {
2266
2510
  static profiler = profiler.services;
2267
2511
  static clearCache = {
2268
2512
  normalizer: Normalizer.clear,
2513
+ filter: Filter.clearPipeline,
2269
2514
  metric: Metric.clear,
2270
2515
  phonetic: Phonetic.clear
2271
2516
  };
@@ -2285,20 +2530,22 @@ class CmpStr {
2285
2530
  switch (cond) {
2286
2531
  case 'metric':
2287
2532
  if (!CmpStr.metric.has(test))
2288
- throw new Error(
2533
+ throw new CmpStrNotFoundError(
2289
2534
  `CmpStr <metric> must be set, call .setMetric(), ` +
2290
- `use CmpStr.metric.list() for available metrics`
2535
+ `use CmpStr.metric.list() for available metrics`,
2536
+ { metric: test }
2291
2537
  );
2292
2538
  break;
2293
2539
  case 'phonetic':
2294
2540
  if (!CmpStr.phonetic.has(test))
2295
- throw new Error(
2541
+ throw new CmpStrNotFoundError(
2296
2542
  `CmpStr <phonetic> must be set, call .setPhonetic(), ` +
2297
- `use CmpStr.phonetic.list() for available phonetic algorithms`
2543
+ `use CmpStr.phonetic.list() for available phonetic algorithms`,
2544
+ { phonetic: test }
2298
2545
  );
2299
2546
  break;
2300
2547
  default:
2301
- throw new Error(`Cmpstr condition <${cond}> unknown`);
2548
+ throw new CmpStrInternalError(`Cmpstr condition <${cond}> unknown`);
2302
2549
  }
2303
2550
  }
2304
2551
  assertMany(...cond) {
@@ -2337,31 +2584,42 @@ class CmpStr {
2337
2584
  return StructuredData.create(data, key);
2338
2585
  }
2339
2586
  compute(a, b, opt, mode, raw, skip) {
2340
- const resolved = this.resolveOptions(opt);
2341
- this.assert('metric', resolved.metric);
2342
- const A = skip ? a : this.prepare(a, resolved);
2343
- const B = skip ? b : this.prepare(b, resolved);
2344
- if (
2345
- resolved.safeEmpty &&
2346
- ((Array.isArray(A) && A.length === 0) ||
2347
- (Array.isArray(B) && B.length === 0) ||
2348
- A === '' ||
2349
- B === '')
2350
- ) {
2351
- return [];
2352
- }
2353
- const metric = factory['metric'](resolved.metric, A, B, resolved.opt);
2354
- if (resolved.output !== 'prep') metric.setOriginal(a, b);
2355
- metric.run(mode);
2356
- const result = this.postProcess(metric.getResults(), resolved);
2357
- 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
+ );
2358
2611
  }
2359
2612
  output(result, raw) {
2360
- return (raw ?? this.options.raw)
2361
- ? result
2362
- : Array.isArray(result)
2363
- ? result.map((r) => ({ source: r.a, target: r.b, match: r.res }))
2364
- : { 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
+ );
2365
2623
  }
2366
2624
  clone = () => Object.assign(Object.create(Object.getPrototypeOf(this)), this);
2367
2625
  reset() {
@@ -2377,8 +2635,14 @@ class CmpStr {
2377
2635
  return this;
2378
2636
  }
2379
2637
  setSerializedOptions(opt) {
2380
- this.options = JSON.parse(opt);
2381
- 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
+ );
2382
2646
  }
2383
2647
  setOption(path, value) {
2384
2648
  set(this.options, path, value);
@@ -2521,24 +2785,30 @@ class CmpStrAsync extends CmpStr {
2521
2785
  : phonetic.getIndexAsync(input).then((r) => r.join(delimiter));
2522
2786
  }
2523
2787
  async computeAsync(a, b, opt, mode, raw, skip) {
2524
- const resolved = this.resolveOptions(opt);
2525
- this.assert('metric', resolved.metric);
2526
- const A = skip ? a : await this.prepareAsync(a, resolved);
2527
- const B = skip ? b : await this.prepareAsync(b, resolved);
2528
- if (
2529
- resolved.safeEmpty &&
2530
- ((Array.isArray(A) && A.length === 0) ||
2531
- (Array.isArray(B) && B.length === 0) ||
2532
- A === '' ||
2533
- B === '')
2534
- ) {
2535
- return [];
2536
- }
2537
- const metric = factory['metric'](resolved.metric, A, B, resolved.opt);
2538
- if (resolved.output !== 'prep') metric.setOriginal(a, b);
2539
- await metric.runAsync(mode);
2540
- const result = this.postProcess(metric.getResults(), resolved);
2541
- 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
+ );
2542
2812
  }
2543
2813
  async testAsync(a, b, opt) {
2544
2814
  return this.computeAsync(a, b, opt, 'single');
@@ -2640,6 +2910,7 @@ class CmpStrAsync extends CmpStr {
2640
2910
  export {
2641
2911
  CmpStr,
2642
2912
  CmpStrAsync,
2913
+ Errors as CmpStrError,
2643
2914
  DeepMerge,
2644
2915
  DiffChecker,
2645
2916
  Filter,
@@ -2656,4 +2927,3 @@ export {
2656
2927
  StructuredData,
2657
2928
  TextAnalyzer
2658
2929
  };
2659
- //# sourceMappingURL=CmpStr.esm.js.map