spotr 1.0.0-alpha.2 → 1.0.0-alpha.4

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,10 +2,12 @@
2
2
 
3
3
  # Spotr
4
4
 
5
- A powerful and minimal client-side fuzzy search library with zero dependencies.
5
+ A powerful fuzzy search library for client-side collections in TypeScript.
6
6
 
7
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
8
 
9
+ **[Homepage](https://andymerskin.github.io/spotr/)** • **[Examples](https://andymerskin.github.io/spotr/examples/)**
10
+
9
11
  ## Features
10
12
 
11
13
  - **Zero Dependencies** - Custom Levenshtein implementation, no external deps
package/dist/Spotr.cjs CHANGED
@@ -1,2 +1,2 @@
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;
1
+ "use strict";var k=Object.defineProperty;var S=(u,e,t)=>e in u?k(u,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):u[e]=t;var o=(u,e,t)=>S(u,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("./utils/validate.cjs"),y=require("./fuzzy/scorer.cjs"),T=require("./utils/tokenize.cjs");class L{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=f.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=y.normalizeFieldConfig(e.fields,this._threshold),this._keywords=e.keywords?f.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 n of c)e.set(n.toLowerCase(),t)}return e}get collection(){return this._collection}get options(){return this._optionsSnapshot}setCollection(e){this._collection=f.validateCollection(e)}query(e){const t=T.tokenize(e);if(t.length===0)return{results:[],matchedKeywords:[],tokens:[],warnings:[]};const{keywordTokens:c,searchTokens:n}=this._extractKeywords(t);let h=this._collection;const s=[];if(c.size>0){const r=this._applyKeywords(h,c);h=r.collection,s.push(...r.matchedKeywords)}if(n.length===0){const r=[];return{results:h.map(_=>{const{score:w,warnings:p}=y.scoreItem(_,[],this._fields,this._caseSensitive,this._maxStringLength);return r.push(...p),{item:_,score:w}}),matchedKeywords:s,tokens:n,warnings:[...new Set(r)]}}const i=this._minMatchCharLength>1?n.filter(r=>r.length>=this._minMatchCharLength):n;if(i.length===0)return{results:[],matchedKeywords:s,tokens:n,warnings:[]};const l=[],d=[];for(const r of i){let a=r;a.length>this._maxStringLength&&(l.push(`Query string truncated from ${a.length} to ${this._maxStringLength} characters for performance`),a=a.slice(0,this._maxStringLength)),d.push(this._caseSensitive?a:a.toLowerCase())}const g=[];for(const r of h){const{score:a,warnings:_}=y.scoreItem(r,d,this._fields,this._caseSensitive,this._maxStringLength);l.push(..._),a>0&&g.push({item:r,score:a})}return g.sort((r,a)=>a.score-r.score),{results:this._limit<1/0?g.slice(0,this._limit):g,matchedKeywords:s,tokens:n,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 n of e){const h=this._caseSensitive?n:n.toLowerCase(),s=this._keywordTriggerMap.get(h);if(s){const i=t.get(s.name)||[];i.push(n),t.set(s.name,i)}else c.push(n)}return{keywordTokens:t,searchTokens:c}}_applyKeywords(e,t){if(!this._keywords)return{collection:e,matchedKeywords:[]};let c=e;const n=[],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,d=l(c,i);if(!Array.isArray(d)){console.error(`[Spotr] Keyword handler "${s.name}" must return an array, received ${typeof d}. Skipping this filter.`);continue}c=d,n.push({name:s.name,terms:i})}}else{const s=[],i=new Set;for(const l of this._keywords.definitions){const d=h.get(l.name);if(d){const g=l.handler,m=g(this._collection,d);if(!Array.isArray(m)){console.error(`[Spotr] Keyword handler "${l.name}" must return an array, received ${typeof m}. Skipping this filter.`);continue}n.push({name:l.name,terms:d});for(const r of m)i.has(r)||(i.add(r),s.push(r))}}c=s}return{collection:c,matchedKeywords:n}}}exports.Spotr=L;
2
2
  //# sourceMappingURL=Spotr.cjs.map
