/** * Fuse.js v7.5.0 - Lightweight fuzzy-search (http://fusejs.io) * * Copyright (c) 2026 Kiro Risk (http://kiro.me) * All Rights Reserved. Apache Software License 2.0 * * http://www.apache.org/licenses/LICENSE-2.0 */ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); //#region src/core/errorMessages.ts const FUSE_WORKER_UNSUPPORTED_FN_OPTION = (option) => `FuseWorker does not support function-valued option '${option}': functions cannot be transferred to Web Workers via postMessage. Remove this option or fall back to Fuse.`; const FUSE_WORKER_TOKEN_SEARCH_UNSUPPORTED = "FuseWorker does not support useTokenSearch: token search depends on corpus-level statistics (df, fieldCount) that are computed per shard, so per-shard scores would diverge from single-thread Fuse. Use Fuse on the main thread for token search."; //#endregion //#region src/workers/FuseWorker.ts const DEFAULT_MAX_WORKERS = 8; function getDefaultWorkerCount() { const hw = typeof navigator !== "undefined" ? navigator.hardwareConcurrency || 4 : 4; return Math.min(hw, DEFAULT_MAX_WORKERS); } const browserWorkerBase = typeof document !== "undefined" ? document.currentScript?.src || new URL("fuse-worker.cjs", document.baseURI).href : void 0; function resolveDefaultWorkerUrl() { if (browserWorkerBase !== void 0) return new URL("./fuse.worker.mjs", browserWorkerBase); try { return new URL("./fuse.worker.mjs", require("url").pathToFileURL(__filename).href); } catch { if (browserWorkerBase !== void 0) return new URL("./fuse.worker.mjs", browserWorkerBase); throw new Error("FuseWorker: unable to resolve the worker script URL automatically; pass workerOptions.workerUrl explicitly."); } } var FuseWorker = class FuseWorker { constructor(docs, options, workerOptions) { this._shards = null; this._addCursor = 0; this._initPromise = null; this._pending = /* @__PURE__ */ new Map(); this._nextId = 0; this._docs = docs.slice(); this._options = options || {}; this._workerOptions = workerOptions || {}; FuseWorker._assertNoFunctionOptions(this._options); if (this._options.useTokenSearch) throw new Error(FUSE_WORKER_TOKEN_SEARCH_UNSUPPORTED); this._workerUrl = this._workerOptions.workerUrl || resolveDefaultWorkerUrl(); } static _assertNoFunctionOptions(options) { if (typeof options.sortFn === "function") throw new Error(FUSE_WORKER_UNSUPPORTED_FN_OPTION("sortFn")); if (typeof options.getFn === "function") throw new Error(FUSE_WORKER_UNSUPPORTED_FN_OPTION("getFn")); if (typeof options.tokenize === "function") throw new Error(FUSE_WORKER_UNSUPPORTED_FN_OPTION("tokenize")); const keys = options.keys; if (Array.isArray(keys)) for (let i = 0, len = keys.length; i < len; i += 1) { const key = keys[i]; if (key && typeof key === "object" && !Array.isArray(key)) { if (typeof key.getFn === "function") { const name = key.name; const label = Array.isArray(name) ? name.join(".") : name ?? String(i); throw new Error(FUSE_WORKER_UNSUPPORTED_FN_OPTION(`keys[${label}].getFn`)); } } } } _getNumWorkers() { return this._workerOptions.numWorkers || getDefaultWorkerCount(); } _ensureInit() { if (this._initPromise) return this._initPromise; this._initPromise = this._init(); return this._initPromise; } _spawnWorker() { const worker = new Worker(this._workerUrl, { type: "module" }); worker.onmessage = (e) => { const { id, result, error } = e.data; const handler = this._pending.get(id); if (!handler) return; this._pending.delete(id); if (error) handler.reject(new Error(error)); else handler.resolve(result); }; worker.onerror = (e) => { for (const [, handler] of this._pending) handler.reject(new Error(e.message)); }; return worker; } _workerInitOptions() { return { ...this._options, includeScore: true }; } async _init() { const numWorkers = this._getNumWorkers(); const chunkSize = Math.ceil(this._docs.length / numWorkers); this._shards = []; this._addCursor = 0; const workerInitOptions = this._workerInitOptions(); const initPromises = []; for (let i = 0; i < numWorkers; i++) { const start = i * chunkSize; const end = Math.min(start + chunkSize, this._docs.length); const chunk = this._docs.slice(start, end); const globalIndices = []; for (let j = start; j < end; j += 1) globalIndices.push(j); const shard = { worker: this._spawnWorker(), globalIndices }; this._shards.push(shard); initPromises.push(this._call(shard.worker, "init", [chunk, workerInitOptions])); } await Promise.all(initPromises); } _call(worker, method, args) { const id = this._nextId++; return new Promise((resolve, reject) => { this._pending.set(id, { resolve, reject }); worker.postMessage({ id, method, args }); }); } async search(query, options) { await this._ensureInit(); const shards = this._shards; const results = await Promise.all(shards.map((s) => this._call(s.worker, "search", [query, options]))); const merged = []; for (let i = 0, len = results.length; i < len; i += 1) { const { globalIndices } = shards[i]; for (const r of results[i]) merged.push({ ...r, refIndex: globalIndices[r.refIndex] }); } if (this._options.shouldSort !== false) merged.sort((a, b) => { const sa = a.score ?? 0; const sb = b.score ?? 0; if (sa === sb) return a.refIndex < b.refIndex ? -1 : 1; return sa < sb ? -1 : 1; }); else merged.sort((a, b) => a.refIndex - b.refIndex); if (!this._options.includeScore) for (let i = 0, len = merged.length; i < len; i += 1) { const r = merged[i]; merged[i] = r.matches !== void 0 ? { item: r.item, refIndex: r.refIndex, matches: r.matches } : { item: r.item, refIndex: r.refIndex }; } const limit = options?.limit; if (limit && limit > 0) return merged.slice(0, limit); return merged; } async add(doc) { await this._ensureInit(); const shards = this._shards; const shard = shards[this._addCursor % shards.length]; this._addCursor += 1; const globalIdx = this._docs.length; this._docs.push(doc); shard.globalIndices.push(globalIdx); await this._call(shard.worker, "add", [doc]); } async setCollection(docs) { this._docs = docs.slice(); if (!this._shards) { this._initPromise = null; return; } const shards = this._shards; const chunkSize = Math.ceil(this._docs.length / shards.length); this._addCursor = 0; const tasks = []; for (let i = 0, len = shards.length; i < len; i += 1) { const start = i * chunkSize; const end = Math.min(start + chunkSize, this._docs.length); const chunk = this._docs.slice(start, end); const globalIndices = []; for (let j = start; j < end; j += 1) globalIndices.push(j); shards[i].globalIndices = globalIndices; tasks.push(this._call(shards[i].worker, "setCollection", [chunk])); } await Promise.all(tasks); } terminate() { if (this._shards) { for (const { worker } of this._shards) worker.terminate(); this._shards = null; } this._initPromise = null; for (const [, handler] of this._pending) handler.reject(/* @__PURE__ */ new Error("FuseWorker terminated")); this._pending.clear(); } }; //#endregion exports.FuseWorker = FuseWorker;