@kumix/storage 0.1.0 → 0.1.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"cloudinary.js","names":["cloudinary","publicId"],"sources":["../src/providers/cloudinary.ts","../src/services/cloudinary.ts","../src/cloudinary.ts"],"sourcesContent":["/**\n * Cloudinary storage provider implementation\n * Provides cloud storage operations using Cloudinary's API for media management\n */\n\nimport { v2 as cloudinary } from \"cloudinary\";\n\nimport type {\n BatchDeleteOptions,\n BatchDeleteResult,\n CloudinaryConfig,\n CopyFolderOptions,\n CopyFolderResult,\n CopyOptions,\n CopyResult,\n CreateFolderOptions,\n CreateFolderResult,\n DeleteFolderOptions,\n DeleteFolderResult,\n DeleteOptions,\n DeleteResult,\n DownloadOptions,\n DownloadResult,\n DuplicateOptions,\n DuplicateResult,\n ExistsResult,\n FolderExistsResult,\n ListFoldersOptions,\n ListFoldersResult,\n ListOptions,\n ListResult,\n MoveOptions,\n MoveResult,\n PresignedUrlOptions,\n PresignedUrlResult,\n RenameFolderOptions,\n RenameFolderResult,\n StorageInterface,\n UploadOptions,\n UploadResult,\n} from \"../types\";\n\n/**\n * Cloudinary storage provider\n * Implements StorageInterface for Cloudinary cloud storage\n * @internal\n */\nexport class CloudinaryProvider implements StorageInterface {\n private config: CloudinaryConfig;\n\n constructor(config: CloudinaryConfig) {\n this.config = config;\n\n // Configure Cloudinary\n cloudinary.config({\n cloud_name: config.cloudName,\n api_key: config.apiKey,\n api_secret: config.apiSecret,\n secure: config.secure !== false,\n });\n }\n\n // File operations\n async upload(options: UploadOptions): Promise<UploadResult> {\n try {\n // Convert Buffer/Uint8Array to base64 for Cloudinary\n let fileData: string;\n if (typeof options.file === \"string\") {\n fileData = options.file;\n } else {\n fileData = `data:${options.contentType || \"application/octet-stream\"};base64,${Buffer.from(options.file).toString(\"base64\")}`;\n }\n\n // Determine resource type based on content type\n let resourceType: \"image\" | \"video\" | \"raw\" = \"raw\";\n if (options.contentType?.startsWith(\"image/\")) {\n resourceType = \"image\";\n } else if (options.contentType?.startsWith(\"video/\")) {\n resourceType = \"video\";\n }\n\n // Extract folder and filename from key for Cloudinary folder support\n const keyParts = options.key.split(\"/\");\n let folder = \"\";\n let publicId = keyParts[keyParts.length - 1];\n\n if (keyParts.length > 1) {\n folder = keyParts.slice(0, -1).join(\"/\");\n }\n\n const dotIndex = publicId.lastIndexOf(\".\");\n if (dotIndex > 0) {\n publicId = publicId.slice(0, dotIndex);\n }\n\n const uploadOptions: Record<string, unknown> = {\n public_id: publicId,\n resource_type: resourceType,\n overwrite: true,\n invalidate: true,\n ...options.metadata,\n };\n\n // Add folder if extracted from key\n if (folder) {\n uploadOptions.folder = folder;\n }\n\n // Add folder if enabled in config\n if (this.config.folder) {\n uploadOptions.folder = `${this.config.folder}/${folder}`;\n }\n\n const result = await cloudinary.uploader.upload(fileData, uploadOptions);\n\n // Sample result structure\n // {\n // asset_id: 'b4033628c0897843e61e362167f338d6',\n // public_id: 'starter/workspaces/logo/ws_1KBQ2V0VRKK5EN936G8S2XZ30',\n // version: 1765965394,\n // version_id: 'abd49804c49061cef406f8d703466849',\n // signature: 'ff52169a483bada7ca4943ce322132137bec4d2f',\n // width: 256,\n // height: 256,\n // format: 'png',\n // resource_type: 'image',\n // created_at: '2025-12-17T09:52:12Z',\n // tags: [],\n // bytes: 76429,\n // type: 'upload',\n // etag: '85e34db0ea9ce8e771e25bd8eb33ba56',\n // placeholder: false,\n // url: 'http://res.cloudinary.com/droen5ejq/image/upload/v1765965394/starter/workspaces/logo/ws_1KBQ2V0VRKK5EN936G8S2XZ30.png',\n // secure_url: 'https://res.cloudinary.com/droen5ejq/image/upload/v1765965394/starter/workspaces/logo/ws_1KBQ2V0VRKK5EN936G8S2XZ30.png',\n // asset_folder: 'starter/workspaces/logo',\n // display_name: 'ws_1KBQ2V0VRKK5EN936G8S2XZ30',\n // overwritten: true,\n // api_key: '255239588173378'\n // }\n\n const publicUrl = this.getPublicUrl(result.public_id, result.format);\n\n return {\n success: true,\n key: result.public_id,\n url: result.secure_url,\n publicUrl,\n etag: result.etag,\n size: result.bytes,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Upload failed\",\n };\n }\n }\n\n async download(options: DownloadOptions): Promise<DownloadResult> {\n try {\n // Cloudinary doesn't support direct download like S3\n // We need to fetch from the public URL\n const publicUrl = this.getPublicUrl(options.key);\n\n const response = await fetch(publicUrl);\n if (!response.ok) {\n return {\n success: false,\n error: `Failed to download: ${response.status} ${response.statusText}`,\n };\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const content = Buffer.from(arrayBuffer);\n\n return {\n success: true,\n content, // Main content field\n data: content, // Alias for backward compatibility\n contentType: response.headers.get(\"content-type\") || undefined,\n contentLength: content.length,\n lastModified: response.headers.get(\"last-modified\")\n ? new Date(response.headers.get(\"last-modified\")!)\n : new Date(),\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Download failed\",\n };\n }\n }\n\n async delete(options: DeleteOptions): Promise<DeleteResult> {\n try {\n // Extract folder and filename for proper deletion\n const keyParts = options.key.split(\"/\");\n let publicId = options.key;\n\n if (keyParts.length > 1) {\n const folder = keyParts.slice(0, -1).join(\"/\");\n const filename = keyParts[keyParts.length - 1];\n publicId = `${folder}/${filename}`;\n }\n\n const dotIndex = publicId.lastIndexOf(\".\");\n if (dotIndex > 0) {\n publicId = publicId.slice(0, dotIndex);\n }\n\n // Add folder if enabled in config\n if (this.config.folder) {\n publicId = `${this.config.folder}/${publicId}`;\n }\n\n // Determine resource type from key/path\n let resourceType: \"image\" | \"video\" | \"raw\" = \"raw\";\n\n // Try to get resource info first to determine type\n try {\n await cloudinary.api.resource(publicId, { resource_type: \"image\" });\n resourceType = \"image\";\n } catch {\n try {\n await cloudinary.api.resource(publicId, { resource_type: \"video\" });\n resourceType = \"video\";\n } catch {\n resourceType = \"raw\";\n }\n }\n\n await cloudinary.uploader.destroy(publicId, {\n resource_type: resourceType,\n invalidate: true,\n });\n\n return {\n success: true,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Delete failed\",\n };\n }\n }\n\n async batchDelete(options: BatchDeleteOptions): Promise<BatchDeleteResult> {\n try {\n // Use Cloudinary Admin API for batch delete\n return {\n success: false,\n errors: options.keys.map((key) => ({\n key,\n error: \"Cloudinary batch delete not implemented yet\",\n })),\n };\n } catch (error) {\n return {\n success: false,\n errors: options.keys.map((key) => ({\n key,\n error: error instanceof Error ? error.message : \"Batch delete failed\",\n })),\n };\n }\n }\n\n async list(options: ListOptions = {}): Promise<ListResult> {\n try {\n const prefix = options.prefix || \"\";\n const maxResults = options.maxKeys || 50;\n\n // List all resource types\n const [images, videos, rawFiles] = await Promise.all([\n cloudinary.api\n .resources({\n type: \"upload\",\n resource_type: \"image\",\n prefix,\n max_results: maxResults,\n next_cursor: options.continuationToken,\n })\n .catch(() => ({ resources: [], next_cursor: undefined })),\n\n cloudinary.api\n .resources({\n type: \"upload\",\n resource_type: \"video\",\n prefix,\n max_results: maxResults,\n next_cursor: options.continuationToken,\n })\n .catch(() => ({ resources: [], next_cursor: undefined })),\n\n cloudinary.api\n .resources({\n type: \"upload\",\n resource_type: \"raw\",\n prefix,\n max_results: maxResults,\n next_cursor: options.continuationToken,\n })\n .catch(() => ({ resources: [], next_cursor: undefined })),\n ]);\n\n const allResources = [\n ...(images.resources || []),\n ...(videos.resources || []),\n ...(rawFiles.resources || []),\n ];\n\n const files = allResources.map((resource: Record<string, unknown>) => ({\n key: String(resource.public_id || \"\"),\n size: Number(resource.bytes) || 0,\n lastModified: new Date(String(resource.created_at || new Date())),\n etag: String(resource.etag || \"\"),\n contentType: resource.format\n ? `${resource.resource_type}/${resource.format}`\n : \"application/octet-stream\",\n }));\n\n return {\n success: true,\n files,\n isTruncated: !!(images.next_cursor || videos.next_cursor || rawFiles.next_cursor),\n nextContinuationToken: images.next_cursor || videos.next_cursor || rawFiles.next_cursor,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"List failed\",\n };\n }\n }\n\n async exists(_key: string): Promise<ExistsResult> {\n try {\n // Use Cloudinary Admin API to check if resource exists\n return {\n exists: false,\n error: \"Cloudinary exists check not implemented yet\",\n };\n } catch (error) {\n return {\n exists: false,\n error: error instanceof Error ? error.message : \"Check existence failed\",\n };\n }\n }\n\n async copy(_options: CopyOptions): Promise<CopyResult> {\n try {\n // Cloudinary doesn't have direct copy, would need to download and re-upload\n return {\n success: false,\n error: \"Cloudinary copy not implemented yet\",\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Copy failed\",\n };\n }\n }\n\n async move(_options: MoveOptions): Promise<MoveResult> {\n try {\n // Use Cloudinary Admin API to rename/move\n return {\n success: false,\n error: \"Cloudinary move not implemented yet\",\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Move failed\",\n };\n }\n }\n\n async duplicate(options: DuplicateOptions): Promise<DuplicateResult> {\n // Duplicate is just an alias for copy\n return this.copy(options);\n }\n\n async getPresignedUrl(_options: PresignedUrlOptions): Promise<PresignedUrlResult> {\n try {\n // Cloudinary uses different approach - signed URLs\n return {\n success: false,\n error: \"Cloudinary presigned URL not implemented yet\",\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Presigned URL generation failed\",\n };\n }\n }\n\n getPublicUrl(key: string, extension?: string): string {\n // Generate Cloudinary URL\n const protocol = this.config.secure !== false ? \"https\" : \"http\";\n const baseUrl = `${protocol}://res.cloudinary.com/${this.config.cloudName}`;\n\n // Use key directly as public ID - folder structure comes from the key itself\n const publicId = key;\n\n // For images: /image/upload/v1234567890/sample.jpg\n // For videos: /video/upload/v1234567890/sample.mp4\n // For raw files: /raw/upload/v1234567890/sample.pdf\n\n // Default to image for now, in real implementation you'd detect file type\n return `${baseUrl}/image/upload/${publicId}${extension ? `.${extension}` : \"\"}`;\n }\n\n // Folder operations\n async createFolder(options: CreateFolderOptions): Promise<CreateFolderResult> {\n try {\n // Cloudinary folders are created implicitly when uploading files\n return {\n success: true,\n path: options.path,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Create folder failed\",\n };\n }\n }\n\n async deleteFolder(options: DeleteFolderOptions): Promise<DeleteFolderResult> {\n try {\n // Cloudinary requires deleting all assets in folder first\n // Then delete the folder itself\n const folderPath = options.path.endsWith(\"/\") ? options.path.slice(0, -1) : options.path;\n\n if (options.recursive) {\n // Delete all resources in the folder\n await cloudinary.api.delete_resources_by_prefix(folderPath);\n }\n\n // Delete the folder\n await cloudinary.api.delete_folder(folderPath);\n\n return {\n success: true,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Delete folder failed\",\n };\n }\n }\n\n async listFolders(_options?: ListFoldersOptions): Promise<ListFoldersResult> {\n try {\n // Use Cloudinary Admin API to list folders\n const result = await cloudinary.api.root_folders();\n\n const folders = (result.folders || []).map((folder: Record<string, unknown>) => ({\n name: folder.name,\n path: folder.path,\n }));\n\n return {\n success: true,\n folders,\n isTruncated: false,\n nextContinuationToken: undefined,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"List folders failed\",\n };\n }\n }\n\n async folderExists(_path: string): Promise<FolderExistsResult> {\n try {\n // Check if folder exists in Cloudinary\n return {\n exists: false,\n error: \"Cloudinary folder exists check not implemented yet\",\n };\n } catch (error) {\n return {\n exists: false,\n error: error instanceof Error ? error.message : \"Check folder existence failed\",\n };\n }\n }\n\n async renameFolder(_options: RenameFolderOptions): Promise<RenameFolderResult> {\n try {\n // Cloudinary folder rename would require moving all assets\n return {\n success: false,\n error: \"Cloudinary rename folder not implemented yet\",\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Rename folder failed\",\n };\n }\n }\n\n async copyFolder(_options: CopyFolderOptions): Promise<CopyFolderResult> {\n try {\n // Cloudinary folder copy would require copying all assets\n return {\n success: false,\n error: \"Cloudinary copy folder not implemented yet\",\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Copy folder failed\",\n };\n }\n }\n}\n","/**\n * Cloudinary storage service implementation\n * High-level service wrapper for Cloudinary storage operations with convenience methods\n */\n\nimport { loadCloudinaryConfig } from \"../config\";\nimport { CloudinaryProvider } from \"../providers/cloudinary\";\nimport type {\n BatchDeleteOptions,\n BatchDeleteResult,\n CloudinaryConfig,\n CopyFolderOptions,\n CopyFolderResult,\n CopyOptions,\n CopyResult,\n CreateFolderOptions,\n CreateFolderResult,\n DeleteFolderOptions,\n DeleteFolderResult,\n DeleteOptions,\n DeleteResult,\n DownloadOptions,\n DownloadResult,\n DuplicateOptions,\n DuplicateResult,\n ExistsResult,\n FolderExistsResult,\n ListFoldersOptions,\n ListFoldersResult,\n ListOptions,\n ListResult,\n MoveOptions,\n MoveResult,\n PresignedUrlOptions,\n PresignedUrlResult,\n RenameFolderOptions,\n RenameFolderResult,\n StorageInterface,\n UploadOptions,\n UploadResult,\n} from \"../types\";\n\n/**\n * Cloudinary storage service\n * High-level service for Cloudinary storage operations with convenience methods\n * @public\n */\nexport class CloudinaryService implements StorageInterface {\n private provider: CloudinaryProvider;\n private config: CloudinaryConfig;\n\n constructor();\n constructor(config: CloudinaryConfig);\n constructor(config?: CloudinaryConfig) {\n this.config = config || loadCloudinaryConfig();\n this.provider = new CloudinaryProvider(this.config);\n }\n\n // Utility methods\n getConfig(): CloudinaryConfig {\n return { ...this.config };\n }\n\n getProvider(): string {\n return this.config.provider;\n }\n\n getPublicUrl(key: string): string {\n return this.provider.getPublicUrl(key);\n }\n\n // File operations\n async upload(options: UploadOptions): Promise<UploadResult> {\n return this.provider.upload(options);\n }\n\n async download(options: DownloadOptions): Promise<DownloadResult> {\n return this.provider.download(options);\n }\n\n async delete(options: DeleteOptions): Promise<DeleteResult> {\n return this.provider.delete(options);\n }\n\n async batchDelete(options: BatchDeleteOptions): Promise<BatchDeleteResult> {\n return this.provider.batchDelete(options);\n }\n\n async list(options?: ListOptions): Promise<ListResult> {\n return this.provider.list(options);\n }\n\n async exists(key: string): Promise<ExistsResult> {\n return this.provider.exists(key);\n }\n\n async copy(options: CopyOptions): Promise<CopyResult> {\n return this.provider.copy(options);\n }\n\n async move(options: MoveOptions): Promise<MoveResult> {\n return this.provider.move(options);\n }\n\n async duplicate(options: DuplicateOptions): Promise<DuplicateResult> {\n return this.provider.duplicate(options);\n }\n\n async getPresignedUrl(options: PresignedUrlOptions): Promise<PresignedUrlResult> {\n return this.provider.getPresignedUrl(options);\n }\n\n // Folder operations\n async createFolder(options: CreateFolderOptions): Promise<CreateFolderResult> {\n return this.provider.createFolder(options);\n }\n\n async deleteFolder(options: DeleteFolderOptions): Promise<DeleteFolderResult> {\n return this.provider.deleteFolder(options);\n }\n\n async listFolders(options?: ListFoldersOptions): Promise<ListFoldersResult> {\n return this.provider.listFolders(options);\n }\n\n async folderExists(path: string): Promise<FolderExistsResult> {\n return this.provider.folderExists(path);\n }\n\n async renameFolder(options: RenameFolderOptions): Promise<RenameFolderResult> {\n return this.provider.renameFolder(options);\n }\n\n async copyFolder(options: CopyFolderOptions): Promise<CopyFolderResult> {\n return this.provider.copyFolder(options);\n }\n\n // Convenience methods\n async uploadFile(\n key: string,\n file: Buffer | Uint8Array | string,\n contentType?: string,\n metadata?: Record<string, string>,\n ): Promise<UploadResult> {\n return this.upload({ key, file, contentType, metadata });\n }\n\n async downloadFile(key: string): Promise<DownloadResult> {\n return this.download({ key });\n }\n\n async deleteFile(key: string): Promise<DeleteResult> {\n return this.delete({ key });\n }\n\n async deleteFiles(keys: string[]): Promise<BatchDeleteResult> {\n return this.batchDelete({ keys });\n }\n\n async listFiles(prefix?: string, maxKeys?: number): Promise<ListResult> {\n return this.list({ prefix, maxKeys });\n }\n\n async fileExists(key: string): Promise<boolean> {\n const result = await this.exists(key);\n return result.exists;\n }\n\n async copyFile(\n sourceKey: string,\n destinationKey: string,\n metadata?: Record<string, string>,\n ): Promise<CopyResult> {\n return this.copy({ sourceKey, destinationKey, metadata });\n }\n\n async moveFile(\n sourceKey: string,\n destinationKey: string,\n metadata?: Record<string, string>,\n ): Promise<MoveResult> {\n return this.move({ sourceKey, destinationKey, metadata });\n }\n\n async duplicateFile(\n sourceKey: string,\n destinationKey: string,\n metadata?: Record<string, string>,\n ): Promise<DuplicateResult> {\n return this.duplicate({ sourceKey, destinationKey, metadata });\n }\n\n async renameFile(\n sourceKey: string,\n destinationKey: string,\n metadata?: Record<string, string>,\n ): Promise<MoveResult> {\n return this.moveFile(sourceKey, destinationKey, metadata);\n }\n\n async getDownloadUrl(key: string, expiresIn?: number): Promise<PresignedUrlResult> {\n return this.getPresignedUrl({ key, operation: \"get\", expiresIn });\n }\n\n async getUploadUrl(\n key: string,\n contentType?: string,\n expiresIn?: number,\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl({\n key,\n operation: \"put\",\n contentType,\n expiresIn,\n });\n }\n\n // Folder convenience methods\n async createFolderPath(path: string): Promise<CreateFolderResult> {\n return this.createFolder({ path });\n }\n\n async deleteFolderPath(path: string, recursive = false): Promise<DeleteFolderResult> {\n return this.deleteFolder({ path, recursive });\n }\n\n async folderPathExists(path: string): Promise<boolean> {\n const result = await this.folderExists(path);\n return result.exists;\n }\n\n async renameFolderPath(oldPath: string, newPath: string): Promise<RenameFolderResult> {\n return this.renameFolder({ oldPath, newPath });\n }\n\n async copyFolderPath(\n sourcePath: string,\n destinationPath: string,\n recursive = true,\n ): Promise<CopyFolderResult> {\n return this.copyFolder({ sourcePath, destinationPath, recursive });\n }\n}\n","import type { EnvRecord } from \"./config\";\nimport { loadCloudinaryConfig } from \"./config\";\nimport { CloudinaryService } from \"./services/cloudinary\";\nimport type { CloudinaryConfig } from \"./types\";\n\n// === SERVICE CLASSES ===\nexport { CloudinaryService } from \"./services/cloudinary\";\n\n// === MAIN FACTORY FUNCTIONS (Primary API) ===\n/**\n * Create Cloudinary service using environment variables or manual configuration\n * @param config Optional Cloudinary configuration. If not provided, loads from environment variables\n * @param env - Optional environment record\n * @returns CloudinaryService instance\n * @throws Error if configuration is invalid or environment variables are missing\n * @public\n *\n * @example\n * ```typescript\n * import { createCloudinary } from '@kumix/storage/cloudinary';\n *\n * // Using environment variables\n * // Set: KUMIX_CLOUDINARY_CLOUD_NAME=my-cloud, KUMIX_CLOUDINARY_API_KEY=123..., etc.\n * const cloudinaryFromEnv = createCloudinary();\n * await cloudinaryFromEnv.uploadFile('photo.jpg', imageBuffer);\n *\n * // Using manual configuration\n * const cloudinary = createCloudinary({\n * provider: 'cloudinary',\n * cloudName: 'my-cloud-name',\n * apiKey: '123456789012345',\n * apiSecret: 'your-api-secret',\n * secure: true,\n * folder: 'uploads'\n * });\n *\n * // Upload with transformations\n * await cloudinary.uploadFile('profile-pic.jpg', imageBuffer);\n *\n * // Get optimized URL\n * const publicUrl = cloudinary.getPublicUrl('profile-pic.jpg');\n * console.log(publicUrl); // Cloudinary optimized URL\n * ```\n */\nexport function createCloudinary(): CloudinaryService;\nexport function createCloudinary(config: CloudinaryConfig): CloudinaryService;\nexport function createCloudinary(config: CloudinaryConfig, env: EnvRecord): CloudinaryService;\nexport function createCloudinary(config?: CloudinaryConfig, env?: EnvRecord): CloudinaryService {\n if (config) {\n return new CloudinaryService(config);\n }\n const envConfig = loadCloudinaryConfig(env);\n return new CloudinaryService(envConfig);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cloudinary.js","names":[],"sources":["../src/providers/cloudinary.ts","../src/services/cloudinary.ts","../src/cloudinary.ts"],"sourcesContent":["/**\n * Cloudinary storage provider implementation\n * Provides cloud storage operations using Cloudinary's API for media management\n */\n\nimport type {\n BatchDeleteOptions,\n BatchDeleteResult,\n CloudinaryConfig,\n CopyFolderOptions,\n CopyFolderResult,\n CopyOptions,\n CopyResult,\n CreateFolderOptions,\n CreateFolderResult,\n DeleteFolderOptions,\n DeleteFolderResult,\n DeleteOptions,\n DeleteResult,\n DownloadOptions,\n DownloadResult,\n DuplicateOptions,\n DuplicateResult,\n ExistsResult,\n FolderExistsResult,\n ListFoldersOptions,\n ListFoldersResult,\n ListOptions,\n ListResult,\n MoveOptions,\n MoveResult,\n PresignedUrlOptions,\n PresignedUrlResult,\n RenameFolderOptions,\n RenameFolderResult,\n StorageInterface,\n UploadOptions,\n UploadResult,\n} from \"../types\";\n\n/**\n * Cloudinary storage provider\n * Implements StorageInterface for Cloudinary cloud storage\n * @internal\n */\n// biome-ignore lint/suspicious/noExplicitAny: Cloudinary SDK is dynamically imported and untyped here.\ntype CloudinaryClient = any;\n\nexport class CloudinaryProvider implements StorageInterface {\n private config: CloudinaryConfig;\n private _client: CloudinaryClient | null = null;\n\n constructor(config: CloudinaryConfig) {\n this.config = config;\n }\n\n /**\n * Lazily load the Cloudinary SDK and re-apply this instance's config to the\n * module-global v2 client. The SDK is dynamically imported so the optional\n * `cloudinary` peer dep isn't required at module load time — consumers who\n * only use the S3 provider can import the package without installing it.\n *\n * NOTE: the Cloudinary SDK keeps a single global config, so creating multiple\n * `CloudinaryProvider` instances with different credentials in the same\n * process is not supported — the most recently-configured instance wins.\n * `getClient()` re-applies config before each API call to narrow the race\n * window when instances are interleaved.\n */\n private async getClient(): Promise<CloudinaryClient> {\n if (!this._client) {\n try {\n const mod = await import(\"cloudinary\");\n this._client = mod.v2 ?? (mod as { default?: { v2?: CloudinaryClient } }).default?.v2;\n } catch {\n throw new Error(\n \"cloudinary is not available in this runtime. \" +\n \"Install the `cloudinary` package to use the Cloudinary storage provider.\",\n );\n }\n }\n this._client.config({\n cloud_name: this.config.cloudName,\n api_key: this.config.apiKey,\n api_secret: this.config.apiSecret,\n secure: this.config.secure !== false,\n });\n return this._client;\n }\n\n // File operations\n async upload(options: UploadOptions): Promise<UploadResult> {\n try {\n const cloudinary = await this.getClient();\n // Convert Buffer/Uint8Array to base64 for Cloudinary\n let fileData: string;\n if (typeof options.file === \"string\") {\n fileData = options.file;\n } else {\n fileData = `data:${options.contentType || \"application/octet-stream\"};base64,${Buffer.from(options.file).toString(\"base64\")}`;\n }\n\n // Determine resource type based on content type\n let resourceType: \"image\" | \"video\" | \"raw\" = \"raw\";\n if (options.contentType?.startsWith(\"image/\")) {\n resourceType = \"image\";\n } else if (options.contentType?.startsWith(\"video/\")) {\n resourceType = \"video\";\n }\n\n // Extract folder and filename from key for Cloudinary folder support\n const keyParts = options.key.split(\"/\");\n let folder = \"\";\n let publicId = keyParts[keyParts.length - 1];\n\n if (keyParts.length > 1) {\n folder = keyParts.slice(0, -1).join(\"/\");\n }\n\n // Only strip the extension for image/video assets. Cloudinary keeps the\n // extension as part of the public_id for `raw` resources, so stripping it\n // would break the stored-id / `getPublicUrl` round-trip for raw files.\n if (resourceType !== \"raw\") {\n const dotIndex = publicId.lastIndexOf(\".\");\n if (dotIndex > 0) {\n publicId = publicId.slice(0, dotIndex);\n }\n }\n\n const uploadOptions: Record<string, unknown> = {\n public_id: publicId,\n resource_type: resourceType,\n overwrite: true,\n invalidate: true,\n // Send user metadata under `context`/`metadata` instead of spreading it\n // at the top level. Spreading last let callers override security-\n // sensitive fields like `public_id`, `resource_type`, and `folder`.\n ...(options.metadata ? { context: options.metadata } : {}),\n };\n\n // Combine config folder + extracted folder without trailing slashes\n const folderParts: string[] = [];\n if (this.config.folder) folderParts.push(this.config.folder);\n if (folder) folderParts.push(folder);\n if (folderParts.length > 0) {\n uploadOptions.folder = folderParts.join(\"/\");\n }\n\n const result = await cloudinary.uploader.upload(fileData, uploadOptions);\n\n // Sample result structure\n // {\n // asset_id: 'b4033628c0897843e61e362167f338d6',\n // public_id: 'starter/workspaces/logo/ws_1KBQ2V0VRKK5EN936G8S2XZ30',\n // version: 1765965394,\n // version_id: 'abd49804c49061cef406f8d703466849',\n // signature: 'ff52169a483bada7ca4943ce322132137bec4d2f',\n // width: 256,\n // height: 256,\n // format: 'png',\n // resource_type: 'image',\n // created_at: '2025-12-17T09:52:12Z',\n // tags: [],\n // bytes: 76429,\n // type: 'upload',\n // etag: '85e34db0ea9ce8e771e25bd8eb33ba56',\n // placeholder: false,\n // url: 'http://res.cloudinary.com/droen5ejq/image/upload/v1765965394/starter/workspaces/logo/ws_1KBQ2V0VRKK5EN936G8S2XZ30.png',\n // secure_url: 'https://res.cloudinary.com/droen5ejq/image/upload/v1765965394/starter/workspaces/logo/ws_1KBQ2V0VRKK5EN936G8S2XZ30.png',\n // asset_folder: 'starter/workspaces/logo',\n // display_name: 'ws_1KBQ2V0VRKK5EN936G8S2XZ30',\n // overwritten: true,\n // api_key: '255239588173378'\n // }\n\n const publicUrl = this.getPublicUrl(result.public_id, result.format, resourceType);\n\n return {\n success: true,\n key: result.public_id,\n url: result.secure_url,\n publicUrl,\n etag: result.etag,\n size: result.bytes,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Upload failed\",\n };\n }\n }\n\n async download(options: DownloadOptions): Promise<DownloadResult> {\n try {\n // Cloudinary doesn't support direct download like S3, so we fetch from\n // the public URL. Split the key into a base + extension and pass the\n // extension through so `getPublicUrl` can infer the correct resource\n // type (image/video/raw). Passing the bare key would default to `raw`\n // and 404 for image/video assets.\n const dotIndex = options.key.lastIndexOf(\".\");\n const hasExt = dotIndex > 0 && dotIndex < options.key.length - 1;\n const baseKey = hasExt ? options.key.slice(0, dotIndex) : options.key;\n const extension = hasExt ? options.key.slice(dotIndex + 1) : undefined;\n const publicUrl = this.getPublicUrl(baseKey, extension);\n\n const response = await fetch(publicUrl);\n if (!response.ok) {\n return {\n success: false,\n error: `Failed to download: ${response.status} ${response.statusText}`,\n };\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const content = Buffer.from(arrayBuffer);\n\n return {\n success: true,\n content, // Main content field\n data: content, // Alias for backward compatibility\n contentType: response.headers.get(\"content-type\") || undefined,\n contentLength: content.length,\n lastModified: response.headers.get(\"last-modified\")\n ? new Date(response.headers.get(\"last-modified\")!)\n : new Date(),\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Download failed\",\n };\n }\n }\n\n async delete(options: DeleteOptions): Promise<DeleteResult> {\n try {\n const cloudinary = await this.getClient();\n // Strip extension to get public_id\n let publicId = options.key;\n const dotIndex = publicId.lastIndexOf(\".\");\n if (dotIndex > 0) {\n publicId = publicId.slice(0, dotIndex);\n }\n\n // Add folder if enabled in config\n if (this.config.folder) {\n publicId = `${this.config.folder}/${publicId}`;\n }\n\n // Determine resource type from key/path\n let resourceType: \"image\" | \"video\" | \"raw\" = \"raw\";\n\n // Try to get resource info first to determine type\n try {\n await cloudinary.api.resource(publicId, { resource_type: \"image\" });\n resourceType = \"image\";\n } catch {\n try {\n await cloudinary.api.resource(publicId, { resource_type: \"video\" });\n resourceType = \"video\";\n } catch {\n resourceType = \"raw\";\n }\n }\n\n await cloudinary.uploader.destroy(publicId, {\n resource_type: resourceType,\n invalidate: true,\n });\n\n return {\n success: true,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Delete failed\",\n };\n }\n }\n\n async batchDelete(options: BatchDeleteOptions): Promise<BatchDeleteResult> {\n try {\n const cloudinary = await this.getClient();\n // Convert keys to public_ids (strip extensions, add folder prefix)\n const publicIds = options.keys.map((key) => {\n let publicId = key;\n const dotIndex = publicId.lastIndexOf(\".\");\n if (dotIndex > 0) {\n publicId = publicId.slice(0, dotIndex);\n }\n if (this.config.folder) {\n publicId = `${this.config.folder}/${publicId}`;\n }\n return publicId;\n });\n\n const result = await cloudinary.api.delete_resources(publicIds);\n\n // delete_resources returns an object keyed by public_id with status info\n const deleted: string[] = [];\n const errors: { key: string; error: string }[] = [];\n\n for (const [publicId, info] of Object.entries(result)) {\n const originalKey = options.keys[publicIds.indexOf(publicId)] || publicId;\n const status = (info as Record<string, unknown>)?.status;\n if (status === \"deleted\" || status === \"not_found\") {\n deleted.push(originalKey);\n } else {\n errors.push({ key: originalKey, error: `Delete status: ${String(status)}` });\n }\n }\n\n return {\n success: errors.length === 0,\n deleted,\n errors,\n };\n } catch (error) {\n return {\n success: false,\n errors: options.keys.map((key) => ({\n key,\n error: error instanceof Error ? error.message : \"Batch delete failed\",\n })),\n };\n }\n }\n\n async list(options: ListOptions = {}): Promise<ListResult> {\n try {\n const cloudinary = await this.getClient();\n const prefix = options.prefix || \"\";\n const maxResults = options.maxKeys || 50;\n\n // Each Cloudinary resource_type (image/video/raw) maintains its OWN\n // opaque cursor. The previous implementation queried all three in\n // parallel and returned `images.next_cursor || videos.next_cursor ||\n // raw.next_cursor` — so a page-2 token belonging to (say) videos was\n // fed to the images call on the next request, producing wrong/empty\n // results. We now query ONE type per page call and encode the owning\n // type into the continuation token so the next call resumes the right\n // type. Types are visited in stable order (image → video → raw); when a\n // type's page is empty we auto-advance to the next type within the same\n // call so the caller never receives an empty page.\n const TYPES = [\"image\", \"video\", \"raw\"] as const;\n type RType = (typeof TYPES)[number];\n\n let type: RType = \"image\";\n let cursor: string | undefined;\n if (options.continuationToken) {\n const sep = options.continuationToken.indexOf(\":\");\n if (sep > 0) {\n const t = options.continuationToken.slice(0, sep);\n const c = options.continuationToken.slice(sep + 1);\n if ((TYPES as readonly string[]).includes(t)) {\n type = t as RType;\n cursor = c || undefined;\n }\n }\n }\n\n const fetchPage = (rt: RType, c?: string) =>\n cloudinary.api\n .resources({\n type: \"upload\",\n resource_type: rt,\n prefix,\n max_results: maxResults,\n ...(c ? { next_cursor: c } : {}),\n })\n .catch(() => ({ resources: [], next_cursor: undefined }));\n\n let page = await fetchPage(type, cursor);\n let resources: Record<string, unknown>[] = (page.resources || []) as Record<\n string,\n unknown\n >[];\n let nextCursor: string | undefined = page.next_cursor;\n\n // Auto-advance: if this type returned nothing and has no next page, move\n // to the next resource type (still within maxResults) so callers don't\n // see empty intermediate pages while iterating through all types.\n let typeIdx = TYPES.indexOf(type);\n while (!nextCursor && resources.length === 0 && typeIdx < TYPES.length - 1) {\n typeIdx++;\n type = TYPES[typeIdx];\n page = await fetchPage(type);\n resources = (page.resources || []) as Record<string, unknown>[];\n nextCursor = page.next_cursor;\n }\n\n const files = resources.map((resource) => ({\n key: String(resource.public_id || \"\"),\n size: Number(resource.bytes) || 0,\n lastModified: new Date(String(resource.created_at || new Date())),\n etag: String(resource.etag || \"\"),\n contentType: this.formatToMime(\n String(resource.resource_type || \"raw\"),\n resource.format ? String(resource.format) : undefined,\n ),\n }));\n\n const nextContinuationToken = nextCursor ? `${type}:${nextCursor}` : undefined;\n\n return {\n success: true,\n files,\n isTruncated: !!nextContinuationToken,\n nextContinuationToken,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"List failed\",\n };\n }\n }\n\n async exists(key: string): Promise<ExistsResult> {\n try {\n const cloudinary = await this.getClient();\n // Strip extension and add config folder prefix for the public_id\n let publicId = key;\n const dotIndex = publicId.lastIndexOf(\".\");\n if (dotIndex > 0) {\n publicId = publicId.slice(0, dotIndex);\n }\n if (this.config.folder) {\n publicId = `${this.config.folder}/${publicId}`;\n }\n\n // Try each resource type until found\n for (const resourceType of [\"image\", \"video\", \"raw\"] as const) {\n try {\n await cloudinary.api.resource(publicId, { resource_type: resourceType });\n return { exists: true };\n } catch {\n // Try next resource type\n }\n }\n\n return { exists: false };\n } catch (error) {\n return {\n exists: false,\n error: error instanceof Error ? error.message : \"Check existence failed\",\n };\n }\n }\n\n async copy(_options: CopyOptions): Promise<CopyResult> {\n return {\n success: false,\n error: \"Cloudinary copy not implemented yet\",\n };\n }\n\n async move(_options: MoveOptions): Promise<MoveResult> {\n return {\n success: false,\n error: \"Cloudinary move not implemented yet\",\n };\n }\n\n async duplicate(options: DuplicateOptions): Promise<DuplicateResult> {\n // Duplicate is just an alias for copy\n return this.copy(options);\n }\n\n async getPresignedUrl(_options: PresignedUrlOptions): Promise<PresignedUrlResult> {\n return {\n success: false,\n error: \"Cloudinary presigned URL not implemented yet\",\n };\n }\n\n getPublicUrl(key: string, extension?: string, resourceType?: \"image\" | \"video\" | \"raw\"): string {\n // Generate Cloudinary URL\n const protocol = this.config.secure !== false ? \"https\" : \"http\";\n const baseUrl = `${protocol}://res.cloudinary.com/${this.config.cloudName}`;\n\n // `upload()` prepends `config.folder` to the stored public_id, so callers\n // holding a relative key need the folder re-prepended here to round-trip\n // correctly. If the key already starts with the folder (full public_id),\n // don't double-prefix.\n let publicId = key;\n if (this.config.folder && !publicId.startsWith(`${this.config.folder}/`)) {\n publicId = `${this.config.folder}/${publicId}`;\n }\n\n // Use explicit resource type if provided, otherwise infer from extension\n const type = resourceType || this.inferResourceType(extension || key);\n\n return `${baseUrl}/${type}/upload/${publicId}${extension ? `.${extension}` : \"\"}`;\n }\n\n /**\n * Build a valid MIME type from a Cloudinary `resource_type` + `format`.\n * Naive `${resource_type}/${format}` produces invalid types like `raw/pdf`\n * or `image/jpg`, so map known formats to their real MIME types and fall back\n * to `application/octet-stream` for raw/unknown assets.\n */\n private formatToMime(resourceType: string, format?: string): string {\n if (!format) return \"application/octet-stream\";\n const fmt = format.toLowerCase();\n const overrides: Record<string, string> = {\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n svg: \"image/svg+xml\",\n pdf: \"application/pdf\",\n mov: \"video/quicktime\",\n mp3: \"audio/mpeg\",\n };\n if (overrides[fmt]) return overrides[fmt];\n if (resourceType === \"image\" || resourceType === \"video\") {\n return `${resourceType}/${fmt}`;\n }\n return \"application/octet-stream\";\n }\n\n /** Infer Cloudinary resource type from a filename or extension */\n private inferResourceType(nameOrExt: string): \"image\" | \"video\" | \"raw\" {\n const ext = nameOrExt.includes(\".\")\n ? nameOrExt.split(\".\").pop()?.toLowerCase() || \"\"\n : nameOrExt.toLowerCase();\n const imageExts = [\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"svg\", \"bmp\", \"ico\", \"avif\", \"tiff\"];\n const videoExts = [\"mp4\", \"webm\", \"mov\", \"avi\", \"mkv\", \"flv\", \"wmv\", \"m4v\", \"ogv\"];\n if (imageExts.includes(ext)) return \"image\";\n if (videoExts.includes(ext)) return \"video\";\n return \"raw\";\n }\n\n // Folder operations\n async createFolder(options: CreateFolderOptions): Promise<CreateFolderResult> {\n // Cloudinary folders are created implicitly when uploading files\n return {\n success: true,\n path: options.path,\n };\n }\n\n async deleteFolder(options: DeleteFolderOptions): Promise<DeleteFolderResult> {\n try {\n const cloudinary = await this.getClient();\n // Cloudinary requires deleting all assets in folder first, then the\n // folder itself. Prepend `config.folder` so this matches the prefix that\n // `upload`/`delete`/`exists` use — otherwise a recursive delete targets\n // the wrong (unprefixed) prefix and silently deletes nothing (or the\n // wrong assets). Preserve the trailing slash for the prefix delete so we\n // only match `folder/*` and never a sibling like `folderXYZ/*`. Stripping\n // the slash made `deleteFolder(\"folder/\")` also delete `folderXYZ/...` —\n // a data-loss hazard.\n const normalizedPath = options.path.replace(/\\/$/, \"\");\n const scopedPath = this.config.folder\n ? `${this.config.folder}/${normalizedPath}`\n : normalizedPath;\n const folderPath = `${scopedPath}/`;\n const folderApiPath = scopedPath;\n\n if (options.recursive) {\n // Delete all resources in the folder\n await cloudinary.api.delete_resources_by_prefix(folderPath);\n }\n\n // Delete the folder\n await cloudinary.api.delete_folder(folderApiPath);\n\n return {\n success: true,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Delete folder failed\",\n };\n }\n }\n\n async listFolders(_options?: ListFoldersOptions): Promise<ListFoldersResult> {\n try {\n const cloudinary = await this.getClient();\n // Use Cloudinary Admin API to list folders\n const result = await cloudinary.api.root_folders();\n\n const folders = (result.folders || []).map((folder: Record<string, unknown>) => ({\n name: String(folder.name ?? \"\"),\n path: String(folder.path ?? \"\"),\n }));\n\n return {\n success: true,\n folders,\n isTruncated: false,\n nextContinuationToken: undefined,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"List folders failed\",\n };\n }\n }\n\n async folderExists(_path: string): Promise<FolderExistsResult> {\n // Previously this returned `{ exists: false, error: \"...\" }`, which looks\n // like a definitive \"the folder does not exist\" to a caller doing\n // `if (!result.exists)`. Return an `error`-only result (no `exists` claim)\n // so the unimplemented state is distinguishable from a real \"not found\".\n return {\n exists: false,\n error:\n \"Cloudinary folder existence is not implemented yet — this result does NOT mean the folder is absent.\",\n };\n }\n\n async renameFolder(_options: RenameFolderOptions): Promise<RenameFolderResult> {\n return {\n success: false,\n error: \"Cloudinary rename folder not implemented yet\",\n };\n }\n\n async copyFolder(_options: CopyFolderOptions): Promise<CopyFolderResult> {\n return {\n success: false,\n error: \"Cloudinary copy folder not implemented yet\",\n };\n }\n}\n","/**\n * Cloudinary storage service implementation\n * High-level service wrapper for Cloudinary storage operations with convenience methods\n */\n\nimport { loadCloudinaryConfig } from \"../config\";\nimport { CloudinaryProvider } from \"../providers/cloudinary\";\nimport type {\n BatchDeleteOptions,\n BatchDeleteResult,\n CloudinaryConfig,\n CopyFolderOptions,\n CopyFolderResult,\n CopyOptions,\n CopyResult,\n CreateFolderOptions,\n CreateFolderResult,\n DeleteFolderOptions,\n DeleteFolderResult,\n DeleteOptions,\n DeleteResult,\n DownloadOptions,\n DownloadResult,\n DuplicateOptions,\n DuplicateResult,\n ExistsResult,\n FolderExistsResult,\n ListFoldersOptions,\n ListFoldersResult,\n ListOptions,\n ListResult,\n MoveOptions,\n MoveResult,\n PresignedUrlOptions,\n PresignedUrlResult,\n RenameFolderOptions,\n RenameFolderResult,\n StorageInterface,\n UploadOptions,\n UploadResult,\n} from \"../types\";\n\n/**\n * Cloudinary storage service\n * High-level service for Cloudinary storage operations with convenience methods\n * @public\n */\nexport class CloudinaryService implements StorageInterface {\n private provider: CloudinaryProvider;\n private config: CloudinaryConfig;\n\n constructor();\n constructor(config: CloudinaryConfig);\n constructor(config?: CloudinaryConfig) {\n this.config = config || loadCloudinaryConfig();\n this.provider = new CloudinaryProvider(this.config);\n }\n\n // Utility methods\n getConfig(): CloudinaryConfig {\n return { ...this.config };\n }\n\n getProvider(): string {\n return this.config.provider;\n }\n\n getPublicUrl(key: string): string {\n return this.provider.getPublicUrl(key);\n }\n\n // File operations\n async upload(options: UploadOptions): Promise<UploadResult> {\n return this.provider.upload(options);\n }\n\n async download(options: DownloadOptions): Promise<DownloadResult> {\n return this.provider.download(options);\n }\n\n async delete(options: DeleteOptions): Promise<DeleteResult> {\n return this.provider.delete(options);\n }\n\n async batchDelete(options: BatchDeleteOptions): Promise<BatchDeleteResult> {\n return this.provider.batchDelete(options);\n }\n\n async list(options?: ListOptions): Promise<ListResult> {\n return this.provider.list(options);\n }\n\n async exists(key: string): Promise<ExistsResult> {\n return this.provider.exists(key);\n }\n\n async copy(options: CopyOptions): Promise<CopyResult> {\n return this.provider.copy(options);\n }\n\n async move(options: MoveOptions): Promise<MoveResult> {\n return this.provider.move(options);\n }\n\n async duplicate(options: DuplicateOptions): Promise<DuplicateResult> {\n return this.provider.duplicate(options);\n }\n\n async getPresignedUrl(options: PresignedUrlOptions): Promise<PresignedUrlResult> {\n return this.provider.getPresignedUrl(options);\n }\n\n // Folder operations\n async createFolder(options: CreateFolderOptions): Promise<CreateFolderResult> {\n return this.provider.createFolder(options);\n }\n\n async deleteFolder(options: DeleteFolderOptions): Promise<DeleteFolderResult> {\n return this.provider.deleteFolder(options);\n }\n\n async listFolders(options?: ListFoldersOptions): Promise<ListFoldersResult> {\n return this.provider.listFolders(options);\n }\n\n async folderExists(path: string): Promise<FolderExistsResult> {\n return this.provider.folderExists(path);\n }\n\n async renameFolder(options: RenameFolderOptions): Promise<RenameFolderResult> {\n return this.provider.renameFolder(options);\n }\n\n async copyFolder(options: CopyFolderOptions): Promise<CopyFolderResult> {\n return this.provider.copyFolder(options);\n }\n\n // Convenience methods\n async uploadFile(\n key: string,\n file: Buffer | Uint8Array | string,\n options?: Partial<UploadOptions>,\n ): Promise<UploadResult> {\n return this.upload({ key, file, ...options });\n }\n\n async downloadFile(key: string): Promise<DownloadResult> {\n return this.download({ key });\n }\n\n async deleteFile(key: string): Promise<DeleteResult> {\n return this.delete({ key });\n }\n\n async deleteFiles(keys: string[]): Promise<BatchDeleteResult> {\n return this.batchDelete({ keys });\n }\n\n async listFiles(prefix?: string, maxKeys?: number): Promise<ListResult> {\n return this.list({ prefix, maxKeys });\n }\n\n async fileExists(key: string): Promise<boolean> {\n const result = await this.exists(key);\n return result.exists;\n }\n\n async copyFile(\n sourceKey: string,\n destinationKey: string,\n options?: Partial<CopyOptions>,\n ): Promise<CopyResult> {\n return this.copy({ sourceKey, destinationKey, ...options });\n }\n\n async moveFile(\n sourceKey: string,\n destinationKey: string,\n options?: Partial<MoveOptions>,\n ): Promise<MoveResult> {\n return this.move({ sourceKey, destinationKey, ...options });\n }\n\n async duplicateFile(\n sourceKey: string,\n destinationKey: string,\n options?: Partial<DuplicateOptions>,\n ): Promise<DuplicateResult> {\n return this.duplicate({ sourceKey, destinationKey, ...options });\n }\n\n async renameFile(\n sourceKey: string,\n destinationKey: string,\n options?: Partial<MoveOptions>,\n ): Promise<MoveResult> {\n return this.moveFile(sourceKey, destinationKey, options);\n }\n\n async getDownloadUrl(key: string, expiresIn?: number): Promise<PresignedUrlResult> {\n return this.getPresignedUrl({ key, operation: \"get\", expiresIn });\n }\n\n async getUploadUrl(\n key: string,\n contentType?: string,\n expiresIn?: number,\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl({\n key,\n operation: \"put\",\n contentType,\n expiresIn,\n });\n }\n\n // Folder convenience methods\n async createFolderPath(path: string): Promise<CreateFolderResult> {\n return this.createFolder({ path });\n }\n\n async deleteFolderPath(path: string, recursive = false): Promise<DeleteFolderResult> {\n return this.deleteFolder({ path, recursive });\n }\n\n async folderPathExists(path: string): Promise<boolean> {\n const result = await this.folderExists(path);\n return result.exists;\n }\n\n async renameFolderPath(oldPath: string, newPath: string): Promise<RenameFolderResult> {\n return this.renameFolder({ oldPath, newPath });\n }\n\n async copyFolderPath(\n sourcePath: string,\n destinationPath: string,\n recursive = true,\n ): Promise<CopyFolderResult> {\n return this.copyFolder({ sourcePath, destinationPath, recursive });\n }\n}\n","import type { EnvRecord } from \"./config\";\nimport { loadCloudinaryConfig } from \"./config\";\nimport { CloudinaryService } from \"./services/cloudinary\";\nimport type { CloudinaryConfig } from \"./types\";\n\n// === SERVICE CLASSES ===\nexport { CloudinaryService } from \"./services/cloudinary\";\n\n// === MAIN FACTORY FUNCTIONS (Primary API) ===\n/**\n * Create Cloudinary service using environment variables or manual configuration\n * @param config Optional Cloudinary configuration. If not provided, loads from environment variables\n * @param env - Optional environment record\n * @returns CloudinaryService instance\n * @throws Error if configuration is invalid or environment variables are missing\n * @public\n *\n * @example\n * ```typescript\n * import { createCloudinary } from '@kumix/storage/cloudinary';\n *\n * // Using environment variables\n * // Set: KUMIX_CLOUDINARY_CLOUD_NAME=my-cloud, KUMIX_CLOUDINARY_API_KEY=123..., etc.\n * const cloudinaryFromEnv = createCloudinary();\n * await cloudinaryFromEnv.uploadFile('photo.jpg', imageBuffer);\n *\n * // Using manual configuration\n * const cloudinary = createCloudinary({\n * provider: 'cloudinary',\n * cloudName: 'my-cloud-name',\n * apiKey: '123456789012345',\n * apiSecret: 'your-api-secret',\n * secure: true,\n * folder: 'uploads'\n * });\n *\n * // Upload with transformations\n * await cloudinary.uploadFile('profile-pic.jpg', imageBuffer);\n *\n * // Get optimized URL\n * const publicUrl = cloudinary.getPublicUrl('profile-pic.jpg');\n * console.log(publicUrl); // Cloudinary optimized URL\n * ```\n */\nexport function createCloudinary(): CloudinaryService;\nexport function createCloudinary(config: CloudinaryConfig): CloudinaryService;\nexport function createCloudinary(config: CloudinaryConfig, env: EnvRecord): CloudinaryService;\nexport function createCloudinary(config: undefined, env: EnvRecord): CloudinaryService;\nexport function createCloudinary(config?: CloudinaryConfig, env?: EnvRecord): CloudinaryService {\n if (config) {\n return new CloudinaryService(config);\n }\n const envConfig = loadCloudinaryConfig(env);\n return new CloudinaryService(envConfig);\n}\n"],"mappings":"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"}
@@ -24,6 +24,7 @@ const ENV_VARS = {
24
24
  KUMIX_S3_BUCKET: "KUMIX_S3_BUCKET",
25
25
  KUMIX_S3_ACCESS_KEY_ID: "KUMIX_S3_ACCESS_KEY_ID",
26
26
  KUMIX_S3_SECRET_ACCESS_KEY: "KUMIX_S3_SECRET_ACCESS_KEY",
27
+ KUMIX_S3_SESSION_TOKEN: "KUMIX_S3_SESSION_TOKEN",
27
28
  KUMIX_S3_ENDPOINT: "KUMIX_S3_ENDPOINT",
28
29
  KUMIX_S3_FORCE_PATH_STYLE: "KUMIX_S3_FORCE_PATH_STYLE",
29
30
  KUMIX_S3_PUBLIC_URL: "KUMIX_S3_PUBLIC_URL",
@@ -101,7 +102,7 @@ function getBooleanEnvVar(key, defaultValue = false, env) {
101
102
  * ```
102
103
  */
103
104
  function loadS3Config(env) {
104
- const provider = getRequiredEnvVar(ENV_VARS.KUMIX_S3_PROVIDER, env);
105
+ const provider = getEnvVar(ENV_VARS.KUMIX_S3_PROVIDER, env) || "aws";
105
106
  const validProviders = [
106
107
  "aws",
107
108
  "cloudflare-r2",
@@ -117,6 +118,7 @@ function loadS3Config(env) {
117
118
  bucket: getRequiredEnvVar(ENV_VARS.KUMIX_S3_BUCKET, env),
118
119
  accessKeyId: getRequiredEnvVar(ENV_VARS.KUMIX_S3_ACCESS_KEY_ID, env),
119
120
  secretAccessKey: getRequiredEnvVar(ENV_VARS.KUMIX_S3_SECRET_ACCESS_KEY, env),
121
+ sessionToken: getEnvVar(ENV_VARS.KUMIX_S3_SESSION_TOKEN, env),
120
122
  endpoint: getEnvVar(ENV_VARS.KUMIX_S3_ENDPOINT, env),
121
123
  forcePathStyle: getBooleanEnvVar(ENV_VARS.KUMIX_S3_FORCE_PATH_STYLE, false, env),
122
124
  publicUrl: getEnvVar(ENV_VARS.KUMIX_S3_PUBLIC_URL, env)
@@ -204,11 +206,7 @@ function loadStorageConfig(env) {
204
206
  const hasCloudinaryVars = getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_CLOUD_NAME, env) && getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_API_KEY, env) && getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_API_SECRET, env);
205
207
  const hasS3Vars = getEnvVar(ENV_VARS.KUMIX_S3_BUCKET, env) && getEnvVar(ENV_VARS.KUMIX_S3_ACCESS_KEY_ID, env) && getEnvVar(ENV_VARS.KUMIX_S3_SECRET_ACCESS_KEY, env);
206
208
  if (hasCloudinaryVars) return loadCloudinaryConfig(env);
207
- if (hasS3Vars) {
208
- const config = loadS3Config(env);
209
- if (!config.provider) config.provider = "aws";
210
- return config;
211
- }
209
+ if (hasS3Vars) return loadS3Config(env);
212
210
  throw new Error("No storage configuration found. Please set either:\n- S3: KUMIX_S3_PROVIDER, KUMIX_S3_REGION, KUMIX_S3_BUCKET, KUMIX_S3_ACCESS_KEY_ID, KUMIX_S3_SECRET_ACCESS_KEY\n- Cloudinary: KUMIX_CLOUDINARY_CLOUD_NAME, KUMIX_CLOUDINARY_API_KEY, KUMIX_CLOUDINARY_API_SECRET");
213
211
  }
214
212
  /**
@@ -333,7 +331,6 @@ function getStorageProvider(env) {
333
331
  */
334
332
  function validateS3EnvVars(env) {
335
333
  const missing = [
336
- ENV_VARS.KUMIX_S3_PROVIDER,
337
334
  ENV_VARS.KUMIX_S3_REGION,
338
335
  ENV_VARS.KUMIX_S3_BUCKET,
339
336
  ENV_VARS.KUMIX_S3_ACCESS_KEY_ID,
@@ -397,9 +394,20 @@ function validateCloudinaryEnvVars(env) {
397
394
  */
398
395
  function getStorageEnvVars(env) {
399
396
  const result = {};
397
+ const SENSITIVE_PATTERNS = [
398
+ /SECRET/i,
399
+ /API_SECRET/i,
400
+ /API_KEY/i,
401
+ /ACCESS_KEY_ID/i,
402
+ /SECRET_ACCESS_KEY/i,
403
+ /_KEY_ID$/i,
404
+ /TOKEN/i,
405
+ /PASSWORD/i
406
+ ];
407
+ const isSensitive = (key) => SENSITIVE_PATTERNS.some((p) => p.test(key));
400
408
  Object.values(ENV_VARS).forEach((key) => {
401
409
  const value = getEnvVar(key, env);
402
- if (value) if (key.includes("SECRET") || key.includes("API_SECRET")) result[key] = `${value.substring(0, 4)}***`;
410
+ if (value) if (isSensitive(key)) result[key] = "****";
403
411
  else result[key] = value;
404
412
  });
405
413
  return result;
@@ -407,4 +415,4 @@ function getStorageEnvVars(env) {
407
415
  //#endregion
408
416
  export { isStorageConfigured as a, loadStorageConfig as c, hasStorageConfig as i, validateCloudinaryEnvVars as l, getStorageEnvVars as n, loadCloudinaryConfig as o, getStorageProvider as r, loadS3Config as s, ENV_VARS as t, validateS3EnvVars as u };
409
417
 
410
- //# sourceMappingURL=config-t-NVJICl.js.map
418
+ //# sourceMappingURL=config-By5ib98s.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-By5ib98s.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * Storage configuration from environment variables\n * Provides automatic configuration loading from environment variables\n */\n\nimport type { CloudinaryConfig, S3Config, StorageConfig } from \"./types\";\n\n/**\n * Cross-runtime environment record.\n * Pass `process.env` on Node.js, `ctx.env` on Cloudflare Workers,\n * `Deno.env.toObject()` on Deno, or a plain object in the browser.\n */\nexport type EnvRecord = Record<string, string | undefined>;\n\nfunction getDefaultEnv(): EnvRecord | undefined {\n if (typeof process !== \"undefined\" && process?.env) {\n return process.env as EnvRecord;\n }\n return undefined;\n}\n\nfunction resolveEnv(env?: EnvRecord): EnvRecord | undefined {\n return env ?? getDefaultEnv();\n}\n\n/**\n * Environment variable names for storage configuration\n * @public\n *\n * @example\n * ```typescript\n * import { ENV_VARS } from '@kumix/storage';\n *\n * // Check if specific env vars are set\n * console.log(process.env[ENV_VARS.KUMIX_S3_BUCKET]);\n * console.log(process.env[ENV_VARS.KUMIX_CLOUDINARY_CLOUD_NAME]);\n * ```\n */\nexport const ENV_VARS = {\n // S3 Configuration\n KUMIX_S3_PROVIDER: \"KUMIX_S3_PROVIDER\",\n KUMIX_S3_REGION: \"KUMIX_S3_REGION\",\n KUMIX_S3_BUCKET: \"KUMIX_S3_BUCKET\",\n KUMIX_S3_ACCESS_KEY_ID: \"KUMIX_S3_ACCESS_KEY_ID\",\n KUMIX_S3_SECRET_ACCESS_KEY: \"KUMIX_S3_SECRET_ACCESS_KEY\",\n KUMIX_S3_SESSION_TOKEN: \"KUMIX_S3_SESSION_TOKEN\",\n KUMIX_S3_ENDPOINT: \"KUMIX_S3_ENDPOINT\",\n KUMIX_S3_FORCE_PATH_STYLE: \"KUMIX_S3_FORCE_PATH_STYLE\",\n KUMIX_S3_PUBLIC_URL: \"KUMIX_S3_PUBLIC_URL\",\n\n // Cloudinary Configuration\n KUMIX_CLOUDINARY_CLOUD_NAME: \"KUMIX_CLOUDINARY_CLOUD_NAME\",\n KUMIX_CLOUDINARY_API_KEY: \"KUMIX_CLOUDINARY_API_KEY\",\n KUMIX_CLOUDINARY_API_SECRET: \"KUMIX_CLOUDINARY_API_SECRET\",\n KUMIX_CLOUDINARY_SECURE: \"KUMIX_CLOUDINARY_SECURE\",\n KUMIX_CLOUDINARY_FOLDER: \"KUMIX_CLOUDINARY_FOLDER\",\n} as const;\n\n/**\n * Get environment variable value\n * @param key - Environment variable name\n * @param env - Optional environment record\n * @internal\n */\nfunction getEnvVar(key: string, env?: EnvRecord): string | undefined {\n const e = resolveEnv(env);\n if (!e) return undefined;\n return e[key];\n}\n\n/**\n * Get required environment variable value\n * @param key - Environment variable name\n * @param env - Optional environment record\n * @internal\n */\nfunction getRequiredEnvVar(key: string, env?: EnvRecord): string {\n const value = getEnvVar(key, env);\n if (!value) {\n throw new Error(`Missing required environment variable: ${key}`);\n }\n return value;\n}\n\n/**\n * Get boolean environment variable value\n * @param key - Environment variable name\n * @param defaultValue - Default boolean value if not set\n * @param env - Optional environment record\n * @internal\n */\nfunction getBooleanEnvVar(key: string, defaultValue = false, env?: EnvRecord): boolean {\n const value = getEnvVar(key, env);\n if (!value) return defaultValue;\n return value.toLowerCase() === \"true\" || value === \"1\";\n}\n\n/**\n * Load S3 configuration from environment variables\n * @param env - Optional environment record\n * @returns S3 configuration object\n * @throws Error if required environment variables are missing\n * @public\n *\n * @example\n * ```typescript\n * import { loadS3Config } from '@kumix/storage';\n *\n * // Set environment variables first:\n * // KUMIX_S3_PROVIDER=aws\n * // KUMIX_S3_REGION=us-east-1\n * // KUMIX_S3_BUCKET=my-bucket\n * // KUMIX_S3_ACCESS_KEY_ID=AKIA...\n * // KUMIX_S3_SECRET_ACCESS_KEY=secret...\n *\n * try {\n * const config = loadS3Config();\n * console.log(config);\n * // Returns: {\n * // provider: 'aws',\n * // region: 'us-east-1',\n * // bucket: 'my-bucket',\n * // accessKeyId: 'AKIA...',\n * // secretAccessKey: 'secret...'\n * // }\n * } catch (error) {\n * console.error('Missing S3 configuration:', error.message);\n * }\n * ```\n */\nexport function loadS3Config(env?: EnvRecord): S3Config {\n const provider = (getEnvVar(ENV_VARS.KUMIX_S3_PROVIDER, env) || \"aws\") as S3Config[\"provider\"];\n\n // Validate provider\n const validProviders: S3Config[\"provider\"][] = [\n \"aws\",\n \"cloudflare-r2\",\n \"minio\",\n \"digitalocean\",\n \"supabase\",\n \"custom\",\n ];\n if (!validProviders.includes(provider)) {\n throw new Error(\n `Invalid S3 provider: ${provider}. Must be one of: ${validProviders.join(\", \")}`,\n );\n }\n\n return {\n provider,\n region: getRequiredEnvVar(ENV_VARS.KUMIX_S3_REGION, env),\n bucket: getRequiredEnvVar(ENV_VARS.KUMIX_S3_BUCKET, env),\n accessKeyId: getRequiredEnvVar(ENV_VARS.KUMIX_S3_ACCESS_KEY_ID, env),\n secretAccessKey: getRequiredEnvVar(ENV_VARS.KUMIX_S3_SECRET_ACCESS_KEY, env),\n sessionToken: getEnvVar(ENV_VARS.KUMIX_S3_SESSION_TOKEN, env),\n endpoint: getEnvVar(ENV_VARS.KUMIX_S3_ENDPOINT, env),\n forcePathStyle: getBooleanEnvVar(ENV_VARS.KUMIX_S3_FORCE_PATH_STYLE, false, env),\n publicUrl: getEnvVar(ENV_VARS.KUMIX_S3_PUBLIC_URL, env),\n };\n}\n\n/**\n * Load Cloudinary configuration from environment variables\n * @param env - Optional environment record\n * @returns Cloudinary configuration object\n * @throws Error if required environment variables are missing\n * @public\n *\n * @example\n * ```typescript\n * import { loadCloudinaryConfig } from '@kumix/storage';\n *\n * // Set environment variables first:\n * // KUMIX_CLOUDINARY_CLOUD_NAME=my-cloud\n * // KUMIX_CLOUDINARY_API_KEY=123456789\n * // KUMIX_CLOUDINARY_API_SECRET=secret...\n * // KUMIX_CLOUDINARY_SECURE=true (optional)\n * // KUMIX_CLOUDINARY_FOLDER=uploads (optional)\n *\n * try {\n * const config = loadCloudinaryConfig();\n * console.log(config);\n * // Returns: {\n * // provider: 'cloudinary',\n * // cloudName: 'my-cloud',\n * // apiKey: '123456789',\n * // apiSecret: 'secret...',\n * // secure: true,\n * // folder: 'uploads'\n * // }\n * } catch (error) {\n * console.error('Missing Cloudinary configuration:', error.message);\n * }\n * ```\n */\nexport function loadCloudinaryConfig(env?: EnvRecord): CloudinaryConfig {\n return {\n provider: \"cloudinary\",\n cloudName: getRequiredEnvVar(ENV_VARS.KUMIX_CLOUDINARY_CLOUD_NAME, env),\n apiKey: getRequiredEnvVar(ENV_VARS.KUMIX_CLOUDINARY_API_KEY, env),\n apiSecret: getRequiredEnvVar(ENV_VARS.KUMIX_CLOUDINARY_API_SECRET, env),\n secure: getBooleanEnvVar(ENV_VARS.KUMIX_CLOUDINARY_SECURE, true, env),\n folder: getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_FOLDER, env),\n };\n}\n\n/**\n * Auto-detect and load storage configuration from environment variables\n * Determines the provider based on available environment variables\n * @param env - Optional environment record\n * @returns Storage configuration object (S3 or Cloudinary)\n * @throws Error if no valid configuration is found\n * @public\n *\n * @example\n * ```typescript\n * import { loadStorageConfig } from '@kumix/storage';\n *\n * // Auto-detection based on available env vars\n * try {\n * const config = loadStorageConfig();\n *\n * if (config.provider === 'cloudinary') {\n * console.log('Using Cloudinary:', config.cloudName);\n * } else {\n * console.log('Using S3:', config.provider, config.bucket);\n * }\n * } catch (error) {\n * console.error('No storage configuration found:', error.message);\n * }\n *\n * // Explicit provider (set KUMIX_S3_PROVIDER=cloudinary)\n * // Will load Cloudinary config even if S3 vars are also present\n * ```\n */\nexport function loadStorageConfig(env?: EnvRecord): StorageConfig {\n const provider = getEnvVar(ENV_VARS.KUMIX_S3_PROVIDER, env);\n\n // If provider is explicitly set, use it\n if (provider) {\n if (provider === \"cloudinary\") {\n return loadCloudinaryConfig(env);\n }\n return loadS3Config(env);\n }\n\n // Auto-detect based on available environment variables\n const hasCloudinaryVars =\n getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_CLOUD_NAME, env) &&\n getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_API_KEY, env) &&\n getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_API_SECRET, env);\n\n const hasS3Vars =\n getEnvVar(ENV_VARS.KUMIX_S3_BUCKET, env) &&\n getEnvVar(ENV_VARS.KUMIX_S3_ACCESS_KEY_ID, env) &&\n getEnvVar(ENV_VARS.KUMIX_S3_SECRET_ACCESS_KEY, env);\n\n if (hasCloudinaryVars) {\n return loadCloudinaryConfig(env);\n }\n if (hasS3Vars) {\n // Default to AWS if no provider specified\n return loadS3Config(env);\n }\n\n throw new Error(\n \"No storage configuration found. Please set either:\\n\" +\n \"- S3: KUMIX_S3_PROVIDER, KUMIX_S3_REGION, KUMIX_S3_BUCKET, KUMIX_S3_ACCESS_KEY_ID, KUMIX_S3_SECRET_ACCESS_KEY\\n\" +\n \"- Cloudinary: KUMIX_CLOUDINARY_CLOUD_NAME, KUMIX_CLOUDINARY_API_KEY, KUMIX_CLOUDINARY_API_SECRET\",\n );\n}\n\n/**\n * Check if storage configuration is available in environment variables\n * @param env - Optional environment record\n * @returns True if valid storage configuration is found\n * @public\n *\n * @example\n * ```typescript\n * import { hasStorageConfig, loadStorageConfig } from '@kumix/storage';\n *\n * if (hasStorageConfig()) {\n * const config = loadStorageConfig();\n * console.log('Storage configured:', config.provider);\n * } else {\n * console.log('Please set storage environment variables');\n * }\n *\n * // Use in conditional initialization\n * const storage = hasStorageConfig() ? createStorage() : null;\n * ```\n */\nexport function hasStorageConfig(env?: EnvRecord): boolean {\n try {\n loadStorageConfig(env);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Check if storage configuration is available in environment variables\n * @param env - Optional environment record\n * @returns True if either S3 or Cloudinary configuration is available\n * @public\n *\n * @example\n * ```typescript\n * import { isStorageConfigured, createS3, createCloudinary } from '@kumix/storage';\n *\n * if (isStorageConfigured()) {\n * // Choose your preferred provider\n * const s3 = createS3();\n * // OR\n * const cloudinary = createCloudinary();\n *\n * await s3.uploadFile('test.txt', 'Hello World');\n * } else {\n * console.log('Storage not configured. Please set environment variables.');\n * }\n *\n * // Use in conditional initialization\n * const storage = isStorageConfigured() ? createS3() : null;\n * if (storage) {\n * await storage.uploadFile('file.txt', content);\n * }\n * ```\n */\nexport function isStorageConfigured(env?: EnvRecord): boolean {\n return hasStorageConfig(env);\n}\n\n/**\n * Get storage provider name from environment variables without loading full config\n * @param env - Optional environment record\n * @returns Provider name or undefined if not configured\n * @public\n *\n * @example\n * ```typescript\n * import { getStorageProvider } from '@kumix/storage';\n *\n * const provider = getStorageProvider();\n *\n * switch (provider) {\n * case 'aws':\n * console.log('Using AWS S3');\n * break;\n * case 'cloudinary':\n * console.log('Using Cloudinary');\n * break;\n * case 'cloudflare-r2':\n * console.log('Using Cloudflare R2');\n * break;\n * default:\n * console.log('No storage provider configured');\n * }\n *\n * // Quick check without throwing errors\n * if (getStorageProvider()) {\n * // Storage is configured\n * }\n * ```\n */\nexport function getStorageProvider(env?: EnvRecord): string | undefined {\n const provider = getEnvVar(ENV_VARS.KUMIX_S3_PROVIDER, env);\n if (provider) return provider;\n\n // Auto-detect\n const hasCloudinaryVars = getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_CLOUD_NAME, env);\n const hasS3Vars = getEnvVar(ENV_VARS.KUMIX_S3_BUCKET, env);\n\n if (hasCloudinaryVars) return \"cloudinary\";\n if (hasS3Vars) return \"aws\"; // Default S3 provider\n\n return undefined;\n}\n\n/**\n * Validate if all required environment variables are set for S3\n * @param env - Optional environment record\n * @returns Validation result with missing variables\n * @public\n *\n * @example\n * ```typescript\n * import { validateS3EnvVars } from '@kumix/storage';\n *\n * const validation = validateS3EnvVars();\n *\n * if (validation.valid) {\n * console.log('S3 configuration is complete');\n * } else {\n * console.log('Missing S3 variables:', validation.missing);\n * // Returns: [\"KUMIX_S3_REGION\", \"KUMIX_S3_BUCKET\"]\n * }\n * ```\n */\nexport function validateS3EnvVars(env?: EnvRecord): {\n valid: boolean;\n missing: string[];\n} {\n const required = [\n ENV_VARS.KUMIX_S3_REGION,\n ENV_VARS.KUMIX_S3_BUCKET,\n ENV_VARS.KUMIX_S3_ACCESS_KEY_ID,\n ENV_VARS.KUMIX_S3_SECRET_ACCESS_KEY,\n ];\n\n const missing = required.filter((key) => !getEnvVar(key, env));\n\n return {\n valid: missing.length === 0,\n missing,\n };\n}\n\n/**\n * Validate if all required environment variables are set for Cloudinary\n * @param env - Optional environment record\n * @returns Validation result with missing variables\n * @public\n *\n * @example\n * ```typescript\n * import { validateCloudinaryEnvVars } from '@kumix/storage';\n *\n * const validation = validateCloudinaryEnvVars();\n *\n * if (validation.valid) {\n * console.log('Cloudinary configuration is complete');\n * } else {\n * console.log('Missing Cloudinary variables:', validation.missing);\n * // Returns: [\"KUMIX_CLOUDINARY_API_KEY\"]\n * }\n * ```\n */\nexport function validateCloudinaryEnvVars(env?: EnvRecord): {\n valid: boolean;\n missing: string[];\n} {\n const required = [\n ENV_VARS.KUMIX_CLOUDINARY_CLOUD_NAME,\n ENV_VARS.KUMIX_CLOUDINARY_API_KEY,\n ENV_VARS.KUMIX_CLOUDINARY_API_SECRET,\n ];\n\n const missing = required.filter((key) => !getEnvVar(key, env));\n\n return {\n valid: missing.length === 0,\n missing,\n };\n}\n\n/**\n * Get all available environment variables for debugging\n * @param env - Optional environment record\n * @returns Object containing all storage-related env vars\n * @public\n *\n * @example\n * ```typescript\n * import { getStorageEnvVars } from '@kumix/storage';\n *\n * const envVars = getStorageEnvVars();\n * console.log('Storage environment variables:', envVars);\n * // Returns: {\n * // KUMIX_S3_PROVIDER: 'aws',\n * // KUMIX_S3_REGION: 'us-east-1',\n * // KUMIX_S3_BUCKET: 'my-bucket',\n * // // ... other set variables (secrets are masked)\n * // }\n * ```\n */\nexport function getStorageEnvVars(env?: EnvRecord): Record<string, string | undefined> {\n const result: Record<string, string | undefined> = {};\n\n // Mask any value whose key looks like a credential. Previously only\n // SECRET/​API_SECRET values were masked, leaking the S3 access key ID and the\n // Cloudinary API key in plaintext. We now also redact *_KEY_ID, *_API_KEY,\n // *ACCESS_KEY_ID, *SECRET_ACCESS_KEY, *TOKEN, and *PASSWORD.\n const SENSITIVE_PATTERNS = [\n /SECRET/i,\n /API_SECRET/i,\n /API_KEY/i,\n /ACCESS_KEY_ID/i,\n /SECRET_ACCESS_KEY/i,\n /_KEY_ID$/i,\n /TOKEN/i,\n /PASSWORD/i,\n ];\n const isSensitive = (key: string) => SENSITIVE_PATTERNS.some((p) => p.test(key));\n\n Object.values(ENV_VARS).forEach((key) => {\n const value = getEnvVar(key, env);\n if (value) {\n // Mask sensitive values. Indicate presence only — even partial prefixes\n // leak meaningful information about short secrets.\n if (isSensitive(key)) {\n result[key] = \"****\";\n } else {\n result[key] = value;\n }\n }\n });\n\n return result;\n}\n"],"mappings":"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"}
@@ -33,6 +33,8 @@ interface S3Config {
33
33
  accessKeyId: string;
34
34
  /** Secret access key for authentication */
35
35
  secretAccessKey: string;
36
+ /** Optional session token for AWS STS / SSO / role-chain temporary credentials */
37
+ sessionToken?: string;
36
38
  /** Custom endpoint for S3-compatible services (MinIO, R2, etc.) */
37
39
  endpoint?: string;
38
40
  /** Force path style for services like MinIO */
@@ -85,18 +87,18 @@ interface FolderInfo {
85
87
  name: string;
86
88
  /** Full folder path */
87
89
  path: string;
88
- /** Total size of all files in folder */
89
- size: number;
90
- /** Number of files in folder */
91
- fileCount: number;
92
- /** Last modified date */
93
- lastModified: Date;
90
+ /** Total size of all files in folder (requires aggregation; often omitted) */
91
+ size?: number;
92
+ /** Number of files in folder (requires aggregation; often omitted) */
93
+ fileCount?: number;
94
+ /** Last modified date (requires aggregation; often omitted) */
95
+ lastModified?: Date;
94
96
  }
95
97
  //#endregion
96
98
  //#region src/types/file-operations.d.ts
97
99
  interface UploadOptions {
98
100
  key: string;
99
- file: Buffer | Uint8Array | string;
101
+ file: Uint8Array | string;
100
102
  contentType?: string;
101
103
  metadata?: Record<string, string>;
102
104
  cacheControl?: string;
@@ -119,8 +121,8 @@ interface DownloadOptions {
119
121
  }
120
122
  interface DownloadResult {
121
123
  success: boolean;
122
- content?: Buffer;
123
- data?: Buffer;
124
+ content?: Uint8Array;
125
+ data?: Uint8Array;
124
126
  contentType?: string;
125
127
  contentLength?: number;
126
128
  lastModified?: Date;
@@ -328,6 +330,7 @@ declare const ENV_VARS: {
328
330
  readonly KUMIX_S3_BUCKET: "KUMIX_S3_BUCKET";
329
331
  readonly KUMIX_S3_ACCESS_KEY_ID: "KUMIX_S3_ACCESS_KEY_ID";
330
332
  readonly KUMIX_S3_SECRET_ACCESS_KEY: "KUMIX_S3_SECRET_ACCESS_KEY";
333
+ readonly KUMIX_S3_SESSION_TOKEN: "KUMIX_S3_SESSION_TOKEN";
331
334
  readonly KUMIX_S3_ENDPOINT: "KUMIX_S3_ENDPOINT";
332
335
  readonly KUMIX_S3_FORCE_PATH_STYLE: "KUMIX_S3_FORCE_PATH_STYLE";
333
336
  readonly KUMIX_S3_PUBLIC_URL: "KUMIX_S3_PUBLIC_URL";
@@ -591,4 +594,4 @@ declare function validateCloudinaryEnvVars(env?: EnvRecord): {
591
594
  declare function getStorageEnvVars(env?: EnvRecord): Record<string, string | undefined>;
592
595
  //#endregion
593
596
  export { DownloadOptions as A, PresignedUrlResult as B, RenameFolderResult as C, CopyResult as D, CopyOptions as E, ListOptions as F, FolderInfo as G, UploadResult as H, ListResult as I, StorageConfig as J, S3Config as K, MoveOptions as L, DuplicateOptions as M, DuplicateResult as N, DeleteOptions as O, ExistsResult as P, MoveResult as R, RenameFolderOptions as S, BatchDeleteResult as T, CloudinaryConfig as U, UploadOptions as V, FileInfo as W, StorageProvider as Y, DeleteFolderOptions as _, hasStorageConfig as a, ListFoldersOptions as b, loadS3Config as c, validateS3EnvVars as d, StorageInterface as f, CreateFolderResult as g, CreateFolderOptions as h, getStorageProvider as i, DownloadResult as j, DeleteResult as k, loadStorageConfig as l, CopyFolderResult as m, EnvRecord as n, isStorageConfigured as o, CopyFolderOptions as p, S3ProviderType as q, getStorageEnvVars as r, loadCloudinaryConfig as s, ENV_VARS as t, validateCloudinaryEnvVars as u, DeleteFolderResult as v, BatchDeleteOptions as w, ListFoldersResult as x, FolderExistsResult as y, PresignedUrlOptions as z };
594
- //# sourceMappingURL=config-J6AdDh_c.d.ts.map
597
+ //# sourceMappingURL=config-DGzEH-Zd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-DGzEH-Zd.d.ts","names":[],"sources":["../src/types/base.ts","../src/types/file-operations.ts","../src/types/folder-operations.ts","../src/types/storage-interface.ts","../src/config.ts"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","names":[],"sources":["../src/helpers.ts"],"mappings":"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"}
1
+ {"version":3,"file":"helpers.d.ts","names":[],"sources":["../src/helpers.ts"],"mappings":"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"}
package/dist/helpers.js CHANGED
@@ -1,9 +1,80 @@
1
- import { lookup } from "mime-types";
2
1
  //#region src/helpers.ts
3
2
  /**
4
3
  * Storage helper utilities
5
4
  * Provides utility functions for file operations, MIME type detection, and path manipulation
6
5
  */
6
+ const MIME_BY_EXTENSION = {
7
+ jpg: "image/jpeg",
8
+ jpeg: "image/jpeg",
9
+ png: "image/png",
10
+ gif: "image/gif",
11
+ webp: "image/webp",
12
+ avif: "image/avif",
13
+ bmp: "image/bmp",
14
+ svg: "image/svg+xml",
15
+ ico: "image/x-icon",
16
+ tiff: "image/tiff",
17
+ tif: "image/tiff",
18
+ heic: "image/heic",
19
+ heif: "image/heif",
20
+ mp4: "video/mp4",
21
+ webm: "video/webm",
22
+ mov: "video/quicktime",
23
+ avi: "video/x-msvideo",
24
+ mkv: "video/x-matroska",
25
+ m4v: "video/x-m4v",
26
+ wmv: "video/x-ms-wmv",
27
+ flv: "video/x-flv",
28
+ mp3: "audio/mpeg",
29
+ wav: "audio/wav",
30
+ ogg: "audio/ogg",
31
+ flac: "audio/flac",
32
+ aac: "audio/aac",
33
+ m4a: "audio/x-m4a",
34
+ opus: "audio/opus",
35
+ pdf: "application/pdf",
36
+ doc: "application/msword",
37
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
38
+ xls: "application/vnd.ms-excel",
39
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
40
+ ppt: "application/vnd.ms-powerpoint",
41
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
42
+ txt: "text/plain",
43
+ md: "text/markdown",
44
+ html: "text/html",
45
+ htm: "text/html",
46
+ css: "text/css",
47
+ csv: "text/csv",
48
+ ics: "text/calendar",
49
+ json: "application/json",
50
+ xml: "application/xml",
51
+ yaml: "application/x-yaml",
52
+ yml: "application/x-yaml",
53
+ js: "text/javascript",
54
+ mjs: "text/javascript",
55
+ ts: "text/typescript",
56
+ jsx: "text/jsx",
57
+ tsx: "text/tsx",
58
+ zip: "application/zip",
59
+ gz: "application/gzip",
60
+ tar: "application/x-tar",
61
+ rar: "application/vnd.rar",
62
+ "7z": "application/x-7z-compressed",
63
+ bz2: "application/x-bzip2",
64
+ woff: "font/woff",
65
+ woff2: "font/woff2",
66
+ ttf: "font/ttf",
67
+ otf: "font/otf",
68
+ eot: "application/vnd.ms-fontobject",
69
+ bin: "application/octet-stream",
70
+ exe: "application/octet-stream"
71
+ };
72
+ const lookupMimeByExtension = (fileName) => {
73
+ const dot = fileName.lastIndexOf(".");
74
+ if (dot < 0 || dot === fileName.length - 1) return void 0;
75
+ const ext = fileName.slice(dot + 1).toLowerCase();
76
+ return MIME_BY_EXTENSION[ext];
77
+ };
7
78
  /**
8
79
  * Generate a unique file key with timestamp and random string
9
80
  * @param originalName Original file name
@@ -25,8 +96,8 @@ import { lookup } from "mime-types";
25
96
  function generateFileKey(originalName, prefix) {
26
97
  const timestamp = Date.now();
27
98
  const random = Math.random().toString(36).substring(2, 8);
28
- const extension = originalName.split(".").pop();
29
- const fileName = `${originalName.split(".").slice(0, -1).join(".")}-${timestamp}-${random}${extension ? `.${extension}` : ""}`;
99
+ const lastDot = originalName.lastIndexOf(".");
100
+ const fileName = `${lastDot > 0 ? originalName.slice(0, lastDot) : originalName}-${timestamp}-${random}${lastDot > 0 ? originalName.slice(lastDot) : ""}`;
30
101
  return prefix ? `${prefix}/${fileName}` : fileName;
31
102
  }
32
103
  /**
@@ -50,7 +121,7 @@ function generateFileKey(originalName, prefix) {
50
121
  * ```
51
122
  */
52
123
  function getMimeType(fileName) {
53
- return lookup(fileName) || "application/octet-stream";
124
+ return lookupMimeByExtension(fileName) || "application/octet-stream";
54
125
  }
55
126
  /**
56
127
  * Validate file size against maximum allowed size
@@ -140,6 +211,7 @@ function validateFileType(fileName, allowedTypes) {
140
211
  */
141
212
  function formatFileSize(bytes) {
142
213
  if (bytes === 0) return "0 Bytes";
214
+ if (bytes < 0) return `-${formatFileSize(-bytes)}`;
143
215
  const k = 1024;
144
216
  const sizes = [
145
217
  "Bytes",
@@ -148,7 +220,7 @@ function formatFileSize(bytes) {
148
220
  "GB",
149
221
  "TB"
150
222
  ];
151
- const i = Math.floor(Math.log(bytes) / Math.log(k));
223
+ const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
152
224
  return `${parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
153
225
  }
154
226
  /**
@@ -169,7 +241,7 @@ function formatFileSize(bytes) {
169
241
  * ```
170
242
  */
171
243
  function sanitizeFileName(fileName) {
172
- return fileName.replace(/[^a-zA-Z0-9.-]/g, "_").replace(/_{2,}/g, "_").replace(/^_|_$/g, "").toLowerCase();
244
+ return fileName.replace(/[^a-zA-Z0-9.-]/g, "_").replace(/_{2,}/g, "_").replace(/^_|_$/g, "").replace(/_+(\.[a-zA-Z0-9]+)$/, "$1").toLowerCase();
173
245
  }
174
246
  /**
175
247
  * Extract file extension from file name
@@ -192,8 +264,9 @@ function sanitizeFileName(fileName) {
192
264
  * ```
193
265
  */
194
266
  function getFileExtension(fileName) {
195
- const extension = fileName.split(".").pop();
196
- return extension ? `.${extension.toLowerCase()}` : "";
267
+ const lastDot = fileName.lastIndexOf(".");
268
+ if (lastDot <= 0 || lastDot === fileName.length - 1) return "";
269
+ return `.${fileName.slice(lastDot + 1).toLowerCase()}`;
197
270
  }
198
271
  /**
199
272
  * Generate cache control header for HTTP responses
@@ -288,7 +361,7 @@ function buildPublicUrl(baseUrl, bucket, key, supabase) {
288
361
  cleanBaseUrl = baseUrl.replace(/\/$/, "");
289
362
  const cleanKey = key.replace(/^\//, "");
290
363
  if (supabase) {
291
- cleanBaseUrl = cleanBaseUrl.replace(/\.storage/, "").replace(/\/storage\/v1\/s3$/, "");
364
+ cleanBaseUrl = cleanBaseUrl.replace(/\.storage(?=\.supabase\.(?:co|in|net))/, "").replace(/\/storage\/v1\/s3$/, "");
292
365
  cleanBaseUrl = `${cleanBaseUrl}/storage/v1/object/public`;
293
366
  }
294
367
  return `${cleanBaseUrl}/${bucket === "" ? "" : `${bucket}/`}${cleanKey}`;
@@ -410,7 +483,7 @@ function getFileInfo(file) {
410
483
  function generateUniqueKey(fileName, prefix, includeTimestamp = true) {
411
484
  const sanitized = sanitizeFileName(fileName);
412
485
  const extension = getFileExtension(sanitized);
413
- const baseName = sanitized.replace(extension, "");
486
+ const baseName = extension ? sanitized.slice(0, sanitized.length - extension.length) : sanitized;
414
487
  let uniquePart = "";
415
488
  if (includeTimestamp) uniquePart = `-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
416
489
  const finalName = `${baseName}${uniquePart}${extension}`;
@@ -871,7 +944,7 @@ function bufferToBase64(buffer, includeDataUrl = false, mimeType) {
871
944
  * }
872
945
  * ```
873
946
  */
874
- async function generateFileHash(content, algorithm = "md5") {
947
+ async function generateFileHash(content, algorithm = "sha256") {
875
948
  if (algorithm !== "md5" && typeof crypto !== "undefined" && crypto.subtle) {
876
949
  const rawData = typeof content === "string" ? new TextEncoder().encode(content) : content;
877
950
  const hashBuffer = await crypto.subtle.digest(algorithm === "sha1" ? "SHA-1" : "SHA-256", rawData);