reg-cli 0.18.16 → 0.19.0-rc0

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.
Files changed (46) hide show
  1. package/README.md +155 -46
  2. package/dist/cli.cjs +221 -0
  3. package/dist/cli.d.cts +1 -0
  4. package/dist/cli.d.mts +1 -0
  5. package/dist/cli.mjs +221 -0
  6. package/dist/entry.cjs +119 -0
  7. package/dist/entry.d.cts +15 -0
  8. package/dist/entry.d.mts +15 -0
  9. package/dist/entry.mjs +118 -0
  10. package/dist/index.cjs +212 -0
  11. package/dist/index.d.cts +52 -0
  12. package/dist/index.d.mts +52 -0
  13. package/dist/index.mjs +206 -0
  14. package/dist/progress-BB7D-xke.mjs +28 -0
  15. package/dist/progress-UOiKHaTY.cjs +33 -0
  16. package/dist/reg.wasm +0 -0
  17. package/dist/rolldown-runtime-D_mwlA32.cjs +43 -0
  18. package/{report/ui/dist/worker.js → dist/shared/report-worker.js} +859 -247
  19. package/dist/tracing-7HO8hac9.mjs +378 -0
  20. package/dist/tracing-DlLL541f.cjs +445 -0
  21. package/dist/worker.cjs +143 -0
  22. package/dist/worker.d.cts +1 -0
  23. package/dist/worker.d.mts +1 -0
  24. package/dist/worker.mjs +142 -0
  25. package/dist/ximgdiff-DzkRRnkW.mjs +41 -0
  26. package/dist/ximgdiff-wmW1BH1H.cjs +41 -0
  27. package/package.json +61 -112
  28. package/dist/.keep +0 -0
  29. package/dist/cli.js +0 -214
  30. package/dist/diff.js +0 -80
  31. package/dist/icon.js +0 -12
  32. package/dist/image-finder.js +0 -25
  33. package/dist/index.js +0 -200
  34. package/dist/log.js +0 -21
  35. package/dist/process-adaptor.js +0 -40
  36. package/dist/report.js +0 -170
  37. package/dist/tracing.js +0 -169
  38. package/report/assets/favicon_failure.png +0 -0
  39. package/report/assets/favicon_success.png +0 -0
  40. package/report/sample/actual/sample.png +0 -0
  41. package/report/sample/diff/sample.png +0 -0
  42. package/report/sample/expected/sample.png +0 -0
  43. package/report/ui/dist/report.js +0 -123
  44. package/report/ui/dist/style.css +0 -1
  45. package/template/template.html +0 -20
  46. /package/{template → dist/shared}/worker_pre.js +0 -0
@@ -7,7 +7,10 @@
7
7
  if (typeof value == "string") {
8
8
  return value;
9
9
  }
10
- let result = value + "";
10
+ if (typeof value === "bigint") {
11
+ return value.toString();
12
+ }
13
+ const result = value + "";
11
14
  return result == "0" && 1 / value == -Infinity ? "-0" : result;
12
15
  }
