@memberjunction/storage 3.1.0 → 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
@@ -107,19 +107,19 @@ class ClientCredentialsAuthProvider {
107
107
  client_id: this.clientId,
108
108
  scope: 'https://graph.microsoft.com/.default',
109
109
  client_secret: this.clientSecret,
110
- grant_type: 'client_credentials'
110
+ grant_type: 'client_credentials',
111
111
  });
112
112
  const response = await fetch(this.tokenEndpoint, {
113
113
  method: 'POST',
114
114
  headers: {
115
- 'Content-Type': 'application/x-www-form-urlencoded'
115
+ 'Content-Type': 'application/x-www-form-urlencoded',
116
116
  },
117
- body: data
117
+ body: data,
118
118
  });
119
119
  if (!response.ok) {
120
120
  throw new Error(`Failed to get access token: ${response.statusText}`);
121
121
  }
122
- const json = await response.json();
122
+ const json = (await response.json());
123
123
  this.accessToken = json.access_token;
124
124
  // Set token expiration time (subtract 5 minutes as a buffer)
125
125
  const expiresIn = json.expires_in || 3600;
@@ -127,6 +127,121 @@ class ClientCredentialsAuthProvider {
127
127
  return this.accessToken;
128
128
  }
129
129
  }
130
+ /**
131
+ * Implementation of the Microsoft Graph API AuthenticationProvider interface
132
+ * that uses the OAuth2 refresh token flow for per-user authentication.
133
+ *
134
+ * This provider handles token acquisition using a refresh token, enabling
135
+ * users to access their own OneDrive/SharePoint files rather than a shared
136
+ * service account.
137
+ *
138
+ * @remarks
139
+ * This class is designed for scenarios where each user authenticates with
140
+ * their own Microsoft account via OAuth. The refresh token is obtained
141
+ * through the OAuth authorization code flow and stored per-user.
142
+ */
143
+ class RefreshTokenAuthProvider {
144
+ /**
145
+ * Azure AD application (client) ID
146
+ */
147
+ clientId;
148
+ /**
149
+ * Azure AD application client secret
150
+ */
151
+ clientSecret;
152
+ /**
153
+ * OAuth2 refresh token for obtaining new access tokens
154
+ */
155
+ refreshToken;
156
+ /**
157
+ * OAuth2 token endpoint URL
158
+ */
159
+ tokenEndpoint;
160
+ /**
161
+ * Cached access token
162
+ */
163
+ accessToken = null;
164
+ /**
165
+ * Expiration timestamp for the cached token
166
+ */
167
+ tokenExpiration = null;
168
+ /**
169
+ * Callback to persist new tokens when they are refreshed
170
+ */
171
+ onTokenRefresh;
172
+ /**
173
+ * Creates a new RefreshTokenAuthProvider instance
174
+ *
175
+ * @param clientId - The Azure AD application (client) ID
176
+ * @param clientSecret - The Azure AD application client secret
177
+ * @param refreshToken - The OAuth2 refresh token
178
+ * @param tenantId - The Azure AD tenant ID (use 'common' for multi-tenant)
179
+ * @param onTokenRefresh - Optional callback to persist new tokens
180
+ */
181
+ constructor(clientId, clientSecret, refreshToken, tenantId = 'common', onTokenRefresh) {
182
+ this.clientId = clientId;
183
+ this.clientSecret = clientSecret;
184
+ this.refreshToken = refreshToken;
185
+ this.tokenEndpoint = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
186
+ this.onTokenRefresh = onTokenRefresh;
187
+ }
188
+ /**
189
+ * Gets an access token for Microsoft Graph API using the refresh token.
190
+ *
191
+ * This method implements the AuthenticationProvider interface required by the
192
+ * Microsoft Graph client. It uses the refresh token to obtain a new access token
193
+ * when needed, and caches the token until it expires.
194
+ *
195
+ * @returns A Promise that resolves to the access token string
196
+ * @throws Error if token acquisition fails
197
+ */
198
+ async getAccessToken() {
199
+ if (this.accessToken && this.tokenExpiration && this.tokenExpiration > new Date()) {
200
+ return this.accessToken;
201
+ }
202
+ console.log('[SharePoint RefreshTokenAuth] Refreshing access token...');
203
+ const data = new URLSearchParams({
204
+ client_id: this.clientId,
205
+ client_secret: this.clientSecret,
206
+ refresh_token: this.refreshToken,
207
+ grant_type: 'refresh_token',
208
+ scope: 'https://graph.microsoft.com/Files.ReadWrite.All offline_access',
209
+ });
210
+ const response = await fetch(this.tokenEndpoint, {
211
+ method: 'POST',
212
+ headers: {
213
+ 'Content-Type': 'application/x-www-form-urlencoded',
214
+ },
215
+ body: data,
216
+ });
217
+ if (!response.ok) {
218
+ const errorText = await response.text();
219
+ console.error('[SharePoint RefreshTokenAuth] Token refresh failed:', errorText);
220
+ throw new Error(`Failed to refresh access token: ${response.statusText}`);
221
+ }
222
+ const json = (await response.json());
223
+ this.accessToken = json.access_token;
224
+ // Set token expiration time (subtract 5 minutes as a buffer)
225
+ const expiresIn = json.expires_in || 3600;
226
+ this.tokenExpiration = new Date(Date.now() + (expiresIn - 300) * 1000);
227
+ console.log('[SharePoint RefreshTokenAuth] Token refreshed successfully, expires:', this.tokenExpiration);
228
+ // Microsoft may return a new refresh token - if so, persist it
229
+ if (json.refresh_token && json.refresh_token !== this.refreshToken) {
230
+ console.log('[SharePoint RefreshTokenAuth] New refresh token received, persisting...');
231
+ this.refreshToken = json.refresh_token;
232
+ if (this.onTokenRefresh) {
233
+ try {
234
+ await this.onTokenRefresh(json.refresh_token, json.access_token);
235
+ console.log('[SharePoint RefreshTokenAuth] New tokens persisted successfully');
236
+ }
237
+ catch (callbackError) {
238
+ console.error('[SharePoint RefreshTokenAuth] Failed to persist new tokens:', callbackError);
239
+ }
240
+ }
241
+ }
242
+ return this.accessToken;
243
+ }
244
+ }
130
245
  /**
131
246
  * FileStorageBase implementation for Microsoft SharePoint using the Microsoft Graph API
132
247
  *
@@ -194,38 +309,123 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
194
309
  * Optional ID of a subfolder to use as the root folder (if specified)
195
310
  */
