@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
@@ -115,19 +115,28 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
115
115
  if (accessToken) {
116
116
  // Use access token directly
117
117
  this._accessToken = accessToken;
118
- this._client = new dropbox_1.Dropbox({ accessToken });
118
+ const dropboxConfig = { accessToken };
119
+ // For Dropbox Business/Team accounts, specify the team member
120
+ if (config && 'selectUser' in config && config.selectUser) {
121
+ dropboxConfig.selectUser = config.selectUser;
122
+ }
123
+ this._client = new dropbox_1.Dropbox(dropboxConfig);
119
124
  }
120
125
  else if (refreshToken && appKey && appSecret) {
121
126
  // Use refresh token with app credentials
122
- this._client = new dropbox_1.Dropbox({
127
+ const dropboxConfig = {
123
128
  refreshToken,
124
129
  clientId: appKey,
125
- clientSecret: appSecret
126
- });
127
- }
128
- else {
129
- throw new Error('Dropbox storage requires either STORAGE_DROPBOX_ACCESS_TOKEN or STORAGE_DROPBOX_REFRESH_TOKEN with APP_KEY and APP_SECRET');
130
+ clientSecret: appSecret,
131
+ };
132
+ // For Dropbox Business/Team accounts, specify the team member
133
+ if (config && 'selectUser' in config && config.selectUser) {
134
+ dropboxConfig.selectUser = config.selectUser;
135
+ }
136
+ this._client = new dropbox_1.Dropbox(dropboxConfig);
130
137
  }
138
+ // Note: If no credentials are available, client will be initialized in initialize() method
139
+ // This allows for database-driven configuration to be passed after construction
131
140
  // Root path, optional (defaults to empty which is root)
132
141
  this._rootPath = config?.rootPath || env.get('STORAGE_DROPBOX_ROOT_PATH').default('').asString();
133
142
  // Ensure root path starts with / if not empty
@@ -135,12 +144,67 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
135
144
  this._rootPath = '/' + this._rootPath;
136
145
  }
137
146
  }
147
+ /**
148
+ * Initialize the Dropbox client with configuration from database or other runtime source.
149
+ * This allows configuration to be passed after construction, overriding environment variables.
150
+ *
151
+ * @param config - Optional configuration object with accessToken, refreshToken, etc.
152
+ */
153
+ async initialize(config) {
154
+ // Always call super to store accountId and accountName
155
+ await super.initialize(config);
156
+ if (!config) {
157
+ return; // Nothing to do, constructor already handled config from env/file
158
+ }
159
+ // If config is provided, reinitialize the client with it
160
+ const accessToken = config.accessToken;
161
+ const refreshToken = config.refreshToken;
162
+ // Support both naming conventions: clientID/clientSecret (standard) and appKey/appSecret (Dropbox terminology)
163
+ const appKey = config.clientID || config.appKey;
164
+ const appSecret = config.clientSecret || config.appSecret;
165
+ // Prefer refresh token over access token when both are available
166
+ // Access tokens expire (4 hours), refresh tokens are long-lived and auto-refresh
167
+ if (refreshToken && appKey && appSecret) {
168
+ // Use refresh token with app credentials - this is the preferred method
169
+ const dropboxConfig = {
170
+ refreshToken,
171
+ clientId: appKey,
172
+ clientSecret: appSecret,
173
+ };
174
+ // For Dropbox Business/Team accounts, specify the team member
175
+ if (config.selectUser) {
176
+ dropboxConfig.selectUser = config.selectUser;
177
+ }
178
+ this._client = new dropbox_1.Dropbox(dropboxConfig);
179
+ // Set a placeholder for IsConfigured check - the SDK will get a real token on first API call
180
+ this._accessToken = 'refresh-token-mode';
181
+ }
182
+ else if (accessToken) {
183
+ // Fall back to access token if no refresh token available
184
+ // Note: This will fail when the access token expires (typically 4 hours)
185
+ this._accessToken = accessToken;
186
+ const dropboxConfig = { accessToken };
187
+ // For Dropbox Business/Team accounts, specify the team member
188
+ if (config.selectUser) {
189
+ dropboxConfig.selectUser = config.selectUser;
190
+ }
191
+ this._client = new dropbox_1.Dropbox(dropboxConfig);
192
+ }
193
+ // Update root path if provided
194
+ if (config.rootPath) {
195
+ this._rootPath = config.rootPath;
196
+ // Ensure root path starts with / if not empty
197
+ if (this._rootPath && !this._rootPath.startsWith('/')) {
198
+ this._rootPath = '/' + this._rootPath;
199
+ }
200
+ }
201
+ }
138
202
  /**
139
203
  * Checks if Dropbox provider is properly configured.
140
204
  * Returns true if access token is present.
141
205
  */
