Skip to content

@siteswarm/media

Headless digital asset ingestion, Cloudflare Images edge transformation URL builder, responsive srcsets, and R2 storage key management for SiteSwarm applications.

Adheres strictly to the Headless Invariant: pure TypeScript logic with 0 HTML/CSS rendering.



@siteswarm/media provides the foundational headless utilities for image transformation, responsive srcset construction, content-addressed storage key generation, and asset metadata validation across the SiteSwarm monorepo. It powers @siteswarm/cms-media, @siteswarm/components, and client-facing storefronts (such as Greenleaf Bakery, Auto Repair, and marketing applications).


  • Edge Image Transformations: Cloudflare Images transformations cost approximately $0.50 per 1,000 unique transformations. To protect client unit economics, image transformations and heavy asset storage are encapsulated behind clean capability boundaries.
  • Decoupled Retainers: Low-overhead sites run with static/D1 assets near $0/month, whereas clients opting into dynamic media catalogs and high-res asset pipelines leverage this capability under a commercial retainer ($29–$99/month).
  • Headless Invariant: This package produces pure URLs, srcset strings, structured definitions, and key paths. Visual rendering and layout markup belong in application templates or @siteswarm/components.

  • ⚡ Cloudflare Images Grammar: Formats option strings (width=...,format=auto,quality=80) and generates normalized edge transformation paths.
  • 📐 Zero Cumulative Layout Shift (CLS): Calculates reduced mathematical aspect ratios (e.g. 16 / 9, 4 / 3, 1 / 1) to eliminate layout shift during image loading.
  • 🖼️ Modern Format Delivery: Automatically generates multi-source picture definitions for AVIF and WebP formats with responsive widths.
  • 🔒 R2 Content-Addressed Storage Keys: Generates collision-free, sanitized keys with tenant isolation and cryptographic SHA-256 hashes.
  • 🛡️ Edge Cache Optimization: Exports immutable caching headers (IMMUTABLE_MEDIA_CACHE_CONTROL) guaranteeing 1-year browser and edge caching for content-addressed assets.
  • 🧪 Type Safety with Zod: Validates media asset payloads with runtime schema enforcement (MediaAssetSchema).

Cloudflare Images URL Transformation Grammar

Section titled “Cloudflare Images URL Transformation Grammar”

Cloudflare Images supports on-the-fly transformations via the /cdn-cgi/image/<options>/<sourcePath> path grammar on Cloudflare-proxied zones.

  1. Relative Transformation URL (same domain):

    /cdn-cgi/image/<options>/<sourcePath>

    Example: /cdn-cgi/image/width=640,quality=80,format=auto/uploads/bakery/croissant.jpg

  2. Absolute Transformation URL (custom zone domain):

    https://<zoneDomain>/cdn-cgi/image/<options>/<sourcePath>

    Example: https://greenleafbakery.com/cdn-cgi/image/width=1024,format=avif/uploads/hero.jpg

  3. Remote External Source URL:

    /cdn-cgi/image/<options>/https://<remoteDomain>/<path>

    Example: /cdn-cgi/image/width=800,format=webp/https://r2.siteswarm.io/assets/pastry.png

Parameter Type Default Description
width number – Target output width in pixels (positive integer).
height number – Target output height in pixels (positive integer).
quality number – Compression quality integer between 1 and 100.
format "auto" | "avif" | "webp" | "jpeg" | "png" "auto" Target output encoding. "auto" serves AVIF/WebP based on Accept header.
fit "scale-down" | "contain" | "cover" | "crop" | "pad" – Scaling mode relative to target bounding box.
metadata "keep" | "copyright" | "none" – EXIF metadata retention policy.
blur number – Blur radius (1 to 250). Useful for progressive blur placeholders.
sharpen number – Unsharp masking strength (0.0 to 10.0).
rotate number – Rotation angle in degrees (90, 180, 270).
gravity "auto" | "left" | "right" | "top" | "bottom" | "center" – Crop focal point alignment or face/saliency detection ("auto").
trim string – Trim borders ("<top>;<right>;<bottom>;<left>").
onerror "redirect" – Fallback behavior when image transformation fails; redirects to original image.

Note: Cloudflare requires at least one parameter in /cdn-cgi/image/<options>/.... If an empty options object is supplied, formatTransformOptions() automatically injects format=auto.


Responsive Breakpoints & Srcset Generation

Section titled “Responsive Breakpoints & Srcset Generation”

Standardized breakpoints ensure consistent browser selection without creating duplicate cache artifacts:

  • Default Breakpoints: [360, 640, 1024, 1920] (exported via DEFAULT_RESPONSIVE_WIDTHS).
  • Standard Alternate Breakpoints: [320, 640, 1024, 1920] for mobile-first fluid designs.
