@memberjunction/storage 3.1.1 → 3.2.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 (47) hide show
  1. package/dist/__tests__/FileStorageBase.test.d.ts +6 -0
  2. package/dist/__tests__/FileStorageBase.test.d.ts.map +1 -0
  3. package/dist/__tests__/FileStorageBase.test.js +213 -0
  4. package/dist/__tests__/FileStorageBase.test.js.map +1 -0
  5. package/dist/__tests__/util.test.d.ts +7 -0
  6. package/dist/__tests__/util.test.d.ts.map +1 -0
  7. package/dist/__tests__/util.test.js +326 -0
  8. package/dist/__tests__/util.test.js.map +1 -0
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +28 -14
  11. package/dist/config.js.map +1 -1
  12. package/dist/drivers/AWSFileStorage.d.ts +20 -0
  13. package/dist/drivers/AWSFileStorage.d.ts.map +1 -1
  14. package/dist/drivers/AWSFileStorage.js +43 -18
  15. package/dist/drivers/AWSFileStorage.js.map +1 -1
  16. package/dist/drivers/AzureFileStorage.d.ts +1 -1
  17. package/dist/drivers/AzureFileStorage.d.ts.map +1 -1
  18. package/dist/drivers/AzureFileStorage.js +17 -17
  19. package/dist/drivers/AzureFileStorage.js.map +1 -1
  20. package/dist/drivers/BoxFileStorage.d.ts +47 -1
  21. package/dist/drivers/BoxFileStorage.d.ts.map +1 -1
  22. package/dist/drivers/BoxFileStorage.js +219 -95
  23. package/dist/drivers/BoxFileStorage.js.map +1 -1
  24. package/dist/drivers/DropboxFileStorage.d.ts +59 -0
  25. package/dist/drivers/DropboxFileStorage.d.ts.map +1 -1
  26. package/dist/drivers/DropboxFileStorage.js +314 -62
  27. package/dist/drivers/DropboxFileStorage.js.map +1 -1
  28. package/dist/drivers/GoogleDriveFileStorage.d.ts +29 -0
  29. package/dist/drivers/GoogleDriveFileStorage.d.ts.map +1 -1
  30. package/dist/drivers/GoogleDriveFileStorage.js +220 -72
  31. package/dist/drivers/GoogleDriveFileStorage.js.map +1 -1
  32. package/dist/drivers/GoogleFileStorage.d.ts.map +1 -1
  33. package/dist/drivers/GoogleFileStorage.js +12 -12
  34. package/dist/drivers/GoogleFileStorage.js.map +1 -1
  35. package/dist/drivers/SharePointFileStorage.d.ts +64 -5
  36. package/dist/drivers/SharePointFileStorage.d.ts.map +1 -1
  37. package/dist/drivers/SharePointFileStorage.js +265 -94
  38. package/dist/drivers/SharePointFileStorage.js.map +1 -1
  39. package/dist/generic/FileStorageBase.d.ts +79 -13
  40. package/dist/generic/FileStorageBase.d.ts.map +1 -1
  41. package/dist/generic/FileStorageBase.js +57 -12
  42. package/dist/generic/FileStorageBase.js.map +1 -1
  43. package/dist/util.d.ts +429 -11
  44. package/dist/util.d.ts.map +1 -1
  45. package/dist/util.js +677 -16
  46. package/dist/util.js.map +1 -1
  47. package/package.json +11 -5
@@ -31,6 +31,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
31
31
  var __metadata = (this && this.__metadata) || function (k, v) {
32
32
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
33
33
  };
34
+ var GoogleDriveFileStorage_1;
34
35
  Object.defineProperty(exports, "__esModule", { value: true });
35
36
  exports.GoogleDriveFileStorage = void 0;
36
37
  const googleapis_1 = require("googleapis");