142
206
  get IsConfigured() {
143
- return !!(this._accessToken);
207
+ return !!this._accessToken;
144
208
  }
145
209
  /**
146
210
  * Normalizes a path to be compatible with Dropbox API
@@ -153,20 +217,25 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
153
217
  * @returns A normalized path string suitable for Dropbox API calls
154
218
  */
155
219
  _normalizePath(path) {
220
+ console.log('[DropboxFileStorage._normalizePath] Input:', {
221
+ path,
222
+ rootPath: this._rootPath,
223
+ });
156
224
  // Combine root path with the given path
157
225
  let fullPath = path;
158
226
  if (this._rootPath) {
159
- fullPath = path ?
160
- (path.startsWith('/') ? this._rootPath + path : this._rootPath + '/' + path) :
161
- this._rootPath;
227
+ fullPath = path ? (path.startsWith('/') ? this._rootPath + path : this._rootPath + '/' + path) : this._rootPath;
162
228
  }
163
229
  else if (!fullPath.startsWith('/') && fullPath !== '') {
164
230
  fullPath = '/' + fullPath;
165
231
  }
232
+ console.log('[DropboxFileStorage._normalizePath] After combining root path:', { fullPath });
166
233
  // For root, Dropbox uses empty string instead of "/"
167
234
  if (fullPath === '/') {
235
+ console.log('[DropboxFileStorage._normalizePath] Converted root "/" to empty string');
168
236
  return '';
169
237
  }
238
+ console.log('[DropboxFileStorage._normalizePath] Final result:', { fullPath });
170
239
  return fullPath;
171
240
  }
172
241
  /**
@@ -184,7 +253,7 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
184
253
  try {
185
254
  const response = await this._client.filesGetMetadata({
186
255
  path: normalizedPath,
187
- include_media_info: false
256
+ include_media_info: false,
188
257
  });
189
258
  return response.result;
190
259
  }
@@ -206,6 +275,12 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
206
275
  _convertToMetadata(item, parentPath = '') {
207
276
  const isDirectory = item['.tag'] === 'folder';
208
277
  const name = item.name;
278
+ console.log('[DropboxFileStorage._convertToMetadata] Processing item:', {
279
+ name,
280
+ path_display: item.path_display,
281
+ rootPath: this._rootPath,
282
+ parentPath,
283
+ });
209
284
  // Extract path from item.path_display
210
285
  let path = '';
211
286
  if (item.path_display) {
@@ -213,35 +288,53 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
213
288
  const pathParts = item.path_display.split('/');
214
289
  pathParts.pop();
215
290
  path = pathParts.join('/');
291
+ console.log('[DropboxFileStorage._convertToMetadata] After extracting directory:', { path });
216
292
  // Remove root path if present
217
293
  if (this._rootPath && path.startsWith(this._rootPath)) {
294
+ const oldPath = path;
218
295
  path = path.substring(this._rootPath.length);
296
+ console.log('[DropboxFileStorage._convertToMetadata] Removed root path:', {
297
+ oldPath,
298
+ newPath: path,
299
+ rootPath: this._rootPath,
300
+ });
219
301
  }
220
302
  // Remove leading slash
221
303
  if (path.startsWith('/')) {
222
304
  path = path.substring(1);
305
+ console.log('[DropboxFileStorage._convertToMetadata] Removed leading slash:', { path });
223
306
  }
224
307
  }
225
308
  // Use parentPath if provided
226
309
  if (parentPath) {
310
+ console.log('[DropboxFileStorage._convertToMetadata] Using parentPath:', { parentPath });
227
311
  path = parentPath;
228
312
  }
229
- // Construct full path
230
- const fullPath = path ? `${path}/${name}` : name;
313
+ // Construct full path - ensure no double slashes
314
+ let fullPath = name;
315
+ if (path) {
316
+ // Remove trailing slash from path and leading slash from name
317
+ const cleanPath = path.endsWith('/') ? path.slice(0, -1) : path;
318
+ const cleanName = name.startsWith('/') ? name.slice(1) : name;
319
+ fullPath = `${cleanPath}/${cleanName}`;
320
+ }
321
+ console.log('[DropboxFileStorage._convertToMetadata] Final result:', {
322
+ name,
323
+ path,
324
+ fullPath,
325
+ });
231
326
  return {
232
327
  name,
233
328
  path,
234
329
  fullPath,
235
- size: isDirectory ? 0 : (item.size || 0),
236
- contentType: isDirectory ?
237
- 'application/x-directory' :
238
- (mime.lookup(name) || 'application/octet-stream'),
330
+ size: isDirectory ? 0 : item.size || 0,
331
+ contentType: isDirectory ? 'application/x-directory' : mime.lookup(name) || 'application/octet-stream',
239
332
  lastModified: isDirectory ? new Date() : new Date(item.server_modified || Date.now()),
240
333
  isDirectory,
241
334
  customMetadata: {
242
335
  id: item.id,
243
- rev: item.rev
244
- }
336
+ rev: isDirectory ? undefined : item.rev,
337
+ },
245
338
  };
246
339
  }
247
340
  /**
@@ -267,10 +360,77 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
267
360
  * }
268
361
  * ```
269
362
  */
