Skip to content

v0.5.0

Choose a tag to compare

@sylvesterdamgaard sylvesterdamgaard released this 20 Sep 07:01
· 55 commits to main since this release

Fixed (breaking)

A conflicting mutation skipped the host's validator. The validator ran only for Applied, Partial and Noop, and a conflict still commits its draft — so a preserved candidate reached storage without the host ever seeing it. In the Laravel transport the validator carries the authorization re-check against the record as locked, so the one path this package exists for was also the one path where a principal whose permission changed between the outer check and the lock had their proposal preserved anyway.

Breaking: Store::commitsAfter() takes an optional $entityType. An implementation of the contract must accept it; the reference adapters and anything extending them already do.

Security

Stored payloads are decoded against a named list of the 28 classes the engine actually stores, rather than unserialize()'s default of allowing any class. Any row an attacker can write — a restored backup, the replica database on an end-user's device, SQL injection elsewhere in the host — was an object-injection chain against whatever that application had loaded.

Payloads now carry a format version, so a later change of encoding is detectable on read. Rows written before the tag still read.

Performance

A view bound to one entity type no longer pays for another type's writes. Measured on a catch-up over 2,000 commits across ten types with the view matching one: 255ms over twenty round trips, down to 7ms over two.

Added

EntityTypeView — every live record of one entity type in the space, queryable so the store pages it through an index.

migrate() reconciles an existing installation: missing columns and indexes are added, nothing is dropped or retyped. Verified on SQLite, MySQL 8 and PostgreSQL, including that a second run changes nothing.