Changelog - 2026-09-11
Kernel hardening
Enhancement Bug Fix Behavior ChangeIn one line. Four behaviors every consumer worked around by hand are now the framework's defaults, and two silent data-layer bugs are fixed.
What changed
- One logger per scope.
Logger.getInstance({ scope })returns one instance per scope, so a line logged byDefaultAuthServicesays[DefaultAuthService]. It used to return the first instance ever created, whatever scope was asked for.enableDebugstays one switch for every scope. - Services are singletons by default.
application.service(Class)andapplication.injectable(scope, Class)bind withBindingScopes.SINGLETON. Every known consumer set this by hand on every binding. - The application binds itself.
start()binds the application underCoreBindings.APPLICATION_INSTANCE; a manual binding of the same key still wins because it runs later inbindContext. - Per-call headers reach the wire.
send({ params: { headers } })merges those headers over the computed ones. They were accepted by the type and dropped. x-www-form-urlencodedis really URL-encoded.bodyType: RequestBodyTypes.FORM_URL_ENCODEDsends aURLSearchParamsbody. It used to send multipartFormDataunder a URL-encoded content type.- A refresh that throws synchronously is a failed refresh.
authRecovery.refreshTokenthrowing (rather than rejecting) now callsonAuthFailureand surfaces the original 401, like a rejection does. - Browser packages compile without Bun's ambient types.
getRequestHeaderdeclaresRecord<string, string>; the emitted declarations no longer inline Bun'sHeaders.
Who is affected
- Applications that bind
APPLICATION_INSTANCEor setSINGLETONby hand. No action needed; the manual binding still applies. - Code that relied on a transient
service()binding. Pass a scope explicitly:application.bind({ key }).toClass(Class).setScope(BindingScopes.TRANSIENT). - Code that sent
x-www-form-urlencodedbodies. The wire format is now what the content type says; a server that accepted the old multipart body by accident may need checking.