Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@orchidjs/sifter

Package Overview
Dependencies
Maintainers
0
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@orchidjs/sifter - npm Package Compare versions

Comparing version 1.0.3 to 1.1.0

dist/cjs/package.json

1391

dist/cjs/sifter.js

@@ -1,773 +0,3 @@

/*! sifter.js | https://github.com/orchidjs/sifter.js | Apache License (v2) */
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
/*! @orchidjs/unicode-variants | https://github.com/orchidjs/unicode-variants | Apache License (v2) */
"use strict";
/**
* Convert array of strings to a regular expression
* ex ['ab','a'] => (?:ab|a)
* ex ['a','b'] => [ab]
* @param {string[]} chars
* @return {string}
*/
const arrayToPattern = chars => {
chars = chars.filter(Boolean);
if (chars.length < 2) {
return chars[0] || '';
}
return maxValueLength(chars) == 1 ? '[' + chars.join('') + ']' : '(?:' + chars.join('|') + ')';
};
/**
* @param {string[]} array
* @return {string}
*/
const sequencePattern = array => {
if (!hasDuplicates(array)) {
return array.join('');
}
let pattern = '';
let prev_char_count = 0;
const prev_pattern = () => {
if (prev_char_count > 1) {
pattern += '{' + prev_char_count + '}';
}
};
array.forEach((char, i) => {
if (char === array[i - 1]) {
prev_char_count++;
return;
}
prev_pattern();
pattern += char;
prev_char_count = 1;
});
prev_pattern();
return pattern;
};
/**
* Convert array of strings to a regular expression
* ex ['ab','a'] => (?:ab|a)
* ex ['a','b'] => [ab]
* @param {Set<string>} chars
* @return {string}
*/
const setToPattern = chars => {
let array = toArray(chars);
return arrayToPattern(array);
};
/**
*
* https://stackoverflow.com/questions/7376598/in-javascript-how-do-i-check-if-an-array-has-duplicate-values
* @param {any[]} array
*/
const hasDuplicates = array => {
return new Set(array).size !== array.length;
};
/**
* https://stackoverflow.com/questions/63006601/why-does-u-throw-an-invalid-escape-error
* @param {string} str
* @return {string}
*/
const escape_regex = str => {
return (str + '').replace(/([\$\(-\+\.\?\[-\^\{-\}])/g, '\\$1');
};
/**
* Return the max length of array values
* @param {string[]} array
*
*/
const maxValueLength = array => {
return array.reduce((longest, value) => Math.max(longest, unicodeLength(value)), 0);
};
/**
* @param {string} str
*/
const unicodeLength = str => {
return toArray(str).length;
};
/**
* @param {any} p
* @return {any[]}
*/
const toArray = p => Array.from(p);
/*! @orchidjs/unicode-variants | https://github.com/orchidjs/unicode-variants | Apache License (v2) */
/**
* Get all possible combinations of substrings that add up to the given string
* https://stackoverflow.com/questions/30169587/find-all-the-combination-of-substrings-that-add-up-to-the-given-string
* @param {string} input
* @return {string[][]}
*/
const allSubstrings = input => {
if (input.length === 1) return [[input]];
/** @type {string[][]} */
let result = [];
const start = input.substring(1);
const suba = allSubstrings(start);
suba.forEach(function (subresult) {
let tmp = subresult.slice(0);
tmp[0] = input.charAt(0) + tmp[0];
result.push(tmp);
tmp = subresult.slice(0);
tmp.unshift(input.charAt(0));
result.push(tmp);
});
return result;
};
/*! @orchidjs/unicode-variants | https://github.com/orchidjs/unicode-variants | Apache License (v2) */
/**
* @typedef {{[key:string]:string}} TUnicodeMap
* @typedef {{[key:string]:Set<string>}} TUnicodeSets
* @typedef {[[number,number]]} TCodePoints
* @typedef {{folded:string,composed:string,code_point:number}} TCodePointObj
* @typedef {{start:number,end:number,length:number,substr:string}} TSequencePart
*/
/** @type {TCodePoints} */
const code_points = [[0, 65535]];
const accent_pat = '[\u0300-\u036F\u{b7}\u{2be}\u{2bc}]';
/** @type {TUnicodeMap} */
let unicode_map;
/** @type {RegExp} */
let multi_char_reg;
const max_char_length = 3;
/** @type {TUnicodeMap} */
const latin_convert = {};
/** @type {TUnicodeMap} */
const latin_condensed = {
'/': '⁄∕',
'0': '߀',
"a": "ⱥɐɑ",
"aa": "ꜳ",
"ae": "æǽǣ",
"ao": "ꜵ",
"au": "ꜷ",
"av": "ꜹꜻ",
"ay": "ꜽ",
"b": "ƀɓƃ",
"c": "ꜿƈȼↄ",
"d": "đɗɖᴅƌꮷԁɦ",
"e": "ɛǝᴇɇ",
"f": "ꝼƒ",
"g": "ǥɠꞡᵹꝿɢ",
"h": "ħⱨⱶɥ",
"i": "ɨı",
"j": "ɉȷ",
"k": "ƙⱪꝁꝃꝅꞣ",
"l": "łƚɫⱡꝉꝇꞁɭ",
"m": "ɱɯϻ",
"n": "ꞥƞɲꞑᴎлԉ",
"o": "øǿɔɵꝋꝍᴑ",
"oe": "œ",
"oi": "ƣ",
"oo": "ꝏ",
"ou": "ȣ",
"p": "ƥᵽꝑꝓꝕρ",
"q": "ꝗꝙɋ",
"r": "ɍɽꝛꞧꞃ",
"s": "ßȿꞩꞅʂ",
"t": "ŧƭʈⱦꞇ",
"th": "þ",
"tz": "ꜩ",
"u": "ʉ",
"v": "ʋꝟʌ",
"vy": "ꝡ",
"w": "ⱳ",
"y": "ƴɏỿ",
"z": "ƶȥɀⱬꝣ",
"hv": "ƕ"
};
for (let latin in latin_condensed) {
let unicode = latin_condensed[latin] || '';
for (let i = 0; i < unicode.length; i++) {
let char = unicode.substring(i, i + 1);
latin_convert[char] = latin;
}
}
const convert_pat = new RegExp(Object.keys(latin_convert).join('|') + '|' + accent_pat, 'gu');
/**
* Initialize the unicode_map from the give code point ranges
*
* @param {TCodePoints=} _code_points
*/
const initialize = _code_points => {
if (unicode_map !== undefined) return;
unicode_map = generateMap(_code_points || code_points);
};
/**
* Helper method for normalize a string
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize
* @param {string} str
* @param {string} form
*/
const normalize = (str, form = 'NFKD') => str.normalize(form);
/**
* Remove accents without reordering string
* calling str.normalize('NFKD') on \u{594}\u{595}\u{596} becomes \u{596}\u{594}\u{595}
* via https://github.com/krisk/Fuse/issues/133#issuecomment-318692703
* @param {string} str
* @return {string}
*/
const asciifold = str => {
return toArray(str).reduce(
/**
* @param {string} result
* @param {string} char
*/
(result, char) => {
return result + _asciifold(char);
}, '');
};
/**
* @param {string} str
* @return {string}
*/
const _asciifold = str => {
str = normalize(str).toLowerCase().replace(convert_pat,
/** @type {string} */
char => {
return latin_convert[char] || '';
}); //return str;
return normalize(str, 'NFC');
};
/**
* Generate a list of unicode variants from the list of code points
* @param {TCodePoints} code_points
* @yield {TCodePointObj}
*/
function* generator(code_points) {
for (const [code_point_min, code_point_max] of code_points) {
for (let i = code_point_min; i <= code_point_max; i++) {
let composed = String.fromCharCode(i);
let folded = asciifold(composed);
if (folded == composed.toLowerCase()) {
continue;
} // skip when folded is a string longer than 3 characters long
// bc the resulting regex patterns will be long
// eg:
// folded صلى الله عليه وسلم length 18 code point 65018
// folded جل جلاله length 8 code point 65019
if (folded.length > max_char_length) {
continue;
}
if (folded.length == 0) {
continue;
}
yield {
folded: folded,
composed: composed,
code_point: i
};
}
}
}
/**
* Generate a unicode map from the list of code points
* @param {TCodePoints} code_points
* @return {TUnicodeSets}
*/
const generateSets = code_points => {
/** @type {{[key:string]:Set<string>}} */
const unicode_sets = {};
/**
* @param {string} folded
* @param {string} to_add
*/
const addMatching = (folded, to_add) => {
/** @type {Set<string>} */
const folded_set = unicode_sets[folded] || new Set();
const patt = new RegExp('^' + setToPattern(folded_set) + '$', 'iu');
if (to_add.match(patt)) {
return;
}
folded_set.add(escape_regex(to_add));
unicode_sets[folded] = folded_set;
};
for (let value of generator(code_points)) {
addMatching(value.folded, value.folded);
addMatching(value.folded, value.composed);
}
return unicode_sets;
};
/**
* Generate a unicode map from the list of code points
* ae => (?:(?:ae|Æ|Ǽ|Ǣ)|(?:A|Ⓐ|A...)(?:E|ɛ|Ⓔ...))
*
* @param {TCodePoints} code_points
* @return {TUnicodeMap}
*/
const generateMap = code_points => {
/** @type {TUnicodeSets} */
const unicode_sets = generateSets(code_points);
/** @type {TUnicodeMap} */
const unicode_map = {};
/** @type {string[]} */
let multi_char = [];
for (let folded in unicode_sets) {
let set = unicode_sets[folded];
if (set) {
unicode_map[folded] = setToPattern(set);
}
if (folded.length > 1) {
multi_char.push(escape_regex(folded));
}
}
multi_char.sort((a, b) => b.length - a.length);
const multi_char_patt = arrayToPattern(multi_char);
multi_char_reg = new RegExp('^' + multi_char_patt, 'u');
return unicode_map;
};
/**
* Map each element of an array from it's folded value to all possible unicode matches
* @param {string[]} strings
* @param {number} min_replacement
* @return {string}
*/
const mapSequence = (strings, min_replacement = 1) => {
let chars_replaced = 0;
strings = strings.map(str => {
if (unicode_map[str]) {
chars_replaced += str.length;
}
return unicode_map[str] || str;
});
if (chars_replaced >= min_replacement) {
return sequencePattern(strings);
}
return '';
};
/**
* Convert a short string and split it into all possible patterns
* Keep a pattern only if min_replacement is met
*
* 'abc'
* => [['abc'],['ab','c'],['a','bc'],['a','b','c']]
* => ['abc-pattern','ab-c-pattern'...]
*
*
* @param {string} str
* @param {number} min_replacement
* @return {string}
*/
const substringsToPattern = (str, min_replacement = 1) => {
min_replacement = Math.max(min_replacement, str.length - 1);
return arrayToPattern(allSubstrings(str).map(sub_pat => {
return mapSequence(sub_pat, min_replacement);
}));
};
/**
* Convert an array of sequences into a pattern
* [{start:0,end:3,length:3,substr:'iii'}...] => (?:iii...)
*
* @param {Sequence[]} sequences
* @param {boolean} all
*/
const sequencesToPattern = (sequences, all = true) => {
let min_replacement = sequences.length > 1 ? 1 : 0;
return arrayToPattern(sequences.map(sequence => {
let seq = [];
const len = all ? sequence.length() : sequence.length() - 1;
for (let j = 0; j < len; j++) {
seq.push(substringsToPattern(sequence.substrs[j] || '', min_replacement));
}
return sequencePattern(seq);
}));
};
/**
* Return true if the sequence is already in the sequences
* @param {Sequence} needle_seq
* @param {Sequence[]} sequences
*/
const inSequences = (needle_seq, sequences) => {
for (const seq of sequences) {
if (seq.start != needle_seq.start || seq.end != needle_seq.end) {
continue;
}
if (seq.substrs.join('') !== needle_seq.substrs.join('')) {
continue;
}
let needle_parts = needle_seq.parts;
/**
* @param {TSequencePart} part
*/
const filter = part => {
for (const needle_part of needle_parts) {
if (needle_part.start === part.start && needle_part.substr === part.substr) {
return false;
}
if (part.length == 1 || needle_part.length == 1) {
continue;
} // check for overlapping parts
// a = ['::=','==']
// b = ['::','===']
// a = ['r','sm']
// b = ['rs','m']
if (part.start < needle_part.start && part.end > needle_part.start) {
return true;
}
if (needle_part.start < part.start && needle_part.end > part.start) {
return true;
}
}
return false;
};
let filtered = seq.parts.filter(filter);
if (filtered.length > 0) {
continue;
}
return true;
}
return false;
};
class Sequence {
constructor() {
/** @type {TSequencePart[]} */
this.parts = [];
/** @type {string[]} */
this.substrs = [];
this.start = 0;
this.end = 0;
}
/**
* @param {TSequencePart|undefined} part
*/
add(part) {
if (part) {
this.parts.push(part);
this.substrs.push(part.substr);
this.start = Math.min(part.start, this.start);
this.end = Math.max(part.end, this.end);
}
}
last() {
return this.parts[this.parts.length - 1];
}
length() {
return this.parts.length;
}
/**
* @param {number} position
* @param {TSequencePart} last_piece
*/
clone(position, last_piece) {
let clone = new Sequence();
let parts = JSON.parse(JSON.stringify(this.parts));
let last_part = parts.pop();
for (const part of parts) {
clone.add(part);
}
let last_substr = last_piece.substr.substring(0, position - last_part.start);
let clone_last_len = last_substr.length;
clone.add({
start: last_part.start,
end: last_part.start + clone_last_len,
length: clone_last_len,
substr: last_substr
});
return clone;
}
}
/**
* Expand a regular expression pattern to include unicode variants
* eg /a/ becomes /aⓐaẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐɑAⒶAÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄȺⱯ/
*
* Issue:
* ﺊﺋ [ 'ﺊ = \\u{fe8a}', 'ﺋ = \\u{fe8b}' ]
* becomes: ئئ [ 'ي = \\u{64a}', 'ٔ = \\u{654}', 'ي = \\u{64a}', 'ٔ = \\u{654}' ]
*
* İIJ = IIJ = ⅡJ
*
* 1/2/4
*
* @param {string} str
* @return {string|undefined}
*/
const getPattern = str => {
initialize();
str = asciifold(str);
let pattern = '';
let sequences = [new Sequence()];
for (let i = 0; i < str.length; i++) {
let substr = str.substring(i);
let match = substr.match(multi_char_reg);
const char = str.substring(i, i + 1);
const match_str = match ? match[0] : null; // loop through sequences
// add either the char or multi_match
let overlapping = [];
let added_types = new Set();
for (const sequence of sequences) {
const last_piece = sequence.last();
if (!last_piece || last_piece.length == 1 || last_piece.end <= i) {
// if we have a multi match
if (match_str) {
const len = match_str.length;
sequence.add({
start: i,
end: i + len,
length: len,
substr: match_str
});
added_types.add('1');
} else {
sequence.add({
start: i,
end: i + 1,
length: 1,
substr: char
});
added_types.add('2');
}
} else if (match_str) {
let clone = sequence.clone(i, last_piece);
const len = match_str.length;
clone.add({
start: i,
end: i + len,
length: len,
substr: match_str
});
overlapping.push(clone);
} else {
// don't add char
// adding would create invalid patterns: 234 => [2,34,4]
added_types.add('3');
}
} // if we have overlapping
if (overlapping.length > 0) {
// ['ii','iii'] before ['i','i','iii']
overlapping = overlapping.sort((a, b) => {
return a.length() - b.length();
});
for (let clone of overlapping) {
// don't add if we already have an equivalent sequence
if (inSequences(clone, sequences)) {
continue;
}
sequences.push(clone);
}
continue;
} // if we haven't done anything unique
// clean up the patterns
// helps keep patterns smaller
// if str = 'r₨㎧aarss', pattern will be 446 instead of 655
if (i > 0 && added_types.size == 1 && !added_types.has('3')) {
pattern += sequencesToPattern(sequences, false);
let new_seq = new Sequence();
const old_seq = sequences[0];
if (old_seq) {
new_seq.add(old_seq.last());
}
sequences = [new_seq];
}
}
pattern += sequencesToPattern(sequences, true);
return pattern;
};
/**
* A property getter resolving dot-notation
* @param {Object} obj The root object to fetch property on
* @param {String} name The optionally dotted property name to fetch
* @return {Object} The resolved property value
*/
const getAttr = (obj, name) => {
if (!obj) return;
return obj[name];
};
/**
* A property getter resolving dot-notation
* @param {Object} obj The root object to fetch property on
* @param {String} name The optionally dotted property name to fetch
* @return {Object} The resolved property value
*/
const getAttrNesting = (obj, name) => {
if (!obj) return;
var part,
names = name.split(".");
while ((part = names.shift()) && (obj = obj[part]));
return obj;
};
/**
* Calculates how close of a match the
* given value is against a search token.
*
*/
const scoreValue = (value, token, weight) => {
var score, pos;
if (!value) return 0;
value = value + '';
if (token.regex == null) return 0;
pos = value.search(token.regex);
if (pos === -1) return 0;
score = token.string.length / value.length;
if (pos === 0) score += 0.5;
return score * weight;
};
/**
* Cast object property to an array if it exists and has a value
*
*/
const propToArray = (obj, key) => {
var value = obj[key];
if (typeof value == 'function') return value;
if (value && !Array.isArray(value)) {
obj[key] = [value];
}
};
/**
* Iterates over arrays and hashes.
*
* ```
* iterate(this.items, function(item, id) {
* // invoked for each item
* });
* ```
*
*/
const iterate = (object, callback) => {
if (Array.isArray(object)) {
object.forEach(callback);
} else {
for (var key in object) {
if (object.hasOwnProperty(key)) {
callback(object[key], key);
}
}
}
};
const cmp = (a, b) => {
if (typeof a === 'number' && typeof b === 'number') {
return a > b ? 1 : a < b ? -1 : 0;
}
a = asciifold(a + '').toLowerCase();
b = asciifold(b + '').toLowerCase();
if (a > b) return 1;
if (b > a) return -1;
return 0;
};
/**
* sifter.js

@@ -787,354 +17,313 @@ * Copyright (c) 2013–2020 Brian Reavis & contributors

*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPattern = exports.cmp = exports.iterate = exports.propToArray = exports.getAttrNesting = exports.getAttr = exports.scoreValue = exports.Sifter = void 0;
const utils_ts_1 = require("./utils.js");
Object.defineProperty(exports, "scoreValue", { enumerable: true, get: function () { return utils_ts_1.scoreValue; } });
Object.defineProperty(exports, "getAttr", { enumerable: true, get: function () { return utils_ts_1.getAttr; } });
Object.defineProperty(exports, "getAttrNesting", { enumerable: true, get: function () { return utils_ts_1.getAttrNesting; } });
Object.defineProperty(exports, "propToArray", { enumerable: true, get: function () { return utils_ts_1.propToArray; } });
Object.defineProperty(exports, "iterate", { enumerable: true, get: function () { return utils_ts_1.iterate; } });
Object.defineProperty(exports, "cmp", { enumerable: true, get: function () { return utils_ts_1.cmp; } });
const unicode_variants_1 = require("@orchidjs/unicode-variants");
Object.defineProperty(exports, "getPattern", { enumerable: true, get: function () { return unicode_variants_1.getPattern; } });
class Sifter {
// []|{};
/**
* Textually searches arrays and hashes of objects
* by property (or multiple properties). Designed
* specifically for autocomplete.
*
*/
constructor(items, settings) {
this.items = void 0;
this.settings = void 0;
this.items = items;
this.settings = settings || {
diacritics: true
};
}
/**
* Splits a search string into an array of individual
* regexps to be used to match results.
*
*/
tokenize(query, respect_word_boundaries, weights) {
if (!query || !query.length) return [];
const tokens = [];
const words = query.split(/\s+/);
var field_regex;
if (weights) {
field_regex = new RegExp('^(' + Object.keys(weights).map(escape_regex).join('|') + ')\:(.*)$');
items; // []|{};
settings;
/**
* Textually searches arrays and hashes of objects
* by property (or multiple properties). Designed
* specifically for autocomplete.
*
*/
constructor(items, settings) {
this.items = items;
this.settings = settings || { diacritics: true };
}
words.forEach(word => {
let field_match;
let field = null;
let regex = null; // look for "field:query" tokens
if (field_regex && (field_match = word.match(field_regex))) {
field = field_match[1];
word = field_match[2];
}
if (word.length > 0) {
if (this.settings.diacritics) {
regex = getPattern(word) || null;
} else {
regex = escape_regex(word);
;
/**
* Splits a search string into an array of individual
* regexps to be used to match results.
*
*/
tokenize(query, respect_word_boundaries, weights) {
if (!query || !query.length)
return [];
const tokens = [];
const words = query.split(/\s+/);
var field_regex;
if (weights) {
field_regex = new RegExp('^(' + Object.keys(weights).map(unicode_variants_1.escape_regex).join('|') + ')\:(.*)$');
}
if (regex && respect_word_boundaries) regex = "\\b" + regex;
}
tokens.push({
string: word,
regex: regex ? new RegExp(regex, 'iu') : null,
field: field
});
});
return tokens;
}
/**
* Returns a function to be used to score individual results.
*
* Good matches will have a higher score than poor matches.
* If an item is not a match, 0 will be returned by the function.
*
* @returns {T.ScoreFn}
*/
getScoreFunction(query, options) {
var search = this.prepareSearch(query, options);
return this._getScoreFunction(search);
}
/**
* @returns {T.ScoreFn}
*
*/
_getScoreFunction(search) {
const tokens = search.tokens,
token_count = tokens.length;
if (!token_count) {
return function () {
return 0;
};
words.forEach((word) => {
let field_match;
let field = null;
let regex = null;
// look for "field:query" tokens
if (field_regex && (field_match = word.match(field_regex))) {
field = field_match[1];
word = field_match[2];
}
if (word.length > 0) {
if (this.settings.diacritics) {
regex = (0, unicode_variants_1.getPattern)(word) || null;
}
else {
regex = (0, unicode_variants_1.escape_regex)(word);
}
if (regex && respect_word_boundaries)
regex = "\\b" + regex;
}
tokens.push({
string: word,
regex: regex ? new RegExp(regex, 'iu') : null,
field: field,
});
});
return tokens;
}
const fields = search.options.fields,
weights = search.weights,
field_count = fields.length,
getAttrFn = search.getAttrFn;
if (!field_count) {
return function () {
return 1;
};
;
/**
* Returns a function to be used to score individual results.
*
* Good matches will have a higher score than poor matches.
* If an item is not a match, 0 will be returned by the function.
*
* @returns {T.ScoreFn}
*/
getScoreFunction(query, options) {
var search = this.prepareSearch(query, options);
return this._getScoreFunction(search);
}
/**
* Calculates the score of an object
* against the search query.
* @returns {T.ScoreFn}
*
*/
const scoreObject = function () {
if (field_count === 1) {
return function (token, data) {
const field = fields[0].field;
return scoreValue(getAttrFn(data, field), token, weights[field] || 1);
};
}
return function (token, data) {
var sum = 0; // is the token specific to a field?
if (token.field) {
const value = getAttrFn(data, token.field);
if (!token.regex && value) {
sum += 1 / field_count;
} else {
sum += scoreValue(value, token, 1);
}
} else {
iterate(weights, (weight, field) => {
sum += scoreValue(getAttrFn(data, field), token, weight);
});
_getScoreFunction(search) {
const tokens = search.tokens, token_count = tokens.length;
if (!token_count) {
return function () { return 0; };
}
return sum / field_count;
};
}();
if (token_count === 1) {
return function (data) {
return scoreObject(tokens[0], data);
};
}
if (search.options.conjunction === 'and') {
return function (data) {
var score,
sum = 0;
for (let token of tokens) {
score = scoreObject(token, data);
if (score <= 0) return 0;
sum += score;
const fields = search.options.fields, weights = search.weights, field_count = fields.length, getAttrFn = search.getAttrFn;
if (!field_count) {
return function () { return 1; };
}
return sum / token_count;
};
} else {
return function (data) {
var sum = 0;
iterate(tokens, token => {
sum += scoreObject(token, data);
});
return sum / token_count;
};
/**
* Calculates the score of an object
* against the search query.
*
*/
const scoreObject = (function () {
if (field_count === 1) {
return function (token, data) {
const field = fields[0].field;
return (0, utils_ts_1.scoreValue)(getAttrFn(data, field), token, weights[field] || 1);
};
}
return function (token, data) {
var sum = 0;
// is the token specific to a field?
if (token.field) {
const value = getAttrFn(data, token.field);
if (!token.regex && value) {
sum += (1 / field_count);
}
else {
sum += (0, utils_ts_1.scoreValue)(value, token, 1);
}
}
else {
(0, utils_ts_1.iterate)(weights, (weight, field) => {
sum += (0, utils_ts_1.scoreValue)(getAttrFn(data, field), token, weight);
});
}
return sum / field_count;
};
})();
if (token_count === 1) {
return function (data) {
return scoreObject(tokens[0], data);
};
}
if (search.options.conjunction === 'and') {
return function (data) {
var score, sum = 0;
for (let token of tokens) {
score = scoreObject(token, data);
if (score <= 0)
return 0;
sum += score;
}
return sum / token_count;
};
}
else {
return function (data) {
var sum = 0;
(0, utils_ts_1.iterate)(tokens, (token) => {
sum += scoreObject(token, data);
});
return sum / token_count;
};
}
}
}
/**
* Returns a function that can be used to compare two
* results, for sorting purposes. If no sorting should
* be performed, `null` will be returned.
*
* @return function(a,b)
*/
getSortFunction(query, options) {
var search = this.prepareSearch(query, options);
return this._getSortFunction(search);
}
_getSortFunction(search) {
var implicit_score,
sort_flds = [];
const self = this,
options = search.options,
sort = !search.query && options.sort_empty ? options.sort_empty : options.sort;
if (typeof sort == 'function') {
return sort.bind(this);
}
;
/**
* Fetches the specified sort field value
* from a search result item.
* Returns a function that can be used to compare two
* results, for sorting purposes. If no sorting should
* be performed, `null` will be returned.
*
* @return function(a,b)
*/
const get_field = function get_field(name, result) {
if (name === '$score') return result.score;
return search.getAttrFn(self.items[result.id], name);
}; // parse options
if (sort) {
for (let s of sort) {
if (search.query || s.field !== '$score') {
sort_flds.push(s);
getSortFunction(query, options) {
var search = this.prepareSearch(query, options);
return this._getSortFunction(search);
}
_getSortFunction(search) {
var implicit_score, sort_flds = [];
const self = this, options = search.options, sort = (!search.query && options.sort_empty) ? options.sort_empty : options.sort;
if (typeof sort == 'function') {
return sort.bind(this);
}
}
} // the "$score" field is implied to be the primary
// sort field, unless it's manually specified
if (search.query) {
implicit_score = true;
for (let fld of sort_flds) {
if (fld.field === '$score') {
implicit_score = false;
break;
/**
* Fetches the specified sort field value
* from a search result item.
*
*/
const get_field = function (name, result) {
if (name === '$score')
return result.score;
return search.getAttrFn(self.items[result.id], name);
};
// parse options
if (sort) {
for (let s of sort) {
if (search.query || s.field !== '$score') {
sort_flds.push(s);
}
}
}
}
if (implicit_score) {
sort_flds.unshift({
field: '$score',
direction: 'desc'
});
} // without a search.query, all items will have the same score
} else {
sort_flds = sort_flds.filter(fld => fld.field !== '$score');
} // build function
const sort_flds_count = sort_flds.length;
if (!sort_flds_count) {
return null;
// the "$score" field is implied to be the primary
// sort field, unless it's manually specified
if (search.query) {
implicit_score = true;
for (let fld of sort_flds) {
if (fld.field === '$score') {
implicit_score = false;
break;
}
}
if (implicit_score) {
sort_flds.unshift({ field: '$score', direction: 'desc' });
}
// without a search.query, all items will have the same score
}
else {
sort_flds = sort_flds.filter((fld) => fld.field !== '$score');
}
// build function
const sort_flds_count = sort_flds.length;
if (!sort_flds_count) {
return null;
}
return function (a, b) {
var result, field;
for (let sort_fld of sort_flds) {
field = sort_fld.field;
let multiplier = sort_fld.direction === 'desc' ? -1 : 1;
result = multiplier * (0, utils_ts_1.cmp)(get_field(field, a), get_field(field, b));
if (result)
return result;
}
return 0;
};
}
return function (a, b) {
var result, field;
for (let sort_fld of sort_flds) {
field = sort_fld.field;
let multiplier = sort_fld.direction === 'desc' ? -1 : 1;
result = multiplier * cmp(get_field(field, a), get_field(field, b));
if (result) return result;
}
return 0;
};
}
/**
* Parses a search query and returns an object
* with tokens and fields ready to be populated
* with results.
*
*/
prepareSearch(query, optsUser) {
const weights = {};
var options = Object.assign({}, optsUser);
propToArray(options, 'sort');
propToArray(options, 'sort_empty'); // convert fields to new format
if (options.fields) {
propToArray(options, 'fields');
const fields = [];
options.fields.forEach(field => {
if (typeof field == 'string') {
field = {
field: field,
weight: 1
};
;
/**
* Parses a search query and returns an object
* with tokens and fields ready to be populated
* with results.
*
*/
prepareSearch(query, optsUser) {
const weights = {};
var options = Object.assign({}, optsUser);
(0, utils_ts_1.propToArray)(options, 'sort');
(0, utils_ts_1.propToArray)(options, 'sort_empty');
// convert fields to new format
if (options.fields) {
(0, utils_ts_1.propToArray)(options, 'fields');
const fields = [];
options.fields.forEach((field) => {
if (typeof field == 'string') {
field = { field: field, weight: 1 };
}
fields.push(field);
weights[field.field] = ('weight' in field) ? field.weight : 1;
});
options.fields = fields;
}
fields.push(field);
weights[field.field] = 'weight' in field ? field.weight : 1;
});
options.fields = fields;
return {
options: options,
query: query.toLowerCase().trim(),
tokens: this.tokenize(query, options.respect_word_boundaries, weights),
total: 0,
items: [],
weights: weights,
getAttrFn: (options.nesting) ? utils_ts_1.getAttrNesting : utils_ts_1.getAttr,
};
}
return {
options: options,
query: query.toLowerCase().trim(),
tokens: this.tokenize(query, options.respect_word_boundaries, weights),
total: 0,
items: [],
weights: weights,
getAttrFn: options.nesting ? getAttrNesting : getAttr
};
}
/**
* Searches through all items and returns a sorted array of matches.
*
*/
search(query, options) {
var self = this,
score,
search;
search = this.prepareSearch(query, options);
options = search.options;
query = search.query; // generate result scoring function
const fn_score = options.score || self._getScoreFunction(search); // perform search and sort
if (query.length) {
iterate(self.items, (item, id) => {
score = fn_score(item);
if (options.filter === false || score > 0) {
search.items.push({
'score': score,
'id': id
});
;
/**
* Searches through all items and returns a sorted array of matches.
*
*/
search(query, options) {
var self = this, score, search;
search = this.prepareSearch(query, options);
options = search.options;
query = search.query;
// generate result scoring function
const fn_score = options.score || self._getScoreFunction(search);
// perform search and sort
if (query.length) {
(0, utils_ts_1.iterate)(self.items, (item, id) => {
score = fn_score(item);
if (options.filter === false || score > 0) {
search.items.push({ 'score': score, 'id': id });
}
});
}
});
} else {
iterate(self.items, (_, id) => {
search.items.push({
'score': 1,
'id': id
});
});
else {
(0, utils_ts_1.iterate)(self.items, (_, id) => {
search.items.push({ 'score': 1, 'id': id });
});
}
const fn_sort = self._getSortFunction(search);
if (fn_sort)
search.items.sort(fn_sort);
// apply limits
search.total = search.items.length;
if (typeof options.limit === 'number') {
search.items = search.items.slice(0, options.limit);
}
return search;
}
const fn_sort = self._getSortFunction(search);
if (fn_sort) search.items.sort(fn_sort); // apply limits
search.total = search.items.length;
if (typeof options.limit === 'number') {
search.items = search.items.slice(0, options.limit);
}
return search;
}
;
}
exports.Sifter = Sifter;
exports.cmp = cmp;
exports.getAttr = getAttr;
exports.getAttrNesting = getAttrNesting;
exports.getPattern = getPattern;
exports.iterate = iterate;
exports.propToArray = propToArray;
exports.scoreValue = scoreValue;
//# sourceMappingURL=sifter.js.map
__exportStar(require("./types.js"), exports);
//# sourceMappingURL=sifter.js.map

@@ -1,7 +0,1 @@

/*! sifter.js | https://github.com/orchidjs/sifter.js | Apache License (v2) */
import { iterate, cmp, propToArray, getAttrNesting, getAttr, scoreValue } from './utils.js';
export { cmp, getAttr, getAttrNesting, iterate, propToArray, scoreValue } from './utils.js';
import { escape_regex, getPattern } from '@orchidjs/unicode-variants';
export { getPattern } from '@orchidjs/unicode-variants';
/**

@@ -22,347 +16,290 @@ * sifter.js

*/
import { scoreValue, getAttr, getAttrNesting, propToArray, iterate, cmp } from "./utils.js";
import { getPattern, escape_regex } from '@orchidjs/unicode-variants';
class Sifter {
// []|{};
/**
* Textually searches arrays and hashes of objects
* by property (or multiple properties). Designed
* specifically for autocomplete.
*
*/
constructor(items, settings) {
this.items = void 0;
this.settings = void 0;
this.items = items;
this.settings = settings || {
diacritics: true
};
}
/**
* Splits a search string into an array of individual
* regexps to be used to match results.
*
*/
tokenize(query, respect_word_boundaries, weights) {
if (!query || !query.length) return [];
const tokens = [];
const words = query.split(/\s+/);
var field_regex;
if (weights) {
field_regex = new RegExp('^(' + Object.keys(weights).map(escape_regex).join('|') + ')\:(.*)$');
items; // []|{};
settings;
/**
* Textually searches arrays and hashes of objects
* by property (or multiple properties). Designed
* specifically for autocomplete.
*
*/
constructor(items, settings) {
this.items = items;
this.settings = settings || { diacritics: true };
}
words.forEach(word => {
let field_match;
let field = null;
let regex = null; // look for "field:query" tokens
if (field_regex && (field_match = word.match(field_regex))) {
field = field_match[1];
word = field_match[2];
}
if (word.length > 0) {
if (this.settings.diacritics) {
regex = getPattern(word) || null;
} else {
regex = escape_regex(word);
;
/**
* Splits a search string into an array of individual
* regexps to be used to match results.
*
*/
tokenize(query, respect_word_boundaries, weights) {
if (!query || !query.length)
return [];
const tokens = [];
const words = query.split(/\s+/);
var field_regex;
if (weights) {
field_regex = new RegExp('^(' + Object.keys(weights).map(escape_regex).join('|') + ')\:(.*)$');
}
if (regex && respect_word_boundaries) regex = "\\b" + regex;
}
tokens.push({
string: word,
regex: regex ? new RegExp(regex, 'iu') : null,
field: field
});
});
return tokens;
}
/**
* Returns a function to be used to score individual results.
*
* Good matches will have a higher score than poor matches.
* If an item is not a match, 0 will be returned by the function.
*
* @returns {T.ScoreFn}
*/
getScoreFunction(query, options) {
var search = this.prepareSearch(query, options);
return this._getScoreFunction(search);
}
/**
* @returns {T.ScoreFn}
*
*/
_getScoreFunction(search) {
const tokens = search.tokens,
token_count = tokens.length;
if (!token_count) {
return function () {
return 0;
};
words.forEach((word) => {
let field_match;
let field = null;
let regex = null;
// look for "field:query" tokens
if (field_regex && (field_match = word.match(field_regex))) {
field = field_match[1];
word = field_match[2];
}
if (word.length > 0) {
if (this.settings.diacritics) {
regex = getPattern(word) || null;
}
else {
regex = escape_regex(word);
}
if (regex && respect_word_boundaries)
regex = "\\b" + regex;
}
tokens.push({
string: word,
regex: regex ? new RegExp(regex, 'iu') : null,
field: field,
});
});
return tokens;
}
const fields = search.options.fields,
weights = search.weights,
field_count = fields.length,
getAttrFn = search.getAttrFn;
if (!field_count) {
return function () {
return 1;
};
;
/**
* Returns a function to be used to score individual results.
*
* Good matches will have a higher score than poor matches.
* If an item is not a match, 0 will be returned by the function.
*
* @returns {T.ScoreFn}
*/
getScoreFunction(query, options) {
var search = this.prepareSearch(query, options);
return this._getScoreFunction(search);
}
/**
* Calculates the score of an object
* against the search query.
* @returns {T.ScoreFn}
*
*/
const scoreObject = function () {
if (field_count === 1) {
return function (token, data) {
const field = fields[0].field;
return scoreValue(getAttrFn(data, field), token, weights[field] || 1);
};
}
return function (token, data) {
var sum = 0; // is the token specific to a field?
if (token.field) {
const value = getAttrFn(data, token.field);
if (!token.regex && value) {
sum += 1 / field_count;
} else {
sum += scoreValue(value, token, 1);
}
} else {
iterate(weights, (weight, field) => {
sum += scoreValue(getAttrFn(data, field), token, weight);
});
_getScoreFunction(search) {
const tokens = search.tokens, token_count = tokens.length;
if (!token_count) {
return function () { return 0; };
}
return sum / field_count;
};
}();
if (token_count === 1) {
return function (data) {
return scoreObject(tokens[0], data);
};
}
if (search.options.conjunction === 'and') {
return function (data) {
var score,
sum = 0;
for (let token of tokens) {
score = scoreObject(token, data);
if (score <= 0) return 0;
sum += score;
const fields = search.options.fields, weights = search.weights, field_count = fields.length, getAttrFn = search.getAttrFn;
if (!field_count) {
return function () { return 1; };
}
return sum / token_count;
};
} else {
return function (data) {
var sum = 0;
iterate(tokens, token => {
sum += scoreObject(token, data);
});
return sum / token_count;
};
/**
* Calculates the score of an object
* against the search query.
*
*/
const scoreObject = (function () {
if (field_count === 1) {
return function (token, data) {
const field = fields[0].field;
return scoreValue(getAttrFn(data, field), token, weights[field] || 1);
};
}
return function (token, data) {
var sum = 0;
// is the token specific to a field?
if (token.field) {
const value = getAttrFn(data, token.field);
if (!token.regex && value) {
sum += (1 / field_count);
}
else {
sum += scoreValue(value, token, 1);
}
}
else {
iterate(weights, (weight, field) => {
sum += scoreValue(getAttrFn(data, field), token, weight);
});
}
return sum / field_count;
};
})();
if (token_count === 1) {
return function (data) {
return scoreObject(tokens[0], data);
};
}
if (search.options.conjunction === 'and') {
return function (data) {
var score, sum = 0;
for (let token of tokens) {
score = scoreObject(token, data);
if (score <= 0)
return 0;
sum += score;
}
return sum / token_count;
};
}
else {
return function (data) {
var sum = 0;
iterate(tokens, (token) => {
sum += scoreObject(token, data);
});
return sum / token_count;
};
}
}
}
/**
* Returns a function that can be used to compare two
* results, for sorting purposes. If no sorting should
* be performed, `null` will be returned.
*
* @return function(a,b)
*/
getSortFunction(query, options) {
var search = this.prepareSearch(query, options);
return this._getSortFunction(search);
}
_getSortFunction(search) {
var implicit_score,
sort_flds = [];
const self = this,
options = search.options,
sort = !search.query && options.sort_empty ? options.sort_empty : options.sort;
if (typeof sort == 'function') {
return sort.bind(this);
}
;
/**
* Fetches the specified sort field value
* from a search result item.
* Returns a function that can be used to compare two
* results, for sorting purposes. If no sorting should
* be performed, `null` will be returned.
*
* @return function(a,b)
*/
const get_field = function get_field(name, result) {
if (name === '$score') return result.score;
return search.getAttrFn(self.items[result.id], name);
}; // parse options
if (sort) {
for (let s of sort) {
if (search.query || s.field !== '$score') {
sort_flds.push(s);
getSortFunction(query, options) {
var search = this.prepareSearch(query, options);
return this._getSortFunction(search);
}
_getSortFunction(search) {
var implicit_score, sort_flds = [];
const self = this, options = search.options, sort = (!search.query && options.sort_empty) ? options.sort_empty : options.sort;
if (typeof sort == 'function') {
return sort.bind(this);
}
}
} // the "$score" field is implied to be the primary
// sort field, unless it's manually specified
if (search.query) {
implicit_score = true;
for (let fld of sort_flds) {
if (fld.field === '$score') {
implicit_score = false;
break;
/**
* Fetches the specified sort field value
* from a search result item.
*
*/
const get_field = function (name, result) {
if (name === '$score')
return result.score;
return search.getAttrFn(self.items[result.id], name);
};
// parse options
if (sort) {
for (let s of sort) {
if (search.query || s.field !== '$score') {
sort_flds.push(s);
}
}
}
}
if (implicit_score) {
sort_flds.unshift({
field: '$score',
direction: 'desc'
});
} // without a search.query, all items will have the same score
} else {
sort_flds = sort_flds.filter(fld => fld.field !== '$score');
} // build function
const sort_flds_count = sort_flds.length;
if (!sort_flds_count) {
return null;
// the "$score" field is implied to be the primary
// sort field, unless it's manually specified
if (search.query) {
implicit_score = true;
for (let fld of sort_flds) {
if (fld.field === '$score') {
implicit_score = false;
break;
}
}
if (implicit_score) {
sort_flds.unshift({ field: '$score', direction: 'desc' });
}
// without a search.query, all items will have the same score
}
else {
sort_flds = sort_flds.filter((fld) => fld.field !== '$score');
}
// build function
const sort_flds_count = sort_flds.length;
if (!sort_flds_count) {
return null;
}
return function (a, b) {
var result, field;
for (let sort_fld of sort_flds) {
field = sort_fld.field;
let multiplier = sort_fld.direction === 'desc' ? -1 : 1;
result = multiplier * cmp(get_field(field, a), get_field(field, b));
if (result)
return result;
}
return 0;
};
}
return function (a, b) {
var result, field;
for (let sort_fld of sort_flds) {
field = sort_fld.field;
let multiplier = sort_fld.direction === 'desc' ? -1 : 1;
result = multiplier * cmp(get_field(field, a), get_field(field, b));
if (result) return result;
}
return 0;
};
}
/**
* Parses a search query and returns an object
* with tokens and fields ready to be populated
* with results.
*
*/
prepareSearch(query, optsUser) {
const weights = {};
var options = Object.assign({}, optsUser);
propToArray(options, 'sort');
propToArray(options, 'sort_empty'); // convert fields to new format
if (options.fields) {
propToArray(options, 'fields');
const fields = [];
options.fields.forEach(field => {
if (typeof field == 'string') {
field = {
field: field,
weight: 1
};
;
/**
* Parses a search query and returns an object
* with tokens and fields ready to be populated
* with results.
*
*/
prepareSearch(query, optsUser) {
const weights = {};
var options = Object.assign({}, optsUser);
propToArray(options, 'sort');
propToArray(options, 'sort_empty');
// convert fields to new format
if (options.fields) {
propToArray(options, 'fields');
const fields = [];
options.fields.forEach((field) => {
if (typeof field == 'string') {
field = { field: field, weight: 1 };
}
fields.push(field);
weights[field.field] = ('weight' in field) ? field.weight : 1;
});
options.fields = fields;
}
fields.push(field);
weights[field.field] = 'weight' in field ? field.weight : 1;
});
options.fields = fields;
return {
options: options,
query: query.toLowerCase().trim(),
tokens: this.tokenize(query, options.respect_word_boundaries, weights),
total: 0,
items: [],
weights: weights,
getAttrFn: (options.nesting) ? getAttrNesting : getAttr,
};
}
return {
options: options,
query: query.toLowerCase().trim(),
tokens: this.tokenize(query, options.respect_word_boundaries, weights),
total: 0,
items: [],
weights: weights,
getAttrFn: options.nesting ? getAttrNesting : getAttr
};
}
/**
* Searches through all items and returns a sorted array of matches.
*
*/
search(query, options) {
var self = this,
score,
search;
search = this.prepareSearch(query, options);
options = search.options;
query = search.query; // generate result scoring function
const fn_score = options.score || self._getScoreFunction(search); // perform search and sort
if (query.length) {
iterate(self.items, (item, id) => {
score = fn_score(item);
if (options.filter === false || score > 0) {
search.items.push({
'score': score,
'id': id
});
;
/**
* Searches through all items and returns a sorted array of matches.
*
*/
search(query, options) {
var self = this, score, search;
search = this.prepareSearch(query, options);
options = search.options;
query = search.query;
// generate result scoring function
const fn_score = options.score || self._getScoreFunction(search);
// perform search and sort
if (query.length) {
iterate(self.items, (item, id) => {
score = fn_score(item);
if (options.filter === false || score > 0) {
search.items.push({ 'score': score, 'id': id });
}
});
}
});
} else {
iterate(self.items, (_, id) => {
search.items.push({
'score': 1,
'id': id
});
});
else {
iterate(self.items, (_, id) => {
search.items.push({ 'score': 1, 'id': id });
});
}
const fn_sort = self._getSortFunction(search);
if (fn_sort)
search.items.sort(fn_sort);
// apply limits
search.total = search.items.length;
if (typeof options.limit === 'number') {
search.items = search.items.slice(0, options.limit);
}
return search;
}
const fn_sort = self._getSortFunction(search);
if (fn_sort) search.items.sort(fn_sort); // apply limits
search.total = search.items.length;
if (typeof options.limit === 'number') {
search.items = search.items.slice(0, options.limit);
}
return search;
}
;
}
export { Sifter };
//# sourceMappingURL=sifter.js.map
export { Sifter, scoreValue, getAttr, getAttrNesting, propToArray, iterate, cmp, getPattern };
export * from "./types.js";
//# sourceMappingURL=sifter.js.map

@@ -1,4 +0,2 @@

/*! sifter.js | https://github.com/orchidjs/sifter.js | Apache License (v2) */
import { asciifold } from '@orchidjs/unicode-variants';
/**

@@ -10,5 +8,6 @@ * A property getter resolving dot-notation

*/
const getAttr = (obj, name) => {
if (!obj) return;
return obj[name];
export const getAttr = (obj, name) => {
if (!obj)
return;
return obj[name];
};

@@ -21,11 +20,9 @@ /**

*/
const getAttrNesting = (obj, name) => {
if (!obj) return;
var part,
names = name.split(".");
while ((part = names.shift()) && (obj = obj[part]));
return obj;
export const getAttrNesting = (obj, name) => {
if (!obj)
return;
var part, names = name.split(".");
while ((part = names.shift()) && (obj = obj[part]))
;
return obj;
};

@@ -37,13 +34,16 @@ /**

*/
const scoreValue = (value, token, weight) => {
var score, pos;
if (!value) return 0;
value = value + '';
if (token.regex == null) return 0;
pos = value.search(token.regex);
if (pos === -1) return 0;
score = token.string.length / value.length;
if (pos === 0) score += 0.5;
return score * weight;
export const scoreValue = (value, token, weight) => {
var score, pos;
if (!value)
return 0;
value = value + '';
if (token.regex == null)
return 0;
pos = value.search(token.regex);
if (pos === -1)
return 0;
score = token.string.length / value.length;
if (pos === 0)
score += 0.5;
return score * weight;
};

@@ -54,10 +54,9 @@ /**

*/
const propToArray = (obj, key) => {
var value = obj[key];
if (typeof value == 'function') return value;
if (value && !Array.isArray(value)) {
obj[key] = [value];
}
export const propToArray = (obj, key) => {
var value = obj[key];
if (typeof value == 'function')
return value;
if (value && !Array.isArray(value)) {
obj[key] = [value];
}
};

@@ -74,27 +73,26 @@ /**

*/
const iterate = (object, callback) => {
if (Array.isArray(object)) {
object.forEach(callback);
} else {
for (var key in object) {
if (object.hasOwnProperty(key)) {
callback(object[key], key);
}
export const iterate = (object, callback) => {
if (Array.isArray(object)) {
object.forEach(callback);
}
}
else {
for (var key in object) {
if (object.hasOwnProperty(key)) {
callback(object[key], key);
}
}
}
};
const cmp = (a, b) => {
if (typeof a === 'number' && typeof b === 'number') {
return a > b ? 1 : a < b ? -1 : 0;
}
a = asciifold(a + '').toLowerCase();
b = asciifold(b + '').toLowerCase();
if (a > b) return 1;
if (b > a) return -1;
return 0;
export const cmp = (a, b) => {
if (typeof a === 'number' && typeof b === 'number') {
return a > b ? 1 : (a < b ? -1 : 0);
}
a = asciifold(a + '').toLowerCase();
b = asciifold(b + '').toLowerCase();
if (a > b)
return 1;
if (b > a)
return -1;
return 0;
};
export { cmp, getAttr, getAttrNesting, iterate, propToArray, scoreValue };
//# sourceMappingURL=utils.js.map
//# sourceMappingURL=utils.js.map

@@ -16,5 +16,5 @@ /**

*/
import { scoreValue, getAttr, getAttrNesting, propToArray, iterate, cmp } from './utils';
import { scoreValue, getAttr, getAttrNesting, propToArray, iterate, cmp } from './utils.ts';
import { getPattern } from '@orchidjs/unicode-variants';
import * as T from './types';
import * as T from './types.ts';
declare class Sifter {

@@ -73,1 +73,2 @@ items: any;

export { Sifter, scoreValue, getAttr, getAttrNesting, propToArray, iterate, cmp, getPattern };
export * from './types.ts';

@@ -1,12 +0,12 @@

import { Sifter } from './sifter';
export declare type Field = {
import { Sifter } from './sifter.ts';
export type Field = {
field: string;
weight: number;
};
export declare type Sort = {
export type Sort = {
field: string;
direction?: string;
};
export declare type SortFn = (this: Sifter, a: ResultItem, b: ResultItem) => number;
export declare type UserOptions = {
export type SortFn = (this: Sifter, a: ResultItem, b: ResultItem) => number;
export type UserOptions = {
fields: string[] | Field[];

@@ -22,3 +22,3 @@ conjunction: string;

};
export declare type Options = {
export type Options = {
fields: Field[];

@@ -34,3 +34,3 @@ conjunction: string;

};
export declare type Token = {
export type Token = {
string: string;

@@ -40,6 +40,6 @@ regex: RegExp | null;

};
export declare type Weights = {
export type Weights = {
[key: string]: number;
};
export declare type PrepareObj = {
export type PrepareObj = {
options: Options;

@@ -53,9 +53,9 @@ query: string;

};
export declare type Settings = {
export type Settings = {
diacritics: boolean;
};
export declare type ResultItem = {
export type ResultItem = {
score: number;
id: number | string;
};
export declare type ScoreFn = (item: ResultItem) => number;
export type ScoreFn = (item: ResultItem) => number;

@@ -1,2 +0,2 @@

import * as T from './types';
import * as T from './types.ts';
/**

@@ -48,2 +48,2 @@ * A property getter resolving dot-notation

}, callback: (value: any, key: any) => any) => void;
export declare const cmp: (a: number | string, b: number | string) => 1 | -1 | 0;
export declare const cmp: (a: number | string, b: number | string) => 0 | 1 | -1;
/*! sifter.js | https://github.com/orchidjs/sifter.js | Apache License (v2) */
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.sifter = {}));
}(this, (function (exports) { 'use strict';
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.sifter = {}));
})(this, (function (exports) { 'use strict';
/*! @orchidjs/unicode-variants | https://github.com/orchidjs/unicode-variants | Apache License (v2) */
/**
* Convert array of strings to a regular expression
* ex ['ab','a'] => (?:ab|a)
* ex ['a','b'] => [ab]
* @param {string[]} chars
* @return {string}
*/
const arrayToPattern = chars => {
chars = chars.filter(Boolean);
if (chars.length < 2) {
return chars[0] || '';
}
return maxValueLength(chars) == 1 ? '[' + chars.join('') + ']' : '(?:' + chars.join('|') + ')';
};
/**
* @param {string[]} array
* @return {string}
*/
const sequencePattern = array => {
if (!hasDuplicates(array)) {
return array.join('');
}
let pattern = '';
let prev_char_count = 0;
const prev_pattern = () => {
if (prev_char_count > 1) {
pattern += '{' + prev_char_count + '}';
/**
* Convert array of strings to a regular expression
* ex ['ab','a'] => (?:ab|a)
* ex ['a','b'] => [ab]
*/
const arrayToPattern = chars => {
chars = chars.filter(Boolean);
if (chars.length < 2) {
return chars[0] || '';
}
return maxValueLength(chars) == 1 ? '[' + chars.join('') + ']' : '(?:' + chars.join('|') + ')';
};
array.forEach((char, i) => {
if (char === array[i - 1]) {
prev_char_count++;
return;
const sequencePattern = array => {
if (!hasDuplicates(array)) {
return array.join('');
}
let pattern = '';
let prev_char_count = 0;
const prev_pattern = () => {
if (prev_char_count > 1) {
pattern += '{' + prev_char_count + '}';
}
};
array.forEach((char, i) => {
if (char === array[i - 1]) {
prev_char_count++;
return;
}
prev_pattern();
pattern += char;
prev_char_count = 1;
});
prev_pattern();
pattern += char;
prev_char_count = 1;
});
prev_pattern();
return pattern;
};
/**
* Convert array of strings to a regular expression
* ex ['ab','a'] => (?:ab|a)
* ex ['a','b'] => [ab]
* @param {Set<string>} chars
* @return {string}
*/
return pattern;
};
/**
* Convert array of strings to a regular expression
* ex ['ab','a'] => (?:ab|a)
* ex ['a','b'] => [ab]
*/
const setToPattern = chars => {
let array = Array.from(chars);
return arrayToPattern(array);
};
/**
* https://stackoverflow.com/questions/7376598/in-javascript-how-do-i-check-if-an-array-has-duplicate-values
*/
const hasDuplicates = array => {
return new Set(array).size !== array.length;
};
/**
* https://stackoverflow.com/questions/63006601/why-does-u-throw-an-invalid-escape-error
*/
const escape_regex = str => {
return (str + '').replace(/([\$\(-\+\.\?\[-\^\{-\}])/g, '\\$1');
};
/**
* Return the max length of array values
*/
const maxValueLength = array => {
return array.reduce((longest, value) => Math.max(longest, unicodeLength(value)), 0);
};
const unicodeLength = str => {
return Array.from(str).length;
};
const setToPattern = chars => {
let array = toArray(chars);
return arrayToPattern(array);
};
/**
*
* https://stackoverflow.com/questions/7376598/in-javascript-how-do-i-check-if-an-array-has-duplicate-values
* @param {any[]} array
*/
const hasDuplicates = array => {
return new Set(array).size !== array.length;
};
/**
* https://stackoverflow.com/questions/63006601/why-does-u-throw-an-invalid-escape-error
* @param {string} str
* @return {string}
*/
const escape_regex = str => {
return (str + '').replace(/([\$\(-\+\.\?\[-\^\{-\}])/g, '\\$1');
};
/**
* Return the max length of array values
* @param {string[]} array
*
*/
const maxValueLength = array => {
return array.reduce((longest, value) => Math.max(longest, unicodeLength(value)), 0);
};
/**
* @param {string} str
*/
const unicodeLength = str => {
return toArray(str).length;
};
/**
* @param {any} p
* @return {any[]}
*/
const toArray = p => Array.from(p);
/*! @orchidjs/unicode-variants | https://github.com/orchidjs/unicode-variants | Apache License (v2) */
/**
* Get all possible combinations of substrings that add up to the given string
* https://stackoverflow.com/questions/30169587/find-all-the-combination-of-substrings-that-add-up-to-the-given-string
* @param {string} input
* @return {string[][]}
*/
const allSubstrings = input => {
if (input.length === 1) return [[input]];
/** @type {string[][]} */
let result = [];
const start = input.substring(1);
const suba = allSubstrings(start);
suba.forEach(function (subresult) {
let tmp = subresult.slice(0);
tmp[0] = input.charAt(0) + tmp[0];
result.push(tmp);
tmp = subresult.slice(0);
tmp.unshift(input.charAt(0));
result.push(tmp);
});
return result;
};
/*! @orchidjs/unicode-variants | https://github.com/orchidjs/unicode-variants | Apache License (v2) */
/**
* @typedef {{[key:string]:string}} TUnicodeMap
* @typedef {{[key:string]:Set<string>}} TUnicodeSets
* @typedef {[[number,number]]} TCodePoints
* @typedef {{folded:string,composed:string,code_point:number}} TCodePointObj
* @typedef {{start:number,end:number,length:number,substr:string}} TSequencePart
*/
/** @type {TCodePoints} */
const code_points = [[0, 65535]];
const accent_pat = '[\u0300-\u036F\u{b7}\u{2be}\u{2bc}]';
/** @type {TUnicodeMap} */
let unicode_map;
/** @type {RegExp} */
let multi_char_reg;
const max_char_length = 3;
/** @type {TUnicodeMap} */
const latin_convert = {};
/** @type {TUnicodeMap} */
const latin_condensed = {
'/': '⁄∕',
'0': '߀',
"a": "ⱥɐɑ",
"aa": "ꜳ",
"ae": "æǽǣ",
"ao": "ꜵ",
"au": "ꜷ",
"av": "ꜹꜻ",
"ay": "ꜽ",
"b": "ƀɓƃ",
"c": "ꜿƈȼↄ",
"d": "đɗɖᴅƌꮷԁɦ",
"e": "ɛǝᴇɇ",
"f": "ꝼƒ",
"g": "ǥɠꞡᵹꝿɢ",
"h": "ħⱨⱶɥ",
"i": "ɨı",
"j": "ɉȷ",
"k": "ƙⱪꝁꝃꝅꞣ",
"l": "łƚɫⱡꝉꝇꞁɭ",
"m": "ɱɯϻ",
"n": "ꞥƞɲꞑᴎлԉ",
"o": "øǿɔɵꝋꝍᴑ",
"oe": "œ",
"oi": "ƣ",
"oo": "ꝏ",
"ou": "ȣ",
"p": "ƥᵽꝑꝓꝕρ",
"q": "ꝗꝙɋ",
"r": "ɍɽꝛꞧꞃ",
"s": "ßȿꞩꞅʂ",
"t": "ŧƭʈⱦꞇ",
"th": "þ",
"tz": "ꜩ",
"u": "ʉ",
"v": "ʋꝟʌ",
"vy": "ꝡ",
"w": "ⱳ",
"y": "ƴɏỿ",
"z": "ƶȥɀⱬꝣ",
"hv": "ƕ"
};
for (let latin in latin_condensed) {
let unicode = latin_condensed[latin] || '';
for (let i = 0; i < unicode.length; i++) {
let char = unicode.substring(i, i + 1);
latin_convert[char] = latin;
}
}
const convert_pat = new RegExp(Object.keys(latin_convert).join('|') + '|' + accent_pat, 'gu');
/**
* Initialize the unicode_map from the give code point ranges
*
* @param {TCodePoints=} _code_points
*/
const initialize = _code_points => {
if (unicode_map !== undefined) return;
unicode_map = generateMap(_code_points || code_points);
};
/**
* Helper method for normalize a string
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize
* @param {string} str
* @param {string} form
*/
const normalize = (str, form = 'NFKD') => str.normalize(form);
/**
* Remove accents without reordering string
* calling str.normalize('NFKD') on \u{594}\u{595}\u{596} becomes \u{596}\u{594}\u{595}
* via https://github.com/krisk/Fuse/issues/133#issuecomment-318692703
* @param {string} str
* @return {string}
*/
const asciifold = str => {
return toArray(str).reduce(
/**
* @param {string} result
* @param {string} char
* Get all possible combinations of substrings that add up to the given string
* https://stackoverflow.com/questions/30169587/find-all-the-combination-of-substrings-that-add-up-to-the-given-string
*/
(result, char) => {
return result + _asciifold(char);
}, '');
};
/**
* @param {string} str
* @return {string}
*/
const allSubstrings = input => {
if (input.length === 1) return [[input]];
let result = [];
const start = input.substring(1);
const suba = allSubstrings(start);
suba.forEach(function (subresult) {
let tmp = subresult.slice(0);
tmp[0] = input.charAt(0) + tmp[0];
result.push(tmp);
tmp = subresult.slice(0);
tmp.unshift(input.charAt(0));
result.push(tmp);
});
return result;
};
const _asciifold = str => {
str = normalize(str).toLowerCase().replace(convert_pat,
/** @type {string} */
char => {
return latin_convert[char] || '';
}); //return str;
return normalize(str, 'NFC');
};
/**
* Generate a list of unicode variants from the list of code points
* @param {TCodePoints} code_points
* @yield {TCodePointObj}
*/
function* generator(code_points) {
for (const [code_point_min, code_point_max] of code_points) {
for (let i = code_point_min; i <= code_point_max; i++) {
let composed = String.fromCharCode(i);
let folded = asciifold(composed);
if (folded == composed.toLowerCase()) {
continue;
} // skip when folded is a string longer than 3 characters long
// bc the resulting regex patterns will be long
// eg:
// folded صلى الله عليه وسلم length 18 code point 65018
// folded جل جلاله length 8 code point 65019
if (folded.length > max_char_length) {
continue;
}
if (folded.length == 0) {
continue;
}
yield {
folded: folded,
composed: composed,
code_point: i
};
const code_points = [[0, 65535]];
const accent_pat = '[\u0300-\u036F\u{b7}\u{2be}\u{2bc}]';
let unicode_map;
let multi_char_reg;
const max_char_length = 3;
const latin_convert = {};
const latin_condensed = {
'/': '⁄∕',
'0': '߀',
"a": "ⱥɐɑ",
"aa": "ꜳ",
"ae": "æǽǣ",
"ao": "ꜵ",
"au": "ꜷ",
"av": "ꜹꜻ",
"ay": "ꜽ",
"b": "ƀɓƃ",
"c": "ꜿƈȼↄ",
"d": "đɗɖᴅƌꮷԁɦ",
"e": "ɛǝᴇɇ",
"f": "ꝼƒ",
"g": "ǥɠꞡᵹꝿɢ",
"h": "ħⱨⱶɥ",
"i": "ɨı",
"j": "ɉȷ",
"k": "ƙⱪꝁꝃꝅꞣ",
"l": "łƚɫⱡꝉꝇꞁɭ",
"m": "ɱɯϻ",
"n": "ꞥƞɲꞑᴎлԉ",
"o": "øǿɔɵꝋꝍᴑ",
"oe": "œ",
"oi": "ƣ",
"oo": "ꝏ",
"ou": "ȣ",
"p": "ƥᵽꝑꝓꝕρ",
"q": "ꝗꝙɋ",
"r": "ɍɽꝛꞧꞃ",
"s": "ßȿꞩꞅʂ",
"t": "ŧƭʈⱦꞇ",
"th": "þ",
"tz": "ꜩ",
"u": "ʉ",
"v": "ʋꝟʌ",
"vy": "ꝡ",
"w": "ⱳ",
"y": "ƴɏỿ",
"z": "ƶȥɀⱬꝣ",
"hv": "ƕ"
};
for (let latin in latin_condensed) {
let unicode = latin_condensed[latin] || '';
for (let i = 0; i < unicode.length; i++) {
let char = unicode.substring(i, i + 1);
latin_convert[char] = latin;
}
}
}
/**
* Generate a unicode map from the list of code points
* @param {TCodePoints} code_points
* @return {TUnicodeSets}
*/
const generateSets = code_points => {
/** @type {{[key:string]:Set<string>}} */
const unicode_sets = {};
const convert_pat = new RegExp(Object.keys(latin_convert).join('|') + '|' + accent_pat, 'gu');
/**
* @param {string} folded
* @param {string} to_add
* Initialize the unicode_map from the give code point ranges
*/
const addMatching = (folded, to_add) => {
/** @type {Set<string>} */
const folded_set = unicode_sets[folded] || new Set();
const patt = new RegExp('^' + setToPattern(folded_set) + '$', 'iu');
if (to_add.match(patt)) {
return;
}
folded_set.add(escape_regex(to_add));
unicode_sets[folded] = folded_set;
const initialize = _code_points => {
if (unicode_map !== undefined) return;
unicode_map = generateMap(code_points);
};
for (let value of generator(code_points)) {
addMatching(value.folded, value.folded);
addMatching(value.folded, value.composed);
}
return unicode_sets;
};
/**
* Generate a unicode map from the list of code points
* ae => (?:(?:ae|Æ|Ǽ|Ǣ)|(?:A|Ⓐ|A...)(?:E|ɛ|Ⓔ...))
*
* @param {TCodePoints} code_points
* @return {TUnicodeMap}
*/
const generateMap = code_points => {
/** @type {TUnicodeSets} */
const unicode_sets = generateSets(code_points);
/** @type {TUnicodeMap} */
const unicode_map = {};
/** @type {string[]} */
let multi_char = [];
for (let folded in unicode_sets) {
let set = unicode_sets[folded];
if (set) {
unicode_map[folded] = setToPattern(set);
}
if (folded.length > 1) {
multi_char.push(escape_regex(folded));
}
}
multi_char.sort((a, b) => b.length - a.length);
const multi_char_patt = arrayToPattern(multi_char);
multi_char_reg = new RegExp('^' + multi_char_patt, 'u');
return unicode_map;
};
/**
* Map each element of an array from it's folded value to all possible unicode matches
* @param {string[]} strings
* @param {number} min_replacement
* @return {string}
*/
const mapSequence = (strings, min_replacement = 1) => {
let chars_replaced = 0;
strings = strings.map(str => {
if (unicode_map[str]) {
chars_replaced += str.length;
}
return unicode_map[str] || str;
});
if (chars_replaced >= min_replacement) {
return sequencePattern(strings);
}
return '';
};
/**
* Convert a short string and split it into all possible patterns
* Keep a pattern only if min_replacement is met
*
* 'abc'
* => [['abc'],['ab','c'],['a','bc'],['a','b','c']]
* => ['abc-pattern','ab-c-pattern'...]
*
*
* @param {string} str
* @param {number} min_replacement
* @return {string}
*/
const substringsToPattern = (str, min_replacement = 1) => {
min_replacement = Math.max(min_replacement, str.length - 1);
return arrayToPattern(allSubstrings(str).map(sub_pat => {
return mapSequence(sub_pat, min_replacement);
}));
};
/**
* Convert an array of sequences into a pattern
* [{start:0,end:3,length:3,substr:'iii'}...] => (?:iii...)
*
* @param {Sequence[]} sequences
* @param {boolean} all
*/
const sequencesToPattern = (sequences, all = true) => {
let min_replacement = sequences.length > 1 ? 1 : 0;
return arrayToPattern(sequences.map(sequence => {
let seq = [];
const len = all ? sequence.length() : sequence.length() - 1;
for (let j = 0; j < len; j++) {
seq.push(substringsToPattern(sequence.substrs[j] || '', min_replacement));
}
return sequencePattern(seq);
}));
};
/**
* Return true if the sequence is already in the sequences
* @param {Sequence} needle_seq
* @param {Sequence[]} sequences
*/
const inSequences = (needle_seq, sequences) => {
for (const seq of sequences) {
if (seq.start != needle_seq.start || seq.end != needle_seq.end) {
continue;
}
if (seq.substrs.join('') !== needle_seq.substrs.join('')) {
continue;
}
let needle_parts = needle_seq.parts;
/**
* Helper method for normalize a string
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize
*/
const normalize = (str, form = 'NFKD') => str.normalize(form);
/**
* Remove accents without reordering string
* calling str.normalize('NFKD') on \u{594}\u{595}\u{596} becomes \u{596}\u{594}\u{595}
* via https://github.com/krisk/Fuse/issues/133#issuecomment-318692703
*/
const asciifold = str => {
return Array.from(str).reduce(
/**
* @param {TSequencePart} part
* @param {string} result
* @param {string} char
*/
const filter = part => {
for (const needle_part of needle_parts) {
if (needle_part.start === part.start && needle_part.substr === part.substr) {
return false;
(result, char) => {
return result + _asciifold(char);
}, '');
};
const _asciifold = str => {
str = normalize(str).toLowerCase().replace(convert_pat, (/** @type {string} */char) => {
return latin_convert[char] || '';
});
//return str;
return normalize(str, 'NFC');
};
/**
* Generate a list of unicode variants from the list of code points
*/
function* generator(code_points) {
for (const [code_point_min, code_point_max] of code_points) {
for (let i = code_point_min; i <= code_point_max; i++) {
let composed = String.fromCharCode(i);
let folded = asciifold(composed);
if (folded == composed.toLowerCase()) {
continue;
}
if (part.length == 1 || needle_part.length == 1) {
// skip when folded is a string longer than 3 characters long
// bc the resulting regex patterns will be long
// eg:
// folded صلى الله عليه وسلم length 18 code point 65018
// folded جل جلاله length 8 code point 65019
if (folded.length > max_char_length) {
continue;
} // check for overlapping parts
// a = ['::=','==']
// b = ['::','===']
// a = ['r','sm']
// b = ['rs','m']
if (part.start < needle_part.start && part.end > needle_part.start) {
return true;
}
if (needle_part.start < part.start && needle_part.end > part.start) {
return true;
if (folded.length == 0) {
continue;
}
yield {
folded: folded,
composed: composed,
code_point: i
};
}
return false;
};
let filtered = seq.parts.filter(filter);
if (filtered.length > 0) {
continue;
}
return true;
}
return false;
};
class Sequence {
constructor() {
/** @type {TSequencePart[]} */
this.parts = [];
/** @type {string[]} */
this.substrs = [];
this.start = 0;
this.end = 0;
}
/**
* @param {TSequencePart|undefined} part
* Generate a unicode map from the list of code points
*/
add(part) {
if (part) {
this.parts.push(part);
this.substrs.push(part.substr);
this.start = Math.min(part.start, this.start);
this.end = Math.max(part.end, this.end);
const generateSets = code_points => {
const unicode_sets = {};
const addMatching = (folded, to_add) => {
/** @type {Set<string>} */
const folded_set = unicode_sets[folded] || new Set();
const patt = new RegExp('^' + setToPattern(folded_set) + '$', 'iu');
if (to_add.match(patt)) {
return;
}
folded_set.add(escape_regex(to_add));
unicode_sets[folded] = folded_set;
};
for (let value of generator(code_points)) {
addMatching(value.folded, value.folded);
addMatching(value.folded, value.composed);
}
}
last() {
return this.parts[this.parts.length - 1];
}
length() {
return this.parts.length;
}
return unicode_sets;
};
/**
* @param {number} position
* @param {TSequencePart} last_piece
* Generate a unicode map from the list of code points
* ae => (?:(?:ae|Æ|Ǽ|Ǣ)|(?:A|Ⓐ|A...)(?:E|ɛ|Ⓔ...))
*/
clone(position, last_piece) {
let clone = new Sequence();
let parts = JSON.parse(JSON.stringify(this.parts));
let last_part = parts.pop();
for (const part of parts) {
clone.add(part);
const generateMap = code_points => {
const unicode_sets = generateSets(code_points);
const unicode_map = {};
let multi_char = [];
for (let folded in unicode_sets) {
let set = unicode_sets[folded];
if (set) {
unicode_map[folded] = setToPattern(set);
}
if (folded.length > 1) {
multi_char.push(escape_regex(folded));
}
}
let last_substr = last_piece.substr.substring(0, position - last_part.start);
let clone_last_len = last_substr.length;
clone.add({
start: last_part.start,
end: last_part.start + clone_last_len,
length: clone_last_len,
substr: last_substr
multi_char.sort((a, b) => b.length - a.length);
const multi_char_patt = arrayToPattern(multi_char);
multi_char_reg = new RegExp('^' + multi_char_patt, 'u');
return unicode_map;
};
/**
* Map each element of an array from its folded value to all possible unicode matches
*/
const mapSequence = (strings, min_replacement = 1) => {
let chars_replaced = 0;
strings = strings.map(str => {
if (unicode_map[str]) {
chars_replaced += str.length;
}
return unicode_map[str] || str;
});
return clone;
if (chars_replaced >= min_replacement) {
return sequencePattern(strings);
}
return '';
};
/**
* Convert a short string and split it into all possible patterns
* Keep a pattern only if min_replacement is met
*
* 'abc'
* => [['abc'],['ab','c'],['a','bc'],['a','b','c']]
* => ['abc-pattern','ab-c-pattern'...]
*/
const substringsToPattern = (str, min_replacement = 1) => {
min_replacement = Math.max(min_replacement, str.length - 1);
return arrayToPattern(allSubstrings(str).map(sub_pat => {
return mapSequence(sub_pat, min_replacement);
}));
};
/**
* Convert an array of sequences into a pattern
* [{start:0,end:3,length:3,substr:'iii'}...] => (?:iii...)
*/
const sequencesToPattern = (sequences, all = true) => {
let min_replacement = sequences.length > 1 ? 1 : 0;
return arrayToPattern(sequences.map(sequence => {
let seq = [];
const len = all ? sequence.length() : sequence.length() - 1;
for (let j = 0; j < len; j++) {
seq.push(substringsToPattern(sequence.substrs[j] || '', min_replacement));
}
return sequencePattern(seq);
}));
};
/**
* Return true if the sequence is already in the sequences
*/
const inSequences = (needle_seq, sequences) => {
for (const seq of sequences) {
if (seq.start != needle_seq.start || seq.end != needle_seq.end) {
continue;
}
if (seq.substrs.join('') !== needle_seq.substrs.join('')) {
continue;
}
let needle_parts = needle_seq.parts;
const filter = part => {
for (const needle_part of needle_parts) {
if (needle_part.start === part.start && needle_part.substr === part.substr) {
return false;
}
if (part.length == 1 || needle_part.length == 1) {
continue;
}
// check for overlapping parts
// a = ['::=','==']
// b = ['::','===']
// a = ['r','sm']
// b = ['rs','m']
if (part.start < needle_part.start && part.end > needle_part.start) {
return true;
}
if (needle_part.start < part.start && needle_part.end > part.start) {
return true;
}
}
return false;
};
let filtered = seq.parts.filter(filter);
if (filtered.length > 0) {
continue;
}
return true;
}
return false;
};
class Sequence {
constructor() {
this.parts = void 0;
this.substrs = void 0;
this.start = void 0;
this.end = void 0;
this.parts = [];
this.substrs = [];
this.start = 0;
this.end = 0;
}
add(part) {
if (part) {
this.parts.push(part);
this.substrs.push(part.substr);
this.start = Math.min(part.start, this.start);
this.end = Math.max(part.end, this.end);
}
}
last() {
return this.parts[this.parts.length - 1];
}
length() {
return this.parts.length;
}
clone(position, last_piece) {
let clone = new Sequence();
let parts = JSON.parse(JSON.stringify(this.parts));
let last_part = parts.pop();
for (const part of parts) {
clone.add(part);
}
let last_substr = last_piece.substr.substring(0, position - last_part.start);
let clone_last_len = last_substr.length;
clone.add({
start: last_part.start,
end: last_part.start + clone_last_len,
length: clone_last_len,
substr: last_substr
});
return clone;
}
}
}
/**
* Expand a regular expression pattern to include unicode variants
* eg /a/ becomes /aⓐaẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐɑAⒶAÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄȺⱯ/
*
* Issue:
* ﺊﺋ [ 'ﺊ = \\u{fe8a}', 'ﺋ = \\u{fe8b}' ]
* becomes: ئئ [ 'ي = \\u{64a}', 'ٔ = \\u{654}', 'ي = \\u{64a}', 'ٔ = \\u{654}' ]
*
* İIJ = IIJ = ⅡJ
*
* 1/2/4
*
* @param {string} str
* @return {string|undefined}
*/
const getPattern = str => {
initialize();
str = asciifold(str);
let pattern = '';
let sequences = [new Sequence()];
for (let i = 0; i < str.length; i++) {
let substr = str.substring(i);
let match = substr.match(multi_char_reg);
const char = str.substring(i, i + 1);
const match_str = match ? match[0] : null; // loop through sequences
// add either the char or multi_match
let overlapping = [];
let added_types = new Set();
for (const sequence of sequences) {
const last_piece = sequence.last();
if (!last_piece || last_piece.length == 1 || last_piece.end <= i) {
// if we have a multi match
if (match_str) {
/**
* Expand a regular expression pattern to include unicode variants
* eg /a/ becomes /aⓐaẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐɑAⒶAÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄȺⱯ/
*
* Issue:
* ﺊﺋ [ 'ﺊ = \\u{fe8a}', 'ﺋ = \\u{fe8b}' ]
* becomes: ئئ [ 'ي = \\u{64a}', 'ٔ = \\u{654}', 'ي = \\u{64a}', 'ٔ = \\u{654}' ]
*
* İIJ = IIJ = ⅡJ
*
* 1/2/4
*/
const getPattern = str => {
initialize();
str = asciifold(str);
let pattern = '';
let sequences = [new Sequence()];
for (let i = 0; i < str.length; i++) {
let substr = str.substring(i);
let match = substr.match(multi_char_reg);
const char = str.substring(i, i + 1);
const match_str = match ? match[0] : null;
// loop through sequences
// add either the char or multi_match
let overlapping = [];
let added_types = new Set();
for (const sequence of sequences) {
const last_piece = sequence.last();
if (!last_piece || last_piece.length == 1 || last_piece.end <= i) {
// if we have a multi match
if (match_str) {
const len = match_str.length;
sequence.add({
start: i,
end: i + len,
length: len,
substr: match_str
});
added_types.add('1');
} else {
sequence.add({
start: i,
end: i + 1,
length: 1,
substr: char
});
added_types.add('2');
}
} else if (match_str) {
let clone = sequence.clone(i, last_piece);
const len = match_str.length;
sequence.add({
clone.add({
start: i,

@@ -617,530 +445,462 @@ end: i + len,

});
added_types.add('1');
overlapping.push(clone);
} else {
sequence.add({
start: i,
end: i + 1,
length: 1,
substr: char
});
added_types.add('2');
// don't add char
// adding would create invalid patterns: 234 => [2,34,4]
added_types.add('3');
}
} else if (match_str) {
let clone = sequence.clone(i, last_piece);
const len = match_str.length;
clone.add({
start: i,
end: i + len,
length: len,
substr: match_str
}
// if we have overlapping
if (overlapping.length > 0) {
// ['ii','iii'] before ['i','i','iii']
overlapping = overlapping.sort((a, b) => {
return a.length() - b.length();
});
overlapping.push(clone);
} else {
// don't add char
// adding would create invalid patterns: 234 => [2,34,4]
added_types.add('3');
for (let clone of overlapping) {
// don't add if we already have an equivalent sequence
if (inSequences(clone, sequences)) {
continue;
}
sequences.push(clone);
}
continue;
}
} // if we have overlapping
if (overlapping.length > 0) {
// ['ii','iii'] before ['i','i','iii']
overlapping = overlapping.sort((a, b) => {
return a.length() - b.length();
});
for (let clone of overlapping) {
// don't add if we already have an equivalent sequence
if (inSequences(clone, sequences)) {
continue;
// if we haven't done anything unique
// clean up the patterns
// helps keep patterns smaller
// if str = 'r₨㎧aarss', pattern will be 446 instead of 655
if (i > 0 && added_types.size == 1 && !added_types.has('3')) {
pattern += sequencesToPattern(sequences, false);
let new_seq = new Sequence();
const old_seq = sequences[0];
if (old_seq) {
new_seq.add(old_seq.last());
}
sequences.push(clone);
sequences = [new_seq];
}
continue;
} // if we haven't done anything unique
// clean up the patterns
// helps keep patterns smaller
// if str = 'r₨㎧aarss', pattern will be 446 instead of 655
if (i > 0 && added_types.size == 1 && !added_types.has('3')) {
pattern += sequencesToPattern(sequences, false);
let new_seq = new Sequence();
const old_seq = sequences[0];
if (old_seq) {
new_seq.add(old_seq.last());
}
sequences = [new_seq];
}
}
pattern += sequencesToPattern(sequences, true);
return pattern;
};
pattern += sequencesToPattern(sequences, true);
return pattern;
};
/**
* A property getter resolving dot-notation
* @param {Object} obj The root object to fetch property on
* @param {String} name The optionally dotted property name to fetch
* @return {Object} The resolved property value
*/
const getAttr = (obj, name) => {
if (!obj) return;
return obj[name];
};
/**
* A property getter resolving dot-notation
* @param {Object} obj The root object to fetch property on
* @param {String} name The optionally dotted property name to fetch
* @return {Object} The resolved property value
*/
const getAttr = (obj, name) => {
if (!obj) return;
return obj[name];
};
/**
* A property getter resolving dot-notation
* @param {Object} obj The root object to fetch property on
* @param {String} name The optionally dotted property name to fetch
* @return {Object} The resolved property value
*/
const getAttrNesting = (obj, name) => {
if (!obj) return;
var part,
/**
* A property getter resolving dot-notation
* @param {Object} obj The root object to fetch property on
* @param {String} name The optionally dotted property name to fetch
* @return {Object} The resolved property value
*/
const getAttrNesting = (obj, name) => {
if (!obj) return;
var part,
names = name.split(".");
while ((part = names.shift()) && (obj = obj[part]));
return obj;
};
while ((part = names.shift()) && (obj = obj[part]));
return obj;
};
/**
* Calculates how close of a match the
* given value is against a search token.
*
*/
const scoreValue = (value, token, weight) => {
var score, pos;
if (!value) return 0;
value = value + '';
if (token.regex == null) return 0;
pos = value.search(token.regex);
if (pos === -1) return 0;
score = token.string.length / value.length;
if (pos === 0) score += 0.5;
return score * weight;
};
/**
* Cast object property to an array if it exists and has a value
*
*/
const propToArray = (obj, key) => {
var value = obj[key];
if (typeof value == 'function') return value;
if (value && !Array.isArray(value)) {
obj[key] = [value];
}
};
/**
* Iterates over arrays and hashes.
*
* ```
* iterate(this.items, function(item, id) {
* // invoked for each item
* });
* ```
*
*/
const iterate = (object, callback) => {
if (Array.isArray(object)) {
object.forEach(callback);
} else {
for (var key in object) {
if (object.hasOwnProperty(key)) {
callback(object[key], key);
}
}
}
};
const cmp = (a, b) => {
if (typeof a === 'number' && typeof b === 'number') {
return a > b ? 1 : a < b ? -1 : 0;
}
a = asciifold(a + '').toLowerCase();
b = asciifold(b + '').toLowerCase();
if (a > b) return 1;
if (b > a) return -1;
return 0;
};
/**
* sifter.js
* Copyright (c) 2013–2020 Brian Reavis & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* @author Brian Reavis <brian@thirdroute.com>
*/
class Sifter {
// []|{};
/**
* Textually searches arrays and hashes of objects
* by property (or multiple properties). Designed
* specifically for autocomplete.
* Calculates how close of a match the
* given value is against a search token.
*
*/
constructor(items, settings) {
this.items = void 0;
this.settings = void 0;
this.items = items;
this.settings = settings || {
diacritics: true
};
}
const scoreValue = (value, token, weight) => {
var score, pos;
if (!value) return 0;
value = value + '';
if (token.regex == null) return 0;
pos = value.search(token.regex);
if (pos === -1) return 0;
score = token.string.length / value.length;
if (pos === 0) score += 0.5;
return score * weight;
};
/**
* Splits a search string into an array of individual
* regexps to be used to match results.
* Cast object property to an array if it exists and has a value
*
*/
tokenize(query, respect_word_boundaries, weights) {
if (!query || !query.length) return [];
const tokens = [];
const words = query.split(/\s+/);
var field_regex;
if (weights) {
field_regex = new RegExp('^(' + Object.keys(weights).map(escape_regex).join('|') + ')\:(.*)$');
const propToArray = (obj, key) => {
var value = obj[key];
if (typeof value == 'function') return value;
if (value && !Array.isArray(value)) {
obj[key] = [value];
}
};
words.forEach(word => {
let field_match;
let field = null;
let regex = null; // look for "field:query" tokens
if (field_regex && (field_match = word.match(field_regex))) {
field = field_match[1];
word = field_match[2];
}
if (word.length > 0) {
if (this.settings.diacritics) {
regex = getPattern(word) || null;
} else {
regex = escape_regex(word);
/**
* Iterates over arrays and hashes.
*
* ```
* iterate(this.items, function(item, id) {
* // invoked for each item
* });
* ```
*
*/
const iterate = (object, callback) => {
if (Array.isArray(object)) {
object.forEach(callback);
} else {
for (var key in object) {
if (object.hasOwnProperty(key)) {
callback(object[key], key);
}
if (regex && respect_word_boundaries) regex = "\\b" + regex;
}
}
};
const cmp = (a, b) => {
if (typeof a === 'number' && typeof b === 'number') {
return a > b ? 1 : a < b ? -1 : 0;
}
a = asciifold(a + '').toLowerCase();
b = asciifold(b + '').toLowerCase();
if (a > b) return 1;
if (b > a) return -1;
return 0;
};
tokens.push({
string: word,
regex: regex ? new RegExp(regex, 'iu') : null,
field: field
});
});
return tokens;
}
/**
* Returns a function to be used to score individual results.
* sifter.js
* Copyright (c) 2013–2020 Brian Reavis & contributors
*
* Good matches will have a higher score than poor matches.
* If an item is not a match, 0 will be returned by the function.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* @returns {T.ScoreFn}
*/
getScoreFunction(query, options) {
var search = this.prepareSearch(query, options);
return this._getScoreFunction(search);
}
/**
* @returns {T.ScoreFn}
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* @author Brian Reavis <brian@thirdroute.com>
*/
class Sifter {
// []|{};
_getScoreFunction(search) {
const tokens = search.tokens,
token_count = tokens.length;
if (!token_count) {
return function () {
return 0;
/**
* Textually searches arrays and hashes of objects
* by property (or multiple properties). Designed
* specifically for autocomplete.
*
*/
constructor(items, settings) {
this.items = items;
this.settings = settings || {
diacritics: true
};
}
/**
* Splits a search string into an array of individual
* regexps to be used to match results.
*
*/
tokenize(query, respect_word_boundaries, weights) {
if (!query || !query.length) return [];
const tokens = [];
const words = query.split(/\s+/);
var field_regex;
if (weights) {
field_regex = new RegExp('^(' + Object.keys(weights).map(escape_regex).join('|') + ')\:(.*)$');
}
words.forEach(word => {
let field_match;
let field = null;
let regex = null;
const fields = search.options.fields,
weights = search.weights,
field_count = fields.length,
getAttrFn = search.getAttrFn;
if (!field_count) {
return function () {
return 1;
};
// look for "field:query" tokens
if (field_regex && (field_match = word.match(field_regex))) {
field = field_match[1];
word = field_match[2];
}
if (word.length > 0) {
if (this.settings.diacritics) {
regex = getPattern(word) || null;
} else {
regex = escape_regex(word);
}
if (regex && respect_word_boundaries) regex = "\\b" + regex;
}
tokens.push({
string: word,
regex: regex ? new RegExp(regex, 'iu') : null,
field: field
});
});
return tokens;
}
/**
* Calculates the score of an object
* against the search query.
* Returns a function to be used to score individual results.
*
* Good matches will have a higher score than poor matches.
* If an item is not a match, 0 will be returned by the function.
*
* @returns {T.ScoreFn}
*/
getScoreFunction(query, options) {
var search = this.prepareSearch(query, options);
return this._getScoreFunction(search);
}
const scoreObject = function () {
if (field_count === 1) {
return function (token, data) {
const field = fields[0].field;
return scoreValue(getAttrFn(data, field), token, weights[field] || 1);
/**
* @returns {T.ScoreFn}
*
*/
_getScoreFunction(search) {
const tokens = search.tokens,
token_count = tokens.length;
if (!token_count) {
return function () {
return 0;
};
}
const fields = search.options.fields,
weights = search.weights,
field_count = fields.length,
getAttrFn = search.getAttrFn;
if (!field_count) {
return function () {
return 1;
};
}
return function (token, data) {
var sum = 0; // is the token specific to a field?
/**
* Calculates the score of an object
* against the search query.
*
*/
const scoreObject = function () {
if (field_count === 1) {
return function (token, data) {
const field = fields[0].field;
return scoreValue(getAttrFn(data, field), token, weights[field] || 1);
};
}
return function (token, data) {
var sum = 0;
if (token.field) {
const value = getAttrFn(data, token.field);
if (!token.regex && value) {
sum += 1 / field_count;
// is the token specific to a field?
if (token.field) {
const value = getAttrFn(data, token.field);
if (!token.regex && value) {
sum += 1 / field_count;
} else {
sum += scoreValue(value, token, 1);
}
} else {
sum += scoreValue(value, token, 1);
iterate(weights, (weight, field) => {
sum += scoreValue(getAttrFn(data, field), token, weight);
});
}
} else {
iterate(weights, (weight, field) => {
sum += scoreValue(getAttrFn(data, field), token, weight);
return sum / field_count;
};
}();
if (token_count === 1) {
return function (data) {
return scoreObject(tokens[0], data);
};
}
if (search.options.conjunction === 'and') {
return function (data) {
var score,
sum = 0;
for (let token of tokens) {
score = scoreObject(token, data);
if (score <= 0) return 0;
sum += score;
}
return sum / token_count;
};
} else {
return function (data) {
var sum = 0;
iterate(tokens, token => {
sum += scoreObject(token, data);
});
}
return sum / field_count;
};
}();
if (token_count === 1) {
return function (data) {
return scoreObject(tokens[0], data);
};
return sum / token_count;
};
}
}
if (search.options.conjunction === 'and') {
return function (data) {
var score,
sum = 0;
for (let token of tokens) {
score = scoreObject(token, data);
if (score <= 0) return 0;
sum += score;
}
return sum / token_count;
};
} else {
return function (data) {
var sum = 0;
iterate(tokens, token => {
sum += scoreObject(token, data);
});
return sum / token_count;
};
}
}
/**
* Returns a function that can be used to compare two
* results, for sorting purposes. If no sorting should
* be performed, `null` will be returned.
*
* @return function(a,b)
*/
getSortFunction(query, options) {
var search = this.prepareSearch(query, options);
return this._getSortFunction(search);
}
_getSortFunction(search) {
var implicit_score,
sort_flds = [];
const self = this,
options = search.options,
sort = !search.query && options.sort_empty ? options.sort_empty : options.sort;
if (typeof sort == 'function') {
return sort.bind(this);
}
/**
* Fetches the specified sort field value
* from a search result item.
* Returns a function that can be used to compare two
* results, for sorting purposes. If no sorting should
* be performed, `null` will be returned.
*
* @return function(a,b)
*/
getSortFunction(query, options) {
var search = this.prepareSearch(query, options);
return this._getSortFunction(search);
}
_getSortFunction(search) {
var implicit_score,
sort_flds = [];
const self = this,
options = search.options,
sort = !search.query && options.sort_empty ? options.sort_empty : options.sort;
if (typeof sort == 'function') {
return sort.bind(this);
}
/**
* Fetches the specified sort field value
* from a search result item.
*
*/
const get_field = function get_field(name, result) {
if (name === '$score') return result.score;
return search.getAttrFn(self.items[result.id], name);
};
const get_field = function get_field(name, result) {
if (name === '$score') return result.score;
return search.getAttrFn(self.items[result.id], name);
}; // parse options
if (sort) {
for (let s of sort) {
if (search.query || s.field !== '$score') {
sort_flds.push(s);
// parse options
if (sort) {
for (let s of sort) {
if (search.query || s.field !== '$score') {
sort_flds.push(s);
}
}
}
} // the "$score" field is implied to be the primary
// sort field, unless it's manually specified
// the "$score" field is implied to be the primary
// sort field, unless it's manually specified
if (search.query) {
implicit_score = true;
for (let fld of sort_flds) {
if (fld.field === '$score') {
implicit_score = false;
break;
}
}
if (implicit_score) {
sort_flds.unshift({
field: '$score',
direction: 'desc'
});
}
if (search.query) {
implicit_score = true;
// without a search.query, all items will have the same score
} else {
sort_flds = sort_flds.filter(fld => fld.field !== '$score');
}
for (let fld of sort_flds) {
if (fld.field === '$score') {
implicit_score = false;
break;
// build function
const sort_flds_count = sort_flds.length;
if (!sort_flds_count) {
return null;
}
return function (a, b) {
var result, field;
for (let sort_fld of sort_flds) {
field = sort_fld.field;
let multiplier = sort_fld.direction === 'desc' ? -1 : 1;
result = multiplier * cmp(get_field(field, a), get_field(field, b));
if (result) return result;
}
}
return 0;
};
}
/**
* Parses a search query and returns an object
* with tokens and fields ready to be populated
* with results.
*
*/
prepareSearch(query, optsUser) {
const weights = {};
var options = Object.assign({}, optsUser);
propToArray(options, 'sort');
propToArray(options, 'sort_empty');
if (implicit_score) {
sort_flds.unshift({
field: '$score',
direction: 'desc'
// convert fields to new format
if (options.fields) {
propToArray(options, 'fields');
const fields = [];
options.fields.forEach(field => {
if (typeof field == 'string') {
field = {
field: field,
weight: 1
};
}
fields.push(field);
weights[field.field] = 'weight' in field ? field.weight : 1;
});
} // without a search.query, all items will have the same score
} else {
sort_flds = sort_flds.filter(fld => fld.field !== '$score');
} // build function
const sort_flds_count = sort_flds.length;
if (!sort_flds_count) {
return null;
}
return function (a, b) {
var result, field;
for (let sort_fld of sort_flds) {
field = sort_fld.field;
let multiplier = sort_fld.direction === 'desc' ? -1 : 1;
result = multiplier * cmp(get_field(field, a), get_field(field, b));
if (result) return result;
options.fields = fields;
}
return 0;
};
}
/**
* Parses a search query and returns an object
* with tokens and fields ready to be populated
* with results.
*
*/
prepareSearch(query, optsUser) {
const weights = {};
var options = Object.assign({}, optsUser);
propToArray(options, 'sort');
propToArray(options, 'sort_empty'); // convert fields to new format
if (options.fields) {
propToArray(options, 'fields');
const fields = [];
options.fields.forEach(field => {
if (typeof field == 'string') {
field = {
field: field,
weight: 1
};
}
fields.push(field);
weights[field.field] = 'weight' in field ? field.weight : 1;
});
options.fields = fields;
return {
options: options,
query: query.toLowerCase().trim(),
tokens: this.tokenize(query, options.respect_word_boundaries, weights),
total: 0,
items: [],
weights: weights,
getAttrFn: options.nesting ? getAttrNesting : getAttr
};
}
return {
options: options,
query: query.toLowerCase().trim(),
tokens: this.tokenize(query, options.respect_word_boundaries, weights),
total: 0,
items: [],
weights: weights,
getAttrFn: options.nesting ? getAttrNesting : getAttr
};
}
/**
* Searches through all items and returns a sorted array of matches.
*
*/
search(query, options) {
var self = this,
/**
* Searches through all items and returns a sorted array of matches.
*
*/
search(query, options) {
var self = this,
score,
search;
search = this.prepareSearch(query, options);
options = search.options;
query = search.query; // generate result scoring function
search = this.prepareSearch(query, options);
options = search.options;
query = search.query;
const fn_score = options.score || self._getScoreFunction(search); // perform search and sort
// generate result scoring function
const fn_score = options.score || self._getScoreFunction(search);
if (query.length) {
iterate(self.items, (item, id) => {
score = fn_score(item);
if (options.filter === false || score > 0) {
// perform search and sort
if (query.length) {
iterate(self.items, (item, id) => {
score = fn_score(item);
if (options.filter === false || score > 0) {
search.items.push({
'score': score,
'id': id
});
}
});
} else {
iterate(self.items, (_, id) => {
search.items.push({
'score': score,
'score': 1,
'id': id
});
}
});
} else {
iterate(self.items, (_, id) => {
search.items.push({
'score': 1,
'id': id
});
});
}
}
const fn_sort = self._getSortFunction(search);
if (fn_sort) search.items.sort(fn_sort);
const fn_sort = self._getSortFunction(search);
if (fn_sort) search.items.sort(fn_sort); // apply limits
search.total = search.items.length;
if (typeof options.limit === 'number') {
search.items = search.items.slice(0, options.limit);
// apply limits
search.total = search.items.length;
if (typeof options.limit === 'number') {
search.items = search.items.slice(0, options.limit);
}
return search;
}
return search;
}
}
exports.Sifter = Sifter;
exports.cmp = cmp;
exports.getAttr = getAttr;
exports.getAttrNesting = getAttrNesting;
exports.getPattern = getPattern;
exports.iterate = iterate;
exports.propToArray = propToArray;
exports.scoreValue = scoreValue;
exports.Sifter = Sifter;
exports.cmp = cmp;
exports.getAttr = getAttr;
exports.getAttrNesting = getAttrNesting;
exports.getPattern = getPattern;
exports.iterate = iterate;
exports.propToArray = propToArray;
exports.scoreValue = scoreValue;
Object.defineProperty(exports, '__esModule', { value: true });
})));
}));
//# sourceMappingURL=sifter.js.map

@@ -5,18 +5,18 @@ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).sifter={})}(this,(function(t){"use strict"

const n=()=>{r>1&&(e+="{"+r+"}")}
return t.forEach(((s,o)=>{s!==t[o-1]?(n(),e+=s,r=1):r++})),n(),e},n=t=>{let r=u(t)
return e(r)},s=t=>new Set(t).size!==t.length,o=t=>(t+"").replace(/([\$\(-\+\.\?\[-\^\{-\}])/g,"\\$1"),i=t=>t.reduce(((t,e)=>Math.max(t,l(e))),0),l=t=>u(t).length,u=t=>Array.from(t),a=t=>{if(1===t.length)return[[t]]
return t.forEach(((s,o)=>{s!==t[o-1]?(n(),e+=s,r=1):r++})),n(),e},n=t=>{let r=Array.from(t)
return e(r)},s=t=>new Set(t).size!==t.length,o=t=>(t+"").replace(/([\$\(-\+\.\?\[-\^\{-\}])/g,"\\$1"),i=t=>t.reduce(((t,e)=>Math.max(t,l(e))),0),l=t=>Array.from(t).length,u=t=>{if(1===t.length)return[[t]]
let e=[]
const r=t.substring(1)
return a(r).forEach((function(r){let n=r.slice(0)
n[0]=t.charAt(0)+n[0],e.push(n),n=r.slice(0),n.unshift(t.charAt(0)),e.push(n)})),e},f=[[0,65535]]
let c,h
const d={},g={"/":"⁄∕",0:"߀",a:"ⱥɐɑ",aa:"ꜳ",ae:"æǽǣ",ao:"ꜵ",au:"ꜷ",av:"ꜹꜻ",ay:"ꜽ",b:"ƀɓƃ",c:"ꜿƈȼↄ",d:"đɗɖᴅƌꮷԁɦ",e:"ɛǝᴇɇ",f:"ꝼƒ",g:"ǥɠꞡᵹꝿɢ",h:"ħⱨⱶɥ",i:"ɨı",j:"ɉȷ",k:"ƙⱪꝁꝃꝅꞣ",l:"łƚɫⱡꝉꝇꞁɭ",m:"ɱɯϻ",n:"ꞥƞɲꞑᴎлԉ",o:"øǿɔɵꝋꝍᴑ",oe:"œ",oi:"ƣ",oo:"ꝏ",ou:"ȣ",p:"ƥᵽꝑꝓꝕρ",q:"ꝗꝙɋ",r:"ɍɽꝛꞧꞃ",s:"ßȿꞩꞅʂ",t:"ŧƭʈⱦꞇ",th:"þ",tz:"ꜩ",u:"ʉ",v:"ʋꝟʌ",vy:"ꝡ",w:"ⱳ",y:"ƴɏỿ",z:"ƶȥɀⱬꝣ",hv:"ƕ"}
for(let t in g){let e=g[t]||""
return u(r).forEach((function(r){let n=r.slice(0)
n[0]=t.charAt(0)+n[0],e.push(n),n=r.slice(0),n.unshift(t.charAt(0)),e.push(n)})),e},a=[[0,65535]]
let f,c
const h={},d={"/":"⁄∕",0:"߀",a:"ⱥɐɑ",aa:"ꜳ",ae:"æǽǣ",ao:"ꜵ",au:"ꜷ",av:"ꜹꜻ",ay:"ꜽ",b:"ƀɓƃ",c:"ꜿƈȼↄ",d:"đɗɖᴅƌꮷԁɦ",e:"ɛǝᴇɇ",f:"ꝼƒ",g:"ǥɠꞡᵹꝿɢ",h:"ħⱨⱶɥ",i:"ɨı",j:"ɉȷ",k:"ƙⱪꝁꝃꝅꞣ",l:"łƚɫⱡꝉꝇꞁɭ",m:"ɱɯϻ",n:"ꞥƞɲꞑᴎлԉ",o:"øǿɔɵꝋꝍᴑ",oe:"œ",oi:"ƣ",oo:"ꝏ",ou:"ȣ",p:"ƥᵽꝑꝓꝕρ",q:"ꝗꝙɋ",r:"ɍɽꝛꞧꞃ",s:"ßȿꞩꞅʂ",t:"ŧƭʈⱦꞇ",th:"þ",tz:"ꜩ",u:"ʉ",v:"ʋꝟʌ",vy:"ꝡ",w:"ⱳ",y:"ƴɏỿ",z:"ƶȥɀⱬꝣ",hv:"ƕ"}
for(let t in d){let e=d[t]||""
for(let r=0;r<e.length;r++){let n=e.substring(r,r+1)
d[n]=t}}const p=new RegExp(Object.keys(d).join("|")+"|[̀-ͯ·ʾʼ]","gu"),m=(t,e="NFKD")=>t.normalize(e),b=t=>u(t).reduce(((t,e)=>t+y(e)),""),y=t=>(t=m(t).toLowerCase().replace(p,(t=>d[t]||"")),m(t,"NFC"))
const w=t=>{const e={},r=(t,r)=>{const s=e[t]||new Set,i=new RegExp("^"+n(s)+"$","iu")
h[n]=t}}const g=new RegExp(Object.keys(h).join("|")+"|[̀-ͯ·ʾʼ]","gu"),p=(t,e="NFKD")=>t.normalize(e),m=t=>Array.from(t).reduce(((t,e)=>t+y(e)),""),y=t=>(t=p(t).toLowerCase().replace(g,(t=>h[t]||"")),p(t,"NFC"))
const b=t=>{const e={},r=(t,r)=>{const s=e[t]||new Set,i=new RegExp("^"+n(s)+"$","iu")
r.match(i)||(s.add(o(r)),e[t]=s)}
for(let e of function*(t){for(const[e,r]of t)for(let t=e;t<=r;t++){let e=String.fromCharCode(t),r=b(e)
for(let e of function*(t){for(const[e,r]of t)for(let t=e;t<=r;t++){let e=String.fromCharCode(t),r=m(e)
r!=e.toLowerCase()&&(r.length>3||0!=r.length&&(yield{folded:r,composed:e,code_point:t}))}}(t))r(e.folded,e.folded),r(e.folded,e.composed)
return e},v=t=>{const r=w(t),s={}
return e},w=t=>{const r=b(t),s={}
let i=[]

@@ -26,8 +26,8 @@ for(let t in r){let e=r[t]

const l=e(i)
return h=new RegExp("^"+l,"u"),s},S=(t,n=1)=>(n=Math.max(n,t.length-1),e(a(t).map((t=>((t,e=1)=>{let n=0
return t=t.map((t=>(c[t]&&(n+=t.length),c[t]||t))),n>=e?r(t):""})(t,n))))),x=(t,n=!0)=>{let s=t.length>1?1:0
return c=new RegExp("^"+l,"u"),s},v=(t,n=1)=>(n=Math.max(n,t.length-1),e(u(t).map((t=>((t,e=1)=>{let n=0
return t=t.map((t=>(f[t]&&(n+=t.length),f[t]||t))),n>=e?r(t):""})(t,n))))),S=(t,n=!0)=>{let s=t.length>1?1:0
return e(t.map((t=>{let e=[]
const o=n?t.length():t.length()-1
for(let r=0;r<o;r++)e.push(S(t.substrs[r]||"",s))
return r(e)})))},j=(t,e)=>{for(const r of e){if(r.start!=t.start||r.end!=t.end)continue
for(let r=0;r<o;r++)e.push(v(t.substrs[r]||"",s))
return r(e)})))},x=(t,e)=>{for(const r of e){if(r.start!=t.start||r.end!=t.end)continue
if(r.substrs.join("")!==t.substrs.join(""))continue

@@ -39,46 +39,45 @@ let e=t.parts

if(!(r.parts.filter(n).length>0))return!0}return!1}
class _{constructor(){this.parts=[],this.substrs=[],this.start=0,this.end=0}add(t){t&&(this.parts.push(t),this.substrs.push(t.substr),this.start=Math.min(t.start,this.start),this.end=Math.max(t.end,this.end))}last(){return this.parts[this.parts.length-1]}length(){return this.parts.length}clone(t,e){let r=new _,n=JSON.parse(JSON.stringify(this.parts)),s=n.pop()
class A{constructor(){this.parts=void 0,this.substrs=void 0,this.start=void 0,this.end=void 0,this.parts=[],this.substrs=[],this.start=0,this.end=0}add(t){t&&(this.parts.push(t),this.substrs.push(t.substr),this.start=Math.min(t.start,this.start),this.end=Math.max(t.end,this.end))}last(){return this.parts[this.parts.length-1]}length(){return this.parts.length}clone(t,e){let r=new A,n=JSON.parse(JSON.stringify(this.parts)),s=n.pop()
for(const t of n)r.add(t)
let o=e.substr.substring(0,t-s.start),i=o.length
return r.add({start:s.start,end:s.start+i,length:i,substr:o}),r}}const A=t=>{var e
void 0===c&&(c=v(e||f)),t=b(t)
let r="",n=[new _]
for(let e=0;e<t.length;e++){let s=t.substring(e).match(h)
const o=t.substring(e,e+1),i=s?s[0]:null
return r.add({start:s.start,end:s.start+i,length:i,substr:o}),r}}const j=t=>{void 0===f&&(f=w(a)),t=m(t)
let e="",r=[new A]
for(let n=0;n<t.length;n++){let s=t.substring(n).match(c)
const o=t.substring(n,n+1),i=s?s[0]:null
let l=[],u=new Set
for(const t of n){const r=t.last()
if(!r||1==r.length||r.end<=e)if(i){const r=i.length
t.add({start:e,end:e+r,length:r,substr:i}),u.add("1")}else t.add({start:e,end:e+1,length:1,substr:o}),u.add("2")
else if(i){let n=t.clone(e,r)
for(const t of r){const e=t.last()
if(!e||1==e.length||e.end<=n)if(i){const e=i.length
t.add({start:n,end:n+e,length:e,substr:i}),u.add("1")}else t.add({start:n,end:n+1,length:1,substr:o}),u.add("2")
else if(i){let r=t.clone(n,e)
const s=i.length
n.add({start:e,end:e+s,length:s,substr:i}),l.push(n)}else u.add("3")}if(l.length>0){l=l.sort(((t,e)=>t.length()-e.length()))
for(let t of l)j(t,n)||n.push(t)}else if(e>0&&1==u.size&&!u.has("3")){r+=x(n,!1)
let t=new _
const e=n[0]
e&&t.add(e.last()),n=[t]}}return r+=x(n,!0),r},F=(t,e)=>{if(t)return t[e]},E=(t,e)=>{if(t){for(var r,n=e.split(".");(r=n.shift())&&(t=t[r]););return t}},$=(t,e,r)=>{var n,s
return t?(t+="",null==e.regex||-1===(s=t.search(e.regex))?0:(n=e.string.length/t.length,0===s&&(n+=.5),n*r)):0},k=(t,e)=>{var r=t[e]
r.add({start:n,end:n+s,length:s,substr:i}),l.push(r)}else u.add("3")}if(l.length>0){l=l.sort(((t,e)=>t.length()-e.length()))
for(let t of l)x(t,r)||r.push(t)}else if(n>0&&1==u.size&&!u.has("3")){e+=S(r,!1)
let t=new A
const n=r[0]
n&&t.add(n.last()),r=[t]}}return e+=S(r,!0),e},F=(t,e)=>{if(t)return t[e]},_=(t,e)=>{if(t){for(var r,n=e.split(".");(r=n.shift())&&(t=t[r]););return t}},E=(t,e,r)=>{var n,s
return t?(t+="",null==e.regex||-1===(s=t.search(e.regex))?0:(n=e.string.length/t.length,0===s&&(n+=.5),n*r)):0},$=(t,e)=>{var r=t[e]
if("function"==typeof r)return r
r&&!Array.isArray(r)&&(t[e]=[r])},C=(t,e)=>{if(Array.isArray(t))t.forEach(e)
else for(var r in t)t.hasOwnProperty(r)&&e(t[r],r)},z=(t,e)=>"number"==typeof t&&"number"==typeof e?t>e?1:t<e?-1:0:(t=b(t+"").toLowerCase())>(e=b(e+"").toLowerCase())?1:e>t?-1:0
r&&!Array.isArray(r)&&(t[e]=[r])},k=(t,e)=>{if(Array.isArray(t))t.forEach(e)
else for(var r in t)t.hasOwnProperty(r)&&e(t[r],r)},C=(t,e)=>"number"==typeof t&&"number"==typeof e?t>e?1:t<e?-1:0:(t=m(t+"").toLowerCase())>(e=m(e+"").toLowerCase())?1:e>t?-1:0
t.Sifter=
/**
* sifter.js
* Copyright (c) 2013–2020 Brian Reavis & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* @author Brian Reavis <brian@thirdroute.com>
*/
class{constructor(t,e){this.items=void 0,this.settings=void 0,this.items=t,this.settings=e||{diacritics:!0}}tokenize(t,e,r){if(!t||!t.length)return[]
* sifter.js
* Copyright (c) 2013–2020 Brian Reavis & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* @author Brian Reavis <brian@thirdroute.com>
*/
class{constructor(t,e){this.items=t,this.settings=e||{diacritics:!0}}tokenize(t,e,r){if(!t||!t.length)return[]
const n=[],s=t.split(/\s+/)
var i
return r&&(i=new RegExp("^("+Object.keys(r).map(o).join("|")+"):(.*)$")),s.forEach((t=>{let r,s=null,l=null
i&&(r=t.match(i))&&(s=r[1],t=r[2]),t.length>0&&(l=this.settings.diacritics?A(t)||null:o(t),l&&e&&(l="\\b"+l)),n.push({string:t,regex:l?new RegExp(l,"iu"):null,field:s})})),n}getScoreFunction(t,e){var r=this.prepareSearch(t,e)
i&&(r=t.match(i))&&(s=r[1],t=r[2]),t.length>0&&(l=this.settings.diacritics?j(t)||null:o(t),l&&e&&(l="\\b"+l)),n.push({string:t,regex:l?new RegExp(l,"iu"):null,field:s})})),n}getScoreFunction(t,e){var r=this.prepareSearch(t,e)
return this._getScoreFunction(r)}_getScoreFunction(t){const e=t.tokens,r=e.length

@@ -89,5 +88,5 @@ if(!r)return function(){return 0}

const l=1===o?function(t,e){const r=n[0].field
return $(i(e,r),t,s[r]||1)}:function(t,e){var r=0
return E(i(e,r),t,s[r]||1)}:function(t,e){var r=0
if(t.field){const n=i(e,t.field)
!t.regex&&n?r+=1/o:r+=$(n,t,1)}else C(s,((n,s)=>{r+=$(i(e,s),t,n)}))
!t.regex&&n?r+=1/o:r+=E(n,t,1)}else k(s,((n,s)=>{r+=E(i(e,s),t,n)}))
return r/o}

@@ -97,3 +96,3 @@ return 1===r?function(t){return l(e[0],t)}:"and"===t.options.conjunction?function(t){var n,s=0

s+=n}return s/r}:function(t){var n=0
return C(e,(e=>{n+=l(e,t)})),n/r}}getSortFunction(t,e){var r=this.prepareSearch(t,e)
return k(e,(e=>{n+=l(e,t)})),n/r}}getSortFunction(t,e){var r=this.prepareSearch(t,e)
return this._getSortFunction(r)}_getSortFunction(t){var e,r=[]

@@ -108,12 +107,12 @@ const n=this,s=t.options,o=!t.query&&s.sort_empty?s.sort_empty:s.sort

return r.length?function(t,e){var n,s
for(let o of r){if(s=o.field,n=("desc"===o.direction?-1:1)*z(i(s,t),i(s,e)))return n}return 0}:null}prepareSearch(t,e){const r={}
for(let o of r){if(s=o.field,n=("desc"===o.direction?-1:1)*C(i(s,t),i(s,e)))return n}return 0}:null}prepareSearch(t,e){const r={}
var n=Object.assign({},e)
if(k(n,"sort"),k(n,"sort_empty"),n.fields){k(n,"fields")
if($(n,"sort"),$(n,"sort_empty"),n.fields){$(n,"fields")
const t=[]
n.fields.forEach((e=>{"string"==typeof e&&(e={field:e,weight:1}),t.push(e),r[e.field]="weight"in e?e.weight:1})),n.fields=t}return{options:n,query:t.toLowerCase().trim(),tokens:this.tokenize(t,n.respect_word_boundaries,r),total:0,items:[],weights:r,getAttrFn:n.nesting?E:F}}search(t,e){var r,n,s=this
n.fields.forEach((e=>{"string"==typeof e&&(e={field:e,weight:1}),t.push(e),r[e.field]="weight"in e?e.weight:1})),n.fields=t}return{options:n,query:t.toLowerCase().trim(),tokens:this.tokenize(t,n.respect_word_boundaries,r),total:0,items:[],weights:r,getAttrFn:n.nesting?_:F}}search(t,e){var r,n,s=this
n=this.prepareSearch(t,e),e=n.options,t=n.query
const o=e.score||s._getScoreFunction(n)
t.length?C(s.items,((t,s)=>{r=o(t),(!1===e.filter||r>0)&&n.items.push({score:r,id:s})})):C(s.items,((t,e)=>{n.items.push({score:1,id:e})}))
t.length?k(s.items,((t,s)=>{r=o(t),(!1===e.filter||r>0)&&n.items.push({score:r,id:s})})):k(s.items,((t,e)=>{n.items.push({score:1,id:e})}))
const i=s._getSortFunction(n)
return i&&n.items.sort(i),n.total=n.items.length,"number"==typeof e.limit&&(n.items=n.items.slice(0,e.limit)),n}},t.cmp=z,t.getAttr=F,t.getAttrNesting=E,t.getPattern=A,t.iterate=C,t.propToArray=k,t.scoreValue=$,Object.defineProperty(t,"__esModule",{value:!0})}))
return i&&n.items.sort(i),n.total=n.items.length,"number"==typeof e.limit&&(n.items=n.items.slice(0,e.limit)),n}},t.cmp=C,t.getAttr=F,t.getAttrNesting=_,t.getPattern=j,t.iterate=k,t.propToArray=$,t.scoreValue=E}))
//# sourceMappingURL=sifter.min.js.map

@@ -17,5 +17,5 @@ /**

import { scoreValue, getAttr, getAttrNesting, propToArray, iterate, cmp } from './utils';
import { scoreValue, getAttr, getAttrNesting, propToArray, iterate, cmp } from './utils.ts';
import { getPattern, escape_regex } from '@orchidjs/unicode-variants';
import * as T from './types';
import * as T from './types.ts';

@@ -357,1 +357,2 @@ class Sifter{

export { Sifter, scoreValue, getAttr, getAttrNesting, propToArray, iterate, cmp, getPattern }
export * from './types.ts';
import {Sifter} from './sifter';
import {Sifter} from './sifter.ts';

@@ -4,0 +4,0 @@ export type Field = {

import { asciifold } from '@orchidjs/unicode-variants';
import * as T from './types';
import * as T from './types.ts';

@@ -5,0 +5,0 @@

@@ -14,9 +14,18 @@ {

"description": "A library for textually searching arrays and hashes of objects by property (or multiple properties). Designed specifically for autocomplete.",
"version": "1.0.3",
"version": "1.1.0",
"license": "Apache-2.0",
"author": "Brian Reavis <brian@thirdroute.com>",
"main": "dist/umd/sifter.js",
"type": "module",
"main": "dist/cjs/sifter.js",
"module": "dist/esm/sifter.js",
"browser": "dist/umd/sifter.js",
"module": "dist/esm/sifter.js",
"types": "dist/types/sifter.d.ts",
"exports": {
".": {
"import": "./dist/esm/sifter.js",
"require": "./dist/cjs/sifter.js"
},
"./types/*": "./dist/types/*",
"./dist/*": "./dist/*",
"./package.json": "./package.json"
},
"repository": {

@@ -28,22 +37,23 @@ "type": "git",

"test": "jest --coverage",
"test:typescript": "tsc -p .config --noemit",
"test:coveralls": "npm run build && jest --coverage && cat ./coverage/lcov.info | coveralls",
"test:types": "attw --pack .",
"pretest": "npm run build",
"benchmark": "npm run build && node --expose-gc benchmark/index.js",
"build": "npx rollup -c .config/rollup.config.js",
"build:types": "tsc -p .config --emitDeclarationOnly"
"build": "rm -rf dist && npm run build:esm && npm run build:cjs && npm run build:umd && npm run build:types",
"build:esm": "tsc -p .config/tsconfig.esm.json",
"build:cjs": "tsc -p .config/tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > ./dist/cjs/package.json",
"build:umd": "npx rollup -c .config/rollup.config.mjs && echo '{\"type\":\"commonjs\"}' > ./dist/umd/package.json",
"build:types": "tsc -p .config/tsconfig.types.json"
},
"devDependencies": {
"@babel/core": "^7.13.16",
"@babel/plugin-proposal-class-properties": "^7.13.0",
"@babel/preset-env": "^7.13.15",
"@babel/preset-typescript": "^7.13.0",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-node-resolve": "^13.0.0",
"coveralls": "^3.1.0",
"humanize": "0.0.9",
"jest": "^28.1.2",
"rollup": "^2.45.2",
"rollup-plugin-terser": "^7.0.2",
"typescript": "^4.8.3"
"@arethetypeswrong/cli": "^0.17.0",
"@babel/core": "^7.26.0",
"@babel/preset-env": "^7.26.0",
"@babel/preset-typescript": "^7.26.0",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-terser": "^0.4.4",
"humanize": "^0.0.9",
"jest": "^29.7.0",
"rollup": "^4.26.0",
"typescript": "^5.7.1-rc"
},

@@ -71,4 +81,4 @@ "browserslist": [

"dependencies": {
"@orchidjs/unicode-variants": "^1.0.4"
"@orchidjs/unicode-variants": "^1.1.2"
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc