@lvce-editor/file-system-process 5.15.0 → 5.16.0
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 +194 -26
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -666,7 +666,7 @@ const isWebSocketOpen = async webSocket => {
|
|
|
666
666
|
};
|
|
667
667
|
|
|
668
668
|
// @ts-ignore
|
|
669
|
-
const serialize = message => {
|
|
669
|
+
const serialize$1 = message => {
|
|
670
670
|
return JSON.stringify(message);
|
|
671
671
|
};
|
|
672
672
|
|
|
@@ -734,7 +734,7 @@ const wrap$9 = webSocket => {
|
|
|
734
734
|
},
|
|
735
735
|
// @ts-ignore
|
|
736
736
|
send(message) {
|
|
737
|
-
const stringifiedMessage = serialize(message);
|
|
737
|
+
const stringifiedMessage = serialize$1(message);
|
|
738
738
|
wrap.webSocket.send(stringifiedMessage);
|
|
739
739
|
},
|
|
740
740
|
start() {
|
|
@@ -778,10 +778,10 @@ const execute = (command, ...args) => {
|
|
|
778
778
|
|
|
779
779
|
const Two$1 = '2.0';
|
|
780
780
|
const callbacks = Object.create(null);
|
|
781
|
-
const get$
|
|
781
|
+
const get$2 = id => {
|
|
782
782
|
return callbacks[id];
|
|
783
783
|
};
|
|
784
|
-
const remove$
|
|
784
|
+
const remove$3 = id => {
|
|
785
785
|
delete callbacks[id];
|
|
786
786
|
};
|
|
787
787
|
class JsonRpcError extends Error {
|
|
@@ -968,14 +968,14 @@ const warn = (...args) => {
|
|
|
968
968
|
console.warn(...args);
|
|
969
969
|
};
|
|
970
970
|
const resolve = (id, response) => {
|
|
971
|
-
const fn = get$
|
|
971
|
+
const fn = get$2(id);
|
|
972
972
|
if (!fn) {
|
|
973
973
|
console.log(response);
|
|
974
974
|
warn(`callback ${id} may already be disposed`);
|
|
975
975
|
return;
|
|
976
976
|
}
|
|
977
977
|
fn(response);
|
|
978
|
-
remove$
|
|
978
|
+
remove$3(id);
|
|
979
979
|
};
|
|
980
980
|
const E_COMMAND_NOT_FOUND = 'E_COMMAND_NOT_FOUND';
|
|
981
981
|
const getErrorType = prettyError => {
|
|
@@ -1402,13 +1402,13 @@ const createMockRpc = ({
|
|
|
1402
1402
|
};
|
|
1403
1403
|
|
|
1404
1404
|
const rpcs = Object.create(null);
|
|
1405
|
-
const set$
|
|
1405
|
+
const set$3 = (id, rpc) => {
|
|
1406
1406
|
rpcs[id] = rpc;
|
|
1407
1407
|
};
|
|
1408
|
-
const get = id => {
|
|
1408
|
+
const get$1 = id => {
|
|
1409
1409
|
return rpcs[id];
|
|
1410
1410
|
};
|
|
1411
|
-
const remove$
|
|
1411
|
+
const remove$2 = id => {
|
|
1412
1412
|
delete rpcs[id];
|
|
1413
1413
|
};
|
|
1414
1414
|
|
|
@@ -1416,18 +1416,18 @@ const remove$1 = id => {
|
|
|
1416
1416
|
const create$1 = rpcId => {
|
|
1417
1417
|
return {
|
|
1418
1418
|
async dispose() {
|
|
1419
|
-
const rpc = get(rpcId);
|
|
1419
|
+
const rpc = get$1(rpcId);
|
|
1420
1420
|
await rpc.dispose();
|
|
1421
1421
|
},
|
|
1422
1422
|
// @ts-ignore
|
|
1423
1423
|
invoke(method, ...params) {
|
|
1424
|
-
const rpc = get(rpcId);
|
|
1424
|
+
const rpc = get$1(rpcId);
|
|
1425
1425
|
// @ts-ignore
|
|
1426
1426
|
return rpc.invoke(method, ...params);
|
|
1427
1427
|
},
|
|
1428
1428
|
// @ts-ignore
|
|
1429
1429
|
invokeAndTransfer(method, ...params) {
|
|
1430
|
-
const rpc = get(rpcId);
|
|
1430
|
+
const rpc = get$1(rpcId);
|
|
1431
1431
|
// @ts-ignore
|
|
1432
1432
|
return rpc.invokeAndTransfer(method, ...params);
|
|
1433
1433
|
},
|
|
@@ -1435,16 +1435,16 @@ const create$1 = rpcId => {
|
|
|
1435
1435
|
const mockRpc = createMockRpc({
|
|
1436
1436
|
commandMap
|
|
1437
1437
|
});
|
|
1438
|
-
set$
|
|
1438
|
+
set$3(rpcId, mockRpc);
|
|
1439
1439
|
// @ts-ignore
|
|
1440
1440
|
mockRpc[Symbol.dispose] = () => {
|
|
1441
|
-
remove$
|
|
1441
|
+
remove$2(rpcId);
|
|
1442
1442
|
};
|
|
1443
1443
|
// @ts-ignore
|
|
1444
1444
|
return mockRpc;
|
|
1445
1445
|
},
|
|
1446
1446
|
set(rpc) {
|
|
1447
|
-
set$
|
|
1447
|
+
set$3(rpcId, rpc);
|
|
1448
1448
|
}
|
|
1449
1449
|
};
|
|
1450
1450
|
};
|
|
@@ -1455,20 +1455,20 @@ const SharedProcess = 1492;
|
|
|
1455
1455
|
|
|
1456
1456
|
const {
|
|
1457
1457
|
invoke: invoke$1,
|
|
1458
|
-
set: set$
|
|
1458
|
+
set: set$2
|
|
1459
1459
|
} = create$1(MainProcess);
|
|
1460
1460
|
|
|
1461
1461
|
const {
|
|
1462
1462
|
invoke,
|
|
1463
1463
|
invokeAndTransfer,
|
|
1464
|
-
set
|
|
1464
|
+
set: set$1
|
|
1465
1465
|
} = create$1(SharedProcess);
|
|
1466
1466
|
|
|
1467
|
-
const state = {
|
|
1467
|
+
const state$1 = {
|
|
1468
1468
|
id: 0
|
|
1469
1469
|
};
|
|
1470
1470
|
const create = () => {
|
|
1471
|
-
return ++state.id;
|
|
1471
|
+
return ++state$1.id;
|
|
1472
1472
|
};
|
|
1473
1473
|
|
|
1474
1474
|
const ports = Object.create(null);
|
|
@@ -1513,7 +1513,7 @@ const initializeMainProcessRpc = async () => {
|
|
|
1513
1513
|
await invokeAndTransfer('TemporaryMessagePort.sendToElectron', port, MainProcess, FileSystemProcess);
|
|
1514
1514
|
}
|
|
1515
1515
|
});
|
|
1516
|
-
set$
|
|
1516
|
+
set$2(rpc);
|
|
1517
1517
|
};
|
|
1518
1518
|
|
|
1519
1519
|
const assertUri = uri => {
|
|
@@ -1529,6 +1529,149 @@ const ENOENT = 'ENOENT';
|
|
|
1529
1529
|
const EROFS = 'EROFS';
|
|
1530
1530
|
const EXDEV = 'EXDEV';
|
|
1531
1531
|
|
|
1532
|
+
const cacheVersion = 1;
|
|
1533
|
+
const maxEntries = 50_000;
|
|
1534
|
+
const cacheFolderName = 'file-system-process';
|
|
1535
|
+
const cacheFileName = 'file-hashes-v1.json';
|
|
1536
|
+
const state = {
|
|
1537
|
+
cacheFilePath: '',
|
|
1538
|
+
cacheFolderPath: '',
|
|
1539
|
+
entries: new Map(),
|
|
1540
|
+
loadPromise: undefined,
|
|
1541
|
+
persistedRevision: 0,
|
|
1542
|
+
revision: 0,
|
|
1543
|
+
writePromise: undefined
|
|
1544
|
+
};
|
|
1545
|
+
const isSerializedCache = value => {
|
|
1546
|
+
if (!value || typeof value !== 'object') {
|
|
1547
|
+
return false;
|
|
1548
|
+
}
|
|
1549
|
+
const candidate = value;
|
|
1550
|
+
return candidate.version === cacheVersion && Array.isArray(candidate.entries);
|
|
1551
|
+
};
|
|
1552
|
+
const isSerializedEntry = value => {
|
|
1553
|
+
return Array.isArray(value) && value.length === 3 && typeof value[0] === 'string' && typeof value[1] === 'string' && typeof value[2] === 'string' && /^[\da-f]{64}$/.test(value[2]);
|
|
1554
|
+
};
|
|
1555
|
+
const parseCache = content => {
|
|
1556
|
+
const parsed = JSON.parse(content);
|
|
1557
|
+
if (!isSerializedCache(parsed) || !parsed.entries.every(isSerializedEntry)) {
|
|
1558
|
+
return undefined;
|
|
1559
|
+
}
|
|
1560
|
+
return parsed.entries.slice(-maxEntries);
|
|
1561
|
+
};
|
|
1562
|
+
const load = async () => {
|
|
1563
|
+
if (!state.cacheFilePath) {
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
try {
|
|
1567
|
+
const content = await fs.readFile(state.cacheFilePath, 'utf8');
|
|
1568
|
+
const cachedEntries = parseCache(content);
|
|
1569
|
+
if (!cachedEntries) {
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1572
|
+
for (const [path, fingerprint, hash] of cachedEntries) {
|
|
1573
|
+
state.entries.delete(path);
|
|
1574
|
+
state.entries.set(path, {
|
|
1575
|
+
fingerprint,
|
|
1576
|
+
hash
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
} catch {
|
|
1580
|
+
// A persistent cache miss must fall back to hashing the source files.
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
const configure = cacheDirectory => {
|
|
1584
|
+
state.entries.clear();
|
|
1585
|
+
state.cacheFolderPath = cacheDirectory ? join(cacheDirectory, cacheFolderName) : '';
|
|
1586
|
+
state.cacheFilePath = state.cacheFolderPath ? join(state.cacheFolderPath, cacheFileName) : '';
|
|
1587
|
+
state.loadPromise = undefined;
|
|
1588
|
+
state.persistedRevision = 0;
|
|
1589
|
+
state.revision = 0;
|
|
1590
|
+
state.writePromise = undefined;
|
|
1591
|
+
};
|
|
1592
|
+
const ensureLoaded = async () => {
|
|
1593
|
+
state.loadPromise ||= load();
|
|
1594
|
+
await state.loadPromise;
|
|
1595
|
+
};
|
|
1596
|
+
const get = (path, fingerprint) => {
|
|
1597
|
+
const entry = state.entries.get(path);
|
|
1598
|
+
if (!entry || entry.fingerprint !== fingerprint) {
|
|
1599
|
+
return undefined;
|
|
1600
|
+
}
|
|
1601
|
+
state.entries.delete(path);
|
|
1602
|
+
state.entries.set(path, entry);
|
|
1603
|
+
return entry.hash;
|
|
1604
|
+
};
|
|
1605
|
+
const set = (path, fingerprint, hash) => {
|
|
1606
|
+
const previous = state.entries.get(path);
|
|
1607
|
+
state.entries.delete(path);
|
|
1608
|
+
state.entries.set(path, {
|
|
1609
|
+
fingerprint,
|
|
1610
|
+
hash
|
|
1611
|
+
});
|
|
1612
|
+
if (!previous || previous.fingerprint !== fingerprint || previous.hash !== hash) {
|
|
1613
|
+
state.revision++;
|
|
1614
|
+
}
|
|
1615
|
+
if (state.entries.size <= maxEntries) {
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
const oldestPath = state.entries.keys().next().value;
|
|
1619
|
+
if (oldestPath !== undefined) {
|
|
1620
|
+
state.entries.delete(oldestPath);
|
|
1621
|
+
state.revision++;
|
|
1622
|
+
}
|
|
1623
|
+
};
|
|
1624
|
+
const remove$1 = path => {
|
|
1625
|
+
if (state.entries.delete(path)) {
|
|
1626
|
+
state.revision++;
|
|
1627
|
+
}
|
|
1628
|
+
};
|
|
1629
|
+
const serialize = () => {
|
|
1630
|
+
const serializedEntries = Array.from(state.entries, ([path, entry]) => [path, entry.fingerprint, entry.hash]);
|
|
1631
|
+
return JSON.stringify({
|
|
1632
|
+
entries: serializedEntries,
|
|
1633
|
+
version: cacheVersion
|
|
1634
|
+
});
|
|
1635
|
+
};
|
|
1636
|
+
const writeCurrentState = async () => {
|
|
1637
|
+
while (state.cacheFilePath && state.persistedRevision !== state.revision) {
|
|
1638
|
+
const snapshotRevision = state.revision;
|
|
1639
|
+
const temporaryPath = `${state.cacheFilePath}.${process.pid}.tmp`;
|
|
1640
|
+
try {
|
|
1641
|
+
await fs.mkdir(state.cacheFolderPath, {
|
|
1642
|
+
recursive: true
|
|
1643
|
+
});
|
|
1644
|
+
await fs.writeFile(temporaryPath, serialize());
|
|
1645
|
+
await fs.rename(temporaryPath, state.cacheFilePath);
|
|
1646
|
+
state.persistedRevision = snapshotRevision;
|
|
1647
|
+
} catch {
|
|
1648
|
+
try {
|
|
1649
|
+
await fs.rm(temporaryPath, {
|
|
1650
|
+
force: true
|
|
1651
|
+
});
|
|
1652
|
+
} catch {
|
|
1653
|
+
// Best-effort cleanup only.
|
|
1654
|
+
}
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
};
|
|
1659
|
+
const flush = async () => {
|
|
1660
|
+
await ensureLoaded();
|
|
1661
|
+
if (!state.cacheFilePath) {
|
|
1662
|
+
return;
|
|
1663
|
+
}
|
|
1664
|
+
const writePromise = state.writePromise || writeCurrentState();
|
|
1665
|
+
state.writePromise = writePromise;
|
|
1666
|
+
try {
|
|
1667
|
+
await writePromise;
|
|
1668
|
+
} finally {
|
|
1669
|
+
if (state.writePromise === writePromise) {
|
|
1670
|
+
state.writePromise = undefined;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
};
|
|
1674
|
+
|
|
1532
1675
|
const getDisplayPath = path => {
|
|
1533
1676
|
if (path === '') {
|
|
1534
1677
|
return '<empty string>';
|
|
@@ -1682,15 +1825,39 @@ const readFile = async (uri, encoding = Utf8) => {
|
|
|
1682
1825
|
throw new VError(error, `Failed to read file "${uri}"`);
|
|
1683
1826
|
}
|
|
1684
1827
|
};
|
|
1828
|
+
const getFileFingerprint = stats => {
|
|
1829
|
+
return `${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeNs}:${stats.ctimeNs}`;
|
|
1830
|
+
};
|
|
1685
1831
|
const getFileHash = async uri => {
|
|
1832
|
+
let path = '';
|
|
1686
1833
|
try {
|
|
1687
1834
|
string(uri);
|
|
1688
1835
|
assertUri(uri);
|
|
1689
|
-
|
|
1836
|
+
path = fileURLToPath(uri);
|
|
1837
|
+
await ensureLoaded();
|
|
1838
|
+
const statsBeforeRead = await fs.stat(path, {
|
|
1839
|
+
bigint: true
|
|
1840
|
+
});
|
|
1841
|
+
const fingerprintBeforeRead = getFileFingerprint(statsBeforeRead);
|
|
1842
|
+
const cachedHash = get(path, fingerprintBeforeRead);
|
|
1843
|
+
if (cachedHash) {
|
|
1844
|
+
return cachedHash;
|
|
1845
|
+
}
|
|
1690
1846
|
const content = await fs.readFile(path);
|
|
1691
|
-
|
|
1847
|
+
const hash = createHash('sha256').update(content).digest('hex');
|
|
1848
|
+
const statsAfterRead = await fs.stat(path, {
|
|
1849
|
+
bigint: true
|
|
1850
|
+
});
|
|
1851
|
+
const fingerprintAfterRead = getFileFingerprint(statsAfterRead);
|
|
1852
|
+
if (fingerprintBeforeRead === fingerprintAfterRead) {
|
|
1853
|
+
set(path, fingerprintAfterRead, hash);
|
|
1854
|
+
}
|
|
1855
|
+
return hash;
|
|
1692
1856
|
} catch (error) {
|
|
1693
1857
|
if (isEnoentError(error)) {
|
|
1858
|
+
if (path) {
|
|
1859
|
+
remove$1(path);
|
|
1860
|
+
}
|
|
1694
1861
|
throw new FileNotFoundError(uri);
|
|
1695
1862
|
}
|
|
1696
1863
|
throw new VError(error, `Failed to hash file "${uri}"`);
|
|
@@ -1724,6 +1891,7 @@ const getFileHashes = async uris => {
|
|
|
1724
1891
|
await Promise.all(Array.from({
|
|
1725
1892
|
length: workerCount
|
|
1726
1893
|
}, hashNext));
|
|
1894
|
+
await flush();
|
|
1727
1895
|
return hashes;
|
|
1728
1896
|
};
|
|
1729
1897
|
const writeFile = async (uri, content, encoding = Utf8) => {
|
|
@@ -1965,7 +2133,7 @@ const handleElectronMessagePort = async (messagePort, rpcId) => {
|
|
|
1965
2133
|
requiresSocket: false
|
|
1966
2134
|
});
|
|
1967
2135
|
if (rpcId) {
|
|
1968
|
-
set$
|
|
2136
|
+
set$3(rpcId, rpc);
|
|
1969
2137
|
}
|
|
1970
2138
|
};
|
|
1971
2139
|
|
|
@@ -1983,8 +2151,8 @@ const handleWebSocket = async (handle, request) => {
|
|
|
1983
2151
|
});
|
|
1984
2152
|
};
|
|
1985
2153
|
|
|
1986
|
-
const initialize = async
|
|
1987
|
-
|
|
2154
|
+
const initialize = async cacheDirectory => {
|
|
2155
|
+
configure(cacheDirectory);
|
|
1988
2156
|
};
|
|
1989
2157
|
|
|
1990
2158
|
const commandMap = {
|
|
@@ -2036,7 +2204,7 @@ const getRpc = argv => {
|
|
|
2036
2204
|
const initializeParentProcessRpc = async argv => {
|
|
2037
2205
|
const rpc = await getRpc(argv);
|
|
2038
2206
|
// TODO we don't know at this point if we have been launched by shared process or electron main process
|
|
2039
|
-
set(rpc);
|
|
2207
|
+
set$1(rpc);
|
|
2040
2208
|
};
|
|
2041
2209
|
|
|
2042
2210
|
const listen = async argv => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lvce-editor/file-system-process",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.16.0",
|
|
4
4
|
"description": "File System Process",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Lvce Editor",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"trash": "^10.1.1",
|
|
22
|
-
"ws": "^8.21.
|
|
22
|
+
"ws": "^8.21.3"
|
|
23
23
|
},
|
|
24
24
|
"engines": {
|
|
25
25
|
"node": ">=24"
|