bluera-knowledge 0.11.18 → 0.11.20

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.
Files changed (48) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +55 -0
  3. package/dist/{chunk-6FHWC36B.js → chunk-HRQD3MPH.js} +8 -6
  4. package/dist/chunk-HRQD3MPH.js.map +1 -0
  5. package/dist/{chunk-ZZNABJMQ.js → chunk-MQGRQ2EG.js} +99 -34
  6. package/dist/chunk-MQGRQ2EG.js.map +1 -0
  7. package/dist/{chunk-ZDEO4WJT.js → chunk-Q2ZGPJ66.js} +22 -70
  8. package/dist/chunk-Q2ZGPJ66.js.map +1 -0
  9. package/dist/{chunk-5NUI6JL6.js → chunk-ZSKQIMD7.js} +5 -2
  10. package/dist/chunk-ZSKQIMD7.js.map +1 -0
  11. package/dist/index.js +36 -18
  12. package/dist/index.js.map +1 -1
  13. package/dist/mcp/server.js +3 -3
  14. package/dist/watch.service-OPLKIDFQ.js +7 -0
  15. package/dist/workers/background-worker-cli.js +3 -3
  16. package/package.json +1 -1
  17. package/src/cli/commands/crawl.ts +1 -1
  18. package/src/cli/commands/index-cmd.test.ts +14 -4
  19. package/src/cli/commands/index-cmd.ts +11 -4
  20. package/src/cli/commands/store.test.ts +211 -18
  21. package/src/cli/commands/store.ts +26 -8
  22. package/src/crawl/article-converter.test.ts +30 -61
  23. package/src/crawl/article-converter.ts +2 -8
  24. package/src/crawl/bridge.test.ts +14 -0
  25. package/src/crawl/bridge.ts +17 -5
  26. package/src/crawl/intelligent-crawler.test.ts +65 -76
  27. package/src/crawl/intelligent-crawler.ts +33 -69
  28. package/src/db/lance.test.ts +3 -4
  29. package/src/db/lance.ts +14 -19
  30. package/src/mcp/server.test.ts +56 -1
  31. package/src/mcp/server.ts +5 -1
  32. package/src/plugin/git-clone.test.ts +44 -0
  33. package/src/plugin/git-clone.ts +4 -0
  34. package/src/services/code-unit.service.test.ts +59 -6
  35. package/src/services/code-unit.service.ts +47 -2
  36. package/src/services/index.ts +19 -3
  37. package/src/services/job.service.test.ts +10 -7
  38. package/src/services/job.service.ts +12 -6
  39. package/src/services/search.service.ts +15 -9
  40. package/src/services/services.test.ts +19 -6
  41. package/src/services/watch.service.test.ts +80 -56
  42. package/src/services/watch.service.ts +9 -6
  43. package/dist/chunk-5NUI6JL6.js.map +0 -1
  44. package/dist/chunk-6FHWC36B.js.map +0 -1
  45. package/dist/chunk-ZDEO4WJT.js.map +0 -1
  46. package/dist/chunk-ZZNABJMQ.js.map +0 -1
  47. package/dist/watch.service-BJV3TI3F.js +0 -7
  48. /package/dist/{watch.service-BJV3TI3F.js.map → watch.service-OPLKIDFQ.js.map} +0 -0
