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.
- package/README.md +24 -18
- package/dist/CmpStr.esm.js +485 -220
- package/dist/CmpStr.esm.min.js +2 -3
- package/dist/CmpStr.umd.js +484 -220
- package/dist/CmpStr.umd.min.js +2 -3
- package/dist/cjs/CmpStr.cjs +57 -36
- package/dist/cjs/CmpStrAsync.cjs +30 -24
- package/dist/cjs/index.cjs +1 -2
- package/dist/cjs/metric/Cosine.cjs +1 -2
- package/dist/cjs/metric/DamerauLevenshtein.cjs +1 -2
- package/dist/cjs/metric/DiceSorensen.cjs +1 -2
- package/dist/cjs/metric/Hamming.cjs +5 -4
- package/dist/cjs/metric/Jaccard.cjs +1 -2
- package/dist/cjs/metric/JaroWinkler.cjs +1 -2
- package/dist/cjs/metric/LCS.cjs +1 -2
- package/dist/cjs/metric/Levenshtein.cjs +1 -2
- package/dist/cjs/metric/Metric.cjs +57 -38
- package/dist/cjs/metric/NeedlemanWunsch.cjs +1 -2
- package/dist/cjs/metric/QGram.cjs +1 -2
- package/dist/cjs/metric/SmithWaterman.cjs +1 -2
- package/dist/cjs/phonetic/Caverphone.cjs +1 -2
- package/dist/cjs/phonetic/Cologne.cjs +1 -2
- package/dist/cjs/phonetic/Metaphone.cjs +1 -2
- package/dist/cjs/phonetic/Phonetic.cjs +55 -35
- package/dist/cjs/phonetic/Soundex.cjs +1 -2
- package/dist/cjs/root.cjs +3 -2
- package/dist/cjs/utils/DeepMerge.cjs +10 -5
- package/dist/cjs/utils/DiffChecker.cjs +1 -2
- package/dist/cjs/utils/Errors.cjs +103 -0
- package/dist/cjs/utils/Filter.cjs +52 -27
- package/dist/cjs/utils/HashTable.cjs +1 -2
- package/dist/cjs/utils/Normalizer.cjs +54 -34
- package/dist/cjs/utils/Pool.cjs +42 -18
- package/dist/cjs/utils/Profiler.cjs +1 -2
- package/dist/cjs/utils/Registry.cjs +46 -22
- package/dist/cjs/utils/StructuredData.cjs +13 -5
- package/dist/cjs/utils/TextAnalyzer.cjs +1 -2
- package/dist/esm/CmpStr.mjs +55 -32
- package/dist/esm/CmpStrAsync.mjs +26 -20
- package/dist/esm/index.mjs +1 -2
- package/dist/esm/metric/Cosine.mjs +1 -2
- package/dist/esm/metric/DamerauLevenshtein.mjs +1 -2
- package/dist/esm/metric/DiceSorensen.mjs +1 -2
- package/dist/esm/metric/Hamming.mjs +5 -4
- package/dist/esm/metric/Jaccard.mjs +1 -2
- package/dist/esm/metric/JaroWinkler.mjs +1 -2
- package/dist/esm/metric/LCS.mjs +1 -2
- package/dist/esm/metric/Levenshtein.mjs +1 -2
- package/dist/esm/metric/Metric.mjs +59 -38
- package/dist/esm/metric/NeedlemanWunsch.mjs +1 -2
- package/dist/esm/metric/QGram.mjs +1 -2
- package/dist/esm/metric/SmithWaterman.mjs +1 -2
- package/dist/esm/phonetic/Caverphone.mjs +1 -2
- package/dist/esm/phonetic/Cologne.mjs +1 -2
- package/dist/esm/phonetic/Metaphone.mjs +1 -2
- package/dist/esm/phonetic/Phonetic.mjs +55 -35
- package/dist/esm/phonetic/Soundex.mjs +1 -2
- package/dist/esm/root.mjs +3 -2
- package/dist/esm/utils/DeepMerge.mjs +10 -5
- package/dist/esm/utils/DiffChecker.mjs +1 -2
- package/dist/esm/utils/Errors.mjs +103 -0
- package/dist/esm/utils/Filter.mjs +52 -27
- package/dist/esm/utils/HashTable.mjs +1 -2
- package/dist/esm/utils/Normalizer.mjs +54 -34
- package/dist/esm/utils/Pool.mjs +38 -18
- package/dist/esm/utils/Profiler.mjs +1 -2
- package/dist/esm/utils/Registry.mjs +46 -22
- package/dist/esm/utils/StructuredData.mjs +13 -5
- package/dist/esm/utils/TextAnalyzer.mjs +1 -2
- package/dist/types/CmpStr.d.ts +5 -1
- package/dist/types/CmpStrAsync.d.ts +1 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/types/metric/Metric.d.ts +6 -5
- package/dist/types/phonetic/Phonetic.d.ts +3 -1
- package/dist/types/root.d.ts +2 -1
- package/dist/types/utils/DeepMerge.d.ts +1 -1
- package/dist/types/utils/Errors.d.ts +137 -0
- package/dist/types/utils/Filter.d.ts +4 -0
- package/dist/types/utils/Normalizer.d.ts +3 -0
- package/dist/types/utils/Pool.d.ts +2 -0
- package/dist/types/utils/Registry.d.ts +3 -3
- package/dist/types/utils/StructuredData.d.ts +3 -1
- package/dist/types/utils/Types.d.ts +26 -0
- package/package.json +7 -7
- package/dist/CmpStr.esm.js.map +0 -1
- package/dist/CmpStr.esm.min.js.map +0 -1
- package/dist/CmpStr.umd.js.map +0 -1
- package/dist/CmpStr.umd.min.js.map +0 -1
- package/dist/cjs/CmpStr.cjs.map +0 -1
- package/dist/cjs/CmpStrAsync.cjs.map +0 -1
- package/dist/cjs/index.cjs.map +0 -1
- package/dist/cjs/metric/Cosine.cjs.map +0 -1
- package/dist/cjs/metric/DamerauLevenshtein.cjs.map +0 -1
- package/dist/cjs/metric/DiceSorensen.cjs.map +0 -1
- package/dist/cjs/metric/Hamming.cjs.map +0 -1
- package/dist/cjs/metric/Jaccard.cjs.map +0 -1
- package/dist/cjs/metric/JaroWinkler.cjs.map +0 -1
- package/dist/cjs/metric/LCS.cjs.map +0 -1
- package/dist/cjs/metric/Levenshtein.cjs.map +0 -1
- package/dist/cjs/metric/Metric.cjs.map +0 -1
- package/dist/cjs/metric/NeedlemanWunsch.cjs.map +0 -1
- package/dist/cjs/metric/QGram.cjs.map +0 -1
- package/dist/cjs/metric/SmithWaterman.cjs.map +0 -1
- package/dist/cjs/phonetic/Caverphone.cjs.map +0 -1
- package/dist/cjs/phonetic/Cologne.cjs.map +0 -1
- package/dist/cjs/phonetic/Metaphone.cjs.map +0 -1
- package/dist/cjs/phonetic/Phonetic.cjs.map +0 -1
- package/dist/cjs/phonetic/Soundex.cjs.map +0 -1
- package/dist/cjs/root.cjs.map +0 -1
- package/dist/cjs/utils/DeepMerge.cjs.map +0 -1
- package/dist/cjs/utils/DiffChecker.cjs.map +0 -1
- package/dist/cjs/utils/Filter.cjs.map +0 -1
- package/dist/cjs/utils/HashTable.cjs.map +0 -1
- package/dist/cjs/utils/Normalizer.cjs.map +0 -1
- package/dist/cjs/utils/Pool.cjs.map +0 -1
- package/dist/cjs/utils/Profiler.cjs.map +0 -1
- package/dist/cjs/utils/Registry.cjs.map +0 -1
- package/dist/cjs/utils/StructuredData.cjs.map +0 -1
- package/dist/cjs/utils/TextAnalyzer.cjs.map +0 -1
- package/dist/esm/CmpStr.mjs.map +0 -1
- package/dist/esm/CmpStrAsync.mjs.map +0 -1
- package/dist/esm/index.mjs.map +0 -1
- package/dist/esm/metric/Cosine.mjs.map +0 -1
- package/dist/esm/metric/DamerauLevenshtein.mjs.map +0 -1
- package/dist/esm/metric/DiceSorensen.mjs.map +0 -1
- package/dist/esm/metric/Hamming.mjs.map +0 -1
- package/dist/esm/metric/Jaccard.mjs.map +0 -1
- package/dist/esm/metric/JaroWinkler.mjs.map +0 -1
- package/dist/esm/metric/LCS.mjs.map +0 -1
- package/dist/esm/metric/Levenshtein.mjs.map +0 -1
- package/dist/esm/metric/Metric.mjs.map +0 -1
- package/dist/esm/metric/NeedlemanWunsch.mjs.map +0 -1
- package/dist/esm/metric/QGram.mjs.map +0 -1
- package/dist/esm/metric/SmithWaterman.mjs.map +0 -1
- package/dist/esm/phonetic/Caverphone.mjs.map +0 -1
- package/dist/esm/phonetic/Cologne.mjs.map +0 -1
- package/dist/esm/phonetic/Metaphone.mjs.map +0 -1
- package/dist/esm/phonetic/Phonetic.mjs.map +0 -1
- package/dist/esm/phonetic/Soundex.mjs.map +0 -1
- package/dist/esm/root.mjs.map +0 -1
- package/dist/esm/utils/DeepMerge.mjs.map +0 -1
- package/dist/esm/utils/DiffChecker.mjs.map +0 -1
- package/dist/esm/utils/Filter.mjs.map +0 -1
- package/dist/esm/utils/HashTable.mjs.map +0 -1
- package/dist/esm/utils/Normalizer.mjs.map +0 -1
- package/dist/esm/utils/Pool.mjs.map +0 -1
- package/dist/esm/utils/Profiler.mjs.map +0 -1
- package/dist/esm/utils/Registry.mjs.map +0 -1
- package/dist/esm/utils/StructuredData.mjs.map +0 -1
- package/dist/esm/utils/TextAnalyzer.mjs.map +0 -1
package/dist/CmpStr.esm.js
CHANGED
|
@@ -1,9 +1,113 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CmpStr v3.2.
|
|
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
|
|
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
|
|
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
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
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
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
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
|
-
|
|
463
|
-
|
|
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
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
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
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
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
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
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
|
|
591
|
-
|
|
592
|
-
|
|
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
|
-
|
|
683
|
-
|
|
684
|
-
|
|
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
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
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
|
-
|
|
708
|
-
|
|
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
|
|
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
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
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
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
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
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
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
|
-
|
|
819
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
1185
|
-
|
|
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
|
|
1403
|
+
throw new CmpStrInternalError(
|
|
1404
|
+
`Method compute() must be overridden in a subclass`
|
|
1405
|
+
);
|
|
1199
1406
|
}
|
|
1200
1407
|
runSingle(i, j) {
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
if (
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
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
|
|
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
|
|
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
|
|
1532
|
+
throw new CmpStrInternalError(`Unsupported async mode <${mode}>`);
|
|
1316
1533
|
}
|
|
1317
1534
|
}
|
|
1318
1535
|
getMetricName = () => this.metric;
|
|
1319
1536
|
getResults() {
|
|
1320
|
-
|
|
1321
|
-
|
|
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
|
|
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)
|
|
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
|
|
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
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
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
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
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
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
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 (
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
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
|
-
|
|
2386
|
-
|
|
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
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
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
|