UNPKG

@aiquants/fuzzy-search

Version:

Advanced fuzzy search library with Levenshtein distance, n-gram indexing, and Web Worker support

2 lines 25 kB
var W={threshold:.4,caseSensitive:!1,learningWeight:1.2,debounceMs:300,multiTermOperator:"and",autoSearchOnIndexRebuild:!0,customWeights:{},ngramSize:2,minNgramOverlap:1,sortBy:"relevance",sortOrder:"desc",enableIndexFiltering:!0,enableLevenshtein:!0,parallelSearchStrategy:"balanced",indexWorkerOptions:{strategy:"hybrid",threshold:.4,ngramOverlapThreshold:.3,minCandidatesRatio:.1,maxCandidatesRatio:.5,jaroWinklerPrefix:.1,maxResults:1e3,relevanceFieldWeight:.2,relevancePerfectMatchBonus:.1},levenshteinWorkerOptions:{threshold:.3,lengthSimilarityThreshold:.1,partialMatchBonus:.1,lengthDiffPenalty:1,maxResults:1e3,relevanceFieldWeight:.15}};function K(a){return typeof a=="object"&&a!==null&&"item"in a&&"score"in a&&"matchedFields"in a}var s="[fuzzy-search]",T=class T{constructor(){this.indexWorker=null;this.levenshteinWorker=null;this.initializationPromise=null;this.referenceCount=0}static getInstance(){return T.instance||(T.instance=new T),T.instance}async getWorkers(e){return this.referenceCount++,this.indexWorker&&this.levenshteinWorker?(console.log(`${s} \u{1F527} Using existing global Workers (ref count: ${this.referenceCount})`),{indexWorker:this.indexWorker,levenshteinWorker:this.levenshteinWorker}):(this.initializationPromise||(this.initializationPromise=this.initializeWorkers(e)),await this.initializationPromise,{indexWorker:this.indexWorker,levenshteinWorker:this.levenshteinWorker})}async initializeWorkers(e){if(!(typeof Worker>"u"))try{if(!this.indexWorker){let r=e.workerUrls?.indexWorker?new URL(e.workerUrls.indexWorker,window.location.origin):new URL("./worker/indexWorker.mjs",import.meta.url);console.log(s,"\u{1F527} Global Index Worker URL:",r.toString()),this.indexWorker=new Worker(r,{type:"module"}),console.log(s,"\u{1F527} Global Index Worker: Initialized successfully")}if(!this.levenshteinWorker){let r=e.workerUrls?.levenshteinWorker?new URL(e.workerUrls.levenshteinWorker,window.location.origin):new URL("./worker/levenshteinWorker.mjs",import.meta.url);console.log(s,"\u{1F527} Global Levenshtein Worker URL:",r.toString()),this.levenshteinWorker=new Worker(r,{type:"module"}),console.log(s,"\u{1F527} Global Levenshtein Worker: Initialized successfully")}}catch(r){console.warn(s,"\u26A0\uFE0F Failed to initialize global Workers:",r)}}releaseReference(){this.referenceCount--,console.log(`${s} \u{1F527} Worker reference released (ref count: ${this.referenceCount})`)}dispose(){this.indexWorker&&(this.indexWorker.terminate(),this.indexWorker=null),this.levenshteinWorker&&(this.levenshteinWorker.terminate(),this.levenshteinWorker=null),this.initializationPromise=null,this.referenceCount=0,console.log(s,"\u{1F527} Global Workers disposed")}};T.instance=null;var F=T,D=class{constructor(){this.listeners=new Map}on(e,r){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e)?.add(r)}off(e,r){let t=this.listeners.get(e);t&&t.delete(r)}emit(e,r){let t=this.listeners.get(e);t&&t.forEach(n=>{try{n(r)}catch(i){console.error(`${s} \u274C Error in event listener for ${e}:`,i)}})}removeAllListeners(){this.listeners.clear()}},E=class{constructor(e=W){this.defaultOptions=e;this.indexWorker=null;this.levenshteinWorker=null;this.searchHistory=[];this.learningData={fieldWeights:{},queryPatterns:{},userPreferences:{preferredThreshold:.4,frequentFields:[],searchStyle:"fuzzy",lastUsedOptions:{}},performanceMetrics:{averageSearchTime:{},indexBuildTime:0}};this.requestId=0;this.pendingRequests=new Map;this.eventEmitter=new D;this.isIndexBuilt=!1;this.lastIndexedDataHash=null;this.stats={totalSearches:0,averageSearchTime:0,popularQueries:[],performanceBySize:{},workers:{index:{stats:{}},levenshtein:{stats:{}}}};this.loadLearningData(),this.workersInitialized=this.initializeWorkers(),setInterval(()=>this.cleanupPendingRequests(),3e4)}async initializeWorkers(){if(!(typeof Worker>"u"))try{let r=await F.getInstance().getWorkers(this.defaultOptions);this.indexWorker=r.indexWorker,this.levenshteinWorker=r.levenshteinWorker,this.indexWorker&&(this.indexWorker.onmessage=this.handleIndexWorkerMessage.bind(this),this.indexWorker.onerror=this.handleIndexWorkerError.bind(this),this.indexWorker.addEventListener("error",t=>{console.error(s,"\u{1F527} Index Worker Error (Event Listener):",t)})),this.levenshteinWorker&&(this.levenshteinWorker.onmessage=this.handleLevenshteinWorkerMessage.bind(this),this.levenshteinWorker.onerror=this.handleLevenshteinWorkerError.bind(this),this.levenshteinWorker.addEventListener("error",t=>{console.error(s,"\u{1F527} Levenshtein Worker Error (Event Listener):",t)})),this.eventEmitter.emit("workers:initialized",{indexWorker:!!this.indexWorker,levenshteinWorker:!!this.levenshteinWorker}),await this.testWorkerConnectivity()}catch(e){console.warn(s,"\u26A0\uFE0F Failed to initialize Workers:",e)}}async testWorkerConnectivity(){try{let e=[];this.indexWorker&&e.push(this.pingWorker(this.indexWorker,"index")),this.levenshteinWorker&&e.push(this.pingWorker(this.levenshteinWorker,"levenshtein")),await Promise.all(e)}catch(e){console.error(s,"\u{1F527} Worker connectivity test failed:",e)}}async pingWorker(e,r){let n=`ping-${r}-${Date.now()}`;await new Promise((i,u)=>{let c=setTimeout(()=>{console.warn(`${s} \u{1F527} ${r==="index"?"Index":"Levenshtein"} Worker: Ping timeout`),e.removeEventListener("message",l),u(new Error(`${r==="index"?"Index":"Levenshtein"} Worker ping timeout`))},5e3),l=p=>{let g=p.data;g.type==="pong"&&g.id===n&&(clearTimeout(c),e.removeEventListener("message",l),console.log(`${s} \u{1F527} ${r==="index"?"Index":"Levenshtein"} Worker: Ping successful`),i())};e.addEventListener("message",l),console.log(`${s} \u{1F527} ${r==="index"?"Index":"Levenshtein"} Worker: Sending ping message:`,n),e.postMessage({type:"ping",id:n})})}handleIndexWorkerMessage(e){let r=e.data;if(console.log(s,"\u{1F527} Index Worker message received:",r.type,r),r.type==="indexSearchResults"){let t=this.pendingRequests.get(r.id);t&&(this.pendingRequests.delete(r.id),r.error?t.reject(new Error(r.error)):t.resolve(r.results||[])),this.eventEmitter.emit("indexWorker:searchCompleted",{requestId:r.id,resultsCount:r.results?.length||0,processingTime:r.processingTime})}else if(r.type==="error"){let t=this.pendingRequests.get(r.id);t&&(this.pendingRequests.delete(r.id),t.reject(new Error(r.error||"Index Worker error"))),console.error(s,"\u274C Index Worker reported error:",{requestId:r.id,message:r.error,processingTime:r.processingTime}),this.eventEmitter.emit("indexWorker:error",{requestId:r.id,error:r.error,processingTime:r.processingTime})}else r.type==="indexReady"?(console.log(s,"\u2705 Index Worker: Index built successfully",r),r.stats&&(this.stats.workers.index.stats=r.stats,this.eventEmitter.emit("worker:statsUpdated",{worker:"index",stats:this.stats.workers.index.stats})),this.eventEmitter.emit("indexWorker:message",r)):r.type==="pong"||r.type==="stats"&&r.stats&&(this.stats.workers.index.stats=r.stats,this.eventEmitter.emit("worker:statsUpdated",{worker:"index",stats:this.stats.workers.index.stats}));this.eventEmitter.emit("indexWorker:message",r)}handleIndexWorkerError(e){console.error(s,"\u{1F527} Index Worker Error:",{message:e.message,filename:e.filename,lineno:e.lineno,colno:e.colno,error:e.error,type:e.type}),this.eventEmitter.emit("indexWorker:error",e)}handleLevenshteinWorkerMessage(e){let r=e.data;if(console.log(s,"\u{1F527} Levenshtein Worker message received:",r.type,"stats present:",!!r.stats),r.type==="levenshteinSearch"){let t=this.pendingRequests.get(r.id);t&&(this.pendingRequests.delete(r.id),r.error?t.reject(new Error(r.error)):t.resolve(r.results||[])),r.stats&&(this.stats.workers.levenshtein.stats=r.stats,this.eventEmitter.emit("worker:statsUpdated",{worker:"levenshtein",stats:this.stats.workers.levenshtein.stats})),this.eventEmitter.emit("levenshteinWorker:searchCompleted",{requestId:r.id,processingTime:r.processingTime,workerType:r.workerType,operationMeta:r.operationMeta})}else r.type==="pong"||r.type==="stats"&&r.stats&&(this.stats.workers.levenshtein.stats=r.stats,this.eventEmitter.emit("worker:statsUpdated",{worker:"levenshtein",stats:this.stats.workers.levenshtein.stats}));this.eventEmitter.emit("levenshteinWorker:message",r)}handleLevenshteinWorkerError(e){console.error(s,"\u{1F527} Levenshtein Worker Error:",{message:e.message,filename:e.filename,lineno:e.lineno,colno:e.colno,error:e.error,type:e.type}),this.eventEmitter.emit("levenshteinWorker:error",e)}isLocalStorageAvailable(){try{return typeof window<"u"&&window.localStorage!==void 0}catch{return!1}}loadLearningData(){try{if(this.isLocalStorageAvailable()){let e=localStorage.getItem("fuzzy-search-learning-data");e?this.learningData=JSON.parse(e):this.initializeDefaultLearningData()}else this.initializeDefaultLearningData()}catch(e){console.warn(s,"\u26A0\uFE0F Failed to load learning data:",e),this.initializeDefaultLearningData()}}initializeDefaultLearningData(){this.learningData={fieldWeights:{},queryPatterns:{},userPreferences:{preferredThreshold:this.defaultOptions.threshold,frequentFields:[],searchStyle:"fuzzy",lastUsedOptions:{}},performanceMetrics:{averageSearchTime:{},indexBuildTime:0}}}saveLearningData(){try{this.isLocalStorageAvailable()&&localStorage.setItem("fuzzy-search-learning-data",JSON.stringify(this.learningData))}catch(e){console.warn(s,"\u26A0\uFE0F Failed to save learning data:",e)}}async search(e,r,t,n){let i=performance.now(),u={...this.defaultOptions,...n};console.log(`${s} \u{1F680} FuzzySearchManager.search called: query="${e}", items.length=${r.length}`),this.eventEmitter.emit("search:start",{query:e,options:u});try{await this.workersInitialized;let c=this.generateDataHash(r,t);if(!this.isIndexBuilt||this.lastIndexedDataHash!==c?(console.log(`${s} \u{1F527} Building index: isIndexBuilt=${this.isIndexBuilt}, dataChanged=${this.lastIndexedDataHash!==c}`),await this.buildIndex(r,t,u),this.isIndexBuilt=!0,this.lastIndexedDataHash=c):console.log(`${s} \u{1F527} Index already built for current data, skipping rebuild`),!(this.indexWorker&&this.levenshteinWorker))throw new Error("Web Workers are not available or not initialized.");let l=await this.searchParallel(e,r,t,u),p=performance.now()-i;this.updateStats(e,p),this.updateLearningData(e,r.length,p);let g={results:l,totalItems:r.length,processingTime:p,query:e,options:u};return this.eventEmitter.emit("search:complete",g),l}catch(c){let l=c instanceof Error?c:new Error("Unknown search error");throw this.eventEmitter.emit("search:error",{error:l,query:e}),l}}async buildIndex(e,r,t){if(!this.indexWorker)throw new Error("Index Worker not available");let n=`build-index-${++this.requestId}`;console.log(`${s} \u{1F4E4} Building index: requestId=${n}`);let i={type:"buildIndex",id:n,items:e,searchFields:r,options:t};return new Promise((u,c)=>{let l=null,p=b=>{let f=b;f.type==="indexReady"&&f.id===n&&(console.log(`${s} \u{1F3AF} buildIndex: Match found! Resolving promise for requestId=${n}`),this.eventEmitter.off("indexWorker:message",p),this.eventEmitter.off("indexWorker:message",g),l&&(clearTimeout(l),l=null),u())},g=b=>{let f=b;f.type==="error"&&f.id===n&&(console.warn(`${s} \u274C buildIndex: Worker reported error for requestId=${n}`,f.error),this.eventEmitter.off("indexWorker:message",p),this.eventEmitter.off("indexWorker:message",g),l&&(clearTimeout(l),l=null),c(new Error(f.error||"Index Worker error")))};this.eventEmitter.on("indexWorker:message",p),this.eventEmitter.on("indexWorker:message",g),this.indexWorker?.postMessage(i),l=setTimeout(()=>{this.eventEmitter.off("indexWorker:message",p),this.eventEmitter.off("indexWorker:message",g),l=null,c(new Error("Index build timeout"))},6e4)})}async searchParallel(e,r,t,n){console.log(`${s} \u{1F310} searchParallel: query="${e}", items.length=${r.length}`);let{enableIndexFiltering:i,enableLevenshtein:u}=n;if(!(i||u))return console.log(`${s} \u26A0\uFE0F Both search stages are disabled. Returning empty results.`),[];let c=i?this.runIndexStage(e,r,t,n):Promise.resolve({stage:"index",results:[],processingTime:0,confidence:0,candidatesProcessed:0}),l=u?this.runLevenshteinStage(e,r,t,n):Promise.resolve({stage:"levenshtein",results:[],processingTime:0,confidence:0,candidatesProcessed:0}),[p,g]=await Promise.all([c,l]),b=this.mergeParallelResults(p,g,n.parallelSearchStrategy||"balanced",n);return console.log(`${s} \u{1F527} Parallel search completed: Index=${p.results.length}, Levenshtein=${g.results.length}, Merged=${b.results.length}`),b.results}async runIndexStage(e,r,t,n){let i=`index-${++this.requestId}`;console.log(`${s} \u{1F4E4} Index Stage: requestId=${i}`);let u={type:"indexSearch",id:i,query:e,items:r,searchFields:t,options:n};return this.executeWorkerStage(this.indexWorker,u,{stage:"index",confidence:.8,timeoutMessage:"Index search timeout",unavailableMessage:"Index Worker not available"})}async runLevenshteinStage(e,r,t,n){let i=`levenshtein-${++this.requestId}`;console.log(`${s} \u{1F4E4} Levenshtein Stage: requestId=${i}`);let u={type:"levenshteinSearch",id:i,query:e,items:r,searchFields:t,options:n};return this.executeWorkerStage(this.levenshteinWorker,u,{stage:"levenshtein",confidence:.9,timeoutMessage:"Levenshtein search timeout",unavailableMessage:"Levenshtein Worker not available"})}executeWorkerStage(e,r,t){if(!e)throw new Error(t.unavailableMessage);return new Promise((n,i)=>{let u=setTimeout(()=>{this.pendingRequests.has(r.id)&&(this.pendingRequests.delete(r.id),i(new Error(t.timeoutMessage)))},3e4);this.pendingRequests.set(r.id,{resolve:c=>{clearTimeout(u),n({stage:t.stage,results:c,processingTime:0,confidence:t.confidence,candidatesProcessed:c.length})},reject:c=>{clearTimeout(u),i(c)},timestamp:Date.now()}),e.postMessage(r)})}mergeParallelResults(e,r,t,n){let i=[];switch(t){case"index-first":i=e.results.length>0?e.results:r.results;break;case"levenshtein-first":i=r.results.length>0?r.results:e.results;break;default:i=this.mergeByScore([...e.results,...r.results],n);break}return{results:i,totalProcessingTime:e.processingTime+r.processingTime,indexStage:e,levenshteinStage:r,mergeStrategy:t,mergeQuality:.8}}mergeByScore(e,r){let t=new Map;e.forEach(c=>{if(c.originalIndex!==void 0){let l=t.get(c.originalIndex);(!l||c.score>l.score)&&t.set(c.originalIndex,c)}});let n=Array.from(t.values()),i=r.sortBy||W.sortBy,u=r.sortOrder||W.sortOrder;return i==="original"?n.sort((c,l)=>{let p=c.originalIndex??0,g=l.originalIndex??0;return u==="asc"?p-g:g-p}):n.sort((c,l)=>u==="asc"?c.score-l.score:l.score-c.score)}generateDataHash(e,r){return`${e.length}|${r.join(",")}|${JSON.stringify(e.slice(0,3))}`}updateStats(e,r){this.stats.totalSearches++,this.stats.averageSearchTime=(this.stats.averageSearchTime*(this.stats.totalSearches-1)+r)/this.stats.totalSearches}updateLearningData(e,r,t){this.learningData.queryPatterns[e]=(this.learningData.queryPatterns[e]??0)+1;let n=this.getPerformanceSizeKey(r);this.learningData.performanceMetrics.averageSearchTime[n]=t,this.stats.totalSearches%10===0&&this.saveLearningData()}getPerformanceSizeKey(e){return e<1e3?"small":e<1e4?"medium":e<1e5?"large":"xlarge"}cleanupPendingRequests(){let e=Date.now(),r=3e4;this.pendingRequests.forEach((t,n)=>{e-t.timestamp>r&&(t.reject(new Error("Request timeout")),this.pendingRequests.delete(n))})}updateSearchHistory(e,r,t=0,n=0){let i={query:e,timestamp:Date.now(),selectedItemId:r,resultCount:t,processingTime:n,searchTime:n,successful:t>0};this.searchHistory.unshift(i),this.searchHistory.length>1e3&&(this.searchHistory=this.searchHistory.slice(0,500))}async getStats(e=5e3){return await Promise.all([this.waitForWorkerStats(this.indexWorker,"index",e),this.waitForWorkerStats(this.levenshteinWorker,"levenshtein",e)]),this.stats}getCurrentStats(){return this.stats}async resetStats(e=5e3){let r=[this.resetWorkerStats(this.indexWorker,"index",e),this.resetWorkerStats(this.levenshteinWorker,"levenshtein",e)];this.stats={totalSearches:0,averageSearchTime:0,popularQueries:[],performanceBySize:{},workers:{index:{stats:this.createEmptyWorkerStats("index")},levenshtein:{stats:this.createEmptyWorkerStats("levenshtein")}}},this.searchHistory=[],await Promise.all(r),console.log(s,"\u{1F4CA} FuzzySearchManager: All statistics have been reset"),this.eventEmitter.emit("stats:reset",{timestamp:Date.now(),resetBy:"manager"})}waitForWorkerStats(e,r,t){return e?new Promise(n=>{let i=setTimeout(()=>{console.warn(`${s} \u26A0\uFE0F getStats: Timeout waiting for ${r} worker.`),this.eventEmitter.off("worker:statsUpdated",u),n()},t),u=c=>{let{worker:l}=c;l===r&&(clearTimeout(i),this.eventEmitter.off("worker:statsUpdated",u),n())};this.eventEmitter.on("worker:statsUpdated",u),e.postMessage({type:"getStats"})}):Promise.resolve()}resetWorkerStats(e,r,t){return e?new Promise(n=>{let i=setTimeout(()=>{console.warn(`${s} \u26A0\uFE0F resetStats: Timeout waiting for ${r} worker.`),e.removeEventListener("message",u),n()},t),u=l=>{l.data.type==="resetStats"&&(clearTimeout(i),e.removeEventListener("message",u),console.log(`${s} \u{1F4CA} ${r} worker statistics have been reset`),n())};e.addEventListener("message",u);let c={type:"resetStats",id:`resetStats-${Date.now()}-${r}`};e.postMessage(c)}):Promise.resolve()}createEmptyWorkerStats(e){return{basic:{totalSearches:0,totalProcessingTime:0,averageSearchTime:0,lastSearchTime:0,errorCount:0,timestamp:Date.now()},workerSpecific:{workerType:e,operationCount:0,operationDistribution:{},specificMetrics:{}},analysis:{fieldPerformance:{},performanceInsights:{recentAverageProcessingTime:0,recentAverageResultCount:0,throughputPerSecond:0,totalHistoryEntries:0,mostCommonQueryLength:0,bestPerformingField:"",similarityDistribution:{highSimilarity:0,mediumSimilarity:0,lowSimilarity:0,veryLowSimilarity:0}},recentHistory:[]}}}getSearchHistory(){return[...this.searchHistory]}on(e,r){this.eventEmitter.on(e,r)}off(e,r){this.eventEmitter.off(e,r)}async prebuildIndex(e,r,t){console.log(`${s} \u{1F3D7}\uFE0F Pre-building index for ${e.length} items`);try{await this.workersInitialized;let n=this.generateDataHash(e,r);!this.isIndexBuilt||this.lastIndexedDataHash!==n?(console.log(`${s} \u{1F527} Pre-building index: isIndexBuilt=${this.isIndexBuilt}, dataChanged=${this.lastIndexedDataHash!==n}`),await this.rebuildIndex(e,r,t),console.log(`${s} \u2705 Index pre-built successfully for ${e.length} items`)):console.log(`${s} \u{1F527} Index already built for current data, skipping pre-build`)}catch(n){throw console.error(s,"\u{1F527} Error during index pre-building:",n),n}}async rebuildIndex(e,r,t){console.log(`${s} \u{1F504} Starting forced index rebuild for ${e.length} items`);let n={...this.defaultOptions,...t};try{await this.workersInitialized,this.isIndexBuilt=!1,this.lastIndexedDataHash=null,await this.buildIndex(e,r,n),console.log(`${s} \u2705 Index rebuild completed successfully for ${e.length} items`),this.eventEmitter.emit("index:rebuilt",{itemCount:e.length,searchFields:r,timestamp:Date.now()})}catch(i){throw console.error(s,"\u274C Failed to rebuild index:",i),this.eventEmitter.emit("index:rebuildError",{error:i instanceof Error?i.message:String(i),timestamp:Date.now()}),i}}dispose(){F.getInstance().releaseReference(),this.indexWorker=null,this.levenshteinWorker=null,this.pendingRequests.clear(),this.eventEmitter.removeAllListeners(),this.saveLearningData()}};import{useCallback as I,useEffect as z,useMemo as C,useRef as B,useState as w}from"react";var m="[fuzzy-search]",R=(a,e)=>{if(typeof a=="string"&&a.trim())return a;if(a instanceof Error&&a.message.trim())return a.message;if(typeof a=="object"&&a!==null){if("error"in a)return R(a.error,e);if("message"in a)return R(a.message,e)}return e};function N(a,e,r){let[t,n]=w(""),[i,u]=w([]),[c,l]=w(!1),[p,g]=w(!1),[b,f]=w(null),[M,L]=w([]),[A,H]=w({totalSearches:0,averageSearchTime:0,itemCount:a.length,resultsCount:0,workerStats:void 0}),y=C(()=>({...W,...r}),[r]),d=B(null),q=B(t);z(()=>{q.current=t},[t]);let P=C(()=>e.map(o=>String(o)),[e]);z(()=>{d.current||(d.current=new E(y),console.log(m,"\u{1F527} FuzzySearchManager created (initial setup)"));let o=()=>{if(d.current){let S=d.current.getCurrentStats(),k={totalSearches:S?.totalSearches??0,averageSearchTime:S?.averageSearchTime??0,itemCount:a.length,resultsCount:i.length,workerStats:{index:S?.workers.index.stats,levenshtein:S?.workers.levenshtein.stats}};H(k)}},h=S=>{let k=R(S,"Index Worker error");console.error(`${m} \u274C Index worker error event`,S),f(k),g(!1)},v=S=>{let k=R(S,"Index rebuild error");console.error(`${m} \u274C Index rebuild error event`,S),f(k),g(!1)},x=S=>{let k=R(S,"Search execution failed");console.error(`${m} \u274C Search error event`,S),f(k)};return d.current.on("worker:statsUpdated",o),d.current.on("search:complete",o),d.current.on("indexWorker:error",h),d.current.on("index:rebuildError",v),d.current.on("search:error",x),()=>{d.current&&(d.current.off("worker:statsUpdated",o),d.current.off("search:complete",o),d.current.off("indexWorker:error",h),d.current.off("index:rebuildError",v),d.current.off("search:error",x))}});let $=I(async o=>{if(d.current){console.log(`${m} \u{1F50D} performSearch called with query: "${o}"`);try{if(console.log(`${m} \u{1F4C8} Setting isSearching to true for query: "${o}"`),l(!0),f(null),!o.trim()){u(a.map((O,U)=>({item:O,score:1,baseScore:1,matchedFields:[],relevanceReason:"\u5168\u4EF6\u8868\u793A",originalIndex:U}))),console.log(`${m} \u{1F4C8} Setting isSearching to false for empty query: "${o}"`),l(!1);return}console.log(`${m} \u{1F680} Starting search with query: "${o}", items count: ${a.length}`);let h=performance.now(),v=await d.current.search(o,a,P,y),S=performance.now()-h;console.log(`${m} \u2705 Search completed with ${v.length} results in ${S}ms`),u(v);let k={query:o,timestamp:Date.now(),resultCount:v.length,processingTime:S,searchTime:S,successful:v.length>0};L(O=>{let U=O.filter(Y=>Y.query!==o);return[k,...U].slice(0,50)})}catch(h){console.log(`${m} \u274C Search error for query: "${o}"`,h),f(h instanceof Error?h.message:"Search failed"),u([])}finally{console.log(`${m} \u{1F4C8} Setting isSearching to false (finally) for query: "${o}"`),l(!1)}}},[a,P,y]);z(()=>{if(!d.current||a.length===0)return;let o=!1,h=y.autoSearchOnIndexRebuild??!0;return console.log(`${m} \u{1F3D7}\uFE0F Data changed, pre-building index for ${a.length} items`),g(!0),d.current.prebuildIndex(a,P,y).then(()=>{!o&&h&&(console.log(`${m} \u{1F501} Re-running search after index rebuild`),$(q.current))}).catch(v=>{console.warn(m,"\u{1F527} Failed to pre-build index:",v),f(R(v,"\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u306E\u4E8B\u524D\u69CB\u7BC9\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"))}).finally(()=>{o||g(!1)}),()=>{o=!0}},[a,P,y,$]),z(()=>()=>{d.current&&(console.log(m,"\u{1F527} FuzzySearchManager disposed (component unmount)"),d.current.dispose(),d.current=null)},[]),z(()=>{console.log(`${m} \u{1F3AF} useEffect triggered - searchTerm: "${t}", debounceMs: ${y.debounceMs}`);let o=setTimeout(()=>{console.log(`${m} \u23F0 Timeout executing performSearch for: "${t}"`),$(t)},y.debounceMs);return()=>{console.log(`${m} \u{1F6AB} Timeout cleared for: "${t}"`),clearTimeout(o)}},[t,y,$]),z(()=>{t.trim()||u(a.map((h,v)=>({item:h,score:1,baseScore:1,matchedFields:[],relevanceReason:"\u5168\u4EF6\u8868\u793A",originalIndex:v})))},[a,t]);let _=I((o,h)=>{console.log(m,"\u{1F3AF} Selected item:",o,"at index:",h)},[]),j=I(()=>{n(""),f(null)},[]),Q=I(()=>{L([])},[]),Z=I(async()=>{if(d.current){await d.current.resetStats(),L([]),f(null);let o=d.current.getCurrentStats(),h={totalSearches:o?.totalSearches??0,averageSearchTime:o?.averageSearchTime??0,itemCount:a.length,resultsCount:i.length,workerStats:{index:o?.workers.index.stats,levenshtein:o?.workers.levenshtein.stats}};H(h),console.log(`${m} \u{1F4CA} useFuzzySearch: Statistics have been reset and refreshed`)}},[a.length,i.length]),G=I((o,h=5)=>{let v=M.filter(x=>x.query.toLowerCase().includes(o.toLowerCase())&&x.query!==o).sort((x,S)=>S.timestamp-x.timestamp).slice(0,h).map(x=>x.query);return Array.from(new Set(v))},[M]),J=I(async o=>{if(!d.current){console.warn(`${m} \u26A0\uFE0F Search manager is not initialized`);return}console.log(`${m} \u{1F504} Rebuilding index for ${a.length} items`),g(!0),f(null);try{let h=o?{...y,...o}:y;await d.current.rebuildIndex(a,P,h),console.log(`${m} \u2705 Index rebuild completed successfully`)}catch(h){let v=h instanceof Error?h.message:"\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u518D\u69CB\u7BC9\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F";throw console.error(m,"\u274C Index rebuild failed:",v),f(v),h}finally{g(!1)}},[a,P,y]);return{searchTerm:t,setSearchTerm:n,filteredItems:i,isSearching:c,isIndexBuilding:p,error:b,searchHistory:M,options:y,selectItem:_,clearSearch:j,clearHistory:Q,resetStats:Z,getSearchSuggestions:G,performanceMetrics:A,rebuildIndex:J}}export{W as DEFAULT_FUZZY_SEARCH_OPTIONS,E as FuzzySearchManager,K as isSearchResultItem,N as useFuzzySearch}; //# sourceMappingURL=index.mjs.map