@@ -518,15 +518,21 @@ export class SearchService {
518
518
  const results: SearchResult[] = [];
519
519
 
520
520
  for (const storeId of stores) {
521
- const hits = await this.lanceStore.fullTextSearch(storeId, query, limit);
522
- results.push(
523
- ...hits.map((r) => ({
524
- id: r.id,
525
- score: r.score,
526
- content: r.content,
527
- metadata: r.metadata,
528
- }))
529
- );
521
+ try {
522
+ const hits = await this.lanceStore.fullTextSearch(storeId, query, limit);
523
+ results.push(
524
+ ...hits.map((r) => ({
525
+ id: r.id,
526
+ score: r.score,
527
+ content: r.content,
528
+ metadata: r.metadata,
529
+ }))
530
+ );
531
+ } catch {
532
+ // FTS index may not exist for this store - continue with other stores
533
+ // and rely on vector search results. This is expected behavior since
534
+ // FTS indexing is optional and hybrid search works with vector-only.
535
+ }
530
536
  }
531
537
 
532
538
  return results.sort((a, b) => b.score - a.score).slice(0, limit);
@@ -30,11 +30,12 @@ describe('destroyServices', () => {
30
30
  expect(mockPythonBridge.stop).toHaveBeenCalledTimes(1);
31
31
  });
32
32
 
33
- it('handles stop errors gracefully', async () => {
33
+ it('throws on stop errors', async () => {
34
34
  mockPythonBridge.stop.mockRejectedValue(new Error('stop failed'));
35
35
 
36
- // destroyServices catches and logs errors instead of propagating (Bug #2 fix)
37
- await expect(destroyServices(mockServices)).resolves.not.toThrow();
36
+ await expect(destroyServices(mockServices)).rejects.toThrow(
37
+ 'Service shutdown failed: stop failed'
38
+ );
38
39
  });
39
40
 
40
41
  it('is idempotent - multiple calls work correctly', async () => {
@@ -52,11 +53,23 @@ describe('destroyServices', () => {
52
53
  expect(mockLance.close).not.toHaveBeenCalled();
53
54
  });
54
55
 
55
- it('handles LanceStore closeAsync errors gracefully', async () => {
56
+ it('throws on LanceStore closeAsync errors', async () => {
56
57
  mockLance.closeAsync.mockRejectedValue(new Error('closeAsync failed'));
57
58
 
58
- // Should not throw even if closeAsync fails
59
- await expect(destroyServices(mockServices)).resolves.not.toThrow();
59
+ await expect(destroyServices(mockServices)).rejects.toThrow(
60
+ 'Service shutdown failed: closeAsync failed'
61
+ );
62
+ });
63
+
64
+ it('attempts all cleanup even if first fails, then throws aggregate', async () => {
65
+ mockLance.closeAsync.mockRejectedValue(new Error('lance failed'));
66
+ mockPythonBridge.stop.mockRejectedValue(new Error('bridge failed'));
67
+
68
+ await expect(destroyServices(mockServices)).rejects.toThrow();
69
+
70
+ // Both should have been called even though first failed
71
+ expect(mockLance.closeAsync).toHaveBeenCalledTimes(1);
72
+ expect(mockPythonBridge.stop).toHaveBeenCalledTimes(1);
60
73
  });
61
74
 
62
75
  it('waits for LanceStore async cleanup before returning', async () => {
@@ -75,10 +75,12 @@ describe('WatchService', () => {
75
75
  });
76
76
 
77
77
  describe('watch - Lifecycle', () => {
78
+ const noopErrorHandler = (): void => {};
79
+
78
80
  it('starts watching a file store', async () => {
79
81
  const { watch } = await import('chokidar');
80
82
 
81
- await watchService.watch(mockFileStore);
83
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
82
84
 
83
85
  expect(watch).toHaveBeenCalledWith(
84
86
  mockFileStore.path,
@@ -93,13 +95,13 @@ describe('WatchService', () => {
93
95
  it('starts watching a repo store', async () => {
94
96
  const { watch } = await import('chokidar');
95
97
 
96
- await watchService.watch(mockRepoStore);
98
+ await watchService.watch(mockRepoStore, 1000, undefined, noopErrorHandler);
97
99
 
98
100
  expect(watch).toHaveBeenCalledWith(mockRepoStore.path, expect.any(Object));
99
101
  });
100
102
 
101
103
  it('sets up event handlers on watcher', async () => {
102
- await watchService.watch(mockFileStore);
104
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
103
105
 
104
106
  const watcher = mockWatchers[0];
105
107
  expect(watcher?.on).toHaveBeenCalledWith('all', expect.any(Function));
@@ -109,10 +111,10 @@ describe('WatchService', () => {
109
111
  it('does not start watching if already watching the same store', async () => {
110
112
  const { watch } = await import('chokidar');
111
113
 
112
- await watchService.watch(mockFileStore);
114
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
113
115
  const callCount1 = (watch as ReturnType<typeof vi.fn>).mock.calls.length;
114
116
 
115
- await watchService.watch(mockFileStore);
117
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
116
118
  const callCount2 = (watch as ReturnType<typeof vi.fn>).mock.calls.length;
117
119
 
118
120
  expect(callCount2).toBe(callCount1);
@@ -121,17 +123,17 @@ describe('WatchService', () => {
121
123
  it('allows watching multiple different stores', async () => {
122
124
  const { watch } = await import('chokidar');
123
125
 
124
- await watchService.watch(mockFileStore);
125
- await watchService.watch(mockRepoStore);
126
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
127
+ await watchService.watch(mockRepoStore, 1000, undefined, noopErrorHandler);
126
128
 
127
129
  expect(watch).toHaveBeenCalledTimes(2);
128
130
  });
129
131
 
130
132
  it('resolves immediately when store is already being watched', async () => {
131
- await watchService.watch(mockFileStore);
133
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
132
134
 
133
135
  const startTime = Date.now();
134
- await watchService.watch(mockFileStore);
136
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
135
137
  const endTime = Date.now();
136
138
 
137
139
  // Should resolve immediately
@@ -140,8 +142,10 @@ describe('WatchService', () => {
140
142
  });
141
143
 
142
144
  describe('watch - Debounce Logic', () => {
145
+ const noopErrorHandler = (): void => {};
146
+
143
147
  it('debounces rapid file changes with default timeout', async () => {
144
- await watchService.watch(mockFileStore);
148
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
145
149
 
146
150
  const watcher = mockWatchers[0];
147
151
  const allHandler = (watcher?.on as ReturnType<typeof vi.fn>).mock.calls.find(
@@ -236,8 +240,10 @@ describe('WatchService', () => {
236
240
  });
237
241
 
238
242
  describe('watch - Reindexing', () => {
243
+ const noopErrorHandler = (): void => {};
244
+
239
245
  it('initializes lance store before reindexing', async () => {
240
- await watchService.watch(mockFileStore);
246
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
241
247
 
242
248
  const watcher = mockWatchers[0];
243
249
  const allHandler = (watcher?.on as ReturnType<typeof vi.fn>).mock.calls.find(
@@ -255,7 +261,7 @@ describe('WatchService', () => {
255
261
  });
256
262
 
257
263
  it('calls indexStore with correct store', async () => {
258
- await watchService.watch(mockFileStore);
264
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
259
265
 
260
266
  const watcher = mockWatchers[0];
261
267
  const allHandler = (watcher?.on as ReturnType<typeof vi.fn>).mock.calls.find(
@@ -272,7 +278,7 @@ describe('WatchService', () => {
272
278
  it('calls onReindex callback after successful reindex', async () => {
273
279
  const onReindex = vi.fn();
274
280
 
275
- await watchService.watch(mockFileStore, 1000, onReindex);
281
+ await watchService.watch(mockFileStore, 1000, onReindex, noopErrorHandler);
276
282
 
277
283
  const watcher = mockWatchers[0];
278
284
  const allHandler = (watcher?.on as ReturnType<typeof vi.fn>).mock.calls.find(
@@ -287,7 +293,7 @@ describe('WatchService', () => {
287
293
  });
288
294
 
289
295
  it('does not call onReindex if not provided', async () => {
290
- await watchService.watch(mockFileStore);
296
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
291
297
 
292
298
  const watcher = mockWatchers[0];
293
299
  const allHandler = (watcher?.on as ReturnType<typeof vi.fn>).mock.calls.find(
@@ -303,8 +309,8 @@ describe('WatchService', () => {
303
309
  });
304
310
 
305
311
  it('handles concurrent reindexing for different stores', async () => {
306
- await watchService.watch(mockFileStore);
307
- await watchService.watch(mockRepoStore);
312
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
313
+ await watchService.watch(mockRepoStore, 1000, undefined, noopErrorHandler);
308
314
 
309
315
  const watcher1 = mockWatchers[0];
310
316
  const watcher2 = mockWatchers[1];
@@ -330,12 +336,13 @@ describe('WatchService', () => {
330
336
  });
331
337
 
332
338
  describe('watch - Error Handling', () => {
333
- it('logs error when reindexing fails', async () => {
334
- const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
339
+ it('calls onError when reindexing fails', async () => {
340
+ const onError = vi.fn();
341
+ const indexError = new Error('Index failed');
335
342
 
336
- mockIndexService.indexStore = vi.fn().mockRejectedValue(new Error('Index failed'));
343
+ mockIndexService.indexStore = vi.fn().mockRejectedValue(indexError);
337
344
 
338
- await watchService.watch(mockFileStore);
345
+ await watchService.watch(mockFileStore, 1000, undefined, onError);
339
346
 
340
347
  const watcher = mockWatchers[0];
341
348
  const allHandler = (watcher?.on as ReturnType<typeof vi.fn>).mock.calls.find(
@@ -346,17 +353,16 @@ describe('WatchService', () => {
346
353
  vi.advanceTimersByTime(1100);
347
354
  await vi.runAllTimersAsync();
348
355
 
349
- expect(consoleErrorSpy).toHaveBeenCalledWith('Error during reindexing:', expect.any(Error));
350
-
351
- consoleErrorSpy.mockRestore();
356
+ expect(onError).toHaveBeenCalledWith(indexError);
352
357
  });
353
358
 
354
- it('logs error when lance initialization fails', async () => {
355
- const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
359
+ it('calls onError when lance initialization fails', async () => {
360
+ const onError = vi.fn();
361
+ const initError = new Error('Init failed');
356
362
 
357
- mockLanceStore.initialize = vi.fn().mockRejectedValue(new Error('Init failed'));
363
+ mockLanceStore.initialize = vi.fn().mockRejectedValue(initError);
358
364
 
359
- await watchService.watch(mockFileStore);
365
+ await watchService.watch(mockFileStore, 1000, undefined, onError);
360
366
 
361
367
  const watcher = mockWatchers[0];
362
368
  const allHandler = (watcher?.on as ReturnType<typeof vi.fn>).mock.calls.find(
@@ -367,15 +373,13 @@ describe('WatchService', () => {
367
373
  vi.advanceTimersByTime(1100);
368
374
  await vi.runAllTimersAsync();
369
375
 
370
- expect(consoleErrorSpy).toHaveBeenCalledWith('Error during reindexing:', expect.any(Error));
371
-
372
- consoleErrorSpy.mockRestore();
376
+ expect(onError).toHaveBeenCalledWith(initError);
373
377
  });
374
378
 
375
- it('handles watcher errors', async () => {
376
- const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
379
+ it('calls onError for watcher errors', async () => {
380
+ const onError = vi.fn();
377
381
 
378
- await watchService.watch(mockFileStore);
382
+ await watchService.watch(mockFileStore, 1000, undefined, onError);
379
383
 
380
384
  const watcher = mockWatchers[0];
381
385
  const errorHandler = (watcher?.on as ReturnType<typeof vi.fn>).mock.calls.find(
@@ -385,13 +389,29 @@ describe('WatchService', () => {
385
389
  const testError = new Error('Watcher error');
386
390
  errorHandler?.(testError);
387
391
 
388
- expect(consoleErrorSpy).toHaveBeenCalledWith('Watcher error:', testError);
392
+ expect(onError).toHaveBeenCalledWith(testError);
393
+ });
394
+
395
+ it('requires onError callback to be provided', async () => {
396
+ // onError is now required - this test verifies the type signature enforces it
397
+ // TypeScript would catch this at compile time, but runtime behavior should still work
398
+ const onError = vi.fn();
399
+ await watchService.watch(mockFileStore, 1000, undefined, onError);
400
+
401
+ const watcher = mockWatchers[0];
402
+ const errorHandler = (watcher?.on as ReturnType<typeof vi.fn>).mock.calls.find(
403
+ (call: unknown[]) => call[0] === 'error'
404
+ )?.[1] as ((error: Error) => void) | undefined;
405
+
406
+ const testError = new Error('Watcher error');
407
+ errorHandler?.(testError);
389
408
 
390
- consoleErrorSpy.mockRestore();
409
+ // Error should be passed to onError, not thrown
410
+ expect(onError).toHaveBeenCalledWith(testError);
391
411
  });
392
412
 
393
413
  it('continues watching after error during reindex', async () => {
394
- const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
414
+ const onError = vi.fn();
395
415
 
396
416
  // First call fails
397
417
  mockIndexService.indexStore = vi
@@ -399,7 +419,7 @@ describe('WatchService', () => {
399
419
  .mockRejectedValueOnce(new Error('First fail'))
400
420
  .mockResolvedValueOnce({ ok: true });
401
421
 
402
- await watchService.watch(mockFileStore);
422
+ await watchService.watch(mockFileStore, 1000, undefined, onError);
403
423
 
404
424
  const watcher = mockWatchers[0];
405
425
  const allHandler = (watcher?.on as ReturnType<typeof vi.fn>).mock.calls.find(
@@ -417,15 +437,15 @@ describe('WatchService', () => {
417
437
  await vi.runAllTimersAsync();
418
438
 
419
439
  expect(mockIndexService.indexStore).toHaveBeenCalledTimes(2);
420
- expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
421
-
422
- consoleErrorSpy.mockRestore();
440
+ expect(onError).toHaveBeenCalledTimes(1);
423
441
  });
424
442
  });
425
443
 
426
444
  describe('unwatch', () => {
445
+ const noopErrorHandler = (): void => {};
446
+
427
447
  it('stops watching a store', async () => {
428
- await watchService.watch(mockFileStore);
448
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
429
449
 
430
450
  const watcher = mockWatchers[0];
431
451
  await watchService.unwatch(mockFileStore.id);
@@ -436,11 +456,11 @@ describe('WatchService', () => {
436
456
  it('removes watcher from internal map', async () => {
437
457
  const { watch } = await import('chokidar');
438
458
 
439
- await watchService.watch(mockFileStore);
459
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
440
460
  await watchService.unwatch(mockFileStore.id);
441
461
 
442
462
  // Trying to watch again should create new watcher
443
- await watchService.watch(mockFileStore);
463
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
444
464
 
445
465
  expect(watch).toHaveBeenCalledTimes(2);
446
466
  });
@@ -451,8 +471,8 @@ describe('WatchService', () => {
451
471
  });
452
472
 
453
473
  it('does not affect other watchers', async () => {
454
- await watchService.watch(mockFileStore);
455
- await watchService.watch(mockRepoStore);
474
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
475
+ await watchService.watch(mockRepoStore, 1000, undefined, noopErrorHandler);
456
476
 
457
477
  const watcher1 = mockWatchers[0];
458
478
  const watcher2 = mockWatchers[1];
@@ -464,7 +484,7 @@ describe('WatchService', () => {
464
484
  });
465
485
 
466
486
  it('clears pending timeout to prevent timer leak', async () => {
467
- await watchService.watch(mockFileStore, 1000);
487
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
468
488
 
469
489
  const watcher = mockWatchers[0];
470
490
  const allHandler = (watcher?.on as ReturnType<typeof vi.fn>).mock.calls.find(
@@ -487,9 +507,11 @@ describe('WatchService', () => {
487
507
  });
488
508
 
489
509
  describe('unwatchAll', () => {
510
+ const noopErrorHandler = (): void => {};
511
+
490
512
  it('stops all watchers', async () => {
491
- await watchService.watch(mockFileStore);
492
- await watchService.watch(mockRepoStore);
513
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
514
+ await watchService.watch(mockRepoStore, 1000, undefined, noopErrorHandler);
493
515
 
494
516
  const watcher1 = mockWatchers[0];
495
517
  const watcher2 = mockWatchers[1];
@@ -503,13 +525,13 @@ describe('WatchService', () => {
503
525
  it('clears all watchers from map', async () => {
504
526
  const { watch } = await import('chokidar');
505
527
 
506
- await watchService.watch(mockFileStore);
507
- await watchService.watch(mockRepoStore);
528
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
529
+ await watchService.watch(mockRepoStore, 1000, undefined, noopErrorHandler);
508
530
 
509
531
  await watchService.unwatchAll();
510
532
 
511
533
  // Watching again should create new watchers
512
- await watchService.watch(mockFileStore);
534
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
513
535
 
514
536
  expect(watch).toHaveBeenCalledTimes(3); // 2 initial + 1 after unwatchAll
515
537
  });
@@ -521,10 +543,12 @@ describe('WatchService', () => {
521
543
  });
522
544
 
523
545
  describe('File Watching Configuration', () => {
546
+ const noopErrorHandler = (): void => {};
547
+
524
548
  it('ignores .git directories', async () => {
525
549
  const { watch } = await import('chokidar');
526
550
 
527
- await watchService.watch(mockFileStore);
551
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
528
552
 
529
553
  const config = (watch as ReturnType<typeof vi.fn>).mock.calls[0]?.[1];
530
554
  expect(config.ignored).toBeInstanceOf(RegExp);
@@ -535,7 +559,7 @@ describe('WatchService', () => {
535
559
  it('ignores node_modules directories', async () => {
536
560
  const { watch } = await import('chokidar');
537
561
 
538
- await watchService.watch(mockFileStore);
562
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
539
563
 
540
564
  const config = (watch as ReturnType<typeof vi.fn>).mock.calls[0]?.[1];
541
565
  expect(config.ignored.test('.node_modules')).toBe(true);
@@ -545,7 +569,7 @@ describe('WatchService', () => {
545
569
  it('ignores dist and build directories', async () => {
546
570
  const { watch } = await import('chokidar');
547
571
 
548
- await watchService.watch(mockFileStore);
572
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
549
573
 
550
574
  const config = (watch as ReturnType<typeof vi.fn>).mock.calls[0]?.[1];
551
575
  expect(config.ignored.test('.dist')).toBe(true);
@@ -555,7 +579,7 @@ describe('WatchService', () => {
555
579
  it('sets persistent to true', async () => {
556
580
  const { watch } = await import('chokidar');
557
581
 
558
- await watchService.watch(mockFileStore);
582
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
559
583
 
560
584
  const config = (watch as ReturnType<typeof vi.fn>).mock.calls[0]?.[1];
561
585
  expect(config.persistent).toBe(true);
@@ -564,7 +588,7 @@ describe('WatchService', () => {
564
588
  it('sets ignoreInitial to true', async () => {
565
589
  const { watch } = await import('chokidar');
566
590
 
567
- await watchService.watch(mockFileStore);
591
+ await watchService.watch(mockFileStore, 1000, undefined, noopErrorHandler);
568
592
 
569
593
  const config = (watch as ReturnType<typeof vi.fn>).mock.calls[0]?.[1];
570
594
  expect(config.ignoreInitial).toBe(true);
@@ -16,8 +16,9 @@ export class WatchService {
16
16
 
17
17
  async watch(
18
18
  store: FileStore | RepoStore,
19
- debounceMs = 1000,
20
- onReindex?: () => void
19
+ debounceMs: number,
20
+ onReindex: (() => void) | undefined,
21
+ onError: (error: Error) => void
21
22
  ): Promise<void> {
22
23
  if (this.watchers.has(store.id)) {
23
24
  return Promise.resolve(); // Already watching
@@ -40,8 +41,9 @@ export class WatchService {
40
41
  await this.lanceStore.initialize(store.id);
41
42
  await this.indexService.indexStore(store);
42
43
  onReindex?.();
43
- } catch (error) {
44
- console.error('Error during reindexing:', error);
44
+ } catch (e) {
45
+ const error = e instanceof Error ? e : new Error(String(e));
46
+ onError(error);
45
47
  }
46
48
  })();
47
49
  }, debounceMs);
@@ -50,8 +52,9 @@ export class WatchService {
50
52
 
51
53
  watcher.on('all', reindexHandler);
52
54
 
53
- watcher.on('error', (error) => {
54
- console.error('Watcher error:', error);
55
+ watcher.on('error', (e) => {
56
+ const error = e instanceof Error ? e : new Error(String(e));
57
+ onError(error);
55
58
  });
56
59
 
57
60
  this.watchers.set(store.id, watcher);