diff --git a/dist/fuse-worker.cjs b/dist/fuse-worker.cjs
index 05f6f32f6..455bf7543 100644
--- a/dist/fuse-worker.cjs
+++ b/dist/fuse-worker.cjs
@@ -12,6 +12,12 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
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/helpers/typeGuards.ts
+function isDefined(value) {
+ return value !== void 0 && value !== null;
+}
+
//#endregion
//#region src/workers/FuseWorker.ts
const DEFAULT_MAX_WORKERS = 8;
@@ -159,6 +165,7 @@ var FuseWorker = class FuseWorker {
return merged;
}
async add(doc) {
+ if (!isDefined(doc)) return;
await this._ensureInit();
const shards = this._shards;
const shard = shards[this._addCursor % shards.length];
diff --git a/dist/fuse-worker.mjs b/dist/fuse-worker.mjs
index 1a6ddc4ec..31e51968e 100644
--- a/dist/fuse-worker.mjs
+++ b/dist/fuse-worker.mjs
@@ -10,6 +10,12 @@
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/helpers/typeGuards.ts
+function isDefined(value) {
+ return value !== void 0 && value !== null;
+}
+
//#endregion
//#region src/workers/FuseWorker.ts
const DEFAULT_MAX_WORKERS = 8;
@@ -156,6 +162,7 @@ var FuseWorker = class FuseWorker {
return merged;
}
async add(doc) {
+ if (!isDefined(doc)) return;
await this._ensureInit();
const shards = this._shards;
const shard = shards[this._addCursor % shards.length];
diff --git a/src/workers/FuseWorker.ts b/src/workers/FuseWorker.ts
index 428327eb7..43532eac6 100644
--- a/src/workers/FuseWorker.ts
+++ b/src/workers/FuseWorker.ts
@@ -1,6 +1,7 @@
///
import * as ErrorMsg from '../core/errorMessages'
+import { isDefined } from '../helpers/typeGuards'
import type {
IFuseOptions,
FuseOptionKey,
@@ -278,6 +279,11 @@ export default class FuseWorker {
}
async add(doc: T): Promise {
+ // Match Fuse.add's no-op before advancing either global or shard indices.
+ if (!isDefined(doc)) {
+ return
+ }
+
await this._ensureInit()
const shards = this._shards!
diff --git a/test/fuse-worker.test.js b/test/fuse-worker.test.js
index 7d1ddbe6e..09bdbbfff 100644
--- a/test/fuse-worker.test.js
+++ b/test/fuse-worker.test.js
@@ -124,6 +124,51 @@ describe('FuseWorker sharding', () => {
fw.terminate()
})
+ test.each([null, undefined])(
+ 'add(%s) before initialization does not spawn workers',
+ async (doc) => {
+ MockWorker.instances = []
+ const fw = new FuseWorker(['alpha'], {}, { numWorkers: 2 })
+ await fw.add(doc)
+ expect(MockWorker.instances).toHaveLength(0)
+ expect(await fw.search('')).toEqual([{ item: 'alpha', refIndex: 0 }])
+ fw.terminate()
+ }
+ )
+
+ test.each([1, 3])(
+ 'nullish additions preserve single-thread results with %s workers',
+ async (numWorkers) => {
+ const docs = ['alpha', 'beta', 'gamma']
+ const options = { includeMatches: true, includeScore: true, threshold: 0 }
+ const fw = new FuseWorker(docs, options, { numWorkers })
+ const fuse = new Fuse(docs.slice(), options)
+ await fw.search('alpha')
+ for (const doc of [null, undefined, 'delta', null, 'epsilon']) {
+ await fw.add(doc)
+ fuse.add(doc)
+ }
+ for (const query of ['', 'delta', 'epsilon', 'alpha']) {
+ expect(await fw.search(query)).toEqual(fuse.search(query))
+ }
+ fw.terminate()
+ }
+ )
+
+ test('falsy but defined additions are not skipped', async () => {
+ const docs = [{ name: 'alpha' }]
+ const options = { keys: ['name'], threshold: 0 }
+ const fw = new FuseWorker(docs, options, { numWorkers: 3 })
+ const fuse = new Fuse(docs.slice(), options)
+ for (const doc of [null, '', 0, false, { name: 'beta' }]) {
+ await fw.add(doc)
+ fuse.add(doc)
+ }
+ expect(await fw.search('')).toEqual(fuse.search(''))
+ expect(await fw.search('beta')).toEqual(fuse.search('beta'))
+ fw.terminate()
+ })
+
test('interleaved search/add/setCollection keeps refIndex consistent', async () => {
MockWorker.instances = []
const initial = Books.slice(0, 4)