@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
@@ -132,6 +132,11 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
132
132
  * Box SDK client for making API calls
133
133
  */
134
134
  _client;
135
+ /**
136
+ * Callback to persist new refresh tokens when they are issued.
137
+ * Box issues new refresh tokens with each token refresh.
138
+ */
139
+ _onTokenRefresh;
135
140
  /**
136
141
  * Creates a new BoxFileStorage instance
137
142
  *
@@ -169,6 +174,10 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
169
174
  * This method must be called after creating a BoxFileStorage instance.
170
175
  * It initializes the Box SDK and creates the client for API calls.
171
176
  *
177
+ * Can optionally accept a config object with OAuth credentials, which is used
178
+ * for per-user OAuth authentication where credentials come from the database.
179
+ *
180
+ * @param config - Optional configuration object containing OAuth2 credentials
172
181
  * @returns A Promise that resolves when initialization is complete
173
182
  *
174
183
  * @example
@@ -178,22 +187,47 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
178
187
  * // Now the storage provider is ready to use
179
188
  * ```
180
189
  */
181
- async initialize() {
182
- // Get access token if not provided
183
- if (!this._accessToken) {
184
- if (this._clientId && this._clientSecret) {
185
- // Use client credentials to get token
190
+ async initialize(config) {
191
+ // Always call super to store accountId and accountName
192
+ await super.initialize(config);
193
+ // If config is provided (from database OAuth), use those credentials
194
+ if (config) {
195
+ if (config.clientID)
196
+ this._clientId = config.clientID;
197
+ if (config.clientSecret)
198
+ this._clientSecret = config.clientSecret;
199
+ if (config.refreshToken)
200
+ this._refreshToken = config.refreshToken;
201
+ if (config.accessToken)
202
+ this._accessToken = config.accessToken;
203
+ if (config.enterpriseID)
204
+ this._enterpriseId = config.enterpriseID;
205
+ if (config.rootFolderID)
206
+ this._rootFolderId = config.rootFolderID;
207
+ // Store the token refresh callback - CRITICAL for Box since it issues new refresh tokens
208
+ if (config.onTokenRefresh)
209
+ this._onTokenRefresh = config.onTokenRefresh;
210
+ }
211
+ // Always get a fresh access token using refresh token if available.
212
+ // Box access tokens are short-lived (~60 min), so stored tokens are likely expired.
213
+ // We should always refresh rather than trusting a stored access token.
214
+ if (this._refreshToken && this._clientId && this._clientSecret) {
215
+ // Use refresh token to get a fresh access token (OAuth flow)
216
+ console.log('[Box] Refreshing access token during initialization...');
217
+ const tokenData = await this._refreshAccessToken();
218
+ this._accessToken = tokenData.access_token;
219
+ }
220
+ else if (!this._accessToken) {
221
+ // No refresh token and no access token - try other auth methods
222
+ if (this._clientId && this._clientSecret && this._enterpriseId) {
223
+ // Use client credentials to get token (JWT flow)
186
224
  this._accessToken = await this._getAccessToken();
187
225
  }
188
- else if (this._refreshToken) {
189
- // Use refresh token to get access token
190
- const tokenData = await this._refreshAccessToken();
191
- this._accessToken = tokenData.access_token;
192
- }
193
226
  else {
194
227
  throw new Error('Box storage requires either access token, refresh token, or client credentials');
195
228
  }
196
229
  }
230
+ // If we only have an access token (no refresh token), use it as-is and hope it's still valid
197
231
  // Initialize Box client with developer token auth
198
232
  const auth = new box_node_sdk_1.BoxDeveloperTokenAuth({ token: this._accessToken });
199
233
  this._client = new box_node_sdk_1.BoxClient({ auth });
@@ -213,7 +247,7 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
213
247
  const response = await fetch('https://api.box.com/oauth2/token', {
214
248
  method: 'POST',
215
249
  headers: {
216
- 'Content-Type': 'application/x-www-form-urlencoded'
250
+ 'Content-Type': 'application/x-www-form-urlencoded',
217
251
  },
218
252
  body: new URLSearchParams({
219
253
  client_id: this._clientId,
@@ -221,14 +255,14 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
221
255
  grant_type: 'client_credentials',
222
256
  box_subject_type: 'enterprise',
223
257
  box_subject_id: this._enterpriseId,
224
- })
258
+ }),
225
259
  });
226
260
  if (!response.ok) {
227
261
  throw new Error(`Failed to get access token: ${response.status} ${response.statusText}`);
228
262
  }
229
- const tokenData = await response.json();
263
+ const tokenData = (await response.json());
230
264
  this._accessToken = tokenData.access_token;
231
- this._tokenExpiresAt = Date.now() + (tokenData.expires_in * 1000) - 60000;
265
+ this._tokenExpiresAt = Date.now() + tokenData.expires_in * 1000 - 60000;
232
266
  return tokenData.access_token;
233
267
  }
234
268
  catch (error) {
@@ -247,22 +281,44 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
247
281
  const response = await fetch('https://api.box.com/oauth2/token', {
248
282
  method: 'POST',
249
283
  headers: {
250
- 'Content-Type': 'application/x-www-form-urlencoded'
284
+ 'Content-Type': 'application/x-www-form-urlencoded',
251
285
  },
252
286
  body: new URLSearchParams({
253
287
  grant_type: 'refresh_token',
254
288
  refresh_token: this._refreshToken,
255
289
  client_id: this._clientId,
256
- client_secret: this._clientSecret
257
- })
290
+ client_secret: this._clientSecret,
291
+ }),
258
292
  });