@@ -73,6 +74,7 @@ const config_1 = require("../config");
73
74
  * ```
74
75
  */
75
76
  let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBase_1.FileStorageBase {
77
+ static { GoogleDriveFileStorage_1 = this; }
76
78
  /** The name of this storage provider, used in error messages */
77
79
  providerName = 'Google Drive';
78
80
  /** The Google Drive API client */
@@ -102,11 +104,12 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
102
104
  this._refreshToken = config?.refreshToken || env.get('STORAGE_GDRIVE_REFRESH_TOKEN').asString();
103
105
  const redirectURI = config?.redirectURI || env.get('STORAGE_GDRIVE_REDIRECT_URI').asString();
104
106
  // Initialize the Google Drive client - support THREE auth methods
107
+ // Note: If no credentials are found here, initialize() will be called later with database config
105
108
  if (keyFile) {
106
109
  // Method 1: Using key file (service account)
107
110
  const auth = new googleapis_1.google.auth.GoogleAuth({
108
111
  keyFile,
109
- scopes: ['https://www.googleapis.com/auth/drive']
112
+ scopes: ['https://www.googleapis.com/auth/drive'],
110
113
  });
111
114
  this._drive = googleapis_1.google.drive({ version: 'v3', auth });
112
115
  }
@@ -122,9 +125,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
122
125
  auth.setCredentials({ refresh_token: this._refreshToken });
123
126
  this._drive = googleapis_1.google.drive({ version: 'v3', auth });
124
127
  }
125
- else {
126
- throw new Error('Google Drive storage requires either STORAGE_GDRIVE_KEY_FILE, STORAGE_GDRIVE_CREDENTIALS_JSON, or OAuth2 credentials (CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN) to be set');
127
- }
128
+ // If no credentials found, _drive will be undefined and initialize() must be called
128
129
  // Optionally set a root folder ID to restrict operations
129
130
  this._rootFolderId = config?.rootFolderID || env.get('STORAGE_GDRIVE_ROOT_FOLDER_ID').asString();
130
131
  }
@@ -135,6 +136,37 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
135
136
  get IsConfigured() {
136
137
  return !!(this._clientID && this._clientSecret && this._refreshToken);
137
138
  }
139
+ /**
140
+ * Initializes the Google Drive storage provider with configuration from the database.
141
+ * This method is called by the FileStorageProviderEngine when loading provider configurations.
142
+ *
143
+ * @param config - Configuration object containing OAuth2 credentials
144
+ */
145
+ async initialize(config) {
146
+ // Always call super to store accountId and accountName
147
+ await super.initialize(config);
148
+ if (!config) {
149
+ return; // Nothing to do, constructor already handled config from env/file
150
+ }
151
+ // Update OAuth2 credentials
152
+ this._clientID = config.clientID || this._clientID;
153
+ this._clientSecret = config.clientSecret || this._clientSecret;
154
+ this._refreshToken = config.refreshToken || this._refreshToken;
155
+ // Update root folder ID if provided
156
+ if (config.rootFolderID) {
157
+ this._rootFolderId = config.rootFolderID;
158
+ }
159
+ // Reinitialize the Google Drive client with new OAuth2 credentials
160
+ if (this._clientID && this._clientSecret && this._refreshToken) {
161
+ const redirectURI = 'urn:ietf:wg:oauth:2.0:oob';
162
+ const auth = new googleapis_1.google.auth.OAuth2(this._clientID, this._clientSecret, redirectURI);
163
+ auth.setCredentials({ refresh_token: this._refreshToken });
164
+ this._drive = googleapis_1.google.drive({ version: 'v3', auth });
165
+ }
166
+ else {
167
+ throw new Error('Google Drive storage requires clientID, clientSecret, and refreshToken to be set');
168
+ }
169
+ }
138
170
  /**
139
171
  * Finds a file or folder by path.
140
172
  *
@@ -148,16 +180,21 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
148
180
  * @private
149
181
  */
150
182
  async _getItemByPath(path) {
151
- if (!path || path === '/' || path === '') {
183
+ console.log('[GoogleDriveFileStorage] _getItemByPath called with:', JSON.stringify(path));
184
+ // Normalize path: remove leading/trailing slashes and collapse multiple slashes
185
+ const normalizedPath = path ? path.replace(/^\/+|\/+$/g, '').replace(/\/+/g, '/') : '';
186
+ console.log('[GoogleDriveFileStorage] Normalized path:', JSON.stringify(normalizedPath));
187
+ if (!normalizedPath) {
152
188
  // Return the root folder or the specified root folder
153
189
  return {
154
190
  id: this._rootFolderId || 'root',
155
191
  name: 'Root',
156
- mimeType: 'application/vnd.google-apps.folder'
192
+ mimeType: 'application/vnd.google-apps.folder',
157
193
  };
158
194
  }
159
195
  // Split path into parts
160
- const pathParts = path.split('/').filter(p => p);
196
+ const pathParts = normalizedPath.split('/').filter((p) => p);
197
+ console.log('[GoogleDriveFileStorage] Path parts:', pathParts);
161
198
  // Start with root folder or the specified root folder
162
199
  let currentParentId = this._rootFolderId || 'root';
163
200
  let currentItem = null;
@@ -165,14 +202,22 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
165
202
  for (let i = 0; i < pathParts.length; i++) {
166
203
  const part = pathParts[i];
167
204
  const isLastPart = i === pathParts.length - 1;
205
+ // Escape single quotes in the part name for Google Drive query syntax
206
+ const escapedPart = part.replace(/'/g, "\\'");
168
207
  // Query for the item
169
208
  const query = isLastPart
170
- ? `name = '${part}' and '${currentParentId}' in parents and trashed = false`
171
- : `name = '${part}' and '${currentParentId}' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false`;
209
+ ? `name = '${escapedPart}' and '${currentParentId}' in parents and trashed = false`
210
+ : `name = '${escapedPart}' and '${currentParentId}' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false`;
211
+ console.log('[GoogleDriveFileStorage] Querying for part:', part, 'Query:', query);
172
212
  const response = await this._drive.files.list({
173
213
  q: query,
174
214
  fields: 'files(id, name, mimeType, size, modifiedTime, parents)',
175
- spaces: 'drive'
215
+ spaces: 'drive',
216
+ });
217
+ console.log('[GoogleDriveFileStorage] Query result:', {
218
+ part,
219
+ filesFound: response.data.files?.length || 0,
220
+ files: response.data.files?.map((f) => ({ id: f.id, name: f.name })),
176
221
  });
177
222
  if (!response.data.files || response.data.files.length === 0) {
178
223
  throw new Error(`Path not found: ${path} (at part: ${part})`);
@@ -183,6 +228,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
183
228
  if (!currentItem) {
184
229
  throw new Error(`Path not found: ${path}`);
185
230
  }
231
+ console.log('[GoogleDriveFileStorage] Found item:', { id: currentItem.id, name: currentItem.name });
186
232
  return currentItem;
187
233
  }
188
234
  /**
@@ -201,7 +247,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
201
247
  return this._rootFolderId || 'root';
202
248
  }
203
249
  // Split path into parts
204
- const pathParts = path.split('/').filter(p => p);
250
+ const pathParts = path.split('/').filter((p) => p);
205
251
  // Start with root folder or the specified root folder
206
252
  let currentParentId = this._rootFolderId || 'root';
207
253
  // Navigate through path parts, creating folders as needed
@@ -211,7 +257,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
211
257
  const response = await this._drive.files.list({
212
258
  q: query,
213
259
  fields: 'files(id)',
214
- spaces: 'drive'
260
+ spaces: 'drive',
215
261
  });
216
262
  if (response.data.files && response.data.files.length > 0) {
217
263
  // Folder exists, use it
@@ -222,11 +268,11 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
222
268
  const folderMetadata = {
223
269
  name: part,
224
270
  mimeType: 'application/vnd.google-apps.folder',
225
- parents: [currentParentId]
271
+ parents: [currentParentId],
226
272
  };
227
273
  const folder = await this._drive.files.create({
228
274
  requestBody: folderMetadata,
229
- fields: 'id'
275
+ fields: 'id',
230
276
  });
231
277
  currentParentId = folder.data.id;
232
278
  }
@@ -247,7 +293,9 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
247
293
  */
248
294
  _fileToMetadata(file, parentPath = '') {
249
295
  const isDirectory = file.mimeType === 'application/vnd.google-apps.folder';
250
- const fullPath = parentPath ? `${parentPath}/${file.name}` : file.name;
296
+ // Normalize parent path: remove trailing slash to avoid double slashes, handle root properly
297
+ const normalizedParent = parentPath && parentPath !== '/' ? parentPath.replace(/\/+$/, '') : '';
298
+ const fullPath = normalizedParent ? `${normalizedParent}/${file.name}` : file.name;
251
299
  return {
252
300
  name: file.name,
253
301
  path: parentPath,
@@ -256,10 +304,10 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
256
304
  contentType: file.mimeType || mime.lookup(file.name) || 'application/octet-stream',
257
305
  lastModified: new Date(file.modifiedTime || Date.now()),
258
306
  isDirectory,
259
- etag: file.etag || undefined,
307
+ etag: file.md5Checksum || undefined,
260
308
  customMetadata: {
261
- fileId: file.id
262
- }
309
+ fileId: file.id,
310
+ },
263
311
  };
264
312
  }
265
313
  /**
@@ -298,6 +346,32 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
298
346
  * console.log(downloadUrl);
299
347
  * ```
