Skip to content

Ecosystem ​

Batteries are s200's unit of middleware reuse: an opt-in module behind its own package entry, options in, Middleware out (see Authoring Batteries for the full contract). This page is the registry — what ships in the box, and how a third-party battery joins the list.

Official ​

Everything below ships in the s200 package itself: one source file per entry, imported as import { … } from 's200/<entry>' — pulling one entry never pulls another. All entries are zero-runtime-dependency except s200/events, which builds on @for-fun/event-emitter (its only dependency — the core stays dependency-free). Entry sizes are tracked per release by the repo's size-limit budgets (pnpm size).

Middleware ​

EntryGives you
s200/corscors — preflights answered in place, headers stamped on the unwind (fallbacks included)
s200/loggerlogger — one line per request, every request (500s included)
s200/request-idrequestId — seeds an id, stamps x-request-id on the unwind
s200/timeouttimeout — narrows ctx.signal, races the chain against a deadline
s200/etagetag — If-None-Match revalidation over byte-backed bodies
s200/compresscompress — gzip/deflate/br via CompressionStream
s200/secure-headerssecureHeaders — the OWASP header set
s200/cachecache — response caching with freshness validators
s200/rate-limitrateLimit — sliding-window limiter with an injectable store
s200/trust-proxytrustProxy — honest client IPs behind a proxy
s200/versionapiVersion — header/Accept API versioning gate, ctx.state.version
s200/alsals, currentCtx — ambient ctx over node:async_hooks (node/bun; not edge)

Auth & sessions ​

EntryGives you
s200/authbasicAuth, bearerAuth — challenge-carrying gates
s200/jwtsignJwt, verifyJwt, jwtAuth — JWS over WebCrypto
s200/oauthcreatePkcePair, buildAuthorizeUrl, exchangeCode — OAuth2 code + PKCE primitives
s200/csrfcreateCsrf — double-submit tokens
s200/sessioncreateSession — cookie sessions with an injectable store
s200/cookiesgetCookie, setCookie, getSignedCookie, setSignedCookie

Bodies & streaming ​

EntryGives you
s200/acceptsaccepts — RFC 9110 content negotiation
s200/queryparseQuery, queryParams — typed query strings
s200/validatevalidate, jsonBody — schema or hand-rolled parse fns
s200/serializeserialize, jsonRaw — declarative response shapes
s200/multipartstreamForm — streaming multipart parts, per-part callbacks
s200/uploaduploadForm — gated uploads into an injected sink
s200/streamingstream, streamSSE — stream and SSE responses

Realtime ​

EntryGives you
s200/websocketupgradeWebSocket — runtime-neutral WS upgrade
s200/websocket/nodecreateUpgradeHandler — the node http server side
s200/websocket/buncreateBunWebSocketBridge — the Bun side
s200/websocket/cloudflarecreateCloudflareWebSocketBridge — the Workers side

Introspection & tooling ​

EntryGives you
s200/route-tablecreateRouteTable — the app as JSON, no execution
s200/metadescribeRoute, describeApp — route annotations
s200/openapiopenapiSpec, openapiJson — the OpenAPI 3.1 document
s200/swaggerswaggerUi — serve the docs UI
s200/codegengenerateClient — typed clients from a route table
s200/clientcreateClient — the typed fetch client
s200/testrequest, testClient, probeApp — in-process app driving
s200/oteltrace — OTel spans per request
s200/devcreateHotApp, importFresh, watchAndReload, routeHints — hot table + dev 404 hints

Operations ​

The application-lifecycle layer — NestJS's enableShutdownHooks, Terminus, @nestjs/config, @nestjs/schedule, and EventEmitter support, in data + functions form:

EntryGives you
s200/lifecyclelifecycle — signal-driven graceful shutdown: flip readiness, stop listening, drain in-flight, run cleanup
s200/healthhealth, readiness, createGate — liveness/readiness probes over injectable checks
s200/configparseEnv, createConfig — Standard-Schema-typed, fail-fast config
s200/schedulecreateScheduler, nextRun — cron (5-field) and interval jobs, injectable clock
s200/eventscreateBus — typed event bus over @for-fun/event-emitter (sync emit, emitAsync)

Sharding & scaling ​

The horizontal-scaling layer — one app, partitioned across units, with the dispatch seam kept runtime-neutral:

Experimental. All five entries in this table are experimental: their APIs may change or be withdrawn in any minor, they are not yet battle-tested in production, and the thread-executor fast path depends on Node-internal listener-transfer behavior (Node ≥26.6).

