webpatser/resonate
Resonate is a Laravel package for adding real-time event broadcasting and pub/sub messaging to your app. It offers a simple, expressive API for publishing and subscribing to events with minimal configuration, helping you build interactive features faster.
onSubscribe/onUnsubscribe and read (connectionsOn) mirrored by write (unsubscribe), plugins can observe and manipulate the full join/leave path. That is the minimum bar for third-party or first-party plugins to be useful without patching core.PluginContext::unsubscribe) means moderation, entitlement revocation, and per-channel rate limiting can ship as plugins rather than as core features or forks. This lowers the cost of saying "no" to niche roadmap requests — the answer becomes "that's a plugin."onClose, not one onUnsubscribe per channel; and PluginContext::unsubscribe() does not emit onUnsubscribe. Both are deliberate. They are also the kind of asymmetry that generates support tickets, so they belong in plugin-author documentation and in the acceptance criteria for any presence feature.Adopt when:
0.x dependency and absorbing interface breaks on a short cadence.Look elsewhere / defer when:
ConnectionLifecycle interface gained a required fourth method in v0.4 — breaking for every existing implementer. That is fine at one release old; it will not be fine later, so treat 1.0 as the real adoption gate.onClose. Design around that or you will build on a false assumption.PluginContext::unsubscribe() to fire onUnsubscribe. It does not — it is a direct action. Plugins that need the hook must call the event path.Adoption criteria to check before committing: migration cost of the v0.4 interface change (one method to implement — near zero today), test coverage confidence (317 tests, reasonable for a 0.x core), and whether the plugin exception isolation (notifyUnsubscribe fan-out) matches your tolerance for a misbehaving plugin silently swallowing lifecycle events.
For executives: This release completes the plugin system's connection lifecycle — plugins can now see a client leave a channel and remove a client from a single channel without dropping the socket. Practically, that converts a class of custom feature requests (presence, moderation, per-channel access control, usage metering) from core engineering work into plugin work, and it removes the main reason anyone would fork the server. It carries one breaking interface change, but the plugin system is a single release old and the change is a single method — the cheapest moment to make it. The strategic read: the extension surface is now whole, which makes declaring the plugin API stable at 1.0 the obvious next milestone and the gate for any external plugin ecosystem.
For engineering: ConnectionLifecycle gains onUnsubscribe(Connection, Channel), fired from the explicit pusher:unsubscribe path via PluginManager::notifyUnsubscribe() — an exception-isolated fan-out, so one failing plugin cannot break the others. PluginContext::unsubscribe(Connection, string $channel) is the write-side counterpart to connectionsOn(), scoped to a single channel and leaving the socket and its other subscriptions intact. Two intentional asymmetries to internalize: unsubscribe() is a direct action and does not emit onUnsubscribe, and a closing connection is reported once through onClose rather than as N unsubscribes. The interface change is breaking for existing implementers — budget the one-method update in the same sprint as the dependency bump.
Ask: approve the breaking change now while the blast radius is one method and zero external consumers; commit to a documented plugin-author contract and a 1.0 stability target as the follow-on.
How can I help you explore Laravel packages today?