300
348
  */
349
+ /**
350
+ * Map of Google Workspace MIME types to their export formats.
351
+ * Google Workspace files must be exported to these formats for download.
352
+ */
353
+ static GOOGLE_WORKSPACE_EXPORT_MAP = {
354
+ 'application/vnd.google-apps.document': {
355
+ mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
356
+ format: 'docx',
357
+ urlBase: 'https://docs.google.com/document/d',
358
+ },
359
+ 'application/vnd.google-apps.spreadsheet': {
360
+ mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
361
+ format: 'xlsx',
362
+ urlBase: 'https://docs.google.com/spreadsheets/d',
363
+ },
364
+ 'application/vnd.google-apps.presentation': {
365
+ mimeType: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
366
+ format: 'pptx',
367
+ urlBase: 'https://docs.google.com/presentation/d',
368
+ },
369
+ 'application/vnd.google-apps.drawing': {
370
+ mimeType: 'image/png',
371
+ format: 'png',
372
+ urlBase: 'https://docs.google.com/drawings/d',
373
+ },
374
+ };
301
375
  async CreatePreAuthDownloadUrl(objectName) {
302
376
  try {
303
377
  // Get the file by path
@@ -305,26 +379,71 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
305
379
  if (!file.id) {
306
380
  throw new Error(`File not found: ${objectName}`);
307
381
  }
308
- // Create a temporary web view link
309
- const permission = await this._drive.permissions.create({
310
- fileId: file.id,
311
- requestBody: {
312
- role: 'reader',
313
- type: 'anyone',
314
- expirationTime: new Date(Date.now() + 10 * 60 * 1000).toISOString() // 10 minutes
382
+ console.log(`[GoogleDrive] CreatePreAuthDownloadUrl for: ${objectName}, mimeType: ${file.mimeType}`);
383
+ // Check if this is a Google Workspace file that needs export
384
+ const exportInfo = GoogleDriveFileStorage_1.GOOGLE_WORKSPACE_EXPORT_MAP[file.mimeType];
385
+ if (exportInfo) {
386
+ // Google Workspace file - return export URL
387
+ // These URLs work without authentication if the file is shared
388
+ const exportUrl = `${exportInfo.urlBase}/${file.id}/export?format=${exportInfo.format}`;
389
+ console.log(`[GoogleDrive] Google Workspace file, using export URL: ${exportUrl}`);
390
+ return exportUrl;
391
+ }
392
+ // Check for other Google Workspace types that can't be exported
393
+ if (file.mimeType?.startsWith('application/vnd.google-apps.')) {
394
+ // For unsupported types (Forms, Sites, Maps, etc.), return the web view link
395
+ console.log(`[GoogleDrive] Unsupported Google Workspace type: ${file.mimeType}, returning webViewLink`);
396
+ const fileInfo = await this._drive.files.get({
397
+ fileId: file.id,
398
+ fields: 'webViewLink',
399
+ });
400
+ if (fileInfo.data.webViewLink) {
401
+ return fileInfo.data.webViewLink;
315
402
  }
316
- });
317
- // Get web view link
403
+ throw new Error(`Cannot download Google Workspace file of type: ${file.mimeType}`);
404
+ }
405
+ // Regular file - try to get direct download link
318
406
  const fileInfo = await this._drive.files.get({
319
407
  fileId: file.id,
320
- fields: 'webViewLink, webContentLink'
408
+ fields: 'webViewLink, webContentLink',
321
409
  });
322
- // Prefer direct download link if available
323
- return fileInfo.data.webContentLink || fileInfo.data.webViewLink;
410
+ // webContentLink is the direct download link (only available for non-Google files)
411
+ if (fileInfo.data.webContentLink) {
412
+ console.log(`[GoogleDrive] Using webContentLink for direct download`);
413
+ return fileInfo.data.webContentLink;
414
+ }
415
+ // Try to create a public sharing permission to enable download
416
+ try {
417
+ await this._drive.permissions.create({
418
+ fileId: file.id,
419
+ requestBody: {
420
+ role: 'reader',
421
+ type: 'anyone',
422
+ },
423
+ });
424
+ // Fetch updated file info
425
+ const updatedFileInfo = await this._drive.files.get({
426
+ fileId: file.id,
427
+ fields: 'webContentLink',
428
+ });
429
+ if (updatedFileInfo.data.webContentLink) {
430
+ return updatedFileInfo.data.webContentLink;
431
+ }
432
+ }
433
+ catch (permError) {
434
+ console.warn(`[GoogleDrive] Could not create public permission:`, permError);
435
+ }
436
+ // Fallback to web view link
437
+ if (fileInfo.data.webViewLink) {
438
+ console.log(`[GoogleDrive] Falling back to webViewLink`);
439
+ return fileInfo.data.webViewLink;
440
+ }
441
+ throw new Error(`No download link available for: ${objectName}`);
324
442
  }
325
443
  catch (error) {
326
- console.error('Error creating pre-auth download URL', { objectName, error });
327
- throw new Error(`Failed to create download URL for: ${objectName}`);
444
+ const errorMessage = error instanceof Error ? error.message : String(error);
445
+ console.error('Error creating pre-auth download URL', { objectName, error: errorMessage });
446
+ throw new Error(`Failed to create download URL for: ${objectName}. ${errorMessage}`);
328
447
  }
329
448
  }
330
449
  /**
@@ -370,15 +489,15 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
370
489
  await this._drive.files.update({
371
490
  fileId: file.id,
372
491
  requestBody: {
373
- name: newName
374
- }
492
+ name: newName,
493
+ },
375
494
  });
376
495
  // Update parents (remove old parents and add new parent)
377
496
  await this._drive.files.update({
378
497
  fileId: file.id,
379
498
  removeParents: file.parents?.join(','),
380
499
  addParents: newParentId,
381
- fields: 'id, parents'
500
+ fields: 'id, parents',
382
501
  });
383
502
  return true;
384
503
  }
@@ -419,7 +538,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
419
538
  }
420
539
  // Delete the file (move to trash)
421
540
  await this._drive.files.delete({
422
- fileId: file.id
541
+ fileId: file.id,
423
542
  });
424
543
  return true;
425
544
  }
@@ -461,8 +580,15 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
461
580
  */
462
581
  async ListObjects(prefix, delimiter = '/') {
463
582
  try {
583
+ console.log('[GoogleDriveFileStorage] ListObjects called with prefix:', prefix);
584
+ // Check if drive client is initialized
585
+ if (!this._drive) {
586
+ console.error('[GoogleDriveFileStorage] Drive client not initialized!');
587
+ throw new Error('Google Drive client not initialized. Call initialize() first.');
588
+ }
464
589
  // Get the folder
465
590
  const folder = await this._getItemByPath(prefix);
591
+ console.log('[GoogleDriveFileStorage] Got folder:', { id: folder.id, name: folder.name });
466
592
  if (!folder.id) {
467
593
  throw new Error(`Folder not found: ${prefix}`);
468
594
  }
@@ -470,7 +596,11 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
470
596
  const response = await this._drive.files.list({
471
597
  q: `'${folder.id}' in parents and trashed = false`,
472
598
  fields: 'files(id, name, mimeType, size, modifiedTime, parents)',
473
- spaces: 'drive'
599
+ spaces: 'drive',
600
+ });
601
+ console.log('[GoogleDriveFileStorage] Got files response:', {
602
+ fileCount: response.data.files?.length || 0,
603
+ files: response.data.files?.map((f) => ({ name: f.name, mimeType: f.mimeType })),
474
604
  });
475
605
  const objects = [];
476
606
  const prefixes = [];
@@ -480,17 +610,16 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
480
610
  objects.push(this._fileToMetadata(file, prefix));
481
611
  // If it's a folder, add to prefixes
482
612
  if (file.mimeType === 'application/vnd.google-apps.folder') {
483
- const folderPath = prefix
484
- ? (prefix.endsWith('/') ? `${prefix}${file.name}` : `${prefix}/${file.name}`)
485
- : file.name;
613
+ const folderPath = prefix ? (prefix.endsWith('/') ? `${prefix}${file.name}` : `${prefix}/${file.name}`) : file.name;
486
614
  prefixes.push(`${folderPath}/`);
487
615
  }
488
616
  }
489
617
  }
618
+ console.log('[GoogleDriveFileStorage] Returning:', { objectCount: objects.length, prefixCount: prefixes.length });
490
619
  return { objects, prefixes };
491
620
  }
