v0.21.0
✨ Added
Runtime
- Built-in
fx.failhelpers (notFound,forbidden,conflict,foreignKey,unauthorized,rateLimited,serviceUnavailable,database,internal) are always on — noerrors: { NotFound }bag required. Domain codes (OutOfStock,FlightFull, keelDuplicate) still go onerrors:. okengine/client/explain—match/matchError/explaincollapse unknown flow and transport errors into a UX kind (auth,missing,invalid, …). Dispatch: named code → kind arm → required_.matchtakes the whole{ data, error }envelope (ok+ those arms).ValidationErrorissues type as{ message, path }[](explain.fieldsfor forms).fx.storeSQL unique / FK / NOT NULL / CHECK / invalid / too_long / out_of_range / connection failures become typedfailvalues (same family asValidationError). Serialization / deadlock / lock-busy (40001/40P01/55P03, SQLite BUSY) stay thrown soflow.retrycan run, thenServiceUnavailable. Redis connection /CLUSTERDOWN/READONLYmap toServiceUnavailable;BUSY/TRYAGAINfollow the same retry-then-503 path.WRONGTYPEstaysInternalError. Filesfs/s3AccessDeniedand path-escape keys map toForbidden;SlowDown/EBUSYfollow retry-then-503;NoSuchBucket/ENOSPCareServiceUnavailable.- Boot fatals print cause + fix (
formatFatalError).VaultBootErroris **OKE1510**. No TTY stack unlessOKE_DEBUG=1. Request-line chips includeerror.code(409 Conflict).
Docs
- Site header adds a Discord square before npm (blurple tile, invite
https://discord.gg/j2hkZZbnp), matching npm’s flush square cell and Clyde glyph height to the npm “n”, including the mobile menu. - Flow / HTTP / Store / Errors / Vault / Try It document always-on helpers, honest 404/409/503 tables, SQL / KV / files auto-fail, and the OKE1510 TTY dump.
- Errors reference catalogs
fx.failhelpers, always-on vs domainerrors:, HTTP statuses, and SQL / KV / files store auto-map. - Calling documents
match/matchError/explain(kind arms, required_, dispatch order) and JSONNotFoundvs a plain-text router 404. Errors smallest example usesmatch.
💥 Breaking Changes
Runtime
- Built-in HTTP statuses:
NotFound**404** (was 400),Conflict/ForeignKey**409**,AuthRateLimited**429** (was 400),ServiceUnavailable**503**. Domain codes stay **400**. Router miss remains plain-text404. - SQL unique violations are
Conflict**409**, not500 InternalError. InternalErroruses the catalog message only — it no longer copies the thrownError.message.- Typed client
ContractErrorsalways includes built-in codes; a declared key still wins (shorter{ code }tightening). store.resourceno longer stamps Manifesterrors: { NotFound }by default.
♻️ Changed
Runtime
- Export gzip regression baselines refreshed in
budgets.jsonafter always-onfx.failhelpers, store SQL/KV/files auto-map, and i18n catalog strings (kernel edge 16.99 kB and client 4.99 kB still under the 17 kB / 5 kB absolute caps).
Docs
- Try It is a first-app walkthrough: scaffold file map,
GET/POST /usersin one routes file, persist, then Signal + Channel, then split files /http.resourceand short Gate · Clock · Vault · AI explainers. - Try It Create explains why Bun ≥ 1.4.2 and Docker Compose (host app, production-shaped data plane) and links the install pages.
- Try It puts **The files** before First HTTP — roles (Flow, Store, root, boot), not the blank tree as the only layout.
- Try It HTTP contracts can live in the router file or in
shapes.ts. - Try It First HTTP explains the
{ data, error }envelope (success vsValidationError/fx.fail). - Try It First HTTP explains
.public()— required posture, open with no session. - Try It First Signal declares in
src/signals/, binds a worker insrc/flows/workers/, and emits from create ({ key }foronce). Code samples on the page focus the lines that change; earlier context dims.
Dev, Keel & create-oke
- Compose default RustFS pin:
1.0.0-rc.5→1.0.0(stable, novprefix). - create-oke shorter and examples/keel use
fail.notFound/fail.forbidden/fail.conflicthelpers. Keel drops redundanterrors: { NotFound }bags and keeps domainDuplicate/Unavailable. Shorter keeps{ code }tightening on NotFound / Forbidden / Conflict.
🐛 Fixed
Runtime
- Untriggered RPC flows (
POST /_oke/...) returning unhandled throws now serialize to 500InternalError(or store auto-mapped failure) instead of empty204 No Content. fx.call()andapp.call()rethrow unhandled exceptions across flow boundaries instead of returningundefined.
Client
TransportErrorruntime instances now consistently populateerror.messagematchingerror.data.message. The type requiresmessage(same string asdata.message).- SSE handshake failure decoding prioritizes top-level
error.messagewhile preserving fallback toerror.data.messageanderror.code. - Typed client
ContractErrorstreats a missingerrorsbag as the built-in map — a phantomRecord<string, unknown>no longer wipesNotFound.id. matchsuccessokdata is the success type, notO | null.
Docs
- Calling / Errors document
TransportError.message === error.data.message. Flow / fx note thatfx.call/app.callrethrow unhandled throws (not silentundefined).