363
+ /**
364
+ * Creates a pre-authenticated upload URL for a file
365
+ *
366
+ * This method generates a time-limited URL that can be used to upload
367
+ * a file directly to Dropbox without additional authentication.
368
+ *
369
+ * @param objectName - Path where the file should be uploaded (e.g., 'documents/report.pdf')
370
+ * @returns A Promise that resolves to an object containing the upload URL and provider key
371
+ * @throws Error if URL creation fails
372
+ *
373
+ * @remarks
374
+ * - Dropbox temporary upload links typically expire after 4 hours
375
+ * - Maximum file size for upload via temporary link is 150MB
376
+ * - The upload must use Content-Type: application/octet-stream
377
+ * - The URL is for one-time use only
378
+ *
379
+ * @example
380
+ * ```typescript
381
+ * try {
382
+ * // Generate a pre-authenticated upload URL
383
+ * const uploadPayload = await storage.CreatePreAuthUploadUrl('documents/financial-report.pdf');
384
+ *
385
+ * console.log(`Upload the file to this URL: ${uploadPayload.UploadUrl}`);
386
+ *
387
+ * // Use the URL to upload file directly from client
388
+ * // POST request with Content-Type: application/octet-stream
389
+ * } catch (error) {
390
+ * console.error('Error creating upload URL:', error.message);
391
+ * }
392
+ * ```
393
+ */
270
394
  async CreatePreAuthUploadUrl(objectName) {
271
- // Dropbox doesn't support pre-signed URLs for uploads in the same way as S3
272
- // Use PutObject method for uploads instead
273
- this.throwUnsupportedOperationError('CreatePreAuthUploadUrl');
395
+ try {
396
+ console.log('[DropboxFileStorage.CreatePreAuthUploadUrl] Input:', {
397
+ objectName,
398
+ rootPath: this._rootPath,
399
+ });
400
+ const normalizedPath = this._normalizePath(objectName);
401
+ console.log('[DropboxFileStorage.CreatePreAuthUploadUrl] After normalization:', {
402
+ normalizedPath,
403
+ });
404
+ // Create a temporary upload link
405
+ // Note: commit_info is optional, defaults to overwrite mode
406
+ const response = await this._client.filesGetTemporaryUploadLink({
407
+ commit_info: {
408
+ path: normalizedPath,
409
+ mode: { '.tag': 'overwrite' },
410
+ autorename: false,
411
+ mute: true,
412
+ },
413
+ });
414
+ console.log('[DropboxFileStorage.CreatePreAuthUploadUrl] Success:', {
415
+ link: response.result.link,
416
+ });
417
+ return {
418
+ UploadUrl: response.result.link,
419
+ ProviderKey: normalizedPath,
420
+ };
421
+ }
422
+ catch (error) {
423
+ console.error('[DropboxFileStorage.CreatePreAuthUploadUrl] Error:', {
424
+ objectName,
425
+ rootPath: this._rootPath,
426
+ error: error.message || error,
427
+ errorDetails: error.error || error,
428
+ errorStatus: error.status,
429
+ fullError: JSON.stringify(error, null, 2),
430
+ });
431
+ const errorMsg = error.error?.error_summary || error.message || JSON.stringify(error);
432
+ throw new Error(`Failed to create upload URL for: ${objectName} - ${errorMsg}`);
433
+ }
274
434
  }
