@lvce-editor/file-system-process 5.15.1 → 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.
Files changed (2) hide show
  1. package/dist/index.js +176 -52
  2. 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$1 = id => {
781
+ const get$2 = id => {
782
782
  return callbacks[id];
783
783
  };
784
- const remove$2 = id => {
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$1(id);
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$2(id);
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$2 = (id, rpc) => {
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$1 = id => {
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$2(rpcId, mockRpc);
1438
+ set$3(rpcId, mockRpc);
1439
1439
  // @ts-ignore
1440
1440
  mockRpc[Symbol.dispose] = () => {
1441
- remove$1(rpcId);
1441
+ remove$2(rpcId);
1442
1442
  };
1443
1443
  // @ts-ignore
1444
1444
  return mockRpc;
1445
1445
  },
1446
1446
  set(rpc) {
1447
- set$2(rpcId, rpc);
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$1
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$1(rpc);
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,44 +1825,21 @@ const readFile = async (uri, encoding = Utf8) => {
1682
1825
  throw new VError(error, `Failed to read file "${uri}"`);
1683
1826
  }
1684
1827
  };
1685
- const fileHashCache = new Map();
1686
- const maxFileHashCacheEntries = 50_000;
1687
1828
  const getFileFingerprint = stats => {
1688
1829
  return `${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeNs}:${stats.ctimeNs}`;
1689
1830
  };
1690
- const getCachedFileHash = (path, fingerprint) => {
1691
- const entry = fileHashCache.get(path);
1692
- if (!entry || entry.fingerprint !== fingerprint) {
1693
- return undefined;
1694
- }
1695
- fileHashCache.delete(path);
1696
- fileHashCache.set(path, entry);
1697
- return entry.hash;
1698
- };
1699
- const setCachedFileHash = (path, fingerprint, hash) => {
1700
- fileHashCache.delete(path);
1701
- fileHashCache.set(path, {
1702
- fingerprint,
1703
- hash
1704
- });
1705
- if (fileHashCache.size <= maxFileHashCacheEntries) {
1706
- return;
1707
- }
1708
- const oldestPath = fileHashCache.keys().next().value;
1709
- if (oldestPath) {
1710
- fileHashCache.delete(oldestPath);
1711
- }
1712
- };
1713
1831
  const getFileHash = async uri => {
1832
+ let path = '';
1714
1833
  try {
1715
1834
  string(uri);
1716
1835
  assertUri(uri);
1717
- const path = fileURLToPath(uri);
1836
+ path = fileURLToPath(uri);
1837
+ await ensureLoaded();
1718
1838
  const statsBeforeRead = await fs.stat(path, {
1719
1839
  bigint: true
1720
1840
  });
1721
1841
  const fingerprintBeforeRead = getFileFingerprint(statsBeforeRead);
1722
- const cachedHash = getCachedFileHash(path, fingerprintBeforeRead);
1842
+ const cachedHash = get(path, fingerprintBeforeRead);
1723
1843
  if (cachedHash) {
1724
1844
  return cachedHash;
1725
1845
  }
@@ -1730,11 +1850,14 @@ const getFileHash = async uri => {
1730
1850
  });
1731
1851
  const fingerprintAfterRead = getFileFingerprint(statsAfterRead);
1732
1852
  if (fingerprintBeforeRead === fingerprintAfterRead) {
1733
- setCachedFileHash(path, fingerprintAfterRead, hash);
1853
+ set(path, fingerprintAfterRead, hash);
1734
1854
  }
1735
1855
  return hash;
1736
1856
  } catch (error) {
1737
1857
  if (isEnoentError(error)) {
1858
+ if (path) {
1859
+ remove$1(path);
1860
+ }
1738
1861
  throw new FileNotFoundError(uri);
1739
1862
  }
1740
1863
  throw new VError(error, `Failed to hash file "${uri}"`);
@@ -1768,6 +1891,7 @@ const getFileHashes = async uris => {
1768
1891
  await Promise.all(Array.from({
1769
1892
  length: workerCount
1770
1893
  }, hashNext));
1894
+ await flush();
1771
1895
  return hashes;
1772
1896
  };
1773
1897
  const writeFile = async (uri, content, encoding = Utf8) => {
@@ -2009,7 +2133,7 @@ const handleElectronMessagePort = async (messagePort, rpcId) => {
2009
2133
  requiresSocket: false
2010
2134
  });
2011
2135
  if (rpcId) {
2012
- set$2(rpcId, rpc);
2136
+ set$3(rpcId, rpc);
2013
2137
  }
2014
2138
  };
2015
2139
 
@@ -2027,8 +2151,8 @@ const handleWebSocket = async (handle, request) => {
2027
2151
  });
2028
2152
  };
2029
2153
 
2030
- const initialize = async () => {
2031
- // TODO this function is not needed anymore
2154
+ const initialize = async cacheDirectory => {
2155
+ configure(cacheDirectory);
2032
2156
  };
2033
2157
 
2034
2158
  const commandMap = {
@@ -2080,7 +2204,7 @@ const getRpc = argv => {
2080
2204
  const initializeParentProcessRpc = async argv => {
2081
2205
  const rpc = await getRpc(argv);
2082
2206
  // TODO we don't know at this point if we have been launched by shared process or electron main process
2083
- set(rpc);
2207
+ set$1(rpc);
2084
2208
  };
2085
2209
 
2086
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.15.1",
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.1"
22
+ "ws": "^8.21.3"
23
23
  },
24
24
  "engines": {
25
25
  "node": ">=24"