Files & renderer
Two features for file uploads and email templates.
files-provider-s3
Status: ✅ Stable
What: S3-compatible file-storage backend. Targets AWS S3, Cloudflare R2, MinIO, Backblaze B2 — anything that speaks the S3 API. Ships upload URLs (presigned), read URLs (direct or presigned), delete operations.
How it works: createS3Provider({ region, bucket, accessKey, secretKey, endpoint?, forcePathStyle? }) builds a provider that the
framework file layer uses. forcePathStyle matters for MinIO + R2
(they don’t support virtual-host style). Per-tenant buckets are
possible via bucket: (tenantId) => \tenant-${tenantId}-files“ —
gives natural tenant isolation at the S3 level.
Env helper: createS3ProviderFromEnv() reads the standard S3
env vars (AWS_ACCESS_KEY_ID etc.) — fastest setup for single-tenant
apps, no code path change when you migrate later.
Example:
import { createS3Provider } from "@kumiko/bundled-features/files-provider-s3";
const fileProvider = createS3Provider({ region: "eu-central-1", bucket: "kumiko-app-files", accessKey: process.env["AWS_ACCESS_KEY_ID"]!, secretKey: process.env["AWS_SECRET_ACCESS_KEY"]!,});
// In a handler: presigned upload URL for the browserr.writeHandler({ qn: "files:upload-url", handler: async (ctx, { filename }) => { return fileProvider.uploadUrl({ key: `tenant-${ctx.tenantId}/${filename}`, contentType: "application/octet-stream", expiresIn: 600, // seconds }); },});renderer-simple
Status: ✅ Stable
What: Minimal HTML renderer for email templates. No React, no
SSR pipeline — just a template string with {{ placeholders }} and
a list of helpers (i18n, date format, URL builder).
How it works: an email template is a function
(data, helpers) => { html, text }. renderer-simple is registered
as a renderer implementation in delivery and handles layout
inheritance (header + footer reusable) and plain-text fallback (text
variant by stripping HTML).
When not: marketing mails with complex layouts → render with an
external tool like MJML or unlayer and pipe the output through
unchanged. renderer-simple is for transactional mails (reset
link, incident update, welcome) — what the app sends itself, not
what marketing sends.
Example:
import { createRendererSimpleFeature } from "@kumiko/bundled-features/renderer-simple";
features: [createRendererSimpleFeature(), createDeliveryFeature(), /* ... */];
// Define a template (in your feature)r.template({ id: "incident-created", renderer: "simple", render: ({ data }) => ({ subject: `[${data.tenant}] Incident: ${data.title}`, html: `<h1>${data.title}</h1><p>${data.body}</p>`, text: `${data.title}\n\n${data.body}`, }),});See also
- Bundled-features overview
- Notifications —
deliveryusesrenderer-simplefor email templates