spotr 1.0.0-alpha.0 → 1.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/Spotr.cjs +1 -1
  2. package/dist/Spotr.cjs.map +1 -1
  3. package/dist/Spotr.js +84 -78
  4. package/dist/Spotr.js.map +1 -1
  5. package/dist/bundle-size.json +4 -0
  6. package/dist/errors.cjs +1 -1
  7. package/dist/errors.cjs.map +1 -1
  8. package/dist/errors.js +6 -5
  9. package/dist/errors.js.map +1 -1
  10. package/dist/fuzzy/levenshtein.cjs +1 -1
  11. package/dist/fuzzy/levenshtein.cjs.map +1 -1
  12. package/dist/fuzzy/levenshtein.js +39 -23
  13. package/dist/fuzzy/levenshtein.js.map +1 -1
  14. package/dist/fuzzy/scorer.cjs +1 -1
  15. package/dist/fuzzy/scorer.cjs.map +1 -1
  16. package/dist/fuzzy/scorer.js +26 -19
  17. package/dist/fuzzy/scorer.js.map +1 -1
  18. package/dist/index.cjs +1 -1
  19. package/dist/index.d.ts +21 -3
  20. package/dist/index.js +2 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/preact.cjs.map +1 -1
  23. package/dist/preact.d.ts +23 -2
  24. package/dist/preact.js.map +1 -1
  25. package/dist/react.cjs.map +1 -1
  26. package/dist/react.d.ts +23 -2
  27. package/dist/react.js.map +1 -1
  28. package/dist/solid.cjs.map +1 -1
  29. package/dist/solid.d.ts +29 -24
  30. package/dist/solid.js.map +1 -1
  31. package/dist/svelte.cjs.map +1 -1
  32. package/dist/svelte.d.ts +28 -20
  33. package/dist/svelte.js.map +1 -1
  34. package/dist/types.cjs +2 -0
  35. package/dist/types.cjs.map +1 -0
  36. package/dist/types.js +5 -0
  37. package/dist/types.js.map +1 -0
  38. package/dist/utils/validate.cjs +1 -1
  39. package/dist/utils/validate.cjs.map +1 -1
  40. package/dist/utils/validate.js +36 -29
  41. package/dist/utils/validate.js.map +1 -1
  42. package/dist/vue.cjs.map +1 -1
  43. package/dist/vue.d.ts +24 -3
  44. package/dist/vue.js.map +1 -1
  45. package/package.json +30 -18
  46. package/LICENSE +0 -21
  47. package/README.md +0 -185
