@devisfuture/mega-collection 1.3.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,51 +8,56 @@ If this package saved you some time, a ⭐ on GitHub would be much appreciated.
8
8
 
9
9
  # @devisfuture/mega-collection
10
10
 
11
- > Search, filter & sort engine for **100K+** item collections in JavaScript / TypeScript.
11
+ > Tools to search, filter, and sort large arrays in JavaScript / TypeScript.
12
12
 
13
13
  ## Navigation
14
14
 
15
- - [What does this package solve](#what-does-this-package-solve) – explains the core problem addressed by the library
16
- - [Features](#features) – overview of package capabilities
17
- - [React demo](#react-demo) – link to example app and live demo
18
- - [Install](#install) – npm installation instructions
19
- - [Quick Start](#quick-start) – examples for using the engines
20
- - [All-in-one: `MergeEngines`](#all-in-one-mergeengines) – combine search, filter, sort
21
- - [Search only](#search-only) – text search engine usage
22
- - [Filter only](#filter-only) – multi-criteria filter engine
23
- - [Sort only](#sort-only) – sort engine examples
24
- - [API Reference](#api-reference) – detailed methods and options
25
- - [`MergeEngines<T>`](#mergeenginest-root-module) – unified facade around dataset
26
- - [`TextSearchEngine<T>`](#textsearchenginet-search-module) – trigram search engine
27
- - [`FilterEngine<T>`](#filterenginet-filter-module) – indexed filter engine
28
- - [`SortEngine<T>`](#sortenginet-sort-module) – pre-sorted comparators
29
- - [Types](#types) – exported TypeScript types
30
- - [Architecture](#architecture) – project structure overview
31
- - [Build](#build) – build and development commands
32
- - [Contributing](#contributing) – guidelines for contributors
33
- - [Security](#security) – policy information
34
- - [License](#license) – MIT license terms
15
+ - [What does this package solve](#what-does-this-package-solve) – what problem this package helps with
16
+ - [Features](#features) – what the package can do
17
+ - [React demo](#react-demo) – example project and live demo
18
+ - [Install](#install) – how to install the package
19
+ - [Quick Start](#quick-start) – basic usage examples
20
+ - [All-in-one: `MergeEngines`](#all-in-one-mergeengines) – use search, filter, and sort from one engine
21
+ - [Search only](#search-only) – use only text search
22
+ - [Flat collections search](#flat-collections-search) – search simple fields like `name` or `city`
23
+ - [Nested collections search](#nested-collections-search) – search inside nested arrays like `orders.status`
24
+ - [Filter only](#filter-only) – use only filtering
25
+ - [Flat collections filter](#flat-collections-filter) – filter by simple top-level fields
26
+ - [Nested collections filter](#nested-collections-filter) – filter by nested array fields
27
+ - [Sort only](#sort-only) – use only sorting
28
+ - [API Reference](#api-reference) – list of options and methods
29
+ - [`MergeEngines<T>`](#mergeenginest-root-module) – one engine that combines everything
30
+ - [`TextSearchEngine<T>`](#textsearchenginet-search-module) – engine for text search
31
+ - [`FilterEngine<T>`](#filterenginet-filter-module) – engine for filtering
32
+ - [`SortEngine<T>`](#sortenginet-sort-module) – engine for sorting
33
+ - [Types](#types) – TypeScript types you can import
34
+ - [Build](#build) – commands for build and development
35
+ - [Contributing](#contributing) – contribution rules
36
+ - [Security](#security) – security policy
37
+ - [License](#license) – license information
35
38
 
36
39
  ## What does this package solve
37
40
 
38
41
  Sometimes in projects, you need to iterate through huge collections (100K+ elements in an array) that have come from the server. Usually, the most common features are searching, filtering, and sorting.
39
- So, this package helps to perform searching, filtering, and sorting of large collections faster than standard JavaScript methods. This operation is performed before rendering the UI content.
42
+ This package helps you search, filter, and sort large collections faster than plain JavaScript methods. Usually you do this before showing data in the UI.
40
43
 
41
44
  Zero dependencies. Tree-shakeable. Import only what you need.
42
45
 
43
46
  Each engine lives in its own entry point (`/search`, `/filter`, `/sort`).
44
47
  Importing just `@devisfuture/mega-collection/search` or the other sub-modules means
45
- only that code ends up in your bundle unused engines stay out. For example, if
46
- you only pull in `TextSearchEngine` the filter and sort logic won’t be included.
48
+ only that code goes into your bundle. Unused engines stay out. For example, if
49
+ you only import `TextSearchEngine`, the filter and sort code will not be included.
47
50
 
48
51
  ## Features
49
52
 
50
- | Capability | Strategy | Complexity |
51
- | -------------------------- | -------------------------------------- | ---------------------------------- |
52
- | **Indexed filter** | Hash-Map index (`Map<value, T[]>`) | **O(1)** |
53
- | **Multi-value filter** | Index intersection + `Set` membership | **O(k)** indexed / **O(n)** linear |
54
- | **Text search** (contains) | Trigram inverted index + verify | **O(candidates)** |
55
- | **Sorting** | Pre-sorted index (cached) / V8 TimSort | **O(n)** cached / **O(n log n)** |
53
+ | Capability | Strategy | Complexity |
54
+ | ---------------------------- | -------------------------------------- | ---------------------------------- |
55
+ | **Indexed filter** | Hash-Map index (`Map<value, T[]>`) | **O(1)** |
56
+ | **Multi-value filter** | Index intersection + `Set` membership | **O(k)** indexed / **O(n)** linear |
57
+ | **Nested collection filter** | Pre-built nested index + `Set` lookup | **O(k)** indexed / **O(n)** linear |
58
+ | **Text search** (contains) | Trigram inverted index + verify | **O(candidates)** |
59
+ | **Nested collection search** | Nested trigram index + verify | **O(candidates)** |
60
+ | **Sorting** | Pre-sorted index (cached) / V8 TimSort | **O(n)** cached / **O(n log n)** |
56
61
 
57
62
  ## React demo
58
63
 
@@ -78,18 +83,31 @@ interface User {
78
83
  city: string;
79
84
  age: number;
80
85
  }
86
+
87
+ interface Order {
88
+ id: string;
89
+ status: string;
90
+ }
91
+
92
+ interface UserWithOrders extends User {
93
+ orders: Order[];
94
+ }
81
95
  ```
82
96
 
83
97
  ### All-in-one: `MergeEngines`
84
98
 
85
- Use `MergeEngines` to combine search, filter and sort around a single shared dataset.
86
- Declare which engines you need in `imports` only those are initialised.
99
+ Use `MergeEngines` when you want one engine that works with the same dataset.
100
+ Add the engines you need in `imports`. Only those engines will be created.
101
+
102
+ You can create many engine instances in one project for different collections.
103
+ Each instance keeps its own data and its own indexes, so they work separately
104
+ and do not overwrite each other.
87
105
 
88
106
  Each engine accepts an optional `fields` array (set via the `search`,
89
107
  `filter` or `sort` option) which tells it which properties should be indexed up
90
- front. Indexes power the fast paths used throughout the library; you can leave
91
- these options out and everything still works, but the code will fall back to
92
- simple linear scans.
108
+ front. This means the engine prepares those fields once so later calls can be
109
+ faster. If you skip `fields`, everything still works, but the engine may need
110
+ to scan the full array.
93
111
 
94
112
  ```ts
95
113
  import { MergeEngines } from "@devisfuture/mega-collection";
@@ -111,6 +129,26 @@ engine
111
129
  .sort([{ field: "age", direction: "asc" }])
112
130
  .filter([{ field: "city", values: ["Miami", "New York"] }]);
113
131
 
132
+ // with nested collection fields (e.g. orders inside each user)
133
+ const nestedEngine = new MergeEngines<UserWithOrders>({
134
+ imports: [TextSearchEngine, SortEngine, FilterEngine],
135
+ data: usersWithOrders,
136
+ search: {
137
+ fields: ["name", "city"],
138
+ nestedFields: ["orders.status"],
139
+ minQueryLength: 2,
140
+ },
141
+ filter: {
142
+ fields: ["city", "age"],
143
+ nestedFields: ["orders.status"],
144
+ filterByPreviousResult: true,
145
+ },
146
+ sort: { fields: ["age", "name", "city"] },
147
+ });
148
+
149
+ nestedEngine.search("pending"); // finds users whose orders contain "pending"
150
+ nestedEngine.filter([{ field: "orders.status", values: ["delivered"] }]);
151
+
114
152
  // update dataset later without creating a new instance
115
153
  engine.data([
116
154
  {
@@ -125,32 +163,32 @@ engine.data([
125
163
  engine.clearIndexes("search").clearIndexes("sort").clearIndexes("filter");
126
164
  engine.clearData("search").clearData("sort").clearData("filter");
127
165
 
128
- // get original dataset from a specific module
129
- engine.getOriginData("search"); // "search" | "sort" | "filter"
166
+ // get shared original dataset
167
+ engine.getOriginData();
130
168
  ```
131
169
 
132
170
  ---
133
171
 
134
172
  ### Search only
135
173
 
174
+ Use `TextSearchEngine` when you only need text search.
175
+ The examples below show the difference between simple fields and nested fields.
176
+
177
+ #### Flat collections search
178
+
136
179
  ```ts
137
180
  import { TextSearchEngine } from "@devisfuture/mega-collection/search";
138
181
 
139
- // `fields` tells the engine which properties to index for fast lookups. The
140
- // index is built once during construction; if you omit `fields` the engine
141
- // still works but every search will scan the entire dataset.
182
+ // `fields` tells the engine which fields it should prepare for faster search.
183
+ // If you skip `fields`, search still works, but it will scan the full dataset.
142
184
  const engine = new TextSearchEngine<User>({
143
185
  data: users,
144
186
  fields: ["name", "city"],
145
187
  minQueryLength: 2, // begins searching when query length >= 2
146
188
  });
147
189
 
148
- // note: inputs shorter than `minQueryLength` bypass the indexed search and
149
- // simply return the original dataset (rather than clearing the result).
150
- // A one‑character search usually matches most of the dataset, so avoiding
151
- // extra work makes typing feel snappier. Once the query reaches the
152
- // threshold the indexed search kicks in and performance improves
153
- // dramatically. Empty/blank queries return the original dataset.
190
+ // If the query is shorter than `minQueryLength`, the engine returns the
191
+ // original dataset. Empty or blank queries also return the original dataset.
154
192
 
155
193
  engine.search("john"); // searches all indexed fields, deduplicated
156
194
  engine.search("name", "john"); // searches a specific field
@@ -161,19 +199,40 @@ engine.data(users);
161
199
  // access original dataset stored in the engine
162
200
  engine.getOriginData();
163
201
 
164
- // chain support
165
- engine.search("john").clearIndexes().clearData();
202
+ // service methods stay on the engine instance
203
+ engine.clearIndexes();
204
+ engine.clearData();
205
+ ```
206
+
207
+ #### Nested collections search
208
+
209
+ ```ts
210
+ import { TextSearchEngine } from "@devisfuture/mega-collection/search";
211
+
212
+ // Search inside nested arrays. `nestedFields` uses dot notation.
213
+ const nestedSearch = new TextSearchEngine<UserWithOrders>({
214
+ data: usersWithOrders,
215
+ fields: ["name", "city"],
216
+ nestedFields: ["orders.status"],
217
+ minQueryLength: 2,
218
+ });
219
+
220
+ nestedSearch.search("pending"); // finds users whose orders match
221
+ nestedSearch.search("orders.status", "delivered"); // search a specific nested field
166
222
  ```
167
223
 
168
224
  ### Filter only
169
225
 
226
+ Use `FilterEngine` when you only need filtering.
227
+ The examples below show the difference between simple fields and nested fields.
228
+
229
+ #### Flat collections filter
230
+
170
231
  ```ts
171
232
  import { FilterEngine } from "@devisfuture/mega-collection/filter";
172
233
 
173
- // `fields` config tells the filter engine which properties should have an
174
- // index built. Indexed lookups are O(1) per value, so multi-criteria queries
175
- // can be orders of magnitude faster. Without `fields` the engine still filters
176
- // correctly but always does a linear scan.
234
+ // `fields` tells the engine which fields should be prepared for faster filter
235
+ // lookups. Without `fields`, filtering still works, but it will scan the data.
177
236
  const engine = new FilterEngine<User>({
178
237
  data: users,
179
238
  fields: ["city", "age"],
@@ -201,26 +260,45 @@ engine
201
260
  // Sequential mode example:
202
261
  // 1) First call filters by city
203
262
  const byCity = engine.filter([{ field: "city", values: ["Miami"] }]);
204
- // 2) Second call filters only inside previous result
263
+ // 2) Second call filters only inside the previous result
205
264
  const byCityAndAge = engine.filter([{ field: "age", values: [22] }]);
206
265
  ```
207
266
 
267
+ #### Nested collections filter
268
+
269
+ ```ts
270
+ import { FilterEngine } from "@devisfuture/mega-collection/filter";
271
+
272
+ // Filter inside nested arrays. `nestedFields` uses dot notation.
273
+ const nestedFilter = new FilterEngine<UserWithOrders>({
274
+ data: usersWithOrders,
275
+ fields: ["city", "age"],
276
+ nestedFields: ["orders.status"],
277
+ filterByPreviousResult: true,
278
+ });
279
+
280
+ nestedFilter.filter([{ field: "orders.status", values: ["pending"] }]);
281
+ nestedFilter.filter([
282
+ { field: "orders.status", values: ["pending"] },
283
+ { field: "city", values: ["New-York"] },
284
+ ]);
285
+ ```
286
+
208
287
  ### Sort only
209
288
 
289
+ Use `SortEngine` when you only need sorting.
290
+
210
291
  ```ts
211
292
  import { SortEngine } from "@devisfuture/mega-collection/sort";
212
293
 
213
- // `fields` instructs the engine to pre-build a sorted index for each property.
214
- // When you later run a single-field sort the result can be pulled directly
215
- // from that index in linear time. If you leave out `fields` the engine still
216
- // sorts correctly, it merely falls back to standard `Array.prototype.sort`
217
- // (O(n log n)).
294
+ // `fields` tells the engine which fields should be prepared for faster
295
+ // single-field sorting. If you skip `fields`, sorting still works.
218
296
  const engine = new SortEngine<User>({
219
297
  data: users,
220
298
  fields: ["age", "name", "city"],
221
299
  });
222
300
 
223
- // Single-field sort — O(n) via cached index
301
+ // Single-field sort
224
302
  engine.sort([{ field: "age", direction: "asc" }]);
225
303
 
226
304
  // replace dataset without re-initializing
@@ -229,14 +307,11 @@ engine.data(users);
229
307
  // access original dataset stored in the engine
230
308
  engine.getOriginData();
231
309
 
232
- // chain support
233
- engine
234
- .sort([{ field: "age", direction: "asc" }])
235
- .sort([{ field: "name", direction: "desc" }])
236
- .clearIndexes()
237
- .clearData();
310
+ // service methods stay on the engine instance
311
+ engine.clearIndexes();
312
+ engine.clearData();
238
313
 
239
- // Multi-field sort — O(n log n)
314
+ // Multi-field sort
240
315
  engine.sort([
241
316
  { field: "age", direction: "asc" },
242
317
  { field: "name", direction: "desc" },
@@ -249,7 +324,7 @@ engine.sort([
249
324
 
250
325
  ### `MergeEngines<T>` (root module)
251
326
 
252
- Unified facade that composes all three engines around a shared dataset.
327
+ One engine that brings search, filter, and sort together on the same dataset.
253
328
 
254
329
  **Constructor options:**
255
330
 
@@ -257,8 +332,8 @@ Unified facade that composes all three engines around a shared dataset.
257
332
  | --------- | ----------------------------------------------------------- | -------------------------------------------- |
258
333
  | `imports` | `(typeof TextSearchEngine \| SortEngine \| FilterEngine)[]` | Engine classes to activate |
259
334
  | `data` | `T[]` | Shared dataset — passed once at construction |
260
- | `search` | `{ fields, minQueryLength? }` | Config for TextSearchEngine |
261
- | `filter` | `{ fields, filterByPreviousResult? }` | Config for FilterEngine |
335
+ | `search` | `{ fields, nestedFields?, minQueryLength? }` | Config for TextSearchEngine |
336
+ | `filter` | `{ fields, nestedFields?, filterByPreviousResult? }` | Config for FilterEngine |
262
337
  | `sort` | `{ fields }` | Config for SortEngine |
263
338
 
264
339
  **Methods:**
@@ -271,7 +346,7 @@ Unified facade that composes all three engines around a shared dataset.
271
346
  | `sort(data, descriptors, inPlace?)` | Sort with an explicit dataset |
272
347
  | `filter(criteria)` | Filter using stored dataset |
273
348
  | `filter(data, criteria)` | Filter with an explicit dataset |
274
- | `getOriginData(module)` | Get the original dataset from one module (`"search"`, `"sort"`, `"filter"`) |
349
+ | `getOriginData()` | Get the shared original dataset |
275
350
  | `data(data)` | Replace stored dataset for all imported modules, rebuilding configured indexes and resetting filter state where applicable |
276
351
  | `clearIndexes(module)` | Clear indexes for one module (`"search"`, `"sort"`, `"filter"`) |
277
352
  | `clearData(module)` | Clear stored data for one module (`"search"`, `"sort"`, `"filter"`) |
@@ -280,40 +355,45 @@ Unified facade that composes all three engines around a shared dataset.
280
355
 
281
356
  ### `TextSearchEngine<T>` (search module)
282
357
 
283
- Trigram-based text search engine.
358
+ Text search engine. It supports `nestedFields` if you need to search inside
359
+ nested collections such as `["orders.status"]`. Search methods return plain
360
+ arrays.
284
361
 
285
- | Method | Description |
286
- | ---------------------- | ----------------------------------------------------- |
287
- | `search(query)` | Search all indexed fields, deduplicated |
288
- | `search(field, query)` | Search a specific indexed field |
289
- | `getOriginData()` | Get the original stored dataset |
290
- | `data(data)` | Replace stored dataset and rebuild configured indexes |
291
- | `clearIndexes()` | Clear n-gram indexes |
292
- | `clearData()` | Clear stored data |
362
+ | Method | Description |
363
+ | ---------------------- | ---------------------------------------------------------- |
364
+ | `search(query)` | Search all indexed fields (including nested), deduplicated |
365
+ | `search(field, query)` | Search a specific indexed field or nested field path |
366
+ | `getOriginData()` | Get the original stored dataset |
367
+ | `data(data)` | Replace stored dataset and rebuild configured indexes |
368
+ | `clearIndexes()` | Clear n-gram indexes (including nested) |
369
+ | `clearData()` | Clear stored data |
293
370
 
294
371
  ### `FilterEngine<T>` (filter module)
295
372
 
296
- Multi-criteria AND filter with index-accelerated fast path.
373
+ Filter engine for one or more rules. It supports `nestedFields` if you need to
374
+ filter by values inside nested collections such as `["orders.status"]`.
297
375
 
298
376
  Constructor option highlights:
299
377
 
300
- | Option | Type | Description |
301
- | ------------------------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
302
- | `filterByPreviousResult` | `boolean` | When `true`, each `filter(criteria)` call filters from previous result. Defaults to `false` (each call starts from the original dataset). |
378
+ | Option | Type | Description |
379
+ | ------------------------ | ---------- | ---------------------------------------------------------------------------------------------------------------------------------- |
380
+ | `filterByPreviousResult` | `boolean` | When `true`, the next `filter(criteria)` call works on the previous result. By default each call starts from the original dataset. |
381
+ | `nestedFields` | `string[]` | Nested field paths in dot notation, for example `["orders.status"]`. |
303
382
 
304
383
  | Method | Description |
305
384
  | ------------------------ | -------------------------------------------------------------------------- |
306
- | `filter(criteria)` | Filter using stored dataset |
385
+ | `filter(criteria)` | Filter using stored dataset (supports nested field criteria) |
307
386
  | `filter(data, criteria)` | Filter with an explicit dataset |
308
387
  | `getOriginData()` | Get the original stored dataset |
309
388
  | `data(data)` | Replace stored dataset, rebuild configured indexes, and reset filter state |
310
389
  | `resetFilterState()` | Reset previous-result state for sequential filtering |
311
- | `clearIndexes()` | Free all index memory |
390
+ | `clearIndexes()` | Free all index memory (including nested indexes) |
312
391
  | `clearData()` | Clear stored data |
313
392
 
314
393
  ### `SortEngine<T>` (sort module)
315
394
 
316
- Sorting with pre-compiled comparators and cached sort indexes.
395
+ Sort engine with prepared indexes for faster sorting in common cases. Sort
396
+ methods return plain arrays.
317
397
 
318
398
  | Method | Description |
319
399
  | ----------------------------------- | ----------------------------------------------------- |
@@ -326,7 +406,7 @@ Sorting with pre-compiled comparators and cached sort indexes.
326
406
 
327
407
  ---
328
408
 
329
- **Note on `data` method:** Calling the `data` method automatically rebuilds configured indexes and resets any internal state (such as filter state in FilterEngine), so it is sufficient on its own without needing to call `clearIndexes` separately.
409
+ **Note on `data` method:** Calling `data` updates the stored dataset. It also rebuilds configured indexes and resets internal state when needed, so you usually do not need to call `clearIndexes` before it.
330
410
 
331
411
  ## Types
332
412
 
@@ -359,27 +439,6 @@ import type {
359
439
 
360
440
  ---
361
441
 
362
- ## Architecture
363
-
364
- ```
365
- src/
366
- types.ts — Shared type definitions
367
- indexer.ts — Hash-Map index engine (internal, O(1) lookups)
368
- search/
369
- text-search.ts — Trigram inverted index engine
370
- index.ts — Search module entry point
371
- filter/
372
- filter.ts — Multi-criteria filter engine (owns Indexer internally)
373
- index.ts — Filter module entry point
374
- sort/
375
- sorter.ts — Sort engine (TimSort + index-sort)
376
- index.ts — Sort module entry point
377
- merge/
378
- merge-engines.ts — MergeEngines unified facade
379
- index.ts — Merge module entry point
380
- index.ts — Root barrel export
381
- ```
382
-
383
442
  ## Build
384
443
 
385
444
  ```bash
@@ -0,0 +1,2 @@
1
+ var p=class u extends Error{constructor(e){super(e),this.name="TextSearchEngineError";}static missingDatasetForBuildIndex(){return new u("TextSearchEngine: no dataset in memory. Either pass `data` in the constructor options, or call buildIndex(data, field).")}};function T(u){let e=u.toLowerCase(),t=Math.min(3,e.length),s=e.length-t+1,n=new Array(s);for(let i=0;i<s;i++)n[i]=e.substring(i,i+t);return n}function L(u,e){let t=u.get(e);if(t)return t;let s=new Set;return u.set(e,s),s}function I(u){let e=T(u);if(e.length<=12)return new Set(e);let t=new Set,s=e.length-1,n=11;for(let i=0;i<=n;i++){let o=Math.round(i*s/n);t.add(e[o]);}return t}function x(u,e,t){for(let s=0,n=e.length;s<n;s++){let i=n-s,o=Math.min(3,i);for(let r=1;r<=o;r++){let a=e.substring(s,s+r);L(u,a).add(t);}}}var y=class{constructor(){this.registeredFields=new Set;this.fieldDescriptors=new Map;this.ngramIndexes=new Map;this.normalizedFieldValues=new Map;}registerFields(e){if(e?.length)for(let t=0;t<e.length;t++)this.registerField(e[t]);}hasRegisteredFields(){return this.registeredFields.size>0}hasField(e){return this.registeredFields.has(e)}hasIndexes(){return this.ngramIndexes.size>0}clearIndexes(){this.ngramIndexes.clear(),this.normalizedFieldValues.clear();}buildIndexes(e){this.clearIndexes();for(let t of this.registeredFields)this.buildIndex(e,t);}searchAllIndexedFields(e,t,s){let n=new Set,i=[];for(let o of this.registeredFields)for(let r of this.searchIndexedField(e,o,t,s))n.has(r)||(n.add(r),i.push(r));return i}searchIndexedField(e,t,s,n){let i=this.ngramIndexes.get(t);if(!i)return [];let o=[];for(let d of n){let l=i.get(d);if(!l)return [];o.push(l);}o.sort((d,l)=>d.size-l.size);let r=o[0],a=o.length,h=[],c=this.normalizedFieldValues.get(t);for(let d of r){let l=true;for(let m=1;m<a;m++)if(!o[m].has(d)){l=false;break}if(!l)continue;let g=e[d];!g||!c?.[d]?.includes(s)||h.push(g);}return h}searchFieldLinear(e,t,s){let n=this.fieldDescriptors.get(t);if(!n)return [];let{collectionKey:i,nestedKey:o}=n,r=[];for(let a=0;a<e.length;a++){let h=e[a],c=h[i];if(!Array.isArray(c))continue;let d=false;for(let l=0;l<c.length;l++){let g=c[l][o];if(typeof g=="string"&&g.toLowerCase().includes(s)){d=true;break}}d&&r.push(h);}return r}matchesAnyField(e,t){for(let s of this.registeredFields){let n=this.fieldDescriptors.get(s);if(!n)continue;let{collectionKey:i,nestedKey:o}=n,r=e[i];if(Array.isArray(r))for(let a=0;a<r.length;a++){let h=r[a][o];if(typeof h=="string"&&h.toLowerCase().includes(t))return true}}return false}registerField(e){let t=this.createDescriptor(e);t&&(this.registeredFields.add(e),this.fieldDescriptors.set(e,t));}createDescriptor(e){let t=e.indexOf(".");return t===-1?null:{collectionKey:e.substring(0,t),nestedKey:e.substring(t+1)}}buildIndex(e,t){let s=this.fieldDescriptors.get(t);if(!s)return;let{collectionKey:n,nestedKey:i}=s,o=new Map,r=new Array(e.length);for(let a=0,h=e.length;a<h;a++){let d=e[a][n];if(!Array.isArray(d))continue;let l=[];for(let g=0;g<d.length;g++){let f=d[g][i];if(typeof f!="string")continue;let m=f.toLowerCase();l.push(m),x(o,m,a);}l.length!==0&&(r[a]=l.join(`
2
+ `));}this.ngramIndexes.set(t,o),this.normalizedFieldValues.set(t,r);}};var F=class{constructor(e={}){this.ngramIndexes=new Map;this.normalizedFieldValues=new Map;this.dataset=[];this.indexedFields=new Set;this.nestedCollection=new y;if(this.minQueryLength=e.minQueryLength??1,this.nestedCollection.registerFields(e.nestedFields),!e.data)return;this.dataset=e.data;let t=e.fields?.length,s=this.nestedCollection.hasRegisteredFields();if(t)for(let n of e.fields)this.indexedFields.add(n);(t||s)&&this.rebuildConfiguredIndexes();}rebuildConfiguredIndexes(){this.ngramIndexes.clear(),this.normalizedFieldValues.clear(),this.nestedCollection.clearIndexes();for(let e of this.indexedFields)this.buildIndex(this.dataset,e);this.nestedCollection.hasRegisteredFields()&&this.nestedCollection.buildIndexes(this.dataset);}buildIndex(e,t){let s,n;if(Array.isArray(e))s=e,n=t;else {if(!this.dataset.length)throw p.missingDatasetForBuildIndex();s=this.dataset,n=e;}this.dataset=s;let i=new Map,o=new Array(s.length);for(let r=0,a=s.length;r<a;r++){let h=s[r][n];if(typeof h!="string")continue;let c=h.toLowerCase();o[r]=c,x(i,c,r);}return this.ngramIndexes.set(n,i),this.normalizedFieldValues.set(n,o),this}search(e,t){return t===void 0?this.searchAllFields(e):this.searchField(e,t)}normalizeQuery(e){return e.trim().toLowerCase()}searchAllFields(e){let t=this.normalizeQuery(e);if(!t)return this.dataset;if(t.length<this.minQueryLength)return this.dataset;if(!this.ngramIndexes.size&&!this.nestedCollection.hasIndexes())return this.searchAllFieldsLinear(t);let s=I(t);if(!s.size)return [];let n=new Set,i=[];for(let o of this.ngramIndexes.keys())for(let r of this.searchFieldWithPreparedQuery(o,t,s))n.has(r)||(n.add(r),i.push(r));for(let o of this.nestedCollection.searchAllIndexedFields(this.dataset,t,s))n.has(o)||(n.add(o),i.push(o));return i}searchField(e,t){let s=this.normalizeQuery(t);if(!s)return this.dataset;if(s.length<this.minQueryLength)return this.dataset;if(this.nestedCollection.hasField(e)){let i=I(s);return i.size?this.nestedCollection.hasIndexes()?this.nestedCollection.searchIndexedField(this.dataset,e,s,i):this.nestedCollection.searchFieldLinear(this.dataset,e,s):[]}if(!this.ngramIndexes.size)return this.searchFieldLinear(e,s);let n=I(s);return n.size?this.searchFieldWithPreparedQuery(e,s,n):[]}searchFieldWithPreparedQuery(e,t,s){let n=this.ngramIndexes.get(e);if(!n)return [];let i=[];for(let c of s){let d=n.get(c);if(!d)return [];i.push(d);}i.sort((c,d)=>c.size-d.size);let o=i[0],r=i.length,a=[],h=this.normalizedFieldValues.get(e);for(let c of o){let d=true;for(let f=1;f<r;f++)if(!i[f].has(c)){d=false;break}if(!d)continue;let l=this.dataset[c];if(!l)continue;h?.[c]?.includes(t)&&a.push(l);}return a}searchAllFieldsLinear(e){if(!this.dataset.length)return [];let t=[];for(let s=0;s<this.dataset.length;s++){let n=this.dataset[s],i=false;for(let o in n){let r=n[o];if(typeof r=="string"&&r.toLowerCase().includes(e)){i=true;break}}i||(i=this.nestedCollection.matchesAnyField(n,e)),i&&t.push(n);}return t}searchFieldLinear(e,t){if(!this.dataset.length)return [];if(this.nestedCollection.hasField(e))return this.nestedCollection.searchFieldLinear(this.dataset,e,t);let s=[];for(let n=0;n<this.dataset.length;n++){let i=this.dataset[n][e];typeof i=="string"&&i.toLowerCase().includes(t)&&s.push(this.dataset[n]);}return s}clearIndexes(){return this.ngramIndexes.clear(),this.normalizedFieldValues.clear(),this.nestedCollection.clearIndexes(),this}getOriginData(){return this.dataset}data(e){return this.dataset=e,this.rebuildConfiguredIndexes(),this}clearData(){return this.dataset=[],this.ngramIndexes.clear(),this.normalizedFieldValues.clear(),this.nestedCollection.clearIndexes(),this}};export{F as a};
@@ -0,0 +1 @@
1
+ var C=class{constructor(){this.indexes=new Map;}buildIndex(e,t){let n=new Map;for(let s=0,r=e.length;s<r;s++){let i=e[s],l=i[t];if(l==null)continue;let a=n.get(l);a?a.push(i):n.set(l,[i]);}this.indexes.set(t,n);}getByValue(e,t){let n=this.indexes.get(e);return n?n.get(t)??[]:[]}getByValues(e,t){let n=this.indexes.get(e);if(!n)return [];if(t.length===1)return n.get(t[0])??[];let s=new Set,r=[];for(let i=0;i<t.length;i++){let l=n.get(t[i]);if(l!==void 0)for(let a=0;a<l.length;a++){let u=l[a];s.has(u)||(s.add(u),r.push(u));}}return r}hasIndex(e){return this.indexes.has(e)}clear(){this.indexes.clear();}getIndexMap(e){return this.indexes.get(e)}};var v=class{constructor(e){this.callbacks=e;}create(e){let t=e;return Object.defineProperty(t,"filter",{value:(n,s)=>s===void 0?this.callbacks.filter(e,n):this.callbacks.filter(n,s),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"clearIndexes",{value:()=>this.callbacks.clearIndexes(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"data",{value:n=>this.callbacks.data(n),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"getOriginData",{value:()=>this.callbacks.getOriginData(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"clearData",{value:()=>this.callbacks.clearData(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"resetFilterState",{value:()=>this.callbacks.resetFilterState(),enumerable:false,configurable:true,writable:true}),t}};var T=class p extends Error{constructor(e){super(e),this.name="FilterEngineError";}static missingDatasetForBuildIndex(){return new p("FilterEngine: no dataset in memory. Either pass `data` in the constructor options, or call buildIndex(data, field).")}static missingDatasetForFilter(){return new p("FilterEngine: no dataset in memory. Either pass `data` in the constructor options, or call filter(data, criteria).")}};var x=Symbol("FilterEngine.execute");var F=class{constructor(){this.registeredFields=new Set;this.fieldDescriptors=new Map;this.indexes=new Map;}registerFields(e){if(e?.length)for(let t=0;t<e.length;t++)this.registerField(e[t]);}hasRegisteredFields(){return this.registeredFields.size>0}hasField(e){return this.registeredFields.has(e)}clearIndexes(){this.indexes.clear();}buildIndexes(e){this.indexes.clear();for(let t of this.registeredFields)this.buildIndex(e,t);}filter(e,t,n){if(e.length===0||t.length===0)return e;let s=[],r=[];for(let a=0;a<t.length;a++){let u=t[a];if(this.indexes.has(u.field)){s.push(u);continue}r.push(u);}let i=e,l=e===n?null:new Set(e);if(s.length>0&&(i=this.filterByIndexes(s,l),i.length===0))return i;for(let a=0;a<r.length;a++)if(i=this.filterLinearly(i,r[a]),i.length===0)return i;return i}registerField(e){let t=this.createDescriptor(e);t&&(this.registeredFields.add(e),this.fieldDescriptors.set(e,t));}createDescriptor(e){let t=e.indexOf(".");return t===-1?null:{collectionKey:e.substring(0,t),nestedKey:e.substring(t+1)}}buildIndex(e,t){let n=this.fieldDescriptors.get(t);if(!n)return;let{collectionKey:s,nestedKey:r}=n,i=new Map;for(let l=0,a=e.length;l<a;l++){let u=e[l],h=u[s];if(Array.isArray(h))for(let c=0;c<h.length;c++){let o=h[c][r];if(o==null)continue;let d=i.get(o);if(d){d[d.length-1]!==u&&d.push(u);continue}i.set(o,[u]);}}this.indexes.set(t,i);}filterByIndexes(e,t){if(e.length===1){let i=this.indexes.get(e[0].field);if(!i)return [];let l=this.getItemsByValues(i,e[0].values);if(l.length===0)return [];if(t===null)return l;let a=[];for(let u=0;u<l.length;u++){let h=l[u];t.has(h)&&a.push(h);}return a}let n=e.map(i=>({criterion:i,size:this.estimateIndexSize(i)})).sort((i,l)=>i.size-l.size),s=t,r=[];for(let i=0;i<n.length;i++){let{criterion:l}=n[i],a=this.indexes.get(l.field);if(!a)return [];let u=this.getItemsByValues(a,l.values);if(u.length===0)return [];if(s===null)r=u;else {r=[];for(let h=0;h<u.length;h++){let c=u[h];s.has(c)&&r.push(c);}}if(r.length===0)return [];s=new Set(r);}return r}getItemsByValues(e,t){if(t.length===1)return e.get(t[0])??[];let n=new Set,s=[];for(let r=0;r<t.length;r++){let i=e.get(t[r]);if(i)for(let l=0;l<i.length;l++){let a=i[l];n.has(a)||(n.add(a),s.push(a));}}return s}estimateIndexSize(e){let t=this.indexes.get(e.field);return t?e.values.reduce((n,s)=>{let r=t.get(s);return r?n+r.length:n},0):1/0}filterLinearly(e,t){let n=this.fieldDescriptors.get(t.field);if(!n)return e;let{collectionKey:s,nestedKey:r}=n,i=new Set(t.values),l=[];for(let a=0;a<e.length;a++){let u=e[a],h=u[s];if(!Array.isArray(h))continue;let c=false;for(let o=0;o<h.length;o++)if(i.has(h[o][r])){c=true;break}c&&l.push(u);}return l}};var y=class{constructor(e={}){this.dataset=[];this.indexedFields=new Set;this.nestedCollection=new F;this.previousResult=null;this.previousCriteria=null;this.previousBaseData=null;this.chainBuilder=new v({filter:(e,t)=>t===void 0?this.filter(e):this.filter(e,t),getOriginData:()=>this.getOriginData(),data:e=>this.data(e),clearIndexes:()=>this.clearIndexes(),clearData:()=>this.clearData(),resetFilterState:()=>this.resetFilterState()});if(this.indexer=new C,this.filterByPreviousResult=e.filterByPreviousResult??false,this.nestedCollection.registerFields(e.nestedFields),!e.data)return;this.dataset=e.data;let t=e.fields?.length,n=this.nestedCollection.hasRegisteredFields();if(t)for(let s of e.fields)this.indexedFields.add(s);(t||n)&&this.rebuildConfiguredIndexes();}rebuildConfiguredIndexes(){this.indexer.clear(),this.nestedCollection.clearIndexes();for(let e of this.indexedFields)this.buildIndex(this.dataset,e);this.nestedCollection.hasRegisteredFields()&&this.nestedCollection.buildIndexes(this.dataset);}buildIndex(e,t){if(!Array.isArray(e)){if(!this.dataset.length)throw T.missingDatasetForBuildIndex();return this.indexer.buildIndex(this.dataset,e),this}return this.dataset=e,this.previousResult=null,this.previousCriteria=null,this.previousBaseData=null,this.indexer.buildIndex(e,t),this}clearIndexes(){return this.indexer.clear(),this.nestedCollection.clearIndexes(),this}resetFilterState(){return this.previousResult=null,this.previousCriteria=null,this.previousBaseData=null,this}clearData(){return this.dataset=[],this.indexer.clear(),this.nestedCollection.clearIndexes(),this.resetFilterState(),this}data(e){return this.dataset=e,this.resetFilterState(),this.rebuildConfiguredIndexes(),this}getOriginData(){return this.dataset}filter(e,t){return t===void 0?this.withChain(this[x](e)):this.withChain(this[x](e,t))}[x](e,t){let n=t===void 0,s,r,i;if(n){if(!this.dataset.length)throw T.missingDatasetForFilter();if(r=e,this.filterByPreviousResult&&this.previousResult!==null&&this.previousCriteria!==null&&this.previousBaseData===this.dataset){let o=this.hasCriteriaAdditions(this.previousCriteria,r),d=this.hasCriteriaRemovals(this.previousCriteria,r);if(!o&&!d)return this.previousResult;o&&!d?(s=this.previousResult,i=this.getAddedCriteria(this.previousCriteria,r)):(s=this.dataset,i=r);}else s=this.dataset,i=r;}else if(r=t,s=e,this.filterByPreviousResult&&this.previousResult!==null&&this.previousCriteria!==null&&this.previousBaseData===s){let o=this.hasCriteriaAdditions(this.previousCriteria,r),d=this.hasCriteriaRemovals(this.previousCriteria,r);if(!o&&!d)return this.previousResult;o&&!d?(s=this.previousResult,i=this.getAddedCriteria(this.previousCriteria,r)):i=r;}else i=r;if(r.length===0)return this.filterByPreviousResult&&(this.previousResult=null,this.previousCriteria=null,this.previousBaseData=null),n?this.dataset:s;n&&!i&&(i=r);let l=[],a=[];for(let o=0;o<i.length;o++){let d=i[o];this.nestedCollection.hasField(d.field)?l.push(d):a.push(d);}if(l.length>0&&(s=this.nestedCollection.filter(s,l,this.dataset),s.length===0)){let o=[];return this.filterByPreviousResult&&(this.previousResult=o,this.previousCriteria=this.cloneCriteria(r),this.previousBaseData=n?this.dataset:e),o}if(a.length===0)return this.filterByPreviousResult&&(this.previousResult=s,this.previousCriteria=this.cloneCriteria(r),this.previousBaseData=n?this.dataset:e),s;let{indexedCriteria:u,linearCriteria:h}=a.reduce((o,d)=>(this.indexer.hasIndex(d.field)?o.indexedCriteria.push(d):o.linearCriteria.push(d),o),{indexedCriteria:[],linearCriteria:[]}),c;if(u.length>0&&h.length===0)return c=this.filterViaIndex(u,s),this.filterByPreviousResult&&(this.previousResult=c,this.previousCriteria=this.cloneCriteria(r),this.previousBaseData=n?this.dataset:e),c;if(u.length>0&&h.length>0){let o=this.filterViaIndex(u,s);return c=this.linearFilter(o,h),this.filterByPreviousResult&&(this.previousResult=c,this.previousCriteria=this.cloneCriteria(r),this.previousBaseData=n?this.dataset:e),c}return c=this.linearFilter(s,a),this.filterByPreviousResult&&(this.previousResult=c,this.previousCriteria=this.cloneCriteria(r),this.previousBaseData=n?this.dataset:e),c}withChain(e){return this.chainBuilder.create(e)}cloneCriteria(e){return e.map(({field:t,values:n})=>({field:t,values:[...n]}))}hasCriteriaAdditions(e,t){let n=new Map(e.map(({field:r,values:i})=>[r,new Set(i)])),s=new Map(t.map(({field:r,values:i})=>[r,new Set(i)]));for(let[r,i]of s){let l=n.get(r);if(!l)return true;for(let a of i)if(!l.has(a))return true}return false}hasCriteriaRemovals(e,t){let n=new Map(e.map(({field:r,values:i})=>[r,new Set(i)])),s=new Map(t.map(({field:r,values:i})=>[r,new Set(i)]));for(let[r,i]of n){let l=s.get(r);if(!l)return true;for(let a of i)if(!l.has(a))return true}return false}getAddedCriteria(e,t){let n=new Map(e.map(({field:r,values:i})=>[r,new Set(i)])),s=[];for(let{field:r,values:i}of t){let l=n.get(r);if(!l){s.push({field:r,values:[...i]});continue}let a=i.filter(u=>!l.has(u));a.length>0&&s.push({field:r,values:a});}return s}linearFilter(e,t){let n=new Map(t.map(({field:i,values:l})=>[i,new Set(l)])),s=t.map(({field:i})=>i),r=[];for(let i=0;i<e.length;i++){let l=e[i],a=true;for(let u=0;u<s.length;u++){let h=s[u];if(!n.get(h).has(l[h])){a=false;break}}a&&r.push(l);}return r}filterViaIndex(e,t){let s=t!==this.dataset?new Set(t):null;if(e.length===1){let o=this.indexer.getByValues(e[0].field,e[0].values);if(!s)return o.slice();let d=[];for(let f=0;f<o.length;f++){let g=o[f];s.has(g)&&d.push(g);}return d}let r=e.map(o=>({criterion:o,size:this.estimateIndexSize(o)})).sort((o,d)=>o.size-d.size),{field:i,values:l}=r[0].criterion,a=this.indexer.getByValues(i,l);if(a.length===0)return [];let u=new Map(r.slice(1).map(({criterion:{field:o,values:d}})=>[o,new Set(d)])),h=Array.from(u.keys()),c=[];for(let o=0;o<a.length;o++){let d=a[o],f=true;for(let g=0;g<h.length;g++){let m=h[g];if(!u.get(m).has(d[m])){f=false;break}}f&&s&&!s.has(d)&&(f=false),f&&c.push(d);}return c}estimateIndexSize(e){let t=this.indexer.getIndexMap(e.field);return t?e.values.reduce((n,s)=>{let r=t.get(s);return r?n+r.length:n},0):1/0}};export{x as a,y as b};
@@ -0,0 +1 @@
1
+ var u=class f extends Error{constructor(r){super(r),this.name="SortEngineError";}static missingDatasetForBuildIndex(){return new f("SortEngine: no dataset in memory. Either pass `data` in the constructor options, or call buildIndex(data, field).")}static missingDatasetForSort(){return new f("SortEngine: no dataset in memory. Either pass `data` in the constructor options, or call sort(data, descriptors).")}};var g=class{constructor(r={}){this.cache=new Map;this.dataset=[];this.indexedFields=new Set;if(r.data&&(this.dataset=r.data,r.fields?.length)){for(let a of r.fields)this.indexedFields.add(a);this.rebuildConfiguredIndexes();}}rebuildConfiguredIndexes(){this.cache.clear();for(let r of this.indexedFields)this.buildIndex(this.dataset,r);}buildIndex(r,a){let d,t;if(Array.isArray(r))d=r,t=a;else {if(!this.dataset.length)throw u.missingDatasetForBuildIndex();d=this.dataset,t=r;}this.dataset=d;let e=d.length,s=new Uint32Array(e),n=new Array(e);for(let o=0;o<e;o++)s[o]=o,n[o]=d[o][t];if(typeof n[0]=="number"){let o=new Float64Array(e);for(let l=0;l<e;l++)o[l]=n[l];s.sort((l,c)=>o[l]-o[c]);}else s.sort((o,l)=>{let c=n[o],h=n[l];return c<h?-1:c>h?1:0});return this.cache.set(t,{indexes:s,dataRef:d,itemCount:e}),this}clearIndexes(){return this.cache.clear(),this}clearData(){return this.dataset=[],this.cache.clear(),this}data(r){return this.dataset=r,this.rebuildConfiguredIndexes(),this}getOriginData(){return this.dataset}sort(r,a,d=false){let t,e,s=a===void 0;if(a===void 0){if(!this.dataset.length)throw u.missingDatasetForSort();t=this.dataset,e=r;}else t=r,e=a;if(e.length===0||t.length===0)return t;if(e.length===1){let{field:o,direction:l}=e[0],c=this.cache.get(o);if(s&&c?.dataRef===t&&c.itemCount===t.length)return this.reconstructFromIndex(t,c.indexes,l)}let n=d?t:t.slice();if(e.length===1&&t.length>0&&typeof t[0][e[0].field]=="number")return this.radixSortNumeric(n,e[0].field,e[0].direction);let i=this.buildComparator(e);return n.sort(i),n}reconstructFromIndex(r,a,d){let t=r.length,e=new Array(t);if(d==="asc")for(let s=0;s<t;s++)e[s]=r[a[s]];else for(let s=0;s<t;s++)e[s]=r[a[t-1-s]];return e}buildComparator(r){let a=r.map(({field:e})=>e),d=r.map(({direction:e})=>e==="asc"?1:-1),t=a.length;return (e,s)=>{for(let n=0;n<t;n++){let i=e[a[n]],o=s[a[n]];if(i<o)return -d[n];if(i>o)return d[n]}return 0}}radixSortNumeric(r,a,d){let t=r.length,e=new Float64Array(t);for(let i=0;i<t;i++)e[i]=r[i][a];let s=new Uint32Array(t);for(let i=0;i<t;i++)s[i]=i;s.sort((i,o)=>e[i]-e[o]);let n=new Array(t);if(d==="asc")for(let i=0;i<t;i++)n[i]=r[s[i]];else for(let i=0;i<t;i++)n[t-1-i]=r[s[i]];return n}};export{g as a};
@@ -0,0 +1 @@
1
+ import {a}from'./chunk-4PTQMQ73.mjs';import {b,a as a$2}from'./chunk-5B7Y4C22.mjs';import {a as a$1}from'./chunk-IYELLKOW.mjs';var g=class{constructor(e){this.callbacks=e;}create(e){let t=e;return Object.defineProperty(t,"search",{value:(r,n)=>n===void 0?this.callbacks.search(r):this.callbacks.search(r,n),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"sort",{value:(r,n,i)=>n===void 0?this.callbacks.sort(e,r,i):this.callbacks.sort(r,n,i),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"filter",{value:(r,n)=>n===void 0?this.callbacks.filter(e,r):this.callbacks.filter(r,n),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"clearIndexes",{value:r=>(this.callbacks.clearIndexes(r),this.create(e)),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"clearData",{value:r=>(this.callbacks.clearData(r),this.create(e)),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"data",{value:r=>this.callbacks.data(r),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"getOriginData",{value:()=>this.callbacks.getOriginData(),enumerable:false,configurable:true,writable:true}),t}};var x=o=>o===a?"search":o===a$1?"sort":o===b?"filter":null,y=(o,e,t)=>{if(o===a){let r=new a({data:e,...t});return {moduleName:"search",executeSearch:(n,i)=>i===void 0?r.search(n):r.search(n,i),clearIndexes:()=>r.clearIndexes(),clearData:()=>r.clearData(),data:n=>r.data(n),getOriginData:()=>r.getOriginData()}}if(o===a$1){let r=new a$1({data:e,...t});return {moduleName:"sort",executeSort:(n,i,s)=>i===void 0?r.sort(n):r.sort(n,i,s),clearIndexes:()=>r.clearIndexes(),clearData:()=>r.clearData(),data:n=>r.data(n),getOriginData:()=>r.getOriginData()}}if(o===b){let r=new b({data:e,...t});return {moduleName:"filter",executeFilter:(n,i)=>i===void 0?r[a$2](n):r[a$2](n,i),clearIndexes:()=>r.clearIndexes(),clearData:()=>r.clearData(),data:n=>r.data(n),getOriginData:()=>r.getOriginData()}}return null};var N={search:"TextSearchEngine",sort:"SortEngine",filter:"FilterEngine"},l=class o extends Error{constructor(e){super(e),this.name="MergeEnginesError";}static unavailableMethod(e){return new o(`MergeEngines: Method "${e}" is not available. Add module with method "${e}" to the \`imports\` array.`)}static unavailableEngine(e){let t=N[e];return new o(`MergeEngines: ${t} is not available. Add ${t} to the \`imports\` array.`)}static unavailableGetOriginData(){return new o("MergeEngines: getOriginData is not available. Add TextSearchEngine, SortEngine, or FilterEngine to the `imports` array.")}};var m=class{constructor(e){this.chainBuilder=new g({search:(e,t)=>t===void 0?this.search(e):this.search(e,t),sort:(e,t,r)=>t===void 0?this.sort(e):this.sort(e,t,r),filter:(e,t)=>t===void 0?this.filter(e):this.filter(e,t),getOriginData:()=>this.getOriginData(),data:e=>this.data(e),clearIndexes:e=>this.clearIndexes(e),clearData:e=>this.clearData(e)});let{imports:t,data:r,...n}=e,i=new Set(t),s=null,h=null,M=null,f={},E={},C={},b=null;for(let p of i){let D=x(p);if(!D)continue;let S=this.getModuleInitOptions(D,p.name,n),a=y(p,r,S);a&&(a.moduleName==="search"&&!s&&(s=a),a.moduleName==="sort"&&!h&&(h=a),a.moduleName==="filter"&&!M&&(M=a),f[a.moduleName]=()=>a.clearIndexes(),E[a.moduleName]=()=>a.clearData(),C[a.moduleName]=w=>a.data(w),b??(b=()=>a.getOriginData()));}this.searchModule=s,this.sortModule=h,this.filterModule=M,this.clearIndexMethods=f,this.clearDataMethods=E,this.setDataMethods=C,this.getOriginDataMethod=b;}getModuleInitOptions(e,t,r){let n={};for(let i of [e,t]){let s=r[i];this.isRecord(s)&&Object.assign(n,s);}return n}isRecord(e){return typeof e=="object"&&e!==null}search(e,t){if(!this.searchModule)throw l.unavailableEngine("search");return t===void 0?this.withChain(this.searchModule.executeSearch(e)):this.withChain(this.searchModule.executeSearch(e,t))}sort(e,t,r){if(!this.sortModule)throw l.unavailableEngine("sort");return t===void 0?this.withChain(this.sortModule.executeSort(e)):this.withChain(this.sortModule.executeSort(e,t,r))}filter(e,t){if(!this.filterModule)throw l.unavailableEngine("filter");return t===void 0?this.withChain(this.filterModule.executeFilter(e)):this.withChain(this.filterModule.executeFilter(e,t))}withChain(e){return this.chainBuilder.create(e)}getOriginData(){if(this.getOriginDataMethod)return this.getOriginDataMethod();throw l.unavailableGetOriginData()}clearIndexes(e){let t=this.clearIndexMethods[e];if(t)return t(),this;throw l.unavailableEngine(e)}data(e){let t=["search","sort","filter"];for(let r of t){let n=this.setDataMethods[r];n&&n(e);}return this}clearData(e){let t=this.clearDataMethods[e];if(t)return t(),this;throw l.unavailableEngine(e)}};export{m as a};
@@ -1,16 +1,6 @@
1
- import { C as CollectionItem, F as FilterCriterion } from '../types-D24zQWME.mjs';
2
- export { I as IndexableKey } from '../types-D24zQWME.mjs';
1
+ import { C as CollectionItem, F as FilterCriterion } from '../types-DuQWbqOC.mjs';
2
+ export { I as IndexableKey } from '../types-DuQWbqOC.mjs';
3
3
 
4
- /**
5
- * FilterEngine class for filtering collections by multiple criteria,
6
- * supporting indexed lookups and linear scans for large datasets.
7
- */
8
-
9
- interface FilterEngineOptions<T extends CollectionItem = CollectionItem> {
10
- data?: T[];
11
- fields?: (keyof T & string)[];
12
- filterByPreviousResult?: boolean;
13
- }
14
4
  interface FilterEngineChain<T extends CollectionItem> {
15
5
  filter(criteria: FilterCriterion<T>[]): T[] & FilterEngineChain<T>;
16
6
  filter(data: T[], criteria: FilterCriterion<T>[]): T[] & FilterEngineChain<T>;
@@ -20,14 +10,30 @@ interface FilterEngineChain<T extends CollectionItem> {
20
10
  clearData(): FilterEngine<T>;
21
11
  resetFilterState(): FilterEngine<T>;
22
12
  }
13
+ interface FilterEngineOptions<T extends CollectionItem = CollectionItem> {
14
+ data?: T[];
15
+ fields?: (keyof T & string)[];
16
+ nestedFields?: string[];
17
+ filterByPreviousResult?: boolean;
18
+ }
19
+
20
+ declare const FILTER_ENGINE_EXECUTE: unique symbol;
21
+
22
+ /**
23
+ * FilterEngine class for filtering collections by multiple criteria,
24
+ * supporting indexed lookups and linear scans for large datasets.
25
+ */
26
+
23
27
  declare class FilterEngine<T extends CollectionItem> {
24
28
  private indexer;
25
29
  private readonly filterByPreviousResult;
26
30
  private dataset;
27
31
  private readonly indexedFields;
32
+ private readonly nestedCollection;
28
33
  private previousResult;
29
34
  private previousCriteria;
30
35
  private previousBaseData;
36
+ private readonly chainBuilder;
31
37
  /**
32
38
  * Creates a new FilterEngine with optional data and fields to index.
33
39
  */
@@ -47,6 +53,8 @@ declare class FilterEngine<T extends CollectionItem> {
47
53
  */
48
54
  filter(criteria: FilterCriterion<T>[]): T[] & FilterEngineChain<T>;
49
55
  filter(data: T[], criteria: FilterCriterion<T>[]): T[] & FilterEngineChain<T>;
56
+ [FILTER_ENGINE_EXECUTE](criteria: FilterCriterion<T>[]): T[];
57
+ [FILTER_ENGINE_EXECUTE](data: T[], criteria: FilterCriterion<T>[]): T[];
50
58
  private withChain;
51
59
  private cloneCriteria;
52
60
  /**
@@ -75,4 +83,4 @@ declare class FilterEngine<T extends CollectionItem> {
75
83
  private estimateIndexSize;
76
84
  }
77
85
 
78
- export { CollectionItem, FilterCriterion, FilterEngine, type FilterEngineOptions };
86
+ export { CollectionItem, FilterCriterion, FilterEngine, type FilterEngineChain, type FilterEngineOptions };
@@ -1 +1 @@
1
- export{a as FilterEngine}from'../chunk-VJ4JZMHD.mjs';
1
+ export{b as FilterEngine}from'../chunk-5B7Y4C22.mjs';
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { TextSearchEngine } from './search/index.mjs';
2
2
  export { FilterEngine } from './filter/index.mjs';
3
3
  export { SortEngine } from './sort/index.mjs';
4
- export { EngineApi, EngineConstructor, MergeEngines, MergeEnginesOptions } from './merge/index.mjs';
5
- export { C as CollectionItem, F as FilterCriterion, I as IndexableKey, S as SortDescriptor, a as SortDirection } from './types-D24zQWME.mjs';
4
+ export { EngineApi, EngineConstructor, MergeEngines, MergeEnginesChain, MergeEnginesOptions, MergeModuleName } from './merge/index.mjs';
5
+ export { C as CollectionItem, F as FilterCriterion, I as IndexableKey, S as SortDescriptor, a as SortDirection } from './types-DuQWbqOC.mjs';
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- export{a as TextSearchEngine}from'./chunk-574GKMWM.mjs';export{a as FilterEngine}from'./chunk-VJ4JZMHD.mjs';export{a as SortEngine}from'./chunk-FZVBLKAD.mjs';export{a as MergeEngines}from'./chunk-A5FBM5K3.mjs';
1
+ export{a as MergeEngines}from'./chunk-LKNVBEFD.mjs';export{a as TextSearchEngine}from'./chunk-4PTQMQ73.mjs';export{b as FilterEngine}from'./chunk-5B7Y4C22.mjs';export{a as SortEngine}from'./chunk-IYELLKOW.mjs';
@@ -1,12 +1,19 @@
1
- import { C as CollectionItem, S as SortDescriptor, F as FilterCriterion } from '../types-D24zQWME.mjs';
2
- export { I as IndexableKey, a as SortDirection } from '../types-D24zQWME.mjs';
3
-
4
- /**
5
- * MergeEngines class that provides a unified interface for text search,
6
- * sorting, and filtering operations on collections.
7
- */
1
+ import { C as CollectionItem, S as SortDescriptor, F as FilterCriterion } from '../types-DuQWbqOC.mjs';
2
+ export { I as IndexableKey, a as SortDirection } from '../types-DuQWbqOC.mjs';
8
3
 
9
4
  type MergeModuleName = "search" | "sort" | "filter";
5
+ interface MergeEnginesChain<T extends CollectionItem> {
6
+ search(query: string): T[] & MergeEnginesChain<T>;
7
+ search(field: (keyof T & string) | (string & {}), query: string): T[] & MergeEnginesChain<T>;
8
+ sort(descriptors: SortDescriptor<T>[]): T[] & MergeEnginesChain<T>;
9
+ sort(data: T[], descriptors: SortDescriptor<T>[], inPlace?: boolean): T[] & MergeEnginesChain<T>;
10
+ filter(criteria: FilterCriterion<T>[]): T[] & MergeEnginesChain<T>;
11
+ filter(data: T[], criteria: FilterCriterion<T>[]): T[] & MergeEnginesChain<T>;
12
+ getOriginData(): T[];
13
+ data(data: T[]): MergeEngines<T>;
14
+ clearIndexes(module: MergeModuleName): T[] & MergeEnginesChain<T>;
15
+ clearData(module: MergeModuleName): T[] & MergeEnginesChain<T>;
16
+ }
10
17
  interface EngineConstructor {
11
18
  new (options: Record<string, unknown>): object;
12
19
  prototype: object;
@@ -20,58 +27,42 @@ interface MergeEnginesOptions<T extends CollectionItem> {
20
27
  data: T[];
21
28
  [key: string]: unknown;
22
29
  }
23
- interface MergeEnginesChain<T extends CollectionItem> {
24
- search(query: string): T[] & MergeEnginesChain<T>;
25
- search(field: keyof T & string, query: string): T[] & MergeEnginesChain<T>;
26
- sort(descriptors: SortDescriptor<T>[]): T[] & MergeEnginesChain<T>;
27
- sort(data: T[], descriptors: SortDescriptor<T>[], inPlace?: boolean): T[] & MergeEnginesChain<T>;
28
- filter(criteria: FilterCriterion<T>[]): T[] & MergeEnginesChain<T>;
29
- filter(data: T[], criteria: FilterCriterion<T>[]): T[] & MergeEnginesChain<T>;
30
- getOriginData(module: MergeModuleName): T[];
31
- data(data: T[]): MergeEngines<T>;
32
- clearIndexes(module: MergeModuleName): T[] & MergeEnginesChain<T>;
33
- clearData(module: MergeModuleName): T[] & MergeEnginesChain<T>;
34
- }
30
+
31
+ /**
32
+ * MergeEngines class that provides a unified interface for text search,
33
+ * sorting, and filtering operations on collections.
34
+ */
35
+
35
36
  declare class MergeEngines<T extends CollectionItem> {
36
- private readonly engine;
37
+ private readonly searchModule;
38
+ private readonly sortModule;
39
+ private readonly filterModule;
37
40
  private readonly clearIndexMethods;
38
41
  private readonly clearDataMethods;
39
42
  private readonly setDataMethods;
40
- private readonly getOriginDataMethods;
43
+ private readonly getOriginDataMethod;
44
+ private readonly chainBuilder;
41
45
  /**
42
46
  * Creates a new MergeEngines instance with the given options.
43
47
  * Collects all modules from imports.
44
48
  */
45
49
  constructor(options: MergeEnginesOptions<T>);
46
- private getModuleName;
47
50
  /**
48
51
  * Gets the initialization options for a module.
49
52
  */
50
53
  private getModuleInitOptions;
51
- /**
52
- * Gets the method names from a prototype.
53
- */
54
- private getMethodNames;
55
- /**
56
- * Checks if an object has a specific method.
57
- */
58
- private hasMethod;
59
54
  private isRecord;
60
- /**
61
- * Calls a method on the engine.
62
- */
63
- private callEngineMethod;
64
55
  search(query: string): T[] & MergeEnginesChain<T>;
65
- search(field: keyof T & string, query: string): T[] & MergeEnginesChain<T>;
56
+ search(field: (keyof T & string) | (string & {}), query: string): T[] & MergeEnginesChain<T>;
66
57
  sort(descriptors: SortDescriptor<T>[]): T[] & MergeEnginesChain<T>;
67
58
  sort(data: T[], descriptors: SortDescriptor<T>[], inPlace?: boolean): T[] & MergeEnginesChain<T>;
68
59
  filter(criteria: FilterCriterion<T>[]): T[] & MergeEnginesChain<T>;
69
60
  filter(data: T[], criteria: FilterCriterion<T>[]): T[] & MergeEnginesChain<T>;
70
61
  private withChain;
71
- getOriginData(module: MergeModuleName): T[];
62
+ getOriginData(): T[];
72
63
  clearIndexes(module: MergeModuleName): this;
73
64
  data(data: T[]): this;
74
65
  clearData(module: MergeModuleName): this;
75
66
  }
76
67
 
77
- export { CollectionItem, type EngineApi, type EngineConstructor, FilterCriterion, MergeEngines, type MergeEnginesOptions, SortDescriptor };
68
+ export { CollectionItem, type EngineApi, type EngineConstructor, FilterCriterion, MergeEngines, type MergeEnginesChain, type MergeEnginesOptions, type MergeModuleName, SortDescriptor };
@@ -1 +1 @@
1
- export{a as MergeEngines}from'../chunk-A5FBM5K3.mjs';
1
+ export{a as MergeEngines}from'../chunk-LKNVBEFD.mjs';import'../chunk-4PTQMQ73.mjs';import'../chunk-5B7Y4C22.mjs';import'../chunk-IYELLKOW.mjs';
@@ -1,28 +1,24 @@
1
- import { C as CollectionItem } from '../types-D24zQWME.mjs';
2
- export { I as IndexableKey } from '../types-D24zQWME.mjs';
3
-
4
- /**
5
- * TextSearchEngine class for performing fast substring search on string fields
6
- * using n-gram indexing and intersection.
7
- */
1
+ import { C as CollectionItem } from '../types-DuQWbqOC.mjs';
2
+ export { I as IndexableKey } from '../types-DuQWbqOC.mjs';
8
3
 
9
4
  interface TextSearchEngineOptions<T extends CollectionItem = CollectionItem> {
10
5
  data?: T[];
11
6
  fields?: (keyof T & string)[];
7
+ nestedFields?: string[];
12
8
  minQueryLength?: number;
13
9
  }
14
- interface TextSearchEngineChain<T extends CollectionItem> {
15
- search(query: string): T[] & TextSearchEngineChain<T>;
16
- search(field: keyof T & string, query: string): T[] & TextSearchEngineChain<T>;
17
- getOriginData(): T[];
18
- data(data: T[]): TextSearchEngine<T>;
19
- clearIndexes(): TextSearchEngine<T>;
20
- clearData(): TextSearchEngine<T>;
21
- }
10
+
11
+ /**
12
+ * TextSearchEngine class for performing fast substring search on string fields
13
+ * using n-gram indexing and intersection.
14
+ */
15
+
22
16
  declare class TextSearchEngine<T extends CollectionItem> {
23
17
  private ngramIndexes;
18
+ private normalizedFieldValues;
24
19
  private dataset;
25
20
  private readonly indexedFields;
21
+ private readonly nestedCollection;
26
22
  private readonly minQueryLength;
27
23
  /**
28
24
  * Creates a new TextSearchEngine with optional data and fields to index.
@@ -33,8 +29,8 @@ declare class TextSearchEngine<T extends CollectionItem> {
33
29
  * Builds an n-gram index for the given field.
34
30
  */
35
31
  private buildIndex;
36
- search(query: string): T[] & TextSearchEngineChain<T>;
37
- search(field: keyof T & string, query: string): T[] & TextSearchEngineChain<T>;
32
+ search(query: string): T[];
33
+ search(field: (keyof T & string) | (string & {}), query: string): T[];
38
34
  private normalizeQuery;
39
35
  /**
40
36
  * Searches all indexed fields.
@@ -56,7 +52,6 @@ declare class TextSearchEngine<T extends CollectionItem> {
56
52
  * Searches a specific field linearly without index.
57
53
  */
58
54
  private searchFieldLinear;
59
- private withChain;
60
55
  clearIndexes(): this;
61
56
  getOriginData(): T[];
62
57
  data(data: T[]): this;
@@ -1 +1 @@
1
- export{a as TextSearchEngine}from'../chunk-574GKMWM.mjs';
1
+ export{a as TextSearchEngine}from'../chunk-4PTQMQ73.mjs';
@@ -1,23 +1,16 @@
1
- import { C as CollectionItem, S as SortDescriptor } from '../types-D24zQWME.mjs';
2
- export { a as SortDirection } from '../types-D24zQWME.mjs';
1
+ import { C as CollectionItem, S as SortDescriptor } from '../types-DuQWbqOC.mjs';
2
+ export { a as SortDirection } from '../types-DuQWbqOC.mjs';
3
+
4
+ interface SortEngineOptions<T extends CollectionItem = CollectionItem> {
5
+ data?: T[];
6
+ fields?: (keyof T & string)[];
7
+ }
3
8
 
4
9
  /**
5
10
  * SortEngine class for sorting large datasets using pre-built indexes
6
11
  * for O(n) performance or fallback to in-place sorting.
7
12
  */
8
13
 
9
- interface SortEngineOptions<T extends CollectionItem = CollectionItem> {
10
- data?: T[];
11
- fields?: (keyof T & string)[];
12
- }
13
- interface SortEngineChain<T extends CollectionItem> {
14
- sort(descriptors: SortDescriptor<T>[]): T[] & SortEngineChain<T>;
15
- sort(data: T[], descriptors: SortDescriptor<T>[], inPlace?: boolean): T[] & SortEngineChain<T>;
16
- getOriginData(): T[];
17
- data(data: T[]): SortEngine<T>;
18
- clearIndexes(): SortEngine<T>;
19
- clearData(): SortEngine<T>;
20
- }
21
14
  declare class SortEngine<T extends CollectionItem> {
22
15
  private cache;
23
16
  private dataset;
@@ -41,17 +34,12 @@ declare class SortEngine<T extends CollectionItem> {
41
34
  /**
42
35
  * Sorts the data based on the given descriptors.
43
36
  */
44
- sort(descriptors: SortDescriptor<T>[]): T[] & SortEngineChain<T>;
45
- sort(data: T[], descriptors: SortDescriptor<T>[], inPlace?: boolean): T[] & SortEngineChain<T>;
46
- private withChain;
37
+ sort(descriptors: SortDescriptor<T>[]): T[];
38
+ sort(data: T[], descriptors: SortDescriptor<T>[], inPlace?: boolean): T[];
47
39
  /**
48
40
  * Reconstructs the sorted array from the cached index.
49
41
  */
50
42
  private reconstructFromIndex;
51
- /**
52
- * Checks if the field snapshot is still valid.
53
- */
54
- private isFieldSnapshotValid;
55
43
  /**
56
44
  * Builds a comparator function for sorting.
57
45
  */
@@ -1 +1 @@
1
- export{a as SortEngine}from'../chunk-FZVBLKAD.mjs';
1
+ export{a as SortEngine}from'../chunk-IYELLKOW.mjs';
@@ -3,7 +3,7 @@ type IndexableKey<T> = {
3
3
  [K in keyof T]: T[K] extends string | number ? K : never;
4
4
  }[keyof T];
5
5
  interface FilterCriterion<T extends CollectionItem> {
6
- field: keyof T & string;
6
+ field: (keyof T & string) | (string & {});
7
7
  values: any[];
8
8
  }
9
9
  type SortDirection = "asc" | "desc";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devisfuture/mega-collection",
3
- "version": "1.3.0",
3
+ "version": "2.0.2",
4
4
  "description": "High-performance search, filter & sort engine for 100K+ item collections in JavaScript/TypeScript",
5
5
  "exports": {
6
6
  ".": {
@@ -1 +0,0 @@
1
- var y=3,T=12;function I(l){let e=l.toLowerCase(),n=Math.min(y,e.length),t=e.length-n+1,r=new Array(t);for(let i=0;i<t;i++)r[i]=e.substring(i,i+n);return r}function m(l){let e=I(l);if(e.length<=T)return new Set(e);let n=new Set,t=e.length-1,r=T-1;for(let i=0;i<=r;i++){let s=Math.round(i*t/r);n.add(e[s]);}return n}function b(l,e){let n=l.get(e);if(n)return n;let t=new Set;return l.set(e,t),t}var x=class{constructor(e={}){this.ngramIndexes=new Map;this.dataset=[];this.indexedFields=new Set;if(this.minQueryLength=e.minQueryLength??1,!!e.data&&(this.dataset=e.data,e.fields?.length)){for(let n of e.fields)this.indexedFields.add(n);this.rebuildConfiguredIndexes();}}rebuildConfiguredIndexes(){this.ngramIndexes.clear();for(let e of this.indexedFields)this.buildIndex(this.dataset,e);}buildIndex(e,n){let t,r;if(Array.isArray(e))t=e,r=n;else {if(!this.dataset.length)throw new Error("TextSearchEngine: no dataset in memory. Either pass `data` in the constructor options, or call buildIndex(data, field).");t=this.dataset,r=e;}this.dataset=t;let i=new Map;for(let s=0,c=t.length;s<c;s++){let o=t[s][r];if(typeof o!="string")continue;let h=o.toLowerCase();for(let a=0,d=h.length;a<d;a++){let g=d-a,u=Math.min(y,g);for(let f=1;f<=u;f++){let p=h.substring(a,a+f);b(i,p).add(s);}}}return this.ngramIndexes.set(r,i),this}search(e,n){return n===void 0?this.withChain(this.searchAllFields(e)):this.withChain(this.searchField(e,n))}normalizeQuery(e){return e.trim().toLowerCase()}searchAllFields(e){let n=[...this.ngramIndexes.keys()],t=this.normalizeQuery(e);if(!t)return this.dataset;if(t.length<this.minQueryLength)return this.dataset;if(!n.length)return this.searchAllFieldsLinear(t);let r=m(t);if(!r.size)return [];let i=new Set,s=[];for(let c of n)for(let o of this.searchFieldWithPreparedQuery(c,t,r))i.has(o)||(i.add(o),s.push(o));return s}searchField(e,n){let t=this.normalizeQuery(n);if(!t)return this.dataset;if(t.length<this.minQueryLength)return this.dataset;if(!this.ngramIndexes.size)return this.searchFieldLinear(e,t);let r=m(t);return r.size?this.searchFieldWithPreparedQuery(e,t,r):[]}searchFieldWithPreparedQuery(e,n,t){let r=this.ngramIndexes.get(e);if(!r)return [];let i=[];for(let h of t){let a=r.get(h);if(!a)return [];i.push(a);}i.sort((h,a)=>h.size-a.size);let s=i[0],c=i.length,o=[];for(let h of s){let a=true;for(let u=1;u<c;u++)if(!i[u].has(h)){a=false;break}if(!a)continue;let d=this.dataset[h];if(!d)continue;let g=d[e];typeof g=="string"&&g.toLowerCase().includes(n)&&o.push(d);}return o}searchAllFieldsLinear(e){if(!this.dataset.length)return [];let n=[];for(let t=0;t<this.dataset.length;t++){let r=this.dataset[t],i=false;for(let s of Object.values(r))if(typeof s=="string"&&s.toLowerCase().includes(e)){i=true;break}i&&n.push(r);}return n}searchFieldLinear(e,n){if(!this.dataset.length)return [];let t=[];for(let r=0;r<this.dataset.length;r++){let i=this.dataset[r][e];typeof i=="string"&&i.toLowerCase().includes(n)&&t.push(this.dataset[r]);}return t}withChain(e){let n=e;return Object.defineProperty(n,"search",{value:(t,r)=>r===void 0?this.search(t):this.search(t,r),enumerable:false,configurable:true,writable:true}),Object.defineProperty(n,"clearIndexes",{value:()=>this.clearIndexes(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(n,"getOriginData",{value:()=>this.getOriginData(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(n,"data",{value:t=>this.data(t),enumerable:false,configurable:true,writable:true}),Object.defineProperty(n,"clearData",{value:()=>this.clearData(),enumerable:false,configurable:true,writable:true}),n}clearIndexes(){return this.ngramIndexes.clear(),this}getOriginData(){return this.dataset}data(e){return this.dataset=e,this.rebuildConfiguredIndexes(),this}clearData(){return this.dataset=[],this.ngramIndexes.clear(),this}};export{x as a};
@@ -1 +0,0 @@
1
- var T=class{constructor(e){let{imports:n,data:t,...r}=e,a=new Set(n),s={},l={},c={},d={},u={};for(let M of a){let f=M.prototype,g=this.getMethodNames(f);if(g.length===0)continue;let p=this.getModuleInitOptions(M.name,g,r),i=new M({data:t,...p}),o=this.getModuleName(g);o&&this.hasMethod(i,"clearIndexes")&&!l[o]&&(l[o]=i.clearIndexes.bind(i)),o&&this.hasMethod(i,"clearData")&&!c[o]&&(c[o]=i.clearData.bind(i)),o&&this.hasMethod(i,"data")&&!d[o]&&(d[o]=i.data.bind(i)),o&&this.hasMethod(i,"getOriginData")&&!u[o]&&(u[o]=i.getOriginData.bind(i));for(let h of g)s[h]||this.hasMethod(i,h)&&(s[h]=i[h].bind(i));}this.engine=Object.keys(s).length>0?s:null,this.clearIndexMethods=l,this.clearDataMethods=c,this.setDataMethods=d,this.getOriginDataMethods=u;}getModuleName(e){return e.includes("search")?"search":e.includes("sort")?"sort":e.includes("filter")?"filter":null}getModuleInitOptions(e,n,t){let r={},a=t[e];this.isRecord(a)&&Object.assign(r,a);for(let s of n){let l=t[s];this.isRecord(l)&&Object.assign(r,l);}return r}getMethodNames(e){let n=e;return Object.getOwnPropertyNames(n).filter(t=>t==="constructor"?false:typeof n[t]=="function")}hasMethod(e,n){return typeof e=="object"&&e!==null&&typeof e[n]=="function"}isRecord(e){return typeof e=="object"&&e!==null}callEngineMethod(e,n){let t=this.engine?.[e];if(!t)throw new Error(`MergeEngines: Method "${e}" is not available. Add module with method "${e}" to the \`imports\` array.`);return t(...n)}search(e,n){if(!this.engine?.search)throw new Error("MergeEngines: TextSearchEngine is not available. Add TextSearchEngine to the `imports` array.");return n===void 0?this.withChain(this.callEngineMethod("search",[e])):this.withChain(this.callEngineMethod("search",[e,n]))}sort(e,n,t){if(!this.engine?.sort)throw new Error("MergeEngines: SortEngine is not available. Add SortEngine to the `imports` array.");return n===void 0?this.withChain(this.callEngineMethod("sort",[e])):this.withChain(this.callEngineMethod("sort",[e,n,t]))}filter(e,n){if(!this.engine?.filter)throw new Error("MergeEngines: FilterEngine is not available. Add FilterEngine to the `imports` array.");return n===void 0?this.withChain(this.callEngineMethod("filter",[e])):this.withChain(this.callEngineMethod("filter",[e,n]))}withChain(e){let n=e;return Object.defineProperty(n,"search",{value:(t,r)=>r===void 0?this.search(t):this.search(t,r),enumerable:false,configurable:true,writable:true}),Object.defineProperty(n,"sort",{value:(t,r,a)=>r===void 0?this.sort(e,t,a):this.sort(t,r,a),enumerable:false,configurable:true,writable:true}),Object.defineProperty(n,"filter",{value:(t,r)=>r===void 0?this.filter(e,t):this.filter(t,r),enumerable:false,configurable:true,writable:true}),Object.defineProperty(n,"clearIndexes",{value:t=>(this.clearIndexes(t),this.withChain(e)),enumerable:false,configurable:true,writable:true}),Object.defineProperty(n,"clearData",{value:t=>(this.clearData(t),this.withChain(e)),enumerable:false,configurable:true,writable:true}),Object.defineProperty(n,"data",{value:t=>this.data(t),enumerable:false,configurable:true,writable:true}),Object.defineProperty(n,"getOriginData",{value:t=>this.getOriginData(t),enumerable:false,configurable:true,writable:true}),n}getOriginData(e){let n=this.getOriginDataMethods[e];if(n)return n();let t={search:"TextSearchEngine",sort:"SortEngine",filter:"FilterEngine"};throw new Error(`MergeEngines: ${t[e]} is not available. Add ${t[e]} to the \`imports\` array.`)}clearIndexes(e){let n=this.clearIndexMethods[e];if(n)return n(),this;let t={search:"TextSearchEngine",sort:"SortEngine",filter:"FilterEngine"};throw new Error(`MergeEngines: ${t[e]} is not available. Add ${t[e]} to the \`imports\` array.`)}data(e){let n=["search","sort","filter"];for(let t of n){let r=this.setDataMethods[t];r&&r(e);}return this}clearData(e){let n=this.clearDataMethods[e];if(n)return n(),this;let t={search:"TextSearchEngine",sort:"SortEngine",filter:"FilterEngine"};throw new Error(`MergeEngines: ${t[e]} is not available. Add ${t[e]} to the \`imports\` array.`)}};export{T as a};
@@ -1 +0,0 @@
1
- var u=class{constructor(t={}){this.cache=new Map;this.dataset=[];this.indexedFields=new Set;if(t.data&&(this.dataset=t.data,t.fields?.length)){for(let s of t.fields)this.indexedFields.add(s);this.rebuildConfiguredIndexes();}}rebuildConfiguredIndexes(){this.cache.clear();for(let t of this.indexedFields)this.buildIndex(this.dataset,t);}buildIndex(t,s){let a,e;if(Array.isArray(t))a=t,e=s;else {if(!this.dataset.length)throw new Error("SortEngine: no dataset in memory. Either pass `data` in the constructor options, or call buildIndex(data, field).");a=this.dataset,e=t;}this.dataset=a;let r=a.length,n=new Uint32Array(r);for(let o=0;o<r;o++)n[o]=o;let l=a.map(o=>o[e]);if(typeof l[0]=="number"){let o=new Float64Array(r);for(let d=0;d<r;d++)o[d]=l[d];n.sort((d,c)=>o[d]-o[c]);}else n.sort((o,d)=>{let c=l[o],h=l[d];return c<h?-1:c>h?1:0});return this.cache.set(e,{indexes:n,dataRef:a,itemCount:r,fieldSnapshot:l}),this}clearIndexes(){return this.cache.clear(),this}clearData(){return this.dataset=[],this.cache.clear(),this}data(t){return this.dataset=t,this.rebuildConfiguredIndexes(),this}getOriginData(){return this.dataset}sort(t,s,a=false){let e,r;if(s===void 0){if(!this.dataset.length)throw new Error("SortEngine: no dataset in memory. Either pass `data` in the constructor options, or call sort(data, descriptors).");e=this.dataset,r=t;}else e=t,r=s;if(r.length===0||e.length===0)return this.withChain(e);if(r.length===1){let{field:i,direction:o}=r[0],d=this.cache.get(i);if(d&&d.dataRef===e&&d.itemCount===e.length&&this.isFieldSnapshotValid(e,i,d.fieldSnapshot))return this.withChain(this.reconstructFromIndex(e,d.indexes,o))}let n=a?e:e.slice();if(r.length===1&&e.length>0&&typeof e[0][r[0].field]=="number")return this.withChain(this.radixSortNumeric(n,r[0].field,r[0].direction));let l=this.buildComparator(r);return n.sort(l),this.withChain(n)}withChain(t){let s=t;return Object.defineProperty(s,"sort",{value:(a,e,r=false)=>e===void 0?this.sort(t,a,r):this.sort(a,e,r),enumerable:false,configurable:true,writable:true}),Object.defineProperty(s,"clearIndexes",{value:()=>this.clearIndexes(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(s,"data",{value:a=>this.data(a),enumerable:false,configurable:true,writable:true}),Object.defineProperty(s,"getOriginData",{value:()=>this.getOriginData(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(s,"clearData",{value:()=>this.clearData(),enumerable:false,configurable:true,writable:true}),s}reconstructFromIndex(t,s,a){let e=t.length,r=new Array(e);if(a==="asc")for(let n=0;n<e;n++)r[n]=t[s[n]];else for(let n=0;n<e;n++)r[n]=t[s[e-1-n]];return r}isFieldSnapshotValid(t,s,a){for(let e=0;e<t.length;e++)if(t[e][s]!==a[e])return false;return true}buildComparator(t){let s=t.map(({field:r})=>r),a=t.map(({direction:r})=>r==="asc"?1:-1),e=s.length;return (r,n)=>{for(let l=0;l<e;l++){let i=r[s[l]],o=n[s[l]];if(i<o)return -a[l];if(i>o)return a[l]}return 0}}radixSortNumeric(t,s,a){let e=t.length,r=new Float64Array(e);for(let i=0;i<e;i++)r[i]=t[i][s];let n=new Uint32Array(e);for(let i=0;i<e;i++)n[i]=i;n.sort((i,o)=>r[i]-r[o]);let l=new Array(e);if(a==="asc")for(let i=0;i<e;i++)l[i]=t[n[i]];else for(let i=0;i<e;i++)l[e-1-i]=t[n[i]];return l}};export{u as a};
@@ -1 +0,0 @@
1
- var p=class{constructor(){this.indexes=new Map;}buildIndex(e,r){let n=new Map;for(let s=0,t=e.length;s<t;s++){let i=e[s],a=i[r];if(a==null)continue;let l=n.get(a);l?l.push(i):n.set(a,[i]);}this.indexes.set(r,n);}getByValue(e,r){let n=this.indexes.get(e);return n?n.get(r)??[]:[]}getByValues(e,r){let n=this.indexes.get(e);if(!n)return [];if(r.length===1)return n.get(r[0])??[];let s=new Set,t=[];for(let i=0;i<r.length;i++){let a=n.get(r[i]);if(a!==void 0)for(let l=0;l<a.length;l++){let o=a[l];s.has(o)||(s.add(o),t.push(o));}}return t}hasIndex(e){return this.indexes.has(e)}clear(){this.indexes.clear();}getIndexMap(e){return this.indexes.get(e)}};var v=class{constructor(e={}){this.dataset=[];this.indexedFields=new Set;this.previousResult=null;this.previousCriteria=null;this.previousBaseData=null;if(this.indexer=new p,this.filterByPreviousResult=e.filterByPreviousResult??false,!!e.data&&(this.dataset=e.data,e.fields?.length)){for(let r of e.fields)this.indexedFields.add(r);this.rebuildConfiguredIndexes();}}rebuildConfiguredIndexes(){this.indexer.clear();for(let e of this.indexedFields)this.buildIndex(this.dataset,e);}buildIndex(e,r){if(!Array.isArray(e)){if(!this.dataset.length)throw new Error("FilterEngine: no dataset in memory. Either pass `data` in the constructor options, or call buildIndex(data, field).");return this.indexer.buildIndex(this.dataset,e),this}return this.dataset=e,this.previousResult=null,this.previousCriteria=null,this.previousBaseData=null,this.indexer.buildIndex(e,r),this}clearIndexes(){return this.indexer.clear(),this}resetFilterState(){return this.previousResult=null,this.previousCriteria=null,this.previousBaseData=null,this}clearData(){return this.dataset=[],this.indexer.clear(),this.resetFilterState(),this}data(e){return this.dataset=e,this.resetFilterState(),this.rebuildConfiguredIndexes(),this}getOriginData(){return this.dataset}filter(e,r){let n=r===void 0,s,t,i;if(n){if(!this.dataset.length)throw new Error("FilterEngine: no dataset in memory. Either pass `data` in the constructor options, or call filter(data, criteria).");if(t=e,this.filterByPreviousResult&&this.previousResult!==null&&this.previousCriteria!==null&&this.previousBaseData===this.dataset){let u=this.hasCriteriaAdditions(this.previousCriteria,t),h=this.hasCriteriaRemovals(this.previousCriteria,t);if(!u&&!h)return this.withChain(this.previousResult);u&&!h?(s=this.previousResult,i=this.getAddedCriteria(this.previousCriteria,t)):(s=this.dataset,i=t);}else s=this.dataset,i=t;}else if(t=r,s=e,this.filterByPreviousResult&&this.previousResult!==null&&this.previousCriteria!==null&&this.previousBaseData===s){let u=this.hasCriteriaAdditions(this.previousCriteria,t),h=this.hasCriteriaRemovals(this.previousCriteria,t);if(!u&&!h)return this.withChain(this.previousResult);u&&!h?(s=this.previousResult,i=this.getAddedCriteria(this.previousCriteria,t)):i=t;}else i=t;if(t.length===0)return this.filterByPreviousResult&&(this.previousResult=null,this.previousCriteria=null,this.previousBaseData=null),this.withChain(n?this.dataset:s);n&&!i&&(i=t);let{indexedCriteria:a,linearCriteria:l}=i.reduce((u,h)=>(this.indexer.hasIndex(h.field)?u.indexedCriteria.push(h):u.linearCriteria.push(h),u),{indexedCriteria:[],linearCriteria:[]}),o;if(a.length>0&&l.length===0)return o=this.filterViaIndex(a,s),this.filterByPreviousResult&&(this.previousResult=o,this.previousCriteria=this.cloneCriteria(t),this.previousBaseData=n?this.dataset:e),this.withChain(o);if(a.length>0&&l.length>0){let u=this.filterViaIndex(a,s);return o=this.linearFilter(u,l),this.filterByPreviousResult&&(this.previousResult=o,this.previousCriteria=this.cloneCriteria(t),this.previousBaseData=n?this.dataset:e),this.withChain(o)}return o=this.linearFilter(s,i),this.filterByPreviousResult&&(this.previousResult=o,this.previousCriteria=this.cloneCriteria(t),this.previousBaseData=n?this.dataset:e),this.withChain(o)}withChain(e){let r=e;return Object.defineProperty(r,"filter",{value:(n,s)=>s===void 0?this.filter(e,n):this.filter(n,s),enumerable:false,configurable:true,writable:true}),Object.defineProperty(r,"clearIndexes",{value:()=>this.clearIndexes(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(r,"data",{value:n=>this.data(n),enumerable:false,configurable:true,writable:true}),Object.defineProperty(r,"getOriginData",{value:()=>this.getOriginData(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(r,"clearData",{value:()=>this.clearData(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(r,"resetFilterState",{value:()=>this.resetFilterState(),enumerable:false,configurable:true,writable:true}),r}cloneCriteria(e){return e.map(({field:r,values:n})=>({field:r,values:[...n]}))}hasCriteriaAdditions(e,r){let n=new Map(e.map(({field:t,values:i})=>[t,new Set(i)])),s=new Map(r.map(({field:t,values:i})=>[t,new Set(i)]));for(let[t,i]of s){let a=n.get(t);if(!a)return true;for(let l of i)if(!a.has(l))return true}return false}hasCriteriaRemovals(e,r){let n=new Map(e.map(({field:t,values:i})=>[t,new Set(i)])),s=new Map(r.map(({field:t,values:i})=>[t,new Set(i)]));for(let[t,i]of n){let a=s.get(t);if(!a)return true;for(let l of i)if(!a.has(l))return true}return false}getAddedCriteria(e,r){let n=new Map(e.map(({field:t,values:i})=>[t,new Set(i)])),s=[];for(let{field:t,values:i}of r){let a=n.get(t);if(!a){s.push({field:t,values:[...i]});continue}let l=i.filter(o=>!a.has(o));l.length>0&&s.push({field:t,values:l});}return s}linearFilter(e,r){let n=new Map(r.map(({field:i,values:a})=>[i,new Set(a)])),s=r.map(({field:i})=>i),t=[];for(let i=0;i<e.length;i++){let a=e[i],l=true;for(let o=0;o<s.length;o++){let u=s[o];if(!n.get(u).has(a[u])){l=false;break}}l&&t.push(a);}return t}filterViaIndex(e,r){let s=r!==this.dataset?new Set(r):null;if(e.length===1){let d=this.indexer.getByValues(e[0].field,e[0].values);return s?d.filter(f=>s.has(f)):d}let t=e.map(d=>({criterion:d,size:this.estimateIndexSize(d)})).sort((d,f)=>d.size-f.size),{field:i,values:a}=t[0].criterion,l=this.indexer.getByValues(i,a);if(l.length===0)return [];let o=new Map(t.slice(1).map(({criterion:{field:d,values:f}})=>[d,new Set(f)])),u=Array.from(o.keys()),h=[];for(let d=0;d<l.length;d++){let f=l[d],c=true;for(let g=0;g<u.length;g++){let T=u[g];if(!o.get(T).has(f[T])){c=false;break}}c&&s&&!s.has(f)&&(c=false),c&&h.push(f);}return h}estimateIndexSize(e){let r=this.indexer.getIndexMap(e.field);return r?e.values.reduce((n,s)=>{let t=r.get(s);return t?n+t.length:n},0):1/0}};export{v as a};