@muhgholy/next-drive 4.6.0 → 4.8.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.
@@ -370,70 +370,58 @@ var extractImageMetadata = async (filePath) => {
370
370
  }
371
371
  };
372
372
  var DISPLAY_PRESETS = {
373
- "article-header": 0.9,
374
- // Hero/banner images - high quality
375
- "article-image": 0.85,
376
- // In-content images
377
- "thumbnail": 0.7,
378
- // Small previews - lower quality ok
379
- "avatar": 0.8,
380
- // Profile pictures
381
- "logo": 0.95,
382
- // Branding - needs clarity
383
- "card": 0.8,
384
- // Card components
385
- "gallery": 0.85,
386
- // Gallery/grid images
387
- "og": 0.9,
388
- // Open Graph/social sharing
389
- "icon": 0.75,
390
- // Small icons
391
- "cover": 0.9,
392
- // Full-width covers
393
- "story": 0.85
394
- // Story/vertical format
373
+ "article-header": { ratio: [16, 9], baseWidth: 1200, qualityFactor: 0.9 },
374
+ "article-image": { ratio: [16, 9], baseWidth: 800, qualityFactor: 0.85 },
375
+ "thumbnail": { ratio: [1, 1], baseWidth: 150, qualityFactor: 0.7 },
376
+ "avatar": { ratio: [1, 1], baseWidth: 128, qualityFactor: 0.8 },
377
+ "logo": { ratio: [2, 1], baseWidth: 200, qualityFactor: 0.95 },
378
+ "card": { ratio: [4, 3], baseWidth: 400, qualityFactor: 0.8 },
379
+ "gallery": { ratio: [1, 1], baseWidth: 600, qualityFactor: 0.85 },
380
+ "og": { ratio: [1200, 630], baseWidth: 1200, qualityFactor: 0.9 },
381
+ "icon": { ratio: [1, 1], baseWidth: 48, qualityFactor: 0.75 },
382
+ "cover": { ratio: [16, 9], baseWidth: 1920, qualityFactor: 0.9 },
383
+ "story": { ratio: [9, 16], baseWidth: 1080, qualityFactor: 0.85 },
384
+ "video": { ratio: [16, 9], baseWidth: 1280, qualityFactor: 0.85 },
385
+ "banner": { ratio: [3, 1], baseWidth: 1200, qualityFactor: 0.9 },
386
+ "portrait": { ratio: [3, 4], baseWidth: 600, qualityFactor: 0.85 },
387
+ "landscape": { ratio: [4, 3], baseWidth: 800, qualityFactor: 0.85 }
395
388
  };
