# @mcabreradev/filter

> **Filter arrays like a pro.** A powerful, SQL-like array filtering library for TypeScript with advanced pattern matching, MongoDB-style operators, deep object comparison, geospatial queries, and zero dependencies.

<p align="center">
  <a aria-label="NPM version" href="https://www.npmjs.com/package/@mcabreradev/filter">
    <img alt="" src="https://img.shields.io/npm/v/@mcabreradev/filter.svg?style=for-the-badge&labelColor=0869B8">
  </a>
  <a aria-label="License" href="https://github.com/mcabreradev/filter/blob/main/LICENSE.md">
    <img alt="" src="https://img.shields.io/npm/l/@mcabreradev/filter.svg?style=for-the-badge&labelColor=579805">
  </a>
  <a aria-label="Bundle Size" href="https://bundlephobia.com/package/@mcabreradev/filter">
    <img alt="" src="https://img.shields.io/bundlephobia/minzip/@mcabreradev/filter?style=for-the-badge&labelColor=orange">
  </a>
  <a aria-label="Tree Shaking" href="https://bundlephobia.com/package/@mcabreradev/filter">
    <img alt="" src="https://img.shields.io/badge/tree--shaking-friendly-success?style=for-the-badge&labelColor=green">
  </a>
  <a aria-label="TypeScript" href="#">
    <img alt="" src="https://img.shields.io/badge/TypeScript-Ready-blue.svg?style=for-the-badge&labelColor=blue">
  </a>
  <a aria-label="Zero Dependencies" href="https://bundlephobia.com/package/@mcabreradev/filter">
    <img alt="" src="https://img.shields.io/badge/dependencies-0-purple?style=for-the-badge&labelColor=purple">
  </a>
</p>

<p align="center">
  <a href="#quick-start">Quick Start</a> •
  <a href="#why-youll-love-it">Why You'll Love It</a> •
  <a href="#examples">Examples</a> •
  <a href="https://mcabreradev-filter.vercel.app/playground/">Playground</a> •
  <a href="./docs">Documentation</a>
</p>

---

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

## Table of Contents

