spotr 0.1.3 → 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 +2 -0
- package/dist/Spotr.cjs.map +1 -0
- package/dist/Spotr.js +169 -0
- package/dist/Spotr.js.map +1 -0
- package/dist/bundle-size.json +4 -0
- package/dist/errors.cjs +2 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.js +18 -0
- package/dist/errors.js.map +1 -0
- package/dist/fuzzy/levenshtein.cjs +2 -0
- package/dist/fuzzy/levenshtein.cjs.map +1 -0
- package/dist/fuzzy/levenshtein.js +45 -0
- package/dist/fuzzy/levenshtein.js.map +1 -0
- package/dist/fuzzy/scorer.cjs +2 -0
- package/dist/fuzzy/scorer.cjs.map +1 -0
- package/dist/fuzzy/scorer.js +43 -0
- package/dist/fuzzy/scorer.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +111 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/preact.cjs +2 -0
- package/dist/preact.cjs.map +1 -0
- package/dist/preact.d.ts +92 -0
- package/dist/preact.js +24 -0
- package/dist/preact.js.map +1 -0
- package/dist/react.cjs +2 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.ts +92 -0
- package/dist/react.js +24 -0
- package/dist/react.js.map +1 -0
- package/dist/solid.cjs +2 -0
- package/dist/solid.cjs.map +1 -0
- package/dist/solid.d.ts +100 -0
- package/dist/solid.js +11 -0
- package/dist/solid.js.map +1 -0
- package/dist/svelte.cjs +2 -0
- package/dist/svelte.cjs.map +1 -0
- package/dist/svelte.d.ts +103 -0
- package/dist/svelte.js +11 -0
- package/dist/svelte.js.map +1 -0
- 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/nested.cjs +2 -0
- package/dist/utils/nested.cjs.map +1 -0
- package/dist/utils/nested.js +15 -0
- package/dist/utils/nested.js.map +1 -0
- package/dist/utils/tokenize.cjs +2 -0
- package/dist/utils/tokenize.cjs.map +1 -0
- package/dist/utils/tokenize.js +7 -0
- package/dist/utils/tokenize.js.map +1 -0
- package/dist/utils/validate.cjs +2 -0
- package/dist/utils/validate.cjs.map +1 -0
- package/dist/utils/validate.js +104 -0
- package/dist/utils/validate.js.map +1 -0
- package/dist/vue.cjs +2 -0
- package/dist/vue.cjs.map +1 -0
- package/dist/vue.d.ts +95 -0
- package/dist/vue.js +17 -0
- package/dist/vue.js.map +1 -0
- package/package.json +110 -25
- package/.babelrc +0 -6
- package/.npmignore +0 -37
- package/LICENSE +0 -21
- package/README.md +0 -197
- package/dist/spotr.min.js +0 -1
- package/dist/spotr.min.js.map +0 -1
- package/examples/basic/.babelrc +0 -8
- package/examples/basic/.npmignore +0 -5
- package/examples/basic/README.md +0 -19
- package/examples/basic/companies.json +0 -101
- package/examples/basic/index.html +0 -11
- package/examples/basic/index.js +0 -2
- package/examples/basic/package.json +0 -29
- package/examples/basic/src/App.vue +0 -104
- package/examples/basic/src/main.js +0 -11
- package/examples/basic/src/search.js +0 -15
- package/examples/basic/webpack.config.js +0 -80
- package/examples/basic/yarn.lock +0 -3960
- package/index.js +0 -1
- package/src/spotr.js +0 -120
- package/webpack.config.js +0 -63
- package/yarn.lock +0 -3125
package/dist/Spotr.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var p=Object.defineProperty;var k=(u,e,t)=>e in u?p(u,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):u[e]=t;var o=(u,e,t)=>k(u,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("./utils/validate.cjs"),_=require("./fuzzy/scorer.cjs"),S=require("./utils/tokenize.cjs");class T{constructor(e){o(this,"_collection");o(this,"_fields");o(this,"_keywords");o(this,"_keywordTriggerMap");o(this,"_threshold");o(this,"_limit");o(this,"_debounce");o(this,"_caseSensitive");o(this,"_minMatchCharLength");o(this,"_maxStringLength");o(this,"_debounceTimer",null);o(this,"_optionsSnapshot");this._optionsSnapshot=e;const t=m.validateOptions(e);this._collection=t.collection,this._threshold=t.threshold,this._limit=t.limit,this._debounce=t.debounce,this._caseSensitive=t.caseSensitive,this._minMatchCharLength=t.minMatchCharLength,this._maxStringLength=t.maxStringLength,this._fields=_.normalizeFieldConfig(e.fields,this._threshold),this._keywords=e.keywords?m.validateKeywords(e.keywords):null,this._keywordTriggerMap=this._buildKeywordTriggerMap()}_buildKeywordTriggerMap(){const e=new Map;if(!this._keywords)return e;for(const t of this._keywords.definitions){const c=Array.isArray(t.triggers)?t.triggers:[t.triggers];for(const r of c)e.set(r.toLowerCase(),t)}return e}get collection(){return this._collection}get options(){return this._optionsSnapshot}setCollection(e){this._collection=m.validateCollection(e)}query(e){const t=S.tokenize(e);if(t.length===0)return{results:[],matchedKeywords:[],tokens:[],warnings:[]};const{keywordTokens:c,searchTokens:r}=this._extractKeywords(t);let h=this._collection;const s=[];if(c.size>0){const n=this._applyKeywords(h,c);h=n.collection,s.push(...n.matchedKeywords)}if(r.length===0){const n=[];return{results:h.map(g=>{const{score:y,warnings:w}=_.scoreItem(g,[],this._fields,this._caseSensitive,this._maxStringLength);return n.push(...w),{item:g,score:y}}),matchedKeywords:s,tokens:r,warnings:[...new Set(n)]}}const i=this._minMatchCharLength>1?r.filter(n=>n.length>=this._minMatchCharLength):r;if(i.length===0)return{results:[],matchedKeywords:s,tokens:r,warnings:[]};const l=[],a=[];for(const n of h){const{score:d,warnings:g}=_.scoreItem(n,i,this._fields,this._caseSensitive,this._maxStringLength);l.push(...g),d>0&&a.push({item:n,score:d})}return a.sort((n,d)=>d.score-n.score),{results:this._limit<1/0?a.slice(0,this._limit):a,matchedKeywords:s,tokens:r,warnings:[...new Set(l)]}}queryAsync(e){return new Promise(t=>{this._debounce>0?(this._debounceTimer&&clearTimeout(this._debounceTimer),this._debounceTimer=setTimeout(()=>{t(this.query(e))},this._debounce)):t(this.query(e))})}_extractKeywords(e){const t=new Map,c=[];for(const r of e){const h=this._caseSensitive?r:r.toLowerCase(),s=this._keywordTriggerMap.get(h);if(s){const i=t.get(s.name)||[];i.push(r),t.set(s.name,i)}else c.push(r)}return{keywordTokens:t,searchTokens:c}}_applyKeywords(e,t){if(!this._keywords)return{collection:e,matchedKeywords:[]};let c=e;const r=[],h=new Map;for(const[s,i]of t)h.set(s,i);if(this._keywords.mode==="and")for(const s of this._keywords.definitions){const i=h.get(s.name);if(i){const l=s.handler,a=l(c,i);if(!Array.isArray(a)){console.error(`[Spotr] Keyword handler "${s.name}" must return an array, received ${typeof a}. Skipping this filter.`);continue}c=a,r.push({name:s.name,terms:i})}}else{const s=[],i=new Set;for(const l of this._keywords.definitions){const a=h.get(l.name);if(a){const f=l.handler,n=f(this._collection,a);if(!Array.isArray(n)){console.error(`[Spotr] Keyword handler "${l.name}" must return an array, received ${typeof n}. Skipping this filter.`);continue}r.push({name:l.name,terms:a});for(const d of n)i.has(d)||(i.add(d),s.push(d))}}c=s}return{collection:c,matchedKeywords:r}}}exports.Spotr=T;
|
|
2
|
+
//# sourceMappingURL=Spotr.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Spotr.cjs","sources":["../src/Spotr.ts"],"sourcesContent":["import { normalizeFieldConfig, scoreItem } from './fuzzy';\nimport {\n tokenize,\n validateOptions,\n validateKeywords,\n validateCollection,\n} from './utils';\nimport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n NormalizedFieldConfig,\n NormalizedKeywordsConfig,\n KeywordDefinition,\n} from './types';\n\nexport class Spotr<T extends object> {\n private _collection: T[];\n private _fields: NormalizedFieldConfig[];\n private _keywords: NormalizedKeywordsConfig | null;\n private _keywordTriggerMap: Map<string, KeywordDefinition<unknown>>;\n private _threshold: number;\n private _limit: number;\n private _debounce: number;\n private _caseSensitive: boolean;\n private _minMatchCharLength: number;\n private _maxStringLength: number;\n private _debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private _optionsSnapshot: SpotrOptions<T>;\n\n constructor(options: SpotrOptions<T>) {\n this._optionsSnapshot = options;\n const validated = validateOptions(options);\n this._collection = validated.collection;\n this._threshold = validated.threshold;\n this._limit = validated.limit;\n this._debounce = validated.debounce;\n this._caseSensitive = validated.caseSensitive;\n this._minMatchCharLength = validated.minMatchCharLength;\n this._maxStringLength = validated.maxStringLength;\n\n this._fields = normalizeFieldConfig(options.fields, this._threshold);\n\n this._keywords = options.keywords\n ? validateKeywords(options.keywords)\n : null;\n this._keywordTriggerMap = this._buildKeywordTriggerMap();\n }\n\n private _buildKeywordTriggerMap(): Map<string, KeywordDefinition<unknown>> {\n const map = new Map<string, KeywordDefinition<unknown>>();\n if (!this._keywords) return map;\n\n for (const def of this._keywords.definitions) {\n const triggers = Array.isArray(def.triggers)\n ? def.triggers\n : [def.triggers];\n for (const trigger of triggers) {\n map.set(trigger.toLowerCase(), def);\n }\n }\n return map;\n }\n\n get collection(): T[] {\n return this._collection;\n }\n\n get options(): SpotrOptions<T> {\n return this._optionsSnapshot;\n }\n\n setCollection(collection: T[] | Set<T>): void {\n this._collection = validateCollection(collection as unknown) as T[];\n }\n\n query(search: string): SpotrResult<T> {\n const tokens = tokenize(search);\n\n if (tokens.length === 0) {\n return {\n results: [],\n matchedKeywords: [],\n tokens: [],\n warnings: [],\n };\n }\n\n const { keywordTokens, searchTokens } = this._extractKeywords(tokens);\n\n let filteredCollection = this._collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n if (keywordTokens.size > 0) {\n const result = this._applyKeywords(filteredCollection, keywordTokens);\n filteredCollection = result.collection;\n matchedKeywords.push(...result.matchedKeywords);\n }\n\n if (searchTokens.length === 0) {\n const allWarnings: string[] = [];\n const results: ScoredResult<T>[] = filteredCollection.map((item) => {\n const { score, warnings } = scoreItem(\n item,\n [],\n this._fields,\n this._caseSensitive,\n this._maxStringLength\n );\n allWarnings.push(...warnings);\n return { item, score };\n });\n\n return {\n results,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n const validTokens =\n this._minMatchCharLength > 1\n ? searchTokens.filter((t) => t.length >= this._minMatchCharLength)\n : searchTokens;\n\n if (validTokens.length === 0) {\n return {\n results: [],\n matchedKeywords,\n tokens: searchTokens,\n warnings: [],\n };\n }\n\n const allWarnings: string[] = [];\n const scoredResults: ScoredResult<T>[] = [];\n\n for (const item of filteredCollection) {\n const { score, warnings } = scoreItem(\n item,\n validTokens,\n this._fields,\n this._caseSensitive,\n this._maxStringLength\n );\n allWarnings.push(...warnings);\n\n if (score > 0) {\n scoredResults.push({ item, score });\n }\n }\n\n scoredResults.sort((a, b) => b.score - a.score);\n\n const limitedResults =\n this._limit < Infinity\n ? scoredResults.slice(0, this._limit)\n : scoredResults;\n\n return {\n results: limitedResults,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n queryAsync(search: string): Promise<SpotrResult<T>> {\n return new Promise((resolve) => {\n if (this._debounce > 0) {\n if (this._debounceTimer) {\n clearTimeout(this._debounceTimer);\n }\n this._debounceTimer = setTimeout(() => {\n resolve(this.query(search));\n }, this._debounce);\n } else {\n resolve(this.query(search));\n }\n });\n }\n\n private _extractKeywords(tokens: string[]): {\n keywordTokens: Map<string, string[]>;\n searchTokens: string[];\n } {\n const keywordTokens = new Map<string, string[]>();\n const searchTokens: string[] = [];\n\n for (const token of tokens) {\n const normalizedToken = this._caseSensitive ? token : token.toLowerCase();\n const keywordDef = this._keywordTriggerMap.get(normalizedToken);\n\n if (keywordDef) {\n const existing = keywordTokens.get(keywordDef.name) || [];\n existing.push(token);\n keywordTokens.set(keywordDef.name, existing);\n } else {\n searchTokens.push(token);\n }\n }\n\n return { keywordTokens, searchTokens };\n }\n\n private _applyKeywords(\n collection: T[],\n keywordTokens: Map<string, string[]>\n ): { collection: T[]; matchedKeywords: MatchedKeyword[] } {\n if (!this._keywords) {\n return { collection, matchedKeywords: [] };\n }\n\n let result = collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n const keywordNameToTerms = new Map<string, string[]>();\n for (const [name, terms] of keywordTokens) {\n keywordNameToTerms.set(name, terms);\n }\n\n if (this._keywords.mode === 'and') {\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (\n collection: T[],\n matchedTerms: string[]\n ) => T[];\n const handlerResult = handler(result, terms);\n\n if (!Array.isArray(handlerResult)) {\n console.error(\n `[Spotr] Keyword handler \"${def.name}\" must return an array, received ${typeof handlerResult}. Skipping this filter.`\n );\n // Skip this keyword - keep current collection for this step\n continue;\n }\n\n result = handlerResult;\n matchedKeywords.push({ name: def.name, terms });\n }\n }\n } else {\n const mergedResults: T[] = [];\n const processedItems = new Set<T>();\n\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (\n collection: T[],\n matchedTerms: string[]\n ) => T[];\n const keywordResults = handler(this._collection, terms);\n\n if (!Array.isArray(keywordResults)) {\n console.error(\n `[Spotr] Keyword handler \"${def.name}\" must return an array, received ${typeof keywordResults}. Skipping this filter.`\n );\n // Skip this keyword - don't add its results\n continue;\n }\n\n matchedKeywords.push({ name: def.name, terms });\n\n for (const item of keywordResults) {\n if (!processedItems.has(item)) {\n processedItems.add(item);\n mergedResults.push(item);\n }\n }\n }\n }\n result = mergedResults;\n }\n\n return { collection: result, matchedKeywords };\n }\n}\n"],"names":["Spotr","options","__publicField","validated","validateOptions","normalizeFieldConfig","validateKeywords","map","def","triggers","trigger","collection","validateCollection","search","tokens","tokenize","keywordTokens","searchTokens","filteredCollection","matchedKeywords","result","allWarnings","item","score","warnings","scoreItem","validTokens","t","scoredResults","a","b","resolve","token","normalizedToken","keywordDef","existing","keywordNameToTerms","name","terms","handler","handlerResult","mergedResults","processedItems","keywordResults"],"mappings":"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"}
|
package/dist/Spotr.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
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 {
|
|
8
|
+
constructor(e) {
|
|
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
|
+
this._optionsSnapshot = e;
|
|
22
|
+
const t = k(e);
|
|
23
|
+
this._collection = t.collection, this._threshold = t.threshold, this._limit = t.limit, this._debounce = t.debounce, this._caseSensitive = t.caseSensitive, this._minMatchCharLength = t.minMatchCharLength, this._maxStringLength = t.maxStringLength, this._fields = K(e.fields, this._threshold), this._keywords = e.keywords ? S(e.keywords) : null, this._keywordTriggerMap = this._buildKeywordTriggerMap();
|
|
24
|
+
}
|
|
25
|
+
_buildKeywordTriggerMap() {
|
|
26
|
+
const e = /* @__PURE__ */ new Map();
|
|
27
|
+
if (!this._keywords) return e;
|
|
28
|
+
for (const t of this._keywords.definitions) {
|
|
29
|
+
const c = Array.isArray(t.triggers) ? t.triggers : [t.triggers];
|
|
30
|
+
for (const r of c)
|
|
31
|
+
e.set(r.toLowerCase(), t);
|
|
32
|
+
}
|
|
33
|
+
return e;
|
|
34
|
+
}
|
|
35
|
+
get collection() {
|
|
36
|
+
return this._collection;
|
|
37
|
+
}
|
|
38
|
+
get options() {
|
|
39
|
+
return this._optionsSnapshot;
|
|
40
|
+
}
|
|
41
|
+
setCollection(e) {
|
|
42
|
+
this._collection = T(e);
|
|
43
|
+
}
|
|
44
|
+
query(e) {
|
|
45
|
+
const t = M(e);
|
|
46
|
+
if (t.length === 0)
|
|
47
|
+
return {
|
|
48
|
+
results: [],
|
|
49
|
+
matchedKeywords: [],
|
|
50
|
+
tokens: [],
|
|
51
|
+
warnings: []
|
|
52
|
+
};
|
|
53
|
+
const { keywordTokens: c, searchTokens: r } = this._extractKeywords(t);
|
|
54
|
+
let a = this._collection;
|
|
55
|
+
const s = [];
|
|
56
|
+
if (c.size > 0) {
|
|
57
|
+
const n = this._applyKeywords(a, c);
|
|
58
|
+
a = n.collection, s.push(...n.matchedKeywords);
|
|
59
|
+
}
|
|
60
|
+
if (r.length === 0) {
|
|
61
|
+
const n = [];
|
|
62
|
+
return {
|
|
63
|
+
results: a.map((u) => {
|
|
64
|
+
const { score: f, warnings: y } = _(
|
|
65
|
+
u,
|
|
66
|
+
[],
|
|
67
|
+
this._fields,
|
|
68
|
+
this._caseSensitive,
|
|
69
|
+
this._maxStringLength
|
|
70
|
+
);
|
|
71
|
+
return n.push(...y), { item: u, score: f };
|
|
72
|
+
}),
|
|
73
|
+
matchedKeywords: s,
|
|
74
|
+
tokens: r,
|
|
75
|
+
warnings: [...new Set(n)]
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const i = this._minMatchCharLength > 1 ? r.filter((n) => n.length >= this._minMatchCharLength) : r;
|
|
79
|
+
if (i.length === 0)
|
|
80
|
+
return {
|
|
81
|
+
results: [],
|
|
82
|
+
matchedKeywords: s,
|
|
83
|
+
tokens: r,
|
|
84
|
+
warnings: []
|
|
85
|
+
};
|
|
86
|
+
const l = [], h = [];
|
|
87
|
+
for (const n of a) {
|
|
88
|
+
const { score: d, warnings: u } = _(
|
|
89
|
+
n,
|
|
90
|
+
i,
|
|
91
|
+
this._fields,
|
|
92
|
+
this._caseSensitive,
|
|
93
|
+
this._maxStringLength
|
|
94
|
+
);
|
|
95
|
+
l.push(...u), d > 0 && h.push({ item: n, score: d });
|
|
96
|
+
}
|
|
97
|
+
return h.sort((n, d) => d.score - n.score), {
|
|
98
|
+
results: this._limit < 1 / 0 ? h.slice(0, this._limit) : h,
|
|
99
|
+
matchedKeywords: s,
|
|
100
|
+
tokens: r,
|
|
101
|
+
warnings: [...new Set(l)]
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
queryAsync(e) {
|
|
105
|
+
return new Promise((t) => {
|
|
106
|
+
this._debounce > 0 ? (this._debounceTimer && clearTimeout(this._debounceTimer), this._debounceTimer = setTimeout(() => {
|
|
107
|
+
t(this.query(e));
|
|
108
|
+
}, this._debounce)) : t(this.query(e));
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
_extractKeywords(e) {
|
|
112
|
+
const t = /* @__PURE__ */ new Map(), c = [];
|
|
113
|
+
for (const r of e) {
|
|
114
|
+
const a = this._caseSensitive ? r : r.toLowerCase(), s = this._keywordTriggerMap.get(a);
|
|
115
|
+
if (s) {
|
|
116
|
+
const i = t.get(s.name) || [];
|
|
117
|
+
i.push(r), t.set(s.name, i);
|
|
118
|
+
} else
|
|
119
|
+
c.push(r);
|
|
120
|
+
}
|
|
121
|
+
return { keywordTokens: t, searchTokens: c };
|
|
122
|
+
}
|
|
123
|
+
_applyKeywords(e, t) {
|
|
124
|
+
if (!this._keywords)
|
|
125
|
+
return { collection: e, matchedKeywords: [] };
|
|
126
|
+
let c = e;
|
|
127
|
+
const r = [], a = /* @__PURE__ */ new Map();
|
|
128
|
+
for (const [s, i] of t)
|
|
129
|
+
a.set(s, i);
|
|
130
|
+
if (this._keywords.mode === "and")
|
|
131
|
+
for (const s of this._keywords.definitions) {
|
|
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.`
|
|
138
|
+
);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
c = h, r.push({ name: s.name, terms: i });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
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.`
|
|
153
|
+
);
|
|
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));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
c = s;
|
|
162
|
+
}
|
|
163
|
+
return { collection: c, matchedKeywords: r };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
export {
|
|
167
|
+
x as Spotr
|
|
168
|
+
};
|
|
169
|
+
//# sourceMappingURL=Spotr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Spotr.js","sources":["../src/Spotr.ts"],"sourcesContent":["import { normalizeFieldConfig, scoreItem } from './fuzzy';\nimport {\n tokenize,\n validateOptions,\n validateKeywords,\n validateCollection,\n} from './utils';\nimport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n NormalizedFieldConfig,\n NormalizedKeywordsConfig,\n KeywordDefinition,\n} from './types';\n\nexport class Spotr<T extends object> {\n private _collection: T[];\n private _fields: NormalizedFieldConfig[];\n private _keywords: NormalizedKeywordsConfig | null;\n private _keywordTriggerMap: Map<string, KeywordDefinition<unknown>>;\n private _threshold: number;\n private _limit: number;\n private _debounce: number;\n private _caseSensitive: boolean;\n private _minMatchCharLength: number;\n private _maxStringLength: number;\n private _debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private _optionsSnapshot: SpotrOptions<T>;\n\n constructor(options: SpotrOptions<T>) {\n this._optionsSnapshot = options;\n const validated = validateOptions(options);\n this._collection = validated.collection;\n this._threshold = validated.threshold;\n this._limit = validated.limit;\n this._debounce = validated.debounce;\n this._caseSensitive = validated.caseSensitive;\n this._minMatchCharLength = validated.minMatchCharLength;\n this._maxStringLength = validated.maxStringLength;\n\n this._fields = normalizeFieldConfig(options.fields, this._threshold);\n\n this._keywords = options.keywords\n ? validateKeywords(options.keywords)\n : null;\n this._keywordTriggerMap = this._buildKeywordTriggerMap();\n }\n\n private _buildKeywordTriggerMap(): Map<string, KeywordDefinition<unknown>> {\n const map = new Map<string, KeywordDefinition<unknown>>();\n if (!this._keywords) return map;\n\n for (const def of this._keywords.definitions) {\n const triggers = Array.isArray(def.triggers)\n ? def.triggers\n : [def.triggers];\n for (const trigger of triggers) {\n map.set(trigger.toLowerCase(), def);\n }\n }\n return map;\n }\n\n get collection(): T[] {\n return this._collection;\n }\n\n get options(): SpotrOptions<T> {\n return this._optionsSnapshot;\n }\n\n setCollection(collection: T[] | Set<T>): void {\n this._collection = validateCollection(collection as unknown) as T[];\n }\n\n query(search: string): SpotrResult<T> {\n const tokens = tokenize(search);\n\n if (tokens.length === 0) {\n return {\n results: [],\n matchedKeywords: [],\n tokens: [],\n warnings: [],\n };\n }\n\n const { keywordTokens, searchTokens } = this._extractKeywords(tokens);\n\n let filteredCollection = this._collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n if (keywordTokens.size > 0) {\n const result = this._applyKeywords(filteredCollection, keywordTokens);\n filteredCollection = result.collection;\n matchedKeywords.push(...result.matchedKeywords);\n }\n\n if (searchTokens.length === 0) {\n const allWarnings: string[] = [];\n const results: ScoredResult<T>[] = filteredCollection.map((item) => {\n const { score, warnings } = scoreItem(\n item,\n [],\n this._fields,\n this._caseSensitive,\n this._maxStringLength\n );\n allWarnings.push(...warnings);\n return { item, score };\n });\n\n return {\n results,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n const validTokens =\n this._minMatchCharLength > 1\n ? searchTokens.filter((t) => t.length >= this._minMatchCharLength)\n : searchTokens;\n\n if (validTokens.length === 0) {\n return {\n results: [],\n matchedKeywords,\n tokens: searchTokens,\n warnings: [],\n };\n }\n\n const allWarnings: string[] = [];\n const scoredResults: ScoredResult<T>[] = [];\n\n for (const item of filteredCollection) {\n const { score, warnings } = scoreItem(\n item,\n validTokens,\n this._fields,\n this._caseSensitive,\n this._maxStringLength\n );\n allWarnings.push(...warnings);\n\n if (score > 0) {\n scoredResults.push({ item, score });\n }\n }\n\n scoredResults.sort((a, b) => b.score - a.score);\n\n const limitedResults =\n this._limit < Infinity\n ? scoredResults.slice(0, this._limit)\n : scoredResults;\n\n return {\n results: limitedResults,\n matchedKeywords,\n tokens: searchTokens,\n warnings: [...new Set(allWarnings)],\n };\n }\n\n queryAsync(search: string): Promise<SpotrResult<T>> {\n return new Promise((resolve) => {\n if (this._debounce > 0) {\n if (this._debounceTimer) {\n clearTimeout(this._debounceTimer);\n }\n this._debounceTimer = setTimeout(() => {\n resolve(this.query(search));\n }, this._debounce);\n } else {\n resolve(this.query(search));\n }\n });\n }\n\n private _extractKeywords(tokens: string[]): {\n keywordTokens: Map<string, string[]>;\n searchTokens: string[];\n } {\n const keywordTokens = new Map<string, string[]>();\n const searchTokens: string[] = [];\n\n for (const token of tokens) {\n const normalizedToken = this._caseSensitive ? token : token.toLowerCase();\n const keywordDef = this._keywordTriggerMap.get(normalizedToken);\n\n if (keywordDef) {\n const existing = keywordTokens.get(keywordDef.name) || [];\n existing.push(token);\n keywordTokens.set(keywordDef.name, existing);\n } else {\n searchTokens.push(token);\n }\n }\n\n return { keywordTokens, searchTokens };\n }\n\n private _applyKeywords(\n collection: T[],\n keywordTokens: Map<string, string[]>\n ): { collection: T[]; matchedKeywords: MatchedKeyword[] } {\n if (!this._keywords) {\n return { collection, matchedKeywords: [] };\n }\n\n let result = collection;\n const matchedKeywords: MatchedKeyword[] = [];\n\n const keywordNameToTerms = new Map<string, string[]>();\n for (const [name, terms] of keywordTokens) {\n keywordNameToTerms.set(name, terms);\n }\n\n if (this._keywords.mode === 'and') {\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (\n collection: T[],\n matchedTerms: string[]\n ) => T[];\n const handlerResult = handler(result, terms);\n\n if (!Array.isArray(handlerResult)) {\n console.error(\n `[Spotr] Keyword handler \"${def.name}\" must return an array, received ${typeof handlerResult}. Skipping this filter.`\n );\n // Skip this keyword - keep current collection for this step\n continue;\n }\n\n result = handlerResult;\n matchedKeywords.push({ name: def.name, terms });\n }\n }\n } else {\n const mergedResults: T[] = [];\n const processedItems = new Set<T>();\n\n for (const def of this._keywords.definitions) {\n const terms = keywordNameToTerms.get(def.name);\n if (terms) {\n const handler = def.handler as (\n collection: T[],\n matchedTerms: string[]\n ) => T[];\n const keywordResults = handler(this._collection, terms);\n\n if (!Array.isArray(keywordResults)) {\n console.error(\n `[Spotr] Keyword handler \"${def.name}\" must return an array, received ${typeof keywordResults}. Skipping this filter.`\n );\n // Skip this keyword - don't add its results\n continue;\n }\n\n matchedKeywords.push({ name: def.name, terms });\n\n for (const item of keywordResults) {\n if (!processedItems.has(item)) {\n processedItems.add(item);\n mergedResults.push(item);\n }\n }\n }\n }\n result = mergedResults;\n }\n\n return { collection: result, matchedKeywords };\n }\n}\n"],"names":["Spotr","options","__publicField","validated","validateOptions","normalizeFieldConfig","validateKeywords","map","def","triggers","trigger","collection","validateCollection","search","tokens","tokenize","keywordTokens","searchTokens","filteredCollection","matchedKeywords","result","allWarnings","item","score","warnings","scoreItem","validTokens","t","scoredResults","a","b","resolve","token","normalizedToken","keywordDef","existing","keywordNameToTerms","name","terms","handler","handlerResult","mergedResults","processedItems","keywordResults"],"mappings":"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"}
|
package/dist/errors.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
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
|
+
//# sourceMappingURL=errors.cjs.map
|
|
@@ -0,0 +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 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
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class _ extends Error {
|
|
2
|
+
constructor(I, L) {
|
|
3
|
+
super(I), this.code = L, this.name = "SpotrError";
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
const D = {
|
|
7
|
+
INVALID_COLLECTION: "INVALID_COLLECTION",
|
|
8
|
+
INVALID_FIELD_CONFIG: "INVALID_FIELD_CONFIG",
|
|
9
|
+
INVALID_FIELD_WEIGHT: "INVALID_FIELD_WEIGHT",
|
|
10
|
+
INVALID_KEYWORD: "INVALID_KEYWORD",
|
|
11
|
+
INVALID_HANDLER_RETURN: "INVALID_HANDLER_RETURN",
|
|
12
|
+
INVALID_MAX_STRING_LENGTH: "INVALID_MAX_STRING_LENGTH"
|
|
13
|
+
};
|
|
14
|
+
export {
|
|
15
|
+
D as ErrorCodes,
|
|
16
|
+
_ as SpotrError
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +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 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;"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("../types.cjs");function u(n,o){const r=[];for(let e=0;e<=o.length;e++)r[e]=[e];for(let e=0;e<=n.length;e++)r[0][e]=e;for(let e=1;e<=o.length;e++)for(let t=1;t<=n.length;t++)o.charAt(e-1)===n.charAt(t-1)?r[e][t]=r[e-1][t-1]:r[e][t]=Math.min(r[e-1][t-1]+1,r[e][t-1]+1,r[e-1][t]+1);return r[o.length][n.length]}function m(n,o,r=.3,e=!1,t=d.MAX_STRING_LENGTH){const c=[];let l=n,s=o;l.length>t&&(c.push(`Query string truncated from ${l.length} to ${t} characters for performance`),l=l.slice(0,t)),s.length>t&&(c.push(`Target string truncated from ${s.length} to ${t} characters for performance`),s=s.slice(0,t));const f=e?l:l.toLowerCase(),i=e?s:s.toLowerCase();if(f===i)return{score:1,warnings:c};if(f.length===0||i.length===0)return{score:0,warnings:c};if(i.includes(f))return{score:.9+.1*f.length/i.length,warnings:c};const a=u(f,i),g=Math.max(f.length,i.length),h=1-a/g;return{score:h>=r?h:0,warnings:c}}exports.fuzzyScore=m;exports.levenshteinDistance=u;
|
|
2
|
+
//# sourceMappingURL=levenshtein.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"levenshtein.cjs","sources":["../../src/fuzzy/levenshtein.ts"],"sourcesContent":["import { MAX_STRING_LENGTH } from '../types';\n\nexport function levenshteinDistance(a: string, b: string): number {\n const matrix: number[][] = [];\n\n for (let i = 0; i <= b.length; i++) {\n matrix[i] = [i];\n }\n for (let j = 0; j <= a.length; j++) {\n matrix[0][j] = j;\n }\n\n for (let i = 1; i <= b.length; i++) {\n for (let j = 1; j <= a.length; j++) {\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\n matrix[i][j] = matrix[i - 1][j - 1];\n } else {\n matrix[i][j] = Math.min(\n matrix[i - 1][j - 1] + 1,\n matrix[i][j - 1] + 1,\n matrix[i - 1][j] + 1\n );\n }\n }\n }\n\n return matrix[b.length][a.length];\n}\n\nexport interface FuzzyScoreResult {\n score: number;\n warnings: string[];\n}\n\nexport function fuzzyScore(\n query: string,\n target: string,\n threshold: number = 0.3,\n caseSensitive: boolean = false,\n maxStringLength: number = MAX_STRING_LENGTH\n): FuzzyScoreResult {\n const warnings: string[] = [];\n let q = query;\n let t = target;\n\n if (q.length > maxStringLength) {\n warnings.push(\n `Query string truncated from ${q.length} to ${maxStringLength} characters for performance`\n );\n q = q.slice(0, maxStringLength);\n }\n\n if (t.length > maxStringLength) {\n warnings.push(\n `Target string truncated from ${t.length} to ${maxStringLength} characters for performance`\n );\n t = t.slice(0, maxStringLength);\n }\n\n const normalizedQ = caseSensitive ? q : q.toLowerCase();\n const normalizedT = caseSensitive ? t : t.toLowerCase();\n\n if (normalizedQ === normalizedT) {\n return { score: 1, warnings };\n }\n if (normalizedQ.length === 0 || normalizedT.length === 0) {\n return { score: 0, warnings };\n }\n\n if (normalizedT.includes(normalizedQ)) {\n return {\n score: 0.9 + (0.1 * normalizedQ.length) / normalizedT.length,\n warnings,\n };\n }\n\n const distance = levenshteinDistance(normalizedQ, normalizedT);\n const maxLen = Math.max(normalizedQ.length, normalizedT.length);\n const score = 1 - distance / maxLen;\n\n return {\n score: score >= threshold ? score : 0,\n warnings,\n };\n}\n"],"names":["levenshteinDistance","a","b","matrix","i","j","fuzzyScore","query","target","threshold","caseSensitive","maxStringLength","MAX_STRING_LENGTH","warnings","q","t","normalizedQ","normalizedT","distance","maxLen","score"],"mappings":"gHAEO,SAASA,EAAoBC,EAAWC,EAAmB,CAChE,MAAMC,EAAqB,CAAA,EAE3B,QAASC,EAAI,EAAGA,GAAKF,EAAE,OAAQE,IAC7BD,EAAOC,CAAC,EAAI,CAACA,CAAC,EAEhB,QAASC,EAAI,EAAGA,GAAKJ,EAAE,OAAQI,IAC7BF,EAAO,CAAC,EAAEE,CAAC,EAAIA,EAGjB,QAASD,EAAI,EAAGA,GAAKF,EAAE,OAAQE,IAC7B,QAASC,EAAI,EAAGA,GAAKJ,EAAE,OAAQI,IACzBH,EAAE,OAAOE,EAAI,CAAC,IAAMH,EAAE,OAAOI,EAAI,CAAC,EACpCF,EAAOC,CAAC,EAAEC,CAAC,EAAIF,EAAOC,EAAI,CAAC,EAAEC,EAAI,CAAC,EAElCF,EAAOC,CAAC,EAAEC,CAAC,EAAI,KAAK,IAClBF,EAAOC,EAAI,CAAC,EAAEC,EAAI,CAAC,EAAI,EACvBF,EAAOC,CAAC,EAAEC,EAAI,CAAC,EAAI,EACnBF,EAAOC,EAAI,CAAC,EAAEC,CAAC,EAAI,CAAA,EAM3B,OAAOF,EAAOD,EAAE,MAAM,EAAED,EAAE,MAAM,CAClC,CAOO,SAASK,EACdC,EACAC,EACAC,EAAoB,GACpBC,EAAyB,GACzBC,EAA0BC,oBACR,CAClB,MAAMC,EAAqB,CAAA,EAC3B,IAAIC,EAAIP,EACJQ,EAAIP,EAEJM,EAAE,OAASH,IACbE,EAAS,KACP,+BAA+BC,EAAE,MAAM,OAAOH,CAAe,6BAAA,EAE/DG,EAAIA,EAAE,MAAM,EAAGH,CAAe,GAG5BI,EAAE,OAASJ,IACbE,EAAS,KACP,gCAAgCE,EAAE,MAAM,OAAOJ,CAAe,6BAAA,EAEhEI,EAAIA,EAAE,MAAM,EAAGJ,CAAe,GAGhC,MAAMK,EAAcN,EAAgBI,EAAIA,EAAE,YAAA,EACpCG,EAAcP,EAAgBK,EAAIA,EAAE,YAAA,EAE1C,GAAIC,IAAgBC,EAClB,MAAO,CAAE,MAAO,EAAG,SAAAJ,CAAA,EAErB,GAAIG,EAAY,SAAW,GAAKC,EAAY,SAAW,EACrD,MAAO,CAAE,MAAO,EAAG,SAAAJ,CAAA,EAGrB,GAAII,EAAY,SAASD,CAAW,EAClC,MAAO,CACL,MAAO,GAAO,GAAMA,EAAY,OAAUC,EAAY,OACtD,SAAAJ,CAAA,EAIJ,MAAMK,EAAWlB,EAAoBgB,EAAaC,CAAW,EACvDE,EAAS,KAAK,IAAIH,EAAY,OAAQC,EAAY,MAAM,EACxDG,EAAQ,EAAIF,EAAWC,EAE7B,MAAO,CACL,MAAOC,GAASX,EAAYW,EAAQ,EACpC,SAAAP,CAAA,CAEJ"}
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
|
14
|
+
);
|
|
15
|
+
return r[o.length][n.length];
|
|
16
|
+
}
|
|
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
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
p as fuzzyScore,
|
|
43
|
+
m as levenshteinDistance
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=levenshtein.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"levenshtein.js","sources":["../../src/fuzzy/levenshtein.ts"],"sourcesContent":["import { MAX_STRING_LENGTH } from '../types';\n\nexport function levenshteinDistance(a: string, b: string): number {\n const matrix: number[][] = [];\n\n for (let i = 0; i <= b.length; i++) {\n matrix[i] = [i];\n }\n for (let j = 0; j <= a.length; j++) {\n matrix[0][j] = j;\n }\n\n for (let i = 1; i <= b.length; i++) {\n for (let j = 1; j <= a.length; j++) {\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\n matrix[i][j] = matrix[i - 1][j - 1];\n } else {\n matrix[i][j] = Math.min(\n matrix[i - 1][j - 1] + 1,\n matrix[i][j - 1] + 1,\n matrix[i - 1][j] + 1\n );\n }\n }\n }\n\n return matrix[b.length][a.length];\n}\n\nexport interface FuzzyScoreResult {\n score: number;\n warnings: string[];\n}\n\nexport function fuzzyScore(\n query: string,\n target: string,\n threshold: number = 0.3,\n caseSensitive: boolean = false,\n maxStringLength: number = MAX_STRING_LENGTH\n): FuzzyScoreResult {\n const warnings: string[] = [];\n let q = query;\n let t = target;\n\n if (q.length > maxStringLength) {\n warnings.push(\n `Query string truncated from ${q.length} to ${maxStringLength} characters for performance`\n );\n q = q.slice(0, maxStringLength);\n }\n\n if (t.length > maxStringLength) {\n warnings.push(\n `Target string truncated from ${t.length} to ${maxStringLength} characters for performance`\n );\n t = t.slice(0, maxStringLength);\n }\n\n const normalizedQ = caseSensitive ? q : q.toLowerCase();\n const normalizedT = caseSensitive ? t : t.toLowerCase();\n\n if (normalizedQ === normalizedT) {\n return { score: 1, warnings };\n }\n if (normalizedQ.length === 0 || normalizedT.length === 0) {\n return { score: 0, warnings };\n }\n\n if (normalizedT.includes(normalizedQ)) {\n return {\n score: 0.9 + (0.1 * normalizedQ.length) / normalizedT.length,\n warnings,\n };\n }\n\n const distance = levenshteinDistance(normalizedQ, normalizedT);\n const maxLen = Math.max(normalizedQ.length, normalizedT.length);\n const score = 1 - distance / maxLen;\n\n return {\n score: score >= threshold ? score : 0,\n warnings,\n };\n}\n"],"names":["levenshteinDistance","a","b","matrix","i","j","fuzzyScore","query","target","threshold","caseSensitive","maxStringLength","MAX_STRING_LENGTH","warnings","q","t","normalizedQ","normalizedT","distance","maxLen","score"],"mappings":";AAEO,SAASA,EAAoBC,GAAWC,GAAmB;AAChE,QAAMC,IAAqB,CAAA;AAE3B,WAASC,IAAI,GAAGA,KAAKF,EAAE,QAAQE;AAC7B,IAAAD,EAAOC,CAAC,IAAI,CAACA,CAAC;AAEhB,WAASC,IAAI,GAAGA,KAAKJ,EAAE,QAAQI;AAC7B,IAAAF,EAAO,CAAC,EAAEE,CAAC,IAAIA;AAGjB,WAASD,IAAI,GAAGA,KAAKF,EAAE,QAAQE;AAC7B,aAASC,IAAI,GAAGA,KAAKJ,EAAE,QAAQI;AAC7B,MAAIH,EAAE,OAAOE,IAAI,CAAC,MAAMH,EAAE,OAAOI,IAAI,CAAC,IACpCF,EAAOC,CAAC,EAAEC,CAAC,IAAIF,EAAOC,IAAI,CAAC,EAAEC,IAAI,CAAC,IAElCF,EAAOC,CAAC,EAAEC,CAAC,IAAI,KAAK;AAAA,QAClBF,EAAOC,IAAI,CAAC,EAAEC,IAAI,CAAC,IAAI;AAAA,QACvBF,EAAOC,CAAC,EAAEC,IAAI,CAAC,IAAI;AAAA,QACnBF,EAAOC,IAAI,CAAC,EAAEC,CAAC,IAAI;AAAA,MAAA;AAM3B,SAAOF,EAAOD,EAAE,MAAM,EAAED,EAAE,MAAM;AAClC;AAOO,SAASK,EACdC,GACAC,GACAC,IAAoB,KACpBC,IAAyB,IACzBC,IAA0BC,GACR;AAClB,QAAMC,IAAqB,CAAA;AAC3B,MAAIC,IAAIP,GACJQ,IAAIP;AAER,EAAIM,EAAE,SAASH,MACbE,EAAS;AAAA,IACP,+BAA+BC,EAAE,MAAM,OAAOH,CAAe;AAAA,EAAA,GAE/DG,IAAIA,EAAE,MAAM,GAAGH,CAAe,IAG5BI,EAAE,SAASJ,MACbE,EAAS;AAAA,IACP,gCAAgCE,EAAE,MAAM,OAAOJ,CAAe;AAAA,EAAA,GAEhEI,IAAIA,EAAE,MAAM,GAAGJ,CAAe;AAGhC,QAAMK,IAAcN,IAAgBI,IAAIA,EAAE,YAAA,GACpCG,IAAcP,IAAgBK,IAAIA,EAAE,YAAA;AAE1C,MAAIC,MAAgBC;AAClB,WAAO,EAAE,OAAO,GAAG,UAAAJ,EAAA;AAErB,MAAIG,EAAY,WAAW,KAAKC,EAAY,WAAW;AACrD,WAAO,EAAE,OAAO,GAAG,UAAAJ,EAAA;AAGrB,MAAII,EAAY,SAASD,CAAW;AAClC,WAAO;AAAA,MACL,OAAO,MAAO,MAAMA,EAAY,SAAUC,EAAY;AAAA,MACtD,UAAAJ;AAAA,IAAA;AAIJ,QAAMK,IAAWlB,EAAoBgB,GAAaC,CAAW,GACvDE,IAAS,KAAK,IAAIH,EAAY,QAAQC,EAAY,MAAM,GACxDG,IAAQ,IAAIF,IAAWC;AAE7B,SAAO;AAAA,IACL,OAAOC,KAASX,IAAYW,IAAQ;AAAA,IACpC,UAAAP;AAAA,EAAA;AAEJ;"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("./levenshtein.cjs"),d=require("../utils/nested.cjs"),w=require("../types.cjs");function S(o,n){return o.map(e=>typeof e=="string"?{name:e,weight:1,threshold:n}:{name:e.name,weight:e.weight??1,threshold:e.threshold??n})}function p(o,n,e,l,h=w.MAX_STRING_LENGTH){let u=0,r=0;const s=[];for(const t of e){const c=d.getNestedValue(o,t.name);if(c==null){t.name.includes(".")&&s.push(`Field "${t.name}" not found on item`);continue}const g=String(c);let i=0;for(const m of n){const a=f.fuzzyScore(m,g,t.threshold,l,h);s.push(...a.warnings),i=Math.max(i,a.score)}u+=i*t.weight,r+=t.weight}return{score:r>0?u/r:0,warnings:s}}exports.normalizeFieldConfig=S;exports.scoreItem=p;
|
|
2
|
+
//# sourceMappingURL=scorer.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scorer.cjs","sources":["../../src/fuzzy/scorer.ts"],"sourcesContent":["import { fuzzyScore } from './levenshtein';\nimport { getNestedValue } from '../utils/nested';\nimport { MAX_STRING_LENGTH } from '../types';\nimport type { FieldConfig, NormalizedFieldConfig } from '../types';\n\nexport function normalizeFieldConfig(\n fields: FieldConfig[],\n globalThreshold: number\n): NormalizedFieldConfig[] {\n return fields.map((field) => {\n if (typeof field === 'string') {\n return { name: field, weight: 1, threshold: globalThreshold };\n }\n return {\n name: field.name,\n weight: field.weight ?? 1,\n threshold: field.threshold ?? globalThreshold,\n };\n });\n}\n\nexport function scoreItem<T>(\n item: T,\n tokens: string[],\n fields: NormalizedFieldConfig[],\n caseSensitive: boolean,\n maxStringLength: number = MAX_STRING_LENGTH\n): { score: number; warnings: string[] } {\n let totalScore = 0;\n let totalWeight = 0;\n const warnings: string[] = [];\n\n for (const field of fields) {\n const value = getNestedValue(item, field.name);\n\n if (value == null) {\n if (field.name.includes('.')) {\n warnings.push(`Field \"${field.name}\" not found on item`);\n }\n continue;\n }\n\n const stringValue = String(value);\n\n let bestTokenScore = 0;\n for (const token of tokens) {\n const result = fuzzyScore(\n token,\n stringValue,\n field.threshold,\n caseSensitive,\n maxStringLength\n );\n warnings.push(...result.warnings);\n bestTokenScore = Math.max(bestTokenScore, result.score);\n }\n\n totalScore += bestTokenScore * field.weight;\n totalWeight += field.weight;\n }\n\n return {\n score: totalWeight > 0 ? totalScore / totalWeight : 0,\n warnings,\n };\n}\n"],"names":["normalizeFieldConfig","fields","globalThreshold","field","scoreItem","item","tokens","caseSensitive","maxStringLength","MAX_STRING_LENGTH","totalScore","totalWeight","warnings","value","getNestedValue","stringValue","bestTokenScore","token","result","fuzzyScore"],"mappings":"gLAKO,SAASA,EACdC,EACAC,EACyB,CACzB,OAAOD,EAAO,IAAKE,GACb,OAAOA,GAAU,SACZ,CAAE,KAAMA,EAAO,OAAQ,EAAG,UAAWD,CAAA,EAEvC,CACL,KAAMC,EAAM,KACZ,OAAQA,EAAM,QAAU,EACxB,UAAWA,EAAM,WAAaD,CAAA,CAEjC,CACH,CAEO,SAASE,EACdC,EACAC,EACAL,EACAM,EACAC,EAA0BC,oBACa,CACvC,IAAIC,EAAa,EACbC,EAAc,EAClB,MAAMC,EAAqB,CAAA,EAE3B,UAAWT,KAASF,EAAQ,CAC1B,MAAMY,EAAQC,EAAAA,eAAeT,EAAMF,EAAM,IAAI,EAE7C,GAAIU,GAAS,KAAM,CACbV,EAAM,KAAK,SAAS,GAAG,GACzBS,EAAS,KAAK,UAAUT,EAAM,IAAI,qBAAqB,EAEzD,QACF,CAEA,MAAMY,EAAc,OAAOF,CAAK,EAEhC,IAAIG,EAAiB,EACrB,UAAWC,KAASX,EAAQ,CAC1B,MAAMY,EAASC,EAAAA,WACbF,EACAF,EACAZ,EAAM,UACNI,EACAC,CAAA,EAEFI,EAAS,KAAK,GAAGM,EAAO,QAAQ,EAChCF,EAAiB,KAAK,IAAIA,EAAgBE,EAAO,KAAK,CACxD,CAEAR,GAAcM,EAAiBb,EAAM,OACrCQ,GAAeR,EAAM,MACvB,CAEA,MAAO,CACL,MAAOQ,EAAc,EAAID,EAAaC,EAAc,EACpD,SAAAC,CAAA,CAEJ"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { fuzzyScore as g } from "./levenshtein.js";
|
|
2
|
+
import { getNestedValue as p } from "../utils/nested.js";
|
|
3
|
+
import { MAX_STRING_LENGTH as w } from "../types.js";
|
|
4
|
+
function N(o, n) {
|
|
5
|
+
return o.map((t) => typeof t == "string" ? { name: t, weight: 1, threshold: n } : {
|
|
6
|
+
name: t.name,
|
|
7
|
+
weight: t.weight ?? 1,
|
|
8
|
+
threshold: t.threshold ?? n
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
function k(o, n, t, c, h = w) {
|
|
12
|
+
let m = 0, r = 0;
|
|
13
|
+
const i = [];
|
|
14
|
+
for (const e of t) {
|
|
15
|
+
const u = p(o, e.name);
|
|
16
|
+
if (u == null) {
|
|
17
|
+
e.name.includes(".") && i.push(`Field "${e.name}" not found on item`);
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const f = String(u);
|
|
21
|
+
let s = 0;
|
|
22
|
+
for (const l of n) {
|
|
23
|
+
const a = g(
|
|
24
|
+
l,
|
|
25
|
+
f,
|
|
26
|
+
e.threshold,
|
|
27
|
+
c,
|
|
28
|
+
h
|
|
29
|
+
);
|
|
30
|
+
i.push(...a.warnings), s = Math.max(s, a.score);
|
|
31
|
+
}
|
|
32
|
+
m += s * e.weight, r += e.weight;
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
score: r > 0 ? m / r : 0,
|
|
36
|
+
warnings: i
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
N as normalizeFieldConfig,
|
|
41
|
+
k as scoreItem
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=scorer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scorer.js","sources":["../../src/fuzzy/scorer.ts"],"sourcesContent":["import { fuzzyScore } from './levenshtein';\nimport { getNestedValue } from '../utils/nested';\nimport { MAX_STRING_LENGTH } from '../types';\nimport type { FieldConfig, NormalizedFieldConfig } from '../types';\n\nexport function normalizeFieldConfig(\n fields: FieldConfig[],\n globalThreshold: number\n): NormalizedFieldConfig[] {\n return fields.map((field) => {\n if (typeof field === 'string') {\n return { name: field, weight: 1, threshold: globalThreshold };\n }\n return {\n name: field.name,\n weight: field.weight ?? 1,\n threshold: field.threshold ?? globalThreshold,\n };\n });\n}\n\nexport function scoreItem<T>(\n item: T,\n tokens: string[],\n fields: NormalizedFieldConfig[],\n caseSensitive: boolean,\n maxStringLength: number = MAX_STRING_LENGTH\n): { score: number; warnings: string[] } {\n let totalScore = 0;\n let totalWeight = 0;\n const warnings: string[] = [];\n\n for (const field of fields) {\n const value = getNestedValue(item, field.name);\n\n if (value == null) {\n if (field.name.includes('.')) {\n warnings.push(`Field \"${field.name}\" not found on item`);\n }\n continue;\n }\n\n const stringValue = String(value);\n\n let bestTokenScore = 0;\n for (const token of tokens) {\n const result = fuzzyScore(\n token,\n stringValue,\n field.threshold,\n caseSensitive,\n maxStringLength\n );\n warnings.push(...result.warnings);\n bestTokenScore = Math.max(bestTokenScore, result.score);\n }\n\n totalScore += bestTokenScore * field.weight;\n totalWeight += field.weight;\n }\n\n return {\n score: totalWeight > 0 ? totalScore / totalWeight : 0,\n warnings,\n };\n}\n"],"names":["normalizeFieldConfig","fields","globalThreshold","field","scoreItem","item","tokens","caseSensitive","maxStringLength","MAX_STRING_LENGTH","totalScore","totalWeight","warnings","value","getNestedValue","stringValue","bestTokenScore","token","result","fuzzyScore"],"mappings":";;;AAKO,SAASA,EACdC,GACAC,GACyB;AACzB,SAAOD,EAAO,IAAI,CAACE,MACb,OAAOA,KAAU,WACZ,EAAE,MAAMA,GAAO,QAAQ,GAAG,WAAWD,EAAA,IAEvC;AAAA,IACL,MAAMC,EAAM;AAAA,IACZ,QAAQA,EAAM,UAAU;AAAA,IACxB,WAAWA,EAAM,aAAaD;AAAA,EAAA,CAEjC;AACH;AAEO,SAASE,EACdC,GACAC,GACAL,GACAM,GACAC,IAA0BC,GACa;AACvC,MAAIC,IAAa,GACbC,IAAc;AAClB,QAAMC,IAAqB,CAAA;AAE3B,aAAWT,KAASF,GAAQ;AAC1B,UAAMY,IAAQC,EAAeT,GAAMF,EAAM,IAAI;AAE7C,QAAIU,KAAS,MAAM;AACjB,MAAIV,EAAM,KAAK,SAAS,GAAG,KACzBS,EAAS,KAAK,UAAUT,EAAM,IAAI,qBAAqB;AAEzD;AAAA,IACF;AAEA,UAAMY,IAAc,OAAOF,CAAK;AAEhC,QAAIG,IAAiB;AACrB,eAAWC,KAASX,GAAQ;AAC1B,YAAMY,IAASC;AAAA,QACbF;AAAA,QACAF;AAAA,QACAZ,EAAM;AAAA,QACNI;AAAA,QACAC;AAAA,MAAA;AAEF,MAAAI,EAAS,KAAK,GAAGM,EAAO,QAAQ,GAChCF,IAAiB,KAAK,IAAIA,GAAgBE,EAAO,KAAK;AAAA,IACxD;AAEA,IAAAR,KAAcM,IAAiBb,EAAM,QACrCQ,KAAeR,EAAM;AAAA,EACvB;AAEA,SAAO;AAAA,IACL,OAAOQ,IAAc,IAAID,IAAaC,IAAc;AAAA,IACpD,UAAAC;AAAA,EAAA;AAEJ;"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./Spotr.cjs"),r=require("./errors.cjs"),e=require("./types.cjs");exports.Spotr=o.Spotr;exports.ErrorCodes=r.ErrorCodes;exports.SpotrError=r.SpotrError;exports.MAX_STRING_LENGTH=e.MAX_STRING_LENGTH;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
export declare const ErrorCodes: {
|
|
2
|
+
readonly INVALID_COLLECTION: "INVALID_COLLECTION";
|
|
3
|
+
readonly INVALID_FIELD_CONFIG: "INVALID_FIELD_CONFIG";
|
|
4
|
+
readonly INVALID_FIELD_WEIGHT: "INVALID_FIELD_WEIGHT";
|
|
5
|
+
readonly INVALID_KEYWORD: "INVALID_KEYWORD";
|
|
6
|
+
readonly INVALID_HANDLER_RETURN: "INVALID_HANDLER_RETURN";
|
|
7
|
+
readonly INVALID_MAX_STRING_LENGTH: "INVALID_MAX_STRING_LENGTH";
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export declare type FieldConfig = string | {
|
|
11
|
+
name: string;
|
|
12
|
+
weight?: number;
|
|
13
|
+
threshold?: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export declare type KeywordDefinition<T> = {
|
|
17
|
+
name: string;
|
|
18
|
+
triggers: string | string[];
|
|
19
|
+
/**
|
|
20
|
+
* Custom filter function for keyword-based collection filtering.
|
|
21
|
+
*
|
|
22
|
+
* @param collection - The current collection to filter
|
|
23
|
+
* @param matchedTerms - The trigger terms that matched this keyword
|
|
24
|
+
* @returns Filtered collection array
|
|
25
|
+
*
|
|
26
|
+
* @security Handler functions execute with full access to the collection
|
|
27
|
+
* and any data it contains. Only use trusted handler implementations.
|
|
28
|
+
* Avoid executing untrusted code or exposing sensitive data through handlers.
|
|
29
|
+
*/
|
|
30
|
+
handler: (collection: T[], matchedTerms: string[]) => T[];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export declare type KeywordMode = 'and' | 'or';
|
|
34
|
+
|
|
35
|
+
export declare type KeywordsConfig<T> = KeywordDefinition<T>[] | {
|
|
36
|
+
mode?: KeywordMode;
|
|
37
|
+
definitions: KeywordDefinition<T>[];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export declare interface MatchedKeyword {
|
|
41
|
+
name: string;
|
|
42
|
+
terms: string[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export declare const MAX_STRING_LENGTH = 1000;
|
|
46
|
+
|
|
47
|
+
export declare interface NormalizedFieldConfig {
|
|
48
|
+
name: string;
|
|
49
|
+
weight: number;
|
|
50
|
+
threshold: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export declare interface NormalizedKeywordsConfig {
|
|
54
|
+
mode: KeywordMode;
|
|
55
|
+
definitions: KeywordDefinition<unknown>[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export declare interface ScoredResult<T> {
|
|
59
|
+
item: T;
|
|
60
|
+
score: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export declare class Spotr<T extends object> {
|
|
64
|
+
private _collection;
|
|
65
|
+
private _fields;
|
|
66
|
+
private _keywords;
|
|
67
|
+
private _keywordTriggerMap;
|
|
68
|
+
private _threshold;
|
|
69
|
+
private _limit;
|
|
70
|
+
private _debounce;
|
|
71
|
+
private _caseSensitive;
|
|
72
|
+
private _minMatchCharLength;
|
|
73
|
+
private _maxStringLength;
|
|
74
|
+
private _debounceTimer;
|
|
75
|
+
private _optionsSnapshot;
|
|
76
|
+
constructor(options: SpotrOptions<T>);
|
|
77
|
+
private _buildKeywordTriggerMap;
|
|
78
|
+
get collection(): T[];
|
|
79
|
+
get options(): SpotrOptions<T>;
|
|
80
|
+
setCollection(collection: T[] | Set<T>): void;
|
|
81
|
+
query(search: string): SpotrResult<T>;
|
|
82
|
+
queryAsync(search: string): Promise<SpotrResult<T>>;
|
|
83
|
+
private _extractKeywords;
|
|
84
|
+
private _applyKeywords;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export declare class SpotrError extends Error {
|
|
88
|
+
code: string;
|
|
89
|
+
constructor(message: string, code: string);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export declare interface SpotrOptions<T extends object> {
|
|
93
|
+
collection: readonly T[] | T[] | Set<T>;
|
|
94
|
+
fields: FieldConfig[];
|
|
95
|
+
keywords?: KeywordsConfig<T>;
|
|
96
|
+
threshold?: number;
|
|
97
|
+
limit?: number;
|
|
98
|
+
debounce?: number;
|
|
99
|
+
caseSensitive?: boolean;
|
|
100
|
+
minMatchCharLength?: number;
|
|
101
|
+
maxStringLength?: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export declare interface SpotrResult<T> {
|
|
105
|
+
results: ScoredResult<T>[];
|
|
106
|
+
matchedKeywords: MatchedKeyword[];
|
|
107
|
+
tokens: string[];
|
|
108
|
+
warnings: string[];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export { }
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Spotr as p } from "./Spotr.js";
|
|
2
|
+
import { ErrorCodes as e, SpotrError as f } from "./errors.js";
|
|
3
|
+
import { MAX_STRING_LENGTH as x } from "./types.js";
|
|
4
|
+
export {
|
|
5
|
+
e as ErrorCodes,
|
|
6
|
+
x as MAX_STRING_LENGTH,
|
|
7
|
+
p as Spotr,
|
|
8
|
+
f as SpotrError
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
|
package/dist/preact.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("preact/hooks"),o=require("./Spotr.cjs");function s(r,e){if(r===e)return!0;if(typeof r!="object"||typeof e!="object"||r===null||e===null)return!1;const t=Object.keys(r),c=Object.keys(e);if(t.length!==c.length)return!1;for(const n of t)if(r[n]!==e[n])return!1;return!0}function f(r){const e=u.useRef(r);s(e.current,r)||(e.current=r);const t=u.useRef(null);return(!t.current||t.current.options!==e.current)&&(t.current=new o.Spotr(e.current)),t.current}exports.Spotr=o.Spotr;exports.useSpotr=f;
|
|
2
|
+
//# sourceMappingURL=preact.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preact.cjs","sources":["../src/preact/index.ts"],"sourcesContent":["import { useRef } from 'preact/hooks';\nimport { Spotr } from '../Spotr';\nimport type { SpotrOptions, ExtractItemType } from '../types';\n\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (\n typeof a !== 'object' ||\n typeof b !== 'object' ||\n a === null ||\n b === null\n ) {\n return false;\n }\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (\n (a as Record<string, unknown>)[key] !==\n (b as Record<string, unknown>)[key]\n ) {\n return false;\n }\n }\n\n return true;\n}\n\n// Overload: infer T from collection\nexport function useSpotr<C extends readonly object[] | object[] | Set<object>>(\n options: Omit<SpotrOptions<ExtractItemType<C>>, 'collection'> & {\n collection: C;\n }\n): Spotr<ExtractItemType<C> & object>;\n// Overload: explicit generic\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T>;\nexport function useSpotr<T extends object>(options: SpotrOptions<T>): Spotr<T> {\n const optionsRef = useRef(options);\n\n if (!shallowEqual(optionsRef.current, options)) {\n optionsRef.current = options;\n }\n\n const spotrRef = useRef<Spotr<T> | null>(null);\n\n if (!spotrRef.current || spotrRef.current.options !== optionsRef.current) {\n spotrRef.current = new Spotr(optionsRef.current);\n }\n\n return spotrRef.current;\n}\n\nexport { Spotr } from '../Spotr';\nexport type {\n SpotrOptions,\n SpotrResult,\n ScoredResult,\n MatchedKeyword,\n} from '../types';\n"],"names":["shallowEqual","a","b","keysA","keysB","key","useSpotr","options","optionsRef","useRef","spotrRef","Spotr"],"mappings":"yIAIA,SAASA,EAAaC,EAAYC,EAAqB,CACrD,GAAID,IAAMC,EAAG,MAAO,GACpB,GACE,OAAOD,GAAM,UACb,OAAOC,GAAM,UACbD,IAAM,MACNC,IAAM,KAEN,MAAO,GAGT,MAAMC,EAAQ,OAAO,KAAKF,CAAC,EACrBG,EAAQ,OAAO,KAAKF,CAAC,EAE3B,GAAIC,EAAM,SAAWC,EAAM,OAAQ,MAAO,GAE1C,UAAWC,KAAOF,EAChB,GACGF,EAA8BI,CAAG,IACjCH,EAA8BG,CAAG,EAElC,MAAO,GAIX,MAAO,EACT,CAUO,SAASC,EAA2BC,EAAoC,CAC7E,MAAMC,EAAaC,EAAAA,OAAOF,CAAO,EAE5BP,EAAaQ,EAAW,QAASD,CAAO,IAC3CC,EAAW,QAAUD,GAGvB,MAAMG,EAAWD,EAAAA,OAAwB,IAAI,EAE7C,OAAI,CAACC,EAAS,SAAWA,EAAS,QAAQ,UAAYF,EAAW,WAC/DE,EAAS,QAAU,IAAIC,QAAMH,EAAW,OAAO,GAG1CE,EAAS,OAClB"}
|