v0.37.0

2 November 2026

Signed-in visitors and visitors with option cookies are now served from the page, fragment and {cache} caches.

Added

  • per sets who shares a cache entry: user (the default, one entry per signed-in user), role or all. Write it as per: 'role' on {fragment}, {component} and {cache}, or as {"cache": {"ttl": 300, "per": "role"}} in a view's _index.json.
  • A render is not kept when it read more than its per shares: any session read, user() under role or all, and role(), can() or isLoggedIn() under all. A signed-in visitor's kept page is sent private, no-cache.
  • A cached render is kept per value of each visitor option it read through $app->options->get(). Options::values() returns option values without counting them as read, and Options::readKeys() lists the options read.
  • SessionRoot::role() returns the role name (guest when signed out), SessionRoot::reads() counts identity reads and SessionRoot::audience() names who an entry is shared with.
  • Cache::flushUser() clears the entries kept for one user. Cache::getFor() and Cache::setFor() read and write an entry for an audience, and Cache::per() checks a per value.
  • App::cacheMark() and App::unkeepable() let code check that a render read nothing its per does not share.

Changed

  • A signed-in visitor no longer skips the caches. Cache::getVaried() and Cache::setVaried() take a per argument.
  • A visitor holding an option cookie is served cached pages. A template that reads the cookie directly instead of through $app->options->get() now gets the shared entry.
  • user.update:after and user.delete:before clear only the entries kept for that user. user.create:after no longer empties the cache. A page that shows a user clears itself from a user.* hook.
  • The fragment cache schema is version 4, so the store is emptied once on upgrade.