@kumix/storage 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","names":[],"sources":["../src/helpers.ts"],"sourcesContent":["/**\n * Storage helper utilities\n * Provides utility functions for file operations, MIME type detection, and path manipulation\n */\n\nimport { lookup } from \"mime-types\";\n\n/**\n * Generate a unique file key with timestamp and random string\n * @param originalName Original file name\n * @param prefix Optional prefix for the key\n * @returns Unique file key\n * @public\n *\n * @example\n * ```typescript\n * import { generateFileKey } from '@kumix/storage/helpers';\n *\n * const key1 = generateFileKey('document.pdf');\n * // Returns: \"document-1703123456789-abc123.pdf\"\n *\n * const key2 = generateFileKey('photo.jpg', 'uploads/images');\n * // Returns: \"uploads/images/photo-1703123456789-def456.jpg\"\n * ```\n */\nexport function generateFileKey(originalName: string, prefix?: string): string {\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 8);\n const extension = originalName.split(\".\").pop();\n const baseName = originalName.split(\".\").slice(0, -1).join(\".\");\n\n const fileName = `${baseName}-${timestamp}-${random}${extension ? `.${extension}` : \"\"}`;\n\n return prefix ? `${prefix}/${fileName}` : fileName;\n}\n\n/**\n * Get MIME type from file name or extension\n * @param fileName File name or path\n * @returns MIME type string\n * @public\n *\n * @example\n * ```typescript\n * import { getMimeType } from '@kumix/storage/helpers';\n *\n * const type1 = getMimeType('document.pdf');\n * // Returns: \"application/pdf\"\n *\n * const type2 = getMimeType('photo.jpg');\n * // Returns: \"image/jpeg\"\n *\n * const type3 = getMimeType('unknown.xyz');\n * // Returns: \"application/octet-stream\"\n * ```\n */\nexport function getMimeType(fileName: string): string {\n const mimeType = lookup(fileName);\n return mimeType || \"application/octet-stream\";\n}\n\n/**\n * Validate file size against maximum allowed size\n * @param fileSize File size in bytes\n * @param maxSize Maximum allowed size in bytes\n * @returns Validation result with error message if invalid\n * @public\n *\n * @example\n * ```typescript\n * import { validateFileSize } from '@kumix/storage/helpers';\n *\n * const result1 = validateFileSize(1024 * 1024, 5 * 1024 * 1024); // 1MB vs 5MB limit\n * // Returns: { valid: true }\n *\n * const result2 = validateFileSize(10 * 1024 * 1024, 5 * 1024 * 1024); // 10MB vs 5MB limit\n * // Returns: { valid: false, error: \"File size 10 MB exceeds maximum allowed size 5 MB\" }\n * ```\n */\nexport function validateFileSize(\n fileSize: number,\n maxSize: number,\n): { valid: boolean; error?: string } {\n if (fileSize > maxSize) {\n return {\n valid: false,\n error: `File size ${formatFileSize(fileSize)} exceeds maximum allowed size ${formatFileSize(maxSize)}`,\n };\n }\n return { valid: true };\n}\n\n/**\n * Validate file type against allowed types (MIME types or extensions)\n * @param fileName File name or path\n * @param allowedTypes Array of allowed MIME types or file extensions\n * @returns Validation result with error message if invalid\n * @public\n *\n * @example\n * ```typescript\n * import { validateFileType } from '@kumix/storage/helpers';\n *\n * // Using MIME types\n * const result1 = validateFileType('photo.jpg', ['image/*', 'application/pdf']);\n * // Returns: { valid: true }\n *\n * // Using file extensions\n * const result2 = validateFileType('document.txt', ['.pdf', '.doc', '.docx']);\n * // Returns: { valid: false, error: \"File type not allowed. Allowed types: .pdf, .doc, .docx\" }\n *\n * // Mixed MIME types and extensions\n * const result3 = validateFileType('video.mp4', ['image/*', '.pdf', 'video/mp4']);\n * // Returns: { valid: true }\n * ```\n */\nexport function validateFileType(\n fileName: string,\n allowedTypes: string[],\n): { valid: boolean; error?: string } {\n const mimeType = getMimeType(fileName);\n const extension = fileName.split(\".\").pop()?.toLowerCase();\n\n const isValidMime = allowedTypes.some((type) => {\n if (type.includes(\"*\")) {\n const baseType = type.split(\"/\")[0];\n return mimeType.startsWith(baseType);\n }\n return mimeType === type;\n });\n\n const isValidExtension = extension && allowedTypes.includes(`.${extension}`);\n\n if (!isValidMime && !isValidExtension) {\n return {\n valid: false,\n error: `File type not allowed. Allowed types: ${allowedTypes.join(\", \")}`,\n };\n }\n\n return { valid: true };\n}\n\n/**\n * Format file size in human readable format\n * @param bytes File size in bytes\n * @returns Formatted file size string\n * @public\n *\n * @example\n * ```typescript\n * import { formatFileSize } from '@kumix/storage/helpers';\n *\n * const size1 = formatFileSize(1024);\n * // Returns: \"1 KB\"\n *\n * const size2 = formatFileSize(1048576);\n * // Returns: \"1 MB\"\n *\n * const size3 = formatFileSize(1073741824);\n * // Returns: \"1 GB\"\n * ```\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return \"0 Bytes\";\n\n const k = 1024;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;\n}\n\n/**\n * Sanitize file name for S3 key by removing special characters\n * @param fileName Original file name\n * @returns Sanitized file name safe for S3 keys\n * @public\n *\n * @example\n * ```typescript\n * import { sanitizeFileName } from '@kumix/storage/helpers';\n *\n * const safe1 = sanitizeFileName('My Document (2023).pdf');\n * // Returns: \"my_document_2023.pdf\"\n *\n * const safe2 = sanitizeFileName('file@#$%name.txt');\n * // Returns: \"file_name.txt\"\n * ```\n */\nexport function sanitizeFileName(fileName: string): string {\n return fileName\n .replace(/[^a-zA-Z0-9.-]/g, \"_\") // Replace special chars with underscore\n .replace(/_{2,}/g, \"_\") // Replace multiple underscores with single\n .replace(/^_|_$/g, \"\") // Remove leading/trailing underscores\n .toLowerCase();\n}\n\n/**\n * Extract file extension from file name\n * @param fileName File name or path\n * @returns File extension with dot (e.g., '.pdf')\n * @public\n *\n * @example\n * ```typescript\n * import { getFileExtension } from '@kumix/storage/helpers';\n *\n * const ext1 = getFileExtension('document.pdf');\n * // Returns: \".pdf\"\n *\n * const ext2 = getFileExtension('photo.JPEG');\n * // Returns: \".jpeg\"\n *\n * const ext3 = getFileExtension('noextension');\n * // Returns: \"\"\n * ```\n */\nexport function getFileExtension(fileName: string): string {\n const extension = fileName.split(\".\").pop();\n return extension ? `.${extension.toLowerCase()}` : \"\";\n}\n\n/**\n * Generate cache control header for HTTP responses\n * @param maxAge Cache max age in seconds (default: 1 year)\n * @param isPublic Whether cache should be public or private\n * @returns Cache control header string\n * @public\n *\n * @example\n * ```typescript\n * import { generateCacheControl } from '@kumix/storage/helpers';\n *\n * const cache1 = generateCacheControl();\n * // Returns: \"public, max-age=31536000\"\n *\n * const cache2 = generateCacheControl(3600, false);\n * // Returns: \"private, max-age=3600\"\n *\n * const cache3 = generateCacheControl(86400); // 1 day\n * // Returns: \"public, max-age=86400\"\n * ```\n */\nexport function generateCacheControl(\n maxAge: number = 31536000, // 1 year default\n isPublic: boolean = true,\n): string {\n const visibility = isPublic ? \"public\" : \"private\";\n return `${visibility}, max-age=${maxAge}`;\n}\n\n/**\n * Parse S3 URL to extract bucket and key\n * @param url S3 URL to parse\n * @returns Object containing bucket and key\n * @public\n *\n * @example\n * ```typescript\n * import { parseS3Url } from '@kumix/storage/helpers';\n *\n * // Virtual hosted-style URL\n * const result1 = parseS3Url('https://my-bucket.s3.us-east-1.amazonaws.com/folder/file.pdf');\n * // Returns: { bucket: \"my-bucket\", key: \"folder/file.pdf\" }\n *\n * // Path-style URL\n * const result2 = parseS3Url('https://s3.us-east-1.amazonaws.com/my-bucket/folder/file.pdf');\n * // Returns: { bucket: \"my-bucket\", key: \"folder/file.pdf\" }\n *\n * // Custom endpoint (R2, MinIO)\n * const result3 = parseS3Url('https://my-endpoint.com/my-bucket/folder/file.pdf');\n * // Returns: { bucket: \"my-bucket\", key: \"folder/file.pdf\" }\n * ```\n */\nexport function parseS3Url(url: string): { bucket?: string; key?: string } {\n try {\n const urlObj = new URL(url);\n\n // Handle different S3 URL formats\n if (urlObj.hostname.includes(\"amazonaws.com\")) {\n // Virtual hosted-style: https://bucket.s3.region.amazonaws.com/key\n if (urlObj.hostname.startsWith(\"s3\")) {\n // Path-style: https://s3.region.amazonaws.com/bucket/key\n const pathParts = urlObj.pathname.split(\"/\").filter(Boolean);\n return {\n bucket: pathParts[0],\n key: pathParts.slice(1).join(\"/\"),\n };\n } else {\n // Virtual hosted-style\n const bucket = urlObj.hostname.split(\".\")[0];\n const key = urlObj.pathname.substring(1); // Remove leading slash\n return { bucket, key };\n }\n }\n\n // Custom endpoint (like R2, MinIO)\n const pathParts = urlObj.pathname.split(\"/\").filter(Boolean);\n return {\n bucket: pathParts[0],\n key: pathParts.slice(1).join(\"/\"),\n };\n } catch {\n return {};\n }\n}\n\n/**\n * Build public URL for a file\n * @param baseUrl Base URL of the storage service\n * @param bucket Bucket name\n * @param key File key/path\n * @returns Complete public URL\n * @public\n *\n * @example\n * ```typescript\n * import { buildPublicUrl } from '@kumix/storage/helpers';\n *\n * const url1 = buildPublicUrl('https://cdn.example.com', 'my-bucket', 'folder/file.pdf');\n * // Returns: \"https://cdn.example.com/my-bucket/folder/file.pdf\"\n *\n * const url2 = buildPublicUrl('https://storage.example.com/', 'assets', 'images/logo.png');\n * // Returns: \"https://storage.example.com/assets/images/logo.png\"\n * ```\n */\nexport function buildPublicUrl(\n baseUrl: string,\n bucket: string,\n key: string,\n supabase?: boolean,\n): string {\n let cleanBaseUrl: string;\n\n cleanBaseUrl = baseUrl.replace(/\\/$/, \"\");\n const cleanKey = key.replace(/^\\//, \"\");\n\n if (supabase) {\n // Remove both `.storage` subdomain and S3 path\n cleanBaseUrl = cleanBaseUrl\n .replace(/\\.storage/, \"\") // remove \".storage\"\n .replace(/\\/storage\\/v1\\/s3$/, \"\"); // remove \"/storage/v1/s3\"\n cleanBaseUrl = `${cleanBaseUrl}/storage/v1/object/public`;\n }\n\n return `${cleanBaseUrl}/${bucket === \"\" ? \"\" : `${bucket}/`}${cleanKey}`;\n}\n\n/**\n * Validate S3 key format according to AWS S3 naming rules\n * @param key S3 object key to validate\n * @returns Validation result with error message if invalid\n * @public\n *\n * @example\n * ```typescript\n * import { validateS3Key } from '@kumix/storage/helpers';\n *\n * const result1 = validateS3Key('documents/report.pdf');\n * // Returns: { valid: true }\n *\n * const result2 = validateS3Key('/invalid/key/');\n * // Returns: { valid: false, error: \"Key cannot start or end with forward slash\" }\n * ```\n */\nexport function validateS3Key(key: string): { valid: boolean; error?: string } {\n if (!key || key.length === 0) {\n return { valid: false, error: \"Key cannot be empty\" };\n }\n\n if (key.length > 1024) {\n return { valid: false, error: \"Key cannot exceed 1024 characters\" };\n }\n\n if (key.startsWith(\"/\") || key.endsWith(\"/\")) {\n return {\n valid: false,\n error: \"Key cannot start or end with forward slash\",\n };\n }\n\n // Check for invalid characters\n const invalidChars = /[^\\w\\-_./]/;\n if (invalidChars.test(key)) {\n return { valid: false, error: \"Key contains invalid characters\" };\n }\n\n return { valid: true };\n}\n\n/**\n * Convert File object to Buffer for upload\n * @param file File object from browser input\n * @returns Promise that resolves to Buffer\n * @public\n *\n * @example\n * ```typescript\n * import { fileToBuffer } from '@kumix/storage/helpers';\n *\n * // In browser with file input\n * const fileInput = document.querySelector('input[type=\"file\"]') as HTMLInputElement;\n * const file = fileInput.files?.[0];\n *\n * if (file) {\n * const buffer = await fileToBuffer(file);\n * // Use buffer for upload\n * await storage.uploadFile('uploads/file.pdf', buffer);\n * }\n * ```\n */\nexport async function fileToBuffer(file: File): Promise<Uint8Array> {\n const arrayBuffer = await file.arrayBuffer();\n return new Uint8Array(arrayBuffer);\n}\n\n/**\n * Get file information from File object\n * @param file File object from browser input\n * @returns Object containing file metadata\n * @public\n *\n * @example\n * ```typescript\n * import { getFileInfo } from '@kumix/storage/helpers';\n *\n * // In browser with file input\n * const fileInput = document.querySelector('input[type=\"file\"]') as HTMLInputElement;\n * const file = fileInput.files?.[0];\n *\n * if (file) {\n * const info = getFileInfo(file);\n * console.log(info);\n * // Returns: {\n * // name: \"document.pdf\",\n * // size: 1048576,\n * // type: \"application/pdf\",\n * // lastModified: Date\n * // }\n * }\n * ```\n */\nexport function getFileInfo(file: File): {\n name: string;\n size: number;\n type: string;\n lastModified: Date;\n} {\n return {\n name: file.name,\n size: file.size,\n type: file.type || getMimeType(file.name),\n lastModified: new Date(file.lastModified),\n };\n}\n\n/**\n * Generate unique key with prefix and timestamp\n * @param fileName Original file name\n * @param prefix Optional prefix for the key\n * @param includeTimestamp Whether to include timestamp for uniqueness\n * @returns Unique file key\n * @public\n *\n * @example\n * ```typescript\n * import { generateUniqueKey } from '@kumix/storage/helpers';\n *\n * const key1 = generateUniqueKey('report.pdf', 'documents');\n * // Returns: \"documents/report-1703123456789-abc123.pdf\"\n *\n * const key2 = generateUniqueKey('photo.jpg', 'images', false);\n * // Returns: \"images/photo.jpg\"\n * ```\n */\nexport function generateUniqueKey(\n fileName: string,\n prefix?: string,\n includeTimestamp: boolean = true,\n): string {\n const sanitized = sanitizeFileName(fileName);\n const extension = getFileExtension(sanitized);\n const baseName = sanitized.replace(extension, \"\");\n\n let uniquePart = \"\";\n if (includeTimestamp) {\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 8);\n uniquePart = `-${timestamp}-${random}`;\n }\n\n const finalName = `${baseName}${uniquePart}${extension}`;\n\n return prefix ? `${prefix}/${finalName}` : finalName;\n}\n\n/**\n * Extract bucket, key, and region from S3 URL\n * @param url S3 URL to parse\n * @returns Object containing bucket, key, and region information\n * @public\n *\n * @example\n * ```typescript\n * import { extractS3Info } from '@kumix/storage/helpers';\n *\n * // AWS S3 virtual hosted-style\n * const result1 = extractS3Info('https://my-bucket.s3.us-west-2.amazonaws.com/folder/file.pdf');\n * // Returns: { bucket: \"my-bucket\", key: \"folder/file.pdf\", region: \"us-west-2\" }\n *\n * // AWS S3 path-style\n * const result2 = extractS3Info('https://s3.eu-central-1.amazonaws.com/my-bucket/file.pdf');\n * // Returns: { bucket: \"my-bucket\", key: \"file.pdf\", region: \"eu-central-1\" }\n *\n * // Custom endpoint\n * const result3 = extractS3Info('https://minio.example.com/my-bucket/folder/file.pdf');\n * // Returns: { bucket: \"my-bucket\", key: \"folder/file.pdf\" }\n * ```\n */\nexport function extractS3Info(url: string): {\n bucket?: string;\n key?: string;\n region?: string;\n} {\n try {\n const urlObj = new URL(url);\n\n // AWS S3 URL patterns\n if (urlObj.hostname.includes(\"amazonaws.com\")) {\n if (urlObj.hostname.startsWith(\"s3\")) {\n // Path-style: https://s3.region.amazonaws.com/bucket/key\n const pathParts = urlObj.pathname.split(\"/\").filter(Boolean);\n const region = urlObj.hostname.split(\".\")[1];\n return {\n bucket: pathParts[0],\n key: pathParts.slice(1).join(\"/\"),\n region,\n };\n } else {\n // Virtual hosted-style: https://bucket.s3.region.amazonaws.com/key\n const hostParts = urlObj.hostname.split(\".\");\n const bucket = hostParts[0];\n const region = hostParts[2];\n const key = urlObj.pathname.substring(1);\n return { bucket, key, region };\n }\n }\n\n // Custom endpoint (R2, MinIO, etc.)\n const pathParts = urlObj.pathname.split(\"/\").filter(Boolean);\n return {\n bucket: pathParts[0],\n key: pathParts.slice(1).join(\"/\"),\n };\n } catch {\n return {};\n }\n}\n\n/**\n * Validate S3 configuration object\n * @param config S3 configuration object to validate\n * @returns Validation result with list of errors\n * @public\n *\n * @example\n * ```typescript\n * import { validateS3Config } from '@kumix/storage/helpers';\n *\n * const config = {\n * provider: 'aws',\n * region: 'us-east-1',\n * bucket: 'my-bucket',\n * accessKeyId: 'AKIA...',\n * secretAccessKey: 'secret...'\n * };\n *\n * const result = validateS3Config(config);\n * // Returns: { valid: true, errors: [] }\n *\n * const invalidConfig = { provider: 'aws' }; // Missing required fields\n * const result2 = validateS3Config(invalidConfig);\n * // Returns: { valid: false, errors: [\"Region is required\", \"Bucket is required\", ...] }\n * ```\n */\nexport function validateS3Config(config: Record<string, unknown>): {\n valid: boolean;\n errors: string[];\n} {\n const errors: string[] = [];\n\n if (!config.provider) {\n errors.push(\"Provider is required\");\n }\n\n if (!config.region) {\n errors.push(\"Region is required\");\n }\n\n if (!config.bucket) {\n errors.push(\"Bucket is required\");\n }\n\n if (!config.accessKeyId) {\n errors.push(\"Access Key ID is required\");\n }\n\n if (!config.secretAccessKey) {\n errors.push(\"Secret Access Key is required\");\n }\n\n // Validate bucket name\n if (config.bucket && typeof config.bucket === \"string\") {\n const bucketValidation = validateBucketName(config.bucket);\n if (!bucketValidation.valid) {\n errors.push(`Invalid bucket name: ${bucketValidation.error}`);\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n\n/**\n * Validate S3 bucket name according to AWS naming rules\n * @param bucketName Bucket name to validate\n * @returns Validation result with error message if invalid\n * @public\n *\n * @example\n * ```typescript\n * import { validateBucketName } from '@kumix/storage/helpers';\n *\n * const result1 = validateBucketName('my-valid-bucket');\n * // Returns: { valid: true }\n *\n * const result2 = validateBucketName('My-Invalid-Bucket');\n * // Returns: { valid: false, error: \"Bucket name can only contain lowercase letters, numbers, dots, and hyphens\" }\n *\n * const result3 = validateBucketName('ab');\n * // Returns: { valid: false, error: \"Bucket name must be between 3 and 63 characters\" }\n * ```\n */\nexport function validateBucketName(bucketName: string): {\n valid: boolean;\n error?: string;\n} {\n if (!bucketName || bucketName.length === 0) {\n return { valid: false, error: \"Bucket name cannot be empty\" };\n }\n\n if (bucketName.length < 3 || bucketName.length > 63) {\n return {\n valid: false,\n error: \"Bucket name must be between 3 and 63 characters\",\n };\n }\n\n if (!/^[a-z0-9.-]+$/.test(bucketName)) {\n return {\n valid: false,\n error: \"Bucket name can only contain lowercase letters, numbers, dots, and hyphens\",\n };\n }\n\n if (bucketName.startsWith(\".\") || bucketName.endsWith(\".\")) {\n return {\n valid: false,\n error: \"Bucket name cannot start or end with a dot\",\n };\n }\n\n if (bucketName.startsWith(\"-\") || bucketName.endsWith(\"-\")) {\n return {\n valid: false,\n error: \"Bucket name cannot start or end with a hyphen\",\n };\n }\n\n if (bucketName.includes(\"..\")) {\n return {\n valid: false,\n error: \"Bucket name cannot contain consecutive dots\",\n };\n }\n\n return { valid: true };\n}\n\n/**\n * Generate content disposition header for file downloads\n * @param fileName File name for the download\n * @param disposition Whether to display inline or as attachment\n * @returns Content disposition header string\n * @public\n *\n * @example\n * ```typescript\n * import { getContentDisposition } from '@kumix/storage/helpers';\n *\n * const header1 = getContentDisposition('document.pdf');\n * // Returns: 'attachment; filename=\"document.pdf\"'\n *\n * const header2 = getContentDisposition('image.jpg', 'inline');\n * // Returns: 'inline; filename=\"image.jpg\"'\n *\n * const header3 = getContentDisposition('file@#$name.txt');\n * // Returns: 'attachment; filename=\"filename.txt\"'\n * ```\n */\nexport function getContentDisposition(\n fileName: string,\n disposition: \"inline\" | \"attachment\" = \"attachment\",\n): string {\n const sanitizedName = fileName.replace(/[^\\w\\s.-]/gi, \"\");\n return `${disposition}; filename=\"${sanitizedName}\"`;\n}\n\n/**\n * Check if file is an image based on MIME type\n * @param fileName File name or path\n * @returns True if file is an image\n * @public\n *\n * @example\n * ```typescript\n * import { isImageFile } from '@kumix/storage/helpers';\n *\n * const result1 = isImageFile('photo.jpg');\n * // Returns: true\n *\n * const result2 = isImageFile('document.pdf');\n * // Returns: false\n * ```\n */\nexport function isImageFile(fileName: string): boolean {\n const mimeType = getMimeType(fileName);\n return mimeType.startsWith(\"image/\");\n}\n\n/**\n * Check if file is a video based on MIME type\n * @param fileName File name or path\n * @returns True if file is a video\n * @public\n *\n * @example\n * ```typescript\n * import { isVideoFile } from '@kumix/storage/helpers';\n *\n * const result1 = isVideoFile('movie.mp4');\n * // Returns: true\n *\n * const result2 = isVideoFile('document.pdf');\n * // Returns: false\n * ```\n */\nexport function isVideoFile(fileName: string): boolean {\n const mimeType = getMimeType(fileName);\n return mimeType.startsWith(\"video/\");\n}\n\n/**\n * Check if file is an audio file based on MIME type\n * @param fileName File name or path\n * @returns True if file is an audio file\n * @public\n *\n * @example\n * ```typescript\n * import { isAudioFile } from '@kumix/storage/helpers';\n *\n * const result1 = isAudioFile('song.mp3');\n * // Returns: true\n *\n * const result2 = isAudioFile('document.pdf');\n * // Returns: false\n * ```\n */\nexport function isAudioFile(fileName: string): boolean {\n const mimeType = getMimeType(fileName);\n return mimeType.startsWith(\"audio/\");\n}\n\n/**\n * Check if file is a document based on MIME type\n * @param fileName File name or path\n * @returns True if file is a document\n * @public\n *\n * @example\n * ```typescript\n * import { isDocumentFile } from '@kumix/storage/helpers';\n *\n * const result1 = isDocumentFile('report.pdf');\n * // Returns: true\n *\n * const result2 = isDocumentFile('spreadsheet.xlsx');\n * // Returns: true\n *\n * const result3 = isDocumentFile('photo.jpg');\n * // Returns: false\n * ```\n */\nexport function isDocumentFile(fileName: string): boolean {\n const mimeType = getMimeType(fileName);\n const documentTypes = [\n \"application/pdf\",\n \"application/msword\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.ms-excel\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \"application/vnd.ms-powerpoint\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n \"text/plain\",\n \"text/csv\",\n ];\n return documentTypes.includes(mimeType);\n}\n\n/**\n * Normalize path separators and remove redundant slashes\n * @param path Path to normalize\n * @returns Normalized path\n * @public\n *\n * @example\n * ```typescript\n * import { normalizePath } from '@kumix/storage/helpers';\n *\n * const path1 = normalizePath('folder//subfolder///file.txt');\n * // Returns: \"folder/subfolder/file.txt\"\n *\n * const path2 = normalizePath('\\\\folder\\\\file.txt');\n * // Returns: \"folder/file.txt\"\n *\n * const path3 = normalizePath('/folder/file.txt/');\n * // Returns: \"folder/file.txt\"\n * ```\n */\nexport function normalizePath(path: string): string {\n return path\n .replace(/\\\\/g, \"/\") // Convert backslashes to forward slashes\n .replace(/\\/+/g, \"/\") // Replace multiple slashes with single slash\n .replace(/^\\/|\\/$/g, \"\"); // Remove leading and trailing slashes\n}\n\n/**\n * Join path segments safely with proper separators\n * @param segments Path segments to join\n * @returns Joined path\n * @public\n *\n * @example\n * ```typescript\n * import { joinPath } from '@kumix/storage/helpers';\n *\n * const path1 = joinPath('folder', 'subfolder', 'file.txt');\n * // Returns: \"folder/subfolder/file.txt\"\n *\n * const path2 = joinPath('/folder/', '/subfolder/', '/file.txt/');\n * // Returns: \"folder/subfolder/file.txt\"\n *\n * const path3 = joinPath('', 'folder', '', 'file.txt');\n * // Returns: \"folder/file.txt\"\n * ```\n */\nexport function joinPath(...segments: string[]): string {\n return segments\n .filter((segment) => segment && segment.trim() !== \"\")\n .map((segment) => segment.replace(/^\\/+|\\/+$/g, \"\"))\n .filter((segment) => segment !== \"\")\n .join(\"/\");\n}\n\n/**\n * Get parent directory path from a file key\n * @param key File key or path\n * @returns Parent directory path\n * @public\n *\n * @example\n * ```typescript\n * import { getParentPath } from '@kumix/storage/helpers';\n *\n * const parent1 = getParentPath('folder/subfolder/file.txt');\n * // Returns: \"folder/subfolder\"\n *\n * const parent2 = getParentPath('file.txt');\n * // Returns: \"\"\n *\n * const parent3 = getParentPath('folder/file.txt');\n * // Returns: \"folder\"\n * ```\n */\nexport function getParentPath(key: string): string {\n const normalizedKey = normalizePath(key);\n const lastSlashIndex = normalizedKey.lastIndexOf(\"/\");\n return lastSlashIndex === -1 ? \"\" : normalizedKey.substring(0, lastSlashIndex);\n}\n\n/**\n * Get filename from a file key or path\n * @param key File key or path\n * @returns Filename without path\n * @public\n *\n * @example\n * ```typescript\n * import { getFileName } from '@kumix/storage/helpers';\n *\n * const name1 = getFileName('folder/subfolder/document.pdf');\n * // Returns: \"document.pdf\"\n *\n * const name2 = getFileName('file.txt');\n * // Returns: \"file.txt\"\n *\n * const name3 = getFileName('folder/subfolder/');\n * // Returns: \"\"\n * ```\n */\nexport function getFileName(key: string): string {\n const normalizedKey = normalizePath(key);\n const lastSlashIndex = normalizedKey.lastIndexOf(\"/\");\n return lastSlashIndex === -1 ? normalizedKey : normalizedKey.substring(lastSlashIndex + 1);\n}\n\n/**\n * Convert base64 string to Buffer\n * @param base64 Base64 encoded string\n * @returns Buffer containing the decoded data\n * @public\n *\n * @example\n * ```typescript\n * import { base64ToBuffer } from '@kumix/storage/helpers';\n *\n * const base64 = 'SGVsbG8gV29ybGQ='; // \"Hello World\" in base64\n * const buffer = base64ToBuffer(base64);\n * console.log(buffer.toString()); // \"Hello World\"\n *\n * // For file uploads from base64 data\n * const imageBase64 = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD...';\n * const cleanBase64 = imageBase64.split(',')[1]; // Remove data URL prefix\n * const imageBuffer = base64ToBuffer(cleanBase64);\n * await storage.uploadFile('images/photo.jpg', imageBuffer);\n * ```\n */\nexport function base64ToBuffer(base64: string): Uint8Array {\n const cleanBase64 = base64.includes(\",\") ? base64.split(\",\")[1] : base64;\n\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(cleanBase64, \"base64\");\n }\n\n const binary = atob(cleanBase64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\n/**\n * Convert Buffer to base64 string\n * @param buffer Buffer to encode\n * @param includeDataUrl Whether to include data URL prefix\n * @param mimeType MIME type for data URL (required if includeDataUrl is true)\n * @returns Base64 encoded string\n * @public\n *\n * @example\n * ```typescript\n * import { bufferToBase64 } from '@kumix/storage/helpers';\n *\n * const buffer = Buffer.from('Hello World');\n * const base64 = bufferToBase64(buffer);\n * // Returns: \"SGVsbG8gV29ybGQ=\"\n *\n * // With data URL for images\n * const imageBuffer = await storage.downloadFile('images/photo.jpg');\n * const dataUrl = bufferToBase64(imageBuffer.content!, true, 'image/jpeg');\n * // Returns: \"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD...\"\n *\n * // Use in HTML img tag\n * document.querySelector('img').src = dataUrl;\n * ```\n */\nexport function bufferToBase64(\n buffer: Uint8Array,\n includeDataUrl: boolean = false,\n mimeType?: string,\n): string {\n let base64: string;\n\n if (typeof Buffer !== \"undefined\" && buffer instanceof Buffer) {\n base64 = buffer.toString(\"base64\");\n } else {\n let binary = \"\";\n for (let i = 0; i < buffer.length; i++) {\n binary += String.fromCharCode(buffer[i]);\n }\n base64 = btoa(binary);\n }\n\n if (includeDataUrl) {\n if (!mimeType) {\n throw new Error(\"MIME type is required when includeDataUrl is true\");\n }\n return `data:${mimeType};base64,${base64}`;\n }\n\n return base64;\n}\n\n/**\n * Generate file hash/checksum for integrity verification\n * @param content File content as Buffer or string\n * @param algorithm Hash algorithm to use\n * @returns Hash string\n * @public\n *\n * @example\n * ```typescript\n * import { generateFileHash } from '@kumix/storage/helpers';\n *\n * const content = Buffer.from('Hello World');\n *\n * const md5Hash = generateFileHash(content, 'md5');\n * // Returns: \"b10a8db164e0754105b7a99be72e3fe5\"\n *\n * const sha256Hash = generateFileHash(content, 'sha256');\n * // Returns: \"a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e\"\n *\n * // For file integrity checking\n * const fileBuffer = await storage.downloadFile('document.pdf');\n * const currentHash = generateFileHash(fileBuffer.content!, 'sha256');\n * const expectedHash = 'a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e';\n *\n * if (currentHash === expectedHash) {\n * console.log('File integrity verified');\n * } else {\n * console.log('File may be corrupted');\n * }\n * ```\n */\nexport async function generateFileHash(\n content: Uint8Array | string,\n algorithm: \"md5\" | \"sha1\" | \"sha256\" = \"md5\",\n): Promise<string> {\n if (algorithm !== \"md5\" && typeof crypto !== \"undefined\" && crypto.subtle) {\n const rawData = typeof content === \"string\" ? new TextEncoder().encode(content) : content;\n const hashBuffer = await crypto.subtle.digest(\n algorithm === \"sha1\" ? \"SHA-1\" : \"SHA-256\",\n // biome-ignore lint/suspicious/noExplicitAny: <>\n rawData as any,\n );\n return Array.from(new Uint8Array(hashBuffer))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n }\n\n const nodeCrypto = await import(\"node:crypto\");\n const hash = nodeCrypto.createHash(algorithm);\n // biome-ignore lint/suspicious/noExplicitAny: <>\n hash.update(content as any);\n return hash.digest(\"hex\");\n}\n\n/**\n * Generate multiple unique keys at once for batch operations\n * @param fileNames Array of file names\n * @param prefix Optional prefix for all keys\n * @returns Array of unique keys\n * @public\n *\n * @example\n * ```typescript\n * import { generateBatchKeys } from '@kumix/storage/helpers';\n *\n * const fileNames = ['document1.pdf', 'document2.pdf', 'image.jpg'];\n * const keys = generateBatchKeys(fileNames, 'uploads');\n * // Returns: [\n * // \"uploads/document1-1703123456789-abc123.pdf\",\n * // \"uploads/document2-1703123456790-def456.pdf\",\n * // \"uploads/image-1703123456791-ghi789.jpg\"\n * // ]\n *\n * // Use for batch uploads\n * const files = [file1, file2, file3];\n * const keys = generateBatchKeys(files.map(f => f.name), 'batch-upload');\n *\n * for (let i = 0; i < files.length; i++) {\n * await storage.uploadFile(keys[i], files[i]);\n * }\n * ```\n */\nexport function generateBatchKeys(fileNames: string[], prefix?: string): string[] {\n return fileNames.map((fileName) => generateFileKey(fileName, prefix));\n}\n\n/**\n * Validate multiple files at once with consistent options\n * @param files Array of File objects to validate\n * @param options Validation options\n * @returns Array of validation results\n * @public\n *\n * @example\n * ```typescript\n * import { validateBatchFiles } from '@kumix/storage/helpers';\n *\n * const files = [file1, file2, file3]; // File objects from input\n * const options = {\n * maxSize: 5 * 1024 * 1024, // 5MB\n * allowedTypes: ['image/*', 'application/pdf'],\n * maxFiles: 10\n * };\n *\n * const results = validateBatchFiles(files, options);\n * // Returns: [\n * // { valid: true, fileName: \"image.jpg\" },\n * // { valid: false, fileName: \"large.pdf\", errors: [\"File size exceeds limit\"] },\n * // { valid: true, fileName: \"document.pdf\" }\n * // ]\n *\n * const validFiles = files.filter((_, index) => results[index].valid);\n * const invalidFiles = files.filter((_, index) => !results[index].valid);\n * ```\n */\nexport function validateBatchFiles(\n files: File[],\n options: {\n maxSize?: number;\n allowedTypes?: string[];\n maxFiles?: number;\n },\n): Array<{ valid: boolean; fileName: string; errors?: string[] }> {\n const results: Array<{\n valid: boolean;\n fileName: string;\n errors?: string[];\n }> = [];\n\n // Check max files limit\n if (options.maxFiles && files.length > options.maxFiles) {\n return files.map((file) => ({\n valid: false,\n fileName: file.name,\n errors: [`Maximum ${options.maxFiles} files allowed, but ${files.length} files provided`],\n }));\n }\n\n for (const file of files) {\n const errors: string[] = [];\n\n // Validate file size\n if (options.maxSize) {\n const sizeResult = validateFileSize(file.size, options.maxSize);\n if (!sizeResult.valid && sizeResult.error) {\n errors.push(sizeResult.error);\n }\n }\n\n // Validate file type\n if (options.allowedTypes) {\n const typeResult = validateFileType(file.name, options.allowedTypes);\n if (!typeResult.valid && typeResult.error) {\n errors.push(typeResult.error);\n }\n }\n\n results.push({\n valid: errors.length === 0,\n fileName: file.name,\n errors: errors.length > 0 ? errors : undefined,\n });\n }\n\n return results;\n}\n\n/**\n * Detect file type from content using magic numbers/file signatures\n * @param buffer File content buffer\n * @returns Detected MIME type or 'application/octet-stream' if unknown\n * @public\n *\n * @example\n * ```typescript\n * import { detectFileTypeFromContent } from '@kumix/storage/helpers';\n *\n * // Read file content\n * const fileBuffer = await storage.downloadFile('unknown-file');\n * const detectedType = detectFileTypeFromContent(fileBuffer.content!);\n *\n * console.log(detectedType);\n * // Returns: \"image/jpeg\" for JPEG files\n * // Returns: \"application/pdf\" for PDF files\n * // Returns: \"image/png\" for PNG files\n * // Returns: \"application/octet-stream\" for unknown types\n *\n * // Verify file type matches extension\n * const fileName = 'document.pdf';\n * const expectedType = getMimeType(fileName);\n * const actualType = detectFileTypeFromContent(fileBuffer.content!);\n *\n * if (expectedType !== actualType) {\n * console.warn('File extension does not match content type');\n * }\n * ```\n */\nexport function detectFileTypeFromContent(buffer: Uint8Array): string {\n if (buffer.length === 0) {\n return \"application/octet-stream\";\n }\n\n // Check magic numbers/file signatures\n const header = buffer.subarray(0, 16);\n\n // PDF\n if (header.subarray(0, 4).reduce((s, b) => s + String.fromCharCode(b), \"\") === \"%PDF\") {\n return \"application/pdf\";\n }\n\n // JPEG\n if (header[0] === 0xff && header[1] === 0xd8 && header[2] === 0xff) {\n return \"image/jpeg\";\n }\n\n // PNG\n const pngSig = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);\n if (header.length >= 8 && header.slice(0, 8).every((b, i) => b === pngSig[i])) {\n return \"image/png\";\n }\n\n // GIF\n const gifHead = header.subarray(0, 6).reduce((s, b) => s + String.fromCharCode(b), \"\");\n if (gifHead === \"GIF87a\" || gifHead === \"GIF89a\") {\n return \"image/gif\";\n }\n\n // WebP\n if (\n header.subarray(0, 4).reduce((s, b) => s + String.fromCharCode(b), \"\") === \"RIFF\" &&\n header.subarray(8, 12).reduce((s, b) => s + String.fromCharCode(b), \"\") === \"WEBP\"\n ) {\n return \"image/webp\";\n }\n\n // ZIP (includes DOCX, XLSX, etc.)\n if (header[0] === 0x50 && header[1] === 0x4b && (header[2] === 0x03 || header[2] === 0x05)) {\n const zipContent = new TextDecoder().decode(buffer.slice(0, Math.min(buffer.length, 1000)));\n if (zipContent.includes(\"word/\"))\n return \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\";\n if (zipContent.includes(\"xl/\"))\n return \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\";\n if (zipContent.includes(\"ppt/\"))\n return \"application/vnd.openxmlformats-officedocument.presentationml.presentation\";\n return \"application/zip\";\n }\n\n // MP4\n if (new TextDecoder().decode(header.subarray(4, 8)) === \"ftyp\") {\n return \"video/mp4\";\n }\n\n // MP3\n if (\n (header[0] === 0xff && (header[1] & 0xe0) === 0xe0) ||\n new TextDecoder().decode(header.subarray(0, 3)) === \"ID3\"\n ) {\n return \"audio/mpeg\";\n }\n\n // Text files (check if all bytes are printable ASCII or UTF-8)\n let isText = true;\n const sampleSize = Math.min(buffer.length, 512);\n for (let i = 0; i < sampleSize; i++) {\n const byte = buffer[i];\n if (byte === 0 || (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13)) {\n isText = false;\n break;\n }\n }\n\n if (isText) {\n return \"text/plain\";\n }\n\n return \"application/octet-stream\";\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { A as DownloadOptions, B as PresignedUrlResult, C as RenameFolderResult, D as CopyResult, E as CopyOptions, F as ListOptions, G as FolderInfo, H as UploadResult, I as ListResult, J as StorageConfig, K as S3Config, L as MoveOptions, M as DuplicateOptions, N as DuplicateResult, O as DeleteOptions, P as ExistsResult, R as MoveResult, S as RenameFolderOptions, T as BatchDeleteResult, U as CloudinaryConfig, V as UploadOptions, W as FileInfo, Y as StorageProvider, _ as DeleteFolderOptions, a as hasStorageConfig, b as ListFoldersOptions, c as loadS3Config, d as validateS3EnvVars, f as StorageInterface, g as CreateFolderResult, h as CreateFolderOptions, i as getStorageProvider, j as DownloadResult, k as DeleteResult, l as loadStorageConfig, m as CopyFolderResult, n as EnvRecord, o as isStorageConfigured, p as CopyFolderOptions, q as S3ProviderType, r as getStorageEnvVars, s as loadCloudinaryConfig, t as ENV_VARS, u as validateCloudinaryEnvVars, v as DeleteFolderResult, w as BatchDeleteOptions, x as ListFoldersResult, y as FolderExistsResult, z as PresignedUrlOptions } from "./config-J6AdDh_c.js";
2
+ export { type BatchDeleteOptions, type BatchDeleteResult, type CloudinaryConfig, type CopyFolderOptions, type CopyFolderResult, type CopyOptions, type CopyResult, type CreateFolderOptions, type CreateFolderResult, type DeleteFolderOptions, type DeleteFolderResult, type DeleteOptions, type DeleteResult, type DownloadOptions, type DownloadResult, type DuplicateOptions, type DuplicateResult, ENV_VARS, EnvRecord, type ExistsResult, type FileInfo, type FolderExistsResult, type FolderInfo, type ListFoldersOptions, type ListFoldersResult, type ListOptions, type ListResult, type MoveOptions, type MoveResult, type PresignedUrlOptions, type PresignedUrlResult, type RenameFolderOptions, type RenameFolderResult, type S3Config, type S3ProviderType, type StorageConfig, type StorageInterface, type StorageProvider, type UploadOptions, type UploadResult, getStorageEnvVars, getStorageProvider, hasStorageConfig, isStorageConfigured, loadCloudinaryConfig, loadS3Config, loadStorageConfig, validateCloudinaryEnvVars, validateS3EnvVars };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import { a as isStorageConfigured, c as loadStorageConfig, i as hasStorageConfig, l as validateCloudinaryEnvVars, n as getStorageEnvVars, o as loadCloudinaryConfig, r as getStorageProvider, s as loadS3Config, t as ENV_VARS, u as validateS3EnvVars } from "./config-t-NVJICl.js";
2
+ export { ENV_VARS, getStorageEnvVars, getStorageProvider, hasStorageConfig, isStorageConfigured, loadCloudinaryConfig, loadS3Config, loadStorageConfig, validateCloudinaryEnvVars, validateS3EnvVars };
package/dist/s3.d.ts ADDED
@@ -0,0 +1,109 @@
1
+ import { A as DownloadOptions, B as PresignedUrlResult, C as RenameFolderResult, D as CopyResult, E as CopyOptions, F as ListOptions, H as UploadResult, I as ListResult, K as S3Config, L as MoveOptions, M as DuplicateOptions, N as DuplicateResult, O as DeleteOptions, P as ExistsResult, R as MoveResult, S as RenameFolderOptions, T as BatchDeleteResult, V as UploadOptions, _ as DeleteFolderOptions, b as ListFoldersOptions, f as StorageInterface, g as CreateFolderResult, h as CreateFolderOptions, j as DownloadResult, k as DeleteResult, m as CopyFolderResult, n as EnvRecord, p as CopyFolderOptions, v as DeleteFolderResult, w as BatchDeleteOptions, x as ListFoldersResult, y as FolderExistsResult, z as PresignedUrlOptions } from "./config-J6AdDh_c.js";
2
+
3
+ //#region src/services/s3.d.ts
4
+ /**
5
+ * S3 storage service
6
+ * High-level service for S3-compatible storage operations with convenience methods
7
+ * @public
8
+ */
9
+ declare class S3Service implements StorageInterface {
10
+ private provider;
11
+ private config;
12
+ constructor();
13
+ constructor(config: S3Config);
14
+ private createProvider;
15
+ getConfig(): S3Config;
16
+ getBucket(): string;
17
+ getRegion(): string;
18
+ getProvider(): string;
19
+ getPublicUrl(key: string): string;
20
+ upload(options: UploadOptions): Promise<UploadResult>;
21
+ download(options: DownloadOptions): Promise<DownloadResult>;
22
+ delete(options: DeleteOptions): Promise<DeleteResult>;
23
+ batchDelete(options: BatchDeleteOptions): Promise<BatchDeleteResult>;
24
+ list(options?: ListOptions): Promise<ListResult>;
25
+ exists(key: string): Promise<ExistsResult>;
26
+ copy(options: CopyOptions): Promise<CopyResult>;
27
+ move(options: MoveOptions): Promise<MoveResult>;
28
+ duplicate(options: DuplicateOptions): Promise<DuplicateResult>;
29
+ getPresignedUrl(options: PresignedUrlOptions): Promise<PresignedUrlResult>;
30
+ createFolder(options: CreateFolderOptions): Promise<CreateFolderResult>;
31
+ deleteFolder(options: DeleteFolderOptions): Promise<DeleteFolderResult>;
32
+ listFolders(options?: ListFoldersOptions): Promise<ListFoldersResult>;
33
+ folderExists(path: string): Promise<FolderExistsResult>;
34
+ renameFolder(options: RenameFolderOptions): Promise<RenameFolderResult>;
35
+ copyFolder(options: CopyFolderOptions): Promise<CopyFolderResult>;
36
+ uploadFile(key: string, file: Buffer | Uint8Array | string, options?: Partial<UploadOptions>): Promise<UploadResult>;
37
+ downloadFile(key: string): Promise<DownloadResult>;
38
+ deleteFile(key: string): Promise<DeleteResult>;
39
+ deleteFiles(keys: string[]): Promise<BatchDeleteResult>;
40
+ listFiles(prefix?: string, maxKeys?: number): Promise<ListResult>;
41
+ fileExists(key: string): Promise<boolean>;
42
+ copyFile(sourceKey: string, destinationKey: string, options?: Partial<CopyOptions>): Promise<CopyResult>;
43
+ moveFile(sourceKey: string, destinationKey: string, options?: Partial<MoveOptions>): Promise<MoveResult>;
44
+ duplicateFile(sourceKey: string, destinationKey: string, options?: Partial<DuplicateOptions>): Promise<DuplicateResult>;
45
+ renameFile(sourceKey: string, newKey: string, options?: Partial<MoveOptions>): Promise<MoveResult>;
46
+ getDownloadUrl(key: string, expiresIn?: number): Promise<PresignedUrlResult>;
47
+ getUploadUrl(key: string, contentType?: string, expiresIn?: number): Promise<PresignedUrlResult>;
48
+ createFolderPath(path: string): Promise<CreateFolderResult>;
49
+ deleteFolderPath(path: string, recursive?: boolean): Promise<DeleteFolderResult>;
50
+ folderPathExists(path: string): Promise<boolean>;
51
+ renameFolderPath(oldPath: string, newPath: string): Promise<RenameFolderResult>;
52
+ copyFolderPath(sourcePath: string, destinationPath: string, recursive?: boolean): Promise<CopyFolderResult>;
53
+ }
54
+ //#endregion
55
+ //#region src/s3.d.ts
56
+ /**
57
+ * Create S3 service using environment variables or manual configuration
58
+ * @param config Optional S3 configuration. If not provided, loads from environment variables
59
+ * @param env - Optional environment record
60
+ * @returns S3Service instance
61
+ * @throws Error if configuration is invalid or environment variables are missing
62
+ * @public
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * import { createS3 } from '@kumix/storage/s3';
67
+ *
68
+ * // Using environment variables
69
+ * // Set: KUMIX_S3_PROVIDER=aws, KUMIX_S3_REGION=us-east-1, etc.
70
+ * const s3FromEnv = createS3();
71
+ * await s3FromEnv.uploadFile('test.txt', 'Hello World');
72
+ *
73
+ * // Using manual configuration
74
+ * const s3Manual = createS3({
75
+ * provider: 'aws',
76
+ * region: 'us-east-1',
77
+ * bucket: 'my-bucket',
78
+ * accessKeyId: 'AKIA...',
79
+ * secretAccessKey: 'secret...'
80
+ * });
81
+ *
82
+ * // Cloudflare R2
83
+ * const r2 = createS3({
84
+ * provider: 'cloudflare-r2',
85
+ * region: 'auto',
86
+ * bucket: 'my-r2-bucket',
87
+ * accessKeyId: 'your-r2-key',
88
+ * secretAccessKey: 'your-r2-secret',
89
+ * endpoint: 'https://account-id.r2.cloudflarestorage.com'
90
+ * });
91
+ *
92
+ * // MinIO
93
+ * const minio = createS3({
94
+ * provider: 'minio',
95
+ * region: 'us-east-1',
96
+ * bucket: 'my-minio-bucket',
97
+ * accessKeyId: 'minioadmin',
98
+ * secretAccessKey: 'minioadmin',
99
+ * endpoint: 'http://localhost:9000',
100
+ * forcePathStyle: true
101
+ * });
102
+ * ```
103
+ */
104
+ declare function createS3(): S3Service;
105
+ declare function createS3(config: S3Config): S3Service;
106
+ declare function createS3(config: S3Config, env: EnvRecord): S3Service;
107
+ //#endregion
108
+ export { type EnvRecord, S3Service, createS3 };
109
+ //# sourceMappingURL=s3.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3.d.ts","names":[],"sources":["../src/services/s3.ts","../src/s3.ts"],"mappings":"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"}