import { generateResponsiveSrcSet } from "@siteswarm/media";
const srcset = generateResponsiveSrcSet(
"/uploads/pastries/sourdough.jpg",
[320, 640, 1024, 1920],
{ quality: 80, fit: "scale-down" }
);
// Result:
// "/cdn-cgi/image/width=320,quality=80,fit=scale-down/uploads/pastries/sourdough.jpg 320w, /cdn-cgi/image/width=640,quality=80,fit=scale-down/uploads/pastries/sourdough.jpg 640w, /cdn-cgi/image/width=1024,quality=80,fit=scale-down/uploads/pastries/sourdough.jpg 1024w, /cdn-cgi/image/width=1920,quality=80,fit=scale-down/uploads/pastries/sourdough.jpg 1920w"

R2 Storage Key Management & Multi-Tenant Prefixing

Section titled “R2 Storage Key Management & Multi-Tenant Prefixing”

SiteSwarm utilizes Cloudflare R2 object storage with strict key formatting to avoid key collisions, guarantee multi-tenant data isolation, and enable immutable edge caching.

To guarantee security and tenant isolation across SaaS deployments:

assets/<tenant_id>/<sha256>.<ext>
  • tenant_id: Unique identifier for the client tenant (e.g. greenleaf-bakery).
  • sha256: Cryptographic content hash of the original raw upload.
  • ext: Lowercase sanitized file extension (e.g. jpg, png, webp).

2. Collection & Content-Addressed Key Generator (generateMediaKey)

Section titled “2. Collection & Content-Addressed Key Generator (generateMediaKey)”

For headless CMS and collection uploads:

import { generateMediaKey } from "@siteswarm/media";
const key = generateMediaKey(
"Menu Items",
"Sourdough Boule! (Special).JPG",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
);
// Result:
// "uploads/menu-items/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/sourdough-boule-special.jpg"

The function sanitizes folder names and filenames into URL-safe kebab-case strings while preserving lowercase extensions.


All assets managed under content-addressed keys should be served with the immutable caching header:

import { IMMUTABLE_MEDIA_CACHE_CONTROL } from "@siteswarm/media";
console.log(IMMUTABLE_MEDIA_CACHE_CONTROL);
// => "public, max-age=31536000, immutable"

Because content-addressed keys change whenever the underlying image bytes change, cache invalidation is never required.

To eliminate Cumulative Layout Shift, Astro components calculate and apply explicit aspect ratios to container elements prior to image loading:

import { calculateAspectRatio } from "@siteswarm/media";
calculateAspectRatio(1920, 1080); // => "16 / 9"
calculateAspectRatio(800, 600); // => "4 / 3"
calculateAspectRatio(1200, 800); // => "3 / 2"
calculateAspectRatio(1000, 1000); // => "1 / 1"
calculateAspectRatio(0, 500); // => "auto"

src/components/OptimizedImage.astro
---
import {
buildCloudflareImageUrl,
generatePictureSources,
calculateAspectRatio,
DEFAULT_RESPONSIVE_WIDTHS,
} from "@siteswarm/media";
interface Props {
src: string;
alt: string;
width: number;
height: number;
priority?: boolean;
}
const { src, alt, width, height, priority = false } = Astro.props;
const aspectRatio = calculateAspectRatio(width, height);
const sources = generatePictureSources(
src,
DEFAULT_RESPONSIVE_WIDTHS,
{ quality: 80, fit: "scale-down" },
"(max-width: 640px) 100vw, (max-width: 1024px) 75vw, 50vw"
);
const fallbackSrc = buildCloudflareImageUrl(src, {
width: 640,
quality: 80,
format: "auto",
fit: "scale-down",
});
---
<picture class="media-container" style={`aspect-ratio: ${aspectRatio};`}>
{sources.map((source) => (
<source type={source.type} srcset={source.srcset} sizes={source.sizes} />
))}
<img
src={fallbackSrc}
alt={alt}
width={width}
height={height}
loading={priority ? "eager" : "lazy"}
decoding={priority ? "sync" : "async"}
fetchpriority={priority ? "high" : undefined}
class="h-auto w-full object-cover"
/>
</picture>
<style>
.media-container {
display: block;
position: relative;
overflow: hidden;
contain: layout paint;
}
</style>

Component Usage with @siteswarm/components

Section titled “Component Usage with @siteswarm/components”

In SiteSwarm applications, you can also import the pre-built <Picture /> component:

---
import { Picture } from "@siteswarm/components";
---
<Picture
src="/uploads/bakery/hero.jpg"
alt="Fresh artisan bread loaf"
width={1920}
height={1080}
priority={true}
sizes="(max-width: 768px) 100vw, 1200px"
/>