275
435
  /**
276
436
  * Creates a pre-authenticated download URL for a file
@@ -303,15 +463,29 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
303
463
  */
304
464
  async CreatePreAuthDownloadUrl(objectName) {
305
465
  try {
466
+ console.log('[DropboxFileStorage.CreatePreAuthDownloadUrl] Input:', {
467
+ objectName,
468
+ rootPath: this._rootPath,
469
+ });
306
470
  const normalizedPath = this._normalizePath(objectName);
471
+ console.log('[DropboxFileStorage.CreatePreAuthDownloadUrl] After normalization:', {
472
+ normalizedPath,
473
+ });
307
474
  // Create a temporary download link
308
475
  const response = await this._client.filesGetTemporaryLink({
309
- path: normalizedPath
476
+ path: normalizedPath,
477
+ });
478
+ console.log('[DropboxFileStorage.CreatePreAuthDownloadUrl] Success:', {
479
+ link: response.result.link,
310
480
  });
311
481
  return response.result.link;
312
482
  }
313
483
  catch (error) {
314
- console.error('Error creating pre-auth download URL', { objectName, error });
484
+ console.error('[DropboxFileStorage.CreatePreAuthDownloadUrl] Error:', {
485
+ objectName,
486
+ rootPath: this._rootPath,
487
+ error: error.message || error,
488
+ });
315
489
  throw new Error(`Failed to create download URL for: ${objectName}`);
316
490
  }
317
491
  }
@@ -352,7 +526,7 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
352
526
  await this._client.filesMoveV2({
353
527
  from_path: fromPath,
354
528
  to_path: toPath,
355
- autorename: false
529
+ autorename: false,
356
530
  });
357
531
  return true;
358
532
  }
@@ -388,18 +562,31 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
388
562
  */
