@muhgholy/next-drive 4.23.33 → 4.23.34

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 (44) hide show
  1. package/README.md +79 -3
  2. package/dist/{chunk-HWNIRA6Y.cjs → chunk-DHIHCER6.cjs} +220 -71
  3. package/dist/chunk-DHIHCER6.cjs.map +1 -0
  4. package/dist/{chunk-2DCZENJ2.js → chunk-E72MTT6W.js} +3 -3
  5. package/dist/{chunk-2DCZENJ2.js.map → chunk-E72MTT6W.js.map} +1 -1
  6. package/dist/{chunk-WUG4ATLH.cjs → chunk-PKNXKHGS.cjs} +37 -37
  7. package/dist/{chunk-WUG4ATLH.cjs.map → chunk-PKNXKHGS.cjs.map} +1 -1
  8. package/dist/{chunk-IWV6G7HQ.js → chunk-QMPV4YSZ.js} +221 -73
  9. package/dist/chunk-QMPV4YSZ.js.map +1 -0
  10. package/dist/server/controllers/drive.d.ts +33 -2
  11. package/dist/server/controllers/drive.d.ts.map +1 -1
  12. package/dist/server/errors.d.ts +12 -0
  13. package/dist/server/errors.d.ts.map +1 -0
  14. package/dist/server/express.cjs +16 -12
  15. package/dist/server/express.cjs.map +1 -1
  16. package/dist/server/express.d.ts +3 -0
  17. package/dist/server/express.d.ts.map +1 -1
  18. package/dist/server/express.js +4 -4
  19. package/dist/server/express.js.map +1 -1
  20. package/dist/server/hono.cjs +16 -12
  21. package/dist/server/hono.cjs.map +1 -1
  22. package/dist/server/hono.d.ts +3 -0
  23. package/dist/server/hono.d.ts.map +1 -1
  24. package/dist/server/hono.js +4 -4
  25. package/dist/server/hono.js.map +1 -1
  26. package/dist/server/index.cjs +23 -19
  27. package/dist/server/index.d.ts +3 -0
  28. package/dist/server/index.d.ts.map +1 -1
  29. package/dist/server/index.js +2 -2
  30. package/dist/server/storage-adapters/google.d.ts.map +1 -1
  31. package/dist/server/tus.d.ts.map +1 -1
  32. package/dist/tus-EQ2IXSRE.cjs +20 -0
  33. package/dist/tus-EQ2IXSRE.cjs.map +1 -0
  34. package/dist/tus-ODCMFEBZ.js +3 -0
  35. package/dist/tus-ODCMFEBZ.js.map +1 -0
  36. package/dist/types/server/storage.d.ts +4 -1
  37. package/dist/types/server/storage.d.ts.map +1 -1
  38. package/package.json +1 -1
  39. package/dist/chunk-HWNIRA6Y.cjs.map +0 -1
  40. package/dist/chunk-IWV6G7HQ.js.map +0 -1
  41. package/dist/tus-5LLFMVQK.cjs +0 -20
  42. package/dist/tus-5LLFMVQK.cjs.map +0 -1
  43. package/dist/tus-C4T5ZDTG.js +0 -3
  44. package/dist/tus-C4T5ZDTG.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { Readable } from 'stream';
3
+ import { PassThrough, Readable } from 'stream';
4
4
  import mongoose, { Schema, isValidObjectId } from 'mongoose';
5
5
  import { Server, EVENTS } from '@tus/server';
6
6
  import { FileStore } from '@tus/file-store';
@@ -505,6 +505,15 @@ var getImageSettings = (fileSizeInBytes, qualityPreset, display, size, fit, posi
505
505
  ...resolvedPosition && { position: resolvedPosition }
506
506
  };
507
507
  };