492
621
  catch (error) {
493
- console.error('Error listing objects', { prefix, error });
622
+ console.error('[GoogleDriveFileStorage] Error listing objects', { prefix, error });
494
623
  return { objects: [], prefixes: [] };
495
624
  }
496
625
  }
@@ -520,9 +649,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
520
649
  async CreateDirectory(directoryPath) {
521
650
  try {
522
651
  // Remove trailing slash if present
523
- const normalizedPath = directoryPath.endsWith('/')
524
- ? directoryPath.substring(0, directoryPath.length - 1)
525
- : directoryPath;
652
+ const normalizedPath = directoryPath.endsWith('/') ? directoryPath.substring(0, directoryPath.length - 1) : directoryPath;
526
653
  // Parse path
527
654
  const pathParts = normalizedPath.split('/');
528
655
  const folderName = pathParts.pop() || '';
@@ -533,11 +660,11 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
533
660
  const folderMetadata = {
534
661
  name: folderName,
535
662
  mimeType: 'application/vnd.google-apps.folder',
536
- parents: [parentId]
663
+ parents: [parentId],
537
664
  };
538
665
  await this._drive.files.create({
539
666
  requestBody: folderMetadata,
540
- fields: 'id'
667
+ fields: 'id',
541
668
  });
542
669
  return true;
543
670
  }
@@ -569,9 +696,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
569
696
  async DeleteDirectory(directoryPath, recursive = false) {
570
697
  try {
571
698
  // Remove trailing slash if present
572
- const normalizedPath = directoryPath.endsWith('/')
573
- ? directoryPath.substring(0, directoryPath.length - 1)
574
- : directoryPath;
699
+ const normalizedPath = directoryPath.endsWith('/') ? directoryPath.substring(0, directoryPath.length - 1) : directoryPath;
575
700
  // Get the folder
576
701
  const folder = await this._getItemByPath(normalizedPath);
577
702
  if (!folder.id) {
@@ -583,7 +708,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
583
708
  const response = await this._drive.files.list({
584
709
  q: `'${folder.id}' in parents and trashed = false`,
585
710
  fields: 'files(id)',
586
- spaces: 'drive'
711
+ spaces: 'drive',
587
712
  });
588
713
  if (response.data.files && response.data.files.length > 0) {
589
714
  throw new Error('Directory is not empty');
@@ -591,7 +716,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
591
716
  }
592
717
  // Delete the folder
593
718
  await this._drive.files.delete({
594
- fileId: folder.id
719
+ fileId: folder.id,
595
720
  });
596
721
  return true;
597
722
  }
@@ -641,7 +766,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
641
766
  console.log(`⚡ Fast path: Using Object ID directly: ${params.objectId}`);
642
767
  const response = await this._drive.files.get({
643
768
  fileId: params.objectId,
644
- fields: 'id, name, mimeType, size, modifiedTime, createdTime, parents'
769
+ fields: 'id, name, mimeType, size, modifiedTime, createdTime, parents',
645
770
  });
646
771
  file = response.data;
647
772
  }
@@ -698,10 +823,17 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
698
823
  throw new Error('Either objectId or fullPath must be provided');
699
824
  }
700
825
  let fileId;
826
+ let mimeType;
701
827
  // Fast path: Use objectId if provided
702
828
  if (params.objectId) {
703
829
  fileId = params.objectId;
704
830
  console.log(`⚡ Fast path: Using Object ID directly: ${fileId}`);
831
+ // Need to get the mimeType to check if it's a Google Workspace file
832
+ const fileInfo = await this._drive.files.get({
833
+ fileId: fileId,
834
+ fields: 'mimeType',
835
+ });
836
+ mimeType = fileInfo.data.mimeType || undefined;
705
837
  }
706
838
  else {
707
839
  // Slow path: Resolve path to ID
@@ -711,13 +843,31 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
711
843
  throw new Error(`File not found: ${params.fullPath}`);
712
844
  }
713
845
  fileId = file.id;
846
+ mimeType = file.mimeType || undefined;
714
847
  }
