@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/README.md +9 -1
- package/dist/index.cjs +29 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +29 -3
- package/dist/index.js.map +1 -1
- package/dist/kernel.js +1 -1
- package/dist/kernel.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -446,10 +446,18 @@ Another tab or operation has the file open. The library uses `navigator.locks` t
|
|
|
446
446
|
|
|
447
447
|
## Changelog
|
|
448
448
|
|
|
449
|
+
### v2.0.3 (2025)
|
|
450
|
+
|
|
451
|
+
**Bug Fixes:**
|
|
452
|
+
- Handle idle timeout now works for both Tier 1 and Tier 2
|
|
453
|
+
- Previously only Tier 1 kernel had idle release; Tier 2 kernel now also releases handles after 2s
|
|
454
|
+
- Reduced handle idle timeout from 5s to 2s for faster external tool access
|
|
455
|
+
- Added tests verifying handles are properly released after idle timeout
|
|
456
|
+
|
|
449
457
|
### v2.0.2 (2025)
|
|
450
458
|
|
|
451
459
|
**Improvements:**
|
|
452
|
-
- Sync access handles now auto-release after
|
|
460
|
+
- Sync access handles now auto-release after idle timeout
|
|
453
461
|
- Allows external tools (like OPFS Chrome extension) to access files when idle
|
|
454
462
|
- Maintains full performance during active operations
|
|
455
463
|
|
package/dist/index.cjs
CHANGED
|
@@ -431,31 +431,56 @@ let cachedRoot = null;
|
|
|
431
431
|
const dirCache = new Map();
|
|
432
432
|
|
|
433
433
|
// Sync handle cache - MAJOR performance optimization
|
|
434
|
+
// Handles auto-release after idle timeout to allow external tools to access files
|
|
434
435
|
const syncHandleCache = new Map();
|
|
436
|
+
const syncHandleLastAccess = new Map();
|
|
435
437
|
const MAX_HANDLES = 100;
|
|
438
|
+
const HANDLE_IDLE_TIMEOUT = 2000;
|
|
439
|
+
let idleCleanupTimer = null;
|
|
440
|
+
|
|
441
|
+
function scheduleIdleCleanup() {
|
|
442
|
+
if (idleCleanupTimer) return;
|
|
443
|
+
idleCleanupTimer = setTimeout(() => {
|
|
444
|
+
idleCleanupTimer = null;
|
|
445
|
+
const now = Date.now();
|
|
446
|
+
for (const [p, lastAccess] of syncHandleLastAccess) {
|
|
447
|
+
if (now - lastAccess > HANDLE_IDLE_TIMEOUT) {
|
|
448
|
+
const h = syncHandleCache.get(p);
|
|
449
|
+
if (h) { try { h.flush(); h.close(); } catch {} syncHandleCache.delete(p); }
|
|
450
|
+
syncHandleLastAccess.delete(p);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
if (syncHandleCache.size > 0) scheduleIdleCleanup();
|
|
454
|
+
}, HANDLE_IDLE_TIMEOUT);
|
|
455
|
+
}
|
|
436
456
|
|
|
437
457
|
async function getSyncHandle(filePath, create) {
|
|
438
458
|
const cached = syncHandleCache.get(filePath);
|
|
439
|
-
if (cached)
|
|
459
|
+
if (cached) {
|
|
460
|
+
syncHandleLastAccess.set(filePath, Date.now());
|
|
461
|
+
return cached;
|
|
462
|
+
}
|
|
440
463
|
|
|
441
464
|
// Evict oldest handles if cache is full
|
|
442
465
|
if (syncHandleCache.size >= MAX_HANDLES) {
|
|
443
466
|
const keys = Array.from(syncHandleCache.keys()).slice(0, 10);
|
|
444
467
|
for (const key of keys) {
|
|
445
468
|
const h = syncHandleCache.get(key);
|
|
446
|
-
if (h) { try { h.close(); } catch {} syncHandleCache.delete(key); }
|
|
469
|
+
if (h) { try { h.close(); } catch {} syncHandleCache.delete(key); syncHandleLastAccess.delete(key); }
|
|
447
470
|
}
|
|
448
471
|
}
|
|
449
472
|
|
|
450
473
|
const fh = await getFileHandle(filePath, create);
|
|
451
474
|
const access = await fh.createSyncAccessHandle();
|
|
452
475
|
syncHandleCache.set(filePath, access);
|
|
476
|
+
syncHandleLastAccess.set(filePath, Date.now());
|
|
477
|
+
scheduleIdleCleanup();
|
|
453
478
|
return access;
|
|
454
479
|
}
|
|
455
480
|
|
|
456
481
|
function closeSyncHandle(filePath) {
|
|
457
482
|
const h = syncHandleCache.get(filePath);
|
|
458
|
-
if (h) { try { h.close(); } catch {} syncHandleCache.delete(filePath); }
|
|
483
|
+
if (h) { try { h.close(); } catch {} syncHandleCache.delete(filePath); syncHandleLastAccess.delete(filePath); }
|
|
459
484
|
}
|
|
460
485
|
|
|
461
486
|
function closeHandlesUnder(prefix) {
|
|
@@ -463,6 +488,7 @@ function closeHandlesUnder(prefix) {
|
|
|
463
488
|
if (p === prefix || p.startsWith(prefix + '/')) {
|
|
464
489
|
try { h.close(); } catch {}
|
|
465
490
|
syncHandleCache.delete(p);
|
|
491
|
+
syncHandleLastAccess.delete(p);
|
|
466
492
|
}
|
|
467
493
|
}
|
|
468
494
|
}
|