389
563
  async DeleteObject(objectName) {
390
564
  try {
391
- const normalizedPath = this._normalizePath(objectName);
392
- await this._client.filesDeleteV2({
393
- path: normalizedPath
565
+ // Remove trailing slash if present (Dropbox doesn't accept trailing slashes for folder deletion)
566
+ const normalizedPath = objectName.endsWith('/') ? this._normalizePath(objectName.substring(0, objectName.length - 1)) : this._normalizePath(objectName);
567
+ console.log('[DropboxFileStorage] DeleteObject called:', {
568
+ objectName,
569
+ normalizedPath,
570
+ rootPath: this._rootPath,
571
+ hadTrailingSlash: objectName.endsWith('/'),
572
+ });
573
+ const result = await this._client.filesDeleteV2({
574
+ path: normalizedPath,
394
575
  });
576
+ console.log('[DropboxFileStorage] filesDeleteV2 result:', result);
395
577
  return true;
396
578
  }
397
579
  catch (error) {
398
580
  // If the path doesn't exist, consider it success for idempotency
399
581
  if (error.status === 409 && error.error?.error?.['.tag'] === 'path_lookup') {
582
+ console.log('[DropboxFileStorage] Path not found (already deleted):', objectName);
400
583
  return true;
401
584
  }
402
- console.error('Error deleting object', { objectName, error });
585
+ console.error('[DropboxFileStorage] Error deleting object', {
586
+ objectName,
587
+ normalizedPath: objectName.endsWith('/') ? this._normalizePath(objectName.substring(0, objectName.length - 1)) : this._normalizePath(objectName),
588
+ error,
589
+ });
403
590
  return false;
404
591
  }
405
592
  }
@@ -440,30 +627,83 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
440
627
  async ListObjects(prefix, delimiter = '/') {
441
628
  try {
442
629
  const normalizedPath = this._normalizePath(prefix);
630
+ console.log('[DropboxFileStorage] ListObjects called:', {
631
+ prefix,
632
+ normalizedPath,
633
+ delimiter,
634
+ hasClient: !!this._client,
635
+ isConfigured: this.IsConfigured,
636
+ rootPath: this._rootPath,
637
+ });
638
+ // Debug: Try to get current account info to understand access type
639
+ try {
640
+ const accountInfo = await this._client.usersGetCurrentAccount();
641
+ console.log('[DropboxFileStorage] Account info:', {
642
+ accountId: accountInfo.result.account_id,
643
+ email: accountInfo.result.email,
644
+ name: accountInfo.result.name.display_name,
645
+ });
646
+ }
647
+ catch (error) {
648
+ console.log('[DropboxFileStorage] Could not get account info:', error?.message);
649
+ }
443
650
  const response = await this._client.filesListFolder({
444
651
  path: normalizedPath,
445
652
  recursive: false,
446
653
  include_media_info: false,
447
654
  include_deleted: false,
448
- include_has_explicit_shared_members: false
655
+ include_has_explicit_shared_members: false,
449
656
  });
657
+ console.log('[DropboxFileStorage] filesListFolder response:', {
658
+ entriesCount: response.result.entries.length,
659
+ entries: response.result.entries.map((e) => ({ name: e.name, tag: e['.tag'] })),
660
+ has_more: response.result.has_more,
661
+ cursor: response.result.cursor,
662
+ });
663
+ // Check if we're in an app folder scenario
664
+ if (response.result.entries.length === 0 && normalizedPath === '') {
665
+ console.log('[DropboxFileStorage] Empty root - this might be an app-folder-only token');
666
+ console.log('[DropboxFileStorage] Note: If using app folder access, files are in /Apps/[YourAppName]/');
667
+ console.log('[DropboxFileStorage] You can set rootPath in configuration to point to your app folder');
668
+ // Try to list a few common app folder paths to help diagnose
669
+ const testPaths = ['/Apps', '/Apps/MJ-Files-Test', '/MJ-FileTest'];
670
+ for (const testPath of testPaths) {
671
+ try {
672
+ console.log(`[DropboxFileStorage] Testing path: ${testPath}`);
673
+ const testResponse = await this._client.filesListFolder({ path: testPath, recursive: false });
674
+ console.log(`[DropboxFileStorage] Found ${testResponse.result.entries.length} items at ${testPath}:`, testResponse.result.entries.map((e) => ({ name: e.name, tag: e['.tag'] })));
675
+ }
676
+ catch (testError) {
677
+ const errorMsg = testError.error?.error_summary || testError.message;
678
+ console.log(`[DropboxFileStorage] Cannot access ${testPath}: ${errorMsg}`);
679
+ }
680
+ }
681
+ }
450
682
  const objects = [];
451
683
  const prefixes = [];
452
684
  // Process entries
453
685
  for (const entry of response.result.entries) {
686
+ // Skip deleted entries
687
+ if (entry['.tag'] === 'deleted') {
688
+ continue;
689
+ }
454
690
  objects.push(this._convertToMetadata(entry, prefix));
455
691
  // If it's a folder, add to prefixes
456
692
  if (entry['.tag'] === 'folder') {
457
- const folderPath = prefix
458
- ? (prefix.endsWith('/') ? `${prefix}${entry.name}` : `${prefix}/${entry.name}`)
459
- : entry.name;
693
+ const folderPath = prefix ? (prefix.endsWith('/') ? `${prefix}${entry.name}` : `${prefix}/${entry.name}`) : entry.name;
694
+ console.log('[DropboxFileStorage] Adding folder prefix:', folderPath);
460
695
  prefixes.push(`${folderPath}/`);
461
696
  }
462
697
  }
698
+ console.log('[DropboxFileStorage] Final result:', {
699
+ objectsCount: objects.length,
700
+ prefixesCount: prefixes.length,
701
+ prefixes,
702
+ });
463
703
  return { objects, prefixes };
464
704
  }
465
705
  catch (error) {
466
- console.error('Error listing objects', { prefix, error });
706
+ console.error('[DropboxFileStorage] Error listing objects:', { prefix, error, errorMessage: error?.message, errorStatus: error?.status });
467
707
  return { objects: [], prefixes: [] };
468
708
  }
469
709
  }
@@ -507,7 +747,7 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
507
747
  : this._normalizePath(directoryPath);
508
748
  await this._client.filesCreateFolderV2({
509
749
  path: normalizedPath,
510
- autorename: false
750
+ autorename: false,
511
751
  });
512
752
  return true;
513
753
  }
@@ -564,14 +804,14 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
564
804
  // Check if directory is empty first
565
805
  const listing = await this._client.filesListFolder({
566
806
  path: normalizedPath,
567
- recursive: false
807
+ recursive: false,
568
808
  });
569
809
  if (listing.result.entries.length > 0) {
570
810
  throw new Error('Directory is not empty');
571
811
  }
572
812
  }
