spotr 1.0.0-alpha.0 → 1.0.0-alpha.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.
Files changed (47) hide show
  1. package/README.md +141 -14
  2. package/dist/Spotr.cjs +1 -1
  3. package/dist/Spotr.cjs.map +1 -1
  4. package/dist/Spotr.js +84 -78
  5. package/dist/Spotr.js.map +1 -1
  6. package/dist/bundle-size.json +4 -0
  7. package/dist/errors.cjs +1 -1
  8. package/dist/errors.cjs.map +1 -1
  9. package/dist/errors.js +6 -5
  10. package/dist/errors.js.map +1 -1
  11. package/dist/fuzzy/levenshtein.cjs +1 -1
  12. package/dist/fuzzy/levenshtein.cjs.map +1 -1
  13. package/dist/fuzzy/levenshtein.js +39 -23
  14. package/dist/fuzzy/levenshtein.js.map +1 -1
  15. package/dist/fuzzy/scorer.cjs +1 -1
  16. package/dist/fuzzy/scorer.cjs.map +1 -1
  17. package/dist/fuzzy/scorer.js +26 -19
  18. package/dist/fuzzy/scorer.js.map +1 -1
  19. package/dist/index.cjs +1 -1
  20. package/dist/index.d.ts +21 -3
  21. package/dist/index.js +2 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/preact.cjs.map +1 -1
  24. package/dist/preact.d.ts +23 -2
  25. package/dist/preact.js.map +1 -1
  26. package/dist/react.cjs.map +1 -1
  27. package/dist/react.d.ts +23 -2
  28. package/dist/react.js.map +1 -1
  29. package/dist/solid.cjs.map +1 -1
  30. package/dist/solid.d.ts +29 -24
  31. package/dist/solid.js.map +1 -1
  32. package/dist/svelte.cjs.map +1 -1
  33. package/dist/svelte.d.ts +28 -20
  34. package/dist/svelte.js.map +1 -1
  35. package/dist/types.cjs +2 -0
  36. package/dist/types.cjs.map +1 -0
  37. package/dist/types.js +5 -0
  38. package/dist/types.js.map +1 -0
  39. package/dist/utils/validate.cjs +1 -1
  40. package/dist/utils/validate.cjs.map +1 -1
  41. package/dist/utils/validate.js +36 -29
  42. package/dist/utils/validate.js.map +1 -1
  43. package/dist/vue.cjs.map +1 -1
  44. package/dist/vue.d.ts +24 -3
  45. package/dist/vue.js.map +1 -1
  46. package/package.json +33 -19
  47. package/LICENSE +0 -21
package/README.md CHANGED
@@ -4,13 +4,15 @@
4
4
 
5
5
  A powerful and minimal client-side fuzzy search library with zero dependencies.
6
6
 
