From 78896ff70f0e3aadf10f79aab2e34d310265b368 Mon Sep 17 00:00:00 2001 From: Miguelangel Cabrera Date: Sat, 21 Mar 2026 19:34:10 -0300 Subject: [PATCH 1/4] refactor: remove Svelte integration and related documentation - Removed Svelte integration from the codebase, including all related files and tests. - Updated documentation to reflect the removal of Svelte support, including changelog, cheatsheet, and contributing guidelines. - Adjusted framework integration details to focus on React, Vue, Angular, SolidJS, and Preact. - Cleaned up package.json and index.ts to eliminate references to Svelte. --- .size-limit.json | 6 - CLAUDE.md | 4 +- __test__/modular-imports.test.ts | 10 - docs/.vitepress/config.ts | 2 - docs/CLAUDE.md | 2 +- docs/README.md | 2 +- docs/advanced/architecture.md | 58 +- docs/advanced/bundle-size.md | 5 - docs/advanced/type-system.md | 55 -- docs/api/reference.md | 13 - docs/api/types.md | 160 ----- docs/frameworks/angular.md | 1 - docs/frameworks/index.md | 281 +-------- docs/frameworks/react.md | 1 - docs/frameworks/svelte.md | 295 --------- docs/frameworks/sveltekit.md | 596 ------------------ docs/frameworks/vue.md | 1 - docs/guide/best-practices.md | 40 -- docs/guide/comparison.md | 8 +- docs/guide/faq.md | 20 +- docs/guide/installation.md | 18 +- docs/guide/migration-v2.md | 86 +-- docs/guide/modular-imports.md | 20 - docs/guide/quick-start.md | 2 +- docs/guide/troubleshooting.md | 33 - docs/implementation/operators.md | 4 +- docs/index.md | 15 +- docs/project/changelog.md | 12 +- docs/project/cheatsheet.md | 9 - docs/project/contributing.md | 3 - docs/project/roadmap.md | 41 +- package.json | 12 - src/index.ts | 15 - src/integrations/svelte/index.ts | 12 - src/integrations/svelte/svelte.constants.ts | 3 - src/integrations/svelte/svelte.types.ts | 37 -- src/integrations/svelte/svelte.utils.ts | 21 - .../svelte/use-debounced-filter.test.ts | 145 ----- .../svelte/use-debounced-filter.ts | 56 -- src/integrations/svelte/use-filter.test.ts | 107 ---- src/integrations/svelte/use-filter.ts | 35 - .../svelte/use-filtered-state.test.ts | 116 ---- src/integrations/svelte/use-filtered-state.ts | 36 -- .../svelte/use-paginated-filter.test.ts | 195 ------ .../svelte/use-paginated-filter.ts | 103 --- 45 files changed, 30 insertions(+), 2666 deletions(-) delete mode 100644 docs/frameworks/svelte.md delete mode 100644 docs/frameworks/sveltekit.md delete mode 100644 src/integrations/svelte/index.ts delete mode 100644 src/integrations/svelte/svelte.constants.ts delete mode 100644 src/integrations/svelte/svelte.types.ts delete mode 100644 src/integrations/svelte/svelte.utils.ts delete mode 100644 src/integrations/svelte/use-debounced-filter.test.ts delete mode 100644 src/integrations/svelte/use-debounced-filter.ts delete mode 100644 src/integrations/svelte/use-filter.test.ts delete mode 100644 src/integrations/svelte/use-filter.ts delete mode 100644 src/integrations/svelte/use-filtered-state.test.ts delete mode 100644 src/integrations/svelte/use-filtered-state.ts delete mode 100644 src/integrations/svelte/use-paginated-filter.test.ts delete mode 100644 src/integrations/svelte/use-paginated-filter.ts diff --git a/.size-limit.json b/.size-limit.json index b592fe2..6678f41 100644 --- a/.size-limit.json +++ b/.size-limit.json @@ -65,12 +65,6 @@ "limit": "10 KB", "gzip": true }, - { - "name": "Svelte integration", - "path": "build/integrations/svelte/index.js", - "limit": "10 KB", - "gzip": true - }, { "name": "Angular integration", "path": "build/integrations/angular/index.js", diff --git a/CLAUDE.md b/CLAUDE.md index 267e2d7..039f41b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -**@mcabreradev/filter** is a TypeScript-first filtering engine for arrays with SQL-like wildcards, MongoDB-style operators, lazy evaluation, memoization, and framework integrations (React, Vue, Svelte, Angular, Preact, SolidJS). It provides 18+ operators for advanced filtering with zero dependencies (except Zod for validation). +**@mcabreradev/filter** is a TypeScript-first filtering engine for arrays with SQL-like wildcards, MongoDB-style operators, lazy evaluation, memoization, and framework integrations (React, Vue, Angular, Preact, SolidJS). It provides 18+ operators for advanced filtering with zero dependencies (except Zod for validation). ## Development Commands @@ -132,7 +132,6 @@ pnpm run docs:api - **`src/integrations/`** - Framework integrations (all optional peer deps) - `react/` - React hooks (useFilter, useDebouncedFilter, useFilteredState, usePaginatedFilter) - `vue/` - Vue composables with reactivity - - `svelte/` - Svelte stores - `angular/` - Angular integration - `preact/` - Preact hooks - `solidjs/` - SolidJS integration @@ -213,7 +212,6 @@ To add new operators: Framework-specific code lives in `src/integrations//`: - React uses hooks pattern - Vue uses Composition API -- Svelte uses stores - Angular, Preact, SolidJS also supported - Shared utilities in `src/integrations/shared/` - All optional peer dependencies diff --git a/__test__/modular-imports.test.ts b/__test__/modular-imports.test.ts index ac52d8a..0bfc3bf 100644 --- a/__test__/modular-imports.test.ts +++ b/__test__/modular-imports.test.ts @@ -20,15 +20,6 @@ describe('Modular Imports', () => { expect(vueModule.usePaginatedFilter).toBeDefined(); }); - it('should import Svelte stores from svelte path', async () => { - const svelteModule = await import('../src/integrations/svelte/index.js'); - - expect(svelteModule.useFilter).toBeDefined(); - expect(svelteModule.useFilteredState).toBeDefined(); - expect(svelteModule.useDebouncedFilter).toBeDefined(); - expect(svelteModule.usePaginatedFilter).toBeDefined(); - }); - it('should import React hooks from main index', async () => { const mainModule = await import('../src/index.js'); @@ -43,7 +34,6 @@ describe('Modular Imports', () => { expect(mainModule.useFilterReact).toBeDefined(); expect(mainModule.useFilterVue).toBeDefined(); - expect(mainModule.useFilterSvelte).toBeDefined(); }); }); diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 029c2fd..b3560c4 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -118,7 +118,6 @@ export default defineConfig({ { text: 'Overview', link: '/frameworks/' }, { text: 'React', link: '/frameworks/react' }, { text: 'Vue', link: '/frameworks/vue' }, - { text: 'Svelte', link: '/frameworks/svelte' }, { text: 'Angular', link: '/frameworks/angular' }, { text: 'SolidJS', link: '/frameworks/solidjs' }, { text: 'Preact', link: '/frameworks/preact' }, @@ -130,7 +129,6 @@ export default defineConfig({ items: [ { text: 'Next.js', link: '/frameworks/nextjs' }, { text: 'Nuxt', link: '/frameworks/nuxt' }, - { text: 'SvelteKit', link: '/frameworks/sveltekit' }, ], }, { diff --git a/docs/CLAUDE.md b/docs/CLAUDE.md index e8c15a4..a98d4b7 100644 --- a/docs/CLAUDE.md +++ b/docs/CLAUDE.md @@ -37,7 +37,7 @@ docs/ │ └── theme/ # Custom theme (index.ts, style.css, components/) ├── guide/ # Getting started, core features, configuration ├── operators/ # Operator reference pages -├── frameworks/ # React, Vue, Svelte, Angular, SolidJS, Preact, Next.js, Nuxt, etc. +├── frameworks/ # React, Vue, Angular, SolidJS, Preact, Next.js, Nuxt, etc. ├── api/ # API reference (reference.md, operators.md, types.md) ├── advanced/ # Architecture, type system, performance, migration ├── implementation/ # Deep-dive implementation details diff --git a/docs/README.md b/docs/README.md index 7a057aa..0476882 100644 --- a/docs/README.md +++ b/docs/README.md @@ -42,7 +42,7 @@ pnpm run docs:api - `.vitepress/` - VitePress configuration and theme - `guide/` - Getting started and core feature guides -- `frameworks/` - Framework integration guides (React, Vue, Svelte) +- `frameworks/` - Framework integration guides (React, Vue) - `examples/` - Code examples and use cases - `api/` - API reference documentation - `public/` - Static assets (images, logos, etc.) diff --git a/docs/advanced/architecture.md b/docs/advanced/architecture.md index eb49988..2e393f6 100644 --- a/docs/advanced/architecture.md +++ b/docs/advanced/architecture.md @@ -4,7 +4,7 @@ Deep dive into the architecture of @mcabreradev/filter. ## Overview -@mcabreradev/filter is built with a modular architecture that separates concerns and enables tree-shaking for optimal bundle sizes. The library has evolved through multiple versions, with v5.8.2 featuring MongoDB-style operators, framework integrations (React, Vue, Svelte, Angular, SolidJS, Preact), lazy evaluation, memoization, geospatial operators, datetime operators, and visual debugging. +@mcabreradev/filter is built with a modular architecture that separates concerns and enables tree-shaking for optimal bundle sizes. The library has evolved through multiple versions, with v5.8.2 featuring MongoDB-style operators, framework integrations (React, Vue, Angular, SolidJS, Preact), lazy evaluation, memoization, geospatial operators, datetime operators, and visual debugging. ## Data Flow @@ -84,7 +84,6 @@ src/ └── integrations/ # Framework integrations (v5.3.0+) ├── react/ # useFilter, useFilteredState, useDebouncedFilter, usePaginatedFilter ├── vue/ # Same 4 composables with Composition API - ├── svelte/ # Same 4 patterns with Svelte stores ├── angular/ # Angular integration (v5.7.0+) ├── preact/ # Preact hooks (v5.7.0+) ├── solidjs/ # SolidJS integration (v5.7.0+) @@ -863,54 +862,6 @@ export function useFilter( } ``` -### Svelte Integration - -Uses Svelte stores for reactive state management. - -```typescript -export function useFilter( - data: Writable | Readable, - expression: MaybeStore>, - options?: FilterOptions, -): UseFilterResult { - const expressionStore = toStore(expression); - - const filtered = derived( - [data, expressionStore], - ([$data, $expression]) => { - if (!$data || $data.length === 0) { - return []; - } - - try { - return filter($data, $expression, options); - } catch { - return []; - } - } - ); - - const isFiltering = derived( - [data, filtered], - ([$data, $filtered]) => { - return $filtered.length !== $data.length; - } - ); - - return { - filtered, - isFiltering, - }; -} - -function toStore(value: MaybeStore): Readable { - if (isStore(value)) { - return value; - } - return readable(value); -} -``` - ## Type System ### Generic Type Constraints with Operator Autocomplete @@ -1347,7 +1298,6 @@ export { filterDebug } from './debug/debug-filter'; // Framework integrations in separate entry points export { useFilter, useDebouncedFilter } from './integrations/react'; export { useFilter as useFilterVue } from './integrations/vue'; -export { useFilter as useFilterSvelte } from './integrations/svelte'; ``` ### Code Splitting Configuration @@ -1368,10 +1318,6 @@ export { useFilter as useFilterSvelte } from './integrations/svelte'; "import": "./dist/integrations/vue/index.js", "types": "./dist/integrations/vue/index.d.ts" }, - "./svelte": { - "import": "./dist/integrations/svelte/index.js", - "types": "./dist/integrations/svelte/index.d.ts" - } } } ``` @@ -1518,7 +1464,7 @@ describe('performance', () => { - **v5.1.0**: Lazy evaluation with generators - **v5.2.0**: Multi-layer memoization, logical operators - **v5.3.0**: Initial framework integrations -- **v5.4.0**: Full React, Vue, Svelte support +- **v5.4.0**: Full React, Vue support - **v5.5.0**: Array OR syntax, visual debugging, playground - **v5.6.0**: Geospatial operators, datetime operators - **v5.7.0**: Angular, SolidJS, Preact integrations diff --git a/docs/advanced/bundle-size.md b/docs/advanced/bundle-size.md index 3cca90e..3ae1b84 100644 --- a/docs/advanced/bundle-size.md +++ b/docs/advanced/bundle-size.md @@ -19,7 +19,6 @@ For detailed import syntax and examples, see the [Modular Imports Guide](/guide/ | Operators (granular) | ~5 KB | 58% | Specific operators | | React integration | ~3 KB | 75% | React hooks only | | Vue integration | ~3 KB | 75% | Vue composables only | -| Svelte integration | ~3 KB | 75% | Svelte stores only | | Lazy evaluation | ~2 KB | 83% | Large dataset processing | ## Import Strategies @@ -106,10 +105,6 @@ import { useFilter, useDebouncedFilter } from '@mcabreradev/filter/react'; // Vue import { useFilter } from '@mcabreradev/filter/vue'; // Bundle: ~3 KB (gzipped) - 70% reduction - -// Svelte -import { filterStore } from '@mcabreradev/filter/svelte'; -// Bundle: ~3 KB (gzipped) - 70% reduction ``` **Use when:** diff --git a/docs/advanced/type-system.md b/docs/advanced/type-system.md index eff5070..5208e0b 100644 --- a/docs/advanced/type-system.md +++ b/docs/advanced/type-system.md @@ -699,61 +699,6 @@ const { filtered: searchResults, isPending } = useDebouncedFilter( ); ``` -### Svelte Types - -```typescript -import type { Readable, Writable } from 'svelte/store'; - -type MaybeStore = T | Readable | Writable; - -interface UseFilterResult { - filtered: Readable; - isFiltering: Readable; -} - -interface UseFilteredStateResult { - data: Writable; - expression: Writable>; - filtered: Readable; - isFiltering: Readable; -} - -interface UseDebouncedFilterResult { - filtered: Readable; - isFiltering: Readable; - isPending: Readable; -} - -interface UsePaginatedFilterResult { - filtered: Readable; - isFiltering: Readable; - currentPage: Writable; - totalPages: Readable; - pageSize: Writable; - totalItems: Readable; - hasNextPage: Readable; - hasPreviousPage: Readable; - nextPage: () => void; - previousPage: () => void; - goToPage: (page: number) => void; - setPageSize: (size: number) => void; -} -``` - -**Example**: -```typescript -import { writable } from 'svelte/store'; -import { useFilter } from '@mcabreradev/filter/svelte'; - -const users = writable([...]); -const searchTerm = writable(''); - -const { filtered, isFiltering } = useFilter( - users, - { name: { $contains: searchTerm } } -); -``` - ## Type Guards ### Expression Type Guard diff --git a/docs/api/reference.md b/docs/api/reference.md index 2de049b..ed4b82e 100644 --- a/docs/api/reference.md +++ b/docs/api/reference.md @@ -858,19 +858,6 @@ function useFilter( } ``` -### Svelte Stores - -```typescript -function useFilter( - data: T[] | Readable, - expression: Expression | Readable>, - options?: FilterOptions -): { - filtered: Readable; - isFiltering: Readable; -} -``` - ## Validation ### validateExpression diff --git a/docs/api/types.md b/docs/api/types.md index 6145980..ed598b9 100644 --- a/docs/api/types.md +++ b/docs/api/types.md @@ -633,96 +633,6 @@ const { }: UsePaginatedFilterResult = usePaginatedFilter(products, expression); ``` -## Svelte Types - -### UseFilterResult (Svelte) - -Return type for Svelte `useFilter` store. - -```typescript -interface UseFilterResult { - filtered: Readable; - isFiltering: Readable; -} -``` - -**Usage**: -```typescript -import type { Readable } from 'svelte/store'; - -const { filtered, isFiltering }: { - filtered: Readable; - isFiltering: Readable; -} = useFilter(users, expression); -``` - -### UseFilteredStateResult (Svelte) - -Return type for Svelte `useFilteredState` store. - -```typescript -interface UseFilteredStateResult { - data: Writable; - expression: Writable>; - filtered: Readable; - isFiltering: Readable; -} -``` - -**Usage**: -```typescript -import type { Readable, Writable } from 'svelte/store'; - -const { - data, - expression, - filtered, - isFiltering -}: UseFilteredStateResult = useFilteredState(initialProducts); -``` - -### UseDebouncedFilterResult (Svelte) - -Return type for Svelte `useDebouncedFilter` store. - -```typescript -interface UseDebouncedFilterResult { - filtered: Readable; - isFiltering: Readable; - isPending: Readable; -} -``` - -**Usage**: -```typescript -const { - filtered, - isFiltering, - isPending -}: UseDebouncedFilterResult = useDebouncedFilter(users, expression); -``` - -### UsePaginatedFilterResult (Svelte) - -Return type for Svelte `usePaginatedFilter` store. - -```typescript -interface UsePaginatedFilterResult { - filtered: Readable; - isFiltering: Readable; - currentPage: Writable; - totalPages: Readable; - pageSize: Writable; - totalItems: Readable; - hasNextPage: Readable; - hasPreviousPage: Readable; - nextPage: () => void; - previousPage: () => void; - goToPage: (page: number) => void; - setPageSize: (size: number) => void; -} -``` - ## Angular Types ### FilterService`` @@ -1625,75 +1535,6 @@ interface UsePaginatedFilterResult { } ``` -## Svelte Types - -### UseFilterResult (Svelte) - -Return type for Svelte `useFilter` store. - -```typescript -interface UseFilterResult { - filtered: Readable; - isFiltering: Readable; -} -``` - -**Usage**: -```typescript -import type { Readable } from 'svelte/store'; - -const { filtered, isFiltering }: { - filtered: Readable; - isFiltering: Readable; -} = useFilter(users, expression); -``` - -### UseFilteredStateResult (Svelte) - -Return type for Svelte `useFilteredState` store. - -```typescript -interface UseFilteredStateResult { - data: Writable; - expression: Writable>; - filtered: Readable; - isFiltering: Readable; -} -``` - -### UseDebouncedFilterResult (Svelte) - -Return type for Svelte `useDebouncedFilter` store. - -```typescript -interface UseDebouncedFilterResult { - filtered: Readable; - isFiltering: Readable; - isPending: Readable; -} -``` - -### UsePaginatedFilterResult (Svelte) - -Return type for Svelte `usePaginatedFilter` store. - -```typescript -interface UsePaginatedFilterResult { - filtered: Readable; - isFiltering: Readable; - currentPage: Writable; - totalPages: Readable; - pageSize: Writable; - totalItems: Readable; - hasNextPage: Readable; - hasPreviousPage: Readable; - nextPage: () => void; - previousPage: () => void; - goToPage: (page: number) => void; - setPageSize: (size: number) => void; -} -``` - ## Utility Types ### Operator @@ -1895,5 +1736,4 @@ const activeUsers = activeUserFilter(users); - [Basic Filtering](/guide/basic-filtering) - [React Integration](/frameworks/react) - [Vue Integration](/frameworks/vue) -- [Svelte Integration](/frameworks/svelte) diff --git a/docs/frameworks/angular.md b/docs/frameworks/angular.md index 7a6c2c9..588cc15 100644 --- a/docs/frameworks/angular.md +++ b/docs/frameworks/angular.md @@ -1438,6 +1438,5 @@ filterService.setOptions({ enableCache: true }); - [React Integration](./react.md) - [Vue Integration](./vue.md) -- [Svelte Integration](./svelte.md) - [API Reference](../api/reference.md) diff --git a/docs/frameworks/index.md b/docs/frameworks/index.md index eca48ab..b9f3810 100644 --- a/docs/frameworks/index.md +++ b/docs/frameworks/index.md @@ -1,6 +1,6 @@ --- title: Framework Integrations -description: Complete guide for React, Vue, Svelte, Angular, SolidJS, and Preact integrations +description: Complete guide for React, Vue, Angular, SolidJS, and Preact integrations --- # Framework Integrations @@ -8,7 +8,7 @@ description: Complete guide for React, Vue, Svelte, Angular, SolidJS, and Preact > **Version**: 5.8.2 > **Status**: Stable -Complete guide for using `@mcabreradev/filter` with 6 major frameworks. +Complete guide for using `@mcabreradev/filter` with 5 major frameworks. --- @@ -18,7 +18,6 @@ Complete guide for using `@mcabreradev/filter` with 6 major frameworks. - [Installation](#installation) - [React Integration](#react-integration) - [Vue Integration](#vue-integration) -- [Svelte Integration](#svelte-integration) - [Angular Integration](#angular-integration) ⭐ NEW - [SolidJS Integration](#solidjs-integration) ⭐ NEW - [Preact Integration](#preact-integration) ⭐ NEW @@ -37,7 +36,6 @@ The framework integrations provide idiomatic hooks, composables, services, and s - ⚛️ **[React](/frameworks/react)** - Hooks with automatic re-rendering - 🟢 **[Vue](/frameworks/vue)** - Composition API with reactivity -- 🔴 **[Svelte](/frameworks/svelte)** - Store-based reactive filtering - 🅰️ **[Angular](/frameworks/angular)** - Services and Pipes with Signals ⭐ NEW - 🔷 **[SolidJS](/frameworks/solidjs)** - Signal-based reactive hooks ⭐ NEW - ⚡ **[Preact](/frameworks/preact)** - Lightweight hooks API ⭐ NEW @@ -46,13 +44,12 @@ The framework integrations provide idiomatic hooks, composables, services, and s - **React Hooks**: `useFilter`, `useFilteredState`, `useDebouncedFilter`, `usePaginatedFilter` - **Vue Composables**: Composition API-first with full reactivity -- **Svelte Stores**: Reactive stores with derived state - **Angular Services**: `FilterService`, `DebouncedFilterService`, `PaginatedFilterService`, `FilterPipe` - **SolidJS Hooks**: `useFilter`, `useDebouncedFilter`, `usePaginatedFilter` - **Preact Hooks**: `useFilter`, `useFilteredState`, `useDebouncedFilter`, `usePaginatedFilter` - **Shared Utilities**: Debouncing, pagination, and performance optimizations - **TypeScript**: Full type safety with generics -- **SSR Compatible**: Works with Next.js, Nuxt, SvelteKit, Angular Universal, and SolidStart +- **SSR Compatible**: Works with Next.js, Nuxt, Angular Universal, and SolidStart --- @@ -64,7 +61,6 @@ npm install @mcabreradev/filter # Install peer dependencies for your framework npm install react # For React npm install vue # For Vue -npm install svelte # For Svelte npm install @angular/core # For Angular 17+ npm install solid-js # For SolidJS npm install preact # For Preact @@ -488,185 +484,6 @@ function usePaginatedFilter( --- -## Svelte Integration - -### useFilter - -Svelte store-based filtering. - -```svelte - - -
-

Showing {$filtered.length} active users

- {#each $filtered as user (user.id)} -
{user.name}
- {/each} -
-``` - -**API**: -```typescript -function useFilter( - data: T[] | Readable, - expression: Expression | Readable>, - options?: FilterOptions -): { - filtered: Readable; - isFiltering: Readable; -} -``` - -### useFilteredState - -Stateful filtering with Svelte stores. - -```svelte - -``` - -**API**: -```typescript -function useFilteredState( - initialData?: T[], - initialExpression?: Expression, - options?: FilterOptions -): { - data: Writable; - expression: Writable>; - filtered: Readable; - isFiltering: Readable; -} -``` - -### useDebouncedFilter - -Debounced filtering for Svelte. - -```svelte - - -
- - {#if $isPending} - Searching... - {/if} - -
-``` - -**API**: -```typescript -function useDebouncedFilter( - data: T[] | Readable, - expression: Expression | Readable>, - options?: UseDebouncedFilterOptions -): { - filtered: Readable; - isFiltering: Readable; - isPending: Readable; -} -``` - -### usePaginatedFilter - -Pagination support for Svelte. - -```svelte - - -
- -
- - Page {$pagination.currentPage} of {$pagination.totalPages} - -
-
-``` - -**API**: -```typescript -function usePaginatedFilter( - data: T[] | Readable, - expression: Expression | Readable>, - initialPageSize?: number, - options?: FilterOptions -): { - filtered: Readable; - isFiltering: Readable; - pagination: Readable>; - currentPage: Writable; - pageSize: Writable; - nextPage: () => void; - previousPage: () => void; - goToPage: (page: number) => void; - setPageSize: (size: number) => void; -} -``` - ---- - ## Angular Integration ⭐ **New in v5.7.0**: Full Angular support with Services, Pipes, and Signals! @@ -1352,22 +1169,6 @@ const expression = computed(() => ({ useFilter(data, expression, { enableCache: true }); ``` -### Svelte - -1. **Use derived stores**: For computed values -```typescript -const filtered = derived([data, expression], ([$data, $expr]) => { - return filter($data, $expr); -}); -``` - -2. **Avoid store subscriptions in loops**: Subscribe once at the top level - -3. **Enable caching**: For large datasets -```typescript -useFilter(data, expression, { enableCache: true }); -``` - ### Angular 1. **Use Signals**: Leverage Angular's fine-grained reactivity @@ -1430,10 +1231,6 @@ const { filtered } = useFilter(users, { active: true }); const { filtered } = useFilter(users, expression); // filtered is ComputedRef -// Svelte -const { filtered } = useFilter(users, expression); -// filtered is Readable - // Angular filterService.setData(users); // filtered is Signal @@ -1563,58 +1360,6 @@ const { ``` -### Real-World Svelte Example - -```svelte - - -
- - - - - - -
-``` - --- ## SSR Compatibility @@ -1623,7 +1368,6 @@ All framework integrations are compatible with server-side rendering: - **Next.js**: Works with App Router and Pages Router - **Nuxt**: Compatible with Nuxt 3 -- **SvelteKit**: Full SSR support - **Angular Universal**: Server-side rendering support - **SolidStart**: SSR and streaming support @@ -1652,19 +1396,6 @@ const { filtered } = useFilter(users, { active: true }); ``` -### SvelteKit Example - -```svelte - -``` - ### Angular Universal Example ```typescript @@ -1718,9 +1449,6 @@ const { filtered } = useFilter(users, { active: true }); // After (Vue) const { filtered } = useFilter(users, { active: true }); -// After (Svelte) -const { filtered } = useFilter(users, { active: true }); - // After (Angular) this.filterService.setExpression({ active: true }); @@ -1763,9 +1491,6 @@ const { filtered } = useFilter(users, { active: true }); // After (Vue) const { filtered } = useFilter(users, { active: true }); - -// After (Svelte) -const { filtered } = useFilter(users, { active: true }); ``` ### From Custom Hooks diff --git a/docs/frameworks/react.md b/docs/frameworks/react.md index 59f4646..e172d30 100644 --- a/docs/frameworks/react.md +++ b/docs/frameworks/react.md @@ -1055,6 +1055,5 @@ export default function Loading() { ## Next Steps - [Vue Integration](./vue.md) -- [Svelte Integration](./svelte.md) - [API Reference](../api/reference.md) - [Examples](../examples/) diff --git a/docs/frameworks/svelte.md b/docs/frameworks/svelte.md deleted file mode 100644 index 8041f7d..0000000 --- a/docs/frameworks/svelte.md +++ /dev/null @@ -1,295 +0,0 @@ ---- -title: Svelte Integration -description: Svelte Stores for filtering with @mcabreradev/filter ---- - -# Svelte Integration - -Complete guide for using `@mcabreradev/filter` with Svelte. - -## Installation - -```bash -npm install @mcabreradev/filter -``` - -## Import - -```typescript -import { - useFilter, - useFilteredState, - useDebouncedFilter, - usePaginatedFilter -} from '@mcabreradev/filter/svelte'; -``` - -## Available Functions - -- [`useFilter`](#usefilter) - Basic filtering with Svelte stores -- [`useFilteredState`](#usefilteredstate) - Filtering with writable state -- [`useDebouncedFilter`](#usedebouncedfilter) - Debounced filtering for search -- [`usePaginatedFilter`](#usepaginatedfilter) - Filtering with pagination - -## useFilter - -Basic filtering with Svelte stores. - -```svelte - - -
-

Showing {$filtered.length} active users

- {#each $filtered as user (user.id)} -
{user.name}
- {/each} -
-``` - -### API Reference - -```typescript -function useFilter( - data: Readable | T[], - expression: Readable | null> | Expression | null, - options?: FilterOptions -): UseFilterResult - -interface UseFilterResult { - filtered: Readable; - isFiltering: Readable; -} -``` - -## useFilteredState - -Filtering with writable state management. - -```svelte - - -
- -
Found {$filtered.length} products
-
-``` - -### API Reference - -```typescript -function useFilteredState( - data: Readable | T[], - initialExpression: Expression | null, - options?: FilterOptions -): UseFilteredStateResult - -interface UseFilteredStateResult { - filtered: Readable; - expression: Writable | null>; - setExpression: (expression: Expression | null) => void; - isFiltering: Readable; -} -``` - -## useDebouncedFilter - -Debounced filtering for search inputs. - -```svelte - - -
- - {#if $isPending} - Searching... - {/if} -
Found {$filtered.length} users
-
-``` - -### API Reference - -```typescript -function useDebouncedFilter( - data: Readable | T[], - expression: (() => Expression | null) | Readable | null> | Expression | null, - options?: UseDebouncedFilterOptions -): UseDebouncedFilterResult - -interface UseDebouncedFilterOptions extends FilterOptions { - delay?: number; // Default: 300ms -} - -interface UseDebouncedFilterResult { - filtered: Readable; - isPending: Readable; -} -``` - -## usePaginatedFilter - -Filtering with built-in pagination. - -```svelte - - -
- {#each $paginatedResults as product (product.id)} -
{product.name} - ${product.price}
- {/each} -
- - Page {$currentPage} of {$totalPages} - -
-
-``` - -### API Reference - -```typescript -function usePaginatedFilter( - data: Readable | T[], - expression: Readable | null> | Expression | null, - options?: { - pageSize?: number; - initialPage?: number; - filterOptions?: FilterOptions; - } -): UsePaginatedFilterResult - -interface UsePaginatedFilterResult { - paginatedResults: Readable; - currentPage: Readable; - totalPages: Readable; - totalItems: Readable; - pageSize: Writable; - nextPage: () => void; - prevPage: () => void; - goToPage: (page: number) => void; - setPageSize: (size: number) => void; -} -``` - -## TypeScript Support - -Full type safety with generics: - -```typescript -interface Product { - id: number; - name: string; - price: number; -} - -const { filtered } = useFilter( - products, - { price: { $gte: 100 } } -); -// filtered is typed as Readable -``` - -## SSR Support - -Works with SvelteKit: - -```svelte - -``` - -## Next Steps - -- [React Integration](./react.md) -- [Vue Integration](./vue.md) -- [SvelteKit Integration](./sveltekit.md) -- [API Reference](../api/reference.md) - - diff --git a/docs/frameworks/sveltekit.md b/docs/frameworks/sveltekit.md deleted file mode 100644 index e863398..0000000 --- a/docs/frameworks/sveltekit.md +++ /dev/null @@ -1,596 +0,0 @@ -# SvelteKit Integration - -Guide for using @mcabreradev/filter with SvelteKit. - -## Overview - -@mcabreradev/filter integrates seamlessly with SvelteKit, supporting both client-side and server-side filtering with Svelte stores. - -## Installation - -```bash -# Using npm -npm install @mcabreradev/filter - -# Using yarn -yarn add @mcabreradev/filter - -# Using pnpm -pnpm add @mcabreradev/filter - -## Basic Usage - -### Client-Side Filtering - -```svelte - - - - -{#if $isFiltering} -
Loading...
-{:else} -
- {#each $filtered as product (product.id)} -
{product.name}
- {/each} -
-{/if} -``` - -## Server-Side Rendering - -### Using `load` Function - -```typescript -import { filter } from '@mcabreradev/filter'; -import type { PageLoad } from './$types'; - -interface Product { - id: number; - name: string; - price: number; - category: string; -} - -export const load: PageLoad = async ({ url, fetch }) => { - const category = url.searchParams.get('category') || ''; - const minPrice = Number(url.searchParams.get('minPrice')) || 0; - - const response = await fetch('/api/products'); - const products: Product[] = await response.json(); - - const expression = { - $and: [ - category && { category: { $eq: category } }, - minPrice > 0 && { price: { $gte: minPrice } } - ].filter(Boolean) - }; - - return { - products: filter(products, expression) - }; -}; -``` - -```svelte - - -

Products

-
- {#each data.products as product (product.id)} -
-

{product.name}

-

${product.price}

-
- {/each} -
-``` - -### Server Load Function - -```typescript -import { filter } from '@mcabreradev/filter'; -import type { PageServerLoad } from './$types'; - -export const load: PageServerLoad = async ({ url }) => { - const category = url.searchParams.get('category') || ''; - - const products = await getProducts(); - - const expression = { - category: { $eq: category } - }; - - return { - products: filter(products, expression) - }; -}; -``` - -## API Routes - -### GET Endpoint - -```typescript -import { json } from '@sveltejs/kit'; -import { filter } from '@mcabreradev/filter'; -import type { RequestHandler } from './$types'; - -interface Product { - id: number; - name: string; - price: number; - category: string; -} - -export const GET: RequestHandler = async ({ url }) => { - const category = url.searchParams.get('category'); - const minPrice = url.searchParams.get('minPrice'); - - const products: Product[] = await getProducts(); - - const expression = { - $and: [ - category && { category: { $eq: category } }, - minPrice && { price: { $gte: Number(minPrice) } } - ].filter(Boolean) - }; - - const filtered = filter(products, expression); - - return json({ products: filtered }); -}; -``` - -### POST Endpoint - -```typescript -import { json } from '@sveltejs/kit'; -import { filter } from '@mcabreradev/filter'; -import type { RequestHandler } from './$types'; -import type { Expression } from '@mcabreradev/filter'; - -export const POST: RequestHandler = async ({ request }) => { - const { expression } = await request.json() as { expression: Expression }; - - const products = await getProducts(); - const filtered = filter(products, expression); - - return json({ - products: filtered, - total: filtered.length - }); -}; -``` - -## Advanced Patterns - -### Search with Pagination - -```svelte - - - - - -
- {#each $filtered as product (product.id)} -
{product.name}
- {/each} -
- -
- - Page {$currentPage} of {$totalPages} - -
-``` - -### Debounced Search - -```svelte - - - -{#if $isPending} - Searching... -{/if} - -
- {#each $filtered as product (product.id)} -
{product.name}
- {/each} -
-``` - -### URL Query Parameters - -```svelte - - - - - updateFilter('minPrice', e.target.value)} - placeholder="Min price" -/> - -
- {#each $filtered as product (product.id)} -
{product.name}
- {/each} -
-``` - -### Custom Store Pattern - -Create a reusable store: - -```typescript -import { derived, writable, type Readable, type Writable } from 'svelte/store'; -import { filter } from '@mcabreradev/filter'; -import type { Expression } from '@mcabreradev/filter'; - -interface Product { - id: number; - name: string; - price: number; - category: string; -} - -interface ProductFilters { - searchTerm: string; - category: string; - minPrice: number; - maxPrice: number; -} - -export function createProductFilterStore(initialProducts: Product[] = []) { - const products: Writable = writable(initialProducts); - const filters: Writable = writable({ - searchTerm: '', - category: '', - minPrice: 0, - maxPrice: 0 - }); - - const filtered: Readable = derived( - [products, filters], - ([$products, $filters]) => { - const conditions = []; - - if ($filters.searchTerm) { - conditions.push({ - name: { $regex: new RegExp($filters.searchTerm, 'i') } - }); - } - - if ($filters.category) { - conditions.push({ - category: { $eq: $filters.category } - }); - } - - if ($filters.minPrice > 0) { - conditions.push({ - price: { $gte: $filters.minPrice } - }); - } - - if ($filters.maxPrice > 0) { - conditions.push({ - price: { $lte: $filters.maxPrice } - }); - } - - const expression: Expression = - conditions.length > 0 ? { $and: conditions } : {}; - - return filter($products, expression); - } - ); - - return { - products, - filters, - filtered, - setProducts: (newProducts: Product[]) => products.set(newProducts), - updateFilters: (newFilters: Partial) => - filters.update(f => ({ ...f, ...newFilters })), - resetFilters: () => filters.set({ - searchTerm: '', - category: '', - minPrice: 0, - maxPrice: 0 - }) - }; -} -``` - -Usage: - -```svelte - - - updateFilters({ searchTerm: e.target.value })} - placeholder="Search..." -/> - - - -
- {#each $filtered as product (product.id)} -
{product.name}
- {/each} -
-``` - -## Form Actions - -### Using Form Actions - -```typescript -import { fail } from '@sveltejs/kit'; -import { filter } from '@mcabreradev/filter'; -import type { Actions } from './$types'; - -export const actions: Actions = { - filter: async ({ request }) => { - const data = await request.formData(); - const category = data.get('category') as string; - const minPrice = Number(data.get('minPrice')); - - const products = await getProducts(); - - const expression = { - $and: [ - category && { category: { $eq: category } }, - minPrice > 0 && { price: { $gte: minPrice } } - ].filter(Boolean) - }; - - const filtered = filter(products, expression); - - return { - products: filtered - }; - } -}; -``` - -```svelte - - -
- - - - - -
- -{#if form?.products} -
- {#each form.products as product (product.id)} -
{product.name}
- {/each} -
-{/if} -``` - -## Performance Tips - -### 1. Use Reactive Statements - -```typescript -$: expression = { - status: { $eq: status } -}; -``` - -### 2. Enable Memoization - -```typescript -const { filtered } = useFilter(data, expression, { - memoize: true -}); -``` - -### 3. Use Server Load Functions - -Filter data on the server when possible: - -```typescript -export const load: PageServerLoad = async () => { - const products = await getProducts(); - return { - products: filter(products, expression) - }; -}; -``` - -### 4. Implement Pagination - -```typescript -const { filtered } = usePaginatedFilter(data, expression, 50); -``` - -## TypeScript Support - -Ensure proper TypeScript configuration in `svelte.config.js`: - -```javascript -import adapter from '@sveltejs/adapter-auto'; -import { vitePreprocess } from '@sveltejs/kit/vite'; - -export default { - preprocess: vitePreprocess(), - kit: { - adapter: adapter() - } -}; -``` - -And in `tsconfig.json`: - -```json -{ - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true - } -} -``` - -## Related Resources - -- [Svelte Integration](/frameworks/svelte) -- [Best Practices](/guide/best-practices) -- [Examples](/examples/basic-usage) -- [API Reference](/api/core) - diff --git a/docs/frameworks/vue.md b/docs/frameworks/vue.md index 2f9e9bc..69b36a6 100644 --- a/docs/frameworks/vue.md +++ b/docs/frameworks/vue.md @@ -1197,7 +1197,6 @@ const { filtered } = useFilter(products, { inStock: true }); ## Next Steps - [React Integration](./react.md) -- [Svelte Integration](./svelte.md) - [Nuxt Integration](./nuxt.md) - [API Reference](../api/reference.md) diff --git a/docs/guide/best-practices.md b/docs/guide/best-practices.md index 8a3499d..ab248b6 100644 --- a/docs/guide/best-practices.md +++ b/docs/guide/best-practices.md @@ -242,46 +242,6 @@ watch(filtered, (newFiltered) => { }); ``` -## Svelte Best Practices - -### Use Stores - -Leverage Svelte stores for reactivity: - -```typescript -import { writable } from 'svelte/store'; -import { useFilter } from '@mcabreradev/filter/svelte'; - -const users = writable([]); -const expression = writable({ status: { $eq: 'active' } }); - -const { filtered } = useFilter(users, expression); -``` - -### Reactive Statements - -Use reactive statements for derived values: - -```svelte - - - - -{#each $filtered as user} -
{user.name}
-{/each} -``` - ## Performance Optimization ### Enable Memoization for Large Datasets diff --git a/docs/guide/comparison.md b/docs/guide/comparison.md index f143c3d..cfd9a41 100644 --- a/docs/guide/comparison.md +++ b/docs/guide/comparison.md @@ -12,7 +12,7 @@ This guide compares @mcabreradev/filter with popular alternatives to help you ch |---------|-------------------|--------|----------------|---------|---------| | Bundle Size | ~3KB | ~70KB | 0KB (native) | ~12KB | ~5KB | | TypeScript | ✅ Full support | ⚠️ @types required | ✅ Native | ⚠️ @types required | ❌ Limited | -| Framework Integration | ✅ React, Vue, Svelte | ❌ None | ❌ None | ❌ None | ❌ None | +| Framework Integration | ✅ React, Vue | ❌ None | ❌ None | ❌ None | ❌ None | | Operator-Based | ✅ Yes | ❌ No | ❌ No | ❌ No | ✅ Yes | | Nested Objects | ✅ Yes | ✅ Yes | ⚠️ Manual | ❌ No | ✅ Yes | | Fuzzy Search | ❌ No | ❌ No | ❌ No | ✅ Yes | ❌ No | @@ -109,7 +109,7 @@ const { filtered } = useFilter(data, { **2. Framework Integration** -Built-in hooks for React, Vue, and Svelte. +Built-in hooks for React and Vue. **3. Advanced Operators** @@ -181,7 +181,7 @@ Better type inference and safety. **4. Modern API** -Designed for modern React, Vue, and Svelte. +Designed for modern React and Vue. #### When to Use Sift.js @@ -373,7 +373,7 @@ const { filtered } = useFilter(data, { ### Choose @mcabreradev/filter if you need: -✅ Framework integration (React, Vue, Svelte) +✅ Framework integration (React, Vue) ✅ Type-safe filtering ✅ Complex logical operations ✅ Built-in pagination diff --git a/docs/guide/faq.md b/docs/guide/faq.md index 5018ed2..0d38be9 100644 --- a/docs/guide/faq.md +++ b/docs/guide/faq.md @@ -6,7 +6,7 @@ Common questions about @mcabreradev/filter. ### What is @mcabreradev/filter? -A TypeScript-first filtering library that provides powerful, type-safe filtering capabilities for JavaScript arrays with framework integrations for React, Vue, and Svelte. +A TypeScript-first filtering library that provides powerful, type-safe filtering capabilities for JavaScript arrays with framework integrations for React and Vue. ### Why use this instead of Array.filter()? @@ -36,7 +36,6 @@ Yes. The library is: - Core: ~3KB gzipped - React integration: ~1KB additional - Vue integration: ~1KB additional -- Svelte integration: ~1KB additional Tree-shaking ensures you only bundle what you use. @@ -65,13 +64,10 @@ No, but it's recommended. The library works with JavaScript but provides excelle **Vue**: `vue ^3.0.0` -**Svelte**: `svelte ^3.0.0 || ^4.0.0` - -### Can I use it with older React/Vue/Svelte versions? +### Can I use it with older React/Vue versions? - React 16/17: May work but not officially supported - Vue 2: Not supported (use Vue 3) -- Svelte 3: Supported ## Usage Questions @@ -530,18 +526,6 @@ const { filtered } = useFilter(data, expression); ``` -### Does it work with SvelteKit? - -Yes, use in components: - -```svelte - -``` - ### Can I use it with React Native? Yes, the React integration works with React Native: diff --git a/docs/guide/installation.md b/docs/guide/installation.md index fb79615..44e93b7 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -83,9 +83,6 @@ import { useFilter } from '@mcabreradev/filter/react'; // Vue composables import { useFilter } from '@mcabreradev/filter/vue'; -// Svelte stores -import { useFilter } from '@mcabreradev/filter/svelte'; - // Lazy evaluation import { filterLazy } from '@mcabreradev/filter/lazy'; @@ -150,19 +147,6 @@ const searchTerm = ref(''); const { filtered, isFiltering } = useFilter(users, searchTerm); ``` -### Svelte - -```typescript -// Classic import -import { useFilter } from '@mcabreradev/filter'; - -// Modular import (recommended) -import { useFilter } from '@mcabreradev/filter/svelte'; - -const searchTerm = writable(''); -const { filtered, isFiltering } = useFilter(users, searchTerm); -``` - ## CDN Usage For quick prototyping without a build step: @@ -238,7 +222,7 @@ console.log(adults); // [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }] - [Quick Start](/guide/quick-start) - Get started in minutes - [Modular Imports](/guide/modular-imports) - Optimize bundle size - [Basic Filtering](/guide/basic-filtering) - Learn core concepts -- [Framework Integrations](/frameworks/) - React, Vue, Svelte guides +- [Framework Integrations](/frameworks/) - React, Vue guides ## Troubleshooting diff --git a/docs/guide/migration-v2.md b/docs/guide/migration-v2.md index 9f78531..7a89165 100644 --- a/docs/guide/migration-v2.md +++ b/docs/guide/migration-v2.md @@ -6,10 +6,10 @@ This guide helps you migrate to the latest version of `@mcabreradev/filter` and ### What's New in v5.4.0 -- **Stable Framework Integrations**: Production-ready React, Vue, and Svelte support +- **Stable Framework Integrations**: Production-ready React, Vue, Angular, SolidJS, and Preact support - **Improved Type Safety**: Better TypeScript inference for framework hooks - **Bug Fixes**: Stability improvements across all integrations -- **SSR Compatibility**: Full support for Next.js, Nuxt, and SvelteKit +- **SSR Compatibility**: Full support for Next.js and Nuxt --- @@ -328,51 +328,6 @@ interface UsePaginatedFilterResult { --- -### Svelte Stores - -Svelte integration uses stores for reactive state management. - -#### useFilter - -```typescript -import { writable } from 'svelte/store'; -import { useFilter } from '@mcabreradev/filter'; - -const users = writable([...]); -const searchTerm = writable(''); - -const { filtered, isFiltering } = useFilter(users, { - name: { $contains: searchTerm } -}); -``` - -**Usage in Svelte component:** -```svelte - - -{#if $isFiltering} -

Filtering active...

-{/if} - -{#each $filtered as user (user.id)} -
{user.name}
-{/each} -``` - -**Return Type:** -```typescript -interface UseFilterResult { - filtered: Readable; - isFiltering: Readable; -} -``` - #### useFilteredState ```typescript @@ -503,26 +458,6 @@ const { filtered, isPending } = useDebouncedFilter( ``` -**Svelte:** -```svelte - - - -{#if $isPending} - Typing... -{/if} -``` - ### Pagination with Filtering All frameworks follow the same pattern - filter first, then paginate the results. @@ -575,9 +510,6 @@ const { filtered } = useFilter(products, { price: { $gte: 100 } }); const { filtered } = useFilter(products, { price: { $gte: 100 } }); // filtered is ComputedRef -// Svelte -const { filtered } = useFilter(products, { price: { $gte: 100 } }); -// filtered is Readable ``` --- @@ -588,8 +520,6 @@ All framework integrations are SSR-compatible: - **Next.js**: Works in both App Router and Pages Router - **Nuxt**: Compatible with Nuxt 3 -- **SvelteKit**: Full SSR support - Example with Next.js App Router: ```typescript @@ -661,17 +591,6 @@ const searchTerm = ref(''); // Not const searchTerm = ''; const { filtered } = useFilter(data, { name: { $contains: searchTerm } }); ``` -### Svelte: "Store is not reactive" - -**Problem:** Not using `$` prefix to access store values. - -**Solution:** Use `$` prefix: -```svelte -{#each $filtered as item} -
{item.name}
-{/each} -``` - --- ## Support @@ -688,5 +607,4 @@ Check out the framework-specific guides: - [React Integration Guide](../frameworks/react.md) - [Vue Integration Guide](../frameworks/vue.md) -- [Svelte Integration Guide](../frameworks/svelte.md) diff --git a/docs/guide/modular-imports.md b/docs/guide/modular-imports.md index a2cdbf7..ae3a191 100644 --- a/docs/guide/modular-imports.md +++ b/docs/guide/modular-imports.md @@ -90,25 +90,6 @@ const { filtered, isFiltering } = useFilter(users, searchTerm); - `useDebouncedFilter` - Debounced filtering - `usePaginatedFilter` - Pagination support -### Svelte - -```typescript -// Classic import - exports useFilter for compatibility -import { useFilter } from '@mcabreradev/filter'; - -// Modular import (recommended) -import { useFilter } from '@mcabreradev/filter/svelte'; - -const searchTerm = writable(''); -const { filtered, isFiltering } = useFilter(users, searchTerm); -``` - -**Available stores:** -- `useFilter` - Basic filtering store -- `useFilteredState` - Filtered state store -- `useDebouncedFilter` - Debounced filtering store -- `usePaginatedFilter` - Pagination store - ## Operators ### Comparison Operators @@ -285,7 +266,6 @@ The package is configured with granular exports in `package.json`: "./core": "./build/core/index.js", "./react": "./build/integrations/react/index.js", "./vue": "./build/integrations/vue/index.js", - "./svelte": "./build/integrations/svelte/index.js", "./operators/comparison": "./build/operators/comparison.js", "./operators/array": "./build/operators/array.js", "./operators/string": "./build/operators/string.js", diff --git a/docs/guide/quick-start.md b/docs/guide/quick-start.md index a78f540..7113fed 100644 --- a/docs/guide/quick-start.md +++ b/docs/guide/quick-start.md @@ -290,7 +290,7 @@ Now that you know the basics, explore more advanced features: - [Lazy Evaluation](/guide/lazy-evaluation) - Efficient processing for large datasets - [Memoization](/guide/memoization) - 530x performance boost with caching - [Configuration](/guide/configuration) - All options including orderBy and limit -- [Framework Integration](/frameworks/) - React, Vue, Svelte, Angular, SolidJS, Preact support ⭐ +- [Framework Integration](/frameworks/) - React, Vue, Angular, SolidJS, Preact support ⭐ ## Interactive Playground diff --git a/docs/guide/troubleshooting.md b/docs/guide/troubleshooting.md index c1817ca..1d653e7 100644 --- a/docs/guide/troubleshooting.md +++ b/docs/guide/troubleshooting.md @@ -100,39 +100,6 @@ import type { ComputedRef } from 'vue'; const { filtered }: { filtered: ComputedRef } = useFilter(data, expression); ``` -## Svelte Integration Issues - -### Store Not Updating - -**Problem**: `$filtered` doesn't update in template. - -**Cause**: Not subscribing to store properly. - -**Solution**: -```svelte - - -{#each $filtered as item} -
{item.name}
-{/each} -``` - -### TypeScript Errors with Stores - -**Problem**: Type errors when using Svelte stores. - -**Solution**: -```typescript -import type { Readable } from 'svelte/store'; -import type { User } from './types'; - -const { filtered }: { filtered: Readable } = useFilter(data, expression); -``` - ## Performance Issues ### Slow Filtering on Large Datasets diff --git a/docs/implementation/operators.md b/docs/implementation/operators.md index 33d483c..18dc26f 100644 --- a/docs/implementation/operators.md +++ b/docs/implementation/operators.md @@ -682,7 +682,7 @@ All success criteria from the original plan and subsequent releases were met: - ✅ 100% test coverage for operators module - ✅ Intelligent TypeScript autocomplete - ✅ Visual debugging capabilities -- ✅ Framework integrations (React, Vue, Svelte) +- ✅ Framework integrations (React, Vue) - ✅ Zero dependencies (except Zod for validation) ## Build & Release @@ -770,7 +770,7 @@ The MongoDB-style operators feature has been successfully evolved from v5.0.0 to - Multi-layer memoization (530x-1520x faster) - Lazy evaluation (500x faster for early exit) - Visual debugging with expression trees - - Framework integrations (React, Vue, Svelte) + - Framework integrations (React, Vue) - Array OR syntax - Geospatial distance calculations - Datetime utilities diff --git a/docs/index.md b/docs/index.md index 740212f..88ec389 100644 --- a/docs/index.md +++ b/docs/index.md @@ -62,7 +62,7 @@ features: - icon: 🎨 title: Framework Integration - details: React Hooks, Vue Composables, and Svelte Stores. First-class framework support. + details: React Hooks, Vue Composables, Angular Services, SolidJS, and Preact. First-class framework support. - icon: 🧪 title: Battle-Tested @@ -175,17 +175,6 @@ const { filtered, isFiltering } = useFilter(users, searchTerm); ``` -### Svelte - -```svelte - -``` ## Why Choose This Library? @@ -242,7 +231,7 @@ const { filtered, isFiltering } = useFilter(users, searchTerm);
🎨
Framework Agnostic
- Works everywhere: React, Vue, Svelte, Angular, Node.js, Deno, Bun. First-class hooks and composables included. + Works everywhere: React, Vue, Angular, Node.js, Deno, Bun. First-class hooks and composables included.
diff --git a/docs/project/changelog.md b/docs/project/changelog.md index 2368bc1..fcd3779 100644 --- a/docs/project/changelog.md +++ b/docs/project/changelog.md @@ -15,7 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added exhaustive examples for all 40+ operators (comparison, array, string, logical, geospatial, datetime) - Documented all Performance Monitoring, Type Helpers, and DateTime utilities - Completed configuration guide with orderBy and limit options - - Updated framework integration guides for React, Vue, Svelte, Angular, SolidJS, and Preact + - Updated framework integration guides for React, Vue, Angular, SolidJS, and Preact - **Performance Documentation**: Added detailed performance considerations section - Operator performance rankings (fastest to slowest) - Best practices for optimization @@ -117,7 +117,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Applied after filtering and sorting for predictable results - Works with all expression types and operators - Compatible with caching and debug modes - - Full framework integration support (React, Vue, Svelte, Angular, SolidJS, Preact) + - Full framework integration support (React, Vue, Angular, SolidJS, Preact) - **Complete Documentation**: - Angular integration guide with services, pipes, and SSR examples - SolidJS integration guide with signal patterns and SolidStart SSR @@ -294,7 +294,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Updated React integration documentation - Updated Vue integration documentation -- Updated Svelte integration documentation - Improved API reference clarity ### Fixed @@ -308,7 +307,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Pagination support for all framework integrations - `usePaginatedFilter` hook for React - `usePaginatedFilter` composable for Vue -- `usePaginatedFilter` store for Svelte - Pagination state management (`currentPage`, `totalPages`, `pageSize`) - Pagination actions (`nextPage`, `previousPage`, `goToPage`, `setPageSize`) @@ -326,21 +324,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Debounced filtering support - `useDebouncedFilter` hook for React - `useDebouncedFilter` composable for Vue -- `useDebouncedFilter` store for Svelte - `isPending` state for debounced operations - Configurable delay option ### Changed - Optimized re-render behavior in React - Improved reactivity in Vue -- Enhanced store updates in Svelte - ## [5.1.0] - 2024-07-05 ### Added - `useFilteredState` hook for React - `useFilteredState` composable for Vue -- `useFilteredState` store for Svelte - State management for data and expressions - Setter functions for dynamic updates @@ -446,7 +440,7 @@ See [Migration Guide v5.4](/guide/migration-v2) for detailed migration instructi ### Added - Modern operator-based API - TypeScript support -- Framework integrations (React, Vue, Svelte) +- Framework integrations (React, Vue) - Comprehensive test suite ### Changed diff --git a/docs/project/cheatsheet.md b/docs/project/cheatsheet.md index 5fa2fc8..c81c31e 100644 --- a/docs/project/cheatsheet.md +++ b/docs/project/cheatsheet.md @@ -462,15 +462,6 @@ const searchTerm = ref(''); const { filtered, isFiltering } = useFilter(users, searchTerm); ``` -### Svelte Stores -```typescript -import { writable } from 'svelte/store'; -import { useFilter } from '@mcabreradev/filter/svelte'; - -const searchTerm = writable(''); -const { filtered, isFiltering } = useFilter(users, searchTerm); -``` - --- ## Performance Tips diff --git a/docs/project/contributing.md b/docs/project/contributing.md index c358bf2..9f07c1e 100644 --- a/docs/project/contributing.md +++ b/docs/project/contributing.md @@ -109,7 +109,6 @@ filter/ │ ├── integrations/ # Framework integrations │ │ ├── react/ │ │ ├── vue/ -│ │ ├── svelte/ │ │ └── shared/ │ ├── predicate/ # Predicate functions │ ├── types/ # TypeScript type definitions @@ -441,7 +440,6 @@ When adding framework integrations: **React**: Use hooks, avoid class components **Vue**: Use Composition API -**Svelte**: Use stores ### 2. Maintain Type Safety @@ -485,7 +483,6 @@ console.timeEnd('filter'); - Implement lazy evaluation where appropriate - Avoid unnecessary re-renders in React - Minimize reactivity overhead in Vue -- Optimize store updates in Svelte ### Memory Management diff --git a/docs/project/roadmap.md b/docs/project/roadmap.md index e53e312..1b4d361 100644 --- a/docs/project/roadmap.md +++ b/docs/project/roadmap.md @@ -257,7 +257,7 @@ console.log('Execution time:', result.stats.executionTime); ### 🔴 Critical Priority - Completed #### Framework Integrations ✅ -**Epic**: React, Vue, Svelte Hooks +**Epic**: React, Vue Hooks **Effort**: 5-6 days **Impact**: 🔥 High **Status**: ✅ Completed @@ -265,11 +265,10 @@ console.log('Execution time:', result.stats.executionTime); **Deliverables**: - ✅ React integration (useFilter, useFilteredState, useDebouncedFilter, usePaginatedFilter) - ✅ Vue integration (Composition API composables) -- ✅ Svelte integration (Store-based filtering) - ✅ TypeScript support with full generics - ✅ 100% test coverage - ✅ Comprehensive documentation -- ✅ SSR compatibility (Next.js, Nuxt, SvelteKit) +- ✅ SSR compatibility (Next.js, Nuxt) **Success Metrics**: - ✅ All framework integrations completed @@ -670,7 +669,7 @@ docs/ ### 🔴 Critical Priority #### 13. Framework Integrations ✅ -**Epic**: React, Vue, Svelte Hooks +**Epic**: React, Vue Hooks **Effort**: 5-6 days **Impact**: 🔥 High **Status**: ✅ Completed @@ -694,15 +693,6 @@ docs/ - TypeScript support - Comprehensive tests - Examples and docs -- ✅ Svelte integration - - Store-based filtering - - `useFilter` store - - `useFilteredState` store - - `useDebouncedFilter` store - - `usePaginatedFilter` store - - TypeScript support - - Comprehensive tests - - Examples and docs - ✅ Framework comparison guide - ✅ Comprehensive documentation @@ -744,19 +734,6 @@ src/ vue.constants.ts vue.utils.ts index.ts - svelte/ - use-filter.ts - use-filter.test.ts - use-filtered-state.ts - use-filtered-state.test.ts - use-debounced-filter.ts - use-debounced-filter.test.ts - use-paginated-filter.ts - use-paginated-filter.test.ts - svelte.types.ts - svelte.constants.ts - svelte.utils.ts - index.ts docs/ FRAMEWORK_INTEGRATIONS.md ``` @@ -777,18 +754,11 @@ import { useFilter, usePaginatedFilter } from '@mcabreradev/filter'; const searchTerm = ref(''); const { filtered, isFiltering } = useFilter(users, searchTerm); -// Svelte -import { writable } from 'svelte/store'; -import { useFilter } from '@mcabreradev/filter'; - -const searchTerm = writable(''); -const { filtered, isFiltering } = useFilter(users, searchTerm); ``` **Success Metrics**: - ✅ React hooks implemented and tested (100% coverage) - ✅ Vue composables implemented and tested (100% coverage) -- ✅ Svelte stores implemented and tested (100% coverage) - ✅ Comprehensive documentation created - ✅ TypeScript support with full generics - ✅ SSR compatibility verified @@ -1042,7 +1012,7 @@ const mongoQuery = adapter.translate({ - **NPM Downloads**: 10K/month by Q4 2026 - **GitHub Stars**: 500+ by Q4 2026 - **Contributors**: 10+ active contributors -- **Framework Integrations**: ✅ React, Vue, Svelte (Completed) +- **Framework Integrations**: ✅ React, Vue (Completed) ### Quality Metrics - **Test Coverage**: ✅ Maintain 100% @@ -1090,10 +1060,9 @@ Features are prioritized based on: - ✅ v5.5.0: Array OR Syntax - ✅ v5.5.0: Visual Debugging (debug mode, tree visualization, performance metrics) - ✅ v5.5.0: Interactive Playground -- ✅ v5.4.0: Framework Integrations (React, Vue, Svelte) +- ✅ v5.4.0: Framework Integrations (React, Vue) - ✅ v5.4.0: React Hooks with full feature set - ✅ v5.4.0: Vue Composables with Composition API -- ✅ v5.4.0: Svelte Stores with reactivity - ✅ v5.4.0: Comprehensive framework documentation - ✅ v5.2.0: Enhanced memoization (530x-1520x faster) - ✅ v5.2.0: Logical operators ($and, $or, $not) diff --git a/package.json b/package.json index f1eb1cc..c9e5b73 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,6 @@ "performance", "react", "vue", - "svelte", "framework-integration", "type-safe", "zero-dependencies", @@ -99,7 +98,6 @@ "temporal-query", "react-hooks", "vue-composables", - "svelte-stores", "nextjs", "node", "nodejs", @@ -142,7 +140,6 @@ "rxjs": "^7.8.1", "size-limit": "^11.2.0", "solid-js": "^1.9.11", - "svelte": "^5.54.1", "tsd": "^0.33.0", "tsx": "^4.20.6", "typedoc": "^0.28.17", @@ -158,7 +155,6 @@ "preact": ">=10.0.0", "react": ">=18.0.0", "solid-js": ">=1.8.0", - "svelte": ">=4.0.0 || >=5.0.0", "vue": ">=3.0.0", "zod": ">=3.0.0" }, @@ -175,9 +171,6 @@ "solid-js": { "optional": true }, - "svelte": { - "optional": true - }, "vue": { "optional": true }, @@ -247,11 +240,6 @@ "import": "./build/integrations/vue/index.js", "default": "./build/integrations/vue/index.js" }, - "./svelte": { - "types": "./build/integrations/svelte/index.d.ts", - "import": "./build/integrations/svelte/index.js", - "default": "./build/integrations/svelte/index.js" - }, "./angular": { "types": "./build/integrations/angular/index.d.ts", "import": "./build/integrations/angular/index.js", diff --git a/src/index.ts b/src/index.ts index 0167025..5bb6477 100644 --- a/src/index.ts +++ b/src/index.ts @@ -172,21 +172,6 @@ export type { UsePaginatedFilterResult as UsePaginatedFilterResultVue, } from './integrations/vue/index.js'; -export { - useFilter as useFilterSvelte, - useFilteredState as useFilteredStateSvelte, - useDebouncedFilter as useDebouncedFilterSvelte, - usePaginatedFilter as usePaginatedFilterSvelte, -} from './integrations/svelte/index.js'; - -export type { - UseFilterResult as UseFilterResultSvelte, - UseFilteredStateResult as UseFilteredStateResultSvelte, - UseDebouncedFilterOptions as UseDebouncedFilterOptionsSvelte, - UseDebouncedFilterResult as UseDebouncedFilterResultSvelte, - UsePaginatedFilterResult as UsePaginatedFilterResultSvelte, -} from './integrations/svelte/index.js'; - export { useFilter, useFilteredState, diff --git a/src/integrations/svelte/index.ts b/src/integrations/svelte/index.ts deleted file mode 100644 index 58601b9..0000000 --- a/src/integrations/svelte/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -export { useFilter } from './use-filter'; -export { useFilteredState } from './use-filtered-state'; -export { useDebouncedFilter } from './use-debounced-filter'; -export { usePaginatedFilter } from './use-paginated-filter'; - -export type { - UseFilterResult, - UseFilteredStateResult, - UseDebouncedFilterOptions, - UseDebouncedFilterResult, - UsePaginatedFilterResult, -} from './svelte.types'; diff --git a/src/integrations/svelte/svelte.constants.ts b/src/integrations/svelte/svelte.constants.ts deleted file mode 100644 index 962143e..0000000 --- a/src/integrations/svelte/svelte.constants.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const DEFAULT_DEBOUNCE_DELAY = 300; -export const DEFAULT_PAGE_SIZE = 10; -export const DEFAULT_INITIAL_PAGE = 1; diff --git a/src/integrations/svelte/svelte.types.ts b/src/integrations/svelte/svelte.types.ts deleted file mode 100644 index 2110491..0000000 --- a/src/integrations/svelte/svelte.types.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { Readable, Writable } from 'svelte/store'; -import type { Expression, FilterOptions } from '../../types'; -import type { PaginationResult } from '../shared'; - -export interface UseFilterResult { - filtered: Readable; - isFiltering: Readable; -} - -export interface UseFilteredStateResult { - data: Writable; - expression: Writable>; - filtered: Readable; - isFiltering: Readable; -} - -export interface UseDebouncedFilterOptions extends FilterOptions { - delay?: number; -} - -export interface UseDebouncedFilterResult { - filtered: Readable; - isFiltering: Readable; - isPending: Readable; -} - -export interface UsePaginatedFilterResult { - filtered: Readable; - isFiltering: Readable; - pagination: Readable>; - currentPage: Writable; - pageSize: Writable; - nextPage: () => void; - previousPage: () => void; - goToPage: (page: number) => void; - setPageSize: (size: number) => void; -} diff --git a/src/integrations/svelte/svelte.utils.ts b/src/integrations/svelte/svelte.utils.ts deleted file mode 100644 index f891ce8..0000000 --- a/src/integrations/svelte/svelte.utils.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { Readable } from 'svelte/store'; - -export function isStore(value: unknown): value is Readable { - return ( - value !== null && - typeof value === 'object' && - 'subscribe' in value && - typeof (value as { subscribe: unknown }).subscribe === 'function' - ); -} - -export function getValue(value: T | Readable): T { - if (isStore(value)) { - let currentValue: T = undefined as never; - value.subscribe((v) => { - currentValue = v; - })(); - return currentValue; - } - return value; -} diff --git a/src/integrations/svelte/use-debounced-filter.test.ts b/src/integrations/svelte/use-debounced-filter.test.ts deleted file mode 100644 index 733c60c..0000000 --- a/src/integrations/svelte/use-debounced-filter.test.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { writable, get } from 'svelte/store'; -import { useDebouncedFilter } from './use-debounced-filter'; - -interface TestItem { - id: number; - name: string; - active: boolean; -} - -describe('useDebouncedFilter', () => { - const testData: TestItem[] = [ - { id: 1, name: 'Alice', active: true }, - { id: 2, name: 'Bob', active: false }, - { id: 3, name: 'Charlie', active: true }, - ]; - - beforeEach(() => { - vi.useFakeTimers(); - }); - - afterEach(() => { - vi.restoreAllMocks(); - }); - - it('should debounce filter expression changes', () => { - const data = writable(testData); - const expression = writable('Alice'); - const { filtered, isPending } = useDebouncedFilter(data, expression, { - delay: 300, - }); - - expect(get(isPending)).toBe(true); - - vi.advanceTimersByTime(300); - - expect(get(isPending)).toBe(false); - expect(get(filtered)).toHaveLength(1); - - expression.set('Bob'); - expect(get(isPending)).toBe(true); - - vi.advanceTimersByTime(300); - - expect(get(isPending)).toBe(false); - expect(get(filtered)).toHaveLength(1); - expect(get(filtered)[0].name).toBe('Bob'); - }); - - it('should use default delay', () => { - const data = writable(testData); - const expression = writable('Alice'); - const { filtered, isPending } = useDebouncedFilter(data, expression); - - expect(get(isPending)).toBe(true); - - vi.advanceTimersByTime(300); - - expect(get(isPending)).toBe(false); - expect(get(filtered)).toHaveLength(1); - }); - - it('should handle rapid expression changes', () => { - const data = writable(testData); - const expression = writable('Alice'); - const { filtered, isPending } = useDebouncedFilter(data, expression, { - delay: 300, - }); - - expression.set('Bob'); - vi.advanceTimersByTime(100); - - expression.set('Charlie'); - vi.advanceTimersByTime(100); - - expression.set('Alice'); - vi.advanceTimersByTime(300); - - expect(get(isPending)).toBe(false); - expect(get(filtered)).toHaveLength(1); - expect(get(filtered)[0].name).toBe('Alice'); - }); - - it('should handle object expressions', () => { - const data = writable(testData); - const expression = writable({ active: true }); - const { filtered } = useDebouncedFilter(data, expression, { delay: 300 }); - - vi.advanceTimersByTime(300); - - expect(get(filtered)).toHaveLength(2); - }); - - it('should handle predicate functions', () => { - const data = writable(testData); - const expression = writable((item: TestItem) => item.id > 1); - const { filtered } = useDebouncedFilter(data, expression, { delay: 300 }); - - vi.advanceTimersByTime(300); - - expect(get(filtered)).toHaveLength(2); - }); - - it('should respect filter options', () => { - const data = writable(testData); - const expression = writable('ALICE'); - const { filtered } = useDebouncedFilter(data, expression, { - delay: 300, - caseSensitive: true, - }); - - vi.advanceTimersByTime(300); - - expect(get(filtered)).toEqual([]); - }); - - it('should handle empty data', () => { - const data = writable([]); - const expression = writable('test'); - const { filtered } = useDebouncedFilter(data, expression, { delay: 300 }); - - vi.advanceTimersByTime(300); - - expect(get(filtered)).toEqual([]); - }); - - it('should indicate filtering status', () => { - const data = writable(testData); - const expression = writable({ active: true }); - const { isFiltering } = useDebouncedFilter(data, expression, { delay: 300 }); - - vi.advanceTimersByTime(300); - - expect(get(isFiltering)).toBe(true); - }); - - it('should work with non-store values', () => { - const expression = writable('Alice'); - const { filtered } = useDebouncedFilter(testData, expression, { delay: 300 }); - - vi.advanceTimersByTime(300); - - expect(get(filtered)).toHaveLength(1); - }); -}); diff --git a/src/integrations/svelte/use-debounced-filter.ts b/src/integrations/svelte/use-debounced-filter.ts deleted file mode 100644 index 0e968dc..0000000 --- a/src/integrations/svelte/use-debounced-filter.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { writable, derived, readable, type Readable } from 'svelte/store'; -import { filter } from '../../core'; -import type { Expression } from '../../types'; -import type { UseDebouncedFilterOptions, UseDebouncedFilterResult } from './svelte.types'; -import { DEFAULT_DEBOUNCE_DELAY } from './svelte.constants'; -import { debounce } from '../shared'; -import { isStore } from './svelte.utils'; - -export function useDebouncedFilter( - data: T[] | Readable, - expression: Expression | Readable>, - options?: UseDebouncedFilterOptions, -): UseDebouncedFilterResult { - const { delay = DEFAULT_DEBOUNCE_DELAY, ...filterOptions } = options || {}; - const debouncedExpression = writable>( - isStore(expression) ? ((() => true) as Expression) : expression, - ); - const isPending = writable(false); - - const dataStore = isStore(data) ? data : readable(data); - const expressionStore = isStore(expression) ? expression : readable(expression); - - const debouncedUpdate = debounce((expr: Expression) => { - debouncedExpression.set(expr); - isPending.set(false); - }, delay) as ((expr: Expression) => void) & { cancel: () => void }; - - if (isStore(expression)) { - expressionStore.subscribe((expr) => { - isPending.set(true); - debouncedUpdate(expr); - }); - } - - const filtered = derived([dataStore, debouncedExpression], ([$data, $expression]) => { - if (!$data || $data.length === 0) { - return [] as T[]; - } - - try { - return filter($data, $expression, filterOptions); - } catch { - return [] as T[]; - } - }) as Readable; - - const isFiltering = derived([dataStore, filtered], ([$data, $filtered]) => { - return $filtered.length !== $data.length; - }) as Readable; - - return { - filtered, - isFiltering, - isPending: { subscribe: isPending.subscribe }, - }; -} diff --git a/src/integrations/svelte/use-filter.test.ts b/src/integrations/svelte/use-filter.test.ts deleted file mode 100644 index a10c7c3..0000000 --- a/src/integrations/svelte/use-filter.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { writable, get } from 'svelte/store'; -import { useFilter } from './use-filter'; - -interface TestItem { - id: number; - name: string; - active: boolean; -} - -describe('useFilter', () => { - const testData: TestItem[] = [ - { id: 1, name: 'Alice', active: true }, - { id: 2, name: 'Bob', active: false }, - { id: 3, name: 'Charlie', active: true }, - ]; - - it('should filter data with string expression', () => { - const data = writable(testData); - const expression = writable('Alice'); - const { filtered, isFiltering } = useFilter(data, expression); - - expect(get(filtered)).toEqual([{ id: 1, name: 'Alice', active: true }]); - expect(get(isFiltering)).toBe(true); - }); - - it('should filter data with object expression', () => { - const data = writable(testData); - const expression = writable({ active: true }); - const { filtered, isFiltering } = useFilter(data, expression); - - expect(get(filtered)).toHaveLength(2); - expect(get(isFiltering)).toBe(true); - }); - - it('should filter data with predicate function', () => { - const data = writable(testData); - const expression = writable((item: TestItem) => item.id > 1); - const { filtered } = useFilter(data, expression); - - expect(get(filtered)).toHaveLength(2); - }); - - it('should return empty array for empty data', () => { - const data = writable([]); - const expression = writable('test'); - const { filtered, isFiltering } = useFilter(data, expression); - - expect(get(filtered)).toEqual([]); - expect(get(isFiltering)).toBe(false); - }); - - it('should handle invalid expression gracefully', () => { - const data = writable(testData); - const expression = writable(null as never); - const { filtered } = useFilter(data, expression); - - expect(get(filtered)).toEqual([]); - }); - - it('should react to data changes', () => { - const data = writable(testData); - const expression = writable({ active: true }); - const { filtered } = useFilter(data, expression); - - expect(get(filtered)).toHaveLength(2); - - data.set([...testData, { id: 4, name: 'David', active: true }]); - - expect(get(filtered)).toHaveLength(3); - }); - - it('should react to expression changes', () => { - const data = writable(testData); - const expression = writable>({ active: true }); - const { filtered } = useFilter(data, expression); - - expect(get(filtered)).toHaveLength(2); - - expression.set({ active: false }); - - expect(get(filtered)).toHaveLength(1); - }); - - it('should respect filter options', () => { - const data = writable(testData); - const expression = writable('ALICE'); - const { filtered } = useFilter(data, expression, { caseSensitive: true }); - - expect(get(filtered)).toEqual([]); - }); - - it('should work with non-store values', () => { - const { filtered } = useFilter(testData, 'Alice'); - - expect(get(filtered)).toHaveLength(1); - }); - - it('should indicate when not filtering', () => { - const data = writable(testData); - const expression = writable(() => true); - const { filtered, isFiltering } = useFilter(data, expression); - - expect(get(filtered)).toHaveLength(3); - expect(get(isFiltering)).toBe(false); - }); -}); diff --git a/src/integrations/svelte/use-filter.ts b/src/integrations/svelte/use-filter.ts deleted file mode 100644 index 441010c..0000000 --- a/src/integrations/svelte/use-filter.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { derived, readable, type Readable } from 'svelte/store'; -import { filter } from '../../core'; -import type { Expression, FilterOptions } from '../../types'; -import type { UseFilterResult } from './svelte.types'; -import { isStore } from './svelte.utils'; - -export function useFilter( - data: T[] | Readable, - expression: Expression | Readable>, - options?: FilterOptions, -): UseFilterResult { - const dataStore = isStore(data) ? data : readable(data); - const expressionStore = isStore(expression) ? expression : readable(expression); - - const filtered = derived([dataStore, expressionStore], ([$data, $expression]) => { - if (!$data || $data.length === 0) { - return [] as T[]; - } - - try { - return filter($data, $expression, options); - } catch { - return [] as T[]; - } - }) as Readable; - - const isFiltering = derived([dataStore, filtered], ([$data, $filtered]) => { - return $filtered.length !== $data.length; - }) as Readable; - - return { - filtered, - isFiltering, - }; -} diff --git a/src/integrations/svelte/use-filtered-state.test.ts b/src/integrations/svelte/use-filtered-state.test.ts deleted file mode 100644 index 3904f98..0000000 --- a/src/integrations/svelte/use-filtered-state.test.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { get } from 'svelte/store'; -import { useFilteredState } from './use-filtered-state'; - -interface TestItem { - id: number; - name: string; - active: boolean; -} - -describe('useFilteredState', () => { - const testData: TestItem[] = [ - { id: 1, name: 'Alice', active: true }, - { id: 2, name: 'Bob', active: false }, - { id: 3, name: 'Charlie', active: true }, - ]; - - it('should initialize with default values', () => { - const { data, filtered, isFiltering } = useFilteredState(); - - expect(get(data)).toEqual([]); - expect(get(filtered)).toEqual([]); - expect(get(isFiltering)).toBe(false); - }); - - it('should initialize with provided data', () => { - const { data, filtered } = useFilteredState(testData); - - expect(get(data)).toEqual(testData); - expect(get(filtered)).toEqual(testData); - }); - - it('should filter data with initial expression', () => { - const { filtered, isFiltering } = useFilteredState(testData, { active: true }); - - expect(get(filtered)).toHaveLength(2); - expect(get(isFiltering)).toBe(true); - }); - - it('should update data', () => { - const { data, filtered } = useFilteredState(testData); - - data.set([{ id: 4, name: 'David', active: false }]); - - expect(get(data)).toHaveLength(1); - expect(get(data)[0].name).toBe('David'); - expect(get(filtered)).toHaveLength(1); - }); - - it('should update expression', () => { - const { expression, filtered } = useFilteredState(testData, { active: true }); - - expect(get(filtered)).toHaveLength(2); - - expression.set({ active: false }); - - expect(get(filtered)).toHaveLength(1); - }); - - it('should update filtered results when data changes', () => { - const { data, filtered } = useFilteredState(testData, { active: true }); - - expect(get(filtered)).toHaveLength(2); - - data.set([...testData, { id: 4, name: 'David', active: true }]); - - expect(get(filtered)).toHaveLength(3); - }); - - it('should update filtered results when expression changes', () => { - const { expression, filtered } = useFilteredState(testData); - - expect(get(filtered)).toHaveLength(3); - - expression.set('Alice'); - - expect(get(filtered)).toHaveLength(1); - }); - - it('should handle predicate function expressions', () => { - const { expression, filtered } = useFilteredState(testData, (item) => item.id > 1); - - expect(get(filtered)).toHaveLength(2); - - expression.set((item) => item.id === 1); - - expect(get(filtered)).toHaveLength(1); - }); - - it('should respect filter options', () => { - const { filtered } = useFilteredState(testData, 'ALICE', { - caseSensitive: true, - }); - - expect(get(filtered)).toEqual([]); - }); - - it('should handle empty data gracefully', () => { - const { filtered, isFiltering } = useFilteredState([], 'test'); - - expect(get(filtered)).toEqual([]); - expect(get(isFiltering)).toBe(false); - }); - - it('should be reactive', () => { - const { data, expression, filtered } = useFilteredState(testData); - - expect(get(filtered)).toHaveLength(3); - - expression.set({ active: true }); - expect(get(filtered)).toHaveLength(2); - - data.set(testData.slice(0, 1)); - expect(get(filtered)).toHaveLength(1); - }); -}); diff --git a/src/integrations/svelte/use-filtered-state.ts b/src/integrations/svelte/use-filtered-state.ts deleted file mode 100644 index 935db3c..0000000 --- a/src/integrations/svelte/use-filtered-state.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { writable, derived } from 'svelte/store'; -import { filter } from '../../core'; -import type { Expression, FilterOptions } from '../../types'; -import type { UseFilteredStateResult } from './svelte.types'; - -export function useFilteredState( - initialData: T[] = [], - initialExpression: Expression = () => true, - options?: FilterOptions, -): UseFilteredStateResult { - const data = writable(initialData); - const expression = writable>(initialExpression); - - const filtered = derived([data, expression], ([$data, $expression]) => { - if (!$data || $data.length === 0) { - return []; - } - - try { - return filter($data, $expression, options); - } catch { - return []; - } - }); - - const isFiltering = derived([data, filtered], ([$data, $filtered]) => { - return $filtered.length !== $data.length; - }); - - return { - data, - expression, - filtered, - isFiltering, - }; -} diff --git a/src/integrations/svelte/use-paginated-filter.test.ts b/src/integrations/svelte/use-paginated-filter.test.ts deleted file mode 100644 index 9325bee..0000000 --- a/src/integrations/svelte/use-paginated-filter.test.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { writable, get } from 'svelte/store'; -import { usePaginatedFilter } from './use-paginated-filter'; - -interface TestItem { - id: number; - name: string; - active: boolean; -} - -describe('usePaginatedFilter', () => { - const testData: TestItem[] = Array.from({ length: 25 }, (_, i) => ({ - id: i + 1, - name: `User ${i + 1}`, - active: i % 2 === 0, - })); - - it('should paginate filtered data', () => { - const data = writable(testData); - const expression = writable(() => true); - const { pagination } = usePaginatedFilter(data, expression, 10); - - const paginationValue = get(pagination); - expect(paginationValue.data).toHaveLength(10); - expect(paginationValue.currentPage).toBe(1); - expect(paginationValue.totalPages).toBe(3); - expect(paginationValue.totalItems).toBe(25); - }); - - it('should navigate to next page', () => { - const data = writable(testData); - const expression = writable(() => true); - const { pagination, nextPage, currentPage } = usePaginatedFilter(data, expression, 10); - - nextPage(); - - expect(get(currentPage)).toBe(2); - expect(get(pagination).data[0].id).toBe(11); - }); - - it('should navigate to previous page', () => { - const data = writable(testData); - const expression = writable(() => true); - const { previousPage, nextPage, currentPage } = usePaginatedFilter(data, expression, 10); - - nextPage(); - previousPage(); - - expect(get(currentPage)).toBe(1); - }); - - it('should go to specific page', () => { - const data = writable(testData); - const expression = writable(() => true); - const { pagination, goToPage, currentPage } = usePaginatedFilter(data, expression, 10); - - goToPage(3); - - expect(get(currentPage)).toBe(3); - expect(get(pagination).data).toHaveLength(5); - }); - - it('should not exceed total pages', () => { - const data = writable(testData); - const expression = writable(() => true); - const { goToPage, currentPage } = usePaginatedFilter(data, expression, 10); - - goToPage(10); - - expect(get(currentPage)).toBe(3); - }); - - it('should not go below page 1', () => { - const data = writable(testData); - const expression = writable(() => true); - const { previousPage, currentPage } = usePaginatedFilter(data, expression, 10); - - previousPage(); - - expect(get(currentPage)).toBe(1); - }); - - it('should change page size', () => { - const data = writable(testData); - const expression = writable(() => true); - const { pagination, setPageSize, currentPage, pageSize } = usePaginatedFilter( - data, - expression, - 10, - ); - - setPageSize(5); - - expect(get(pageSize)).toBe(5); - expect(get(pagination).data).toHaveLength(5); - expect(get(pagination).totalPages).toBe(5); - expect(get(currentPage)).toBe(1); - }); - - it('should filter and paginate', () => { - const data = writable(testData); - const expression = writable({ active: true }); - const { pagination, isFiltering } = usePaginatedFilter(data, expression, 5); - - const paginationValue = get(pagination); - expect(paginationValue.totalItems).toBe(13); - expect(paginationValue.totalPages).toBe(3); - expect(get(isFiltering)).toBe(true); - }); - - it('should update pagination when filter changes', () => { - const data = writable(testData); - const expression = writable<{ active: boolean }>({ active: true }); - const { pagination } = usePaginatedFilter(data, expression, 5); - - expect(get(pagination).totalItems).toBe(13); - - expression.set({ active: false }); - - expect(get(pagination).totalItems).toBe(12); - }); - - it('should indicate next page availability', () => { - const data = writable(testData); - const expression = writable(() => true); - const { pagination, goToPage } = usePaginatedFilter(data, expression, 10); - - expect(get(pagination).hasNextPage).toBe(true); - - goToPage(3); - - expect(get(pagination).hasNextPage).toBe(false); - }); - - it('should indicate previous page availability', () => { - const data = writable(testData); - const expression = writable(() => true); - const { pagination, nextPage } = usePaginatedFilter(data, expression, 10); - - expect(get(pagination).hasPreviousPage).toBe(false); - - nextPage(); - - expect(get(pagination).hasPreviousPage).toBe(true); - }); - - it('should handle empty data', () => { - const data = writable([]); - const expression = writable(() => true); - const { pagination } = usePaginatedFilter(data, expression, 10); - - const paginationValue = get(pagination); - expect(paginationValue.data).toEqual([]); - expect(paginationValue.totalPages).toBe(0); - expect(paginationValue.totalItems).toBe(0); - }); - - it('should handle single page', () => { - const smallData = testData.slice(0, 5); - const data = writable(smallData); - const expression = writable(() => true); - const { pagination } = usePaginatedFilter(data, expression, 10); - - const paginationValue = get(pagination); - expect(paginationValue.data).toHaveLength(5); - expect(paginationValue.totalPages).toBe(1); - expect(paginationValue.hasNextPage).toBe(false); - expect(paginationValue.hasPreviousPage).toBe(false); - }); - - it('should validate page size', () => { - const data = writable(testData); - const expression = writable(() => true); - const { pageSize } = usePaginatedFilter(data, expression, -5); - - expect(get(pageSize)).toBe(1); - }); - - it('should reset to page 1 when changing page size', () => { - const data = writable(testData); - const expression = writable(() => true); - const { goToPage, setPageSize, currentPage } = usePaginatedFilter(data, expression, 10); - - goToPage(2); - setPageSize(5); - - expect(get(currentPage)).toBe(1); - }); - - it('should work with non-store values', () => { - const { pagination } = usePaginatedFilter(testData, () => true, 10); - - expect(get(pagination).data).toHaveLength(10); - }); -}); diff --git a/src/integrations/svelte/use-paginated-filter.ts b/src/integrations/svelte/use-paginated-filter.ts deleted file mode 100644 index 0a58983..0000000 --- a/src/integrations/svelte/use-paginated-filter.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { writable, derived, readable, get, type Readable } from 'svelte/store'; -import { filter } from '../../core'; -import type { Expression, FilterOptions } from '../../types'; -import type { UsePaginatedFilterResult } from './svelte.types'; -import { DEFAULT_PAGE_SIZE, DEFAULT_INITIAL_PAGE } from './svelte.constants'; -import { - getPageData, - createPaginationResult, - validatePageNumber, - validatePageSize, - type PaginationResult, -} from '../shared'; -import { isStore } from './svelte.utils'; - -export function usePaginatedFilter( - data: T[] | Readable, - expression: Expression | Readable>, - initialPageSize: number = DEFAULT_PAGE_SIZE, - options?: FilterOptions, -): UsePaginatedFilterResult { - const currentPage = writable(DEFAULT_INITIAL_PAGE); - const pageSize = writable(validatePageSize(initialPageSize)); - - const dataStore = isStore(data) ? data : readable(data); - const expressionStore = isStore(expression) ? expression : readable(expression); - - const filtered = derived([dataStore, expressionStore], ([$data, $expression]) => { - if (!$data || $data.length === 0) { - return [] as T[]; - } - - try { - return filter($data, $expression, options); - } catch { - return [] as T[]; - } - }) as Readable; - - const isFiltering = derived([dataStore, filtered], ([$data, $filtered]) => { - return $filtered.length !== $data.length; - }) as Readable; - - const paginationState = derived( - [currentPage, pageSize, filtered], - ([$currentPage, $pageSize, $filtered]) => ({ - currentPage: $currentPage, - pageSize: $pageSize, - totalItems: ($filtered as T[]).length, - }), - ); - - const pageData = derived( - [filtered, currentPage, pageSize], - ([$filtered, $currentPage, $pageSize]) => { - return getPageData($filtered as T[], $currentPage, $pageSize); - }, - ); - - const pagination = derived( - [pageData, filtered, paginationState], - ([$pageData, $filtered, $paginationState]) => { - return createPaginationResult($pageData, $filtered as T[], $paginationState); - }, - ) as Readable>; - - const nextPage = (): void => { - currentPage.update((page) => { - const totalPages = get(pagination).totalPages; - return validatePageNumber(page + 1, totalPages); - }); - }; - - const previousPage = (): void => { - currentPage.update((page) => { - const totalPages = get(pagination).totalPages; - return validatePageNumber(page - 1, totalPages); - }); - }; - - const goToPage = (page: number): void => { - const totalPages = get(pagination).totalPages; - const validated = validatePageNumber(page, totalPages); - currentPage.set(validated); - }; - - const setPageSize = (size: number): void => { - const validated = validatePageSize(size); - pageSize.set(validated); - currentPage.set(DEFAULT_INITIAL_PAGE); - }; - - return { - filtered, - isFiltering, - pagination, - currentPage, - pageSize, - nextPage, - previousPage, - goToPage, - setPageSize, - }; -} From d996095deb341c4d9942c6f7ba5e32fb88b1a5c4 Mon Sep 17 00:00:00 2001 From: Miguelangel Cabrera Date: Sat, 21 Mar 2026 19:37:48 -0300 Subject: [PATCH 2/4] refactor: streamline pre-commit checks and update package.json scripts --- .husky/pre-commit | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 87c6b93..f6e6e4e 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -5,5 +5,7 @@ pnpm exec lint-staged echo "📝 Type checking..." pnpm run typecheck +pnpm run test +pnpm run test:docs echo "✅ Pre-commit checks passed!" diff --git a/package.json b/package.json index c9e5b73..6da0eb7 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "size": "size-limit", "size:why": "size-limit --why", "analyze": "pnpm run size:why", - "check": "pnpm exec lint-staged && pnpm run test && pnpm run test:types && pnpm run typecheck && pnpm run lint && pnpm run test:docs", + "check": "pnpm exec lint-staged && pnpm run test:types && pnpm run typecheck && pnpm run lint", "prepare": "husky", "prepublish": "pnpm run check", "version": "pnpm run format && git add src/", From 20cc26a942ff7ca3123ad69e853f68753d33c184 Mon Sep 17 00:00:00 2001 From: Miguelangel Cabrera Date: Sat, 21 Mar 2026 19:43:43 -0300 Subject: [PATCH 3/4] chore: sync pnpm lockfile after svelte removal --- pnpm-lock.yaml | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 85994a9..1df2b76 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -107,9 +107,6 @@ importers: solid-js: specifier: ^1.9.11 version: 1.9.11 - svelte: - specifier: ^5.54.1 - version: 5.54.1 tsd: specifier: ^0.33.0 version: 0.33.0 @@ -3651,6 +3648,7 @@ snapshots: dependencies: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 + optional: true '@jridgewell/resolve-uri@3.1.2': {} @@ -3833,6 +3831,7 @@ snapshots: '@sveltejs/acorn-typescript@1.0.6(acorn@8.15.0)': dependencies: acorn: 8.15.0 + optional: true '@testing-library/dom@10.4.1': dependencies: @@ -3945,7 +3944,8 @@ snapshots: '@types/prop-types': 15.7.15 csstype: 3.1.3 - '@types/trusted-types@2.0.7': {} + '@types/trusted-types@2.0.7': + optional: true '@types/unist@2.0.11': {} @@ -4318,7 +4318,8 @@ snapshots: dependencies: dequal: 2.0.3 - aria-query@5.3.1: {} + aria-query@5.3.1: + optional: true array-buffer-byte-length@1.0.2: dependencies: @@ -4347,7 +4348,8 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axobject-query@4.1.0: {} + axobject-query@4.1.0: + optional: true bail@1.0.5: {} @@ -4443,7 +4445,8 @@ snapshots: slice-ansi: 7.1.2 string-width: 8.1.0 - clsx@2.1.1: {} + clsx@2.1.1: + optional: true color-convert@2.0.1: dependencies: @@ -4558,7 +4561,8 @@ snapshots: dequal@2.0.3: {} - devalue@5.6.4: {} + devalue@5.6.4: + optional: true devlop@1.1.0: dependencies: @@ -4822,7 +4826,8 @@ snapshots: transitivePeerDependencies: - supports-color - esm-env@1.2.2: {} + esm-env@1.2.2: + optional: true espree@10.4.0: dependencies: @@ -4838,6 +4843,7 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 '@typescript-eslint/types': 8.57.1 + optional: true esrecurse@4.3.0: dependencies: @@ -5220,6 +5226,7 @@ snapshots: is-reference@3.0.3: dependencies: '@types/estree': 1.0.8 + optional: true is-regex@1.2.1: dependencies: @@ -5391,7 +5398,8 @@ snapshots: rfdc: 1.4.1 wrap-ansi: 9.0.2 - locate-character@3.0.0: {} + locate-character@3.0.0: + optional: true locate-path@5.0.0: dependencies: @@ -6280,6 +6288,7 @@ snapshots: locate-character: 3.0.0 magic-string: 0.30.21 zimmerframe: 1.1.4 + optional: true symbol-tree@3.2.4: {} @@ -6741,7 +6750,8 @@ snapshots: yocto-queue@0.1.0: {} - zimmerframe@1.1.4: {} + zimmerframe@1.1.4: + optional: true zod@4.1.12: {} From 60fc69e933c172c8325e473e81d58bcd62d025eb Mon Sep 17 00:00:00 2001 From: Miguelangel Cabrera Date: Sat, 21 Mar 2026 19:51:42 -0300 Subject: [PATCH 4/4] chore: update changelog for Svelte removal and workflow improvements --- README.md | 307 +++++++++++++++++++++----------------- docs/project/changelog.md | 76 +++++++++- 2 files changed, 246 insertions(+), 137 deletions(-) diff --git a/README.md b/README.md index b7ac547..3a872cf 100644 --- a/README.md +++ b/README.md @@ -32,63 +32,66 @@

--- + + ## Table of Contents -- [@mcabreradev/filter](#mcabreradevfilter) - - [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.8.2 (Current)](#v582-current) - - [v5.8.0](#v580) - - [v5.7.0](#v570) - - [v5.6.0](#v560) - - [v5.5.0](#v550) - - [Contributing](#contributing) - - [License](#license) - - [Support](#support) +- [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) @@ -99,24 +102,27 @@ **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 +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 } + createdAt: { $gte: thirtyDaysAgo }, }); ``` @@ -146,7 +152,7 @@ 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 } + { name: 'Charlie', age: 35, city: 'Berlin', active: true }, ]; // Simple string search — scans all fields @@ -173,43 +179,51 @@ const startsWithAl = filter(users, 'Al%'); ## 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 @@ -228,10 +242,10 @@ filter(products, 'Laptop'); 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 +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); @@ -253,15 +267,12 @@ filter(products, { sizes: { $size: 3 } }); filter(users, { email: { $endsWith: '@company.com' }, name: { $startsWith: 'John' }, - bio: { $regex: /developer/i } + bio: { $regex: /developer/i }, }); // Logical combinators filter(products, { - $and: [ - { inStock: true }, - { $or: [{ rating: { $gte: 4.5 } }, { price: { $lt: 50 } }] } - ] + $and: [{ inStock: true }, { $or: [{ rating: { $gte: 4.5 } }, { price: { $lt: 50 } }] }], }); // Negate with $not @@ -278,7 +289,7 @@ filter(products, { category: ['Electronics', 'Books'] }); // Combine across fields filter(users, { city: ['Berlin', 'Paris', 'London'], - role: ['admin', 'moderator'] + role: ['admin', 'moderator'], }); ``` @@ -292,7 +303,7 @@ 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 } + rating: { $gte: 4.5 }, }); // Bounding box search @@ -300,9 +311,9 @@ filter(places, { location: { $geoBox: { topLeft: { lat: 53.0, lng: 13.0 }, - bottomRight: { lat: 52.0, lng: 14.0 } - } - } + bottomRight: { lat: 52.0, lng: 14.0 }, + }, + }, }); ``` @@ -317,8 +328,8 @@ 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 + date: { $dayOfWeek: [1, 2, 3, 4, 5] }, // Mon–Fri + startTime: { $timeOfDay: { start: 9, end: 17 } }, // 9am–5pm }); // Users of age 18–65 @@ -338,15 +349,15 @@ filter(tasks, { dueDate: { $isBefore: new Date('2025-12-31') } }); const results = filter(largeDataset, expression, { enableCache: true, orderBy: { field: 'price', direction: 'desc' }, - limit: 100 + 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 +const hasAdmin = filterExists(users, { role: 'admin' }); // exits on first match +const activeCount = filterCount(users, { active: true }); // no array allocated ``` ### Real-World: E-commerce Search @@ -368,24 +379,28 @@ const results = filter(products, { category: 'Electronics', price: { $lte: 1000 }, rating: { $gte: 4.5 }, - inStock: true + inStock: true, }); // Full-text search with brand filter const searchResults = filter(products, { name: { $contains: 'laptop' }, brand: ['Apple', 'Dell', 'HP'], - price: { $gte: 500, $lte: 2000 } + price: { $gte: 500, $lte: 2000 }, }); // Sorted and paginated results -const page1 = filter(products, { category: 'Electronics', inStock: true }, { - orderBy: [ - { field: 'price', direction: 'asc' }, - { field: 'rating', direction: 'desc' } - ], - limit: 20 -}); +const page1 = filter( + products, + { category: 'Electronics', inStock: true }, + { + orderBy: [ + { field: 'price', direction: 'asc' }, + { field: 'rating', direction: 'desc' }, + ], + limit: 20, + }, +); ``` --- @@ -468,7 +483,7 @@ import { FilterService } from '@mcabreradev/filter/angular'; @for (user of filterService.filtered(); track user.id) {
{{ user.name }}
} - ` + `, }) export class UserListComponent { filterService = inject(FilterService); @@ -483,7 +498,7 @@ import { useFilter } from '@mcabreradev/filter/solidjs'; function UserList() { const { filtered } = useFilter( () => users, - () => ({ active: true }) + () => ({ active: true }), ); return {(u) =>
{u.name}
}
; } @@ -496,11 +511,18 @@ import { useFilter } from '@mcabreradev/filter/preact'; function UserList() { const { filtered } = useFilter(users, { active: true }); - return
{filtered.map(u =>
{u.name}
)}
; + return ( +
+ {filtered.map((u) => ( +
{u.name}
+ ))} +
+ ); } ``` **Every integration includes:** + - ✅ Full TypeScript generics - ✅ Debounced search hook with `isPending` state - ✅ Pagination hook with `nextPage`, `prevPage`, `goToPage` @@ -515,14 +537,14 @@ function UserList() { ### 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` | +| 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. @@ -538,10 +560,10 @@ interface Product { } filter(products, { - price: { $gte: 100 }, // ✅ number operators + price: { $gte: 100 }, // ✅ number operators name: { $contains: '' }, // ✅ string operators - tags: { $size: 3 }, // ✅ array operators - price: { $contains: '' } // ❌ TypeScript error — string op on number field + tags: { $size: 3 }, // ✅ array operators + price: { $contains: '' }, // ❌ TypeScript error — string op on number field }); ``` @@ -549,15 +571,15 @@ filter(products, { ```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 + 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 }); ``` @@ -589,11 +611,11 @@ const hasBanned = filterExists(users, { role: 'banned' }); 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 | ❌ | ✅ | +| 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)** @@ -609,11 +631,11 @@ const results = filter(largeDataset, { age: { $gte: 18 } }, { enableCache: true 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** | +| 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. @@ -665,13 +687,13 @@ filter(users, { city: 'Berlin', age: { $gte: 18 } }, { debug: true }); ## 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 | +| 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 @@ -686,12 +708,12 @@ 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 | ✅ | +| Import | Size (gzipped) | Tree-Shakeable | +| --------------- | -------------- | -------------- | +| Full | 12 KB | ✅ | +| Core only | 8.4 KB | ✅ | +| React hooks | 9.2 KB | ✅ | +| Lazy evaluation | 5.4 KB | ✅ | --- @@ -728,7 +750,19 @@ filter(data, expression, { enableCache: true, limit: 50 }); ## Changelog -### v5.8.2 (Current) +### 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 @@ -773,6 +807,7 @@ filter(data, expression, { enableCache: true, limit: 50 }); 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 diff --git a/docs/project/changelog.md b/docs/project/changelog.md index fcd3779..74267cb 100644 --- a/docs/project/changelog.md +++ b/docs/project/changelog.md @@ -5,9 +5,29 @@ All notable changes to @mcabreradev/filter are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [5.9.2] - 2026-03-21 + +### Changed + +- Removed Svelte integration support from package exports and dependency declarations. +- Updated project scripts and docs references to reflect framework support changes. + +### Fixed + +- Synced `pnpm-lock.yaml` with `package.json` after Svelte removal to fix CI install failures with `--frozen-lockfile`. +- Improved release workflow reliability for npm publish and GitHub release generation. + +## [5.9.1] - 2026-03-21 + +### Fixed + +- Stabilized release automation to avoid tag/version collisions during patch publishing. +- Hardened npm publishing workflow and release orchestration. + ## [5.8.2] - 2025-11-17 ### Documentation + - **Comprehensive Documentation Update**: Complete overhaul of documentation structure - Consolidated all operator documentation into single comprehensive guide - Expanded API Reference with 40+ exported functions documented @@ -23,6 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Lazy evaluation patterns ### Changed + - Updated documentation structure for better navigation and discoverability - Improved TypeScript examples with comprehensive type coverage - Enhanced operator examples with real-world use cases @@ -30,20 +51,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [5.8.1] - 2025-11-15 ### Fixed + - Minor documentation fixes and typo corrections - Improved error messages for invalid geospatial coordinates - Fixed type inference for nested object expressions ### Changed + - Updated dependencies to latest stable versions - Improved bundle size optimization ## [5.8.3] - 2025-11-26 ### Bug Fixes + - **Cache**: Fixed critical issue where `limit` option was ignored in cache key. Requests with different limits now correctly generate distinct cache keys. ### Performance Improvements + - **Memory**: Replaced unbounded `Map` caches with `LRUCache` strategy. - `FilterCache`: Limited to 100 entries per source array. - `RegexCache`: Limited to 500 compiled patterns. @@ -52,6 +77,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [5.8.0] - 2025-11-10 ### Added + - **OrderBy and Limit Options**: Enhanced configuration system - Full support for single and multi-field sorting - Nested path sorting with dot notation (e.g., 'profile.age') @@ -63,11 +89,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Full generic support for custom types ### Changed + - Optimized internal sorting algorithms for better performance - Improved cache key generation for orderBy and limit combinations - Updated all framework integrations to support orderBy and limit ### Performance + - OrderBy uses stable sort algorithm (10-15% faster than v5.7.0) - Limit applies after filtering/sorting (minimal overhead) - Cache efficiency improved for complex queries with orderBy @@ -75,6 +103,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [5.7.1] - 2025-11-08 ### Fixed + - **$contains Operator**: Fixed type detection edge cases - Improved string vs array context detection - Better handling of undefined values @@ -85,12 +114,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed Preact hook re-render optimization ### Changed + - Improved error messages for invalid expressions - Better TypeScript type inference for logical operators ## [5.7.0] - 2025-11-06 ### Added + - **Framework Integrations**: Angular, SolidJS, and Preact support - **Angular**: Services and Pipes with Signals support - `FilterService`: Core filtering service with Signal-based state @@ -127,6 +158,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated Quick Start guide with new features ### Fixed + - **$contains Operator**: Fixed type detection to distinguish between string and array contexts - Enhanced `hasArrayOps` to check actual value types (not just operator presence) - Enhanced `hasStringOps` to check actual value types @@ -134,17 +166,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed edge case where undefined properties would incorrectly pass $contains checks ### Changed + - Updated framework integrations documentation to v5.7.0 - Updated README with all new features (Angular, SolidJS, Preact, orderBy, limit) - Updated configuration guide with orderBy and limit sections - Improved Quick Start guide with sorting and limiting examples ### Performance + - OrderBy uses efficient comparison functions - Limit applies slice operation after filtering (minimal overhead) - Framework integrations use proper memoization strategies ### Testing + - Added 33 comprehensive tests for limit functionality - Added tests for orderBy with limit combinations - Fixed integration tests for $contains operator with proper type checking @@ -154,6 +189,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [5.6.0] - 2025-11-01 ### Added + - **Geospatial Operators**: Location-based filtering with three powerful spatial operators - `$near`: Find points within radius with optional min/max distance - `$geoBox`: Bounding box queries for rectangular areas @@ -181,24 +217,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - IoT device monitoring patterns ### Changed + - Updated operator count from 18+ to 30+ operators - Enhanced type system to recognize GeoPoint and Date types - Improved autocomplete for geospatial and datetime operators - Extended constants to include all operator keys ### Performance + - Fast distance calculation using spherical law of cosines - Efficient ray casting algorithm for polygon containment - Optimized bounding box checks - Compatible with lazy evaluation for large datasets ### Testing + - Added 26 new comprehensive tests - Total test count: 523 tests (previously 497) - 100% code coverage for geospatial features - Edge case testing for invalid coordinates and missing data ### Datetime Operators + - **Relative Time Filtering**: Filter by last/next N days/hours/minutes - `$recent`: Find events in the last N days/hours/minutes - `$upcoming`: Find events in the next N days/hours/minutes @@ -228,6 +268,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Weekend/weekday filtering patterns ### Testing (Enhanced) + - Added 90 new comprehensive tests for datetime operators - Total test count: 613+ tests (previously 523) - 100% code coverage for all datetime features @@ -236,11 +277,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [5.5.1] - 2025-10-30 ### Fixed + - Bug fixes and stability improvements - Build optimization issues - Type definition exports ### Changed + - Updated documentation with latest features - Improved error messages - Enhanced performance for array operations @@ -248,6 +291,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [5.5.0] - 2025-10-28 ### Added + - **Array OR Syntax**: Intuitive array-based OR filtering without explicit `$in` operator - `filter(products, { category: ['Electronics', 'Books'] })` - Supports wildcards within array values @@ -270,12 +314,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Expression tree visualization ### Changed + - Enhanced array operator performance - Improved type inference for array expressions - Optimized debug tree building - Better error messages for invalid expressions ### Fixed + - Edge cases with empty arrays in expressions - Type inference issues with array OR syntax - Debug output formatting inconsistencies @@ -283,6 +329,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [5.4.0] - 2024-10-26 ### Added + - Comprehensive documentation overhaul - Migration guide for v5.4 - Framework-specific SSR guides @@ -292,11 +339,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Best practices guide ### Changed + - Updated React integration documentation - Updated Vue integration documentation - Improved API reference clarity ### Fixed + - Documentation API inconsistencies - Outdated code examples - Missing TypeScript type references @@ -304,6 +353,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [5.3.0] - 2024-09-15 ### Added + - Pagination support for all framework integrations - `usePaginatedFilter` hook for React - `usePaginatedFilter` composable for Vue @@ -311,16 +361,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Pagination actions (`nextPage`, `previousPage`, `goToPage`, `setPageSize`) ### Changed + - Improved performance for large datasets - Enhanced memoization strategy ### Fixed + - Memory leak in memoization cache - Type inference issues with generic components ## [5.2.0] - 2024-08-10 ### Added + - Debounced filtering support - `useDebouncedFilter` hook for React - `useDebouncedFilter` composable for Vue @@ -328,27 +381,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Configurable delay option ### Changed + - Optimized re-render behavior in React - Improved reactivity in Vue + ## [5.1.0] - 2024-07-05 ### Added + - `useFilteredState` hook for React - `useFilteredState` composable for Vue - State management for data and expressions - Setter functions for dynamic updates ### Changed + - Improved TypeScript type inference - Enhanced documentation with more examples ### Fixed + - Edge case with empty arrays - Null/undefined handling in expressions ## [5.0.0] - 2024-06-01 ### Breaking Changes + - Renamed `data` to `filtered` in return values - Removed `isError` and `error` from return values - Changed `isLoading` to `isFiltering` @@ -357,6 +416,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated minimum Vue version to 3.0+ ### Added + - Full TypeScript rewrite - Improved type safety and inference - Better error messages @@ -364,86 +424,101 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Tree-shaking support ### Changed + - Simplified API surface - Improved documentation - Enhanced test coverage to 100% ### Migration Guide + See [Migration Guide v5.4](/guide/migration-v2) for detailed migration instructions. ## [4.5.0] - 2024-04-15 ### Added + - Lazy evaluation support - `createLazyFilter` function - Chainable lazy operations - Memory optimization for large datasets ### Changed + - Improved operator processing performance - Enhanced memoization algorithm ## [4.4.0] - 2024-03-10 ### Added + - Nested object filtering support - Dot notation for property access - Deep comparison utilities - Array filtering within objects ### Fixed + - Nested property access edge cases - Deep equality comparison issues ## [4.3.0] - 2024-02-05 ### Added + - Custom operator registration - `registerOperator` function - Operator extension API - Plugin system foundation ### Changed + - Refactored operator processing - Improved operator type definitions ## [4.2.0] - 2024-01-15 ### Added + - String operators: `$startsWith`, `$endsWith`, `$contains` - Case-insensitive string matching option - Regular expression operator `$regex` ### Fixed + - String comparison edge cases - Unicode string handling ## [4.1.0] - 2023-12-10 ### Added + - Array operators: `$in`, `$nin` - Logical operators: `$and`, `$or`, `$not` - Complex expression support - Nested logical operations ### Changed + - Improved expression parsing - Enhanced type safety for operators ## [4.0.0] - 2023-11-01 ### Breaking Changes + - Removed jQuery-style API - Changed expression format to object-based - Updated operator syntax ### Added + - Modern operator-based API - TypeScript support - Framework integrations (React, Vue) - Comprehensive test suite ### Changed + - Complete API redesign - Improved performance - Better documentation @@ -472,4 +547,3 @@ See [Contributing Guide](/project/contributing) for information on how to contri ## License MIT License - see [LICENSE](/project/license) for details. -