573
813
  await this._client.filesDeleteV2({
574
- path: normalizedPath
814
+ path: normalizedPath,
575
815
  });
576
816
  return true;
577
817
  }
@@ -643,8 +883,12 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
643
883
  }
644
884
  const response = await this._client.filesGetMetadata({
645
885
  path: path,
646
- include_media_info: false
886
+ include_media_info: false,
647
887
  });
888
+ // Check if the result is a deleted entry
889
+ if (response.result['.tag'] === 'deleted') {
890
+ throw new Error(`Object not found (deleted): ${params.objectId || params.fullPath}`);
891
+ }
648
892
  return this._convertToMetadata(response.result, parentPath);
649
893
  }
650
894
  catch (error) {
@@ -704,10 +948,11 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
704
948
  console.log(`🐌 Slow path: Using path: ${path}`);
705
949
  }
706
950
  const response = await this._client.filesDownload({
707
- path: path
951
+ path: path,
708
952
  });
709
953
  // Extract file content as Buffer
710
954
  // Note: In Dropbox SDK, the file content is in response.result.fileBinary
955
+ // The TypeScript definitions don't include fileBinary, but it's present in the actual response
711
956
  return Buffer.from(response.result.fileBinary);
712
957
  }
713
958
  catch (error) {
@@ -763,7 +1008,7 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
763
1008
  contents: data,
764
1009
  mode: { '.tag': 'overwrite' },
765
1010
  autorename: false,
766
- mute: true
1011
+ mute: true,
767
1012
  });
768
1013
  }
769
1014
  else {
@@ -772,7 +1017,7 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
772
1017
  // Start upload session
773
1018
  const sessionStart = await this._client.filesUploadSessionStart({
774
1019
  close: false,
775
- contents: data.slice(0, CHUNK_SIZE)
1020
+ contents: data.slice(0, CHUNK_SIZE),
776
1021
  });
777
1022
  const sessionId = sessionStart.result.session_id;
778
1023
  let offset = CHUNK_SIZE;
@@ -785,15 +1030,15 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
785
1030
  await this._client.filesUploadSessionFinish({
786
1031
  cursor: {
787
1032
  session_id: sessionId,
788
- offset: offset
1033
+ offset: offset,
789
1034
  },
790
1035
  commit: {
791
1036
  path: normalizedPath,
792
1037
  mode: { '.tag': 'overwrite' },
793
1038
  autorename: false,
794
- mute: true
1039
+ mute: true,
795
1040
  },
796
- contents: chunk
1041
+ contents: chunk,
797
1042
  });
798
1043
  }
799
1044
  else {
@@ -801,10 +1046,10 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
801
1046
  await this._client.filesUploadSessionAppendV2({
802
1047
  cursor: {
803
1048
  session_id: sessionId,
804
- offset: offset
1049
+ offset: offset,
805
1050
  },
806
1051
  close: false,
807
- contents: chunk
1052
+ contents: chunk,
808
1053
  });
809
1054
  }