715
- // Download the file
848
+ // Check if this is a Google Workspace file that needs export
849
+ const exportInfo = mimeType ? GoogleDriveFileStorage_1.GOOGLE_WORKSPACE_EXPORT_MAP[mimeType] : undefined;
850
+ if (exportInfo) {
851
+ // Google Workspace file - use export instead of direct download
852
+ console.log(`[GoogleDrive] Exporting Google Workspace file (${mimeType}) as ${exportInfo.format}`);
853
+ const response = await this._drive.files.export({
854
+ fileId: fileId,
855
+ mimeType: exportInfo.mimeType,
856
+ }, {
857
+ responseType: 'arraybuffer',
858
+ });
859
+ return Buffer.from(response.data);
860
+ }
861
+ // Check for other Google Workspace types that can't be exported
862
+ if (mimeType?.startsWith('application/vnd.google-apps.')) {
863
+ throw new Error(`Cannot download Google Workspace file of type: ${mimeType}. This file type does not support export.`);
864
+ }
865
+ // Regular file - download directly
716
866
  const response = await this._drive.files.get({
717
867
  fileId: fileId,
718
- alt: 'media'
868
+ alt: 'media',
719
869
  }, {
720
- responseType: 'arraybuffer'
870
+ responseType: 'arraybuffer',
721
871
  });
