import { CreatePreAuthUploadUrlPayload, FileSearchOptions, FileSearchResultSet, FileStorageBase, GetObjectParams, GetObjectMetadataParams, GetObjectStreamParams, ObjectStreamResult, StorageListResult, StorageObjectMetadata } from '../generic/FileStorageBase.js'; /** * Callback function called when a new refresh token is issued. * Box issues a new refresh token with every token refresh, invalidating the old one. */ type TokenRefreshCallback = (newRefreshToken: string, newAccessToken?: string) => Promise; import { StorageProviderConfig } from '../generic/FileStorageBase.js'; /** * Configuration interface for Box storage provider. * Supports OAuth2 authentication with refresh token. * Extends StorageProviderConfig to include accountId and accountName. */ interface BoxConfig extends StorageProviderConfig { /** OAuth2 Client ID */ clientID?: string; /** OAuth2 Client Secret */ clientSecret?: string; /** OAuth2 Refresh Token */ refreshToken?: string; /** OAuth2 Access Token (short-lived) */ accessToken?: string; /** Box Enterprise ID for JWT auth */ enterpriseID?: string; /** Optional root folder ID to restrict operations */ rootFolderID?: string; /** * Callback called when a new refresh token is issued. * CRITICAL: Box issues new refresh tokens with each refresh, invalidating the old one. * This callback must be used to persist the new token. */ onTokenRefresh?: TokenRefreshCallback; } /** * FileStorageBase implementation for Box.com cloud storage * * This provider allows working with files stored in Box.com. It supports * authentication via access token, refresh token, or client credentials (JWT). * * @remarks * This implementation requires at least one of the following authentication methods: * * 1. Access Token: * - STORAGE_BOX_ACCESS_TOKEN - A valid Box API access token * * 2. Refresh Token: * - STORAGE_BOX_REFRESH_TOKEN - A valid Box API refresh token * - STORAGE_BOX_CLIENT_ID - Your Box application client ID * - STORAGE_BOX_CLIENT_SECRET - Your Box application client secret * * 3. Client Credentials (JWT): * - STORAGE_BOX_CLIENT_ID - Your Box application client ID * - STORAGE_BOX_CLIENT_SECRET - Your Box application client secret * - STORAGE_BOX_ENTERPRISE_ID - Your Box enterprise ID * * Optional configuration: * - STORAGE_BOX_ROOT_FOLDER_ID - ID of a Box folder to use as the root (defaults to '0' which is the root) * * @example * ```typescript * // Set required environment variables for JWT auth * process.env.STORAGE_BOX_CLIENT_ID = 'your-client-id'; * process.env.STORAGE_BOX_CLIENT_SECRET = 'your-client-secret'; * process.env.STORAGE_BOX_ENTERPRISE_ID = 'your-enterprise-id'; * * // Create the provider * const storage = new BoxFileStorage(); * await storage.initialize(); // Required for JWT auth * * // Upload a file * const fileContent = Buffer.from('Hello, Box!'); * await storage.PutObject('documents/hello.txt', fileContent, 'text/plain'); * * // Download a file * const downloadedContent = await storage.GetObject('documents/hello.txt'); * * // Get a temporary download URL * const downloadUrl = await storage.CreatePreAuthDownloadUrl('documents/hello.txt'); * ``` */ export declare class BoxFileStorage extends FileStorageBase { /** * The name of this storage provider */ protected readonly providerName = "Box"; /** * Box API access token */ private _accessToken; /** * Box API refresh token */ private _refreshToken; /** * Box application client ID */ private _clientId; /** * Box application client secret */ private _clientSecret; /** * Timestamp when current access token expires */ private _tokenExpiresAt; /** * Base URL for Box API */ private _baseApiUrl; /** * Base URL for Box Upload API */ private _uploadApiUrl; /** * ID of the Box folder to use as root */ private _rootFolderId; /** * Box enterprise ID for JWT auth */ private _enterpriseId; /** * Box SDK client for making API calls */ private _client; /** * Callback to persist new refresh tokens when they are issued. * Box issues new refresh tokens with each token refresh. */ private _onTokenRefresh?; /** * Creates a new BoxFileStorage instance * * This constructor reads the required Box authentication configuration * from environment variables. * * @throws Error if refresh token is provided without client ID and secret */ constructor(); /** * Checks if Box provider is properly configured. * Returns true if client credentials or access token are present. * Logs detailed error messages if configuration is incomplete. */ get IsConfigured(): boolean; /** * Initialize Box storage provider. * * **Always call this method** after creating an instance. * * @example Simple Deployment (Environment Variables) * const storage = new BoxFileStorage(); // Constructor loads env vars * await storage.initialize(); // No config - uses env vars * await storage.ListObjects('/'); * * @example Multi-Tenant (Database Credentials) * const storage = new BoxFileStorage(); * await storage.initialize({ * accountId: '12345', * accountName: 'Box Account', * clientID: '...', * clientSecret: '...', * refreshToken: '...', * rootFolderID: '0' * }); * * @param config - Optional. Omit to use env vars, provide to override with database creds. */ initialize(config?: BoxConfig): Promise; /** * Obtains an access token using client credentials flow * * This method requests a new access token using the Box client credentials * flow (JWT) with the enterprise as the subject. * * @private * @returns A Promise that resolves with the access token * @throws Error if token acquisition fails */ private _getAccessToken; /** * Refreshes the access token using the refresh token * * @private * @returns A Promise that resolves with the token data */ private _refreshAccessToken; /** * Returns the current Box API access token * * This method ensures a valid token is available before returning it, * refreshing or generating a new token if necessary. * * @returns A Promise that resolves to a valid access token string * * @example * ```typescript * // Get a valid Box access token * const token = await storage.AccessToken(); * console.log(`Using access token: ${token}`); * ``` */ AccessToken(): Promise; /** * Parses a path string into Box API components * * This helper method converts a standard path string (e.g., 'documents/reports/file.txt') * into components used by the Box API (folder ID, name, parent path). * * @private * @param path - The path to parse * @returns An object containing the parsed components: id, name, and parent */ private _parsePath; /** * Resolves a path string to a Box item ID * * This helper method navigates the Box folder hierarchy to find * the item at the specified path, returning its Box ID. * * @private * @param path - The path to resolve * @returns A Promise that resolves to the Box item ID * @throws Error if the item does not exist */ private _getIdFromPath; /** * Gets both ID and type information for an item at the given path * * @param path - Path to the item * @returns Object with id and type, or null if not found */ private _getItemInfoFromPath; /** * Converts a Box API item to StorageObjectMetadata * * This helper method transforms a Box API item representation into * the standard StorageObjectMetadata format used by FileStorageBase. * * @private * @param item - The Box API item object * @param parentPath - The parent path string * @returns A StorageObjectMetadata object */ private _convertToMetadata; /** * Creates a pre-authenticated upload URL for a file * * This method creates a Box upload session and returns a URL that can be used * to upload file content directly to Box without requiring authentication. * * @param objectName - Path where the file should be uploaded (e.g., 'documents/report.pdf') * @returns A Promise that resolves to an object containing the upload URL and provider key * @throws Error if the URL creation fails * * @remarks * - The parent folder structure will be created automatically if it doesn't exist * - The returned provider key contains the session ID needed to complete the upload * - Box upload sessions expire after a certain period (typically 1 hour) * * @example * ```typescript * try { * // Generate a pre-authenticated upload URL * const uploadInfo = await storage.CreatePreAuthUploadUrl('presentations/quarterly-results.pptx'); * * // The URL can be used to upload content directly * console.log(`Upload URL: ${uploadInfo.UploadUrl}`); * * // Make sure to save the provider key, as it's needed to reference the upload * console.log(`Provider Key: ${uploadInfo.ProviderKey}`); * * // You can use fetch or another HTTP client to upload to this URL * await fetch(uploadInfo.UploadUrl, { * method: 'PUT', * headers: { 'Content-Type': 'application/octet-stream' }, * body: fileContent * }); * } catch (error) { * console.error('Error creating upload URL:', error.message); * } * ``` */ CreatePreAuthUploadUrl(objectName: string): Promise; /** * Creates a pre-authenticated download URL for a file * * This method generates a time-limited URL that can be used to download * a file without authentication. The URL typically expires after 60 minutes. * * @param objectName - Path to the file to download (e.g., 'documents/report.pdf') * @returns A Promise that resolves to the download URL string * @throws Error if the file doesn't exist or URL creation fails * * @remarks * - Cannot be used with upload sessions that haven't been completed * - Box download URLs typically expire after 60 minutes * - Generated URLs can be shared with users who don't have Box access * * @example * ```typescript * try { * // Generate a pre-authenticated download URL * const downloadUrl = await storage.CreatePreAuthDownloadUrl('documents/financial-report.pdf'); * * console.log(`Download the file using this URL: ${downloadUrl}`); * * // The URL can be shared or used in a browser to download the file * // without requiring Box authentication * } catch (error) { * console.error('Error creating download URL:', error.message); * } * ``` */ CreatePreAuthDownloadUrl(objectName: string): Promise; /** * Moves a file or folder from one location to another * * This method moves a file or folder to a new location in Box storage. * It handles both renaming and changing the parent folder. * * @param oldObjectName - Current path of the object (e.g., 'old-folder/document.docx') * @param newObjectName - New path for the object (e.g., 'new-folder/renamed-document.docx') * @returns A Promise that resolves to true if successful, false otherwise * * @remarks * - Parent folders will be created automatically if they don't exist * - Works with both files and folders * - For folders, all contents will move with the folder * * @example * ```typescript * // Move a file to a different folder and rename it * const moveResult = await storage.MoveObject( * 'documents/old-report.pdf', * 'archive/2023/annual-report.pdf' * ); * * if (moveResult) { * console.log('File moved successfully'); * } else { * console.error('Failed to move file'); * } * ``` */ MoveObject(oldObjectName: string, newObjectName: string): Promise; /** * Deletes a file or folder from Box storage * * This method permanently deletes a file or folder. It can also * handle special cases like incomplete upload sessions. * * @param objectName - Path to the object to delete (e.g., 'documents/old-report.docx') * @returns A Promise that resolves to true if successful, false if an error occurs * * @remarks * - Returns true if the object doesn't exist (for idempotency) * - Can handle special provider keys like upload sessions * - Box puts deleted items in the trash, where they can be recovered for a limited time * - To permanently delete folder contents, use DeleteDirectory with recursive=true * * @example * ```typescript * // Delete a file * const deleteResult = await storage.DeleteObject('temp/draft-document.docx'); * * if (deleteResult) { * console.log('File deleted successfully or already didn\'t exist'); * } else { * console.error('Failed to delete file'); * } * * // Delete an upload session * await storage.DeleteObject('session:1234567890:documents/large-file.zip'); * ``` */ DeleteObject(objectName: string): Promise; /** * Lists files and folders in a given directory * * This method retrieves all files and subfolders in the specified directory. * It returns both a list of object metadata and a list of directory prefixes. * * @param prefix - Path to the directory to list (e.g., 'documents/reports') * @param delimiter - Optional delimiter character (default: '/') * @returns A Promise that resolves to a StorageListResult containing objects and prefixes * * @remarks * - The `objects` array includes both files and folders * - The `prefixes` array includes only folder paths (with trailing slashes) * - Returns empty arrays if the directory doesn't exist * - The delimiter parameter is included for interface compatibility but not used internally * * @example * ```typescript * // List all files and folders in the 'documents' directory * const result = await storage.ListObjects('documents'); * * // Process files and folders * console.log(`Found ${result.objects.length} items:`); * for (const obj of result.objects) { * console.log(`- ${obj.name} (${obj.isDirectory ? 'Folder' : 'File'}, ${obj.size} bytes)`); * } * * // List subfolders only * console.log(`Found ${result.prefixes.length} subfolders:`); * for (const prefix of result.prefixes) { * console.log(`- ${prefix}`); * } * ``` */ ListObjects(prefix: string, delimiter?: string): Promise; /** * Ensures we have a valid access token, refreshing if necessary. * Also reinitializes the Box client with the new token. */ private _ensureValidToken; /** * Creates a new directory (folder) in Box storage * * This method creates a folder at the specified path, automatically * creating any parent folders that don't exist. * * @param directoryPath - Path where the directory should be created (e.g., 'documents/reports/2023') * @returns A Promise that resolves to true if successful, false if an error occurs * * @remarks * - Creates parent directories recursively if they don't exist * - Returns true if the directory already exists (idempotent operation) * - Trailing slashes in the path are automatically removed * * @example * ```typescript * // Create a nested directory structure * const createResult = await storage.CreateDirectory('documents/reports/2023/Q1'); * * if (createResult) { * console.log('Directory created successfully'); * * // Now we can put files in this directory * await storage.PutObject( * 'documents/reports/2023/Q1/financial-summary.xlsx', * fileContent, * 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' * ); * } else { * console.error('Failed to create directory'); * } * ``` */ CreateDirectory(directoryPath: string): Promise; /** * Gets file representation information for a Box file * * This method retrieves information about available representations * (such as thumbnails, previews, or other formats) for a specific file. * * @param fileId - The Box file ID to get representations for * @param repHints - The representation hints string (format and options) * @returns A Promise that resolves to a JSON object containing representations data * @throws Error if the request fails * * @remarks * - Requires a valid file ID (not a path) * - The repHints parameter controls what type of representations are returned * - Common representation types include thumbnails, preview images, and text extractions * * @example * ```typescript * try { * // Get a high-resolution PNG representation of a file * const fileId = '12345'; * const representations = await storage.GetFileRepresentations( * fileId, * 'png?dimensions=2048x2048' * ); * * // Process the representation information * console.log('Available representations:', representations); * } catch (error) { * console.error('Error getting representations:', error.message); * } * ``` */ GetFileRepresentations(fileId: string, repHints?: string): Promise; /** * Deletes a directory from Box storage * * This method deletes a folder and optionally its contents. By default, * it will only delete empty folders unless recursive is set to true. * * @param directoryPath - Path to the directory to delete (e.g., 'documents/old-reports') * @param recursive - If true, delete the directory and all its contents; if false, only delete if empty * @returns A Promise that resolves to true if successful, false if an error occurs * * @remarks * - Returns true if the directory doesn't exist (idempotent operation) * - If recursive=false and the directory contains files, the operation will fail * - Box puts deleted folders in the trash, where they can be recovered for a limited time * - Trailing slashes in the path are automatically removed * * @example * ```typescript * // Try to delete an empty folder * const deleteResult = await storage.DeleteDirectory('temp/empty-folder'); * * // Delete a folder and all its contents * const recursiveDeleteResult = await storage.DeleteDirectory('archive/old-data', true); * * if (recursiveDeleteResult) { * console.log('Folder and all its contents deleted successfully'); * } else { * console.error('Failed to delete folder'); * } * ``` */ DeleteDirectory(directoryPath: string, recursive?: boolean): Promise; /** * Gets metadata for a file or folder * * This method retrieves metadata about a file or folder in Box storage, * such as size, type, and modification date. * * @param objectName - Path to the object to get metadata for (e.g., 'documents/report.pdf') * @returns A Promise that resolves to a StorageObjectMetadata object * @throws Error if the object doesn't exist or cannot be accessed * * @example * ```typescript * try { * // Get metadata for a file * const metadata = await storage.GetObjectMetadata('presentations/quarterly-update.pptx'); * * console.log(`Name: ${metadata.name}`); * console.log(`Path: ${metadata.path}`); * console.log(`Size: ${metadata.size} bytes`); * console.log(`Content Type: ${metadata.contentType}`); * console.log(`Last Modified: ${metadata.lastModified}`); * console.log(`Is Directory: ${metadata.isDirectory}`); * * // Box-specific metadata is available in customMetadata * console.log(`Box ID: ${metadata.customMetadata.id}`); * } catch (error) { * console.error('Error getting metadata:', error.message); * } * ``` */ GetObjectMetadata(params: GetObjectMetadataParams): Promise; /** * Downloads a file's contents * * This method retrieves the raw content of a file as a Buffer. * * @param objectName - Path to the file to download (e.g., 'documents/report.pdf') * @returns A Promise that resolves to a Buffer containing the file's contents * @throws Error if the file doesn't exist or cannot be downloaded * * @remarks * - This method will throw an error if the object is a folder * - For large files, consider using CreatePreAuthDownloadUrl instead * - For upload sessions that haven't been completed, this method will fail * * @example * ```typescript * try { * // Download a text file * const fileContent = await storage.GetObject('documents/notes.txt'); * * // Convert Buffer to string for text files * const textContent = fileContent.toString('utf8'); * console.log('File content:', textContent); * * // For binary files, you can write the buffer to disk * // or process it as needed * } catch (error) { * console.error('Error downloading file:', error.message); * } * ``` */ GetObject(params: GetObjectParams): Promise; /** * Box supports ranged streaming via the download endpoint's `Range` header. */ get SupportsStreaming(): boolean; /** * Streams a file's content from Box, optionally honoring a byte range. * * Uses `downloads.downloadFile(fileId, { headers: { range } })`, which returns a * Node.js readable stream — the file is never buffered fully in memory. The Box SDK * does not surface `Content-Length`/`Content-Range` from this call, so this method * resolves the object's total size (and content type) via {@link GetObjectMetadata} * to populate {@link ObjectStreamResult.ContentLength} / `ContentRange`. The supplied * inclusive `Range` is clamped to the object size so `ContentRange.End` is always valid. * * @param params - Object identifier (prefer objectId) plus optional Range. * @returns A Promise resolving to an {@link ObjectStreamResult}. * @throws Error if the file doesn't exist or cannot be streamed. */ GetObjectStream(params: GetObjectStreamParams): Promise; /** * Uploads a file to Box storage * * This method uploads a file to the specified path in Box storage. It automatically * determines whether to use a simple upload or chunked upload based on file size. * * @param objectName - Path where the file should be uploaded (e.g., 'documents/report.pdf') * @param data - Buffer containing the file content * @param contentType - Optional MIME type of the file (if not provided, it will be guessed from the filename) * @param metadata - Optional metadata to associate with the file (not used in Box implementation) * @returns A Promise that resolves to true if successful, false if an error occurs * * @remarks * - Automatically creates parent directories if they don't exist * - Files smaller than 50MB use a simple upload * - Files 50MB or larger use a chunked upload process * - If a file with the same name exists, it will be replaced * * @example * ```typescript * // Create a simple text file * const textContent = Buffer.from('This is a sample document', 'utf8'); * const uploadResult = await storage.PutObject( * 'documents/sample.txt', * textContent, * 'text/plain' * ); * * // Upload a large file using chunked upload * const largeFileBuffer = fs.readFileSync('/path/to/large-presentation.pptx'); * const largeUploadResult = await storage.PutObject( * 'presentations/quarterly-results.pptx', * largeFileBuffer, * 'application/vnd.openxmlformats-officedocument.presentationml.presentation' * ); * * if (largeUploadResult) { * console.log('Large file uploaded successfully'); * } else { * console.error('Failed to upload large file'); * } * ``` */ PutObject(objectName: string, data: Buffer, contentType?: string, metadata?: Record): Promise; /** * Copies a file from one location to another * * This method creates a copy of a file at a new location. The original file * remains unchanged. * * @param sourceObjectName - Path to the source file (e.g., 'templates/report-template.docx') * @param destinationObjectName - Path where the copy should be created (e.g., 'documents/new-report.docx') * @returns A Promise that resolves to true if successful, false if an error occurs * * @remarks * - Only files can be copied; folders cannot be copied with this method * - Parent directories in the destination path will be created automatically if they don't exist * - If a file with the same name exists at the destination, it will be replaced * * @example * ```typescript * // Copy a template file to a new location with a different name * const copyResult = await storage.CopyObject( * 'templates/financial-report.xlsx', * 'reports/2023/q1-financial-report.xlsx' * ); * * if (copyResult) { * console.log('File copied successfully'); * } else { * console.error('Failed to copy file'); * } * ``` */ CopyObject(sourceObjectName: string, destinationObjectName: string): Promise; /** * Checks if a file or folder exists * * This method verifies whether an object (file or folder) exists at the specified path. * * @param objectName - Path to check (e.g., 'documents/report.pdf') * @returns A Promise that resolves to true if the object exists, false otherwise * * @example * ```typescript * // Check if a file exists before attempting to download it * const exists = await storage.ObjectExists('presentations/quarterly-update.pptx'); * * if (exists) { * // File exists, proceed with download * const fileContent = await storage.GetObject('presentations/quarterly-update.pptx'); * // Process the file... * } else { * console.log('File does not exist'); * } * ``` */ ObjectExists(objectName: string): Promise; /** * Checks if a directory exists * * This method verifies whether a folder exists at the specified path. * Unlike ObjectExists, this method also checks that the item is a folder. * * @param directoryPath - Path to check (e.g., 'documents/reports') * @returns A Promise that resolves to true if the directory exists, false otherwise * * @remarks * - Returns false if the path exists but points to a file instead of a folder * - Trailing slashes in the path are automatically removed * * @example * ```typescript * // Check if a directory exists before creating a file in it * const dirExists = await storage.DirectoryExists('documents/reports'); * * if (!dirExists) { * // Create the directory first * await storage.CreateDirectory('documents/reports'); * } * * // Now we can safely put a file in this directory * await storage.PutObject('documents/reports/annual-summary.pdf', fileContent, 'application/pdf'); * ``` */ DirectoryExists(directoryPath: string): Promise; /** * Finds a Box folder ID by traversing a path string * * This helper method navigates through the Box folder hierarchy, * following each segment of the path to find the ID of the target folder. * It uses pagination to handle large folders efficiently. * * @private * @param path - The path string to resolve (e.g., 'documents/reports/2023') * @returns A Promise that resolves to the Box folder ID * @throws Error if any segment of the path cannot be found */ private _findFolderIdByPath; /** * Search files in Box using Box Search API. * * This method provides full-text search capabilities across file names and content * using Box's native search functionality. It supports filtering by file type, * date ranges, and path prefixes. * * @param query - The search query string. Supports boolean operators (AND, OR, NOT) and exact phrases in quotes * @param options - Optional search options to filter and customize results * @param options.maxResults - Maximum number of results to return (default: 100, max: 200) * @param options.fileTypes - Array of file types to filter by (e.g., ['pdf', 'docx'] or ['application/pdf']) * @param options.modifiedAfter - Only return files modified after this date * @param options.modifiedBefore - Only return files modified before this date * @param options.pathPrefix - Restrict search to files within this path (e.g., 'documents/reports/') * @param options.searchContent - Whether to search file contents (Box searches content by default) * @returns A Promise resolving to a FileSearchResultSet containing matching files * * @remarks * - Box searches both file names and content by default * - The searchContent option is provided for interface compatibility but doesn't restrict Box's behavior * - File types can be specified as extensions or MIME types * - Date filters use Box's created_at_range parameter * - Path prefix is implemented by filtering results to ancestor folder IDs * - Results are sorted by relevance when available * * @example * ```typescript * // Search for PDF files containing "quarterly report" * const results = await storage.SearchFiles('quarterly report', { * fileTypes: ['pdf'], * pathPrefix: 'documents/reports', * modifiedAfter: new Date('2023-01-01'), * maxResults: 50 * }); * * console.log(`Found ${results.results.length} matching files`); * for (const file of results.results) { * console.log(`- ${file.path} (${file.size} bytes, score: ${file.relevance})`); * if (file.excerpt) { * console.log(` Excerpt: ${file.excerpt}`); * } * } * ``` */ SearchFiles(query: string, options?: FileSearchOptions): Promise; /** * Builds a list of file extensions from file type specifications * * This helper method converts generic file type specifications (extensions or MIME types) * into Box API fileExtensions parameter values. * * @private * @param fileTypes - Array of file extensions (e.g., 'pdf', 'docx') or MIME types * @returns Array of file extension strings (without dots) */ private _buildFileExtensionsFilter; /** * Reconstructs the full path to a Box item from its pathCollection * * This helper method builds the complete path by traversing the parent * folder hierarchy stored in the item's pathCollection. * * @private * @param item - The Box item with pathCollection data * @returns The full path string (e.g., 'documents/reports/file.pdf') */ private _reconstructPath; /** * Checks if a string is a Box Object ID (numeric) vs a path (contains /) * * @param identifier - String to check * @returns True if it's a Box file/folder ID, false if it's a path */ private _isObjectId; } export {}; //# sourceMappingURL=BoxFileStorage.d.ts.map