@devisfuture/mega-collection 1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 devisfuture
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,285 @@
1
+ <p align="center">
2
+ <img src="./illustration.png" alt="electron-modular illustration" width="640" />
3
+ </p>
4
+
5
+ [![codecov](https://codecov.io/gh/trae-op/mega-collection/graph/badge.svg?token=MZHSKKPV79)](https://codecov.io/gh/trae-op/mega-collection) [![TypeScript](https://img.shields.io/badge/TypeScript-%233178C6.svg?style=flat&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
6
+
7
+ # @devisfuture/mega-collection
8
+
9
+ > Search, filter & sort engine for **50 K+** item collections in JavaScript / TypeScript.
10
+
11
+ What does this package solve?
12
+
13
+ Sometimes in projects, you need to iterate through huge collections (20,000, 40,000, 80,000 elements in an array) that have come from the server. Usually, the most common features are searching, filtering, and sorting.
14
+ 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.
15
+
16
+ Zero dependencies. Tree-shakeable. Import only what you need.
17
+
18
+ Each engine lives in its own entry point (`/search`, `/filter`, `/sort`).
19
+ Importing just `@devisfuture/mega-collection/search` or the other sub-modules means
20
+ only that code ends up in your bundle — unused engines stay out. For example, if
21
+ you only pull in `TextSearchEngine` the filter and sort logic won’t be included.
22
+
23
+ ## Features
24
+
25
+ | Capability | Strategy | Complexity |
26
+ | -------------------------- | -------------------------------------- | ---------------------------------- |
27
+ | **Indexed filter** | Hash-Map index (`Map<value, T[]>`) | **O(1)** |
28
+ | **Multi-value filter** | Index intersection + `Set` membership | **O(k)** indexed / **O(n)** linear |
29
+ | **Text search** (contains) | Trigram inverted index + verify | **O(candidates)** |
30
+ | **Sorting** | Pre-sorted index (cached) / V8 TimSort | **O(n)** cached / **O(n log n)** |
31
+
32
+ ## React demo
33
+
34
+ A small repository demonstrates using `@devisfuture/mega-collection` in a React project:
35
+
36
+ https://github.com/trae-op/quick-start_react_mega-collection
37
+
38
+ The example shows search, filter, sort and merge all modules setups with a minimal UI.
39
+
40
+ ## Install
41
+
42
+ ```bash
43
+ npm install @devisfuture/mega-collection
44
+ ```
45
+
46
+ _This package is framework-agnostic and works in all popular front‑end frameworks including React, Angular, Vue and so on._
47
+
48
+ ## Quick Start
49
+
50
+ ```ts
51
+ interface User {
52
+ id: number;
53
+ name: string;
54
+ city: string;
55
+ age: number;
56
+ }
57
+ ```
58
+
59
+ ### All-in-one: `MergeEngines`
60
+
61
+ Use `MergeEngines` to combine search, filter and sort around a single shared dataset.
62
+ Declare which engines you need in `imports` — only those are initialised.
63
+
64
+ ```ts
65
+ import { MergeEngines } from "@devisfuture/mega-collection";
66
+ import { TextSearchEngine } from "@devisfuture/mega-collection/search";
67
+ import { SortEngine } from "@devisfuture/mega-collection/sort";
68
+ import { FilterEngine } from "@devisfuture/mega-collection/filter";
69
+
70
+ const engine = new MergeEngines<User>({
71
+ imports: [TextSearchEngine, SortEngine, FilterEngine],
72
+ data: users,
73
+ search: { fields: ["name", "city"], minQueryLength: 2 },
74
+ filter: { fields: ["city", "age"], filterByPreviousResult: true },
75
+ sort: { fields: ["age", "name", "city"] },
76
+ });
77
+
78
+ // dataset is passed once at init — no need to repeat it in every call
79
+ engine.search("john");
80
+ engine.sort([{ field: "age", direction: "asc" }]);
81
+ engine.filter([{ field: "city", values: ["Kyiv", "Lviv"] }]);
82
+ ```
83
+
84
+ ---
85
+
86
+ ### Search only
87
+
88
+ ```ts
89
+ import { TextSearchEngine } from "@devisfuture/mega-collection/search";
90
+
91
+ const engine = new TextSearchEngine<User>({
92
+ data: users,
93
+ fields: ["name", "city"],
94
+ minQueryLength: 2,
95
+ });
96
+
97
+ engine.search("john"); // searches all indexed fields, deduplicated
98
+ engine.search("name", "john"); // searches a specific field
99
+ ```
100
+
101
+ ### Filter only
102
+
103
+ ```ts
104
+ import { FilterEngine } from "@devisfuture/mega-collection/filter";
105
+
106
+ const engine = new FilterEngine<User>({
107
+ data: users,
108
+ fields: ["city", "age"],
109
+ filterByPreviousResult: true,
110
+ });
111
+
112
+ engine.filter([
113
+ { field: "city", values: ["Kyiv", "Lviv"] },
114
+ { field: "age", values: [25, 30, 35] },
115
+ ]);
116
+
117
+ // Sequential mode example:
118
+ // 1) First call filters by city
119
+ const byCity = engine.filter([{ field: "city", values: ["Dnipro"] }]);
120
+ // 2) Second call filters only inside previous result
121
+ const byCityAndAge = engine.filter([{ field: "age", values: [22] }]);
122
+ ```
123
+
124
+ ### Sort only
125
+
126
+ ```ts
127
+ import { SortEngine } from "@devisfuture/mega-collection/sort";
128
+
129
+ const engine = new SortEngine<User>({
130
+ data: users,
131
+ fields: ["age", "name", "city"],
132
+ });
133
+
134
+ // Single-field sort — O(n) via cached index
135
+ engine.sort([{ field: "age", direction: "asc" }]);
136
+
137
+ // Multi-field sort — O(n log n)
138
+ engine.sort([
139
+ { field: "age", direction: "asc" },
140
+ { field: "name", direction: "desc" },
141
+ ]);
142
+ ```
143
+
144
+ ---
145
+
146
+ ## API Reference
147
+
148
+ ### `MergeEngines<T>` (root module)
149
+
150
+ Unified facade that composes all three engines around a shared dataset.
151
+
152
+ **Constructor options:**
153
+
154
+ | Option | Type | Description |
155
+ | --------- | ----------------------------------------------------------- | -------------------------------------------- |
156
+ | `imports` | `(typeof TextSearchEngine \| SortEngine \| FilterEngine)[]` | Engine classes to activate |
157
+ | `data` | `T[]` | Shared dataset — passed once at construction |
158
+ | `search` | `{ fields, minQueryLength? }` | Config for TextSearchEngine |
159
+ | `filter` | `{ fields, filterByPreviousResult? }` | Config for FilterEngine |
160
+ | `sort` | `{ fields }` | Config for SortEngine |
161
+
162
+ **Methods:**
163
+
164
+ | Method | Description |
165
+ | ----------------------------------- | ------------------------------- |
166
+ | `search(query)` | Search all indexed fields |
167
+ | `search(field, query)` | Search a specific field |
168
+ | `sort(descriptors)` | Sort using stored dataset |
169
+ | `sort(data, descriptors, inPlace?)` | Sort with an explicit dataset |
170
+ | `filter(criteria)` | Filter using stored dataset |
171
+ | `filter(data, criteria)` | Filter with an explicit dataset |
172
+
173
+ ---
174
+
175
+ ### `TextSearchEngine<T>` (search module)
176
+
177
+ Trigram-based text search engine.
178
+
179
+ | Method | Description |
180
+ | ---------------------- | --------------------------------------- |
181
+ | `search(query)` | Search all indexed fields, deduplicated |
182
+ | `search(field, query)` | Search a specific indexed field |
183
+ | `clear()` | Free memory |
184
+
185
+ ### `FilterEngine<T>` (filter module)
186
+
187
+ Multi-criteria AND filter with index-accelerated fast path.
188
+
189
+ Constructor option highlights:
190
+
191
+ | Option | Type | Description |
192
+ | ------------------------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
193
+ | `filterByPreviousResult` | `boolean` | When `true`, each `filter(criteria)` call filters from previous result. Defaults to `false` (each call starts from the original dataset). |
194
+
195
+ | Method | Description |
196
+ | ------------------------ | ---------------------------------------------------- |
197
+ | `filter(criteria)` | Filter using stored dataset |
198
+ | `filter(data, criteria)` | Filter with an explicit dataset |
199
+ | `resetFilterState()` | Reset previous-result state for sequential filtering |
200
+ | `clearIndexes()` | Free all index memory |
201
+
202
+ ### `SortEngine<T>` (sort module)
203
+
204
+ Sorting with pre-compiled comparators and cached sort indexes.
205
+
206
+ | Method | Description |
207
+ | ----------------------------------- | ----------------------------- |
208
+ | `sort(descriptors)` | Sort using stored dataset |
209
+ | `sort(data, descriptors, inPlace?)` | Sort with an explicit dataset |
210
+ | `clearIndexes()` | Free all cached indexes |
211
+
212
+ ---
213
+
214
+ ## Types
215
+
216
+ All types are exported from the root package and from each sub-module:
217
+
218
+ ```ts
219
+ import type {
220
+ CollectionItem,
221
+ IndexableKey,
222
+ FilterCriterion,
223
+ SortDescriptor,
224
+ SortDirection,
225
+ MergeEnginesOptions,
226
+ } from "@devisfuture/mega-collection";
227
+ ```
228
+
229
+ Or from individual sub-modules:
230
+
231
+ ```ts
232
+ import type {
233
+ CollectionItem,
234
+ IndexableKey,
235
+ } from "@devisfuture/mega-collection/search";
236
+ import type { FilterCriterion } from "@devisfuture/mega-collection/filter";
237
+ import type {
238
+ SortDescriptor,
239
+ SortDirection,
240
+ } from "@devisfuture/mega-collection/sort";
241
+ ```
242
+
243
+ ---
244
+
245
+ ## Architecture
246
+
247
+ ```
248
+ src/
249
+ types.ts — Shared type definitions
250
+ indexer.ts — Hash-Map index engine (internal, O(1) lookups)
251
+ search/
252
+ text-search.ts — Trigram inverted index engine
253
+ index.ts — Search module entry point
254
+ filter/
255
+ filter.ts — Multi-criteria filter engine (owns Indexer internally)
256
+ index.ts — Filter module entry point
257
+ sort/
258
+ sorter.ts — Sort engine (TimSort + index-sort)
259
+ index.ts — Sort module entry point
260
+ merge/
261
+ merge-engines.ts — MergeEngines unified facade
262
+ index.ts — Merge module entry point
263
+ index.ts — Root barrel export
264
+ ```
265
+
266
+ ## Build
267
+
268
+ ```bash
269
+ npm install
270
+ npm run build # Build ESM + declarations
271
+ npm run typecheck # Type-check without emitting
272
+ npm run dev # Watch mode
273
+ ```
274
+
275
+ ## Contributing
276
+
277
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
278
+
279
+ ## Security
280
+
281
+ See [SECURITY.md](SECURITY.md) for our security policy.
282
+
283
+ ## License
284
+
285
+ MIT — see [LICENSE](LICENSE) for details.
@@ -0,0 +1 @@
1
+ var T=3,f=12;function I(d){let t=d.toLowerCase(),n=Math.min(T,t.length),e=t.length-n+1,s=new Array(e);for(let r=0;r<e;r++)s[r]=t.substring(r,r+n);return s}function m(d){let t=I(d);if(t.length<=f)return new Set(t);let n=new Set,e=t.length-1,s=f-1;for(let r=0;r<=s;r++){let a=Math.round(r*e/s);n.add(t[a]);}return n}function L(d,t){let n=d.get(t);if(n)return n;let e=new Set;return d.set(t,e),e}var y=class{constructor(t={}){this.ngramIndexes=new Map;this.data=[];if(this.minQueryLength=t.minQueryLength??1,!!t.data&&(this.data=t.data,!!t.fields?.length))for(let n of t.fields)this.buildIndex(t.data,n);}buildIndex(t,n){let e,s;if(Array.isArray(t))e=t,s=n;else {if(!this.data.length)throw new Error("TextSearchEngine: no dataset in memory. Either pass `data` in the constructor options, or call buildIndex(data, field).");e=this.data,s=t;}this.data=e;let r=new Map;for(let a=0,l=e.length;a<l;a++){let g=e[a][s];if(typeof g!="string")continue;let o=g.toLowerCase();for(let i=0,u=o.length;i<u;i++){let c=u-i,p=Math.min(T,c);for(let h=1;h<=p;h++){let x=o.substring(i,i+h);L(r,x).add(a);}}}return this.ngramIndexes.set(s,r),this}search(t,n){return n===void 0?this.searchAllFields(t):this.searchField(t,n)}searchAllFields(t){let n=[...this.ngramIndexes.keys()];if(!n.length)return [];let e=t.trim().toLowerCase();if(!e)return [];if(e.length<this.minQueryLength)return [];let s=m(e);if(!s.size)return [];let r=new Set,a=[];for(let l of n)for(let g of this.searchFieldWithPreparedQuery(l,e,s))r.has(g)||(r.add(g),a.push(g));return a}searchField(t,n){let e=n.trim().toLowerCase();if(!e)return [];if(e.length<this.minQueryLength)return [];let s=m(e);return s.size?this.searchFieldWithPreparedQuery(t,e,s):[]}searchFieldWithPreparedQuery(t,n,e){let s=this.ngramIndexes.get(t);if(!s)return [];let r=[];for(let o of e){let i=s.get(o);if(!i)return [];r.push(i);}r.sort((o,i)=>o.size-i.size);let a=r[0],l=r.length,g=[];for(let o of a){let i=true;for(let c=1;c<l;c++)if(!r[c].has(o)){i=false;break}if(!i)continue;let u=this.data[o][t];typeof u=="string"&&u.toLowerCase().includes(n)&&g.push(this.data[o]);}return g}clear(){this.ngramIndexes.clear(),this.data=[];}};export{y as a};
@@ -0,0 +1 @@
1
+ var u=class{constructor(n={}){this.cache=new Map;this.data=[];if(n.data&&(this.data=n.data,!!n.fields?.length))for(let a of n.fields)this.buildIndex(n.data,a);}buildIndex(n,a){let l,t;if(Array.isArray(n))l=n,t=a;else {if(!this.data.length)throw new Error("SortEngine: no dataset in memory. Either pass `data` in the constructor options, or call buildIndex(data, field).");l=this.data,t=n;}this.data=l;let e=l.length,i=new Uint32Array(e);for(let o=0;o<e;o++)i[o]=o;let s=l.map(o=>o[t]);if(typeof s[0]=="number"){let o=new Float64Array(e);for(let c=0;c<e;c++)o[c]=s[c];i.sort((c,d)=>o[c]-o[d]);}else i.sort((o,c)=>{let d=s[o],f=s[c];return d<f?-1:d>f?1:0});return this.cache.set(t,{indexes:i,dataRef:l,itemCount:e,fieldSnapshot:s}),this}clearIndexes(){this.cache.clear();}sort(n,a,l=false){let t,e;if(a===void 0){if(!this.data.length)throw new Error("SortEngine: no dataset in memory. Either pass `data` in the constructor options, or call sort(data, descriptors).");t=this.data,e=n;}else t=n,e=a;if(e.length===0||t.length===0)return t;if(e.length===1){let{field:r,direction:o}=e[0],c=this.cache.get(r);if(c&&c.dataRef===t&&c.itemCount===t.length&&this.isFieldSnapshotValid(t,r,c.fieldSnapshot))return this.reconstructFromIndex(t,c.indexes,o)}let i=l?t:t.slice();if(e.length===1&&t.length>0&&typeof t[0][e[0].field]=="number")return this.radixSortNumeric(i,e[0].field,e[0].direction);let s=this.buildComparator(e);return i.sort(s),i}reconstructFromIndex(n,a,l){let t=n.length,e=new Array(t);if(l==="asc")for(let i=0;i<t;i++)e[i]=n[a[i]];else for(let i=0;i<t;i++)e[i]=n[a[t-1-i]];return e}isFieldSnapshotValid(n,a,l){for(let t=0;t<n.length;t++)if(n[t][a]!==l[t])return false;return true}buildComparator(n){let a=n.map(({field:e})=>e),l=n.map(({direction:e})=>e==="asc"?1:-1),t=a.length;return (e,i)=>{for(let s=0;s<t;s++){let r=e[a[s]],o=i[a[s]];if(r<o)return -l[s];if(r>o)return l[s]}return 0}}radixSortNumeric(n,a,l){let t=n.length,e=new Float64Array(t);for(let r=0;r<t;r++)e[r]=n[r][a];let i=new Uint32Array(t);for(let r=0;r<t;r++)i[r]=r;i.sort((r,o)=>e[r]-e[o]);let s=new Array(t);if(l==="asc")for(let r=0;r<t;r++)s[r]=n[i[r]];else for(let r=0;r<t;r++)s[t-1-r]=n[i[r]];return s}};export{u as a};
@@ -0,0 +1 @@
1
+ import {a as a$1}from'./chunk-4TBU4VWK.mjs';import {a as a$3}from'./chunk-XFQ56UZU.mjs';import {a as a$2}from'./chunk-DBAABXBP.mjs';var a=class{constructor(e){let{imports:r,data:t,search:l,filter:g,sort:f}=e,i=new Set(r),T=i.has(a$1),c=i.has(a$2),h=i.has(a$3);this.searchEngine=T?new a$1({data:t,fields:l?.fields,minQueryLength:l?.minQueryLength}):null,this.sortEngine=c?new a$2({data:t,fields:f?.fields}):null,this.filterEngine=h?new a$3({data:t,fields:g?.fields,filterByPreviousResult:g?.filterByPreviousResult}):null;}search(e,r){if(!this.searchEngine)throw new Error("MergeEngines: TextSearchEngine is not available. Add TextSearchEngine to the `imports` array.");return r===void 0?this.searchEngine.search(e):this.searchEngine.search(e,r)}sort(e,r,t){if(!this.sortEngine)throw new Error("MergeEngines: SortEngine is not available. Add SortEngine to the `imports` array.");return r===void 0?this.sortEngine.sort(e):this.sortEngine.sort(e,r,t)}filter(e,r){if(!this.filterEngine)throw new Error("MergeEngines: FilterEngine is not available. Add FilterEngine to the `imports` array.");return r===void 0?this.filterEngine.filter(e):this.filterEngine.filter(e,r)}};export{a};
@@ -0,0 +1 @@
1
+ var c=class{constructor(){this.indexes=new Map;}buildIndex(t,s){let n=new Map;for(let r=0,e=t.length;r<e;r++){let i=t[r],l=i[s];if(l==null)continue;let a=n.get(l);a?a.push(i):n.set(l,[i]);}this.indexes.set(s,n);}getByValue(t,s){let n=this.indexes.get(t);return n?n.get(s)??[]:[]}getByValues(t,s){let n=this.indexes.get(t);if(!n)return [];if(s.length===1)return n.get(s[0])??[];let r=new Set,e=[];for(let i=0;i<s.length;i++){let l=n.get(s[i]);if(l!==void 0)for(let a=0;a<l.length;a++){let o=l[a];r.has(o)||(r.add(o),e.push(o));}}return e}hasIndex(t){return this.indexes.has(t)}clear(){this.indexes.clear();}getIndexMap(t){return this.indexes.get(t)}};var C=class{constructor(t={}){this.data=[];this.previousResult=null;this.previousCriteria=null;this.previousBaseData=null;if(this.indexer=new c,this.filterByPreviousResult=t.filterByPreviousResult??false,!!t.data&&(this.data=t.data,!!t.fields?.length))for(let s of t.fields)this.buildIndex(t.data,s);}buildIndex(t,s){if(!Array.isArray(t)){if(!this.data.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.data,t),this}return this.data=t,this.previousResult=null,this.previousCriteria=null,this.previousBaseData=null,this.indexer.buildIndex(t,s),this}clearIndexes(){this.indexer.clear();}resetFilterState(){this.previousResult=null,this.previousCriteria=null,this.previousBaseData=null;}filter(t,s){let n=s===void 0,r,e,i;if(n){if(!this.data.length)throw new Error("FilterEngine: no dataset in memory. Either pass `data` in the constructor options, or call filter(data, criteria).");if(e=t,this.filterByPreviousResult&&this.previousResult!==null&&this.previousCriteria!==null&&this.previousBaseData===this.data){let u=this.hasCriteriaAdditions(this.previousCriteria,e),h=this.hasCriteriaRemovals(this.previousCriteria,e);if(!u&&!h)return this.previousResult;u&&!h?(r=this.previousResult,i=this.getAddedCriteria(this.previousCriteria,e)):(r=this.data,i=e);}else r=this.data,i=e;}else if(e=s,r=t,this.filterByPreviousResult&&this.previousResult!==null&&this.previousCriteria!==null&&this.previousBaseData===r){let u=this.hasCriteriaAdditions(this.previousCriteria,e),h=this.hasCriteriaRemovals(this.previousCriteria,e);if(!u&&!h)return this.previousResult;u&&!h?(r=this.previousResult,i=this.getAddedCriteria(this.previousCriteria,e)):i=e;}else i=e;if(e.length===0)return this.filterByPreviousResult&&(this.previousResult=null,this.previousCriteria=null,this.previousBaseData=null),n?this.data:r;n&&!i&&(i=e);let{indexedCriteria:l,linearCriteria:a}=i.reduce((u,h)=>(this.indexer.hasIndex(h.field)?u.indexedCriteria.push(h):u.linearCriteria.push(h),u),{indexedCriteria:[],linearCriteria:[]}),o;if(l.length>0&&a.length===0)return o=this.filterViaIndex(l,r),this.filterByPreviousResult&&(this.previousResult=o,this.previousCriteria=this.cloneCriteria(e),this.previousBaseData=n?this.data:t),o;if(l.length>0&&a.length>0){let u=this.filterViaIndex(l,r);return o=this.linearFilter(u,a),this.filterByPreviousResult&&(this.previousResult=o,this.previousCriteria=this.cloneCriteria(e),this.previousBaseData=n?this.data:t),o}return o=this.linearFilter(r,i),this.filterByPreviousResult&&(this.previousResult=o,this.previousCriteria=this.cloneCriteria(e),this.previousBaseData=n?this.data:t),o}cloneCriteria(t){return t.map(({field:s,values:n})=>({field:s,values:[...n]}))}hasCriteriaAdditions(t,s){let n=new Map(t.map(({field:e,values:i})=>[e,new Set(i)])),r=new Map(s.map(({field:e,values:i})=>[e,new Set(i)]));for(let[e,i]of r){let l=n.get(e);if(!l)return true;for(let a of i)if(!l.has(a))return true}return false}hasCriteriaRemovals(t,s){let n=new Map(t.map(({field:e,values:i})=>[e,new Set(i)])),r=new Map(s.map(({field:e,values:i})=>[e,new Set(i)]));for(let[e,i]of n){let l=r.get(e);if(!l)return true;for(let a of i)if(!l.has(a))return true}return false}getAddedCriteria(t,s){let n=new Map(t.map(({field:e,values:i})=>[e,new Set(i)])),r=[];for(let{field:e,values:i}of s){let l=n.get(e);if(!l){r.push({field:e,values:[...i]});continue}let a=i.filter(o=>!l.has(o));a.length>0&&r.push({field:e,values:a});}return r}linearFilter(t,s){let n=new Map(s.map(({field:i,values:l})=>[i,new Set(l)])),r=s.map(({field:i})=>i),e=[];for(let i=0;i<t.length;i++){let l=t[i],a=true;for(let o=0;o<r.length;o++){let u=r[o];if(!n.get(u).has(l[u])){a=false;break}}a&&e.push(l);}return e}filterViaIndex(t,s){let r=s!==this.data?new Set(s):null;if(t.length===1){let d=this.indexer.getByValues(t[0].field,t[0].values);return r?d.filter(f=>r.has(f)):d}let e=t.map(d=>({criterion:d,size:this.estimateIndexSize(d)})).sort((d,f)=>d.size-f.size),{field:i,values:l}=e[0].criterion,a=this.indexer.getByValues(i,l);if(a.length===0)return [];let o=new Map(e.slice(1).map(({criterion:{field:d,values:f}})=>[d,new Set(f)])),u=Array.from(o.keys()),h=[];for(let d=0;d<a.length;d++){let f=a[d],p=true;for(let v=0;v<u.length;v++){let g=u[v];if(!o.get(g).has(f[g])){p=false;break}}p&&r&&!r.has(f)&&(p=false),p&&h.push(f);}return h}estimateIndexSize(t){let s=this.indexer.getIndexMap(t.field);return s?t.values.reduce((n,r)=>{let e=s.get(r);return e?n+e.length:n},0):1/0}};export{C as a};
@@ -0,0 +1,118 @@
1
+ import { C as CollectionItem, F as FilterCriterion } from '../types-BlZO_NQA.mjs';
2
+ export { I as IndexableKey } from '../types-BlZO_NQA.mjs';
3
+
4
+ /**
5
+ * FilterEngine — multi-criteria filtering optimised for 10 M+ rows.
6
+ *
7
+ * Strategy:
8
+ * 1. If hash-map indexes exist (built via buildIndex), use O(1) lookups per
9
+ * value and intersect results across fields. This is the *fast path*.
10
+ *
11
+ * 2. If no index is available for a field, fall back to a single-pass linear
12
+ * scan using a `Set` for each criterion (O(n) but with O(1) membership
13
+ * test per item).
14
+ *
15
+ * Multiple criteria are combined with AND logic: an item must satisfy
16
+ * ALL criteria to be included.
17
+ */
18
+
19
+ interface FilterEngineOptions<T extends CollectionItem = CollectionItem> {
20
+ /**
21
+ * The dataset to index. When provided together with `fields`, all indexes
22
+ * are built automatically inside the constructor — no manual `buildIndex`
23
+ * calls needed.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * const engine = new FilterEngine<User>({ data: users, fields: ["city", "age"] });
28
+ * engine.filter(users, [{ field: "city", values: ["Kyiv"] }]);
29
+ * ```
30
+ */
31
+ data?: T[];
32
+ /**
33
+ * Fields to build a hash-map index for. Requires `data` to be set as well.
34
+ * When both are present, `buildIndex` is called for each field in the constructor.
35
+ */
36
+ fields?: (keyof T & string)[];
37
+ /**
38
+ * Enables sequential filtering by the previous filter result.
39
+ *
40
+ * When `true`, each call to `filter(criteria)` (without explicit `data`)
41
+ * uses the previous filter output as the next input dataset.
42
+ *
43
+ * @default false
44
+ */
45
+ filterByPreviousResult?: boolean;
46
+ }
47
+ declare class FilterEngine<T extends CollectionItem> {
48
+ private indexer;
49
+ private readonly filterByPreviousResult;
50
+ /** Reference to the full dataset (set via the constructor or `buildIndex`). */
51
+ private data;
52
+ /** Last filter output used as the next input in sequential mode. */
53
+ private previousResult;
54
+ /** Last criteria used in sequential mode. */
55
+ private previousCriteria;
56
+ /** Dataset reference used to compute previous sequential result. */
57
+ private previousBaseData;
58
+ constructor(options?: FilterEngineOptions<T>);
59
+ /**
60
+ * Build a hash-map index for a field to enable O(1) fast-path filtering.
61
+ *
62
+ * Two call signatures are supported:
63
+ * - `buildIndex(data, field)` — explicit dataset (original API)
64
+ * - `buildIndex(field)` — reuses the dataset supplied in the constructor
65
+ *
66
+ * @returns `this` for chaining.
67
+ */
68
+ private buildIndex;
69
+ /**
70
+ * Free all index memory.
71
+ */
72
+ clearIndexes(): void;
73
+ /**
74
+ * Reset sequential filtering state.
75
+ *
76
+ * Useful when `filterByPreviousResult` is enabled and you want
77
+ * the next `filter(criteria)` call to start from the full dataset.
78
+ */
79
+ resetFilterState(): void;
80
+ /**
81
+ * Apply multiple filter criteria (AND logic).
82
+ *
83
+ * Two call signatures:
84
+ * - `filter(criteria)` — uses the dataset supplied in the constructor.
85
+ * - `filter(data, criteria)` — explicit dataset (original API).
86
+ *
87
+ * @returns Filtered array.
88
+ */
89
+ filter(criteria: FilterCriterion<T>[]): T[];
90
+ filter(data: T[], criteria: FilterCriterion<T>[]): T[];
91
+ private cloneCriteria;
92
+ private hasCriteriaAdditions;
93
+ private hasCriteriaRemovals;
94
+ private getAddedCriteria;
95
+ /**
96
+ * Single-pass linear filter using pre-indexed criteria value Sets.
97
+ * O(n × k) where n = data.length, k = criteria count.
98
+ * Each criterion check is O(1) via Map + Set lookup — no nested data iteration.
99
+ */
100
+ private linearFilter;
101
+ /**
102
+ * Pure index-based filtering with selectivity-driven pruning.
103
+ *
104
+ * Strategy:
105
+ * 1. Sort criteria by estimated result-set size (smallest first).
106
+ * 2. Materialise only the most selective criterion via the index.
107
+ * 3. Pre-index remaining criteria values into Sets for O(1) checks.
108
+ * 4. Single-pass filter over candidates — no nested collection iteration.
109
+ */
110
+ private filterViaIndex;
111
+ /**
112
+ * Estimate the number of items an indexed criterion would return.
113
+ * Used to sort criteria by selectivity for faster intersection.
114
+ */
115
+ private estimateIndexSize;
116
+ }
117
+
118
+ export { CollectionItem, FilterCriterion, FilterEngine, type FilterEngineOptions };
@@ -0,0 +1 @@
1
+ export{a as FilterEngine}from'../chunk-XFQ56UZU.mjs';
@@ -0,0 +1,111 @@
1
+ import { TextSearchEngine, TextSearchEngineOptions } from './search/index.mjs';
2
+ import { FilterEngine } from './filter/index.mjs';
3
+ import { SortEngine } from './sort/index.mjs';
4
+ import { C as CollectionItem, S as SortDescriptor, F as FilterCriterion } from './types-BlZO_NQA.mjs';
5
+ export { I as IndexableKey, a as SortDirection } from './types-BlZO_NQA.mjs';
6
+
7
+ /**
8
+ * MergeEngines — unified facade that composes TextSearchEngine, SortEngine
9
+ * and FilterEngine into a single entry point.
10
+ *
11
+ * Design:
12
+ * - The `imports` array declares which sub-engines to activate.
13
+ * - Each sub-engine is initialised lazily only when its constructor appears
14
+ * in `imports` AND the corresponding config section is provided.
15
+ * - Delegates `search`, `sort` and `filter` calls directly to the underlying
16
+ * engines — zero logic duplication.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { MergeEngines } from "@devisfuture/mega-collection";
21
+ * import { TextSearchEngine } from "@devisfuture/mega-collection/search";
22
+ * import { SortEngine } from "@devisfuture/mega-collection/sort";
23
+ * import { FilterEngine } from "@devisfuture/mega-collection/filter";
24
+ *
25
+ * const engine = new MergeEngines<User>({
26
+ * imports: [TextSearchEngine, SortEngine, FilterEngine],
27
+ * data: users,
28
+ * search: { fields: ["city", "name"], minQueryLength: 2 },
29
+ * filter: { fields: ["city", "age"] },
30
+ * sort: { fields: ["age", "name", "city"] },
31
+ * });
32
+ *
33
+ * engine.search("john");
34
+ * engine.sort([{ field: "age", direction: "asc" }]);
35
+ * engine.filter([{ field: "city", values: ["Kyiv"] }]);
36
+ * ```
37
+ */
38
+
39
+ /** Search-specific configuration (excludes `data` — shared at the top level). */
40
+ interface MergeSearchConfig<T extends CollectionItem> {
41
+ fields: (keyof T & string)[];
42
+ minQueryLength?: TextSearchEngineOptions<T>["minQueryLength"];
43
+ }
44
+ /** Filter-specific configuration (excludes `data` — shared at the top level). */
45
+ interface MergeFilterConfig<T extends CollectionItem> {
46
+ fields: (keyof T & string)[];
47
+ filterByPreviousResult?: boolean;
48
+ }
49
+ /** Sort-specific configuration (excludes `data` — shared at the top level). */
50
+ interface MergeSortConfig<T extends CollectionItem> {
51
+ fields: (keyof T & string)[];
52
+ }
53
+ /** Union of the three engine constructors accepted in `imports`. */
54
+ type EngineConstructor = typeof TextSearchEngine | typeof SortEngine | typeof FilterEngine;
55
+ interface MergeEnginesOptions<T extends CollectionItem> {
56
+ /**
57
+ * List of engine classes to activate.
58
+ * Only engines present in this array will be initialised.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * imports: [TextSearchEngine, SortEngine, FilterEngine]
63
+ * ```
64
+ */
65
+ imports: EngineConstructor[];
66
+ /** Shared dataset — passed to every activated engine. */
67
+ data: T[];
68
+ /** Config for TextSearchEngine (requires `TextSearchEngine` in `imports`). */
69
+ search?: MergeSearchConfig<T>;
70
+ /** Config for FilterEngine (requires `FilterEngine` in `imports`). */
71
+ filter?: MergeFilterConfig<T>;
72
+ /** Config for SortEngine (requires `SortEngine` in `imports`). */
73
+ sort?: MergeSortConfig<T>;
74
+ }
75
+ declare class MergeEngines<T extends CollectionItem> {
76
+ private readonly searchEngine;
77
+ private readonly sortEngine;
78
+ private readonly filterEngine;
79
+ constructor(options: MergeEnginesOptions<T>);
80
+ /**
81
+ * Search items by substring across all indexed fields.
82
+ *
83
+ * Delegates to `TextSearchEngine.search`.
84
+ *
85
+ * @throws {Error} If `TextSearchEngine` was not included in `imports`.
86
+ */
87
+ search(query: string): T[];
88
+ search(field: keyof T & string, query: string): T[];
89
+ /**
90
+ * Sort items by one or more fields.
91
+ *
92
+ * Delegates to `SortEngine.sort`.
93
+ * Uses the shared dataset from the constructor when called without `data`.
94
+ *
95
+ * @throws {Error} If `SortEngine` was not included in `imports`.
96
+ */
97
+ sort(descriptors: SortDescriptor<T>[]): T[];
98
+ sort(data: T[], descriptors: SortDescriptor<T>[], inPlace?: boolean): T[];
99
+ /**
100
+ * Filter items by multiple criteria (AND logic).
101
+ *
102
+ * Delegates to `FilterEngine.filter`.
103
+ * Uses the shared dataset from the constructor when called without `data`.
104
+ *
105
+ * @throws {Error} If `FilterEngine` was not included in `imports`.
106
+ */
107
+ filter(criteria: FilterCriterion<T>[]): T[];
108
+ filter(data: T[], criteria: FilterCriterion<T>[]): T[];
109
+ }
110
+
111
+ export { CollectionItem, type EngineConstructor, FilterCriterion, FilterEngine, MergeEngines, type MergeEnginesOptions, type MergeFilterConfig, type MergeSearchConfig, type MergeSortConfig, SortDescriptor, SortEngine, TextSearchEngine };
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ export{a as MergeEngines}from'./chunk-OE5GVXOE.mjs';export{a as TextSearchEngine}from'./chunk-4TBU4VWK.mjs';export{a as FilterEngine}from'./chunk-XFQ56UZU.mjs';export{a as SortEngine}from'./chunk-DBAABXBP.mjs';
@@ -0,0 +1,5 @@
1
+ export { EngineConstructor, MergeEngines, MergeEnginesOptions, MergeFilterConfig, MergeSearchConfig, MergeSortConfig } from '../index.mjs';
2
+ export { C as CollectionItem, F as FilterCriterion, I as IndexableKey, S as SortDescriptor, a as SortDirection } from '../types-BlZO_NQA.mjs';
3
+ import '../search/index.mjs';
4
+ import '../filter/index.mjs';
5
+ import '../sort/index.mjs';
@@ -0,0 +1 @@
1
+ export{a as MergeEngines}from'../chunk-OE5GVXOE.mjs';import'../chunk-4TBU4VWK.mjs';import'../chunk-XFQ56UZU.mjs';import'../chunk-DBAABXBP.mjs';
@@ -0,0 +1,98 @@
1
+ import { C as CollectionItem } from '../types-BlZO_NQA.mjs';
2
+ export { I as IndexableKey } from '../types-BlZO_NQA.mjs';
3
+
4
+ /**
5
+ * TextSearchEngine — fast substring search on 10 M+ string fields.
6
+ *
7
+ * Strategy:
8
+ * 1. **N-gram index (1..3 chars)** — every string is split into overlapping
9
+ * 1/2/3-character grams. Each gram maps to a Set of item indexes.
10
+ * At query time we intersect posting lists to get *candidates*, then verify
11
+ * with `String.includes` (which is very fast on a small candidate set).
12
+ *
13
+ * Building the trigram index is O(n·L) where L is average string length.
14
+ * Query time is roughly O(|candidates| + |postingList intersections|).
15
+ */
16
+
17
+ interface TextSearchEngineOptions<T extends CollectionItem = CollectionItem> {
18
+ /**
19
+ * The dataset to index. When provided together with `fields`, all indexes
20
+ * are built automatically inside the constructor — no manual `buildIndex`
21
+ * calls needed.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const engine = new TextSearchEngine<User>({ data: users, fields: ["name", "city"] });
26
+ * engine.search("john"); // searches both fields, deduplicated
27
+ * ```
28
+ */
29
+ data?: T[];
30
+ /**
31
+ * Fields to build a trigram index for. Requires `data` to be set as well.
32
+ * When both are present, `buildIndex` is called for each field in the constructor.
33
+ */
34
+ fields?: (keyof T & string)[];
35
+ /**
36
+ * Minimum number of characters required before a search is executed.
37
+ * Queries shorter than this return an empty array immediately.
38
+ *
39
+ * Why this matters: a single-character query uses 1-grams whose posting
40
+ * lists can span the majority of the dataset (e.g. "a" matches 70–80 % of
41
+ * names/cities), forcing `String.includes` verification over tens of
42
+ * thousands of candidates. Setting this to 2 or 3 dramatically reduces
43
+ * the candidate set and keeps searches fast even with 100 k+ items.
44
+ *
45
+ * @default 1 (backwards-compatible; set to 2 or 3 for better perf)
46
+ */
47
+ minQueryLength?: number;
48
+ }
49
+ declare class TextSearchEngine<T extends CollectionItem> {
50
+ /**
51
+ * field → ngram → Set<index in data[]>
52
+ * We store *indexes into the data array* (not the objects) to save memory.
53
+ */
54
+ private ngramIndexes;
55
+ /** Reference to the full dataset (set once via `buildIndex` or the constructor). */
56
+ private data;
57
+ /** Minimum query length before the engine executes a search. */
58
+ private readonly minQueryLength;
59
+ constructor(options?: TextSearchEngineOptions<T>);
60
+ /**
61
+ * Build n-gram index (1..3 chars) for one field. O(n·L).
62
+ *
63
+ * Two call signatures are supported:
64
+ * - `buildIndex(data, field)` — explicit dataset (original API)
65
+ * - `buildIndex(field)` — reuses the dataset supplied in the constructor
66
+ *
67
+ * Trigram extraction is inlined to avoid allocating a temporary array
68
+ * and a deduplication Set per item. Posting lists are `Set<number>`,
69
+ * so duplicate `.add()` calls for the same itemIndex are no-ops — we
70
+ * get correctness without per-item deduplication overhead.
71
+ *
72
+ * For 10 M+ items this eliminates ~20 M transient object allocations
73
+ * and dramatically reduces GC pressure.
74
+ */
75
+ private buildIndex;
76
+ /**
77
+ * Search items by substring (contains) using the trigram index.
78
+ *
79
+ * Two call signatures are supported:
80
+ * - `search(query)` — searches **all** indexed fields and returns a
81
+ * deduplicated union (preserves field order).
82
+ * - `search(field, query)` — searches a single specific field.
83
+ *
84
+ * Both paths return only items whose field value actually contains the query
85
+ * (trigram candidates are verified with `String.includes`).
86
+ */
87
+ search(query: string): T[];
88
+ search(field: keyof T & string, query: string): T[];
89
+ /** Search across every indexed field and return a deduplicated union. */
90
+ private searchAllFields;
91
+ /** Core search implementation for a single field. */
92
+ private searchField;
93
+ private searchFieldWithPreparedQuery;
94
+ /** Free memory. */
95
+ clear(): void;
96
+ }
97
+
98
+ export { CollectionItem, TextSearchEngine, type TextSearchEngineOptions };
@@ -0,0 +1 @@
1
+ export{a as TextSearchEngine}from'../chunk-4TBU4VWK.mjs';
@@ -0,0 +1,106 @@
1
+ import { C as CollectionItem, S as SortDescriptor } from '../types-BlZO_NQA.mjs';
2
+ export { a as SortDirection } from '../types-BlZO_NQA.mjs';
3
+
4
+ /**
5
+ * SortEngine — high-performance sorting for 10 M+ rows.
6
+ *
7
+ * Strategy:
8
+ * 1. **Pre-sorted index cache** (fastest): `buildIndex` pre-computes a sorted
9
+ * `Uint32Array` of item positions once — O(n log n). Every subsequent
10
+ * `sort` call on that field is O(n) reconstruction (no comparison at all).
11
+ * Reversing direction is also O(n) — just walk the index backwards.
12
+ *
13
+ * 2. **In-place sort** using the native V8 TimSort (`Array.prototype.sort`)
14
+ * which is O(n log n) and very cache-friendly for large arrays.
15
+ *
16
+ * 3. **Pre-compiled comparator**: we build a single comparator function that
17
+ * handles multi-field sorting with correct direction, avoiding per-compare
18
+ * overhead of dynamic field resolution.
19
+ *
20
+ * 4. **Typed-array radix sort** (fallback for single numeric field):
21
+ * O(n) index-sort using `Float64Array` for extreme speed on numeric data.
22
+ *
23
+ * 5. Returns a **new array** by default to keep the original intact.
24
+ * Pass `inPlace: true` to mutate.
25
+ */
26
+
27
+ interface SortEngineOptions<T extends CollectionItem = CollectionItem> {
28
+ /**
29
+ * The dataset to index. When provided together with `fields`, all indexes
30
+ * are built automatically inside the constructor — no manual `buildIndex`
31
+ * calls needed.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const engine = new SortEngine<User>({ data: users, fields: ["age", "name", "city"] });
36
+ * engine.sort(users, [{ field: "age", direction: "asc" }]);
37
+ * ```
38
+ */
39
+ data?: T[];
40
+ /**
41
+ * Fields to pre-sort and cache. Requires `data` to be set as well.
42
+ * When both are present, `buildIndex` is called for each field in the constructor.
43
+ */
44
+ fields?: (keyof T & string)[];
45
+ }
46
+ declare class SortEngine<T extends CollectionItem> {
47
+ /** field → cached ascending sort index */
48
+ private cache;
49
+ /** Reference to the full dataset (set via the constructor or `buildIndex`). */
50
+ private data;
51
+ constructor(options?: SortEngineOptions<T>);
52
+ /**
53
+ * Pre-compute and cache a sorted index for a field.
54
+ *
55
+ * Two call signatures are supported:
56
+ * - `buildIndex(data, field)` — explicit dataset (original API)
57
+ * - `buildIndex(field)` — reuses the dataset supplied in the constructor
58
+ *
59
+ * Call this once per field; all subsequent `sort` calls on that field
60
+ * will use the cache — O(n) instead of O(n log n).
61
+ *
62
+ * @returns `this` for chaining.
63
+ */
64
+ private buildIndex;
65
+ /**
66
+ * Free all cached sort indexes.
67
+ */
68
+ clearIndexes(): void;
69
+ /**
70
+ * Sort items by one or more fields.
71
+ *
72
+ * Two call signatures:
73
+ * - `sort(descriptors)` — uses the dataset supplied in the constructor.
74
+ * - `sort(data, descriptors)` — explicit dataset (original API).
75
+ *
76
+ * If `buildIndex` was called for the leading sort field and the dataset
77
+ * reference matches, sorting is O(n) via cached indexes regardless of
78
+ * direction. Otherwise falls back to O(n log n) TimSort.
79
+ *
80
+ * @returns The sorted array.
81
+ */
82
+ sort(descriptors: SortDescriptor<T>[]): T[];
83
+ sort(data: T[], descriptors: SortDescriptor<T>[], inPlace?: boolean): T[];
84
+ /**
85
+ * Reconstruct a sorted array from a pre-built ascending index.
86
+ * O(n) — no comparisons, just array reads.
87
+ */
88
+ private reconstructFromIndex;
89
+ private isFieldSnapshotValid;
90
+ /**
91
+ * Build a single comparator function for multi-field sorting.
92
+ * Captures field names and direction multipliers in closure for speed.
93
+ */
94
+ private buildComparator;
95
+ /**
96
+ * Radix sort for a single numeric field.
97
+ * Uses index-array approach: sort an auxiliary index array by the numeric
98
+ * values, then reorder items by those indexes.
99
+ * O(n) time for integers; O(n) for floats via float-to-int encoding.
100
+ *
101
+ * Falls back to native sort if the range is too large (sparse data).
102
+ */
103
+ private radixSortNumeric;
104
+ }
105
+
106
+ export { CollectionItem, SortDescriptor, SortEngine, type SortEngineOptions };
@@ -0,0 +1 @@
1
+ export{a as SortEngine}from'../chunk-DBAABXBP.mjs';
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Core type definitions for MegaCollection.
3
+ *
4
+ * All generics use `T` for the item type and constrain it to `Record<string, any>`
5
+ * so that every item is an indexable plain object.
6
+ */
7
+ /** Any plain object whose values can be indexed. */
8
+ type CollectionItem = Record<string, any>;
9
+ /**
10
+ * Extract only the keys of T whose values are `string | number`.
11
+ * Used to restrict indexing / sorting to primitive-comparable fields.
12
+ */
13
+ type IndexableKey<T> = {
14
+ [K in keyof T]: T[K] extends string | number ? K : never;
15
+ }[keyof T];
16
+ /** A single filter criterion: field name → set of acceptable values. */
17
+ interface FilterCriterion<T extends CollectionItem> {
18
+ field: keyof T & string;
19
+ values: any[];
20
+ }
21
+ /** Sorting direction. */
22
+ type SortDirection = "asc" | "desc";
23
+ /** Sorting descriptor. */
24
+ interface SortDescriptor<T extends CollectionItem> {
25
+ field: keyof T & string;
26
+ direction: SortDirection;
27
+ }
28
+
29
+ export type { CollectionItem as C, FilterCriterion as F, IndexableKey as I, SortDescriptor as S, SortDirection as a };
package/package.json ADDED
@@ -0,0 +1,105 @@
1
+ {
2
+ "name": "@devisfuture/mega-collection",
3
+ "version": "1.0.2",
4
+ "description": "High-performance search, filter & sort engine for 10M+ item collections in JavaScript/TypeScript",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.mts",
8
+ "default": "./dist/index.mjs"
9
+ },
10
+ "./search": {
11
+ "types": "./dist/search/index.d.mts",
12
+ "default": "./dist/search/index.mjs"
13
+ },
14
+ "./filter": {
15
+ "types": "./dist/filter/index.d.mts",
16
+ "default": "./dist/filter/index.mjs"
17
+ },
18
+ "./sort": {
19
+ "types": "./dist/sort/index.d.mts",
20
+ "default": "./dist/sort/index.mjs"
21
+ },
22
+ "./merge": {
23
+ "types": "./dist/merge/index.d.mts",
24
+ "default": "./dist/merge/index.mjs"
25
+ }
26
+ },
27
+ "typesVersions": {
28
+ "*": {
29
+ "search": [
30
+ "./dist/search/index.d.mts"
31
+ ],
32
+ "filter": [
33
+ "./dist/filter/index.d.mts"
34
+ ],
35
+ "sort": [
36
+ "./dist/sort/index.d.mts"
37
+ ],
38
+ "merge": [
39
+ "./dist/merge/index.d.mts"
40
+ ]
41
+ }
42
+ },
43
+ "files": [
44
+ "dist"
45
+ ],
46
+ "sideEffects": false,
47
+ "scripts": {
48
+ "build": "tsup",
49
+ "build:watch": "tsup --watch",
50
+ "dev": "tsup --watch",
51
+ "test": "vitest run",
52
+ "test:watch": "vitest",
53
+ "test:coverage": "vitest run --coverage",
54
+ "test:ci": "vitest run --coverage --reporter=verbose --reporter=json",
55
+ "clean": "rm -rf dist",
56
+ "prepublishOnly": "npm run build",
57
+ "typecheck": "tsc --noEmit"
58
+ },
59
+ "keywords": [
60
+ "search",
61
+ "filter",
62
+ "sort",
63
+ "collection",
64
+ "performance",
65
+ "trigram",
66
+ "inverted-index",
67
+ "hashmap",
68
+ "large-dataset",
69
+ "big-data",
70
+ "typescript",
71
+ "zero-dependencies",
72
+ "tree-shakeable",
73
+ "esm",
74
+ "indexer",
75
+ "50k",
76
+ "100k",
77
+ "text-search",
78
+ "multi-filter",
79
+ "sort-engine",
80
+ "merge-engines",
81
+ "o1-lookup"
82
+ ],
83
+ "license": "MIT",
84
+ "publishConfig": {
85
+ "access": "public",
86
+ "registry": "https://registry.npmjs.org/"
87
+ },
88
+ "repository": {
89
+ "type": "git",
90
+ "url": "git+https://github.com/trae-op/mega-collection.git"
91
+ },
92
+ "bugs": {
93
+ "url": "https://github.com/trae-op/mega-collection/issues"
94
+ },
95
+ "homepage": "https://github.com/trae-op/mega-collection#readme",
96
+ "engines": {
97
+ "node": ">=16.0.0"
98
+ },
99
+ "devDependencies": {
100
+ "@vitest/coverage-v8": "^3.2.4",
101
+ "tsup": "^8.0.0",
102
+ "typescript": "^5.3.0",
103
+ "vitest": "^3.2.4"
104
+ }
105
+ }