Repository navigation
v0.5.0
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.