# Data Model
Type Boundary Diagram
.png)
Public Types
UploadSessionId
type UploadSessionId = string;
Format: upl_<timestamp>_<random6> (e.g., upl_1711100400000_k3f9x2).
Generated by UploadEngineImpl.generateUploadId(). Stable and never reused.
UploadContext
Describes where and why an upload was initiated. Write-once, read-only 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
};
| Field | Required | Purpose |
|---|---|---|
source |
Yes | Logical origin — used for analytics, completion routing, UI projections |
contentType |
Yes | Backend-recognized content type passed to /init and /complete endpoints |
entityId |
No | Associated domain entity identifier |
dropZoneKey |
No | UI surface identifier (drop zone name) |
meta |
No | Immutable intent metadata for post-upload domain actions |
The engine stores this object but never interprets it.
UploadMeta
type UploadMeta = Readonly<{
intent: string; // e.g. "CREATE_VIDEO", "UPLOAD_BRAND_IMAGE"
payload: Record<string, unknown>; // arbitrary serializable domain data
owner?: UploadOwner; // content-level routing for UploadListener
}>;
type UploadOwner = {
contentType: ContentTypeEnum | 'video'; // 'video' is special — backend doesn't accept 'vod'
contentId: string;
};
- Written once at
start()— immutable thereafter - Survives UI unmounts, route changes, and page reloads (via persistence)
UploadListenerreadsintent+ownerto route post-upload domain actions
UploadSessionSnapshot
The only data shape UI layers may consume. Produced by toPublicSnapshotAdapter().
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'
};
UploadEvent
Discriminated union of 7 event types. See events.md for full contract.
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 };
UploadCompletionHandler<T>
type UploadCompletionHandler<T = unknown> = (args: {
fileUrl: string;
file: File;
context: UploadContext;
}) => Promise<T>;
Best-effort. Failure here does not invalidate the upload.
UploadDomainProcessedEvent<DomainShape>
Emitted after domain actions complete (outside the engine, by UploadListener):
type UploadDomainProcessedEvent<DomainShape> = {
owner: UploadOwner;
uploadId: UploadSessionId;
dropZoneKey?: string;
entity: DomainShape;
};
Internal Types
:::warning
These types are not exported and may change without notice. UI/feature modules must never depend on them.
:::
InternalUploadState
The 10-state enum governing the state machine (see lifecycle.md):
type InternalUploadState =
| 'idle'
| 'initializing'
| 'uploading'
| 'paused'
| 'retrying'
| 'finalizing'
| 'committing'
| 'completed'
| 'needs-attention'
| 'aborted';
Internal → Public mapping:
| Internal States | Public State |
|---|---|
idle, initializing, uploading, retrying, finalizing, committing |
'uploading' |
paused |
'paused' |
completed |
'completed' |
needs-attention |
'needs-attention' |
aborted |
'aborted' |
InternalUploadSnapshot
Full internal state held by UploadSession:
type InternalUploadSnapshot = {
uploadId: UploadSessionId;
file: File;
state: InternalUploadState;
progress: number;
context: UploadContext;
uploadServiceData?: {
uploadId: string; // multipart upload ID from backend
key: string; // S3 object key
bucket: string; // S3 bucket
prefixUrl: string; // URL prefix for final file URL
chunkSize: number; // server-authoritative chunk size
totalChunks: number; // ceil(fileSize / chunkSize)
};
completedParts: CompletedPart[];
fileUrl?: string;
error?: { stage: 'upload' | 'completion'; message: string };
persistence?: { key: string; createdAt: number };
chunkConfig?: { chunkSize: number; windowSize: number };
internalFlags?: { enableParallelChunkProcessing?: boolean };
};
CompletedPart
type CompletedPart = {
PartNumber: number;
ETag: string;
};
Maps directly to AWS S3 multipart completion format. Sorted by PartNumber before sending to /complete.
PersistedUploadState
Serializable, tenant-scoped state saved to sessionStorage:
type PersistedUploadState = {
key: string; // "<siteId>:<site>:<epoch>"
tenant: { site: string; siteId: string };
createdAt: number;
uploadId: UploadSessionId;
fileMeta: { name: string; size: number; type: string };
context: UploadContext;
uploadServiceData?: {
/* same as InternalUploadSnapshot */
};
completedParts: CompletedPart[];
progress: number;
state: 'paused' | 'uploading' | 'needs-attention';
chunkConfig?: { chunkSize: number; windowSize: number };
internalFlags?: Record<string, unknown>;
};
:::info Persistence rules
- Only
pausedandneeds-attentionstates are rehydrated on app reloadFileobject is not persisted (not serializable) — rehydrated uploads create a placeholder- Persistence key format:
<siteId>:<site>:<epoch>(e.g.,acme123:ACME:1768765855488)
:::
Data Adapters
The persistence.adapter.ts file provides four adapter functions bridging internal ↔ persisted ↔ public:
| Adapter | From | To |
|---|---|---|
toPersistedStateAdapter() |
InternalUploadSnapshot |
PersistedUploadState | null |
fromPersistedStateAdapter() |
PersistedUploadState |
UploadSession |
toPublicSnapshotAdapter() |
InternalUploadSnapshot |
UploadSessionSnapshot |
mapInternalStateAdapter() |
InternalUploadState |
Public state string |

Events vs Snapshots
| Events | Snapshots | |
|---|---|---|
| What | What happened | Current state |
| When | Emitted once per transition | Queried on demand |
| Shape | UploadEvent (discriminated union) |
UploadSessionSnapshot |
| Use for | Reactions (domain actions) | Observation (UI rendering) |
| Replay | No — do not reconstruct state from events | Yes — always reflects latest |