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.
- package/README.md +26 -18
- package/dist/CmpStr.esm.js +490 -220
- package/dist/CmpStr.esm.min.js +2 -3
- package/dist/CmpStr.umd.js +489 -220
- package/dist/CmpStr.umd.min.js +2 -3
- package/dist/cjs/CmpStr.cjs +58 -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 +56 -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 +56 -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 +56 -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 +12 -6
- package/dist/types/CmpStrAsync.d.ts +6 -4
- package/dist/types/index.d.ts +3 -2
- package/dist/types/metric/Cosine.d.ts +2 -1
- package/dist/types/metric/DamerauLevenshtein.d.ts +2 -1
- package/dist/types/metric/DiceSorensen.d.ts +2 -1
- package/dist/types/metric/Hamming.d.ts +2 -1
- package/dist/types/metric/Jaccard.d.ts +2 -1
- package/dist/types/metric/JaroWinkler.d.ts +2 -1
- package/dist/types/metric/LCS.d.ts +2 -1
- package/dist/types/metric/Levenshtein.d.ts +2 -1
- package/dist/types/metric/Metric.d.ts +7 -5
- package/dist/types/metric/NeedlemanWunsch.d.ts +2 -1
- package/dist/types/metric/QGram.d.ts +2 -1
- package/dist/types/metric/SmithWaterman.d.ts +2 -1
- package/dist/types/metric/index.d.ts +1 -0
- package/dist/types/phonetic/Caverphone.d.ts +2 -1
- package/dist/types/phonetic/Cologne.d.ts +2 -1
- package/dist/types/phonetic/Metaphone.d.ts +2 -1
- package/dist/types/phonetic/Phonetic.d.ts +4 -1
- package/dist/types/phonetic/Soundex.d.ts +2 -1
- package/dist/types/phonetic/index.d.ts +1 -0
- package/dist/types/root.d.ts +2 -1
- package/dist/types/utils/DeepMerge.d.ts +3 -2
- package/dist/types/utils/DiffChecker.d.ts +2 -1
- package/dist/types/utils/Errors.d.ts +137 -0
- package/dist/types/utils/Filter.d.ts +33 -22
- package/dist/types/utils/HashTable.d.ts +2 -1
- package/dist/types/utils/Normalizer.d.ts +5 -1
- package/dist/types/utils/Pool.d.ts +4 -1
- package/dist/types/utils/Profiler.d.ts +3 -2
- package/dist/types/utils/Registry.d.ts +5 -4
- package/dist/types/utils/StructuredData.d.ts +5 -2
- package/dist/types/utils/TextAnalyzer.d.ts +2 -1
- package/dist/types/utils/Types.d.ts +34 -2
- package/package.json +10 -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,19 +579,35 @@ 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) {
|
|
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
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
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
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
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
|
|
587
|
-
|
|
588
|
-
|
|
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
|
-
|
|
679
|
-
|
|
680
|
-
|
|
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
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
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
|
-
|
|
704
|
-
|
|
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
|
|
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
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
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
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
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
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
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
|
-
|
|
815
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
1181
|
-
|
|
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
|
|
1403
|
+
throw new CmpStrInternalError(
|
|
1404
|
+
`Method compute() must be overridden in a subclass`
|
|
1405
|
+
);
|
|
1195
1406
|
}
|
|
1196
1407
|
runSingle(i, j) {
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
if (
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
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
|
|
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
|
|
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
|
|
1532
|
+
throw new CmpStrInternalError(`Unsupported async mode <${mode}>`);
|
|
1312
1533
|
}
|
|
1313
1534
|
}
|
|
1314
1535
|
getMetricName = () => this.metric;
|
|
1315
1536
|
getResults() {
|
|
1316
|
-
|
|
1317
|
-
|
|
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
|
|
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)
|
|
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
|
|
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
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
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
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
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
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
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 (
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
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
|
-
|
|
2381
|
-
|
|
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
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
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
|