UNPKG

@furystack/core

Version:
132 lines 6.29 kB
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