@@ -1 +1 @@
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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"}
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 [], // no search tokens to normalize\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 // Pre-normalize tokens once (truncate, case) to avoid redundant work in the hot path\n const allWarnings: string[] = [];\n const normalizedTokens: string[] = [];\n for (const token of validTokens) {\n let t = token;\n if (t.length > this._maxStringLength) {\n allWarnings.push(\n `Query string truncated from ${t.length} to ${this._maxStringLength} characters for performance`\n );\n t = t.slice(0, this._maxStringLength);\n }\n normalizedTokens.push(this._caseSensitive ? t : t.toLowerCase());\n }\n\n const scoredResults: ScoredResult<T>[] = [];\n\n for (const item of filteredCollection) {\n const { score, warnings } = scoreItem(\n item,\n normalizedTokens,\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","normalizedTokens","token","scoredResults","a","b","resolve","normalizedToken","keywordDef","existing","keywordNameToTerms","name","terms","handler","handlerResult","mergedResults","processedItems","keywordResults"],"mappings":"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"}
package/dist/Spotr.js CHANGED
@@ -1,10 +1,10 @@
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 {
1
+ var p = Object.defineProperty;
2
+ var k = (m, e, t) => e in m ? p(m, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : m[e] = t;
3
+ var o = (m, e, t) => k(m, typeof e != "symbol" ? e + "" : e, t);
4
+ import { validateOptions as S, validateKeywords as T, validateCollection as L } from "./utils/validate.js";
5
+ import { normalizeFieldConfig as K, scoreItem as f } from "./fuzzy/scorer.js";
6
+ import { tokenize as v } from "./utils/tokenize.js";
7
+ class A {
8
8
  constructor(e) {
9
9
  o(this, "_collection");
10
10
  o(this, "_fields");
@@ -19,16 +19,16 @@ class x {
19
19
  o(this, "_debounceTimer", null);
20
20
  o(this, "_optionsSnapshot");
21
21
  this._optionsSnapshot = e;
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();
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._maxStringLength = t.maxStringLength, this._fields = K(e.fields, this._threshold), this._keywords = e.keywords ? T(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 c = Array.isArray(t.triggers) ? t.triggers : [t.triggers];
30
- for (const r of c)
31
- e.set(r.toLowerCase(), t);
29
+ const h = Array.isArray(t.triggers) ? t.triggers : [t.triggers];
30
+ for (const n of h)
31
+ e.set(n.toLowerCase(), t);
32
32
  }
33
33
  return e;
34
34
  }
@@ -39,10 +39,10 @@ class x {
39
39
  return this._optionsSnapshot;
40
40
  }
41
41
  setCollection(e) {
42
- this._collection = T(e);
42
+ this._collection = L(e);
43
43
  }
44
44
  query(e) {
45
- const t = M(e);
45
+ const t = v(e);
46
46
  if (t.length === 0)
47
47
  return {
48
48
  results: [],
@@ -50,54 +50,62 @@ class x {
50
50
  tokens: [],
51
51
  warnings: []
52
52
  };
53
- const { keywordTokens: c, searchTokens: r } = this._extractKeywords(t);
53
+ const { keywordTokens: h, searchTokens: n } = this._extractKeywords(t);
54
54
  let a = this._collection;
55
55
  const s = [];
56
- if (c.size > 0) {
57
- const n = this._applyKeywords(a, c);
58
- a = n.collection, s.push(...n.matchedKeywords);
56
+ if (h.size > 0) {
57
+ const r = this._applyKeywords(a, h);
58
+ a = r.collection, s.push(...r.matchedKeywords);
59
59
  }
60
- if (r.length === 0) {
61
- const n = [];
60
+ if (n.length === 0) {
61
+ const r = [];
62
62
  return {
63
- results: a.map((u) => {
64
- const { score: f, warnings: y } = _(
65
- u,
63
+ results: a.map((_) => {
64
+ const { score: y, warnings: w } = f(
65
+ _,
66
66
  [],
67
+ // no search tokens to normalize
67
68
  this._fields,
68
69
  this._caseSensitive,
69
70
  this._maxStringLength
70
71
  );
71
- return n.push(...y), { item: u, score: f };
72
+ return r.push(...w), { item: _, score: y };
72
73
  }),
73
74
  matchedKeywords: s,
74
- tokens: r,
75
- warnings: [...new Set(n)]
75
+ tokens: n,
76
+ warnings: [...new Set(r)]
76
77
  };
77
78
  }
78
- const i = this._minMatchCharLength > 1 ? r.filter((n) => n.length >= this._minMatchCharLength) : r;
79
+ const i = this._minMatchCharLength > 1 ? n.filter((r) => r.length >= this._minMatchCharLength) : n;
79
80
  if (i.length === 0)
80
81
  return {
81
82
  results: [],
82
83
  matchedKeywords: s,
83
- tokens: r,
84
+ tokens: n,
84
85
  warnings: []
85
86
  };
86
- const l = [], h = [];
87
- for (const n of a) {
88
- const { score: d, warnings: u } = _(
89
- n,
90
- i,
87
+ const l = [], d = [];
88
+ for (const r of i) {
89
+ let c = r;
90
+ c.length > this._maxStringLength && (l.push(
91
+ `Query string truncated from ${c.length} to ${this._maxStringLength} characters for performance`
92
+ ), c = c.slice(0, this._maxStringLength)), d.push(this._caseSensitive ? c : c.toLowerCase());
93
+ }
94
+ const g = [];
95
+ for (const r of a) {
96
+ const { score: c, warnings: _ } = f(
97
+ r,
98
+ d,
91
99
  this._fields,
92
100
  this._caseSensitive,
93
101
  this._maxStringLength
94
102
  );
95
- l.push(...u), d > 0 && h.push({ item: n, score: d });
103
+ l.push(..._), c > 0 && g.push({ item: r, score: c });
96
104
  }
97
- return h.sort((n, d) => d.score - n.score), {
98
- results: this._limit < 1 / 0 ? h.slice(0, this._limit) : h,
105
+ return g.sort((r, c) => c.score - r.score), {
106
+ results: this._limit < 1 / 0 ? g.slice(0, this._limit) : g,
99
107
  matchedKeywords: s,
100
- tokens: r,
108
+ tokens: n,
101
109
  warnings: [...new Set(l)]
102
110
  };
103
111
  }
@@ -109,61 +117,61 @@ class x {
109
117
  });
110
118
  }
111
119
  _extractKeywords(e) {
112
- const t = /* @__PURE__ */ new Map(), c = [];
113
- for (const r of e) {
114
- const a = this._caseSensitive ? r : r.toLowerCase(), s = this._keywordTriggerMap.get(a);
120
+ const t = /* @__PURE__ */ new Map(), h = [];
121
+ for (const n of e) {
122
+ const a = this._caseSensitive ? n : n.toLowerCase(), s = this._keywordTriggerMap.get(a);
115
123
  if (s) {
116
124
  const i = t.get(s.name) || [];
117
- i.push(r), t.set(s.name, i);
125
+ i.push(n), t.set(s.name, i);
118
126
  } else
119
- c.push(r);
127
+ h.push(n);
120
128
  }
121
- return { keywordTokens: t, searchTokens: c };
129
+ return { keywordTokens: t, searchTokens: h };
122
130
  }
123
131
  _applyKeywords(e, t) {
124
132
  if (!this._keywords)
125
133
  return { collection: e, matchedKeywords: [] };
126
- let c = e;
127
- const r = [], a = /* @__PURE__ */ new Map();
134
+ let h = e;
135
+ const n = [], a = /* @__PURE__ */ new Map();
128
136
  for (const [s, i] of t)
129
137
  a.set(s, i);
130
138
  if (this._keywords.mode === "and")
131
139
  for (const s of this._keywords.definitions) {
132
140
  const i = a.get(s.name);
133
141
  if (i) {
134
- const l = s.handler, h = l(c, i);
135
- if (!Array.isArray(h)) {
142
+ const l = s.handler, d = l(h, i);
143
+ if (!Array.isArray(d)) {
136
144
  console.error(
137
- `[Spotr] Keyword handler "${s.name}" must return an array, received ${typeof h}. Skipping this filter.`
145
+ `[Spotr] Keyword handler "${s.name}" must return an array, received ${typeof d}. Skipping this filter.`
138
146
  );
139
147
  continue;
140
148
  }
141
- c = h, r.push({ name: s.name, terms: i });
149
+ h = d, n.push({ name: s.name, terms: i });
142
150
  }
143
151
  }
144
152
  else {
145
153
  const s = [], i = /* @__PURE__ */ new Set();
146
154
  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)) {
155
+ const d = a.get(l.name);
156
+ if (d) {
157
+ const g = l.handler, u = g(this._collection, d);
158
+ if (!Array.isArray(u)) {
151
159
  console.error(
152
- `[Spotr] Keyword handler "${l.name}" must return an array, received ${typeof n}. Skipping this filter.`
160
+ `[Spotr] Keyword handler "${l.name}" must return an array, received ${typeof u}. Skipping this filter.`
153
161
  );
154
162
  continue;
155
163
  }
156
- r.push({ name: l.name, terms: h });
157
- for (const d of n)
158
- i.has(d) || (i.add(d), s.push(d));
164
+ n.push({ name: l.name, terms: d });
165
+ for (const r of u)
166
+ i.has(r) || (i.add(r), s.push(r));
159
167
  }
160
168
  }
161
- c = s;
169
+ h = s;
162
170
  }
163
- return { collection: c, matchedKeywords: r };
171
+ return { collection: h, matchedKeywords: n };
164
172
  }
165
173
  }
166
174
  export {
167
- x as Spotr
175
+ A as Spotr
168
176
  };
169
177
  //# sourceMappingURL=Spotr.js.map
package/dist/Spotr.js.map CHANGED
@@ -1 +1 @@
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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"}
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 [], // no search tokens to normalize\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 // Pre-normalize tokens once (truncate, case) to avoid redundant work in the hot path\n const allWarnings: string[] = [];\n const normalizedTokens: string[] = [];\n for (const token of validTokens) {\n let t = token;\n if (t.length > this._maxStringLength) {\n allWarnings.push(\n `Query string truncated from ${t.length} to ${this._maxStringLength} characters for performance`\n );\n t = t.slice(0, this._maxStringLength);\n }\n normalizedTokens.push(this._caseSensitive ? t : t.toLowerCase());\n }\n\n const scoredResults: ScoredResult<T>[] = [];\n\n for (const item of filteredCollection) {\n const { score, warnings } = scoreItem(\n item,\n normalizedTokens,\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","normalizedTokens","token","scoredResults","a","b","resolve","normalizedToken","keywordDef","existing","keywordNameToTerms","name","terms","handler","handlerResult","mergedResults","processedItems","keywordResults"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  {
2
- "gzipSize": 3515,
3
- "formatted": "3.4 kB"
2
+ "gzipSize": 3592,
3
+ "formatted": "3.5 kB"
4
4
  }
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("../types.cjs");function u(n,o){const r=[];for(let e=0;e<=o.length;e++)r[e]=[e];for(let e=0;e<=n.length;e++)r[0][e]=e;for(let e=1;e<=o.length;e++)for(let t=1;t<=n.length;t++)o.charAt(e-1)===n.charAt(t-1)?r[e][t]=r[e-1][t-1]:r[e][t]=Math.min(r[e-1][t-1]+1,r[e][t-1]+1,r[e-1][t]+1);return r[o.length][n.length]}function m(n,o,r=.3,e=!1,t=d.MAX_STRING_LENGTH){const c=[];let l=n,s=o;l.length>t&&(c.push(`Query string truncated from ${l.length} to ${t} characters for performance`),l=l.slice(0,t)),s.length>t&&(c.push(`Target string truncated from ${s.length} to ${t} characters for performance`),s=s.slice(0,t));const f=e?l:l.toLowerCase(),i=e?s:s.toLowerCase();if(f===i)return{score:1,warnings:c};if(f.length===0||i.length===0)return{score:0,warnings:c};if(i.includes(f))return{score:.9+.1*f.length/i.length,warnings:c};const a=u(f,i),g=Math.max(f.length,i.length),h=1-a/g;return{score:h>=r?h:0,warnings:c}}exports.fuzzyScore=m;exports.levenshteinDistance=u;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function f(n,e){const h=[];for(let t=0;t<=e.length;t++)h[t]=[t];for(let t=0;t<=n.length;t++)h[0][t]=t;for(let t=1;t<=e.length;t++)for(let c=1;c<=n.length;c++)e.charAt(t-1)===n.charAt(c-1)?h[t][c]=h[t-1][c-1]:h[t][c]=Math.min(h[t-1][c-1]+1,h[t][c-1]+1,h[t-1][c]+1);return h[e.length][n.length]}function l(n,e,h=.3){if(n===e)return 1;if(n.length===0||e.length===0)return 0;if(e.includes(n))return .9+.1*n.length/e.length;const t=f(n,e),c=Math.max(n.length,e.length),s=1-t/c;return s>=h?s:0}exports.fuzzyScore=l;exports.levenshteinDistance=f;
2
2
  //# sourceMappingURL=levenshtein.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"levenshtein.cjs","sources":["../../src/fuzzy/levenshtein.ts"],"sourcesContent":["import { MAX_STRING_LENGTH } from '../types';\n\nexport function levenshteinDistance(a: string, b: string): number {\n const matrix: number[][] = [];\n\n for (let i = 0; i <= b.length; i++) {\n matrix[i] = [i];\n }\n for (let j = 0; j <= a.length; j++) {\n matrix[0][j] = j;\n }\n\n for (let i = 1; i <= b.length; i++) {\n for (let j = 1; j <= a.length; j++) {\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\n matrix[i][j] = matrix[i - 1][j - 1];\n } else {\n matrix[i][j] = Math.min(\n matrix[i - 1][j - 1] + 1,\n matrix[i][j - 1] + 1,\n matrix[i - 1][j] + 1\n );\n }\n }\n }\n\n return matrix[b.length][a.length];\n}\n\nexport interface FuzzyScoreResult {\n score: number;\n warnings: string[];\n}\n\nexport function fuzzyScore(\n query: string,\n target: string,\n threshold: number = 0.3,\n caseSensitive: boolean = false,\n maxStringLength: number = MAX_STRING_LENGTH\n): FuzzyScoreResult {\n const warnings: string[] = [];\n let q = query;\n let t = target;\n\n if (q.length > maxStringLength) {\n warnings.push(\n `Query string truncated from ${q.length} to ${maxStringLength} characters for performance`\n );\n q = q.slice(0, maxStringLength);\n }\n\n if (t.length > maxStringLength) {\n warnings.push(\n `Target string truncated from ${t.length} to ${maxStringLength} characters for performance`\n );\n t = t.slice(0, maxStringLength);\n }\n\n const normalizedQ = caseSensitive ? q : q.toLowerCase();\n const normalizedT = caseSensitive ? t : t.toLowerCase();\n\n if (normalizedQ === normalizedT) {\n return { score: 1, warnings };\n }\n if (normalizedQ.length === 0 || normalizedT.length === 0) {\n return { score: 0, warnings };\n }\n\n if (normalizedT.includes(normalizedQ)) {\n return {\n score: 0.9 + (0.1 * normalizedQ.length) / normalizedT.length,\n warnings,\n };\n }\n\n const distance = levenshteinDistance(normalizedQ, normalizedT);\n const maxLen = Math.max(normalizedQ.length, normalizedT.length);\n const score = 1 - distance / maxLen;\n\n return {\n score: score >= threshold ? score : 0,\n warnings,\n };\n}\n"],"names":["levenshteinDistance","a","b","matrix","i","j","fuzzyScore","query","target","threshold","caseSensitive","maxStringLength","MAX_STRING_LENGTH","warnings","q","t","normalizedQ","normalizedT","distance","maxLen","score"],"mappings":"gHAEO,SAASA,EAAoBC,EAAWC,EAAmB,CAChE,MAAMC,EAAqB,CAAA,EAE3B,QAASC,EAAI,EAAGA,GAAKF,EAAE,OAAQE,IAC7BD,EAAOC,CAAC,EAAI,CAACA,CAAC,EAEhB,QAASC,EAAI,EAAGA,GAAKJ,EAAE,OAAQI,IAC7BF,EAAO,CAAC,EAAEE,CAAC,EAAIA,EAGjB,QAASD,EAAI,EAAGA,GAAKF,EAAE,OAAQE,IAC7B,QAASC,EAAI,EAAGA,GAAKJ,EAAE,OAAQI,IACzBH,EAAE,OAAOE,EAAI,CAAC,IAAMH,EAAE,OAAOI,EAAI,CAAC,EACpCF,EAAOC,CAAC,EAAEC,CAAC,EAAIF,EAAOC,EAAI,CAAC,EAAEC,EAAI,CAAC,EAElCF,EAAOC,CAAC,EAAEC,CAAC,EAAI,KAAK,IAClBF,EAAOC,EAAI,CAAC,EAAEC,EAAI,CAAC,EAAI,EACvBF,EAAOC,CAAC,EAAEC,EAAI,CAAC,EAAI,EACnBF,EAAOC,EAAI,CAAC,EAAEC,CAAC,EAAI,CAAA,EAM3B,OAAOF,EAAOD,EAAE,MAAM,EAAED,EAAE,MAAM,CAClC,CAOO,SAASK,EACdC,EACAC,EACAC,EAAoB,GACpBC,EAAyB,GACzBC,EAA0BC,oBACR,CAClB,MAAMC,EAAqB,CAAA,EAC3B,IAAIC,EAAIP,EACJQ,EAAIP,EAEJM,EAAE,OAASH,IACbE,EAAS,KACP,+BAA+BC,EAAE,MAAM,OAAOH,CAAe,6BAAA,EAE/DG,EAAIA,EAAE,MAAM,EAAGH,CAAe,GAG5BI,EAAE,OAASJ,IACbE,EAAS,KACP,gCAAgCE,EAAE,MAAM,OAAOJ,CAAe,6BAAA,EAEhEI,EAAIA,EAAE,MAAM,EAAGJ,CAAe,GAGhC,MAAMK,EAAcN,EAAgBI,EAAIA,EAAE,YAAA,EACpCG,EAAcP,EAAgBK,EAAIA,EAAE,YAAA,EAE1C,GAAIC,IAAgBC,EAClB,MAAO,CAAE,MAAO,EAAG,SAAAJ,CAAA,EAErB,GAAIG,EAAY,SAAW,GAAKC,EAAY,SAAW,EACrD,MAAO,CAAE,MAAO,EAAG,SAAAJ,CAAA,EAGrB,GAAII,EAAY,SAASD,CAAW,EAClC,MAAO,CACL,MAAO,GAAO,GAAMA,EAAY,OAAUC,EAAY,OACtD,SAAAJ,CAAA,EAIJ,MAAMK,EAAWlB,EAAoBgB,EAAaC,CAAW,EACvDE,EAAS,KAAK,IAAIH,EAAY,OAAQC,EAAY,MAAM,EACxDG,EAAQ,EAAIF,EAAWC,EAE7B,MAAO,CACL,MAAOC,GAASX,EAAYW,EAAQ,EACpC,SAAAP,CAAA,CAEJ"}
1
+ {"version":3,"file":"levenshtein.cjs","sources":["../../src/fuzzy/levenshtein.ts"],"sourcesContent":["/**\n * Calculates the Levenshtein distance (edit distance) between two strings.\n *\n * The Levenshtein distance is the minimum number of single-character edits\n * (insertions, deletions, or substitutions) required to transform string `a`\n * into string `b`.\n *\n * Uses dynamic programming with a 2D matrix where matrix[i][j] represents\n * the minimum edit distance between the first i characters of `b` and the\n * first j characters of `a`.\n *\n * @param a - First string\n * @param b - Second string\n * @returns The Levenshtein distance between the two strings\n */\nexport function levenshteinDistance(a: string, b: string): number {\n // Initialize the dynamic programming matrix\n // matrix[i][j] = edit distance between b[0..i-1] and a[0..j-1]\n const matrix: number[][] = [];\n\n // Initialize first column: transforming empty string to b[0..i-1] requires i insertions\n for (let i = 0; i <= b.length; i++) {\n matrix[i] = [i];\n }\n\n // Initialize first row: transforming a[0..j-1] to empty string requires j deletions\n for (let j = 0; j <= a.length; j++) {\n matrix[0][j] = j;\n }\n\n // Fill the matrix using dynamic programming\n // For each position, consider three possible operations:\n // 1. Substitution: matrix[i-1][j-1] + 1 (if characters differ)\n // 2. Insertion: matrix[i-1][j] + 1 (insert character from b)\n // 3. Deletion: matrix[i][j-1] + 1 (delete character from a)\n for (let i = 1; i <= b.length; i++) {\n for (let j = 1; j <= a.length; j++) {\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\n // Characters match: no edit needed, carry forward previous distance\n matrix[i][j] = matrix[i - 1][j - 1];\n } else {\n // Characters differ: take minimum of three edit operations\n matrix[i][j] = Math.min(\n matrix[i - 1][j - 1] + 1, // Substitution: replace a[j-1] with b[i-1]\n matrix[i][j - 1] + 1, // Deletion: delete a[j-1]\n matrix[i - 1][j] + 1 // Insertion: insert b[i-1] into a\n );\n }\n }\n }\n\n // Return the final distance (bottom-right corner of matrix)\n return matrix[b.length][a.length];\n}\n\n/**\n * Calculates a fuzzy similarity score between two pre-normalized strings.\n * Callers must truncate and apply case normalization before calling.\n *\n * The scoring algorithm uses a tiered approach:\n * 1. Exact match: Returns score of 1.0\n * 2. Substring match: Returns score between 0.9-1.0 based on match ratio\n * 3. Levenshtein distance: Returns normalized similarity score (0-1)\n *\n * Scores below the threshold are returned as 0 to filter out poor matches.\n *\n * @param normalizedQuery - Pre-normalized query string (truncated, case-adjusted)\n * @param normalizedTarget - Pre-normalized target string (truncated, case-adjusted)\n * @param threshold - Minimum score threshold. Scores below this return 0\n * @returns Similarity score between 0 and 1\n */\nexport function fuzzyScore(\n normalizedQuery: string,\n normalizedTarget: string,\n threshold: number = 0.3\n): number {\n // Exact match: perfect score\n if (normalizedQuery === normalizedTarget) {\n return 1;\n }\n\n // Empty string: no match possible\n if (normalizedQuery.length === 0 || normalizedTarget.length === 0) {\n return 0;\n }\n\n // Substring match: query is contained within target\n // Score ranges from 0.9 (short query in long target) to 1.0 (query equals target)\n if (normalizedTarget.includes(normalizedQuery)) {\n return 0.9 + (0.1 * normalizedQuery.length) / normalizedTarget.length;\n }\n\n // Calculate Levenshtein distance and convert to similarity score\n const distance = levenshteinDistance(normalizedQuery, normalizedTarget);\n const maxLen = Math.max(normalizedQuery.length, normalizedTarget.length);\n const score = 1 - distance / maxLen;\n\n return score >= threshold ? score : 0;\n}\n"],"names":["levenshteinDistance","a","b","matrix","i","j","fuzzyScore","normalizedQuery","normalizedTarget","threshold","distance","maxLen","score"],"mappings":"gFAeO,SAASA,EAAoBC,EAAWC,EAAmB,CAGhE,MAAMC,EAAqB,CAAA,EAG3B,QAASC,EAAI,EAAGA,GAAKF,EAAE,OAAQE,IAC7BD,EAAOC,CAAC,EAAI,CAACA,CAAC,EAIhB,QAASC,EAAI,EAAGA,GAAKJ,EAAE,OAAQI,IAC7BF,EAAO,CAAC,EAAEE,CAAC,EAAIA,EAQjB,QAASD,EAAI,EAAGA,GAAKF,EAAE,OAAQE,IAC7B,QAASC,EAAI,EAAGA,GAAKJ,EAAE,OAAQI,IACzBH,EAAE,OAAOE,EAAI,CAAC,IAAMH,EAAE,OAAOI,EAAI,CAAC,EAEpCF,EAAOC,CAAC,EAAEC,CAAC,EAAIF,EAAOC,EAAI,CAAC,EAAEC,EAAI,CAAC,EAGlCF,EAAOC,CAAC,EAAEC,CAAC,EAAI,KAAK,IAClBF,EAAOC,EAAI,CAAC,EAAEC,EAAI,CAAC,EAAI,EACvBF,EAAOC,CAAC,EAAEC,EAAI,CAAC,EAAI,EACnBF,EAAOC,EAAI,CAAC,EAAEC,CAAC,EAAI,CAAA,EAO3B,OAAOF,EAAOD,EAAE,MAAM,EAAED,EAAE,MAAM,CAClC,CAkBO,SAASK,EACdC,EACAC,EACAC,EAAoB,GACZ,CAER,GAAIF,IAAoBC,EACtB,MAAO,GAIT,GAAID,EAAgB,SAAW,GAAKC,EAAiB,SAAW,EAC9D,MAAO,GAKT,GAAIA,EAAiB,SAASD,CAAe,EAC3C,MAAO,IAAO,GAAMA,EAAgB,OAAUC,EAAiB,OAIjE,MAAME,EAAWV,EAAoBO,EAAiBC,CAAgB,EAChEG,EAAS,KAAK,IAAIJ,EAAgB,OAAQC,EAAiB,MAAM,EACjEI,EAAQ,EAAIF,EAAWC,EAE7B,OAAOC,GAASH,EAAYG,EAAQ,CACtC"}
@@ -1,45 +1,33 @@
1
- import { MAX_STRING_LENGTH as g } from "../types.js";
2
- function m(n, o) {
3
- const r = [];
4
- for (let e = 0; e <= o.length; e++)
5
- r[e] = [e];
6
- for (let e = 0; e <= n.length; e++)
7
- r[0][e] = e;
8
- for (let e = 1; e <= o.length; e++)
9
- for (let t = 1; t <= n.length; t++)
10
- o.charAt(e - 1) === n.charAt(t - 1) ? r[e][t] = r[e - 1][t - 1] : r[e][t] = Math.min(
11
- r[e - 1][t - 1] + 1,
12
- r[e][t - 1] + 1,
13
- r[e - 1][t] + 1
1
+ function s(n, h) {
2
+ const c = [];
3
+ for (let t = 0; t <= h.length; t++)
4
+ c[t] = [t];
5
+ for (let t = 0; t <= n.length; t++)
6
+ c[0][t] = t;
7
+ for (let t = 1; t <= h.length; t++)
8
+ for (let e = 1; e <= n.length; e++)
9
+ h.charAt(t - 1) === n.charAt(e - 1) ? c[t][e] = c[t - 1][e - 1] : c[t][e] = Math.min(
10
+ c[t - 1][e - 1] + 1,
11
+ // Substitution: replace a[j-1] with b[i-1]
12
+ c[t][e - 1] + 1,
13
+ // Deletion: delete a[j-1]
14
+ c[t - 1][e] + 1
15
+ // Insertion: insert b[i-1] into a
14
16
  );
15
- return r[o.length][n.length];
17
+ return c[h.length][n.length];
16
18
  }
17
- function p(n, o, r = 0.3, e = !1, t = g) {
18
- const l = [];
19
- let c = n, s = o;
20
- c.length > t && (l.push(
21
- `Query string truncated from ${c.length} to ${t} characters for performance`
22
- ), c = c.slice(0, t)), s.length > t && (l.push(
23
- `Target string truncated from ${s.length} to ${t} characters for performance`
24
- ), s = s.slice(0, t));
25
- const f = e ? c : c.toLowerCase(), h = e ? s : s.toLowerCase();
26
- if (f === h)
27
- return { score: 1, warnings: l };
28
- if (f.length === 0 || h.length === 0)
29
- return { score: 0, warnings: l };
30
- if (h.includes(f))
31
- return {
32
- score: 0.9 + 0.1 * f.length / h.length,
33
- warnings: l
34
- };
35
- const a = m(f, h), u = Math.max(f.length, h.length), i = 1 - a / u;
36
- return {
37
- score: i >= r ? i : 0,
38
- warnings: l
39
- };
19
+ function l(n, h, c = 0.3) {
20
+ if (n === h)
21
+ return 1;
22
+ if (n.length === 0 || h.length === 0)
23
+ return 0;
24
+ if (h.includes(n))
25
+ return 0.9 + 0.1 * n.length / h.length;
26
+ const t = s(n, h), e = Math.max(n.length, h.length), f = 1 - t / e;
27
+ return f >= c ? f : 0;
40
28
  }
41
29
  export {
42
- p as fuzzyScore,
43
- m as levenshteinDistance
30
+ l as fuzzyScore,
31
+ s as levenshteinDistance
44
32
  };
45
33
  //# sourceMappingURL=levenshtein.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"levenshtein.js","sources":["../../src/fuzzy/levenshtein.ts"],"sourcesContent":["import { MAX_STRING_LENGTH } from '../types';\n\nexport function levenshteinDistance(a: string, b: string): number {\n const matrix: number[][] = [];\n\n for (let i = 0; i <= b.length; i++) {\n matrix[i] = [i];\n }\n for (let j = 0; j <= a.length; j++) {\n matrix[0][j] = j;\n }\n\n for (let i = 1; i <= b.length; i++) {\n for (let j = 1; j <= a.length; j++) {\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\n matrix[i][j] = matrix[i - 1][j - 1];\n } else {\n matrix[i][j] = Math.min(\n matrix[i - 1][j - 1] + 1,\n matrix[i][j - 1] + 1,\n matrix[i - 1][j] + 1\n );\n }\n }\n }\n\n return matrix[b.length][a.length];\n}\n\nexport interface FuzzyScoreResult {\n score: number;\n warnings: string[];\n}\n\nexport function fuzzyScore(\n query: string,\n target: string,\n threshold: number = 0.3,\n caseSensitive: boolean = false,\n maxStringLength: number = MAX_STRING_LENGTH\n): FuzzyScoreResult {\n const warnings: string[] = [];\n let q = query;\n let t = target;\n\n if (q.length > maxStringLength) {\n warnings.push(\n `Query string truncated from ${q.length} to ${maxStringLength} characters for performance`\n );\n q = q.slice(0, maxStringLength);\n }\n\n if (t.length > maxStringLength) {\n warnings.push(\n `Target string truncated from ${t.length} to ${maxStringLength} characters for performance`\n );\n t = t.slice(0, maxStringLength);\n }\n\n const normalizedQ = caseSensitive ? q : q.toLowerCase();\n const normalizedT = caseSensitive ? t : t.toLowerCase();\n\n if (normalizedQ === normalizedT) {\n return { score: 1, warnings };\n }\n if (normalizedQ.length === 0 || normalizedT.length === 0) {\n return { score: 0, warnings };\n }\n\n if (normalizedT.includes(normalizedQ)) {\n return {\n score: 0.9 + (0.1 * normalizedQ.length) / normalizedT.length,\n warnings,\n };\n }\n\n const distance = levenshteinDistance(normalizedQ, normalizedT);\n const maxLen = Math.max(normalizedQ.length, normalizedT.length);\n const score = 1 - distance / maxLen;\n\n return {\n score: score >= threshold ? score : 0,\n warnings,\n };\n}\n"],"names":["levenshteinDistance","a","b","matrix","i","j","fuzzyScore","query","target","threshold","caseSensitive","maxStringLength","MAX_STRING_LENGTH","warnings","q","t","normalizedQ","normalizedT","distance","maxLen","score"],"mappings":";AAEO,SAASA,EAAoBC,GAAWC,GAAmB;AAChE,QAAMC,IAAqB,CAAA;AAE3B,WAASC,IAAI,GAAGA,KAAKF,EAAE,QAAQE;AAC7B,IAAAD,EAAOC,CAAC,IAAI,CAACA,CAAC;AAEhB,WAASC,IAAI,GAAGA,KAAKJ,EAAE,QAAQI;AAC7B,IAAAF,EAAO,CAAC,EAAEE,CAAC,IAAIA;AAGjB,WAASD,IAAI,GAAGA,KAAKF,EAAE,QAAQE;AAC7B,aAASC,IAAI,GAAGA,KAAKJ,EAAE,QAAQI;AAC7B,MAAIH,EAAE,OAAOE,IAAI,CAAC,MAAMH,EAAE,OAAOI,IAAI,CAAC,IACpCF,EAAOC,CAAC,EAAEC,CAAC,IAAIF,EAAOC,IAAI,CAAC,EAAEC,IAAI,CAAC,IAElCF,EAAOC,CAAC,EAAEC,CAAC,IAAI,KAAK;AAAA,QAClBF,EAAOC,IAAI,CAAC,EAAEC,IAAI,CAAC,IAAI;AAAA,QACvBF,EAAOC,CAAC,EAAEC,IAAI,CAAC,IAAI;AAAA,QACnBF,EAAOC,IAAI,CAAC,EAAEC,CAAC,IAAI;AAAA,MAAA;AAM3B,SAAOF,EAAOD,EAAE,MAAM,EAAED,EAAE,MAAM;AAClC;AAOO,SAASK,EACdC,GACAC,GACAC,IAAoB,KACpBC,IAAyB,IACzBC,IAA0BC,GACR;AAClB,QAAMC,IAAqB,CAAA;AAC3B,MAAIC,IAAIP,GACJQ,IAAIP;AAER,EAAIM,EAAE,SAASH,MACbE,EAAS;AAAA,IACP,+BAA+BC,EAAE,MAAM,OAAOH,CAAe;AAAA,EAAA,GAE/DG,IAAIA,EAAE,MAAM,GAAGH,CAAe,IAG5BI,EAAE,SAASJ,MACbE,EAAS;AAAA,IACP,gCAAgCE,EAAE,MAAM,OAAOJ,CAAe;AAAA,EAAA,GAEhEI,IAAIA,EAAE,MAAM,GAAGJ,CAAe;AAGhC,QAAMK,IAAcN,IAAgBI,IAAIA,EAAE,YAAA,GACpCG,IAAcP,IAAgBK,IAAIA,EAAE,YAAA;AAE1C,MAAIC,MAAgBC;AAClB,WAAO,EAAE,OAAO,GAAG,UAAAJ,EAAA;AAErB,MAAIG,EAAY,WAAW,KAAKC,EAAY,WAAW;AACrD,WAAO,EAAE,OAAO,GAAG,UAAAJ,EAAA;AAGrB,MAAII,EAAY,SAASD,CAAW;AAClC,WAAO;AAAA,MACL,OAAO,MAAO,MAAMA,EAAY,SAAUC,EAAY;AAAA,MACtD,UAAAJ;AAAA,IAAA;AAIJ,QAAMK,IAAWlB,EAAoBgB,GAAaC,CAAW,GACvDE,IAAS,KAAK,IAAIH,EAAY,QAAQC,EAAY,MAAM,GACxDG,IAAQ,IAAIF,IAAWC;AAE7B,SAAO;AAAA,IACL,OAAOC,KAASX,IAAYW,IAAQ;AAAA,IACpC,UAAAP;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"levenshtein.js","sources":["../../src/fuzzy/levenshtein.ts"],"sourcesContent":["/**\n * Calculates the Levenshtein distance (edit distance) between two strings.\n *\n * The Levenshtein distance is the minimum number of single-character edits\n * (insertions, deletions, or substitutions) required to transform string `a`\n * into string `b`.\n *\n * Uses dynamic programming with a 2D matrix where matrix[i][j] represents\n * the minimum edit distance between the first i characters of `b` and the\n * first j characters of `a`.\n *\n * @param a - First string\n * @param b - Second string\n * @returns The Levenshtein distance between the two strings\n */\nexport function levenshteinDistance(a: string, b: string): number {\n // Initialize the dynamic programming matrix\n // matrix[i][j] = edit distance between b[0..i-1] and a[0..j-1]\n const matrix: number[][] = [];\n\n // Initialize first column: transforming empty string to b[0..i-1] requires i insertions\n for (let i = 0; i <= b.length; i++) {\n matrix[i] = [i];\n }\n\n // Initialize first row: transforming a[0..j-1] to empty string requires j deletions\n for (let j = 0; j <= a.length; j++) {\n matrix[0][j] = j;\n }\n\n // Fill the matrix using dynamic programming\n // For each position, consider three possible operations:\n // 1. Substitution: matrix[i-1][j-1] + 1 (if characters differ)\n // 2. Insertion: matrix[i-1][j] + 1 (insert character from b)\n // 3. Deletion: matrix[i][j-1] + 1 (delete character from a)\n for (let i = 1; i <= b.length; i++) {\n for (let j = 1; j <= a.length; j++) {\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\n // Characters match: no edit needed, carry forward previous distance\n matrix[i][j] = matrix[i - 1][j - 1];\n } else {\n // Characters differ: take minimum of three edit operations\n matrix[i][j] = Math.min(\n matrix[i - 1][j - 1] + 1, // Substitution: replace a[j-1] with b[i-1]\n matrix[i][j - 1] + 1, // Deletion: delete a[j-1]\n matrix[i - 1][j] + 1 // Insertion: insert b[i-1] into a\n );\n }\n }\n }\n\n // Return the final distance (bottom-right corner of matrix)\n return matrix[b.length][a.length];\n}\n\n/**\n * Calculates a fuzzy similarity score between two pre-normalized strings.\n * Callers must truncate and apply case normalization before calling.\n *\n * The scoring algorithm uses a tiered approach:\n * 1. Exact match: Returns score of 1.0\n * 2. Substring match: Returns score between 0.9-1.0 based on match ratio\n * 3. Levenshtein distance: Returns normalized similarity score (0-1)\n *\n * Scores below the threshold are returned as 0 to filter out poor matches.\n *\n * @param normalizedQuery - Pre-normalized query string (truncated, case-adjusted)\n * @param normalizedTarget - Pre-normalized target string (truncated, case-adjusted)\n * @param threshold - Minimum score threshold. Scores below this return 0\n * @returns Similarity score between 0 and 1\n */\nexport function fuzzyScore(\n normalizedQuery: string,\n normalizedTarget: string,\n threshold: number = 0.3\n): number {\n // Exact match: perfect score\n if (normalizedQuery === normalizedTarget) {\n return 1;\n }\n\n // Empty string: no match possible\n if (normalizedQuery.length === 0 || normalizedTarget.length === 0) {\n return 0;\n }\n\n // Substring match: query is contained within target\n // Score ranges from 0.9 (short query in long target) to 1.0 (query equals target)\n if (normalizedTarget.includes(normalizedQuery)) {\n return 0.9 + (0.1 * normalizedQuery.length) / normalizedTarget.length;\n }\n\n // Calculate Levenshtein distance and convert to similarity score\n const distance = levenshteinDistance(normalizedQuery, normalizedTarget);\n const maxLen = Math.max(normalizedQuery.length, normalizedTarget.length);\n const score = 1 - distance / maxLen;\n\n return score >= threshold ? score : 0;\n}\n"],"names":["levenshteinDistance","a","b","matrix","i","j","fuzzyScore","normalizedQuery","normalizedTarget","threshold","distance","maxLen","score"],"mappings":"AAeO,SAASA,EAAoBC,GAAWC,GAAmB;AAGhE,QAAMC,IAAqB,CAAA;AAG3B,WAASC,IAAI,GAAGA,KAAKF,EAAE,QAAQE;AAC7B,IAAAD,EAAOC,CAAC,IAAI,CAACA,CAAC;AAIhB,WAASC,IAAI,GAAGA,KAAKJ,EAAE,QAAQI;AAC7B,IAAAF,EAAO,CAAC,EAAEE,CAAC,IAAIA;AAQjB,WAASD,IAAI,GAAGA,KAAKF,EAAE,QAAQE;AAC7B,aAASC,IAAI,GAAGA,KAAKJ,EAAE,QAAQI;AAC7B,MAAIH,EAAE,OAAOE,IAAI,CAAC,MAAMH,EAAE,OAAOI,IAAI,CAAC,IAEpCF,EAAOC,CAAC,EAAEC,CAAC,IAAIF,EAAOC,IAAI,CAAC,EAAEC,IAAI,CAAC,IAGlCF,EAAOC,CAAC,EAAEC,CAAC,IAAI,KAAK;AAAA,QAClBF,EAAOC,IAAI,CAAC,EAAEC,IAAI,CAAC,IAAI;AAAA;AAAA,QACvBF,EAAOC,CAAC,EAAEC,IAAI,CAAC,IAAI;AAAA;AAAA,QACnBF,EAAOC,IAAI,CAAC,EAAEC,CAAC,IAAI;AAAA;AAAA,MAAA;AAO3B,SAAOF,EAAOD,EAAE,MAAM,EAAED,EAAE,MAAM;AAClC;AAkBO,SAASK,EACdC,GACAC,GACAC,IAAoB,KACZ;AAER,MAAIF,MAAoBC;AACtB,WAAO;AAIT,MAAID,EAAgB,WAAW,KAAKC,EAAiB,WAAW;AAC9D,WAAO;AAKT,MAAIA,EAAiB,SAASD,CAAe;AAC3C,WAAO,MAAO,MAAMA,EAAgB,SAAUC,EAAiB;AAIjE,QAAME,IAAWV,EAAoBO,GAAiBC,CAAgB,GAChEG,IAAS,KAAK,IAAIJ,EAAgB,QAAQC,EAAiB,MAAM,GACjEI,IAAQ,IAAIF,IAAWC;AAE7B,SAAOC,KAASH,IAAYG,IAAQ;AACtC;"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("./levenshtein.cjs"),d=require("../utils/nested.cjs"),w=require("../types.cjs");function S(o,n){return o.map(e=>typeof e=="string"?{name:e,weight:1,threshold:n}:{name:e.name,weight:e.weight??1,threshold:e.threshold??n})}function p(o,n,e,l,h=w.MAX_STRING_LENGTH){let u=0,r=0;const s=[];for(const t of e){const c=d.getNestedValue(o,t.name);if(c==null){t.name.includes(".")&&s.push(`Field "${t.name}" not found on item`);continue}const g=String(c);let i=0;for(const m of n){const a=f.fuzzyScore(m,g,t.threshold,l,h);s.push(...a.warnings),i=Math.max(i,a.score)}u+=i*t.weight,r+=t.weight}return{score:r>0?u/r:0,warnings:s}}exports.normalizeFieldConfig=S;exports.scoreItem=p;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("./levenshtein.cjs"),p=require("../utils/nested.cjs"),w=require("../types.cjs");function z(r,o){return r.map(e=>typeof e=="string"?{name:e,weight:1,threshold:o}:{name:e.name,weight:e.weight??1,threshold:e.threshold??o})}function T(r,o,e,h,s=w.MAX_STRING_LENGTH){let l=0,i=0;const c=[];for(const t of e){const u=p.getNestedValue(r,t.name);if(u==null){t.name.includes(".")&&c.push(`Field "${t.name}" not found on item`);continue}let n=String(u);n.length>s&&(c.push(`Target string truncated from ${n.length} to ${s} characters for performance`),n=n.slice(0,s));const f=h?n:n.toLowerCase();let a=0;for(const m of o){const g=d.fuzzyScore(m,f,t.threshold);a=Math.max(a,g)}l+=a*t.weight,i+=t.weight}return{score:i>0?l/i:0,warnings:c}}exports.normalizeFieldConfig=z;exports.scoreItem=T;
2
2
  //# sourceMappingURL=scorer.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"scorer.cjs","sources":["../../src/fuzzy/scorer.ts"],"sourcesContent":["import { fuzzyScore } from './levenshtein';\nimport { getNestedValue } from '../utils/nested';\nimport { MAX_STRING_LENGTH } from '../types';\nimport type { FieldConfig, NormalizedFieldConfig } from '../types';\n\nexport function normalizeFieldConfig(\n fields: FieldConfig[],\n globalThreshold: number\n): NormalizedFieldConfig[] {\n return fields.map((field) => {\n if (typeof field === 'string') {\n return { name: field, weight: 1, threshold: globalThreshold };\n }\n return {\n name: field.name,\n weight: field.weight ?? 1,\n threshold: field.threshold ?? globalThreshold,\n };\n });\n}\n\nexport function scoreItem<T>(\n item: T,\n tokens: string[],\n fields: NormalizedFieldConfig[],\n caseSensitive: boolean,\n maxStringLength: number = MAX_STRING_LENGTH\n): { score: number; warnings: string[] } {\n let totalScore = 0;\n let totalWeight = 0;\n const warnings: string[] = [];\n\n for (const field of fields) {\n const value = getNestedValue(item, field.name);\n\n if (value == null) {\n if (field.name.includes('.')) {\n warnings.push(`Field \"${field.name}\" not found on item`);\n }\n continue;\n }\n\n const stringValue = String(value);\n\n let bestTokenScore = 0;\n for (const token of tokens) {\n const result = fuzzyScore(\n token,\n stringValue,\n field.threshold,\n caseSensitive,\n maxStringLength\n );\n warnings.push(...result.warnings);\n bestTokenScore = Math.max(bestTokenScore, result.score);\n }\n\n totalScore += bestTokenScore * field.weight;\n totalWeight += field.weight;\n }\n\n return {\n score: totalWeight > 0 ? totalScore / totalWeight : 0,\n warnings,\n };\n}\n"],"names":["normalizeFieldConfig","fields","globalThreshold","field","scoreItem","item","tokens","caseSensitive","maxStringLength","MAX_STRING_LENGTH","totalScore","totalWeight","warnings","value","getNestedValue","stringValue","bestTokenScore","token","result","fuzzyScore"],"mappings":"gLAKO,SAASA,EACdC,EACAC,EACyB,CACzB,OAAOD,EAAO,IAAKE,GACb,OAAOA,GAAU,SACZ,CAAE,KAAMA,EAAO,OAAQ,EAAG,UAAWD,CAAA,EAEvC,CACL,KAAMC,EAAM,KACZ,OAAQA,EAAM,QAAU,EACxB,UAAWA,EAAM,WAAaD,CAAA,CAEjC,CACH,CAEO,SAASE,EACdC,EACAC,EACAL,EACAM,EACAC,EAA0BC,oBACa,CACvC,IAAIC,EAAa,EACbC,EAAc,EAClB,MAAMC,EAAqB,CAAA,EAE3B,UAAWT,KAASF,EAAQ,CAC1B,MAAMY,EAAQC,EAAAA,eAAeT,EAAMF,EAAM,IAAI,EAE7C,GAAIU,GAAS,KAAM,CACbV,EAAM,KAAK,SAAS,GAAG,GACzBS,EAAS,KAAK,UAAUT,EAAM,IAAI,qBAAqB,EAEzD,QACF,CAEA,MAAMY,EAAc,OAAOF,CAAK,EAEhC,IAAIG,EAAiB,EACrB,UAAWC,KAASX,EAAQ,CAC1B,MAAMY,EAASC,EAAAA,WACbF,EACAF,EACAZ,EAAM,UACNI,EACAC,CAAA,EAEFI,EAAS,KAAK,GAAGM,EAAO,QAAQ,EAChCF,EAAiB,KAAK,IAAIA,EAAgBE,EAAO,KAAK,CACxD,CAEAR,GAAcM,EAAiBb,EAAM,OACrCQ,GAAeR,EAAM,MACvB,CAEA,MAAO,CACL,MAAOQ,EAAc,EAAID,EAAaC,EAAc,EACpD,SAAAC,CAAA,CAEJ"}
1
+ {"version":3,"file":"scorer.cjs","sources":["../../src/fuzzy/scorer.ts"],"sourcesContent":["import { fuzzyScore } from './levenshtein';\nimport { getNestedValue } from '../utils/nested';\nimport { MAX_STRING_LENGTH } from '../types';\nimport type { FieldConfig, NormalizedFieldConfig } from '../types';\n\nexport function normalizeFieldConfig(\n fields: FieldConfig[],\n globalThreshold: number\n): NormalizedFieldConfig[] {\n return fields.map((field) => {\n if (typeof field === 'string') {\n return { name: field, weight: 1, threshold: globalThreshold };\n }\n return {\n name: field.name,\n weight: field.weight ?? 1,\n threshold: field.threshold ?? globalThreshold,\n };\n });\n}\n\nexport function scoreItem<T>(\n item: T,\n normalizedTokens: string[],\n fields: NormalizedFieldConfig[],\n caseSensitive: boolean,\n maxStringLength: number = MAX_STRING_LENGTH\n): { score: number; warnings: string[] } {\n let totalScore = 0;\n let totalWeight = 0;\n const warnings: string[] = [];\n\n for (const field of fields) {\n const value = getNestedValue(item, field.name);\n\n if (value == null) {\n if (field.name.includes('.')) {\n warnings.push(`Field \"${field.name}\" not found on item`);\n }\n continue;\n }\n\n let stringValue = String(value);\n\n // Normalize target once per field (truncate, collect warnings)\n if (stringValue.length > maxStringLength) {\n warnings.push(\n `Target string truncated from ${stringValue.length} to ${maxStringLength} characters for performance`\n );\n stringValue = stringValue.slice(0, maxStringLength);\n }\n const normalizedTarget = caseSensitive\n ? stringValue\n : stringValue.toLowerCase();\n\n let bestTokenScore = 0;\n for (const normalizedToken of normalizedTokens) {\n const score = fuzzyScore(\n normalizedToken,\n normalizedTarget,\n field.threshold\n );\n bestTokenScore = Math.max(bestTokenScore, score);\n }\n\n totalScore += bestTokenScore * field.weight;\n totalWeight += field.weight;\n }\n\n return {\n score: totalWeight > 0 ? totalScore / totalWeight : 0,\n warnings,\n };\n}\n"],"names":["normalizeFieldConfig","fields","globalThreshold","field","scoreItem","item","normalizedTokens","caseSensitive","maxStringLength","MAX_STRING_LENGTH","totalScore","totalWeight","warnings","value","getNestedValue","stringValue","normalizedTarget","bestTokenScore","normalizedToken","score","fuzzyScore"],"mappings":"gLAKO,SAASA,EACdC,EACAC,EACyB,CACzB,OAAOD,EAAO,IAAKE,GACb,OAAOA,GAAU,SACZ,CAAE,KAAMA,EAAO,OAAQ,EAAG,UAAWD,CAAA,EAEvC,CACL,KAAMC,EAAM,KACZ,OAAQA,EAAM,QAAU,EACxB,UAAWA,EAAM,WAAaD,CAAA,CAEjC,CACH,CAEO,SAASE,EACdC,EACAC,EACAL,EACAM,EACAC,EAA0BC,oBACa,CACvC,IAAIC,EAAa,EACbC,EAAc,EAClB,MAAMC,EAAqB,CAAA,EAE3B,UAAWT,KAASF,EAAQ,CAC1B,MAAMY,EAAQC,EAAAA,eAAeT,EAAMF,EAAM,IAAI,EAE7C,GAAIU,GAAS,KAAM,CACbV,EAAM,KAAK,SAAS,GAAG,GACzBS,EAAS,KAAK,UAAUT,EAAM,IAAI,qBAAqB,EAEzD,QACF,CAEA,IAAIY,EAAc,OAAOF,CAAK,EAG1BE,EAAY,OAASP,IACvBI,EAAS,KACP,gCAAgCG,EAAY,MAAM,OAAOP,CAAe,6BAAA,EAE1EO,EAAcA,EAAY,MAAM,EAAGP,CAAe,GAEpD,MAAMQ,EAAmBT,EACrBQ,EACAA,EAAY,YAAA,EAEhB,IAAIE,EAAiB,EACrB,UAAWC,KAAmBZ,EAAkB,CAC9C,MAAMa,EAAQC,EAAAA,WACZF,EACAF,EACAb,EAAM,SAAA,EAERc,EAAiB,KAAK,IAAIA,EAAgBE,CAAK,CACjD,CAEAT,GAAcO,EAAiBd,EAAM,OACrCQ,GAAeR,EAAM,MACvB,CAEA,MAAO,CACL,MAAOQ,EAAc,EAAID,EAAaC,EAAc,EACpD,SAAAC,CAAA,CAEJ"}
@@ -1,43 +1,45 @@
1
- import { fuzzyScore as g } from "./levenshtein.js";
2
- import { getNestedValue as p } from "../utils/nested.js";
1
+ import { fuzzyScore as p } from "./levenshtein.js";
2
+ import { getNestedValue as d } from "../utils/nested.js";
3
3
  import { MAX_STRING_LENGTH as w } from "../types.js";
4
- function N(o, n) {
5
- return o.map((t) => typeof t == "string" ? { name: t, weight: 1, threshold: n } : {
6
- name: t.name,
7
- weight: t.weight ?? 1,
8
- threshold: t.threshold ?? n
4
+ function S(n, r) {
5
+ return n.map((e) => typeof e == "string" ? { name: e, weight: 1, threshold: r } : {
6
+ name: e.name,
7
+ weight: e.weight ?? 1,
8
+ threshold: e.threshold ?? r
9
9
  });
10
10
  }
11
- function k(o, n, t, c, h = w) {
12
- let m = 0, r = 0;
13
- const i = [];
14
- for (const e of t) {
15
- const u = p(o, e.name);
16
- if (u == null) {
17
- e.name.includes(".") && i.push(`Field "${e.name}" not found on item`);
11
+ function $(n, r, e, f, i = w) {
12
+ let m = 0, s = 0;
13
+ const a = [];
14
+ for (const t of e) {
15
+ const l = d(n, t.name);
16
+ if (l == null) {
17
+ t.name.includes(".") && a.push(`Field "${t.name}" not found on item`);
18
18
  continue;
19
19
  }
20
- const f = String(u);
21
- let s = 0;
22
- for (const l of n) {
23
- const a = g(
24
- l,
25
- f,
26
- e.threshold,
27
- c,
28
- h
20
+ let o = String(l);
21
+ o.length > i && (a.push(
22
+ `Target string truncated from ${o.length} to ${i} characters for performance`
23
+ ), o = o.slice(0, i));
24
+ const u = f ? o : o.toLowerCase();
25
+ let c = 0;
26
+ for (const h of r) {
27
+ const g = p(
28
+ h,
29
+ u,
30
+ t.threshold
29
31
  );
30
- i.push(...a.warnings), s = Math.max(s, a.score);
32
+ c = Math.max(c, g);
31
33
  }
32
- m += s * e.weight, r += e.weight;
34
+ m += c * t.weight, s += t.weight;
33
35
  }
34
36
  return {
35
- score: r > 0 ? m / r : 0,
36
- warnings: i
37
+ score: s > 0 ? m / s : 0,
38
+ warnings: a
37
39
  };
38
40
  }
39
41
  export {
40
- N as normalizeFieldConfig,
41
- k as scoreItem
42
+ S as normalizeFieldConfig,
43
+ $ as scoreItem
42
44
  };
43
45
  //# sourceMappingURL=scorer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scorer.js","sources":["../../src/fuzzy/scorer.ts"],"sourcesContent":["import { fuzzyScore } from './levenshtein';\nimport { getNestedValue } from '../utils/nested';\nimport { MAX_STRING_LENGTH } from '../types';\nimport type { FieldConfig, NormalizedFieldConfig } from '../types';\n\nexport function normalizeFieldConfig(\n fields: FieldConfig[],\n globalThreshold: number\n): NormalizedFieldConfig[] {\n return fields.map((field) => {\n if (typeof field === 'string') {\n return { name: field, weight: 1, threshold: globalThreshold };\n }\n return {\n name: field.name,\n weight: field.weight ?? 1,\n threshold: field.threshold ?? globalThreshold,\n };\n });\n}\n\nexport function scoreItem<T>(\n item: T,\n tokens: string[],\n fields: NormalizedFieldConfig[],\n caseSensitive: boolean,\n maxStringLength: number = MAX_STRING_LENGTH\n): { score: number; warnings: string[] } {\n let totalScore = 0;\n let totalWeight = 0;\n const warnings: string[] = [];\n\n for (const field of fields) {\n const value = getNestedValue(item, field.name);\n\n if (value == null) {\n if (field.name.includes('.')) {\n warnings.push(`Field \"${field.name}\" not found on item`);\n }\n continue;\n }\n\n const stringValue = String(value);\n\n let bestTokenScore = 0;\n for (const token of tokens) {\n const result = fuzzyScore(\n token,\n stringValue,\n field.threshold,\n caseSensitive,\n maxStringLength\n );\n warnings.push(...result.warnings);\n bestTokenScore = Math.max(bestTokenScore, result.score);\n }\n\n totalScore += bestTokenScore * field.weight;\n totalWeight += field.weight;\n }\n\n return {\n score: totalWeight > 0 ? totalScore / totalWeight : 0,\n warnings,\n };\n}\n"],"names":["normalizeFieldConfig","fields","globalThreshold","field","scoreItem","item","tokens","caseSensitive","maxStringLength","MAX_STRING_LENGTH","totalScore","totalWeight","warnings","value","getNestedValue","stringValue","bestTokenScore","token","result","fuzzyScore"],"mappings":";;;AAKO,SAASA,EACdC,GACAC,GACyB;AACzB,SAAOD,EAAO,IAAI,CAACE,MACb,OAAOA,KAAU,WACZ,EAAE,MAAMA,GAAO,QAAQ,GAAG,WAAWD,EAAA,IAEvC;AAAA,IACL,MAAMC,EAAM;AAAA,IACZ,QAAQA,EAAM,UAAU;AAAA,IACxB,WAAWA,EAAM,aAAaD;AAAA,EAAA,CAEjC;AACH;AAEO,SAASE,EACdC,GACAC,GACAL,GACAM,GACAC,IAA0BC,GACa;AACvC,MAAIC,IAAa,GACbC,IAAc;AAClB,QAAMC,IAAqB,CAAA;AAE3B,aAAWT,KAASF,GAAQ;AAC1B,UAAMY,IAAQC,EAAeT,GAAMF,EAAM,IAAI;AAE7C,QAAIU,KAAS,MAAM;AACjB,MAAIV,EAAM,KAAK,SAAS,GAAG,KACzBS,EAAS,KAAK,UAAUT,EAAM,IAAI,qBAAqB;AAEzD;AAAA,IACF;AAEA,UAAMY,IAAc,OAAOF,CAAK;AAEhC,QAAIG,IAAiB;AACrB,eAAWC,KAASX,GAAQ;AAC1B,YAAMY,IAASC;AAAA,QACbF;AAAA,QACAF;AAAA,QACAZ,EAAM;AAAA,QACNI;AAAA,QACAC;AAAA,MAAA;AAEF,MAAAI,EAAS,KAAK,GAAGM,EAAO,QAAQ,GAChCF,IAAiB,KAAK,IAAIA,GAAgBE,EAAO,KAAK;AAAA,IACxD;AAEA,IAAAR,KAAcM,IAAiBb,EAAM,QACrCQ,KAAeR,EAAM;AAAA,EACvB;AAEA,SAAO;AAAA,IACL,OAAOQ,IAAc,IAAID,IAAaC,IAAc;AAAA,IACpD,UAAAC;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"scorer.js","sources":["../../src/fuzzy/scorer.ts"],"sourcesContent":["import { fuzzyScore } from './levenshtein';\nimport { getNestedValue } from '../utils/nested';\nimport { MAX_STRING_LENGTH } from '../types';\nimport type { FieldConfig, NormalizedFieldConfig } from '../types';\n\nexport function normalizeFieldConfig(\n fields: FieldConfig[],\n globalThreshold: number\n): NormalizedFieldConfig[] {\n return fields.map((field) => {\n if (typeof field === 'string') {\n return { name: field, weight: 1, threshold: globalThreshold };\n }\n return {\n name: field.name,\n weight: field.weight ?? 1,\n threshold: field.threshold ?? globalThreshold,\n };\n });\n}\n\nexport function scoreItem<T>(\n item: T,\n normalizedTokens: string[],\n fields: NormalizedFieldConfig[],\n caseSensitive: boolean,\n maxStringLength: number = MAX_STRING_LENGTH\n): { score: number; warnings: string[] } {\n let totalScore = 0;\n let totalWeight = 0;\n const warnings: string[] = [];\n\n for (const field of fields) {\n const value = getNestedValue(item, field.name);\n\n if (value == null) {\n if (field.name.includes('.')) {\n warnings.push(`Field \"${field.name}\" not found on item`);\n }\n continue;\n }\n\n let stringValue = String(value);\n\n // Normalize target once per field (truncate, collect warnings)\n if (stringValue.length > maxStringLength) {\n warnings.push(\n `Target string truncated from ${stringValue.length} to ${maxStringLength} characters for performance`\n );\n stringValue = stringValue.slice(0, maxStringLength);\n }\n const normalizedTarget = caseSensitive\n ? stringValue\n : stringValue.toLowerCase();\n\n let bestTokenScore = 0;\n for (const normalizedToken of normalizedTokens) {\n const score = fuzzyScore(\n normalizedToken,\n normalizedTarget,\n field.threshold\n );\n bestTokenScore = Math.max(bestTokenScore, score);\n }\n\n totalScore += bestTokenScore * field.weight;\n totalWeight += field.weight;\n }\n\n return {\n score: totalWeight > 0 ? totalScore / totalWeight : 0,\n warnings,\n };\n}\n"],"names":["normalizeFieldConfig","fields","globalThreshold","field","scoreItem","item","normalizedTokens","caseSensitive","maxStringLength","MAX_STRING_LENGTH","totalScore","totalWeight","warnings","value","getNestedValue","stringValue","normalizedTarget","bestTokenScore","normalizedToken","score","fuzzyScore"],"mappings":";;;AAKO,SAASA,EACdC,GACAC,GACyB;AACzB,SAAOD,EAAO,IAAI,CAACE,MACb,OAAOA,KAAU,WACZ,EAAE,MAAMA,GAAO,QAAQ,GAAG,WAAWD,EAAA,IAEvC;AAAA,IACL,MAAMC,EAAM;AAAA,IACZ,QAAQA,EAAM,UAAU;AAAA,IACxB,WAAWA,EAAM,aAAaD;AAAA,EAAA,CAEjC;AACH;AAEO,SAASE,EACdC,GACAC,GACAL,GACAM,GACAC,IAA0BC,GACa;AACvC,MAAIC,IAAa,GACbC,IAAc;AAClB,QAAMC,IAAqB,CAAA;AAE3B,aAAWT,KAASF,GAAQ;AAC1B,UAAMY,IAAQC,EAAeT,GAAMF,EAAM,IAAI;AAE7C,QAAIU,KAAS,MAAM;AACjB,MAAIV,EAAM,KAAK,SAAS,GAAG,KACzBS,EAAS,KAAK,UAAUT,EAAM,IAAI,qBAAqB;AAEzD;AAAA,IACF;AAEA,QAAIY,IAAc,OAAOF,CAAK;AAG9B,IAAIE,EAAY,SAASP,MACvBI,EAAS;AAAA,MACP,gCAAgCG,EAAY,MAAM,OAAOP,CAAe;AAAA,IAAA,GAE1EO,IAAcA,EAAY,MAAM,GAAGP,CAAe;AAEpD,UAAMQ,IAAmBT,IACrBQ,IACAA,EAAY,YAAA;AAEhB,QAAIE,IAAiB;AACrB,eAAWC,KAAmBZ,GAAkB;AAC9C,YAAMa,IAAQC;AAAA,QACZF;AAAA,QACAF;AAAA,QACAb,EAAM;AAAA,MAAA;AAER,MAAAc,IAAiB,KAAK,IAAIA,GAAgBE,CAAK;AAAA,IACjD;AAEA,IAAAT,KAAcO,IAAiBd,EAAM,QACrCQ,KAAeR,EAAM;AAAA,EACvB;AAEA,SAAO;AAAA,IACL,OAAOQ,IAAc,IAAID,IAAaC,IAAc;AAAA,IACpD,UAAAC;AAAA,EAAA;AAEJ;"}
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./Spotr.cjs"),r=require("./errors.cjs"),e=require("./types.cjs");exports.Spotr=o.Spotr;exports.ErrorCodes=r.ErrorCodes;exports.SpotrError=r.SpotrError;exports.MAX_STRING_LENGTH=e.MAX_STRING_LENGTH;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./Spotr.cjs"),r=require("./errors.cjs"),t=require("./types.cjs"),o=require("./utils/nested.cjs");exports.Spotr=e.Spotr;exports.ErrorCodes=r.ErrorCodes;exports.SpotrError=r.SpotrError;exports.MAX_STRING_LENGTH=t.MAX_STRING_LENGTH;exports.getNestedValue=o.getNestedValue;
2
2
  //# sourceMappingURL=index.cjs.map
package/dist/index.d.ts CHANGED
@@ -13,6 +13,8 @@ export declare type FieldConfig = string | {
13
13
  threshold?: number;
14
14
  };
15
15
 
16
+ export declare function getNestedValue(obj: unknown, path: string): unknown;
17
+
16
18
  export declare type KeywordDefinition<T> = {
17
19
  name: string;
18
20
  triggers: string | string[];
package/dist/index.js CHANGED
@@ -1,10 +1,12 @@
1
- import { Spotr as p } from "./Spotr.js";
2
- import { ErrorCodes as e, SpotrError as f } from "./errors.js";
1
+ import { Spotr as e } from "./Spotr.js";
2
+ import { ErrorCodes as p, SpotrError as f } from "./errors.js";
3
3
  import { MAX_STRING_LENGTH as x } from "./types.js";
4
+ import { getNestedValue as N } from "./utils/nested.js";
4
5
  export {
5
- e as ErrorCodes,
6
+ p as ErrorCodes,
6
7
  x as MAX_STRING_LENGTH,
7
- p as Spotr,
8
- f as SpotrError
8
+ e as Spotr,
9
+ f as SpotrError,
10
+ N as getNestedValue
9
11
  };
10
12
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
package/dist/preact.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("preact/hooks"),o=require("./Spotr.cjs");function s(r,e){if(r===e)return!0;if(typeof r!="object"||typeof e!="object"||r===null||e===null)return!1;const t=Object.keys(r),c=Object.keys(e);if(t.length!==c.length)return!1;for(const n of t)if(r[n]!==e[n])return!1;return!0}function f(r){const e=u.useRef(r);s(e.current,r)||(e.current=r);const t=u.useRef(null);return(!t.current||t.current.options!==e.current)&&(t.current=new o.Spotr(e.current)),t.current}exports.Spotr=o.Spotr;exports.useSpotr=f;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("preact/hooks"),o=require("./Spotr.cjs"),n=require("./utils/shallowEqual.cjs");function c(t){const r=u.useRef(t);n.shallowEqual(r.current,t)||(r.current=t);const e=u.useRef(null);return(!e.current||e.current.options!==r.current)&&(e.current=new o.Spotr(r.current)),e.current}exports.Spotr=o.Spotr;exports.useSpotr=c;
2
2
  //# sourceMappingURL=preact.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"preact.cjs","sources":["../src/preact/index.ts"],"sourcesContent":["import { useRef } from 'preact/hooks';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\n\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (\n typeof a !== 'object' ||\n typeof b !== 'object' ||\n a === null ||\n b === null\n ) {\n return false;\n }\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (\n (a as Record<string, unknown>)[key] !==\n (b as Record<string, unknown>)[key]\n ) {\n return false;\n }\n }\n\n return true;\n}\n\n// Overload: infer T from collection\nexport function useSpotr<C extends readonly object[] | object[] | Set<object>>(\n options: Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {\n collection: C;\n }\n): Spotr<ExtractItemType<C> & object>;\n// Overload: explicit generic\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T>;\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T> {\n const optionsRef = useRef(options);\n\n if (!shallowEqual(optionsRef.current, options)) {\n optionsRef.current = options;\n }\n\n const spotrRef = useRef<Spotr<T> | null>(null);\n\n if (!spotrRef.current || spotrRef.current.options !== optionsRef.current) {\n spotrRef.current = new Spotr(optionsRef.current);\n }\n\n return spotrRef.current;\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["shallowEqual","a","b","keysA","keysB","key","useSpotr","options","optionsRef","useRef","spotrRef","Spotr"],"mappings":"yIAIA,SAASA,EAAaC,EAAYC,EAAqB,CACrD,GAAID,IAAMC,EAAG,MAAO,GACpB,GACE,OAAOD,GAAM,UACb,OAAOC,GAAM,UACbD,IAAM,MACNC,IAAM,KAEN,MAAO,GAGT,MAAMC,EAAQ,OAAO,KAAKF,CAAC,EACrBG,EAAQ,OAAO,KAAKF,CAAC,EAE3B,GAAIC,EAAM,SAAWC,EAAM,OAAQ,MAAO,GAE1C,UAAWC,KAAOF,EAChB,GACGF,EAA8BI,CAAG,IACjCH,EAA8BG,CAAG,EAElC,MAAO,GAIX,MAAO,EACT,CAUO,SAASC,EAA2BC,EAAoC,CAC7E,MAAMC,EAAaC,EAAAA,OAAOF,CAAO,EAE5BP,EAAaQ,EAAW,QAASD,CAAO,IAC3CC,EAAW,QAAUD,GAGvB,MAAMG,EAAWD,EAAAA,OAAwB,IAAI,EAE7C,OAAI,CAACC,EAAS,SAAWA,EAAS,QAAQ,UAAYF,EAAW,WAC/DE,EAAS,QAAU,IAAIC,QAAMH,EAAW,OAAO,GAG1CE,EAAS,OAClB"}
1
+ {"version":3,"file":"preact.cjs","sources":["../src/preact/index.ts"],"sourcesContent":["import { useRef } from 'preact/hooks';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\nimport { shallowEqual } from '../utils';\n\n// Overload: infer T from collection\nexport function useSpotr<C extends readonly object[] | object[] | Set<object>>(\n options: Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {\n collection: C;\n }\n): Spotr<ExtractItemType<C> & object>;\n// Overload: explicit generic\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T>;\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T> {\n const optionsRef = useRef(options);\n\n if (!shallowEqual(optionsRef.current, options)) {\n optionsRef.current = options;\n }\n\n const spotrRef = useRef<Spotr<T> | null>(null);\n\n if (!spotrRef.current || spotrRef.current.options !== optionsRef.current) {\n spotrRef.current = new Spotr(optionsRef.current);\n }\n\n return spotrRef.current;\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["useSpotr","options","optionsRef","useRef","shallowEqual","spotrRef","Spotr"],"mappings":"+KAaO,SAASA,EAA2BC,EAAoC,CAC7E,MAAMC,EAAaC,EAAAA,OAAOF,CAAO,EAE5BG,EAAAA,aAAaF,EAAW,QAASD,CAAO,IAC3CC,EAAW,QAAUD,GAGvB,MAAMI,EAAWF,EAAAA,OAAwB,IAAI,EAE7C,OAAI,CAACE,EAAS,SAAWA,EAAS,QAAQ,UAAYH,EAAW,WAC/DG,EAAS,QAAU,IAAIC,QAAMJ,EAAW,OAAO,GAG1CG,EAAS,OAClB"}
package/dist/preact.js CHANGED
@@ -1,24 +1,14 @@
1
- import { useRef as u } from "preact/hooks";
2
- import { Spotr as f } from "./Spotr.js";
3
- function c(r, e) {
4
- if (r === e) return !0;
5
- if (typeof r != "object" || typeof e != "object" || r === null || e === null)
6
- return !1;
7
- const t = Object.keys(r), o = Object.keys(e);
8
- if (t.length !== o.length) return !1;
9
- for (const n of t)
10
- if (r[n] !== e[n])
11
- return !1;
12
- return !0;
13
- }
14
- function i(r) {
15
- const e = u(r);
16
- c(e.current, r) || (e.current = r);
17
- const t = u(null);
18
- return (!t.current || t.current.options !== e.current) && (t.current = new f(e.current)), t.current;
1
+ import { useRef as n } from "preact/hooks";
2
+ import { Spotr as o } from "./Spotr.js";
3
+ import { shallowEqual as u } from "./utils/shallowEqual.js";
4
+ function i(e) {
5
+ const r = n(e);
6
+ u(r.current, e) || (r.current = e);
7
+ const t = n(null);
8
+ return (!t.current || t.current.options !== r.current) && (t.current = new o(r.current)), t.current;
19
9
  }
20
10
  export {
21
- f as Spotr,
11
+ o as Spotr,
22
12
  i as useSpotr
23
13
  };
24
14
  //# sourceMappingURL=preact.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"preact.js","sources":["../src/preact/index.ts"],"sourcesContent":["import { useRef } from 'preact/hooks';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\n\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (\n typeof a !== 'object' ||\n typeof b !== 'object' ||\n a === null ||\n b === null\n ) {\n return false;\n }\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (\n (a as Record<string, unknown>)[key] !==\n (b as Record<string, unknown>)[key]\n ) {\n return false;\n }\n }\n\n return true;\n}\n\n// Overload: infer T from collection\nexport function useSpotr<C extends readonly object[] | object[] | Set<object>>(\n options: Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {\n collection: C;\n }\n): Spotr<ExtractItemType<C> & object>;\n// Overload: explicit generic\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T>;\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T> {\n const optionsRef = useRef(options);\n\n if (!shallowEqual(optionsRef.current, options)) {\n optionsRef.current = options;\n }\n\n const spotrRef = useRef<Spotr<T> | null>(null);\n\n if (!spotrRef.current || spotrRef.current.options !== optionsRef.current) {\n spotrRef.current = new Spotr(optionsRef.current);\n }\n\n return spotrRef.current;\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["shallowEqual","a","b","keysA","keysB","key","useSpotr","options","optionsRef","useRef","spotrRef","Spotr"],"mappings":";;AAIA,SAASA,EAAaC,GAAYC,GAAqB;AACrD,MAAID,MAAMC,EAAG,QAAO;AACpB,MACE,OAAOD,KAAM,YACb,OAAOC,KAAM,YACbD,MAAM,QACNC,MAAM;AAEN,WAAO;AAGT,QAAMC,IAAQ,OAAO,KAAKF,CAAC,GACrBG,IAAQ,OAAO,KAAKF,CAAC;AAE3B,MAAIC,EAAM,WAAWC,EAAM,OAAQ,QAAO;AAE1C,aAAWC,KAAOF;AAChB,QACGF,EAA8BI,CAAG,MACjCH,EAA8BG,CAAG;AAElC,aAAO;AAIX,SAAO;AACT;AAUO,SAASC,EAA2BC,GAAoC;AAC7E,QAAMC,IAAaC,EAAOF,CAAO;AAEjC,EAAKP,EAAaQ,EAAW,SAASD,CAAO,MAC3CC,EAAW,UAAUD;AAGvB,QAAMG,IAAWD,EAAwB,IAAI;AAE7C,UAAI,CAACC,EAAS,WAAWA,EAAS,QAAQ,YAAYF,EAAW,aAC/DE,EAAS,UAAU,IAAIC,EAAMH,EAAW,OAAO,IAG1CE,EAAS;AAClB;"}
1
+ {"version":3,"file":"preact.js","sources":["../src/preact/index.ts"],"sourcesContent":["import { useRef } from 'preact/hooks';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\nimport { shallowEqual } from '../utils';\n\n// Overload: infer T from collection\nexport function useSpotr<C extends readonly object[] | object[] | Set<object>>(\n options: Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {\n collection: C;\n }\n): Spotr<ExtractItemType<C> & object>;\n// Overload: explicit generic\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T>;\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T> {\n const optionsRef = useRef(options);\n\n if (!shallowEqual(optionsRef.current, options)) {\n optionsRef.current = options;\n }\n\n const spotrRef = useRef<Spotr<T> | null>(null);\n\n if (!spotrRef.current || spotrRef.current.options !== optionsRef.current) {\n spotrRef.current = new Spotr(optionsRef.current);\n }\n\n return spotrRef.current;\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["useSpotr","options","optionsRef","useRef","shallowEqual","spotrRef","Spotr"],"mappings":";;;AAaO,SAASA,EAA2BC,GAAoC;AAC7E,QAAMC,IAAaC,EAAOF,CAAO;AAEjC,EAAKG,EAAaF,EAAW,SAASD,CAAO,MAC3CC,EAAW,UAAUD;AAGvB,QAAMI,IAAWF,EAAwB,IAAI;AAE7C,UAAI,CAACE,EAAS,WAAWA,EAAS,QAAQ,YAAYH,EAAW,aAC/DG,EAAS,UAAU,IAAIC,EAAMJ,EAAW,OAAO,IAG1CG,EAAS;AAClB;"}
package/dist/react.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("react"),o=require("./Spotr.cjs");function s(r,e){if(r===e)return!0;if(typeof r!="object"||typeof e!="object"||r===null||e===null)return!1;const t=Object.keys(r),c=Object.keys(e);if(t.length!==c.length)return!1;for(const n of t)if(r[n]!==e[n])return!1;return!0}function f(r){const e=u.useRef(r);s(e.current,r)||(e.current=r);const t=u.useRef(null);return(!t.current||t.current.options!==e.current)&&(t.current=new o.Spotr(e.current)),t.current}exports.Spotr=o.Spotr;exports.useSpotr=f;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("react"),n=require("./Spotr.cjs"),o=require("./utils/shallowEqual.cjs");function c(t){const r=u.useRef(t);o.shallowEqual(r.current,t)||(r.current=t);const e=u.useRef(null);return(!e.current||e.current.options!==r.current)&&(e.current=new n.Spotr(r.current)),e.current}exports.Spotr=n.Spotr;exports.useSpotr=c;
2
2
  //# sourceMappingURL=react.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"react.cjs","sources":["../src/react/index.ts"],"sourcesContent":["import { useRef } from 'react';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\n\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (\n typeof a !== 'object' ||\n typeof b !== 'object' ||\n a === null ||\n b === null\n ) {\n return false;\n }\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (\n (a as Record<string, unknown>)[key] !==\n (b as Record<string, unknown>)[key]\n ) {\n return false;\n }\n }\n\n return true;\n}\n\n// Overload: infer T from collection\nexport function useSpotr<C extends readonly object[] | object[] | Set<object>>(\n options: Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {\n collection: C;\n }\n): Spotr<ExtractItemType<C> & object>;\n// Overload: explicit generic\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T>;\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T> {\n const optionsRef = useRef(options);\n\n if (!shallowEqual(optionsRef.current, options)) {\n optionsRef.current = options;\n }\n\n const spotrRef = useRef<Spotr<T> | null>(null);\n\n if (!spotrRef.current || spotrRef.current.options !== optionsRef.current) {\n spotrRef.current = new Spotr(optionsRef.current);\n }\n\n return spotrRef.current;\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["shallowEqual","a","b","keysA","keysB","key","useSpotr","options","optionsRef","useRef","spotrRef","Spotr"],"mappings":"kIAIA,SAASA,EAAaC,EAAYC,EAAqB,CACrD,GAAID,IAAMC,EAAG,MAAO,GACpB,GACE,OAAOD,GAAM,UACb,OAAOC,GAAM,UACbD,IAAM,MACNC,IAAM,KAEN,MAAO,GAGT,MAAMC,EAAQ,OAAO,KAAKF,CAAC,EACrBG,EAAQ,OAAO,KAAKF,CAAC,EAE3B,GAAIC,EAAM,SAAWC,EAAM,OAAQ,MAAO,GAE1C,UAAWC,KAAOF,EAChB,GACGF,EAA8BI,CAAG,IACjCH,EAA8BG,CAAG,EAElC,MAAO,GAIX,MAAO,EACT,CAUO,SAASC,EAA2BC,EAAoC,CAC7E,MAAMC,EAAaC,EAAAA,OAAOF,CAAO,EAE5BP,EAAaQ,EAAW,QAASD,CAAO,IAC3CC,EAAW,QAAUD,GAGvB,MAAMG,EAAWD,EAAAA,OAAwB,IAAI,EAE7C,OAAI,CAACC,EAAS,SAAWA,EAAS,QAAQ,UAAYF,EAAW,WAC/DE,EAAS,QAAU,IAAIC,QAAMH,EAAW,OAAO,GAG1CE,EAAS,OAClB"}
1
+ {"version":3,"file":"react.cjs","sources":["../src/react/index.ts"],"sourcesContent":["import { useRef } from 'react';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\nimport { shallowEqual } from '../utils';\n\n// Overload: infer T from collection\nexport function useSpotr<C extends readonly object[] | object[] | Set<object>>(\n options: Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {\n collection: C;\n }\n): Spotr<ExtractItemType<C> & object>;\n// Overload: explicit generic\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T>;\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T> {\n const optionsRef = useRef(options);\n\n if (!shallowEqual(optionsRef.current, options)) {\n optionsRef.current = options;\n }\n\n const spotrRef = useRef<Spotr<T> | null>(null);\n\n if (!spotrRef.current || spotrRef.current.options !== optionsRef.current) {\n spotrRef.current = new Spotr(optionsRef.current);\n }\n\n return spotrRef.current;\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["useSpotr","options","optionsRef","useRef","shallowEqual","spotrRef","Spotr"],"mappings":"wKAaO,SAASA,EAA2BC,EAAoC,CAC7E,MAAMC,EAAaC,EAAAA,OAAOF,CAAO,EAE5BG,EAAAA,aAAaF,EAAW,QAASD,CAAO,IAC3CC,EAAW,QAAUD,GAGvB,MAAMI,EAAWF,EAAAA,OAAwB,IAAI,EAE7C,OAAI,CAACE,EAAS,SAAWA,EAAS,QAAQ,UAAYH,EAAW,WAC/DG,EAAS,QAAU,IAAIC,QAAMJ,EAAW,OAAO,GAG1CG,EAAS,OAClB"}
package/dist/react.js CHANGED
@@ -1,24 +1,14 @@
1
- import { useRef as u } from "react";
2
- import { Spotr as f } from "./Spotr.js";
3
- function c(r, e) {
4
- if (r === e) return !0;
5
- if (typeof r != "object" || typeof e != "object" || r === null || e === null)
6
- return !1;
7
- const t = Object.keys(r), o = Object.keys(e);
8
- if (t.length !== o.length) return !1;
9
- for (const n of t)
10
- if (r[n] !== e[n])
11
- return !1;
12
- return !0;
13
- }
14
- function i(r) {
15
- const e = u(r);
16
- c(e.current, r) || (e.current = r);
17
- const t = u(null);
18
- return (!t.current || t.current.options !== e.current) && (t.current = new f(e.current)), t.current;
1
+ import { useRef as n } from "react";
2
+ import { Spotr as o } from "./Spotr.js";
3
+ import { shallowEqual as u } from "./utils/shallowEqual.js";
4
+ function i(e) {
5
+ const r = n(e);
6
+ u(r.current, e) || (r.current = e);
7
+ const t = n(null);
8
+ return (!t.current || t.current.options !== r.current) && (t.current = new o(r.current)), t.current;
19
9
  }
20
10
  export {
21
- f as Spotr,
11
+ o as Spotr,
22
12
  i as useSpotr
23
13
  };
24
14
  //# sourceMappingURL=react.js.map
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","sources":["../src/react/index.ts"],"sourcesContent":["import { useRef } from 'react';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\n\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (\n typeof a !== 'object' ||\n typeof b !== 'object' ||\n a === null ||\n b === null\n ) {\n return false;\n }\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (\n (a as Record<string, unknown>)[key] !==\n (b as Record<string, unknown>)[key]\n ) {\n return false;\n }\n }\n\n return true;\n}\n\n// Overload: infer T from collection\nexport function useSpotr<C extends readonly object[] | object[] | Set<object>>(\n options: Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {\n collection: C;\n }\n): Spotr<ExtractItemType<C> & object>;\n// Overload: explicit generic\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T>;\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T> {\n const optionsRef = useRef(options);\n\n if (!shallowEqual(optionsRef.current, options)) {\n optionsRef.current = options;\n }\n\n const spotrRef = useRef<Spotr<T> | null>(null);\n\n if (!spotrRef.current || spotrRef.current.options !== optionsRef.current) {\n spotrRef.current = new Spotr(optionsRef.current);\n }\n\n return spotrRef.current;\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["shallowEqual","a","b","keysA","keysB","key","useSpotr","options","optionsRef","useRef","spotrRef","Spotr"],"mappings":";;AAIA,SAASA,EAAaC,GAAYC,GAAqB;AACrD,MAAID,MAAMC,EAAG,QAAO;AACpB,MACE,OAAOD,KAAM,YACb,OAAOC,KAAM,YACbD,MAAM,QACNC,MAAM;AAEN,WAAO;AAGT,QAAMC,IAAQ,OAAO,KAAKF,CAAC,GACrBG,IAAQ,OAAO,KAAKF,CAAC;AAE3B,MAAIC,EAAM,WAAWC,EAAM,OAAQ,QAAO;AAE1C,aAAWC,KAAOF;AAChB,QACGF,EAA8BI,CAAG,MACjCH,EAA8BG,CAAG;AAElC,aAAO;AAIX,SAAO;AACT;AAUO,SAASC,EAA2BC,GAAoC;AAC7E,QAAMC,IAAaC,EAAOF,CAAO;AAEjC,EAAKP,EAAaQ,EAAW,SAASD,CAAO,MAC3CC,EAAW,UAAUD;AAGvB,QAAMG,IAAWD,EAAwB,IAAI;AAE7C,UAAI,CAACC,EAAS,WAAWA,EAAS,QAAQ,YAAYF,EAAW,aAC/DE,EAAS,UAAU,IAAIC,EAAMH,EAAW,OAAO,IAG1CE,EAAS;AAClB;"}
1
+ {"version":3,"file":"react.js","sources":["../src/react/index.ts"],"sourcesContent":["import { useRef } from 'react';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\nimport { shallowEqual } from '../utils';\n\n// Overload: infer T from collection\nexport function useSpotr<C extends readonly object[] | object[] | Set<object>>(\n options: Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {\n collection: C;\n }\n): Spotr<ExtractItemType<C> & object>;\n// Overload: explicit generic\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T>;\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T> {\n const optionsRef = useRef(options);\n\n if (!shallowEqual(optionsRef.current, options)) {\n optionsRef.current = options;\n }\n\n const spotrRef = useRef<Spotr<T> | null>(null);\n\n if (!spotrRef.current || spotrRef.current.options !== optionsRef.current) {\n spotrRef.current = new Spotr(optionsRef.current);\n }\n\n return spotrRef.current;\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["useSpotr","options","optionsRef","useRef","shallowEqual","spotrRef","Spotr"],"mappings":";;;AAaO,SAASA,EAA2BC,GAAoC;AAC7E,QAAMC,IAAaC,EAAOF,CAAO;AAEjC,EAAKG,EAAaF,EAAW,SAASD,CAAO,MAC3CC,EAAW,UAAUD;AAGvB,QAAMI,IAAWF,EAAwB,IAAI;AAE7C,UAAI,CAACE,EAAS,WAAWA,EAAS,QAAQ,YAAYH,EAAW,aAC/DG,EAAS,UAAU,IAAIC,EAAMJ,EAAW,OAAO,IAG1CG,EAAS;AAClB;"}
package/dist/solid.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("solid-js"),r=require("./Spotr.cjs");function u(o){const s=new r.Spotr(o),[e,c]=t.createSignal(""),n=t.createMemo(()=>s.query(e()));return{query:e,setQuery:c,results:n}}exports.Spotr=r.Spotr;exports.createSpotr=u;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("solid-js"),t=require("./Spotr.cjs");function c(e){return o.createMemo(()=>{const r=typeof e=="function"?e():e;return new t.Spotr(r)})}exports.Spotr=t.Spotr;exports.createSpotr=c;
2
2
  //# sourceMappingURL=solid.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"solid.cjs","sources":["../src/solid/index.ts"],"sourcesContent":["import { createSignal, createMemo } from 'solid-js';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\n\n// Overload: infer T from collection\nexport function createSpotr<\n C extends readonly object[] | object[] | Set<object>,\n>(\n options: Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {\n collection: C;\n }\n): {\n query: () => string;\n setQuery: (query: string) => void;\n results: () => import('../types').SpotrResult<ExtractItemType<C> & object>;\n};\n// Overload: explicit generic\nexport function createSpotr<T extends object>(\n options: SpotrOptions<T>\n): {\n query: () => string;\n setQuery: (query: string) => void;\n results: () => import('../types').SpotrResult<T>;\n};\nexport function createSpotr<T extends object>(options: SpotrOptions<T>) {\n const spotr = new Spotr(options);\n const [query, setQuery] = createSignal('');\n const results = createMemo(() => spotr.query(query()));\n return { query, setQuery, results };\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["createSpotr","options","spotr","Spotr","query","setQuery","createSignal","results","createMemo"],"mappings":"qIAwBO,SAASA,EAA8BC,EAA0B,CACtE,MAAMC,EAAQ,IAAIC,EAAAA,MAAMF,CAAO,EACzB,CAACG,EAAOC,CAAQ,EAAIC,EAAAA,aAAa,EAAE,EACnCC,EAAUC,EAAAA,WAAW,IAAMN,EAAM,MAAME,EAAA,CAAO,CAAC,EACrD,MAAO,CAAE,MAAAA,EAAO,SAAAC,EAAU,QAAAE,CAAA,CAC5B"}
1
+ {"version":3,"file":"solid.cjs","sources":["../src/solid/index.ts"],"sourcesContent":["import { createMemo } from 'solid-js';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\n\n// Overload: infer T from collection\nexport function createSpotr<\n C extends readonly object[] | object[] | Set<object>,\n>(\n options:\n | (Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & { collection: C })\n | (() => Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {\n collection: C;\n })\n): () => Spotr<ExtractItemType<C> & object>;\n// Overload: explicit generic\nexport function createSpotr<T extends object>(\n options: SpotrOptions<T> | (() => SpotrOptions<T>)\n): () => Spotr<T>;\nexport function createSpotr<T extends object>(\n options: SpotrOptions<T> | (() => SpotrOptions<T>)\n): () => Spotr<T> {\n return createMemo(() => {\n const opts = typeof options === 'function' ? options() : options;\n return new Spotr(opts);\n }) as () => Spotr<T>;\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["createSpotr","options","createMemo","opts","Spotr"],"mappings":"qIAkBO,SAASA,EACdC,EACgB,CAChB,OAAOC,EAAAA,WAAW,IAAM,CACtB,MAAMC,EAAO,OAAOF,GAAY,WAAaA,IAAYA,EACzD,OAAO,IAAIG,EAAAA,MAAMD,CAAI,CACvB,CAAC,CACH"}
package/dist/solid.d.ts CHANGED
@@ -1,16 +1,10 @@
1
- export declare function createSpotr<C extends readonly object[] | object[] | Set<object>>(options: Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {
1
+ export declare function createSpotr<C extends readonly object[] | object[] | Set<object>>(options: (Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {
2
2
  collection: C;
3
- }): {
4
- query: () => string;
5
- setQuery: (query: string) => void;
6
- results: () => SpotrResult<ExtractItemType<C> & object>;
7
- };
3
+ }) | (() => Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {
4
+ collection: C;
5
+ })): () => Spotr<ExtractItemType<C> & object>;
8
6
 
9
- export declare function createSpotr<T extends object>(options: SpotrOptions<T>): {
10
- query: () => string;
11
- setQuery: (query: string) => void;
12
- results: () => SpotrResult<T>;
13
- };
7
+ export declare function createSpotr<T extends object>(options: SpotrOptions<T> | (() => SpotrOptions<T>)): () => Spotr<T>;
14
8
 
15
9
  /* Excluded from this release type: ExtractItemType */
16
10
 
package/dist/solid.js CHANGED
@@ -1,11 +1,13 @@
1
- import { createSignal as c, createMemo as s } from "solid-js";
2
- import { Spotr as p } from "./Spotr.js";
3
- function a(t) {
4
- const e = new p(t), [r, o] = c(""), n = s(() => e.query(r()));
5
- return { query: r, setQuery: o, results: n };
1
+ import { createMemo as e } from "solid-js";
2
+ import { Spotr as o } from "./Spotr.js";
3
+ function f(r) {
4
+ return e(() => {
5
+ const t = typeof r == "function" ? r() : r;
6
+ return new o(t);
7
+ });
6
8
  }
7
9
  export {
8
- p as Spotr,
9
- a as createSpotr
10
+ o as Spotr,
11
+ f as createSpotr
10
12
  };
11
13
  //# sourceMappingURL=solid.js.map
package/dist/solid.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"solid.js","sources":["../src/solid/index.ts"],"sourcesContent":["import { createSignal, createMemo } from 'solid-js';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\n\n// Overload: infer T from collection\nexport function createSpotr<\n C extends readonly object[] | object[] | Set<object>,\n>(\n options: Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {\n collection: C;\n }\n): {\n query: () => string;\n setQuery: (query: string) => void;\n results: () => import('../types').SpotrResult<ExtractItemType<C> & object>;\n};\n// Overload: explicit generic\nexport function createSpotr<T extends object>(\n options: SpotrOptions<T>\n): {\n query: () => string;\n setQuery: (query: string) => void;\n results: () => import('../types').SpotrResult<T>;\n};\nexport function createSpotr<T extends object>(options: SpotrOptions<T>) {\n const spotr = new Spotr(options);\n const [query, setQuery] = createSignal('');\n const results = createMemo(() => spotr.query(query()));\n return { query, setQuery, results };\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["createSpotr","options","spotr","Spotr","query","setQuery","createSignal","results","createMemo"],"mappings":";;AAwBO,SAASA,EAA8BC,GAA0B;AACtE,QAAMC,IAAQ,IAAIC,EAAMF,CAAO,GACzB,CAACG,GAAOC,CAAQ,IAAIC,EAAa,EAAE,GACnCC,IAAUC,EAAW,MAAMN,EAAM,MAAME,EAAA,CAAO,CAAC;AACrD,SAAO,EAAE,OAAAA,GAAO,UAAAC,GAAU,SAAAE,EAAA;AAC5B;"}
1
+ {"version":3,"file":"solid.js","sources":["../src/solid/index.ts"],"sourcesContent":["import { createMemo } from 'solid-js';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\n\n// Overload: infer T from collection\nexport function createSpotr<\n C extends readonly object[] | object[] | Set<object>,\n>(\n options:\n | (Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & { collection: C })\n | (() => Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {\n collection: C;\n })\n): () => Spotr<ExtractItemType<C> & object>;\n// Overload: explicit generic\nexport function createSpotr<T extends object>(\n options: SpotrOptions<T> | (() => SpotrOptions<T>)\n): () => Spotr<T>;\nexport function createSpotr<T extends object>(\n options: SpotrOptions<T> | (() => SpotrOptions<T>)\n): () => Spotr<T> {\n return createMemo(() => {\n const opts = typeof options === 'function' ? options() : options;\n return new Spotr(opts);\n }) as () => Spotr<T>;\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["createSpotr","options","createMemo","opts","Spotr"],"mappings":";;AAkBO,SAASA,EACdC,GACgB;AAChB,SAAOC,EAAW,MAAM;AACtB,UAAMC,IAAO,OAAOF,KAAY,aAAaA,MAAYA;AACzD,WAAO,IAAIG,EAAMD,CAAI;AAAA,EACvB,CAAC;AACH;"}
package/dist/svelte.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("svelte/store"),o=require("./Spotr.cjs");function u(n){const r=new o.Spotr(n),t=e.writable(""),s=e.derived(t,c=>r.query(c));return{spotr:r,query:t,results:s}}exports.Spotr=o.Spotr;exports.createSpotr=u;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("svelte/store"),r=require("./Spotr.cjs");function c(e){return e!=null&&typeof e.subscribe=="function"?t.derived(e,u=>new r.Spotr(u)):t.readable(new r.Spotr(e))}exports.Spotr=r.Spotr;exports.createSpotr=c;
2
2
  //# sourceMappingURL=svelte.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"svelte.cjs","sources":["../src/svelte/index.ts"],"sourcesContent":["import { writable, derived } from 'svelte/store';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\n\n// Overload: infer T from collection\nexport function createSpotr<\n C extends readonly object[] | object[] | Set<object>,\n>(\n options: Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {\n collection: C;\n }\n): {\n spotr: Spotr<ExtractItemType<C> & object>;\n query: import('svelte/store').Writable<string>;\n results: import('svelte/store').Readable<\n import('../types').SpotrResult<ExtractItemType<C> & object>\n >;\n};\n// Overload: explicit generic\nexport function createSpotr<T extends object>(\n options: SpotrOptions<T>\n): {\n spotr: Spotr<T>;\n query: import('svelte/store').Writable<string>;\n results: import('svelte/store').Readable<import('../types').SpotrResult<T>>;\n};\nexport function createSpotr<T extends object>(options: SpotrOptions<T>) {\n const spotr = new Spotr(options);\n const query = writable('');\n const results = derived(query, ($query: string) => spotr.query($query));\n return { spotr, query, results };\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["createSpotr","options","spotr","Spotr","query","writable","results","derived","$query"],"mappings":"yIA0BO,SAASA,EAA8BC,EAA0B,CACtE,MAAMC,EAAQ,IAAIC,EAAAA,MAAMF,CAAO,EACzBG,EAAQC,EAAAA,SAAS,EAAE,EACnBC,EAAUC,EAAAA,QAAQH,EAAQI,GAAmBN,EAAM,MAAMM,CAAM,CAAC,EACtE,MAAO,CAAE,MAAAN,EAAO,MAAAE,EAAO,QAAAE,CAAA,CACzB"}
1
+ {"version":3,"file":"svelte.cjs","sources":["../src/svelte/index.ts"],"sourcesContent":["import { readable, derived } from 'svelte/store';\nimport type { Readable } from 'svelte/store';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\n\n// Overload: infer T from collection\nexport function createSpotr<\n C extends readonly object[] | object[] | Set<object>,\n>(\n options:\n | (Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & { collection: C })\n | Readable<\n Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & { collection: C }\n >\n): Readable<Spotr<ExtractItemType<C> & object>>;\n// Overload: explicit generic\nexport function createSpotr<T extends object>(\n options: SpotrOptions<T> | Readable<SpotrOptions<T>>\n): Readable<Spotr<T>>;\nexport function createSpotr<T extends object>(\n options: SpotrOptions<T> | Readable<SpotrOptions<T>>\n): Readable<Spotr<T>> {\n const hasSubscribe =\n options != null &&\n typeof (options as Readable<SpotrOptions<T>>).subscribe === 'function';\n\n if (hasSubscribe) {\n return derived(\n options as Readable<SpotrOptions<T>>,\n (opts) => new Spotr(opts)\n );\n }\n return readable(new Spotr(options as SpotrOptions<T>));\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["createSpotr","options","derived","opts","Spotr","readable"],"mappings":"yIAmBO,SAASA,EACdC,EACoB,CAKpB,OAHEA,GAAW,MACX,OAAQA,EAAsC,WAAc,WAGrDC,EAAAA,QACLD,EACCE,GAAS,IAAIC,EAAAA,MAAMD,CAAI,CAAA,EAGrBE,WAAS,IAAID,QAAMH,CAA0B,CAAC,CACvD"}
package/dist/svelte.d.ts CHANGED
@@ -1,19 +1,12 @@
1
1
  import { Readable } from 'svelte/store';
2
- import { Writable } from 'svelte/store';
3
2
 
4
- export declare function createSpotr<C extends readonly object[] | object[] | Set<object>>(options: Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {
3
+ export declare function createSpotr<C extends readonly object[] | object[] | Set<object>>(options: (Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {
5
4
  collection: C;
6
- }): {
7
- spotr: Spotr<ExtractItemType<C> & object>;
8
- query: Writable<string>;
9
- results: Readable<SpotrResult<ExtractItemType<C> & object>>;
10
- };
5
+ }) | Readable<Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {
6
+ collection: C;
7
+ }>): Readable<Spotr<ExtractItemType<C> & object>>;
11
8
 
12
- export declare function createSpotr<T extends object>(options: SpotrOptions<T>): {
13
- spotr: Spotr<T>;
14
- query: Writable<string>;
15
- results: Readable<SpotrResult<T>>;
16
- };
9
+ export declare function createSpotr<T extends object>(options: SpotrOptions<T> | Readable<SpotrOptions<T>>): Readable<Spotr<T>>;
17
10
 
18
11
  /* Excluded from this release type: ExtractItemType */
19
12
 
package/dist/svelte.js CHANGED
@@ -1,11 +1,13 @@
1
- import { writable as p, derived as s } from "svelte/store";
2
- import { Spotr as c } from "./Spotr.js";
3
- function m(o) {
4
- const r = new c(o), t = p(""), e = s(t, (n) => r.query(n));
5
- return { spotr: r, query: t, results: e };
1
+ import { derived as u, readable as b } from "svelte/store";
2
+ import { Spotr as e } from "./Spotr.js";
3
+ function i(r) {
4
+ return r != null && typeof r.subscribe == "function" ? u(
5
+ r,
6
+ (t) => new e(t)
7
+ ) : b(new e(r));
6
8
  }
7
9
  export {
8
- c as Spotr,
9
- m as createSpotr
10
+ e as Spotr,
11
+ i as createSpotr
10
12
  };
11
13
  //# sourceMappingURL=svelte.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"svelte.js","sources":["../src/svelte/index.ts"],"sourcesContent":["import { writable, derived } from 'svelte/store';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\n\n// Overload: infer T from collection\nexport function createSpotr<\n C extends readonly object[] | object[] | Set<object>,\n>(\n options: Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {\n collection: C;\n }\n): {\n spotr: Spotr<ExtractItemType<C> & object>;\n query: import('svelte/store').Writable<string>;\n results: import('svelte/store').Readable<\n import('../types').SpotrResult<ExtractItemType<C> & object>\n >;\n};\n// Overload: explicit generic\nexport function createSpotr<T extends object>(\n options: SpotrOptions<T>\n): {\n spotr: Spotr<T>;\n query: import('svelte/store').Writable<string>;\n results: import('svelte/store').Readable<import('../types').SpotrResult<T>>;\n};\nexport function createSpotr<T extends object>(options: SpotrOptions<T>) {\n const spotr = new Spotr(options);\n const query = writable('');\n const results = derived(query, ($query: string) => spotr.query($query));\n return { spotr, query, results };\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["createSpotr","options","spotr","Spotr","query","writable","results","derived","$query"],"mappings":";;AA0BO,SAASA,EAA8BC,GAA0B;AACtE,QAAMC,IAAQ,IAAIC,EAAMF,CAAO,GACzBG,IAAQC,EAAS,EAAE,GACnBC,IAAUC,EAAQH,GAAO,CAACI,MAAmBN,EAAM,MAAMM,CAAM,CAAC;AACtE,SAAO,EAAE,OAAAN,GAAO,OAAAE,GAAO,SAAAE,EAAA;AACzB;"}
1
+ {"version":3,"file":"svelte.js","sources":["../src/svelte/index.ts"],"sourcesContent":["import { readable, derived } from 'svelte/store';\nimport type { Readable } from 'svelte/store';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\n\n// Overload: infer T from collection\nexport function createSpotr<\n C extends readonly object[] | object[] | Set<object>,\n>(\n options:\n | (Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & { collection: C })\n | Readable<\n Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & { collection: C }\n >\n): Readable<Spotr<ExtractItemType<C> & object>>;\n// Overload: explicit generic\nexport function createSpotr<T extends object>(\n options: SpotrOptions<T> | Readable<SpotrOptions<T>>\n): Readable<Spotr<T>>;\nexport function createSpotr<T extends object>(\n options: SpotrOptions<T> | Readable<SpotrOptions<T>>\n): Readable<Spotr<T>> {\n const hasSubscribe =\n options != null &&\n typeof (options as Readable<SpotrOptions<T>>).subscribe === 'function';\n\n if (hasSubscribe) {\n return derived(\n options as Readable<SpotrOptions<T>>,\n (opts) => new Spotr(opts)\n );\n }\n return readable(new Spotr(options as SpotrOptions<T>));\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["createSpotr","options","derived","opts","Spotr","readable"],"mappings":";;AAmBO,SAASA,EACdC,GACoB;AAKpB,SAHEA,KAAW,QACX,OAAQA,EAAsC,aAAc,aAGrDC;AAAA,IACLD;AAAA,IACA,CAACE,MAAS,IAAIC,EAAMD,CAAI;AAAA,EAAA,IAGrBE,EAAS,IAAID,EAAMH,CAA0B,CAAC;AACvD;"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function o(e,t){if(e===t)return!0;if(typeof e!="object"||typeof t!="object"||e===null||t===null)return!1;const l=Object.keys(e),n=Object.keys(t);if(l.length!==n.length)return!1;for(const r of l)if(e[r]!==t[r])return!1;return!0}exports.shallowEqual=o;
2
+ //# sourceMappingURL=shallowEqual.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shallowEqual.cjs","sources":["../../src/utils/shallowEqual.ts"],"sourcesContent":["export function shallowEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (\n typeof a !== 'object' ||\n typeof b !== 'object' ||\n a === null ||\n b === null\n ) {\n return false;\n }\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (\n (a as Record<string, unknown>)[key] !==\n (b as Record<string, unknown>)[key]\n ) {\n return false;\n }\n }\n\n return true;\n}\n"],"names":["shallowEqual","a","b","keysA","keysB","key"],"mappings":"gFAAO,SAASA,EAAaC,EAAYC,EAAqB,CAC5D,GAAID,IAAMC,EAAG,MAAO,GACpB,GACE,OAAOD,GAAM,UACb,OAAOC,GAAM,UACbD,IAAM,MACNC,IAAM,KAEN,MAAO,GAGT,MAAMC,EAAQ,OAAO,KAAKF,CAAC,EACrBG,EAAQ,OAAO,KAAKF,CAAC,EAE3B,GAAIC,EAAM,SAAWC,EAAM,OAAQ,MAAO,GAE1C,UAAWC,KAAOF,EAChB,GACGF,EAA8BI,CAAG,IACjCH,EAA8BG,CAAG,EAElC,MAAO,GAIX,MAAO,EACT"}
@@ -0,0 +1,15 @@
1
+ function l(e, t) {
2
+ if (e === t) return !0;
3
+ if (typeof e != "object" || typeof t != "object" || e === null || t === null)
4
+ return !1;
5
+ const n = Object.keys(e), f = Object.keys(t);
6
+ if (n.length !== f.length) return !1;
7
+ for (const r of n)
8
+ if (e[r] !== t[r])
9
+ return !1;
10
+ return !0;
11
+ }
12
+ export {
13
+ l as shallowEqual
14
+ };
15
+ //# sourceMappingURL=shallowEqual.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shallowEqual.js","sources":["../../src/utils/shallowEqual.ts"],"sourcesContent":["export function shallowEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (\n typeof a !== 'object' ||\n typeof b !== 'object' ||\n a === null ||\n b === null\n ) {\n return false;\n }\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (\n (a as Record<string, unknown>)[key] !==\n (b as Record<string, unknown>)[key]\n ) {\n return false;\n }\n }\n\n return true;\n}\n"],"names":["shallowEqual","a","b","keysA","keysB","key"],"mappings":"AAAO,SAASA,EAAaC,GAAYC,GAAqB;AAC5D,MAAID,MAAMC,EAAG,QAAO;AACpB,MACE,OAAOD,KAAM,YACb,OAAOC,KAAM,YACbD,MAAM,QACNC,MAAM;AAEN,WAAO;AAGT,QAAMC,IAAQ,OAAO,KAAKF,CAAC,GACrBG,IAAQ,OAAO,KAAKF,CAAC;AAE3B,MAAIC,EAAM,WAAWC,EAAM,OAAQ,QAAO;AAE1C,aAAWC,KAAOF;AAChB,QACGF,EAA8BI,CAAG,MACjCH,EAA8BG,CAAG;AAElC,aAAO;AAIX,SAAO;AACT;"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "spotr",
3
- "version": "1.0.0-alpha.2",
4
- "description": "A powerful and minimal client-side fuzzy search library",
3
+ "version": "1.0.0-alpha.4",
4
+ "description": "A powerful fuzzy search library for client-side collections in TypeScript",
5
5
  "author": "Andy Merskin <andymerskin@gmail.com>",
6
6
  "license": "MIT",
7
7
  "repository": "https://github.com/andymerskin/spotr",