259
293
  if (!response.ok) {
260
294
  throw new Error(`Failed to refresh token: ${response.status} ${response.statusText}`);
261
295
  }
262
- const data = await response.json();
296
+ const data = (await response.json());
263
297
  this._accessToken = data.access_token;
264
- this._refreshToken = data.refresh_token || this._refreshToken;
265
- this._tokenExpiresAt = Date.now() + (data.expires_in * 1000) - 60000;
298
+ this._tokenExpiresAt = Date.now() + data.expires_in * 1000 - 60000;
299
+ // CRITICAL: Box issues a NEW refresh token with every token refresh.
300
+ // The old refresh token is immediately invalidated.
301
+ // We MUST persist the new refresh token to the database.
302
+ if (data.refresh_token) {
303
+ this._refreshToken = data.refresh_token;
304
+ // Call the callback to persist the new tokens to the database
305
+ if (this._onTokenRefresh) {
306
+ console.log('[Box] New refresh token received, persisting to database...');
307
+ try {
308
+ await this._onTokenRefresh(data.refresh_token, data.access_token);
309
+ console.log('[Box] New tokens persisted successfully');
310
+ }
311
+ catch (callbackError) {
312
+ console.error('[Box] Failed to persist new tokens:', callbackError);
313
+ // Don't throw here - we still have the tokens in memory and can continue
314
+ // But the next time the server restarts, authentication will fail
315
+ }
316
+ }
317
+ else {
318
+ console.warn('[Box] New refresh token received but no callback registered to persist it!');
319
+ console.warn('[Box] This will cause authentication to fail after server restart.');
320
+ }
321
+ }
266
322
  return data;
267
323
  }
268
324
  catch (error) {
@@ -294,7 +350,7 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
294
350
  if (this._refreshToken && this._clientId && this._clientSecret) {
295
351
  const tokenData = await this._refreshAccessToken();
296
352
  this._accessToken = tokenData.access_token;
297
- this._tokenExpiresAt = Date.now() + (tokenData.expires_in * 1000);
353
+ this._tokenExpiresAt = Date.now() + tokenData.expires_in * 1000;
298
354
  }
299
355
  else if (this._clientId && this._clientSecret && this._enterpriseId) {
300
356
  this._accessToken = await this._getAccessToken();
@@ -362,14 +418,14 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
362
418
  // Try as file first, then folder
363
419
  try {
364
420
  const fileInfo = await this._client.files.getFileById(parsedPath.id, {
365
- queryParams: { fields: ['id', 'type'] }
421
+ queryParams: { fields: ['id', 'type'] },
366
422
  });
367
423
  return { id: fileInfo.id, type: fileInfo.type };
368
424
  }
369
425
  catch {
370
426
  try {
371
427
  const folderInfo = await this._client.folders.getFolderById(parsedPath.id, {
372
- queryParams: { fields: ['id', 'type'] }
428
+ queryParams: { fields: ['id', 'type'] },
373
429
  });
374
430
  return { id: folderInfo.id, type: folderInfo.type };
375
431
  }
@@ -396,8 +452,8 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
396
452
  queryParams: {
397
453
  fields: ['name', 'type', 'id'],
398
454
  limit: LIMIT,
399
- offset: offset
400
- }
455
+ offset: offset,
456
+ },
401
457
  });