EntryGives you
s200/shardpolicy, shardSpecs, shardApp, matchShard — route policy annotations, the serializable shard descriptor, partition, and the dispatch seam
s200/gatewaynginxConf, gatewayRoutes, albRules — pure generators: the same specs → nginx.conf / Gateway API HTTPRoute / ALB listener rules
s200/shard-devcreateDispatcher — in-process dispatcher with the same prefix semantics — local parity without nginx
s200/executorrunShards — executors (inline / thread / process / external) + supervisor: lazy thread spawn with Node ≥26.6 listener transfer, forked process shims, restart with backoff
s200/unit-metricscreateUnitMetrics, unitMetricsEndpoint — the scheduler's eyes: in-flight, queue depth, event-loop utilization, RSS, warm/cold — as middleware + endpoint

Full guide: Sharding & Scheduling.

Runtime adapters ​

Not middleware — one line each to serve the same app on another runtime:

EntryGives you
s200/nodeserve (light mode, TLS/HTTP2, WSS upgrade) + file readers
s200/bunserve + the Bun file readers
s200/cloudflarecreateHandler — the module worker's default export

Recipes ​

Repository-level TypeScript, deliberately not package exports — vendor the file, bring your own client, inject:

RecipeFor
recipes/redis-stores.tsshared rateLimit/createSession stores over Redis — full guide: Distributed Stores
recipes/oidc.tscreateOidc — OIDC Authorization Code + PKCE (S256) login over s200/cookies/jwt/session — full guide: OIDC Login

Battery ideas — good first contributions ​

The official batteries stay lean on purpose. The list below is what the registry would love to see third parties ship — none of it is on an official roadmap; every idea extends an existing entry through its public seams (option objects, injected stores, Middleware composition), and the standard submission template applies to each.

  • Cron nicknames and day-of-month/day-of-week extensions (@daily, L, W) for s200/schedule — the shipped parser is strict 5-field cron.
  • The OpenTelemetry logs signal plus an OTLP exporter for s200/otel — the shipped entry is zero-dependency with an injectable tracer/meter; a wire-protocol exporter is the canonical case for a listed dependency.
  • More Scalar documentation knobs (layout, dark-mode default, custom CSS) for s200/swagger — beyond the shipped url/title/theme/cdn options.
  • OAuth providers (GitHub, Google, …) on top of s200/oauth + s200/session — the shipped s200/oauth covers the PKCE/auth-url/ token-exchange primitives; the full generic OIDC login flow is the recipes/oidc.ts recipe, and s200/auth the challenge-carrying basics.
  • More shared stores for s200/session and s200/rate-limit — Postgres, SQLite, … following the injectable-store seam the recipes/redis-stores.ts recipe sets (Redis is already covered there).
  • Locale fallback chains on s200/accepts — the shipped accepts().language() picks the best of the provided tags; BCP 47 region fallback and Intl-aware matching are the natural extension.

Submit your battery ​

The registry below is for third-party batteries — anything importable that follows the battery contract. Before you submit, check whether one of the official entries above already covers it; a smaller core with a healthy ecosystem around it is the point.

Inclusion criteria ​

  • Data + functions paradigm. A factory takes options and returns a Middleware (or data) — no classes, no this, no module-level registration side effects. Authoring Batteries walks the two runtime shapes (gates and unwind stampers) with examples from the built-ins.
  • Tree-shakable. One entry per capability, ESM, side-effect-free module scope — importing your battery must not pull anything the importer did not name.
  • Zero runtime dependencies preferred. Everything else is Web Standard or injected by the caller (stores, schemas, clocks, verifiers, sinks) — this is how the built-ins stay zero-dependency. A battery with a dependency can still be listed; the dependency column exists so importers see the cost.
  • Honest size. The number in your row is the gzipped byte cost of the entry as an importer actually pulls it (e.g. size-limit, or gzip -9 < dist/entry.mjs | wc -c), measured, not estimated.

Submission template ​

Open a PR against this page adding one row to the registry table, filled per this template:

md
| Name | Entry | Depends on | Size (gz) | Notes |
| ---- | ----- | ---------- | --------- | ----- |
| your-battery | `your-battery` | — | 0.4 kB | one line: what it does, and which seam it plugs (gate / unwind stamper / data) |
  • Name — the npm package name, prefixed s200-battery- if you want the namespace read as an ecosystem convention (not required).
  • Entry — the subpath importers write, e.g. s200-battery-og/image (one row per entry; multi-entry packages get one row each).
  • Depends on — runtime dependencies, — when none. Peer dependencies count, with the version range.
  • Size (gz) — measured gzipped entry size (see criteria above).
  • Notes — one line, present tense: what it does. Link the repo docs in the Name cell.

Process ​

  1. Author per the Authoring Batteries guide; ship tests and a README with the measured size.
  2. Open a PR that adds your row to the registry table below — the table is the review surface, and landing the PR is the listing.
  3. Rows whose package stops resolving or whose size drifts un-communicated get one notice PR, then removal.

Registry — third-party batteries ​

NameEntryDepends onSize (gz)Notes

The table starts empty on purpose — no placeholder rows. Each cell follows the submission template above; the first real entry sets the precedent.

Released under the MIT License.