cmpstr 3.2.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -18
- package/dist/CmpStr.esm.js +1904 -1211
- package/dist/CmpStr.esm.min.js +2 -3
- package/dist/CmpStr.umd.js +1924 -1236
- package/dist/CmpStr.umd.min.js +2 -3
- package/dist/cjs/CmpStr.cjs +134 -64
- package/dist/cjs/CmpStrAsync.cjs +60 -37
- 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 +90 -53
- 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 +80 -48
- package/dist/cjs/phonetic/Soundex.cjs +1 -2
- package/dist/cjs/root.cjs +6 -3
- package/dist/cjs/utils/DeepMerge.cjs +109 -99
- package/dist/cjs/utils/DiffChecker.cjs +1 -2
- package/dist/cjs/utils/Errors.cjs +106 -0
- package/dist/cjs/utils/Filter.cjs +97 -37
- package/dist/cjs/utils/HashTable.cjs +44 -30
- package/dist/cjs/utils/Normalizer.cjs +84 -35
- package/dist/cjs/utils/OptionsValidator.cjs +211 -0
- package/dist/cjs/utils/Pool.cjs +57 -19
- package/dist/cjs/utils/Profiler.cjs +41 -28
- package/dist/cjs/utils/Registry.cjs +48 -24
- package/dist/cjs/utils/StructuredData.cjs +95 -57
- package/dist/cjs/utils/TextAnalyzer.cjs +1 -2
- package/dist/esm/CmpStr.mjs +133 -61
- package/dist/esm/CmpStrAsync.mjs +56 -33
- 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 +92 -53
- 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 +83 -48
- package/dist/esm/phonetic/Soundex.mjs +1 -2
- package/dist/esm/root.mjs +5 -4
- package/dist/esm/utils/DeepMerge.mjs +109 -95
- package/dist/esm/utils/DiffChecker.mjs +1 -2
- package/dist/esm/utils/Errors.mjs +106 -0
- package/dist/esm/utils/Filter.mjs +97 -37
- package/dist/esm/utils/HashTable.mjs +44 -30
- package/dist/esm/utils/Normalizer.mjs +84 -35
- package/dist/esm/utils/OptionsValidator.mjs +210 -0
- package/dist/esm/utils/Pool.mjs +53 -19
- package/dist/esm/utils/Profiler.mjs +41 -28
- package/dist/esm/utils/Registry.mjs +48 -24
- package/dist/esm/utils/StructuredData.mjs +95 -57
- package/dist/esm/utils/TextAnalyzer.mjs +1 -2
- package/dist/types/CmpStr.d.ts +25 -14
- package/dist/types/CmpStrAsync.d.ts +4 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/types/metric/Metric.d.ts +15 -14
- package/dist/types/phonetic/Phonetic.d.ts +7 -4
- package/dist/types/root.d.ts +4 -2
- package/dist/types/utils/DeepMerge.d.ts +80 -58
- package/dist/types/utils/Errors.d.ts +154 -0
- package/dist/types/utils/Filter.d.ts +8 -1
- package/dist/types/utils/HashTable.d.ts +12 -11
- package/dist/types/utils/Normalizer.d.ts +5 -1
- package/dist/types/utils/OptionsValidator.d.ts +193 -0
- package/dist/types/utils/Pool.d.ts +2 -0
- package/dist/types/utils/Profiler.d.ts +9 -28
- package/dist/types/utils/Registry.d.ts +3 -3
- package/dist/types/utils/StructuredData.d.ts +6 -1
- package/dist/types/utils/Types.d.ts +39 -1
- package/package.json +20 -11
- 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
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
// CmpStr v3.
|
|
1
|
+
// CmpStr v3.3.0 build-3699f85-260318 by Paul Köhler @komed3 / MIT License
|
|
2
|
+
import { ErrorUtil } from './Errors.mjs';
|
|
2
3
|
import { HashTable } from './HashTable.mjs';
|
|
3
4
|
|
|
4
5
|
class Normalizer {
|
|
@@ -15,42 +16,91 @@ class Normalizer {
|
|
|
15
16
|
return Array.from(new Set(flags)).sort().join('');
|
|
16
17
|
}
|
|
17
18
|
static getPipeline(flags) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
19
|
+
return ErrorUtil.wrap(
|
|
20
|
+
() => {
|
|
21
|
+
const cached = Normalizer.pipeline.get(flags);
|
|
22
|
+
if (cached) return cached;
|
|
23
|
+
const { REGEX } = Normalizer;
|
|
24
|
+
const steps = [];
|
|
25
|
+
for (let i = 0; i < flags.length; i++) {
|
|
26
|
+
switch (flags[i]) {
|
|
27
|
+
case 'd':
|
|
28
|
+
steps.push((s) => s.normalize('NFD'));
|
|
29
|
+
break;
|
|
30
|
+
case 'i':
|
|
31
|
+
steps.push((s) => s.toLowerCase());
|
|
32
|
+
break;
|
|
33
|
+
case 'k':
|
|
34
|
+
steps.push((s) => s.replace(REGEX.nonLetters, ''));
|
|
35
|
+
break;
|
|
36
|
+
case 'n':
|
|
37
|
+
steps.push((s) => s.replace(REGEX.nonNumbers, ''));
|
|
38
|
+
break;
|
|
39
|
+
case 'r':
|
|
40
|
+
steps.push((s) => s.replace(REGEX.doubleChars, '$1'));
|
|
41
|
+
break;
|
|
42
|
+
case 's':
|
|
43
|
+
steps.push((s) => s.replace(REGEX.specialChars, ''));
|
|
44
|
+
break;
|
|
45
|
+
case 't':
|
|
46
|
+
steps.push((s) => s.trim());
|
|
47
|
+
break;
|
|
48
|
+
case 'u':
|
|
49
|
+
steps.push((s) => s.normalize('NFC'));
|
|
50
|
+
break;
|
|
51
|
+
case 'w':
|
|
52
|
+
steps.push((s) => s.replace(REGEX.whitespace, ' '));
|
|
53
|
+
break;
|
|
54
|
+
case 'x':
|
|
55
|
+
steps.push((s) => s.normalize('NFKC'));
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const fn = (input) => {
|
|
60
|
+
let v = input;
|
|
61
|
+
for (let i = 0; i < steps.length; i++) v = steps[i](v);
|
|
62
|
+
return v;
|
|
63
|
+
};
|
|
64
|
+
Normalizer.pipeline.set(flags, fn);
|
|
65
|
+
return fn;
|
|
66
|
+
},
|
|
67
|
+
`Failed to create normalization pipeline for flags: ${flags}`,
|
|
68
|
+
{ flags }
|
|
69
|
+
);
|
|
38
70
|
}
|
|
39
|
-
static normalize(input, flags) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
71
|
+
static normalize(input, flags, normalizedFlags) {
|
|
72
|
+
return ErrorUtil.wrap(
|
|
73
|
+
() => {
|
|
74
|
+
if (!flags || typeof flags !== 'string' || !input) return input;
|
|
75
|
+
flags = normalizedFlags ?? this.canonicalFlags(flags);
|
|
76
|
+
const pipeline = Normalizer.getPipeline(flags);
|
|
77
|
+
const normalizeOne = (s) => {
|
|
78
|
+
const key = Normalizer.cache.key(flags, [s]);
|
|
79
|
+
if (key && Normalizer.cache.has(key))
|
|
80
|
+
return Normalizer.cache.get(key);
|
|
81
|
+
const res = pipeline(s);
|
|
82
|
+
if (key) Normalizer.cache.set(key, res);
|
|
83
|
+
return res;
|
|
84
|
+
};
|
|
85
|
+
return Array.isArray(input)
|
|
86
|
+
? input.map(normalizeOne)
|
|
87
|
+
: normalizeOne(input);
|
|
88
|
+
},
|
|
89
|
+
`Failed to normalize input with flags: ${flags}`,
|
|
90
|
+
{ input, flags }
|
|
91
|
+
);
|
|
49
92
|
}
|
|
50
93
|
static async normalizeAsync(input, flags) {
|
|
51
|
-
return await
|
|
52
|
-
|
|
53
|
-
|
|
94
|
+
return await ErrorUtil.wrapAsync(
|
|
95
|
+
async () => {
|
|
96
|
+
if (!flags || typeof flags !== 'string' || !input) return input;
|
|
97
|
+
return await (Array.isArray(input)
|
|
98
|
+
? Promise.all(input.map((s) => Normalizer.normalize(s, flags)))
|
|
99
|
+
: Promise.resolve(Normalizer.normalize(input, flags)));
|
|
100
|
+
},
|
|
101
|
+
`Failed to asynchronously normalize input with flags: ${flags}`,
|
|
102
|
+
{ input, flags }
|
|
103
|
+
);
|
|
54
104
|
}
|
|
55
105
|
static clear() {
|
|
56
106
|
Normalizer.pipeline.clear();
|
|
@@ -59,4 +109,3 @@ class Normalizer {
|
|
|
59
109
|
}
|
|
60
110
|
|
|
61
111
|
export { Normalizer };
|
|
62
|
-
//# sourceMappingURL=Normalizer.mjs.map
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// CmpStr v3.3.0 build-3699f85-260318 by Paul Köhler @komed3 / MIT License
|
|
2
|
+
import { CmpStrValidationError } from './Errors.mjs';
|
|
3
|
+
import '../metric/Cosine.mjs';
|
|
4
|
+
import '../metric/DamerauLevenshtein.mjs';
|
|
5
|
+
import '../metric/DiceSorensen.mjs';
|
|
6
|
+
import '../metric/Hamming.mjs';
|
|
7
|
+
import '../metric/Jaccard.mjs';
|
|
8
|
+
import '../metric/JaroWinkler.mjs';
|
|
9
|
+
import '../metric/LCS.mjs';
|
|
10
|
+
import '../metric/Levenshtein.mjs';
|
|
11
|
+
import '../metric/NeedlemanWunsch.mjs';
|
|
12
|
+
import '../metric/QGram.mjs';
|
|
13
|
+
import '../metric/SmithWaterman.mjs';
|
|
14
|
+
import { MetricRegistry } from '../metric/Metric.mjs';
|
|
15
|
+
import '../phonetic/Caverphone.mjs';
|
|
16
|
+
import '../phonetic/Cologne.mjs';
|
|
17
|
+
import '../phonetic/Metaphone.mjs';
|
|
18
|
+
import '../phonetic/Soundex.mjs';
|
|
19
|
+
import { PhoneticRegistry } from '../phonetic/Phonetic.mjs';
|
|
20
|
+
|
|
21
|
+
class OptionsValidator {
|
|
22
|
+
static ALLOWED_FLAGS = new Set([
|
|
23
|
+
'd',
|
|
24
|
+
'u',
|
|
25
|
+
'x',
|
|
26
|
+
'w',
|
|
27
|
+
't',
|
|
28
|
+
'r',
|
|
29
|
+
's',
|
|
30
|
+
'k',
|
|
31
|
+
'n',
|
|
32
|
+
'i'
|
|
33
|
+
]);
|
|
34
|
+
static ALLOWED_OUTPUT = new Set(['orig', 'prep']);
|
|
35
|
+
static ALLOWED_MODES = new Set(['default', 'batch', 'single', 'pairwise']);
|
|
36
|
+
static ALLOWED_SORT = new Set(['asc', 'desc']);
|
|
37
|
+
static PROCESSORS = {
|
|
38
|
+
phonetic: (opt) => {
|
|
39
|
+
if (!opt) return;
|
|
40
|
+
OptionsValidator.validatePhoneticName(opt.algo);
|
|
41
|
+
OptionsValidator.validatePhoneticOptions(opt.opt);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
static METRIC_OPT_MAP = {
|
|
45
|
+
mode: (v) => OptionsValidator.validateMode(v),
|
|
46
|
+
delimiter: (v) => OptionsValidator.validateString(v, 'opt.delimiter'),
|
|
47
|
+
pad: (v) => OptionsValidator.validateString(v, 'opt.pad'),
|
|
48
|
+
q: (v) => OptionsValidator.validateNumber(v, 'opt.q'),
|
|
49
|
+
match: (v) => OptionsValidator.validateNumber(v, 'opt.match'),
|
|
50
|
+
mismatch: (v) => OptionsValidator.validateNumber(v, 'opt.mismatch'),
|
|
51
|
+
gap: (v) => OptionsValidator.validateNumber(v, 'opt.gap')
|
|
52
|
+
};
|
|
53
|
+
static PHONETIC_OPT_MAP = {
|
|
54
|
+
map: (v) =>
|
|
55
|
+
OptionsValidator.validateString(v, 'processors.phonetic.opt.map'),
|
|
56
|
+
delimiter: (v) =>
|
|
57
|
+
OptionsValidator.validateString(v, 'processors.phonetic.opt.delimiter'),
|
|
58
|
+
length: (v) =>
|
|
59
|
+
OptionsValidator.validateNumber(v, 'processors.phonetic.opt.length'),
|
|
60
|
+
pad: (v) =>
|
|
61
|
+
OptionsValidator.validateString(v, 'processors.phonetic.opt.pad'),
|
|
62
|
+
dedupe: (v) =>
|
|
63
|
+
OptionsValidator.validateBoolean(v, 'processors.phonetic.opt.dedupe'),
|
|
64
|
+
fallback: (v) =>
|
|
65
|
+
OptionsValidator.validateString(v, 'processors.phonetic.opt.fallback')
|
|
66
|
+
};
|
|
67
|
+
static CMPSTR_OPT_MAP = {
|
|
68
|
+
raw: (v) => OptionsValidator.validateBoolean(v, 'raw'),
|
|
69
|
+
removeZero: (v) => OptionsValidator.validateBoolean(v, 'removeZero'),
|
|
70
|
+
safeEmpty: (v) => OptionsValidator.validateBoolean(v, 'safeEmpty'),
|
|
71
|
+
flags: (v) => OptionsValidator.validateFlags(v),
|
|
72
|
+
metric: (v) => OptionsValidator.validateMetricName(v),
|
|
73
|
+
output: (v) => OptionsValidator.validateOutput(v),
|
|
74
|
+
opt: (v) => OptionsValidator.validateMetricOptions(v),
|
|
75
|
+
processors: (v) => OptionsValidator.validateProcessors(v),
|
|
76
|
+
sort: (v) => OptionsValidator.validateSort(v, 'sort'),
|
|
77
|
+
objectsOnly: (v) => OptionsValidator.validateBoolean(v, 'objectsOnly')
|
|
78
|
+
};
|
|
79
|
+
static set2string(set) {
|
|
80
|
+
return Array.from(set).join(' | ');
|
|
81
|
+
}
|
|
82
|
+
static validateType(value, name, type) {
|
|
83
|
+
if (value === undefined) return;
|
|
84
|
+
if (typeof value !== type || (type === 'number' && Number.isNaN(value))) {
|
|
85
|
+
throw new CmpStrValidationError(
|
|
86
|
+
`Invalid option <${name}>: expected ${type}`,
|
|
87
|
+
{ name, value }
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
static validateEnum(value, name, set) {
|
|
92
|
+
if (value === undefined) return;
|
|
93
|
+
if (typeof value !== 'string' || !set.has(value)) {
|
|
94
|
+
throw new CmpStrValidationError(
|
|
95
|
+
`Invalid option <${name}>: expected ${OptionsValidator.set2string(set)}`,
|
|
96
|
+
{ name, value }
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
static validateMap(opt, map) {
|
|
101
|
+
if (!opt) return;
|
|
102
|
+
for (const k in opt) {
|
|
103
|
+
const fn = map[k];
|
|
104
|
+
if (!fn)
|
|
105
|
+
throw new CmpStrValidationError(`Invalid option <${k}>`, {
|
|
106
|
+
option: k,
|
|
107
|
+
value: map[k]
|
|
108
|
+
});
|
|
109
|
+
fn(opt[k]);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
static validateRegistryName(value, name, label, has, list) {
|
|
113
|
+
if (value === undefined) return;
|
|
114
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
115
|
+
throw new CmpStrValidationError(
|
|
116
|
+
`Invalid option <${name}>: expected non-empty string`,
|
|
117
|
+
{ name, value }
|
|
118
|
+
);
|
|
119
|
+
if (!has(value))
|
|
120
|
+
throw new CmpStrValidationError(`${label} <${value}> is not registered`, {
|
|
121
|
+
name,
|
|
122
|
+
value,
|
|
123
|
+
available: list()
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
static validateBoolean(value, name) {
|
|
127
|
+
OptionsValidator.validateType(value, name, 'boolean');
|
|
128
|
+
}
|
|
129
|
+
static validateNumber(value, name) {
|
|
130
|
+
OptionsValidator.validateType(value, name, 'number');
|
|
131
|
+
}
|
|
132
|
+
static validateString(value, name) {
|
|
133
|
+
OptionsValidator.validateType(value, name, 'string');
|
|
134
|
+
}
|
|
135
|
+
static validateFlags(value) {
|
|
136
|
+
if (value === undefined) return;
|
|
137
|
+
if (typeof value !== 'string')
|
|
138
|
+
throw new CmpStrValidationError(
|
|
139
|
+
`Invalid option <flags>: expected string`,
|
|
140
|
+
{ flags: value }
|
|
141
|
+
);
|
|
142
|
+
for (let i = 0; i < value.length; i++) {
|
|
143
|
+
const ch = value[i];
|
|
144
|
+
if (!OptionsValidator.ALLOWED_FLAGS.has(ch))
|
|
145
|
+
throw new CmpStrValidationError(
|
|
146
|
+
`Invalid normalization flag <${ch}> in <flags>: expected ${OptionsValidator.set2string(OptionsValidator.ALLOWED_FLAGS)}`,
|
|
147
|
+
{ flags: value, invalid: ch }
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
static validateOutput(value) {
|
|
152
|
+
OptionsValidator.validateEnum(
|
|
153
|
+
value,
|
|
154
|
+
'output',
|
|
155
|
+
OptionsValidator.ALLOWED_OUTPUT
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
static validateMode(value) {
|
|
159
|
+
OptionsValidator.validateEnum(
|
|
160
|
+
value,
|
|
161
|
+
'mode',
|
|
162
|
+
OptionsValidator.ALLOWED_MODES
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
static validateSort(value, name) {
|
|
166
|
+
if (value === undefined || typeof value === 'boolean') return;
|
|
167
|
+
OptionsValidator.validateEnum(value, name, OptionsValidator.ALLOWED_SORT);
|
|
168
|
+
}
|
|
169
|
+
static validateMetricName(value) {
|
|
170
|
+
OptionsValidator.validateRegistryName(
|
|
171
|
+
value,
|
|
172
|
+
'metric',
|
|
173
|
+
'Comparison metric',
|
|
174
|
+
MetricRegistry.has,
|
|
175
|
+
MetricRegistry.list
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
static validatePhoneticName(value) {
|
|
179
|
+
OptionsValidator.validateRegistryName(
|
|
180
|
+
value,
|
|
181
|
+
'phonetic',
|
|
182
|
+
'Phonetic algorithm',
|
|
183
|
+
PhoneticRegistry.has,
|
|
184
|
+
PhoneticRegistry.list
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
static validateMetricOptions(opt) {
|
|
188
|
+
OptionsValidator.validateMap(opt, OptionsValidator.METRIC_OPT_MAP);
|
|
189
|
+
}
|
|
190
|
+
static validatePhoneticOptions(opt) {
|
|
191
|
+
OptionsValidator.validateMap(opt, OptionsValidator.PHONETIC_OPT_MAP);
|
|
192
|
+
}
|
|
193
|
+
static validateProcessors(opt) {
|
|
194
|
+
if (!opt) return;
|
|
195
|
+
for (const key in opt) {
|
|
196
|
+
const fn = OptionsValidator.PROCESSORS[key];
|
|
197
|
+
if (!fn)
|
|
198
|
+
throw new CmpStrValidationError(
|
|
199
|
+
`Invalid processor type <${key}> in <processors>: expected ${Object.keys(OptionsValidator.PROCESSORS).join(' | ')}`,
|
|
200
|
+
{ processors: opt, invalid: key }
|
|
201
|
+
);
|
|
202
|
+
fn(opt[key]);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
static validateOptions(opt) {
|
|
206
|
+
OptionsValidator.validateMap(opt, OptionsValidator.CMPSTR_OPT_MAP);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export { OptionsValidator };
|
package/dist/esm/utils/Pool.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
// CmpStr v3.
|
|
1
|
+
// CmpStr v3.3.0 build-3699f85-260318 by Paul Köhler @komed3 / MIT License
|
|
2
|
+
import { CmpStrUsageError, ErrorUtil } from './Errors.mjs';
|
|
3
|
+
|
|
2
4
|
class RingPool {
|
|
3
5
|
maxSize;
|
|
4
6
|
buffers = [];
|
|
@@ -7,22 +9,39 @@ class RingPool {
|
|
|
7
9
|
this.maxSize = maxSize;
|
|
8
10
|
}
|
|
9
11
|
acquire(minSize, allowOversize) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
return ErrorUtil.wrap(
|
|
13
|
+
() => {
|
|
14
|
+
const buffers = this.buffers;
|
|
15
|
+
const len = buffers.length;
|
|
16
|
+
for (let i = 0; i < len; i++) {
|
|
17
|
+
const idx = (this.pointer + i) % len;
|
|
18
|
+
const item = buffers[idx];
|
|
19
|
+
const size = item.size;
|
|
20
|
+
if (size >= minSize && (allowOversize || size === minSize)) {
|
|
21
|
+
this.pointer = (idx + 1) % len;
|
|
22
|
+
return item;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
},
|
|
27
|
+
`Failed to acquire buffer of size >= ${minSize} from pool`,
|
|
28
|
+
{ minSize, allowOversize }
|
|
29
|
+
);
|
|
20
30
|
}
|
|
21
31
|
release(item) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
ErrorUtil.wrap(
|
|
33
|
+
() => {
|
|
34
|
+
const buffers = this.buffers;
|
|
35
|
+
if (buffers.length < this.maxSize) {
|
|
36
|
+
buffers.push(item);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
buffers[this.pointer] = item;
|
|
40
|
+
this.pointer = (this.pointer + 1) % this.maxSize;
|
|
41
|
+
},
|
|
42
|
+
`Failed to release buffer back to pool`,
|
|
43
|
+
{ item }
|
|
44
|
+
);
|
|
26
45
|
}
|
|
27
46
|
clear() {
|
|
28
47
|
this.buffers = [];
|
|
@@ -37,6 +56,12 @@ class Pool {
|
|
|
37
56
|
maxItemSize: 2048,
|
|
38
57
|
allowOversize: true
|
|
39
58
|
},
|
|
59
|
+
'arr[]': {
|
|
60
|
+
type: 'arr[]',
|
|
61
|
+
maxSize: 4,
|
|
62
|
+
maxItemSize: 1024,
|
|
63
|
+
allowOversize: false
|
|
64
|
+
},
|
|
40
65
|
'number[]': {
|
|
41
66
|
type: 'number[]',
|
|
42
67
|
maxSize: 16,
|
|
@@ -54,6 +79,7 @@ class Pool {
|
|
|
54
79
|
};
|
|
55
80
|
static POOLS = {
|
|
56
81
|
int32: new RingPool(64),
|
|
82
|
+
'arr[]': new RingPool(4),
|
|
57
83
|
'number[]': new RingPool(16),
|
|
58
84
|
'string[]': new RingPool(2),
|
|
59
85
|
set: new RingPool(8),
|
|
@@ -63,6 +89,8 @@ class Pool {
|
|
|
63
89
|
switch (type) {
|
|
64
90
|
case 'int32':
|
|
65
91
|
return new Int32Array(size);
|
|
92
|
+
case 'arr[]':
|
|
93
|
+
return new Array(size);
|
|
66
94
|
case 'number[]':
|
|
67
95
|
return new Float64Array(size);
|
|
68
96
|
case 'string[]':
|
|
@@ -75,6 +103,8 @@ class Pool {
|
|
|
75
103
|
}
|
|
76
104
|
static acquire(type, size) {
|
|
77
105
|
const CONFIG = this.CONFIG[type];
|
|
106
|
+
if (!CONFIG)
|
|
107
|
+
throw new CmpStrUsageError(`Unsupported pool type <${type}>`, { type });
|
|
78
108
|
if (size > CONFIG.maxItemSize) return this.allocate(type, size);
|
|
79
109
|
const item = this.POOLS[type].acquire(size, CONFIG.allowOversize);
|
|
80
110
|
if (item)
|
|
@@ -82,13 +112,17 @@ class Pool {
|
|
|
82
112
|
return this.allocate(type, size);
|
|
83
113
|
}
|
|
84
114
|
static acquireMany(type, sizes) {
|
|
85
|
-
|
|
115
|
+
const out = new Array(sizes.length);
|
|
116
|
+
for (let i = 0; i < sizes.length; i++)
|
|
117
|
+
out[i] = this.acquire(type, sizes[i]);
|
|
118
|
+
return out;
|
|
86
119
|
}
|
|
87
120
|
static release(type, buffer, size) {
|
|
88
|
-
|
|
89
|
-
|
|
121
|
+
const CONFIG = this.CONFIG[type];
|
|
122
|
+
if (!CONFIG)
|
|
123
|
+
throw new CmpStrUsageError(`Unsupported pool type <${type}>`, { type });
|
|
124
|
+
if (size <= CONFIG.maxItemSize) this.POOLS[type].release({ buffer, size });
|
|
90
125
|
}
|
|
91
126
|
}
|
|
92
127
|
|
|
93
128
|
export { Pool };
|
|
94
|
-
//# sourceMappingURL=Pool.mjs.map
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
// CmpStr v3.
|
|
1
|
+
// CmpStr v3.3.0 build-3699f85-260318 by Paul Köhler @komed3 / MIT License
|
|
2
2
|
class Profiler {
|
|
3
3
|
active;
|
|
4
4
|
static ENV;
|
|
5
5
|
static instance;
|
|
6
6
|
nowFn;
|
|
7
7
|
memFn;
|
|
8
|
-
store =
|
|
8
|
+
store = [];
|
|
9
|
+
last;
|
|
9
10
|
totalTime = 0;
|
|
10
11
|
totalMem = 0;
|
|
11
12
|
static detectEnv() {
|
|
12
|
-
if (typeof process !== 'undefined'
|
|
13
|
+
if (typeof process !== 'undefined' && process.versions?.node)
|
|
14
|
+
Profiler.ENV = 'nodejs';
|
|
13
15
|
else if (typeof performance !== 'undefined') Profiler.ENV = 'browser';
|
|
14
16
|
else Profiler.ENV = 'unknown';
|
|
15
17
|
}
|
|
@@ -21,7 +23,7 @@ class Profiler {
|
|
|
21
23
|
this.active = active;
|
|
22
24
|
switch (Profiler.ENV) {
|
|
23
25
|
case 'nodejs':
|
|
24
|
-
this.nowFn = () => Number(process.hrtime.bigint())
|
|
26
|
+
this.nowFn = () => Number(process.hrtime.bigint()) * 1e-6;
|
|
25
27
|
this.memFn = () => process.memoryUsage().heapUsed;
|
|
26
28
|
break;
|
|
27
29
|
case 'browser':
|
|
@@ -34,40 +36,52 @@ class Profiler {
|
|
|
34
36
|
break;
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
startMem = this.mem();
|
|
42
|
-
const res = fn();
|
|
43
|
-
const deltaTime = this.now() - startTime,
|
|
44
|
-
deltaMem = this.mem() - startMem;
|
|
45
|
-
this.store.add({ time: deltaTime, mem: deltaMem, res, meta });
|
|
46
|
-
((this.totalTime += deltaTime), (this.totalMem += deltaMem));
|
|
47
|
-
return res;
|
|
39
|
+
storeRes(entry) {
|
|
40
|
+
this.store.push((this.last = entry));
|
|
41
|
+
this.totalTime += entry.time;
|
|
42
|
+
this.totalMem += entry.mem;
|
|
48
43
|
}
|
|
49
|
-
enable
|
|
44
|
+
enable() {
|
|
50
45
|
this.active = true;
|
|
51
|
-
}
|
|
52
|
-
disable
|
|
46
|
+
}
|
|
47
|
+
disable() {
|
|
53
48
|
this.active = false;
|
|
54
|
-
}
|
|
49
|
+
}
|
|
55
50
|
clear() {
|
|
56
|
-
this.store.
|
|
51
|
+
this.store.length = 0;
|
|
52
|
+
this.last = undefined;
|
|
57
53
|
this.totalTime = 0;
|
|
58
54
|
this.totalMem = 0;
|
|
59
55
|
}
|
|
60
56
|
run(fn, meta = {}) {
|
|
61
|
-
|
|
57
|
+
if (!this.active) return fn();
|
|
58
|
+
const startTime = this.nowFn(),
|
|
59
|
+
startMem = this.memFn();
|
|
60
|
+
const res = fn();
|
|
61
|
+
const deltaTime = this.nowFn() - startTime,
|
|
62
|
+
deltaMem = this.memFn() - startMem;
|
|
63
|
+
this.storeRes({ time: deltaTime, mem: deltaMem, res, meta });
|
|
64
|
+
return res;
|
|
62
65
|
}
|
|
63
66
|
async runAsync(fn, meta = {}) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
if (!this.active) return fn();
|
|
68
|
+
const startTime = this.nowFn(),
|
|
69
|
+
startMem = this.memFn();
|
|
70
|
+
const res = await fn();
|
|
71
|
+
const deltaTime = this.nowFn() - startTime,
|
|
72
|
+
deltaMem = this.memFn() - startMem;
|
|
73
|
+
this.storeRes({ time: deltaTime, mem: deltaMem, res, meta });
|
|
74
|
+
return res;
|
|
75
|
+
}
|
|
76
|
+
getAll() {
|
|
77
|
+
return [...this.store];
|
|
78
|
+
}
|
|
79
|
+
getLast() {
|
|
80
|
+
return this.last;
|
|
81
|
+
}
|
|
82
|
+
getTotal() {
|
|
83
|
+
return { time: this.totalTime, mem: this.totalMem };
|
|
67
84
|
}
|
|
68
|
-
getAll = () => [...this.store];
|
|
69
|
-
getLast = () => this.getAll().pop();
|
|
70
|
-
getTotal = () => ({ time: this.totalTime, mem: this.totalMem });
|
|
71
85
|
services = Object.freeze({
|
|
72
86
|
enable: this.enable.bind(this),
|
|
73
87
|
disable: this.disable.bind(this),
|
|
@@ -79,4 +93,3 @@ class Profiler {
|
|
|
79
93
|
}
|
|
80
94
|
|
|
81
95
|
export { Profiler };
|
|
82
|
-
//# sourceMappingURL=Profiler.mjs.map
|
|
@@ -1,20 +1,37 @@
|
|
|
1
|
-
// CmpStr v3.
|
|
1
|
+
// CmpStr v3.3.0 build-3699f85-260318 by Paul Köhler @komed3 / MIT License
|
|
2
|
+
import { ErrorUtil, CmpStrNotFoundError } from './Errors.mjs';
|
|
3
|
+
|
|
2
4
|
const registry = Object.create(null);
|
|
3
5
|
const factory = Object.create(null);
|
|
4
6
|
function Registry(reg, ctor) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
ErrorUtil.assert(
|
|
8
|
+
!(reg in registry || reg in factory),
|
|
9
|
+
`Registry <${reg}> already exists / overwriting is forbidden`,
|
|
10
|
+
{ registry: reg }
|
|
11
|
+
);
|
|
9
12
|
const classes = Object.create(null);
|
|
10
13
|
const service = Object.freeze({
|
|
11
14
|
add(name, cls, update = false) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
ErrorUtil.assert(
|
|
16
|
+
typeof name === 'string' && name.length > 0,
|
|
17
|
+
`Class name must be a non-empty string`,
|
|
18
|
+
{ registry: reg, name }
|
|
19
|
+
);
|
|
20
|
+
ErrorUtil.assert(
|
|
21
|
+
typeof cls === 'function',
|
|
22
|
+
`Class must be a constructor function`,
|
|
23
|
+
{ registry: reg, class: cls }
|
|
24
|
+
);
|
|
25
|
+
ErrorUtil.assert(
|
|
26
|
+
cls.prototype instanceof ctor,
|
|
27
|
+
`Class must extend <${reg}>`,
|
|
28
|
+
{ registry: reg, class: cls }
|
|
29
|
+
);
|
|
30
|
+
ErrorUtil.assert(
|
|
31
|
+
update || !(name in classes),
|
|
32
|
+
`Class <${name}> already exists / use <update=true> to overwrite`,
|
|
33
|
+
{ registry: reg, name }
|
|
34
|
+
);
|
|
18
35
|
classes[name] = cls;
|
|
19
36
|
},
|
|
20
37
|
remove(name) {
|
|
@@ -27,8 +44,16 @@ function Registry(reg, ctor) {
|
|
|
27
44
|
return Object.keys(classes);
|
|
28
45
|
},
|
|
29
46
|
get(name) {
|
|
30
|
-
|
|
31
|
-
|
|
47
|
+
ErrorUtil.assert(
|
|
48
|
+
typeof name === 'string' && name.length > 0,
|
|
49
|
+
`Class name must be a non-empty string`,
|
|
50
|
+
{ registry: reg, name }
|
|
51
|
+
);
|
|
52
|
+
ErrorUtil.assert(
|
|
53
|
+
name in classes,
|
|
54
|
+
`Class <${name}> not registered for <${reg}>`,
|
|
55
|
+
{ registry: reg, name }
|
|
56
|
+
);
|
|
32
57
|
return classes[name];
|
|
33
58
|
}
|
|
34
59
|
});
|
|
@@ -38,19 +63,18 @@ function Registry(reg, ctor) {
|
|
|
38
63
|
}
|
|
39
64
|
function resolveCls(reg, cls) {
|
|
40
65
|
if (!(reg in registry))
|
|
41
|
-
throw new
|
|
42
|
-
|
|
66
|
+
throw new CmpStrNotFoundError(`Registry <${reg}> does not exist`, {
|
|
67
|
+
registry: reg
|
|
68
|
+
});
|
|
69
|
+
return typeof cls === 'string' ? registry[reg].get(cls) : cls;
|
|
43
70
|
}
|
|
44
71
|
function createFromRegistry(reg, cls, ...args) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
});
|
|
52
|
-
}
|
|
72
|
+
const ctor = resolveCls(reg, cls);
|
|
73
|
+
return ErrorUtil.wrap(
|
|
74
|
+
() => new ctor(...args),
|
|
75
|
+
`Failed to create instance of class <${ctor.name ?? cls}> from registry <${reg}>`,
|
|
76
|
+
{ registry: reg, class: cls, args }
|
|
77
|
+
);
|
|
53
78
|
}
|
|
54
79
|
|
|
55
80
|
export { Registry, createFromRegistry, factory, registry, resolveCls };
|
|
56
|
-
//# sourceMappingURL=Registry.mjs.map
|