@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/opfs-hybrid.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());
|
|
@@ -1215,11 +1310,19 @@ var OPFS = class {
|
|
|
1215
1310
|
if (fileHandle) {
|
|
1216
1311
|
let buffer;
|
|
1217
1312
|
if (this.useSync) {
|
|
1218
|
-
const
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1313
|
+
const releaseLock = await fileLockManager.acquire(resolvedPath);
|
|
1314
|
+
try {
|
|
1315
|
+
const access = await fileHandle.createSyncAccessHandle();
|
|
1316
|
+
try {
|
|
1317
|
+
const size = access.getSize();
|
|
1318
|
+
buffer = new Uint8Array(size);
|
|
1319
|
+
access.read(buffer);
|
|
1320
|
+
} finally {
|
|
1321
|
+
access.close();
|
|
1322
|
+
}
|
|
1323
|
+
} finally {
|
|
1324
|
+
releaseLock();
|
|
1325
|
+
}
|
|
1223
1326
|
} else {
|
|
1224
1327
|
const file = await fileHandle.getFile();
|
|
1225
1328
|
buffer = new Uint8Array(await file.arrayBuffer());
|
|
@@ -1276,11 +1379,19 @@ var OPFS = class {
|
|
|
1276
1379
|
}
|
|
1277
1380
|
let buffer;
|
|
1278
1381
|
if (this.useSync) {
|
|
1279
|
-
const
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1382
|
+
const releaseLock = await fileLockManager.acquire(resolvedPath);
|
|
1383
|
+
try {
|
|
1384
|
+
const access = await fileHandle.createSyncAccessHandle();
|
|
1385
|
+
try {
|
|
1386
|
+
const size = access.getSize();
|
|
1387
|
+
buffer = new Uint8Array(size);
|
|
1388
|
+
access.read(buffer);
|
|
1389
|
+
} finally {
|
|
1390
|
+
access.close();
|
|
1391
|
+
}
|
|
1392
|
+
} finally {
|
|
1393
|
+
releaseLock();
|
|
1394
|
+
}
|
|
1284
1395
|
} else {
|
|
1285
1396
|
const file = await fileHandle.getFile();
|
|
1286
1397
|
buffer = new Uint8Array(await file.arrayBuffer());
|
|
@@ -1312,10 +1423,18 @@ var OPFS = class {
|
|
|
1312
1423
|
const { fileHandle } = await this.handleManager.getHandle(resolvedPath, { create: true });
|
|
1313
1424
|
const buffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
1314
1425
|
if (this.useSync) {
|
|
1315
|
-
const
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1426
|
+
const releaseLock = await fileLockManager.acquire(resolvedPath);
|
|
1427
|
+
try {
|
|
1428
|
+
const access = await fileHandle.createSyncAccessHandle();
|
|
1429
|
+
try {
|
|
1430
|
+
access.truncate(buffer.length);
|
|
1431
|
+
access.write(buffer, { at: 0 });
|
|
1432
|
+
} finally {
|
|
1433
|
+
access.close();
|
|
1434
|
+
}
|
|
1435
|
+
} finally {
|
|
1436
|
+
releaseLock();
|
|
1437
|
+
}
|
|
1319
1438
|
} else {
|
|
1320
1439
|
const writable = await fileHandle.createWritable();
|
|
1321
1440
|
await writable.write(buffer);
|
|
@@ -1928,9 +2047,17 @@ var OPFS = class {
|
|
|
1928
2047
|
const { fileHandle } = await this.handleManager.getHandle(resolvedPath);
|
|
1929
2048
|
if (!fileHandle) throw createENOENT(path);
|
|
1930
2049
|
if (this.useSync) {
|
|
1931
|
-
const
|
|
1932
|
-
|
|
1933
|
-
|
|
2050
|
+
const releaseLock = await fileLockManager.acquire(resolvedPath);
|
|
2051
|
+
try {
|
|
2052
|
+
const access = await fileHandle.createSyncAccessHandle();
|
|
2053
|
+
try {
|
|
2054
|
+
access.truncate(len);
|
|
2055
|
+
} finally {
|
|
2056
|
+
access.close();
|
|
2057
|
+
}
|
|
2058
|
+
} finally {
|
|
2059
|
+
releaseLock();
|
|
2060
|
+
}
|
|
1934
2061
|
} else {
|
|
1935
2062
|
const file = await fileHandle.getFile();
|
|
1936
2063
|
const data = new Uint8Array(await file.arrayBuffer());
|