Asset management for Verevoir — a pluggable BlobStore interface for binary data, metadata persistence via StorageAdapter, and automatic dimension extraction via Sharp.
- BlobStore — pluggable interface for binary storage (put/get/delete). Ships with
MemoryBlobStore; S3/R2 implementations follow later.
- AssetManager — orchestrates
StorageAdapter (metadata) + BlobStore (binary data) for upload, download, get, delete, list, and updateMetadata operations.
- Metadata Extraction — reads pixel dimensions from bitmap images via Sharp on upload. SVG and video get null dimensions.
- Hotspot — mutable focal point (0–1 range) for smart cropping. Set via
updateMetadata().
npm install @verevoir/assets
import { AssetManager, MemoryBlobStore } from '@verevoir/assets';
import { MemoryAdapter } from '@verevoir/storage';
const storage = new MemoryAdapter();
const blobStore = new MemoryBlobStore();
await storage.connect();
const manager = new AssetManager({ storage, blobStore });
// Upload — dimensions extracted automatically
const asset = await manager.upload({
data: imageBuffer,
filename: 'hero.jpg',
contentType: 'image/jpeg',
createdBy: 'user-1',
});
// asset.width → 1920, asset.height → 1080
// Set a hotspot
await manager.updateMetadata(asset.id, {
hotspot: { x: 0.5, y: 0.3 },
});
// Download
const result = await manager.download(asset.id);
// result.asset — metadata, result.data — Uint8Array
// List
const assets = await manager.list({ limit: 10 });
// Delete
await manager.delete(asset.id);
| Method |
Description |
upload(input) |
Store binary data + create metadata. Extracts dimensions for bitmap images. |
get(id) |
Retrieve asset metadata by ID, or null. |
download(id) |
Retrieve metadata + binary data, or null. |
delete(id) |
Delete both blob and metadata. Throws if not found. |
list(options?) |
List assets with optional filtering, sorting, pagination. |
updateMetadata(id, update) |
Update mutable fields (hotspot, filename). Throws if not found. |
| Field |
Type |
Description |
id |
string |
Unique identifier |
filename |
string |
Original filename (mutable via updateMetadata) |
contentType |
string |
MIME type |
size |
number |
Byte size |
blobKey |
string |
Key in the BlobStore |
createdBy |
string |
Uploader identity |
type |
'image' | 'video' |
Derived from MIME prefix |
format |
'bitmap' | 'vector' |
SVG → vector, everything else → bitmap |
width |
number | null |
Pixel width (null for SVG/video) |
height |
number | null |
Pixel height (null for SVG/video) |
hotspot |
Hotspot | null |
Focal point { x: 0–1, y: 0–1 } |
createdAt |
Date |
Creation timestamp |
updatedAt |
Date |
Last update timestamp |
| File |
Responsibility |
src/types.ts |
Core interfaces: BlobStore, Asset, Hotspot, AssetMetadataUpdate, UploadInput, DownloadResult |
src/metadata.ts |
extractMetadata() — Sharp-based dimension extraction for bitmap images |
src/memory.ts |
MemoryBlobStore — Map-based in-memory blob storage with defensive copies |
src/manager.ts |
AssetManager — orchestrates StorageAdapter + BlobStore for asset CRUD |
src/index.ts |
Public API exports |
- Blob before metadata — orphaned blob is recoverable; orphaned metadata is not.
- Blob-immutable, metadata-mutable — binary data never changes. Hotspot and filename can be updated.
- Sharp for extraction only — reads dimensions on upload; never used for runtime resizing.
- Null dimensions for video and SVG — video would need ffprobe; SVG has no inherent pixel size.
Uint8Array not Buffer — more portable across environments.
- Media — assets, imgproxy URLs, and image display
- Integration Guide — connecting content models, storage, editor, and more
npm install # Install dependencies
make build # Compile TypeScript
make test # Run test suite
make lint # Check formatting