@componentor/fs 1.2.3 → 1.2.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/dist/index.js +214 -87
- package/dist/index.js.map +1 -1
- package/dist/opfs-hybrid.js +214 -87
- package/dist/opfs-hybrid.js.map +1 -1
- package/dist/opfs-worker.js +214 -87
- package/dist/opfs-worker.js.map +1 -1
- package/package.json +1 -1
- package/src/handle-manager.ts +64 -0
- package/src/index.ts +51 -19
- package/src/packed-storage.ts +125 -84
- package/src/symlink-manager.ts +15 -6
package/dist/index.js
CHANGED
|
@@ -145,6 +145,52 @@ function segments(path) {
|
|
|
145
145
|
// src/handle-manager.ts
|
|
146
146
|
var FILE_HANDLE_POOL_SIZE = 50;
|
|
147
147
|
var DIR_CACHE_MAX_SIZE = 200;
|
|
148
|
+
var FileLockManager = class {
|
|
149
|
+
locks = /* @__PURE__ */ new Map();
|
|
150
|
+
lockResolvers = /* @__PURE__ */ new Map();
|
|
151
|
+
waitQueues = /* @__PURE__ */ new Map();
|
|
152
|
+
/**
|
|
153
|
+
* Acquire an exclusive lock on a file path.
|
|
154
|
+
* If the file is already locked, waits until it's released.
|
|
155
|
+
* Returns a release function that MUST be called when done.
|
|
156
|
+
*/
|
|
157
|
+
async acquire(path) {
|
|
158
|
+
const normalizedPath = normalize(path);
|
|
159
|
+
while (this.locks.has(normalizedPath)) {
|
|
160
|
+
await this.locks.get(normalizedPath);
|
|
161
|
+
}
|
|
162
|
+
let resolve;
|
|
163
|
+
const lockPromise = new Promise((r) => {
|
|
164
|
+
resolve = r;
|
|
165
|
+
});
|
|
166
|
+
this.locks.set(normalizedPath, lockPromise);
|
|
167
|
+
this.lockResolvers.set(normalizedPath, resolve);
|
|
168
|
+
return () => {
|
|
169
|
+
const resolver = this.lockResolvers.get(normalizedPath);
|
|
170
|
+
this.locks.delete(normalizedPath);
|
|
171
|
+
this.lockResolvers.delete(normalizedPath);
|
|
172
|
+
if (resolver) resolver();
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Check if a file is currently locked
|
|
177
|
+
*/
|
|
178
|
+
isLocked(path) {
|
|
179
|
+
return this.locks.has(normalize(path));
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Clear all locks (use with caution, mainly for cleanup)
|
|
183
|
+
*/
|
|
184
|
+
clearAll() {
|
|
185
|
+
for (const resolver of this.lockResolvers.values()) {
|
|
186
|
+
resolver();
|
|
187
|
+
}
|
|
188
|
+
this.locks.clear();
|
|
189
|
+
this.lockResolvers.clear();
|
|
190
|
+
this.waitQueues.clear();
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
var fileLockManager = new FileLockManager();
|
|
148
194
|
var HandleManager = class {
|
|
149
195
|
rootPromise;
|
|
150
196
|
dirCache = /* @__PURE__ */ new Map();
|
|
@@ -402,13 +448,21 @@ var SymlinkManager = class {
|
|
|
402
448
|
if (!fileHandle) return;
|
|
403
449
|
const buffer = new TextEncoder().encode(data);
|
|
404
450
|
if (this.useSync) {
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
451
|
+
const releaseLock = await fileLockManager.acquire(SYMLINK_FILE);
|
|
452
|
+
try {
|
|
453
|
+
const access = await fileHandle.createSyncAccessHandle();
|
|
454
|
+
try {
|
|
455
|
+
access.truncate(0);
|
|
456
|
+
let written = 0;
|
|
457
|
+
while (written < buffer.length) {
|
|
458
|
+
written += access.write(buffer.subarray(written), { at: written });
|
|
459
|
+
}
|
|
460
|
+
} finally {
|
|
461
|
+
access.close();
|
|
462
|
+
}
|
|
463
|
+
} finally {
|
|
464
|
+
releaseLock();
|
|
410
465
|
}
|
|
411
|
-
access.close();
|
|
412
466
|
} else {
|
|
413
467
|
const writable = await fileHandle.createWritable();
|
|
414
468
|
await writable.write(buffer);
|
|
@@ -681,6 +735,7 @@ var PackedStorage = class {
|
|
|
681
735
|
/**
|
|
682
736
|
* Load pack index from disk (always reloads to support hybrid mode)
|
|
683
737
|
* Verifies CRC32 checksum for integrity
|
|
738
|
+
* Note: Caller must hold the lock
|
|
684
739
|
*/
|
|
685
740
|
async loadIndex() {
|
|
686
741
|
try {
|
|
@@ -689,29 +744,36 @@ var PackedStorage = class {
|
|
|
689
744
|
return {};
|
|
690
745
|
}
|
|
691
746
|
if (this.useSync) {
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
747
|
+
const releaseLock = await fileLockManager.acquire(PACK_FILE);
|
|
748
|
+
try {
|
|
749
|
+
const access = await fileHandle.createSyncAccessHandle();
|
|
750
|
+
try {
|
|
751
|
+
const size = access.getSize();
|
|
752
|
+
if (size < 8) {
|
|
753
|
+
return {};
|
|
754
|
+
}
|
|
755
|
+
const header = new Uint8Array(8);
|
|
756
|
+
access.read(header, { at: 0 });
|
|
757
|
+
const view = new DataView(header.buffer);
|
|
758
|
+
const indexLen = view.getUint32(0, true);
|
|
759
|
+
const storedCrc = view.getUint32(4, true);
|
|
760
|
+
const contentSize = size - 8;
|
|
761
|
+
const content = new Uint8Array(contentSize);
|
|
762
|
+
access.read(content, { at: 8 });
|
|
763
|
+
if (this.useChecksum && storedCrc !== 0) {
|
|
764
|
+
const calculatedCrc = crc32(content);
|
|
765
|
+
if (calculatedCrc !== storedCrc) {
|
|
766
|
+
throw createECORRUPTED(PACK_FILE);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
const indexJson = new TextDecoder().decode(content.subarray(0, indexLen));
|
|
770
|
+
return JSON.parse(indexJson);
|
|
771
|
+
} finally {
|
|
772
|
+
access.close();
|
|
711
773
|
}
|
|
774
|
+
} finally {
|
|
775
|
+
releaseLock();
|
|
712
776
|
}
|
|
713
|
-
const indexJson = new TextDecoder().decode(content.subarray(0, indexLen));
|
|
714
|
-
return JSON.parse(indexJson);
|
|
715
777
|
} else {
|
|
716
778
|
const file = await fileHandle.getFile();
|
|
717
779
|
const data = new Uint8Array(await file.arrayBuffer());
|
|
@@ -764,10 +826,18 @@ var PackedStorage = class {
|
|
|
764
826
|
if (!fileHandle) return null;
|
|
765
827
|
let buffer;
|
|
766
828
|
if (this.useSync) {
|
|
767
|
-
const
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
829
|
+
const releaseLock = await fileLockManager.acquire(PACK_FILE);
|
|
830
|
+
try {
|
|
831
|
+
const access = await fileHandle.createSyncAccessHandle();
|
|
832
|
+
try {
|
|
833
|
+
buffer = new Uint8Array(entry.size);
|
|
834
|
+
access.read(buffer, { at: entry.offset });
|
|
835
|
+
} finally {
|
|
836
|
+
access.close();
|
|
837
|
+
}
|
|
838
|
+
} finally {
|
|
839
|
+
releaseLock();
|
|
840
|
+
}
|
|
771
841
|
} else {
|
|
772
842
|
const file = await fileHandle.getFile();
|
|
773
843
|
const data = new Uint8Array(await file.arrayBuffer());
|
|
@@ -806,17 +876,25 @@ var PackedStorage = class {
|
|
|
806
876
|
}
|
|
807
877
|
const decompressPromises = [];
|
|
808
878
|
if (this.useSync) {
|
|
809
|
-
const
|
|
810
|
-
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
879
|
+
const releaseLock = await fileLockManager.acquire(PACK_FILE);
|
|
880
|
+
try {
|
|
881
|
+
const access = await fileHandle.createSyncAccessHandle();
|
|
882
|
+
try {
|
|
883
|
+
for (const { path, offset, size, originalSize } of toRead) {
|
|
884
|
+
const buffer = new Uint8Array(size);
|
|
885
|
+
access.read(buffer, { at: offset });
|
|
886
|
+
if (originalSize !== void 0) {
|
|
887
|
+
decompressPromises.push({ path, promise: decompress(buffer) });
|
|
888
|
+
} else {
|
|
889
|
+
results.set(path, buffer);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
} finally {
|
|
893
|
+
access.close();
|
|
817
894
|
}
|
|
895
|
+
} finally {
|
|
896
|
+
releaseLock();
|
|
818
897
|
}
|
|
819
|
-
access.close();
|
|
820
898
|
} else {
|
|
821
899
|
const file = await fileHandle.getFile();
|
|
822
900
|
const data = new Uint8Array(await file.arrayBuffer());
|
|
@@ -897,15 +975,24 @@ var PackedStorage = class {
|
|
|
897
975
|
}
|
|
898
976
|
/**
|
|
899
977
|
* Write the pack file to OPFS
|
|
978
|
+
* Note: Caller must hold the lock
|
|
900
979
|
*/
|
|
901
980
|
async writePackFile(data) {
|
|
902
981
|
const { fileHandle } = await this.handleManager.getHandle(PACK_FILE, { create: true });
|
|
903
982
|
if (!fileHandle) return;
|
|
904
983
|
if (this.useSync) {
|
|
905
|
-
const
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
984
|
+
const releaseLock = await fileLockManager.acquire(PACK_FILE);
|
|
985
|
+
try {
|
|
986
|
+
const access = await fileHandle.createSyncAccessHandle();
|
|
987
|
+
try {
|
|
988
|
+
access.truncate(data.length);
|
|
989
|
+
access.write(data, { at: 0 });
|
|
990
|
+
} finally {
|
|
991
|
+
access.close();
|
|
992
|
+
}
|
|
993
|
+
} finally {
|
|
994
|
+
releaseLock();
|
|
995
|
+
}
|
|
909
996
|
} else {
|
|
910
997
|
const writable = await fileHandle.createWritable();
|
|
911
998
|
await writable.write(data);
|
|
@@ -925,33 +1012,41 @@ var PackedStorage = class {
|
|
|
925
1012
|
const encoder = new TextEncoder();
|
|
926
1013
|
const newIndexBuf = encoder.encode(JSON.stringify(index));
|
|
927
1014
|
if (this.useSync) {
|
|
928
|
-
const
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1015
|
+
const releaseLock = await fileLockManager.acquire(PACK_FILE);
|
|
1016
|
+
try {
|
|
1017
|
+
const access = await fileHandle.createSyncAccessHandle();
|
|
1018
|
+
try {
|
|
1019
|
+
const size = access.getSize();
|
|
1020
|
+
const oldHeader = new Uint8Array(8);
|
|
1021
|
+
access.read(oldHeader, { at: 0 });
|
|
1022
|
+
const oldIndexLen = new DataView(oldHeader.buffer).getUint32(0, true);
|
|
1023
|
+
const dataStart = 8 + oldIndexLen;
|
|
1024
|
+
const dataSize = size - dataStart;
|
|
1025
|
+
const dataPortion = new Uint8Array(dataSize);
|
|
1026
|
+
if (dataSize > 0) {
|
|
1027
|
+
access.read(dataPortion, { at: dataStart });
|
|
1028
|
+
}
|
|
1029
|
+
const newContent = new Uint8Array(newIndexBuf.length + dataSize);
|
|
1030
|
+
newContent.set(newIndexBuf, 0);
|
|
1031
|
+
if (dataSize > 0) {
|
|
1032
|
+
newContent.set(dataPortion, newIndexBuf.length);
|
|
1033
|
+
}
|
|
1034
|
+
const checksum = this.useChecksum ? crc32(newContent) : 0;
|
|
1035
|
+
const newHeader = new Uint8Array(8);
|
|
1036
|
+
const view = new DataView(newHeader.buffer);
|
|
1037
|
+
view.setUint32(0, newIndexBuf.length, true);
|
|
1038
|
+
view.setUint32(4, checksum, true);
|
|
1039
|
+
const newFile = new Uint8Array(8 + newContent.length);
|
|
1040
|
+
newFile.set(newHeader, 0);
|
|
1041
|
+
newFile.set(newContent, 8);
|
|
1042
|
+
access.truncate(newFile.length);
|
|
1043
|
+
access.write(newFile, { at: 0 });
|
|
1044
|
+
} finally {
|
|
1045
|
+
access.close();
|
|
1046
|
+
}
|
|
1047
|
+
} finally {
|
|
1048
|
+
releaseLock();
|
|
943
1049
|
}
|
|
944
|
-
const checksum = this.useChecksum ? crc32(newContent) : 0;
|
|
945
|
-
const newHeader = new Uint8Array(8);
|
|
946
|
-
const view = new DataView(newHeader.buffer);
|
|
947
|
-
view.setUint32(0, newIndexBuf.length, true);
|
|
948
|
-
view.setUint32(4, checksum, true);
|
|
949
|
-
const newFile = new Uint8Array(8 + newContent.length);
|
|
950
|
-
newFile.set(newHeader, 0);
|
|
951
|
-
newFile.set(newContent, 8);
|
|
952
|
-
access.truncate(newFile.length);
|
|
953
|
-
access.write(newFile, { at: 0 });
|
|
954
|
-
access.close();
|
|
955
1050
|
} else {
|
|
956
1051
|
const file = await fileHandle.getFile();
|
|
957
1052
|
const oldData = new Uint8Array(await file.arrayBuffer());
|
|
@@ -1621,11 +1716,19 @@ var OPFS = class {
|
|
|
1621
1716
|
if (fileHandle) {
|
|
1622
1717
|
let buffer;
|
|
1623
1718
|
if (this.useSync) {
|
|
1624
|
-
const
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1719
|
+
const releaseLock = await fileLockManager.acquire(resolvedPath);
|
|
1720
|
+
try {
|
|
1721
|
+
const access = await fileHandle.createSyncAccessHandle();
|
|
1722
|
+
try {
|
|
1723
|
+
const size = access.getSize();
|
|
1724
|
+
buffer = new Uint8Array(size);
|
|
1725
|
+
access.read(buffer);
|
|
1726
|
+
} finally {
|
|
1727
|
+
access.close();
|
|
1728
|
+
}
|
|
1729
|
+
} finally {
|
|
1730
|
+
releaseLock();
|
|
1731
|
+
}
|
|
1629
1732
|
} else {
|
|
1630
1733
|
const file = await fileHandle.getFile();
|
|
1631
1734
|
buffer = new Uint8Array(await file.arrayBuffer());
|
|
@@ -1682,11 +1785,19 @@ var OPFS = class {
|
|
|
1682
1785
|
}
|
|
1683
1786
|
let buffer;
|
|
1684
1787
|
if (this.useSync) {
|
|
1685
|
-
const
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1788
|
+
const releaseLock = await fileLockManager.acquire(resolvedPath);
|
|
1789
|
+
try {
|
|
1790
|
+
const access = await fileHandle.createSyncAccessHandle();
|
|
1791
|
+
try {
|
|
1792
|
+
const size = access.getSize();
|
|
1793
|
+
buffer = new Uint8Array(size);
|
|
1794
|
+
access.read(buffer);
|
|
1795
|
+
} finally {
|
|
1796
|
+
access.close();
|
|
1797
|
+
}
|
|
1798
|
+
} finally {
|
|
1799
|
+
releaseLock();
|
|
1800
|
+
}
|
|
1690
1801
|
} else {
|
|
1691
1802
|
const file = await fileHandle.getFile();
|
|
1692
1803
|
buffer = new Uint8Array(await file.arrayBuffer());
|
|
@@ -1718,10 +1829,18 @@ var OPFS = class {
|
|
|
1718
1829
|
const { fileHandle } = await this.handleManager.getHandle(resolvedPath, { create: true });
|
|
1719
1830
|
const buffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
1720
1831
|
if (this.useSync) {
|
|
1721
|
-
const
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1832
|
+
const releaseLock = await fileLockManager.acquire(resolvedPath);
|
|
1833
|
+
try {
|
|
1834
|
+
const access = await fileHandle.createSyncAccessHandle();
|
|
1835
|
+
try {
|
|
1836
|
+
access.truncate(buffer.length);
|
|
1837
|
+
access.write(buffer, { at: 0 });
|
|
1838
|
+
} finally {
|
|
1839
|
+
access.close();
|
|
1840
|
+
}
|
|
1841
|
+
} finally {
|
|
1842
|
+
releaseLock();
|
|
1843
|
+
}
|
|
1725
1844
|
} else {
|
|
1726
1845
|
const writable = await fileHandle.createWritable();
|
|
1727
1846
|
await writable.write(buffer);
|
|
@@ -2334,9 +2453,17 @@ var OPFS = class {
|
|
|
2334
2453
|
const { fileHandle } = await this.handleManager.getHandle(resolvedPath);
|
|
2335
2454
|
if (!fileHandle) throw createENOENT(path);
|
|
2336
2455
|
if (this.useSync) {
|
|
2337
|
-
const
|
|
2338
|
-
|
|
2339
|
-
|
|
2456
|
+
const releaseLock = await fileLockManager.acquire(resolvedPath);
|
|
2457
|
+
try {
|
|
2458
|
+
const access = await fileHandle.createSyncAccessHandle();
|
|
2459
|
+
try {
|
|
2460
|
+
access.truncate(len);
|
|
2461
|
+
} finally {
|
|
2462
|
+
access.close();
|
|
2463
|
+
}
|
|
2464
|
+
} finally {
|
|
2465
|
+
releaseLock();
|
|
2466
|
+
}
|
|
2340
2467
|
} else {
|
|
2341
2468
|
const file = await fileHandle.getFile();
|
|
2342
2469
|
const data = new Uint8Array(await file.arrayBuffer());
|