/** * BaseStorageAdapter - Abstract base class for all storage adapters * * Provides common functionality: * - Built-in retry logic with exponential backoff * - Error handling and categorization * - Health check interface * - Circuit breaker integration * - Request/response logging * - Correlation ID propagation * - API Client initialization (optional for REST API adapters) * * Subclasses MUST implement: * - upload() - Upload file to storage * - download() - Download file from storage * - delete() - Delete file from storage * - getSignedUrl() - Generate signed/presigned URL * - isAvailable() - Check if adapter is configured and ready * - healthCheck() - Provider health status * * Retry Configuration: * - By default, uses STORAGE_RETRY_CONFIG constants (3 attempts, exponential backoff) * - Can be customized per adapter via BaseStorageAdapterConfig.retry * - Can be disabled by setting retry to false * - Automatically tracks retry attempts in results * * @example * ```ts * class MyStorageAdapter extends BaseStorageAdapter { * async upload(params: UploadParams): Promise { * // Implementation * } * // ... other methods * } * ``` */ import type { StorageBucketInfo, StorageCopyFileParams, StorageListBucketsResult, StorageListFilesParams, StorageListFilesResult, StorageMoveFileParams, StoragePublicUrlParams, StorageReplaceFileParams, StorageSignedUploadUrlParams, StorageSignedUploadUrlResult, StorageUpdateBucketParams, BaseStorageAdapterConfig, UploadParams, UploadResult, DownloadParams, DownloadResult, DeleteParams, FileDeleteResult, PresignedUrlOptions, PresignedUrlResult, AdapterHealthCheck, StorageRetryConfig, FileMetadata, BucketConfiguration, UploadProgressEvent, STORAGE_ADAPTER_TYPE, BUCKET_PURPOSE, STORAGE_ENVIRONMENT } from '@plyaz/types/storage'; import { ADAPTER_HEALTH_STATUS } from '@plyaz/types/storage'; import { StoragePackageError } from '@plyaz/errors'; import { type LoggerInterface } from '@plyaz/types'; /** * BaseStorageAdapter - Abstract base class for all storage adapters * * Provides: * - Automatic retry logic with exponential backoff * - Error handling and wrapping * - Health monitoring * - Logging * - Retry attempt tracking * * Subclasses must implement all abstract methods */ export declare abstract class BaseStorageAdapter { readonly name: string; readonly type: STORAGE_ADAPTER_TYPE; readonly priority: number; readonly enabled: boolean; protected readonly logger?: LoggerInterface; protected readonly retryConfig: StorageRetryConfig | false; protected readonly autoCreateBuckets: boolean; readonly defaultEnvironment?: STORAGE_ENVIRONMENT; readonly defaultRegion?: string; protected readonly adapterOnProgress?: (progress: UploadProgressEvent) => void; protected readonly adapterAbortSignal?: AbortSignal; private apiClient; private clientInitPromise; private readonly apiClientConfig?; private lastHealthCheck?; private healthStatus; constructor(config: BaseStorageAdapterConfig); /** * Initialize API client for REST API-based adapters * SDK-based adapters (CloudflareR2, Supabase) do NOT need this */ private initializeApiClient; /** * Ensure API client is initialized (for REST API adapters) * Throws if adapter requires API client but it's not initialized */ protected ensureApiClientInitialized(): Promise; /** * Upload a file to storage * Subclasses must implement the actual upload logic */ abstract upload(params: UploadParams): Promise; /** * Download a file from storage * Subclasses must implement the actual download logic */ abstract download(params: DownloadParams): Promise; /** * Delete a file from storage * Subclasses must implement the actual delete logic */ abstract delete(params: DeleteParams): Promise; /** * Generate a presigned/signed URL for file access * Subclasses must implement the URL generation logic */ abstract getSignedUrl(options: PresignedUrlOptions): Promise; /** * Check if adapter is available and properly configured * Subclasses must implement availability check */ abstract isAvailable(): boolean; /** * Perform health check on the storage provider * Subclasses must implement provider-specific health check */ abstract healthCheck(): Promise; /** * Get file metadata (required for compliance checks) * Subclasses must implement metadata retrieval * * @param fileId - Unique file identifier * @returns File metadata * @param fileId - File ID or path * @param bucket - Optional bucket name (required for dynamic bucket mode) * @throws StoragePackageError if file not found */ abstract getFileMetadata(fileId: string, bucket?: string): Promise; /** * Restore a soft-deleted file * Subclasses must implement file restoration * * @param fileId - Unique file identifier * @returns Restored file metadata * @throws StoragePackageError if file not found or cannot be restored */ abstract restoreFile(fileId: string): Promise; /** * Translate bucket purpose to provider-specific bucket name * Each adapter implements its own naming convention * * Examples: * - CloudflareR2: BUCKET_PURPOSE.COMPLIANCE → "plyaz-prod-weur-records-vault" * - Supabase: BUCKET_PURPOSE.COMPLIANCE → "compliance" * - S3: BUCKET_PURPOSE.COMPLIANCE → "plyaz-compliance-prod-us-east-1" * * @param purpose - Universal bucket purpose * @param config - Bucket configuration (business model, tier, org, env, region) * @returns Provider-specific bucket name */ abstract translateBucketPurpose(purpose: BUCKET_PURPOSE, config: BucketConfiguration): string; /** * Create a bucket if it doesn't exist * Optional: Only needed if adapter supports bucket creation * * @param bucketName - Provider-specific bucket name * @param purpose - Bucket purpose for configuration * @throws StoragePackageError if bucket creation fails */ abstract createBucket?(bucketName: string, purpose: BUCKET_PURPOSE): Promise; /** * List all buckets * Optional: Only needed if adapter supports bucket listing * * @returns Promise - List of all buckets * @throws StoragePackageError if operation fails */ abstract listBuckets?(): Promise; /** * Get bucket details * Optional: Only needed if adapter supports bucket info retrieval * * @param bucketName - Bucket name * @returns Promise - Bucket information * @throws StoragePackageError if bucket not found */ abstract getBucket?(bucketName: string): Promise; /** * Update bucket settings * Optional: Only needed if adapter supports bucket updates * * @param params - Update parameters * @throws StoragePackageError if operation fails */ abstract updateBucket?(params: StorageUpdateBucketParams): Promise; /** * List files in a bucket * Optional: Only needed if adapter supports file listing * * @param params - List parameters * @returns Promise - List of files * @throws StoragePackageError if operation fails */ abstract listFiles?(params: StorageListFilesParams): Promise; /** * Move a file from one location to another * Optional: Only needed if adapter supports file moving * * @param params - Move parameters * @returns Promise - Metadata of moved file * @throws StoragePackageError if operation fails */ abstract moveFile?(params: StorageMoveFileParams): Promise; /** * Copy a file to another location * Optional: Only needed if adapter supports file copying * * @param params - Copy parameters * @returns Promise - Metadata of copied file * @throws StoragePackageError if operation fails */ abstract copyFile?(params: StorageCopyFileParams): Promise; /** * Replace/overwrite an existing file * Optional: Only needed if adapter supports file replacement * * @param params - Replace parameters * @returns Promise - Metadata of replaced file * @throws StoragePackageError if operation fails */ abstract replaceFile?(params: StorageReplaceFileParams): Promise; /** * Create a signed URL for direct upload * Optional: Only needed if adapter supports signed upload URLs * * @param params - Signed upload URL parameters * @returns Promise - Signed URL and metadata * @throws StoragePackageError if operation fails */ abstract createSignedUploadUrl?(params: StorageSignedUploadUrlParams): Promise; /** * Get public URL for a file * Optional: Only needed if adapter supports public URLs * * @param params - Public URL parameters * @returns Promise - Public URL * @throws StoragePackageError if operation fails */ abstract getPublicUrl?(params: StoragePublicUrlParams): Promise; /** * Upload large file using chunked/multipart upload * Optional: Only needed if adapter supports chunked uploads for large files * Handles entire chunked upload flow internally using provider SDK * * @param params - Upload parameters (same as regular upload) * @returns Promise - Upload result with file metadata * @throws StoragePackageError if operation fails */ uploadChunked?(params: UploadParams): Promise; /** * Get file size limit for a bucket purpose * Used by StorageService for validation before upload * * @param purpose - Bucket purpose (MEDIA_IMAGES, COMPLIANCE, etc.) * @returns Maximum file size in bytes, or undefined for no limit */ abstract getFileSizeLimit(purpose: BUCKET_PURPOSE): number | undefined; /** * Get allowed MIME types for a bucket purpose * Used by StorageService for validation before upload * * @param purpose - Bucket purpose (MEDIA_IMAGES, COMPLIANCE, etc.) * @returns Array of allowed MIME types, or undefined to allow all */ abstract getAllowedMimeTypes(purpose: BUCKET_PURPOSE): string[] | undefined; /** * Generate file key/path for storage * Each adapter implements its own key generation strategy * * @param params - Upload parameters * @returns Generated file key/path */ protected abstract generateFileKey(params: UploadParams): string; /** * Get current health status */ getHealthStatus(): ADAPTER_HEALTH_STATUS; /** * Get last health check timestamp */ getLastHealthCheck(): Date | undefined; /** * Update health status (called after health checks) */ protected updateHealthStatus(status: ADAPTER_HEALTH_STATUS): void; /** * Execute operation with retry logic * Wraps any async operation with automatic retry based on adapter config */ protected withRetry(operation: () => Promise, operationName: string): Promise; /** * Wrap errors with StoragePackageError * Provides consistent error handling across all adapters */ protected wrapError(error: unknown, operation: string, context?: Record): StoragePackageError; /** * Log operation start */ protected logOperationStart(operation: string, params?: Record): void; /** * Log operation success */ protected logOperationSuccess(operation: string, result?: Record): void; /** * Log operation error */ protected logOperationError(operation: string, error: unknown): void; /** * Get adapter information */ getAdapterInfo(): { name: string; type: STORAGE_ADAPTER_TYPE; priority: number; enabled: boolean; healthStatus: ADAPTER_HEALTH_STATUS; lastHealthCheck?: Date; }; /** * Ensure bucket exists, creating it if necessary * Uses autoCreateBuckets flag and createBucket() method * * @param purpose - Bucket purpose * @param config - Bucket configuration * @returns Provider-specific bucket name */ protected ensureBucket(purpose: BUCKET_PURPOSE, config: BucketConfiguration): Promise; /** * Get bucket name for a given purpose and configuration * Does NOT create the bucket, just returns the name * * @param purpose - Bucket purpose * @param config - Bucket configuration * @returns Provider-specific bucket name */ getBucketName(purpose: BUCKET_PURPOSE, config: BucketConfiguration): string; /** * Prepare upload parameters (common logic for all adapters) * Determines bucket, generates file key, ensures bucket exists * * @param params - Upload parameters * @returns Prepared bucket name and file key * @protected */ protected prepareUpload(params: UploadParams): Promise<{ bucketName: string; fileKey: string; bucketPurpose: BUCKET_PURPOSE; }>; /** * Convert stream to buffer (optional, subclasses can override) * @param stream - Readable stream * @returns Buffer * @protected */ protected streamToBuffer(_stream: globalThis.NodeJS.ReadableStream): Promise; /** * Convert file parameter to Buffer * Wrapper around utility function that provides adapter's streamToBuffer * * @param file - File data (Buffer or stream) * @returns Buffer * @protected */ protected convertToBuffer(file: Buffer | globalThis.NodeJS.ReadableStream | null | undefined): Promise; /** * Build FileMetadata result object * Common logic for constructing metadata response * * @param params - Upload parameters * @param fileKey - Generated file key * @param bucketName - Bucket name * @param bucketPurpose - Bucket purpose * @param fileSize - File size in bytes * @param path - Optional custom path (defaults to fileKey) * @returns FileMetadata * @protected */ protected buildFileMetadata(options: { params: UploadParams; fileKey: string; bucketName: string; bucketPurpose: BUCKET_PURPOSE; fileSize: number; path?: string; }): FileMetadata; /** * Emit progress callback (per-upload or adapter-level) * Calls both callbacks if both are configured * * @param params - Upload parameters * @param progress - Progress event data * @protected */ protected emitProgress(params: UploadParams, progress: UploadProgressEvent): void; /** * Upload buffer to a URL with progress tracking. * * Uses @plyaz/api's uploadWithProgress which handles both Node.js and browser * environments with real-time progress events. * * @param options - Upload options * @param options.url - The target URL to upload to * @param options.buffer - The file buffer to upload * @param options.contentType - MIME type of the file * @param options.params - Original upload params (for progress callbacks) * @param options.fileId - File ID for progress tracking * @param options.filename - Filename for progress tracking * @param options.headers - Additional headers to send * @param options.method - HTTP method (default: PUT) * @returns Promise resolving when upload completes * @protected */ protected uploadBufferWithProgress(options: { url: string; buffer: Buffer; contentType: string; params: UploadParams; fileId: string; filename: string; headers?: Record; method?: 'PUT' | 'POST'; }): Promise; }