402
458
  // Look for the item by name
403
459
  const item = folderItems.entries?.find((i) => i.name === parsedPath.name);
@@ -439,17 +495,15 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
439
495
  name,
440
496
  path,
441
497
  fullPath,
442
- size: isDirectory ? 0 : (item.size || 0),
443
- contentType: isDirectory ?
444
- 'application/x-directory' :
445
- (item.content_type || mime.lookup(name) || 'application/octet-stream'),
498
+ size: isDirectory ? 0 : item.size || 0,
499
+ contentType: isDirectory ? 'application/x-directory' : item.content_type || mime.lookup(name) || 'application/octet-stream',
446
500
  lastModified: new Date(item.modified_at || item.created_at || Date.now()),
447
501
  isDirectory,
448
502
  etag: item.etag,
449
503
  customMetadata: {
450
504
  id: item.id,
451
- sequence_id: item.sequence_id
452
- }
505
+ sequence_id: item.sequence_id,
506
+ },
453
507
  };
454
508
  }
455
509
  /**
@@ -624,16 +678,16 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
624
678
  await this._client.folders.updateFolderById(sourceInfo.id, {
625
679
  requestBody: {
626
680
  parent: { id: destParentId },
627
- name: destPath.name
628
- }
681
+ name: destPath.name,
682
+ },
629
683
  });
630
684
  }
631
685
  else {
632
686
  await this._client.files.updateFileById(sourceInfo.id, {
633
687
  requestBody: {
634
688
  parent: { id: destParentId },
635
- name: destPath.name
636
- }
689
+ name: destPath.name,
690
+ },
637
691
  });
638
692
  }
639
693
  return true;
@@ -740,38 +794,100 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
740
794
  */