508
+
509
+ // src/server/errors.ts
510
+ var DriveError = class extends Error {
511
+ constructor(code, message) {
512
+ super(message);
513
+ this.name = "DriveError";
514
+ this.code = code;
515
+ }
516
+ };
508
517
  var generatePlaceholderThumbnail = async (outputPath, mimeType) => {
509
518
  const typeParts = mimeType.split("/");
510
519
  const subtype = typeParts[1] || "file";
@@ -961,7 +970,7 @@ var GoogleDriveProvider = {
961
970
  return { usedInBytes: 0, quotaInBytes: 0 };
962
971
  }
963
972
  },
964
- openStream: async (item, accountId) => {
973
+ openStream: async (item, accountId, options) => {
965
974
  if (item.information.type === "FOLDER") throw new Error("Could not open Google Drive file: folders cannot be streamed");
966
975
  const cachePath = path.join(getDriveConfig().storage.path, "file", item._id.toString(), "data.bin");
967
976
  if (fs.existsSync(cachePath)) {
@@ -978,24 +987,36 @@ var GoogleDriveProvider = {
978
987
  { fileId: item.provider.google.id, alt: "media" },
979
988
  { responseType: "stream" }
980
989
  );
981
- const fileDir = path.dirname(cachePath);
982
- if (!fs.existsSync(fileDir)) fs.mkdirSync(fileDir, { recursive: true });
983
- const tempPath = `${cachePath}.tmp`;
984
- const writeStream = fs.createWriteStream(tempPath);
985
- res.data.pipe(writeStream);
986
- writeStream.on("finish", () => {
987
- try {
988
- fs.renameSync(tempPath, cachePath);
989
- } catch {
990
- fs.unlinkSync(tempPath);
991
- }
992
- });
993
- writeStream.on("error", () => fs.unlink(tempPath, () => {
994
- }));
990
+ let cacheWritePromise;
991
+ if (!options?.skipCache) {
992
+ const fileDir = path.dirname(cachePath);
993
+ if (!fs.existsSync(fileDir)) fs.mkdirSync(fileDir, { recursive: true });
994
+ const tempPath = `${cachePath}.tmp`;
995
+ const writeStream = fs.createWriteStream(tempPath);
996
+ res.data.pipe(writeStream);
997
+ cacheWritePromise = new Promise((resolve, reject) => {
998
+ writeStream.on("finish", () => {
999
+ try {
1000
+ fs.renameSync(tempPath, cachePath);
1001
+ resolve();
1002
+ } catch {
1003
+ fs.unlink(tempPath, () => {
1004
+ });
1005
+ reject(new Error("Could not cache Google Drive file: failed to finalize the cached file"));
1006
+ }
1007
+ });
1008
+ writeStream.on("error", (err) => {
1009
+ fs.unlink(tempPath, () => {
1010
+ });
1011
+ reject(err);
1012
+ });
1013
+ });
1014
+ }
995
1015
  return {
996
1016
  stream: res.data,
997
1017
  mime: item.information.mime,
998
- size: item.information.sizeInBytes
1018
+ size: item.information.sizeInBytes,
1019
+ cacheWritePromise
999
1020
  };
1000
1021
  },
1001
1022
  getThumbnail: async (item, accountId) => {
@@ -1259,28 +1280,44 @@ var driveAddSignedUrlToken = (item, config) => {
1259
1280
  var driveAddSignedUrlTokens = (items, config) => {
1260
1281
  return items.map((item) => driveAddSignedUrlToken(item, config));
1261
1282
  };
1262
- var driveReadFile = async (file) => {
1283
+ var driveReadFile = async (file, options) => {
1263
1284
  let drive;
1264
1285
  if (typeof file === "string") {
1265
1286
  const doc = await drive_default.findById(file);
1266
- if (!doc) throw new Error("Could not read file: the file no longer exists");
1287
+ if (!doc) throw new DriveError("NOT_FOUND", "Could not read file: the file no longer exists");
1267
1288
  drive = doc;
1268
1289
  } else if ("toClient" in file) {
1269
1290
  drive = file;
1270
1291
  } else {
1271
- throw new Error("Could not read file: invalid file reference provided");
1292
+ throw new DriveError("INVALID_REFERENCE", "Could not read file: invalid file reference provided");
1272
1293
  }
1273
1294
  if (drive.information.type !== "FILE") {
1274
- throw new Error("Could not read file: this item is a folder, not a file");
1295
+ throw new DriveError("INVALID_TYPE", "Could not read file: this item is a folder, not a file");
1275
1296
  }
1276
1297
  const provider = drive.provider?.type === "GOOGLE" ? GoogleDriveProvider : LocalStorageProvider;
1277
1298
  const accountId = drive.storageAccountId?.toString();
1278
- return await provider.openStream(drive, accountId);
1299
+ const driveId = String(drive._id);
1300
+ let result;
1301
+ if (provider === GoogleDriveProvider && cacheWriteLocks.has(driveId)) {
1302
+ result = await provider.openStream(drive, accountId, { skipCache: true });
1303
+ } else {
1304
+ result = await provider.openStream(drive, accountId);
1305
+ if (result.cacheWritePromise) {
1306
+ const cacheWritePromise = result.cacheWritePromise;
1307
+ cacheWritePromise.catch(() => {
1308
+ });
1309
+ cacheWriteLocks.set(driveId, cacheWritePromise);
1310
+ cacheWritePromise.finally(() => {
1311
+ if (cacheWriteLocks.get(driveId) === cacheWritePromise) cacheWriteLocks.delete(driveId);
1312
+ });
1313
+ }
1314
+ }
1315
+ return { ...result, stream: instrumentReadableForProgress(result.stream, result.size, options) };
1279
1316
  };
1280
1317
  var driveInfo = async (source) => {
1281
1318
  const fileId = typeof source === "string" ? source : source.id;
1282
1319
  const drive = await drive_default.findById(fileId);
1283
- if (!drive) throw new Error("Could not load file details: the file no longer exists");
1320
+ if (!drive) throw new DriveError("NOT_FOUND", "Could not load file details: the file no longer exists");
1284
1321
  let parentName;
1285
1322
  if (drive.parentId) {
1286
1323
  const parent = await drive_default.findById(drive.parentId);
@@ -1315,19 +1352,107 @@ var driveInfo = async (source) => {
1315
1352
  }
1316
1353
  return info;
1317
1354
  };
1318
- var driveFilePath = async (file) => {
1355
+ var computeTransferPercentage = (bytesDownloaded, totalBytes) => {
1356
+ if (!totalBytes) return 0;
1357
+ return Math.min(100, Math.round(bytesDownloaded / totalBytes * 100));
1358
+ };
1359
+ var cacheWriteLocks = /* @__PURE__ */ new Map();
1360
+ var inFlightGoogleDownloads = /* @__PURE__ */ new Map();
1361
+ var downloadGoogleFileToCache = async (drive, accountId, cachePath, totalBytes, listeners, controller) => {
1362
+ if (controller.signal.aborted) {
1363
+ throw new DriveError("ABORTED", "Could not download file: the download was aborted");
1364
+ }
1365
+ const { stream } = await GoogleDriveProvider.openStream(drive, accountId, { skipCache: true });
1366
+ if (controller.signal.aborted) {
1367
+ stream.destroy();
1368
+ throw new DriveError("ABORTED", "Could not download file: the download was aborted");
1369
+ }
1370
+ const fileDir = path.dirname(cachePath);
1371
+ if (!fs.existsSync(fileDir)) fs.mkdirSync(fileDir, { recursive: true });
1372
+ const tempPath = `${cachePath}.tmp`;
1373
+ const writeStream = fs.createWriteStream(tempPath);
1374
+ const cleanupTempFile = () => fs.unlink(tempPath, () => {
1375
+ });
1376
+ const onAbort = () => {
1377
+ const abortError = new DriveError("ABORTED", "Could not download file: the download was aborted");
1378
+ stream.destroy(abortError);
1379
+ writeStream.destroy(abortError);
1380
+ };
1381
+ controller.signal.addEventListener("abort", onAbort, { once: true });
1382
+ try {
1383
+ let bytesDownloaded = 0;
1384
+ await new Promise((resolve, reject) => {
1385
+ stream.on("data", (chunk) => {
1386
+ bytesDownloaded += chunk.length;
1387
+ const info = {
1388
+ bytesDownloaded,
1389
+ totalBytes,
1390
+ percentage: computeTransferPercentage(bytesDownloaded, totalBytes)
1391
+ };
1392
+ for (const listener of listeners) listener(info);
1393
+ });
1394
+ stream.pipe(writeStream);
1395
+ writeStream.on("finish", resolve);
1396
+ writeStream.on("error", reject);
1397
+ stream.on("error", reject);
1398
+ });
1399
+ } catch (err) {
1400
+ cleanupTempFile();
1401
+ if (controller.signal.aborted) {
1402
+ throw new DriveError("ABORTED", "Could not download file: the download was aborted");
1403
+ }
1404
+ throw new DriveError("DOWNLOAD_FAILED", `Could not download file: ${err instanceof Error ? err.message : "an unknown error occurred"}`);
1405
+ } finally {
1406
+ controller.signal.removeEventListener("abort", onAbort);
1407
+ }
1408
+ try {
1409
+ fs.renameSync(tempPath, cachePath);
1410
+ } catch (err) {
1411
+ if (err instanceof Error && "code" in err && err.code === "EXDEV") {
1412
+ fs.copyFileSync(tempPath, cachePath);
1413
+ fs.unlinkSync(tempPath);
1414
+ } else {
1415
+ cleanupTempFile();
1416
+ throw new DriveError("DOWNLOAD_FAILED", "Could not download file: failed to finalize the cached file");
1417
+ }
1418
+ }
1419
+ };
1420
+ var instrumentReadableForProgress = (source, totalBytes, options) => {
1421
+ if (!options?.onProgress && !options?.signal) return source;
1422
+ const passThrough = new PassThrough();
1423
+ let bytesDownloaded = 0;
1424
+ source.on("data", (chunk) => {
1425
+ bytesDownloaded += chunk.length;
1426
+ options.onProgress?.({
1427
+ bytesDownloaded,
1428
+ totalBytes,
1429
+ percentage: computeTransferPercentage(bytesDownloaded, totalBytes)
1430
+ });
1431
+ });
1432
+ source.on("error", (err) => passThrough.destroy(err));
1433
+ source.pipe(passThrough);
1434
+ if (options.signal) {
1435
+ const onAbort = () => {
1436
+ source.destroy(new DriveError("ABORTED", "Could not read file: the read was aborted"));
1437
+ };
1438
+ if (options.signal.aborted) onAbort();
1439
+ else options.signal.addEventListener("abort", onAbort, { once: true });
1440
+ }
1441
+ return passThrough;
1442
+ };
1443
+ var driveFilePath = async (file, options) => {
1319
1444
  let drive;
1320
1445
  if (typeof file === "string") {
1321
1446
  const doc = await drive_default.findById(file);
1322
- if (!doc) throw new Error("Could not locate file: the file no longer exists");
1447
+ if (!doc) throw new DriveError("NOT_FOUND", "Could not locate file: the file no longer exists");
1323
1448
  drive = doc;
1324
1449
  } else if ("toClient" in file) {
1325
1450
  drive = file;
1326
1451
  } else {
1327
- throw new Error("Could not locate file: invalid file reference provided");
1452
+ throw new DriveError("INVALID_REFERENCE", "Could not locate file: invalid file reference provided");
1328
1453
  }
1329
1454
  if (drive.information.type !== "FILE") {
1330
- throw new Error("Could not locate file: this item is a folder, not a file");
1455
+ throw new DriveError("INVALID_TYPE", "Could not locate file: this item is a folder, not a file");
1331
1456
  }
1332
1457
  const config = getDriveConfig();
1333
1458
  const STORAGE_PATH = config.storage.path;
@@ -1335,7 +1460,7 @@ var driveFilePath = async (file) => {
1335
1460
  if (providerType === "LOCAL") {
1336
1461
  const filePath = path.join(STORAGE_PATH, "file", String(drive._id), "data.bin");
1337
1462
  if (!fs.existsSync(filePath)) {
1338
- throw new Error("Could not locate file: the stored file is missing from disk");
1463
+ throw new DriveError("FILE_MISSING", "Could not locate file: the stored file is missing from disk");
1339
1464
  }
1340
1465
  return Object.freeze({
1341
1466
  path: filePath,
@@ -1346,11 +1471,34 @@ var driveFilePath = async (file) => {
1346
1471
  });
1347
1472
  }
1348
1473
  if (providerType === "GOOGLE") {
1349
- const fileDir = path.join(STORAGE_PATH, "file", String(drive._id));
1474
+ const driveId = String(drive._id);
1475
+ const fileDir = path.join(STORAGE_PATH, "file", driveId);
1350
1476
  const cachedFilePath = path.join(fileDir, "data.bin");
1477
+ const expectedSize = drive.information.sizeInBytes;
1478
+ const isCached = () => {
1479
+ if (!fs.existsSync(cachedFilePath)) return false;
1480
+ return fs.statSync(cachedFilePath).size === expectedSize;
1481
+ };
1482
+ if (isCached()) {
1483
+ return Object.freeze({
1484
+ path: cachedFilePath,
1485
+ name: drive.name,
1486
+ mime: drive.information.mime,
1487
+ size: drive.information.sizeInBytes,
1488
+ provider: "GOOGLE"
1489
+ });
1490
+ }
1351
1491
  if (fs.existsSync(cachedFilePath)) {
1352
- const stats = fs.statSync(cachedFilePath);
1353
- if (stats.size === drive.information.sizeInBytes) {
1492
+ fs.unlinkSync(cachedFilePath);
1493
+ }
1494
+ const accountId = drive.storageAccountId?.toString();
1495
+ const existingLock = cacheWriteLocks.get(driveId);
1496
+ if (existingLock) {
1497
+ try {
1498
+ await existingLock;
1499
+ } catch {
1500
+ }
1501
+ if (isCached()) {
1354
1502
  return Object.freeze({
1355
1503
  path: cachedFilePath,
1356
1504
  name: drive.name,
@@ -1359,31 +1507,26 @@ var driveFilePath = async (file) => {
1359
1507
  provider: "GOOGLE"
1360
1508
  });
1361
1509
  }
1362
- fs.unlinkSync(cachedFilePath);
1363
1510
  }
1364
- const accountId = drive.storageAccountId?.toString();
1365
- const { stream } = await GoogleDriveProvider.openStream(drive, accountId);
1366
- if (!fs.existsSync(fileDir)) {
1367
- fs.mkdirSync(fileDir, { recursive: true });
1511
+ let entry = inFlightGoogleDownloads.get(driveId);
1512
+ if (!entry) {
1513
+ const controller = new AbortController();
1514
+ const listeners = /* @__PURE__ */ new Set();
1515
+ const promise = downloadGoogleFileToCache(drive, accountId, cachedFilePath, expectedSize, listeners, controller).finally(() => {
1516
+ inFlightGoogleDownloads.delete(driveId);
1517
+ if (cacheWriteLocks.get(driveId) === promise) cacheWriteLocks.delete(driveId);
1518
+ });
1519
+ entry = { promise, controller, listeners };
1520
+ inFlightGoogleDownloads.set(driveId, entry);
1521
+ cacheWriteLocks.set(driveId, promise);
1368
1522
  }
1369
- const tempPath = `${cachedFilePath}.tmp`;
1370
- const writeStream = fs.createWriteStream(tempPath);
1371
- await new Promise((resolve, reject) => {
1372
- stream.pipe(writeStream);
1373
- writeStream.on("finish", resolve);
1374
- writeStream.on("error", reject);
1375
- stream.on("error", reject);
1376
- });
1377
- try {
1378
- fs.renameSync(tempPath, cachedFilePath);
1379
- } catch (err) {
1380
- if (err instanceof Error && "code" in err && err.code === "EXDEV") {
1381
- fs.copyFileSync(tempPath, cachedFilePath);
1382
- fs.unlinkSync(tempPath);
1383
- } else {
1384
- throw err;
1385
- }
1523
+ const activeEntry = entry;
1524
+ if (options?.onProgress) activeEntry.listeners.add(options.onProgress);
1525
+ if (options?.signal) {
1526
+ if (options.signal.aborted) activeEntry.controller.abort();
1527
+ else options.signal.addEventListener("abort", () => activeEntry.controller.abort(), { once: true });
1386
1528
  }
1529
+ await activeEntry.promise;
1387
1530
  return Object.freeze({
1388
1531
  path: cachedFilePath,
1389
1532
  name: drive.name,
@@ -1392,7 +1535,7 @@ var driveFilePath = async (file) => {
1392
1535
  provider: "GOOGLE"
1393
1536
  });
1394
1537
  }
1395
- throw new Error(`Could not locate file: unsupported storage provider "${providerType}"`);
1538
+ throw new DriveError("UNSUPPORTED_PROVIDER", `Could not locate file: unsupported storage provider "${providerType}"`);
1396
1539
  };
1397
1540
  var driveList = async (options) => {
1398
1541
  const { key, folderId, accountId, limit = 100, afterId } = options;
@@ -1400,7 +1543,7 @@ var driveList = async (options) => {
1400
1543
  if (accountId && accountId !== "LOCAL") {
1401
1544
  const account = await drive_default.db.model("StorageAccount").findOne({ _id: accountId, owner: key });
1402
1545
  if (!account) {
1403
- throw new Error("Could not list files: storage account not found or access denied");
1546
+ throw new DriveError("ACCESS_DENIED", "Could not list files: storage account not found or access denied");
1404
1547
  }
1405
1548
  if (account.metadata.provider === "GOOGLE") {
1406
1549
  providerName = "GOOGLE";
@@ -1428,7 +1571,7 @@ var driveListFiles = async (options) => {
1428
1571
  if (accountId && accountId !== "LOCAL") {
1429
1572
  const account = await drive_default.db.model("StorageAccount").findOne({ _id: accountId, owner: key });
1430
1573
  if (!account) {
1431
- throw new Error("Could not load files: storage account not found or access denied");
1574
+ throw new DriveError("ACCESS_DENIED", "Could not load files: storage account not found or access denied");
1432
1575
  }
1433
1576
  if (account.metadata.provider === "GOOGLE") {
1434
1577
  providerName = "GOOGLE";
@@ -1472,7 +1615,7 @@ var driveDelete = async (source, options) => {
1472
1615
  let driveId;
1473
1616
  if (typeof source === "string") {
1474
1617
  const doc = await drive_default.findById(source);
1475
- if (!doc) throw new Error("Could not delete: the file no longer exists");
1618
+ if (!doc) throw new DriveError("NOT_FOUND", "Could not delete: the file no longer exists");
1476
1619
  drive = doc;
1477
1620
  driveId = source;
1478
1621
  } else if ("toClient" in source) {
@@ -1480,7 +1623,7 @@ var driveDelete = async (source, options) => {
1480
1623
  driveId = String(drive._id);
1481
1624
  } else {
1482
1625
  const doc = await drive_default.findById(source.id);
1483
- if (!doc) throw new Error("Could not delete: the selected file no longer exists");
1626
+ if (!doc) throw new DriveError("NOT_FOUND", "Could not delete: the selected file no longer exists");
1484
1627
  drive = doc;
1485
1628
  driveId = source.id;
1486
1629
  }
@@ -1492,7 +1635,7 @@ var driveDelete = async (source, options) => {
1492
1635
  trashedAt: null
1493
1636
  });
1494
1637
  if (childCount > 0) {
1495
- throw new Error(`Could not delete folder: it still contains ${childCount} item(s). Enable recursive delete to remove the folder and everything inside it.`);
1638
+ throw new DriveError("FOLDER_NOT_EMPTY", `Could not delete folder: it still contains ${childCount} item(s). Enable recursive delete to remove the folder and everything inside it.`);
1496
1639
  }
1497
1640
  }
1498
1641
  const provider = drive.provider?.type === "GOOGLE" ? GoogleDriveProvider : LocalStorageProvider;
@@ -1503,17 +1646,17 @@ var driveDelete = async (source, options) => {
1503
1646
  var resolveFolderByPath = async (folderPath, owner, accountId) => {
1504
1647
  const normalizedPath = folderPath.replace(/^\/+|\/+$/g, "");
1505
1648
  if (!normalizedPath) {
1506
- throw new Error("Could not resolve folder: the folder path is empty");
1649
+ throw new DriveError("INVALID_PATH", "Could not resolve folder: the folder path is empty");
1507
1650
  }
1508
1651
  const segments = normalizedPath.split("/").filter((s) => s.length > 0);
1509
1652
  if (segments.length === 0) {
1510
- throw new Error("Could not resolve folder: the folder path is invalid");
1653
+ throw new DriveError("INVALID_PATH", "Could not resolve folder: the folder path is invalid");
1511
1654
  }
1512
1655
  let providerName = "LOCAL";
1513
1656
  if (accountId && accountId !== "LOCAL") {
1514
1657
  const account = await drive_default.db.model("StorageAccount").findOne({ _id: accountId, owner });
1515
1658
  if (!account) {
1516
- throw new Error("Could not resolve folder: storage account not found or access denied");
1659
+ throw new DriveError("ACCESS_DENIED", "Could not resolve folder: storage account not found or access denied");
1517
1660
  }
1518
1661
  if (account.metadata.provider === "GOOGLE") {
1519
1662
  providerName = "GOOGLE";
@@ -1547,7 +1690,7 @@ var driveUpload = async (source, key, options) => {
1547
1690
  if (accountId && accountId !== "LOCAL") {
1548
1691
  const account = await drive_default.db.model("StorageAccount").findOne({ _id: accountId, owner: key });
1549
1692
  if (!account) {
1550
- throw new Error("Could not upload: storage account not found or access denied");
1693
+ throw new DriveError("ACCESS_DENIED", "Could not upload: storage account not found or access denied");
1551
1694
  }
1552
1695
  if (account.metadata.provider === "GOOGLE") {
1553
1696
  provider = GoogleDriveProvider;
@@ -1558,7 +1701,7 @@ var driveUpload = async (source, key, options) => {
1558
1701
  let fileSize;
1559
1702
  if (typeof source === "string") {
1560
1703
  if (!fs.existsSync(source)) {
1561
- throw new Error("Could not upload: source file not found");
1704
+ throw new DriveError("FILE_MISSING", "Could not upload: source file not found");
1562
1705
  }
1563
1706
  sourceFilePath = source;
1564
1707
  const stats = fs.statSync(source);
@@ -1617,17 +1760,17 @@ var driveUpload = async (source, key, options) => {
1617
1760
  mimeType = mimeTypes[ext] || "application/octet-stream";
1618
1761
  }
1619
1762
  if (config.security && !validateMimeType(mimeType, config.security.allowedMimeTypes)) {
1620
- throw new Error(`Could not upload: file type "${mimeType}" is not allowed`);
1763
+ throw new DriveError("INVALID_MIME_TYPE", `Could not upload: file type "${mimeType}" is not allowed`);
1621
1764
  }
1622
1765
  if (config.security && fileSize > config.security.maxUploadSizeInBytes) {
1623
- throw new Error("Could not upload: file is larger than the maximum allowed size");
1766
+ throw new DriveError("FILE_TOO_LARGE", "Could not upload: file is larger than the maximum allowed size");
1624
1767
  }
1625
1768
  const isRootMode = config.mode === "ROOT";
1626
1769
  if (!options.enforce && !isRootMode) {
1627
1770
  const information = await getDriveInformation({ method: "KEY", key });
1628
1771
  const quota = await provider.getQuota(key, accountId, information.storage.quotaInBytes);
1629
1772
  if (quota.usedInBytes + fileSize > quota.quotaInBytes) {
1630
- throw new Error("Could not upload: you have run out of storage space");
1773
+ throw new DriveError("QUOTA_EXCEEDED", "Could not upload: you have run out of storage space");
1631
1774
  }
1632
1775
  }
1633
1776
  let resolvedParentId = null;
@@ -1739,7 +1882,7 @@ var driveCleanup = async () => {
1739
1882
  }
1740
1883
  }
1741
1884
  try {
1742
- const { getTusServer: getTusServer2 } = await import('./tus-C4T5ZDTG.js');
1885
+ const { getTusServer: getTusServer2 } = await import('./tus-ODCMFEBZ.js');
1743
1886
  await getTusServer2().cleanUpExpiredUploads();
1744
1887
  } catch (e) {
1745
1888
  console.error("[next-drive] Failed to clean up expired tus uploads:", e);
@@ -1828,8 +1971,13 @@ var getTusServer = () => {
1828
1971
  if (!ctx) return Number.MAX_SAFE_INTEGER;
1829
1972
  return ctx.authenticated ? ctx.config.security?.maxUploadSizeInBytes ?? Number.MAX_SAFE_INTEGER : ctx.config.security?.unauthenticated?.maxUploadSizeInBytes ?? 0;
1830
1973
  },
1831
- // ** Keep the upload id in the query string so every method stays on `?action=upload`.
1832
- generateUrl: (_req, { path: p, id }) => `${p}?action=upload&id=${id}`,
1974
+ // ** Keep the upload id in the query string so every method stays on `?action=upload`. Derive
1975
+ // ** the path from the ACTUAL incoming request rather than the static `apiPath` this server was
1976
+ // ** constructed with - the two can drift apart (config.apiUrl is frozen on `globalThis` for the
1977
+ // ** life of the process, see getTusServer() above, so it can go stale after a proxy/route change
1978
+ // ** without a full restart). Mirroring the request's own path makes the continuation URL always
1979
+ // ** match wherever the client actually is, regardless of what `apiUrl` was configured with.
1980
+ generateUrl: (req, { id }) => `${new URL(req.url).pathname}?action=upload&id=${id}`,
1833
1981
  // ** SECURITY: the id becomes a filesystem path inside FileStore (fs.unlink/read/resolve), and it
1834
1982
  // ** arrives from the client-controlled query string. Only accept the exact format our
1835
1983
  // ** namingFunction produces (32 lowercase hex) so a crafted `?id=../../..` cannot traverse out
@@ -1984,6 +2132,6 @@ var handleUpload = async (req, res, ctx) => {
1984
2132
  res.end(response.body ? Buffer.from(await response.arrayBuffer()) : void 0);
1985
2133
  };
1986
2134
 
1987
- export { GoogleDriveProvider, LocalStorageProvider, account_default, driveCleanup, driveConfiguration, driveConfirm, driveDelete, driveFilePath, driveGetUrl, driveInfo, driveList, driveListFiles, drivePurgeExpired, driveReadFile, driveUpload, drive_default, getDriveConfig, getDriveInformation, getImageSettings, getTusServer, handleUpload, resolveProvider, resolveTusCorsOptions, withSignedUrl, withSignedUrls };
1988
- //# sourceMappingURL=chunk-IWV6G7HQ.js.map
1989
- //# sourceMappingURL=chunk-IWV6G7HQ.js.map
2135
+ export { DriveError, GoogleDriveProvider, LocalStorageProvider, account_default, driveCleanup, driveConfiguration, driveConfirm, driveDelete, driveFilePath, driveGetUrl, driveInfo, driveList, driveListFiles, drivePurgeExpired, driveReadFile, driveUpload, drive_default, getDriveConfig, getDriveInformation, getImageSettings, getTusServer, handleUpload, resolveProvider, resolveTusCorsOptions, withSignedUrl, withSignedUrls };
2136
+ //# sourceMappingURL=chunk-QMPV4YSZ.js.map
2137
+ //# sourceMappingURL=chunk-QMPV4YSZ.js.map