13
16
  function toString(value) {
@@ -49,7 +52,7 @@
49
52
  this._keyMap = {};
50
53
  let totalWeight = 0;
51
54
  keys.forEach((key) => {
52
- let obj = createKey(key);
55
+ const obj = createKey(key);
53
56
  this._keys.push(obj);
54
57
  this._keyMap[obj.id] = obj;
55
58
  totalWeight += obj.weight;
@@ -94,7 +97,13 @@
94
97
  id = createKeyId(name);
95
98
  getFn = key.getFn;
96
99
  }
97
- return { path, id, weight, src, getFn };
100
+ return {
101
+ path,
102
+ id,
103
+ weight,
104
+ src,
105
+ getFn
106
+ };
98
107
  }
99
108
  function createKeyPath(key) {
100
109
  return isArray(key) ? key : key.split(".");
@@ -103,29 +112,35 @@
103
112
  return isArray(key) ? key.join(".") : key;
104
113
  }
105
114
  function get(obj, path) {
106
- let list = [];
115
+ const list = [];
107
116
  let arr = false;
108
- const deepGet = (obj2, path2, index) => {
117
+ const deepGet = (obj2, path2, index, arrayIndex) => {
109
118
  if (!isDefined(obj2)) {
110
119
  return;
111
120
  }
112
121
  if (!path2[index]) {
113
- list.push(obj2);
122
+ list.push(arrayIndex !== void 0 ? {
123
+ v: obj2,
124
+ i: arrayIndex
125
+ } : obj2);
114
126
  } else {
115
- let key = path2[index];
127
+ const key = path2[index];
116
128
  const value = obj2[key];
117
129
  if (!isDefined(value)) {
118
130
  return;
119
131
  }
120
- if (index === path2.length - 1 && (isString(value) || isNumber(value) || isBoolean(value))) {
121
- list.push(toString(value));
132
+ if (index === path2.length - 1 && (isString(value) || isNumber(value) || isBoolean(value) || typeof value === "bigint")) {
133
+ list.push(arrayIndex !== void 0 ? {
134
+ v: toString(value),
135
+ i: arrayIndex
136
+ } : toString(value));
122
137
  } else if (isArray(value)) {
123
138
  arr = true;
124
139
  for (let i = 0, len = value.length; i < len; i += 1) {
125
- deepGet(value[i], path2, index + 1);
140
+ deepGet(value[i], path2, index + 1, i);
126
141
  }
127
142
  } else if (path2.length) {
128
- deepGet(value, path2, index + 1);
143
+ deepGet(value, path2, index + 1, arrayIndex);
129
144
  }
130
145
  }
131
146
  };
@@ -133,67 +148,37 @@
133
148
  return arr ? list : list[0];
134
149
  }
135
150
  const MatchOptions = {
136
- // Whether the matches should be included in the result set. When `true`, each record in the result
137
- // set will include the indices of the matched characters.
138
- // These can consequently be used for highlighting purposes.
139
151
  includeMatches: false,
140
- // When `true`, the matching function will continue to the end of a search pattern even if
141
- // a perfect match has already been located in the string.
142
152
  findAllMatches: false,
143
- // Minimum number of characters that must be matched before a result is considered a match
144
153
  minMatchCharLength: 1
145
154
  };
146
155
  const BasicOptions = {
147
- // When `true`, the algorithm continues searching to the end of the input even if a perfect
148
- // match is found before the end of the same input.
149
156
  isCaseSensitive: false,
150
- // When `true`, the algorithm will ignore diacritics (accents) in comparisons
151
157
  ignoreDiacritics: false,
152
- // When true, the matching function will continue to the end of a search pattern even if
153
158
  includeScore: false,
154
- // List of properties that will be searched. This also supports nested properties.
155
159
  keys: [],
156
- // Whether to sort the result list, by score
157
160
  shouldSort: true,
158
- // Default sort function: sort by ascending score, ascending index
159
161
  sortFn: (a, b) => a.score === b.score ? a.idx < b.idx ? -1 : 1 : a.score < b.score ? -1 : 1
160
162
  };
161
163
  const FuzzyOptions = {
162
- // Approximately where in the text is the pattern expected to be found?
163
164
  location: 0,
164
- // At what point does the match algorithm give up. A threshold of '0.0' requires a perfect match
165
- // (of both letters and location), a threshold of '1.0' would match anything.
166
165
  threshold: 0.6,
167
- // Determines how close the match must be to the fuzzy location (specified above).
168
- // An exact letter match which is 'distance' characters away from the fuzzy location
169
- // would score as a complete mismatch. A distance of '0' requires the match be at
170
- // the exact location specified, a threshold of '1000' would require a perfect match
171
- // to be within 800 characters of the fuzzy location to be found using a 0.8 threshold.
172
166
  distance: 100
173
167
  };
174
168
  const AdvancedOptions = {
175
- // When `true`, it enables the use of unix-like search commands
176
169
  useExtendedSearch: false,
177
- // The get function to use when fetching an object's properties.
178
- // The default will search nested paths *ie foo.bar.baz*
170
+ useTokenSearch: false,
179
171
  getFn: get,
180
- // When `true`, search will ignore `location` and `distance`, so it won't matter
181
- // where in the string the pattern appears.
182
- // More info: https://fusejs.io/concepts/scoring-theory.html#fuzziness-score
183
172
  ignoreLocation: false,
184
- // When `true`, the calculation for the relevance score (used for sorting) will
185
- // ignore the field-length norm.
186
- // More info: https://fusejs.io/concepts/scoring-theory.html#field-length-norm
187
173
  ignoreFieldNorm: false,
188
- // The weight to determine how much field length norm effects scoring.
189
174
  fieldNormWeight: 1
190
175
  };
191
- var Config = {
176
+ const Config = Object.freeze({
192
177
  ...BasicOptions,
193
178
  ...MatchOptions,
194
179
  ...FuzzyOptions,
195
180
  ...AdvancedOptions
196
- };
181
+ });
197
182
  const SPACE = /[^ ]+/g;
198
183
  function norm(weight = 1, mantissa = 3) {
199
184
  const cache = /* @__PURE__ */ new Map();
@@ -222,6 +207,9 @@
222
207
  this.norm = norm(fieldNormWeight, 3);
223
208
  this.getFn = getFn;
224
209
  this.isCreated = false;
210
+ this.docs = [];
211
+ this.keys = [];
212
+ this._keysMap = {};
225
213
  this.setIndexRecords();
226
214
  }
227
215
  setSources(docs = []) {
@@ -269,6 +257,15 @@
269
257
  this.records[i].i -= 1;
270
258
  }
271
259
  }
260
+ // Removes docs at the specified indices (must be sorted ascending)
261
+ removeAll(indices) {
262
+ for (let i = indices.length - 1; i >= 0; i -= 1) {
263
+ this.records.splice(indices[i], 1);
264
+ }
265
+ for (let i = 0, len = this.records.length; i < len; i += 1) {
266
+ this.records[i].i = i;
267
+ }
268
+ }
272
269
  getValueForItemAtKeyId(item, keyId) {
273
270
  return item[this._keysMap[keyId]];
274
271
  }
@@ -279,7 +276,7 @@
279
276
  if (!isDefined(doc) || isBlank(doc)) {
280
277
  return;
281
278
  }
282
- let record = {
279
+ const record = {
283
280
  v: doc,
284
281
  i: docIndex,
285
282
  n: this.norm.get(doc)
@@ -287,39 +284,46 @@
287
284
  this.records.push(record);
288
285
  }
289
286
  _addObject(doc, docIndex) {
290
- let record = { i: docIndex, $: {} };
287
+ const record = {
288
+ i: docIndex,
289
+ $: {}
290
+ };
291
291
  this.keys.forEach((key, keyIndex) => {
292
- let value = key.getFn ? key.getFn(doc) : this.getFn(doc, key.path);
292
+ const value = key.getFn ? key.getFn(doc) : this.getFn(doc, key.path);
293
293
  if (!isDefined(value)) {
294
294
  return;
295
295
  }
296
296
  if (isArray(value)) {
297
- let subRecords = [];
298
- const stack = [{ nestedArrIndex: -1, value }];
299
- while (stack.length) {
300
- const { nestedArrIndex, value: value2 } = stack.pop();
301
- if (!isDefined(value2)) {
297
+ const subRecords = [];
298
+ for (let i = 0, len = value.length; i < len; i += 1) {
299
+ const item = value[i];
300
+ if (!isDefined(item)) {
302
301
  continue;
303
302
  }
304
- if (isString(value2) && !isBlank(value2)) {
305
- let subRecord = {
306
- v: value2,
307
- i: nestedArrIndex,
308
- n: this.norm.get(value2)
309
- };
310
- subRecords.push(subRecord);
311
- } else if (isArray(value2)) {
312
- value2.forEach((item, k) => {
313
- stack.push({
314
- nestedArrIndex: k,
315
- value: item
316
- });
317
- });
318
- } else ;
303
+ if (isString(item)) {
304
+ if (!isBlank(item)) {
305
+ const subRecord = {
306
+ v: item,
307
+ i,
308
+ n: this.norm.get(item)
309
+ };
310
+ subRecords.push(subRecord);
311
+ }
312
+ } else if (isDefined(item.v)) {
313
+ const text = isString(item.v) ? item.v : toString(item.v);
314
+ if (!isBlank(text)) {
315
+ const subRecord = {
316
+ v: text,
317
+ i: item.i,
318
+ n: this.norm.get(text)
319
+ };
320
+ subRecords.push(subRecord);
321
+ }
322
+ }
319
323
  }
320
324
  record.$[keyIndex] = subRecords;
321
325
  } else if (isString(value) && !isBlank(value)) {
322
- let subRecord = {
326
+ const subRecord = {
323
327
  v: value,
324
328
  n: this.norm.get(value)
325
329
  };
@@ -330,49 +334,51 @@
330
334
  }
331
335
  toJSON() {
332
336
  return {
333
- keys: this.keys,
337
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
338
+ keys: this.keys.map(({
339
+ getFn,
340
+ ...key
341
+ }) => key),
334
342
  records: this.records
335
343
  };
336
344
  }
337
345
  }
338
- function createIndex(keys, docs, { getFn = Config.getFn, fieldNormWeight = Config.fieldNormWeight } = {}) {
339
- const myIndex = new FuseIndex({ getFn, fieldNormWeight });
346
+ function createIndex(keys, docs, {
347
+ getFn = Config.getFn,
348
+ fieldNormWeight = Config.fieldNormWeight
349
+ } = {}) {
350
+ const myIndex = new FuseIndex({
351
+ getFn,
352
+ fieldNormWeight
353
+ });
340
354
  myIndex.setKeys(keys.map(createKey));
341
355
  myIndex.setSources(docs);
342
356
  myIndex.create();
343
357
  return myIndex;
344
358
  }
345
- function parseIndex(data, { getFn = Config.getFn, fieldNormWeight = Config.fieldNormWeight } = {}) {
346
- const { keys, records } = data;
347
- const myIndex = new FuseIndex({ getFn, fieldNormWeight });
359
+ function parseIndex(data, {
360
+ getFn = Config.getFn,
361
+ fieldNormWeight = Config.fieldNormWeight
362
+ } = {}) {
363
+ const {
364
+ keys,
365
+ records
366
+ } = data;
367
+ const myIndex = new FuseIndex({
368
+ getFn,
369
+ fieldNormWeight
370
+ });
348
371
  myIndex.setKeys(keys);
349
372
  myIndex.setIndexRecords(records);
350
373
  return myIndex;
351
374
  }
352
- function computeScore$1(pattern, {
353
- errors = 0,
354
- currentLocation = 0,
355
- expectedLocation = 0,
356
- distance = Config.distance,
357
- ignoreLocation = Config.ignoreLocation
358
- } = {}) {
359
- const accuracy = errors / pattern.length;
360
- if (ignoreLocation) {
361
- return accuracy;
362
- }
363
- const proximity = Math.abs(expectedLocation - currentLocation);
364
- if (!distance) {
365
- return proximity ? 1 : accuracy;
366
- }
367
- return accuracy + proximity / distance;
368
- }
369
375
  function convertMaskToIndices(matchmask = [], minMatchCharLength = Config.minMatchCharLength) {
370
- let indices = [];
376
+ const indices = [];
371
377
  let start = -1;
372
378
  let end = -1;
373
379
  let i = 0;
374
380
  for (let len = matchmask.length; i < len; i += 1) {
375
- let match = matchmask[i];
381
+ const match = matchmask[i];
376
382
  if (match && start === -1) {
377
383
  start = i;
378
384
  } else if (!match && start !== -1) {
@@ -406,16 +412,18 @@
406
412
  const expectedLocation = Math.max(0, Math.min(location, textLen));
407
413
  let currentThreshold = threshold;
408
414
  let bestLocation = expectedLocation;
415
+ const calcScore = (errors, currentLocation) => {
416
+ const accuracy = errors / patternLen;
417
+ if (ignoreLocation) return accuracy;
418
+ const proximity = Math.abs(expectedLocation - currentLocation);
419
+ if (!distance) return proximity ? 1 : accuracy;
420
+ return accuracy + proximity / distance;
421
+ };
409
422
  const computeMatches = minMatchCharLength > 1 || includeMatches;
410
423
  const matchMask = computeMatches ? Array(textLen) : [];
411
424
  let index;
412
425
  while ((index = text.indexOf(pattern, bestLocation)) > -1) {
413
- let score = computeScore$1(pattern, {
414
- currentLocation: index,
415
- expectedLocation,
416
- distance,
417
- ignoreLocation
418
- });
426
+ const score = calcScore(0, index);
419
427
  currentThreshold = Math.min(score, currentThreshold);
420
428
  bestLocation = index + patternLen;
421
429
  if (computeMatches) {
@@ -435,13 +443,7 @@
435
443
  let binMin = 0;
436
444
  let binMid = binMax;
437
445
  while (binMin < binMid) {
438
- const score2 = computeScore$1(pattern, {
439
- errors: i,
440
- currentLocation: expectedLocation + binMid,
441
- expectedLocation,
442
- distance,
443
- ignoreLocation
444
- });
446
+ const score2 = calcScore(i, expectedLocation + binMid);
445
447
  if (score2 <= currentThreshold) {
446
448
  binMin = binMid;
447
449
  } else {
@@ -451,12 +453,12 @@
451
453
  }
452
454
  binMax = binMid;
453
455
  let start = Math.max(1, expectedLocation - binMid + 1);
454
- let finish = findAllMatches ? textLen : Math.min(expectedLocation + binMid, textLen) + patternLen;
455
- let bitArr = Array(finish + 2);
456
+ const finish = findAllMatches ? textLen : Math.min(expectedLocation + binMid, textLen) + patternLen;
457
+ const bitArr = Array(finish + 2);
456
458
  bitArr[finish + 1] = (1 << i) - 1;
457
459
  for (let j = finish; j >= start; j -= 1) {
458
- let currentLocation = j - 1;
459
- let charMatch = patternAlphabet[text.charAt(currentLocation)];
460
+ const currentLocation = j - 1;
461
+ const charMatch = patternAlphabet[text[currentLocation]];
460
462
  if (computeMatches) {
461
463
  matchMask[currentLocation] = +!!charMatch;
462
464
  }
@@ -465,13 +467,7 @@
465
467
  bitArr[j] |= (lastBitArr[j + 1] | lastBitArr[j]) << 1 | 1 | lastBitArr[j + 1];
466
468
  }
467
469
  if (bitArr[j] & mask) {
468
- finalScore = computeScore$1(pattern, {
469
- errors: i,
470
- currentLocation,
471
- expectedLocation,
472
- distance,
473
- ignoreLocation
474
- });
470
+ finalScore = calcScore(i, currentLocation);
475
471
  if (finalScore <= currentThreshold) {
476
472
  currentThreshold = finalScore;
477
473
  bestLocation = currentLocation;
@@ -482,13 +478,7 @@
482
478
  }
483
479
  }
484
480
  }
485
- const score = computeScore$1(pattern, {
486
- errors: i + 1,
487
- currentLocation: expectedLocation,
488
- expectedLocation,
489
- distance,
490
- ignoreLocation
491
- });
481
+ const score = calcScore(i + 1, expectedLocation);
492
482
  if (score > currentThreshold) {
493
483
  break;
494
484
  }
@@ -510,14 +500,56 @@
510
500
  return result;
511
501
  }
512
502
  function createPatternAlphabet(pattern) {
513
- let mask = {};
503
+ const mask = {};
514
504
  for (let i = 0, len = pattern.length; i < len; i += 1) {
515
505
  const char = pattern.charAt(i);
516
506
  mask[char] = (mask[char] || 0) | 1 << len - i - 1;
517
507
  }
518
508
  return mask;
519
509
  }
520
- const stripDiacritics = String.prototype.normalize ? (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, "") : (str) => str;
510
+ function mergeIndices(indices) {
511
+ if (indices.length <= 1) return indices;
512
+ indices.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
513
+ const merged = [indices[0]];
514
+ for (let i = 1, len = indices.length; i < len; i += 1) {
515
+ const last = merged[merged.length - 1];
516
+ const curr = indices[i];
517
+ if (curr[0] <= last[1] + 1) {
518
+ last[1] = Math.max(last[1], curr[1]);
519
+ } else {
520
+ merged.push(curr);
521
+ }
522
+ }
523
+ return merged;
524
+ }
525
+ const NON_DECOMPOSABLE_MAP = {
526
+ "ł": "l",
527
+ // ł
528
+ "Ł": "L",
529
+ // Ł
530
+ "đ": "d",
531
+ // đ
532
+ "Đ": "D",
533
+ // Đ
534
+ "ø": "o",
535
+ // ø
536
+ "Ø": "O",
537
+ // Ø
538
+ "ħ": "h",
539
+ // ħ
540
+ "Ħ": "H",
541
+ // Ħ
542
+ "ŧ": "t",
543
+ // ŧ
544
+ "Ŧ": "T",
545
+ // Ŧ
546
+ "ı": "i",
547
+ // ı
548
+ "ß": "ss"
549
+ // ß
550
+ };
551
+ const NON_DECOMPOSABLE_RE = new RegExp("[" + Object.keys(NON_DECOMPOSABLE_MAP).join("") + "]", "g");
552
+ const stripDiacritics = String.prototype.normalize ? (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;
521
553
  class BitapSearch {
522
554
  constructor(pattern, {
523
555
  location = Config.location,
@@ -573,11 +605,15 @@
573
605
  }
574
606
  }
575
607
  searchIn(text) {
576
- const { isCaseSensitive, ignoreDiacritics, includeMatches } = this.options;
608
+ const {
609
+ isCaseSensitive,
610
+ ignoreDiacritics,
611
+ includeMatches
612
+ } = this.options;
577
613
  text = isCaseSensitive ? text : text.toLowerCase();
578
614
  text = ignoreDiacritics ? stripDiacritics(text) : text;
579
615
  if (this.pattern === text) {
580
- let result2 = {
616
+ const result2 = {
581
617
  isMatch: true,
582
618
  score: 0
583
619
  };
@@ -594,11 +630,19 @@
594
630
  minMatchCharLength,
595
631
  ignoreLocation
596
632
  } = this.options;
597
- let allIndices = [];
633
+ const allIndices = [];
598
634
  let totalScore = 0;
599
635
  let hasMatches = false;
600
- this.chunks.forEach(({ pattern, alphabet, startIndex }) => {
601
- const { isMatch, score, indices } = search(text, pattern, alphabet, {
636
+ this.chunks.forEach(({
637
+ pattern,
638
+ alphabet,
639
+ startIndex
640
+ }) => {
641
+ const {
642
+ isMatch,
643
+ score,
644
+ indices
645
+ } = search(text, pattern, alphabet, {
602
646
  location: location + startIndex,
603
647
  distance,
604
648
  threshold,
@@ -612,15 +656,15 @@
612
656
  }
613
657
  totalScore += score;
614
658
  if (isMatch && indices) {
615
- allIndices = [...allIndices, ...indices];
659
+ allIndices.push(...indices);
616
660
  }
617
661
  });
618
- let result = {
662
+ const result = {
619
663
  isMatch: hasMatches,
620
664
  score: hasMatches ? totalScore / this.chunks.length : 1
621
665
  };
622
666
  if (hasMatches && includeMatches) {
623
- result.indices = allIndices;
667
+ result.indices = mergeIndices(allIndices);
624
668
  }
625
669
  return result;
626
670
  }
@@ -635,7 +679,12 @@
635
679
  static isSingleMatch(pattern) {
636
680
  return getMatch(pattern, this.singleRegex);
637
681
  }
638
- search() {
682
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
683
+ search(_text) {
684
+ return {
685
+ isMatch: false,
686
+ score: 1
687
+ };
639
688
  }
640
689
  }
641
690
  function getMatch(pattern, exp) {
@@ -843,30 +892,58 @@
843
892
  };
844
893
  }
845
894
  }
846
- const searchers = [
847
- ExactMatch,
848
- IncludeMatch,
849
- PrefixExactMatch,
850
- InversePrefixExactMatch,
851
- InverseSuffixExactMatch,
852
- SuffixExactMatch,
853
- InverseExactMatch,
854
- FuzzyMatch
855
- ];
895
+ const searchers = [ExactMatch, IncludeMatch, PrefixExactMatch, InversePrefixExactMatch, InverseSuffixExactMatch, SuffixExactMatch, InverseExactMatch, FuzzyMatch];
856
896
  const searchersLen = searchers.length;
857
- const SPACE_RE = / +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/;
897
+ const ESCAPED_PIPE = "\0";
858
898
  const OR_TOKEN = "|";
899
+ function tokenize(pattern) {
900
+ const tokens = [];
901
+ const len = pattern.length;
902
+ let i = 0;
903
+ while (i < len) {
904
+ while (i < len && pattern[i] === " ") i++;
905
+ if (i >= len) break;
906
+ let j = i;
907
+ while (j < len && pattern[j] !== " " && pattern[j] !== '"') j++;
908
+ if (j < len && pattern[j] === '"') {
909
+ j++;
910
+ while (j < len) {
911
+ if (pattern[j] === '"') {
912
+ const next = j + 1;
913
+ if (next >= len || pattern[next] === " ") {
914
+ j++;
915
+ break;
916
+ }
917
+ if (pattern[next] === "$" && (next + 1 >= len || pattern[next + 1] === " ")) {
918
+ j += 2;
919
+ break;
920
+ }
921
+ }
922
+ j++;
923
+ }
924
+ tokens.push(pattern.substring(i, j));
925
+ i = j;
926
+ } else {
927
+ while (j < len && pattern[j] !== " ") j++;
928
+ tokens.push(pattern.substring(i, j));
929
+ i = j;
930
+ }
931
+ }
932
+ return tokens;
933
+ }
859
934
  function parseQuery(pattern, options = {}) {
860
- return pattern.split(OR_TOKEN).map((item) => {
861
- let query = item.trim().split(SPACE_RE).filter((item2) => item2 && !!item2.trim());
862
- let results = [];
935
+ const escaped = pattern.replace(/\\\|/g, ESCAPED_PIPE);
936
+ return escaped.split(OR_TOKEN).map((item) => {
937
+ const restored = item.replace(/\u0000/g, "|");
938
+ const query = tokenize(restored.trim()).filter((item2) => item2 && !!item2.trim());
939
+ const results = [];
863
940
  for (let i = 0, len = query.length; i < len; i += 1) {
864
941
  const queryItem = query[i];
865
942
  let found = false;
866
943
  let idx = -1;
867
944
  while (!found && ++idx < searchersLen) {
868
945
  const searcher = searchers[idx];
869
- let token = searcher.isMultiMatch(queryItem);
946
+ const token = searcher.isMultiMatch(queryItem);
870
947
  if (token) {
871
948
  results.push(new searcher(token, options));
872
949
  found = true;
@@ -878,7 +955,7 @@
878
955
  idx = -1;
879
956
  while (++idx < searchersLen) {
880
957
  const searcher = searchers[idx];
881
- let token = searcher.isSingleMatch(queryItem);
958
+ const token = searcher.isSingleMatch(queryItem);
882
959
  if (token) {
883
960
  results.push(new searcher(token, options));
884
961
  break;
@@ -921,6 +998,9 @@
921
998
  static condition(_, options) {
922
999
  return options.useExtendedSearch;
923
1000
  }
1001
+ // Note: searchIn operates on a single text value and sets hasInverse on the
1002
+ // result when inverse patterns are involved. _searchObjectList uses this to
1003
+ // switch from "ANY key" to "ALL keys" aggregation. See #712.
924
1004
  searchIn(text) {
925
1005
  const query = this.query;
926
1006
  if (!query) {
@@ -929,26 +1009,39 @@
929
1009
  score: 1
930
1010
  };
931
1011
  }
932
- const { includeMatches, isCaseSensitive, ignoreDiacritics } = this.options;
1012
+ const {
1013
+ includeMatches,
1014
+ isCaseSensitive,
1015
+ ignoreDiacritics
1016
+ } = this.options;
933
1017
  text = isCaseSensitive ? text : text.toLowerCase();
934
1018
  text = ignoreDiacritics ? stripDiacritics(text) : text;
935
1019
  let numMatches = 0;
936
- let allIndices = [];
1020
+ const allIndices = [];
937
1021
  let totalScore = 0;
1022
+ let hasInverse = false;
938
1023
  for (let i = 0, qLen = query.length; i < qLen; i += 1) {
939
1024
  const searchers2 = query[i];
940
1025
  allIndices.length = 0;
941
1026
  numMatches = 0;
1027
+ hasInverse = false;
942
1028
  for (let j = 0, pLen = searchers2.length; j < pLen; j += 1) {
943
1029
  const searcher = searchers2[j];
944
- const { isMatch, indices, score } = searcher.search(text);
1030
+ const {
1031
+ isMatch,
1032
+ indices,
1033
+ score
1034
+ } = searcher.search(text);
945
1035
  if (isMatch) {
946
1036
  numMatches += 1;
947
1037
  totalScore += score;
1038
+ const type = searcher.constructor.type;
1039
+ if (type.startsWith("inverse")) {
1040
+ hasInverse = true;
1041
+ }
948
1042
  if (includeMatches) {
949
- const type = searcher.constructor.type;
950
1043
  if (MultiMatchSet.has(type)) {
951
- allIndices = [...allIndices, ...indices];
1044
+ allIndices.push(...indices);
952
1045
  } else {
953
1046
  allIndices.push(indices);
954
1047
  }
@@ -957,16 +1050,20 @@
957
1050
  totalScore = 0;
958
1051
  numMatches = 0;
959
1052
  allIndices.length = 0;
1053
+ hasInverse = false;
960
1054
  break;
961
1055
  }
962
1056
  }
963
1057
  if (numMatches) {
964
- let result = {
1058
+ const result = {
965
1059
  isMatch: true,
966
1060
  score: totalScore / numMatches
967
1061
  };
1062
+ if (hasInverse) {
1063
+ result.hasInverse = true;
1064
+ }
968
1065
  if (includeMatches) {
969
- result.indices = allIndices;
1066
+ result.indices = mergeIndices(allIndices);
970
1067
  }
971
1068
  return result;
972
1069
  }
@@ -983,7 +1080,7 @@
983
1080
  }
984
1081
  function createSearcher(pattern, options) {
985
1082
  for (let i = 0, len = registeredSearchers.length; i < len; i += 1) {
986
- let searcherClass = registeredSearchers[i];
1083
+ const searcherClass = registeredSearchers[i];
987
1084
  if (searcherClass.condition(pattern, options)) {
988
1085
  return new searcherClass(pattern, options);
989
1086
  }
@@ -1006,9 +1103,21 @@
1006
1103
  [key]: query[key]
1007
1104
  }))
1008
1105
  });
1009
- function parse(query, options, { auto = true } = {}) {
1106
+ function parse(query, options, {
1107
+ auto = true
1108
+ } = {}) {
1010
1109
  const next = (query2) => {
1011
- let keys = Object.keys(query2);
1110
+ if (isString(query2)) {
1111
+ const obj = {
1112
+ keyId: null,
1113
+ pattern: query2
1114
+ };
1115
+ if (auto) {
1116
+ obj.searcher = createSearcher(query2, options);
1117
+ }
1118
+ return obj;
1119
+ }
1120
+ const keys = Object.keys(query2);
1012
1121
  const isQueryPath = isPath(query2);
1013
1122
  if (!isQueryPath && keys.length > 1 && !isExpression(query2)) {
1014
1123
  return next(convertToExplicit(query2));
@@ -1028,7 +1137,7 @@
1028
1137
  }
1029
1138
  return obj;
1030
1139
  }
1031
- let node = {
1140
+ const node = {
1032
1141
  children: [],
1033
1142
  operator: keys[0]
1034
1143
  };
@@ -1047,19 +1156,85 @@
1047
1156
  }
1048
1157
  return next(query);
1049
1158
  }
1050
- function computeScore(results, { ignoreFieldNorm = Config.ignoreFieldNorm }) {
1159
+ function computeScoreSingle(matches, {
1160
+ ignoreFieldNorm = Config.ignoreFieldNorm
1161
+ }) {
1162
+ let totalScore = 1;
1163
+ matches.forEach(({
1164
+ key,
1165
+ norm: norm2,
1166
+ score
1167
+ }) => {
1168
+ const weight = key ? key.weight : null;
1169
+ totalScore *= Math.pow(score === 0 && weight ? Number.EPSILON : score, (weight || 1) * (ignoreFieldNorm ? 1 : norm2));
1170
+ });
1171
+ return totalScore;
1172
+ }
1173
+ function computeScore(results, {
1174
+ ignoreFieldNorm = Config.ignoreFieldNorm
1175
+ }) {
1051
1176
  results.forEach((result) => {
1052
- let totalScore = 1;
1053
- result.matches.forEach(({ key, norm: norm2, score }) => {
1054
- const weight = key ? key.weight : null;
1055
- totalScore *= Math.pow(
1056
- score === 0 && weight ? Number.EPSILON : score,
1057
- (weight || 1) * (ignoreFieldNorm ? 1 : norm2)
1058
- );
1177
+ result.score = computeScoreSingle(result.matches, {
1178
+ ignoreFieldNorm
1059
1179
  });
1060
- result.score = totalScore;
1061
1180
  });
1062
1181
  }
1182
+ class MaxHeap {
1183
+ constructor(limit) {
1184
+ this.limit = limit;
1185
+ this.heap = [];
1186
+ }
1187
+ get size() {
1188
+ return this.heap.length;
1189
+ }
1190
+ shouldInsert(score) {
1191
+ return this.size < this.limit || score < this.heap[0].score;
1192
+ }
1193
+ insert(item) {
1194
+ if (this.size < this.limit) {
1195
+ this.heap.push(item);
1196
+ this._bubbleUp(this.size - 1);
1197
+ } else if (item.score < this.heap[0].score) {
1198
+ this.heap[0] = item;
1199
+ this._sinkDown(0);
1200
+ }
1201
+ }
1202
+ extractSorted(sortFn) {
1203
+ return this.heap.sort(sortFn);
1204
+ }
1205
+ _bubbleUp(i) {
1206
+ const heap = this.heap;
1207
+ while (i > 0) {
1208
+ const parent = i - 1 >> 1;
1209
+ if (heap[i].score <= heap[parent].score) break;
1210
+ const tmp = heap[i];
1211
+ heap[i] = heap[parent];
1212
+ heap[parent] = tmp;
1213
+ i = parent;
1214
+ }
1215
+ }
1216
+ _sinkDown(i) {
1217
+ const heap = this.heap;
1218
+ const len = heap.length;
1219
+ let largest = i;
1220
+ do {
1221
+ i = largest;
1222
+ const left = 2 * i + 1;
1223
+ const right = 2 * i + 2;
1224
+ if (left < len && heap[left].score > heap[largest].score) {
1225
+ largest = left;
1226
+ }
1227
+ if (right < len && heap[right].score > heap[largest].score) {
1228
+ largest = right;
1229
+ }
1230
+ if (largest !== i) {
1231
+ const tmp = heap[i];
1232
+ heap[i] = heap[largest];
1233
+ heap[largest] = tmp;
1234
+ }
1235
+ } while (largest !== i);
1236
+ }
1237
+ }
1063
1238
  function transformMatches(result, data) {
1064
1239
  const matches = result.matches;
1065
1240
  data.matches = [];
@@ -1070,8 +1245,11 @@
1070
1245
  if (!isDefined(match.indices) || !match.indices.length) {
1071
1246
  return;
1072
1247
  }
1073
- const { indices, value } = match;
1074
- let obj = {
1248
+ const {
1249
+ indices,
1250
+ value
1251
+ } = match;
1252
+ const obj = {
1075
1253
  indices,
1076
1254
  value
1077
1255
  };
@@ -1095,7 +1273,9 @@
1095
1273
  if (includeMatches) transformers.push(transformMatches);
1096
1274
  if (includeScore) transformers.push(transformScore);
1097
1275
  return results.map((result) => {
1098
- const { idx } = result;
1276
+ const {
1277
+ idx
1278
+ } = result;
1099
1279
  const data = {
1100
1280
  item: docs[idx],
1101
1281
  refIndex: idx
@@ -1108,12 +1288,174 @@
1108
1288
  return data;
1109
1289
  });
1110
1290
  }
1291
+ const WORD = /\b\w+\b/g;
1292
+ function createAnalyzer({
1293
+ isCaseSensitive = false,
1294
+ ignoreDiacritics = false
1295
+ } = {}) {
1296
+ return {
1297
+ tokenize(text) {
1298
+ if (!isCaseSensitive) {
1299
+ text = text.toLowerCase();
1300
+ }
1301
+ if (ignoreDiacritics) {
1302
+ text = stripDiacritics(text);
1303
+ }
1304
+ return text.match(WORD) || [];
1305
+ }
1306
+ };
1307
+ }
1308
+ function buildInvertedIndex(records, keyCount, analyzer) {
1309
+ const terms = /* @__PURE__ */ new Map();
1310
+ const df = /* @__PURE__ */ new Map();
1311
+ let fieldCount = 0;
1312
+ function addField(text, docIdx, keyIdx, subIdx) {
1313
+ const tokens = analyzer.tokenize(text);
1314
+ if (!tokens.length) return;
1315
+ fieldCount++;
1316
+ const termFreqs = /* @__PURE__ */ new Map();
1317
+ for (const token of tokens) {
1318
+ termFreqs.set(token, (termFreqs.get(token) || 0) + 1);
1319
+ }
1320
+ for (const [term, tf] of termFreqs) {
1321
+ const posting = {
1322
+ docIdx,
1323
+ keyIdx,
1324
+ subIdx,
1325
+ tf
1326
+ };
1327
+ let postings = terms.get(term);
1328
+ if (!postings) {
1329
+ postings = [];
1330
+ terms.set(term, postings);
1331
+ }
1332
+ postings.push(posting);
1333
+ df.set(term, (df.get(term) || 0) + 1);
1334
+ }
1335
+ }
1336
+ for (const record of records) {
1337
+ const {
1338
+ i: docIdx,
1339
+ v,
1340
+ $: fields
1341
+ } = record;
1342
+ if (v !== void 0) {
1343
+ addField(v, docIdx, -1, -1);
1344
+ continue;
1345
+ }
1346
+ if (fields) {
1347
+ for (let keyIdx = 0; keyIdx < keyCount; keyIdx++) {
1348
+ const value = fields[keyIdx];
1349
+ if (!value) continue;
1350
+ if (Array.isArray(value)) {
1351
+ for (const sub of value) {
1352
+ addField(sub.v, docIdx, keyIdx, sub.i ?? -1);
1353
+ }
1354
+ } else {
1355
+ addField(value.v, docIdx, keyIdx, -1);
1356
+ }
1357
+ }
1358
+ }
1359
+ }
1360
+ return {
1361
+ terms,
1362
+ fieldCount,
1363
+ df
1364
+ };
1365
+ }
1366
+ function addToInvertedIndex(index, record, keyCount, analyzer) {
1367
+ const {
1368
+ i: docIdx,
1369
+ v,
1370
+ $: fields
1371
+ } = record;
1372
+ function addField(text, keyIdx, subIdx) {
1373
+ const tokens = analyzer.tokenize(text);
1374
+ if (!tokens.length) return;
1375
+ index.fieldCount++;
1376
+ const termFreqs = /* @__PURE__ */ new Map();
1377
+ for (const token of tokens) {
1378
+ termFreqs.set(token, (termFreqs.get(token) || 0) + 1);
1379
+ }
1380
+ for (const [term, tf] of termFreqs) {
1381
+ const posting = {
1382
+ docIdx,
1383
+ keyIdx,
1384
+ subIdx,
1385
+ tf
1386
+ };
1387
+ let postings = index.terms.get(term);
1388
+ if (!postings) {
1389
+ postings = [];
1390
+ index.terms.set(term, postings);
1391
+ }
1392
+ postings.push(posting);
1393
+ index.df.set(term, (index.df.get(term) || 0) + 1);
1394
+ }
1395
+ }
1396
+ if (v !== void 0) {
1397
+ addField(v, -1, -1);
1398
+ return;
1399
+ }
1400
+ if (fields) {
1401
+ for (let keyIdx = 0; keyIdx < keyCount; keyIdx++) {
1402
+ const value = fields[keyIdx];
1403
+ if (!value) continue;
1404
+ if (Array.isArray(value)) {
1405
+ for (const sub of value) {
1406
+ addField(sub.v, keyIdx, sub.i ?? -1);
1407
+ }
1408
+ } else {
1409
+ addField(value.v, keyIdx, -1);
1410
+ }
1411
+ }
1412
+ }
1413
+ }
1414
+ function removeFromInvertedIndex(index, docIdx) {
1415
+ for (const [term, postings] of index.terms) {
1416
+ const filtered = postings.filter((p) => p.docIdx !== docIdx);
1417
+ const removed = postings.length - filtered.length;
1418
+ if (removed > 0) {
1419
+ index.fieldCount -= removed;
1420
+ index.df.set(term, (index.df.get(term) || 0) - removed);
1421
+ if (filtered.length === 0) {
1422
+ index.terms.delete(term);
1423
+ index.df.delete(term);
1424
+ } else {
1425
+ index.terms.set(term, filtered);
1426
+ }
1427
+ }
1428
+ }
1429
+ }
1111
1430
  class Fuse {
1112
- constructor(docs, options = {}, index) {
1113
- this.options = { ...Config, ...options };
1431
+ // Statics are assigned in entry.ts
1432
+ constructor(docs, options, index) {
1433
+ this.options = {
1434
+ ...Config,
1435
+ ...options
1436
+ };
1114
1437
  if (this.options.useExtendedSearch && false) ;
1438
+ if (this.options.useTokenSearch && false) ;
1115
1439
  this._keyStore = new KeyStore(this.options.keys);
1440
+ this._docs = docs;
1441
+ this._myIndex = null;
1442
+ this._invertedIndex = null;
1116
1443
  this.setCollection(docs, index);
1444
+ this._lastQuery = null;
1445
+ this._lastSearcher = null;
1446
+ }
1447
+ _getSearcher(query) {
1448
+ if (this._lastQuery === query) {
1449
+ return this._lastSearcher;
1450
+ }
1451
+ const opts = this._invertedIndex ? {
1452
+ ...this.options,
1453
+ _invertedIndex: this._invertedIndex
1454
+ } : this.options;
1455
+ const searcher = createSearcher(query, opts);
1456
+ this._lastQuery = query;
1457
+ this._lastSearcher = searcher;
1458
+ return searcher;
1117
1459
  }
1118
1460
  setCollection(docs, index) {
1119
1461
  this._docs = docs;
@@ -1124,6 +1466,13 @@
1124
1466
  getFn: this.options.getFn,
1125
1467
  fieldNormWeight: this.options.fieldNormWeight
1126
1468
  });
1469
+ if (this.options.useTokenSearch) {
1470
+ const analyzer = createAnalyzer({
1471
+ isCaseSensitive: this.options.isCaseSensitive,
1472
+ ignoreDiacritics: this.options.ignoreDiacritics
1473
+ });
1474
+ this._invertedIndex = buildInvertedIndex(this._myIndex.records, this._myIndex.keys.length, analyzer);
1475
+ }
1127
1476
  }
1128
1477
  add(doc) {
1129
1478
  if (!isDefined(doc)) {
@@ -1131,28 +1480,52 @@
1131
1480
  }
1132
1481
  this._docs.push(doc);
1133
1482
  this._myIndex.add(doc);
1483
+ if (this._invertedIndex) {
1484
+ const record = this._myIndex.records[this._myIndex.records.length - 1];
1485
+ const analyzer = createAnalyzer({
1486
+ isCaseSensitive: this.options.isCaseSensitive,
1487
+ ignoreDiacritics: this.options.ignoreDiacritics
1488
+ });
1489
+ addToInvertedIndex(this._invertedIndex, record, this._myIndex.keys.length, analyzer);
1490
+ }
1134
1491
  }
1135
1492
  remove(predicate = () => false) {
1136
1493
  const results = [];
1494
+ const indicesToRemove = [];
1137
1495
  for (let i = 0, len = this._docs.length; i < len; i += 1) {
1138
- const doc = this._docs[i];
1139
- if (predicate(doc, i)) {
1140
- this.removeAt(i);
1141
- i -= 1;
1142
- len -= 1;
1143
- results.push(doc);
1496
+ if (predicate(this._docs[i], i)) {
1497
+ results.push(this._docs[i]);
1498
+ indicesToRemove.push(i);
1499
+ }
1500
+ }
1501
+ if (indicesToRemove.length) {
1502
+ if (this._invertedIndex) {
1503
+ for (const idx of indicesToRemove) {
1504
+ removeFromInvertedIndex(this._invertedIndex, idx);
1505
+ }
1144
1506
  }
1507
+ for (let i = indicesToRemove.length - 1; i >= 0; i -= 1) {
1508
+ this._docs.splice(indicesToRemove[i], 1);
1509
+ }
1510
+ this._myIndex.removeAll(indicesToRemove);
1145
1511
  }
1146
1512
  return results;
1147
1513
  }
1148
1514
  removeAt(idx) {
1149
- this._docs.splice(idx, 1);
1515
+ if (this._invertedIndex) {
1516
+ removeFromInvertedIndex(this._invertedIndex, idx);
1517
+ }
1518
+ const doc = this._docs.splice(idx, 1)[0];
1150
1519
  this._myIndex.removeAt(idx);
1520
+ return doc;
1151
1521
  }
1152
1522
  getIndex() {
1153
1523
  return this._myIndex;
1154
1524
  }
1155
- search(query, { limit = -1 } = {}) {
1525
+ search(query, options) {
1526
+ const {
1527
+ limit = -1
1528
+ } = options || {};
1156
1529
  const {
1157
1530
  includeMatches,
1158
1531
  includeScore,
@@ -1160,34 +1533,92 @@
1160
1533
  sortFn,
1161
1534
  ignoreFieldNorm
1162
1535
  } = this.options;
1163
- let results = isString(query) ? isString(this._docs[0]) ? this._searchStringList(query) : this._searchObjectList(query) : this._searchLogical(query);
1164
- computeScore(results, { ignoreFieldNorm });
1165
- if (shouldSort) {
1166
- results.sort(sortFn);
1536
+ if (isString(query) && !query.trim()) {
1537
+ let docs = this._docs.map((item, idx) => ({
1538
+ item,
1539
+ refIndex: idx
1540
+ }));
1541
+ if (isNumber(limit) && limit > -1) {
1542
+ docs = docs.slice(0, limit);
1543
+ }
1544
+ return docs;
1167
1545
  }
1168
- if (isNumber(limit) && limit > -1) {
1169
- results = results.slice(0, limit);
1546
+ const useHeap = isNumber(limit) && limit > 0 && isString(query);
1547
+ let results;
1548
+ if (useHeap) {
1549
+ const heap = new MaxHeap(limit);
1550
+ if (isString(this._docs[0])) {
1551
+ this._searchStringList(query, {
1552
+ heap,
1553
+ ignoreFieldNorm
1554
+ });
1555
+ } else {
1556
+ this._searchObjectList(query, {
1557
+ heap,
1558
+ ignoreFieldNorm
1559
+ });
1560
+ }
1561
+ results = heap.extractSorted(sortFn);
1562
+ } else {
1563
+ results = isString(query) ? isString(this._docs[0]) ? this._searchStringList(query) : this._searchObjectList(query) : this._searchLogical(query);
1564
+ computeScore(results, {
1565
+ ignoreFieldNorm
1566
+ });
1567
+ if (shouldSort) {
1568
+ results.sort(sortFn);
1569
+ }
1570
+ if (isNumber(limit) && limit > -1) {
1571
+ results = results.slice(0, limit);
1572
+ }
1170
1573
  }
1171
1574
  return format(results, this._docs, {
1172
1575
  includeMatches,
1173
1576
  includeScore
1174
1577
  });
1175
1578
  }
1176
- _searchStringList(query) {
1177
- const searcher = createSearcher(query, this.options);
1178
- const { records } = this._myIndex;
1179
- const results = [];
1180
- records.forEach(({ v: text, i: idx, n: norm2 }) => {
1579
+ _searchStringList(query, {
1580
+ heap,
1581
+ ignoreFieldNorm
1582
+ } = {}) {
1583
+ const searcher = this._getSearcher(query);
1584
+ const {
1585
+ records
1586
+ } = this._myIndex;
1587
+ const results = heap ? null : [];
1588
+ records.forEach(({
1589
+ v: text,
1590
+ i: idx,
1591
+ n: norm2
1592
+ }) => {
1181
1593
  if (!isDefined(text)) {
1182
1594
  return;
1183
1595
  }
1184
- const { isMatch, score, indices } = searcher.searchIn(text);
1596
+ const {
1597
+ isMatch,
1598
+ score,
1599
+ indices
1600
+ } = searcher.searchIn(text);
1185
1601
  if (isMatch) {
1186
- results.push({
1602
+ const result = {
1187
1603
  item: text,
1188
1604
  idx,
1189
- matches: [{ score, value: text, norm: norm2, indices }]
1190
- });
1605
+ matches: [{
1606
+ score,
1607
+ value: text,
1608
+ norm: norm2,
1609
+ indices
1610
+ }]
1611
+ };
1612
+ if (heap) {
1613
+ result.score = computeScoreSingle(result.matches, {
1614
+ ignoreFieldNorm
1615
+ });
1616
+ if (heap.shouldInsert(result.score)) {
1617
+ heap.insert(result);
1618
+ }
1619
+ } else {
1620
+ results.push(result);
1621
+ }
1191
1622
  }
1192
1623
  });
1193
1624
  return results;
@@ -1195,94 +1626,171 @@
1195
1626
  _searchLogical(query) {
1196
1627
  const expression = parse(query, this.options);
1197
1628
  const evaluate = (node, item, idx) => {
1198
- if (!node.children) {
1199
- const { keyId, searcher } = node;
1200
- const matches = this._findMatches({
1201
- key: this._keyStore.get(keyId),
1202
- value: this._myIndex.getValueForItemAtKeyId(item, keyId),
1629
+ if (!("children" in node)) {
1630
+ const {
1631
+ keyId,
1203
1632
  searcher
1204
- });
1633
+ } = node;
1634
+ let matches;
1635
+ if (keyId === null) {
1636
+ matches = [];
1637
+ this._myIndex.keys.forEach((key, keyIndex) => {
1638
+ matches.push(...this._findMatches({
1639
+ key,
1640
+ value: item[keyIndex],
1641
+ searcher
1642
+ }));
1643
+ });
1644
+ } else {
1645
+ matches = this._findMatches({
1646
+ key: this._keyStore.get(keyId),
1647
+ value: this._myIndex.getValueForItemAtKeyId(item, keyId),
1648
+ searcher
1649
+ });
1650
+ }
1205
1651
  if (matches && matches.length) {
1206
- return [
1207
- {
1208
- idx,
1209
- item,
1210
- matches
1211
- }
1212
- ];
1652
+ return [{
1653
+ idx,
1654
+ item,
1655
+ matches
1656
+ }];
1213
1657
  }
1214
1658
  return [];
1215
1659
  }
1660
+ const {
1661
+ children,
1662
+ operator
1663
+ } = node;
1216
1664
  const res = [];
1217
- for (let i = 0, len = node.children.length; i < len; i += 1) {
1218
- const child = node.children[i];
1665
+ for (let i = 0, len = children.length; i < len; i += 1) {
1666
+ const child = children[i];
1219
1667
  const result = evaluate(child, item, idx);
1220
1668
  if (result.length) {
1221
1669
  res.push(...result);
1222
- } else if (node.operator === LogicalOperator.AND) {
1670
+ } else if (operator === LogicalOperator.AND) {
1223
1671
  return [];
1224
1672
  }
1225
1673
  }
1226
1674
  return res;
1227
1675
  };
1228
1676
  const records = this._myIndex.records;
1229
- const resultMap = {};
1677
+ const resultMap = /* @__PURE__ */ new Map();
1230
1678
  const results = [];
1231
- records.forEach(({ $: item, i: idx }) => {
1679
+ records.forEach(({
1680
+ $: item,
1681
+ i: idx
1682
+ }) => {
1232
1683
  if (isDefined(item)) {
1233
- let expResults = evaluate(expression, item, idx);
1684
+ const expResults = evaluate(expression, item, idx);
1234
1685
  if (expResults.length) {
1235
- if (!resultMap[idx]) {
1236
- resultMap[idx] = { idx, item, matches: [] };
1237
- results.push(resultMap[idx]);
1686
+ if (!resultMap.has(idx)) {
1687
+ resultMap.set(idx, {
1688
+ idx,
1689
+ item,
1690
+ matches: []
1691
+ });
1692
+ results.push(resultMap.get(idx));
1238
1693
  }
1239
- expResults.forEach(({ matches }) => {
1240
- resultMap[idx].matches.push(...matches);
1694
+ expResults.forEach(({
1695
+ matches
1696
+ }) => {
1697
+ resultMap.get(idx).matches.push(...matches);
1241
1698
  });
1242
1699
  }
1243
1700
  }
1244
1701
  });
1245
1702
  return results;
1246
1703
  }
1247
- _searchObjectList(query) {
1248
- const searcher = createSearcher(query, this.options);
1249
- const { keys, records } = this._myIndex;
1250
- const results = [];
1251
- records.forEach(({ $: item, i: idx }) => {
1704
+ // When a search involves inverse patterns (e.g. !Syrup), the aggregation
1705
+ // across keys switches from "ANY key matches" to "ALL keys must match."
1706
+ // This is signaled by hasInverse on the SearchResult from ExtendedSearch.
1707
+ //
1708
+ // For mixed patterns like "^hello !Syrup", a key failure is ambiguous —
1709
+ // it could be the positive or inverse term that failed. In that case we
1710
+ // conservatively exclude the item, which is strictly better than the old
1711
+ // behavior of including it. See: https://github.com/krisk/Fuse/issues/712
1712
+ _searchObjectList(query, {
1713
+ heap,
1714
+ ignoreFieldNorm
1715
+ } = {}) {
1716
+ const searcher = this._getSearcher(query);
1717
+ const {
1718
+ keys,
1719
+ records
1720
+ } = this._myIndex;
1721
+ const results = heap ? null : [];
1722
+ records.forEach(({
1723
+ $: item,
1724
+ i: idx
1725
+ }) => {
1252
1726
  if (!isDefined(item)) {
1253
1727
  return;
1254
1728
  }
1255
- let matches = [];
1729
+ const matches = [];
1730
+ let anyKeyFailed = false;
1731
+ let hasInverse = false;
1256
1732
  keys.forEach((key, keyIndex) => {
1257
- matches.push(
1258
- ...this._findMatches({
1259
- key,
1260
- value: item[keyIndex],
1261
- searcher
1262
- })
1263
- );
1733
+ const keyMatches = this._findMatches({
1734
+ key,
1735
+ value: item[keyIndex],
1736
+ searcher
1737
+ });
1738
+ if (keyMatches.length) {
1739
+ matches.push(...keyMatches);
1740
+ if (keyMatches[0].hasInverse) {
1741
+ hasInverse = true;
1742
+ }
1743
+ } else {
1744
+ anyKeyFailed = true;
1745
+ }
1264
1746
  });
1747
+ if (hasInverse && anyKeyFailed) {
1748
+ return;
1749
+ }
1265
1750
  if (matches.length) {
1266
- results.push({
1751
+ const result = {
1267
1752
  idx,
1268
1753
  item,
1269
1754
  matches
1270
- });
1755
+ };
1756
+ if (heap) {
1757
+ result.score = computeScoreSingle(result.matches, {
1758
+ ignoreFieldNorm
1759
+ });
1760
+ if (heap.shouldInsert(result.score)) {
1761
+ heap.insert(result);
1762
+ }
1763
+ } else {
1764
+ results.push(result);
1765
+ }
1271
1766
  }
1272
1767
  });
1273
1768
  return results;
1274
1769
  }
1275
- _findMatches({ key, value, searcher }) {
1770
+ _findMatches({
1771
+ key,
1772
+ value,
1773
+ searcher
1774
+ }) {
1276
1775
  if (!isDefined(value)) {
1277
1776
  return [];
1278
1777
  }
1279
- let matches = [];
1778
+ const matches = [];
1280
1779
  if (isArray(value)) {
1281
- value.forEach(({ v: text, i: idx, n: norm2 }) => {
1780
+ value.forEach(({
1781
+ v: text,
1782
+ i: idx,
1783
+ n: norm2
1784
+ }) => {
1282
1785
  if (!isDefined(text)) {
1283
1786
  return;
1284
1787
  }
1285
- const { isMatch, score, indices } = searcher.searchIn(text);
1788
+ const {
1789
+ isMatch,
1790
+ score,
1791
+ indices,
1792
+ hasInverse
1793
+ } = searcher.searchIn(text);
1286
1794
  if (isMatch) {
1287
1795
  matches.push({
1288
1796
  score,
@@ -1290,30 +1798,134 @@
1290
1798
  value: text,
1291
1799
  idx,
1292
1800
  norm: norm2,
1293
- indices
1801
+ indices,
1802
+ hasInverse
1294
1803
  });
1295
1804
  }
1296
1805
  });
1297
1806
  } else {
1298
- const { v: text, n: norm2 } = value;
1299
- const { isMatch, score, indices } = searcher.searchIn(text);
1807
+ const {
1808
+ v: text,
1809
+ n: norm2
1810
+ } = value;
1811
+ const {
1812
+ isMatch,
1813
+ score,
1814
+ indices,
1815
+ hasInverse
1816
+ } = searcher.searchIn(text);
1300
1817
  if (isMatch) {
1301
- matches.push({ score, key, value: text, norm: norm2, indices });
1818
+ matches.push({
1819
+ score,
1820
+ key,
1821
+ value: text,
1822
+ norm: norm2,
1823
+ indices,
1824
+ hasInverse
1825
+ });
1302
1826
  }
1303
1827
  }
1304
1828
  return matches;
1305
1829
  }
1306
1830
  }
1307
- Fuse.version = "7.1.0";
1831
+ class TokenSearch {
1832
+ static condition(_, options) {
1833
+ return options.useTokenSearch;
1834
+ }
1835
+ constructor(pattern, options) {
1836
+ this.options = options;
1837
+ this.analyzer = createAnalyzer({
1838
+ isCaseSensitive: options.isCaseSensitive,
1839
+ ignoreDiacritics: options.ignoreDiacritics
1840
+ });
1841
+ const queryTerms = this.analyzer.tokenize(pattern);
1842
+ const invertedIndex = options._invertedIndex;
1843
+ const {
1844
+ df,
1845
+ fieldCount
1846
+ } = invertedIndex;
1847
+ this.termSearchers = [];
1848
+ this.idfWeights = [];
1849
+ for (const term of queryTerms) {
1850
+ this.termSearchers.push(new BitapSearch(term, {
1851
+ location: options.location,
1852
+ threshold: options.threshold,
1853
+ distance: options.distance,
1854
+ includeMatches: options.includeMatches,
1855
+ findAllMatches: options.findAllMatches,
1856
+ minMatchCharLength: options.minMatchCharLength,
1857
+ isCaseSensitive: options.isCaseSensitive,
1858
+ ignoreDiacritics: options.ignoreDiacritics,
1859
+ ignoreLocation: true
1860
+ }));
1861
+ const docFreq = df.get(term) || 0;
1862
+ const idf = Math.log(1 + (fieldCount - docFreq + 0.5) / (docFreq + 0.5));
1863
+ this.idfWeights.push(idf);
1864
+ }
1865
+ }
1866
+ searchIn(text) {
1867
+ if (!this.termSearchers.length) {
1868
+ return {
1869
+ isMatch: false,
1870
+ score: 1
1871
+ };
1872
+ }
1873
+ const allIndices = [];
1874
+ let weightedScore = 0;
1875
+ let maxPossibleScore = 0;
1876
+ let matchedCount = 0;
1877
+ for (let i = 0; i < this.termSearchers.length; i++) {
1878
+ const result = this.termSearchers[i].searchIn(text);
1879
+ const idf = this.idfWeights[i];
1880
+ maxPossibleScore += idf;
1881
+ if (result.isMatch) {
1882
+ matchedCount++;
1883
+ weightedScore += idf * (1 - result.score);
1884
+ if (result.indices) {
1885
+ allIndices.push(...result.indices);
1886
+ }
1887
+ }
1888
+ }
1889
+ if (matchedCount === 0) {
1890
+ return {
1891
+ isMatch: false,
1892
+ score: 1
1893
+ };
1894
+ }
1895
+ const normalized = maxPossibleScore > 0 ? 1 - weightedScore / maxPossibleScore : 0;
1896
+ const searchResult = {
1897
+ isMatch: true,
1898
+ score: Math.max(1e-3, normalized)
1899
+ };
1900
+ if (this.options.includeMatches && allIndices.length) {
1901
+ searchResult.indices = mergeIndices(allIndices);
1902
+ }
1903
+ return searchResult;
1904
+ }
1905
+ }
1906
+ Fuse.version = "7.3.0";
1308
1907
  Fuse.createIndex = createIndex;
1309
1908
  Fuse.parseIndex = parseIndex;
1310
1909
  Fuse.config = Config;
1910
+ Fuse.match = function(pattern, text, options) {
1911
+ const searcher = createSearcher(pattern, {
1912
+ ...Config,
1913
+ ...options
1914
+ });
1915
+ return searcher.searchIn(text);
1916
+ };
1311
1917
  {
1312
1918
  Fuse.parseQuery = parse;
1313
1919
  }
1314
1920
  {
1315
1921
  register(ExtendedSearch);
1316
1922
  }
1923
+ {
1924
+ register(TokenSearch);
1925
+ }
1926
+ Fuse.use = function(...plugins) {
1927
+ plugins.forEach((plugin) => register(plugin));
1928
+ };
1317
1929
  const version = "0.3.5";
1318
1930
  const packageJson = {
1319
1931
  version