810
1055
  offset += chunk.length;
@@ -854,7 +1099,7 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
854
1099
  await this._client.filesCopyV2({
855
1100
  from_path: fromPath,
856
1101
  to_path: toPath,
857
- autorename: false
1102
+ autorename: false,
858
1103
  });
859
1104
  return true;
860
1105
  }
@@ -924,9 +1169,7 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
924
1169
  async DirectoryExists(directoryPath) {
925
1170
  try {
926
1171
  // Remove trailing slash if present
927
- const normalizedPath = directoryPath.endsWith('/')
928
- ? directoryPath.substring(0, directoryPath.length - 1)
929
- : directoryPath;
1172
+ const normalizedPath = directoryPath.endsWith('/') ? directoryPath.substring(0, directoryPath.length - 1) : directoryPath;
930
1173
  const item = await this._getMetadata(normalizedPath);
931
1174
  return item['.tag'] === 'folder';
932
1175
  }
@@ -976,13 +1219,13 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
976
1219
  const maxResults = options?.maxResults || 100;
977
1220
  // Build Dropbox search options
978
1221
  const searchOptions = {
979
- query: query,
1222
+ query,
980
1223
  options: {
981
1224
  max_results: Math.min(maxResults, 1000), // Dropbox max is 1000
982
1225
  path: options?.pathPrefix ? this._normalizePath(options.pathPrefix) : undefined,
983
- file_status: 'active', // Exclude deleted files
984
- filename_only: !options?.searchContent // Search filename only or filename + content
985
- }
1226
+ file_status: { '.tag': 'active' }, // Exclude deleted files
1227
+ filename_only: !options?.searchContent, // Search filename only or filename + content
1228
+ },
986
1229
  };
987
1230
  // Add file extension filter if fileTypes provided
988
1231
  if (options?.fileTypes && options.fileTypes.length > 0) {
@@ -996,10 +1239,19 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
996
1239
  const results = [];
997
1240
  // Process search results
998
1241
  for (const match of response.result.matches || []) {
999
- // The metadata field contains the actual file/folder metadata
1000
- const metadata = match.metadata.metadata;
1242
+ // The metadata field is MetadataV2, which can be MetadataV2Metadata or MetadataV2Other
1243
+ // We only want MetadataV2Metadata which has the actual file/folder metadata
1244
+ if (match.metadata['.tag'] !== 'metadata') {
1245
+ continue;
1246
+ }
1247
+ const metadataV2 = match.metadata;
1248
+ // Skip deleted entries
1249
+ if (metadataV2.metadata['.tag'] === 'deleted') {
1250
+ continue;
1251
+ }
1252
+ const metadata = metadataV2.metadata;
1001
1253
  // Skip if not a file (could be folder or other type)
1002
- if (!metadata || metadata['.tag'] !== 'file') {
1254
+ if (metadata['.tag'] !== 'file') {
1003
1255
  continue;
1004
1256
  }
1005
1257
  // Apply date filters client-side (Dropbox search doesn't support date filters directly)
@@ -1026,12 +1278,12 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
1026
1278
  matchInFilename: this._checkFilenameMatch(fileName, query),
1027
1279
  customMetadata: {
1028
1280
  id: metadata.id,
1029
- rev: metadata.rev
1281
+ rev: metadata.rev,
1030
1282
  },
1031
1283
  providerData: {
1032
1284
  dropboxId: metadata.id,
1033
- pathLower: metadata.path_lower
1034
- }
1285
+ pathLower: metadata.path_lower,
1286
+ },
1035
1287
  });
1036
1288
  }
1037
1289
  // Check if there are more results available
@@ -1040,7 +1292,7 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
1040
1292
  results,
1041
1293
  totalMatches: undefined, // Dropbox doesn't provide total count
1042
1294
  hasMore,
1043
- nextPageToken: hasMore ? 'continue' : undefined // Dropbox uses continue endpoint for pagination
1295
+ nextPageToken: hasMore ? 'continue' : undefined, // Dropbox uses continue endpoint for pagination
1044
1296
  };
1045
1297
  }
1046
1298
  catch (error) {