- [The Problem](#the-problem)
- [Quick Start](#quick-start)
  - [Install](#install)
  - [Your First Filter](#your-first-filter)
- [Why You'll Love It](#why-youll-love-it)
  - [🚀 **Blazing Fast**](#-blazing-fast)
  - [🎯 **Developer Friendly**](#-developer-friendly)
  - [🔧 **Incredibly Flexible**](#-incredibly-flexible)
  - [📦 **Production Ready**](#-production-ready)
  - [🪶 **Ultra Lightweight**](#-ultra-lightweight)
  - [🔒 **Type-Safe by Default**](#-type-safe-by-default)
  - [🎨 **Framework Agnostic**](#-framework-agnostic)
  - [📊 **Handles Big Data**](#-handles-big-data)
- [Examples](#examples)
  - [Basic Filtering](#basic-filtering)
  - [MongoDB-Style Operators](#mongodb-style-operators)
  - [Array OR Syntax (Intuitive!)](#array-or-syntax-intuitive)
  - [Geospatial Queries](#geospatial-queries)
  - [Datetime Filtering](#datetime-filtering)
  - [Performance Optimization](#performance-optimization)
  - [Real-World: E-commerce Search](#real-world-e-commerce-search)
- [Framework Integrations](#framework-integrations)
  - [React](#react)
  - [Vue](#vue)
  - [Svelte](#svelte)
  - [Angular](#angular)
  - [SolidJS](#solidjs)
  - [Preact](#preact)
- [Core Features](#core-features)
  - [Supported Operators](#supported-operators)
  - [TypeScript Support](#typescript-support)
  - [Configuration Options](#configuration-options)
- [Advanced Features](#advanced-features)
  - [Lazy Evaluation](#lazy-evaluation)
  - [Memoization & Caching](#memoization--caching)
  - [Visual Debugging](#visual-debugging)
- [Documentation](#documentation)
  - [📖 Complete Guides](#-complete-guides)
  - [🎯 Quick Links](#-quick-links)
- [Performance](#performance)
- [Bundle Size](#bundle-size)
- [Browser Support](#browser-support)
- [Migration from v3.x](#migration-from-v3x)
- [Changelog](#changelog)
  - [v5.9.2 (Current)](#v592-current)
  - [v5.9.1](#v591)
  - [v5.8.2](#v582)
  - [v5.8.0](#v580)
  - [v5.7.0](#v570)
  - [v5.6.0](#v560)
  - [v5.5.0](#v550)
- [Contributing](#contributing)
- [License](#license)
- [Support](#support)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

---

## The Problem

**Tired of writing complex filter logic?** Stop wrestling with nested `Array.filter()` chains and verbose conditionals. Write clean, declarative filters that read like queries.

**Before — the usual mess:**

```typescript
const results = data.filter(
  (item) =>
    item.age >= 18 &&
    item.status === 'active' &&
    (item.role === 'admin' || item.role === 'moderator') &&
    item.email.endsWith('@company.com') &&
    item.createdAt >= thirtyDaysAgo,
);
```

**After — clean and declarative:**

```typescript
const results = filter(data, {
  age: { $gte: 18 },
  status: 'active',
  role: ['admin', 'moderator'],
  email: { $endsWith: '@company.com' },
  createdAt: { $gte: thirtyDaysAgo },
});
```

**Same result. 70% less code. 100% more readable.**

---

## Quick Start

### Install

```bash
npm install @mcabreradev/filter
# or
pnpm add @mcabreradev/filter
# or
yarn add @mcabreradev/filter
```

**Requirements:** Node.js >= 20, TypeScript 5.0+ (optional)

### Your First Filter

```typescript
import { filter } from '@mcabreradev/filter';

const users = [
  { name: 'Alice', age: 30, city: 'Berlin', active: true },
  { name: 'Bob', age: 25, city: 'London', active: false },
  { name: 'Charlie', age: 35, city: 'Berlin', active: true },
];

// Simple string search — scans all fields
const berlinUsers = filter(users, 'Berlin');
// → [{ name: 'Alice', ... }, { name: 'Charlie', ... }]

// Object matching — AND logic across fields
const activeBerlinUsers = filter(users, { city: 'Berlin', active: true });
// → [{ name: 'Alice', ... }]

// MongoDB-style operators
const adults = filter(users, { age: { $gte: 18 } });
// → All users

// SQL-like wildcards
const startsWithAl = filter(users, 'Al%');
// → [{ name: 'Alice', ... }]
```

**🎮 [Try it in the Playground →](https://mcabreradev-filter.vercel.app/playground/)**

---

## Why You'll Love It

### 🚀 **Blazing Fast**

- **530x faster** on repeated queries with optional LRU caching
- **500x faster** with lazy evaluation for large datasets
- Compiled predicates and regex patterns cached automatically

### 🎯 **Developer Friendly**

- Intuitive API — reads like English
- SQL-like wildcards (`%`, `_`) you already know
- Full TypeScript generics with intelligent autocomplete

### 🔧 **Incredibly Flexible**

- Four filtering strategies: strings, objects, operators, predicates
- Combine them seamlessly in a single expression
- Works with any data shape — flat, nested, arrays

### 📦 **Production Ready**

- **1,004+ tests** ensuring bulletproof reliability
- Zero runtime dependencies (only Zod for optional validation)
- Battle-tested in production applications
- MIT licensed

### 🪶 **Ultra Lightweight**

- Full package: **12KB gzipped**
- Core only: **8.4KB gzipped**
- Zero mandatory dependencies
- Tree-shakeable — only pay for what you use

### 🔒 **Type-Safe by Default**

- Built with strict TypeScript
- Catch errors at compile time, not runtime
- Full IntelliSense for operators based on field types

### 🎨 **Framework Agnostic**

- First-class hooks: React, Vue, Svelte, Angular, SolidJS, Preact
- Debounced search, pagination, and reactive state out of the box
- SSR compatible: Next.js, Nuxt, SvelteKit

### 📊 **Handles Big Data**

- Generator-based lazy evaluation for millions of records
- Early exit — stop processing when you have enough results
- LRU caches with TTL prevent memory leaks in long-running apps

---

## Examples

### Basic Filtering

```typescript
// String matching — searches all string properties
filter(products, 'Laptop');

// Exact field matching — AND logic
filter(products, { category: 'Electronics', price: { $lt: 1000 } });

// SQL wildcard patterns
filter(users, '%alice%'); // contains 'alice'
filter(users, 'Al%'); // starts with 'Al'
filter(users, '%son'); // ends with 'son'
filter(users, 'J_hn'); // single-char wildcard

// Predicate functions — full control
filter(users, (u) => u.score > 90 && u.verified);
```

### MongoDB-Style Operators

```typescript
// Comparison
filter(products, { price: { $gte: 100, $lte: 500 } });
filter(products, { rating: { $gt: 4 }, stock: { $ne: 0 } });

// Array membership
filter(products, { category: { $in: ['Electronics', 'Books'] } });
filter(products, { tags: { $contains: 'sale' } });
filter(products, { sizes: { $size: 3 } });

// String matching
filter(users, {
  email: { $endsWith: '@company.com' },
  name: { $startsWith: 'John' },
  bio: { $regex: /developer/i },
});

// Logical combinators
filter(products, {
  $and: [{ inStock: true }, { $or: [{ rating: { $gte: 4.5 } }, { price: { $lt: 50 } }] }],
});

// Negate with $not
filter(users, { role: { $not: 'banned' } });
```

### Array OR Syntax (Intuitive!)

```typescript
// Pass an array → automatic OR logic, no $in needed
filter(products, { category: ['Electronics', 'Books'] });
// Same as: { category: { $in: ['Electronics', 'Books'] } }

// Combine across fields
filter(users, {
  city: ['Berlin', 'Paris', 'London'],
  role: ['admin', 'moderator'],
});
```

### Geospatial Queries

```typescript
import { filter, type GeoPoint } from '@mcabreradev/filter';

const userLocation: GeoPoint = { lat: 52.52, lng: 13.405 };

// Find restaurants within 5km rated 4.5+
filter(restaurants, {
  location: { $near: { center: userLocation, maxDistanceMeters: 5000 } },
  rating: { $gte: 4.5 },
});

// Bounding box search
filter(places, {
  location: {
    $geoBox: {
      topLeft: { lat: 53.0, lng: 13.0 },
      bottomRight: { lat: 52.0, lng: 14.0 },
    },
  },
});
```

### Datetime Filtering

```typescript
// Events in next 7 days
filter(events, { date: { $upcoming: { days: 7 } } });

// Recent activity (last 24 hours)
filter(logs, { createdAt: { $recent: { hours: 24 } } });

// Weekday events during business hours
filter(events, {
  date: { $dayOfWeek: [1, 2, 3, 4, 5] }, // Mon–Fri
  startTime: { $timeOfDay: { start: 9, end: 17 } }, // 9am–5pm
});

// Users of age 18–65
filter(users, { birthDate: { $age: { min: 18, max: 65 } } });

// Weekend-only events
filter(events, { date: { $isWeekend: true } });

// Events before a deadline
filter(tasks, { dueDate: { $isBefore: new Date('2025-12-31') } });
```

### Performance Optimization

```typescript
// LRU caching — 530x faster on repeat queries
const results = filter(largeDataset, expression, {
  enableCache: true,
  orderBy: { field: 'price', direction: 'desc' },
  limit: 100,
});

// Lazy evaluation — process millions of records without loading all into memory
import { filterFirst, filterExists, filterCount, filterLazy } from '@mcabreradev/filter';

const first10 = filterFirst(millionRecords, { premium: true }, 10);
const hasAdmin = filterExists(users, { role: 'admin' }); // exits on first match
const activeCount = filterCount(users, { active: true }); // no array allocated
```

### Real-World: E-commerce Search

```typescript
interface Product {
  id: number;
  name: string;
  price: number;
  category: string;
  brand: string;
  rating: number;
  inStock: boolean;
  tags: string[];
}

// Affordable, highly-rated electronics in stock
const results = filter<Product>(products, {
  category: 'Electronics',
  price: { $lte: 1000 },
  rating: { $gte: 4.5 },
  inStock: true,
});

// Full-text search with brand filter
const searchResults = filter<Product>(products, {
  name: { $contains: 'laptop' },
  brand: ['Apple', 'Dell', 'HP'],
  price: { $gte: 500, $lte: 2000 },
});

// Sorted and paginated results
const page1 = filter<Product>(
  products,
  { category: 'Electronics', inStock: true },
  {
    orderBy: [
      { field: 'price', direction: 'asc' },
      { field: 'rating', direction: 'desc' },
    ],
    limit: 20,
  },
);
```

---

## Framework Integrations

First-class hooks and composables — reactive, debounced, paginated, ready to drop in:

### React

```typescript
import { useFilter, useDebouncedFilter, usePaginatedFilter } from '@mcabreradev/filter/react';

function UserList() {
  const { filtered, isFiltering } = useFilter(users, { active: true });

  return (
    <ul>
      {filtered.map(u => <li key={u.id}>{u.name}</li>)}
    </ul>
  );
}

// Debounced live search
function SearchBox() {
  const [query, setQuery] = useState('');
  const { filtered, isPending } = useDebouncedFilter(users, query, { delay: 300 });

  return (
    <>
      <input onChange={e => setQuery(e.target.value)} />
      {isPending ? <Spinner /> : filtered.map(u => <User key={u.id} user={u} />)}
    </>
  );
}
```

### Vue

```vue
<script setup lang="ts">
import { ref } from 'vue';
import { useFilter } from '@mcabreradev/filter/vue';

const expression = ref({ active: true });
const { filtered, isFiltering } = useFilter(users, expression);
</script>

<template>
  <ul>
    <li v-for="user in filtered" :key="user.id">{{ user.name }}</li>
  </ul>
</template>
```

### Svelte

```svelte
<script lang="ts">
  import { writable } from 'svelte/store';
  import { useFilter } from '@mcabreradev/filter/svelte';

  const expression = writable({ active: true });
  const { filtered } = useFilter(users, expression);
</script>

{#each $filtered as user}
  <p>{user.name}</p>
{/each}
```

### Angular

```typescript
import { FilterService } from '@mcabreradev/filter/angular';

@Component({
  providers: [FilterService],
  template: `
    @for (user of filterService.filtered(); track user.id) {
      <div>{{ user.name }}</div>
    }
  `,
})
export class UserListComponent {
  filterService = inject(FilterService<User>);
}
```

### SolidJS

```tsx
import { useFilter } from '@mcabreradev/filter/solidjs';

function UserList() {
  const { filtered } = useFilter(
    () => users,
    () => ({ active: true }),
  );
  return <For each={filtered()}>{(u) => <div>{u.name}</div>}</For>;
}
```

### Preact

```tsx
import { useFilter } from '@mcabreradev/filter/preact';

function UserList() {
  const { filtered } = useFilter(users, { active: true });
  return (
    <div>
      {filtered.map((u) => (
        <div key={u.id}>{u.name}</div>
      ))}
    </div>
  );
}
```

**Every integration includes:**

- ✅ Full TypeScript generics
- ✅ Debounced search hook with `isPending` state
- ✅ Pagination hook with `nextPage`, `prevPage`, `goToPage`
- ✅ SSR compatible
- ✅ 100% test coverage

📖 **[Complete Framework Guide →](./docs/frameworks/index.md)**

---

## Core Features

### Supported Operators

| Category       | Operators                                                                                               |
| -------------- | ------------------------------------------------------------------------------------------------------- |
| **Comparison** | `$gt` `$gte` `$lt` `$lte` `$eq` `$ne`                                                                   |
| **Array**      | `$in` `$nin` `$contains` `$size`                                                                        |
| **String**     | `$startsWith` `$endsWith` `$contains` `$regex` `$match`                                                 |
| **Logical**    | `$and` `$or` `$not`                                                                                     |
| **Geospatial** | `$near` `$geoBox` `$geoPolygon`                                                                         |
| **Datetime**   | `$recent` `$upcoming` `$dayOfWeek` `$timeOfDay` `$age` `$isWeekday` `$isWeekend` `$isBefore` `$isAfter` |

> 18+ operators covering every filtering scenario you'll encounter.

### TypeScript Support

Full type safety — autocomplete shows only valid operators for each field type:

```typescript
interface Product {
  name: string;
  price: number;
  tags: string[];
}

filter<Product>(products, {
  price: { $gte: 100 }, // ✅ number operators
  name: { $contains: '' }, // ✅ string operators
  tags: { $size: 3 }, // ✅ array operators
  price: { $contains: '' }, // ❌ TypeScript error — string op on number field
});
```

### Configuration Options

```typescript
filter(data, expression, {
  caseSensitive: false, // default: false
  maxDepth: 3, // nested object traversal depth (1–10)
  enableCache: true, // LRU result caching (530x speedup)
  orderBy: 'price', // sort field or array of fields
  limit: 10, // cap result count
  debug: true, // print expression tree to console
  verbose: true, // detailed per-item evaluation logs
  showTimings: true, // execution time per operator
  enablePerformanceMonitoring: true, // collect performance metrics
});
```

---

## Advanced Features

### Lazy Evaluation

Process large datasets without loading everything into memory:

```typescript
import { filterLazy, filterFirst, filterExists, filterCount } from '@mcabreradev/filter';

// Generator — pull items one by one, exit any time
const lazy = filterLazy(millionRecords, { active: true });
for (const item of lazy) {
  process(item);
  if (shouldStop) break; // ← zero wasted work
}

// Grab first N matches
const top10 = filterFirst(users, { premium: true }, 10);

// Check existence — exits on first match
const hasBanned = filterExists(users, { role: 'banned' });

// Count matches — no array allocated
const total = filterCount(orders, { status: 'pending' });
```

| Scenario                | Array.filter | filterLazy / filterFirst |
| ----------------------- | ------------ | ------------------------ |
| First match in 1M items | ~50ms        | **~0.1ms**               |
| Memory for 1M items     | ~80MB        | **~0KB**                 |
| Early exit              | ❌           | ✅                       |

📖 **[Lazy Evaluation Guide →](./docs/guide/lazy-evaluation.md)**

### Memoization & Caching

Three-tier LRU caching strategy with automatic TTL eviction:

```typescript
// First call — compiles predicates, runs filter, stores result
const results = filter(largeDataset, { age: { $gte: 18 } }, { enableCache: true });

// Subsequent calls — returns cached result instantly
const same = filter(largeDataset, { age: { $gte: 18 } }, { enableCache: true });
```

| Scenario                | Without Cache | With Cache | Speedup   |
| ----------------------- | ------------- | ---------- | --------- |
| Simple query, 10K items | 5.3ms         | 0.01ms     | **530x**  |
| Regex pattern           | 12.1ms        | 0.02ms     | **605x**  |
| Complex nested query    | 15.2ms        | 0.01ms     | **1520x** |

Caches are bounded (LRU, max 500 entries each) and auto-expire after 5 minutes — safe for long-running servers.

📖 **[Memoization Guide →](./docs/guide/memoization.md)**

### Visual Debugging

Built-in tree visualization for understanding filter behavior:

```typescript
filter(users, { city: 'Berlin', age: { $gte: 18 } }, { debug: true });

// Console output:
// ┌─ Filter Debug Tree
// │  Expression: {"city":"Berlin","age":{"$gte":18}}
// │  Matched: 3/10 items (30.0%)
// │  Execution time: 0.42ms
// ├─ ✓ city = "Berlin"         [3 matches]
// └─ ✓ age >= 18               [3 matches]
```

📖 **[Debug Guide →](./docs/guide/debugging.md)**

---

## Documentation

### 📖 Complete Guides

- **[Getting Started](./docs/guide/getting-started.md)** — Installation and first steps
- **[All Operators](./docs/guide/operators.md)** — Complete operator reference
- **[Geospatial Queries](./docs/guide/geospatial-operators.md)** — Location-based filtering
- **[Datetime Operators](./docs/guide/datetime-operators.md)** — Temporal filtering
- **[Framework Integrations](./docs/frameworks/index.md)** — React, Vue, Svelte, Angular, SolidJS, Preact
- **[Lazy Evaluation](./docs/guide/lazy-evaluation.md)** — Efficient large dataset processing
- **[Memoization & Caching](./docs/guide/memoization.md)** — Performance optimization
- **[Visual Debugging](./docs/guide/debugging.md)** — Debug mode and tree visualization

### 🎯 Quick Links

- [Interactive Playground](https://mcabreradev-filter.vercel.app/playground/) 🎮
- [API Reference](./docs/api/reference.md)
- [Examples](./examples/)
- [Migration Guide](./docs/advanced/migration.md)
- [Performance Benchmarks](./docs/advanced/performance-benchmarks.md)
- [FAQ](./docs/guide/faq.md)

---

## Performance

| Technique             | Benefit                                             |
| --------------------- | --------------------------------------------------- |
| Early-exit operators  | Skip remaining items on first mismatch              |
| LRU result cache      | 530x–1520x speedup on repeated queries              |
| LRU predicate cache   | Compiled predicates reused across calls             |
| LRU regex cache       | Compiled patterns reused, bounded to 500 entries    |
| Lazy generators       | 500x faster when you don't need all results         |
| Absolute TTL eviction | Stale entries removed after 5 min — no memory leaks |

```typescript
// Enable all optimizations at once
filter(data, expression, { enableCache: true });

// Maximum efficiency for large datasets
const first100 = filterFirst(millionRecords, { active: true }, 100);
```

---

## Bundle Size

| Import          | Size (gzipped) | Tree-Shakeable |
| --------------- | -------------- | -------------- |
| Full            | 12 KB          | ✅             |
| Core only       | 8.4 KB         | ✅             |
| React hooks     | 9.2 KB         | ✅             |
| Lazy evaluation | 5.4 KB         | ✅             |

---

## Browser Support

Works in all modern browsers and Node.js:

- **Node.js:** >= 20
- **Browsers:** Chrome, Firefox, Safari, Edge (latest versions)
- **TypeScript:** >= 5.0
- **Module Systems:** ESM, CommonJS

---

## Migration from v3.x

**Good news:** v5.x is **100% backward compatible**. All v3.x code continues to work.

```typescript
// ✅ All v3.x syntax still works
filter(data, 'string');
filter(data, { prop: 'value' });
filter(data, (item) => true);
filter(data, '%pattern%');

// ✅ New in v5.x
filter(data, { age: { $gte: 18 } });
filter(data, expression, { enableCache: true, limit: 50 });
```

📖 **[Migration Guide →](./docs/advanced/migration.md)**

---

## Changelog

### v5.9.2 (Current)

- 🧹 **Refactor**: Removed Svelte integration exports, dependencies, and related docs from the package.
- 🔧 **CI/CD**: Hardened npm publish workflow with safer version resolution and improved release handling.
- 📦 **Release Reliability**: Synced lockfile with dependency changes to avoid CI failures with `--frozen-lockfile`.
- ✅ **Publishing**: Improved release pipeline behavior so versioning and GitHub Releases run more consistently.

### v5.9.1

- 🚀 **Release**: Published patch release with workflow and release-process fixes.
- 🏷️ **Versioning**: Stabilized tag/version flow to avoid collisions with existing release tags.

### v5.8.2

- 🐛 **Bug Fix**: Wildcard regex now correctly escapes all special characters (`.`, `+`, `*`, `?`, `(`, `[`, `^`, etc.) — patterns like `%.txt` or `a.b%` no longer silently break
- 🐛 **Bug Fix**: `$timeOfDay` with `start > end` (e.g. `{ start: 22, end: 5 }`) now correctly fails validation instead of silently never matching
- 🐛 **Bug Fix**: React `useDebouncedFilter` now reacts to `delay` prop changes — previously the initial delay was frozen for the hook's lifetime
- 🔒 **Validation**: `limit` option now validated by schema — negative or non-integer values throw a clear configuration error
- 🔒 **Validation**: `debug`, `verbose`, `showTimings`, `colorize`, `enablePerformanceMonitoring` options now validated by schema
- ⚡ **Performance**: Pattern-matching regex cache now delegates to the shared LRU `MemoizationManager` — the previously unbounded `Map` is gone
- ⚡ **Performance**: LRU cache TTL is now absolute (expire 5 min after creation) instead of sliding — entries can no longer live forever under heavy load
- 🧹 **Code Quality**: Svelte pagination replaced `subscribe()()` anti-pattern with idiomatic `get()` from `svelte/store`
- ✅ **Tests**: 1,004+ tests — added coverage for every bug fixed in this release

### v5.8.0

- 🎨 **New Framework Integrations**: Angular, SolidJS, and Preact support
- 🔢 **Limit Option**: New `limit` configuration to restrict result count
- 📊 **OrderBy Option**: Sort filtered results by field(s) in ascending or descending order
- ✅ 993+ tests with comprehensive coverage

### v5.7.0

- 🅰️ **Angular**: Services and Pipes with Signals support
- 🔷 **SolidJS**: Signal-based reactive hooks
- ⚡ **Preact**: Lightweight hooks API

### v5.6.0

- 🌍 **Geospatial Operators**: Location-based filtering with `$near`, `$geoBox`, `$geoPolygon`
- 📅 **Datetime Operators**: Temporal filtering with `$recent`, `$upcoming`, `$dayOfWeek`, `$age`

### v5.5.0

- 🎨 **Array OR Syntax**: Intuitive array-based OR filtering
- 🐛 **Visual Debugging**: Built-in debug mode with expression tree visualization
- 🎮 **Interactive Playground**: Online playground for testing filters

📖 **[Full Changelog →](./docs/project/changelog.md)**

---

## Contributing

We welcome contributions! Please read our [Contributing Guide](./CONTRIBUTING.md) for details.

**Ways to Contribute:**

- Report bugs or request features via [GitHub Issues](https://github.com/mcabreradev/filter/issues)
- Submit pull requests with bug fixes or new features
- Improve documentation
- Share your use cases and examples

---

## License

MIT License — see [LICENSE.md](./LICENSE.md) for details.

Copyright (c) 2025 Miguelangel Cabrera

---

## Support

- 📖 [Complete Documentation](./docs)
- 💬 [GitHub Discussions](https://github.com/mcabreradev/filter/discussions)
- 🐛 [Issue Tracker](https://github.com/mcabreradev/filter/issues)
- ⭐ [Star on GitHub](https://github.com/mcabreradev/filter)

---

<p align="center">
  <strong>Made with ❤️ for the JavaScript/TypeScript community</strong>
</p>