396
- var SIZE_PRESETS = {
397
- // Square sizes
389
+ var SIZE_SCALES = {
390
+ "xs": 0.25,
391
+ "sm": 0.5,
392
+ "md": 1,
393
+ "lg": 1.5,
394
+ "xl": 2,
395
+ "2xl": 2.5
396
+ };
397
+ var STANDALONE_SIZES = {
398
398
  "xs": { width: 64, height: 64 },
399
399
  "sm": { width: 128, height: 128 },
400
400
  "md": { width: 256, height: 256 },
401
401
  "lg": { width: 512, height: 512 },
402
402
  "xl": { width: 1024, height: 1024 },
403
403
  "2xl": { width: 1600, height: 1600 },
404
- // Named squares
405
404
  "icon": { width: 48, height: 48 },
406
405
  "thumb": { width: 150, height: 150 },
407
406
  "square": { width: 600, height: 600 },
408
407
  "avatar-sm": { width: 64, height: 64 },
409
408
  "avatar-md": { width: 128, height: 128 },
410
409
  "avatar-lg": { width: 256, height: 256 },
411
- // Landscape (16:9)
412
410
  "landscape-sm": { width: 480, height: 270 },
413
411
  "landscape": { width: 800, height: 450 },
414
412
  "landscape-lg": { width: 1280, height: 720 },
415
413
  "landscape-xl": { width: 1920, height: 1080 },
416
- // Portrait (9:16)
417
414
  "portrait-sm": { width: 270, height: 480 },
418
415
  "portrait": { width: 450, height: 800 },
419
416
  "portrait-lg": { width: 720, height: 1280 },
420
- // Wide/Banner (OG, social)
421
417
  "wide": { width: 1200, height: 630 },
422
- // Open Graph standard
423
418
  "banner": { width: 1200, height: 400 },
424
- // Banner/header
425
419
  "banner-sm": { width: 800, height: 200 },
426
- // Classic photo ratios
427
420
  "photo-4x3": { width: 800, height: 600 },
428
- // 4:3
429
421
  "photo-3x2": { width: 900, height: 600 },
430
- // 3:2
431
- // Story/vertical (9:16)
432
422
  "story": { width: 1080, height: 1920 },
433
- // Video thumbnails
434
423
  "video": { width: 1280, height: 720 },
435
424
  "video-sm": { width: 640, height: 360 },
436
- // Card sizes
437
425
  "card-sm": { width: 300, height: 200 },
438
426
  "card": { width: 400, height: 300 },
439
427
  "card-lg": { width: 600, height: 400 }
@@ -447,8 +435,24 @@ var getImageSettings = (fileSizeInBytes, qualityPreset, display, size) => {
447
435
  const n = parseInt(qualityPreset, 10);
448
436
  if (!isNaN(n)) baseQuality = Math.min(100, Math.max(1, n));
449
437
  }
450
- const displayFactor = display && DISPLAY_PRESETS[display] ? DISPLAY_PRESETS[display] : 1;
451
- baseQuality = Math.round(baseQuality * displayFactor);
438
+ let width;
439
+ let height;
440
+ let qualityFactor = 1;
441
+ const displayPreset = display ? DISPLAY_PRESETS[display] : void 0;
442
+ if (displayPreset) {
443
+ qualityFactor = displayPreset.qualityFactor;
444
+ const [ratioW, ratioH] = displayPreset.ratio;
445
+ const scale = size && SIZE_SCALES[size] ? SIZE_SCALES[size] : 1;
446
+ width = Math.round(displayPreset.baseWidth * scale);
447
+ height = Math.round(width * ratioH / ratioW);
448
+ } else if (size) {
449
+ const standalone = STANDALONE_SIZES[size];
450
+ if (standalone) {
451
+ width = standalone.width;
452
+ height = standalone.height;
453
+ }
454
+ }
455
+ baseQuality = Math.round(baseQuality * qualityFactor);
452
456
  let quality = baseQuality;
453
457
  let effort = 4;
454
458
  let pngCompression = 6;
@@ -476,12 +480,11 @@ var getImageSettings = (fileSizeInBytes, qualityPreset, display, size) => {
476
480
  pngCompression = 7;
477
481
  }
478
482
  }
479
- const dimensions = size && SIZE_PRESETS[size] ? SIZE_PRESETS[size] : void 0;
480
483
  return {
481
484
  quality: Math.max(1, Math.min(100, quality)),
482
485
  effort,
483
486
  pngCompression,
484
- ...dimensions && { width: dimensions.width, height: dimensions.height }
487
+ ...width && height && { width, height }
485
488
  };
486
489
  };
