@holoscript/std 6.0.3 → 7.0.0

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 (69) hide show
  1. package/dist/{chunk-7HVUYGPS.js → chunk-DZHAVOCH.js} +12 -5
  2. package/dist/chunk-DZHAVOCH.js.map +1 -0
  3. package/dist/chunk-PR4QN5HX.js +43 -0
  4. package/dist/chunk-PR4QN5HX.js.map +1 -0
  5. package/dist/chunk-XBA4ARST.js +9313 -0
  6. package/dist/chunk-XBA4ARST.js.map +1 -0
  7. package/dist/{chunk-W2Q3LUCM.js → chunk-XJIFG7G3.js} +3 -3
  8. package/dist/chunk-XJIFG7G3.js.map +1 -0
  9. package/dist/collections.js +1 -0
  10. package/dist/fs/__tests__/file-watch-system.test.d.ts +2 -0
  11. package/dist/fs/__tests__/file-watch-system.test.d.ts.map +1 -0
  12. package/dist/fs/__tests__/path-utilities.test.d.ts +2 -0
  13. package/dist/fs/__tests__/path-utilities.test.d.ts.map +1 -0
  14. package/dist/fs/__tests__/path.test.d.ts +2 -0
  15. package/dist/fs/__tests__/path.test.d.ts.map +1 -0
  16. package/dist/fs/fs.d.ts +288 -0
  17. package/dist/fs/fs.d.ts.map +1 -0
  18. package/dist/fs/index.cjs +9338 -0
  19. package/dist/fs/index.cjs.map +1 -0
  20. package/dist/fs/index.d.ts +51 -0
  21. package/dist/fs/index.d.ts.map +1 -0
  22. package/dist/fs/index.js +204 -0
  23. package/dist/fs/index.js.map +1 -0
  24. package/dist/fs/path.d.ts +135 -0
  25. package/dist/fs/path.d.ts.map +1 -0
  26. package/dist/fs/watch.d.ts +133 -0
  27. package/dist/fs/watch.d.ts.map +1 -0
  28. package/dist/index.cjs +12660 -3435
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +8 -3
  33. package/dist/index.js.map +1 -1
  34. package/dist/math.cjs +6 -2
  35. package/dist/math.cjs.map +1 -1
  36. package/dist/math.js +2 -1
  37. package/dist/string.cjs +2 -2
  38. package/dist/string.cjs.map +1 -1
  39. package/dist/string.js +2 -1
  40. package/dist/time.js +1 -0
  41. package/dist/traits/EconomicPrimitives.js +1 -0
  42. package/dist/traits/EconomicTraits.js +1 -0
  43. package/dist/traits/VRRTraits.d.ts +2 -2
  44. package/dist/types.d.ts.map +1 -1
  45. package/package.json +7 -2
  46. package/src/__tests__/index.test.ts +8 -8
  47. package/src/__tests__/standard-library-fundamentals.test.ts +21 -4
  48. package/src/__tests__/string-decoupled.test.ts +6 -7
  49. package/src/fs/__tests__/file-watch-system.test.ts +675 -0
  50. package/src/fs/__tests__/path-utilities.test.ts +375 -0
  51. package/src/fs/__tests__/path.test.ts +287 -0
  52. package/src/fs/fs.ts +692 -0
  53. package/src/fs/index.ts +194 -0
  54. package/src/fs/path.ts +310 -0
  55. package/src/fs/watch.ts +413 -0
  56. package/src/index.ts +3 -1
  57. package/src/math.ts +1 -1
  58. package/src/string.ts +2 -2
  59. package/src/traits/ARTraits.ts +2 -2
  60. package/src/traits/IoTTraits.ts +2 -2
  61. package/src/traits/VRRTraits.ts +2 -2
  62. package/src/traits/__tests__/ARTraits.test.ts +3 -9
  63. package/src/traits/__tests__/EconomicPrimitives.test.ts +7 -7
  64. package/src/traits/__tests__/EconomicTraits.test.ts +9 -7
  65. package/src/types.ts +10 -3
  66. package/tsup.config.ts +3 -0
  67. package/CHANGELOG.md +0 -7
  68. package/dist/chunk-7HVUYGPS.js.map +0 -1
  69. package/dist/chunk-W2Q3LUCM.js.map +0 -1
