reg-cli 0.18.16 → 0.19.0-rc1
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/LICENSE +0 -0
- package/README.md +155 -46
- package/dist/cli.cjs +220 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +221 -0
- package/dist/index.cjs +212 -0
- package/dist/index.d.cts +52 -0
- package/dist/index.d.mts +52 -0
- package/dist/index.mjs +207 -0
- package/dist/reg.wasm +0 -0
- package/dist/runner.cjs +228 -0
- package/dist/runner.d.cts +1 -0
- package/dist/runner.d.mts +1 -0
- package/dist/runner.mjs +228 -0
- package/dist/shared/report-worker.js +1766 -0
- package/{template → dist/shared}/worker_pre.js +0 -0
- package/dist/tracing-Bo38b9aR.mjs +422 -0
- package/dist/tracing-Cf9TNPXQ.cjs +543 -0
- package/dist/ximgdiff-B16oe7EL.cjs +41 -0
- package/dist/ximgdiff-D8GAJaxA.mjs +41 -0
- package/package.json +79 -113
- package/dist/.keep +0 -0
- package/dist/cli.js +0 -214
- package/dist/diff.js +0 -80
- package/dist/icon.js +0 -12
- package/dist/image-finder.js +0 -25
- package/dist/index.js +0 -200
- package/dist/log.js +0 -21
- package/dist/process-adaptor.js +0 -40
- package/dist/report.js +0 -170
- package/dist/tracing.js +0 -169
- package/report/assets/favicon_failure.png +0 -0
- package/report/assets/favicon_success.png +0 -0
- package/report/sample/actual/sample.png +0 -0
- package/report/sample/diff/sample.png +0 -0
- package/report/sample/expected/sample.png +0 -0
- package/report/ui/dist/report.js +0 -123
- package/report/ui/dist/style.css +0 -1
- package/report/ui/dist/worker.js +0 -1535
- package/template/template.html +0 -20
|
@@ -0,0 +1,1766 @@
|
|
|
1
|
+
(function() {
|
|
2
|
+
"use strict";
|
|
3
|
+
function isArray(value) {
|
|
4
|
+
return !Array.isArray ? getTag(value) === "[object Array]" : Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
function baseToString(value) {
|
|
7
|
+
if (typeof value == "string") return value;
|
|
8
|
+
if (typeof value === "bigint") return value.toString();
|
|
9
|
+
const result = value + "";
|
|
10
|
+
return result == "0" && 1 / value == -Infinity ? "-0" : result;
|
|
11
|
+
}
|
|
12
|
+
function toString(value) {
|
|
13
|
+
return value == null ? "" : baseToString(value);
|
|
14
|
+
}
|
|
15
|
+
function isString(value) {
|
|
16
|
+
return typeof value === "string";
|
|
17
|
+
}
|
|
18
|
+
function isNumber(value) {
|
|
19
|
+
return typeof value === "number";
|
|
20
|
+
}
|
|
21
|
+
function isBoolean(value) {
|
|
22
|
+
return value === true || value === false || isObjectLike(value) && getTag(value) == "[object Boolean]";
|
|
23
|
+
}
|
|
24
|
+
function isObject(value) {
|
|
25
|
+
return typeof value === "object";
|
|
26
|
+
}
|
|
27
|
+
function isObjectLike(value) {
|
|
28
|
+
return isObject(value) && value !== null;
|
|
29
|
+
}
|
|
30
|
+
function isDefined(value) {
|
|
31
|
+
return value !== void 0 && value !== null;
|
|
32
|
+
}
|
|
33
|
+
function isBlank(value) {
|
|
34
|
+
return !value.trim().length;
|
|
35
|
+
}
|
|
36
|
+
function getTag(value) {
|
|
37
|
+
return value == null ? value === void 0 ? "[object Undefined]" : "[object Null]" : Object.prototype.toString.call(value);
|
|
38
|
+
}
|
|
39
|
+
const INCORRECT_INDEX_TYPE = "Incorrect 'index' type";
|
|
40
|
+
const INVALID_DOC_INDEX = "Invalid doc index: must be a non-negative integer within the bounds of the docs array";
|
|
41
|
+
const LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY = (key) => `Invalid value for key ${key}`;
|
|
42
|
+
const PATTERN_LENGTH_TOO_LARGE = (max) => `Pattern length exceeds max of ${max}.`;
|
|
43
|
+
const MISSING_KEY_PROPERTY = (name) => `Missing ${name} property in key`;
|
|
44
|
+
const INVALID_KEY_WEIGHT_VALUE = (key) => `Property 'weight' in key '${key}' must be a positive integer`;
|
|
45
|
+
const FUSE_MATCH_TOKEN_SEARCH_UNSUPPORTED = "Fuse.match does not support useTokenSearch: token search requires corpus-level statistics (df, fieldCount) that a one-off string comparison does not have. Use new Fuse(...).search(...) instead.";
|
|
46
|
+
const hasOwn = Object.prototype.hasOwnProperty;
|
|
47
|
+
var KeyStore = class {
|
|
48
|
+
constructor(keys) {
|
|
49
|
+
this._keys = [];
|
|
50
|
+
this._keyMap = {};
|
|
51
|
+
let totalWeight = 0;
|
|
52
|
+
keys.forEach((key) => {
|
|
53
|
+
const obj = createKey(key);
|
|
54
|
+
this._keys.push(obj);
|
|
55
|
+
this._keyMap[obj.id] = obj;
|
|
56
|
+
totalWeight += obj.weight;
|
|
57
|
+
});
|
|
58
|
+
this._keys.forEach((key) => {
|
|
59
|
+
key.weight /= totalWeight;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
get(keyId) {
|
|
63
|
+
return this._keyMap[keyId];
|
|
64
|
+
}
|
|
65
|
+
keys() {
|
|
66
|
+
return this._keys;
|
|
67
|
+
}
|
|
68
|
+
toJSON() {
|
|
69
|
+
return JSON.stringify(this._keys);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
function createKey(key) {
|
|
73
|
+
let path = null;
|
|
74
|
+
let id = null;
|
|
75
|
+
let src = null;
|
|
76
|
+
let weight = 1;
|
|
77
|
+
let getFn = null;
|
|
78
|
+
if (isString(key) || isArray(key)) {
|
|
79
|
+
src = key;
|
|
80
|
+
path = createKeyPath(key);
|
|
81
|
+
id = createKeyId(key);
|
|
82
|
+
} else {
|
|
83
|
+
if (!hasOwn.call(key, "name")) throw new Error(MISSING_KEY_PROPERTY("name"));
|
|
84
|
+
const name = key.name;
|
|
85
|
+
src = name;
|
|
86
|
+
if (hasOwn.call(key, "weight") && key.weight !== void 0) {
|
|
87
|
+
weight = key.weight;
|
|
88
|
+
if (weight <= 0) throw new Error(INVALID_KEY_WEIGHT_VALUE(createKeyId(name)));
|
|
89
|
+
}
|
|
90
|
+
path = createKeyPath(name);
|
|
91
|
+
id = createKeyId(name);
|
|
92
|
+
getFn = key.getFn ?? null;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
path,
|
|
96
|
+
id,
|
|
97
|
+
weight,
|
|
98
|
+
src,
|
|
99
|
+
getFn
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function createKeyPath(key) {
|
|
103
|
+
return isArray(key) ? key : key.split(".");
|
|
104
|
+
}
|
|
105
|
+
function createKeyId(key) {
|
|
106
|
+
return isArray(key) ? key.join(".") : key;
|
|
107
|
+
}
|
|
108
|
+
function get(obj, path) {
|
|
109
|
+
const list = [];
|
|
110
|
+
let arr = false;
|
|
111
|
+
const deepGet = (obj2, path2, index, arrayIndex) => {
|
|
112
|
+
if (!isDefined(obj2)) return;
|
|
113
|
+
if (!path2[index]) list.push(arrayIndex !== void 0 ? {
|
|
114
|
+
v: obj2,
|
|
115
|
+
i: arrayIndex
|
|
116
|
+
} : obj2);
|
|
117
|
+
else {
|
|
118
|
+
const value = obj2[path2[index]];
|
|
119
|
+
if (!isDefined(value)) return;
|
|
120
|
+
if (index === path2.length - 1 && (isString(value) || isNumber(value) || isBoolean(value) || typeof value === "bigint")) list.push(arrayIndex !== void 0 ? {
|
|
121
|
+
v: toString(value),
|
|
122
|
+
i: arrayIndex
|
|
123
|
+
} : toString(value));
|
|
124
|
+
else if (isArray(value)) {
|
|
125
|
+
arr = true;
|
|
126
|
+
for (let i = 0, len = value.length; i < len; i += 1) deepGet(value[i], path2, index + 1, i);
|
|
127
|
+
} else if (path2.length) deepGet(value, path2, index + 1, arrayIndex);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
deepGet(obj, isString(path) ? path.split(".") : path, 0);
|
|
131
|
+
return arr ? list : list[0];
|
|
132
|
+
}
|
|
133
|
+
const MatchOptions = {
|
|
134
|
+
includeMatches: false,
|
|
135
|
+
findAllMatches: false,
|
|
136
|
+
minMatchCharLength: 1
|
|
137
|
+
};
|
|
138
|
+
const BasicOptions = {
|
|
139
|
+
isCaseSensitive: false,
|
|
140
|
+
ignoreDiacritics: false,
|
|
141
|
+
includeScore: false,
|
|
142
|
+
keys: [],
|
|
143
|
+
shouldSort: true,
|
|
144
|
+
sortFn: (a, b) => a.score === b.score ? a.idx < b.idx ? -1 : 1 : a.score < b.score ? -1 : 1
|
|
145
|
+
};
|
|
146
|
+
const FuzzyOptions = {
|
|
147
|
+
location: 0,
|
|
148
|
+
threshold: 0.6,
|
|
149
|
+
distance: 100
|
|
150
|
+
};
|
|
151
|
+
const AdvancedOptions = {
|
|
152
|
+
useExtendedSearch: false,
|
|
153
|
+
useTokenSearch: false,
|
|
154
|
+
tokenize: void 0,
|
|
155
|
+
tokenMatch: "any",
|
|
156
|
+
getFn: get,
|
|
157
|
+
ignoreLocation: false,
|
|
158
|
+
ignoreFieldNorm: false,
|
|
159
|
+
fieldNormWeight: 1
|
|
160
|
+
};
|
|
161
|
+
const Config = Object.freeze({
|
|
162
|
+
...BasicOptions,
|
|
163
|
+
...MatchOptions,
|
|
164
|
+
...FuzzyOptions,
|
|
165
|
+
...AdvancedOptions
|
|
166
|
+
});
|
|
167
|
+
function isWordSeparator(code) {
|
|
168
|
+
return code >= 9 && code <= 13 || code === 32 || code === 160;
|
|
169
|
+
}
|
|
170
|
+
function norm(weight = 1, mantissa = 3) {
|
|
171
|
+
const cache = /* @__PURE__ */ new Map();
|
|
172
|
+
const m = Math.pow(10, mantissa);
|
|
173
|
+
return {
|
|
174
|
+
get(value) {
|
|
175
|
+
let numTokens = 0;
|
|
176
|
+
let inWord = false;
|
|
177
|
+
for (let i = 0; i < value.length; i++) if (!isWordSeparator(value.charCodeAt(i))) {
|
|
178
|
+
if (!inWord) {
|
|
179
|
+
numTokens++;
|
|
180
|
+
inWord = true;
|
|
181
|
+
}
|
|
182
|
+
} else inWord = false;
|
|
183
|
+
if (numTokens === 0) numTokens = 1;
|
|
184
|
+
if (cache.has(numTokens)) return cache.get(numTokens);
|
|
185
|
+
const n = Math.round(m / Math.pow(numTokens, 0.5 * weight)) / m;
|
|
186
|
+
cache.set(numTokens, n);
|
|
187
|
+
return n;
|
|
188
|
+
},
|
|
189
|
+
clear() {
|
|
190
|
+
cache.clear();
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
var FuseIndex = class {
|
|
195
|
+
constructor({ getFn = Config.getFn, fieldNormWeight = Config.fieldNormWeight } = {}) {
|
|
196
|
+
this.norm = norm(fieldNormWeight, 3);
|
|
197
|
+
this.getFn = getFn;
|
|
198
|
+
this.isCreated = false;
|
|
199
|
+
this.docs = [];
|
|
200
|
+
this.keys = [];
|
|
201
|
+
this._keysMap = {};
|
|
202
|
+
this.setIndexRecords();
|
|
203
|
+
}
|
|
204
|
+
setSources(docs = []) {
|
|
205
|
+
this.docs = docs;
|
|
206
|
+
}
|
|
207
|
+
setIndexRecords(records = []) {
|
|
208
|
+
this.records = records;
|
|
209
|
+
}
|
|
210
|
+
setKeys(keys = []) {
|
|
211
|
+
this.keys = keys;
|
|
212
|
+
this._keysMap = {};
|
|
213
|
+
keys.forEach((key, idx) => {
|
|
214
|
+
this._keysMap[key.id] = idx;
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
create() {
|
|
218
|
+
if (this.isCreated || !this.docs.length) return;
|
|
219
|
+
this.isCreated = true;
|
|
220
|
+
const len = this.docs.length;
|
|
221
|
+
this.records = new Array(len);
|
|
222
|
+
let recordCount = 0;
|
|
223
|
+
if (isString(this.docs[0])) for (let i = 0; i < len; i++) {
|
|
224
|
+
const record = this._createStringRecord(this.docs[i], i);
|
|
225
|
+
if (record) this.records[recordCount++] = record;
|
|
226
|
+
}
|
|
227
|
+
else for (let i = 0; i < len; i++) this.records[recordCount++] = this._createObjectRecord(this.docs[i], i);
|
|
228
|
+
this.records.length = recordCount;
|
|
229
|
+
this.norm.clear();
|
|
230
|
+
}
|
|
231
|
+
add(doc, docIndex) {
|
|
232
|
+
if (!Number.isInteger(docIndex) || docIndex < 0) throw new Error(INVALID_DOC_INDEX);
|
|
233
|
+
if (isString(doc)) {
|
|
234
|
+
const record2 = this._createStringRecord(doc, docIndex);
|
|
235
|
+
if (record2) this.records.push(record2);
|
|
236
|
+
return record2;
|
|
237
|
+
}
|
|
238
|
+
const record = this._createObjectRecord(doc, docIndex);
|
|
239
|
+
this.records.push(record);
|
|
240
|
+
return record;
|
|
241
|
+
}
|
|
242
|
+
removeAt(idx) {
|
|
243
|
+
if (!Number.isInteger(idx) || idx < 0) throw new Error(INVALID_DOC_INDEX);
|
|
244
|
+
for (let i = 0, len = this.records.length; i < len; i += 1) if (this.records[i].i === idx) {
|
|
245
|
+
this.records.splice(i, 1);
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
for (let i = 0, len = this.records.length; i < len; i += 1) if (this.records[i].i > idx) this.records[i].i -= 1;
|
|
249
|
+
}
|
|
250
|
+
removeAll(indices) {
|
|
251
|
+
const toRemove = /* @__PURE__ */ new Set();
|
|
252
|
+
for (const v of indices) if (Number.isInteger(v) && v >= 0) toRemove.add(v);
|
|
253
|
+
if (toRemove.size === 0) return;
|
|
254
|
+
this.records = this.records.filter((r) => !toRemove.has(r.i));
|
|
255
|
+
const sorted = Array.from(toRemove).sort((a, b) => a - b);
|
|
256
|
+
for (const record of this.records) {
|
|
257
|
+
let lo = 0;
|
|
258
|
+
let hi = sorted.length;
|
|
259
|
+
while (lo < hi) {
|
|
260
|
+
const mid = lo + hi >>> 1;
|
|
261
|
+
if (sorted[mid] < record.i) lo = mid + 1;
|
|
262
|
+
else hi = mid;
|
|
263
|
+
}
|
|
264
|
+
record.i -= lo;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
getValueForItemAtKeyId(item, keyId) {
|
|
268
|
+
return item[this._keysMap[keyId]];
|
|
269
|
+
}
|
|
270
|
+
size() {
|
|
271
|
+
return this.records.length;
|
|
272
|
+
}
|
|
273
|
+
_createStringRecord(doc, docIndex) {
|
|
274
|
+
if (!isDefined(doc) || isBlank(doc)) return null;
|
|
275
|
+
return {
|
|
276
|
+
v: doc,
|
|
277
|
+
i: docIndex,
|
|
278
|
+
n: this.norm.get(doc)
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
_createObjectRecord(doc, docIndex) {
|
|
282
|
+
const record = {
|
|
283
|
+
i: docIndex,
|
|
284
|
+
$: {}
|
|
285
|
+
};
|
|
286
|
+
for (let keyIndex = 0, keyLen = this.keys.length; keyIndex < keyLen; keyIndex++) {
|
|
287
|
+
const key = this.keys[keyIndex];
|
|
288
|
+
const value = key.getFn ? key.getFn(doc) : this.getFn(doc, key.path);
|
|
289
|
+
if (!isDefined(value)) continue;
|
|
290
|
+
if (isArray(value)) {
|
|
291
|
+
const subRecords = [];
|
|
292
|
+
for (let i = 0, len = value.length; i < len; i += 1) {
|
|
293
|
+
const item = value[i];
|
|
294
|
+
if (!isDefined(item)) continue;
|
|
295
|
+
if (isString(item)) {
|
|
296
|
+
if (!isBlank(item)) {
|
|
297
|
+
const subRecord = {
|
|
298
|
+
v: item,
|
|
299
|
+
i,
|
|
300
|
+
n: this.norm.get(item)
|
|
301
|
+
};
|
|
302
|
+
subRecords.push(subRecord);
|
|
303
|
+
}
|
|
304
|
+
} else if (isDefined(item.v)) {
|
|
305
|
+
const text = isString(item.v) ? item.v : toString(item.v);
|
|
306
|
+
if (!isBlank(text)) {
|
|
307
|
+
const subRecord = {
|
|
308
|
+
v: text,
|
|
309
|
+
i: item.i,
|
|
310
|
+
n: this.norm.get(text)
|
|
311
|
+
};
|
|
312
|
+
subRecords.push(subRecord);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
record.$[keyIndex] = subRecords;
|
|
317
|
+
} else if (isString(value) && !isBlank(value)) {
|
|
318
|
+
const subRecord = {
|
|
319
|
+
v: value,
|
|
320
|
+
n: this.norm.get(value)
|
|
321
|
+
};
|
|
322
|
+
record.$[keyIndex] = subRecord;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return record;
|
|
326
|
+
}
|
|
327
|
+
toJSON() {
|
|
328
|
+
return {
|
|
329
|
+
keys: this.keys.map(({ getFn, ...key }) => key),
|
|
330
|
+
records: this.records
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
function createIndex(keys, docs, { getFn = Config.getFn, fieldNormWeight = Config.fieldNormWeight } = {}) {
|
|
335
|
+
const myIndex = new FuseIndex({
|
|
336
|
+
getFn,
|
|
337
|
+
fieldNormWeight
|
|
338
|
+
});
|
|
339
|
+
myIndex.setKeys(keys.map(createKey));
|
|
340
|
+
myIndex.setSources(docs);
|
|
341
|
+
myIndex.create();
|
|
342
|
+
return myIndex;
|
|
343
|
+
}
|
|
344
|
+
function parseIndex(data, { getFn = Config.getFn, fieldNormWeight = Config.fieldNormWeight } = {}) {
|
|
345
|
+
const { keys, records } = data;
|
|
346
|
+
const myIndex = new FuseIndex({
|
|
347
|
+
getFn,
|
|
348
|
+
fieldNormWeight
|
|
349
|
+
});
|
|
350
|
+
myIndex.setKeys(keys);
|
|
351
|
+
myIndex.setIndexRecords(records);
|
|
352
|
+
return myIndex;
|
|
353
|
+
}
|
|
354
|
+
function convertMaskToIndices(matchmask = [], minMatchCharLength = Config.minMatchCharLength) {
|
|
355
|
+
const indices = [];
|
|
356
|
+
let start = -1;
|
|
357
|
+
let end = -1;
|
|
358
|
+
let i = 0;
|
|
359
|
+
for (let len = matchmask.length; i < len; i += 1) {
|
|
360
|
+
const match = matchmask[i];
|
|
361
|
+
if (match && start === -1) start = i;
|
|
362
|
+
else if (!match && start !== -1) {
|
|
363
|
+
end = i - 1;
|
|
364
|
+
if (end - start + 1 >= minMatchCharLength) indices.push([start, end]);
|
|
365
|
+
start = -1;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
if (matchmask[i - 1] && i - start >= minMatchCharLength) indices.push([start, i - 1]);
|
|
369
|
+
return indices;
|
|
370
|
+
}
|
|
371
|
+
function search(text, pattern, patternAlphabet, { location = Config.location, distance = Config.distance, threshold = Config.threshold, findAllMatches = Config.findAllMatches, minMatchCharLength = Config.minMatchCharLength, includeMatches = Config.includeMatches, ignoreLocation = Config.ignoreLocation } = {}) {
|
|
372
|
+
if (pattern.length > 32) throw new Error(PATTERN_LENGTH_TOO_LARGE(32));
|
|
373
|
+
const patternLen = pattern.length;
|
|
374
|
+
const textLen = text.length;
|
|
375
|
+
const expectedLocation = Math.max(0, Math.min(location, textLen));
|
|
376
|
+
let currentThreshold = threshold;
|
|
377
|
+
let bestLocation = expectedLocation;
|
|
378
|
+
const calcScore = (errors, currentLocation) => {
|
|
379
|
+
const accuracy = errors / patternLen;
|
|
380
|
+
if (ignoreLocation) return accuracy;
|
|
381
|
+
const proximity = Math.abs(expectedLocation - currentLocation);
|
|
382
|
+
if (!distance) return proximity ? 1 : accuracy;
|
|
383
|
+
return accuracy + proximity / distance;
|
|
384
|
+
};
|
|
385
|
+
const computeMatches = minMatchCharLength > 1 || includeMatches;
|
|
386
|
+
const matchMask = computeMatches ? Array(textLen) : [];
|
|
387
|
+
let index;
|
|
388
|
+
while ((index = text.indexOf(pattern, bestLocation)) > -1) {
|
|
389
|
+
const score = calcScore(0, index);
|
|
390
|
+
currentThreshold = Math.min(score, currentThreshold);
|
|
391
|
+
bestLocation = index + patternLen;
|
|
392
|
+
if (computeMatches) {
|
|
393
|
+
let i = 0;
|
|
394
|
+
while (i < patternLen) {
|
|
395
|
+
matchMask[index + i] = 1;
|
|
396
|
+
i += 1;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
bestLocation = -1;
|
|
401
|
+
let lastBitArr = [];
|
|
402
|
+
let finalScore = 1;
|
|
403
|
+
let bestErrors = 0;
|
|
404
|
+
let binMax = patternLen + textLen;
|
|
405
|
+
const mask = 1 << patternLen - 1;
|
|
406
|
+
for (let i = 0; i < patternLen; i += 1) {
|
|
407
|
+
let binMin = 0;
|
|
408
|
+
let binMid = binMax;
|
|
409
|
+
while (binMin < binMid) {
|
|
410
|
+
if (calcScore(i, expectedLocation + binMid) <= currentThreshold) binMin = binMid;
|
|
411
|
+
else binMax = binMid;
|
|
412
|
+
binMid = Math.floor((binMax - binMin) / 2 + binMin);
|
|
413
|
+
}
|
|
414
|
+
binMax = binMid;
|
|
415
|
+
let start = Math.max(1, expectedLocation - binMid + 1);
|
|
416
|
+
const finish = findAllMatches ? textLen : Math.min(expectedLocation + binMid, textLen) + patternLen;
|
|
417
|
+
const bitArr = Array(finish + 2);
|
|
418
|
+
bitArr[finish + 1] = (1 << i) - 1;
|
|
419
|
+
for (let j = finish; j >= start; j -= 1) {
|
|
420
|
+
const currentLocation = j - 1;
|
|
421
|
+
const charMatch = patternAlphabet[text[currentLocation]];
|
|
422
|
+
bitArr[j] = (bitArr[j + 1] << 1 | 1) & charMatch;
|
|
423
|
+
if (i) bitArr[j] |= (lastBitArr[j + 1] | lastBitArr[j]) << 1 | 1 | lastBitArr[j + 1];
|
|
424
|
+
if (bitArr[j] & mask) {
|
|
425
|
+
finalScore = calcScore(i, currentLocation);
|
|
426
|
+
if (finalScore <= currentThreshold) {
|
|
427
|
+
currentThreshold = finalScore;
|
|
428
|
+
bestLocation = currentLocation;
|
|
429
|
+
bestErrors = i;
|
|
430
|
+
if (bestLocation <= expectedLocation) break;
|
|
431
|
+
start = Math.max(1, 2 * expectedLocation - bestLocation);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (calcScore(i + 1, expectedLocation) > currentThreshold) break;
|
|
436
|
+
lastBitArr = bitArr;
|
|
437
|
+
}
|
|
438
|
+
if (computeMatches && bestLocation >= 0) {
|
|
439
|
+
const matchEnd = Math.min(textLen - 1, bestLocation + patternLen - 1 + bestErrors);
|
|
440
|
+
for (let k = bestLocation; k <= matchEnd; k += 1) if (patternAlphabet[text[k]]) matchMask[k] = 1;
|
|
441
|
+
}
|
|
442
|
+
const result = {
|
|
443
|
+
isMatch: bestLocation >= 0,
|
|
444
|
+
score: Math.max(1e-3, finalScore)
|
|
445
|
+
};
|
|
446
|
+
if (computeMatches) {
|
|
447
|
+
const indices = convertMaskToIndices(matchMask, minMatchCharLength);
|
|
448
|
+
if (!indices.length) result.isMatch = false;
|
|
449
|
+
else if (includeMatches) result.indices = indices;
|
|
450
|
+
}
|
|
451
|
+
return result;
|
|
452
|
+
}
|
|
453
|
+
function createPatternAlphabet(pattern) {
|
|
454
|
+
const mask = {};
|
|
455
|
+
for (let i = 0, len = pattern.length; i < len; i += 1) {
|
|
456
|
+
const char = pattern.charAt(i);
|
|
457
|
+
mask[char] = (mask[char] || 0) | 1 << len - i - 1;
|
|
458
|
+
}
|
|
459
|
+
return mask;
|
|
460
|
+
}
|
|
461
|
+
function mergeIndices(indices) {
|
|
462
|
+
if (indices.length <= 1) return indices;
|
|
463
|
+
indices.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
464
|
+
const merged = [indices[0]];
|
|
465
|
+
for (let i = 1, len = indices.length; i < len; i += 1) {
|
|
466
|
+
const last = merged[merged.length - 1];
|
|
467
|
+
const curr = indices[i];
|
|
468
|
+
if (curr[0] <= last[1] + 1) last[1] = Math.max(last[1], curr[1]);
|
|
469
|
+
else merged.push(curr);
|
|
470
|
+
}
|
|
471
|
+
return merged;
|
|
472
|
+
}
|
|
473
|
+
const NON_DECOMPOSABLE_MAP = {
|
|
474
|
+
"ł": "l",
|
|
475
|
+
"Ł": "L",
|
|
476
|
+
"đ": "d",
|
|
477
|
+
"Đ": "D",
|
|
478
|
+
"ø": "o",
|
|
479
|
+
"Ø": "O",
|
|
480
|
+
"ħ": "h",
|
|
481
|
+
"Ħ": "H",
|
|
482
|
+
"ŧ": "t",
|
|
483
|
+
"Ŧ": "T",
|
|
484
|
+
"ı": "i",
|
|
485
|
+
"ß": "ss"
|
|
486
|
+
};
|
|
487
|
+
const NON_DECOMPOSABLE_RE = new RegExp("[" + Object.keys(NON_DECOMPOSABLE_MAP).join("") + "]", "g");
|
|
488
|
+
const stripDiacritics = typeof String.prototype.normalize === "function" ? (str) => str.normalize("NFD").replace(/[\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u07FD\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D3-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u09FE\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0AFA-\u0AFF\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C04\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D00-\u0D03\u0D3B\u0D3C\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF7-\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA8FF\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9E5\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F]/g, "").replace(NON_DECOMPOSABLE_RE, (ch) => NON_DECOMPOSABLE_MAP[ch]) : (str) => str;
|
|
489
|
+
var BitapSearch = class {
|
|
490
|
+
constructor(pattern, { location = Config.location, threshold = Config.threshold, distance = Config.distance, includeMatches = Config.includeMatches, findAllMatches = Config.findAllMatches, minMatchCharLength = Config.minMatchCharLength, isCaseSensitive = Config.isCaseSensitive, ignoreDiacritics = Config.ignoreDiacritics, ignoreLocation = Config.ignoreLocation } = {}) {
|
|
491
|
+
this.options = {
|
|
492
|
+
location,
|
|
493
|
+
threshold,
|
|
494
|
+
distance,
|
|
495
|
+
includeMatches,
|
|
496
|
+
findAllMatches,
|
|
497
|
+
minMatchCharLength,
|
|
498
|
+
isCaseSensitive,
|
|
499
|
+
ignoreDiacritics,
|
|
500
|
+
ignoreLocation
|
|
501
|
+
};
|
|
502
|
+
pattern = isCaseSensitive ? pattern : pattern.toLowerCase();
|
|
503
|
+
pattern = ignoreDiacritics ? stripDiacritics(pattern) : pattern;
|
|
504
|
+
this.pattern = pattern;
|
|
505
|
+
this.chunks = [];
|
|
506
|
+
if (!this.pattern.length) return;
|
|
507
|
+
const addChunk = (pattern2, startIndex) => {
|
|
508
|
+
this.chunks.push({
|
|
509
|
+
pattern: pattern2,
|
|
510
|
+
alphabet: createPatternAlphabet(pattern2),
|
|
511
|
+
startIndex
|
|
512
|
+
});
|
|
513
|
+
};
|
|
514
|
+
const len = this.pattern.length;
|
|
515
|
+
if (len > 32) {
|
|
516
|
+
let i = 0;
|
|
517
|
+
const remainder = len % 32;
|
|
518
|
+
const end = len - remainder;
|
|
519
|
+
while (i < end) {
|
|
520
|
+
addChunk(this.pattern.substr(i, 32), i);
|
|
521
|
+
i += 32;
|
|
522
|
+
}
|
|
523
|
+
if (remainder) {
|
|
524
|
+
const startIndex = len - 32;
|
|
525
|
+
addChunk(this.pattern.substr(startIndex), startIndex);
|
|
526
|
+
}
|
|
527
|
+
} else addChunk(this.pattern, 0);
|
|
528
|
+
}
|
|
529
|
+
searchIn(text) {
|
|
530
|
+
const { isCaseSensitive, ignoreDiacritics, includeMatches } = this.options;
|
|
531
|
+
text = isCaseSensitive ? text : text.toLowerCase();
|
|
532
|
+
text = ignoreDiacritics ? stripDiacritics(text) : text;
|
|
533
|
+
if (this.pattern === text) {
|
|
534
|
+
if (text.length < this.options.minMatchCharLength) return {
|
|
535
|
+
isMatch: false,
|
|
536
|
+
score: 1
|
|
537
|
+
};
|
|
538
|
+
const result2 = {
|
|
539
|
+
isMatch: true,
|
|
540
|
+
score: 0
|
|
541
|
+
};
|
|
542
|
+
if (includeMatches) result2.indices = [[0, text.length - 1]];
|
|
543
|
+
return result2;
|
|
544
|
+
}
|
|
545
|
+
const { location, distance, threshold, findAllMatches, minMatchCharLength, ignoreLocation } = this.options;
|
|
546
|
+
const allIndices = [];
|
|
547
|
+
let totalScore = 0;
|
|
548
|
+
let hasMatches = false;
|
|
549
|
+
this.chunks.forEach(({ pattern, alphabet, startIndex }) => {
|
|
550
|
+
const { isMatch, score, indices } = search(text, pattern, alphabet, {
|
|
551
|
+
location: location + startIndex,
|
|
552
|
+
distance,
|
|
553
|
+
threshold,
|
|
554
|
+
findAllMatches,
|
|
555
|
+
minMatchCharLength,
|
|
556
|
+
includeMatches,
|
|
557
|
+
ignoreLocation
|
|
558
|
+
});
|
|
559
|
+
if (isMatch) hasMatches = true;
|
|
560
|
+
totalScore += score;
|
|
561
|
+
if (isMatch && indices) allIndices.push(...indices);
|
|
562
|
+
});
|
|
563
|
+
const result = {
|
|
564
|
+
isMatch: hasMatches,
|
|
565
|
+
score: hasMatches ? totalScore / this.chunks.length : 1
|
|
566
|
+
};
|
|
567
|
+
if (hasMatches && includeMatches) result.indices = mergeIndices(allIndices);
|
|
568
|
+
return result;
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
const MULTI_MATCH_TYPES = /* @__PURE__ */ new Set(["fuzzy", "include"]);
|
|
572
|
+
function isInverse(type) {
|
|
573
|
+
return type.startsWith("inverse");
|
|
574
|
+
}
|
|
575
|
+
const matchers = [
|
|
576
|
+
{
|
|
577
|
+
type: "exact",
|
|
578
|
+
multiRegex: /^="(.*)"$/,
|
|
579
|
+
singleRegex: /^=(.*)$/,
|
|
580
|
+
create: (pattern) => ({
|
|
581
|
+
type: "exact",
|
|
582
|
+
search(text) {
|
|
583
|
+
const isMatch = text === pattern;
|
|
584
|
+
return {
|
|
585
|
+
isMatch,
|
|
586
|
+
score: isMatch ? 0 : 1,
|
|
587
|
+
indices: [0, pattern.length - 1]
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
})
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
type: "include",
|
|
594
|
+
multiRegex: /^'"(.*)"$/,
|
|
595
|
+
singleRegex: /^'(.*)$/,
|
|
596
|
+
create: (pattern) => ({
|
|
597
|
+
type: "include",
|
|
598
|
+
search(text) {
|
|
599
|
+
let location = 0;
|
|
600
|
+
let index;
|
|
601
|
+
const indices = [];
|
|
602
|
+
const patternLen = pattern.length;
|
|
603
|
+
while ((index = text.indexOf(pattern, location)) > -1) {
|
|
604
|
+
location = index + patternLen;
|
|
605
|
+
indices.push([index, location - 1]);
|
|
606
|
+
}
|
|
607
|
+
const isMatch = !!indices.length;
|
|
608
|
+
return {
|
|
609
|
+
isMatch,
|
|
610
|
+
score: isMatch ? 0 : 1,
|
|
611
|
+
indices
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
})
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
type: "prefix-exact",
|
|
618
|
+
multiRegex: /^\^"(.*)"$/,
|
|
619
|
+
singleRegex: /^\^(.*)$/,
|
|
620
|
+
create: (pattern) => ({
|
|
621
|
+
type: "prefix-exact",
|
|
622
|
+
search(text) {
|
|
623
|
+
const isMatch = text.startsWith(pattern);
|
|
624
|
+
return {
|
|
625
|
+
isMatch,
|
|
626
|
+
score: isMatch ? 0 : 1,
|
|
627
|
+
indices: [0, pattern.length - 1]
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
})
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
type: "inverse-prefix-exact",
|
|
634
|
+
multiRegex: /^!\^"(.*)"$/,
|
|
635
|
+
singleRegex: /^!\^(.*)$/,
|
|
636
|
+
create: (pattern) => ({
|
|
637
|
+
type: "inverse-prefix-exact",
|
|
638
|
+
search(text) {
|
|
639
|
+
const isMatch = !text.startsWith(pattern);
|
|
640
|
+
return {
|
|
641
|
+
isMatch,
|
|
642
|
+
score: isMatch ? 0 : 1,
|
|
643
|
+
indices: [0, text.length - 1]
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
})
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
type: "inverse-suffix-exact",
|
|
650
|
+
multiRegex: /^!"(.*)"\$$/,
|
|
651
|
+
singleRegex: /^!(.*)\$$/,
|
|
652
|
+
create: (pattern) => ({
|
|
653
|
+
type: "inverse-suffix-exact",
|
|
654
|
+
search(text) {
|
|
655
|
+
const isMatch = !text.endsWith(pattern);
|
|
656
|
+
return {
|
|
657
|
+
isMatch,
|
|
658
|
+
score: isMatch ? 0 : 1,
|
|
659
|
+
indices: [0, text.length - 1]
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
})
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
type: "suffix-exact",
|
|
666
|
+
multiRegex: /^"(.*)"\$$/,
|
|
667
|
+
singleRegex: /^(.*)\$$/,
|
|
668
|
+
create: (pattern) => ({
|
|
669
|
+
type: "suffix-exact",
|
|
670
|
+
search(text) {
|
|
671
|
+
const isMatch = text.endsWith(pattern);
|
|
672
|
+
return {
|
|
673
|
+
isMatch,
|
|
674
|
+
score: isMatch ? 0 : 1,
|
|
675
|
+
indices: [text.length - pattern.length, text.length - 1]
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
})
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
type: "inverse-exact",
|
|
682
|
+
multiRegex: /^!"(.*)"$/,
|
|
683
|
+
singleRegex: /^!(.*)$/,
|
|
684
|
+
create: (pattern) => ({
|
|
685
|
+
type: "inverse-exact",
|
|
686
|
+
search(text) {
|
|
687
|
+
const isMatch = text.indexOf(pattern) === -1;
|
|
688
|
+
return {
|
|
689
|
+
isMatch,
|
|
690
|
+
score: isMatch ? 0 : 1,
|
|
691
|
+
indices: [0, text.length - 1]
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
})
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
type: "fuzzy",
|
|
698
|
+
multiRegex: /^"(.*)"$/,
|
|
699
|
+
singleRegex: /^(.*)$/,
|
|
700
|
+
create: (pattern, options = {}) => {
|
|
701
|
+
const bitap = new BitapSearch(pattern, {
|
|
702
|
+
location: options.location ?? Config.location,
|
|
703
|
+
threshold: options.threshold ?? Config.threshold,
|
|
704
|
+
distance: options.distance ?? Config.distance,
|
|
705
|
+
includeMatches: options.includeMatches ?? Config.includeMatches,
|
|
706
|
+
findAllMatches: options.findAllMatches ?? Config.findAllMatches,
|
|
707
|
+
minMatchCharLength: options.minMatchCharLength ?? Config.minMatchCharLength,
|
|
708
|
+
isCaseSensitive: options.isCaseSensitive ?? Config.isCaseSensitive,
|
|
709
|
+
ignoreDiacritics: options.ignoreDiacritics ?? Config.ignoreDiacritics,
|
|
710
|
+
ignoreLocation: options.ignoreLocation ?? Config.ignoreLocation
|
|
711
|
+
});
|
|
712
|
+
return {
|
|
713
|
+
type: "fuzzy",
|
|
714
|
+
search(text) {
|
|
715
|
+
return bitap.searchIn(text);
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
];
|
|
721
|
+
const matchersLen = matchers.length;
|
|
722
|
+
const ESCAPED_PIPE = "\0";
|
|
723
|
+
const OR_TOKEN = "|";
|
|
724
|
+
function tokenize(pattern) {
|
|
725
|
+
const tokens = [];
|
|
726
|
+
const len = pattern.length;
|
|
727
|
+
let i = 0;
|
|
728
|
+
while (i < len) {
|
|
729
|
+
while (i < len && pattern[i] === " ") i++;
|
|
730
|
+
if (i >= len) break;
|
|
731
|
+
let j = i;
|
|
732
|
+
while (j < len && pattern[j] !== " " && pattern[j] !== '"') j++;
|
|
733
|
+
if (j < len && pattern[j] === '"') {
|
|
734
|
+
j++;
|
|
735
|
+
while (j < len) {
|
|
736
|
+
if (pattern[j] === '"') {
|
|
737
|
+
const next = j + 1;
|
|
738
|
+
if (next >= len || pattern[next] === " ") {
|
|
739
|
+
j++;
|
|
740
|
+
break;
|
|
741
|
+
}
|
|
742
|
+
if (pattern[next] === "$" && (next + 1 >= len || pattern[next + 1] === " ")) {
|
|
743
|
+
j += 2;
|
|
744
|
+
break;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
j++;
|
|
748
|
+
}
|
|
749
|
+
tokens.push(pattern.substring(i, j));
|
|
750
|
+
i = j;
|
|
751
|
+
} else {
|
|
752
|
+
while (j < len && pattern[j] !== " ") j++;
|
|
753
|
+
tokens.push(pattern.substring(i, j));
|
|
754
|
+
i = j;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
return tokens;
|
|
758
|
+
}
|
|
759
|
+
function getMatch(pattern, exp) {
|
|
760
|
+
const matches = pattern.match(exp);
|
|
761
|
+
return matches ? matches[1] : null;
|
|
762
|
+
}
|
|
763
|
+
function parseQuery(pattern, options = {}) {
|
|
764
|
+
return pattern.replace(/\\\|/g, ESCAPED_PIPE).split(OR_TOKEN).map((item) => {
|
|
765
|
+
const query = tokenize(item.replace(/\u0000/g, "|").trim()).filter((item2) => item2 && !!item2.trim());
|
|
766
|
+
const results = [];
|
|
767
|
+
for (let i = 0, len = query.length; i < len; i += 1) {
|
|
768
|
+
const queryItem = query[i];
|
|
769
|
+
let found = false;
|
|
770
|
+
let idx = -1;
|
|
771
|
+
while (!found && ++idx < matchersLen) {
|
|
772
|
+
const def = matchers[idx];
|
|
773
|
+
const token = getMatch(queryItem, def.multiRegex);
|
|
774
|
+
if (token) {
|
|
775
|
+
results.push(def.create(token, options));
|
|
776
|
+
found = true;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
if (found) continue;
|
|
780
|
+
idx = -1;
|
|
781
|
+
while (++idx < matchersLen) {
|
|
782
|
+
const def = matchers[idx];
|
|
783
|
+
const token = getMatch(queryItem, def.singleRegex);
|
|
784
|
+
if (token) {
|
|
785
|
+
results.push(def.create(token, options));
|
|
786
|
+
break;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
return results;
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
var ExtendedSearch = class {
|
|
794
|
+
constructor(pattern, { isCaseSensitive = Config.isCaseSensitive, ignoreDiacritics = Config.ignoreDiacritics, includeMatches = Config.includeMatches, minMatchCharLength = Config.minMatchCharLength, ignoreLocation = Config.ignoreLocation, findAllMatches = Config.findAllMatches, location = Config.location, threshold = Config.threshold, distance = Config.distance } = {}) {
|
|
795
|
+
this.query = null;
|
|
796
|
+
this.options = {
|
|
797
|
+
isCaseSensitive,
|
|
798
|
+
ignoreDiacritics,
|
|
799
|
+
includeMatches,
|
|
800
|
+
minMatchCharLength,
|
|
801
|
+
findAllMatches,
|
|
802
|
+
ignoreLocation,
|
|
803
|
+
location,
|
|
804
|
+
threshold,
|
|
805
|
+
distance
|
|
806
|
+
};
|
|
807
|
+
pattern = isCaseSensitive ? pattern : pattern.toLowerCase();
|
|
808
|
+
pattern = ignoreDiacritics ? stripDiacritics(pattern) : pattern;
|
|
809
|
+
this.pattern = pattern;
|
|
810
|
+
this.query = parseQuery(this.pattern, this.options);
|
|
811
|
+
}
|
|
812
|
+
static condition(_, options) {
|
|
813
|
+
return options.useExtendedSearch;
|
|
814
|
+
}
|
|
815
|
+
searchIn(text) {
|
|
816
|
+
const query = this.query;
|
|
817
|
+
if (!query) return {
|
|
818
|
+
isMatch: false,
|
|
819
|
+
score: 1
|
|
820
|
+
};
|
|
821
|
+
const { includeMatches, isCaseSensitive, ignoreDiacritics } = this.options;
|
|
822
|
+
text = isCaseSensitive ? text : text.toLowerCase();
|
|
823
|
+
text = ignoreDiacritics ? stripDiacritics(text) : text;
|
|
824
|
+
let numMatches = 0;
|
|
825
|
+
const allIndices = [];
|
|
826
|
+
let totalScore = 0;
|
|
827
|
+
let hasInverse = false;
|
|
828
|
+
for (let i = 0, qLen = query.length; i < qLen; i += 1) {
|
|
829
|
+
const searchers = query[i];
|
|
830
|
+
allIndices.length = 0;
|
|
831
|
+
numMatches = 0;
|
|
832
|
+
hasInverse = false;
|
|
833
|
+
for (let j = 0, pLen = searchers.length; j < pLen; j += 1) {
|
|
834
|
+
const matcher = searchers[j];
|
|
835
|
+
const { isMatch, indices, score } = matcher.search(text);
|
|
836
|
+
if (isMatch) {
|
|
837
|
+
numMatches += 1;
|
|
838
|
+
totalScore += score;
|
|
839
|
+
if (isInverse(matcher.type)) hasInverse = true;
|
|
840
|
+
if (includeMatches) if (MULTI_MATCH_TYPES.has(matcher.type)) allIndices.push(...indices);
|
|
841
|
+
else allIndices.push(indices);
|
|
842
|
+
} else {
|
|
843
|
+
totalScore = 0;
|
|
844
|
+
numMatches = 0;
|
|
845
|
+
allIndices.length = 0;
|
|
846
|
+
hasInverse = false;
|
|
847
|
+
break;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
if (numMatches) {
|
|
851
|
+
const result = {
|
|
852
|
+
isMatch: true,
|
|
853
|
+
score: totalScore / numMatches
|
|
854
|
+
};
|
|
855
|
+
if (hasInverse) result.hasInverse = true;
|
|
856
|
+
if (includeMatches) result.indices = mergeIndices(allIndices);
|
|
857
|
+
return result;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
return {
|
|
861
|
+
isMatch: false,
|
|
862
|
+
score: 1
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
const registeredSearchers = [];
|
|
867
|
+
function register(...args) {
|
|
868
|
+
registeredSearchers.push(...args);
|
|
869
|
+
}
|
|
870
|
+
function createSearcher(pattern, options) {
|
|
871
|
+
for (let i = 0, len = registeredSearchers.length; i < len; i += 1) {
|
|
872
|
+
const searcherClass = registeredSearchers[i];
|
|
873
|
+
if (searcherClass.condition(pattern, options)) return new searcherClass(pattern, options);
|
|
874
|
+
}
|
|
875
|
+
return new BitapSearch(pattern, options);
|
|
876
|
+
}
|
|
877
|
+
const LogicalOperator = {
|
|
878
|
+
AND: "$and",
|
|
879
|
+
OR: "$or"
|
|
880
|
+
};
|
|
881
|
+
const KeyType = {
|
|
882
|
+
PATH: "$path",
|
|
883
|
+
PATTERN: "$val"
|
|
884
|
+
};
|
|
885
|
+
const isExpression = (query) => !!(query[LogicalOperator.AND] || query[LogicalOperator.OR]);
|
|
886
|
+
const isPath = (query) => !!query[KeyType.PATH];
|
|
887
|
+
const isLeaf = (query) => !isArray(query) && isObject(query) && !isExpression(query);
|
|
888
|
+
const convertToExplicit = (query) => ({ [LogicalOperator.AND]: Object.keys(query).map((key) => ({ [key]: query[key] })) });
|
|
889
|
+
function parse(query, options, { auto = true } = {}) {
|
|
890
|
+
const next = (query2) => {
|
|
891
|
+
if (isString(query2)) {
|
|
892
|
+
const obj = {
|
|
893
|
+
keyId: null,
|
|
894
|
+
pattern: query2
|
|
895
|
+
};
|
|
896
|
+
if (auto) obj.searcher = createSearcher(query2, options);
|
|
897
|
+
return obj;
|
|
898
|
+
}
|
|
899
|
+
const keys = Object.keys(query2);
|
|
900
|
+
const isQueryPath = isPath(query2);
|
|
901
|
+
if (!isQueryPath && keys.length > 1 && !isExpression(query2)) return next(convertToExplicit(query2));
|
|
902
|
+
if (isLeaf(query2)) {
|
|
903
|
+
const key = isQueryPath ? query2[KeyType.PATH] : keys[0];
|
|
904
|
+
const pattern = isQueryPath ? query2[KeyType.PATTERN] : query2[key];
|
|
905
|
+
if (!isString(pattern)) throw new Error(LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY(key));
|
|
906
|
+
const obj = {
|
|
907
|
+
keyId: createKeyId(key),
|
|
908
|
+
pattern
|
|
909
|
+
};
|
|
910
|
+
if (auto) obj.searcher = createSearcher(pattern, options);
|
|
911
|
+
return obj;
|
|
912
|
+
}
|
|
913
|
+
const node = {
|
|
914
|
+
children: [],
|
|
915
|
+
operator: keys[0]
|
|
916
|
+
};
|
|
917
|
+
keys.forEach((key) => {
|
|
918
|
+
const value = query2[key];
|
|
919
|
+
if (isArray(value)) value.forEach((item) => {
|
|
920
|
+
node.children.push(next(item));
|
|
921
|
+
});
|
|
922
|
+
});
|
|
923
|
+
return node;
|
|
924
|
+
};
|
|
925
|
+
if (!isExpression(query)) query = convertToExplicit(query);
|
|
926
|
+
return next(query);
|
|
927
|
+
}
|
|
928
|
+
function computeScoreSingle(matches, { ignoreFieldNorm = Config.ignoreFieldNorm }) {
|
|
929
|
+
let totalScore = 1;
|
|
930
|
+
matches.forEach(({ key, norm: norm2, score }) => {
|
|
931
|
+
const weight = key ? key.weight : null;
|
|
932
|
+
totalScore *= Math.pow(score === 0 && weight ? Number.EPSILON : score, (weight || 1) * (ignoreFieldNorm ? 1 : norm2));
|
|
933
|
+
});
|
|
934
|
+
return totalScore;
|
|
935
|
+
}
|
|
936
|
+
function computeScore(results, { ignoreFieldNorm = Config.ignoreFieldNorm }) {
|
|
937
|
+
results.forEach((result) => {
|
|
938
|
+
result.score = computeScoreSingle(result.matches, { ignoreFieldNorm });
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
var MaxHeap = class {
|
|
942
|
+
constructor(limit, comparator) {
|
|
943
|
+
this.limit = limit;
|
|
944
|
+
this.heap = [];
|
|
945
|
+
this.comparator = comparator;
|
|
946
|
+
}
|
|
947
|
+
get size() {
|
|
948
|
+
return this.heap.length;
|
|
949
|
+
}
|
|
950
|
+
insert(item) {
|
|
951
|
+
if (this.size < this.limit) {
|
|
952
|
+
this.heap.push(item);
|
|
953
|
+
this._bubbleUp(this.size - 1);
|
|
954
|
+
} else if (this.comparator(item, this.heap[0]) < 0) {
|
|
955
|
+
this.heap[0] = item;
|
|
956
|
+
this._sinkDown(0);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
extractSorted() {
|
|
960
|
+
return this.heap.sort(this.comparator);
|
|
961
|
+
}
|
|
962
|
+
_bubbleUp(i) {
|
|
963
|
+
const heap = this.heap;
|
|
964
|
+
while (i > 0) {
|
|
965
|
+
const parent = i - 1 >> 1;
|
|
966
|
+
if (this.comparator(heap[i], heap[parent]) <= 0) break;
|
|
967
|
+
const tmp = heap[i];
|
|
968
|
+
heap[i] = heap[parent];
|
|
969
|
+
heap[parent] = tmp;
|
|
970
|
+
i = parent;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
_sinkDown(i) {
|
|
974
|
+
const heap = this.heap;
|
|
975
|
+
const len = heap.length;
|
|
976
|
+
let largest = i;
|
|
977
|
+
do {
|
|
978
|
+
i = largest;
|
|
979
|
+
const left = 2 * i + 1;
|
|
980
|
+
const right = 2 * i + 2;
|
|
981
|
+
if (left < len && this.comparator(heap[left], heap[largest]) > 0) largest = left;
|
|
982
|
+
if (right < len && this.comparator(heap[right], heap[largest]) > 0) largest = right;
|
|
983
|
+
if (largest !== i) {
|
|
984
|
+
const tmp = heap[i];
|
|
985
|
+
heap[i] = heap[largest];
|
|
986
|
+
heap[largest] = tmp;
|
|
987
|
+
}
|
|
988
|
+
} while (largest !== i);
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
function formatMatches(result) {
|
|
992
|
+
const matches = [];
|
|
993
|
+
result.matches.forEach((match) => {
|
|
994
|
+
if (!isDefined(match.indices) || !match.indices.length) return;
|
|
995
|
+
const obj = {
|
|
996
|
+
indices: match.indices,
|
|
997
|
+
value: match.value
|
|
998
|
+
};
|
|
999
|
+
if (match.key) obj.key = match.key.id;
|
|
1000
|
+
if (match.idx > -1) obj.refIndex = match.idx;
|
|
1001
|
+
matches.push(obj);
|
|
1002
|
+
});
|
|
1003
|
+
return matches;
|
|
1004
|
+
}
|
|
1005
|
+
function format(results, docs, { includeMatches = Config.includeMatches, includeScore = Config.includeScore } = {}) {
|
|
1006
|
+
return results.map((result) => {
|
|
1007
|
+
const { idx } = result;
|
|
1008
|
+
const data = {
|
|
1009
|
+
item: docs[idx],
|
|
1010
|
+
refIndex: idx
|
|
1011
|
+
};
|
|
1012
|
+
if (includeMatches) data.matches = formatMatches(result);
|
|
1013
|
+
if (includeScore) data.score = result.score;
|
|
1014
|
+
return data;
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
const DEFAULT_TOKEN = /[\p{L}\p{M}\p{N}_]+/gu;
|
|
1018
|
+
const warned = /* @__PURE__ */ new WeakSet();
|
|
1019
|
+
function warnNonGlobal(regex) {
|
|
1020
|
+
if (!warned.has(regex)) {
|
|
1021
|
+
warned.add(regex);
|
|
1022
|
+
console.warn(`[Fuse] tokenize regex ${regex} lacks the global flag; only the first match per text will be returned. Add the 'g' flag.`);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
function resolveTokenize(tokenize2) {
|
|
1026
|
+
if (typeof tokenize2 === "function") {
|
|
1027
|
+
let validated = false;
|
|
1028
|
+
return (text) => {
|
|
1029
|
+
const result = tokenize2(text);
|
|
1030
|
+
if (!validated) {
|
|
1031
|
+
validated = true;
|
|
1032
|
+
if (!Array.isArray(result) || result.some((t) => typeof t !== "string")) throw new Error(`[Fuse] tokenize function must return string[]; received ${Array.isArray(result) ? "array containing non-strings" : typeof result}.`);
|
|
1033
|
+
}
|
|
1034
|
+
return result;
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
if (tokenize2 instanceof RegExp) {
|
|
1038
|
+
if (!tokenize2.global) warnNonGlobal(tokenize2);
|
|
1039
|
+
return (text) => text.match(tokenize2) || [];
|
|
1040
|
+
}
|
|
1041
|
+
return (text) => text.match(DEFAULT_TOKEN) || [];
|
|
1042
|
+
}
|
|
1043
|
+
function createAnalyzer({ isCaseSensitive = false, ignoreDiacritics = false, tokenize: tokenize2 } = {}) {
|
|
1044
|
+
const tokenizeFn = resolveTokenize(tokenize2);
|
|
1045
|
+
return { tokenize(text) {
|
|
1046
|
+
if (!isCaseSensitive) text = text.toLowerCase();
|
|
1047
|
+
if (ignoreDiacritics) text = stripDiacritics(text);
|
|
1048
|
+
return tokenizeFn(text);
|
|
1049
|
+
} };
|
|
1050
|
+
}
|
|
1051
|
+
var TokenSearch = class {
|
|
1052
|
+
static condition(_, options) {
|
|
1053
|
+
return options.useTokenSearch;
|
|
1054
|
+
}
|
|
1055
|
+
constructor(pattern, options) {
|
|
1056
|
+
this.options = options;
|
|
1057
|
+
this.analyzer = createAnalyzer({
|
|
1058
|
+
isCaseSensitive: options.isCaseSensitive,
|
|
1059
|
+
ignoreDiacritics: options.ignoreDiacritics,
|
|
1060
|
+
tokenize: options.tokenize
|
|
1061
|
+
});
|
|
1062
|
+
const queryTerms = this.analyzer.tokenize(pattern);
|
|
1063
|
+
const { df, fieldCount } = options._invertedIndex;
|
|
1064
|
+
this.termSearchers = [];
|
|
1065
|
+
this.idfWeights = [];
|
|
1066
|
+
for (const term of queryTerms) {
|
|
1067
|
+
this.termSearchers.push(new BitapSearch(term, {
|
|
1068
|
+
location: options.location,
|
|
1069
|
+
threshold: options.threshold,
|
|
1070
|
+
distance: options.distance,
|
|
1071
|
+
includeMatches: options.includeMatches,
|
|
1072
|
+
findAllMatches: options.findAllMatches,
|
|
1073
|
+
minMatchCharLength: options.minMatchCharLength,
|
|
1074
|
+
isCaseSensitive: options.isCaseSensitive,
|
|
1075
|
+
ignoreDiacritics: options.ignoreDiacritics,
|
|
1076
|
+
ignoreLocation: true
|
|
1077
|
+
}));
|
|
1078
|
+
const docFreq = df.get(term) || 0;
|
|
1079
|
+
const idf = Math.log(1 + (fieldCount - docFreq + 0.5) / (docFreq + 0.5));
|
|
1080
|
+
this.idfWeights.push(idf);
|
|
1081
|
+
}
|
|
1082
|
+
this.combineAll = options.tokenMatch === "all";
|
|
1083
|
+
this.numTerms = this.termSearchers.length;
|
|
1084
|
+
this.useMask = this.numTerms <= 31;
|
|
1085
|
+
}
|
|
1086
|
+
searchIn(text) {
|
|
1087
|
+
if (!this.termSearchers.length) return {
|
|
1088
|
+
isMatch: false,
|
|
1089
|
+
score: 1
|
|
1090
|
+
};
|
|
1091
|
+
const allIndices = [];
|
|
1092
|
+
let weightedScore = 0;
|
|
1093
|
+
let maxPossibleScore = 0;
|
|
1094
|
+
let matchedCount = 0;
|
|
1095
|
+
let matchedMask = 0;
|
|
1096
|
+
const matchedTerms = this.combineAll && !this.useMask ? /* @__PURE__ */ new Set() : null;
|
|
1097
|
+
for (let i = 0; i < this.termSearchers.length; i++) {
|
|
1098
|
+
const result = this.termSearchers[i].searchIn(text);
|
|
1099
|
+
const idf = this.idfWeights[i];
|
|
1100
|
+
maxPossibleScore += idf;
|
|
1101
|
+
if (result.isMatch) {
|
|
1102
|
+
matchedCount++;
|
|
1103
|
+
weightedScore += idf * (1 - result.score);
|
|
1104
|
+
if (result.indices) allIndices.push(...result.indices);
|
|
1105
|
+
if (this.combineAll) if (this.useMask) matchedMask |= 1 << i;
|
|
1106
|
+
else matchedTerms.add(i);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
if (matchedCount === 0) return {
|
|
1110
|
+
isMatch: false,
|
|
1111
|
+
score: 1
|
|
1112
|
+
};
|
|
1113
|
+
const normalized = maxPossibleScore > 0 ? 1 - weightedScore / maxPossibleScore : 0;
|
|
1114
|
+
const searchResult = {
|
|
1115
|
+
isMatch: true,
|
|
1116
|
+
score: Math.max(1e-3, normalized)
|
|
1117
|
+
};
|
|
1118
|
+
if (this.options.includeMatches && allIndices.length) searchResult.indices = mergeIndices(allIndices);
|
|
1119
|
+
if (this.combineAll) {
|
|
1120
|
+
if (this.useMask) searchResult.matchedMask = matchedMask;
|
|
1121
|
+
else searchResult.matchedTerms = matchedTerms;
|
|
1122
|
+
searchResult.termCount = this.numTerms;
|
|
1123
|
+
}
|
|
1124
|
+
return searchResult;
|
|
1125
|
+
}
|
|
1126
|
+
};
|
|
1127
|
+
function addField(index, text, docIdx, analyzer) {
|
|
1128
|
+
const tokens = analyzer.tokenize(text);
|
|
1129
|
+
if (!tokens.length) return;
|
|
1130
|
+
index.fieldCount++;
|
|
1131
|
+
index.docFieldCount.set(docIdx, (index.docFieldCount.get(docIdx) || 0) + 1);
|
|
1132
|
+
const distinctTerms = new Set(tokens);
|
|
1133
|
+
let perDocTerms = index.docTermFieldHits.get(docIdx);
|
|
1134
|
+
if (!perDocTerms) {
|
|
1135
|
+
perDocTerms = /* @__PURE__ */ new Map();
|
|
1136
|
+
index.docTermFieldHits.set(docIdx, perDocTerms);
|
|
1137
|
+
}
|
|
1138
|
+
for (const term of distinctTerms) {
|
|
1139
|
+
perDocTerms.set(term, (perDocTerms.get(term) || 0) + 1);
|
|
1140
|
+
index.df.set(term, (index.df.get(term) || 0) + 1);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
function ingestRecord(index, record, keyCount, analyzer) {
|
|
1144
|
+
const { i: docIdx, v, $: fields } = record;
|
|
1145
|
+
if (v !== void 0) {
|
|
1146
|
+
addField(index, v, docIdx, analyzer);
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
if (!fields) return;
|
|
1150
|
+
for (let keyIdx = 0; keyIdx < keyCount; keyIdx++) {
|
|
1151
|
+
const value = fields[keyIdx];
|
|
1152
|
+
if (!value) continue;
|
|
1153
|
+
if (Array.isArray(value)) for (const sub of value) addField(index, sub.v, docIdx, analyzer);
|
|
1154
|
+
else addField(index, value.v, docIdx, analyzer);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
function buildInvertedIndex(records, keyCount, analyzer) {
|
|
1158
|
+
const index = {
|
|
1159
|
+
fieldCount: 0,
|
|
1160
|
+
df: /* @__PURE__ */ new Map(),
|
|
1161
|
+
docFieldCount: /* @__PURE__ */ new Map(),
|
|
1162
|
+
docTermFieldHits: /* @__PURE__ */ new Map()
|
|
1163
|
+
};
|
|
1164
|
+
for (const record of records) ingestRecord(index, record, keyCount, analyzer);
|
|
1165
|
+
return index;
|
|
1166
|
+
}
|
|
1167
|
+
function addToInvertedIndex(index, record, keyCount, analyzer) {
|
|
1168
|
+
ingestRecord(index, record, keyCount, analyzer);
|
|
1169
|
+
}
|
|
1170
|
+
function removeFromInvertedIndex(index, docIdx) {
|
|
1171
|
+
const fieldCount = index.docFieldCount.get(docIdx);
|
|
1172
|
+
if (fieldCount === void 0) return;
|
|
1173
|
+
index.fieldCount -= fieldCount;
|
|
1174
|
+
index.docFieldCount.delete(docIdx);
|
|
1175
|
+
const perDocTerms = index.docTermFieldHits.get(docIdx);
|
|
1176
|
+
if (!perDocTerms) return;
|
|
1177
|
+
for (const [term, hits] of perDocTerms) {
|
|
1178
|
+
const next = (index.df.get(term) || 0) - hits;
|
|
1179
|
+
if (next <= 0) index.df.delete(term);
|
|
1180
|
+
else index.df.set(term, next);
|
|
1181
|
+
}
|
|
1182
|
+
index.docTermFieldHits.delete(docIdx);
|
|
1183
|
+
}
|
|
1184
|
+
function removeAndShiftInvertedIndex(index, removedIndices) {
|
|
1185
|
+
if (removedIndices.length === 0) return;
|
|
1186
|
+
const sorted = Array.from(new Set(removedIndices)).sort((a, b) => a - b);
|
|
1187
|
+
for (const idx of sorted) removeFromInvertedIndex(index, idx);
|
|
1188
|
+
const shift = (oldIdx) => {
|
|
1189
|
+
let lo = 0;
|
|
1190
|
+
let hi = sorted.length;
|
|
1191
|
+
while (lo < hi) {
|
|
1192
|
+
const mid = lo + hi >>> 1;
|
|
1193
|
+
if (sorted[mid] < oldIdx) lo = mid + 1;
|
|
1194
|
+
else hi = mid;
|
|
1195
|
+
}
|
|
1196
|
+
return oldIdx - lo;
|
|
1197
|
+
};
|
|
1198
|
+
const firstRemoved = sorted[0];
|
|
1199
|
+
const shiftedDocFieldCount = /* @__PURE__ */ new Map();
|
|
1200
|
+
for (const [oldKey, count] of index.docFieldCount) shiftedDocFieldCount.set(oldKey > firstRemoved ? shift(oldKey) : oldKey, count);
|
|
1201
|
+
index.docFieldCount = shiftedDocFieldCount;
|
|
1202
|
+
const shiftedDocTermFieldHits = /* @__PURE__ */ new Map();
|
|
1203
|
+
for (const [oldKey, terms] of index.docTermFieldHits) shiftedDocTermFieldHits.set(oldKey > firstRemoved ? shift(oldKey) : oldKey, terms);
|
|
1204
|
+
index.docTermFieldHits = shiftedDocTermFieldHits;
|
|
1205
|
+
}
|
|
1206
|
+
var Fuse = class {
|
|
1207
|
+
constructor(docs, options, index) {
|
|
1208
|
+
this.options = {
|
|
1209
|
+
...Config,
|
|
1210
|
+
...options
|
|
1211
|
+
};
|
|
1212
|
+
if (this.options.useExtendedSearch && false) ;
|
|
1213
|
+
if (this.options.useTokenSearch && false) ;
|
|
1214
|
+
this._keyStore = new KeyStore(this.options.keys);
|
|
1215
|
+
this._docs = docs;
|
|
1216
|
+
this._myIndex = null;
|
|
1217
|
+
this._invertedIndex = null;
|
|
1218
|
+
this.setCollection(docs, index);
|
|
1219
|
+
this._lastQuery = null;
|
|
1220
|
+
this._lastSearcher = null;
|
|
1221
|
+
}
|
|
1222
|
+
_getSearcher(query) {
|
|
1223
|
+
if (this._lastQuery === query) return this._lastSearcher;
|
|
1224
|
+
const searcher = createSearcher(query, this._invertedIndex ? {
|
|
1225
|
+
...this.options,
|
|
1226
|
+
_invertedIndex: this._invertedIndex
|
|
1227
|
+
} : this.options);
|
|
1228
|
+
this._lastQuery = query;
|
|
1229
|
+
this._lastSearcher = searcher;
|
|
1230
|
+
return searcher;
|
|
1231
|
+
}
|
|
1232
|
+
setCollection(docs, index) {
|
|
1233
|
+
this._docs = docs;
|
|
1234
|
+
if (index && !(index instanceof FuseIndex)) throw new Error(INCORRECT_INDEX_TYPE);
|
|
1235
|
+
this._myIndex = index || createIndex(this.options.keys, this._docs, {
|
|
1236
|
+
getFn: this.options.getFn,
|
|
1237
|
+
fieldNormWeight: this.options.fieldNormWeight
|
|
1238
|
+
});
|
|
1239
|
+
if (this.options.useTokenSearch) {
|
|
1240
|
+
const analyzer = createAnalyzer({
|
|
1241
|
+
isCaseSensitive: this.options.isCaseSensitive,
|
|
1242
|
+
ignoreDiacritics: this.options.ignoreDiacritics,
|
|
1243
|
+
tokenize: this.options.tokenize
|
|
1244
|
+
});
|
|
1245
|
+
this._invertedIndex = buildInvertedIndex(this._myIndex.records, this._myIndex.keys.length, analyzer);
|
|
1246
|
+
}
|
|
1247
|
+
this._invalidateSearcherCache();
|
|
1248
|
+
}
|
|
1249
|
+
add(doc) {
|
|
1250
|
+
if (!isDefined(doc)) return;
|
|
1251
|
+
this._docs.push(doc);
|
|
1252
|
+
const record = this._myIndex.add(doc, this._docs.length - 1);
|
|
1253
|
+
if (this._invertedIndex && record) {
|
|
1254
|
+
const analyzer = createAnalyzer({
|
|
1255
|
+
isCaseSensitive: this.options.isCaseSensitive,
|
|
1256
|
+
ignoreDiacritics: this.options.ignoreDiacritics,
|
|
1257
|
+
tokenize: this.options.tokenize
|
|
1258
|
+
});
|
|
1259
|
+
addToInvertedIndex(this._invertedIndex, record, this._myIndex.keys.length, analyzer);
|
|
1260
|
+
}
|
|
1261
|
+
this._invalidateSearcherCache();
|
|
1262
|
+
}
|
|
1263
|
+
remove(predicate = () => false) {
|
|
1264
|
+
const results = [];
|
|
1265
|
+
const indicesToRemove = [];
|
|
1266
|
+
for (let i = 0, len = this._docs.length; i < len; i += 1) if (predicate(this._docs[i], i)) {
|
|
1267
|
+
results.push(this._docs[i]);
|
|
1268
|
+
indicesToRemove.push(i);
|
|
1269
|
+
}
|
|
1270
|
+
if (indicesToRemove.length) {
|
|
1271
|
+
if (this._invertedIndex) removeAndShiftInvertedIndex(this._invertedIndex, indicesToRemove);
|
|
1272
|
+
const toRemove = new Set(indicesToRemove);
|
|
1273
|
+
this._docs = this._docs.filter((_, i) => !toRemove.has(i));
|
|
1274
|
+
this._myIndex.removeAll(indicesToRemove);
|
|
1275
|
+
this._invalidateSearcherCache();
|
|
1276
|
+
}
|
|
1277
|
+
return results;
|
|
1278
|
+
}
|
|
1279
|
+
removeAt(idx) {
|
|
1280
|
+
if (!Number.isInteger(idx) || idx < 0 || idx >= this._docs.length) throw new Error(INVALID_DOC_INDEX);
|
|
1281
|
+
if (this._invertedIndex) removeAndShiftInvertedIndex(this._invertedIndex, [idx]);
|
|
1282
|
+
const doc = this._docs.splice(idx, 1)[0];
|
|
1283
|
+
this._myIndex.removeAt(idx);
|
|
1284
|
+
this._invalidateSearcherCache();
|
|
1285
|
+
return doc;
|
|
1286
|
+
}
|
|
1287
|
+
_invalidateSearcherCache() {
|
|
1288
|
+
this._lastQuery = null;
|
|
1289
|
+
this._lastSearcher = null;
|
|
1290
|
+
}
|
|
1291
|
+
getIndex() {
|
|
1292
|
+
return this._myIndex;
|
|
1293
|
+
}
|
|
1294
|
+
_normalizedKeys() {
|
|
1295
|
+
return this._myIndex.keys.map((key) => this._keyStore.get(key.id) || key);
|
|
1296
|
+
}
|
|
1297
|
+
search(query, options) {
|
|
1298
|
+
const { limit = -1 } = options || {};
|
|
1299
|
+
const { includeMatches, includeScore, shouldSort, sortFn, ignoreFieldNorm } = this.options;
|
|
1300
|
+
if (isString(query) && !query.trim()) {
|
|
1301
|
+
let docs = this._docs.map((item, idx) => ({
|
|
1302
|
+
item,
|
|
1303
|
+
refIndex: idx
|
|
1304
|
+
}));
|
|
1305
|
+
if (isNumber(limit) && limit > -1) docs = docs.slice(0, limit);
|
|
1306
|
+
return docs;
|
|
1307
|
+
}
|
|
1308
|
+
const useHeap = shouldSort && isNumber(limit) && limit > 0 && isString(query);
|
|
1309
|
+
const comparator = sortFn;
|
|
1310
|
+
const stable = (a, b) => comparator(a, b) || a.idx - b.idx;
|
|
1311
|
+
let results;
|
|
1312
|
+
if (useHeap) {
|
|
1313
|
+
const heap = new MaxHeap(limit, stable);
|
|
1314
|
+
if (isString(this._docs[0])) this._searchStringList(query, {
|
|
1315
|
+
heap,
|
|
1316
|
+
ignoreFieldNorm
|
|
1317
|
+
});
|
|
1318
|
+
else this._searchObjectList(query, {
|
|
1319
|
+
heap,
|
|
1320
|
+
ignoreFieldNorm
|
|
1321
|
+
});
|
|
1322
|
+
results = heap.extractSorted();
|
|
1323
|
+
} else {
|
|
1324
|
+
results = isString(query) ? isString(this._docs[0]) ? this._searchStringList(query) : this._searchObjectList(query) : this._searchLogical(query);
|
|
1325
|
+
computeScore(results, { ignoreFieldNorm });
|
|
1326
|
+
if (shouldSort) results.sort(isString(query) ? stable : comparator);
|
|
1327
|
+
if (isNumber(limit) && limit > -1) results = results.slice(0, limit);
|
|
1328
|
+
}
|
|
1329
|
+
return format(results, this._docs, {
|
|
1330
|
+
includeMatches,
|
|
1331
|
+
includeScore
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
_searchStringList(query, { heap, ignoreFieldNorm } = {}) {
|
|
1335
|
+
const searcher = this._getSearcher(query);
|
|
1336
|
+
const requireAllTokens = this.options.useTokenSearch && this.options.tokenMatch === "all";
|
|
1337
|
+
const { records } = this._myIndex;
|
|
1338
|
+
const results = heap ? null : [];
|
|
1339
|
+
records.forEach(({ v: text, i: idx, n: norm2 }) => {
|
|
1340
|
+
if (!isDefined(text)) return;
|
|
1341
|
+
const searchResult = searcher.searchIn(text);
|
|
1342
|
+
if (searchResult.isMatch) {
|
|
1343
|
+
const match = {
|
|
1344
|
+
score: searchResult.score,
|
|
1345
|
+
value: text,
|
|
1346
|
+
norm: norm2,
|
|
1347
|
+
indices: searchResult.indices
|
|
1348
|
+
};
|
|
1349
|
+
if (requireAllTokens) {
|
|
1350
|
+
match.matchedMask = searchResult.matchedMask;
|
|
1351
|
+
match.matchedTerms = searchResult.matchedTerms;
|
|
1352
|
+
match.termCount = searchResult.termCount;
|
|
1353
|
+
}
|
|
1354
|
+
const matches = [match];
|
|
1355
|
+
if (!requireAllTokens || this._coversAllTokens(matches)) {
|
|
1356
|
+
const result = {
|
|
1357
|
+
item: text,
|
|
1358
|
+
idx,
|
|
1359
|
+
matches
|
|
1360
|
+
};
|
|
1361
|
+
if (heap) {
|
|
1362
|
+
result.score = computeScoreSingle(result.matches, { ignoreFieldNorm });
|
|
1363
|
+
heap.insert(result);
|
|
1364
|
+
} else results.push(result);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
});
|
|
1368
|
+
return results;
|
|
1369
|
+
}
|
|
1370
|
+
_searchLogical(query) {
|
|
1371
|
+
const expression = parse(query, this.options);
|
|
1372
|
+
const keys = this._normalizedKeys();
|
|
1373
|
+
const evaluate = (node, item, idx) => {
|
|
1374
|
+
if (!("children" in node)) {
|
|
1375
|
+
const { keyId, searcher } = node;
|
|
1376
|
+
let matches;
|
|
1377
|
+
if (keyId === null) {
|
|
1378
|
+
matches = [];
|
|
1379
|
+
keys.forEach((key, keyIndex) => {
|
|
1380
|
+
matches.push(...this._findMatches({
|
|
1381
|
+
key,
|
|
1382
|
+
value: item[keyIndex],
|
|
1383
|
+
searcher
|
|
1384
|
+
}));
|
|
1385
|
+
});
|
|
1386
|
+
} else matches = this._findMatches({
|
|
1387
|
+
key: this._keyStore.get(keyId),
|
|
1388
|
+
value: this._myIndex.getValueForItemAtKeyId(item, keyId),
|
|
1389
|
+
searcher
|
|
1390
|
+
});
|
|
1391
|
+
if (matches && matches.length) return [{
|
|
1392
|
+
idx,
|
|
1393
|
+
item,
|
|
1394
|
+
matches
|
|
1395
|
+
}];
|
|
1396
|
+
return [];
|
|
1397
|
+
}
|
|
1398
|
+
const { children, operator } = node;
|
|
1399
|
+
const res = [];
|
|
1400
|
+
for (let i = 0, len = children.length; i < len; i += 1) {
|
|
1401
|
+
const child = children[i];
|
|
1402
|
+
const result = evaluate(child, item, idx);
|
|
1403
|
+
if (result.length) res.push(...result);
|
|
1404
|
+
else if (operator === LogicalOperator.AND) return [];
|
|
1405
|
+
}
|
|
1406
|
+
return res;
|
|
1407
|
+
};
|
|
1408
|
+
const records = this._myIndex.records;
|
|
1409
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
1410
|
+
const results = [];
|
|
1411
|
+
records.forEach(({ $: item, i: idx }) => {
|
|
1412
|
+
if (isDefined(item)) {
|
|
1413
|
+
const expResults = evaluate(expression, item, idx);
|
|
1414
|
+
if (expResults.length) {
|
|
1415
|
+
if (!resultMap.has(idx)) {
|
|
1416
|
+
resultMap.set(idx, {
|
|
1417
|
+
idx,
|
|
1418
|
+
item,
|
|
1419
|
+
matches: []
|
|
1420
|
+
});
|
|
1421
|
+
results.push(resultMap.get(idx));
|
|
1422
|
+
}
|
|
1423
|
+
expResults.forEach(({ matches }) => {
|
|
1424
|
+
resultMap.get(idx).matches.push(...matches);
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
});
|
|
1429
|
+
return results;
|
|
1430
|
+
}
|
|
1431
|
+
_searchObjectList(query, { heap, ignoreFieldNorm } = {}) {
|
|
1432
|
+
const searcher = this._getSearcher(query);
|
|
1433
|
+
const requireAllTokens = this.options.useTokenSearch && this.options.tokenMatch === "all";
|
|
1434
|
+
const { records } = this._myIndex;
|
|
1435
|
+
const keys = this._normalizedKeys();
|
|
1436
|
+
const results = heap ? null : [];
|
|
1437
|
+
records.forEach(({ $: item, i: idx }) => {
|
|
1438
|
+
if (!isDefined(item)) return;
|
|
1439
|
+
const matches = [];
|
|
1440
|
+
let anyKeyFailed = false;
|
|
1441
|
+
let hasInverse = false;
|
|
1442
|
+
keys.forEach((key, keyIndex) => {
|
|
1443
|
+
const keyMatches = this._findMatches({
|
|
1444
|
+
key,
|
|
1445
|
+
value: item[keyIndex],
|
|
1446
|
+
searcher
|
|
1447
|
+
});
|
|
1448
|
+
if (keyMatches.length) {
|
|
1449
|
+
matches.push(...keyMatches);
|
|
1450
|
+
if (keyMatches[0].hasInverse) hasInverse = true;
|
|
1451
|
+
} else anyKeyFailed = true;
|
|
1452
|
+
});
|
|
1453
|
+
if (hasInverse && anyKeyFailed) return;
|
|
1454
|
+
if (matches.length && (!requireAllTokens || this._coversAllTokens(matches))) {
|
|
1455
|
+
const result = {
|
|
1456
|
+
idx,
|
|
1457
|
+
item,
|
|
1458
|
+
matches
|
|
1459
|
+
};
|
|
1460
|
+
if (heap) {
|
|
1461
|
+
result.score = computeScoreSingle(result.matches, { ignoreFieldNorm });
|
|
1462
|
+
heap.insert(result);
|
|
1463
|
+
} else results.push(result);
|
|
1464
|
+
}
|
|
1465
|
+
});
|
|
1466
|
+
return results;
|
|
1467
|
+
}
|
|
1468
|
+
_findMatches({ key, value, searcher }) {
|
|
1469
|
+
if (!isDefined(value)) return [];
|
|
1470
|
+
const matches = [];
|
|
1471
|
+
if (isArray(value)) value.forEach(({ v: text, i: idx, n: norm2 }) => {
|
|
1472
|
+
if (!isDefined(text)) return;
|
|
1473
|
+
const searchResult = searcher.searchIn(text);
|
|
1474
|
+
if (searchResult.isMatch) {
|
|
1475
|
+
const match = {
|
|
1476
|
+
score: searchResult.score,
|
|
1477
|
+
key,
|
|
1478
|
+
value: text,
|
|
1479
|
+
idx,
|
|
1480
|
+
norm: norm2,
|
|
1481
|
+
indices: searchResult.indices,
|
|
1482
|
+
hasInverse: searchResult.hasInverse
|
|
1483
|
+
};
|
|
1484
|
+
if (searchResult.termCount !== void 0) {
|
|
1485
|
+
match.matchedMask = searchResult.matchedMask;
|
|
1486
|
+
match.matchedTerms = searchResult.matchedTerms;
|
|
1487
|
+
match.termCount = searchResult.termCount;
|
|
1488
|
+
}
|
|
1489
|
+
matches.push(match);
|
|
1490
|
+
}
|
|
1491
|
+
});
|
|
1492
|
+
else {
|
|
1493
|
+
const { v: text, n: norm2 } = value;
|
|
1494
|
+
const searchResult = searcher.searchIn(text);
|
|
1495
|
+
if (searchResult.isMatch) {
|
|
1496
|
+
const match = {
|
|
1497
|
+
score: searchResult.score,
|
|
1498
|
+
key,
|
|
1499
|
+
value: text,
|
|
1500
|
+
norm: norm2,
|
|
1501
|
+
indices: searchResult.indices,
|
|
1502
|
+
hasInverse: searchResult.hasInverse
|
|
1503
|
+
};
|
|
1504
|
+
if (searchResult.termCount !== void 0) {
|
|
1505
|
+
match.matchedMask = searchResult.matchedMask;
|
|
1506
|
+
match.matchedTerms = searchResult.matchedTerms;
|
|
1507
|
+
match.termCount = searchResult.termCount;
|
|
1508
|
+
}
|
|
1509
|
+
matches.push(match);
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
return matches;
|
|
1513
|
+
}
|
|
1514
|
+
_coversAllTokens(matches) {
|
|
1515
|
+
const termCount = matches.length ? matches[0].termCount : void 0;
|
|
1516
|
+
if (termCount === void 0) return true;
|
|
1517
|
+
if (termCount <= 31) {
|
|
1518
|
+
let coverage2 = 0;
|
|
1519
|
+
for (let i = 0; i < matches.length; i++) coverage2 |= matches[i].matchedMask || 0;
|
|
1520
|
+
return coverage2 === 2 ** termCount - 1;
|
|
1521
|
+
}
|
|
1522
|
+
const coverage = /* @__PURE__ */ new Set();
|
|
1523
|
+
for (let i = 0; i < matches.length; i++) {
|
|
1524
|
+
const terms = matches[i].matchedTerms;
|
|
1525
|
+
if (terms) for (const t of terms) coverage.add(t);
|
|
1526
|
+
}
|
|
1527
|
+
return coverage.size === termCount;
|
|
1528
|
+
}
|
|
1529
|
+
};
|
|
1530
|
+
Fuse.version = "7.5.0";
|
|
1531
|
+
Fuse.createIndex = createIndex;
|
|
1532
|
+
Fuse.parseIndex = parseIndex;
|
|
1533
|
+
Fuse.config = Config;
|
|
1534
|
+
Fuse.match = function(pattern, text, options) {
|
|
1535
|
+
if (options && options.useTokenSearch) throw new Error(FUSE_MATCH_TOKEN_SEARCH_UNSUPPORTED);
|
|
1536
|
+
return createSearcher(pattern, {
|
|
1537
|
+
...Config,
|
|
1538
|
+
...options
|
|
1539
|
+
}).searchIn(text);
|
|
1540
|
+
};
|
|
1541
|
+
Fuse.parseQuery = parse;
|
|
1542
|
+
register(ExtendedSearch);
|
|
1543
|
+
register(TokenSearch);
|
|
1544
|
+
Fuse.use = function(...plugins) {
|
|
1545
|
+
plugins.forEach((plugin) => register(plugin));
|
|
1546
|
+
};
|
|
1547
|
+
var entry_default = Fuse;
|
|
1548
|
+
const version = "0.3.5";
|
|
1549
|
+
const packageJson = {
|
|
1550
|
+
version
|
|
1551
|
+
};
|
|
1552
|
+
function instantiateCachedURL(dbVersion, url, importObject) {
|
|
1553
|
+
const dbName = "wasm-cache";
|
|
1554
|
+
const storeName = "wasm-cache";
|
|
1555
|
+
function openDatabase() {
|
|
1556
|
+
return new Promise((resolve, reject) => {
|
|
1557
|
+
const request = indexedDB.open(dbName, dbVersion);
|
|
1558
|
+
request.onerror = reject.bind(null, "Error opening wasm cache database");
|
|
1559
|
+
request.onsuccess = () => {
|
|
1560
|
+
resolve(request.result);
|
|
1561
|
+
};
|
|
1562
|
+
request.onupgradeneeded = (event) => {
|
|
1563
|
+
const db = request.result;
|
|
1564
|
+
if (db.objectStoreNames.contains(storeName)) {
|
|
1565
|
+
console.log(`Clearing out version ${event.oldVersion} wasm cache`);
|
|
1566
|
+
db.deleteObjectStore(storeName);
|
|
1567
|
+
}
|
|
1568
|
+
console.log(`Creating version ${event.newVersion} wasm cache`);
|
|
1569
|
+
db.createObjectStore(storeName);
|
|
1570
|
+
};
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
function lookupInDatabase(db) {
|
|
1574
|
+
return new Promise((resolve, reject) => {
|
|
1575
|
+
const store = db.transaction([storeName]).objectStore(storeName);
|
|
1576
|
+
const request = store.get(url);
|
|
1577
|
+
request.onerror = reject.bind(null, `Error getting wasm module ${url}`);
|
|
1578
|
+
request.onsuccess = () => {
|
|
1579
|
+
if (request.result) {
|
|
1580
|
+
resolve(request.result);
|
|
1581
|
+
} else {
|
|
1582
|
+
reject(`Module ${url} was not found in wasm cache`);
|
|
1583
|
+
}
|
|
1584
|
+
};
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
function storeInDatabase(db, module) {
|
|
1588
|
+
const store = db.transaction([storeName], "readwrite").objectStore(storeName);
|
|
1589
|
+
try {
|
|
1590
|
+
const request = store.put(module, url);
|
|
1591
|
+
request.onerror = (err) => {
|
|
1592
|
+
console.log(`Failed to store in wasm cache: ${err}`);
|
|
1593
|
+
};
|
|
1594
|
+
request.onsuccess = () => {
|
|
1595
|
+
console.log(`Successfully stored ${url} in wasm cache`);
|
|
1596
|
+
};
|
|
1597
|
+
} catch (e) {
|
|
1598
|
+
console.warn("An error was thrown... in storing wasm cache...");
|
|
1599
|
+
console.warn(e);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
async function fetchAndInstantiate() {
|
|
1603
|
+
const response = await fetch(url);
|
|
1604
|
+
const buffer = await response.arrayBuffer();
|
|
1605
|
+
return await WebAssembly.instantiate(buffer, importObject);
|
|
1606
|
+
}
|
|
1607
|
+
return openDatabase().then(
|
|
1608
|
+
(db) => {
|
|
1609
|
+
return lookupInDatabase(db).then(
|
|
1610
|
+
(module) => {
|
|
1611
|
+
console.log(`Found ${url} in wasm cache`);
|
|
1612
|
+
return WebAssembly.instantiate(module, importObject);
|
|
1613
|
+
},
|
|
1614
|
+
(errMsg) => {
|
|
1615
|
+
console.log(errMsg);
|
|
1616
|
+
return fetchAndInstantiate().then((results) => {
|
|
1617
|
+
setTimeout(() => storeInDatabase(db, results.module), 0);
|
|
1618
|
+
return results.instance;
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1621
|
+
);
|
|
1622
|
+
},
|
|
1623
|
+
(errMsg) => {
|
|
1624
|
+
console.log(errMsg);
|
|
1625
|
+
return fetchAndInstantiate().then((results) => results.instance);
|
|
1626
|
+
}
|
|
1627
|
+
);
|
|
1628
|
+
}
|
|
1629
|
+
class ModuleClass {
|
|
1630
|
+
constructor({ init, version: version2, wasmUrl }) {
|
|
1631
|
+
this._init = init;
|
|
1632
|
+
this._version = version2;
|
|
1633
|
+
this._wasmUrl = wasmUrl;
|
|
1634
|
+
}
|
|
1635
|
+
locateFile(baseName) {
|
|
1636
|
+
return self.location.pathname.replace(/\[^\/]*$/, "/") + baseName;
|
|
1637
|
+
}
|
|
1638
|
+
instantiateWasm(imports, callback) {
|
|
1639
|
+
instantiateCachedURL(this._version, this._wasmUrl, imports).then(
|
|
1640
|
+
(instance) => callback(instance)
|
|
1641
|
+
);
|
|
1642
|
+
return {};
|
|
1643
|
+
}
|
|
1644
|
+
onInit(callback) {
|
|
1645
|
+
this._init = callback;
|
|
1646
|
+
}
|
|
1647
|
+
onRuntimeInitialized() {
|
|
1648
|
+
if (this._init) {
|
|
1649
|
+
return this._init(this);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
var WorkerEventType = /* @__PURE__ */ ((WorkerEventType2) => {
|
|
1654
|
+
WorkerEventType2["INIT_CALC"] = "init";
|
|
1655
|
+
WorkerEventType2["REQUEST_CALC"] = "req_calc";
|
|
1656
|
+
WorkerEventType2["RESULT_CALC"] = "res_calc";
|
|
1657
|
+
WorkerEventType2["INIT_FILTER"] = "init_filter";
|
|
1658
|
+
WorkerEventType2["REQUEST_FILTER"] = "req_filter";
|
|
1659
|
+
WorkerEventType2["RESULT_FILTER"] = "res_filter";
|
|
1660
|
+
return WorkerEventType2;
|
|
1661
|
+
})(WorkerEventType || {});
|
|
1662
|
+
const ximgdiffVersionString = packageJson.version;
|
|
1663
|
+
const _self = self;
|
|
1664
|
+
function version2number(version2) {
|
|
1665
|
+
const [, major, minor, patch] = version2.match(/^(\d*)\.(\d*)\.(\d*)/);
|
|
1666
|
+
return +major * 1e4 + +minor * 100 + +patch;
|
|
1667
|
+
}
|
|
1668
|
+
let loaded = false;
|
|
1669
|
+
let lastCalcData = null;
|
|
1670
|
+
const cachedEntity = {
|
|
1671
|
+
new: [],
|
|
1672
|
+
passed: [],
|
|
1673
|
+
failed: [],
|
|
1674
|
+
deleted: []
|
|
1675
|
+
};
|
|
1676
|
+
const calc = ({
|
|
1677
|
+
payload: { raw, img1, img2, actualSrc, expectedSrc, seq }
|
|
1678
|
+
}) => {
|
|
1679
|
+
const diffResult = _self.Module.detectDiff(_self.Module, img1, img2, {});
|
|
1680
|
+
_self.postMessage({
|
|
1681
|
+
type: WorkerEventType.RESULT_CALC,
|
|
1682
|
+
payload: {
|
|
1683
|
+
seq,
|
|
1684
|
+
raw,
|
|
1685
|
+
actualSrc,
|
|
1686
|
+
expectedSrc,
|
|
1687
|
+
result: {
|
|
1688
|
+
...diffResult,
|
|
1689
|
+
images: [
|
|
1690
|
+
{ width: img1.width, height: img1.height },
|
|
1691
|
+
{ width: img2.width, height: img2.height }
|
|
1692
|
+
]
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
});
|
|
1696
|
+
};
|
|
1697
|
+
const filter = ({
|
|
1698
|
+
payload: { input }
|
|
1699
|
+
}) => {
|
|
1700
|
+
if (!input) {
|
|
1701
|
+
return _self.postMessage({
|
|
1702
|
+
type: WorkerEventType.RESULT_FILTER,
|
|
1703
|
+
payload: {
|
|
1704
|
+
newItems: cachedEntity.new,
|
|
1705
|
+
passedItems: cachedEntity.passed,
|
|
1706
|
+
failedItems: cachedEntity.failed,
|
|
1707
|
+
deletedItems: cachedEntity.deleted
|
|
1708
|
+
}
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
const search2 = (entities) => {
|
|
1712
|
+
const fuse = new entry_default(entities, {
|
|
1713
|
+
shouldSort: false,
|
|
1714
|
+
isCaseSensitive: false,
|
|
1715
|
+
findAllMatches: true,
|
|
1716
|
+
location: 0,
|
|
1717
|
+
distance: 100,
|
|
1718
|
+
minMatchCharLength: 1,
|
|
1719
|
+
threshold: 0.2,
|
|
1720
|
+
keys: ["name"]
|
|
1721
|
+
});
|
|
1722
|
+
return fuse.search(input).map(({ item }) => item);
|
|
1723
|
+
};
|
|
1724
|
+
_self.postMessage({
|
|
1725
|
+
type: WorkerEventType.RESULT_FILTER,
|
|
1726
|
+
payload: {
|
|
1727
|
+
newItems: search2(cachedEntity.new),
|
|
1728
|
+
passedItems: search2(cachedEntity.passed),
|
|
1729
|
+
failedItems: search2(cachedEntity.failed),
|
|
1730
|
+
deletedItems: search2(cachedEntity.deleted)
|
|
1731
|
+
}
|
|
1732
|
+
});
|
|
1733
|
+
};
|
|
1734
|
+
_self.Module = new ModuleClass({
|
|
1735
|
+
version: version2number(ximgdiffVersionString),
|
|
1736
|
+
wasmUrl: _self.wasmUrl,
|
|
1737
|
+
init: () => {
|
|
1738
|
+
loaded = true;
|
|
1739
|
+
if (lastCalcData != null) {
|
|
1740
|
+
calc(lastCalcData);
|
|
1741
|
+
}
|
|
1742
|
+
_self.postMessage({ type: WorkerEventType.INIT_CALC });
|
|
1743
|
+
}
|
|
1744
|
+
});
|
|
1745
|
+
_self.addEventListener("message", ({ data }) => {
|
|
1746
|
+
console.log("Received: ", data);
|
|
1747
|
+
switch (data.type) {
|
|
1748
|
+
case WorkerEventType.REQUEST_CALC:
|
|
1749
|
+
if (loaded) {
|
|
1750
|
+
calc(data);
|
|
1751
|
+
} else {
|
|
1752
|
+
lastCalcData = data;
|
|
1753
|
+
}
|
|
1754
|
+
break;
|
|
1755
|
+
case WorkerEventType.INIT_FILTER:
|
|
1756
|
+
cachedEntity.new = data.payload.newItems;
|
|
1757
|
+
cachedEntity.passed = data.payload.passedItems;
|
|
1758
|
+
cachedEntity.failed = data.payload.failedItems;
|
|
1759
|
+
cachedEntity.deleted = data.payload.deletedItems;
|
|
1760
|
+
break;
|
|
1761
|
+
case WorkerEventType.REQUEST_FILTER:
|
|
1762
|
+
filter(data);
|
|
1763
|
+
break;
|
|
1764
|
+
}
|
|
1765
|
+
});
|
|
1766
|
+
})();
|