API

Prev Next

API

The Upload Engine exposes a deliberately small public API.

Everything listed here is a supported contract — any change to signatures or semantics is a breaking change.

Exported Surface

// src/lib/internal/UploadEngine/index.ts
export { UploadEngine } from './UploadEngine';
export type {
  UploadContext,
  UploadMeta,
  StartUploadOptions,
  UploadSessionSnapshot,
  UploadEvent,
  UploadCompletionHandler,
  UploadSessionId,
} from './UploadTypes';

UploadEngine is a singleton — instantiated once at module level. All consumers share the same instance.

Methods

start<T>(options): UploadSessionId

Starts a new upload session and returns its stable identity.

start<T>(options: StartUploadOptions<T>): UploadSessionId

StartUploadOptions<T>

type StartUploadOptions<T = unknown> = {
  file: File;
  context: UploadContext;
  onComplete?: UploadCompletionHandler<T>; // best-effort callback
  onError?: (error: Error) => void; // advisory error notification
  chunkSize?: number; // override (default: 5 MB)
  enableParallelChunkProcessing?: boolean; // feature-flagged parallel chunks
};

Behavior:

  • Generates a unique uploadId (upl_<timestamp>_<random>)
  • Creates an UploadSession with state idle
  • Transitions to initializing, emits UPLOAD_STARTED
  • Sets persistence metadata (tenant-scoped: <siteId>:<site>:<epoch>)
  • Applies chunk config (caller override → persisted → default 5 MB)
  • Enqueues into UploadQueue (max 5 concurrent sessions)

Example:

import { UploadEngine } from '@/lib/internal/UploadEngine';

const uploadId = UploadEngine.start({
  file: selectedFile,
  context: {
    source: 'content-create',
    contentType: 'video',
    entityId: contentId,
    meta: {
      intent: 'CREATE_VIDEO',
      payload: { title: 'Intro Video', seriesId: 'abc123' },
      owner: { contentType: 'video', contentId },
    },
  },
  onComplete: async ({ fileUrl, file, context }) => {
    await saveVideoToBackend(fileUrl, context);
  },
});

pause(uploadId): void

Pauses an active upload.

pause(uploadId: UploadSessionId): void

What happens internally:

  • Calls worker.requestPause() → sets pauseRequested = true and aborts the AbortController
  • Calls session.pause() → transitions state to paused, emits UPLOAD_PAUSED
  • Persists current state via UploadPersistence.save() (tenant-scoped sessionStorage)
  • Calls queue.finish(uploadId) → frees a concurrency slot
UploadEngine.pause(uploadId);

resume(uploadId): void

Resumes a paused or needs-attention upload.

resume(uploadId: UploadSessionId): void

What happens internally:

  • Restores chunk config from session (persisted chunkSize → default 5 MB)
  • Emits UPLOAD_RESUMED event immediately for UI feedback
  • Re-enqueues the session into UploadQueue
  • When the queue starts the worker, initialize() detects existing uploadServiceData and resumes from the last completed chunk

:::warning Design note
resume() does not call session.resume(). Instead, the worker's initialize()session.startUploading() handles the pauseduploading transition. This is intentional.
:::

UploadEngine.resume(uploadId);

abort(uploadId): void

Aborts an upload. This is a terminal operation — aborted uploads cannot be resumed.

abort(uploadId: UploadSessionId): void

What happens internally:

  • Calls worker.abort() → aborts the AbortController
  • Calls session.abort() → transitions to aborted (idempotent if already terminal), emits UPLOAD_ABORTED
  • On UPLOAD_ABORTED event, handleEvent() removes persistence data and deletes the session from the engine's map
  • Calls queue.finish(uploadId) → frees a concurrency slot
UploadEngine.abort(uploadId);

get(uploadId): UploadSessionSnapshot | null

Returns the current public snapshot for a given upload, or null if not found.

get(uploadId: UploadSessionId): UploadSessionSnapshot | null

The returned snapshot is a read-only projection of internal state via toPublicSnapshotAdapter().

const snapshot = UploadEngine.get(uploadId);
if (snapshot?.state === 'completed') {
  console.log('File URL:', snapshot.fileUrl);
}

list(): UploadSessionSnapshot[]

Returns public snapshots for all sessions (active, paused, completed, needs-attention).

list(): UploadSessionSnapshot[]

Ordering is not guaranteed. Aborted sessions are not included (they are deleted on abort).

const uploads = UploadEngine.list();
const active = uploads.filter((u) => u.state === 'uploading');

Public Types

