@n8n/utils 1.37.2 → 1.38.1
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/dist/assert.cjs +11 -2
- package/dist/assert.cjs.map +1 -0
- package/dist/assert.d.cts +5 -2
- package/dist/assert.d.mts +5 -2
- package/dist/assert.mjs +10 -2
- package/dist/assert.mjs.map +1 -0
- package/dist/client-assertion.cjs +49 -0
- package/dist/client-assertion.cjs.map +1 -0
- package/dist/client-assertion.d.cts +12 -0
- package/dist/client-assertion.d.mts +12 -0
- package/dist/client-assertion.mjs +48 -0
- package/dist/client-assertion.mjs.map +1 -0
- package/dist/create-event-queue.cjs +40 -2
- package/dist/create-event-queue.cjs.map +1 -0
- package/dist/create-event-queue.d.cts +7 -2
- package/dist/create-event-queue.d.mts +7 -2
- package/dist/create-event-queue.mjs +39 -2
- package/dist/create-event-queue.mjs.map +1 -0
- package/dist/ensure-error.cjs +15 -0
- package/dist/ensure-error.cjs.map +1 -0
- package/dist/ensure-error.mjs +9 -0
- package/dist/ensure-error.mjs.map +1 -0
- package/dist/errors/ensure-error.cjs +3 -0
- package/dist/errors/ensure-error.d.cts +5 -0
- package/dist/errors/ensure-error.d.mts +5 -0
- package/dist/errors/ensure-error.mjs +3 -0
- package/dist/event-bus.cjs +43 -2
- package/dist/event-bus.cjs.map +1 -0
- package/dist/event-bus.d.cts +14 -2
- package/dist/event-bus.d.mts +14 -2
- package/dist/event-bus.mjs +42 -2
- package/dist/event-bus.mjs.map +1 -0
- package/dist/files/is-windows-file-path.cjs +11 -2
- package/dist/files/is-windows-file-path.cjs.map +1 -0
- package/dist/files/is-windows-file-path.d.cts +5 -2
- package/dist/files/is-windows-file-path.d.mts +5 -2
- package/dist/files/is-windows-file-path.mjs +10 -2
- package/dist/files/is-windows-file-path.mjs.map +1 -0
- package/dist/files/sanitize-filename.cjs +61 -2
- package/dist/files/sanitize-filename.cjs.map +1 -0
- package/dist/files/sanitize-filename.d.cts +5 -2
- package/dist/files/sanitize-filename.d.mts +5 -2
- package/dist/files/sanitize-filename.mjs +60 -2
- package/dist/files/sanitize-filename.mjs.map +1 -0
- package/dist/format-pem-block.d.cts +5 -2
- package/dist/format-pem-block.d.mts +5 -2
- package/dist/generate-nano-id.cjs +28 -2
- package/dist/generate-nano-id.cjs.map +1 -0
- package/dist/generate-nano-id.d.cts +5 -2
- package/dist/generate-nano-id.d.mts +5 -2
- package/dist/generate-nano-id.mjs +28 -2
- package/dist/generate-nano-id.mjs.map +1 -0
- package/dist/get-jwt-expiry.cjs +22 -2
- package/dist/get-jwt-expiry.cjs.map +1 -0
- package/dist/get-jwt-expiry.d.cts +5 -2
- package/dist/get-jwt-expiry.d.mts +5 -2
- package/dist/get-jwt-expiry.mjs +21 -2
- package/dist/get-jwt-expiry.mjs.map +1 -0
- package/dist/is-record.cjs +8 -2
- package/dist/is-record.cjs.map +1 -0
- package/dist/is-record.d.cts +5 -2
- package/dist/is-record.d.mts +5 -2
- package/dist/is-record.mjs +7 -2
- package/dist/is-record.mjs.map +1 -0
- package/dist/number/smart-decimal.cjs +10 -2
- package/dist/number/smart-decimal.cjs.map +1 -0
- package/dist/number/smart-decimal.d.cts +5 -2
- package/dist/number/smart-decimal.d.mts +5 -2
- package/dist/number/smart-decimal.mjs +9 -2
- package/dist/number/smart-decimal.mjs.map +1 -0
- package/dist/placeholder.cjs +80 -7
- package/dist/placeholder.cjs.map +1 -0
- package/dist/placeholder.d.cts +14 -2
- package/dist/placeholder.d.mts +14 -2
- package/dist/placeholder.mjs +74 -2
- package/dist/placeholder.mjs.map +1 -0
- package/dist/promise/deferred-promise.cjs +14 -0
- package/dist/promise/deferred-promise.cjs.map +1 -0
- package/dist/promise/deferred-promise.d.cts +12 -0
- package/dist/promise/deferred-promise.d.mts +12 -0
- package/dist/promise/deferred-promise.mjs +13 -0
- package/dist/promise/deferred-promise.mjs.map +1 -0
- package/dist/result.cjs +30 -0
- package/dist/result.cjs.map +1 -0
- package/dist/result.d.cts +16 -0
- package/dist/result.d.mts +16 -0
- package/dist/result.mjs +28 -0
- package/dist/result.mjs.map +1 -0
- package/dist/retry.cjs +39 -2
- package/dist/retry.cjs.map +1 -0
- package/dist/retry.d.cts +6 -2
- package/dist/retry.d.mts +6 -2
- package/dist/retry.mjs +38 -2
- package/dist/retry.mjs.map +1 -0
- package/dist/scrub-secrets.cjs +44 -4
- package/dist/scrub-secrets.cjs.map +1 -0
- package/dist/scrub-secrets.d.cts +7 -2
- package/dist/scrub-secrets.d.mts +7 -2
- package/dist/scrub-secrets.mjs +41 -2
- package/dist/scrub-secrets.mjs.map +1 -0
- package/dist/search/re-rank-search-results.cjs +19 -2
- package/dist/search/re-rank-search-results.cjs.map +1 -0
- package/dist/search/re-rank-search-results.d.cts +13 -2
- package/dist/search/re-rank-search-results.d.mts +13 -2
- package/dist/search/re-rank-search-results.mjs +18 -2
- package/dist/search/re-rank-search-results.mjs.map +1 -0
- package/dist/search/sublime-search.cjs +182 -3
- package/dist/search/sublime-search.cjs.map +1 -0
- package/dist/search/sublime-search.d.cts +15 -2
- package/dist/search/sublime-search.d.mts +15 -2
- package/dist/search/sublime-search.mjs +180 -2
- package/dist/search/sublime-search.mjs.map +1 -0
- package/dist/sort/sort-by-property.cjs +12 -2
- package/dist/sort/sort-by-property.cjs.map +1 -0
- package/dist/sort/sort-by-property.d.cts +5 -2
- package/dist/sort/sort-by-property.d.mts +5 -2
- package/dist/sort/sort-by-property.mjs +11 -2
- package/dist/sort/sort-by-property.mjs.map +1 -0
- package/dist/string/truncate.cjs +32 -3
- package/dist/string/truncate.cjs.map +1 -0
- package/dist/string/truncate.d.cts +6 -2
- package/dist/string/truncate.d.mts +6 -2
- package/dist/string/truncate.mjs +30 -2
- package/dist/string/truncate.mjs.map +1 -0
- package/dist/types.d.cts +5 -2
- package/dist/types.d.mts +5 -2
- package/package.json +11 -12
- package/dist/assert2.cjs +0 -17
- package/dist/assert2.cjs.map +0 -1
- package/dist/assert2.d.cts +0 -5
- package/dist/assert2.d.mts +0 -5
- package/dist/assert2.mjs +0 -11
- package/dist/assert2.mjs.map +0 -1
- package/dist/create-event-queue2.cjs +0 -46
- package/dist/create-event-queue2.cjs.map +0 -1
- package/dist/create-event-queue2.d.cts +0 -7
- package/dist/create-event-queue2.d.mts +0 -7
- package/dist/create-event-queue2.mjs +0 -40
- package/dist/create-event-queue2.mjs.map +0 -1
- package/dist/event-bus2.cjs +0 -49
- package/dist/event-bus2.cjs.map +0 -1
- package/dist/event-bus2.d.cts +0 -14
- package/dist/event-bus2.d.mts +0 -14
- package/dist/event-bus2.mjs +0 -43
- package/dist/event-bus2.mjs.map +0 -1
- package/dist/format-pem-block2.d.cts +0 -5
- package/dist/format-pem-block2.d.mts +0 -5
- package/dist/generate-nano-id2.cjs +0 -34
- package/dist/generate-nano-id2.cjs.map +0 -1
- package/dist/generate-nano-id2.d.cts +0 -5
- package/dist/generate-nano-id2.d.mts +0 -5
- package/dist/generate-nano-id2.mjs +0 -29
- package/dist/generate-nano-id2.mjs.map +0 -1
- package/dist/get-jwt-expiry2.cjs +0 -28
- package/dist/get-jwt-expiry2.cjs.map +0 -1
- package/dist/get-jwt-expiry2.d.cts +0 -5
- package/dist/get-jwt-expiry2.d.mts +0 -5
- package/dist/get-jwt-expiry2.mjs +0 -22
- package/dist/get-jwt-expiry2.mjs.map +0 -1
- package/dist/index.cjs +0 -44
- package/dist/index.d.cts +0 -19
- package/dist/index.d.mts +0 -19
- package/dist/index.mjs +0 -19
- package/dist/is-record2.cjs +0 -14
- package/dist/is-record2.cjs.map +0 -1
- package/dist/is-record2.d.cts +0 -5
- package/dist/is-record2.d.mts +0 -5
- package/dist/is-record2.mjs +0 -8
- package/dist/is-record2.mjs.map +0 -1
- package/dist/is-windows-file-path.cjs +0 -17
- package/dist/is-windows-file-path.cjs.map +0 -1
- package/dist/is-windows-file-path.d.cts +0 -5
- package/dist/is-windows-file-path.d.mts +0 -5
- package/dist/is-windows-file-path.mjs +0 -11
- package/dist/is-windows-file-path.mjs.map +0 -1
- package/dist/placeholder2.cjs +0 -111
- package/dist/placeholder2.cjs.map +0 -1
- package/dist/placeholder2.d.cts +0 -14
- package/dist/placeholder2.d.mts +0 -14
- package/dist/placeholder2.mjs +0 -75
- package/dist/placeholder2.mjs.map +0 -1
- package/dist/re-rank-search-results.cjs +0 -25
- package/dist/re-rank-search-results.cjs.map +0 -1
- package/dist/re-rank-search-results.d.cts +0 -13
- package/dist/re-rank-search-results.d.mts +0 -13
- package/dist/re-rank-search-results.mjs +0 -19
- package/dist/re-rank-search-results.mjs.map +0 -1
- package/dist/retry2.cjs +0 -45
- package/dist/retry2.cjs.map +0 -1
- package/dist/retry2.d.cts +0 -6
- package/dist/retry2.d.mts +0 -6
- package/dist/retry2.mjs +0 -39
- package/dist/retry2.mjs.map +0 -1
- package/dist/sanitize-filename.cjs +0 -67
- package/dist/sanitize-filename.cjs.map +0 -1
- package/dist/sanitize-filename.d.cts +0 -5
- package/dist/sanitize-filename.d.mts +0 -5
- package/dist/sanitize-filename.mjs +0 -61
- package/dist/sanitize-filename.mjs.map +0 -1
- package/dist/scrub-secrets2.cjs +0 -59
- package/dist/scrub-secrets2.cjs.map +0 -1
- package/dist/scrub-secrets2.d.cts +0 -7
- package/dist/scrub-secrets2.d.mts +0 -7
- package/dist/scrub-secrets2.mjs +0 -41
- package/dist/scrub-secrets2.mjs.map +0 -1
- package/dist/smart-decimal.cjs +0 -16
- package/dist/smart-decimal.cjs.map +0 -1
- package/dist/smart-decimal.d.cts +0 -5
- package/dist/smart-decimal.d.mts +0 -5
- package/dist/smart-decimal.mjs +0 -10
- package/dist/smart-decimal.mjs.map +0 -1
- package/dist/sort-by-property.cjs +0 -18
- package/dist/sort-by-property.cjs.map +0 -1
- package/dist/sort-by-property.d.cts +0 -5
- package/dist/sort-by-property.d.mts +0 -5
- package/dist/sort-by-property.mjs +0 -12
- package/dist/sort-by-property.mjs.map +0 -1
- package/dist/sublime-search.cjs +0 -193
- package/dist/sublime-search.cjs.map +0 -1
- package/dist/sublime-search.d.cts +0 -15
- package/dist/sublime-search.d.mts +0 -15
- package/dist/sublime-search.mjs +0 -181
- package/dist/sublime-search.mjs.map +0 -1
- package/dist/truncate.cjs +0 -43
- package/dist/truncate.cjs.map +0 -1
- package/dist/truncate.d.cts +0 -6
- package/dist/truncate.d.mts +0 -6
- package/dist/truncate.mjs +0 -31
- package/dist/truncate.mjs.map +0 -1
- package/dist/types2.d.cts +0 -5
- package/dist/types2.d.mts +0 -5
|
@@ -1,4 +1,183 @@
|
|
|
1
|
-
const require_sublime_search = require('../sublime-search.cjs');
|
|
2
1
|
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
//#region src/search/sublime-search.ts
|
|
3
|
+
const SEQUENTIAL_BONUS = 60;
|
|
4
|
+
const SEPARATOR_BONUS = 38;
|
|
5
|
+
const CAMEL_BONUS = 30;
|
|
6
|
+
const FIRST_LETTER_BONUS = 15;
|
|
7
|
+
const LEADING_LETTER_PENALTY = -20;
|
|
8
|
+
const MAX_LEADING_LETTER_PENALTY = -200;
|
|
9
|
+
const UNMATCHED_LETTER_PENALTY = -2.5;
|
|
10
|
+
const DEFAULT_KEYS = [{
|
|
11
|
+
key: "properties.displayName",
|
|
12
|
+
weight: 1.3
|
|
13
|
+
}, {
|
|
14
|
+
key: "properties.codex.alias",
|
|
15
|
+
weight: 1
|
|
16
|
+
}];
|
|
17
|
+
/**
|
|
18
|
+
* Returns true if each character in pattern is found sequentially within target
|
|
19
|
+
* @param {*} pattern string
|
|
20
|
+
* @param {*} target string
|
|
21
|
+
*/
|
|
22
|
+
function fuzzyMatchSimple(pattern, target) {
|
|
23
|
+
let patternIdx = 0;
|
|
24
|
+
let strIdx = 0;
|
|
25
|
+
while (patternIdx < pattern.length && strIdx < target.length) {
|
|
26
|
+
if (pattern.charAt(patternIdx).toLowerCase() === target.charAt(strIdx).toLowerCase()) patternIdx++;
|
|
27
|
+
++strIdx;
|
|
28
|
+
}
|
|
29
|
+
return pattern.length !== 0 && target.length !== 0 && patternIdx === pattern.length;
|
|
30
|
+
}
|
|
31
|
+
function fuzzyMatchRecursive(pattern, target, patternCurIndex, targetCurrIndex, targetMatches, matches, maxMatches, nextMatch, recursionCount, recursionLimit) {
|
|
32
|
+
let outScore = 0;
|
|
33
|
+
if (++recursionCount >= recursionLimit) return {
|
|
34
|
+
matched: false,
|
|
35
|
+
outScore
|
|
36
|
+
};
|
|
37
|
+
if (patternCurIndex === pattern.length || targetCurrIndex === target.length) return {
|
|
38
|
+
matched: false,
|
|
39
|
+
outScore
|
|
40
|
+
};
|
|
41
|
+
let recursiveMatch = false;
|
|
42
|
+
let bestRecursiveMatches = [];
|
|
43
|
+
let bestRecursiveScore = 0;
|
|
44
|
+
let firstMatch = true;
|
|
45
|
+
while (patternCurIndex < pattern.length && targetCurrIndex < target.length) {
|
|
46
|
+
if (pattern[patternCurIndex].toLowerCase() === target[targetCurrIndex].toLowerCase()) {
|
|
47
|
+
if (nextMatch >= maxMatches) return {
|
|
48
|
+
matched: false,
|
|
49
|
+
outScore
|
|
50
|
+
};
|
|
51
|
+
if (firstMatch && targetMatches) {
|
|
52
|
+
matches = [...targetMatches];
|
|
53
|
+
firstMatch = false;
|
|
54
|
+
}
|
|
55
|
+
const recursiveMatches = [];
|
|
56
|
+
const recursiveResult = fuzzyMatchRecursive(pattern, target, patternCurIndex, targetCurrIndex + 1, matches, recursiveMatches, maxMatches, nextMatch, recursionCount, recursionLimit);
|
|
57
|
+
const recursiveScore = recursiveResult.outScore;
|
|
58
|
+
if (recursiveResult.matched) {
|
|
59
|
+
if (!recursiveMatch || recursiveScore > bestRecursiveScore) {
|
|
60
|
+
bestRecursiveMatches = [...recursiveMatches];
|
|
61
|
+
bestRecursiveScore = recursiveScore;
|
|
62
|
+
}
|
|
63
|
+
recursiveMatch = true;
|
|
64
|
+
}
|
|
65
|
+
matches[nextMatch++] = targetCurrIndex;
|
|
66
|
+
++patternCurIndex;
|
|
67
|
+
}
|
|
68
|
+
++targetCurrIndex;
|
|
69
|
+
}
|
|
70
|
+
const matched = patternCurIndex === pattern.length;
|
|
71
|
+
if (matched) {
|
|
72
|
+
outScore = 100;
|
|
73
|
+
if (!target.toLowerCase().startsWith("n8n")) {
|
|
74
|
+
let penalty = LEADING_LETTER_PENALTY * matches[0];
|
|
75
|
+
penalty = penalty < MAX_LEADING_LETTER_PENALTY ? MAX_LEADING_LETTER_PENALTY : penalty;
|
|
76
|
+
outScore += penalty;
|
|
77
|
+
}
|
|
78
|
+
const unmatched = target.length - nextMatch;
|
|
79
|
+
outScore += UNMATCHED_LETTER_PENALTY * unmatched;
|
|
80
|
+
for (let i = 0; i < nextMatch; i++) {
|
|
81
|
+
const currIdx = matches[i];
|
|
82
|
+
if (i > 0) {
|
|
83
|
+
if (currIdx === matches[i - 1] + 1) outScore += SEQUENTIAL_BONUS;
|
|
84
|
+
}
|
|
85
|
+
if (currIdx > 0) {
|
|
86
|
+
const neighbor = target[currIdx - 1];
|
|
87
|
+
const curr = target[currIdx];
|
|
88
|
+
if (neighbor !== neighbor.toUpperCase() && curr !== curr.toLowerCase()) outScore += CAMEL_BONUS;
|
|
89
|
+
if (neighbor === "_" || neighbor === " ") outScore += SEPARATOR_BONUS;
|
|
90
|
+
} else outScore += FIRST_LETTER_BONUS;
|
|
91
|
+
}
|
|
92
|
+
if (recursiveMatch && (!matched || bestRecursiveScore > outScore)) {
|
|
93
|
+
matches = [...bestRecursiveMatches];
|
|
94
|
+
outScore = bestRecursiveScore;
|
|
95
|
+
return {
|
|
96
|
+
matched: true,
|
|
97
|
+
outScore
|
|
98
|
+
};
|
|
99
|
+
} else if (matched) return {
|
|
100
|
+
matched: true,
|
|
101
|
+
outScore
|
|
102
|
+
};
|
|
103
|
+
else return {
|
|
104
|
+
matched: false,
|
|
105
|
+
outScore
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
matched: false,
|
|
110
|
+
outScore
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Does a fuzzy search to find pattern inside a string.
|
|
115
|
+
* @param {*} pattern string pattern to search for
|
|
116
|
+
* @param {*} target string string which is being searched
|
|
117
|
+
* @returns [boolean, number] a boolean which tells if pattern was
|
|
118
|
+
* found or not and a search score
|
|
119
|
+
*/
|
|
120
|
+
function fuzzyMatch(pattern, target) {
|
|
121
|
+
return fuzzyMatchRecursive(pattern, target, 0, 0, null, [], 256, 0, 0, 5);
|
|
122
|
+
}
|
|
123
|
+
function getValue(obj, prop) {
|
|
124
|
+
if (obj.hasOwnProperty(prop)) return obj[prop];
|
|
125
|
+
const segments = prop.split(".");
|
|
126
|
+
let result = obj;
|
|
127
|
+
let i = 0;
|
|
128
|
+
while (result && i < segments.length) {
|
|
129
|
+
const key = segments[i];
|
|
130
|
+
result = result[key];
|
|
131
|
+
i++;
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
function scoreSearchValue(filter, value, weight) {
|
|
136
|
+
if (!fuzzyMatchSimple(filter, value)) return void 0;
|
|
137
|
+
const match = fuzzyMatch(filter, value);
|
|
138
|
+
if (!match.matched) return void 0;
|
|
139
|
+
return match.outScore * weight;
|
|
140
|
+
}
|
|
141
|
+
function sublimeSearch(filter, data, keys = DEFAULT_KEYS, limit) {
|
|
142
|
+
const results = [];
|
|
143
|
+
for (const item of data) {
|
|
144
|
+
let itemMatchScore;
|
|
145
|
+
for (const { key, weight } of keys) {
|
|
146
|
+
const value = getValue(item, key);
|
|
147
|
+
if (Array.isArray(value)) for (const entry of value) {
|
|
148
|
+
if (typeof entry !== "string") continue;
|
|
149
|
+
const score = scoreSearchValue(filter, entry, weight);
|
|
150
|
+
if (score !== void 0 && (itemMatchScore === void 0 || score > itemMatchScore)) itemMatchScore = score;
|
|
151
|
+
}
|
|
152
|
+
else if (typeof value === "string") {
|
|
153
|
+
const score = scoreSearchValue(filter, value, weight);
|
|
154
|
+
if (score !== void 0 && (itemMatchScore === void 0 || score > itemMatchScore)) itemMatchScore = score;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (itemMatchScore !== void 0) {
|
|
158
|
+
const result = {
|
|
159
|
+
score: itemMatchScore,
|
|
160
|
+
item
|
|
161
|
+
};
|
|
162
|
+
if (limit === void 0 || results.length < limit) results.push(result);
|
|
163
|
+
else {
|
|
164
|
+
let lowestIndex = 0;
|
|
165
|
+
let lowestScore = results[0].score;
|
|
166
|
+
for (let i = 1; i < results.length; i++) if (results[i].score < lowestScore) {
|
|
167
|
+
lowestIndex = i;
|
|
168
|
+
lowestScore = results[i].score;
|
|
169
|
+
}
|
|
170
|
+
if (result.score > lowestScore) results[lowestIndex] = result;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
results.sort((a, b) => {
|
|
175
|
+
return b.score - a.score;
|
|
176
|
+
});
|
|
177
|
+
return results;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
//#endregion
|
|
181
|
+
exports.DEFAULT_KEYS = DEFAULT_KEYS;
|
|
182
|
+
exports.sublimeSearch = sublimeSearch;
|
|
183
|
+
//# sourceMappingURL=sublime-search.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sublime-search.cjs","names":["bestRecursiveMatches: number[]","recursiveMatches: number[]","results: Array<{ score: number; item: T }>","itemMatchScore: number | undefined","result: { score: number; item: T }"],"sources":["../../src/search/sublime-search.ts"],"sourcesContent":["/*\n * Constants and utility functions used for searching for node types in node creator component\n * based on https://github.com/forrestthewoods/lib_fts/blob/master/code/fts_fuzzy_match.js\n */\n\nconst SEQUENTIAL_BONUS = 60; // bonus for adjacent matches\nconst SEPARATOR_BONUS = 38; // bonus if match occurs after a separator\nconst CAMEL_BONUS = 30; // bonus if match is uppercase and prev is lower\nconst FIRST_LETTER_BONUS = 15; // bonus if the first letter is matched\n\nconst LEADING_LETTER_PENALTY = -20; // penalty applied for every letter in str before the first match\nconst MAX_LEADING_LETTER_PENALTY = -200; // maximum penalty for leading letters\nconst UNMATCHED_LETTER_PENALTY = -2.5;\n\nexport const DEFAULT_KEYS = [\n\t{ key: 'properties.displayName', weight: 1.3 },\n\t{ key: 'properties.codex.alias', weight: 1 },\n];\n\n/**\n * Returns true if each character in pattern is found sequentially within target\n * @param {*} pattern string\n * @param {*} target string\n */\nfunction fuzzyMatchSimple(pattern: string, target: string): boolean {\n\tlet patternIdx = 0;\n\tlet strIdx = 0;\n\n\twhile (patternIdx < pattern.length && strIdx < target.length) {\n\t\tconst patternChar = pattern.charAt(patternIdx).toLowerCase();\n\t\tconst targetChar = target.charAt(strIdx).toLowerCase();\n\t\tif (patternChar === targetChar) {\n\t\t\tpatternIdx++;\n\t\t}\n\t\t++strIdx;\n\t}\n\n\treturn pattern.length !== 0 && target.length !== 0 && patternIdx === pattern.length;\n}\n\nfunction fuzzyMatchRecursive(\n\tpattern: string,\n\ttarget: string,\n\tpatternCurIndex: number,\n\ttargetCurrIndex: number,\n\ttargetMatches: null | number[],\n\tmatches: number[],\n\tmaxMatches: number,\n\tnextMatch: number,\n\trecursionCount: number,\n\trecursionLimit: number,\n): { matched: boolean; outScore: number } {\n\tlet outScore = 0;\n\n\t// Return if recursion limit is reached.\n\tif (++recursionCount >= recursionLimit) {\n\t\treturn { matched: false, outScore };\n\t}\n\n\t// Return if we reached ends of strings.\n\tif (patternCurIndex === pattern.length || targetCurrIndex === target.length) {\n\t\treturn { matched: false, outScore };\n\t}\n\n\t// Recursion params\n\tlet recursiveMatch = false;\n\tlet bestRecursiveMatches: number[] = [];\n\tlet bestRecursiveScore = 0;\n\n\t// Loop through pattern and str looking for a match.\n\tlet firstMatch = true;\n\twhile (patternCurIndex < pattern.length && targetCurrIndex < target.length) {\n\t\t// Match found.\n\t\tif (pattern[patternCurIndex].toLowerCase() === target[targetCurrIndex].toLowerCase()) {\n\t\t\tif (nextMatch >= maxMatches) {\n\t\t\t\treturn { matched: false, outScore };\n\t\t\t}\n\n\t\t\tif (firstMatch && targetMatches) {\n\t\t\t\tmatches = [...targetMatches];\n\t\t\t\tfirstMatch = false;\n\t\t\t}\n\n\t\t\tconst recursiveMatches: number[] = [];\n\t\t\tconst recursiveResult = fuzzyMatchRecursive(\n\t\t\t\tpattern,\n\t\t\t\ttarget,\n\t\t\t\tpatternCurIndex,\n\t\t\t\ttargetCurrIndex + 1,\n\t\t\t\tmatches,\n\t\t\t\trecursiveMatches,\n\t\t\t\tmaxMatches,\n\t\t\t\tnextMatch,\n\t\t\t\trecursionCount,\n\t\t\t\trecursionLimit,\n\t\t\t);\n\n\t\t\tconst recursiveScore = recursiveResult.outScore;\n\t\t\tif (recursiveResult.matched) {\n\t\t\t\t// Pick best recursive score.\n\t\t\t\tif (!recursiveMatch || recursiveScore > bestRecursiveScore) {\n\t\t\t\t\tbestRecursiveMatches = [...recursiveMatches];\n\t\t\t\t\tbestRecursiveScore = recursiveScore;\n\t\t\t\t}\n\t\t\t\trecursiveMatch = true;\n\t\t\t}\n\n\t\t\tmatches[nextMatch++] = targetCurrIndex;\n\t\t\t++patternCurIndex;\n\t\t}\n\t\t++targetCurrIndex;\n\t}\n\n\tconst matched = patternCurIndex === pattern.length;\n\n\tif (matched) {\n\t\toutScore = 100;\n\n\t\t// Apply leading letter penalty (if not n8n-prefixed)\n\t\tif (!target.toLowerCase().startsWith('n8n')) {\n\t\t\tlet penalty = LEADING_LETTER_PENALTY * matches[0];\n\t\t\tpenalty = penalty < MAX_LEADING_LETTER_PENALTY ? MAX_LEADING_LETTER_PENALTY : penalty;\n\t\t\toutScore += penalty;\n\t\t}\n\n\t\t//Apply unmatched penalty\n\t\tconst unmatched = target.length - nextMatch;\n\t\toutScore += UNMATCHED_LETTER_PENALTY * unmatched;\n\n\t\t// Apply ordering bonuses\n\t\tfor (let i = 0; i < nextMatch; i++) {\n\t\t\tconst currIdx = matches[i];\n\n\t\t\tif (i > 0) {\n\t\t\t\tconst prevIdx = matches[i - 1];\n\t\t\t\tif (currIdx === prevIdx + 1) {\n\t\t\t\t\toutScore += SEQUENTIAL_BONUS;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check for bonuses based on neighbor character value.\n\t\t\tif (currIdx > 0) {\n\t\t\t\t// Camel case\n\t\t\t\tconst neighbor = target[currIdx - 1];\n\t\t\t\tconst curr = target[currIdx];\n\t\t\t\tif (neighbor !== neighbor.toUpperCase() && curr !== curr.toLowerCase()) {\n\t\t\t\t\toutScore += CAMEL_BONUS;\n\t\t\t\t}\n\t\t\t\tconst isNeighbourSeparator = neighbor === '_' || neighbor === ' ';\n\t\t\t\tif (isNeighbourSeparator) {\n\t\t\t\t\toutScore += SEPARATOR_BONUS;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// First letter\n\t\t\t\toutScore += FIRST_LETTER_BONUS;\n\t\t\t}\n\t\t}\n\n\t\t// Return best result\n\t\tif (recursiveMatch && (!matched || bestRecursiveScore > outScore)) {\n\t\t\t// Recursive score is better than \"this\"\n\t\t\tmatches = [...bestRecursiveMatches];\n\t\t\toutScore = bestRecursiveScore;\n\t\t\treturn { matched: true, outScore };\n\t\t} else if (matched) {\n\t\t\t// \"this\" score is better than recursive\n\t\t\treturn { matched: true, outScore };\n\t\t} else {\n\t\t\treturn { matched: false, outScore };\n\t\t}\n\t}\n\treturn { matched: false, outScore };\n}\n\n/**\n * Does a fuzzy search to find pattern inside a string.\n * @param {*} pattern string pattern to search for\n * @param {*} target string string which is being searched\n * @returns [boolean, number] a boolean which tells if pattern was\n * found or not and a search score\n */\nfunction fuzzyMatch(pattern: string, target: string): { matched: boolean; outScore: number } {\n\tconst recursionCount = 0;\n\tconst recursionLimit = 5;\n\tconst matches: number[] = [];\n\tconst maxMatches = 256;\n\n\treturn fuzzyMatchRecursive(\n\t\tpattern,\n\t\ttarget,\n\t\t0 /* patternCurIndex */,\n\t\t0 /* strCurrIndex */,\n\t\tnull /* srcMatces */,\n\t\tmatches,\n\t\tmaxMatches,\n\t\t0 /* nextMatch */,\n\t\trecursionCount,\n\t\trecursionLimit,\n\t);\n}\n\n// prop = 'key'\n// prop = 'key1.key2'\n// prop = ['key1', 'key2']\nfunction getValue<T extends object>(obj: T, prop: string): unknown {\n\tif (obj.hasOwnProperty(prop)) {\n\t\treturn obj[prop as keyof T];\n\t}\n\n\tconst segments = prop.split('.');\n\n\tlet result = obj;\n\tlet i = 0;\n\twhile (result && i < segments.length) {\n\t\tconst key = segments[i] as keyof T;\n\t\tresult = result[key] as T;\n\t\ti++;\n\t}\n\treturn result;\n}\n\nfunction scoreSearchValue(filter: string, value: string, weight: number): number | undefined {\n\tif (!fuzzyMatchSimple(filter, value)) return undefined;\n\n\tconst match = fuzzyMatch(filter, value);\n\tif (!match.matched) return undefined;\n\n\treturn match.outScore * weight;\n}\n\nexport function sublimeSearch<T extends object>(\n\tfilter: string,\n\tdata: readonly T[],\n\tkeys: Array<{ key: string; weight: number }> = DEFAULT_KEYS,\n\tlimit?: number,\n): Array<{ score: number; item: T }> {\n\tconst results: Array<{ score: number; item: T }> = [];\n\n\tfor (const item of data) {\n\t\tlet itemMatchScore: number | undefined;\n\n\t\tfor (const { key, weight } of keys) {\n\t\t\tconst value = getValue(item, key);\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\tfor (const entry of value) {\n\t\t\t\t\tif (typeof entry !== 'string') continue;\n\n\t\t\t\t\tconst score = scoreSearchValue(filter, entry, weight);\n\t\t\t\t\tif (score !== undefined && (itemMatchScore === undefined || score > itemMatchScore)) {\n\t\t\t\t\t\titemMatchScore = score;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (typeof value === 'string') {\n\t\t\t\tconst score = scoreSearchValue(filter, value, weight);\n\t\t\t\tif (score !== undefined && (itemMatchScore === undefined || score > itemMatchScore)) {\n\t\t\t\t\titemMatchScore = score;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (itemMatchScore !== undefined) {\n\t\t\tconst result: { score: number; item: T } = {\n\t\t\t\tscore: itemMatchScore,\n\t\t\t\titem,\n\t\t\t};\n\n\t\t\tif (limit === undefined || results.length < limit) {\n\t\t\t\tresults.push(result);\n\t\t\t} else {\n\t\t\t\tlet lowestIndex = 0;\n\t\t\t\tlet lowestScore = results[0].score;\n\t\t\t\tfor (let i = 1; i < results.length; i++) {\n\t\t\t\t\tif (results[i].score < lowestScore) {\n\t\t\t\t\t\tlowestIndex = i;\n\t\t\t\t\t\tlowestScore = results[i].score;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (result.score > lowestScore) {\n\t\t\t\t\tresults[lowestIndex] = result;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tresults.sort((a, b) => {\n\t\treturn b.score - a.score;\n\t});\n\n\treturn results;\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/search/sublime-search.d.ts
|
|
2
|
+
declare const DEFAULT_KEYS: {
|
|
3
|
+
key: string;
|
|
4
|
+
weight: number;
|
|
5
|
+
}[];
|
|
6
|
+
declare function sublimeSearch<T extends object>(filter: string, data: readonly T[], keys?: Array<{
|
|
7
|
+
key: string;
|
|
8
|
+
weight: number;
|
|
9
|
+
}>, limit?: number): Array<{
|
|
10
|
+
score: number;
|
|
11
|
+
item: T;
|
|
12
|
+
}>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { DEFAULT_KEYS, sublimeSearch };
|
|
15
|
+
//# sourceMappingURL=sublime-search.d.cts.map
|
|
@@ -1,2 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/search/sublime-search.d.ts
|
|
2
|
+
declare const DEFAULT_KEYS: {
|
|
3
|
+
key: string;
|
|
4
|
+
weight: number;
|
|
5
|
+
}[];
|
|
6
|
+
declare function sublimeSearch<T extends object>(filter: string, data: readonly T[], keys?: Array<{
|
|
7
|
+
key: string;
|
|
8
|
+
weight: number;
|
|
9
|
+
}>, limit?: number): Array<{
|
|
10
|
+
score: number;
|
|
11
|
+
item: T;
|
|
12
|
+
}>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { DEFAULT_KEYS, sublimeSearch };
|
|
15
|
+
//# sourceMappingURL=sublime-search.d.mts.map
|
|
@@ -1,3 +1,181 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/search/sublime-search.ts
|
|
2
|
+
const SEQUENTIAL_BONUS = 60;
|
|
3
|
+
const SEPARATOR_BONUS = 38;
|
|
4
|
+
const CAMEL_BONUS = 30;
|
|
5
|
+
const FIRST_LETTER_BONUS = 15;
|
|
6
|
+
const LEADING_LETTER_PENALTY = -20;
|
|
7
|
+
const MAX_LEADING_LETTER_PENALTY = -200;
|
|
8
|
+
const UNMATCHED_LETTER_PENALTY = -2.5;
|
|
9
|
+
const DEFAULT_KEYS = [{
|
|
10
|
+
key: "properties.displayName",
|
|
11
|
+
weight: 1.3
|
|
12
|
+
}, {
|
|
13
|
+
key: "properties.codex.alias",
|
|
14
|
+
weight: 1
|
|
15
|
+
}];
|
|
16
|
+
/**
|
|
17
|
+
* Returns true if each character in pattern is found sequentially within target
|
|
18
|
+
* @param {*} pattern string
|
|
19
|
+
* @param {*} target string
|
|
20
|
+
*/
|
|
21
|
+
function fuzzyMatchSimple(pattern, target) {
|
|
22
|
+
let patternIdx = 0;
|
|
23
|
+
let strIdx = 0;
|
|
24
|
+
while (patternIdx < pattern.length && strIdx < target.length) {
|
|
25
|
+
if (pattern.charAt(patternIdx).toLowerCase() === target.charAt(strIdx).toLowerCase()) patternIdx++;
|
|
26
|
+
++strIdx;
|
|
27
|
+
}
|
|
28
|
+
return pattern.length !== 0 && target.length !== 0 && patternIdx === pattern.length;
|
|
29
|
+
}
|
|
30
|
+
function fuzzyMatchRecursive(pattern, target, patternCurIndex, targetCurrIndex, targetMatches, matches, maxMatches, nextMatch, recursionCount, recursionLimit) {
|
|
31
|
+
let outScore = 0;
|
|
32
|
+
if (++recursionCount >= recursionLimit) return {
|
|
33
|
+
matched: false,
|
|
34
|
+
outScore
|
|
35
|
+
};
|
|
36
|
+
if (patternCurIndex === pattern.length || targetCurrIndex === target.length) return {
|
|
37
|
+
matched: false,
|
|
38
|
+
outScore
|
|
39
|
+
};
|
|
40
|
+
let recursiveMatch = false;
|
|
41
|
+
let bestRecursiveMatches = [];
|
|
42
|
+
let bestRecursiveScore = 0;
|
|
43
|
+
let firstMatch = true;
|
|
44
|
+
while (patternCurIndex < pattern.length && targetCurrIndex < target.length) {
|
|
45
|
+
if (pattern[patternCurIndex].toLowerCase() === target[targetCurrIndex].toLowerCase()) {
|
|
46
|
+
if (nextMatch >= maxMatches) return {
|
|
47
|
+
matched: false,
|
|
48
|
+
outScore
|
|
49
|
+
};
|
|
50
|
+
if (firstMatch && targetMatches) {
|
|
51
|
+
matches = [...targetMatches];
|
|
52
|
+
firstMatch = false;
|
|
53
|
+
}
|
|
54
|
+
const recursiveMatches = [];
|
|
55
|
+
const recursiveResult = fuzzyMatchRecursive(pattern, target, patternCurIndex, targetCurrIndex + 1, matches, recursiveMatches, maxMatches, nextMatch, recursionCount, recursionLimit);
|
|
56
|
+
const recursiveScore = recursiveResult.outScore;
|
|
57
|
+
if (recursiveResult.matched) {
|
|
58
|
+
if (!recursiveMatch || recursiveScore > bestRecursiveScore) {
|
|
59
|
+
bestRecursiveMatches = [...recursiveMatches];
|
|
60
|
+
bestRecursiveScore = recursiveScore;
|
|
61
|
+
}
|
|
62
|
+
recursiveMatch = true;
|
|
63
|
+
}
|
|
64
|
+
matches[nextMatch++] = targetCurrIndex;
|
|
65
|
+
++patternCurIndex;
|
|
66
|
+
}
|
|
67
|
+
++targetCurrIndex;
|
|
68
|
+
}
|
|
69
|
+
const matched = patternCurIndex === pattern.length;
|
|
70
|
+
if (matched) {
|
|
71
|
+
outScore = 100;
|
|
72
|
+
if (!target.toLowerCase().startsWith("n8n")) {
|
|
73
|
+
let penalty = LEADING_LETTER_PENALTY * matches[0];
|
|
74
|
+
penalty = penalty < MAX_LEADING_LETTER_PENALTY ? MAX_LEADING_LETTER_PENALTY : penalty;
|
|
75
|
+
outScore += penalty;
|
|
76
|
+
}
|
|
77
|
+
const unmatched = target.length - nextMatch;
|
|
78
|
+
outScore += UNMATCHED_LETTER_PENALTY * unmatched;
|
|
79
|
+
for (let i = 0; i < nextMatch; i++) {
|
|
80
|
+
const currIdx = matches[i];
|
|
81
|
+
if (i > 0) {
|
|
82
|
+
if (currIdx === matches[i - 1] + 1) outScore += SEQUENTIAL_BONUS;
|
|
83
|
+
}
|
|
84
|
+
if (currIdx > 0) {
|
|
85
|
+
const neighbor = target[currIdx - 1];
|
|
86
|
+
const curr = target[currIdx];
|
|
87
|
+
if (neighbor !== neighbor.toUpperCase() && curr !== curr.toLowerCase()) outScore += CAMEL_BONUS;
|
|
88
|
+
if (neighbor === "_" || neighbor === " ") outScore += SEPARATOR_BONUS;
|
|
89
|
+
} else outScore += FIRST_LETTER_BONUS;
|
|
90
|
+
}
|
|
91
|
+
if (recursiveMatch && (!matched || bestRecursiveScore > outScore)) {
|
|
92
|
+
matches = [...bestRecursiveMatches];
|
|
93
|
+
outScore = bestRecursiveScore;
|
|
94
|
+
return {
|
|
95
|
+
matched: true,
|
|
96
|
+
outScore
|
|
97
|
+
};
|
|
98
|
+
} else if (matched) return {
|
|
99
|
+
matched: true,
|
|
100
|
+
outScore
|
|
101
|
+
};
|
|
102
|
+
else return {
|
|
103
|
+
matched: false,
|
|
104
|
+
outScore
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
matched: false,
|
|
109
|
+
outScore
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Does a fuzzy search to find pattern inside a string.
|
|
114
|
+
* @param {*} pattern string pattern to search for
|
|
115
|
+
* @param {*} target string string which is being searched
|
|
116
|
+
* @returns [boolean, number] a boolean which tells if pattern was
|
|
117
|
+
* found or not and a search score
|
|
118
|
+
*/
|
|
119
|
+
function fuzzyMatch(pattern, target) {
|
|
120
|
+
return fuzzyMatchRecursive(pattern, target, 0, 0, null, [], 256, 0, 0, 5);
|
|
121
|
+
}
|
|
122
|
+
function getValue(obj, prop) {
|
|
123
|
+
if (obj.hasOwnProperty(prop)) return obj[prop];
|
|
124
|
+
const segments = prop.split(".");
|
|
125
|
+
let result = obj;
|
|
126
|
+
let i = 0;
|
|
127
|
+
while (result && i < segments.length) {
|
|
128
|
+
const key = segments[i];
|
|
129
|
+
result = result[key];
|
|
130
|
+
i++;
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
function scoreSearchValue(filter, value, weight) {
|
|
135
|
+
if (!fuzzyMatchSimple(filter, value)) return void 0;
|
|
136
|
+
const match = fuzzyMatch(filter, value);
|
|
137
|
+
if (!match.matched) return void 0;
|
|
138
|
+
return match.outScore * weight;
|
|
139
|
+
}
|
|
140
|
+
function sublimeSearch(filter, data, keys = DEFAULT_KEYS, limit) {
|
|
141
|
+
const results = [];
|
|
142
|
+
for (const item of data) {
|
|
143
|
+
let itemMatchScore;
|
|
144
|
+
for (const { key, weight } of keys) {
|
|
145
|
+
const value = getValue(item, key);
|
|
146
|
+
if (Array.isArray(value)) for (const entry of value) {
|
|
147
|
+
if (typeof entry !== "string") continue;
|
|
148
|
+
const score = scoreSearchValue(filter, entry, weight);
|
|
149
|
+
if (score !== void 0 && (itemMatchScore === void 0 || score > itemMatchScore)) itemMatchScore = score;
|
|
150
|
+
}
|
|
151
|
+
else if (typeof value === "string") {
|
|
152
|
+
const score = scoreSearchValue(filter, value, weight);
|
|
153
|
+
if (score !== void 0 && (itemMatchScore === void 0 || score > itemMatchScore)) itemMatchScore = score;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (itemMatchScore !== void 0) {
|
|
157
|
+
const result = {
|
|
158
|
+
score: itemMatchScore,
|
|
159
|
+
item
|
|
160
|
+
};
|
|
161
|
+
if (limit === void 0 || results.length < limit) results.push(result);
|
|
162
|
+
else {
|
|
163
|
+
let lowestIndex = 0;
|
|
164
|
+
let lowestScore = results[0].score;
|
|
165
|
+
for (let i = 1; i < results.length; i++) if (results[i].score < lowestScore) {
|
|
166
|
+
lowestIndex = i;
|
|
167
|
+
lowestScore = results[i].score;
|
|
168
|
+
}
|
|
169
|
+
if (result.score > lowestScore) results[lowestIndex] = result;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
results.sort((a, b) => {
|
|
174
|
+
return b.score - a.score;
|
|
175
|
+
});
|
|
176
|
+
return results;
|
|
177
|
+
}
|
|
2
178
|
|
|
3
|
-
|
|
179
|
+
//#endregion
|
|
180
|
+
export { DEFAULT_KEYS, sublimeSearch };
|
|
181
|
+
//# sourceMappingURL=sublime-search.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sublime-search.mjs","names":["bestRecursiveMatches: number[]","recursiveMatches: number[]","results: Array<{ score: number; item: T }>","itemMatchScore: number | undefined","result: { score: number; item: T }"],"sources":["../../src/search/sublime-search.ts"],"sourcesContent":["/*\n * Constants and utility functions used for searching for node types in node creator component\n * based on https://github.com/forrestthewoods/lib_fts/blob/master/code/fts_fuzzy_match.js\n */\n\nconst SEQUENTIAL_BONUS = 60; // bonus for adjacent matches\nconst SEPARATOR_BONUS = 38; // bonus if match occurs after a separator\nconst CAMEL_BONUS = 30; // bonus if match is uppercase and prev is lower\nconst FIRST_LETTER_BONUS = 15; // bonus if the first letter is matched\n\nconst LEADING_LETTER_PENALTY = -20; // penalty applied for every letter in str before the first match\nconst MAX_LEADING_LETTER_PENALTY = -200; // maximum penalty for leading letters\nconst UNMATCHED_LETTER_PENALTY = -2.5;\n\nexport const DEFAULT_KEYS = [\n\t{ key: 'properties.displayName', weight: 1.3 },\n\t{ key: 'properties.codex.alias', weight: 1 },\n];\n\n/**\n * Returns true if each character in pattern is found sequentially within target\n * @param {*} pattern string\n * @param {*} target string\n */\nfunction fuzzyMatchSimple(pattern: string, target: string): boolean {\n\tlet patternIdx = 0;\n\tlet strIdx = 0;\n\n\twhile (patternIdx < pattern.length && strIdx < target.length) {\n\t\tconst patternChar = pattern.charAt(patternIdx).toLowerCase();\n\t\tconst targetChar = target.charAt(strIdx).toLowerCase();\n\t\tif (patternChar === targetChar) {\n\t\t\tpatternIdx++;\n\t\t}\n\t\t++strIdx;\n\t}\n\n\treturn pattern.length !== 0 && target.length !== 0 && patternIdx === pattern.length;\n}\n\nfunction fuzzyMatchRecursive(\n\tpattern: string,\n\ttarget: string,\n\tpatternCurIndex: number,\n\ttargetCurrIndex: number,\n\ttargetMatches: null | number[],\n\tmatches: number[],\n\tmaxMatches: number,\n\tnextMatch: number,\n\trecursionCount: number,\n\trecursionLimit: number,\n): { matched: boolean; outScore: number } {\n\tlet outScore = 0;\n\n\t// Return if recursion limit is reached.\n\tif (++recursionCount >= recursionLimit) {\n\t\treturn { matched: false, outScore };\n\t}\n\n\t// Return if we reached ends of strings.\n\tif (patternCurIndex === pattern.length || targetCurrIndex === target.length) {\n\t\treturn { matched: false, outScore };\n\t}\n\n\t// Recursion params\n\tlet recursiveMatch = false;\n\tlet bestRecursiveMatches: number[] = [];\n\tlet bestRecursiveScore = 0;\n\n\t// Loop through pattern and str looking for a match.\n\tlet firstMatch = true;\n\twhile (patternCurIndex < pattern.length && targetCurrIndex < target.length) {\n\t\t// Match found.\n\t\tif (pattern[patternCurIndex].toLowerCase() === target[targetCurrIndex].toLowerCase()) {\n\t\t\tif (nextMatch >= maxMatches) {\n\t\t\t\treturn { matched: false, outScore };\n\t\t\t}\n\n\t\t\tif (firstMatch && targetMatches) {\n\t\t\t\tmatches = [...targetMatches];\n\t\t\t\tfirstMatch = false;\n\t\t\t}\n\n\t\t\tconst recursiveMatches: number[] = [];\n\t\t\tconst recursiveResult = fuzzyMatchRecursive(\n\t\t\t\tpattern,\n\t\t\t\ttarget,\n\t\t\t\tpatternCurIndex,\n\t\t\t\ttargetCurrIndex + 1,\n\t\t\t\tmatches,\n\t\t\t\trecursiveMatches,\n\t\t\t\tmaxMatches,\n\t\t\t\tnextMatch,\n\t\t\t\trecursionCount,\n\t\t\t\trecursionLimit,\n\t\t\t);\n\n\t\t\tconst recursiveScore = recursiveResult.outScore;\n\t\t\tif (recursiveResult.matched) {\n\t\t\t\t// Pick best recursive score.\n\t\t\t\tif (!recursiveMatch || recursiveScore > bestRecursiveScore) {\n\t\t\t\t\tbestRecursiveMatches = [...recursiveMatches];\n\t\t\t\t\tbestRecursiveScore = recursiveScore;\n\t\t\t\t}\n\t\t\t\trecursiveMatch = true;\n\t\t\t}\n\n\t\t\tmatches[nextMatch++] = targetCurrIndex;\n\t\t\t++patternCurIndex;\n\t\t}\n\t\t++targetCurrIndex;\n\t}\n\n\tconst matched = patternCurIndex === pattern.length;\n\n\tif (matched) {\n\t\toutScore = 100;\n\n\t\t// Apply leading letter penalty (if not n8n-prefixed)\n\t\tif (!target.toLowerCase().startsWith('n8n')) {\n\t\t\tlet penalty = LEADING_LETTER_PENALTY * matches[0];\n\t\t\tpenalty = penalty < MAX_LEADING_LETTER_PENALTY ? MAX_LEADING_LETTER_PENALTY : penalty;\n\t\t\toutScore += penalty;\n\t\t}\n\n\t\t//Apply unmatched penalty\n\t\tconst unmatched = target.length - nextMatch;\n\t\toutScore += UNMATCHED_LETTER_PENALTY * unmatched;\n\n\t\t// Apply ordering bonuses\n\t\tfor (let i = 0; i < nextMatch; i++) {\n\t\t\tconst currIdx = matches[i];\n\n\t\t\tif (i > 0) {\n\t\t\t\tconst prevIdx = matches[i - 1];\n\t\t\t\tif (currIdx === prevIdx + 1) {\n\t\t\t\t\toutScore += SEQUENTIAL_BONUS;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check for bonuses based on neighbor character value.\n\t\t\tif (currIdx > 0) {\n\t\t\t\t// Camel case\n\t\t\t\tconst neighbor = target[currIdx - 1];\n\t\t\t\tconst curr = target[currIdx];\n\t\t\t\tif (neighbor !== neighbor.toUpperCase() && curr !== curr.toLowerCase()) {\n\t\t\t\t\toutScore += CAMEL_BONUS;\n\t\t\t\t}\n\t\t\t\tconst isNeighbourSeparator = neighbor === '_' || neighbor === ' ';\n\t\t\t\tif (isNeighbourSeparator) {\n\t\t\t\t\toutScore += SEPARATOR_BONUS;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// First letter\n\t\t\t\toutScore += FIRST_LETTER_BONUS;\n\t\t\t}\n\t\t}\n\n\t\t// Return best result\n\t\tif (recursiveMatch && (!matched || bestRecursiveScore > outScore)) {\n\t\t\t// Recursive score is better than \"this\"\n\t\t\tmatches = [...bestRecursiveMatches];\n\t\t\toutScore = bestRecursiveScore;\n\t\t\treturn { matched: true, outScore };\n\t\t} else if (matched) {\n\t\t\t// \"this\" score is better than recursive\n\t\t\treturn { matched: true, outScore };\n\t\t} else {\n\t\t\treturn { matched: false, outScore };\n\t\t}\n\t}\n\treturn { matched: false, outScore };\n}\n\n/**\n * Does a fuzzy search to find pattern inside a string.\n * @param {*} pattern string pattern to search for\n * @param {*} target string string which is being searched\n * @returns [boolean, number] a boolean which tells if pattern was\n * found or not and a search score\n */\nfunction fuzzyMatch(pattern: string, target: string): { matched: boolean; outScore: number } {\n\tconst recursionCount = 0;\n\tconst recursionLimit = 5;\n\tconst matches: number[] = [];\n\tconst maxMatches = 256;\n\n\treturn fuzzyMatchRecursive(\n\t\tpattern,\n\t\ttarget,\n\t\t0 /* patternCurIndex */,\n\t\t0 /* strCurrIndex */,\n\t\tnull /* srcMatces */,\n\t\tmatches,\n\t\tmaxMatches,\n\t\t0 /* nextMatch */,\n\t\trecursionCount,\n\t\trecursionLimit,\n\t);\n}\n\n// prop = 'key'\n// prop = 'key1.key2'\n// prop = ['key1', 'key2']\nfunction getValue<T extends object>(obj: T, prop: string): unknown {\n\tif (obj.hasOwnProperty(prop)) {\n\t\treturn obj[prop as keyof T];\n\t}\n\n\tconst segments = prop.split('.');\n\n\tlet result = obj;\n\tlet i = 0;\n\twhile (result && i < segments.length) {\n\t\tconst key = segments[i] as keyof T;\n\t\tresult = result[key] as T;\n\t\ti++;\n\t}\n\treturn result;\n}\n\nfunction scoreSearchValue(filter: string, value: string, weight: number): number | undefined {\n\tif (!fuzzyMatchSimple(filter, value)) return undefined;\n\n\tconst match = fuzzyMatch(filter, value);\n\tif (!match.matched) return undefined;\n\n\treturn match.outScore * weight;\n}\n\nexport function sublimeSearch<T extends object>(\n\tfilter: string,\n\tdata: readonly T[],\n\tkeys: Array<{ key: string; weight: number }> = DEFAULT_KEYS,\n\tlimit?: number,\n): Array<{ score: number; item: T }> {\n\tconst results: Array<{ score: number; item: T }> = [];\n\n\tfor (const item of data) {\n\t\tlet itemMatchScore: number | undefined;\n\n\t\tfor (const { key, weight } of keys) {\n\t\t\tconst value = getValue(item, key);\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\tfor (const entry of value) {\n\t\t\t\t\tif (typeof entry !== 'string') continue;\n\n\t\t\t\t\tconst score = scoreSearchValue(filter, entry, weight);\n\t\t\t\t\tif (score !== undefined && (itemMatchScore === undefined || score > itemMatchScore)) {\n\t\t\t\t\t\titemMatchScore = score;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (typeof value === 'string') {\n\t\t\t\tconst score = scoreSearchValue(filter, value, weight);\n\t\t\t\tif (score !== undefined && (itemMatchScore === undefined || score > itemMatchScore)) {\n\t\t\t\t\titemMatchScore = score;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (itemMatchScore !== undefined) {\n\t\t\tconst result: { score: number; item: T } = {\n\t\t\t\tscore: itemMatchScore,\n\t\t\t\titem,\n\t\t\t};\n\n\t\t\tif (limit === undefined || results.length < limit) {\n\t\t\t\tresults.push(result);\n\t\t\t} else {\n\t\t\t\tlet lowestIndex = 0;\n\t\t\t\tlet lowestScore = results[0].score;\n\t\t\t\tfor (let i = 1; i < results.length; i++) {\n\t\t\t\t\tif (results[i].score < lowestScore) {\n\t\t\t\t\t\tlowestIndex = i;\n\t\t\t\t\t\tlowestScore = results[i].score;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (result.score > lowestScore) {\n\t\t\t\t\tresults[lowestIndex] = result;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tresults.sort((a, b) => {\n\t\treturn b.score - a.score;\n\t});\n\n\treturn results;\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
const require_sort_by_property = require('../sort-by-property.cjs');
|
|
2
1
|
|
|
3
|
-
|
|
2
|
+
//#region src/sort/sort-by-property.ts
|
|
3
|
+
const sortByProperty = (property, arr, order = "asc") => arr.sort((a, b) => {
|
|
4
|
+
const result = String(a[property]).localeCompare(String(b[property]), void 0, {
|
|
5
|
+
numeric: true,
|
|
6
|
+
sensitivity: "base"
|
|
7
|
+
});
|
|
8
|
+
return order === "asc" ? result : -result;
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
12
|
+
exports.sortByProperty = sortByProperty;
|
|
13
|
+
//# sourceMappingURL=sort-by-property.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sort-by-property.cjs","names":[],"sources":["../../src/sort/sort-by-property.ts"],"sourcesContent":["export const sortByProperty = <T>(\n\tproperty: keyof T,\n\tarr: T[],\n\torder: 'asc' | 'desc' = 'asc',\n): T[] =>\n\tarr.sort((a, b) => {\n\t\tconst result = String(a[property]).localeCompare(String(b[property]), undefined, {\n\t\t\tnumeric: true,\n\t\t\tsensitivity: 'base',\n\t\t});\n\t\treturn order === 'asc' ? result : -result;\n\t});\n"],"mappings":";;AAAA,MAAa,kBACZ,UACA,KACA,QAAwB,UAExB,IAAI,MAAM,GAAG,MAAM;CAClB,MAAM,SAAS,OAAO,EAAE,UAAU,CAAC,cAAc,OAAO,EAAE,UAAU,EAAE,QAAW;EAChF,SAAS;EACT,aAAa;EACb,CAAC;AACF,QAAO,UAAU,QAAQ,SAAS,CAAC;EAClC"}
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/sort/sort-by-property.d.ts
|
|
2
|
+
declare const sortByProperty: <T>(property: keyof T, arr: T[], order?: "asc" | "desc") => T[];
|
|
3
|
+
//#endregion
|
|
4
|
+
export { sortByProperty };
|
|
5
|
+
//# sourceMappingURL=sort-by-property.d.cts.map
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/sort/sort-by-property.d.ts
|
|
2
|
+
declare const sortByProperty: <T>(property: keyof T, arr: T[], order?: "asc" | "desc") => T[];
|
|
3
|
+
//#endregion
|
|
4
|
+
export { sortByProperty };
|
|
5
|
+
//# sourceMappingURL=sort-by-property.d.mts.map
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/sort/sort-by-property.ts
|
|
2
|
+
const sortByProperty = (property, arr, order = "asc") => arr.sort((a, b) => {
|
|
3
|
+
const result = String(a[property]).localeCompare(String(b[property]), void 0, {
|
|
4
|
+
numeric: true,
|
|
5
|
+
sensitivity: "base"
|
|
6
|
+
});
|
|
7
|
+
return order === "asc" ? result : -result;
|
|
8
|
+
});
|
|
2
9
|
|
|
3
|
-
|
|
10
|
+
//#endregion
|
|
11
|
+
export { sortByProperty };
|
|
12
|
+
//# sourceMappingURL=sort-by-property.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sort-by-property.mjs","names":[],"sources":["../../src/sort/sort-by-property.ts"],"sourcesContent":["export const sortByProperty = <T>(\n\tproperty: keyof T,\n\tarr: T[],\n\torder: 'asc' | 'desc' = 'asc',\n): T[] =>\n\tarr.sort((a, b) => {\n\t\tconst result = String(a[property]).localeCompare(String(b[property]), undefined, {\n\t\t\tnumeric: true,\n\t\t\tsensitivity: 'base',\n\t\t});\n\t\treturn order === 'asc' ? result : -result;\n\t});\n"],"mappings":";AAAA,MAAa,kBACZ,UACA,KACA,QAAwB,UAExB,IAAI,MAAM,GAAG,MAAM;CAClB,MAAM,SAAS,OAAO,EAAE,UAAU,CAAC,cAAc,OAAO,EAAE,UAAU,EAAE,QAAW;EAChF,SAAS;EACT,aAAa;EACb,CAAC;AACF,QAAO,UAAU,QAAQ,SAAS,CAAC;EAClC"}
|
package/dist/string/truncate.cjs
CHANGED
|
@@ -1,4 +1,33 @@
|
|
|
1
|
-
const require_truncate = require('../truncate.cjs');
|
|
2
1
|
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
//#region src/string/truncate.ts
|
|
3
|
+
const truncate = (text, length = 30) => text.length > length ? text.slice(0, length) + "..." : text;
|
|
4
|
+
/**
|
|
5
|
+
* Replace part of given text with ellipsis following the rules below:
|
|
6
|
+
*
|
|
7
|
+
* - Remove chars just before the last word, as long as the last word is between
|
|
8
|
+
* minLastWordLength and 15 chars
|
|
9
|
+
* - Otherwise preserve the last `lastCharsLength` chars and remove chars before that
|
|
10
|
+
*/
|
|
11
|
+
function truncateBeforeLast(text, maxLength, lastCharsLength = 5, minLastWordLength = 1) {
|
|
12
|
+
const chars = [];
|
|
13
|
+
const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
14
|
+
for (const { segment } of segmenter.segment(text)) chars.push(segment);
|
|
15
|
+
if (chars.length <= maxLength) return text;
|
|
16
|
+
const lastWordIndex = chars.findLastIndex((ch) => ch.match(/^\s+$/)) + 1;
|
|
17
|
+
const lastWord = chars.slice(lastWordIndex);
|
|
18
|
+
const ellipsis = "…";
|
|
19
|
+
const ellipsisLength = 1;
|
|
20
|
+
if (lastWord.length >= minLastWordLength && lastWord.length < 15) {
|
|
21
|
+
const charsToRemove = chars.length - maxLength + ellipsisLength;
|
|
22
|
+
const indexBeforeLastWord = lastWordIndex;
|
|
23
|
+
const keepLength = indexBeforeLastWord - charsToRemove;
|
|
24
|
+
if (keepLength > 0) return chars.slice(0, keepLength).join("") + ellipsis + chars.slice(indexBeforeLastWord).join("");
|
|
25
|
+
}
|
|
26
|
+
if (lastCharsLength < 1) return chars.slice(0, maxLength).join("") + ellipsis;
|
|
27
|
+
return chars.slice(0, maxLength - lastCharsLength - ellipsisLength).join("") + ellipsis + chars.slice(-lastCharsLength).join("");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
exports.truncate = truncate;
|
|
32
|
+
exports.truncateBeforeLast = truncateBeforeLast;
|
|
33
|
+
//# sourceMappingURL=truncate.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"truncate.cjs","names":["chars: string[]"],"sources":["../../src/string/truncate.ts"],"sourcesContent":["export const truncate = (text: string, length = 30): string =>\n\ttext.length > length ? text.slice(0, length) + '...' : text;\n\n/**\n * Replace part of given text with ellipsis following the rules below:\n *\n * - Remove chars just before the last word, as long as the last word is between\n * minLastWordLength and 15 chars\n * - Otherwise preserve the last `lastCharsLength` chars and remove chars before that\n */\nexport function truncateBeforeLast(\n\ttext: string,\n\tmaxLength: number,\n\tlastCharsLength: number = 5,\n\tminLastWordLength: number = 1,\n): string {\n\tconst chars: string[] = [];\n\n\tconst segmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' });\n\n\tfor (const { segment } of segmenter.segment(text)) {\n\t\tchars.push(segment);\n\t}\n\n\tif (chars.length <= maxLength) {\n\t\treturn text;\n\t}\n\n\tconst lastWhitespaceIndex = chars.findLastIndex((ch) => ch.match(/^\\s+$/));\n\tconst lastWordIndex = lastWhitespaceIndex + 1;\n\tconst lastWord = chars.slice(lastWordIndex);\n\tconst ellipsis = '…';\n\tconst ellipsisLength = ellipsis.length;\n\n\tif (lastWord.length >= minLastWordLength && lastWord.length < 15) {\n\t\tconst charsToRemove = chars.length - maxLength + ellipsisLength;\n\t\tconst indexBeforeLastWord = lastWordIndex;\n\t\tconst keepLength = indexBeforeLastWord - charsToRemove;\n\n\t\tif (keepLength > 0) {\n\t\t\treturn (\n\t\t\t\tchars.slice(0, keepLength).join('') + ellipsis + chars.slice(indexBeforeLastWord).join('')\n\t\t\t);\n\t\t}\n\t}\n\n\tif (lastCharsLength < 1) {\n\t\treturn chars.slice(0, maxLength).join('') + ellipsis;\n\t}\n\n\treturn (\n\t\tchars.slice(0, maxLength - lastCharsLength - ellipsisLength).join('') +\n\t\tellipsis +\n\t\tchars.slice(-lastCharsLength).join('')\n\t);\n}\n"],"mappings":";;AAAA,MAAa,YAAY,MAAc,SAAS,OAC/C,KAAK,SAAS,SAAS,KAAK,MAAM,GAAG,OAAO,GAAG,QAAQ;;;;;;;;AASxD,SAAgB,mBACf,MACA,WACA,kBAA0B,GAC1B,oBAA4B,GACnB;CACT,MAAMA,QAAkB,EAAE;CAE1B,MAAM,YAAY,IAAI,KAAK,UAAU,QAAW,EAAE,aAAa,YAAY,CAAC;AAE5E,MAAK,MAAM,EAAE,aAAa,UAAU,QAAQ,KAAK,CAChD,OAAM,KAAK,QAAQ;AAGpB,KAAI,MAAM,UAAU,UACnB,QAAO;CAIR,MAAM,gBADsB,MAAM,eAAe,OAAO,GAAG,MAAM,QAAQ,CAAC,GAC9B;CAC5C,MAAM,WAAW,MAAM,MAAM,cAAc;CAC3C,MAAM,WAAW;CACjB,MAAM,iBAAiB;AAEvB,KAAI,SAAS,UAAU,qBAAqB,SAAS,SAAS,IAAI;EACjE,MAAM,gBAAgB,MAAM,SAAS,YAAY;EACjD,MAAM,sBAAsB;EAC5B,MAAM,aAAa,sBAAsB;AAEzC,MAAI,aAAa,EAChB,QACC,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,GAAG,GAAG,WAAW,MAAM,MAAM,oBAAoB,CAAC,KAAK,GAAG;;AAK7F,KAAI,kBAAkB,EACrB,QAAO,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,GAAG,GAAG;AAG7C,QACC,MAAM,MAAM,GAAG,YAAY,kBAAkB,eAAe,CAAC,KAAK,GAAG,GACrE,WACA,MAAM,MAAM,CAAC,gBAAgB,CAAC,KAAK,GAAG"}
|
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/string/truncate.d.ts
|
|
2
|
+
declare const truncate: (text: string, length?: number) => string;
|
|
3
|
+
declare function truncateBeforeLast(text: string, maxLength: number, lastCharsLength?: number, minLastWordLength?: number): string;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { truncate, truncateBeforeLast };
|
|
6
|
+
//# sourceMappingURL=truncate.d.cts.map
|
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/string/truncate.d.ts
|
|
2
|
+
declare const truncate: (text: string, length?: number) => string;
|
|
3
|
+
declare function truncateBeforeLast(text: string, maxLength: number, lastCharsLength?: number, minLastWordLength?: number): string;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { truncate, truncateBeforeLast };
|
|
6
|
+
//# sourceMappingURL=truncate.d.mts.map
|
package/dist/string/truncate.mjs
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/string/truncate.ts
|
|
2
|
+
const truncate = (text, length = 30) => text.length > length ? text.slice(0, length) + "..." : text;
|
|
3
|
+
/**
|
|
4
|
+
* Replace part of given text with ellipsis following the rules below:
|
|
5
|
+
*
|
|
6
|
+
* - Remove chars just before the last word, as long as the last word is between
|
|
7
|
+
* minLastWordLength and 15 chars
|
|
8
|
+
* - Otherwise preserve the last `lastCharsLength` chars and remove chars before that
|
|
9
|
+
*/
|
|
10
|
+
function truncateBeforeLast(text, maxLength, lastCharsLength = 5, minLastWordLength = 1) {
|
|
11
|
+
const chars = [];
|
|
12
|
+
const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
13
|
+
for (const { segment } of segmenter.segment(text)) chars.push(segment);
|
|
14
|
+
if (chars.length <= maxLength) return text;
|
|
15
|
+
const lastWordIndex = chars.findLastIndex((ch) => ch.match(/^\s+$/)) + 1;
|
|
16
|
+
const lastWord = chars.slice(lastWordIndex);
|
|
17
|
+
const ellipsis = "…";
|
|
18
|
+
const ellipsisLength = 1;
|
|
19
|
+
if (lastWord.length >= minLastWordLength && lastWord.length < 15) {
|
|
20
|
+
const charsToRemove = chars.length - maxLength + ellipsisLength;
|
|
21
|
+
const indexBeforeLastWord = lastWordIndex;
|
|
22
|
+
const keepLength = indexBeforeLastWord - charsToRemove;
|
|
23
|
+
if (keepLength > 0) return chars.slice(0, keepLength).join("") + ellipsis + chars.slice(indexBeforeLastWord).join("");
|
|
24
|
+
}
|
|
25
|
+
if (lastCharsLength < 1) return chars.slice(0, maxLength).join("") + ellipsis;
|
|
26
|
+
return chars.slice(0, maxLength - lastCharsLength - ellipsisLength).join("") + ellipsis + chars.slice(-lastCharsLength).join("");
|
|
27
|
+
}
|
|
2
28
|
|
|
3
|
-
|
|
29
|
+
//#endregion
|
|
30
|
+
export { truncate, truncateBeforeLast };
|
|
31
|
+
//# sourceMappingURL=truncate.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"truncate.mjs","names":["chars: string[]"],"sources":["../../src/string/truncate.ts"],"sourcesContent":["export const truncate = (text: string, length = 30): string =>\n\ttext.length > length ? text.slice(0, length) + '...' : text;\n\n/**\n * Replace part of given text with ellipsis following the rules below:\n *\n * - Remove chars just before the last word, as long as the last word is between\n * minLastWordLength and 15 chars\n * - Otherwise preserve the last `lastCharsLength` chars and remove chars before that\n */\nexport function truncateBeforeLast(\n\ttext: string,\n\tmaxLength: number,\n\tlastCharsLength: number = 5,\n\tminLastWordLength: number = 1,\n): string {\n\tconst chars: string[] = [];\n\n\tconst segmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' });\n\n\tfor (const { segment } of segmenter.segment(text)) {\n\t\tchars.push(segment);\n\t}\n\n\tif (chars.length <= maxLength) {\n\t\treturn text;\n\t}\n\n\tconst lastWhitespaceIndex = chars.findLastIndex((ch) => ch.match(/^\\s+$/));\n\tconst lastWordIndex = lastWhitespaceIndex + 1;\n\tconst lastWord = chars.slice(lastWordIndex);\n\tconst ellipsis = '…';\n\tconst ellipsisLength = ellipsis.length;\n\n\tif (lastWord.length >= minLastWordLength && lastWord.length < 15) {\n\t\tconst charsToRemove = chars.length - maxLength + ellipsisLength;\n\t\tconst indexBeforeLastWord = lastWordIndex;\n\t\tconst keepLength = indexBeforeLastWord - charsToRemove;\n\n\t\tif (keepLength > 0) {\n\t\t\treturn (\n\t\t\t\tchars.slice(0, keepLength).join('') + ellipsis + chars.slice(indexBeforeLastWord).join('')\n\t\t\t);\n\t\t}\n\t}\n\n\tif (lastCharsLength < 1) {\n\t\treturn chars.slice(0, maxLength).join('') + ellipsis;\n\t}\n\n\treturn (\n\t\tchars.slice(0, maxLength - lastCharsLength - ellipsisLength).join('') +\n\t\tellipsis +\n\t\tchars.slice(-lastCharsLength).join('')\n\t);\n}\n"],"mappings":";AAAA,MAAa,YAAY,MAAc,SAAS,OAC/C,KAAK,SAAS,SAAS,KAAK,MAAM,GAAG,OAAO,GAAG,QAAQ;;;;;;;;AASxD,SAAgB,mBACf,MACA,WACA,kBAA0B,GAC1B,oBAA4B,GACnB;CACT,MAAMA,QAAkB,EAAE;CAE1B,MAAM,YAAY,IAAI,KAAK,UAAU,QAAW,EAAE,aAAa,YAAY,CAAC;AAE5E,MAAK,MAAM,EAAE,aAAa,UAAU,QAAQ,KAAK,CAChD,OAAM,KAAK,QAAQ;AAGpB,KAAI,MAAM,UAAU,UACnB,QAAO;CAIR,MAAM,gBADsB,MAAM,eAAe,OAAO,GAAG,MAAM,QAAQ,CAAC,GAC9B;CAC5C,MAAM,WAAW,MAAM,MAAM,cAAc;CAC3C,MAAM,WAAW;CACjB,MAAM,iBAAiB;AAEvB,KAAI,SAAS,UAAU,qBAAqB,SAAS,SAAS,IAAI;EACjE,MAAM,gBAAgB,MAAM,SAAS,YAAY;EACjD,MAAM,sBAAsB;EAC5B,MAAM,aAAa,sBAAsB;AAEzC,MAAI,aAAa,EAChB,QACC,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,GAAG,GAAG,WAAW,MAAM,MAAM,oBAAoB,CAAC,KAAK,GAAG;;AAK7F,KAAI,kBAAkB,EACrB,QAAO,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,GAAG,GAAG;AAG7C,QACC,MAAM,MAAM,GAAG,YAAY,kBAAkB,eAAe,CAAC,KAAK,GAAG,GACrE,WACA,MAAM,MAAM,CAAC,gBAAgB,CAAC,KAAK,GAAG"}
|
package/dist/types.d.cts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
|
|
3
|
+
//#endregion
|
|
4
|
+
export { DistributiveOmit };
|
|
5
|
+
//# sourceMappingURL=types.d.cts.map
|
package/dist/types.d.mts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
|
|
3
|
+
//#endregion
|
|
4
|
+
export { DistributiveOmit };
|
|
5
|
+
//# sourceMappingURL=types.d.mts.map
|