487
490
  var objectIdSchema = zod.z.string().refine((val) => mongoose.isValidObjectId(val), {
@@ -1430,6 +1433,46 @@ var driveDelete = async (source, options) => {
1430
1433
  const owner = drive.owner;
1431
1434
  await provider.delete([driveId], owner, accountId);
1432
1435
  };
1436
+ var resolveFolderByPath = async (folderPath, owner, accountId) => {
1437
+ const normalizedPath = folderPath.replace(/^\/+|\/+$/g, "");
1438
+ if (!normalizedPath) {
1439
+ throw new Error("Folder path cannot be empty");
1440
+ }
1441
+ const segments = normalizedPath.split("/").filter((s) => s.length > 0);
1442
+ if (segments.length === 0) {
1443
+ throw new Error("Invalid folder path");
1444
+ }
1445
+ let providerName = "LOCAL";
1446
+ if (accountId && accountId !== "LOCAL") {
1447
+ const account = await drive_default.db.model("StorageAccount").findOne({ _id: accountId, owner });
1448
+ if (!account) {
1449
+ throw new Error("Invalid Storage Account");
1450
+ }
1451
+ if (account.metadata.provider === "GOOGLE") {
1452
+ providerName = "GOOGLE";
1453
+ }
1454
+ }
1455
+ let currentParentId = null;
1456
+ for (const segmentName of segments) {
1457
+ const existingFolder = await drive_default.findOne({
1458
+ owner,
1459
+ "provider.type": providerName,
1460
+ storageAccountId: accountId || null,
1461
+ parentId: currentParentId,
1462
+ name: segmentName,
1463
+ "information.type": "FOLDER",
1464
+ trashedAt: null
1465
+ });
1466
+ if (existingFolder) {
1467
+ currentParentId = String(existingFolder._id);
1468
+ } else {
1469
+ const provider = providerName === "GOOGLE" ? GoogleDriveProvider : LocalStorageProvider;
1470
+ const newFolder = await provider.createFolder(segmentName, currentParentId, owner, accountId);
1471
+ currentParentId = newFolder.id;
1472
+ }
1473
+ }
1474
+ return currentParentId;
1475
+ };
1433
1476
  var driveUpload = async (source, key, options) => {
1434
1477
  const config = getDriveConfig();
1435
1478
  let provider = LocalStorageProvider;
@@ -1519,12 +1562,20 @@ var driveUpload = async (source, key, options) => {
1519
1562
  throw new Error("Storage quota exceeded");
1520
1563
  }
1521
1564
  }
1565
+ let resolvedParentId = null;
1566
+ if (options.folder && "path" in options.folder) {
1567
+ resolvedParentId = await resolveFolderByPath(options.folder.path, key, accountId);
1568
+ } else if (options.folder && "id" in options.folder && options.folder.id !== "root") {
1569
+ resolvedParentId = options.folder.id;
1570
+ } else if (options.parentId && options.parentId !== "root") {
1571
+ resolvedParentId = options.parentId;
1572
+ }
1522
1573
  const drive = new drive_default({
1523
1574
  owner: key,
1524
1575
  storageAccountId: accountId || null,
1525
1576
  provider: { type: provider.name },
1526
1577
  name: options.name,
1527
- parentId: options.parentId === "root" || !options.parentId ? null : options.parentId,
1578
+ parentId: resolvedParentId,
1528
1579
  order: await getNextOrderValue(key),
1529
1580
  information: { type: "FILE", sizeInBytes: fileSize, mime: mimeType, path: "" },
1530
1581
  status: "UPLOADING"
@@ -1685,6 +1736,9 @@ var driveAPIHandler = async (req, res) => {
1685
1736
  const settings = getImageSettings(fileSize, quality, display, sizePreset);
1686
1737
  let targetFormat = format || mime.split("/")[1];
1687
1738
  if (targetFormat === "jpg") targetFormat = "jpeg";
1739
+ if (!["jpeg", "png", "webp", "avif"].includes(targetFormat)) {
1740
+ targetFormat = format || "webp";
1741
+ }
1688
1742
  const cacheDir = path__default.default.join(config.storage.path, "file", drive._id.toString(), "cache");
1689
1743
  const cacheKey = [
1690
1744
  "opt",
@@ -1721,13 +1775,17 @@ var driveAPIHandler = async (req, res) => {
1721
1775
  pipeline = pipeline.png({ compressionLevel: settings.pngCompression, adaptiveFiltering: true });
1722
1776
  res.setHeader("Content-Type", "image/png");
1723
1777
  } else if (targetFormat === "webp") {
1724
- pipeline = pipeline.webp({ quality: settings.quality, effort: settings.effort });
1778
+ const webpEffort = Math.min(settings.effort, 6);
1779
+ pipeline = pipeline.webp({ quality: settings.quality, effort: webpEffort });
1725
1780
  res.setHeader("Content-Type", "image/webp");
1726
1781
  } else if (targetFormat === "avif") {
1727
1782
  pipeline = pipeline.avif({ quality: settings.quality, effort: settings.effort });
1728
1783
  res.setHeader("Content-Type", "image/avif");
1729
1784
  }
1730
1785
  res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
1786
+ pipeline.on("error", (err) => {
1787
+ console.error("[next-drive] Pipeline error:", err);
1788
+ });
1731
1789
  stream.pipe(pipeline);
1732
1790
  pipeline.clone().toFile(cachePath).catch((e) => console.error("[next-drive] Cache write failed:", e));
1733
1791
  pipeline.clone().pipe(res);
@@ -2282,5 +2340,5 @@ exports.driveReadFile = driveReadFile;
2282
2340
  exports.driveUpload = driveUpload;
2283
2341
  exports.getDriveConfig = getDriveConfig;
2284
2342
  exports.getDriveInformation = getDriveInformation;
2285
- //# sourceMappingURL=chunk-I3AR7V7Z.cjs.map
2286
- //# sourceMappingURL=chunk-I3AR7V7Z.cjs.map
2343
+ //# sourceMappingURL=chunk-TAG5UMWA.cjs.map
2344
+ //# sourceMappingURL=chunk-TAG5UMWA.cjs.map