buildCloudflareImageUrl(src, options?, zoneDomain?): string

Section titled “buildCloudflareImageUrl(src, options?, zoneDomain?): string”

Constructs a Cloudflare Images edge transformation URL.

  • Parameters:
    • src (string): The source image path (relative or absolute URL).
    • options (CloudflareImageTransformOptions): Transformation options.
    • zoneDomain (string, optional): Optional custom zone domain (e.g. example.com).
  • Returns: Formatted URL string.

generateResponsiveSrcSet(src, widths?, options?, zoneDomain?): string

Section titled “generateResponsiveSrcSet(src, widths?, options?, zoneDomain?): string”

Generates a responsive comma-separated srcset string.

  • Parameters:
    • src (string): Asset path.
    • widths (readonly number[], optional): Array of widths (defaults to [360, 640, 1024, 1920]).
    • options (Omit<CloudflareImageTransformOptions, "width">, optional): Transformation options.
    • zoneDomain (string, optional): Optional zone domain.
  • Returns: Standard srcset string (e.g. /url 360w, /url 640w).

generatePictureSources(src, widths?, options?, sizes?, zoneDomain?): PictureSourceDefinition[]

Section titled “generatePictureSources(src, widths?, options?, sizes?, zoneDomain?): PictureSourceDefinition[]”

Generates structured <source> definitions for modern formats (image/avif and image/webp).

  • Parameters:
    • src (string): Asset path.
    • widths (readonly number[], optional): Array of target widths.
    • options (Omit<CloudflareImageTransformOptions, "width" | "format">, optional): Base transformation options.
    • sizes (string, optional): HTML sizes attribute.
    • zoneDomain (string, optional): Optional zone domain.
  • Returns: Array of PictureSourceDefinition objects.

generateMediaKey(collection, filename, uuidOrHash?): string

Section titled “generateMediaKey(collection, filename, uuidOrHash?): string”

Generates a collision-free, sanitized, content-addressed R2 key.

  • Parameters:
    • collection (string): Target folder or collection name (e.g. "Pastries").
    • filename (string): Original filename.
    • uuidOrHash (string, optional): Unique UUID or SHA-256 hash. Defaults to crypto.randomUUID().
  • Returns: Key string (uploads/<collection>/<uuidOrHash>/<sanitized-filename>).

calculateAspectRatio(width, height): string

Section titled “calculateAspectRatio(width, height): string”

Calculates a reduced aspect ratio string (e.g. "16 / 9", "4 / 3", "auto").

  • Parameters:
    • width (number): Image width.
    • height (number): Image height.
  • Returns: CSS aspect-ratio string.

Serializes CloudflareImageTransformOptions into a comma-separated key-value parameter string. Defaults to "format=auto" if no options are specified.

Normalizes image source paths, strips any existing /cdn-cgi/image/... prefixes to prevent duplicate wrapping, and ensures valid leading slash grammar.


export const IMMUTABLE_MEDIA_CACHE_CONTROL = "public, max-age=31536000, immutable";
export const DEFAULT_RESPONSIVE_WIDTHS: readonly number[] = [360, 640, 1024, 1920] as const;

export type CloudflareImageFormat = "auto" | "avif" | "webp" | "jpeg" | "png";
export type CloudflareImageFit = "scale-down" | "contain" | "cover" | "crop" | "pad";
export type CloudflareImageMetadata = "keep" | "copyright" | "none";
export type CloudflareImageGravity = "auto" | "left" | "right" | "top" | "bottom" | "center";
export interface CloudflareImageTransformOptions {
width?: number;
height?: number;
quality?: number;
format?: CloudflareImageFormat;
fit?: CloudflareImageFit;
metadata?: CloudflareImageMetadata;
blur?: number;
sharpen?: number;
rotate?: number;
gravity?: CloudflareImageGravity;
trim?: string;
onerror?: "redirect";
}
export interface PictureSourceDefinition {
type: string;
srcset: string;
sizes?: string;
}

import { z } from "zod";
export const MediaAssetSchema = z.object({
id: z.string().min(1),
key: z.string().min(1),
filename: z.string().min(1),
contentType: z.string().min(1),
byteSize: z.number().int().nonnegative(),
width: z.number().int().positive().optional(),
height: z.number().int().positive().optional(),
aspectRatio: z.string().optional(),
altText: z.string().default(""),
uploadedAt: z.string().datetime().optional(),
});
export type MediaAsset = z.infer<typeof MediaAssetSchema>;

Run unit tests and type checks from the repository root:

Terminal window
# Run tests for @siteswarm/media
pnpm --filter @siteswarm/media test
# Run TypeScript type check
pnpm --filter @siteswarm/media check
# Run monorepo governance and type check
pnpm run check