722
872
  return Buffer.from(response.data);
723
873
  }
@@ -781,8 +931,8 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
781
931
  fileId: existingFileId,
782
932
  media: {
783
933
  body: data,
784
- mimeType: effectiveContentType
785
- }
934
+ mimeType: effectiveContentType,
935
+ },
786
936
  });
787
937
  }
788
938
  else {
@@ -790,13 +940,13 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
790
940
  await this._drive.files.create({
791
941
  requestBody: {
792
942
  name: fileName,
793
- parents: [parentId]
943
+ parents: [parentId],
794
944
  },
795
945
  media: {
796
946
  body: data,
797
- mimeType: effectiveContentType
947
+ mimeType: effectiveContentType,
798
948
  },
799
- fields: 'id'
949
+ fields: 'id',
800
950
  });
801
951
  }
802
952
  return true;
@@ -849,8 +999,8 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
849
999
  fileId: sourceFile.id,
850
1000
  requestBody: {
851
1001
  name: destFileName,
852
- parents: [destParentId]
853
- }
1002
+ parents: [destParentId],
1003
+ },
854
1004
  });
855
1005
  return true;
856
1006
  }
@@ -918,9 +1068,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
918
1068
  async DirectoryExists(directoryPath) {
919
1069
  try {
920
1070
  // Remove trailing slash if present
921
- const normalizedPath = directoryPath.endsWith('/')
922
- ? directoryPath.substring(0, directoryPath.length - 1)
923
- : directoryPath;
1071
+ const normalizedPath = directoryPath.endsWith('/') ? directoryPath.substring(0, directoryPath.length - 1) : directoryPath;
924
1072
  const item = await this._getItemByPath(normalizedPath);
925
1073
  return item.mimeType === 'application/vnd.google-apps.folder';
926
1074
  }
@@ -976,11 +1124,11 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
976
1124
  }
