@furystack/core
Version:
Core FuryStack package
132 lines • 6.29 kB
JavaScript
import { isLogicalOperator, isOperator } from './models/physical-store.js';
const escapeRegexMeta = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const evaluateLike = (value, likeString) => {
const likeRegex = `^${likeString.split('%').map(escapeRegexMeta).join('.*')}$`;
return value.match(new RegExp(likeRegex, 'i'));
};
/**
* In-memory evaluation of a {@link FilterType} expression. Used by
* {@link InMemoryStore.find} and any consumer that needs to filter without
* round-tripping a real store. Throws on unknown operators (defensive — the
* type system rejects them at compile time).
*
* Supported field operators: `$eq`, `$ne`, `$in`, `$nin`, `$gt`, `$gte`,
* `$lt`, `$lte`, `$startsWith`, `$endsWith`, `$like` (`%` wildcard, case
* insensitive), `$regex`. Logical: `$and`, `$or`.
*/
export function filterItems(values, filter) {
if (!filter) {
return values;
}
return values.filter((item) => {
const filterRecord = filter;
const itemRecord = item;
for (const key in filterRecord) {
if (isLogicalOperator(key)) {
const filterValue = filterRecord[key];
switch (key) {
case '$and':
if (filterValue.some((v) => !filterItems([item], v).length)) {
return false;
}
break;
case '$or':
if (filterValue.some((v) => filterItems([item], v).length)) {
break;
}
return false;
default:
throw new Error(`The logical operation '${key}' is not a valid operation`);
}
}
else {
const fieldFilter = filterRecord[key];
if (typeof fieldFilter === 'object' && fieldFilter !== null) {
for (const filterKey in fieldFilter) {
if (isOperator(filterKey)) {
const itemValue = itemRecord[key];
const filterValue = fieldFilter[filterKey];
switch (filterKey) {
case '$eq':
if (filterValue !== itemValue) {
return false;
}
break;
case '$ne':
if (filterValue === itemValue) {
return false;
}
break;
case '$in':
if (!filterValue.includes(itemValue)) {
return false;
}
break;
case '$nin':
if (filterValue.includes(itemValue)) {
return false;
}
break;
case '$lt':
if (itemValue < filterValue) {
break;
}
return false;
case '$lte':
if (itemValue <= filterValue) {
break;
}
return false;
case '$gt':
if (itemValue > filterValue) {
break;
}
return false;
case '$gte':
if (itemValue >= filterValue) {
break;
}
return false;
case '$regex':
try {
if (!new RegExp(filterValue).test(String(itemValue))) {
return false;
}
}
catch (e) {
throw new Error(`Invalid regular expression for $regex filter on field '${key}': ${e.message}`, { cause: e });
}
break;
case '$startsWith':
if (!itemValue.startsWith(filterValue)) {
return false;
}
break;
case '$endsWith':
if (!itemValue.endsWith(filterValue)) {
return false;
}
break;
case '$like':
if (!evaluateLike(itemValue, filterValue)) {
return false;
}
break;
default:
throw new Error(`The expression (${filterKey}) is not a supported filter operation`);
}
}
else {
throw new Error(`The filter key '${filterKey}' is not a valid operation`);
}
}
}
else {
throw new Error(`The filter has to be an object, got ${typeof fieldFilter} for field '${key}'`);
}
}
}
return true;
});
}
//# sourceMappingURL=filter-items.js.map