@@ -0,0 +1,675 @@
1
+ /**
2
+ * @holoscript/fs watch module acceptance tests
3
+ * Covers: FileWatcher class, watch utilities (watchCallback, watchFileTypes,
4
+ * watchFile, watchDebounced, watchOnce, watchBatched, watchFiltered,
5
+ * watchEvents, watchFiles, watchDirs), event handling, debouncing
6
+ */
7
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
8
+ import {
9
+ FileWatcher,
10
+ watch,
11
+ watchCallback,
12
+ watchFileTypes,
13
+ watchFile,
14
+ watchDebounced,
15
+ watchOnce,
16
+ watchBatched,
17
+ watchFiltered,
18
+ watchEvents,
19
+ watchFiles,
20
+ watchDirs,
21
+ type WatchEvent,
22
+ } from '../watch.js';
23
+
24
+ // Mock chokidar to avoid filesystem operations in tests
25
+ vi.mock('chokidar', () => {
26
+ const mockWatcher = {
27
+ on: vi.fn().mockReturnThis(),
28
+ close: vi.fn().mockResolvedValue(undefined),
29
+ add: vi.fn().mockReturnThis(),
30
+ unwatch: vi.fn().mockReturnThis(),
31
+ getWatched: vi.fn().mockReturnValue({}),
32
+ };
33
+
34
+ return {
35
+ default: {
36
+ watch: vi.fn(() => mockWatcher),
37
+ },
38
+ };
39
+ });
40
+
41
+ // ═══════════════════════════════════════════════
42
+ // FileWatcher — constructor and basic methods
43
+ // ═══════════════════════════════════════════════
44
+ describe('FileWatcher — constructor', () => {
45
+ it('creates instance with single path', () => {
46
+ const watcher = new FileWatcher('/test/path');
47
+ expect(watcher).toBeInstanceOf(FileWatcher);
48
+ });
49
+
50
+ it('creates instance with array of paths', () => {
51
+ const watcher = new FileWatcher(['/path1', '/path2']);
52
+ expect(watcher).toBeInstanceOf(FileWatcher);
53
+ });
54
+
55
+ it('creates instance with options', () => {
56
+ const watcher = new FileWatcher('/test', {
57
+ ignored: '**/*.log',
58
+ usePolling: true,
59
+ interval: 200,
60
+ });
61
+ expect(watcher).toBeInstanceOf(FileWatcher);
62
+ });
63
+
64
+ it('creates instance with emitInitial false', () => {
65
+ const watcher = new FileWatcher('/test', { emitInitial: false });
66
+ expect(watcher).toBeInstanceOf(FileWatcher);
67
+ });
68
+
69
+ it('creates instance with depth option', () => {
70
+ const watcher = new FileWatcher('/test', { depth: 2 });
71
+ expect(watcher).toBeInstanceOf(FileWatcher);
72
+ });
73
+
74
+ it('creates instance with debounce option', () => {
75
+ const watcher = new FileWatcher('/test', { debounce: 500 });
76
+ expect(watcher).toBeInstanceOf(FileWatcher);
77
+ });
78
+ });
79
+
80
+ describe('FileWatcher — start()', () => {
81
+ let watcher: FileWatcher;
82
+
83
+ beforeEach(() => {
84
+ watcher = new FileWatcher('/test');
85
+ });
86
+
87
+ afterEach(async () => {
88
+ await watcher.stop();
89
+ });
90
+
91
+ it('is a method', () => {
92
+ expect(typeof watcher.start).toBe('function');
93
+ });
94
+
95
+ it('returns this for chaining', () => {
96
+ const result = watcher.start();
97
+ expect(result).toBe(watcher);
98
+ });
99
+
100
+ it('start() is idempotent (calling twice returns this)', () => {
101
+ watcher.start();
102
+ const result = watcher.start();
103
+ expect(result).toBe(watcher);
104
+ });
105
+ });
106
+
107
+ describe('FileWatcher — stop()', () => {
108
+ it('is a method', () => {
109
+ const watcher = new FileWatcher('/test');
110
+ expect(typeof watcher.stop).toBe('function');
111
+ });
112
+
113
+ it('returns a Promise', async () => {
114
+ const watcher = new FileWatcher('/test').start();
115
+ const result = watcher.stop();
116
+ expect(result).toBeInstanceOf(Promise);
117
+ await result;
118
+ });
119
+
120
+ it('can be called without starting', async () => {
121
+ const watcher = new FileWatcher('/test');
122
+ await expect(watcher.stop()).resolves.toBeUndefined();
123
+ });
124
+ });
125
+
126
+ describe('FileWatcher — add()', () => {
127
+ let watcher: FileWatcher;
128
+
129
+ beforeEach(() => {
130
+ watcher = new FileWatcher('/test').start();
131
+ });
132
+
133
+ afterEach(async () => {
134
+ await watcher.stop();
135
+ });
136
+
137
+ it('is a method', () => {
138
+ expect(typeof watcher.add).toBe('function');
139
+ });
140
+
141
+ it('accepts a single path', () => {
142
+ const result = watcher.add('/new/path');
143
+ expect(result).toBe(watcher);
144
+ });
145
+
146
+ it('accepts an array of paths', () => {
147
+ const result = watcher.add(['/path1', '/path2']);
148
+ expect(result).toBe(watcher);
149
+ });
150
+
151
+ it('returns this for chaining', () => {
152
+ const result = watcher.add('/new/path');
153
+ expect(result).toBe(watcher);
154
+ });
155
+ });
156
+
157
+ describe('FileWatcher — unwatch()', () => {
158
+ let watcher: FileWatcher;
159
+
160
+ beforeEach(() => {
161
+ watcher = new FileWatcher(['/test1', '/test2']).start();
162
+ });
163
+
164
+ afterEach(async () => {
165
+ await watcher.stop();
166
+ });
167
+
168
+ it('is a method', () => {
169
+ expect(typeof watcher.unwatch).toBe('function');
170
+ });
171
+
172
+ it('accepts a single path', () => {
173
+ const result = watcher.unwatch('/test1');
174
+ expect(result).toBe(watcher);
175
+ });
176
+
177
+ it('accepts an array of paths', () => {
178
+ const result = watcher.unwatch(['/test1', '/test2']);
179
+ expect(result).toBe(watcher);
180
+ });
181
+
182
+ it('returns this for chaining', () => {
183
+ const result = watcher.unwatch('/test1');
184
+ expect(result).toBe(watcher);
185
+ });
186
+ });
187
+
188
+ describe('FileWatcher — getWatched()', () => {
189
+ it('is a method', () => {
190
+ const watcher = new FileWatcher('/test');
191
+ expect(typeof watcher.getWatched).toBe('function');
192
+ });
193
+
194
+ it('returns an object', () => {
195
+ const watcher = new FileWatcher('/test').start();
196
+ const watched = watcher.getWatched();
197
+ expect(typeof watched).toBe('object');
198
+ });
199
+
200
+ it('returns empty object when not started', () => {
201
+ const watcher = new FileWatcher('/test');
202
+ const watched = watcher.getWatched();
203
+ expect(watched).toEqual({});
204
+ });
205
+ });
206
+
207
+ describe('FileWatcher — isReady', () => {
208
+ it('is a getter', () => {
209
+ const watcher = new FileWatcher('/test');
210
+ expect(typeof watcher.isReady).toBe('boolean');
211
+ });
212
+
213
+ it('returns false before start()', () => {
214
+ const watcher = new FileWatcher('/test');
215
+ expect(watcher.isReady).toBe(false);
216
+ });
217
+
218
+ it('returns true after start()', () => {
219
+ const watcher = new FileWatcher('/test').start();
220
+ expect(watcher.isReady).toBe(true);
221
+ });
222
+ });
223
+
224
+ describe('FileWatcher — on() event handlers', () => {
225
+ let watcher: FileWatcher;
226
+
227
+ beforeEach(() => {
228
+ watcher = new FileWatcher('/test');
229
+ });
230
+
231
+ afterEach(async () => {
232
+ await watcher.stop();
233
+ });
234
+
235
+ it('is a method', () => {
236
+ expect(typeof watcher.on).toBe('function');
237
+ });
238
+
239
+ it('accepts add event', () => {
240
+ const listener = vi.fn();
241
+ const result = watcher.on('add', listener);
242
+ expect(result).toBe(watcher);
243
+ });
244
+
245
+ it('accepts change event', () => {
246
+ const listener = vi.fn();
247
+ const result = watcher.on('change', listener);
248
+ expect(result).toBe(watcher);
249
+ });
250
+
251
+ it('accepts unlink event', () => {
252
+ const listener = vi.fn();
253
+ const result = watcher.on('unlink', listener);
254
+ expect(result).toBe(watcher);
255
+ });
256
+
257
+ it('accepts addDir event', () => {
258
+ const listener = vi.fn();
259
+ const result = watcher.on('addDir', listener);
260
+ expect(result).toBe(watcher);
261
+ });
262
+
263
+ it('accepts unlinkDir event', () => {
264
+ const listener = vi.fn();
265
+ const result = watcher.on('unlinkDir', listener);
266
+ expect(result).toBe(watcher);
267
+ });
268
+
269
+ it('accepts all event', () => {
270
+ const listener = vi.fn();
271
+ const result = watcher.on('all', listener);
272
+ expect(result).toBe(watcher);
273
+ });
274
+
275
+ it('accepts error event', () => {
276
+ const listener = vi.fn();
277
+ const result = watcher.on('error', listener);
278
+ expect(result).toBe(watcher);
279
+ });
280
+
281
+ it('accepts ready event', () => {
282
+ const listener = vi.fn();
283
+ const result = watcher.on('ready', listener);
284
+ expect(result).toBe(watcher);
285
+ });
286
+
287
+ it('returns this for chaining', () => {
288
+ const result = watcher.on('add', () => {});
289
+ expect(result).toBe(watcher);
290
+ });
291
+ });
292
+
293
+ // ═══════════════════════════════════════════════
294
+ // Utility functions
295
+ // ═══════════════════════════════════════════════
296
+ describe('watch() utility', () => {
297
+ it('is a function', () => {
298
+ expect(typeof watch).toBe('function');
299
+ });
300
+
301
+ it('returns FileWatcher instance', () => {
302
+ const watcher = watch('/test');
303
+ expect(watcher).toBeInstanceOf(FileWatcher);
304
+ });
305
+
306
+ it('accepts single path', () => {
307
+ const watcher = watch('/test');
308
+ expect(watcher).toBeInstanceOf(FileWatcher);
309
+ });
310
+
311
+ it('accepts array of paths', () => {
312
+ const watcher = watch(['/path1', '/path2']);
313
+ expect(watcher).toBeInstanceOf(FileWatcher);
314
+ });
315
+
316
+ it('accepts options', () => {
317
+ const watcher = watch('/test', { interval: 200 });
318
+ expect(watcher).toBeInstanceOf(FileWatcher);
319
+ });
320
+
321
+ it('watcher is already started', () => {
322
+ const watcher = watch('/test');
323
+ expect(watcher.isReady).toBe(true);
324
+ });
325
+ });
326
+
327
+ describe('watchCallback() utility', () => {
328
+ it('is a function', () => {
329
+ expect(typeof watchCallback).toBe('function');
330
+ });
331
+
332
+ it('returns FileWatcher instance', () => {
333
+ const watcher = watchCallback('/test', () => {});
334
+ expect(watcher).toBeInstanceOf(FileWatcher);
335
+ });
336
+
337
+ it('accepts single path and callback', () => {
338
+ const cb = vi.fn();
339
+ const watcher = watchCallback('/test', cb);
340
+ expect(watcher).toBeInstanceOf(FileWatcher);
341
+ });
342
+
343
+ it('accepts array of paths', () => {
344
+ const watcher = watchCallback(['/path1', '/path2'], () => {});
345
+ expect(watcher).toBeInstanceOf(FileWatcher);
346
+ });
347
+
348
+ it('accepts options', () => {
349
+ const watcher = watchCallback('/test', () => {}, { interval: 300 });
350
+ expect(watcher).toBeInstanceOf(FileWatcher);
351
+ });
352
+ });
353
+
354
+ describe('watchFileTypes() utility', () => {
355
+ it('is a function', () => {
356
+ expect(typeof watchFileTypes).toBe('function');
357
+ });
358
+
359
+ it('returns FileWatcher instance', () => {
360
+ const watcher = watchFileTypes('/dir', ['.ts'], () => {});
361
+ expect(watcher).toBeInstanceOf(FileWatcher);
362
+ });
363
+
364
+ it('accepts extensions without dot', () => {
365
+ const watcher = watchFileTypes('/dir', ['ts', 'js'], () => {});
366
+ expect(watcher).toBeInstanceOf(FileWatcher);
367
+ });
368
+
369
+ it('accepts extensions with dot', () => {
370
+ const watcher = watchFileTypes('/dir', ['.ts', '.js'], () => {});
371
+ expect(watcher).toBeInstanceOf(FileWatcher);
372
+ });
373
+
374
+ it('accepts options', () => {
375
+ const watcher = watchFileTypes('/dir', ['.ts'], () => {}, { depth: 2 });
376
+ expect(watcher).toBeInstanceOf(FileWatcher);
377
+ });
378
+ });
379
+
380
+ describe('watchFile() utility', () => {
381
+ it('is a function', () => {
382
+ expect(typeof watchFile).toBe('function');
383
+ });
384
+
385
+ it('returns FileWatcher instance', () => {
386
+ const watcher = watchFile('/file.txt', () => {});
387
+ expect(watcher).toBeInstanceOf(FileWatcher);
388
+ });
389
+
390
+ it('accepts path and callback', () => {
391
+ const cb = vi.fn();
392
+ const watcher = watchFile('/file.txt', cb);
393
+ expect(watcher).toBeInstanceOf(FileWatcher);
394
+ });
395
+
396
+ it('accepts options', () => {
397
+ const watcher = watchFile('/file.txt', () => {}, { persistent: true });
398
+ expect(watcher).toBeInstanceOf(FileWatcher);
399
+ });
400
+ });
401
+
402
+ describe('watchDebounced() utility', () => {
403
+ it('is a function', () => {
404
+ expect(typeof watchDebounced).toBe('function');
405
+ });
406
+
407
+ it('returns FileWatcher instance', () => {
408
+ const watcher = watchDebounced('/test', () => {});
409
+ expect(watcher).toBeInstanceOf(FileWatcher);
410
+ });
411
+
412
+ it('accepts paths and callback', () => {
413
+ const cb = vi.fn();
414
+ const watcher = watchDebounced('/test', cb);
415
+ expect(watcher).toBeInstanceOf(FileWatcher);
416
+ });
417
+
418
+ it('accepts custom debounce duration', () => {
419
+ const watcher = watchDebounced('/test', () => {}, 500);
420
+ expect(watcher).toBeInstanceOf(FileWatcher);
421
+ });
422
+
423
+ it('accepts options', () => {
424
+ const watcher = watchDebounced('/test', () => {}, 300, { depth: 1 });
425
+ expect(watcher).toBeInstanceOf(FileWatcher);
426
+ });
427
+ });
428
+
429
+ describe('watchOnce() utility', () => {
430
+ beforeEach(() => {
431
+ vi.useFakeTimers();
432
+ });
433
+
434
+ afterEach(() => {
435
+ vi.useRealTimers();
436
+ });
437
+
438
+ it('is a function', () => {
439
+ expect(typeof watchOnce).toBe('function');
440
+ });
441
+
442
+ it('returns a Promise', () => {
443
+ const result = watchOnce('/test');
444
+ expect(result).toBeInstanceOf(Promise);
445
+ // Clean up - the promise won't resolve in test env
446
+ result.catch(() => {});
447
+ });
448
+
449
+ it('accepts single path', () => {
450
+ const result = watchOnce('/test');
451
+ expect(result).toBeInstanceOf(Promise);
452
+ result.catch(() => {});
453
+ });
454
+
455
+ it('accepts array of paths', () => {
456
+ const result = watchOnce(['/path1', '/path2']);
457
+ expect(result).toBeInstanceOf(Promise);
458
+ result.catch(() => {});
459
+ });
460
+
461
+ it('accepts options', () => {
462
+ const result = watchOnce('/test', { persistent: false });
463
+ expect(result).toBeInstanceOf(Promise);
464
+ result.catch(() => {});
465
+ });
466
+ });
467
+
468
+ describe('watchBatched() utility', () => {
469
+ it('is a function', () => {
470
+ expect(typeof watchBatched).toBe('function');
471
+ });
472
+
473
+ it('returns FileWatcher instance', () => {
474
+ const watcher = watchBatched('/test', () => {});
475
+ expect(watcher).toBeInstanceOf(FileWatcher);
476
+ });
477
+
478
+ it('accepts paths and callback', () => {
479
+ const cb = vi.fn();
480
+ const watcher = watchBatched('/test', cb);
481
+ expect(watcher).toBeInstanceOf(FileWatcher);
482
+ });
483
+
484
+ it('accepts custom batch duration', () => {
485
+ const watcher = watchBatched('/test', () => {}, 200);
486
+ expect(watcher).toBeInstanceOf(FileWatcher);
487
+ });
488
+
489
+ it('accepts options', () => {
490
+ const watcher = watchBatched('/test', () => {}, 100, { depth: 3 });
491
+ expect(watcher).toBeInstanceOf(FileWatcher);
492
+ });
493
+
494
+ it('callback receives array of events', () => {
495
+ const cb = vi.fn();
496
+ watchBatched('/test', cb);
497
+ // Callback signature expects WatchEvent[]
498
+ expect(typeof cb).toBe('function');
499
+ });
500
+ });
501
+
502
+ describe('watchFiltered() utility', () => {
503
+ it('is a function', () => {
504
+ expect(typeof watchFiltered).toBe('function');
505
+ });
506
+
507
+ it('returns FileWatcher instance', () => {
508
+ const filter = () => true;
509
+ const watcher = watchFiltered('/test', filter, () => {});
510
+ expect(watcher).toBeInstanceOf(FileWatcher);
511
+ });
512
+
513
+ it('accepts paths, filter, and callback', () => {
514
+ const filter = vi.fn(() => true);
515
+ const callback = vi.fn();
516
+ const watcher = watchFiltered('/test', filter, callback);
517
+ expect(watcher).toBeInstanceOf(FileWatcher);
518
+ });
519
+
520
+ it('accepts options', () => {
521
+ const filter = () => true;
522
+ const watcher = watchFiltered('/test', filter, () => {}, { interval: 150 });
523
+ expect(watcher).toBeInstanceOf(FileWatcher);
524
+ });
525
+ });
526
+
527
+ describe('watchEvents() utility', () => {
528
+ it('is a function', () => {
529
+ expect(typeof watchEvents).toBe('function');
530
+ });
531
+
532
+ it('returns FileWatcher instance', () => {
533
+ const watcher = watchEvents('/test', ['add'], () => {});
534
+ expect(watcher).toBeInstanceOf(FileWatcher);
535
+ });
536
+
537
+ it('accepts paths, event types array, and callback', () => {
538
+ const cb = vi.fn();
539
+ const watcher = watchEvents('/test', ['add', 'change'], cb);
540
+ expect(watcher).toBeInstanceOf(FileWatcher);
541
+ });
542
+
543
+ it('accepts single event type', () => {
544
+ const watcher = watchEvents('/test', ['unlink'], () => {});
545
+ expect(watcher).toBeInstanceOf(FileWatcher);
546
+ });
547
+
548
+ it('accepts multiple event types', () => {
549
+ const watcher = watchEvents('/test', ['add', 'change', 'unlink'], () => {});
550
+ expect(watcher).toBeInstanceOf(FileWatcher);
551
+ });
552
+
553
+ it('accepts options', () => {
554
+ const watcher = watchEvents('/test', ['add'], () => {}, { persistent: false });
555
+ expect(watcher).toBeInstanceOf(FileWatcher);
556
+ });
557
+ });
558
+
559
+ describe('watchFiles() utility', () => {
560
+ it('is a function', () => {
561
+ expect(typeof watchFiles).toBe('function');
562
+ });
563
+
564
+ it('returns FileWatcher instance', () => {
565
+ const watcher = watchFiles('/test', () => {});
566
+ expect(watcher).toBeInstanceOf(FileWatcher);
567
+ });
568
+
569
+ it('accepts paths and callback', () => {
570
+ const cb = vi.fn();
571
+ const watcher = watchFiles('/test', cb);
572
+ expect(watcher).toBeInstanceOf(FileWatcher);
573
+ });
574
+
575
+ it('accepts options', () => {
576
+ const watcher = watchFiles('/test', () => {}, { depth: 5 });
577
+ expect(watcher).toBeInstanceOf(FileWatcher);
578
+ });
579
+ });
580
+
581
+ describe('watchDirs() utility', () => {
582
+ it('is a function', () => {
583
+ expect(typeof watchDirs).toBe('function');
584
+ });
585
+
586
+ it('returns FileWatcher instance', () => {
587
+ const watcher = watchDirs('/test', () => {});
588
+ expect(watcher).toBeInstanceOf(FileWatcher);
589
+ });
590
+
591
+ it('accepts paths and callback', () => {
592
+ const cb = vi.fn();
593
+ const watcher = watchDirs('/test', cb);
594
+ expect(watcher).toBeInstanceOf(FileWatcher);
595
+ });
596
+
597
+ it('accepts options', () => {
598
+ const watcher = watchDirs('/test', () => {}, { usePolling: true });
599
+ expect(watcher).toBeInstanceOf(FileWatcher);
600
+ });
601
+ });
602
+
603
+ // ═══════════════════════════════════════════════
604
+ // Integration scenarios (structure/type validation)
605
+ // ═══════════════════════════════════════════════
606
+ describe('watch module integration scenarios', () => {
607
+ it('FileWatcher can chain start().add().on()', () => {
608
+ const watcher = new FileWatcher('/test');
609
+ const result = watcher
610
+ .start()
611
+ .add('/new')
612
+ .on('add', () => {});
613
+ expect(result).toBe(watcher);
614
+ });
615
+
616
+ it('watch utility creates started watcher', () => {
617
+ const watcher = watch('/test');
618
+ expect(watcher.isReady).toBe(true);
619
+ });
620
+
621
+ it('watchCallback registers all event listener', () => {
622
+ const cb = vi.fn();
623
+ const watcher = watchCallback('/test', cb);
624
+ expect(watcher).toBeInstanceOf(FileWatcher);
625
+ });
626
+
627
+ it('watchFileTypes constructs glob patterns for extensions', () => {
628
+ const cb = vi.fn();
629
+ const watcher = watchFileTypes('/src', ['ts', 'js'], cb);
630
+ expect(watcher).toBeInstanceOf(FileWatcher);
631
+ });
632
+
633
+ it('watchFile sets depth: 0 to watch single file only', () => {
634
+ const cb = vi.fn();
635
+ const watcher = watchFile('/file.txt', cb);
636
+ expect(watcher).toBeInstanceOf(FileWatcher);
637
+ });
638
+
639
+ it('watchDebounced applies debounce option', () => {
640
+ const cb = vi.fn();
641
+ const watcher = watchDebounced('/test', cb, 500);
642
+ expect(watcher).toBeInstanceOf(FileWatcher);
643
+ });
644
+
645
+ it('watchBatched collects events in batches', () => {
646
+ const cb = vi.fn();
647
+ const watcher = watchBatched('/test', cb, 100);
648
+ expect(watcher).toBeInstanceOf(FileWatcher);
649
+ });
650
+
651
+ it('watchFiltered applies filter predicate', () => {
652
+ const filter = (event: WatchEvent) => event.type === 'change';
653
+ const cb = vi.fn();
654
+ const watcher = watchFiltered('/test', filter, cb);
655
+ expect(watcher).toBeInstanceOf(FileWatcher);
656
+ });
657
+
658
+ it('watchEvents filters by event type array', () => {
659
+ const cb = vi.fn();
660
+ const watcher = watchEvents('/test', ['add', 'change'], cb);
661
+ expect(watcher).toBeInstanceOf(FileWatcher);
662
+ });
663
+
664
+ it('watchFiles watches only file events (add/change/unlink)', () => {
665
+ const cb = vi.fn();
666
+ const watcher = watchFiles('/test', cb);
667
+ expect(watcher).toBeInstanceOf(FileWatcher);
668
+ });
669
+
670
+ it('watchDirs watches only directory events (addDir/unlinkDir)', () => {
671
+ const cb = vi.fn();
672
+ const watcher = watchDirs('/test', cb);
673
+ expect(watcher).toBeInstanceOf(FileWatcher);
674
+ });
675
+ });