npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@forge-cms/s3

v0.13.3

Published

S3-compatible StorageAdapter for ForgeCMS (AWS S3, Garage, Backblaze B2, Wasabi and other S3 API services).

Readme

@forge-cms/s3

A server-side StorageAdapter for ForgeCMS backed by the S3 API (AWS SDK for JavaScript v3). One adapter, configured per service — not a family of provider integrations.

pnpm add @forge-cms/s3
import { S3StorageAdapter } from '@forge-cms/s3';

const storage = new S3StorageAdapter({
  bucket: env.S3_BUCKET,
  region: env.S3_REGION,
  endpoint: env.S3_ENDPOINT, // omit for AWS S3
  credentials: {
    accessKeyId: env.S3_ACCESS_KEY_ID,
    secretAccessKey: env.S3_SECRET_ACCESS_KEY
  }
});

Pass it to ForgeCmsRuntime as adapters.storage. The adapter is server-side only: it holds credentials and must never be imported by browser code (@forge-cms/angular and @forge-cms/admin do not depend on it). Your application reads its own environment and passes values in explicitly; the adapter reads no environment variables itself.

Options

| Option | Required | Meaning | | ---------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | bucket | yes | Existing bucket. Forge never creates buckets, policies or IAM. | | region | yes | Region / signing region. Never defaulted. S3-compatible services publish the value to use (Garage: its s3_region). | | endpoint | no | Absolute http:/https: URL of an S3-compatible service. Omit for AWS S3. Plain http: is for local development and tests only — use HTTPS elsewhere. | | credentials | no | { accessKeyId, secretAccessKey, sessionToken? }. Omit to use the AWS SDK's normal server-side credential resolution (env, shared config, IAM role…). | | forcePathStyle | no | AWS SDK v3 forcePathStyle, default false. Many self-hosted services need true. | | publicUrlBase | no | Base of getPublicUrl(). Default /api/media. See Public URLs. |

An empty bucket/region, a non-http(s) endpoint or blank explicit credentials throw a clear configuration error at construction. Error messages never contain credentials or the endpoint string.

Behaviour

Same contract as the InMemory and R2 adapters (runStorageAdapterContractTests):

  • put keeps exact bytes, content type and custom metadata. Accepts Blob, ArrayBuffer, Uint8Array and ReadableStream; the object is buffered in memory (upload size limits belong to Forge's upload handler). No multipart or presigned uploads.
  • get returns the bytes as an ArrayBuffer, or null only when the key does not exist (NoSuchKey/NotFound). A missing bucket, 403, bad credentials, network failure or a service error rejects — never null.
  • delete is idempotent: a missing key resolves. Other failures reject.
  • list(prefix?) returns every matching object (key, size); provider pages are followed transparently. Content type and metadata are not populated by list (no per-object requests).
  • S3 normalises custom metadata keys to lowercase and values must be ASCII; use lowercase keys.

Public URLs and access

getPublicUrl(key) percent-encodes each key segment and keeps / as the hierarchy separator: media/id-my photo #1.png → /api/media/media/id-my%20photo%20%231.png.

  • Default /api/media (recommended). URLs go through Forge's handleFile, which finds the owning document, applies collection/row access and draft visibility, and sets cache policy (public vs authenticated private, no-store). The bucket can stay private.
  • A direct bucket/CDN base (publicUrlBase: 'https://cdn.example.com') is opt-in and bypasses handleFile and every Forge access check. Setting it does not make anything public; making the bucket or CDN readable is your infrastructure's job, and the adapter configures no bucket policy.

Provider profiles

Only Garage is exercised in CI. The others are configuration examples for services that implement the S3 API; nothing here claims they are tested.

AWS S3 — configurable, not CI-certified

new S3StorageAdapter({
  bucket: 'my-forge-media',
  region: 'eu-west-1'
  // credentials omitted → AWS SDK provider chain (e.g. an IAM role); or pass them explicitly.
  // no endpoint; forcePathStyle stays false
});

Forge does not provision the bucket or IAM permissions (needs s3:GetObject, s3:PutObject, s3:DeleteObject, s3:ListBucket).

Garage — certified in CI (spec 082)

new S3StorageAdapter({
  bucket: 'forge-s3-test',
  region: 'garage', // Garage's `s3_region`
  endpoint: 'http://127.0.0.1:3900', // local HTTP: development and tests only
  forcePathStyle: true,
  credentials: { accessKeyId: 'GK…', secretAccessKey: '…' }
});

pnpm test:s3 (repository root) starts a pinned dxflrs/garage container, creates a throwaway key and bucket, and runs the shared contract plus focused cases. It needs Docker and fails without it.

Backblaze B2 (S3-compatible API) — configuration example — not part of the P01 CI certification matrix

new S3StorageAdapter({
  bucket: 'my-bucket',
  region: 'us-west-004', // must match the endpoint's region
  endpoint: 'https://s3.us-west-004.backblazeb2.com',
  credentials: { accessKeyId: env.B2_KEY_ID, secretAccessKey: env.B2_APPLICATION_KEY }
});

Wasabi — configuration example — not part of the P01 CI certification matrix

new S3StorageAdapter({
  bucket: 'my-bucket',
  region: 'eu-central-1',
  endpoint: 'https://s3.eu-central-1.wasabisys.com',
  credentials: { accessKeyId: env.WASABI_ACCESS_KEY, secretAccessKey: env.WASABI_SECRET_KEY }
});

Check your provider's documentation for the exact endpoint and whether it requires path-style addressing.

Limits (P01)

Basic adapter only. Not included: presigned or browser-direct uploads, multipart uploads, streaming responses, CDN features, image transforms, bucket creation, lifecycle policies, backup/restore of objects, and the libSQL + S3 upload lifecycle guide (later 0.10 work).