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): string
getUrlAsync(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): boolean
use(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