@gallop.software/studio 2.3.40 → 2.3.41

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.
@@ -1725,26 +1725,134 @@ async function handleMoveStream(request) {
1725
1725
  const moved = [];
1726
1726
  const errors = [];
1727
1727
  const sourceFolders = /* @__PURE__ */ new Set();
1728
- const total = paths.length;
1729
- sendEvent({ type: "start", total });
1730
- for (let i = 0; i < paths.length; i++) {
1731
- const itemPath = paths[i];
1728
+ let totalFiles = 0;
1729
+ const expandedItems = [];
1730
+ for (const itemPath of paths) {
1732
1731
  const safePath = itemPath.replace(/\.\./g, "");
1733
1732
  const itemName = path6.basename(safePath);
1734
- const newAbsolutePath = path6.join(absoluteDestination, itemName);
1735
1733
  const oldRelativePath = safePath.replace(/^public\/?/, "");
1736
1734
  const destWithoutPublic = safeDestination.replace(/^public\/?/, "");
1737
1735
  const newRelativePath = destWithoutPublic ? path6.join(destWithoutPublic, itemName) : itemName;
1738
1736
  const oldKey = "/" + oldRelativePath;
1739
1737
  const newKey = "/" + newRelativePath;
1738
+ const newAbsolutePath = path6.join(absoluteDestination, itemName);
1739
+ const absolutePath = getWorkspacePath(safePath);
1740
+ let hasLocalItem = false;
1741
+ let isDirectory = false;
1742
+ try {
1743
+ const stats = await fs6.stat(absolutePath);
1744
+ hasLocalItem = true;
1745
+ isDirectory = stats.isDirectory();
1746
+ } catch {
1747
+ }
1748
+ if (hasLocalItem && isDirectory) {
1749
+ const countFilesRecursive = async (dir) => {
1750
+ let count = 0;
1751
+ const entries = await fs6.readdir(dir, { withFileTypes: true });
1752
+ for (const entry of entries) {
1753
+ if (entry.isDirectory()) {
1754
+ count += await countFilesRecursive(path6.join(dir, entry.name));
1755
+ } else {
1756
+ count++;
1757
+ }
1758
+ }
1759
+ return count;
1760
+ };
1761
+ totalFiles += await countFilesRecursive(absolutePath);
1762
+ expandedItems.push({ itemPath, safePath, itemName, oldKey, newKey, newAbsolutePath, isVirtualFolder: false });
1763
+ } else if (!hasLocalItem) {
1764
+ const folderPrefix = oldKey + "/";
1765
+ const virtualItems = [];
1766
+ for (const [key, metaEntry] of Object.entries(meta)) {
1767
+ if (key.startsWith(folderPrefix) && metaEntry && typeof metaEntry === "object") {
1768
+ const relativePath = key.slice(folderPrefix.length);
1769
+ const destNewKey = newKey + "/" + relativePath;
1770
+ virtualItems.push({ oldKey: key, newKey: destNewKey, entry: metaEntry });
1771
+ }
1772
+ }
1773
+ if (virtualItems.length > 0) {
1774
+ totalFiles += virtualItems.length;
1775
+ expandedItems.push({ itemPath, safePath, itemName, oldKey, newKey, newAbsolutePath, isVirtualFolder: true, virtualFolderItems: virtualItems });
1776
+ sourceFolders.add(absolutePath);
1777
+ } else {
1778
+ totalFiles++;
1779
+ expandedItems.push({ itemPath, safePath, itemName, oldKey, newKey, newAbsolutePath, isVirtualFolder: false });
1780
+ }
1781
+ } else {
1782
+ totalFiles++;
1783
+ expandedItems.push({ itemPath, safePath, itemName, oldKey, newKey, newAbsolutePath, isVirtualFolder: false });
1784
+ }
1785
+ }
1786
+ sendEvent({ type: "start", total: totalFiles });
1787
+ let processedFiles = 0;
1788
+ for (const expandedItem of expandedItems) {
1789
+ const { itemPath, safePath, itemName, oldKey, newKey, newAbsolutePath, isVirtualFolder, virtualFolderItems } = expandedItem;
1790
+ if (isVirtualFolder && virtualFolderItems) {
1791
+ for (const vItem of virtualFolderItems) {
1792
+ const itemEntry = vItem.entry;
1793
+ const isItemInCloud = itemEntry.c !== void 0;
1794
+ const itemCdnUrl = isItemInCloud ? cdnUrls[itemEntry.c] : void 0;
1795
+ const isItemInR2 = isItemInCloud && itemCdnUrl === r2PublicUrl;
1796
+ const itemHasThumbnails = isProcessed(itemEntry);
1797
+ if (isItemInR2) {
1798
+ try {
1799
+ const itemLocalPath = getPublicPath(vItem.newKey);
1800
+ const buffer = await downloadFromCdn(vItem.oldKey);
1801
+ await fs6.mkdir(path6.dirname(itemLocalPath), { recursive: true });
1802
+ await fs6.writeFile(itemLocalPath, buffer);
1803
+ if (itemHasThumbnails) {
1804
+ const oldThumbPaths = getAllThumbnailPaths(vItem.oldKey);
1805
+ const newThumbPaths = getAllThumbnailPaths(vItem.newKey);
1806
+ for (let t = 0; t < oldThumbPaths.length; t++) {
1807
+ try {
1808
+ const thumbBuffer = await downloadFromCdn(oldThumbPaths[t]);
1809
+ const newThumbLocalPath = getPublicPath(newThumbPaths[t]);
1810
+ await fs6.mkdir(path6.dirname(newThumbLocalPath), { recursive: true });
1811
+ await fs6.writeFile(newThumbLocalPath, thumbBuffer);
1812
+ } catch {
1813
+ }
1814
+ }
1815
+ }
1816
+ await deleteFromCdn(vItem.oldKey, itemHasThumbnails);
1817
+ await uploadOriginalToCdn(vItem.newKey);
1818
+ if (itemHasThumbnails) {
1819
+ await uploadToCdn(vItem.newKey);
1820
+ }
1821
+ try {
1822
+ await fs6.unlink(itemLocalPath);
1823
+ } catch {
1824
+ }
1825
+ if (itemHasThumbnails) {
1826
+ await deleteLocalThumbnails(vItem.newKey);
1827
+ }
1828
+ } catch (err) {
1829
+ console.error(`Failed to move cloud item ${vItem.oldKey}:`, err);
1830
+ }
1831
+ }
1832
+ delete meta[vItem.oldKey];
1833
+ meta[vItem.newKey] = itemEntry;
1834
+ processedFiles++;
1835
+ sendEvent({
1836
+ type: "progress",
1837
+ current: processedFiles,
1838
+ total: totalFiles,
1839
+ moved: moved.length,
1840
+ percent: Math.round(processedFiles / totalFiles * 100),
1841
+ currentFile: path6.basename(vItem.newKey)
1842
+ });
1843
+ }
1844
+ moved.push(itemPath);
1845
+ continue;
1846
+ }
1740
1847
  if (meta[newKey]) {
1741
1848
  errors.push(`${itemName} already exists in destination`);
1849
+ processedFiles++;
1742
1850
  sendEvent({
1743
1851
  type: "progress",
1744
- current: i + 1,
1745
- total,
1852
+ current: processedFiles,
1853
+ total: totalFiles,
1746
1854
  moved: moved.length,
1747
- percent: Math.round((i + 1) / total * 100),
1855
+ percent: Math.round(processedFiles / totalFiles * 100),
1748
1856
  currentFile: itemName
1749
1857
  });
1750
1858
  continue;
@@ -1771,12 +1879,13 @@ async function handleMoveStream(request) {
1771
1879
  delete meta[oldKey];
1772
1880
  meta[newKey] = newEntry;
1773
1881
  moved.push(itemPath);
1882
+ processedFiles++;
1774
1883
  sendEvent({
1775
1884
  type: "progress",
1776
- current: i + 1,
1777
- total,
1885
+ current: processedFiles,
1886
+ total: totalFiles,
1778
1887
  moved: moved.length,
1779
- percent: Math.round((i + 1) / total * 100),
1888
+ percent: Math.round(processedFiles / totalFiles * 100),
1780
1889
  currentFile: itemName
1781
1890
  });
1782
1891
  } else if (isPushedToR2 && isImage) {
@@ -1807,106 +1916,41 @@ async function handleMoveStream(request) {
1807
1916
  delete meta[oldKey];
1808
1917
  meta[newKey] = newEntry;
1809
1918
  moved.push(itemPath);
1919
+ processedFiles++;
1810
1920
  sendEvent({
1811
1921
  type: "progress",
1812
- current: i + 1,
1813
- total,
1922
+ current: processedFiles,
1923
+ total: totalFiles,
1814
1924
  moved: moved.length,
1815
- percent: Math.round((i + 1) / total * 100),
1925
+ percent: Math.round(processedFiles / totalFiles * 100),
1816
1926
  currentFile: itemName
1817
1927
  });
1818
1928
  } else {
1819
1929
  const absolutePath = getWorkspacePath(safePath);
1820
1930
  if (absoluteDestination.startsWith(absolutePath + path6.sep)) {
1821
1931
  errors.push(`Cannot move ${itemName} into itself`);
1932
+ processedFiles++;
1822
1933
  sendEvent({
1823
1934
  type: "progress",
1824
- current: i + 1,
1825
- total,
1935
+ current: processedFiles,
1936
+ total: totalFiles,
1826
1937
  moved: moved.length,
1827
- percent: Math.round((i + 1) / total * 100),
1938
+ percent: Math.round(processedFiles / totalFiles * 100),
1828
1939
  currentFile: itemName
1829
1940
  });
1830
1941
  continue;
1831
1942
  }
1832
- let hasLocalItem = false;
1833
1943
  try {
1834
1944
  await fs6.access(absolutePath);
1835
- hasLocalItem = true;
1836
1945
  } catch {
1837
- const folderPrefix = oldKey + "/";
1838
- const hasChildrenInMeta = Object.keys(meta).some((key) => key.startsWith(folderPrefix));
1839
- if (hasChildrenInMeta) {
1840
- const itemsToMove = [];
1841
- for (const [key, metaEntry] of Object.entries(meta)) {
1842
- if (key.startsWith(folderPrefix) && metaEntry && typeof metaEntry === "object") {
1843
- const relativePath = key.slice(folderPrefix.length);
1844
- const destNewKey = newKey + "/" + relativePath;
1845
- itemsToMove.push({ oldKey: key, newKey: destNewKey, entry: metaEntry });
1846
- }
1847
- }
1848
- for (const item of itemsToMove) {
1849
- const itemEntry = item.entry;
1850
- const isItemInCloud = itemEntry.c !== void 0;
1851
- const itemCdnUrl = isItemInCloud ? cdnUrls[itemEntry.c] : void 0;
1852
- const isItemInR2 = isItemInCloud && itemCdnUrl === r2PublicUrl;
1853
- const itemHasThumbnails = isProcessed(itemEntry);
1854
- if (isItemInR2) {
1855
- try {
1856
- const itemLocalPath = getPublicPath(item.newKey);
1857
- const buffer = await downloadFromCdn(item.oldKey);
1858
- await fs6.mkdir(path6.dirname(itemLocalPath), { recursive: true });
1859
- await fs6.writeFile(itemLocalPath, buffer);
1860
- if (itemHasThumbnails) {
1861
- const oldThumbPaths = getAllThumbnailPaths(item.oldKey);
1862
- const newThumbPaths = getAllThumbnailPaths(item.newKey);
1863
- for (let t = 0; t < oldThumbPaths.length; t++) {
1864
- try {
1865
- const thumbBuffer = await downloadFromCdn(oldThumbPaths[t]);
1866
- const newThumbLocalPath = getPublicPath(newThumbPaths[t]);
1867
- await fs6.mkdir(path6.dirname(newThumbLocalPath), { recursive: true });
1868
- await fs6.writeFile(newThumbLocalPath, thumbBuffer);
1869
- } catch {
1870
- }
1871
- }
1872
- }
1873
- await deleteFromCdn(item.oldKey, itemHasThumbnails);
1874
- await uploadOriginalToCdn(item.newKey);
1875
- if (itemHasThumbnails) {
1876
- await uploadToCdn(item.newKey);
1877
- }
1878
- try {
1879
- await fs6.unlink(itemLocalPath);
1880
- } catch {
1881
- }
1882
- if (itemHasThumbnails) {
1883
- await deleteLocalThumbnails(item.newKey);
1884
- }
1885
- } catch (err) {
1886
- console.error(`Failed to move cloud item ${item.oldKey}:`, err);
1887
- }
1888
- }
1889
- delete meta[item.oldKey];
1890
- meta[item.newKey] = itemEntry;
1891
- }
1892
- moved.push(itemPath);
1893
- sendEvent({
1894
- type: "progress",
1895
- current: i + 1,
1896
- total,
1897
- moved: moved.length,
1898
- percent: Math.round((i + 1) / total * 100),
1899
- currentFile: itemName
1900
- });
1901
- continue;
1902
- }
1903
1946
  errors.push(`${itemName} not found`);
1947
+ processedFiles++;
1904
1948
  sendEvent({
1905
1949
  type: "progress",
1906
- current: i + 1,
1907
- total,
1950
+ current: processedFiles,
1951
+ total: totalFiles,
1908
1952
  moved: moved.length,
1909
- percent: Math.round((i + 1) / total * 100),
1953
+ percent: Math.round(processedFiles / totalFiles * 100),
1910
1954
  currentFile: itemName
1911
1955
  });
1912
1956
  continue;
@@ -1914,12 +1958,13 @@ async function handleMoveStream(request) {
1914
1958
  try {
1915
1959
  await fs6.access(newAbsolutePath);
1916
1960
  errors.push(`${itemName} already exists in destination`);
1961
+ processedFiles++;
1917
1962
  sendEvent({
1918
1963
  type: "progress",
1919
- current: i + 1,
1920
- total,
1964
+ current: processedFiles,
1965
+ total: totalFiles,
1921
1966
  moved: moved.length,
1922
- percent: Math.round((i + 1) / total * 100),
1967
+ percent: Math.round(processedFiles / totalFiles * 100),
1923
1968
  currentFile: itemName
1924
1969
  });
1925
1970
  continue;
@@ -1955,24 +2000,43 @@ async function handleMoveStream(request) {
1955
2000
  }
1956
2001
  }
1957
2002
  moved.push(itemPath);
2003
+ if (stats.isDirectory()) {
2004
+ const countFilesInDir = async (dir) => {
2005
+ let count = 0;
2006
+ const entries = await fs6.readdir(dir, { withFileTypes: true });
2007
+ for (const entry2 of entries) {
2008
+ if (entry2.isDirectory()) {
2009
+ count += await countFilesInDir(path6.join(dir, entry2.name));
2010
+ } else {
2011
+ count++;
2012
+ }
2013
+ }
2014
+ return count;
2015
+ };
2016
+ const filesInDir = await countFilesInDir(newAbsolutePath);
2017
+ processedFiles += filesInDir;
2018
+ } else {
2019
+ processedFiles++;
2020
+ }
1958
2021
  sendEvent({
1959
2022
  type: "progress",
1960
- current: i + 1,
1961
- total,
2023
+ current: processedFiles,
2024
+ total: totalFiles,
1962
2025
  moved: moved.length,
1963
- percent: Math.round((i + 1) / total * 100),
2026
+ percent: Math.round(processedFiles / totalFiles * 100),
1964
2027
  currentFile: itemName
1965
2028
  });
1966
2029
  }
1967
2030
  } catch (err) {
1968
2031
  console.error(`Failed to move ${itemName}:`, err);
1969
2032
  errors.push(`Failed to move ${itemName}`);
2033
+ processedFiles++;
1970
2034
  sendEvent({
1971
2035
  type: "progress",
1972
- current: i + 1,
1973
- total,
2036
+ current: processedFiles,
2037
+ total: totalFiles,
1974
2038
  moved: moved.length,
1975
- percent: Math.round((i + 1) / total * 100),
2039
+ percent: Math.round(processedFiles / totalFiles * 100),
1976
2040
  currentFile: itemName
1977
2041
  });
1978
2042
  }