Skip to content

v0.11.0

Choose a tag to compare

@pathosDev pathosDev released this 15 Jul 20:12
· 1890 commits to main since this release
feaa8a0

The consistency + fail-fast release. A repo-wide naming sweep lands one vocabulary everywhere — no abbreviations, Options never Settings, Websocket casing — as hard cuts with migration notes. Underneath it, a new OptionsValidator / OptionsError layer makes every configurable thing fail fast on invalid values, on every input path (builder, plain object, HOCON). Plus scoped HTTP error handling, a security-middleware suite, static file serving, and a broad batch of WebSocket/HTTP security hardening from the audit backlog. Pre-1.0 — this minor carries breaking changes; see below.

🚀 New features

Options validation

  • OptionsValidator + OptionsError (#274) — a declarative-but-code validator layer for the XOptions pattern. Validation runs once at consume time on the merged settings, so builder, plain-object, and HOCON inputs are all checked and cross-field rules see the final values.
  • Validators shipped for ~40 options families — every broker (MQTT, Kafka, AMQP, Redis Streams, NATS, JetStream, SSE, TCP, UDP, gRPC client) and the WebSocket client; cluster core, sharding, singleton, and downing strategies; discovery + gossip intervals; leases; caches (RedisCache, MemcachedCache, CachedSnapshotStore, InMemoryCache); CassandraJournal and the S3 / filesystem object-storage backends; the Express/Hono HTTP backends; HTTP middleware + directives; WebSocket routes + resolved policy; WorkerCluster, ProducerController, TestProbe, CircuitBreaker, and BoundedMailbox.
  • RateLimitOptions / IdempotencyOptions fluent buildersrateLimit and idempotent gained the real builders they were already documented to have; the plain-object call form is unchanged.
  • withMaxDecompressedBytes store option — the 512 MiB decompression-bomb guard (#3) is now tunable per object-storage store (Infinity opts out).
  • Per-route WebSocket connection cap — opt-in maxConnections on websocket() routes (builder or actor-ts.http.websocket.maxConnections); upgrades beyond the cap close with 1013 before an actor is wired.

HTTP

  • Scoped error handling + fallback routes (#352) — handleErrors(handler, child) catches exceptions from a subtree, fallback(handler) answers unmatched requests, ServerBuilder.withErrorHandler(...) is the server-wide last resort. Uniform precedence across Fastify/Express/Hono.
  • Security-middleware suite (#353) — cors (compiler-expanded per-pattern preflight routes), strictTransportSecurity/hsts, contentSecurityPolicy, csrfProtection + requireSameOrigin, securityHeaders, requestId, BasicAuth, requestTimeout — each with an XOptions builder. Plus public parseCookies / serializeCookie.
  • Static file serving (#354) — getFromFile, getFromDirectory, getFromBrowseableDirectory: MIME detection, conditional requests (weak ETag + Last-Modified → 304), single Range (206/416), HEAD, and XSS-safe listings. Plus a MIME-type registry (contentTypeFor) and streaming response bodies (ReadableStream<Uint8Array>) on all three backends.
  • HTML response utilities (#352) — escapeHtml, an auto-escaping html tagged template with a SafeHtml brand, rawHtml, completeHtml.

⚠️ Breaking changes (pre-1.0)

  • WebSocket → Websocket (single-cap), no Ws abbreviationWebSocketServerActor/WebSocketClientActorWebsocketServerActor/WebsocketClientActor, Ws* supporting types → Websocket*, wsSend()websocketSend(), module moved src/http/ws/src/http/websocket/. The websocket() directive, the global WebSocket, and the Sec-WebSocket-Protocol header are unchanged.
  • Abbreviations spelled out in type/member names: *Cmd*Command, *Msg*Message, *Ack*Acknowledgment, ByPid*ByPersistenceId*, *Impl*Implementation, *Ctor*Constructor. Testkit too: TestProbe.expectMsg()expectMessage(), expectMsgType()expectMessageType(). Wire/discriminator string literals are unchanged.
  • One config vocabulary: Options, never Settings — remaining *Settings types → *OptionsType; BrokerSettings.ts folded into BrokerOptions.ts (BrokerSettingsErrorBrokerOptionsError); the BrokerActor glue renamed (readOptionsFromConfig / requiredOptions / builtInDefaultOptions / options).
  • Command vs Signal unified on kind — MQTT and WebSocket internal mailbox signals are kind-tagged plain objects; the bad-payload hook is onInvalidMessage everywhere (MQTT's onDecodeError is gone); WebSocketAcceptSignalWebsocketAcceptCommand.
  • Invalid option values throw OptionsError (#274) at construction / actor start instead of a bare Error — and previously-unchecked builder/plain-object paths are now checked. Missing required broker settings still throw BrokerOptionsError; malformed HOCON still throws ConfigError.
  • InMemoryCache joins the XOptions familyInMemoryCacheOptions builder + validator + HOCON defaults under actor-ts.cache.in-memory; the internal InMemoryCacheSettings interface is removed (a plain { maxEntries, cleanupMs } object still works).
  • CircuitBreaker + BoundedMailbox validate their optionsOptionsError instead of bare Error; maxFailures/resetTimeoutMs and capacity are required at runtime (a builder without them previously produced a breaker that never opened / an unbounded "bounded" mailbox); callTimeoutMs: 0 now throws (omit it to disable).
  • HTTP structural types — the Route / CompiledEndpoint unions gain fallback and cors variants (exhaustive matches must handle them); ServerBuilder gains a required withErrorHandler; HttpError gains an optional 4th headers parameter and BearerTokenAuth 401s expose the challenge on err.headers['www-authenticate'] (#352, #353).

🔒 Security

  • WS-1 (HIGH) — WebSocket upgrade crash hardened — a malformed percent-escape in the upgrade path was process-fatal (unhandled rejection) on the Express backend, reachable pre-auth. Now a non-match → 404, with a last-resort socket guard.
  • WS-2 (HIGH) — Cross-Site WebSocket Hijacking (CSWSH) defence — new allowedOrigins on websocket() routes: a listed-but-wrong Origin is rejected with 403 before the handshake on all three backends.
  • HTTP-1 (MEDIUM-HIGH) — Hono body-size cap enforced before buffering — oversized Content-Length now rejects with 413 before reading the body.
  • HTTP-2 (MEDIUM-HIGH) — InMemoryCache is bounded (LRU) — attacker-chosen keys (idempotency, rate-limit) can no longer grow the default cache without limit; defaults maxEntries: 10_000, background sweep every 60 s.
  • HTTP-4 (MEDIUM) — idempotency responses can be scoped per caller — opt-in identity: (req) => string folds the authenticated principal into the cache key, preventing cross-user response disclosure.
  • #3 (MEDIUM) — decompression-bomb cap on stored bodiesdecodeBody caps decompressed size at 512 MiB by default, now tunable per store.
  • WS-3 (MEDIUM) — transport-level WebSocket frame cap (Express + Fastify) — oversized frames are rejected at the protocol level (1 MiB) instead of being buffered in full first.
  • WS-4 (MEDIUM) — WebSocket backpressure works on Hono — the adapter now surfaces bufferedAmount, so maxBufferedBytes / onBackpressure are no longer no-ops there.
  • WS-5 (MEDIUM, partial) — per-route connection admission cap — see New features; the handshake/idle-timeout and hub-mailbox-bounding sub-parts remain tracked follow-ups.
  • WS-6 (LOW) — CRLF stripped from raw upgrade-reject headers — no response splitting via app-supplied header values on the Express reject path.
  • BRK-1 / BRK-2 (MEDIUM) — inbound buffer caps for TCP lines + SSE — un-delimited streams now drop the connection instead of buffering forever.
  • #6 (LOW) — consistent SQL/CQL identifier validation — SQLite and Cassandra now validate config-sourced identifiers like Postgres/MariaDB already did.
  • #9 (hardening) — JSON deserialization ignores the __proto__ setter — hostile payloads can't change a decoded object's prototype.
  • CORS, CSRF, and security-header middleware (#353) — correct preflight handling, HMAC-signed double-submit CSRF token, HSTS/CSP/COOP/CORP/nosniff/frame-options, constant-time secret comparisons; WWW-Authenticate challenges now reach the wire.
  • Hardened path-traversal defence for static files (#354) — full pre-validation decode, segment rejection (.., NUL, backslash, drive/ADS), symlink confinement, uniform 404s.

📚 Documentation

  • Server-WebSocket page moved from IO into the HTTP section (#351); stale API-reference pages reconciled with the shipped code (#360).
  • Middleware pages corrected to actual behavior: rate-limit headers + keying on req.remoteAddress (HTTP-3), idempotency in-flight → 409, response-cache single-flight (EN + DE).
  • The reference config now documents the actor-ts.http.websocket policy section; *Settings prose repointed to the *OptionsType vocabulary (#349).

Full changelog: CHANGELOG.md · 2665 tests green, ~94 % line coverage.