741
795
  async ListObjects(prefix, delimiter = '/') {
742
796
  try {
797
+ // Ensure we have a valid token before making API calls
798
+ await this._ensureValidToken();
799
+ console.log(`[Box ListObjects] Called with prefix: "${prefix}", delimiter: "${delimiter}"`);
800
+ // Normalize the prefix - remove leading/trailing slashes for consistent handling
801
+ let normalizedPrefix = prefix;
802
+ if (normalizedPrefix && normalizedPrefix !== '/') {
803
+ // Remove leading slash
804
+ if (normalizedPrefix.startsWith('/')) {
805
+ normalizedPrefix = normalizedPrefix.substring(1);
806
+ }
807
+ // Remove trailing slash
808
+ if (normalizedPrefix.endsWith('/')) {
809
+ normalizedPrefix = normalizedPrefix.substring(0, normalizedPrefix.length - 1);
810
+ }
811
+ }
812
+ else {
813
+ // Empty string or "/" means root
814
+ normalizedPrefix = '';
815
+ }
816
+ console.log(`[Box ListObjects] Normalized prefix: "${normalizedPrefix}"`);
743
817
  let folderId;
744
- try {
745
- folderId = await this._getIdFromPath(prefix);
818
+ // Handle root folder case
819
+ if (!normalizedPrefix || normalizedPrefix === '') {
820
+ folderId = this._rootFolderId;
821
+ console.log(`[Box ListObjects] Using root folder ID: ${folderId}`);
746
822
  }
747
- catch (error) {
748
- // If folder doesn't exist, return empty result
749
- return { objects: [], prefixes: [] };
823
+ else {
824
+ try {
825
+ const itemInfo = await this._getItemInfoFromPath(normalizedPrefix);
826
+ if (!itemInfo) {
827
+ console.log(`[Box ListObjects] Folder not found for prefix: "${normalizedPrefix}"`);
828
+ return { objects: [], prefixes: [] };
829
+ }
830
+ if (itemInfo.type !== 'folder') {
831
+ console.log(`[Box ListObjects] Path "${normalizedPrefix}" is not a folder (type: ${itemInfo.type})`);
832
+ return { objects: [], prefixes: [] };
833
+ }
834
+ folderId = itemInfo.id;
835
+ }
836
+ catch (error) {
837
+ // If folder doesn't exist, return empty result
838
+ const errorMsg = error instanceof Error ? error.message : String(error);
839
+ console.log(`[Box ListObjects] Error resolving path "${normalizedPrefix}": ${errorMsg}`);
840
+ return { objects: [], prefixes: [] };
841
+ }
750
842
  }
843
+ console.log(`[Box ListObjects] Listing folder ID: ${folderId} for prefix: "${normalizedPrefix}"`);
751
844
  // Get folder contents using SDK
752
845
  const result = await this._client.folders.getFolderItems(folderId, {
753
846
  queryParams: {
754
- fields: ['id', 'name', 'type', 'size', 'content_type', 'modified_at', 'created_at', 'etag', 'sequence_id']
755
- }
847
+ fields: ['id', 'name', 'type', 'size', 'content_type', 'modified_at', 'created_at', 'etag', 'sequence_id'],
848
+ },
756
849
  });
757
850
  const objects = [];
758
851
  const prefixes = [];
759
- // Process entries
852
+ // Process entries - use normalizedPrefix for consistent path handling
760
853
  for (const entry of result.entries) {
761
- objects.push(this._convertToMetadata(entry, prefix));
854
+ objects.push(this._convertToMetadata(entry, normalizedPrefix));
762
855
  // If it's a folder, add to prefixes
763
856
  if (entry.type === 'folder') {
764
- const folderPath = prefix
765
- ? (prefix.endsWith('/') ? `${prefix}${entry.name}` : `${prefix}/${entry.name}`)
766
- : entry.name;
857
+ const folderPath = normalizedPrefix ? `${normalizedPrefix}/${entry.name}` : entry.name;
767
858
  prefixes.push(`${folderPath}/`);
768
859
  }
769
860
  }
861
+ console.log(`[Box ListObjects] Found ${objects.length} objects and ${prefixes.length} prefixes`);
770
862
  return { objects, prefixes };
771
863
  }
772
864
  catch (error) {
773
- console.error('Error listing objects', { prefix, error });
774
- return { objects: [], prefixes: [] };
865
+ console.error('[Box ListObjects] Error:', { prefix, error: error instanceof Error ? error.message : error });
866
+ // Re-throw with more context so the error is visible to the user
867
+ throw new Error(`Failed to list Box folder "${prefix}": ${error instanceof Error ? error.message : 'Unknown error'}`);
868
+ }
869
+ }
870
+ /**
871
+ * Ensures we have a valid access token, refreshing if necessary.
872
+ * Also reinitializes the Box client with the new token.
873
+ */
874
+ async _ensureValidToken() {
875
+ // Check if token is expired or about to expire (within 5 minutes)
876
+ const tokenExpiresSoon = this._tokenExpiresAt && Date.now() > this._tokenExpiresAt - 300000;
877
+ if (!this._accessToken || tokenExpiresSoon) {
878
+ console.log('[Box] Token expired or expiring soon, refreshing...');
879
+ if (this._refreshToken && this._clientId && this._clientSecret) {
880
+ const tokenData = await this._refreshAccessToken();
881
+ this._accessToken = tokenData.access_token;
882
+ this._tokenExpiresAt = Date.now() + tokenData.expires_in * 1000 - 60000;
883
+ // Reinitialize the Box client with the new token
884
+ const auth = new box_node_sdk_1.BoxDeveloperTokenAuth({ token: this._accessToken });
885
+ this._client = new box_node_sdk_1.BoxClient({ auth });
886
+ console.log('[Box] Token refreshed successfully');
887
+ }
888
+ else {
889
+ throw new Error('Cannot refresh Box token: missing credentials');
890
+ }
775
891
  }
776
892
  }
777
893
  /**
@@ -809,14 +925,14 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
809
925
  */
810
926
  async CreateDirectory(directoryPath) {
811
927
  try {
928
+ // Ensure we have a valid token before making API calls
929
+ await this._ensureValidToken();
812
930
  // Root directory always exists
813
931
  if (!directoryPath || directoryPath === '/' || directoryPath === '') {
814
932
  return true;
815
933
  }
816
934
  // Remove trailing slash if present
817
- const normalizedPath = directoryPath.endsWith('/')
818
- ? directoryPath.substring(0, directoryPath.length - 1)
819
- : directoryPath;
935
+ const normalizedPath = directoryPath.endsWith('/') ? directoryPath.substring(0, directoryPath.length - 1) : directoryPath;
820
936
  // First check if directory already exists
821
937
  try {
822
938
  if (await this.DirectoryExists(normalizedPath)) {
@@ -852,15 +968,14 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
852
968
  try {
853
969
  await this._client.folders.createFolder({
854
970
  name: folderName,
855
- parent: { id: parentFolderId }
971
+ parent: { id: parentFolderId },
856
972
  });
857
973
  console.log(`✅ Folder created successfully: ${normalizedPath}`);
858
974
  return true;
859
975
  }
860
976
  catch (error) {
861
977
  // Handle conflicts - if the folder already exists, that's a success
862
- if (error.statusCode === 409 ||
863
- (error.message && error.message.includes('item_name_in_use'))) {
978
+ if (error.statusCode === 409 || (error.message && error.message.includes('item_name_in_use'))) {
864
979
  console.log(`Folder already exists (conflict): ${normalizedPath}`);
865
980
  return true;
866
981
  }
@@ -873,7 +988,6 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
873
988
  return false;
874
989
  }
875
990
  }
876
- ;
877
991
  /**
878
992
  * Gets file representation information for a Box file
879
993
  *
@@ -912,11 +1026,11 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
912
1026
  // Get file with representations field - SDK handles auth automatically
913
1027
  const file = await this._client.files.getFileById(fileId, {
914
1028
  queryParams: {
915
- fields: ['representations']
1029
+ fields: ['representations'],
916
1030
  },
917
1031
  headers: {
918
- xRepHints: repHints
919
- }
1032
+ xRepHints: repHints,
1033
+ },
920
1034
  });
921
1035
  // Convert to plain JSON object
922
1036
  return JSON.parse(JSON.stringify(file));
@@ -926,7 +1040,6 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
926
1040
  throw error;
927
1041
  }
928
1042
  }
929
- ;
930
1043
  /**
931
1044
  * Deletes a directory from Box storage
932
1045
  *
@@ -961,9 +1074,7 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
961
1074
  async DeleteDirectory(directoryPath, recursive = false) {
962
1075
  try {
963
1076
  // Remove trailing slash if present
964
- const normalizedPath = directoryPath.endsWith('/')
965
- ? directoryPath.substring(0, directoryPath.length - 1)
966
- : directoryPath;
1077
+ const normalizedPath = directoryPath.endsWith('/') ? directoryPath.substring(0, directoryPath.length - 1) : directoryPath;
967
1078
  // Get folder ID
968
1079
  let folderId;
969
1080
  try {
@@ -977,8 +1088,8 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
977
1088
  if (!recursive) {
978
1089
  const contents = await this._client.folders.getFolderItems(folderId, {
979
1090
  queryParams: {
980
- limit: 1
981
- }
1091
+ limit: 1,
1092
+ },
982
1093
  });
983
1094
  if (contents.entries.length > 0) {
984
1095
  throw new Error('Directory is not empty');
@@ -988,8 +1099,8 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
988
1099
  if (recursive) {
989
1100
  await this._client.folders.deleteFolderById(folderId, {
990
1101
  queryParams: {
991
- recursive: true
992
- }
1102
+ recursive: true,
1103
+ },
993
1104
  });
994
1105
  }
995
1106
  else {
@@ -1050,13 +1161,13 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
1050
1161
  // Try as file first, then folder
1051
1162
  try {
1052
1163
  const fileInfo = await this._client.files.getFileById(params.objectId, {
1053
- queryParams: { fields: ['id', 'type'] }
1164
+ queryParams: { fields: ['id', 'type'] },
1054
1165
  });
1055
1166
  itemInfo = { id: fileInfo.id, type: fileInfo.type };
1056
1167
  }
1057
1168
  catch {
1058
1169
  const folderInfo = await this._client.folders.getFolderById(params.objectId, {
1059
- queryParams: { fields: ['id', 'type'] }
1170
+ queryParams: { fields: ['id', 'type'] },
1060
1171
  });
1061
1172
  itemInfo = { id: folderInfo.id, type: folderInfo.type };
1062
1173
  }
@@ -1074,8 +1185,8 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
1074
1185
  // Get full metadata using the SDK based on type
1075
1186
  const options = {
1076
1187
  queryParams: {
1077
- fields: ['id', 'name', 'type', 'size', 'content_type', 'modified_at', 'created_at', 'etag', 'sequence_id']
1078
- }
1188
+ fields: ['id', 'name', 'type', 'size', 'content_type', 'modified_at', 'created_at', 'etag', 'sequence_id'],
1189
+ },
1079
1190
  };
1080
1191
  const metadata = itemInfo.type === 'folder'
1081
1192
  ? await this._client.folders.getFolderById(itemInfo.id, options)
@@ -1120,6 +1231,8 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
1120
1231
  */
1121
1232
  async GetObject(params) {
1122
1233
  try {
1234
+ // Ensure we have a valid token before making API calls
1235
+ await this._ensureValidToken();
1123
1236
  // Validate params
1124
1237
  if (!params.objectId && !params.fullPath) {
1125
1238
  throw new Error('Either objectId or fullPath must be provided');
@@ -1201,9 +1314,13 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
1201
1314
  */
1202
1315
  async PutObject(objectName, data, contentType, metadata) {
1203
1316
  try {
1204
- console.log(`PutObject: ${objectName}`);
1317
+ // Ensure we have a valid token before making API calls
1318
+ await this._ensureValidToken();
1319
+ console.log(`[Box PutObject] Starting upload for: ${objectName}, size: ${data.length} bytes`);
1320
+ console.log(`[Box PutObject] Account: ${this.AccountName}, Root folder ID: ${this._rootFolderId}`);
1205
1321
  // Get the parent folder ID and file name
1206
1322
  const parsedPath = this._parsePath(objectName);
1323
+ console.log(`[Box PutObject] Parsed path:`, { name: parsedPath.name, parent: parsedPath.parent });
1207
1324
  let parentId = this._rootFolderId;
1208
1325
  if (parsedPath.parent) {
1209
1326
  try {
@@ -1232,33 +1349,43 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
1232
1349
  try {
1233
1350
  // Convert Buffer to Readable stream for the SDK
1234
1351
  const fileStream = stream_1.Readable.from(data);
1352
+ console.log(`[Box PutObject] Uploading to parent folder ID: ${parentId}, filename: ${parsedPath.name}`);
1235
1353
  if (fileId) {
1236
1354
  // Update existing file (upload new version)
1355
+ console.log(`[Box PutObject] File exists (ID: ${fileId}), uploading new version...`);
1237
1356
  await this._client.uploads.uploadFileVersion(fileId, {
1238
1357
  attributes: {
1239
- name: parsedPath.name
1358
+ name: parsedPath.name,
1240
1359
  },
1241
- file: fileStream
1360
+ file: fileStream,
1242
1361
  });
1243
1362
  console.log(`✅ File updated successfully: ${objectName}`);
1244
1363
  }
1245
1364
  else {
1246
1365
  // Upload new file
1366
+ console.log(`[Box PutObject] File does not exist, creating new file...`);
1247
1367
  await this._client.uploads.uploadFile({
1248
1368
  attributes: {
1249
1369
  name: parsedPath.name,
1250
- parent: { id: parentId }
1370
+ parent: { id: parentId },
1251
1371
  },
1252
- file: fileStream
1372
+ file: fileStream,
1253
1373
  });
1254
1374
  console.log(`✅ File uploaded successfully: ${objectName}`);
1255
1375
  }
1256
1376
  return true;
1257
1377
  }
1258
1378
  catch (uploadError) {
1259
- console.error(`Error uploading file:`, uploadError);
1260
- if (uploadError.statusCode === 409) {
1261
- console.log(`File already exists (conflict): ${objectName}`);
1379
+ const error = uploadError;
1380
+ console.error(`[Box PutObject] Error uploading file:`, {
1381
+ statusCode: error.statusCode,
1382
+ message: error.message,
1383
+ code: error.code,
1384
+ contextInfo: error.context_info,
1385
+ fullError: uploadError,
1386
+ });
1387
+ if (error.statusCode === 409) {
1388
+ console.log(`[Box PutObject] File already exists (conflict): ${objectName}`);
1262
1389
  return true;
1263
1390
  }
1264
1391
  return false;
@@ -1269,7 +1396,6 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
1269
1396
  return false;
1270
1397
  }
1271
1398
  }
1272
- ;
1273
1399
  /**
1274
1400
  * Copies a file from one location to another
1275
1401
  *
@@ -1327,7 +1453,7 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
1327
1453
  // Copy the file using SDK
1328
1454
  await this._client.files.copyFile(sourceInfo.id, {
1329
1455
  parent: { id: destParentId },
1330
- name: destPath.name
1456
+ name: destPath.name,
1331
1457
  });
1332
1458
  return true;
1333
1459
  }
@@ -1401,9 +1527,7 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
1401
1527
  return true;
1402
1528
  }
1403
1529
  // Remove trailing slash if present
1404
- const normalizedPath = directoryPath.endsWith('/')
1405
- ? directoryPath.substring(0, directoryPath.length - 1)
1406
- : directoryPath;
1530
+ const normalizedPath = directoryPath.endsWith('/') ? directoryPath.substring(0, directoryPath.length - 1) : directoryPath;
1407
1531
  try {
1408
1532
  const folderId = await this._findFolderIdByPath(normalizedPath);
1409
1533
  console.log(`✅ Directory ${normalizedPath} exists with ID: ${folderId}`);
@@ -1411,8 +1535,8 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
1411
1535
  try {
1412
1536
  const folderInfo = await this._client.folders.getFolderById(folderId, {
1413
1537
  queryParams: {
1414
- fields: ['type']
1415
- }
1538
+ fields: ['type'],
1539
+ },
1416
1540
  });
1417
1541
  return folderInfo.type === 'folder';
1418
1542
  }
@@ -1447,7 +1571,7 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
1447
1571
  async _findFolderIdByPath(path) {
1448
1572
  try {
1449
1573
  // Split the path into segments
1450
- const pathSegments = path.split('/').filter(segment => segment.length > 0);
1574
+ const pathSegments = path.split('/').filter((segment) => segment.length > 0);
1451
1575
  // Handle "All Files" special case - it's not an actual folder name in the API
1452
1576
  if (pathSegments.length > 0 && pathSegments[0] === 'All Files') {
1453
1577
  pathSegments.shift(); // Remove "All Files" from the path
@@ -1468,8 +1592,8 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
1468
1592
  queryParams: {
1469
1593
  fields: ['name', 'type', 'id'],
1470
1594
  limit: LIMIT,
1471
- offset: offset
1472
- }
1595
+ offset: offset,
1596
+ },
1473
1597
  });
1474
1598
  // Filter to only folders
1475
1599
  const folders = items.entries?.filter((item) => item.type === 'folder') || [];
@@ -1548,7 +1672,7 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
1548
1672
  query,
1549
1673
  limit: maxResults,
1550
1674
  fields: ['id', 'name', 'type', 'size', 'content_type', 'modified_at', 'created_at', 'etag', 'path_collection', 'parent'],
1551
- type: 'file' // Only search for files, not folders
1675
+ type: 'file', // Only search for files, not folders
1552
1676
  };
1553
1677
  // Build file extensions filter from fileTypes option
1554
1678
  if (options?.fileTypes && options.fileTypes.length > 0) {
@@ -1590,7 +1714,7 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
1590
1714
  // First verify the folder is accessible to avoid 404 errors with enterprise auth
1591
1715
  try {
1592
1716
  await this._client.folders.getFolderById(this._rootFolderId, {
1593
- queryParams: { fields: ['id'] }
1717
+ queryParams: { fields: ['id'] },
1594
1718
  });
1595
1719
  console.log(`✅ Scoping search to configured root folder: ${this._rootFolderId}`);
1596
1720
  searchParams.ancestorFolderIds = [this._rootFolderId];
@@ -1643,12 +1767,12 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
1643
1767
  matchInFilename: undefined,
1644
1768
  customMetadata: {
1645
1769
  id: fileItem.id || '',
1646
- etag: fileItem.etag || ''
1770
+ etag: fileItem.etag || '',
1647
1771
  },
1648
1772
  providerData: {
1649
1773
  boxItemId: fileItem.id,
1650
- boxItemType: fileItem.type
1651
- }
1774
+ boxItemType: fileItem.type,
1775
+ },
1652
1776
  });
1653
1777
  }
1654
1778
  }
@@ -1660,7 +1784,7 @@ let BoxFileStorage = class BoxFileStorage extends FileStorageBase_1.FileStorageB
1660
1784
  totalMatches,
1661
1785
  hasMore,
1662
1786
  // Box SDK v10 search doesn't provide pagination tokens in the standard response
1663
- nextPageToken: undefined
1787
+ nextPageToken: undefined,
1664
1788
  };
1665
1789
  }
1666
1790
  catch (error) {