import { I as IStorageAdapter, U as UploadOptions, d as UploadResult, e as StorageAdapterInfo, b as StoragePluginConfig, S as StoragePlugin, B as BulkDeleteResult } from '../_dts-chunks/storage.d-CEowrt6p.d.ts'; export { f as ClientUploadData, C as CollectionStorageConfig, a as CollectionStorageMap, F as FileMetadata, g as ImageMetadata, c as ImageProcessor, M as MediaStorage, h as MediaStorageConfig, P as ProcessedImage, i as StorageType, j as getImageProcessor, k as getMediaStorage, l as initializeMediaStorage, r as resetImageProcessor, m as resetMediaStorage } from '../_dts-chunks/storage.d-CEowrt6p.d.ts'; import { I as ImageSizeVariant } from '../_dts-chunks/media.d-DtIw8UQM.d.ts'; import 'zod'; import '../_dts-chunks/error-codes.d-CbwkO1ux.d.ts'; /** * Base Storage Adapter * * BaseStorageAdapter abstract class with common functionality for storage * adapters. Concrete adapters extend this class to inherit auto-detected * capabilities, sanitizeFilename(), generateKey(), etc. * * For the IStorageAdapter interface itself, import from ../types directly. */ /** * Abstract base class for storage adapters. * * Provides common functionality and helper methods that all storage adapters * can use. Concrete adapters should extend this class to inherit: * - Default getInfo() implementation with auto-detected capabilities * - sanitizeFilename() helper for secure filename handling * - generateKey() helper for unique storage key generation * * @example * ```typescript * class MyStorageAdapter extends BaseStorageAdapter { * async upload(buffer: Buffer, options: UploadOptions): Promise { * const key = this.generateKey(options.filename, options.folder); * const sanitized = this.sanitizeFilename(options.filename); * // ... upload logic * } * * async delete(filePath: string): Promise { ... } * async exists(filePath: string): Promise { ... } * getPublicUrl(filePath: string): string { ... } * getType(): string { return 'my-storage'; } * } * ``` */ declare abstract class BaseStorageAdapter implements IStorageAdapter { /** * Upload file buffer to storage. * Must be implemented by concrete adapters. */ abstract upload(buffer: Buffer, options: UploadOptions): Promise; /** * Delete file from storage. * Must be implemented by concrete adapters. */ abstract delete(filePath: string): Promise; /** * Check if file exists in storage. * Must be implemented by concrete adapters. */ abstract exists(filePath: string): Promise; /** * Get public URL for file. * Must be implemented by concrete adapters. */ abstract getPublicUrl(filePath: string): string; /** * Get storage type identifier. * Must be implemented by concrete adapters. */ abstract getType(): string; /** * Get adapter info including capabilities. * * Default implementation that auto-detects capabilities by checking * if getSignedUrl and getPresignedUploadUrl methods are implemented. * Override in subclasses for more accurate capability reporting. * * @returns Adapter info with type, name, and capability flags */ getInfo(): StorageAdapterInfo; /** * Sanitize filename to prevent directory traversal and storage issues. * * Security measures: * - Remove path separators (/, \) * - Keep only basename (no directories) * - Replace problematic characters with hyphens * - Preserve alphanumeric, dots, underscores, hyphens * * @param filename - Original filename to sanitize * @returns Sanitized filename safe for storage * * @example * ```typescript * this.sanitizeFilename('../../../etc/passwd') // 'passwd' * this.sanitizeFilename('my file (1).jpg') // 'my-file--1-.jpg' * this.sanitizeFilename('photo.jpg') // 'photo.jpg' * ``` */ protected sanitizeFilename(filename: string): string; /** * Generate a unique storage key with date-based prefix. * * Creates keys in format: {folder}/{year}/{month}/{uuid}-{sanitized-filename} * This provides: * - Unique keys via UUID to prevent collisions * - Date-based organization for easier management * - Readable filenames for debugging * * @param filename - Original filename (will be sanitized) * @param folder - Optional folder/prefix for organizing uploads * @returns Generated storage key * * @example * ```typescript * this.generateKey('photo.jpg') * // '2026/01/abc-123-...-photo.jpg' * * this.generateKey('doc.pdf', 'documents') * // 'documents/2026/01/abc-123-...-doc.pdf' * ``` */ protected generateKey(filename: string, folder?: string): string; } /** * Local Disk Storage Types * * Configuration for the local filesystem storage adapter. * Used as the default storage for development when no cloud env vars are set. */ /** * Local disk storage adapter configuration. * * @example Default (zero-config) * ```typescript * localStorage({ collections: { media: true } }) * ``` * * @example Custom paths * ```typescript * localStorage({ * basePath: './public/media', * baseUrl: '/media', * collections: { media: true } * }) * ``` */ interface LocalStorageConfig extends StoragePluginConfig { /** * Directory to store uploaded files. * Relative to the project root or absolute path. * * @default './public/uploads' */ basePath?: string; /** * Base URL prefix for serving files via Next.js static file serving. * Files in `public/uploads/` are served at `/uploads/...` by Next.js. * * @default '/uploads' */ baseUrl?: string; } /** * Local Disk Storage Plugin * * Factory function that creates a storage plugin for local filesystem storage. * Used as the default storage for development when no cloud env vars are set. * * @example Zero-config (auto-detected, no explicit config needed) * ```typescript * // In nextly.config.ts — local storage is used automatically when * // no cloud env vars (BLOB_READ_WRITE_TOKEN, S3_BUCKET, etc.) are set. * export default defineConfig({ * storage: await getStorageFromEnv() * }) * ``` * * @example Explicit configuration * ```typescript * import { localStorage } from 'nextly/storage' * * export default defineConfig({ * storage: [ * localStorage({ * basePath: './public/uploads', * baseUrl: '/uploads', * collections: { media: true } * }) * ] * }) * ``` */ /** * Create a local disk storage plugin for Nextly. * * Files are stored on the local filesystem and served via Next.js * static file serving. Best for development — use cloud storage * (S3, Vercel Blob, Uploadthing) for production. * * @param config - Local storage configuration * @returns A StoragePlugin that MediaStorage can register */ declare function localStorage(config: LocalStorageConfig): StoragePlugin; /** * Local Disk Storage Adapter * * Stores files on the local filesystem. Used as the default storage adapter * for development when no cloud storage env vars are detected. * * Files are stored in `./public/uploads/` by default and served via * Next.js static file serving at `/uploads/...`. * * @example * ```typescript * const adapter = new LocalStorageAdapter({ * basePath: './public/uploads', * baseUrl: '/uploads', * }); * * const result = await adapter.upload(buffer, { * filename: 'photo.jpg', * mimeType: 'image/jpeg', * }); * // result.url = '/uploads/2026/04/abc-photo.jpg' * // result.path = '2026/04/abc-photo.jpg' * // File on disk: ./public/uploads/2026/04/abc-photo.jpg * ``` */ interface LocalAdapterConfig { /** Directory to store files (default: ./public/uploads) */ basePath: string; /** URL prefix for serving files (default: /uploads) */ baseUrl: string; } declare class LocalStorageAdapter extends BaseStorageAdapter { private readonly basePath; private readonly baseUrl; constructor(config: LocalAdapterConfig); /** * Upload file to local disk. * Creates directories as needed and writes the file buffer. */ upload(buffer: Buffer, options: UploadOptions): Promise; /** * Delete file from local disk. * Silently succeeds if the file doesn't exist. */ delete(filePath: string): Promise; /** * Bulk delete files from local disk. * Uses parallel unlinks with Promise.allSettled for best performance. */ bulkDelete(filePaths: string[]): Promise; /** * Check if file exists on local disk. */ exists(filePath: string): Promise; /** * Get public URL for a file. * Returns baseUrl + relative path for Next.js static file serving. */ getPublicUrl(filePath: string): string; /** * Get storage type identifier. */ getType(): string; /** * Read file contents from local disk. * Returns the file buffer, or null if file not found. */ read(filePath: string): Promise; /** * Resolve a relative file path to an absolute path within basePath. * Throws if the resolved path would escape basePath (path traversal attack). */ private resolveAndValidate; /** * Auto-add the uploads directory to .gitignore on first upload. * Prevents accidentally committing uploaded files to git. */ private ensureGitignore; } /** * Image Size Generation Pipeline * * Generates named image size variants for uploaded images. * Uses Sharp (via ImageProcessor) for resizing and format conversion. * Each variant is uploaded to the same storage adapter as the original. */ /** * Configuration for a single image size. * Matches the image_sizes DB table structure. */ interface ImageSizeConfig { name: string; width?: number | null; height?: number | null; fit: "cover" | "inside" | "contain" | "fill"; quality: number; format: "auto" | "webp" | "jpeg" | "png" | "avif"; } /** * Options for generating image sizes. */ interface GenerateImageSizesOptions { /** Focal point X (0-100, percentage from left) */ focalX?: number | null; /** Focal point Y (0-100, percentage from top) */ focalY?: number | null; /** Collection slug for storage routing */ collection?: string; /** Storage folder/prefix */ folder?: string; } /** * Function signature for uploading a buffer to storage. * Passed in to decouple from MediaStorage singleton. */ type UploadFn = (buffer: Buffer, options: { filename: string; mimeType: string; folder?: string; collection?: string; }) => Promise; /** * Generate all configured image size variants for an uploaded image. * * For each size config: * 1. Resize/crop the original buffer using ImageProcessor * 2. Upload the variant via the provided upload function * 3. Collect metadata (url, path, width, height, filesize, mimeType, filename) * * @param originalBuffer - The original image file buffer * @param originalFilename - The original filename (used to derive variant filenames) * @param sizes - Array of image size configurations * @param uploadFn - Function to upload each variant to storage * @param options - Focal point and routing options * @returns Map of size name → variant metadata */ declare function generateImageSizes(originalBuffer: Buffer, originalFilename: string, sizes: ImageSizeConfig[], uploadFn: UploadFn, options?: GenerateImageSizesOptions): Promise>; /** * Delete all size variants for a media item from storage. * * @param sizes - The sizes JSONB object from the media record * @param deleteFn - Function to delete a file by its storage path */ declare function deleteImageSizes(sizes: Record | null | undefined, deleteFn: (path: string) => Promise): Promise; /** * Retry Utility for Storage Operations * * Provides exponential backoff retry logic for transient failures. * Used by storage adapters for upload, delete, and other operations. * * Features: * - Exponential backoff with jitter * - Configurable max attempts * - Custom retry condition * - Timeout support * * @example * ```typescript * const result = await withRetry( * () => storage.upload(buffer, options), * { * maxAttempts: 3, * baseDelayMs: 1000, * shouldRetry: (error) => isTransientError(error), * } * ); * ``` */ interface RetryOptions { /** Maximum number of attempts (default: 3) */ maxAttempts?: number; /** Base delay in milliseconds (default: 1000) */ baseDelayMs?: number; /** Maximum delay in milliseconds (default: 30000) */ maxDelayMs?: number; /** Exponential backoff factor (default: 2) */ backoffFactor?: number; /** Add random jitter to delay (default: true) */ jitter?: boolean; /** Custom function to determine if error is retryable */ shouldRetry?: (error: unknown, attempt: number) => boolean; /** Callback called before each retry attempt */ onRetry?: (error: unknown, attempt: number, delayMs: number) => void; } /** * Check if an error is a transient error that should be retried. * * Transient errors include: * - Network timeouts * - Connection resets * - Rate limiting (429) * - Server errors (5xx) * - DNS resolution failures */ declare function isTransientError(error: unknown): boolean; /** * Execute an async function with retry logic. * * Uses exponential backoff with jitter to handle transient failures. * * @param fn - Async function to execute * @param options - Retry configuration options * @returns Result of the function * @throws Last error if all retries fail * * @example * ```typescript * // Basic usage * const result = await withRetry(() => uploadFile(buffer)); * * // With custom options * const result = await withRetry( * () => uploadFile(buffer), * { * maxAttempts: 5, * baseDelayMs: 500, * onRetry: (err, attempt) => console.log(`Retry ${attempt}:`, err.message), * } * ); * ``` */ declare function withRetry(fn: () => Promise, options?: RetryOptions): Promise; /** * Create a retryable version of an async function. * * Useful for wrapping multiple functions with the same retry config. * * @example * ```typescript * const retryableUpload = createRetryable( * (buffer: Buffer, opts: UploadOptions) => storage.upload(buffer, opts), * { maxAttempts: 3 } * ); * * await retryableUpload(buffer, { filename: 'test.jpg', mimeType: 'image/jpeg' }); * ``` */ declare function createRetryable(fn: (...args: TArgs) => Promise, options?: RetryOptions): (...args: TArgs) => Promise; /** * SVG Security Utilities * * Provides CSP header constants and helpers for securing SVG file responses. * SVG files can contain embedded `