v0.0.53

7 July 2026

App-level SQLite cache, image effects and lifecycle hooks for collection items, and file-identity fixes for subdirectory-stored files.

Added

  • Cache: SQLite-backed key/value cache at site/storage/cache/{name}.db. $app->cache(string $name = 'app') returns a named instance. API: get, has, set, remember, remove, flush. set()/remember() accept an optional TTL in seconds. Enabled by default; disable per name via config.cache.{name}.enabled = false.
  • item.update:after and item.delete:before lifecycle hooks, alongside the existing page/user hooks. New Item::delete() emits the delete hook before removing the item's folder.
  • Image transform DSL: blur, brightness, contrast, gamma, invert, pixelate, sharpen effects, alongside the existing grayscale.
  • FieldTypeCollection items are now indexed into search_index, same as pages. new() accepts an optional deterministic $uuid; added has() and item() lookups.

Fixed

  • PageFile/PageImage derived a file's identity from its basename only, breaking URL and mime resolution for files stored in subdirectories. Both now carry the full relative filename.
  • Toolkit\Http's cURL header callback measured the trimmed line's length instead of the raw line's, breaking on HTTP/2 status lines.
  • PageFiles::meta() computed a created timestamp fallback even for files that don't exist; now guarded with F::exists().

Changed

  • Str::slug() now preserves underscores mid-string (still trims both - and _ from the ends); the {slug} route token widened to match.
  • App::renderFragment() now merges the page's app controller context into fragment renders, same as full-page renders.
  • Renamed the cache addon to http-purge, to avoid colliding with the new Cache class.
  • Item storage sharding simplified from two levels to one ({2-char-hash}/{uuid}); item creation moved from Registry into FieldTypeCollection::new().