196
311
  _rootFolderId;
312
+ /**
313
+ * OAuth2 Client ID (for per-user OAuth flow)
314
+ */
315
+ _clientID;
316
+ /**
317
+ * OAuth2 Client Secret (for per-user OAuth flow)
318
+ */
319
+ _clientSecret;
320
+ /**
321
+ * OAuth2 Refresh Token (for per-user OAuth flow)
322
+ */
323
+ _refreshToken;
324
+ /**
325
+ * Azure AD Tenant ID
326
+ */
327
+ _tenantID;
328
+ /**
329
+ * Callback for persisting refreshed tokens
330
+ */
331
+ _onTokenRefresh;
197
332
  /**
198
333
  * Creates a new SharePointFileStorage instance
199
334
  *
200
- * This constructor reads required configuration from environment variables
201
- * and initializes the Microsoft Graph client.
202
- *
203
- * @throws Error if required environment variables are missing
335
+ * This constructor reads configuration from environment variables if available.
336
+ * If no environment variables are set, the provider can be initialized later
337
+ * via the initialize() method with OAuth credentials from the database.
204
338
  */
205
339
  constructor() {
206
340
  super();
207
341
  // Try to get config from centralized configuration
208
342
  const config = (0, config_1.getProviderConfig)('sharePoint');
209
- // Extract values from config, fall back to env vars
210
- const clientId = config?.clientID || env.get('STORAGE_SHAREPOINT_CLIENT_ID').required().asString();
211
- const clientSecret = config?.clientSecret || env.get('STORAGE_SHAREPOINT_CLIENT_SECRET').required().asString();
212
- const tenantId = config?.tenantID || env.get('STORAGE_SHAREPOINT_TENANT_ID').required().asString();
213
- this._siteId = config?.siteID || env.get('STORAGE_SHAREPOINT_SITE_ID').required().asString();
214
- this._driveId = config?.driveID || env.get('STORAGE_SHAREPOINT_DRIVE_ID').required().asString();
215
- // Optionally set a root folder within the SharePoint drive
216
- this._rootFolderId = config?.rootFolderID || env.get('STORAGE_SHAREPOINT_ROOT_FOLDER_ID').asString();
217
- // Initialize Graph client with auth provider
218
- const authProvider = new ClientCredentialsAuthProvider(clientId, clientSecret, tenantId);
219
- this._client = microsoft_graph_client_1.Client.initWithMiddleware({
220
- authProvider: authProvider
221
- });
343
+ // Extract values from config, fall back to env vars (don't require them - initialize() may be called later)
344
+ const clientId = config?.clientID || env.get('STORAGE_SHAREPOINT_CLIENT_ID').asString();
345
+ const clientSecret = config?.clientSecret || env.get('STORAGE_SHAREPOINT_CLIENT_SECRET').asString();
346
+ const tenantId = config?.tenantID || env.get('STORAGE_SHAREPOINT_TENANT_ID').asString();
347
+ const siteId = config?.siteID || env.get('STORAGE_SHAREPOINT_SITE_ID').asString();
348
+ const driveId = config?.driveID || env.get('STORAGE_SHAREPOINT_DRIVE_ID').asString();
349
+ // Store OAuth credentials for IsConfigured check
350
+ this._clientID = clientId;
351
+ this._clientSecret = clientSecret;
352
+ this._tenantID = tenantId;
353
+ // Only initialize if we have all required credentials (env/config-based setup)
354
+ if (clientId && clientSecret && tenantId && siteId && driveId) {
355
+ this._siteId = siteId;
356
+ this._driveId = driveId;
357
+ // Optionally set a root folder within the SharePoint drive
358
+ this._rootFolderId = config?.rootFolderID || env.get('STORAGE_SHAREPOINT_ROOT_FOLDER_ID').asString();
359
+ // Initialize Graph client with client credentials auth provider (service account)
360
+ const authProvider = new ClientCredentialsAuthProvider(clientId, clientSecret, tenantId);
361
+ this._client = microsoft_graph_client_1.Client.initWithMiddleware({
362
+ authProvider: authProvider,
363
+ });
364
+ }
365
+ // If credentials not available, initialize() must be called with OAuth config
222
366
  }
