FluxMedia
Copy page
Open in ChatGPTOpen in Claude

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)
  • getUrl returns valid URL strings