package/dist/Spotr.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var k=Object.defineProperty;var T=(u,e,t)=>e in u?k(u,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):u[e]=t;var c=(u,e,t)=>T(u,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("./errors.cjs"),y=require("./utils/validate.cjs"),f=require("./fuzzy/scorer.cjs"),S=require("./utils/tokenize.cjs");class K{constructor(e){c(this,"_collection");c(this,"_fields");c(this,"_keywords");c(this,"_keywordTriggerMap");c(this,"_threshold");c(this,"_limit");c(this,"_debounce");c(this,"_caseSensitive");c(this,"_minMatchCharLength");c(this,"_debounceTimer",null);c(this,"_optionsSnapshot");this._optionsSnapshot=e;const t=y.validateOptions(e);this._collection=t.collection,this._threshold=t.threshold,this._limit=t.limit,this._debounce=t.debounce,this._caseSensitive=t.caseSensitive,this._minMatchCharLength=t.minMatchCharLength,this._fields=f.normalizeFieldConfig(e.fields,this._threshold),this._keywords=e.keywords?y.validateKeywords(e.keywords):null,this._keywordTriggerMap=this._buildKeywordTriggerMap()}_buildKeywordTriggerMap(){const e=new Map;if(!this._keywords)return e;for(const t of this._keywords.definitions){const i=Array.isArray(t.triggers)?t.triggers:[t.triggers];for(const r of i)e.set(r.toLowerCase(),t)}return e}get collection(){return this._collection}get options(){return this._optionsSnapshot}setCollection(e){this._collection=e instanceof Set?Array.from(e):e}query(e){const t=S.tokenize(e);if(t.length===0)return{results:[],matchedKeywords:[],tokens:[],warnings:[]};const{keywordTokens:i,searchTokens:r}=this._extractKeywords(t);let a=this._collection;const s=[];if(i.size>0){const o=this._applyKeywords(a,i);a=o.collection,s.push(...o.matchedKeywords)}if(r.length===0){const o=[];return{results:a.map(_=>{const{score:g,warnings:p}=f.scoreItem(_,[],this._fields,this._caseSensitive);return o.push(...p),{item:_,score:g}}),matchedKeywords:s,tokens:r,warnings:[...new Set(o)]}}const n=this._minMatchCharLength>1?r.filter(o=>o.length>=this._minMatchCharLength):r;if(n.length===0)return{results:[],matchedKeywords:s,tokens:r,warnings:[]};const h=[],l=[];for(const o of a){const{score:d,warnings:_}=f.scoreItem(o,n,this._fields,this._caseSensitive);h.push(..._),d>0&&l.push({item:o,score:d})}return l.sort((o,d)=>d.score-o.score),{results:this._limit<1/0?l.slice(0,this._limit):l,matchedKeywords:s,tokens:r,warnings:[...new Set(h)]}}queryAsync(e){return new Promise(t=>{this._debounce>0?(this._debounceTimer&&clearTimeout(this._debounceTimer),this._debounceTimer=setTimeout(()=>{t(this.query(e))},this._debounce)):t(this.query(e))})}_extractKeywords(e){const t=new Map,i=[];for(const r of e){const a=this._caseSensitive?r:r.toLowerCase(),s=this._keywordTriggerMap.get(a);if(s){const n=t.get(s.name)||[];n.push(r),t.set(s.name,n)}else i.push(r)}return{keywordTokens:t,searchTokens:i}}_applyKeywords(e,t){if(!this._keywords)return{collection:e,matchedKeywords:[]};let i=e;const r=[],a=new Map;for(const[s,n]of t)a.set(s,n);if(this._keywords.mode==="and")for(const s of this._keywords.definitions){const n=a.get(s.name);if(n){const h=s.handler;if(i=h(i,n),r.push({name:s.name,terms:n}),!Array.isArray(i))throw new m.SpotrError(`Keyword handler "${s.name}" must return an array`,m.ErrorCodes.INVALID_HANDLER_RETURN)}}else{const s=[],n=new Set;for(const h of this._keywords.definitions){const l=a.get(h.name);if(l){const w=h.handler,o=w(this._collection,l);if(r.push({name:h.name,terms:l}),!Array.isArray(o))throw new m.SpotrError(`Keyword handler "${h.name}" must return an array`,m.ErrorCodes.INVALID_HANDLER_RETURN);for(const d of o)n.has(d)||(n.add(d),s.push(d))}}i=s}return{collection:i,matchedKeywords:r}}}exports.Spotr=K;
1
+ "use strict";var p=Object.defineProperty;var k=(u,e,t)=>e in u?p(u,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):u[e]=t;var o=(u,e,t)=>k(u,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("./utils/validate.cjs"),_=require("./fuzzy/scorer.cjs"),S=require("./utils/tokenize.cjs");class T{constructor(e){o(this,"_collection");o(this,"_fields");o(this,"_keywords");o(this,"_keywordTriggerMap");o(this,"_threshold");o(this,"_limit");o(this,"_debounce");o(this,"_caseSensitive");o(this,"_minMatchCharLength");o(this,"_maxStringLength");o(this,"_debounceTimer",null);o(this,"_optionsSnapshot");this._optionsSnapshot=e;const t=m.validateOptions(e);this._collection=t.collection,this._threshold=t.threshold,this._limit=t.limit,this._debounce=t.debounce,this._caseSensitive=t.caseSensitive,this._minMatchCharLength=t.minMatchCharLength,this._maxStringLength=t.maxStringLength,this._fields=_.normalizeFieldConfig(e.fields,this._threshold),this._keywords=e.keywords?m.validateKeywords(e.keywords):null,this._keywordTriggerMap=this._buildKeywordTriggerMap()}_buildKeywordTriggerMap(){const e=new Map;if(!this._keywords)return e;for(const t of this._keywords.definitions){const c=Array.isArray(t.triggers)?t.triggers:[t.triggers];for(const r of c)e.set(r.toLowerCase(),t)}return e}get collection(){return this._collection}get options(){return this._optionsSnapshot}setCollection(e){this._collection=m.validateCollection(e)}query(e){const t=S.tokenize(e);if(t.length===0)return{results:[],matchedKeywords:[],tokens:[],warnings:[]};const{keywordTokens:c,searchTokens:r}=this._extractKeywords(t);let h=this._collection;const s=[];if(c.size>0){const n=this._applyKeywords(h,c);h=n.collection,s.push(...n.matchedKeywords)}if(r.length===0){const n=[];return{results:h.map(g=>{const{score:y,warnings:w}=_.scoreItem(g,[],this._fields,this._caseSensitive,this._maxStringLength);return n.push(...w),{item:g,score:y}}),matchedKeywords:s,tokens:r,warnings:[...new Set(n)]}}const i=this._minMatchCharLength>1?r.filter(n=>n.length>=this._minMatchCharLength):r;if(i.length===0)return{results:[],matchedKeywords:s,tokens:r,warnings:[]};const l=[],a=[];for(const n of h){const{score:d,warnings:g}=_.scoreItem(n,i,this._fields,this._caseSensitive,this._maxStringLength);l.push(...g),d>0&&a.push({item:n,score:d})}return a.sort((n,d)=>d.score-n.score),{results:this._limit<1/0?a.slice(0,this._limit):a,matchedKeywords:s,tokens:r,warnings:[...new Set(l)]}}queryAsync(e){return new Promise(t=>{this._debounce>0?(this._debounceTimer&&clearTimeout(this._debounceTimer),this._debounceTimer=setTimeout(()=>{t(this.query(e))},this._debounce)):t(this.query(e))})}_extractKeywords(e){const t=new Map,c=[];for(const r of e){const h=this._caseSensitive?r:r.toLowerCase(),s=this._keywordTriggerMap.get(h);if(s){const i=t.get(s.name)||[];i.push(r),t.set(s.name,i)}else c.push(r)}return{keywordTokens:t,searchTokens:c}}_applyKeywords(e,t){if(!this._keywords)return{collection:e,matchedKeywords:[]};let c=e;const r=[],h=new Map;for(const[s,i]of t)h.set(s,i);if(this._keywords.mode==="and")for(const s of this._keywords.definitions){const i=h.get(s.name);if(i){const l=s.handler,a=l(c,i);if(!Array.isArray(a)){console.error(`[Spotr] Keyword handler "${s.name}" must return an array, received ${typeof a}. Skipping this filter.`);continue}c=a,r.push({name:s.name,terms:i})}}else{const s=[],i=new Set;for(const l of this._keywords.definitions){const a=h.get(l.name);if(a){const f=l.handler,n=f(this._collection,a);if(!Array.isArray(n)){console.error(`[Spotr] Keyword handler "${l.name}" must return an array, received ${typeof n}. Skipping this filter.`);continue}r.push({name:l.name,terms:a});for(const d of n)i.has(d)||(i.add(d),s.push(d))}}c=s}return{collection:c,matchedKeywords:r}}}exports.Spotr=T;
2
2
  //# sourceMappingURL=Spotr.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Spotr.cjs","sources":["../src/Spotr.ts"],"sourcesContent":["import { SpotrError, ErrorCodes } from './errors';\nimport { normalizeFieldConfig, scoreItem } from './fuzzy';\nimport { tokenize, validateOptions, validateKeywords } from './utils';\nimport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n NormalizedFieldConfig,\n NormalizedKeywordsConfig,\n KeywordDefinition,\n} from './types';\n\nexport class Spotr<T extends object> {\n private _collection: T[];\n private _fields: NormalizedFieldConfig[];\n private _keywords: NormalizedKeywordsConfig | null;\n private _keywordTriggerMap: Map<string, KeywordDefinition<unknown>>;\n private _threshold: number;\n private _limit: number;\n private _debounce: number;\n private _caseSensitive: boolean;\n private _minMatchCharLength: number;\n private _debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private _optionsSnapshot: SpotrOptions<T>;\n\n constructor(options: SpotrOptions<T>) {\n this._optionsSnapshot = options;\n const validated = validateOptions(options);\n this._collection = validated.collection;\n this._threshold = validated.threshold;\n this._limit = validated.limit;\n this._debounce = validated.debounce;\n this._caseSensitive = validated.caseSensitive;\n this._minMatchCharLength = validated.minMatchCharLength;\n\n this._fields = normalizeFieldConfig(options.fields, this._threshold);\n\n this._keywords = options.keywords ? validateKeywords(options.keywords) : null;\n this._keywordTriggerMap = this._buildKeywordTriggerMap();\n }\n\n private _buildKeywordTriggerMap(): Map<string, KeywordDefinition<unknown>> {\n const map = new Map<string, KeywordDefinition<unknown>>();\n if (!this._keywords) return map;\n\n for (const def of this._keywords.definitions) {\n const triggers = Array.isArray(def.triggers) ? def.triggers : [def.triggers];\n for (const trigger of triggers) {\n map.set(trigger.toLowerCase(), def);\n }\n }\n return map;\n }\n\n get collection(): T[] {\n return this._collection;\n }\n\n get options(): SpotrOptions<T> {\n return this._optionsSnapshot;\n }\n\n setCollection(collection: T[] | Set<T>): void {\n this._collection = collection instanceof Set ? Array.from(collection) : collection;\n }\n\n query(search: string): SpotrResult<T> {\n const tokens = tokenize(search);\n \n if (tokens.length === 0) {\n return {\n results: [],\n matchedKeywords: [],\n tokens: [],\n warnings: [],\n };\n }\n\n const { keywordTokens, searchTokens } = this._extractKeywords(tokens);\n\n let filteredCollection = this._collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n if (keywordTokens.size > 0) {\n const result = this._applyKeywords(filteredCollection, keywordTokens);\n filteredCollection = result.collection;\n matchedKeywords.push(...result.matchedKeywords);\n }\n\n if (searchTokens.length === 0) {\n const allWarnings: string[] = [];\n const results: ScoredResult<T>[] = filteredCollection.map((item) => {\n const { score, warnings } = scoreItem(\n item,\n [],\n this._fields,\n this._caseSensitive\n );\n allWarnings.push(...warnings);\n return { item, score };\n });\n\n return {\n results,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n const validTokens = this._minMatchCharLength > 1\n ? searchTokens.filter((t) => t.length >= this._minMatchCharLength)\n : searchTokens;\n\n if (validTokens.length === 0) {\n return {\n results: [],\n matchedKeywords,\n tokens: searchTokens,\n warnings: [],\n };\n }\n\n const allWarnings: string[] = [];\n const scoredResults: ScoredResult<T>[] = [];\n\n for (const item of filteredCollection) {\n const { score, warnings } = scoreItem(\n item,\n validTokens,\n this._fields,\n this._caseSensitive\n );\n allWarnings.push(...warnings);\n\n if (score > 0) {\n scoredResults.push({ item, score });\n }\n }\n\n scoredResults.sort((a, b) => b.score - a.score);\n\n const limitedResults = this._limit < Infinity\n ? scoredResults.slice(0, this._limit)\n : scoredResults;\n\n return {\n results: limitedResults,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n queryAsync(search: string): Promise<SpotrResult<T>> {\n return new Promise((resolve) => {\n if (this._debounce > 0) {\n if (this._debounceTimer) {\n clearTimeout(this._debounceTimer);\n }\n this._debounceTimer = setTimeout(() => {\n resolve(this.query(search));\n }, this._debounce);\n } else {\n resolve(this.query(search));\n }\n });\n }\n\n private _extractKeywords(tokens: string[]): {\n keywordTokens: Map<string, string[]>;\n searchTokens: string[];\n } {\n const keywordTokens = new Map<string, string[]>();\n const searchTokens: string[] = [];\n\n for (const token of tokens) {\n const normalizedToken = this._caseSensitive ? token : token.toLowerCase();\n const keywordDef = this._keywordTriggerMap.get(normalizedToken);\n\n if (keywordDef) {\n const existing = keywordTokens.get(keywordDef.name) || [];\n existing.push(token);\n keywordTokens.set(keywordDef.name, existing);\n } else {\n searchTokens.push(token);\n }\n }\n\n return { keywordTokens, searchTokens };\n }\n\n private _applyKeywords(\n collection: T[],\n keywordTokens: Map<string, string[]>\n ): { collection: T[]; matchedKeywords: MatchedKeyword[] } {\n if (!this._keywords) {\n return { collection, matchedKeywords: [] };\n }\n\n let result = collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n const keywordNameToTerms = new Map<string, string[]>();\n for (const [name, terms] of keywordTokens) {\n keywordNameToTerms.set(name, terms);\n }\n\n if (this._keywords.mode === 'and') {\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (collection: T[], matchedTerms: string[]) => T[];\n result = handler(result, terms);\n matchedKeywords.push({ name: def.name, terms });\n\n if (!Array.isArray(result)) {\n throw new SpotrError(\n `Keyword handler \"${def.name}\" must return an array`,\n ErrorCodes.INVALID_HANDLER_RETURN\n );\n }\n }\n }\n } else {\n const mergedResults: T[] = [];\n const processedItems = new Set<T>();\n\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (collection: T[], matchedTerms: string[]) => T[];\n const keywordResults = handler(this._collection, terms);\n matchedKeywords.push({ name: def.name, terms });\n\n if (!Array.isArray(keywordResults)) {\n throw new SpotrError(\n `Keyword handler \"${def.name}\" must return an array`,\n ErrorCodes.INVALID_HANDLER_RETURN\n );\n }\n\n for (const item of keywordResults) {\n if (!processedItems.has(item)) {\n processedItems.add(item);\n mergedResults.push(item);\n }\n }\n }\n }\n result = mergedResults;\n }\n\n return { collection: result, matchedKeywords };\n }\n}\n"],"names":["Spotr","options","__publicField","validated","validateOptions","normalizeFieldConfig","validateKeywords","map","def","triggers","trigger","collection","search","tokens","tokenize","keywordTokens","searchTokens","filteredCollection","matchedKeywords","result","allWarnings","item","score","warnings","scoreItem","validTokens","t","scoredResults","a","b","resolve","token","normalizedToken","keywordDef","existing","keywordNameToTerms","name","terms","handler","SpotrError","ErrorCodes","mergedResults","processedItems","keywordResults"],"mappings":"wXAaO,MAAMA,CAAwB,CAanC,YAAYC,EAA0B,CAZ9BC,EAAA,oBACAA,EAAA,gBACAA,EAAA,kBACAA,EAAA,2BACAA,EAAA,mBACAA,EAAA,eACAA,EAAA,kBACAA,EAAA,uBACAA,EAAA,4BACAA,EAAA,sBAAuD,MACvDA,EAAA,yBAGN,KAAK,iBAAmBD,EACxB,MAAME,EAAYC,EAAAA,gBAAgBH,CAAO,EACzC,KAAK,YAAcE,EAAU,WAC7B,KAAK,WAAaA,EAAU,UAC5B,KAAK,OAASA,EAAU,MACxB,KAAK,UAAYA,EAAU,SAC3B,KAAK,eAAiBA,EAAU,cAChC,KAAK,oBAAsBA,EAAU,mBAErC,KAAK,QAAUE,EAAAA,qBAAqBJ,EAAQ,OAAQ,KAAK,UAAU,EAEnE,KAAK,UAAYA,EAAQ,SAAWK,EAAAA,iBAAiBL,EAAQ,QAAQ,EAAI,KACzE,KAAK,mBAAqB,KAAK,wBAAA,CACjC,CAEQ,yBAAmE,CACzE,MAAMM,MAAU,IAChB,GAAI,CAAC,KAAK,UAAW,OAAOA,EAE5B,UAAWC,KAAO,KAAK,UAAU,YAAa,CAC5C,MAAMC,EAAW,MAAM,QAAQD,EAAI,QAAQ,EAAIA,EAAI,SAAW,CAACA,EAAI,QAAQ,EAC3E,UAAWE,KAAWD,EACpBF,EAAI,IAAIG,EAAQ,YAAA,EAAeF,CAAG,CAEtC,CACA,OAAOD,CACT,CAEA,IAAI,YAAkB,CACpB,OAAO,KAAK,WACd,CAEA,IAAI,SAA2B,CAC7B,OAAO,KAAK,gBACd,CAEA,cAAcI,EAAgC,CAC5C,KAAK,YAAcA,aAAsB,IAAM,MAAM,KAAKA,CAAU,EAAIA,CAC1E,CAEA,MAAMC,EAAgC,CACpC,MAAMC,EAASC,EAAAA,SAASF,CAAM,EAE9B,GAAIC,EAAO,SAAW,EACpB,MAAO,CACL,QAAS,CAAA,EACT,gBAAiB,CAAA,EACjB,OAAQ,CAAA,EACR,SAAU,CAAA,CAAC,EAIf,KAAM,CAAE,cAAAE,EAAe,aAAAC,CAAA,EAAiB,KAAK,iBAAiBH,CAAM,EAEpE,IAAII,EAAqB,KAAK,YAC9B,MAAMC,EAAoC,CAAA,EAE1C,GAAIH,EAAc,KAAO,EAAG,CAC1B,MAAMI,EAAS,KAAK,eAAeF,EAAoBF,CAAa,EACpEE,EAAqBE,EAAO,WAC5BD,EAAgB,KAAK,GAAGC,EAAO,eAAe,CAChD,CAEA,GAAIH,EAAa,SAAW,EAAG,CAC7B,MAAMI,EAAwB,CAAA,EAY9B,MAAO,CACL,QAZiCH,EAAmB,IAAKI,GAAS,CAClE,KAAM,CAAE,MAAAC,EAAO,SAAAC,CAAA,EAAaC,EAAAA,UAC1BH,EACA,CAAA,EACA,KAAK,QACL,KAAK,cAAA,EAEPD,OAAAA,EAAY,KAAK,GAAGG,CAAQ,EACrB,CAAE,KAAAF,EAAM,MAAAC,CAAA,CACjB,CAAC,EAIC,gBAAAJ,EACA,OAAQF,EACR,SAAU,CAAC,GAAG,IAAI,IAAII,CAAW,CAAC,CAAA,CAEtC,CAEA,MAAMK,EAAc,KAAK,oBAAsB,EAC3CT,EAAa,OAAQU,GAAMA,EAAE,QAAU,KAAK,mBAAmB,EAC/DV,EAEJ,GAAIS,EAAY,SAAW,EACzB,MAAO,CACL,QAAS,CAAA,EACT,gBAAAP,EACA,OAAQF,EACR,SAAU,CAAA,CAAC,EAIf,MAAMI,EAAwB,CAAA,EACxBO,EAAmC,CAAA,EAEzC,UAAWN,KAAQJ,EAAoB,CACrC,KAAM,CAAE,MAAAK,EAAO,SAAAC,CAAA,EAAaC,EAAAA,UAC1BH,EACAI,EACA,KAAK,QACL,KAAK,cAAA,EAEPL,EAAY,KAAK,GAAGG,CAAQ,EAExBD,EAAQ,GACVK,EAAc,KAAK,CAAE,KAAAN,EAAM,MAAAC,CAAA,CAAO,CAEtC,CAEA,OAAAK,EAAc,KAAK,CAACC,EAAGC,IAAMA,EAAE,MAAQD,EAAE,KAAK,EAMvC,CACL,QALqB,KAAK,OAAS,IACjCD,EAAc,MAAM,EAAG,KAAK,MAAM,EAClCA,EAIF,gBAAAT,EACA,OAAQF,EACR,SAAU,CAAC,GAAG,IAAI,IAAII,CAAW,CAAC,CAAA,CAEtC,CAEA,WAAWR,EAAyC,CAClD,OAAO,IAAI,QAASkB,GAAY,CAC1B,KAAK,UAAY,GACf,KAAK,gBACP,aAAa,KAAK,cAAc,EAElC,KAAK,eAAiB,WAAW,IAAM,CACrCA,EAAQ,KAAK,MAAMlB,CAAM,CAAC,CAC5B,EAAG,KAAK,SAAS,GAEjBkB,EAAQ,KAAK,MAAMlB,CAAM,CAAC,CAE9B,CAAC,CACH,CAEQ,iBAAiBC,EAGvB,CACA,MAAME,MAAoB,IACpBC,EAAyB,CAAA,EAE/B,UAAWe,KAASlB,EAAQ,CAC1B,MAAMmB,EAAkB,KAAK,eAAiBD,EAAQA,EAAM,YAAA,EACtDE,EAAa,KAAK,mBAAmB,IAAID,CAAe,EAE9D,GAAIC,EAAY,CACd,MAAMC,EAAWnB,EAAc,IAAIkB,EAAW,IAAI,GAAK,CAAA,EACvDC,EAAS,KAAKH,CAAK,EACnBhB,EAAc,IAAIkB,EAAW,KAAMC,CAAQ,CAC7C,MACElB,EAAa,KAAKe,CAAK,CAE3B,CAEA,MAAO,CAAE,cAAAhB,EAAe,aAAAC,CAAA,CAC1B,CAEQ,eACNL,EACAI,EACwD,CACxD,GAAI,CAAC,KAAK,UACR,MAAO,CAAE,WAAAJ,EAAY,gBAAiB,EAAC,EAGzC,IAAIQ,EAASR,EACb,MAAMO,EAAoC,CAAA,EAEpCiB,MAAyB,IAC/B,SAAW,CAACC,EAAMC,CAAK,IAAKtB,EAC1BoB,EAAmB,IAAIC,EAAMC,CAAK,EAGpC,GAAI,KAAK,UAAU,OAAS,MAC1B,UAAW7B,KAAO,KAAK,UAAU,YAAa,CAC5C,MAAM6B,EAAQF,EAAmB,IAAI3B,EAAI,IAAI,EAC7C,GAAI6B,EAAO,CACT,MAAMC,EAAU9B,EAAI,QAIpB,GAHAW,EAASmB,EAAQnB,EAAQkB,CAAK,EAC9BnB,EAAgB,KAAK,CAAE,KAAMV,EAAI,KAAM,MAAA6B,EAAO,EAE1C,CAAC,MAAM,QAAQlB,CAAM,EACvB,MAAM,IAAIoB,EAAAA,WACR,oBAAoB/B,EAAI,IAAI,yBAC5BgC,aAAW,sBAAA,CAGjB,CACF,KACK,CACL,MAAMC,EAAqB,CAAA,EACrBC,MAAqB,IAE3B,UAAWlC,KAAO,KAAK,UAAU,YAAa,CAC5C,MAAM6B,EAAQF,EAAmB,IAAI3B,EAAI,IAAI,EAC7C,GAAI6B,EAAO,CACT,MAAMC,EAAU9B,EAAI,QACdmC,EAAiBL,EAAQ,KAAK,YAAaD,CAAK,EAGtD,GAFAnB,EAAgB,KAAK,CAAE,KAAMV,EAAI,KAAM,MAAA6B,EAAO,EAE1C,CAAC,MAAM,QAAQM,CAAc,EAC/B,MAAM,IAAIJ,EAAAA,WACR,oBAAoB/B,EAAI,IAAI,yBAC5BgC,aAAW,sBAAA,EAIf,UAAWnB,KAAQsB,EACZD,EAAe,IAAIrB,CAAI,IAC1BqB,EAAe,IAAIrB,CAAI,EACvBoB,EAAc,KAAKpB,CAAI,EAG7B,CACF,CACAF,EAASsB,CACX,CAEA,MAAO,CAAE,WAAYtB,EAAQ,gBAAAD,CAAA,CAC/B,CACF"}
1
+ {"version":3,"file":"Spotr.cjs","sources":["../src/Spotr.ts"],"sourcesContent":["import { normalizeFieldConfig, scoreItem } from './fuzzy';\nimport {\n tokenize,\n validateOptions,\n validateKeywords,\n validateCollection,\n} from './utils';\nimport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n NormalizedFieldConfig,\n NormalizedKeywordsConfig,\n KeywordDefinition,\n} from './types';\n\nexport class Spotr<T extends object> {\n private _collection: T[];\n private _fields: NormalizedFieldConfig[];\n private _keywords: NormalizedKeywordsConfig | null;\n private _keywordTriggerMap: Map<string, KeywordDefinition<unknown>>;\n private _threshold: number;\n private _limit: number;\n private _debounce: number;\n private _caseSensitive: boolean;\n private _minMatchCharLength: number;\n private _maxStringLength: number;\n private _debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private _optionsSnapshot: SpotrOptions<T>;\n\n constructor(options: SpotrOptions<T>) {\n this._optionsSnapshot = options;\n const validated = validateOptions(options);\n this._collection = validated.collection;\n this._threshold = validated.threshold;\n this._limit = validated.limit;\n this._debounce = validated.debounce;\n this._caseSensitive = validated.caseSensitive;\n this._minMatchCharLength = validated.minMatchCharLength;\n this._maxStringLength = validated.maxStringLength;\n\n this._fields = normalizeFieldConfig(options.fields, this._threshold);\n\n this._keywords = options.keywords\n ? validateKeywords(options.keywords)\n : null;\n this._keywordTriggerMap = this._buildKeywordTriggerMap();\n }\n\n private _buildKeywordTriggerMap(): Map<string, KeywordDefinition<unknown>> {\n const map = new Map<string, KeywordDefinition<unknown>>();\n if (!this._keywords) return map;\n\n for (const def of this._keywords.definitions) {\n const triggers = Array.isArray(def.triggers)\n ? def.triggers\n : [def.triggers];\n for (const trigger of triggers) {\n map.set(trigger.toLowerCase(), def);\n }\n }\n return map;\n }\n\n get collection(): T[] {\n return this._collection;\n }\n\n get options(): SpotrOptions<T> {\n return this._optionsSnapshot;\n }\n\n setCollection(collection: T[] | Set<T>): void {\n this._collection = validateCollection(collection as unknown) as T[];\n }\n\n query(search: string): SpotrResult<T> {\n const tokens = tokenize(search);\n\n if (tokens.length === 0) {\n return {\n results: [],\n matchedKeywords: [],\n tokens: [],\n warnings: [],\n };\n }\n\n const { keywordTokens, searchTokens } = this._extractKeywords(tokens);\n\n let filteredCollection = this._collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n if (keywordTokens.size > 0) {\n const result = this._applyKeywords(filteredCollection, keywordTokens);\n filteredCollection = result.collection;\n matchedKeywords.push(...result.matchedKeywords);\n }\n\n if (searchTokens.length === 0) {\n const allWarnings: string[] = [];\n const results: ScoredResult<T>[] = filteredCollection.map((item) => {\n const { score, warnings } = scoreItem(\n item,\n [],\n this._fields,\n this._caseSensitive,\n this._maxStringLength\n );\n allWarnings.push(...warnings);\n return { item, score };\n });\n\n return {\n results,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n const validTokens =\n this._minMatchCharLength > 1\n ? searchTokens.filter((t) => t.length >= this._minMatchCharLength)\n : searchTokens;\n\n if (validTokens.length === 0) {\n return {\n results: [],\n matchedKeywords,\n tokens: searchTokens,\n warnings: [],\n };\n }\n\n const allWarnings: string[] = [];\n const scoredResults: ScoredResult<T>[] = [];\n\n for (const item of filteredCollection) {\n const { score, warnings } = scoreItem(\n item,\n validTokens,\n this._fields,\n this._caseSensitive,\n this._maxStringLength\n );\n allWarnings.push(...warnings);\n\n if (score > 0) {\n scoredResults.push({ item, score });\n }\n }\n\n scoredResults.sort((a, b) => b.score - a.score);\n\n const limitedResults =\n this._limit < Infinity\n ? scoredResults.slice(0, this._limit)\n : scoredResults;\n\n return {\n results: limitedResults,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n queryAsync(search: string): Promise<SpotrResult<T>> {\n return new Promise((resolve) => {\n if (this._debounce > 0) {\n if (this._debounceTimer) {\n clearTimeout(this._debounceTimer);\n }\n this._debounceTimer = setTimeout(() => {\n resolve(this.query(search));\n }, this._debounce);\n } else {\n resolve(this.query(search));\n }\n });\n }\n\n private _extractKeywords(tokens: string[]): {\n keywordTokens: Map<string, string[]>;\n searchTokens: string[];\n } {\n const keywordTokens = new Map<string, string[]>();\n const searchTokens: string[] = [];\n\n for (const token of tokens) {\n const normalizedToken = this._caseSensitive ? token : token.toLowerCase();\n const keywordDef = this._keywordTriggerMap.get(normalizedToken);\n\n if (keywordDef) {\n const existing = keywordTokens.get(keywordDef.name) || [];\n existing.push(token);\n keywordTokens.set(keywordDef.name, existing);\n } else {\n searchTokens.push(token);\n }\n }\n\n return { keywordTokens, searchTokens };\n }\n\n private _applyKeywords(\n collection: T[],\n keywordTokens: Map<string, string[]>\n ): { collection: T[]; matchedKeywords: MatchedKeyword[] } {\n if (!this._keywords) {\n return { collection, matchedKeywords: [] };\n }\n\n let result = collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n const keywordNameToTerms = new Map<string, string[]>();\n for (const [name, terms] of keywordTokens) {\n keywordNameToTerms.set(name, terms);\n }\n\n if (this._keywords.mode === 'and') {\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (\n collection: T[],\n matchedTerms: string[]\n ) => T[];\n const handlerResult = handler(result, terms);\n\n if (!Array.isArray(handlerResult)) {\n console.error(\n `[Spotr] Keyword handler \"${def.name}\" must return an array, received ${typeof handlerResult}. Skipping this filter.`\n );\n // Skip this keyword - keep current collection for this step\n continue;\n }\n\n result = handlerResult;\n matchedKeywords.push({ name: def.name, terms });\n }\n }\n } else {\n const mergedResults: T[] = [];\n const processedItems = new Set<T>();\n\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (\n collection: T[],\n matchedTerms: string[]\n ) => T[];\n const keywordResults = handler(this._collection, terms);\n\n if (!Array.isArray(keywordResults)) {\n console.error(\n `[Spotr] Keyword handler \"${def.name}\" must return an array, received ${typeof keywordResults}. Skipping this filter.`\n );\n // Skip this keyword - don't add its results\n continue;\n }\n\n matchedKeywords.push({ name: def.name, terms });\n\n for (const item of keywordResults) {\n if (!processedItems.has(item)) {\n processedItems.add(item);\n mergedResults.push(item);\n }\n }\n }\n }\n result = mergedResults;\n }\n\n return { collection: result, matchedKeywords };\n }\n}\n"],"names":["Spotr","options","__publicField","validated","validateOptions","normalizeFieldConfig","validateKeywords","map","def","triggers","trigger","collection","validateCollection","search","tokens","tokenize","keywordTokens","searchTokens","filteredCollection","matchedKeywords","result","allWarnings","item","score","warnings","scoreItem","validTokens","t","scoredResults","a","b","resolve","token","normalizedToken","keywordDef","existing","keywordNameToTerms","name","terms","handler","handlerResult","mergedResults","processedItems","keywordResults"],"mappings":"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"}
package/dist/Spotr.js CHANGED
@@ -1,33 +1,33 @@
1
- var k = Object.defineProperty;
2
- var T = (m, e, t) => e in m ? k(m, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : m[e] = t;
3
- var a = (m, e, t) => T(m, typeof e != "symbol" ? e + "" : e, t);
4
- import { SpotrError as f, ErrorCodes as w } from "./errors.js";
5
- import { validateOptions as S, validateKeywords as K } from "./utils/validate.js";
6
- import { normalizeFieldConfig as M, scoreItem as y } from "./fuzzy/scorer.js";
7
- import { tokenize as A } from "./utils/tokenize.js";
8
- class I {
1
+ var w = Object.defineProperty;
2
+ var p = (m, e, t) => e in m ? w(m, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : m[e] = t;
3
+ var o = (m, e, t) => p(m, typeof e != "symbol" ? e + "" : e, t);
4
+ import { validateOptions as k, validateKeywords as S, validateCollection as T } from "./utils/validate.js";
5
+ import { normalizeFieldConfig as K, scoreItem as _ } from "./fuzzy/scorer.js";
6
+ import { tokenize as M } from "./utils/tokenize.js";
7
+ class x {
9
8
  constructor(e) {
10
- a(this, "_collection");
11
- a(this, "_fields");
12
- a(this, "_keywords");
13
- a(this, "_keywordTriggerMap");
14
- a(this, "_threshold");
15
- a(this, "_limit");
16
- a(this, "_debounce");
17
- a(this, "_caseSensitive");
18
- a(this, "_minMatchCharLength");
19
- a(this, "_debounceTimer", null);
20
- a(this, "_optionsSnapshot");
9
+ o(this, "_collection");
10
+ o(this, "_fields");
11
+ o(this, "_keywords");
12
+ o(this, "_keywordTriggerMap");
13
+ o(this, "_threshold");
14
+ o(this, "_limit");
15
+ o(this, "_debounce");
16
+ o(this, "_caseSensitive");
17
+ o(this, "_minMatchCharLength");
18
+ o(this, "_maxStringLength");
19
+ o(this, "_debounceTimer", null);
20
+ o(this, "_optionsSnapshot");
21
21
  this._optionsSnapshot = e;
22
- const t = S(e);
23
- this._collection = t.collection, this._threshold = t.threshold, this._limit = t.limit, this._debounce = t.debounce, this._caseSensitive = t.caseSensitive, this._minMatchCharLength = t.minMatchCharLength, this._fields = M(e.fields, this._threshold), this._keywords = e.keywords ? K(e.keywords) : null, this._keywordTriggerMap = this._buildKeywordTriggerMap();
22
+ const t = k(e);
23
+ this._collection = t.collection, this._threshold = t.threshold, this._limit = t.limit, this._debounce = t.debounce, this._caseSensitive = t.caseSensitive, this._minMatchCharLength = t.minMatchCharLength, this._maxStringLength = t.maxStringLength, this._fields = K(e.fields, this._threshold), this._keywords = e.keywords ? S(e.keywords) : null, this._keywordTriggerMap = this._buildKeywordTriggerMap();
24
24
  }
25
25
  _buildKeywordTriggerMap() {
26
26
  const e = /* @__PURE__ */ new Map();
27
27
  if (!this._keywords) return e;
28
28
  for (const t of this._keywords.definitions) {
29
- const i = Array.isArray(t.triggers) ? t.triggers : [t.triggers];
30
- for (const r of i)
29
+ const c = Array.isArray(t.triggers) ? t.triggers : [t.triggers];
30
+ for (const r of c)
31
31
  e.set(r.toLowerCase(), t);
32
32
  }
33
33
  return e;
@@ -39,10 +39,10 @@ class I {
39
39
  return this._optionsSnapshot;
40
40
  }
41
41
  setCollection(e) {
42
- this._collection = e instanceof Set ? Array.from(e) : e;
42
+ this._collection = T(e);
43
43
  }
44
44
  query(e) {
45
- const t = A(e);
45
+ const t = M(e);
46
46
  if (t.length === 0)
47
47
  return {
48
48
  results: [],
@@ -50,53 +50,55 @@ class I {
50
50
  tokens: [],
51
51
  warnings: []
52
52
  };
53
- const { keywordTokens: i, searchTokens: r } = this._extractKeywords(t);
54
- let h = this._collection;
53
+ const { keywordTokens: c, searchTokens: r } = this._extractKeywords(t);
54
+ let a = this._collection;
55
55
  const s = [];
56
- if (i.size > 0) {
57
- const o = this._applyKeywords(h, i);
58
- h = o.collection, s.push(...o.matchedKeywords);
56
+ if (c.size > 0) {
57
+ const n = this._applyKeywords(a, c);
58
+ a = n.collection, s.push(...n.matchedKeywords);
59
59
  }
60
60
  if (r.length === 0) {
61
- const o = [];
61
+ const n = [];
62
62
  return {
63
- results: h.map((u) => {
64
- const { score: g, warnings: p } = y(
63
+ results: a.map((u) => {
64
+ const { score: f, warnings: y } = _(
65
65
  u,
66
66
  [],
67
67
  this._fields,
68
- this._caseSensitive
68
+ this._caseSensitive,
69
+ this._maxStringLength
69
70
  );
70
- return o.push(...p), { item: u, score: g };
71
+ return n.push(...y), { item: u, score: f };
71
72
  }),
72
73
  matchedKeywords: s,
73
74
  tokens: r,
74
- warnings: [...new Set(o)]
75
+ warnings: [...new Set(n)]
75
76
  };
76
77
  }
77
- const n = this._minMatchCharLength > 1 ? r.filter((o) => o.length >= this._minMatchCharLength) : r;
78
- if (n.length === 0)
78
+ const i = this._minMatchCharLength > 1 ? r.filter((n) => n.length >= this._minMatchCharLength) : r;
79
+ if (i.length === 0)
79
80
  return {
80
81
  results: [],
81
82
  matchedKeywords: s,
82
83
  tokens: r,
83
84
  warnings: []
84
85
  };
85
- const c = [], l = [];
86
- for (const o of h) {
87
- const { score: d, warnings: u } = y(
88
- o,
86
+ const l = [], h = [];
87
+ for (const n of a) {
88
+ const { score: d, warnings: u } = _(
89
89
  n,
90
+ i,
90
91
  this._fields,
91
- this._caseSensitive
92
+ this._caseSensitive,
93
+ this._maxStringLength
92
94
  );
93
- c.push(...u), d > 0 && l.push({ item: o, score: d });
95
+ l.push(...u), d > 0 && h.push({ item: n, score: d });
94
96
  }
95
- return l.sort((o, d) => d.score - o.score), {
96
- results: this._limit < 1 / 0 ? l.slice(0, this._limit) : l,
97
+ return h.sort((n, d) => d.score - n.score), {
98
+ results: this._limit < 1 / 0 ? h.slice(0, this._limit) : h,
97
99
  matchedKeywords: s,
98
100
  tokens: r,
99
- warnings: [...new Set(c)]
101
+ warnings: [...new Set(l)]
100
102
  };
101
103
  }
102
104
  queryAsync(e) {
@@ -107,57 +109,61 @@ class I {
107
109
  });
108
110
  }
109
111
  _extractKeywords(e) {
110
- const t = /* @__PURE__ */ new Map(), i = [];
112
+ const t = /* @__PURE__ */ new Map(), c = [];
111
113
  for (const r of e) {
112
- const h = this._caseSensitive ? r : r.toLowerCase(), s = this._keywordTriggerMap.get(h);
114
+ const a = this._caseSensitive ? r : r.toLowerCase(), s = this._keywordTriggerMap.get(a);
113
115
  if (s) {
114
- const n = t.get(s.name) || [];
115
- n.push(r), t.set(s.name, n);
116
+ const i = t.get(s.name) || [];
117
+ i.push(r), t.set(s.name, i);
116
118
  } else
117
- i.push(r);
119
+ c.push(r);
118
120
  }
119
- return { keywordTokens: t, searchTokens: i };
121
+ return { keywordTokens: t, searchTokens: c };
120
122
  }
121
123
  _applyKeywords(e, t) {
122
124
  if (!this._keywords)
123
125
  return { collection: e, matchedKeywords: [] };
124
- let i = e;
125
- const r = [], h = /* @__PURE__ */ new Map();
126
- for (const [s, n] of t)
127
- h.set(s, n);
126
+ let c = e;
127
+ const r = [], a = /* @__PURE__ */ new Map();
128
+ for (const [s, i] of t)
129
+ a.set(s, i);
128
130
  if (this._keywords.mode === "and")
129
131
  for (const s of this._keywords.definitions) {
130
- const n = h.get(s.name);
131
- if (n) {
132
- const c = s.handler;
133
- if (i = c(i, n), r.push({ name: s.name, terms: n }), !Array.isArray(i))
134
- throw new f(
135
- `Keyword handler "${s.name}" must return an array`,
136
- w.INVALID_HANDLER_RETURN
132
+ const i = a.get(s.name);
133
+ if (i) {
134
+ const l = s.handler, h = l(c, i);
135
+ if (!Array.isArray(h)) {
136
+ console.error(
137
+ `[Spotr] Keyword handler "${s.name}" must return an array, received ${typeof h}. Skipping this filter.`
137
138
  );
139
+ continue;
140
+ }
141
+ c = h, r.push({ name: s.name, terms: i });
138
142
  }
139
143
  }
140
144
  else {
141
- const s = [], n = /* @__PURE__ */ new Set();
142
- for (const c of this._keywords.definitions) {
143
- const l = h.get(c.name);
144
- if (l) {
145
- const _ = c.handler, o = _(this._collection, l);
146
- if (r.push({ name: c.name, terms: l }), !Array.isArray(o))
147
- throw new f(
148
- `Keyword handler "${c.name}" must return an array`,
149
- w.INVALID_HANDLER_RETURN
145
+ const s = [], i = /* @__PURE__ */ new Set();
146
+ for (const l of this._keywords.definitions) {
147
+ const h = a.get(l.name);
148
+ if (h) {
149
+ const g = l.handler, n = g(this._collection, h);
150
+ if (!Array.isArray(n)) {
151
+ console.error(
152
+ `[Spotr] Keyword handler "${l.name}" must return an array, received ${typeof n}. Skipping this filter.`
150
153
  );
151
- for (const d of o)
152
- n.has(d) || (n.add(d), s.push(d));
154
+ continue;
155
+ }
156
+ r.push({ name: l.name, terms: h });
157
+ for (const d of n)
158
+ i.has(d) || (i.add(d), s.push(d));
153
159
  }
154
160
  }
155
- i = s;
161
+ c = s;
156
162
  }
157
- return { collection: i, matchedKeywords: r };
163
+ return { collection: c, matchedKeywords: r };
158
164
  }
159
165
  }
160
166
  export {
161
- I as Spotr
167
+ x as Spotr
162
168
  };
163
169
  //# sourceMappingURL=Spotr.js.map
package/dist/Spotr.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Spotr.js","sources":["../src/Spotr.ts"],"sourcesContent":["import { SpotrError, ErrorCodes } from './errors';\nimport { normalizeFieldConfig, scoreItem } from './fuzzy';\nimport { tokenize, validateOptions, validateKeywords } from './utils';\nimport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n NormalizedFieldConfig,\n NormalizedKeywordsConfig,\n KeywordDefinition,\n} from './types';\n\nexport class Spotr<T extends object> {\n private _collection: T[];\n private _fields: NormalizedFieldConfig[];\n private _keywords: NormalizedKeywordsConfig | null;\n private _keywordTriggerMap: Map<string, KeywordDefinition<unknown>>;\n private _threshold: number;\n private _limit: number;\n private _debounce: number;\n private _caseSensitive: boolean;\n private _minMatchCharLength: number;\n private _debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private _optionsSnapshot: SpotrOptions<T>;\n\n constructor(options: SpotrOptions<T>) {\n this._optionsSnapshot = options;\n const validated = validateOptions(options);\n this._collection = validated.collection;\n this._threshold = validated.threshold;\n this._limit = validated.limit;\n this._debounce = validated.debounce;\n this._caseSensitive = validated.caseSensitive;\n this._minMatchCharLength = validated.minMatchCharLength;\n\n this._fields = normalizeFieldConfig(options.fields, this._threshold);\n\n this._keywords = options.keywords ? validateKeywords(options.keywords) : null;\n this._keywordTriggerMap = this._buildKeywordTriggerMap();\n }\n\n private _buildKeywordTriggerMap(): Map<string, KeywordDefinition<unknown>> {\n const map = new Map<string, KeywordDefinition<unknown>>();\n if (!this._keywords) return map;\n\n for (const def of this._keywords.definitions) {\n const triggers = Array.isArray(def.triggers) ? def.triggers : [def.triggers];\n for (const trigger of triggers) {\n map.set(trigger.toLowerCase(), def);\n }\n }\n return map;\n }\n\n get collection(): T[] {\n return this._collection;\n }\n\n get options(): SpotrOptions<T> {\n return this._optionsSnapshot;\n }\n\n setCollection(collection: T[] | Set<T>): void {\n this._collection = collection instanceof Set ? Array.from(collection) : collection;\n }\n\n query(search: string): SpotrResult<T> {\n const tokens = tokenize(search);\n \n if (tokens.length === 0) {\n return {\n results: [],\n matchedKeywords: [],\n tokens: [],\n warnings: [],\n };\n }\n\n const { keywordTokens, searchTokens } = this._extractKeywords(tokens);\n\n let filteredCollection = this._collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n if (keywordTokens.size > 0) {\n const result = this._applyKeywords(filteredCollection, keywordTokens);\n filteredCollection = result.collection;\n matchedKeywords.push(...result.matchedKeywords);\n }\n\n if (searchTokens.length === 0) {\n const allWarnings: string[] = [];\n const results: ScoredResult<T>[] = filteredCollection.map((item) => {\n const { score, warnings } = scoreItem(\n item,\n [],\n this._fields,\n this._caseSensitive\n );\n allWarnings.push(...warnings);\n return { item, score };\n });\n\n return {\n results,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n const validTokens = this._minMatchCharLength > 1\n ? searchTokens.filter((t) => t.length >= this._minMatchCharLength)\n : searchTokens;\n\n if (validTokens.length === 0) {\n return {\n results: [],\n matchedKeywords,\n tokens: searchTokens,\n warnings: [],\n };\n }\n\n const allWarnings: string[] = [];\n const scoredResults: ScoredResult<T>[] = [];\n\n for (const item of filteredCollection) {\n const { score, warnings } = scoreItem(\n item,\n validTokens,\n this._fields,\n this._caseSensitive\n );\n allWarnings.push(...warnings);\n\n if (score > 0) {\n scoredResults.push({ item, score });\n }\n }\n\n scoredResults.sort((a, b) => b.score - a.score);\n\n const limitedResults = this._limit < Infinity\n ? scoredResults.slice(0, this._limit)\n : scoredResults;\n\n return {\n results: limitedResults,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n queryAsync(search: string): Promise<SpotrResult<T>> {\n return new Promise((resolve) => {\n if (this._debounce > 0) {\n if (this._debounceTimer) {\n clearTimeout(this._debounceTimer);\n }\n this._debounceTimer = setTimeout(() => {\n resolve(this.query(search));\n }, this._debounce);\n } else {\n resolve(this.query(search));\n }\n });\n }\n\n private _extractKeywords(tokens: string[]): {\n keywordTokens: Map<string, string[]>;\n searchTokens: string[];\n } {\n const keywordTokens = new Map<string, string[]>();\n const searchTokens: string[] = [];\n\n for (const token of tokens) {\n const normalizedToken = this._caseSensitive ? token : token.toLowerCase();\n const keywordDef = this._keywordTriggerMap.get(normalizedToken);\n\n if (keywordDef) {\n const existing = keywordTokens.get(keywordDef.name) || [];\n existing.push(token);\n keywordTokens.set(keywordDef.name, existing);\n } else {\n searchTokens.push(token);\n }\n }\n\n return { keywordTokens, searchTokens };\n }\n\n private _applyKeywords(\n collection: T[],\n keywordTokens: Map<string, string[]>\n ): { collection: T[]; matchedKeywords: MatchedKeyword[] } {\n if (!this._keywords) {\n return { collection, matchedKeywords: [] };\n }\n\n let result = collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n const keywordNameToTerms = new Map<string, string[]>();\n for (const [name, terms] of keywordTokens) {\n keywordNameToTerms.set(name, terms);\n }\n\n if (this._keywords.mode === 'and') {\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (collection: T[], matchedTerms: string[]) => T[];\n result = handler(result, terms);\n matchedKeywords.push({ name: def.name, terms });\n\n if (!Array.isArray(result)) {\n throw new SpotrError(\n `Keyword handler \"${def.name}\" must return an array`,\n ErrorCodes.INVALID_HANDLER_RETURN\n );\n }\n }\n }\n } else {\n const mergedResults: T[] = [];\n const processedItems = new Set<T>();\n\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (collection: T[], matchedTerms: string[]) => T[];\n const keywordResults = handler(this._collection, terms);\n matchedKeywords.push({ name: def.name, terms });\n\n if (!Array.isArray(keywordResults)) {\n throw new SpotrError(\n `Keyword handler \"${def.name}\" must return an array`,\n ErrorCodes.INVALID_HANDLER_RETURN\n );\n }\n\n for (const item of keywordResults) {\n if (!processedItems.has(item)) {\n processedItems.add(item);\n mergedResults.push(item);\n }\n }\n }\n }\n result = mergedResults;\n }\n\n return { collection: result, matchedKeywords };\n }\n}\n"],"names":["Spotr","options","__publicField","validated","validateOptions","normalizeFieldConfig","validateKeywords","map","def","triggers","trigger","collection","search","tokens","tokenize","keywordTokens","searchTokens","filteredCollection","matchedKeywords","result","allWarnings","item","score","warnings","scoreItem","validTokens","t","scoredResults","a","b","resolve","token","normalizedToken","keywordDef","existing","keywordNameToTerms","name","terms","handler","SpotrError","ErrorCodes","mergedResults","processedItems","keywordResults"],"mappings":"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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 [],\n this._fields,\n this._caseSensitive,\n this._maxStringLength\n );\n allWarnings.push(...warnings);\n return { item, score };\n });\n\n return {\n results,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n const validTokens =\n this._minMatchCharLength > 1\n ? searchTokens.filter((t) => t.length >= this._minMatchCharLength)\n : searchTokens;\n\n if (validTokens.length === 0) {\n return {\n results: [],\n matchedKeywords,\n tokens: searchTokens,\n warnings: [],\n };\n }\n\n const allWarnings: string[] = [];\n const scoredResults: ScoredResult<T>[] = [];\n\n for (const item of filteredCollection) {\n const { score, warnings } = scoreItem(\n item,\n validTokens,\n this._fields,\n this._caseSensitive,\n this._maxStringLength\n );\n allWarnings.push(...warnings);\n\n if (score > 0) {\n scoredResults.push({ item, score });\n }\n }\n\n scoredResults.sort((a, b) => b.score - a.score);\n\n const limitedResults =\n this._limit < Infinity\n ? scoredResults.slice(0, this._limit)\n : scoredResults;\n\n return {\n results: limitedResults,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n queryAsync(search: string): Promise<SpotrResult<T>> {\n return new Promise((resolve) => {\n if (this._debounce > 0) {\n if (this._debounceTimer) {\n clearTimeout(this._debounceTimer);\n }\n this._debounceTimer = setTimeout(() => {\n resolve(this.query(search));\n }, this._debounce);\n } else {\n resolve(this.query(search));\n }\n });\n }\n\n private _extractKeywords(tokens: string[]): {\n keywordTokens: Map<string, string[]>;\n searchTokens: string[];\n } {\n const keywordTokens = new Map<string, string[]>();\n const searchTokens: string[] = [];\n\n for (const token of tokens) {\n const normalizedToken = this._caseSensitive ? token : token.toLowerCase();\n const keywordDef = this._keywordTriggerMap.get(normalizedToken);\n\n if (keywordDef) {\n const existing = keywordTokens.get(keywordDef.name) || [];\n existing.push(token);\n keywordTokens.set(keywordDef.name, existing);\n } else {\n searchTokens.push(token);\n }\n }\n\n return { keywordTokens, searchTokens };\n }\n\n private _applyKeywords(\n collection: T[],\n keywordTokens: Map<string, string[]>\n ): { collection: T[]; matchedKeywords: MatchedKeyword[] } {\n if (!this._keywords) {\n return { collection, matchedKeywords: [] };\n }\n\n let result = collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n const keywordNameToTerms = new Map<string, string[]>();\n for (const [name, terms] of keywordTokens) {\n keywordNameToTerms.set(name, terms);\n }\n\n if (this._keywords.mode === 'and') {\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (\n collection: T[],\n matchedTerms: string[]\n ) => T[];\n const handlerResult = handler(result, terms);\n\n if (!Array.isArray(handlerResult)) {\n console.error(\n `[Spotr] Keyword handler \"${def.name}\" must return an array, received ${typeof handlerResult}. Skipping this filter.`\n );\n // Skip this keyword - keep current collection for this step\n continue;\n }\n\n result = handlerResult;\n matchedKeywords.push({ name: def.name, terms });\n }\n }\n } else {\n const mergedResults: T[] = [];\n const processedItems = new Set<T>();\n\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (\n collection: T[],\n matchedTerms: string[]\n ) => T[];\n const keywordResults = handler(this._collection, terms);\n\n if (!Array.isArray(keywordResults)) {\n console.error(\n `[Spotr] Keyword handler \"${def.name}\" must return an array, received ${typeof keywordResults}. Skipping this filter.`\n );\n // Skip this keyword - don't add its results\n continue;\n }\n\n matchedKeywords.push({ name: def.name, terms });\n\n for (const item of keywordResults) {\n if (!processedItems.has(item)) {\n processedItems.add(item);\n mergedResults.push(item);\n }\n }\n }\n }\n result = mergedResults;\n }\n\n return { collection: result, matchedKeywords };\n }\n}\n"],"names":["Spotr","options","__publicField","validated","validateOptions","normalizeFieldConfig","validateKeywords","map","def","triggers","trigger","collection","validateCollection","search","tokens","tokenize","keywordTokens","searchTokens","filteredCollection","matchedKeywords","result","allWarnings","item","score","warnings","scoreItem","validTokens","t","scoredResults","a","b","resolve","token","normalizedToken","keywordDef","existing","keywordNameToTerms","name","terms","handler","handlerResult","mergedResults","processedItems","keywordResults"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ {
2
+ "gzipSize": 3515,
3
+ "formatted": "3.4 kB"
4
+ }
package/dist/errors.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class E extends Error{constructor(I,r){super(I),this.code=r,this.name="SpotrError"}}const L={INVALID_COLLECTION:"INVALID_COLLECTION",INVALID_FIELD_CONFIG:"INVALID_FIELD_CONFIG",INVALID_FIELD_WEIGHT:"INVALID_FIELD_WEIGHT",INVALID_KEYWORD:"INVALID_KEYWORD",INVALID_HANDLER_RETURN:"INVALID_HANDLER_RETURN"};exports.ErrorCodes=L;exports.SpotrError=E;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class L extends Error{constructor(I,r){super(I),this.code=r,this.name="SpotrError"}}const N={INVALID_COLLECTION:"INVALID_COLLECTION",INVALID_FIELD_CONFIG:"INVALID_FIELD_CONFIG",INVALID_FIELD_WEIGHT:"INVALID_FIELD_WEIGHT",INVALID_KEYWORD:"INVALID_KEYWORD",INVALID_HANDLER_RETURN:"INVALID_HANDLER_RETURN",INVALID_MAX_STRING_LENGTH:"INVALID_MAX_STRING_LENGTH"};exports.ErrorCodes=N;exports.SpotrError=L;
2
2
  //# sourceMappingURL=errors.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.cjs","sources":["../src/errors.ts"],"sourcesContent":["export class SpotrError extends Error {\n constructor(\n message: string,\n public code: string\n ) {\n super(message);\n this.name = 'SpotrError';\n }\n}\n\nexport const ErrorCodes = {\n INVALID_COLLECTION: 'INVALID_COLLECTION',\n INVALID_FIELD_CONFIG: 'INVALID_FIELD_CONFIG',\n INVALID_FIELD_WEIGHT: 'INVALID_FIELD_WEIGHT',\n INVALID_KEYWORD: 'INVALID_KEYWORD',\n INVALID_HANDLER_RETURN: 'INVALID_HANDLER_RETURN',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n"],"names":["SpotrError","message","code","ErrorCodes"],"mappings":"gFAAO,MAAMA,UAAmB,KAAM,CACpC,YACEC,EACOC,EACP,CACA,MAAMD,CAAO,EAFN,KAAA,KAAAC,EAGP,KAAK,KAAO,YACd,CACF,CAEO,MAAMC,EAAa,CACxB,mBAAoB,qBACpB,qBAAsB,uBACtB,qBAAsB,uBACtB,gBAAiB,kBACjB,uBAAwB,wBAC1B"}
1
+ {"version":3,"file":"errors.cjs","sources":["../src/errors.ts"],"sourcesContent":["export class SpotrError extends Error {\n constructor(\n message: string,\n public code: string\n ) {\n super(message);\n this.name = 'SpotrError';\n }\n}\n\nexport const ErrorCodes = {\n INVALID_COLLECTION: 'INVALID_COLLECTION',\n INVALID_FIELD_CONFIG: 'INVALID_FIELD_CONFIG',\n INVALID_FIELD_WEIGHT: 'INVALID_FIELD_WEIGHT',\n INVALID_KEYWORD: 'INVALID_KEYWORD',\n INVALID_HANDLER_RETURN: 'INVALID_HANDLER_RETURN',\n INVALID_MAX_STRING_LENGTH: 'INVALID_MAX_STRING_LENGTH',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n"],"names":["SpotrError","message","code","ErrorCodes"],"mappings":"gFAAO,MAAMA,UAAmB,KAAM,CACpC,YACEC,EACOC,EACP,CACA,MAAMD,CAAO,EAFN,KAAA,KAAAC,EAGP,KAAK,KAAO,YACd,CACF,CAEO,MAAMC,EAAa,CACxB,mBAAoB,qBACpB,qBAAsB,uBACtB,qBAAsB,uBACtB,gBAAiB,kBACjB,uBAAwB,yBACxB,0BAA2B,2BAC7B"}
package/dist/errors.js CHANGED
@@ -1,17 +1,18 @@
1
- class D extends Error {
1
+ class _ extends Error {
2
2
  constructor(I, L) {
3
3
  super(I), this.code = L, this.name = "SpotrError";
4
4
  }
5
5
  }
6
- const E = {
6
+ const D = {
7
7
  INVALID_COLLECTION: "INVALID_COLLECTION",
8
8
  INVALID_FIELD_CONFIG: "INVALID_FIELD_CONFIG",
9
9
  INVALID_FIELD_WEIGHT: "INVALID_FIELD_WEIGHT",
10
10
  INVALID_KEYWORD: "INVALID_KEYWORD",
11
- INVALID_HANDLER_RETURN: "INVALID_HANDLER_RETURN"
11
+ INVALID_HANDLER_RETURN: "INVALID_HANDLER_RETURN",
12
+ INVALID_MAX_STRING_LENGTH: "INVALID_MAX_STRING_LENGTH"
12
13
  };
13
14
  export {
14
- E as ErrorCodes,
15
- D as SpotrError
15
+ D as ErrorCodes,
16
+ _ as SpotrError
16
17
  };
17
18
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sources":["../src/errors.ts"],"sourcesContent":["export class SpotrError extends Error {\n constructor(\n message: string,\n public code: string\n ) {\n super(message);\n this.name = 'SpotrError';\n }\n}\n\nexport const ErrorCodes = {\n INVALID_COLLECTION: 'INVALID_COLLECTION',\n INVALID_FIELD_CONFIG: 'INVALID_FIELD_CONFIG',\n INVALID_FIELD_WEIGHT: 'INVALID_FIELD_WEIGHT',\n INVALID_KEYWORD: 'INVALID_KEYWORD',\n INVALID_HANDLER_RETURN: 'INVALID_HANDLER_RETURN',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n"],"names":["SpotrError","message","code","ErrorCodes"],"mappings":"AAAO,MAAMA,UAAmB,MAAM;AAAA,EACpC,YACEC,GACOC,GACP;AACA,UAAMD,CAAO,GAFN,KAAA,OAAAC,GAGP,KAAK,OAAO;AAAA,EACd;AACF;AAEO,MAAMC,IAAa;AAAA,EACxB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,iBAAiB;AAAA,EACjB,wBAAwB;AAC1B;"}
1
+ {"version":3,"file":"errors.js","sources":["../src/errors.ts"],"sourcesContent":["export class SpotrError extends Error {\n constructor(\n message: string,\n public code: string\n ) {\n super(message);\n this.name = 'SpotrError';\n }\n}\n\nexport const ErrorCodes = {\n INVALID_COLLECTION: 'INVALID_COLLECTION',\n INVALID_FIELD_CONFIG: 'INVALID_FIELD_CONFIG',\n INVALID_FIELD_WEIGHT: 'INVALID_FIELD_WEIGHT',\n INVALID_KEYWORD: 'INVALID_KEYWORD',\n INVALID_HANDLER_RETURN: 'INVALID_HANDLER_RETURN',\n INVALID_MAX_STRING_LENGTH: 'INVALID_MAX_STRING_LENGTH',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n"],"names":["SpotrError","message","code","ErrorCodes"],"mappings":"AAAO,MAAMA,UAAmB,MAAM;AAAA,EACpC,YACEC,GACOC,GACP;AACA,UAAMD,CAAO,GAFN,KAAA,OAAAC,GAGP,KAAK,OAAO;AAAA,EACd;AACF;AAEO,MAAMC,IAAa;AAAA,EACxB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,iBAAiB;AAAA,EACjB,wBAAwB;AAAA,EACxB,2BAA2B;AAC7B;"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function h(l,r){const n=[];for(let t=0;t<=r.length;t++)n[t]=[t];for(let t=0;t<=l.length;t++)n[0][t]=t;for(let t=1;t<=r.length;t++)for(let e=1;e<=l.length;e++)r.charAt(t-1)===l.charAt(e-1)?n[t][e]=n[t-1][e-1]:n[t][e]=Math.min(n[t-1][e-1]+1,n[t][e-1]+1,n[t-1][e]+1);return n[r.length][l.length]}function f(l,r,n=.3,t=!1){const e=t?l:l.toLowerCase(),o=t?r:r.toLowerCase();if(e===o)return 1;if(e.length===0||o.length===0)return 0;if(o.includes(e))return .9+.1*e.length/o.length;const i=h(e,o),s=Math.max(e.length,o.length),c=1-i/s;return c>=n?c:0}exports.fuzzyScore=f;exports.levenshteinDistance=h;
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;
2
2
  //# sourceMappingURL=levenshtein.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"levenshtein.cjs","sources":["../../src/fuzzy/levenshtein.ts"],"sourcesContent":["export 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 function fuzzyScore(\n query: string,\n target: string,\n threshold: number = 0.3,\n caseSensitive: boolean = false\n): number {\n const q = caseSensitive ? query : query.toLowerCase();\n const t = caseSensitive ? target : target.toLowerCase();\n\n if (q === t) return 1;\n if (q.length === 0 || t.length === 0) return 0;\n\n if (t.includes(q)) {\n return 0.9 + (0.1 * q.length / t.length);\n }\n\n const distance = levenshteinDistance(q, t);\n const maxLen = Math.max(q.length, t.length);\n const score = 1 - (distance / maxLen);\n\n return score >= threshold ? score : 0;\n}\n"],"names":["levenshteinDistance","a","b","matrix","i","j","fuzzyScore","query","target","threshold","caseSensitive","q","t","distance","maxLen","score"],"mappings":"gFAAO,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,CAEO,SAASK,EACdC,EACAC,EACAC,EAAoB,GACpBC,EAAyB,GACjB,CACR,MAAMC,EAAID,EAAgBH,EAAQA,EAAM,YAAA,EAClCK,EAAIF,EAAgBF,EAASA,EAAO,YAAA,EAE1C,GAAIG,IAAMC,EAAG,MAAO,GACpB,GAAID,EAAE,SAAW,GAAKC,EAAE,SAAW,EAAG,MAAO,GAE7C,GAAIA,EAAE,SAASD,CAAC,EACd,MAAO,IAAO,GAAMA,EAAE,OAASC,EAAE,OAGnC,MAAMC,EAAWb,EAAoBW,EAAGC,CAAC,EACnCE,EAAS,KAAK,IAAIH,EAAE,OAAQC,EAAE,MAAM,EACpCG,EAAQ,EAAKF,EAAWC,EAE9B,OAAOC,GAASN,EAAYM,EAAQ,CACtC"}
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,29 +1,45 @@
1
- function f(l, r) {
2
- const n = [];
3
- for (let t = 0; t <= r.length; t++)
4
- n[t] = [t];
5
- for (let t = 0; t <= l.length; t++)
6
- n[0][t] = t;
7
- for (let t = 1; t <= r.length; t++)
8
- for (let e = 1; e <= l.length; e++)
9
- r.charAt(t - 1) === l.charAt(e - 1) ? n[t][e] = n[t - 1][e - 1] : n[t][e] = Math.min(
10
- n[t - 1][e - 1] + 1,
11
- n[t][e - 1] + 1,
12
- n[t - 1][e] + 1
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
13
14
  );
14
- return n[r.length][l.length];
15
+ return r[o.length][n.length];
15
16
  }
16
- function i(l, r, n = 0.3, t = !1) {
17
- const e = t ? l : l.toLowerCase(), o = t ? r : r.toLowerCase();
18
- if (e === o) return 1;
19
- if (e.length === 0 || o.length === 0) return 0;
20
- if (o.includes(e))
21
- return 0.9 + 0.1 * e.length / o.length;
22
- const c = f(e, o), s = Math.max(e.length, o.length), h = 1 - c / s;
23
- return h >= n ? h : 0;
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
+ };
24
40
  }
25
41
  export {
26
- i as fuzzyScore,
27
- f as levenshteinDistance
42
+ p as fuzzyScore,
43
+ m as levenshteinDistance
28
44
  };
29
45
  //# sourceMappingURL=levenshtein.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"levenshtein.js","sources":["../../src/fuzzy/levenshtein.ts"],"sourcesContent":["export 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 function fuzzyScore(\n query: string,\n target: string,\n threshold: number = 0.3,\n caseSensitive: boolean = false\n): number {\n const q = caseSensitive ? query : query.toLowerCase();\n const t = caseSensitive ? target : target.toLowerCase();\n\n if (q === t) return 1;\n if (q.length === 0 || t.length === 0) return 0;\n\n if (t.includes(q)) {\n return 0.9 + (0.1 * q.length / t.length);\n }\n\n const distance = levenshteinDistance(q, t);\n const maxLen = Math.max(q.length, t.length);\n const score = 1 - (distance / maxLen);\n\n return score >= threshold ? score : 0;\n}\n"],"names":["levenshteinDistance","a","b","matrix","i","j","fuzzyScore","query","target","threshold","caseSensitive","q","t","distance","maxLen","score"],"mappings":"AAAO,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;AAEO,SAASK,EACdC,GACAC,GACAC,IAAoB,KACpBC,IAAyB,IACjB;AACR,QAAMC,IAAID,IAAgBH,IAAQA,EAAM,YAAA,GAClCK,IAAIF,IAAgBF,IAASA,EAAO,YAAA;AAE1C,MAAIG,MAAMC,EAAG,QAAO;AACpB,MAAID,EAAE,WAAW,KAAKC,EAAE,WAAW,EAAG,QAAO;AAE7C,MAAIA,EAAE,SAASD,CAAC;AACd,WAAO,MAAO,MAAMA,EAAE,SAASC,EAAE;AAGnC,QAAMC,IAAWb,EAAoBW,GAAGC,CAAC,GACnCE,IAAS,KAAK,IAAIH,EAAE,QAAQC,EAAE,MAAM,GACpCG,IAAQ,IAAKF,IAAWC;AAE9B,SAAOC,KAASN,IAAYM,IAAQ;AACtC;"}
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,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("./levenshtein.cjs"),f=require("../utils/nested.cjs");function d(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 w(o,n,e,a){let s=0,r=0;const c=[];for(const t of e){const u=f.getNestedValue(o,t.name);if(u==null){t.name.includes(".")&&c.push(`Field "${t.name}" not found on item`);continue}const l=String(u);let i=0;for(const h of n){const g=m.fuzzyScore(h,l,t.threshold,a);i=Math.max(i,g)}s+=i*t.weight,r+=t.weight}return{score:r>0?s/r:0,warnings:c}}exports.normalizeFieldConfig=d;exports.scoreItem=w;
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;
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 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): { 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 score = fuzzyScore(token, stringValue, field.threshold, caseSensitive);\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","tokens","caseSensitive","totalScore","totalWeight","warnings","value","getNestedValue","stringValue","bestTokenScore","token","score","fuzzyScore"],"mappings":"sJAIO,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,EACuC,CACvC,IAAIC,EAAa,EACbC,EAAc,EAClB,MAAMC,EAAqB,CAAA,EAE3B,UAAWP,KAASF,EAAQ,CAC1B,MAAMU,EAAQC,EAAAA,eAAeP,EAAMF,EAAM,IAAI,EAE7C,GAAIQ,GAAS,KAAM,CACbR,EAAM,KAAK,SAAS,GAAG,GACzBO,EAAS,KAAK,UAAUP,EAAM,IAAI,qBAAqB,EAEzD,QACF,CAEA,MAAMU,EAAc,OAAOF,CAAK,EAEhC,IAAIG,EAAiB,EACrB,UAAWC,KAAST,EAAQ,CAC1B,MAAMU,EAAQC,EAAAA,WAAWF,EAAOF,EAAaV,EAAM,UAAWI,CAAa,EAC3EO,EAAiB,KAAK,IAAIA,EAAgBE,CAAK,CACjD,CAEAR,GAAcM,EAAiBX,EAAM,OACrCM,GAAeN,EAAM,MACvB,CAEA,MAAO,CACL,MAAOM,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 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"}