added all files to project

This commit is contained in:
2022-03-10 10:36:59 +01:00
parent 09dd957b33
commit 46a936d7de
25351 changed files with 3883356 additions and 0 deletions
@@ -0,0 +1,248 @@
"use strict";
/* compile needles to hierarchical map object */
const parser = require('./parser');
const iterator = require('../generic/iterator');
const traverser = require('../generic/traverser');
const {
defineProperty
} = require('../generic/helper');
const {
Wildcard
} = require('./wildcard');
const LEAF = Symbol('leaf');
const markLeaf = (input, match, readonly) => defineProperty(input, LEAF, match, readonly);
const isLeaf = input => LEAF in input;
const isMatch = input => input !== undefined && input[LEAF] === true;
module.exports.isLeaf = isLeaf;
module.exports.isMatch = isMatch;
const HAS_MATCHES = Symbol('has-matches');
const setHasMatches = input => defineProperty(input, HAS_MATCHES, true);
const hasMatches = input => input[HAS_MATCHES] === true;
module.exports.hasMatches = hasMatches;
const merge = (input, symbol, value) => {
if (input[symbol] === undefined) {
defineProperty(input, symbol, []);
}
if (!input[symbol].includes(value)) {
input[symbol].push(value);
}
};
const LEAF_NEEDLES = Symbol('leaf-needles');
const addLeafNeedle = (input, needle) => merge(input, LEAF_NEEDLES, needle);
const getLeafNeedles = input => input[LEAF_NEEDLES] || [];
module.exports.getLeafNeedles = getLeafNeedles;
const LEAF_NEEDLES_EXCLUDE = Symbol('leaf-needles-exclude');
const addLeafNeedleExclude = (input, needle) => merge(input, LEAF_NEEDLES_EXCLUDE, needle);
const getLeafNeedlesExclude = input => input[LEAF_NEEDLES_EXCLUDE] || [];
module.exports.getLeafNeedlesExclude = getLeafNeedlesExclude;
const LEAF_NEEDLES_MATCH = Symbol('leaf-needles-match');
const addLeafNeedleMatch = (input, needle) => merge(input, LEAF_NEEDLES_MATCH, needle);
const getLeafNeedlesMatch = input => input[LEAF_NEEDLES_MATCH] || [];
module.exports.getLeafNeedlesMatch = getLeafNeedlesMatch;
const NEEDLES = Symbol('needles');
const addNeedle = (input, needle) => merge(input, NEEDLES, needle);
const getNeedles = input => input[NEEDLES];
module.exports.getNeedles = getNeedles;
const INDEX = Symbol('index');
const setIndex = (input, index, readonly) => defineProperty(input, INDEX, index, readonly);
const getIndex = input => input[INDEX];
module.exports.getIndex = getIndex;
const WILDCARD = Symbol('wildcard');
const setWildcard = (input, wildcard) => defineProperty(input, WILDCARD, wildcard);
const getWildcard = input => input[WILDCARD];
module.exports.getWildcard = getWildcard;
const VALUES = Symbol('values');
const setValues = (input, entries) => defineProperty(input, VALUES, entries);
const getValues = input => input[VALUES];
module.exports.getValues = getValues;
module.exports.excludedBy = searches => Array.from(new Set([].concat(...searches.map(e => getLeafNeedlesExclude(e)))));
module.exports.matchedBy = searches => Array.from(new Set([].concat(...searches.map(e => getLeafNeedlesMatch(e)))));
module.exports.traversedBy = searches => Array.from(new Set([].concat(...searches.map(e => getNeedles(e)))));
module.exports.isLastLeafMatch = searches => {
let maxLeafIndex = Number.MIN_SAFE_INTEGER;
let maxLeaf = null;
for (let idx = 0, len = searches.length; idx < len; idx += 1) {
const s = searches[idx];
const index = getIndex(s);
if (index !== undefined && index > maxLeafIndex) {
maxLeafIndex = index;
maxLeaf = s;
}
}
return maxLeaf !== null && isMatch(maxLeaf);
};
const iterate = (tower, needle, tree, {
onAdd,
onFin
}) => {
const stack = [[[tower, null]]];
const wildcards = [];
let excluded = false;
iterator.iterate(tree, (type, wc) => {
if (type === 'RM') {
if (wc.excluded === true) {
excluded = false;
}
stack.pop();
wildcards.pop();
} else if (type === 'ADD') {
if (wc.excluded === true) {
if (excluded) {
throw new Error(`Redundant Exclusion: "${needle}"`);
}
excluded = true;
}
const toAdd = [];
const wcParent = wildcards[wildcards.length - 1];
stack[stack.length - 1].forEach(([cur]) => onAdd(cur, wc, wcParent, e => toAdd.push([e, cur])));
stack.push(toAdd);
wildcards.push(wc);
} else {
stack[stack.length - 1].filter(([cur]) => cur !== tower).forEach(([cur, parent]) => onFin(cur, wc[wc.length - 1], parent, excluded));
}
});
};
const applyNeedle = (tower, needle, tree, strict, ctx) => {
iterate(tower, needle, tree, {
onAdd: (cur, wc, wcParent, next) => {
addNeedle(cur, needle);
const redundantRecursion = wcParent !== undefined && wc.isStarRec && wc.value === wcParent.value;
if (redundantRecursion && strict) {
throw new Error(`Redundant Recursion: "${needle}"`);
}
if (!redundantRecursion) {
if (cur[wc] === undefined) {
const child = {}; // eslint-disable-next-line no-param-reassign
cur[wc] = child;
setWildcard(child, wc);
}
next(cur[wc]);
}
if (wc.isStarRec) {
next(cur);
}
},
onFin: (cur, wc, parent, excluded) => {
if (strict) {
if (wc.isSimpleStarRec) {
const unnecessary = Object.keys(parent).filter(k => !['**', ''].includes(k));
if (unnecessary.length !== 0) {
throw new Error(`Needle Target Invalidated: "${parent[unnecessary[0]][NEEDLES][0]}" by "${needle}"`);
}
}
}
addNeedle(cur, needle);
if (strict && LEAF_NEEDLES in cur) {
throw new Error(`Redundant Needle Target: "${cur[LEAF_NEEDLES][0]}" vs "${needle}"`);
}
addLeafNeedle(cur, needle, strict);
if (excluded) {
addLeafNeedleExclude(cur, needle);
} else {
addLeafNeedleMatch(cur, needle);
}
markLeaf(cur, !excluded, strict);
setIndex(cur, ctx.index, strict);
ctx.index += 1;
}
});
};
const finalizeTower = tower => {
const matches = [];
let lastDepth = -1;
traverser.traverse(tower, (type, obj, depth) => {
if (type === 'EXIT') {
const isUp = lastDepth === depth + 1;
if (isUp && matches[lastDepth] === true || isMatch(obj)) {
matches[depth] = true;
setHasMatches(obj);
}
if (isUp) {
matches[lastDepth] = false;
}
setValues(obj, Object.entries(obj).filter(([k]) => k !== '').map(e => e[1]).reverse());
lastDepth = depth;
}
});
};
module.exports.compile = (needles, strict = true, useArraySelector = true) => {
const tower = {};
const ctx = {
index: 0
};
for (let idx = 0; idx < needles.length; idx += 1) {
const needle = needles[idx];
const tree = [parser.parse(needle, useArraySelector)];
applyNeedle(tower, needle, tree, strict, ctx);
}
setWildcard(tower, new Wildcard('*', false));
finalizeTower(tower);
return tower;
};
@@ -0,0 +1,36 @@
"use strict";
module.exports = (kwargs, ctx) => {
if (ctx.rtn === 'context') {
return {
onMatch: () => {},
finish: () => ctx.context
};
}
if (ctx.rtn === 'bool') {
let result = false;
return {
onMatch: () => {
result = true;
},
finish: () => result
};
}
if (ctx.rtn === 'count') {
let result = 0;
return {
onMatch: () => {
result += 1;
},
finish: () => result
};
}
const result = [];
return {
onMatch: () => result.push(kwargs[ctx.rtn]),
finish: () => ctx.abort ? result[0] : result
};
};
@@ -0,0 +1,209 @@
"use strict";
const assert = require('assert');
const compiler = require('./compiler');
const Result = require('./find-result');
const {
toPath
} = require('../generic/helper');
const formatPath = (input, ctx) => ctx.joined ? toPath(input) : [...input];
module.exports = (haystack_, searches_, ctx) => {
const stack = [false, searches_, null, 0];
const path = [];
const parents = [];
let depth;
let segment;
let searches;
let isMatch;
let haystack = haystack_;
const kwargs = {
getKey: () => formatPath(path, ctx),
get key() {
return kwargs.getKey();
},
getValue: () => haystack,
get value() {
return kwargs.getValue();
},
getEntry: () => [formatPath(path, ctx), haystack],
get entry() {
return kwargs.getEntry();
},
getIsMatch: () => isMatch,
get isMatch() {
return kwargs.getIsMatch();
},
getMatchedBy: () => compiler.matchedBy(searches),
get matchedBy() {
return kwargs.getMatchedBy();
},
getExcludedBy: () => compiler.excludedBy(searches),
get excludedBy() {
return kwargs.getExcludedBy();
},
getTraversedBy: () => compiler.traversedBy(searches),
get traversedBy() {
return kwargs.getTraversedBy();
},
getProperty: () => path[path.length - 1],
get property() {
return kwargs.getProperty();
},
getParent: () => parents[parents.length - 1],
get parent() {
return kwargs.getParent();
},
getParents: () => [...parents].reverse(),
get parents() {
return kwargs.getParents();
},
getIsCircular: () => parents.includes(haystack),
get isCircular() {
return kwargs.getIsCircular();
},
getIsLeaf: () => !(haystack instanceof Object),
get isLeaf() {
return kwargs.getIsLeaf();
},
context: ctx.context
};
const result = Result(kwargs, ctx);
do {
depth = stack.pop();
segment = stack.pop();
searches = stack.pop();
isMatch = stack.pop();
const diff = path.length - depth;
for (let idx = 0; idx < diff; idx += 1) {
parents.pop();
path.pop();
}
if (diff === -1) {
parents.push(haystack);
path.push(segment);
haystack = haystack[segment];
} else if (segment !== null) {
path[path.length - 1] = segment;
haystack = parents[parents.length - 1][segment];
} else {
haystack = haystack_;
}
if (isMatch) {
if (ctx.filterFn === undefined || ctx.filterFn(kwargs) !== false) {
result.onMatch();
if (ctx.abort) {
return result.finish();
}
} // eslint-disable-next-line no-continue
continue;
}
if (!searches.some(s => compiler.hasMatches(s))) {
// eslint-disable-next-line no-continue
continue;
}
if (ctx.useArraySelector === false && Array.isArray(haystack)) {
if (ctx.breakFn === undefined || ctx.breakFn(kwargs) !== true) {
for (let idx = 0, len = haystack.length; idx < len; idx += 1) {
stack.push(false, searches, idx, depth + 1);
}
} // eslint-disable-next-line no-continue
continue;
}
const searchesIn = searches;
if (compiler.isLastLeafMatch(searches)) {
stack.push(true, searches, segment, depth);
isMatch = true;
}
if ('' in searches[0] && path.every(p => Number.isInteger(p))) {
assert(searches.length === 1);
stack.push(true, [searches[0]['']], segment, depth);
isMatch = true;
searches = [searches[0]['']];
}
if ((ctx.breakFn === undefined || ctx.breakFn(kwargs) !== true) && haystack instanceof Object) {
const isArray = Array.isArray(haystack);
const keys = Object.keys(haystack);
if (!isArray && ctx.compareFn) {
keys.sort(ctx.compareFn);
}
if (!ctx.reverse) {
keys.reverse();
}
for (let kIdx = 0, kLen = keys.length; kIdx < kLen; kIdx += 1) {
const key = keys[kIdx];
const searchesOut = [];
for (let sIdx = 0, sLen = searchesIn.length; sIdx !== sLen; sIdx += 1) {
const search = searchesIn[sIdx];
const wildcard = compiler.getWildcard(search);
if (wildcard.isRec && wildcard.anyMatch(key)) {
searchesOut.push(search);
}
const values = compiler.getValues(search);
let eIdx = values.length; // eslint-disable-next-line no-plusplus
while (eIdx--) {
const value = values[eIdx];
if (compiler.getWildcard(value).typeMatch(key, isArray)) {
searchesOut.push(value);
}
}
}
stack.push(false, searchesOut, isArray ? Number(key) : key, depth + 1);
}
}
} while (stack.length !== 0);
return result.finish();
};
@@ -0,0 +1,156 @@
"use strict";
const assert = require('assert');
const {
defineProperty
} = require('../generic/helper');
const {
Wildcard
} = require('./wildcard');
const IS_EXCLUDED = Symbol('is-excluded');
const markExcluded = input => defineProperty(input, IS_EXCLUDED, true);
const isExcluded = input => input[IS_EXCLUDED] === true;
const throwError = (msg, input, context = {}) => {
throw new Error(Object.entries(context).reduce((p, [k, v]) => `${p}, ${k} ${v}`, `${msg}: ${input}`));
};
const getSimple = arrOrSet => {
if (Array.isArray(arrOrSet)) {
return arrOrSet.length === 1 ? arrOrSet[0] : arrOrSet;
}
return arrOrSet.size === 1 ? arrOrSet.values().next().value : arrOrSet;
};
module.exports = input => {
let cResult = new Set();
let inArray = false;
let excludeNext = false;
let cursor = 0; // group related
const parentStack = [];
const newChild = asOr => {
if (isExcluded(cResult)) {
assert(excludeNext === false);
excludeNext = true;
}
parentStack.push(cResult);
cResult = asOr ? new Set() : [];
};
const finishChild = () => {
const parent = parentStack.pop();
const parentIsArray = Array.isArray(parent);
const child = getSimple(cResult);
if (!parentIsArray && child instanceof Set) {
child.forEach(e => parent.add(e));
} else {
parent[parentIsArray ? 'push' : 'add'](child);
}
cResult = parent;
};
newChild(false);
return {
setInArray: (flag, idx) => {
if (inArray === flag) {
throwError(inArray ? 'Bad Array Start' : 'Bad Array Terminator', input, {
char: idx
});
}
inArray = flag;
},
finishElement: (idx, {
err,
fins,
finReq = false
}) => {
const isFinished = cursor === idx;
if (isFinished && !fins.includes(input[idx - 1] || null)) {
throwError(err, input, {
char: idx
});
}
if (!isFinished) {
if (finReq) {
throwError(err, input, {
char: idx
});
}
const ele = input.slice(cursor, idx);
if (inArray && !(/^[?*+\d]+$/.test(ele) || ele.startsWith('(') && ele.endsWith(')'))) {
throwError('Bad Array Selector', input, {
selector: ele
});
}
cResult.push(new Wildcard(inArray ? `[${ele}]` : ele, excludeNext));
excludeNext = false;
}
cursor = idx + 1;
},
startExclusion: idx => {
if (excludeNext !== false) {
throwError('Redundant Exclusion', input, {
char: idx
});
}
excludeNext = true;
},
startGroup: () => {
newChild(true);
if (excludeNext) {
markExcluded(cResult);
excludeNext = false;
}
newChild(false);
},
newGroupElement: () => {
finishChild();
newChild(false);
},
finishGroup: idx => {
if (parentStack.length < 2) {
throwError('Unexpected Group Terminator', input, {
char: idx
});
}
finishChild();
finishChild();
},
finalizeResult: () => {
finishChild();
assert(excludeNext === false);
if (parentStack.length !== 0) {
throwError('Non Terminated Group', input);
}
if (inArray) {
throwError('Non Terminated Array', input);
}
return getSimple(cResult);
}
};
};
@@ -0,0 +1,135 @@
"use strict";
const {
Wildcard
} = require('./wildcard');
const Result = require('./parser-result');
const throwError = (msg, input, context = {}) => {
throw new Error(Object.entries(context).reduce((p, [k, v]) => `${p}, ${k} ${v}`, `${msg}: ${input}`));
};
module.exports.parse = (input, useArraySelector = true) => {
if (input === '') {
return new Wildcard('', false);
}
const result = Result(input);
const inputLength = input.length;
let escaped = false;
let bracketDepth = 0;
for (let idx = 0; idx < inputLength; idx += 1) {
const char = input[idx];
if (escaped === false && bracketDepth === 0) {
switch (char) {
case '.':
result.finishElement(idx, {
err: 'Bad Path Separator',
fins: [']', '}']
});
break;
case '[':
if (useArraySelector === false) {
throwError('Forbidden Array Selector', input, {
char: idx
});
}
result.finishElement(idx, {
err: 'Bad Array Start',
fins: [null, '!', '{', ',', '}', ']']
});
result.setInArray(true, idx);
break;
case ']':
result.finishElement(idx, {
err: 'Bad Array Terminator',
fins: ['}']
});
result.setInArray(false, idx);
break;
case '{':
result.finishElement(idx, {
err: 'Bad Group Start',
fins: [null, '!', '.', '[', '{', ','],
finReq: true
});
result.startGroup();
break;
case ',':
result.finishElement(idx, {
err: 'Bad Group Separator',
fins: [']', '}']
});
result.newGroupElement();
break;
case '}':
result.finishElement(idx, {
err: 'Bad Group Terminator',
fins: [']', '}']
});
result.finishGroup(idx);
break;
case '!':
result.finishElement(idx, {
err: 'Bad Exclusion',
fins: [null, '.', ',', '{', '['],
finReq: true
});
result.startExclusion(idx);
break;
default:
break;
}
}
if (escaped === false) {
switch (char) {
case '(':
bracketDepth += 1;
break;
case ')':
if (bracketDepth === 0) {
throwError('Unexpected Parentheses', input, {
char: idx
});
}
bracketDepth -= 1;
break;
default:
break;
}
}
escaped = char === '\\' ? !escaped : false;
}
if (escaped !== false) {
throwError('Dangling Escape', input, {
char: inputLength - 1
});
}
if (bracketDepth !== 0) {
throwError('Unterminated Parentheses', input);
}
result.finishElement(inputLength, {
err: 'Bad Terminator',
fins: [']', '}']
});
return result.finalizeResult();
};
@@ -0,0 +1,108 @@
"use strict";
const {
escapeRegex,
asRegex
} = require('../generic/helper');
const parseWildcard = str => {
let regex = '';
let escaped = false;
for (let idx = 0; idx < str.length; idx += 1) {
const char = str[idx];
if (!escaped && char === '\\') {
escaped = true;
} else if (!escaped && char === '*') {
regex += '.*';
} else if (!escaped && char === '+') {
regex += '.+';
} else if (!escaped && char === '?') {
regex += '.';
} else {
regex += escapeRegex(char);
escaped = false;
}
}
return new RegExp(`^${regex}$`);
};
module.exports.parseWildcard = parseWildcard;
const compileWildcard = str => {
if (['**', '++'].includes(str)) {
return asRegex('.*');
}
if ((str.startsWith('**(') || str.startsWith('++(')) && str.endsWith(')')) {
return asRegex(str.slice(3, -1));
}
if (str.startsWith('[(') && str.endsWith(')]')) {
return asRegex(str.slice(2, -2));
}
if (str.startsWith('(') && str.endsWith(')')) {
return asRegex(str.slice(1, -1));
}
if (str.startsWith('[') && str.endsWith(']')) {
return parseWildcard(str.slice(1, -1));
}
return parseWildcard(str);
};
class Wildcard extends String {
constructor(value, excluded) {
super(value);
this.value = value;
this.excluded = excluded;
this.regex = compileWildcard(value);
this.isArrayTarget = value.startsWith('[') && value.endsWith(']');
this.isSimpleStarRec = value === '**';
this.isSimplePlusRec = value === '++';
this.isSimpleRec = this.isSimpleStarRec || this.isSimplePlusRec;
this.isRegexStarRec = value.startsWith('**(') && value.endsWith(')');
this.isRegexPlusRec = value.startsWith('++(') && value.endsWith(')');
this.isRegexRec = this.isRegexStarRec || this.isRegexPlusRec;
this.isStarRec = this.isSimpleStarRec || this.isRegexStarRec;
this.isPlusRec = this.isSimplePlusRec || this.isRegexPlusRec;
this.isRec = this.isStarRec || this.isPlusRec;
this.isAnyArrayTarget = value === '[*]';
this.isAnyObjTarget = value === '*';
}
anyMatch(key) {
if (this.isSimpleRec) {
return true;
}
return this.regex.test(key);
}
typeMatch(key, isArray) {
if (this.isSimpleRec) {
return true;
}
if (isArray && this.isAnyArrayTarget) {
return true;
}
if (!isArray && this.isAnyObjTarget) {
return true;
}
if (isArray !== this.isArrayTarget && !this.isRec) {
return false;
}
return this.regex.test(key);
}
}
module.exports.Wildcard = Wildcard;
@@ -0,0 +1,24 @@
"use strict";
module.exports.defineProperty = (target, k, v, readonly = true) => Object.defineProperty(target, k, {
value: v,
writable: !readonly
});
const specialChars = /[?!,.*+[\](){}\\]/g;
const escape = input => input.replace(specialChars, '\\$&');
module.exports.escape = escape;
module.exports.escapeRegex = char => char.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
module.exports.asRegex = regexStr => {
try {
return new RegExp(regexStr);
} catch (e) {
throw new Error(`Invalid Regex: "${regexStr}"`);
}
};
module.exports.toPath = input => input.reduce((p, c) => `${p}${typeof c === 'number' ? `[${c}]` : `${p ? '.' : ''}${escape(c)}`}`, '');
@@ -0,0 +1,65 @@
"use strict";
const iterate = (tree, cb) => {
const stack = [tree];
const parent = [null];
const count = [];
const depth = [];
const path = [];
let idx = 0;
let inc = true;
while (idx !== -1) {
const e = stack[idx];
if (e instanceof Set) {
stack[idx] = [...e];
stack[idx].or = true;
} else if (Array.isArray(e)) {
if (e.or !== true) {
stack.splice(idx, 1, ...e);
parent.splice(idx, 1, ...new Array(e.length).fill(parent[idx]));
if (parent[idx] !== null) {
depth[parent[idx]] += e.length - 1;
}
} else {
if (count[idx] === undefined) {
count[idx] = 0;
depth[idx] = 0;
} else if (depth[idx] !== 0) {
stack.splice(idx + 1, depth[idx]);
parent.splice(idx + 1, depth[idx]);
depth[idx] = 0;
}
if (count[idx] < e.length) {
stack.splice(idx + 1, 0, e[count[idx]]);
parent.splice(idx + 1, 0, idx);
count[idx] = (count[idx] || 0) + 1;
depth[idx] += 1;
inc = true;
idx += 1;
} else {
count[idx] = 0;
idx -= 1;
}
}
} else if (inc === true) {
path.push(e);
cb('ADD', e);
if (idx === stack.length - 1) {
cb('FIN', path);
inc = false;
} else {
idx += 1;
}
} else {
cb('RM', path.pop());
idx -= 1;
}
}
};
module.exports.iterate = iterate;
@@ -0,0 +1,35 @@
"use strict";
module.exports.traverse = (obj, cb) => {
const stack = [obj];
const counts = [1];
let depth = 0;
let inc = true;
while (stack.length !== 0) {
if (inc === true) {
const cur = stack[stack.length - 1];
cb('ENTER', cur, depth);
const values = Object.values(cur);
if (values.length !== 0) {
stack.push(...values);
depth += 1;
counts[depth] = values.length;
} else {
inc = false;
}
} else {
const cur = stack.pop();
cb('EXIT', cur, depth);
counts[depth] -= 1;
if (counts[depth] === 0) {
counts.pop();
depth -= 1;
} else {
inc = true;
}
}
}
};
+46
View File
@@ -0,0 +1,46 @@
"use strict";
const assert = require('assert');
const compiler = require('./core/compiler');
const find = require('./core/find');
module.exports = (needles, opts = {}) => {
assert(Array.isArray(needles));
assert(opts instanceof Object && !Array.isArray(opts));
if (needles.length === 0) {
return (_, ctx) => ctx === undefined ? [] : ctx;
}
const ctx = {
filterFn: undefined,
breakFn: undefined,
compareFn: undefined,
reverse: true,
abort: false,
rtn: undefined,
joined: false,
useArraySelector: true,
strict: true,
...opts
};
assert(Object.keys(ctx).length === 9, 'Unexpected Option provided!');
assert(['function', 'undefined'].includes(typeof ctx.filterFn));
assert(['function', 'undefined'].includes(typeof ctx.breakFn));
assert(['function', 'undefined'].includes(typeof ctx.compareFn));
assert(typeof ctx.reverse === 'boolean');
assert(typeof ctx.abort === 'boolean');
assert([undefined, 'context', 'key', 'value', 'entry', 'property', 'parent', 'parents', 'bool', 'count'].includes(opts.rtn));
assert(typeof ctx.joined === 'boolean');
assert(typeof ctx.useArraySelector === 'boolean');
assert(typeof ctx.strict === 'boolean');
const search = compiler.compile(needles, ctx.strict, ctx.useArraySelector); // keep separate for performance
return (haystack, context) => find(haystack, [search], {
context,
...ctx,
rtn: ctx.rtn || (context === undefined ? 'key' : 'context')
});
};