223
367
  /**
224
368
  * Checks if SharePoint provider is properly configured.
225
- * Returns true if all required Microsoft Graph credentials are present.
369
+ * Returns true if the Graph client is initialized and has required IDs.
226
370
  */
227
371
  get IsConfigured() {
228
- return !!(this._siteId && this._driveId && this._client);
372
+ return !!(this._client && this._driveId);
373
+ }
374
+ /**
375
+ * Initializes the SharePoint storage provider with OAuth configuration from the database.
376
+ * This method is called by the FileStorageProviderEngine when loading per-user provider configurations.
377
+ *
378
+ * For per-user OAuth, this sets up the RefreshTokenAuthProvider which uses the user's
379
+ * refresh token to obtain access tokens for their OneDrive/SharePoint files.
380
+ *
381
+ * @param config - Configuration object containing OAuth2 credentials
382
+ */
383
+ async initialize(config) {
384
+ // Always call super to store accountId and accountName
385
+ await super.initialize(config);
386
+ if (!config) {
387
+ return; // Nothing to do, constructor already handled config from env/file
388
+ }
389
+ console.log('[SharePoint] Initializing with OAuth config...');
390
+ // Update OAuth2 credentials
391
+ this._clientID = config.clientID || this._clientID;
392
+ this._clientSecret = config.clientSecret || this._clientSecret;
393
+ this._refreshToken = config.refreshToken || this._refreshToken;
394
+ this._tenantID = config.tenantID || this._tenantID || 'common';
395
+ this._onTokenRefresh = config.onTokenRefresh;
396
+ // Update site/drive IDs if provided
397
+ if (config.siteID) {
398
+ this._siteId = config.siteID;
399
+ }
400
+ if (config.driveID) {
401
+ this._driveId = config.driveID;
402
+ }
403
+ if (config.rootFolderID) {
404
+ this._rootFolderId = config.rootFolderID;
405
+ }
406
+ // Validate we have required OAuth credentials
407
+ if (!this._clientID || !this._clientSecret || !this._refreshToken) {
408
+ throw new Error('SharePoint OAuth requires clientID, clientSecret, and refreshToken');
409
+ }
410
+ // Initialize the Graph client with refresh token auth provider (per-user OAuth)
411
+ const authProvider = new RefreshTokenAuthProvider(this._clientID, this._clientSecret, this._refreshToken, this._tenantID, this._onTokenRefresh);
412
+ this._client = microsoft_graph_client_1.Client.initWithMiddleware({
413
+ authProvider: authProvider,
414
+ });
415
+ // If no driveID provided, get the user's default OneDrive
416
+ if (!this._driveId) {
417
+ console.log("[SharePoint] No driveID provided, getting user's OneDrive...");
418
+ try {
419
+ const driveResponse = await this._client.api('/me/drive').get();
420
+ this._driveId = driveResponse.id;
421
+ console.log("[SharePoint] Using user's OneDrive:", this._driveId);
422
+ }
423
+ catch (error) {
424
+ console.error("[SharePoint] Failed to get user's OneDrive:", error);
425
+ throw new Error("Failed to get user's OneDrive. Ensure the refresh token has Files.ReadWrite.All scope.");
426
+ }
427
+ }
428
+ console.log('[SharePoint] Initialized successfully with driveId:', this._driveId);
229
429
  }
