@gallop.software/studio 1.3.1 → 1.3.2

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.
@@ -1393,9 +1393,143 @@ async function handleReprocess(request) {
1393
1393
  return _server.NextResponse.json({ error: "Failed to reprocess images" }, { status: 500 });
1394
1394
  }
1395
1395
  }
1396
- async function handleProcessAllStream() {
1396
+ async function handleReprocessStream(request) {
1397
1397
  const publicUrl = _optionalChain([process, 'access', _38 => _38.env, 'access', _39 => _39.CLOUDFLARE_R2_PUBLIC_URL, 'optionalAccess', _40 => _40.replace, 'call', _41 => _41(/\/\s*$/, "")]);
1398
1398
  const encoder = new TextEncoder();
1399
+ let imageKeys;
1400
+ try {
1401
+ const body = await request.json();
1402
+ imageKeys = body.imageKeys;
1403
+ if (!imageKeys || !Array.isArray(imageKeys) || imageKeys.length === 0) {
1404
+ return _server.NextResponse.json({ error: "No image keys provided" }, { status: 400 });
1405
+ }
1406
+ } catch (e29) {
1407
+ return _server.NextResponse.json({ error: "Invalid request body" }, { status: 400 });
1408
+ }
1409
+ const stream = new ReadableStream({
1410
+ async start(controller) {
1411
+ const sendEvent = (data) => {
1412
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
1413
+
1414
+ `));
1415
+ };
1416
+ try {
1417
+ const meta = await loadMeta();
1418
+ const cdnUrls = getCdnUrls(meta);
1419
+ const processed = [];
1420
+ const errors = [];
1421
+ const urlsToPurge = [];
1422
+ const total = imageKeys.length;
1423
+ sendEvent({ type: "start", total });
1424
+ for (let i = 0; i < imageKeys.length; i++) {
1425
+ let imageKey = imageKeys[i];
1426
+ if (!imageKey.startsWith("/")) {
1427
+ imageKey = `/${imageKey}`;
1428
+ }
1429
+ sendEvent({
1430
+ type: "progress",
1431
+ current: i + 1,
1432
+ total,
1433
+ percent: Math.round((i + 1) / total * 100),
1434
+ message: `Processing ${imageKey.slice(1)}...`
1435
+ });
1436
+ try {
1437
+ let buffer;
1438
+ const entry = getMetaEntry(meta, imageKey);
1439
+ const existingCdnIndex = _optionalChain([entry, 'optionalAccess', _42 => _42.c]);
1440
+ const existingCdnUrl = existingCdnIndex !== void 0 ? cdnUrls[existingCdnIndex] : void 0;
1441
+ const isInOurR2 = existingCdnUrl === publicUrl;
1442
+ const isRemote = existingCdnIndex !== void 0 && !isInOurR2;
1443
+ const originalPath = _path2.default.join(process.cwd(), "public", imageKey);
1444
+ try {
1445
+ buffer = await _fs.promises.readFile(originalPath);
1446
+ } catch (e30) {
1447
+ if (isInOurR2) {
1448
+ buffer = await downloadFromCdn(imageKey);
1449
+ const dir = _path2.default.dirname(originalPath);
1450
+ await _fs.promises.mkdir(dir, { recursive: true });
1451
+ await _fs.promises.writeFile(originalPath, buffer);
1452
+ } else if (isRemote && existingCdnUrl) {
1453
+ const remoteUrl = `${existingCdnUrl}${imageKey}`;
1454
+ buffer = await downloadFromRemoteUrl(remoteUrl);
1455
+ const dir = _path2.default.dirname(originalPath);
1456
+ await _fs.promises.mkdir(dir, { recursive: true });
1457
+ await _fs.promises.writeFile(originalPath, buffer);
1458
+ } else {
1459
+ throw new Error(`File not found: ${imageKey}`);
1460
+ }
1461
+ }
1462
+ const ext = _path2.default.extname(imageKey).toLowerCase();
1463
+ const isSvg = ext === ".svg";
1464
+ if (isSvg) {
1465
+ const imageDir = _path2.default.dirname(imageKey.slice(1));
1466
+ const imagesPath = _path2.default.join(process.cwd(), "public", "images", imageDir === "." ? "" : imageDir);
1467
+ await _fs.promises.mkdir(imagesPath, { recursive: true });
1468
+ const fileName = _path2.default.basename(imageKey);
1469
+ const destPath = _path2.default.join(imagesPath, fileName);
1470
+ await _fs.promises.writeFile(destPath, buffer);
1471
+ meta[imageKey] = {
1472
+ ...entry,
1473
+ o: { w: 0, h: 0 },
1474
+ b: "",
1475
+ f: { w: 0, h: 0 }
1476
+ };
1477
+ if (isRemote) {
1478
+ delete meta[imageKey].c;
1479
+ }
1480
+ } else {
1481
+ const updatedEntry = await processImage(buffer, imageKey);
1482
+ if (isInOurR2) {
1483
+ updatedEntry.c = existingCdnIndex;
1484
+ await uploadToCdn(imageKey);
1485
+ for (const thumbPath of _chunkWOHZ4LYGjs.getAllThumbnailPaths.call(void 0, imageKey)) {
1486
+ urlsToPurge.push(`${publicUrl}${thumbPath}`);
1487
+ }
1488
+ await deleteLocalThumbnails(imageKey);
1489
+ try {
1490
+ await _fs.promises.unlink(originalPath);
1491
+ } catch (e31) {
1492
+ }
1493
+ }
1494
+ meta[imageKey] = updatedEntry;
1495
+ }
1496
+ processed.push(imageKey);
1497
+ } catch (error) {
1498
+ console.error(`Failed to reprocess ${imageKey}:`, error);
1499
+ errors.push(imageKey);
1500
+ }
1501
+ }
1502
+ sendEvent({ type: "cleanup", message: "Saving metadata..." });
1503
+ await saveMeta(meta);
1504
+ if (urlsToPurge.length > 0) {
1505
+ sendEvent({ type: "cleanup", message: "Purging CDN cache..." });
1506
+ await purgeCloudflareCache(urlsToPurge);
1507
+ }
1508
+ sendEvent({
1509
+ type: "complete",
1510
+ processed: processed.length,
1511
+ errors: errors.length,
1512
+ message: `Processed ${processed.length} image${processed.length !== 1 ? "s" : ""}${errors.length > 0 ? `, ${errors.length} error${errors.length !== 1 ? "s" : ""}` : ""}`
1513
+ });
1514
+ controller.close();
1515
+ } catch (error) {
1516
+ console.error("Reprocess stream error:", error);
1517
+ sendEvent({ type: "error", message: "Failed to process images" });
1518
+ controller.close();
1519
+ }
1520
+ }
1521
+ });
1522
+ return new Response(stream, {
1523
+ headers: {
1524
+ "Content-Type": "text/event-stream",
1525
+ "Cache-Control": "no-cache",
1526
+ Connection: "keep-alive"
1527
+ }
1528
+ });
1529
+ }
1530
+ async function handleProcessAllStream() {
1531
+ const publicUrl = _optionalChain([process, 'access', _43 => _43.env, 'access', _44 => _44.CLOUDFLARE_R2_PUBLIC_URL, 'optionalAccess', _45 => _45.replace, 'call', _46 => _46(/\/\s*$/, "")]);
1532
+ const encoder = new TextEncoder();
1399
1533
  const stream = new ReadableStream({
1400
1534
  async start(controller) {
1401
1535
  const sendEvent = (data) => {
@@ -1488,7 +1622,7 @@ async function handleProcessAllStream() {
1488
1622
  await deleteLocalThumbnails(key);
1489
1623
  try {
1490
1624
  await _fs.promises.unlink(fullPath);
1491
- } catch (e29) {
1625
+ } catch (e32) {
1492
1626
  }
1493
1627
  }
1494
1628
  processed.push(key.slice(1));
@@ -1527,13 +1661,13 @@ async function handleProcessAllStream() {
1527
1661
  }
1528
1662
  }
1529
1663
  }
1530
- } catch (e30) {
1664
+ } catch (e33) {
1531
1665
  }
1532
1666
  }
1533
1667
  const imagesDir = _path2.default.join(process.cwd(), "public", "images");
1534
1668
  try {
1535
1669
  await findOrphans(imagesDir);
1536
- } catch (e31) {
1670
+ } catch (e34) {
1537
1671
  }
1538
1672
  async function removeEmptyDirs(dir) {
1539
1673
  try {
@@ -1551,13 +1685,13 @@ async function handleProcessAllStream() {
1551
1685
  await _fs.promises.rmdir(dir);
1552
1686
  }
1553
1687
  return isEmpty;
1554
- } catch (e32) {
1688
+ } catch (e35) {
1555
1689
  return true;
1556
1690
  }
1557
1691
  }
1558
1692
  try {
1559
1693
  await removeEmptyDirs(imagesDir);
1560
- } catch (e33) {
1694
+ } catch (e36) {
1561
1695
  }
1562
1696
  await saveMeta(meta);
1563
1697
  if (urlsToPurge.length > 0) {
@@ -1625,7 +1759,7 @@ async function handleScanStream() {
1625
1759
  allFiles.push({ relativePath: relPath, fullPath });
1626
1760
  }
1627
1761
  }
1628
- } catch (e34) {
1762
+ } catch (e37) {
1629
1763
  }
1630
1764
  }
1631
1765
  const publicDir = _path2.default.join(process.cwd(), "public");
@@ -1684,7 +1818,7 @@ async function handleScanStream() {
1684
1818
  o: { w: metadata.width || 0, h: metadata.height || 0 },
1685
1819
  b: blurhash
1686
1820
  };
1687
- } catch (e35) {
1821
+ } catch (e38) {
1688
1822
  meta[imageKey] = { o: { w: 0, h: 0 } };
1689
1823
  }
1690
1824
  }
@@ -1724,13 +1858,13 @@ async function handleScanStream() {
1724
1858
  }
1725
1859
  }
1726
1860
  }
1727
- } catch (e36) {
1861
+ } catch (e39) {
1728
1862
  }
1729
1863
  }
1730
1864
  const imagesDir = _path2.default.join(process.cwd(), "public", "images");
1731
1865
  try {
1732
1866
  await findOrphans(imagesDir);
1733
- } catch (e37) {
1867
+ } catch (e40) {
1734
1868
  }
1735
1869
  await saveMeta(meta);
1736
1870
  sendEvent({
@@ -1797,13 +1931,13 @@ async function handleDeleteOrphans(request) {
1797
1931
  await _fs.promises.rmdir(dir);
1798
1932
  }
1799
1933
  return isEmpty;
1800
- } catch (e38) {
1934
+ } catch (e41) {
1801
1935
  return true;
1802
1936
  }
1803
1937
  }
1804
1938
  try {
1805
1939
  await removeEmptyDirs(imagesDir);
1806
- } catch (e39) {
1940
+ } catch (e42) {
1807
1941
  }
1808
1942
  return _server.NextResponse.json({
1809
1943
  success: true,
@@ -1985,6 +2119,9 @@ async function POST(request) {
1985
2119
  if (route === "reprocess") {
1986
2120
  return handleReprocess(request);
1987
2121
  }
2122
+ if (route === "reprocess-stream") {
2123
+ return handleReprocessStream(request);
2124
+ }
1988
2125
  if (route === "process-all") {
1989
2126
  return handleProcessAllStream();
1990
2127
  }