UploadSessionId

type UploadSessionId = string;
// Format: "upl_<timestamp>_<random6>"
// Example: "upl_1711100400000_k3f9x2"

Stable for the entire lifetime of the upload. Never reused.

UploadContext

Describes where and why an upload was initiated. Stored immutably for the entire lifecycle.

type UploadContext = {
  source: 'account-avatar' | 'content-create' | 'content-details' | 'brand-settings' | string;

  contentType:
    | 'image'
    | 'video'
    | 'audio'
    | 'document'
    | 'closedCaptions'
    | 'brand'
    | 'brand-video'
    | 'androidKeystore'
    | 'firetvKeystore'
    | 'androidLauncherXML';

  entityId?: string; // contentId, brandId, userId, etc.
  dropZoneKey?: string; // originating drop zone (e.g. "_16x9Images")
  meta?: UploadMeta; // immutable intent metadata
};

UploadMeta

Immutable intent metadata carried through the entire upload lifecycle.

type UploadMeta = Readonly<{
  intent: string; // e.g. "CREATE_VIDEO", "UPLOAD_BRAND_IMAGE"
  payload: Record<string, unknown>; // arbitrary serializable data
  owner?: UploadOwner; // optional content-level routing
}>;

type UploadOwner = {
  contentType: ContentTypeEnum | 'video';
  contentId: string;
};

The engine stores metadata but never interprets it. Intent resolution happens in UploadListener.

UploadSessionSnapshot

The only data shape UI layers are allowed to consume.

type UploadSessionSnapshot = {
  uploadId: UploadSessionId;
  fileName: string;
  fileSize: number;
  state: 'uploading' | 'paused' | 'completed' | 'needs-attention' | 'aborted';
  progress: number; // 0–100, monotonic
  context: UploadContext;
  fileUrl?: string; // present only when state === 'completed'
  error?: {
    stage: 'upload' | 'completion';
    message: string;
  }; // present only when state === 'needs-attention'
};

:::info Internal → Public State Mapping
Internal states idle, initializing, retrying, finalizing, and committing all map to 'uploading' in the public snapshot. This hides orchestration complexity from consumers.
:::

UploadEvent

Discriminated union of all events emitted by the engine. Events represent facts, not commands.

type UploadEvent =
  | { type: 'UPLOAD_STARTED'; uploadId: UploadSessionId }
  | { type: 'UPLOAD_PROGRESS'; uploadId: UploadSessionId; progress: number }
  | { type: 'UPLOAD_PAUSED'; uploadId: UploadSessionId }
  | { type: 'UPLOAD_RESUMED'; uploadId: UploadSessionId }
  | { type: 'UPLOAD_COMPLETED'; uploadId: UploadSessionId; fileUrl: string }
  | { type: 'UPLOAD_NEEDS_ATTENTION'; uploadId: UploadSessionId; reason: string }
  | { type: 'UPLOAD_ABORTED'; uploadId: UploadSessionId };

Events are published on the global Signal bus under topic 'upload':

import { Signal } from '@/lib/internal/Signal';

const unsub = Signal.subscribe<{ event: UploadEvent }>('upload', ({ event }) => {
  if (event.type === 'UPLOAD_COMPLETED') {
    console.log(event.fileUrl);
  }
});

// later
unsub();

UploadCompletionHandler<T>

Optional callback invoked after successful multipart completion.

type UploadCompletionHandler<T = unknown> = (args: {
  fileUrl: string;
  file: File;
  context: UploadContext;
}) => Promise<T>;

:::danger Completion handlers are best-effort

  • Handler failure does not invalidate the upload
  • Has 1 retry attempt (category: completion-handler)
  • If handler fails after retry, the upload enters needs-attention with stage: 'completion'
  • Domain correctness must be driven by Signal events, not this callback
    :::

Constants

Constant Value Description
MAX_PARALLEL_UPLOADS 5 Max concurrent sessions in UploadQueue
DEFAULT_CHUNK_SIZE 5 MB (5 × 1024 × 1024) Default chunk size if not overridden
DEFAULT_WINDOW_SIZE 5 Chunks per presigned-URL batch window
PARALLEL_CHUNK_LIMIT 5 Max concurrent chunk uploads (when parallel mode enabled)

API Flow Diagram

Image

Stability Guarantees

  • Backward compatible — public method signatures will not change
  • Additive only — new optional fields may be added to StartUploadOptions, UploadContext, UploadSessionSnapshot
  • Explicit migrations — any breaking change ships with a documented migration path (see migrations)