| title | Migration Guide |
|---|---|
| description | Migrate from v3.x to v5.x or from native Array.filter() |
v5.6.1 introduces a breaking change to achieve truly zero production dependencies.
- Zod moved from
dependenciestopeerDependencies(optional) - @vercel/analytics, @vue-leaflet, leaflet moved to
devDependencies - Bundle size reduced by 81%: 65.63 KB → 12.02 KB
If you use validation features:
# Install zod as a peer dependency
npm install zod
# or
pnpm add zod
# or
yarn add zodIf you DON'T use validation features:
No action required! The library works without any dependencies.
Zod is only needed if you use these functions:
import { validateExpression, validateOptions } from '@mcabreradev/filter';
// These require zod to be installed
validateExpression(expression);
validateOptions(options);Core filtering works without zod:
import { filter } from '@mcabreradev/filter';
// This works without any dependencies ✅
filter(users, { age: { $gte: 18 } });- ✅ 81% smaller bundle (12.02 KB vs 65.63 KB)
- ✅ Truly zero dependencies for core functionality
- ✅ Optional validation - install only if needed
- ✅ Better tree-shaking in production builds
Version 5.0.0 represents a major refactoring of @mcabreradev/filter with improved type safety, modularity, and new features. This guide will help you migrate from v3.x to v5.0.0.
- v3.x: Node.js >= 18
- v5.0.0: Node.js >= 20
Action Required: Ensure your environment is running Node.js 20 or higher.
v5.0.0 is built with strict TypeScript mode enabled, which may reveal type issues in your consuming code.
Before (v3.x):
const result = filter(data, expression);After (v5.0.0):
import { filter } from '@mcabreradev/filter';
const result = filter<MyType>(data, expression);v5.0.0+ includes optional runtime validation using Zod. Invalid expressions throw descriptive errors when validation is used.
::: tip Optional Feature Runtime validation requires Zod to be installed as a peer dependency:
npm install zodCore filtering works without Zod! :::
Before (v3.x):
filter(data, undefined);After (v5.0.0) with validation:
import { validateExpression } from '@mcabreradev/filter';
validateExpression(undefined);This will throw: Error: Invalid filter expression: Expected string | number | boolean | null | function | object, received undefined
The project now standardizes on pnpm. While you can still use npm or yarn to install the package, if you're contributing to the project, you'll need pnpm.
v5.0.0 introduces an optional third parameter for configuration:
import { filter } from '@mcabreradev/filter';
const result = filter(data, expression, {
caseSensitive: true,
maxDepth: 5,
enableCache: true,
customComparator: (a, b) => a === b
});Available Options:
caseSensitive(boolean, default:false): Enable case-sensitive matchingmaxDepth(number, default:3): Maximum depth for nested object comparison (1-10)enableCache(boolean, default:false): Enable result cachingcustomComparator(function, optional): Custom comparison function
v5.0.0 provides better TypeScript support with explicit types:
import type { Expression, FilterOptions } from '@mcabreradev/filter';
const expression: Expression<User> = { name: 'John' };
const options: FilterOptions = { caseSensitive: true };
const result = filter<User>(users, expression, options);Expressions and options are now validated at runtime:
import { validateExpression, validateOptions } from '@mcabreradev/filter';
try {
const validated = validateExpression(expression);
const options = validateOptions({ maxDepth: 15 });
} catch (error) {
console.error(error.message);
}- Regex compilation is now cached
- Optional result caching via
enableCacheoption - Configurable maximum depth to prevent excessive recursion
v5.0.0 introduces powerful MongoDB-style operators for advanced filtering:
filter(products, { price: { $gt: 100 } });
filter(products, { price: { $gte: 100, $lte: 500 } });
filter(orders, {
date: {
$gte: new Date('2025-01-01'),
$lte: new Date('2025-12-31')
}
});
filter(products, { price: { $ne: 0 } });Available: $gt, $gte, $lt, $lte, $eq, $ne
filter(products, { category: { $in: ['Electronics', 'Books'] } });
filter(products, { category: { $nin: ['Furniture'] } });
filter(products, { tags: { $contains: 'sale' } });
filter(products, { tags: { $size: 3 } });Available: $in, $nin, $contains, $size
filter(users, { name: { $startsWith: 'Al' } });
filter(users, { email: { $endsWith: '@company.com' } });
filter(products, { description: { $contains: 'wireless' } });Available: $startsWith, $endsWith, $contains
filter(products, {
price: { $gte: 100, $lte: 500 },
category: { $in: ['Electronics', 'Accessories'] },
name: { $startsWith: 'Pro' }
});See: Operators Guide for comprehensive operator documentation.
Ensure you're running Node.js 20 or higher:
node --versionUsing npm:
npm install @mcabreradev/filter@5.0.0Using yarn:
yarn add @mcabreradev/filter@5.0.0Using pnpm:
pnpm add @mcabreradev/filter@5.0.0The default export remains the same, but named exports are recommended:
Before (v3.x):
import filter from '@mcabreradev/filter';After (v5.0.0) - Both work:
import filter from '@mcabreradev/filter';
import { filter } from '@mcabreradev/filter';For better type safety, add explicit type parameters:
interface User {
name: string;
age: number;
}
const users: User[] = [
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 }
];
const result = filter<User>(users, { age: 30 });Wrap filter calls in try-catch if you're passing dynamic expressions:
try {
const result = filter(data, userProvidedExpression);
} catch (error) {
console.error('Invalid filter expression:', error.message);
}Take advantage of new configuration options for better control:
const result = filter(data, '%berlin%', {
caseSensitive: true,
maxDepth: 2
});No changes required - Works exactly the same:
const result = filter(customers, 'Berlin');No changes required - Works exactly the same:
const result = filter(customers, '%erlin');
const result2 = filter(customers, '_erlin');No changes required - Works exactly the same:
const result = filter(customers, { city: 'Berlin' });No changes required - Works exactly the same:
const result = filter(customers, (item) => item.age > 18);v5.0.0 feature:
const result = filter(
[{ name: 'BERLIN' }, { name: 'berlin' }],
'berlin',
{ caseSensitive: true }
);Before (v3.x) - Using predicates:
const result = filter(products, (p) => p.price >= 100 && p.price <= 500);After (v5.0.0) - Using operators (recommended):
const result = filter(products, {
price: { $gte: 100, $lte: 500 }
});Benefits of operators:
- More declarative and readable
- Can be serialized to JSON
- Better TypeScript type safety
- Runtime validation
Before (v3.x):
const result = filter(products, (p) =>
p.price < 500 &&
['Electronics', 'Furniture'].includes(p.category) &&
p.name.startsWith('Pro')
);After (v5.0.0):
const result = filter(products, {
price: { $lt: 500 },
category: { $in: ['Electronics', 'Furniture'] },
name: { $startsWith: 'Pro' }
});After migrating, run your test suite to ensure everything works as expected:
npm testCheck for:
- TypeScript compilation errors
- Runtime validation errors
- Changed filter behavior (if you relied on edge cases)
Cause: You're passing an invalid expression (e.g., undefined).
Solution: Ensure your expression is a string, number, boolean, null, object, or function:
if (expression !== undefined) {
const result = filter(data, expression);
}Cause: maxDepth must be between 1 and 10.
Solution: Use a valid maxDepth value:
const result = filter(data, expression, { maxDepth: 5 });Cause: Strict mode reveals previously hidden type issues.
Solution: Add explicit types to your code:
const expression: Expression<MyType> = { ... };
const result = filter<MyType>(data, expression);If you encounter issues during migration:
- Check the README for updated examples
- Review the TypeScript types for API reference
- Open an issue on GitHub
If you need to rollback to v3.x:
npm install @mcabreradev/filter@3.1.3Note: v3.x will continue to receive critical bug fixes for 6 months after v5.0.0 release.