977
1125
  // Add file type filter
978
1126
  if (options?.fileTypes && options.fileTypes.length > 0) {
979
- const mimeTypes = options.fileTypes.map(ft => {
1127
+ const mimeTypes = options.fileTypes.map((ft) => {
980
1128
  // Convert extensions to MIME types if needed
981
1129
  return ft.includes('/') ? ft : mime.lookup(ft) || ft;
982
1130
  });
983
- const mimeQuery = mimeTypes.map(mt => `mimeType='${mt}'`).join(' or ');
1131
+ const mimeQuery = mimeTypes.map((mt) => `mimeType='${mt}'`).join(' or ');
984
1132
  queryParts.push(`(${mimeQuery})`);
985
1133
  }
986
1134
  // Add date filters
@@ -1024,7 +1172,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
1024
1172
  q: finalQuery,
1025
1173
  pageSize: maxResults,
1026
1174
  fields: 'nextPageToken, files(id, name, mimeType, size, modifiedTime, parents, properties)',
1027
- orderBy: 'modifiedTime desc'
1175
+ orderBy: 'modifiedTime desc',
1028
1176
  });
1029
1177
  const files = response.data.files || [];
1030
1178
  const results = [];
@@ -1042,14 +1190,14 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
1042
1190
  objectId: file.id || '', // Google Drive file ID for direct access
