@componentor/fs 2.0.2 → 2.0.4

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.
package/dist/index.js CHANGED
@@ -427,31 +427,56 @@ let cachedRoot = null;
427
427
  const dirCache = new Map();
428
428
 
429
429
  // Sync handle cache - MAJOR performance optimization
430
+ // Handles auto-release after idle timeout to allow external tools to access files
430
431
  const syncHandleCache = new Map();
432
+ const syncHandleLastAccess = new Map();
431
433
  const MAX_HANDLES = 100;
434
+ const HANDLE_IDLE_TIMEOUT = 2000;
435
+ let idleCleanupTimer = null;
436
+
437
+ function scheduleIdleCleanup() {
438
+ if (idleCleanupTimer) return;
439
+ idleCleanupTimer = setTimeout(() => {
440
+ idleCleanupTimer = null;
441
+ const now = Date.now();
442
+ for (const [p, lastAccess] of syncHandleLastAccess) {
443
+ if (now - lastAccess > HANDLE_IDLE_TIMEOUT) {
444
+ const h = syncHandleCache.get(p);
445
+ if (h) { try { h.flush(); h.close(); } catch {} syncHandleCache.delete(p); }
446
+ syncHandleLastAccess.delete(p);
447
+ }
448
+ }
449
+ if (syncHandleCache.size > 0) scheduleIdleCleanup();
450
+ }, HANDLE_IDLE_TIMEOUT);
451
+ }
432
452
 
433
453
  async function getSyncHandle(filePath, create) {
434
454
  const cached = syncHandleCache.get(filePath);
435
- if (cached) return cached;
455
+ if (cached) {
456
+ syncHandleLastAccess.set(filePath, Date.now());
457
+ return cached;
458
+ }
436
459
 
437
460
  // Evict oldest handles if cache is full
438
461
  if (syncHandleCache.size >= MAX_HANDLES) {
439
462
  const keys = Array.from(syncHandleCache.keys()).slice(0, 10);
440
463
  for (const key of keys) {
441
464
  const h = syncHandleCache.get(key);
442
- if (h) { try { h.close(); } catch {} syncHandleCache.delete(key); }
465
+ if (h) { try { h.close(); } catch {} syncHandleCache.delete(key); syncHandleLastAccess.delete(key); }
443
466
  }
444
467
  }
445
468
 
446
469
  const fh = await getFileHandle(filePath, create);
447
470
  const access = await fh.createSyncAccessHandle();
448
471
  syncHandleCache.set(filePath, access);
472
+ syncHandleLastAccess.set(filePath, Date.now());
473
+ scheduleIdleCleanup();
449
474
  return access;
450
475
  }
451
476
 
452
477
  function closeSyncHandle(filePath) {
453
478
  const h = syncHandleCache.get(filePath);
454
- if (h) { try { h.close(); } catch {} syncHandleCache.delete(filePath); }
479
+ if (h) { try { h.close(); } catch {} syncHandleCache.delete(filePath); syncHandleLastAccess.delete(filePath); }
455
480
  }
456
481
 
457
482
  function closeHandlesUnder(prefix) {
@@ -459,6 +484,7 @@ function closeHandlesUnder(prefix) {
459
484
  if (p === prefix || p.startsWith(prefix + '/')) {
460
485
  try { h.close(); } catch {}
461
486
  syncHandleCache.delete(p);
487
+ syncHandleLastAccess.delete(p);
462
488
  }
463
489
  }
464
490
  }