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.
- package/dist/Spotr.cjs +1 -1
- package/dist/Spotr.cjs.map +1 -1
- package/dist/Spotr.js +84 -78
- package/dist/Spotr.js.map +1 -1
- package/dist/bundle-size.json +4 -0
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.js +6 -5
- package/dist/errors.js.map +1 -1
- package/dist/fuzzy/levenshtein.cjs +1 -1
- package/dist/fuzzy/levenshtein.cjs.map +1 -1
- package/dist/fuzzy/levenshtein.js +39 -23
- package/dist/fuzzy/levenshtein.js.map +1 -1
- package/dist/fuzzy/scorer.cjs +1 -1
- package/dist/fuzzy/scorer.cjs.map +1 -1
- package/dist/fuzzy/scorer.js +26 -19
- package/dist/fuzzy/scorer.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +21 -3
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/preact.cjs.map +1 -1
- package/dist/preact.d.ts +23 -2
- package/dist/preact.js.map +1 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.ts +23 -2
- package/dist/react.js.map +1 -1
- package/dist/solid.cjs.map +1 -1
- package/dist/solid.d.ts +29 -24
- package/dist/solid.js.map +1 -1
- package/dist/svelte.cjs.map +1 -1
- package/dist/svelte.d.ts +28 -20
- package/dist/svelte.js.map +1 -1
- package/dist/types.cjs +2 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/validate.cjs +1 -1
- package/dist/utils/validate.cjs.map +1 -1
- package/dist/utils/validate.js +36 -29
- package/dist/utils/validate.js.map +1 -1
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.d.ts +24 -3
- package/dist/vue.js.map +1 -1
- package/package.json +30 -18
- package/LICENSE +0 -21
- package/README.md +0 -185
package/dist/Spotr.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
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
|
package/dist/Spotr.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Spotr.cjs","sources":["../src/Spotr.ts"],"sourcesContent":["import { SpotrError, ErrorCodes } from './errors';\nimport { normalizeFieldConfig, scoreItem } from './fuzzy';\nimport { tokenize, validateOptions, validateKeywords } from './utils';\nimport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n NormalizedFieldConfig,\n NormalizedKeywordsConfig,\n KeywordDefinition,\n} from './types';\n\nexport class Spotr<T extends object> {\n private _collection: T[];\n private _fields: NormalizedFieldConfig[];\n private _keywords: NormalizedKeywordsConfig | null;\n private _keywordTriggerMap: Map<string, KeywordDefinition<unknown>>;\n private _threshold: number;\n private _limit: number;\n private _debounce: number;\n private _caseSensitive: boolean;\n private _minMatchCharLength: number;\n private _debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private _optionsSnapshot: SpotrOptions<T>;\n\n constructor(options: SpotrOptions<T>) {\n this._optionsSnapshot = options;\n const validated = validateOptions(options);\n this._collection = validated.collection;\n this._threshold = validated.threshold;\n this._limit = validated.limit;\n this._debounce = validated.debounce;\n this._caseSensitive = validated.caseSensitive;\n this._minMatchCharLength = validated.minMatchCharLength;\n\n this._fields = normalizeFieldConfig(options.fields, this._threshold);\n\n this._keywords = options.keywords ? validateKeywords(options.keywords) : null;\n this._keywordTriggerMap = this._buildKeywordTriggerMap();\n }\n\n private _buildKeywordTriggerMap(): Map<string, KeywordDefinition<unknown>> {\n const map = new Map<string, KeywordDefinition<unknown>>();\n if (!this._keywords) return map;\n\n for (const def of this._keywords.definitions) {\n const triggers = Array.isArray(def.triggers) ? def.triggers : [def.triggers];\n for (const trigger of triggers) {\n map.set(trigger.toLowerCase(), def);\n }\n }\n return map;\n }\n\n get collection(): T[] {\n return this._collection;\n }\n\n get options(): SpotrOptions<T> {\n return this._optionsSnapshot;\n }\n\n setCollection(collection: T[] | Set<T>): void {\n this._collection = collection instanceof Set ? Array.from(collection) : collection;\n }\n\n query(search: string): SpotrResult<T> {\n const tokens = tokenize(search);\n \n if (tokens.length === 0) {\n return {\n results: [],\n matchedKeywords: [],\n tokens: [],\n warnings: [],\n };\n }\n\n const { keywordTokens, searchTokens } = this._extractKeywords(tokens);\n\n let filteredCollection = this._collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n if (keywordTokens.size > 0) {\n const result = this._applyKeywords(filteredCollection, keywordTokens);\n filteredCollection = result.collection;\n matchedKeywords.push(...result.matchedKeywords);\n }\n\n if (searchTokens.length === 0) {\n const allWarnings: string[] = [];\n const results: ScoredResult<T>[] = filteredCollection.map((item) => {\n const { score, warnings } = scoreItem(\n item,\n [],\n this._fields,\n this._caseSensitive\n );\n allWarnings.push(...warnings);\n return { item, score };\n });\n\n return {\n results,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n const validTokens = this._minMatchCharLength > 1\n ? searchTokens.filter((t) => t.length >= this._minMatchCharLength)\n : searchTokens;\n\n if (validTokens.length === 0) {\n return {\n results: [],\n matchedKeywords,\n tokens: searchTokens,\n warnings: [],\n };\n }\n\n const allWarnings: string[] = [];\n const scoredResults: ScoredResult<T>[] = [];\n\n for (const item of filteredCollection) {\n const { score, warnings } = scoreItem(\n item,\n validTokens,\n this._fields,\n this._caseSensitive\n );\n allWarnings.push(...warnings);\n\n if (score > 0) {\n scoredResults.push({ item, score });\n }\n }\n\n scoredResults.sort((a, b) => b.score - a.score);\n\n const limitedResults = this._limit < Infinity\n ? scoredResults.slice(0, this._limit)\n : scoredResults;\n\n return {\n results: limitedResults,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n queryAsync(search: string): Promise<SpotrResult<T>> {\n return new Promise((resolve) => {\n if (this._debounce > 0) {\n if (this._debounceTimer) {\n clearTimeout(this._debounceTimer);\n }\n this._debounceTimer = setTimeout(() => {\n resolve(this.query(search));\n }, this._debounce);\n } else {\n resolve(this.query(search));\n }\n });\n }\n\n private _extractKeywords(tokens: string[]): {\n keywordTokens: Map<string, string[]>;\n searchTokens: string[];\n } {\n const keywordTokens = new Map<string, string[]>();\n const searchTokens: string[] = [];\n\n for (const token of tokens) {\n const normalizedToken = this._caseSensitive ? token : token.toLowerCase();\n const keywordDef = this._keywordTriggerMap.get(normalizedToken);\n\n if (keywordDef) {\n const existing = keywordTokens.get(keywordDef.name) || [];\n existing.push(token);\n keywordTokens.set(keywordDef.name, existing);\n } else {\n searchTokens.push(token);\n }\n }\n\n return { keywordTokens, searchTokens };\n }\n\n private _applyKeywords(\n collection: T[],\n keywordTokens: Map<string, string[]>\n ): { collection: T[]; matchedKeywords: MatchedKeyword[] } {\n if (!this._keywords) {\n return { collection, matchedKeywords: [] };\n }\n\n let result = collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n const keywordNameToTerms = new Map<string, string[]>();\n for (const [name, terms] of keywordTokens) {\n keywordNameToTerms.set(name, terms);\n }\n\n if (this._keywords.mode === 'and') {\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (collection: T[], matchedTerms: string[]) => T[];\n result = handler(result, terms);\n matchedKeywords.push({ name: def.name, terms });\n\n if (!Array.isArray(result)) {\n throw new SpotrError(\n `Keyword handler \"${def.name}\" must return an array`,\n ErrorCodes.INVALID_HANDLER_RETURN\n );\n }\n }\n }\n } else {\n const mergedResults: T[] = [];\n const processedItems = new Set<T>();\n\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (collection: T[], matchedTerms: string[]) => T[];\n const keywordResults = handler(this._collection, terms);\n matchedKeywords.push({ name: def.name, terms });\n\n if (!Array.isArray(keywordResults)) {\n throw new SpotrError(\n `Keyword handler \"${def.name}\" must return an array`,\n ErrorCodes.INVALID_HANDLER_RETURN\n );\n }\n\n for (const item of keywordResults) {\n if (!processedItems.has(item)) {\n processedItems.add(item);\n mergedResults.push(item);\n }\n }\n }\n }\n result = mergedResults;\n }\n\n return { collection: result, matchedKeywords };\n }\n}\n"],"names":["Spotr","options","__publicField","validated","validateOptions","normalizeFieldConfig","validateKeywords","map","def","triggers","trigger","collection","search","tokens","tokenize","keywordTokens","searchTokens","filteredCollection","matchedKeywords","result","allWarnings","item","score","warnings","scoreItem","validTokens","t","scoredResults","a","b","resolve","token","normalizedToken","keywordDef","existing","keywordNameToTerms","name","terms","handler","SpotrError","ErrorCodes","mergedResults","processedItems","keywordResults"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Spotr.cjs","sources":["../src/Spotr.ts"],"sourcesContent":["import { normalizeFieldConfig, scoreItem } from './fuzzy';\nimport {\n tokenize,\n validateOptions,\n validateKeywords,\n validateCollection,\n} from './utils';\nimport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n NormalizedFieldConfig,\n NormalizedKeywordsConfig,\n KeywordDefinition,\n} from './types';\n\nexport class Spotr<T extends object> {\n private _collection: T[];\n private _fields: NormalizedFieldConfig[];\n private _keywords: NormalizedKeywordsConfig | null;\n private _keywordTriggerMap: Map<string, KeywordDefinition<unknown>>;\n private _threshold: number;\n private _limit: number;\n private _debounce: number;\n private _caseSensitive: boolean;\n private _minMatchCharLength: number;\n private _maxStringLength: number;\n private _debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private _optionsSnapshot: SpotrOptions<T>;\n\n constructor(options: SpotrOptions<T>) {\n this._optionsSnapshot = options;\n const validated = validateOptions(options);\n this._collection = validated.collection;\n this._threshold = validated.threshold;\n this._limit = validated.limit;\n this._debounce = validated.debounce;\n this._caseSensitive = validated.caseSensitive;\n this._minMatchCharLength = validated.minMatchCharLength;\n this._maxStringLength = validated.maxStringLength;\n\n this._fields = normalizeFieldConfig(options.fields, this._threshold);\n\n this._keywords = options.keywords\n ? validateKeywords(options.keywords)\n : null;\n this._keywordTriggerMap = this._buildKeywordTriggerMap();\n }\n\n private _buildKeywordTriggerMap(): Map<string, KeywordDefinition<unknown>> {\n const map = new Map<string, KeywordDefinition<unknown>>();\n if (!this._keywords) return map;\n\n for (const def of this._keywords.definitions) {\n const triggers = Array.isArray(def.triggers)\n ? def.triggers\n : [def.triggers];\n for (const trigger of triggers) {\n map.set(trigger.toLowerCase(), def);\n }\n }\n return map;\n }\n\n get collection(): T[] {\n return this._collection;\n }\n\n get options(): SpotrOptions<T> {\n return this._optionsSnapshot;\n }\n\n setCollection(collection: T[] | Set<T>): void {\n this._collection = validateCollection(collection as unknown) as T[];\n }\n\n query(search: string): SpotrResult<T> {\n const tokens = tokenize(search);\n\n if (tokens.length === 0) {\n return {\n results: [],\n matchedKeywords: [],\n tokens: [],\n warnings: [],\n };\n }\n\n const { keywordTokens, searchTokens } = this._extractKeywords(tokens);\n\n let filteredCollection = this._collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n if (keywordTokens.size > 0) {\n const result = this._applyKeywords(filteredCollection, keywordTokens);\n filteredCollection = result.collection;\n matchedKeywords.push(...result.matchedKeywords);\n }\n\n if (searchTokens.length === 0) {\n const allWarnings: string[] = [];\n const results: ScoredResult<T>[] = filteredCollection.map((item) => {\n const { score, warnings } = scoreItem(\n item,\n [],\n this._fields,\n this._caseSensitive,\n this._maxStringLength\n );\n allWarnings.push(...warnings);\n return { item, score };\n });\n\n return {\n results,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n const validTokens =\n this._minMatchCharLength > 1\n ? searchTokens.filter((t) => t.length >= this._minMatchCharLength)\n : searchTokens;\n\n if (validTokens.length === 0) {\n return {\n results: [],\n matchedKeywords,\n tokens: searchTokens,\n warnings: [],\n };\n }\n\n const allWarnings: string[] = [];\n const scoredResults: ScoredResult<T>[] = [];\n\n for (const item of filteredCollection) {\n const { score, warnings } = scoreItem(\n item,\n validTokens,\n this._fields,\n this._caseSensitive,\n this._maxStringLength\n );\n allWarnings.push(...warnings);\n\n if (score > 0) {\n scoredResults.push({ item, score });\n }\n }\n\n scoredResults.sort((a, b) => b.score - a.score);\n\n const limitedResults =\n this._limit < Infinity\n ? scoredResults.slice(0, this._limit)\n : scoredResults;\n\n return {\n results: limitedResults,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n queryAsync(search: string): Promise<SpotrResult<T>> {\n return new Promise((resolve) => {\n if (this._debounce > 0) {\n if (this._debounceTimer) {\n clearTimeout(this._debounceTimer);\n }\n this._debounceTimer = setTimeout(() => {\n resolve(this.query(search));\n }, this._debounce);\n } else {\n resolve(this.query(search));\n }\n });\n }\n\n private _extractKeywords(tokens: string[]): {\n keywordTokens: Map<string, string[]>;\n searchTokens: string[];\n } {\n const keywordTokens = new Map<string, string[]>();\n const searchTokens: string[] = [];\n\n for (const token of tokens) {\n const normalizedToken = this._caseSensitive ? token : token.toLowerCase();\n const keywordDef = this._keywordTriggerMap.get(normalizedToken);\n\n if (keywordDef) {\n const existing = keywordTokens.get(keywordDef.name) || [];\n existing.push(token);\n keywordTokens.set(keywordDef.name, existing);\n } else {\n searchTokens.push(token);\n }\n }\n\n return { keywordTokens, searchTokens };\n }\n\n private _applyKeywords(\n collection: T[],\n keywordTokens: Map<string, string[]>\n ): { collection: T[]; matchedKeywords: MatchedKeyword[] } {\n if (!this._keywords) {\n return { collection, matchedKeywords: [] };\n }\n\n let result = collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n const keywordNameToTerms = new Map<string, string[]>();\n for (const [name, terms] of keywordTokens) {\n keywordNameToTerms.set(name, terms);\n }\n\n if (this._keywords.mode === 'and') {\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (\n collection: T[],\n matchedTerms: string[]\n ) => T[];\n const handlerResult = handler(result, terms);\n\n if (!Array.isArray(handlerResult)) {\n console.error(\n `[Spotr] Keyword handler \"${def.name}\" must return an array, received ${typeof handlerResult}. Skipping this filter.`\n );\n // Skip this keyword - keep current collection for this step\n continue;\n }\n\n result = handlerResult;\n matchedKeywords.push({ name: def.name, terms });\n }\n }\n } else {\n const mergedResults: T[] = [];\n const processedItems = new Set<T>();\n\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (\n collection: T[],\n matchedTerms: string[]\n ) => T[];\n const keywordResults = handler(this._collection, terms);\n\n if (!Array.isArray(keywordResults)) {\n console.error(\n `[Spotr] Keyword handler \"${def.name}\" must return an array, received ${typeof keywordResults}. Skipping this filter.`\n );\n // Skip this keyword - don't add its results\n continue;\n }\n\n matchedKeywords.push({ name: def.name, terms });\n\n for (const item of keywordResults) {\n if (!processedItems.has(item)) {\n processedItems.add(item);\n mergedResults.push(item);\n }\n }\n }\n }\n result = mergedResults;\n }\n\n return { collection: result, matchedKeywords };\n }\n}\n"],"names":["Spotr","options","__publicField","validated","validateOptions","normalizeFieldConfig","validateKeywords","map","def","triggers","trigger","collection","validateCollection","search","tokens","tokenize","keywordTokens","searchTokens","filteredCollection","matchedKeywords","result","allWarnings","item","score","warnings","scoreItem","validTokens","t","scoredResults","a","b","resolve","token","normalizedToken","keywordDef","existing","keywordNameToTerms","name","terms","handler","handlerResult","mergedResults","processedItems","keywordResults"],"mappings":"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"}
|
package/dist/Spotr.js
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
30
|
-
for (const r of
|
|
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 =
|
|
42
|
+
this._collection = T(e);
|
|
43
43
|
}
|
|
44
44
|
query(e) {
|
|
45
|
-
const t =
|
|
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:
|
|
54
|
-
let
|
|
53
|
+
const { keywordTokens: c, searchTokens: r } = this._extractKeywords(t);
|
|
54
|
+
let a = this._collection;
|
|
55
55
|
const s = [];
|
|
56
|
-
if (
|
|
57
|
-
const
|
|
58
|
-
|
|
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
|
|
61
|
+
const n = [];
|
|
62
62
|
return {
|
|
63
|
-
results:
|
|
64
|
-
const { score:
|
|
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
|
|
71
|
+
return n.push(...y), { item: u, score: f };
|
|
71
72
|
}),
|
|
72
73
|
matchedKeywords: s,
|
|
73
74
|
tokens: r,
|
|
74
|
-
warnings: [...new Set(
|
|
75
|
+
warnings: [...new Set(n)]
|
|
75
76
|
};
|
|
76
77
|
}
|
|
77
|
-
const
|
|
78
|
-
if (
|
|
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
|
|
86
|
-
for (const
|
|
87
|
-
const { score: d, warnings: u } =
|
|
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
|
-
|
|
95
|
+
l.push(...u), d > 0 && h.push({ item: n, score: d });
|
|
94
96
|
}
|
|
95
|
-
return
|
|
96
|
-
results: this._limit < 1 / 0 ?
|
|
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(
|
|
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(),
|
|
112
|
+
const t = /* @__PURE__ */ new Map(), c = [];
|
|
111
113
|
for (const r of e) {
|
|
112
|
-
const
|
|
114
|
+
const a = this._caseSensitive ? r : r.toLowerCase(), s = this._keywordTriggerMap.get(a);
|
|
113
115
|
if (s) {
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
+
const i = t.get(s.name) || [];
|
|
117
|
+
i.push(r), t.set(s.name, i);
|
|
116
118
|
} else
|
|
117
|
-
|
|
119
|
+
c.push(r);
|
|
118
120
|
}
|
|
119
|
-
return { keywordTokens: t, searchTokens:
|
|
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
|
|
125
|
-
const r = [],
|
|
126
|
-
for (const [s,
|
|
127
|
-
|
|
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
|
|
131
|
-
if (
|
|
132
|
-
const
|
|
133
|
-
if (
|
|
134
|
-
|
|
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 = [],
|
|
142
|
-
for (const
|
|
143
|
-
const
|
|
144
|
-
if (
|
|
145
|
-
const
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
`Keyword handler "${
|
|
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
|
-
|
|
152
|
-
|
|
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
|
-
|
|
161
|
+
c = s;
|
|
156
162
|
}
|
|
157
|
-
return { collection:
|
|
163
|
+
return { collection: c, matchedKeywords: r };
|
|
158
164
|
}
|
|
159
165
|
}
|
|
160
166
|
export {
|
|
161
|
-
|
|
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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"}
|
package/dist/errors.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class
|
|
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
|
package/dist/errors.cjs.map
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
|
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
|
-
|
|
15
|
-
|
|
15
|
+
D as ErrorCodes,
|
|
16
|
+
_ as SpotrError
|
|
16
17
|
};
|
|
17
18
|
//# sourceMappingURL=errors.js.map
|
package/dist/errors.js.map
CHANGED
|
@@ -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;
|
|
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
|
|
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":["
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
|
15
|
+
return r[o.length][n.length];
|
|
15
16
|
}
|
|
16
|
-
function
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
27
|
-
|
|
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":["
|
|
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;"}
|
package/dist/fuzzy/scorer.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
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
|
|
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"}
|