v1.59.0 — Unukalhai: first-party frontend serving + OpenAPI doc ergonomics
Theme: First-party frontend serving + OpenAPI doc ergonomics. A new
ServiceProvider::serveFrontend()seam serves a built SPA or static bundle at any literal path (e.g./admin) with secure asset serving, anindex.htmldeep-link fallback, and a content-hash-aware cache split — replacing the/extensions/{mount}-onlymountStatic()(removed, along with the deadSpaManager/StaticFileDetector/SpaProvider). Alongside: the OpenAPI reflect generator now gives inferred401/403/429responses a JSON body schema and lets#[FromQuery]/#[FromRoute]carrydescription/example(cutting repeated controller attributes), and a router fix makesHEADrequests to any file response safe. Minor release (new features + a breakingmountStatic()removal, per the pre-public policy); no migrations, no env changes. Has### Upgrade Notes.
Added
- OpenAPI: inferred error responses now include JSON body schemas. The reflect generator's automatic
401/403(secured routes) and429(rate-limited routes) responses now carry a default{success,message}JSON schema, while keeping rate-limit headers and preserving explicit#[ApiResponse]precedence. Newdocumentation.errorsconfig lets apps swap in a reflected DTO schema and opt into always-emitted statuses such as500. - OpenAPI:
#[FromQuery]and#[FromRoute]can document source parameters. Both source attributes now accept optionaldescriptionandexamplearguments, and the reflect generator includes them on generated query/path parameters. Hydration remains presence-based and unchanged. ServiceProvider::serveFrontend(string $path, string $dir, array $options = [])— serve a first-party SPA or static bundle at any literal path (e.g./admin), with secure asset serving (traversal/dotfile/.phpdenial, mime, ETag/304), anindex.htmldeep-link fallback for client-side routing (spaFallback, default true), and a cache split (immutable content-hashed assets,no-cacheshell). Request trailing slashes are handled by the router; the mount argument itself is strict.
Fixed
HEADrequests to aBinaryFileResponseno longer 500.Router::dispatch()stripped the HEAD body withsetContent(''), whichBinaryFileResponserejects; it now swaps in a body-lessResponsepreserving status and headers. Affects any file/download route, not justserveFrontend().
Removed
ServiceProvider::mountStatic()and the unusedSpaManager/StaticFileDetector/SpaProvider— superseded byserveFrontend(), which serves at any literal path (not just/extensions/{mount}) and adds the SPA deep-link fallbackmountStatic()lacked. No production consumers existed.
Upgrade Notes
mountStatic()is removed — migrate toserveFrontend(). Replace$this->mountStatic('foo', $dir)(which served at/extensions/foo) with$this->serveFrontend('/foo', $dir)(any literal path, plus anindex.htmldeep-link fallback). The mount path is now a strict literal (leading/, lowercase[a-z0-9-]segments, no trailing slash). For a plain static bundle that should404on a miss (the old no-SPA behavior), pass['spaFallback' => false]. NoteserveFrontend()no-ops with a warning if the bundle has noindex.html(whenspaFallbackis on).SpaManager/StaticFileDetector/SpaProviderare removed (dead code —SpaProvideris dropped from the container's provider list). They had no callers; if you somehow resolvedStaticFileDetectorfrom the container, it is gone.