@molecule/api-deploy-target-s3
Provider bond · deploy-target · API (Node) · v1.1.1 · Apache-2.0
Static-site deploy target for molecule.dev on any S3-compatible bucket (Tigris, Cloudflare R2, AWS S3, MinIO)
npm install @molecule/api-deploy-target-s3npm · Source on GitHub · Implements @molecule/api-deploy-target
How it works
@molecule/api-deploy-target-s3 is a provider bond on the API (Node) side: it implements the deploy-target core interface (@molecule/api-deploy-target) with a concrete vendor or library behind it.
Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.
import { setProvider } from '@molecule/api-deploy-target'
import { createS3DeployTarget } from '@molecule/api-deploy-target-s3'
setProvider(
'static',
createS3DeployTarget({
bucket: 'my-sites',
publicBaseUrl: 'https://my-sites.fly.storage.tigris.dev',
endpoint: 'https://fly.storage.tigris.dev',
accessKeyId: process.env.SITES_KEY_ID,
secretAccessKey: process.env.SITES_SECRET,
}),
)
// Or configure from DEPLOY_TARGET_S3_* env vars:
import { provider } from '@molecule/api-deploy-target-s3'
setProvider('static', provider)Works with: @molecule/api-deploy-target
Reference
S3-compatible static-site deploy target for molecule.dev.
Implements @molecule/api-deploy-target for apps with no server — a
prerendered site or a client-routed SPA — by publishing the build output to
a publicly readable bucket on any S3-compatible store (Tigris, Cloudflare R2,
AWS S3, MinIO). No machine is created, nothing boots, nothing idles: a deploy
is N object PUTs plus one manifest, and removing a site is a prefix delete.
Each deploy is an immutable release under
<keyPrefix><siteId>/<releaseId>/ — files/<path> for every file and
manifest.json (a StaticSiteManifest) written LAST, so a release whose
manifest exists is complete. The release's origin is
{ kind: 'static-files', filesBaseUrl, manifestUrl } — public URLs an edge
serves the site from.
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
import { setProvider } from '@molecule/api-deploy-target'
import { createS3DeployTarget } from '@molecule/api-deploy-target-s3'
setProvider(
'static',
createS3DeployTarget({
bucket: 'my-sites',
publicBaseUrl: 'https://my-sites.fly.storage.tigris.dev',
endpoint: 'https://fly.storage.tigris.dev',
accessKeyId: process.env.SITES_KEY_ID,
secretAccessKey: process.env.SITES_SECRET,
}),
)
// Or configure from DEPLOY_TARGET_S3_* env vars:
import { provider } from '@molecule/api-deploy-target-s3'
setProvider('static', provider)
Type
provider
Installation
npm install @molecule/api-deploy-target-s3 @aws-sdk/client-s3 @molecule/api-deploy-target @molecule/api-proxy-agent
API
Interfaces
S3SendClient
The subset of an S3 client this target uses — an S3Client satisfies it.
interface S3SendClient {
/**
* Send one S3 command.
*
* @param command - The command.
* @returns The command's output.
*/
send(command: unknown): Promise<unknown>
}
S3DeployTargetConfig
Configuration for createS3DeployTarget.
interface S3DeployTargetConfig {
/** Bucket the sites are published to. It must be publicly readable. */
bucket: string
/**
* Public URL the bucket's objects are readable at (no trailing slash
* needed): the object `sites/a/b/manifest.json` must be served at
* `${publicBaseUrl}/sites/a/b/manifest.json`.
*/
publicBaseUrl: string
/** S3 API endpoint for S3-compatible stores; omit for AWS S3. */
endpoint?: string
/** Region (`auto` for Tigris and R2). Default `auto`. */
region?: string
/** Path-style addressing (MinIO). Default `false`. */
forcePathStyle?: boolean
/** Access key id; omit to use the SDK's default credential chain. */
accessKeyId?: string
/** Secret access key; required with `accessKeyId`. */
secretAccessKey?: string
/** Key prefix every site lives under. Default `sites/`. */
keyPrefix?: string
/** Provider name recorded on each release. Default `s3`. */
name?: string
/** Milliseconds to wait for the S3 connection to open. Default 10000. */
connectionTimeoutMs?: number
/** Milliseconds of socket inactivity after which a stalled S3 request is aborted. Default 60000. */
socketTimeoutMs?: number
/** Parallel uploads per deploy. Default 16. */
concurrency?: number
/**
* A client to use instead of building one — for tests, or to share a
* client the application already configured.
*/
client?: S3SendClient
}
Functions
contentTypeFor(path, given)
The Content-Type a file is served with: the caller's, else by extension,
else application/octet-stream.
function contentTypeFor(path: string, given?: string): string
path— Site-relative path.given— A content type the caller supplied, if any.
Returns: The content type.
invalidSitePath(path)
Validate a site-relative path. Refused, never normalized: a normalized path is a file the caller did not ask for.
function invalidSitePath(path: string): string | null
path— The path.
Returns: The problem, or null when it is fine.
createS3DeployTarget(config)
Create a static-site deploy target backed by an S3-compatible bucket.
function createS3DeployTarget(config: S3DeployTargetConfig): DeployTargetProvider
config— Bucket, public URL, endpoint and credentials.
Returns: The target.
Constants
provider
The S3 deploy target configured from DEPLOY_TARGET_S3_* env vars. Use
createS3DeployTarget to pass configuration explicitly instead.
const provider: DeployTargetProvider
Core Interface
Implements @molecule/api-deploy-target interface.
Bond Wiring
Setup function to register this provider with the core interface:
import { setProvider } from '@molecule/api-deploy-target'
import { provider } from '@molecule/api-deploy-target-s3'
export function setupDeployTargetS3(): void {
setProvider(provider)
}
Injection Notes
Requirements
Peer dependencies:
@molecule/api-deploy-target^1.0.0
Runtime Dependencies
-
@aws-sdk/client-s3 -
@molecule/api-deploy-target -
@molecule/api-proxy-agent -
The bucket must be publicly readable, and
publicBaseUrlmust be the URL its objects are served at (https://<bucket>.fly.storage.tigris.dev,https://pub-<id>.r2.dev). The object store itself is not the web server: it has no directory indexes, base paths or SPA fallback, so put an edge in front that reads the manifest (see@molecule/api-deploy-target) — do not hand visitorsfilesBaseUrllinks. -
Every file is given site-relative (
/index.html,/assets/x.js) even when the site is built for a base path — the base goes inrouting.basePath./index.htmlis required; a path with..,., an empty segment, a backslash or a trailing slash is REFUSED, not normalized. -
A failed or cancelled deploy removes what it uploaded and rejects (
error.code === 'cancelled'for a cancellation); it never touches an earlier release. After switching traffic to a new release, reclaim the others withremove(siteId, { keepReleaseIds: [releaseId] }). -
Uses its OWN credentials (
DEPLOY_TARGET_S3_*for the env-configuredprovider, orcreateS3DeployTarget(config)), never theAWS_*ones an app's uploads bucket uses — publishing sites and storing user uploads are different buckets with different access. -
Checksums are sent only where S3 requires them, because some S3-compatible stores reject the SDK's default CRC32 trailers on PUT. Egress honours
HTTPS_PROXY/NO_PROXYthrough@molecule/api-proxy-agent.