230
430
  /**
231
431
  * Gets the SharePoint item ID for a folder at the specified path
@@ -242,11 +442,12 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
242
442
  if (!path || path === '/' || path === '') {
243
443
  return this._rootFolderId || 'root';
244
444
  }
245
- const pathParts = path.split('/').filter(p => p);
445
+ const pathParts = path.split('/').filter((p) => p);
246
446
  let currentFolderId = this._rootFolderId || 'root';
247
447
  for (let i = 0; i < pathParts.length; i++) {
248
448
  const folderName = pathParts[i];
249
- const result = await this._client.api(`/drives/${this._driveId}/items/${currentFolderId}/children`)
449
+ const result = await this._client
450
+ .api(`/drives/${this._driveId}/items/${currentFolderId}/children`)
250
451
  .filter(`name eq '${folderName}' and folder ne null`)
251
452
  .get();
252
453
  if (!result.value || result.value.length === 0) {
@@ -276,9 +477,7 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
276
477
  const normalizedPath = path.startsWith('/') ? path.substring(1) : path;
277
478
  try {
278
479
  // Try to get the item directly by path
279
- const driveRoot = this._rootFolderId ?
280
- `/drives/${this._driveId}/items/${this._rootFolderId}` :
281
- `/drives/${this._driveId}/root`;
480
+ const driveRoot = this._rootFolderId ? `/drives/${this._driveId}/items/${this._rootFolderId}` : `/drives/${this._driveId}/root`;
282
481
  return await this._client.api(`${driveRoot}:/${normalizedPath}`).get();
283
482
  }
284
483
  catch (error) {
@@ -317,7 +516,7 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
317
516
  lastModified: new Date(item.lastModifiedDateTime),
318
517
  isDirectory,
319
518
  etag: item.eTag,
320
- customMetadata: {}
519
+ customMetadata: {},
321
520
  };
322
521
  }
323
522
  /**
@@ -372,11 +571,10 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
372
571
  try {
373
572
  const item = await this._getItemByPath(objectName);
374
573
  // Request a download URL - this is a time-limited URL
375
- const downloadUrl = await this._client.api(`/drives/${this._driveId}/items/${item.id}/createLink`)
376
- .post({
574
+ const downloadUrl = await this._client.api(`/drives/${this._driveId}/items/${item.id}/createLink`).post({
377
575
  type: 'view',
378
576
  scope: 'anonymous',
379
- expirationDateTime: new Date(Date.now() + 10 * 60 * 1000).toISOString() // 10 minutes
577
+ expirationDateTime: new Date(Date.now() + 10 * 60 * 1000).toISOString(), // 10 minutes
380
578
  });
381
579
  return downloadUrl.link.webUrl;
382
580
  }
@@ -421,12 +619,11 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
421
619
  // Get the new parent folder ID
422
620
  const parentFolderId = await this._getParentFolderIdByPath(newParentPath);
423
621
  // Move the item
424
- await this._client.api(`/drives/${this._driveId}/items/${item.id}`)
425
- .update({
622
+ await this._client.api(`/drives/${this._driveId}/items/${item.id}`).update({
426
623
  name: newName,
427
624
  parentReference: {
428
- id: parentFolderId
429
- }
625
+ id: parentFolderId,
626
+ },
430
627
  });
431
628
  return true;
432
629
  }
@@ -465,8 +662,7 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
465
662
  try {
466
663
  const item = await this._getItemByPath(objectName);
467
664
  // Delete the item
468
- await this._client.api(`/drives/${this._driveId}/items/${item.id}`)
469
- .delete();
665
+ await this._client.api(`/drives/${this._driveId}/items/${item.id}`).delete();
470
666
  return true;
471
667
  }
472
668
  catch (error) {
@@ -520,9 +716,7 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
520
716
  for (const item of children.value) {
521
717
  if (item.folder) {
522
718
  // This is a folder/directory
523
- const folderPath = prefix ?
524
- (prefix.endsWith('/') ? `${prefix}${item.name}` : `${prefix}/${item.name}`) :
525
- item.name;
719
+ const folderPath = prefix ? (prefix.endsWith('/') ? `${prefix}${item.name}` : `${prefix}/${item.name}`) : item.name;
526
720
  prefixes.push(`${folderPath}/`);
527
721
  }
528
722
  // Add all items as objects (including folders)
@@ -571,9 +765,7 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
571
765
  async CreateDirectory(directoryPath) {
572
766
  try {
573
767
  // Remove trailing slash if present
574
- const normalizedPath = directoryPath.endsWith('/') ?
575
- directoryPath.substring(0, directoryPath.length - 1) :
576
- directoryPath;
768
+ const normalizedPath = directoryPath.endsWith('/') ? directoryPath.substring(0, directoryPath.length - 1) : directoryPath;
577
769
  // Parse the path to get the parent folder and new folder name
578
770
  const pathParts = normalizedPath.split('/');
579
771
  const folderName = pathParts.pop() || '';
@@ -581,11 +773,10 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
581
773
  // Get the parent folder ID
582
774
  const parentFolderId = await this._getParentFolderIdByPath(parentPath);
583
775
  // Create the folder
584
- await this._client.api(`/drives/${this._driveId}/items/${parentFolderId}/children`)
585
- .post({
776
+ await this._client.api(`/drives/${this._driveId}/items/${parentFolderId}/children`).post({
586
777
  name: folderName,
587
778
  folder: {},
588
- '@microsoft.graph.conflictBehavior': 'fail'
779
+ '@microsoft.graph.conflictBehavior': 'fail',
589
780
  });
590
781
  return true;
591
782
  }
@@ -627,9 +818,7 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
627
818
  async DeleteDirectory(directoryPath, recursive = false) {
628
819
  try {
629
820
  // Remove trailing slash if present
630
- const normalizedPath = directoryPath.endsWith('/') ?
631
- directoryPath.substring(0, directoryPath.length - 1) :
632
- directoryPath;
821
+ const normalizedPath = directoryPath.endsWith('/') ? directoryPath.substring(0, directoryPath.length - 1) : directoryPath;
633
822
  const folder = await this._getItemByPath(normalizedPath);
634
823
  if (!recursive) {
635
824
  // Check if folder is empty
@@ -639,8 +828,7 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
639
828
  }
640
829
  }
641
830
  // Delete the folder (SharePoint will delete recursively by default)
642
- await this._client.api(`/drives/${this._driveId}/items/${folder.id}`)
643
- .delete();
831
+ await this._client.api(`/drives/${this._driveId}/items/${folder.id}`).delete();
644
832
  return true;
645
833
  }
646
834
  catch (error) {
@@ -821,17 +1009,15 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
821
1009
  const effectiveContentType = contentType || mime.lookup(objectName) || 'application/octet-stream';
822
1010
  if (data.length < 4 * 1024 * 1024) {
823
1011
  // For small files (< 4MB), use simple upload
824
- await this._client.api(`/drives/${this._driveId}/items/${parentFolderId}:/${fileName}:/content`)
825
- .put(data);
1012
+ await this._client.api(`/drives/${this._driveId}/items/${parentFolderId}:/${fileName}:/content`).put(data);
826
1013
  }
827
1014
  else {
828
1015
  // For larger files, use upload session
829
1016
  // Create upload session
830
- const uploadSession = await this._client.api(`/drives/${this._driveId}/items/${parentFolderId}:/${fileName}:/createUploadSession`)
831
- .post({
1017
+ const uploadSession = await this._client.api(`/drives/${this._driveId}/items/${parentFolderId}:/${fileName}:/createUploadSession`).post({
832
1018
  item: {
833
- '@microsoft.graph.conflictBehavior': 'replace'
834
- }
1019
+ '@microsoft.graph.conflictBehavior': 'replace',
1020
+ },
835
1021
  });
836
1022
  // Upload the file in chunks (could be improved with parallel uploads)
837
1023
  const maxChunkSize = 60 * 1024 * 1024; // 60 MB chunks
@@ -842,9 +1028,9 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
842
1028
  method: 'PUT',
843
1029
  headers: {
844
1030
  'Content-Length': chunk.length.toString(),
845
- 'Content-Range': contentRange
1031
+ 'Content-Range': contentRange,
846
1032
  },
847
- body: chunk
1033
+ body: chunk,
848
1034
  });
849
1035
  }
850
1036
  }
@@ -896,12 +1082,11 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
896
1082
  // Get destination parent folder ID
897
1083
  const destParentId = await this._getParentFolderIdByPath(destParentPath);
898
1084
  // Create a copy
899
- await this._client.api(`/drives/${this._driveId}/items/${sourceItem.id}/copy`)
900
- .post({
1085
+ await this._client.api(`/drives/${this._driveId}/items/${sourceItem.id}/copy`).post({
901
1086
  parentReference: {
902
- id: destParentId
1087
+ id: destParentId,
903
1088
  },
904
- name: destName
1089
+ name: destName,
905
1090
  });
906
1091
  return true;
907
1092
  }
@@ -975,9 +1160,7 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
975
1160
  async DirectoryExists(directoryPath) {
976
1161
  try {
977
1162
  // Remove trailing slash if present
978
- const normalizedPath = directoryPath.endsWith('/') ?
979
- directoryPath.substring(0, directoryPath.length - 1) :
980
- directoryPath;
1163
+ const normalizedPath = directoryPath.endsWith('/') ? directoryPath.substring(0, directoryPath.length - 1) : directoryPath;
981
1164
  const item = await this._getItemByPath(normalizedPath);
982
1165
  return !!item.folder;
983
1166
  }
@@ -1061,26 +1244,16 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
1061
1244
  {
1062
1245
  entityTypes: ['driveItem'],
1063
1246
  query: {
1064
- queryString: kqlQuery
1247
+ queryString: kqlQuery,
1065
1248
  },
1066
1249
  from: 0,
1067
1250
  size: maxResults,
1068
- fields: [
1069
- 'name',
1070
- 'path',
1071
- 'size',
1072
- 'lastModifiedDateTime',
1073
- 'fileSystemInfo',
1074
- 'webUrl',
1075
- 'id',
1076
- 'contentType'
1077
- ]
1078
- }
1079
- ]
1251
+ fields: ['name', 'path', 'size', 'lastModifiedDateTime', 'fileSystemInfo', 'webUrl', 'id', 'contentType'],
1252
+ },
1253
+ ],
1080
1254
  };
1081
1255
  // Execute the search
1082
- const response = await this._client.api('/search/query')
1083
- .post(searchRequest);
1256
+ const response = await this._client.api('/search/query').post(searchRequest);
1084
1257
  // Transform results
1085
1258
  return this.transformSearchResults(response, maxResults);
1086
1259
  }
@@ -1112,14 +1285,13 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
1112
1285
  queryParts.push(`(Path:"${this._siteId}/${this._driveId}")`);
1113
1286
  // Add path prefix filter if specified
1114
1287
  if (options?.pathPrefix) {
1115
- const normalizedPrefix = options.pathPrefix.startsWith('/')
1116
- ? options.pathPrefix.substring(1)
1117
- : options.pathPrefix;
1288
+ const normalizedPrefix = options.pathPrefix.startsWith('/') ? options.pathPrefix.substring(1) : options.pathPrefix;
1118
1289
  queryParts.push(`(Path:"${this._siteId}/${this._driveId}/${normalizedPrefix}*")`);
1119
1290
  }
1120
1291
  // Add file type filters if specified
1121
1292
  if (options?.fileTypes && options.fileTypes.length > 0) {
1122
- const fileTypeFilters = options.fileTypes.map(fileType => {
1293
+ const fileTypeFilters = options.fileTypes
1294
+ .map((fileType) => {
1123
1295
  // Handle both extensions (pdf) and MIME types (application/pdf)
1124
1296
  if (fileType.includes('/')) {
1125
1297
  // It's a MIME type
@@ -1130,7 +1302,8 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
1130
1302
  const extension = fileType.startsWith('.') ? fileType.substring(1) : fileType;
1131
1303
  return `FileType:${extension}`;
1132
1304
  }
1133
- }).join(' OR ');
1305
+ })
1306
+ .join(' OR ');
1134
1307
  queryParts.push(`(${fileTypeFilters})`);
1135
1308
  }
1136
1309
  // Add date filters if specified
@@ -1187,19 +1360,17 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
1187
1360
  name: resource.name || '',
1188
1361
  size: resource.size || 0,
1189
1362
  contentType: resource.file?.mimeType || mime.lookup(resource.name) || 'application/octet-stream',
1190
- lastModified: resource.lastModifiedDateTime
1191
- ? new Date(resource.lastModifiedDateTime)
1192
- : new Date(),
1363
+ lastModified: resource.lastModifiedDateTime ? new Date(resource.lastModifiedDateTime) : new Date(),
1193
1364
  objectId: resource.id || '', // SharePoint item ID for direct access
1194
- relevance: hit.rank ? (hit.rank / 100.0) : undefined,
1365
+ relevance: hit.rank ? hit.rank / 100.0 : undefined,
1195
1366
  excerpt: hit.summary || undefined,
1196
1367
  matchInFilename: this.determineMatchLocation(hit),
1197
1368
  providerData: {
1198
1369
  id: resource.id,
1199
1370
  webUrl: resource.webUrl,
1200
1371
  driveId: this._driveId,
1201
- siteId: this._siteId
1202
- }
1372
+ siteId: this._siteId,
1373
+ },
1203
1374
  };
1204
1375
  results.push(result);
1205
1376
  }
@@ -1209,7 +1380,7 @@ let SharePointFileStorage = class SharePointFileStorage extends FileStorageBase_
1209
1380
  return {
1210
1381
  results,
1211
1382
  totalMatches,
1212
- hasMore
1383
+ hasMore,
1213
1384
  };
1214
1385
  }
1215
1386
  /**