UNPKG

pushduck

Version:

The fastest way to add file uploads to any web application. Enterprise security, edge-ready. Works with 16+ frameworks and 5+ storage providers. No heavy AWS SDK required.

873 lines (866 loc) 29.3 kB
import { AwsClient } from "aws4fetch"; import { NextRequest } from "next/server"; //#region src/core/providers/providers.d.ts /** * Cloud Storage Providers System * * This provides a clean way to configure different cloud storage providers * with environment-based configuration and type-safe initialization. * * Supported Providers: * - AWS S3, Cloudflare R2, DigitalOcean Spaces, MinIO * * Future Provider Support: * - Enterprise: Azure Blob, IBM Cloud, Oracle OCI * - Cost-Optimized: Wasabi, Backblaze B2, Storj DCS * - Specialized: Telnyx, Tigris, Cloudian HyperStore */ interface BaseProviderConfig { provider: string; region?: string; bucket: string; acl?: string; customDomain?: string; forcePathStyle?: boolean; } interface AWSProviderConfig extends BaseProviderConfig { provider: "aws"; accessKeyId: string; secretAccessKey: string; region: string; sessionToken?: string; } interface CloudflareR2Config extends BaseProviderConfig { provider: "cloudflare-r2"; accountId: string; accessKeyId: string; secretAccessKey: string; region?: "auto"; endpoint?: string; } interface DigitalOceanSpacesConfig extends BaseProviderConfig { provider: "digitalocean-spaces"; accessKeyId: string; secretAccessKey: string; region: string; endpoint?: string; } interface MinIOConfig extends BaseProviderConfig { provider: "minio"; endpoint: string; accessKeyId: string; secretAccessKey: string; useSSL?: boolean; port?: number; } interface AzureBlobConfig extends BaseProviderConfig { provider: "azure-blob"; accountName: string; accessKeyId: string; secretAccessKey: string; endpoint?: string; } interface IBMCloudConfig extends BaseProviderConfig { provider: "ibm-cloud"; accessKeyId: string; secretAccessKey: string; endpoint: string; serviceInstanceId?: string; } interface OracleOCIConfig extends BaseProviderConfig { provider: "oracle-oci"; accessKeyId: string; secretAccessKey: string; endpoint: string; namespace?: string; } interface WasabiConfig extends BaseProviderConfig { provider: "wasabi"; accessKeyId: string; secretAccessKey: string; endpoint?: string; } interface BackblazeB2Config extends BaseProviderConfig { provider: "backblaze-b2"; accessKeyId: string; secretAccessKey: string; endpoint: string; } interface StorjDCSConfig extends BaseProviderConfig { provider: "storj-dcs"; accessKeyId: string; secretAccessKey: string; endpoint?: string; } interface TelnyxStorageConfig extends BaseProviderConfig { provider: "telnyx-storage"; accessKeyId: string; secretAccessKey: string; endpoint: string; } interface TigrisDataConfig extends BaseProviderConfig { provider: "tigris-data"; accessKeyId: string; secretAccessKey: string; endpoint: string; region?: "auto"; } interface CloudianHyperStoreConfig extends BaseProviderConfig { provider: "cloudian-hyperstore"; accessKeyId: string; secretAccessKey: string; endpoint: string; } interface GoogleCloudStorageConfig extends BaseProviderConfig { provider: "gcs"; projectId: string; keyFilename?: string; credentials?: object; } interface S3CompatibleConfig extends BaseProviderConfig { provider: "s3-compatible"; accessKeyId: string; secretAccessKey: string; endpoint: string; } type ProviderConfig = AWSProviderConfig | CloudflareR2Config | DigitalOceanSpacesConfig | MinIOConfig | AzureBlobConfig | IBMCloudConfig | OracleOCIConfig | WasabiConfig | BackblazeB2Config | StorjDCSConfig | TelnyxStorageConfig | TigrisDataConfig | CloudianHyperStoreConfig | GoogleCloudStorageConfig | S3CompatibleConfig; declare const PROVIDER_SPECS: { readonly aws: { readonly provider: "aws"; readonly configKeys: { readonly region: readonly ["AWS_REGION", "S3_REGION"]; readonly bucket: readonly ["AWS_S3_BUCKET", "S3_BUCKET", "S3_BUCKET_NAME"]; readonly accessKeyId: readonly ["AWS_ACCESS_KEY_ID", "S3_ACCESS_KEY_ID"]; readonly secretAccessKey: readonly ["AWS_SECRET_ACCESS_KEY", "S3_SECRET_ACCESS_KEY"]; readonly sessionToken: readonly ["AWS_SESSION_TOKEN"]; readonly acl: readonly ["S3_ACL"]; readonly customDomain: readonly ["S3_CUSTOM_DOMAIN"]; }; readonly defaults: { readonly region: "us-east-1"; readonly acl: "private"; }; }; readonly cloudflareR2: { readonly provider: "cloudflare-r2"; readonly configKeys: { readonly accountId: readonly ["CLOUDFLARE_ACCOUNT_ID", "R2_ACCOUNT_ID"]; readonly bucket: readonly ["CLOUDFLARE_R2_BUCKET", "R2_BUCKET"]; readonly accessKeyId: readonly ["CLOUDFLARE_R2_ACCESS_KEY_ID", "R2_ACCESS_KEY_ID"]; readonly secretAccessKey: readonly ["CLOUDFLARE_R2_SECRET_ACCESS_KEY", "R2_SECRET_ACCESS_KEY"]; readonly endpoint: readonly ["CLOUDFLARE_R2_ENDPOINT", "R2_ENDPOINT"]; readonly customDomain: readonly ["R2_CUSTOM_DOMAIN"]; readonly acl: readonly []; }; readonly defaults: { readonly region: "auto"; readonly acl: "private"; }; readonly customLogic: (config: any, computed: any) => { endpoint: any; }; }; readonly digitalOceanSpaces: { readonly provider: "digitalocean-spaces"; readonly configKeys: { readonly region: readonly ["DO_SPACES_REGION", "DIGITALOCEAN_SPACES_REGION"]; readonly bucket: readonly ["DO_SPACES_BUCKET", "DIGITALOCEAN_SPACES_BUCKET"]; readonly accessKeyId: readonly ["DO_SPACES_ACCESS_KEY_ID", "DIGITALOCEAN_SPACES_ACCESS_KEY_ID"]; readonly secretAccessKey: readonly ["DO_SPACES_SECRET_ACCESS_KEY", "DIGITALOCEAN_SPACES_SECRET_ACCESS_KEY"]; readonly endpoint: readonly ["DO_SPACES_ENDPOINT", "DIGITALOCEAN_SPACES_ENDPOINT"]; readonly customDomain: readonly ["DO_SPACES_CUSTOM_DOMAIN"]; readonly acl: readonly []; }; readonly defaults: { readonly region: "nyc3"; readonly acl: "private"; }; readonly customLogic: (config: any, computed: any) => { endpoint: any; }; }; readonly minio: { readonly provider: "minio"; readonly configKeys: { readonly endpoint: readonly ["MINIO_ENDPOINT"]; readonly bucket: readonly ["MINIO_BUCKET"]; readonly accessKeyId: readonly ["MINIO_ACCESS_KEY_ID", "MINIO_ACCESS_KEY"]; readonly secretAccessKey: readonly ["MINIO_SECRET_ACCESS_KEY", "MINIO_SECRET_KEY"]; readonly region: readonly ["MINIO_REGION"]; readonly customDomain: readonly ["MINIO_CUSTOM_DOMAIN"]; readonly acl: readonly []; }; readonly defaults: { readonly endpoint: "localhost:9000"; readonly region: "us-east-1"; readonly acl: "private"; }; readonly customLogic: (config: any, computed: any) => { useSSL: any; port: number | undefined; }; }; readonly gcs: { readonly provider: "gcs"; readonly configKeys: { readonly projectId: readonly ["GOOGLE_CLOUD_PROJECT_ID", "GCS_PROJECT_ID"]; readonly bucket: readonly ["GCS_BUCKET", "GOOGLE_CLOUD_STORAGE_BUCKET"]; readonly keyFilename: readonly ["GOOGLE_APPLICATION_CREDENTIALS", "GCS_KEY_FILE"]; readonly region: readonly ["GCS_REGION"]; readonly customDomain: readonly ["GCS_CUSTOM_DOMAIN"]; readonly acl: readonly []; }; readonly defaults: { readonly region: "us-central1"; readonly acl: "private"; }; readonly customLogic: (config: any) => { credentials: any; }; }; readonly s3Compatible: { readonly provider: "s3-compatible"; readonly configKeys: { readonly endpoint: readonly ["S3_ENDPOINT", "S3_COMPATIBLE_ENDPOINT"]; readonly bucket: readonly ["S3_BUCKET", "S3_BUCKET_NAME"]; readonly accessKeyId: readonly ["S3_ACCESS_KEY_ID", "ACCESS_KEY_ID"]; readonly secretAccessKey: readonly ["S3_SECRET_ACCESS_KEY", "SECRET_ACCESS_KEY"]; readonly region: readonly ["S3_REGION", "REGION"]; readonly customDomain: readonly ["S3_CUSTOM_DOMAIN"]; readonly acl: readonly ["S3_ACL"]; }; readonly defaults: { readonly region: "us-east-1"; readonly acl: "private"; readonly forcePathStyle: true; }; }; }; type ProviderSpecsType = typeof PROVIDER_SPECS; type ProviderType = keyof ProviderSpecsType; /** * Maps each provider type to its corresponding configuration interface * This enables type-safe provider configuration in createUploadConfig().provider() */ type ProviderConfigMap = { aws: Partial<Omit<AWSProviderConfig, "provider">>; cloudflareR2: Partial<Omit<CloudflareR2Config, "provider">>; digitalOceanSpaces: Partial<Omit<DigitalOceanSpacesConfig, "provider">>; minio: Partial<Omit<MinIOConfig, "provider">>; gcs: Partial<Omit<GoogleCloudStorageConfig, "provider">>; s3Compatible: Partial<Omit<S3CompatibleConfig, "provider">>; }; /** * Type-safe provider configuration function * Usage: createProvider("aws", { bucket: "my-bucket", region: "us-west-2" }) */ declare function createProvider<T extends ProviderType>(type: T, config?: ProviderConfigMap[T]): ProviderConfig; declare function validateProviderConfig(config: ProviderConfig): { valid: boolean; errors: string[]; }; declare function getProviderEndpoint(config: ProviderConfig): string; //#endregion //#region src/core/schema.d.ts interface S3FileConstraints { maxSize?: string | number; minSize?: string | number; allowedTypes?: string[]; allowedExtensions?: string[]; required?: boolean; } interface S3ArrayConstraints { min?: number; max?: number; length?: number; } interface S3ValidationContext { file: File; fieldName: string; allFiles?: Record<string, File | File[]>; } interface S3ValidationResult { success: boolean; error?: { code: string; message: string; path: string[]; }; data?: any; } interface S3TransformContext<T = any> { file: File; metadata?: Record<string, any>; originalData: T; } declare abstract class S3Schema<TInput = any, TOutput = TInput> { protected _constraints: Record<string, any>; protected _transforms: Array<(ctx: S3TransformContext<TInput>) => Promise<any> | any>; protected _validators: Array<(ctx: S3ValidationContext) => S3ValidationResult | Promise<S3ValidationResult>>; protected _optional: boolean; abstract _type: string; abstract _parse(input: unknown): S3ValidationResult | Promise<S3ValidationResult>; validate(input: unknown, context?: Partial<S3ValidationContext>): Promise<S3ValidationResult>; optional(): S3Schema<TInput, TOutput | undefined>; transform<TNewOutput>(transformer: (ctx: S3TransformContext<TOutput>) => Promise<TNewOutput> | TNewOutput): S3Schema<TInput, TNewOutput>; refine(validator: (ctx: S3ValidationContext) => boolean | Promise<boolean>, message: string): this; protected abstract _clone(): this; } declare class S3FileSchema extends S3Schema<File, File> { protected constraints: S3FileConstraints; _type: "file"; constructor(constraints?: S3FileConstraints); _parse(input: unknown): S3ValidationResult; max(size: string | number): S3FileSchema; min(size: string | number): S3FileSchema; types(allowedTypes: string[]): S3FileSchema; extensions(allowedExtensions: string[]): S3FileSchema; array(constraints?: S3ArrayConstraints): S3ArraySchema<this>; protected _clone(): this; middleware<TMetadata>(middleware: (ctx: { req: any; file: { name: string; size: number; type: string; }; metadata: any; }) => Promise<TMetadata> | TMetadata): S3Route<this, TMetadata>; onUploadStart(hook: (ctx: { file: { name: string; size: number; type: string; }; metadata: any; }) => Promise<void> | void): S3Route<this, any>; onUploadComplete(hook: (ctx: { file: { name: string; size: number; type: string; }; metadata: any; url?: string; key?: string; }) => Promise<void> | void): S3Route<this, any>; onUploadError(hook: (ctx: { file: { name: string; size: number; type: string; }; metadata: any; error: Error; }) => Promise<void> | void): S3Route<this, any>; private _parseSize; private _formatSize; } declare class S3ImageSchema extends S3FileSchema { constructor(constraints?: S3FileConstraints); formats(formats: string[]): S3ImageSchema; max(size: string | number): S3ImageSchema; min(size: string | number): S3ImageSchema; types(allowedTypes: string[]): S3ImageSchema; extensions(allowedExtensions: string[]): S3ImageSchema; array(constraints?: S3ArrayConstraints): S3ArraySchema<this>; protected _clone(): this; } declare class S3ArraySchema<T extends S3Schema> extends S3Schema<File[], File[]> { private elementSchema; private arrayConstraints; _type: "array"; constructor(elementSchema: T, arrayConstraints?: S3ArrayConstraints); _parse(input: unknown): Promise<S3ValidationResult>; min(count: number): S3ArraySchema<T>; max(count: number): S3ArraySchema<T>; length(count: number): S3ArraySchema<T>; protected _clone(): this; } declare class S3ObjectSchema<T extends Record<string, S3Schema>> extends S3Schema<{ [K in keyof T]: T[K] extends S3Schema<any, infer U> ? U : never }, { [K in keyof T]: T[K] extends S3Schema<any, infer U> ? U : never }> { private shape; _type: "object"; constructor(shape: T); _parse(input: unknown): Promise<S3ValidationResult>; protected _clone(): this; } type InferS3Input<T extends S3Schema> = T extends S3Schema<infer I, any> ? I : never; type InferS3Output<T extends S3Schema> = T extends S3Schema<any, infer O> ? O : never; //#endregion //#region src/core/storage/client.d.ts /** * Creates and caches an AWS client instance using aws4fetch */ declare function createS3Client(uploadConfig?: UploadConfig): AwsClient; /** * Resets the AWS client instance (useful for testing) */ declare function resetS3Client(): void; interface PresignedUrlOptions { key: string; contentType?: string; contentLength?: number; expiresIn?: number; metadata?: Record<string, string>; } interface PresignedUrlResult { url: string; key: string; fields?: Record<string, string>; } /** * Generates a presigned URL for downloading/viewing a file from S3 */ interface FileKeyOptions { originalName: string; userId?: string; prefix?: string; preserveExtension?: boolean; addTimestamp?: boolean; addRandomId?: boolean; } /** * Generates a unique file key for S3 storage */ interface UploadProgress { loaded: number; total: number; percentage: number; key: string; progress?: number; uploadSpeed?: number; eta?: number; } type ProgressCallback = (progress: UploadProgress) => void; /** * Uploads a file directly to S3 with progress tracking * Note: This is for server-side uploads. Client-side uploads use presigned URLs. */ interface ListFilesOptions { prefix?: string; maxFiles?: number; includeMetadata?: boolean; sortBy?: "key" | "size" | "modified"; sortOrder?: "asc" | "desc"; } interface PaginatedListOptions extends ListFilesOptions { pageSize?: number; continuationToken?: string; } interface FileInfo { key: string; url: string; size: number; contentType: string; lastModified: Date; etag: string; metadata?: Record<string, string>; } interface ListFilesResult { files: FileInfo[]; continuationToken?: string; isTruncated: boolean; totalCount?: number; } /** * Lists files in the bucket with optional filtering and pagination */ interface FileInfoResult { key: string; info: FileInfo | null; error?: string; } interface FileValidationResult { valid: boolean; errors: string[]; warnings: string[]; info: FileInfo; } interface ValidationRules { maxSize?: number; minSize?: number; allowedTypes?: string[]; requiredExtensions?: string[]; customValidators?: ((info: FileInfo) => boolean | string)[]; } /** * Gets detailed information about a file */ interface DeleteFilesResult { deleted: string[]; errors: DeleteError[]; } interface DeleteError { key: string; code: string; message: string; } interface DeleteByPrefixResult { filesFound: number; deleted: string[]; errors: DeleteError[]; dryRun: boolean; } //#endregion //#region src/core/storage/storage-api.d.ts declare class StorageInstance { private readonly config; constructor(config: UploadConfig); /** * Get the current configuration (read-only) */ getConfig(): Readonly<UploadConfig>; /** * Get provider information */ getProviderInfo(): { provider: "aws" | "cloudflare-r2" | "digitalocean-spaces" | "minio" | "azure-blob" | "ibm-cloud" | "oracle-oci" | "wasabi" | "backblaze-b2" | "storj-dcs" | "telnyx-storage" | "tigris-data" | "cloudian-hyperstore" | "gcs" | "s3-compatible"; bucket: string; region: string | undefined; }; list: { files: (options?: ListFilesOptions) => Promise<FileInfo[]>; paginated: (options?: PaginatedListOptions) => Promise<ListFilesResult>; byExtension: (extension: string, prefix?: string) => Promise<FileInfo[]>; bySize: (minSize?: number, maxSize?: number, prefix?: string) => Promise<FileInfo[]>; byDate: (fromDate?: Date, toDate?: Date, prefix?: string) => Promise<FileInfo[]>; directories: (prefix?: string) => Promise<string[]>; paginatedGenerator: (options?: PaginatedListOptions) => AsyncGenerator<FileInfo[], any, any>; }; metadata: { getInfo: (key: string) => Promise<FileInfo>; getBatch: (keys: string[]) => Promise<FileInfoResult[]>; getSize: (key: string) => Promise<number>; getContentType: (key: string) => Promise<string>; getLastModified: (key: string) => Promise<Date>; getCustom: (key: string) => Promise<Record<string, string>>; setCustom: (key: string, metadata: Record<string, string>) => Promise<void>; }; download: { presignedUrl: (key: string, expiresIn?: number) => Promise<string>; url: (key: string) => string; }; upload: { file: (file: File | Buffer, key: string, options?: any) => Promise<string>; presignedUrl: (options: PresignedUrlOptions) => Promise<PresignedUrlResult>; presignedBatch: (requests: PresignedUrlOptions[]) => Promise<PresignedUrlResult[]>; generateKey: (options: FileKeyOptions) => string; }; validation: { exists: (key: string) => Promise<boolean>; existsWithInfo: (key: string) => Promise<FileInfo | null>; validateFile: (key: string, rules: ValidationRules) => Promise<FileValidationResult>; validateFiles: (keys: string[], rules: ValidationRules) => Promise<FileValidationResult[]>; connection: () => Promise<{ success: boolean; error?: string; }>; }; delete: { file: (key: string) => Promise<void>; files: (keys: string[]) => Promise<DeleteFilesResult>; byPrefix: (prefix: string, options?: { dryRun?: boolean; maxFiles?: number; }) => Promise<DeleteByPrefixResult>; }; } /** * Create a new storage instance with the given configuration */ declare function createStorage(config: UploadConfig): StorageInstance; //#endregion //#region src/core/config/upload-config.d.ts declare function createS3Instance(config: UploadConfig): { readonly file: (constraints?: S3FileConstraints) => S3FileSchema; readonly image: (constraints?: S3FileConstraints) => S3ImageSchema; readonly object: <T extends Record<string, any>>(shape: T) => S3ObjectSchema<T>; readonly createRouter: <TRoutes extends Record<string, any>>(routes: TRoutes) => S3Router<{ [K in keyof TRoutes]: TRoutes[K] extends S3Route<any, any> ? TRoutes[K] : S3Route<any, any> }>; }; interface UploadConfig { provider: ProviderConfig; debug?: boolean; enableMetrics?: boolean; defaults?: { maxFileSize?: string | number; allowedFileTypes?: string[]; acl?: string; metadata?: Record<string, any>; }; paths?: { prefix?: string; generateKey?: (file: { name: string; type: string; }, metadata: any) => string; }; security?: { requireAuth?: boolean; allowedOrigins?: string[]; rateLimiting?: { maxUploads?: number; windowMs?: number; }; }; hooks?: { onUploadStart?: (ctx: { file: any; metadata: any; }) => Promise<void> | void; onUploadComplete?: (ctx: { file: any; url: string; metadata: any; }) => Promise<void> | void; onUploadError?: (ctx: { file: any; error: Error; metadata: any; }) => Promise<void> | void; }; } declare class UploadConfigBuilder { private config; /** * Set the storage provider with type-safe configuration */ provider(providerConfig: ProviderConfig): UploadConfigBuilder; provider<T extends ProviderType>(type: T, config: ProviderConfigMap[T]): UploadConfigBuilder; /** * Set default file constraints */ defaults(defaults: UploadConfig["defaults"]): UploadConfigBuilder; /** * Configure file paths and key generation */ paths(paths: UploadConfig["paths"]): UploadConfigBuilder; /** * Configure security settings */ security(security: UploadConfig["security"]): UploadConfigBuilder; /** * Add lifecycle hooks */ hooks(hooks: UploadConfig["hooks"]): UploadConfigBuilder; /** * Enable debug mode */ debug(enabled?: boolean): UploadConfigBuilder; /** * Enable metrics collection */ metrics(enabled?: boolean): UploadConfigBuilder; /** * Build the final configuration and return configured instances */ build(): UploadInitResult; } interface UploadInitResult { config: UploadConfig; storage: StorageInstance; s3: ReturnType<typeof createS3Instance>; } /** * Create a new upload configuration builder * This is the recommended way to configure pushduck */ declare function createUploadConfig(): UploadConfigBuilder; //#endregion //#region src/core/router/router-v2.d.ts interface S3RouteContext { req: NextRequest; metadata?: Record<string, any>; } interface S3FileMetadata$1 { name: string; size: number; type: string; } interface S3MiddlewareContext extends S3RouteContext { file: S3FileMetadata$1; } interface S3LifecycleContext<T = any> { file: S3FileMetadata$1; metadata: T; url?: string; key?: string; } type S3Middleware<TInput = any, TOutput = any> = (ctx: S3MiddlewareContext & { metadata: TInput; }) => Promise<TOutput> | TOutput; type S3LifecycleHook<T = any> = (ctx: S3LifecycleContext<T>) => Promise<void> | void; interface PathContext<TMetadata = any> { file: { name: string; type: string; }; metadata: TMetadata; globalConfig: { prefix?: string; generateKey?: (file: { name: string; type: string; }, metadata: any) => string; }; routeName: string; } interface S3RoutePathConfig<TMetadata = any> { prefix?: string; generateKey?: (ctx: PathContext<TMetadata>) => string; suffix?: string; } declare class S3Route<TSchema extends S3Schema = S3Schema, TMetadata = any> { private schema; private config; constructor(schema: TSchema, config?: S3RouteConfig<TMetadata>); middleware<TNewMetadata>(middleware: S3Middleware<TMetadata, TNewMetadata>): S3Route<TSchema, TNewMetadata>; paths(paths: S3RoutePathConfig<TMetadata>): this; onUploadStart(hook: S3LifecycleHook<TMetadata>): this; onUploadProgress(hook: (ctx: S3LifecycleContext<TMetadata> & { progress: number; }) => Promise<void> | void): this; onUploadComplete(hook: S3LifecycleHook<TMetadata>): this; onUploadError(hook: (ctx: S3LifecycleContext<TMetadata> & { error: Error; }) => Promise<void> | void): this; _getConfig(): S3RouteConfig<TMetadata> & { schema: TSchema; }; } interface S3RouteConfig<TMetadata = any> { middleware?: S3Middleware<any, any>[]; paths?: S3RoutePathConfig<TMetadata>; onUploadStart?: S3LifecycleHook<TMetadata>; onUploadProgress?: (ctx: S3LifecycleContext<TMetadata> & { progress: number; }) => Promise<void> | void; onUploadComplete?: S3LifecycleHook<TMetadata>; onUploadError?: (ctx: S3LifecycleContext<TMetadata> & { error: Error; }) => Promise<void> | void; } type S3RouterDefinition = Record<string, S3Route<any, any>>; declare class S3Router<TRoutes extends S3RouterDefinition> { private config; private routes; constructor(routes: TRoutes, config: UploadConfig); getRoute<K extends keyof TRoutes>(routeName: K): TRoutes[K] | undefined; getRouteNames(): (keyof TRoutes)[]; get handlers(): { GET: (request: Request) => Promise<Response>; POST: (request: Request) => Promise<Response>; }; generatePresignedUrls<K extends keyof TRoutes>(routeName: K, req: NextRequest, files: S3FileMetadata$1[]): Promise<PresignedUrlResponse[]>; handleUploadComplete<K extends keyof TRoutes>(routeName: K, req: NextRequest, completions: UploadCompletion[]): Promise<CompletionResponse[]>; } interface PresignedUrlResponse { success: boolean; file: S3FileMetadata$1; presignedUrl?: string; key?: string; metadata?: any; error?: string; } interface UploadCompletion { key: string; file: S3FileMetadata$1; metadata?: any; } interface CompletionResponse { success: boolean; key: string; url?: string; presignedUrl?: string; file?: S3FileMetadata$1; error?: string; } /** * ✅ Config-aware router factory * Creates router with explicit config dependency */ declare function createS3RouterWithConfig<TRoutes extends S3RouterDefinition>(routes: TRoutes, config: UploadConfig): S3Router<TRoutes>; type InferRouterRoutes<T> = T extends S3Router<infer TRoutes> ? TRoutes : never; type InferRouteInput<T> = T extends S3Route<infer TSchema, any> ? InferS3Input<TSchema> : never; type InferRouteOutput<T> = T extends S3Route<infer TSchema, any> ? InferS3Output<TSchema> : never; type InferRouteMetadata<T> = T extends S3Route<any, infer TMetadata> ? TMetadata : never; type GetRoute<TRouter, TRouteName> = TRouter extends S3Router<infer TRoutes> ? TRouteName extends keyof TRoutes ? TRoutes[TRouteName] : never : never; //#endregion //#region src/types/index.d.ts /** * Centralized Type Definitions for pushduck * * This file consolidates all type definitions to prevent circular dependencies * and provide a single source of truth for types used across the library. */ interface S3UploadedFile { id: string; name: string; size: number; type: string; status: "pending" | "uploading" | "success" | "error"; progress: number; url?: string; key?: string; presignedUrl?: string; error?: string; file?: File; uploadStartTime?: number; uploadSpeed?: number; eta?: number; } interface S3FileMetadata { name: string; size: number; type: string; } interface UploadRouteConfig { endpoint?: string; onStart?: (files: S3FileMetadata[]) => void | Promise<void>; onSuccess?: (results: S3UploadedFile[]) => void | Promise<void>; onError?: (error: Error) => void; onProgress?: (progress: number) => void; } type S3RouteUploadConfig = UploadRouteConfig; type RouteUploadOptions = UploadRouteConfig; interface S3RouteUploadResult { files: S3UploadedFile[]; uploadFiles: (files: File[]) => Promise<void>; reset: () => void; isUploading: boolean; errors: string[]; progress?: number; uploadSpeed?: number; eta?: number; } interface ClientConfig { endpoint: string; fetcher?: (input: RequestInfo, init?: RequestInit) => Promise<Response>; defaultOptions?: RouteUploadOptions; } interface TypedUploadedFile<TOutput = any> extends S3UploadedFile { metadata?: TOutput; constraints?: { maxSize?: string; formats?: readonly string[]; dimensions?: { width?: number; height?: number; }; }; routeName?: string; } interface TypedRouteHook<TRouter = any, TRouteName extends string = string> { files: TypedUploadedFile[]; uploadFiles: (files: File[], metadata?: any) => Promise<any[]>; reset: () => void; isUploading: boolean; errors: string[]; routeName: TRouteName; progress?: number; uploadSpeed?: number; eta?: number; } type RouterRouteNames<T> = T extends S3Router<infer TRoutes> ? keyof TRoutes : never; type InferClientRouter<T> = T extends S3Router<infer TRoutes> ? { readonly [K in keyof TRoutes]: (options?: RouteUploadOptions) => TypedRouteHook<T, K extends string ? K : string> } : never; //#endregion export { AWSProviderConfig, BaseProviderConfig, ClientConfig, CloudflareR2Config, DeleteByPrefixResult, DeleteError, DeleteFilesResult, DigitalOceanSpacesConfig, FileInfo, FileInfoResult, FileKeyOptions, FileValidationResult, GetRoute, GoogleCloudStorageConfig, InferClientRouter, InferRouteInput, InferRouteMetadata, InferRouteOutput, InferRouterRoutes, InferS3Input, InferS3Output, ListFilesOptions, ListFilesResult, MinIOConfig, PaginatedListOptions, PresignedUrlOptions, PresignedUrlResult, ProgressCallback, ProviderConfig, ProviderType, RouterRouteNames, S3ArraySchema, S3FileConstraints, S3FileMetadata, S3FileSchema, S3ImageSchema, S3LifecycleContext, S3LifecycleHook, S3Middleware, S3MiddlewareContext, S3ObjectSchema, S3Route, S3RouteContext, S3RouteUploadConfig, S3RouteUploadResult, S3Router, S3RouterDefinition, S3Schema, S3UploadedFile, StorageInstance, TypedRouteHook, TypedUploadedFile, UploadConfig, UploadConfigBuilder, UploadInitResult, UploadProgress, UploadRouteConfig, ValidationRules, createProvider, createS3Client, createS3RouterWithConfig, createStorage, createUploadConfig, getProviderEndpoint, resetS3Client, validateProviderConfig };