API Reference
Complete API documentation for FluxMedia packages.
MediaUploader
The main class for uploading files. Supports fallback providers, transactional uploads, and plugin orchestration.
Constructor
new MediaUploader(
provider: MediaProvider,
plugins?: FluxMediaPlugin[],
config?: MediaUploaderConfig
)Parameters:
| Parameter | Type | Description |
|---|---|---|
provider | MediaProvider | Primary storage provider |
plugins | FluxMediaPlugin[] | Optional array of plugins to register |
config | MediaUploaderConfig | Optional configuration for fallback, error handling |
MediaUploaderConfig
interface MediaUploaderConfig {
fallbackProvider?: MediaProvider;
fallbackOnErrors?: MediaErrorCode[];
onFallback?: (error: MediaError, fallbackProvider: MediaProvider) => void;
}| Option | Type | Description |
|---|---|---|
fallbackProvider | MediaProvider | Provider to use when primary fails |
fallbackOnErrors | MediaErrorCode[] | Error codes that trigger fallback (default: network, rate-limit, provider errors) |
onFallback | function | Callback when fallback is triggered |
Example:
import { MediaUploader, MediaErrorCode } from '@fluxmedia/core';
const uploader = new MediaUploader(primaryProvider, [analyticsPlugin], {
fallbackProvider: backupProvider,
fallbackOnErrors: [
MediaErrorCode.NETWORK_ERROR,
MediaErrorCode.RATE_LIMITED,
MediaErrorCode.PROVIDER_ERROR,
],
onFallback: (error, provider) => {
console.warn(`Falling back to ${provider.name}: ${error.message}`);
},
});Methods
upload(file, options?)
Upload a single file. Automatically falls back to the fallback provider on qualifying errors.
async upload(file: UploadInput, options?: UploadOptions): Promise<UploadResult>delete(id)
Delete a file by ID.
async delete(id: string): Promise<void>get(id)
Get file metadata.
async get(id: string): Promise<UploadResult>getUrl(id, transform?)
Generate a URL synchronously, optionally with transformations.
getUrl(id: string, transform?: TransformationOptions): stringgetUrlAsync(id, transform?)
Generate a URL asynchronously (for providers that need async URL generation like signed URLs).
async getUrlAsync(id: string, transform?: TransformationOptions): Promise<string>uploadMultiple(files, options?)
Upload multiple files with concurrency control. Routes through plugin hooks.
async uploadMultiple(
files: UploadInput[],
options?: UploadOptions & {
concurrency?: number;
onBatchProgress?: (completed: number, total: number) => void;
}
): Promise<UploadResult[]>deleteMultiple(ids)
Delete multiple files. Routes through plugin hooks.
async deleteMultiple(ids: string[]): Promise<void>uploadWithTransaction(file, options?, callbacks?)
Upload with transaction semantics — commit on success, rollback on failure.
async uploadWithTransaction<T>(
file: UploadInput,
options?: UploadOptions,
callbacks?: TransactionCallbacks<T>
): Promise<{ result: UploadResult; committed: T }>TransactionCallbacks:
interface TransactionCallbacks<T> {
onCommit: (result: UploadResult) => Promise<T>;
onRollback?: (result: UploadResult, error: Error) => Promise<void>;
}Example:
const { result, committed } = await uploader.uploadWithTransaction(
file,
{ folder: 'documents' },
{
onCommit: async (result) => {
// Save to database after successful upload
return await db.documents.create({ url: result.url, storageKey: result.storageKey });
},
onRollback: async (result, error) => {
// Clean up the uploaded file if commit fails
await uploader.delete(result.id);
},
}
);supports(feature)
Check if provider supports a feature.
supports(feature: string): booleanuse(plugin)
Register a plugin. Calls plugin.init() if defined.
async use(plugin: FluxMediaPlugin): Promise<this>Types
UploadInput
Union type for all accepted upload inputs:
type UploadInput = File | Buffer | Readable | ReadableStream;Supports browser File objects, Node.js Buffer, Node.js Readable streams, and web ReadableStream.
UploadOptions
interface UploadOptions {
folder?: string;
filename?: string;
tags?: string[];
metadata?: Record<string, unknown>;
transformation?: TransformationOptions;
onProgress?: (percent: number) => void;
onByteProgress?: (loaded: number, total: number) => void;
uniqueFilename?: boolean; // Generate unique names (default: true)
contentType?: string; // MIME type override
signal?: AbortSignal; // Abort controller signal
}| Option | Type | Description |
|---|---|---|
folder | string | Destination folder path |
filename | string | Custom filename |
tags | string[] | Tags for organization |
metadata | Record<string, unknown> | Custom metadata key-value pairs |
transformation | TransformationOptions | Apply transformations on upload |
onProgress | (percent: number) => void | Normalized 0-100 progress callback |
onByteProgress | (loaded: number, total: number) => void | Raw byte-level progress callback |
uniqueFilename | boolean | Generate unique filenames (default: true) |
contentType | string | Override auto-detected MIME type |
signal | AbortSignal | Cancel in-flight uploads via AbortController |
UploadResult
interface UploadResult {
id: string;
storageKey: string;
url: string;
publicUrl: string;
size: number;
format: string;
width?: number;
height?: number;
provider: string;
metadata: Record<string, unknown>;
createdAt: Date;
}| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for the uploaded file |
storageKey | string | Provider-specific storage key (e.g., S3 object key, Cloudinary public ID) |
url | string | Direct URL to the file |
publicUrl | string | Public-facing URL |
size | number | File size in bytes |
format | string | File format/extension |
width | number | Image width (if applicable) |
height | number | Image height (if applicable) |
provider | string | Provider name that handled the upload |
metadata | Record<string, unknown> | Additional metadata |
createdAt | Date | Upload timestamp |
TransformationOptions
interface TransformationOptions {
width?: number;
height?: number;
fit?: 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
quality?: number;
format?: 'auto' | 'webp' | 'avif' | 'jpg' | 'png';
}MediaProvider
interface MediaProvider {
readonly name: string;
readonly features: ProviderFeatures;
readonly native: unknown;
upload(file: UploadInput, options?: UploadOptions): Promise<UploadResult>;
delete(id: string): Promise<void>;
get(id: string): Promise<UploadResult>;
getUrl(id: string, transform?: TransformationOptions): string;
uploadMultiple(files: UploadInput[], options?: UploadOptions): Promise<UploadResult[]>;
deleteMultiple(ids: string[]): Promise<void>;
search?(query: SearchOptions): Promise<UploadResult[]>;
}ProviderFeatures
interface ProviderFeatures {
transformations: {
resize: boolean;
crop: boolean;
format: boolean;
quality: boolean;
blur: boolean;
rotate: boolean;
effects: boolean;
};
capabilities: {
signedUploads: boolean;
directUpload: boolean;
multipartUpload: boolean;
videoProcessing: boolean;
aiTagging: boolean;
facialDetection: boolean;
};
storage: {
maxFileSize: number;
supportedFormats: string[];
};
}File Type Detection
Detect file types using magic bytes (more reliable than extensions).
getFileType(buffer)
import { getFileType } from '@fluxmedia/core';
const type = await getFileType(buffer);
console.log(type); // { mime: 'image/jpeg', ext: 'jpg' }getFileTypeFromStream(stream)
For large files without loading into memory:
import { getFileTypeFromStream } from '@fluxmedia/core';
const type = await getFileTypeFromStream(readableStream);Helper Functions
import { isImage, isVideo } from '@fluxmedia/core';
if (await isImage(buffer)) {
// Handle image
}
if (await isVideo(buffer)) {
// Handle video
}Errors
MediaError
Structured error class with typed error codes and provider context.
class MediaError extends Error {
code: MediaErrorCode;
provider: string;
originalError?: unknown;
details?: Record<string, unknown>;
}MediaErrorCode
enum MediaErrorCode {
// Upload errors
UPLOAD_FAILED = 'UPLOAD_FAILED',
FILE_TOO_LARGE = 'FILE_TOO_LARGE',
INVALID_FILE_TYPE = 'INVALID_FILE_TYPE',
NETWORK_ERROR = 'NETWORK_ERROR',
// Authentication errors
INVALID_CREDENTIALS = 'INVALID_CREDENTIALS',
UNAUTHORIZED = 'UNAUTHORIZED',
// Provider errors
PROVIDER_ERROR = 'PROVIDER_ERROR',
RATE_LIMITED = 'RATE_LIMITED',
QUOTA_EXCEEDED = 'QUOTA_EXCEEDED',
// Configuration errors
INVALID_CONFIG = 'INVALID_CONFIG',
MISSING_CREDENTIALS = 'MISSING_CREDENTIALS',
// File errors
FILE_NOT_FOUND = 'FILE_NOT_FOUND',
DELETE_FAILED = 'DELETE_FAILED',
}createMediaError
Factory function for creating consistent MediaError instances:
import { createMediaError, MediaErrorCode } from '@fluxmedia/core';
const error = createMediaError(MediaErrorCode.UPLOAD_FAILED, 'Upload timed out', 'cloudinary', {
originalError: err,
details: { timeout: 30000 },
});PartialUploadError
Specialized error for resumable multipart uploads. Preserves upload context so retries can resume from where they left off.
class PartialUploadError extends MediaError {
uploadContext: PartialUploadContext;
}PartialUploadContext
interface PartialUploadContext {
uploadId: string;
completedParts: number[];
totalParts: number;
bytesUploaded: number;
provider: string;
}Example — Handling partial upload failures:
import { PartialUploadError } from '@fluxmedia/core';
import { withRetry } from '@fluxmedia/plugins';
// withRetry automatically resumes from PartialUploadContext
const result = await withRetry(
(resumeContext) =>
uploader.upload(file, {
metadata: resumeContext ? { _resumeFrom: resumeContext } : {},
}),
{ maxRetries: 3, exponentialBackoff: true }
);Plugin Types
FluxMediaPlugin
interface FluxMediaPlugin {
name: string;
version?: string;
optional?: boolean; // If true, errors in hooks are caught and logged
hooks: PluginHooks;
init?: () => Promise<void> | void; // Called when plugin is registered
destroy?: () => Promise<void> | void; // Called when plugin is unregistered
}The optional flag enables graceful degradation — if an optional plugin’s hook throws, the error is caught and the upload continues. This is ideal for non-critical plugins like analytics or metadata extraction.
PluginHooks
interface PluginHooks {
beforeUpload?: (
file: File | Buffer,
options: UploadOptions
) => Promise<{ file: File | Buffer; options: UploadOptions } | void>;
afterUpload?: (result: UploadResult) => Promise<UploadResult>;
onError?: (
error: Error,
context: {
file: File | Buffer;
options: UploadOptions;
phase: UploadPhase;
uploadResult?: UploadResult;
}
) => Promise<void>;
beforeDelete?: (id: string) => Promise<string | void>;
afterDelete?: (id: string) => Promise<void>;
beforeGetUrl?: (
id: string,
transform?: TransformationOptions
) => Promise<{ id: string; transform?: TransformationOptions } | void>;
}UploadPhase
type UploadPhase = 'before' | 'upload' | 'after';Indicates which phase of the upload pipeline the error occurred in, available in onError context.
createPlugin Helper
import { createPlugin } from '@fluxmedia/core';
const myPlugin = createPlugin(
'my-plugin',
{
beforeUpload: async (file, options) => {
return { file, options };
},
},
{ optional: true }
);Analytics Event Types
Typed events for the analytics plugin. The TrackFunction is generic — TypeScript automatically enforces that event data matches the event type.
AnalyticsEventType
type AnalyticsEventType =
| 'media.upload.started'
| 'media.upload.completed'
| 'media.delete.completed'
| 'media.error';TrackFunction
type TrackFunction = <T extends AnalyticsEventType>(event: T, data: AnalyticsEventMap[T]) => void;Event Data Types
interface UploadStartedEventData {
fileName: string;
fileSize: number;
folder?: string;
uploadId: string;
}
interface UploadCompletedEventData {
fileId: string;
fileName: string;
fileSize: number;
format: string;
provider: string;
duration: number;
totalUploads: number;
totalSize: number;
}
interface DeleteCompletedEventData {
fileId: string;
}
interface ErrorEventData {
operation: 'upload' | 'delete' | 'get';
error: { message: string; name: string };
totalErrors: number;
}Testing Utilities
Helpers for testing custom providers and upload logic.
createMockProvider
Creates a mock MediaProvider for unit tests:
import { createMockProvider, createMockUploadResult } from '@fluxmedia/core';
const provider = createMockProvider({
name: 'test-provider',
});
const result = await provider.upload(file);createProviderContractTests
Validates that a custom provider correctly implements the MediaProvider interface:
import { createProviderContractTests } from '@fluxmedia/core';
createProviderContractTests('MyProvider', () => new MyProvider({ ... }));This runs a suite of tests verifying:
- Required properties (
name,features) - Feature matrix structure (transformations, capabilities, storage)
- All required methods exist (
upload,delete,get,getUrl,uploadMultiple,deleteMultiple,native) getUrlreturns valid URL strings