v0.0.39
30 June 2026
SSE toolkit, outbound HTTP client, FTS5 full-text search in the registry, page navigation helpers, image dimension metadata, and Api\Client.
Added
Ssetoolkit class :start()sets SSE headers and disables output buffering;send(event, data, id)emits one event;comment()sends a keep-alive ping;aborted()checks for client disconnect;watch(markerPath, event, intervalMs, keepaliveSecs)polls a marker file for mtime changes and emits an event each time it changes, exiting cleanly when the client disconnects. Apage.update:afterhook now writessite/storage/events/{uuid}.touchso pages can wire aliveroute toSse::watch()without any extra infrastructure.Httptoolkit class andHttpResponse: curl-backed outbound HTTP:Http::get/post/put/patch/delete(url, ...). Array bodies are auto-encoded as JSON withContent-Type: application/json.HttpResponsewraps status, headers, and body withok(),text(),json(), andheader(name)accessors.Api\Client: configurable HTTP client backed byHttp. Constructed via$app->api(string $name): accepts either a named connector fromconfig.api.connectors(withbase_urland optionalheaders) or an absolute URL for ad-hoc use. Supportsget/post/put/patch/deletewith fluentheader()/headers()/timeout()overrides.- Registry schema 6:
search_indexFTS5 virtual table (uuid UNINDEXED, content).search=wordsquery DSL token : each whitespace-separated word becomes a quoted FTS5 phrase (AND-joined), results sorted by BM25 relevance when no explicitsort=is given. Page::slug(): returnsbasename($this->url()).Page::next(bool $includeHidden)/Page::prev(): next and previous siblings in parent's child order.Page::contains(Page $other): true if$otheris this page or a descendant.Page::hasChildren(bool $includeHidden): true if the page has any children.Page::isActive(): true if this page is the current request page.Page::isOpen(): true if this page or any descendant is the current request page (for open nav states).File::$type(PHP 8.4 property hook) : virtual property returning a semantic MIME category string:image,video,audio,document,archive,code, orfile.PageImage::width(),height(),ratio(),isLandscape(),isPortrait(),isSquare(): image dimension metadata, lazily resolved viagetimagesize().Url::join(string ...$parts): joins URL path segments, normalising slashes between them, preserving a leading/.F::clearStat(string $path): wrapsclearstatcache(true, $path).phlat/config.json:emailandapikeys with defaults (smtp://mailpit:1025and a 30s timeout with an emptyconnectorsmap).functions.php:isLoggedIn(),make(string $name), andfieldtypeEditTemplate(string $type)global helpers.
Changed
Page::url()now accepts optional$pathand$queryarguments :$page->url('edit')and$page->url('', ['page' => 2])produce sub-paths and query strings without manual concatenation. The root page now returns/instead of an empty string.