@componentor/fs 2.0.4 → 2.0.5
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 +3 -1
- package/dist/index.cjs +79 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +79 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -373,6 +373,18 @@ declare class OPFSFileSystem {
|
|
|
373
373
|
* Async purge - clears all kernel caches
|
|
374
374
|
*/
|
|
375
375
|
purge(): Promise<void>;
|
|
376
|
+
/**
|
|
377
|
+
* Release all cached file handles.
|
|
378
|
+
* Call this before expecting external tools (OPFS Explorer, browser console, etc.)
|
|
379
|
+
* to modify files. This allows external access without waiting for the idle timeout.
|
|
380
|
+
* Unlike purge(), this only releases file handles without clearing directory caches.
|
|
381
|
+
*/
|
|
382
|
+
releaseAllHandles(): Promise<void>;
|
|
383
|
+
/**
|
|
384
|
+
* Release a specific file's handle.
|
|
385
|
+
* Use this when you know a specific file needs to be externally modified.
|
|
386
|
+
*/
|
|
387
|
+
releaseHandle(filePath: string): Promise<void>;
|
|
376
388
|
constants: {
|
|
377
389
|
readonly F_OK: 0;
|
|
378
390
|
readonly R_OK: 4;
|
package/dist/index.d.ts
CHANGED
|
@@ -373,6 +373,18 @@ declare class OPFSFileSystem {
|
|
|
373
373
|
* Async purge - clears all kernel caches
|
|
374
374
|
*/
|
|
375
375
|
purge(): Promise<void>;
|
|
376
|
+
/**
|
|
377
|
+
* Release all cached file handles.
|
|
378
|
+
* Call this before expecting external tools (OPFS Explorer, browser console, etc.)
|
|
379
|
+
* to modify files. This allows external access without waiting for the idle timeout.
|
|
380
|
+
* Unlike purge(), this only releases file handles without clearing directory caches.
|
|
381
|
+
*/
|
|
382
|
+
releaseAllHandles(): Promise<void>;
|
|
383
|
+
/**
|
|
384
|
+
* Release a specific file's handle.
|
|
385
|
+
* Use this when you know a specific file needs to be externally modified.
|
|
386
|
+
*/
|
|
387
|
+
releaseHandle(filePath: string): Promise<void>;
|
|
376
388
|
constants: {
|
|
377
389
|
readonly F_OK: 0;
|
|
378
390
|
readonly R_OK: 4;
|
package/dist/index.js
CHANGED
|
@@ -567,6 +567,47 @@ async function handleRead(filePath, payload) {
|
|
|
567
567
|
return { data: buf.slice(0, bytesRead) };
|
|
568
568
|
}
|
|
569
569
|
|
|
570
|
+
// Non-blocking read using getFile() - does NOT lock the file
|
|
571
|
+
// Use this for HMR scenarios where external tools need to modify files
|
|
572
|
+
async function handleReadAsync(filePath, payload) {
|
|
573
|
+
const parts = parsePath(filePath);
|
|
574
|
+
const fileName = parts.pop();
|
|
575
|
+
if (!fileName) throw new Error('Invalid file path');
|
|
576
|
+
const dir = parts.length > 0 ? await getDirectoryHandle(parts, false) : await getRoot();
|
|
577
|
+
const fh = await dir.getFileHandle(fileName);
|
|
578
|
+
const file = await fh.getFile();
|
|
579
|
+
|
|
580
|
+
const offset = payload?.offset || 0;
|
|
581
|
+
const len = payload?.len || (file.size - offset);
|
|
582
|
+
|
|
583
|
+
if (offset === 0 && len === file.size) {
|
|
584
|
+
// Fast path: read entire file
|
|
585
|
+
const buf = new Uint8Array(await file.arrayBuffer());
|
|
586
|
+
return { data: buf };
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Partial read using slice
|
|
590
|
+
const slice = file.slice(offset, offset + len);
|
|
591
|
+
const buf = new Uint8Array(await slice.arrayBuffer());
|
|
592
|
+
return { data: buf };
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Force release a file handle - allows external tools to modify the file
|
|
596
|
+
function handleReleaseHandle(filePath) {
|
|
597
|
+
closeSyncHandle(filePath);
|
|
598
|
+
return { success: true };
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Force release ALL file handles - use before HMR notifications
|
|
602
|
+
function handleReleaseAllHandles() {
|
|
603
|
+
for (const [p, h] of syncHandleCache) {
|
|
604
|
+
try { h.close(); } catch {}
|
|
605
|
+
}
|
|
606
|
+
syncHandleCache.clear();
|
|
607
|
+
syncHandleLastAccess.clear();
|
|
608
|
+
return { success: true };
|
|
609
|
+
}
|
|
610
|
+
|
|
570
611
|
async function handleWrite(filePath, payload) {
|
|
571
612
|
const access = await getSyncHandle(filePath, true);
|
|
572
613
|
if (payload?.data) {
|
|
@@ -776,6 +817,7 @@ async function processMessage(msg) {
|
|
|
776
817
|
const { type, path, payload } = msg;
|
|
777
818
|
switch (type) {
|
|
778
819
|
case 'read': return handleRead(path, payload);
|
|
820
|
+
case 'readAsync': return handleReadAsync(path, payload);
|
|
779
821
|
case 'write': return handleWrite(path, payload);
|
|
780
822
|
case 'append': return handleAppend(path, payload);
|
|
781
823
|
case 'truncate': return handleTruncate(path, payload);
|
|
@@ -789,6 +831,8 @@ async function processMessage(msg) {
|
|
|
789
831
|
case 'copy': return handleCopy(path, payload);
|
|
790
832
|
case 'flush': return handleFlush();
|
|
791
833
|
case 'purge': return handlePurge();
|
|
834
|
+
case 'releaseHandle': return handleReleaseHandle(path);
|
|
835
|
+
case 'releaseAllHandles': return handleReleaseAllHandles();
|
|
792
836
|
default: throw new Error('Unknown operation: ' + type);
|
|
793
837
|
}
|
|
794
838
|
}
|
|
@@ -2029,6 +2073,22 @@ var OPFSFileSystem = class _OPFSFileSystem {
|
|
|
2029
2073
|
await this.fastCall("purge", "/");
|
|
2030
2074
|
this.statCache.clear();
|
|
2031
2075
|
}
|
|
2076
|
+
/**
|
|
2077
|
+
* Release all cached file handles.
|
|
2078
|
+
* Call this before expecting external tools (OPFS Explorer, browser console, etc.)
|
|
2079
|
+
* to modify files. This allows external access without waiting for the idle timeout.
|
|
2080
|
+
* Unlike purge(), this only releases file handles without clearing directory caches.
|
|
2081
|
+
*/
|
|
2082
|
+
async releaseAllHandles() {
|
|
2083
|
+
await this.fastCall("releaseAllHandles", "/");
|
|
2084
|
+
}
|
|
2085
|
+
/**
|
|
2086
|
+
* Release a specific file's handle.
|
|
2087
|
+
* Use this when you know a specific file needs to be externally modified.
|
|
2088
|
+
*/
|
|
2089
|
+
async releaseHandle(filePath) {
|
|
2090
|
+
await this.fastCall("releaseHandle", filePath);
|
|
2091
|
+
}
|
|
2032
2092
|
// Constants
|
|
2033
2093
|
constants = constants;
|
|
2034
2094
|
// --- FileHandle Implementation ---
|
|
@@ -2354,8 +2414,9 @@ var OPFSFileSystem = class _OPFSFileSystem {
|
|
|
2354
2414
|
const self2 = this;
|
|
2355
2415
|
let observer = null;
|
|
2356
2416
|
let closed = false;
|
|
2357
|
-
const callback = (records) => {
|
|
2417
|
+
const callback = async (records) => {
|
|
2358
2418
|
if (closed) return;
|
|
2419
|
+
await self2.releaseAllHandles();
|
|
2359
2420
|
for (const record of records) {
|
|
2360
2421
|
if (record.type === "errored" || record.type === "unknown") continue;
|
|
2361
2422
|
const filename = record.relativePathComponents.length > 0 ? record.relativePathComponents[record.relativePathComponents.length - 1] : basename(absPath);
|
|
@@ -2395,26 +2456,38 @@ var OPFSFileSystem = class _OPFSFileSystem {
|
|
|
2395
2456
|
const entries = await this.promises.readdir(absPath);
|
|
2396
2457
|
const currentEntries = new Set(entries);
|
|
2397
2458
|
if (lastEntries !== null) {
|
|
2459
|
+
let hasChanges = false;
|
|
2460
|
+
const added = [];
|
|
2461
|
+
const removed = [];
|
|
2398
2462
|
for (const entry of currentEntries) {
|
|
2399
2463
|
if (!lastEntries.has(entry)) {
|
|
2400
|
-
|
|
2464
|
+
added.push(entry);
|
|
2465
|
+
hasChanges = true;
|
|
2401
2466
|
}
|
|
2402
2467
|
}
|
|
2403
2468
|
for (const entry of lastEntries) {
|
|
2404
2469
|
if (!currentEntries.has(entry)) {
|
|
2405
|
-
|
|
2470
|
+
removed.push(entry);
|
|
2471
|
+
hasChanges = true;
|
|
2406
2472
|
}
|
|
2407
2473
|
}
|
|
2474
|
+
if (hasChanges) {
|
|
2475
|
+
await this.releaseAllHandles();
|
|
2476
|
+
for (const entry of added) cb?.("rename", entry);
|
|
2477
|
+
for (const entry of removed) cb?.("rename", entry);
|
|
2478
|
+
}
|
|
2408
2479
|
}
|
|
2409
2480
|
lastEntries = currentEntries;
|
|
2410
2481
|
} else {
|
|
2411
2482
|
if (lastMtimeMs !== null && stat.mtimeMs !== lastMtimeMs) {
|
|
2483
|
+
await this.releaseAllHandles();
|
|
2412
2484
|
cb?.("change", basename(absPath));
|
|
2413
2485
|
}
|
|
2414
2486
|
lastMtimeMs = stat.mtimeMs;
|
|
2415
2487
|
}
|
|
2416
2488
|
} catch {
|
|
2417
2489
|
if (lastMtimeMs !== null || lastEntries !== null) {
|
|
2490
|
+
await this.releaseAllHandles();
|
|
2418
2491
|
cb?.("rename", basename(absPath));
|
|
2419
2492
|
lastMtimeMs = null;
|
|
2420
2493
|
lastEntries = null;
|
|
@@ -2448,6 +2521,7 @@ var OPFSFileSystem = class _OPFSFileSystem {
|
|
|
2448
2521
|
const stat = await this.promises.stat(absPath);
|
|
2449
2522
|
if (lastStat !== null) {
|
|
2450
2523
|
if (stat.mtimeMs !== lastStat.mtimeMs || stat.size !== lastStat.size) {
|
|
2524
|
+
await this.releaseAllHandles();
|
|
2451
2525
|
cb?.(stat, lastStat);
|
|
2452
2526
|
}
|
|
2453
2527
|
}
|
|
@@ -2455,6 +2529,7 @@ var OPFSFileSystem = class _OPFSFileSystem {
|
|
|
2455
2529
|
} catch {
|
|
2456
2530
|
const emptyStat = createStats({ type: "file", size: 0, mtimeMs: 0, mode: 0 });
|
|
2457
2531
|
if (lastStat !== null) {
|
|
2532
|
+
await this.releaseAllHandles();
|
|
2458
2533
|
cb?.(emptyStat, lastStat);
|
|
2459
2534
|
}
|
|
2460
2535
|
lastStat = emptyStat;
|
|
@@ -2463,6 +2538,7 @@ var OPFSFileSystem = class _OPFSFileSystem {
|
|
|
2463
2538
|
if (_OPFSFileSystem.hasNativeObserver && cb) {
|
|
2464
2539
|
const self2 = this;
|
|
2465
2540
|
const observerCallback = async () => {
|
|
2541
|
+
await self2.releaseAllHandles();
|
|
2466
2542
|
try {
|
|
2467
2543
|
const stat = await self2.promises.stat(absPath);
|
|
2468
2544
|
if (lastStat !== null && (stat.mtimeMs !== lastStat.mtimeMs || stat.size !== lastStat.size)) {
|