@aiquants/fuzzy-search
Version:
Advanced fuzzy search library with Levenshtein distance, n-gram indexing, and Web Worker support
2 lines • 25.8 kB
JavaScript
"use strict";var O=Object.defineProperty;var K=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var V=(i,e)=>{for(var r in e)O(i,r,{get:e[r],enumerable:!0})},ee=(i,e,r,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of N(e))!X.call(i,n)&&n!==r&&O(i,n,{get:()=>e[n],enumerable:!(t=K(e,n))||t.enumerable});return i};var re=i=>ee(O({},"__esModule",{value:!0}),i);var te={};V(te,{DEFAULT_FUZZY_SEARCH_OPTIONS:()=>T,FuzzySearchManager:()=>P,isSearchResultItem:()=>q,useFuzzySearch:()=>B});module.exports=re(te);var T={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 q(i){return typeof i=="object"&&i!==null&&"item"in i&&"score"in i&&"matchedFields"in i}var C={},s="[fuzzy-search]",I=class I{constructor(){this.indexWorker=null;this.levenshteinWorker=null;this.initializationPromise=null;this.referenceCount=0}static getInstance(){return I.instance||(I.instance=new I),I.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",C.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",C.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")}};I.instance=null;var E=I,U=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(a){console.error(`${s} \u274C Error in event listener for ${e}:`,a)}})}removeAllListeners(){this.listeners.clear()}},P=class{constructor(e=T){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 U;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 E.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((a,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=S=>{let p=S.data;p.type==="pong"&&p.id===n&&(clearTimeout(c),e.removeEventListener("message",l),console.log(`${s} \u{1F527} ${r==="index"?"Index":"Levenshtein"} Worker: Ping successful`),a())};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 a=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),S=performance.now()-a;this.updateStats(e,S),this.updateLearningData(e,r.length,S);let p={results:l,totalItems:r.length,processingTime:S,query:e,options:u};return this.eventEmitter.emit("search:complete",p),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 a={type:"buildIndex",id:n,items:e,searchFields:r,options:t};return new Promise((u,c)=>{let l=null,S=W=>{let v=W;v.type==="indexReady"&&v.id===n&&(console.log(`${s} \u{1F3AF} buildIndex: Match found! Resolving promise for requestId=${n}`),this.eventEmitter.off("indexWorker:message",S),this.eventEmitter.off("indexWorker:message",p),l&&(clearTimeout(l),l=null),u())},p=W=>{let v=W;v.type==="error"&&v.id===n&&(console.warn(`${s} \u274C buildIndex: Worker reported error for requestId=${n}`,v.error),this.eventEmitter.off("indexWorker:message",S),this.eventEmitter.off("indexWorker:message",p),l&&(clearTimeout(l),l=null),c(new Error(v.error||"Index Worker error")))};this.eventEmitter.on("indexWorker:message",S),this.eventEmitter.on("indexWorker:message",p),this.indexWorker?.postMessage(a),l=setTimeout(()=>{this.eventEmitter.off("indexWorker:message",S),this.eventEmitter.off("indexWorker:message",p),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:a,enableLevenshtein:u}=n;if(!(a||u))return console.log(`${s} \u26A0\uFE0F Both search stages are disabled. Returning empty results.`),[];let c=a?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}),[S,p]=await Promise.all([c,l]),W=this.mergeParallelResults(S,p,n.parallelSearchStrategy||"balanced",n);return console.log(`${s} \u{1F527} Parallel search completed: Index=${S.results.length}, Levenshtein=${p.results.length}, Merged=${W.results.length}`),W.results}async runIndexStage(e,r,t,n){let a=`index-${++this.requestId}`;console.log(`${s} \u{1F4E4} Index Stage: requestId=${a}`);let u={type:"indexSearch",id:a,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 a=`levenshtein-${++this.requestId}`;console.log(`${s} \u{1F4E4} Levenshtein Stage: requestId=${a}`);let u={type:"levenshteinSearch",id:a,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,a)=>{let u=setTimeout(()=>{this.pendingRequests.has(r.id)&&(this.pendingRequests.delete(r.id),a(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),a(c)},timestamp:Date.now()}),e.postMessage(r)})}mergeParallelResults(e,r,t,n){let a=[];switch(t){case"index-first":a=e.results.length>0?e.results:r.results;break;case"levenshtein-first":a=r.results.length>0?r.results:e.results;break;default:a=this.mergeByScore([...e.results,...r.results],n);break}return{results:a,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()),a=r.sortBy||T.sortBy,u=r.sortOrder||T.sortOrder;return a==="original"?n.sort((c,l)=>{let S=c.originalIndex??0,p=l.originalIndex??0;return u==="asc"?S-p:p-S}):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 a={query:e,timestamp:Date.now(),selectedItemId:r,resultCount:t,processingTime:n,searchTime:n,successful:t>0};this.searchHistory.unshift(a),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 a=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(a),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 a=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(a),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(a){throw console.error(s,"\u274C Failed to rebuild index:",a),this.eventEmitter.emit("index:rebuildError",{error:a instanceof Error?a.message:String(a),timestamp:Date.now()}),a}}dispose(){E.getInstance().releaseReference(),this.indexWorker=null,this.levenshteinWorker=null,this.pendingRequests.clear(),this.eventEmitter.removeAllListeners(),this.saveLearningData()}};var d=require("react");var g="[fuzzy-search]",z=(i,e)=>{if(typeof i=="string"&&i.trim())return i;if(i instanceof Error&&i.message.trim())return i.message;if(typeof i=="object"&&i!==null){if("error"in i)return z(i.error,e);if("message"in i)return z(i.message,e)}return e};function B(i,e,r){let[t,n]=(0,d.useState)(""),[a,u]=(0,d.useState)([]),[c,l]=(0,d.useState)(!1),[S,p]=(0,d.useState)(!1),[W,v]=(0,d.useState)(null),[$,F]=(0,d.useState)([]),[A,D]=(0,d.useState)({totalSearches:0,averageSearchTime:0,itemCount:i.length,resultsCount:0,workerStats:void 0}),x=(0,d.useMemo)(()=>({...T,...r}),[r]),h=(0,d.useRef)(null),H=(0,d.useRef)(t);(0,d.useEffect)(()=>{H.current=t},[t]);let w=(0,d.useMemo)(()=>e.map(o=>String(o)),[e]);(0,d.useEffect)(()=>{h.current||(h.current=new P(x),console.log(g,"\u{1F527} FuzzySearchManager created (initial setup)"));let o=()=>{if(h.current){let f=h.current.getCurrentStats(),b={totalSearches:f?.totalSearches??0,averageSearchTime:f?.averageSearchTime??0,itemCount:i.length,resultsCount:a.length,workerStats:{index:f?.workers.index.stats,levenshtein:f?.workers.levenshtein.stats}};D(b)}},m=f=>{let b=z(f,"Index Worker error");console.error(`${g} \u274C Index worker error event`,f),v(b),p(!1)},y=f=>{let b=z(f,"Index rebuild error");console.error(`${g} \u274C Index rebuild error event`,f),v(b),p(!1)},k=f=>{let b=z(f,"Search execution failed");console.error(`${g} \u274C Search error event`,f),v(b)};return h.current.on("worker:statsUpdated",o),h.current.on("search:complete",o),h.current.on("indexWorker:error",m),h.current.on("index:rebuildError",y),h.current.on("search:error",k),()=>{h.current&&(h.current.off("worker:statsUpdated",o),h.current.off("search:complete",o),h.current.off("indexWorker:error",m),h.current.off("index:rebuildError",y),h.current.off("search:error",k))}});let R=(0,d.useCallback)(async o=>{if(h.current){console.log(`${g} \u{1F50D} performSearch called with query: "${o}"`);try{if(console.log(`${g} \u{1F4C8} Setting isSearching to true for query: "${o}"`),l(!0),v(null),!o.trim()){u(i.map((M,L)=>({item:M,score:1,baseScore:1,matchedFields:[],relevanceReason:"\u5168\u4EF6\u8868\u793A",originalIndex:L}))),console.log(`${g} \u{1F4C8} Setting isSearching to false for empty query: "${o}"`),l(!1);return}console.log(`${g} \u{1F680} Starting search with query: "${o}", items count: ${i.length}`);let m=performance.now(),y=await h.current.search(o,i,w,x),f=performance.now()-m;console.log(`${g} \u2705 Search completed with ${y.length} results in ${f}ms`),u(y);let b={query:o,timestamp:Date.now(),resultCount:y.length,processingTime:f,searchTime:f,successful:y.length>0};F(M=>{let L=M.filter(Y=>Y.query!==o);return[b,...L].slice(0,50)})}catch(m){console.log(`${g} \u274C Search error for query: "${o}"`,m),v(m instanceof Error?m.message:"Search failed"),u([])}finally{console.log(`${g} \u{1F4C8} Setting isSearching to false (finally) for query: "${o}"`),l(!1)}}},[i,w,x]);(0,d.useEffect)(()=>{if(!h.current||i.length===0)return;let o=!1,m=x.autoSearchOnIndexRebuild??!0;return console.log(`${g} \u{1F3D7}\uFE0F Data changed, pre-building index for ${i.length} items`),p(!0),h.current.prebuildIndex(i,w,x).then(()=>{!o&&m&&(console.log(`${g} \u{1F501} Re-running search after index rebuild`),R(H.current))}).catch(y=>{console.warn(g,"\u{1F527} Failed to pre-build index:",y),v(z(y,"\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||p(!1)}),()=>{o=!0}},[i,w,x,R]),(0,d.useEffect)(()=>()=>{h.current&&(console.log(g,"\u{1F527} FuzzySearchManager disposed (component unmount)"),h.current.dispose(),h.current=null)},[]),(0,d.useEffect)(()=>{console.log(`${g} \u{1F3AF} useEffect triggered - searchTerm: "${t}", debounceMs: ${x.debounceMs}`);let o=setTimeout(()=>{console.log(`${g} \u23F0 Timeout executing performSearch for: "${t}"`),R(t)},x.debounceMs);return()=>{console.log(`${g} \u{1F6AB} Timeout cleared for: "${t}"`),clearTimeout(o)}},[t,x,R]),(0,d.useEffect)(()=>{t.trim()||u(i.map((m,y)=>({item:m,score:1,baseScore:1,matchedFields:[],relevanceReason:"\u5168\u4EF6\u8868\u793A",originalIndex:y})))},[i,t]);let _=(0,d.useCallback)((o,m)=>{console.log(g,"\u{1F3AF} Selected item:",o,"at index:",m)},[]),j=(0,d.useCallback)(()=>{n(""),v(null)},[]),Q=(0,d.useCallback)(()=>{F([])},[]),Z=(0,d.useCallback)(async()=>{if(h.current){await h.current.resetStats(),F([]),v(null);let o=h.current.getCurrentStats(),m={totalSearches:o?.totalSearches??0,averageSearchTime:o?.averageSearchTime??0,itemCount:i.length,resultsCount:a.length,workerStats:{index:o?.workers.index.stats,levenshtein:o?.workers.levenshtein.stats}};D(m),console.log(`${g} \u{1F4CA} useFuzzySearch: Statistics have been reset and refreshed`)}},[i.length,a.length]),G=(0,d.useCallback)((o,m=5)=>{let y=$.filter(k=>k.query.toLowerCase().includes(o.toLowerCase())&&k.query!==o).sort((k,f)=>f.timestamp-k.timestamp).slice(0,m).map(k=>k.query);return Array.from(new Set(y))},[$]),J=(0,d.useCallback)(async o=>{if(!h.current){console.warn(`${g} \u26A0\uFE0F Search manager is not initialized`);return}console.log(`${g} \u{1F504} Rebuilding index for ${i.length} items`),p(!0),v(null);try{let m=o?{...x,...o}:x;await h.current.rebuildIndex(i,w,m),console.log(`${g} \u2705 Index rebuild completed successfully`)}catch(m){let y=m instanceof Error?m.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(g,"\u274C Index rebuild failed:",y),v(y),m}finally{p(!1)}},[i,w,x]);return{searchTerm:t,setSearchTerm:n,filteredItems:a,isSearching:c,isIndexBuilding:S,error:W,searchHistory:$,options:x,selectItem:_,clearSearch:j,clearHistory:Q,resetStats:Z,getSearchSuggestions:G,performanceMetrics:A,rebuildIndex:J}}0&&(module.exports={DEFAULT_FUZZY_SEARCH_OPTIONS,FuzzySearchManager,isSearchResultItem,useFuzzySearch});
//# sourceMappingURL=index.js.map