1043
1191
  matchInFilename: file.name.toLowerCase().includes(query.toLowerCase()),
1044
1192
  customMetadata: file.properties,
1045
- providerData: { driveFileId: file.id }
1193
+ providerData: { driveFileId: file.id },
1046
1194
  });
1047
1195
  }
1048
1196
  return {
1049
1197
  results,
1050
1198
  totalMatches: undefined, // Drive API doesn't provide total count
1051
1199
  hasMore: !!response.data.nextPageToken,
1052
- nextPageToken: response.data.nextPageToken || undefined
1200
+ nextPageToken: response.data.nextPageToken || undefined,
1053
1201
  };
1054
1202
  }
1055
1203
  catch (error) {
@@ -1074,7 +1222,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
1074
1222
  while (currentId && currentId !== 'root' && currentId !== this._rootFolderId) {
1075
1223
  const file = await this._drive.files.get({
1076
1224
  fileId: currentId,
1077
- fields: 'id, name, parents'
1225
+ fields: 'id, name, parents',
1078
1226
  });
1079
1227
  if (file.data.name) {
1080
1228
  pathParts.unshift(file.data.name);
@@ -1090,7 +1238,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
1090
1238
  }
1091
1239
  };
1092
1240
  exports.GoogleDriveFileStorage = GoogleDriveFileStorage;
1093
- exports.GoogleDriveFileStorage = GoogleDriveFileStorage = __decorate([
1241
+ exports.GoogleDriveFileStorage = GoogleDriveFileStorage = GoogleDriveFileStorage_1 = __decorate([
1094
1242
  (0, global_1.RegisterClass)(FileStorageBase_1.FileStorageBase, 'Google Drive Storage'),
1095
1243
  __metadata("design:paramtypes", [])
1096
1244
  ], GoogleDriveFileStorage);