@devisfuture/mega-collection 1.0.7 → 1.0.9

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
@@ -2,7 +2,7 @@
2
2
  <img src="./illustration.png" alt="electron-modular illustration" width="640" />
3
3
  </p>
4
4
 
5
- [![codecov](https://codecov.io/gh/trae-op/mega-collection/branch/main/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/)
5
+ [![npm version](https://img.shields.io/npm/v/@devisfuture/mega-collection.svg)](https://www.npmjs.com/package/@devisfuture/mega-collection) [![codecov](https://codecov.io/gh/trae-op/mega-collection/branch/main/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
6
 
7
7
  # @devisfuture/mega-collection
8
8
 
@@ -1,117 +1,35 @@
1
- import { C as CollectionItem, F as FilterCriterion } from '../types-BlZO_NQA.mjs';
2
- export { I as IndexableKey } from '../types-BlZO_NQA.mjs';
1
+ import { C as CollectionItem, F as FilterCriterion } from '../types-D24zQWME.mjs';
2
+ export { I as IndexableKey } from '../types-D24zQWME.mjs';
3
3
 
4
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.
5
+ * FilterEngine class for filtering collections by multiple criteria,
6
+ * supporting indexed lookups and linear scans for large datasets.
17
7
  */
18
8
 
19
9
  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
10
  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
11
  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
12
  filterByPreviousResult?: boolean;
46
13
  }
47
14
  declare class FilterEngine<T extends CollectionItem> {
48
15
  private indexer;
49
16
  private readonly filterByPreviousResult;
50
- /** Reference to the full dataset (set via the constructor or `buildIndex`). */
51
17
  private data;
52
- /** Last filter output used as the next input in sequential mode. */
53
18
  private previousResult;
54
- /** Last criteria used in sequential mode. */
55
19
  private previousCriteria;
56
- /** Dataset reference used to compute previous sequential result. */
57
20
  private previousBaseData;
58
21
  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
22
  private buildIndex;
69
- /**
70
- * Free all index memory.
71
- */
72
23
  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
24
  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
25
  filter(criteria: FilterCriterion<T>[]): T[];
90
26
  filter(data: T[], criteria: FilterCriterion<T>[]): T[];
91
27
  private cloneCriteria;
92
28
  private hasCriteriaAdditions;
93
29
  private hasCriteriaRemovals;
94
30
  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
31
  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
32
  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
33
  private estimateIndexSize;
116
34
  }
117
35
 
package/dist/index.d.mts CHANGED
@@ -1,75 +1,31 @@
1
1
  import { TextSearchEngine, TextSearchEngineOptions } from './search/index.mjs';
2
2
  import { FilterEngine } from './filter/index.mjs';
3
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';
4
+ import { C as CollectionItem, S as SortDescriptor, F as FilterCriterion } from './types-D24zQWME.mjs';
5
+ export { I as IndexableKey, a as SortDirection } from './types-D24zQWME.mjs';
6
6
 
7
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
- * ```
8
+ * MergeEngines class that provides a unified interface for text search,
9
+ * sorting, and filtering operations on collections.
37
10
  */
38
11
 
39
- /** Search-specific configuration (excludes `data` — shared at the top level). */
40
12
  interface MergeSearchConfig<T extends CollectionItem> {
41
13
  fields: (keyof T & string)[];
42
14
  minQueryLength?: TextSearchEngineOptions<T>["minQueryLength"];
43
15
  }
44
- /** Filter-specific configuration (excludes `data` — shared at the top level). */
45
16
  interface MergeFilterConfig<T extends CollectionItem> {
46
17
  fields: (keyof T & string)[];
47
18
  filterByPreviousResult?: boolean;
48
19
  }
49
- /** Sort-specific configuration (excludes `data` — shared at the top level). */
50
20
  interface MergeSortConfig<T extends CollectionItem> {
51
21
  fields: (keyof T & string)[];
52
22
  }
53
- /** Union of the three engine constructors accepted in `imports`. */
54
23
  type EngineConstructor = typeof TextSearchEngine | typeof SortEngine | typeof FilterEngine;
55
24
  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
25
  imports: EngineConstructor[];
66
- /** Shared dataset — passed to every activated engine. */
67
26
  data: T[];
68
- /** Config for TextSearchEngine (requires `TextSearchEngine` in `imports`). */
69
27
  search?: MergeSearchConfig<T>;
70
- /** Config for FilterEngine (requires `FilterEngine` in `imports`). */
71
28
  filter?: MergeFilterConfig<T>;
72
- /** Config for SortEngine (requires `SortEngine` in `imports`). */
73
29
  sort?: MergeSortConfig<T>;
74
30
  }
75
31
  declare class MergeEngines<T extends CollectionItem> {
@@ -77,33 +33,10 @@ declare class MergeEngines<T extends CollectionItem> {
77
33
  private readonly sortEngine;
78
34
  private readonly filterEngine;
79
35
  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
36
  search(query: string): T[];
88
37
  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
38
  sort(descriptors: SortDescriptor<T>[]): T[];
98
39
  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
40
  filter(criteria: FilterCriterion<T>[]): T[];
108
41
  filter(data: T[], criteria: FilterCriterion<T>[]): T[];
109
42
  }
@@ -1,5 +1,5 @@
1
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';
2
+ export { C as CollectionItem, F as FilterCriterion, I as IndexableKey, S as SortDescriptor, a as SortDirection } from '../types-D24zQWME.mjs';
3
3
  import '../search/index.mjs';
4
4
  import '../filter/index.mjs';
5
5
  import '../sort/index.mjs';
@@ -1,97 +1,27 @@
1
- import { C as CollectionItem } from '../types-BlZO_NQA.mjs';
2
- export { I as IndexableKey } from '../types-BlZO_NQA.mjs';
1
+ import { C as CollectionItem } from '../types-D24zQWME.mjs';
2
+ export { I as IndexableKey } from '../types-D24zQWME.mjs';
3
3
 
4
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|).
5
+ * TextSearchEngine class for performing fast substring search on string fields
6
+ * using n-gram indexing and intersection.
15
7
  */
16
8
 
17
9
  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
10
  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
11
  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
12
  minQueryLength?: number;
48
13
  }
49
14
  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
15
  private ngramIndexes;
55
- /** Reference to the full dataset (set once via `buildIndex` or the constructor). */
56
16
  private data;
57
- /** Minimum query length before the engine executes a search. */
58
17
  private readonly minQueryLength;
59
18
  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
19
  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
20
  search(query: string): T[];
88
21
  search(field: keyof T & string, query: string): T[];
89
- /** Search across every indexed field and return a deduplicated union. */
90
22
  private searchAllFields;
91
- /** Core search implementation for a single field. */
92
23
  private searchField;
93
24
  private searchFieldWithPreparedQuery;
94
- /** Free memory. */
95
25
  clear(): void;
96
26
  }
97
27
 
@@ -1,105 +1,26 @@
1
- import { C as CollectionItem, S as SortDescriptor } from '../types-BlZO_NQA.mjs';
2
- export { a as SortDirection } from '../types-BlZO_NQA.mjs';
1
+ import { C as CollectionItem, S as SortDescriptor } from '../types-D24zQWME.mjs';
2
+ export { a as SortDirection } from '../types-D24zQWME.mjs';
3
3
 
4
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.
5
+ * SortEngine class for sorting large datasets using pre-built indexes
6
+ * for O(n) performance or fallback to in-place sorting.
25
7
  */
26
8
 
27
9
  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
10
  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
11
  fields?: (keyof T & string)[];
45
12
  }
46
13
  declare class SortEngine<T extends CollectionItem> {
47
- /** field → cached ascending sort index */
48
14
  private cache;
49
- /** Reference to the full dataset (set via the constructor or `buildIndex`). */
50
15
  private data;
51
16
  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
17
  private buildIndex;
65
- /**
66
- * Free all cached sort indexes.
67
- */
68
18
  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
19
  sort(descriptors: SortDescriptor<T>[]): T[];
83
20
  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
21
  private reconstructFromIndex;
89
22
  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
23
  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
24
  private radixSortNumeric;
104
25
  }
105
26
 
@@ -0,0 +1,15 @@
1
+ type CollectionItem = Record<string, any>;
2
+ type IndexableKey<T> = {
3
+ [K in keyof T]: T[K] extends string | number ? K : never;
4
+ }[keyof T];
5
+ interface FilterCriterion<T extends CollectionItem> {
6
+ field: keyof T & string;
7
+ values: any[];
8
+ }
9
+ type SortDirection = "asc" | "desc";
10
+ interface SortDescriptor<T extends CollectionItem> {
11
+ field: keyof T & string;
12
+ direction: SortDirection;
13
+ }
14
+
15
+ export type { CollectionItem as C, FilterCriterion as F, IndexableKey as I, SortDescriptor as S, SortDirection as a };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devisfuture/mega-collection",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "High-performance search, filter & sort engine for 100K+ item collections in JavaScript/TypeScript",
5
5
  "exports": {
6
6
  ".": {
@@ -1,29 +0,0 @@
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 };