7
+ [![npm version](https://img.shields.io/npm/v/spotr.svg)](https://www.npmjs.com/package/spotr) [![bundle size](https://img.shields.io/badge/bundle%20size-3.4%20kB%20gzipped-brightgreen)](https://bundlephobia.com/package/spotr) [![CI/CD](https://github.com/andymerskin/spotr/actions/workflows/ci.yml/badge.svg)](https://github.com/andymerskin/spotr/actions/workflows/ci.yml)
8
+
7
9
  ## Features
8
10
 
9
11
  - **Zero Dependencies** - Custom Levenshtein implementation, no external deps
10
12
  - **Fuzzy Matching** - Configurable threshold (0-1) for fuzzy string matching
11
13
  - **Nested Field Support** - Dot notation for deeply nested object properties
12
- - **Keyword Filtering** - Pre-filter collections with custom keyword handlers
13
- - **Framework Integrations** - React hooks and Vue composables
14
+ - **Keywords** - Add custom filtering logic for exact keyword matches
15
+ - **Framework Integrations** - React, Vue, Svelte, Solid, and Preact support
14
16
  - **TypeScript** - Full type definitions included
15
17
 
16
18
  ## Installation
@@ -19,6 +21,14 @@ A powerful and minimal client-side fuzzy search library with zero dependencies.
19
21
  npm install spotr
20
22
  ```
21
23
 
24
+ ## CDN
25
+
26
+ You can use Spotr via a CDN:
27
+
28
+ - **unpkg**: `https://unpkg.com/spotr`
29
+ - **jsDelivr**: `https://cdn.jsdelivr.net/npm/spotr`
30
+ - **esm.sh**: `https://esm.sh/spotr`
31
+
22
32
  ## Basic Usage
23
33
 
24
34
  ```typescript
@@ -52,7 +62,7 @@ const { results, matchedKeywords, tokens, warnings } = games.query('witcher');
52
62
  Array or Set of objects to search.
53
63
 
54
64
  ```typescript
55
- collection: gamesArray // or new Set(gamesArray)
65
+ collection: gamesArray; // or new Set(gamesArray)
56
66
  ```
57
67
 
58
68
  ### `fields` (required)
@@ -61,11 +71,11 @@ Properties to search against with weight configuration. Supports dot notation fo
61
71
 
62
72
  ```typescript
63
73
  fields: [
64
- 'title', // string shorthand, weight: 1
65
- { name: 'title', weight: 1 }, // full config
66
- { name: 'email', weight: 0.7 }, // lower priority
74
+ 'title', // string shorthand, weight: 1
75
+ { name: 'title', weight: 1 }, // full config
76
+ { name: 'email', weight: 0.7 }, // lower priority
67
77
  { name: 'address.city', weight: 0.8 }, // nested field
68
- ]
78
+ ];
69
79
  ```
70
80
 
71
81
  ### `keywords` (optional)
@@ -86,7 +96,7 @@ keywords: {
86
96
  triggers: ['ps4', 'ps5', 'xbox', 'pc', 'switch'],
87
97
  handler: (collection, matchedTerms) =>
88
98
  collection.filter(item =>
89
- matchedTerms.some(term =>
99
+ matchedTerms.some(term =>
90
100
  item.platforms.some(p => p.toLowerCase().includes(term))
91
101
  )
92
102
  ),
@@ -115,14 +125,23 @@ Enable case-sensitive matching. Default: `false`
115
125
 
116
126
  Minimum query length to trigger matching. Default: `1`
117
127
 
128
+ ### `maxStringLength` (optional)
129
+
130
+ Maximum string length limit for both search query tokens and collection field values before truncation. Default: `1000`
131
+
132
+ - Search query tokens (search terms) exceeding this limit are truncated
133
+ - Collection field values exceeding this limit are truncated
134
+ - Warnings are added to `result.warnings` when truncation occurs
135
+ - Used for performance optimization to prevent slowdowns with very long strings in fuzzy matching
136
+
118
137
  ## Result Type
119
138
 
120
139
  ```typescript
121
140
  interface SpotrResult<T> {
122
- results: ScoredResult<T>[]; // Array of { item, score }
123
- matchedKeywords: MatchedKeyword[]; // Keywords that matched
124
- tokens: string[]; // Non-keyword search terms
125
- warnings: string[]; // Warnings (e.g., missing nested paths)
141
+ results: ScoredResult<T>[]; // Array of { item, score }
142
+ matchedKeywords: MatchedKeyword[]; // Keywords that matched
143
+ tokens: string[]; // Non-keyword search terms
144
+ warnings: string[]; // Warnings (e.g., missing nested paths)
126
145
  }
127
146
  ```
128
147
 
@@ -136,12 +155,12 @@ function GameSearch({ games, searchQuery }) {
136
155
  collection: games,
137
156
  fields: [{ name: 'title', weight: 1 }],
138
157
  });
139
-
158
+
140
159
  const { results } = useMemo(
141
160
  () => spotr.query(searchQuery),
142
161
  [spotr, searchQuery]
143
162
  );
144
-
163
+
145
164
  return <ResultsTable results={results} />;
146
165
  }
147
166
  ```
@@ -162,6 +181,53 @@ const spotr = useSpotr(() => ({
162
181
  const results = computed(() => spotr.value?.query(query.value));
163
182
  ```
164
183
 
184
+ ## Svelte Store
185
+
186
+ ```typescript
187
+ import { createSpotr } from 'spotr/svelte';
188
+
189
+ const { spotr, query, results } = createSpotr({
190
+ collection: games,
191
+ fields: [{ name: 'title', weight: 1 }],
192
+ });
193
+
194
+ // query is a writable store, results is a derived store
195
+ // Use $query and $results in your Svelte template
196
+ ```
197
+
198
+ ## Solid Hook
199
+
200
+ ```typescript
201
+ import { createSpotr } from 'spotr/solid';
202
+
203
+ const { query, setQuery, results } = createSpotr({
204
+ collection: games,
205
+ fields: [{ name: 'title', weight: 1 }],
206
+ });
207
+
208
+ // query() is a signal getter, setQuery is a signal setter, results() is a memo
209
+ ```
210
+
211
+ ## Preact Hook
212
+
213
+ ```typescript
214
+ import { useSpotr } from 'spotr/preact';
215
+
216
+ function GameSearch({ games, searchQuery }) {
217
+ const spotr = useSpotr({
218
+ collection: games,
219
+ fields: [{ name: 'title', weight: 1 }],
220
+ });
221
+
222
+ const { results } = useMemo(
223
+ () => spotr.query(searchQuery),
224
+ [spotr, searchQuery]
225
+ );
226
+
227
+ return <ResultsTable results={results} />;
228
+ }
229
+ ```
230
+
165
231
  ## API Methods
166
232
 
167
233
  ### `query(search: string): SpotrResult<T>`
@@ -180,6 +246,67 @@ Update the collection.
180
246
 
181
247
  Access the current collection.
182
248
 
249
+ ## Development
250
+
251
+ This project uses [Bun](https://bun.sh) as the package manager. After cloning the repository, install dependencies with:
252
+
253
+ ```sh
254
+ bun install
255
+ ```
256
+
257
+ ### Core Scripts
258
+
259
+ - `bun run build` - Build the library (outputs to `dist/`)
260
+ - `bun run test` - Run all tests once
261
+ - `bun run test:watch` - Run tests in watch mode
262
+ - `bun run test:coverage` - Run tests with coverage report
263
+ - `bun run typecheck` - Type check the library (`tsc --noEmit`)
264
+ - `bun run audit` - Audit the Spotr package for vulnerabilities and apply fixes (`scripts/audit-spotr.ts`)
265
+ - `bun run lint` - Lint the codebase (ESLint)
266
+ - `bun run format` - Format the codebase (Prettier, whole repo from root)
267
+ - `bun run format:check` - Check formatting without modifying
268
+ - `bun run validate` - Full validation (format, lint, typecheck, coverage, examples, build)
269
+ - `bun run size:check` - Verify bundle size under 5KB
270
+ - `bun run clean` - Remove dist and coverage directories
271
+
272
+ ### Example Scripts
273
+
274
+ - `bun run examples:typecheck` - Type check all example applications
275
+ - `bun run examples:sync` - Sync shared files from `examples/shared/` to all examples
276
+ - `bun run examples:install` - Install dependencies for all examples
277
+ - `bun run examples:update` - Update dependencies for all examples
278
+ - `bun run examples:audit` - Audit all example packages for vulnerabilities and apply fixes (`scripts/audit-examples.ts`)
279
+ - `bun run examples:dev` - Launch interactive dev servers for a selected framework (starts all 5 examples on ports 5173-5177)
280
+
281
+ ### Setup Scripts
282
+
283
+ - `bun run prepare` - Setup Husky git hooks (runs automatically on install)
284
+
285
+ ## Releasing
286
+
287
+ To release a new version to npm, use the release script:
288
+
289
+ ```sh
290
+ bun run release
291
+ ```
292
+
293
+ The script automates the release process: it runs `validate` (format check, lint, typecheck, test coverage, examples typecheck, and build) and a bundle size check (enforcing a 5KB gzipped limit) before proceeding with version bumping, commit amending, and publishing.
294
+
295
+ **Note:** Due to a known npm bug with workspaces, `npm version` may not automatically commit and tag changes when the package.json is in a subdirectory. The release script automatically detects this and creates the commit and tag manually if needed.
296
+
297
+ You can also pass a version bump type directly:
298
+
299
+ ```sh
300
+ bun run release patch # for patch releases
301
+ bun run release minor # for minor releases
302
+ bun run release major # for major releases
303
+ bun run release prerelease # increment existing pre-release
304
+ bun run release premajor --preid beta # start major pre-release with beta tag
305
+ bun run release preminor --preid=rc # start minor pre-release with rc tag
306
+ ```
307
+
308
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for the complete release process and manual release steps.
309
+
183
310
  ## License
184
311
 
185
312
  MIT
package/dist/Spotr.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var k=Object.defineProperty;var T=(u,e,t)=>e in u?k(u,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):u[e]=t;var c=(u,e,t)=>T(u,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("./errors.cjs"),y=require("./utils/validate.cjs"),f=require("./fuzzy/scorer.cjs"),S=require("./utils/tokenize.cjs");class K{constructor(e){c(this,"_collection");c(this,"_fields");c(this,"_keywords");c(this,"_keywordTriggerMap");c(this,"_threshold");c(this,"_limit");c(this,"_debounce");c(this,"_caseSensitive");c(this,"_minMatchCharLength");c(this,"_debounceTimer",null);c(this,"_optionsSnapshot");this._optionsSnapshot=e;const t=y.validateOptions(e);this._collection=t.collection,this._threshold=t.threshold,this._limit=t.limit,this._debounce=t.debounce,this._caseSensitive=t.caseSensitive,this._minMatchCharLength=t.minMatchCharLength,this._fields=f.normalizeFieldConfig(e.fields,this._threshold),this._keywords=e.keywords?y.validateKeywords(e.keywords):null,this._keywordTriggerMap=this._buildKeywordTriggerMap()}_buildKeywordTriggerMap(){const e=new Map;if(!this._keywords)return e;for(const t of this._keywords.definitions){const i=Array.isArray(t.triggers)?t.triggers:[t.triggers];for(const r of i)e.set(r.toLowerCase(),t)}return e}get collection(){return this._collection}get options(){return this._optionsSnapshot}setCollection(e){this._collection=e instanceof Set?Array.from(e):e}query(e){const t=S.tokenize(e);if(t.length===0)return{results:[],matchedKeywords:[],tokens:[],warnings:[]};const{keywordTokens:i,searchTokens:r}=this._extractKeywords(t);let a=this._collection;const s=[];if(i.size>0){const o=this._applyKeywords(a,i);a=o.collection,s.push(...o.matchedKeywords)}if(r.length===0){const o=[];return{results:a.map(_=>{const{score:g,warnings:p}=f.scoreItem(_,[],this._fields,this._caseSensitive);return o.push(...p),{item:_,score:g}}),matchedKeywords:s,tokens:r,warnings:[...new Set(o)]}}const n=this._minMatchCharLength>1?r.filter(o=>o.length>=this._minMatchCharLength):r;if(n.length===0)return{results:[],matchedKeywords:s,tokens:r,warnings:[]};const h=[],l=[];for(const o of a){const{score:d,warnings:_}=f.scoreItem(o,n,this._fields,this._caseSensitive);h.push(..._),d>0&&l.push({item:o,score:d})}return l.sort((o,d)=>d.score-o.score),{results:this._limit<1/0?l.slice(0,this._limit):l,matchedKeywords:s,tokens:r,warnings:[...new Set(h)]}}queryAsync(e){return new Promise(t=>{this._debounce>0?(this._debounceTimer&&clearTimeout(this._debounceTimer),this._debounceTimer=setTimeout(()=>{t(this.query(e))},this._debounce)):t(this.query(e))})}_extractKeywords(e){const t=new Map,i=[];for(const r of e){const a=this._caseSensitive?r:r.toLowerCase(),s=this._keywordTriggerMap.get(a);if(s){const n=t.get(s.name)||[];n.push(r),t.set(s.name,n)}else i.push(r)}return{keywordTokens:t,searchTokens:i}}_applyKeywords(e,t){if(!this._keywords)return{collection:e,matchedKeywords:[]};let i=e;const r=[],a=new Map;for(const[s,n]of t)a.set(s,n);if(this._keywords.mode==="and")for(const s of this._keywords.definitions){const n=a.get(s.name);if(n){const h=s.handler;if(i=h(i,n),r.push({name:s.name,terms:n}),!Array.isArray(i))throw new m.SpotrError(`Keyword handler "${s.name}" must return an array`,m.ErrorCodes.INVALID_HANDLER_RETURN)}}else{const s=[],n=new Set;for(const h of this._keywords.definitions){const l=a.get(h.name);if(l){const w=h.handler,o=w(this._collection,l);if(r.push({name:h.name,terms:l}),!Array.isArray(o))throw new m.SpotrError(`Keyword handler "${h.name}" must return an array`,m.ErrorCodes.INVALID_HANDLER_RETURN);for(const d of o)n.has(d)||(n.add(d),s.push(d))}}i=s}return{collection:i,matchedKeywords:r}}}exports.Spotr=K;
1
+ "use strict";var p=Object.defineProperty;var k=(u,e,t)=>e in u?p(u,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):u[e]=t;var o=(u,e,t)=>k(u,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("./utils/validate.cjs"),_=require("./fuzzy/scorer.cjs"),S=require("./utils/tokenize.cjs");class T{constructor(e){o(this,"_collection");o(this,"_fields");o(this,"_keywords");o(this,"_keywordTriggerMap");o(this,"_threshold");o(this,"_limit");o(this,"_debounce");o(this,"_caseSensitive");o(this,"_minMatchCharLength");o(this,"_maxStringLength");o(this,"_debounceTimer",null);o(this,"_optionsSnapshot");this._optionsSnapshot=e;const t=m.validateOptions(e);this._collection=t.collection,this._threshold=t.threshold,this._limit=t.limit,this._debounce=t.debounce,this._caseSensitive=t.caseSensitive,this._minMatchCharLength=t.minMatchCharLength,this._maxStringLength=t.maxStringLength,this._fields=_.normalizeFieldConfig(e.fields,this._threshold),this._keywords=e.keywords?m.validateKeywords(e.keywords):null,this._keywordTriggerMap=this._buildKeywordTriggerMap()}_buildKeywordTriggerMap(){const e=new Map;if(!this._keywords)return e;for(const t of this._keywords.definitions){const c=Array.isArray(t.triggers)?t.triggers:[t.triggers];for(const r of c)e.set(r.toLowerCase(),t)}return e}get collection(){return this._collection}get options(){return this._optionsSnapshot}setCollection(e){this._collection=m.validateCollection(e)}query(e){const t=S.tokenize(e);if(t.length===0)return{results:[],matchedKeywords:[],tokens:[],warnings:[]};const{keywordTokens:c,searchTokens:r}=this._extractKeywords(t);let h=this._collection;const s=[];if(c.size>0){const n=this._applyKeywords(h,c);h=n.collection,s.push(...n.matchedKeywords)}if(r.length===0){const n=[];return{results:h.map(g=>{const{score:y,warnings:w}=_.scoreItem(g,[],this._fields,this._caseSensitive,this._maxStringLength);return n.push(...w),{item:g,score:y}}),matchedKeywords:s,tokens:r,warnings:[...new Set(n)]}}const i=this._minMatchCharLength>1?r.filter(n=>n.length>=this._minMatchCharLength):r;if(i.length===0)return{results:[],matchedKeywords:s,tokens:r,warnings:[]};const l=[],a=[];for(const n of h){const{score:d,warnings:g}=_.scoreItem(n,i,this._fields,this._caseSensitive,this._maxStringLength);l.push(...g),d>0&&a.push({item:n,score:d})}return a.sort((n,d)=>d.score-n.score),{results:this._limit<1/0?a.slice(0,this._limit):a,matchedKeywords:s,tokens:r,warnings:[...new Set(l)]}}queryAsync(e){return new Promise(t=>{this._debounce>0?(this._debounceTimer&&clearTimeout(this._debounceTimer),this._debounceTimer=setTimeout(()=>{t(this.query(e))},this._debounce)):t(this.query(e))})}_extractKeywords(e){const t=new Map,c=[];for(const r of e){const h=this._caseSensitive?r:r.toLowerCase(),s=this._keywordTriggerMap.get(h);if(s){const i=t.get(s.name)||[];i.push(r),t.set(s.name,i)}else c.push(r)}return{keywordTokens:t,searchTokens:c}}_applyKeywords(e,t){if(!this._keywords)return{collection:e,matchedKeywords:[]};let c=e;const r=[],h=new Map;for(const[s,i]of t)h.set(s,i);if(this._keywords.mode==="and")for(const s of this._keywords.definitions){const i=h.get(s.name);if(i){const l=s.handler,a=l(c,i);if(!Array.isArray(a)){console.error(`[Spotr] Keyword handler "${s.name}" must return an array, received ${typeof a}. Skipping this filter.`);continue}c=a,r.push({name:s.name,terms:i})}}else{const s=[],i=new Set;for(const l of this._keywords.definitions){const a=h.get(l.name);if(a){const f=l.handler,n=f(this._collection,a);if(!Array.isArray(n)){console.error(`[Spotr] Keyword handler "${l.name}" must return an array, received ${typeof n}. Skipping this filter.`);continue}r.push({name:l.name,terms:a});for(const d of n)i.has(d)||(i.add(d),s.push(d))}}c=s}return{collection:c,matchedKeywords:r}}}exports.Spotr=T;
2
2
  //# sourceMappingURL=Spotr.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Spotr.cjs","sources":["../src/Spotr.ts"],"sourcesContent":["import { SpotrError, ErrorCodes } from './errors';\nimport { normalizeFieldConfig, scoreItem } from './fuzzy';\nimport { tokenize, validateOptions, validateKeywords } from './utils';\nimport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n NormalizedFieldConfig,\n NormalizedKeywordsConfig,\n KeywordDefinition,\n} from './types';\n\nexport class Spotr<T extends object> {\n private _collection: T[];\n private _fields: NormalizedFieldConfig[];\n private _keywords: NormalizedKeywordsConfig | null;\n private _keywordTriggerMap: Map<string, KeywordDefinition<unknown>>;\n private _threshold: number;\n private _limit: number;\n private _debounce: number;\n private _caseSensitive: boolean;\n private _minMatchCharLength: number;\n private _debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private _optionsSnapshot: SpotrOptions<T>;\n\n constructor(options: SpotrOptions<T>) {\n this._optionsSnapshot = options;\n const validated = validateOptions(options);\n this._collection = validated.collection;\n this._threshold = validated.threshold;\n this._limit = validated.limit;\n this._debounce = validated.debounce;\n this._caseSensitive = validated.caseSensitive;\n this._minMatchCharLength = validated.minMatchCharLength;\n\n this._fields = normalizeFieldConfig(options.fields, this._threshold);\n\n this._keywords = options.keywords ? validateKeywords(options.keywords) : null;\n this._keywordTriggerMap = this._buildKeywordTriggerMap();\n }\n\n private _buildKeywordTriggerMap(): Map<string, KeywordDefinition<unknown>> {\n const map = new Map<string, KeywordDefinition<unknown>>();\n if (!this._keywords) return map;\n\n for (const def of this._keywords.definitions) {\n const triggers = Array.isArray(def.triggers) ? def.triggers : [def.triggers];\n for (const trigger of triggers) {\n map.set(trigger.toLowerCase(), def);\n }\n }\n return map;\n }\n\n get collection(): T[] {\n return this._collection;\n }\n\n get options(): SpotrOptions<T> {\n return this._optionsSnapshot;\n }\n\n setCollection(collection: T[] | Set<T>): void {\n this._collection = collection instanceof Set ? Array.from(collection) : collection;\n }\n\n query(search: string): SpotrResult<T> {\n const tokens = tokenize(search);\n \n if (tokens.length === 0) {\n return {\n results: [],\n matchedKeywords: [],\n tokens: [],\n warnings: [],\n };\n }\n\n const { keywordTokens, searchTokens } = this._extractKeywords(tokens);\n\n let filteredCollection = this._collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n if (keywordTokens.size > 0) {\n const result = this._applyKeywords(filteredCollection, keywordTokens);\n filteredCollection = result.collection;\n matchedKeywords.push(...result.matchedKeywords);\n }\n\n if (searchTokens.length === 0) {\n const allWarnings: string[] = [];\n const results: ScoredResult<T>[] = filteredCollection.map((item) => {\n const { score, warnings } = scoreItem(\n item,\n [],\n this._fields,\n this._caseSensitive\n );\n allWarnings.push(...warnings);\n return { item, score };\n });\n\n return {\n results,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n const validTokens = this._minMatchCharLength > 1\n ? searchTokens.filter((t) => t.length >= this._minMatchCharLength)\n : searchTokens;\n\n if (validTokens.length === 0) {\n return {\n results: [],\n matchedKeywords,\n tokens: searchTokens,\n warnings: [],\n };\n }\n\n const allWarnings: string[] = [];\n const scoredResults: ScoredResult<T>[] = [];\n\n for (const item of filteredCollection) {\n const { score, warnings } = scoreItem(\n item,\n validTokens,\n this._fields,\n this._caseSensitive\n );\n allWarnings.push(...warnings);\n\n if (score > 0) {\n scoredResults.push({ item, score });\n }\n }\n\n scoredResults.sort((a, b) => b.score - a.score);\n\n const limitedResults = this._limit < Infinity\n ? scoredResults.slice(0, this._limit)\n : scoredResults;\n\n return {\n results: limitedResults,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n queryAsync(search: string): Promise<SpotrResult<T>> {\n return new Promise((resolve) => {\n if (this._debounce > 0) {\n if (this._debounceTimer) {\n clearTimeout(this._debounceTimer);\n }\n this._debounceTimer = setTimeout(() => {\n resolve(this.query(search));\n }, this._debounce);\n } else {\n resolve(this.query(search));\n }\n });\n }\n\n private _extractKeywords(tokens: string[]): {\n keywordTokens: Map<string, string[]>;\n searchTokens: string[];\n } {\n const keywordTokens = new Map<string, string[]>();\n const searchTokens: string[] = [];\n\n for (const token of tokens) {\n const normalizedToken = this._caseSensitive ? token : token.toLowerCase();\n const keywordDef = this._keywordTriggerMap.get(normalizedToken);\n\n if (keywordDef) {\n const existing = keywordTokens.get(keywordDef.name) || [];\n existing.push(token);\n keywordTokens.set(keywordDef.name, existing);\n } else {\n searchTokens.push(token);\n }\n }\n\n return { keywordTokens, searchTokens };\n }\n\n private _applyKeywords(\n collection: T[],\n keywordTokens: Map<string, string[]>\n ): { collection: T[]; matchedKeywords: MatchedKeyword[] } {\n if (!this._keywords) {\n return { collection, matchedKeywords: [] };\n }\n\n let result = collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n const keywordNameToTerms = new Map<string, string[]>();\n for (const [name, terms] of keywordTokens) {\n keywordNameToTerms.set(name, terms);\n }\n\n if (this._keywords.mode === 'and') {\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (collection: T[], matchedTerms: string[]) => T[];\n result = handler(result, terms);\n matchedKeywords.push({ name: def.name, terms });\n\n if (!Array.isArray(result)) {\n throw new SpotrError(\n `Keyword handler \"${def.name}\" must return an array`,\n ErrorCodes.INVALID_HANDLER_RETURN\n );\n }\n }\n }\n } else {\n const mergedResults: T[] = [];\n const processedItems = new Set<T>();\n\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (collection: T[], matchedTerms: string[]) => T[];\n const keywordResults = handler(this._collection, terms);\n matchedKeywords.push({ name: def.name, terms });\n\n if (!Array.isArray(keywordResults)) {\n throw new SpotrError(\n `Keyword handler \"${def.name}\" must return an array`,\n ErrorCodes.INVALID_HANDLER_RETURN\n );\n }\n\n for (const item of keywordResults) {\n if (!processedItems.has(item)) {\n processedItems.add(item);\n mergedResults.push(item);\n }\n }\n }\n }\n result = mergedResults;\n }\n\n return { collection: result, matchedKeywords };\n }\n}\n"],"names":["Spotr","options","__publicField","validated","validateOptions","normalizeFieldConfig","validateKeywords","map","def","triggers","trigger","collection","search","tokens","tokenize","keywordTokens","searchTokens","filteredCollection","matchedKeywords","result","allWarnings","item","score","warnings","scoreItem","validTokens","t","scoredResults","a","b","resolve","token","normalizedToken","keywordDef","existing","keywordNameToTerms","name","terms","handler","SpotrError","ErrorCodes","mergedResults","processedItems","keywordResults"],"mappings":"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"}
1
+ {"version":3,"file":"Spotr.cjs","sources":["../src/Spotr.ts"],"sourcesContent":["import { normalizeFieldConfig, scoreItem } from './fuzzy';\nimport {\n tokenize,\n validateOptions,\n validateKeywords,\n validateCollection,\n} from './utils';\nimport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n NormalizedFieldConfig,\n NormalizedKeywordsConfig,\n KeywordDefinition,\n} from './types';\n\nexport class Spotr<T extends object> {\n private _collection: T[];\n private _fields: NormalizedFieldConfig[];\n private _keywords: NormalizedKeywordsConfig | null;\n private _keywordTriggerMap: Map<string, KeywordDefinition<unknown>>;\n private _threshold: number;\n private _limit: number;\n private _debounce: number;\n private _caseSensitive: boolean;\n private _minMatchCharLength: number;\n private _maxStringLength: number;\n private _debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private _optionsSnapshot: SpotrOptions<T>;\n\n constructor(options: SpotrOptions<T>) {\n this._optionsSnapshot = options;\n const validated = validateOptions(options);\n this._collection = validated.collection;\n this._threshold = validated.threshold;\n this._limit = validated.limit;\n this._debounce = validated.debounce;\n this._caseSensitive = validated.caseSensitive;\n this._minMatchCharLength = validated.minMatchCharLength;\n this._maxStringLength = validated.maxStringLength;\n\n this._fields = normalizeFieldConfig(options.fields, this._threshold);\n\n this._keywords = options.keywords\n ? validateKeywords(options.keywords)\n : null;\n this._keywordTriggerMap = this._buildKeywordTriggerMap();\n }\n\n private _buildKeywordTriggerMap(): Map<string, KeywordDefinition<unknown>> {\n const map = new Map<string, KeywordDefinition<unknown>>();\n if (!this._keywords) return map;\n\n for (const def of this._keywords.definitions) {\n const triggers = Array.isArray(def.triggers)\n ? def.triggers\n : [def.triggers];\n for (const trigger of triggers) {\n map.set(trigger.toLowerCase(), def);\n }\n }\n return map;\n }\n\n get collection(): T[] {\n return this._collection;\n }\n\n get options(): SpotrOptions<T> {\n return this._optionsSnapshot;\n }\n\n setCollection(collection: T[] | Set<T>): void {\n this._collection = validateCollection(collection as unknown) as T[];\n }\n\n query(search: string): SpotrResult<T> {\n const tokens = tokenize(search);\n\n if (tokens.length === 0) {\n return {\n results: [],\n matchedKeywords: [],\n tokens: [],\n warnings: [],\n };\n }\n\n const { keywordTokens, searchTokens } = this._extractKeywords(tokens);\n\n let filteredCollection = this._collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n if (keywordTokens.size > 0) {\n const result = this._applyKeywords(filteredCollection, keywordTokens);\n filteredCollection = result.collection;\n matchedKeywords.push(...result.matchedKeywords);\n }\n\n if (searchTokens.length === 0) {\n const allWarnings: string[] = [];\n const results: ScoredResult<T>[] = filteredCollection.map((item) => {\n const { score, warnings } = scoreItem(\n item,\n [],\n this._fields,\n this._caseSensitive,\n this._maxStringLength\n );\n allWarnings.push(...warnings);\n return { item, score };\n });\n\n return {\n results,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n const validTokens =\n this._minMatchCharLength > 1\n ? searchTokens.filter((t) => t.length >= this._minMatchCharLength)\n : searchTokens;\n\n if (validTokens.length === 0) {\n return {\n results: [],\n matchedKeywords,\n tokens: searchTokens,\n warnings: [],\n };\n }\n\n const allWarnings: string[] = [];\n const scoredResults: ScoredResult<T>[] = [];\n\n for (const item of filteredCollection) {\n const { score, warnings } = scoreItem(\n item,\n validTokens,\n this._fields,\n this._caseSensitive,\n this._maxStringLength\n );\n allWarnings.push(...warnings);\n\n if (score > 0) {\n scoredResults.push({ item, score });\n }\n }\n\n scoredResults.sort((a, b) => b.score - a.score);\n\n const limitedResults =\n this._limit < Infinity\n ? scoredResults.slice(0, this._limit)\n : scoredResults;\n\n return {\n results: limitedResults,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n queryAsync(search: string): Promise<SpotrResult<T>> {\n return new Promise((resolve) => {\n if (this._debounce > 0) {\n if (this._debounceTimer) {\n clearTimeout(this._debounceTimer);\n }\n this._debounceTimer = setTimeout(() => {\n resolve(this.query(search));\n }, this._debounce);\n } else {\n resolve(this.query(search));\n }\n });\n }\n\n private _extractKeywords(tokens: string[]): {\n keywordTokens: Map<string, string[]>;\n searchTokens: string[];\n } {\n const keywordTokens = new Map<string, string[]>();\n const searchTokens: string[] = [];\n\n for (const token of tokens) {\n const normalizedToken = this._caseSensitive ? token : token.toLowerCase();\n const keywordDef = this._keywordTriggerMap.get(normalizedToken);\n\n if (keywordDef) {\n const existing = keywordTokens.get(keywordDef.name) || [];\n existing.push(token);\n keywordTokens.set(keywordDef.name, existing);\n } else {\n searchTokens.push(token);\n }\n }\n\n return { keywordTokens, searchTokens };\n }\n\n private _applyKeywords(\n collection: T[],\n keywordTokens: Map<string, string[]>\n ): { collection: T[]; matchedKeywords: MatchedKeyword[] } {\n if (!this._keywords) {\n return { collection, matchedKeywords: [] };\n }\n\n let result = collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n const keywordNameToTerms = new Map<string, string[]>();\n for (const [name, terms] of keywordTokens) {\n keywordNameToTerms.set(name, terms);\n }\n\n if (this._keywords.mode === 'and') {\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (\n collection: T[],\n matchedTerms: string[]\n ) => T[];\n const handlerResult = handler(result, terms);\n\n if (!Array.isArray(handlerResult)) {\n console.error(\n `[Spotr] Keyword handler \"${def.name}\" must return an array, received ${typeof handlerResult}. Skipping this filter.`\n );\n // Skip this keyword - keep current collection for this step\n continue;\n }\n\n result = handlerResult;\n matchedKeywords.push({ name: def.name, terms });\n }\n }\n } else {\n const mergedResults: T[] = [];\n const processedItems = new Set<T>();\n\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (\n collection: T[],\n matchedTerms: string[]\n ) => T[];\n const keywordResults = handler(this._collection, terms);\n\n if (!Array.isArray(keywordResults)) {\n console.error(\n `[Spotr] Keyword handler \"${def.name}\" must return an array, received ${typeof keywordResults}. Skipping this filter.`\n );\n // Skip this keyword - don't add its results\n continue;\n }\n\n matchedKeywords.push({ name: def.name, terms });\n\n for (const item of keywordResults) {\n if (!processedItems.has(item)) {\n processedItems.add(item);\n mergedResults.push(item);\n }\n }\n }\n }\n result = mergedResults;\n }\n\n return { collection: result, matchedKeywords };\n }\n}\n"],"names":["Spotr","options","__publicField","validated","validateOptions","normalizeFieldConfig","validateKeywords","map","def","triggers","trigger","collection","validateCollection","search","tokens","tokenize","keywordTokens","searchTokens","filteredCollection","matchedKeywords","result","allWarnings","item","score","warnings","scoreItem","validTokens","t","scoredResults","a","b","resolve","token","normalizedToken","keywordDef","existing","keywordNameToTerms","name","terms","handler","handlerResult","mergedResults","processedItems","keywordResults"],"mappings":"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"}
package/dist/Spotr.js CHANGED
@@ -1,33 +1,33 @@
1
- var k = Object.defineProperty;
2
- var T = (m, e, t) => e in m ? k(m, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : m[e] = t;
3
- var a = (m, e, t) => T(m, typeof e != "symbol" ? e + "" : e, t);
4
- import { SpotrError as f, ErrorCodes as w } from "./errors.js";
5
- import { validateOptions as S, validateKeywords as K } from "./utils/validate.js";
6
- import { normalizeFieldConfig as M, scoreItem as y } from "./fuzzy/scorer.js";
7
- import { tokenize as A } from "./utils/tokenize.js";
8
- class I {
1
+ var w = Object.defineProperty;
2
+ var p = (m, e, t) => e in m ? w(m, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : m[e] = t;
3
+ var o = (m, e, t) => p(m, typeof e != "symbol" ? e + "" : e, t);
4
+ import { validateOptions as k, validateKeywords as S, validateCollection as T } from "./utils/validate.js";
5
+ import { normalizeFieldConfig as K, scoreItem as _ } from "./fuzzy/scorer.js";
6
+ import { tokenize as M } from "./utils/tokenize.js";
7
+ class x {
9
8
  constructor(e) {
10
- a(this, "_collection");
11
- a(this, "_fields");
12
- a(this, "_keywords");
13
- a(this, "_keywordTriggerMap");
14
- a(this, "_threshold");
15
- a(this, "_limit");
16
- a(this, "_debounce");
17
- a(this, "_caseSensitive");
18
- a(this, "_minMatchCharLength");
19
- a(this, "_debounceTimer", null);
20
- a(this, "_optionsSnapshot");
9
+ o(this, "_collection");
10
+ o(this, "_fields");
11
+ o(this, "_keywords");
12
+ o(this, "_keywordTriggerMap");
13
+ o(this, "_threshold");
14
+ o(this, "_limit");
15
+ o(this, "_debounce");
16
+ o(this, "_caseSensitive");
17
+ o(this, "_minMatchCharLength");
18
+ o(this, "_maxStringLength");
19
+ o(this, "_debounceTimer", null);
20
+ o(this, "_optionsSnapshot");
21
21
  this._optionsSnapshot = e;
22
- const t = S(e);
23
- this._collection = t.collection, this._threshold = t.threshold, this._limit = t.limit, this._debounce = t.debounce, this._caseSensitive = t.caseSensitive, this._minMatchCharLength = t.minMatchCharLength, this._fields = M(e.fields, this._threshold), this._keywords = e.keywords ? K(e.keywords) : null, this._keywordTriggerMap = this._buildKeywordTriggerMap();
22
+ const t = k(e);
23
+ this._collection = t.collection, this._threshold = t.threshold, this._limit = t.limit, this._debounce = t.debounce, this._caseSensitive = t.caseSensitive, this._minMatchCharLength = t.minMatchCharLength, this._maxStringLength = t.maxStringLength, this._fields = K(e.fields, this._threshold), this._keywords = e.keywords ? S(e.keywords) : null, this._keywordTriggerMap = this._buildKeywordTriggerMap();
24
24
  }
25
25
  _buildKeywordTriggerMap() {
26
26
  const e = /* @__PURE__ */ new Map();
27
27
  if (!this._keywords) return e;
28
28
  for (const t of this._keywords.definitions) {
29
- const i = Array.isArray(t.triggers) ? t.triggers : [t.triggers];
30
- for (const r of i)
29
+ const c = Array.isArray(t.triggers) ? t.triggers : [t.triggers];
30
+ for (const r of c)
31
31
  e.set(r.toLowerCase(), t);
32
32
  }
33
33
  return e;
@@ -39,10 +39,10 @@ class I {
39
39
  return this._optionsSnapshot;
40
40
  }
41
41
  setCollection(e) {
42
- this._collection = e instanceof Set ? Array.from(e) : e;
42
+ this._collection = T(e);
43
43
  }
44
44
  query(e) {
45
- const t = A(e);
45
+ const t = M(e);
46
46
  if (t.length === 0)
47
47
  return {
48
48
  results: [],
@@ -50,53 +50,55 @@ class I {
50
50
  tokens: [],
51
51
  warnings: []
52
52
  };
53
- const { keywordTokens: i, searchTokens: r } = this._extractKeywords(t);
54
- let h = this._collection;
53
+ const { keywordTokens: c, searchTokens: r } = this._extractKeywords(t);
54
+ let a = this._collection;
55
55
  const s = [];
56
- if (i.size > 0) {
57
- const o = this._applyKeywords(h, i);
58
- h = o.collection, s.push(...o.matchedKeywords);
56
+ if (c.size > 0) {
57
+ const n = this._applyKeywords(a, c);
58
+ a = n.collection, s.push(...n.matchedKeywords);
59
59
  }
60
60
  if (r.length === 0) {
61
- const o = [];
61
+ const n = [];
62
62
  return {
63
- results: h.map((u) => {
64
- const { score: g, warnings: p } = y(
63
+ results: a.map((u) => {
64
+ const { score: f, warnings: y } = _(
65
65
  u,
66
66
  [],
67
67
  this._fields,
68
- this._caseSensitive
68
+ this._caseSensitive,
69
+ this._maxStringLength
69
70
  );
70
- return o.push(...p), { item: u, score: g };
71
+ return n.push(...y), { item: u, score: f };
71
72
  }),
72
73
  matchedKeywords: s,
73
74
  tokens: r,
74
- warnings: [...new Set(o)]
75
+ warnings: [...new Set(n)]
75
76
  };
76
77
  }
77
- const n = this._minMatchCharLength > 1 ? r.filter((o) => o.length >= this._minMatchCharLength) : r;
78
- if (n.length === 0)
78
+ const i = this._minMatchCharLength > 1 ? r.filter((n) => n.length >= this._minMatchCharLength) : r;
79
+ if (i.length === 0)
79
80
  return {
80
81
  results: [],
81
82
  matchedKeywords: s,
82
83
  tokens: r,
83
84
  warnings: []
84
85
  };
85
- const c = [], l = [];
86
- for (const o of h) {
87
- const { score: d, warnings: u } = y(
88
- o,
86
+ const l = [], h = [];
87
+ for (const n of a) {
88
+ const { score: d, warnings: u } = _(
89
89
  n,
90
+ i,
90
91
  this._fields,
91
- this._caseSensitive
92
+ this._caseSensitive,
93
+ this._maxStringLength
92
94
  );
93
- c.push(...u), d > 0 && l.push({ item: o, score: d });
95
+ l.push(...u), d > 0 && h.push({ item: n, score: d });
94
96
  }
95
- return l.sort((o, d) => d.score - o.score), {
96
- results: this._limit < 1 / 0 ? l.slice(0, this._limit) : l,
97
+ return h.sort((n, d) => d.score - n.score), {
98
+ results: this._limit < 1 / 0 ? h.slice(0, this._limit) : h,
97
99
  matchedKeywords: s,
98
100
  tokens: r,
99
- warnings: [...new Set(c)]
101
+ warnings: [...new Set(l)]
100
102
  };
101
103
  }
102
104
  queryAsync(e) {
@@ -107,57 +109,61 @@ class I {
107
109
  });
108
110
  }
109
111
  _extractKeywords(e) {
110
- const t = /* @__PURE__ */ new Map(), i = [];
112
+ const t = /* @__PURE__ */ new Map(), c = [];
111
113
  for (const r of e) {
112
- const h = this._caseSensitive ? r : r.toLowerCase(), s = this._keywordTriggerMap.get(h);
114
+ const a = this._caseSensitive ? r : r.toLowerCase(), s = this._keywordTriggerMap.get(a);
113
115
  if (s) {
114
- const n = t.get(s.name) || [];
115
- n.push(r), t.set(s.name, n);
116
+ const i = t.get(s.name) || [];
117
+ i.push(r), t.set(s.name, i);
116
118
  } else
117
- i.push(r);
119
+ c.push(r);
118
120
  }
119
- return { keywordTokens: t, searchTokens: i };
121
+ return { keywordTokens: t, searchTokens: c };
120
122
  }
121
123
  _applyKeywords(e, t) {
122
124
  if (!this._keywords)
123
125
  return { collection: e, matchedKeywords: [] };
124
- let i = e;
125
- const r = [], h = /* @__PURE__ */ new Map();
126
- for (const [s, n] of t)
127
- h.set(s, n);
126
+ let c = e;
127
+ const r = [], a = /* @__PURE__ */ new Map();
128
+ for (const [s, i] of t)
129
+ a.set(s, i);
128
130
  if (this._keywords.mode === "and")
129
131
  for (const s of this._keywords.definitions) {
130
- const n = h.get(s.name);
131
- if (n) {
132
- const c = s.handler;
133
- if (i = c(i, n), r.push({ name: s.name, terms: n }), !Array.isArray(i))
134
- throw new f(
135
- `Keyword handler "${s.name}" must return an array`,
136
- w.INVALID_HANDLER_RETURN
132
+ const i = a.get(s.name);
133
+ if (i) {
134
+ const l = s.handler, h = l(c, i);
135
+ if (!Array.isArray(h)) {
136
+ console.error(
137
+ `[Spotr] Keyword handler "${s.name}" must return an array, received ${typeof h}. Skipping this filter.`
137
138
  );
139
+ continue;
140
+ }
141
+ c = h, r.push({ name: s.name, terms: i });
138
142
  }
139
143
  }
140
144
  else {
141
- const s = [], n = /* @__PURE__ */ new Set();
142
- for (const c of this._keywords.definitions) {
143
- const l = h.get(c.name);
144
- if (l) {
145
- const _ = c.handler, o = _(this._collection, l);
146
- if (r.push({ name: c.name, terms: l }), !Array.isArray(o))
147
- throw new f(
148
- `Keyword handler "${c.name}" must return an array`,
149
- w.INVALID_HANDLER_RETURN
145
+ const s = [], i = /* @__PURE__ */ new Set();
146
+ for (const l of this._keywords.definitions) {
147
+ const h = a.get(l.name);
148
+ if (h) {
149
+ const g = l.handler, n = g(this._collection, h);
150
+ if (!Array.isArray(n)) {
151
+ console.error(
152
+ `[Spotr] Keyword handler "${l.name}" must return an array, received ${typeof n}. Skipping this filter.`
150
153
  );
151
- for (const d of o)
152
- n.has(d) || (n.add(d), s.push(d));
154
+ continue;
155
+ }
156
+ r.push({ name: l.name, terms: h });
157
+ for (const d of n)
158
+ i.has(d) || (i.add(d), s.push(d));
153
159
  }
154
160
  }
155
- i = s;
161
+ c = s;
156
162
  }
157
- return { collection: i, matchedKeywords: r };
163
+ return { collection: c, matchedKeywords: r };
158
164
  }
159
165
  }
160
166
  export {
161
- I as Spotr
167
+ x as Spotr
162
168
  };
163
169
  //# sourceMappingURL=Spotr.js.map
package/dist/Spotr.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Spotr.js","sources":["../src/Spotr.ts"],"sourcesContent":["import { SpotrError, ErrorCodes } from './errors';\nimport { normalizeFieldConfig, scoreItem } from './fuzzy';\nimport { tokenize, validateOptions, validateKeywords } from './utils';\nimport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n NormalizedFieldConfig,\n NormalizedKeywordsConfig,\n KeywordDefinition,\n} from './types';\n\nexport class Spotr<T extends object> {\n private _collection: T[];\n private _fields: NormalizedFieldConfig[];\n private _keywords: NormalizedKeywordsConfig | null;\n private _keywordTriggerMap: Map<string, KeywordDefinition<unknown>>;\n private _threshold: number;\n private _limit: number;\n private _debounce: number;\n private _caseSensitive: boolean;\n private _minMatchCharLength: number;\n private _debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private _optionsSnapshot: SpotrOptions<T>;\n\n constructor(options: SpotrOptions<T>) {\n this._optionsSnapshot = options;\n const validated = validateOptions(options);\n this._collection = validated.collection;\n this._threshold = validated.threshold;\n this._limit = validated.limit;\n this._debounce = validated.debounce;\n this._caseSensitive = validated.caseSensitive;\n this._minMatchCharLength = validated.minMatchCharLength;\n\n this._fields = normalizeFieldConfig(options.fields, this._threshold);\n\n this._keywords = options.keywords ? validateKeywords(options.keywords) : null;\n this._keywordTriggerMap = this._buildKeywordTriggerMap();\n }\n\n private _buildKeywordTriggerMap(): Map<string, KeywordDefinition<unknown>> {\n const map = new Map<string, KeywordDefinition<unknown>>();\n if (!this._keywords) return map;\n\n for (const def of this._keywords.definitions) {\n const triggers = Array.isArray(def.triggers) ? def.triggers : [def.triggers];\n for (const trigger of triggers) {\n map.set(trigger.toLowerCase(), def);\n }\n }\n return map;\n }\n\n get collection(): T[] {\n return this._collection;\n }\n\n get options(): SpotrOptions<T> {\n return this._optionsSnapshot;\n }\n\n setCollection(collection: T[] | Set<T>): void {\n this._collection = collection instanceof Set ? Array.from(collection) : collection;\n }\n\n query(search: string): SpotrResult<T> {\n const tokens = tokenize(search);\n \n if (tokens.length === 0) {\n return {\n results: [],\n matchedKeywords: [],\n tokens: [],\n warnings: [],\n };\n }\n\n const { keywordTokens, searchTokens } = this._extractKeywords(tokens);\n\n let filteredCollection = this._collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n if (keywordTokens.size > 0) {\n const result = this._applyKeywords(filteredCollection, keywordTokens);\n filteredCollection = result.collection;\n matchedKeywords.push(...result.matchedKeywords);\n }\n\n if (searchTokens.length === 0) {\n const allWarnings: string[] = [];\n const results: ScoredResult<T>[] = filteredCollection.map((item) => {\n const { score, warnings } = scoreItem(\n item,\n [],\n this._fields,\n this._caseSensitive\n );\n allWarnings.push(...warnings);\n return { item, score };\n });\n\n return {\n results,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n const validTokens = this._minMatchCharLength > 1\n ? searchTokens.filter((t) => t.length >= this._minMatchCharLength)\n : searchTokens;\n\n if (validTokens.length === 0) {\n return {\n results: [],\n matchedKeywords,\n tokens: searchTokens,\n warnings: [],\n };\n }\n\n const allWarnings: string[] = [];\n const scoredResults: ScoredResult<T>[] = [];\n\n for (const item of filteredCollection) {\n const { score, warnings } = scoreItem(\n item,\n validTokens,\n this._fields,\n this._caseSensitive\n );\n allWarnings.push(...warnings);\n\n if (score > 0) {\n scoredResults.push({ item, score });\n }\n }\n\n scoredResults.sort((a, b) => b.score - a.score);\n\n const limitedResults = this._limit < Infinity\n ? scoredResults.slice(0, this._limit)\n : scoredResults;\n\n return {\n results: limitedResults,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n queryAsync(search: string): Promise<SpotrResult<T>> {\n return new Promise((resolve) => {\n if (this._debounce > 0) {\n if (this._debounceTimer) {\n clearTimeout(this._debounceTimer);\n }\n this._debounceTimer = setTimeout(() => {\n resolve(this.query(search));\n }, this._debounce);\n } else {\n resolve(this.query(search));\n }\n });\n }\n\n private _extractKeywords(tokens: string[]): {\n keywordTokens: Map<string, string[]>;\n searchTokens: string[];\n } {\n const keywordTokens = new Map<string, string[]>();\n const searchTokens: string[] = [];\n\n for (const token of tokens) {\n const normalizedToken = this._caseSensitive ? token : token.toLowerCase();\n const keywordDef = this._keywordTriggerMap.get(normalizedToken);\n\n if (keywordDef) {\n const existing = keywordTokens.get(keywordDef.name) || [];\n existing.push(token);\n keywordTokens.set(keywordDef.name, existing);\n } else {\n searchTokens.push(token);\n }\n }\n\n return { keywordTokens, searchTokens };\n }\n\n private _applyKeywords(\n collection: T[],\n keywordTokens: Map<string, string[]>\n ): { collection: T[]; matchedKeywords: MatchedKeyword[] } {\n if (!this._keywords) {\n return { collection, matchedKeywords: [] };\n }\n\n let result = collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n const keywordNameToTerms = new Map<string, string[]>();\n for (const [name, terms] of keywordTokens) {\n keywordNameToTerms.set(name, terms);\n }\n\n if (this._keywords.mode === 'and') {\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (collection: T[], matchedTerms: string[]) => T[];\n result = handler(result, terms);\n matchedKeywords.push({ name: def.name, terms });\n\n if (!Array.isArray(result)) {\n throw new SpotrError(\n `Keyword handler \"${def.name}\" must return an array`,\n ErrorCodes.INVALID_HANDLER_RETURN\n );\n }\n }\n }\n } else {\n const mergedResults: T[] = [];\n const processedItems = new Set<T>();\n\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (collection: T[], matchedTerms: string[]) => T[];\n const keywordResults = handler(this._collection, terms);\n matchedKeywords.push({ name: def.name, terms });\n\n if (!Array.isArray(keywordResults)) {\n throw new SpotrError(\n `Keyword handler \"${def.name}\" must return an array`,\n ErrorCodes.INVALID_HANDLER_RETURN\n );\n }\n\n for (const item of keywordResults) {\n if (!processedItems.has(item)) {\n processedItems.add(item);\n mergedResults.push(item);\n }\n }\n }\n }\n result = mergedResults;\n }\n\n return { collection: result, matchedKeywords };\n }\n}\n"],"names":["Spotr","options","__publicField","validated","validateOptions","normalizeFieldConfig","validateKeywords","map","def","triggers","trigger","collection","search","tokens","tokenize","keywordTokens","searchTokens","filteredCollection","matchedKeywords","result","allWarnings","item","score","warnings","scoreItem","validTokens","t","scoredResults","a","b","resolve","token","normalizedToken","keywordDef","existing","keywordNameToTerms","name","terms","handler","SpotrError","ErrorCodes","mergedResults","processedItems","keywordResults"],"mappings":";;;;;;;AAaO,MAAMA,EAAwB;AAAA,EAanC,YAAYC,GAA0B;AAZ9B,IAAAC,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA,wBAAuD;AACvD,IAAAA,EAAA;AAGN,SAAK,mBAAmBD;AACxB,UAAME,IAAYC,EAAgBH,CAAO;AACzC,SAAK,cAAcE,EAAU,YAC7B,KAAK,aAAaA,EAAU,WAC5B,KAAK,SAASA,EAAU,OACxB,KAAK,YAAYA,EAAU,UAC3B,KAAK,iBAAiBA,EAAU,eAChC,KAAK,sBAAsBA,EAAU,oBAErC,KAAK,UAAUE,EAAqBJ,EAAQ,QAAQ,KAAK,UAAU,GAEnE,KAAK,YAAYA,EAAQ,WAAWK,EAAiBL,EAAQ,QAAQ,IAAI,MACzE,KAAK,qBAAqB,KAAK,wBAAA;AAAA,EACjC;AAAA,EAEQ,0BAAmE;AACzE,UAAMM,wBAAU,IAAA;AAChB,QAAI,CAAC,KAAK,UAAW,QAAOA;AAE5B,eAAWC,KAAO,KAAK,UAAU,aAAa;AAC5C,YAAMC,IAAW,MAAM,QAAQD,EAAI,QAAQ,IAAIA,EAAI,WAAW,CAACA,EAAI,QAAQ;AAC3E,iBAAWE,KAAWD;AACpB,QAAAF,EAAI,IAAIG,EAAQ,YAAA,GAAeF,CAAG;AAAA,IAEtC;AACA,WAAOD;AAAA,EACT;AAAA,EAEA,IAAI,aAAkB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,UAA2B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,cAAcI,GAAgC;AAC5C,SAAK,cAAcA,aAAsB,MAAM,MAAM,KAAKA,CAAU,IAAIA;AAAA,EAC1E;AAAA,EAEA,MAAMC,GAAgC;AACpC,UAAMC,IAASC,EAASF,CAAM;AAE9B,QAAIC,EAAO,WAAW;AACpB,aAAO;AAAA,QACL,SAAS,CAAA;AAAA,QACT,iBAAiB,CAAA;AAAA,QACjB,QAAQ,CAAA;AAAA,QACR,UAAU,CAAA;AAAA,MAAC;AAIf,UAAM,EAAE,eAAAE,GAAe,cAAAC,EAAA,IAAiB,KAAK,iBAAiBH,CAAM;AAEpE,QAAII,IAAqB,KAAK;AAC9B,UAAMC,IAAoC,CAAA;AAE1C,QAAIH,EAAc,OAAO,GAAG;AAC1B,YAAMI,IAAS,KAAK,eAAeF,GAAoBF,CAAa;AACpE,MAAAE,IAAqBE,EAAO,YAC5BD,EAAgB,KAAK,GAAGC,EAAO,eAAe;AAAA,IAChD;AAEA,QAAIH,EAAa,WAAW,GAAG;AAC7B,YAAMI,IAAwB,CAAA;AAY9B,aAAO;AAAA,QACL,SAZiCH,EAAmB,IAAI,CAACI,MAAS;AAClE,gBAAM,EAAE,OAAAC,GAAO,UAAAC,EAAA,IAAaC;AAAA,YAC1BH;AAAA,YACA,CAAA;AAAA,YACA,KAAK;AAAA,YACL,KAAK;AAAA,UAAA;AAEPD,iBAAAA,EAAY,KAAK,GAAGG,CAAQ,GACrB,EAAE,MAAAF,GAAM,OAAAC,EAAA;AAAA,QACjB,CAAC;AAAA,QAIC,iBAAAJ;AAAA,QACA,QAAQF;AAAA,QACR,UAAU,CAAC,GAAG,IAAI,IAAII,CAAW,CAAC;AAAA,MAAA;AAAA,IAEtC;AAEA,UAAMK,IAAc,KAAK,sBAAsB,IAC3CT,EAAa,OAAO,CAACU,MAAMA,EAAE,UAAU,KAAK,mBAAmB,IAC/DV;AAEJ,QAAIS,EAAY,WAAW;AACzB,aAAO;AAAA,QACL,SAAS,CAAA;AAAA,QACT,iBAAAP;AAAA,QACA,QAAQF;AAAA,QACR,UAAU,CAAA;AAAA,MAAC;AAIf,UAAMI,IAAwB,CAAA,GACxBO,IAAmC,CAAA;AAEzC,eAAWN,KAAQJ,GAAoB;AACrC,YAAM,EAAE,OAAAK,GAAO,UAAAC,EAAA,IAAaC;AAAA,QAC1BH;AAAA,QACAI;AAAA,QACA,KAAK;AAAA,QACL,KAAK;AAAA,MAAA;AAEP,MAAAL,EAAY,KAAK,GAAGG,CAAQ,GAExBD,IAAQ,KACVK,EAAc,KAAK,EAAE,MAAAN,GAAM,OAAAC,EAAA,CAAO;AAAA,IAEtC;AAEA,WAAAK,EAAc,KAAK,CAACC,GAAGC,MAAMA,EAAE,QAAQD,EAAE,KAAK,GAMvC;AAAA,MACL,SALqB,KAAK,SAAS,QACjCD,EAAc,MAAM,GAAG,KAAK,MAAM,IAClCA;AAAA,MAIF,iBAAAT;AAAA,MACA,QAAQF;AAAA,MACR,UAAU,CAAC,GAAG,IAAI,IAAII,CAAW,CAAC;AAAA,IAAA;AAAA,EAEtC;AAAA,EAEA,WAAWR,GAAyC;AAClD,WAAO,IAAI,QAAQ,CAACkB,MAAY;AAC9B,MAAI,KAAK,YAAY,KACf,KAAK,kBACP,aAAa,KAAK,cAAc,GAElC,KAAK,iBAAiB,WAAW,MAAM;AACrC,QAAAA,EAAQ,KAAK,MAAMlB,CAAM,CAAC;AAAA,MAC5B,GAAG,KAAK,SAAS,KAEjBkB,EAAQ,KAAK,MAAMlB,CAAM,CAAC;AAAA,IAE9B,CAAC;AAAA,EACH;AAAA,EAEQ,iBAAiBC,GAGvB;AACA,UAAME,wBAAoB,IAAA,GACpBC,IAAyB,CAAA;AAE/B,eAAWe,KAASlB,GAAQ;AAC1B,YAAMmB,IAAkB,KAAK,iBAAiBD,IAAQA,EAAM,YAAA,GACtDE,IAAa,KAAK,mBAAmB,IAAID,CAAe;AAE9D,UAAIC,GAAY;AACd,cAAMC,IAAWnB,EAAc,IAAIkB,EAAW,IAAI,KAAK,CAAA;AACvD,QAAAC,EAAS,KAAKH,CAAK,GACnBhB,EAAc,IAAIkB,EAAW,MAAMC,CAAQ;AAAA,MAC7C;AACE,QAAAlB,EAAa,KAAKe,CAAK;AAAA,IAE3B;AAEA,WAAO,EAAE,eAAAhB,GAAe,cAAAC,EAAA;AAAA,EAC1B;AAAA,EAEQ,eACNL,GACAI,GACwD;AACxD,QAAI,CAAC,KAAK;AACR,aAAO,EAAE,YAAAJ,GAAY,iBAAiB,GAAC;AAGzC,QAAIQ,IAASR;AACb,UAAMO,IAAoC,CAAA,GAEpCiB,wBAAyB,IAAA;AAC/B,eAAW,CAACC,GAAMC,CAAK,KAAKtB;AAC1B,MAAAoB,EAAmB,IAAIC,GAAMC,CAAK;AAGpC,QAAI,KAAK,UAAU,SAAS;AAC1B,iBAAW7B,KAAO,KAAK,UAAU,aAAa;AAC5C,cAAM6B,IAAQF,EAAmB,IAAI3B,EAAI,IAAI;AAC7C,YAAI6B,GAAO;AACT,gBAAMC,IAAU9B,EAAI;AAIpB,cAHAW,IAASmB,EAAQnB,GAAQkB,CAAK,GAC9BnB,EAAgB,KAAK,EAAE,MAAMV,EAAI,MAAM,OAAA6B,GAAO,GAE1C,CAAC,MAAM,QAAQlB,CAAM;AACvB,kBAAM,IAAIoB;AAAA,cACR,oBAAoB/B,EAAI,IAAI;AAAA,cAC5BgC,EAAW;AAAA,YAAA;AAAA,QAGjB;AAAA,MACF;AAAA,SACK;AACL,YAAMC,IAAqB,CAAA,GACrBC,wBAAqB,IAAA;AAE3B,iBAAWlC,KAAO,KAAK,UAAU,aAAa;AAC5C,cAAM6B,IAAQF,EAAmB,IAAI3B,EAAI,IAAI;AAC7C,YAAI6B,GAAO;AACT,gBAAMC,IAAU9B,EAAI,SACdmC,IAAiBL,EAAQ,KAAK,aAAaD,CAAK;AAGtD,cAFAnB,EAAgB,KAAK,EAAE,MAAMV,EAAI,MAAM,OAAA6B,GAAO,GAE1C,CAAC,MAAM,QAAQM,CAAc;AAC/B,kBAAM,IAAIJ;AAAA,cACR,oBAAoB/B,EAAI,IAAI;AAAA,cAC5BgC,EAAW;AAAA,YAAA;AAIf,qBAAWnB,KAAQsB;AACjB,YAAKD,EAAe,IAAIrB,CAAI,MAC1BqB,EAAe,IAAIrB,CAAI,GACvBoB,EAAc,KAAKpB,CAAI;AAAA,QAG7B;AAAA,MACF;AACA,MAAAF,IAASsB;AAAA,IACX;AAEA,WAAO,EAAE,YAAYtB,GAAQ,iBAAAD,EAAA;AAAA,EAC/B;AACF;"}
1
+ {"version":3,"file":"Spotr.js","sources":["../src/Spotr.ts"],"sourcesContent":["import { normalizeFieldConfig, scoreItem } from './fuzzy';\nimport {\n tokenize,\n validateOptions,\n validateKeywords,\n validateCollection,\n} from './utils';\nimport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n NormalizedFieldConfig,\n NormalizedKeywordsConfig,\n KeywordDefinition,\n} from './types';\n\nexport class Spotr<T extends object> {\n private _collection: T[];\n private _fields: NormalizedFieldConfig[];\n private _keywords: NormalizedKeywordsConfig | null;\n private _keywordTriggerMap: Map<string, KeywordDefinition<unknown>>;\n private _threshold: number;\n private _limit: number;\n private _debounce: number;\n private _caseSensitive: boolean;\n private _minMatchCharLength: number;\n private _maxStringLength: number;\n private _debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private _optionsSnapshot: SpotrOptions<T>;\n\n constructor(options: SpotrOptions<T>) {\n this._optionsSnapshot = options;\n const validated = validateOptions(options);\n this._collection = validated.collection;\n this._threshold = validated.threshold;\n this._limit = validated.limit;\n this._debounce = validated.debounce;\n this._caseSensitive = validated.caseSensitive;\n this._minMatchCharLength = validated.minMatchCharLength;\n this._maxStringLength = validated.maxStringLength;\n\n this._fields = normalizeFieldConfig(options.fields, this._threshold);\n\n this._keywords = options.keywords\n ? validateKeywords(options.keywords)\n : null;\n this._keywordTriggerMap = this._buildKeywordTriggerMap();\n }\n\n private _buildKeywordTriggerMap(): Map<string, KeywordDefinition<unknown>> {\n const map = new Map<string, KeywordDefinition<unknown>>();\n if (!this._keywords) return map;\n\n for (const def of this._keywords.definitions) {\n const triggers = Array.isArray(def.triggers)\n ? def.triggers\n : [def.triggers];\n for (const trigger of triggers) {\n map.set(trigger.toLowerCase(), def);\n }\n }\n return map;\n }\n\n get collection(): T[] {\n return this._collection;\n }\n\n get options(): SpotrOptions<T> {\n return this._optionsSnapshot;\n }\n\n setCollection(collection: T[] | Set<T>): void {\n this._collection = validateCollection(collection as unknown) as T[];\n }\n\n query(search: string): SpotrResult<T> {\n const tokens = tokenize(search);\n\n if (tokens.length === 0) {\n return {\n results: [],\n matchedKeywords: [],\n tokens: [],\n warnings: [],\n };\n }\n\n const { keywordTokens, searchTokens } = this._extractKeywords(tokens);\n\n let filteredCollection = this._collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n if (keywordTokens.size > 0) {\n const result = this._applyKeywords(filteredCollection, keywordTokens);\n filteredCollection = result.collection;\n matchedKeywords.push(...result.matchedKeywords);\n }\n\n if (searchTokens.length === 0) {\n const allWarnings: string[] = [];\n const results: ScoredResult<T>[] = filteredCollection.map((item) => {\n const { score, warnings } = scoreItem(\n item,\n [],\n this._fields,\n this._caseSensitive,\n this._maxStringLength\n );\n allWarnings.push(...warnings);\n return { item, score };\n });\n\n return {\n results,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n const validTokens =\n this._minMatchCharLength > 1\n ? searchTokens.filter((t) => t.length >= this._minMatchCharLength)\n : searchTokens;\n\n if (validTokens.length === 0) {\n return {\n results: [],\n matchedKeywords,\n tokens: searchTokens,\n warnings: [],\n };\n }\n\n const allWarnings: string[] = [];\n const scoredResults: ScoredResult<T>[] = [];\n\n for (const item of filteredCollection) {\n const { score, warnings } = scoreItem(\n item,\n validTokens,\n this._fields,\n this._caseSensitive,\n this._maxStringLength\n );\n allWarnings.push(...warnings);\n\n if (score > 0) {\n scoredResults.push({ item, score });\n }\n }\n\n scoredResults.sort((a, b) => b.score - a.score);\n\n const limitedResults =\n this._limit < Infinity\n ? scoredResults.slice(0, this._limit)\n : scoredResults;\n\n return {\n results: limitedResults,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n queryAsync(search: string): Promise<SpotrResult<T>> {\n return new Promise((resolve) => {\n if (this._debounce > 0) {\n if (this._debounceTimer) {\n clearTimeout(this._debounceTimer);\n }\n this._debounceTimer = setTimeout(() => {\n resolve(this.query(search));\n }, this._debounce);\n } else {\n resolve(this.query(search));\n }\n });\n }\n\n private _extractKeywords(tokens: string[]): {\n keywordTokens: Map<string, string[]>;\n searchTokens: string[];\n } {\n const keywordTokens = new Map<string, string[]>();\n const searchTokens: string[] = [];\n\n for (const token of tokens) {\n const normalizedToken = this._caseSensitive ? token : token.toLowerCase();\n const keywordDef = this._keywordTriggerMap.get(normalizedToken);\n\n if (keywordDef) {\n const existing = keywordTokens.get(keywordDef.name) || [];\n existing.push(token);\n keywordTokens.set(keywordDef.name, existing);\n } else {\n searchTokens.push(token);\n }\n }\n\n return { keywordTokens, searchTokens };\n }\n\n private _applyKeywords(\n collection: T[],\n keywordTokens: Map<string, string[]>\n ): { collection: T[]; matchedKeywords: MatchedKeyword[] } {\n if (!this._keywords) {\n return { collection, matchedKeywords: [] };\n }\n\n let result = collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n const keywordNameToTerms = new Map<string, string[]>();\n for (const [name, terms] of keywordTokens) {\n keywordNameToTerms.set(name, terms);\n }\n\n if (this._keywords.mode === 'and') {\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (\n collection: T[],\n matchedTerms: string[]\n ) => T[];\n const handlerResult = handler(result, terms);\n\n if (!Array.isArray(handlerResult)) {\n console.error(\n `[Spotr] Keyword handler \"${def.name}\" must return an array, received ${typeof handlerResult}. Skipping this filter.`\n );\n // Skip this keyword - keep current collection for this step\n continue;\n }\n\n result = handlerResult;\n matchedKeywords.push({ name: def.name, terms });\n }\n }\n } else {\n const mergedResults: T[] = [];\n const processedItems = new Set<T>();\n\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (\n collection: T[],\n matchedTerms: string[]\n ) => T[];\n const keywordResults = handler(this._collection, terms);\n\n if (!Array.isArray(keywordResults)) {\n console.error(\n `[Spotr] Keyword handler \"${def.name}\" must return an array, received ${typeof keywordResults}. Skipping this filter.`\n );\n // Skip this keyword - don't add its results\n continue;\n }\n\n matchedKeywords.push({ name: def.name, terms });\n\n for (const item of keywordResults) {\n if (!processedItems.has(item)) {\n processedItems.add(item);\n mergedResults.push(item);\n }\n }\n }\n }\n result = mergedResults;\n }\n\n return { collection: result, matchedKeywords };\n }\n}\n"],"names":["Spotr","options","__publicField","validated","validateOptions","normalizeFieldConfig","validateKeywords","map","def","triggers","trigger","collection","validateCollection","search","tokens","tokenize","keywordTokens","searchTokens","filteredCollection","matchedKeywords","result","allWarnings","item","score","warnings","scoreItem","validTokens","t","scoredResults","a","b","resolve","token","normalizedToken","keywordDef","existing","keywordNameToTerms","name","terms","handler","handlerResult","mergedResults","processedItems","keywordResults"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ {
2
+ "gzipSize": 3515,
3
+ "formatted": "3.4 kB"
4
+ }
package/dist/errors.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class E extends Error{constructor(I,r){super(I),this.code=r,this.name="SpotrError"}}const L={INVALID_COLLECTION:"INVALID_COLLECTION",INVALID_FIELD_CONFIG:"INVALID_FIELD_CONFIG",INVALID_FIELD_WEIGHT:"INVALID_FIELD_WEIGHT",INVALID_KEYWORD:"INVALID_KEYWORD",INVALID_HANDLER_RETURN:"INVALID_HANDLER_RETURN"};exports.ErrorCodes=L;exports.SpotrError=E;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class L extends Error{constructor(I,r){super(I),this.code=r,this.name="SpotrError"}}const N={INVALID_COLLECTION:"INVALID_COLLECTION",INVALID_FIELD_CONFIG:"INVALID_FIELD_CONFIG",INVALID_FIELD_WEIGHT:"INVALID_FIELD_WEIGHT",INVALID_KEYWORD:"INVALID_KEYWORD",INVALID_HANDLER_RETURN:"INVALID_HANDLER_RETURN",INVALID_MAX_STRING_LENGTH:"INVALID_MAX_STRING_LENGTH"};exports.ErrorCodes=N;exports.SpotrError=L;
2
2
  //# sourceMappingURL=errors.cjs.map