Verbatim port of Fastway-Server's TFWEventBus from fw_plugin_host.pas
per feedback_copy_dont_reinterpret.md. Adjustments limited to:
- Type renames (TFW* -> T*).
- uses clause: drop fw_log; add log.types from fpc-log so the
optional Logger property uses the canonical ecosystem-wide
TLogProc shape, matching every other fpc-* library.
- Per-handler exception logging now calls Logger with
Level=llError, Category='events', and includes the source
plugin (ASourcePlugin parameter) in the message text so the
canonical signature stays meaningful.
Behaviours preserved verbatim: APluginName bulk-Unsubscribe key,
wildcard '*' subscriber, OnBroadcast external-listener tap,
snapshot-iterate-outside-lock pattern, per-handler exception
isolation, TCriticalSection.
docs/DEVELOPER_GUIDE.md added covering threading, payload
ownership, recursive Fire, OnBroadcast, logger plumbing, and
the relationship between fpc-events (ecosystem-wide pub/sub)
and per-library typed observer callbacks (bp.events / cm.events
pattern).
Tests: 44 assertions across 14 scenarios pass on x86_64-linux.
Pre-tag -vh audit on src/ev.bus.pas reports zero hints/warnings.
4.0 KiB
4.0 KiB
Changelog
All notable changes to fpc-events are recorded here. Every release
tag (vX.Y.Z) points at the commit that bumped EVENTS_VERSION
in src/events.version.pas. Downstream consumers should pin
by tag, not commit hash.
Semver intent:
- major — breaks callers (API removal, signature change)
- minor — additive features
- patch — bug fixes, internal cleanups
0.1.0 — 2026-05-05
Initial release. Verbatim port of Fastway-Server's TFWEventBus
class from fw_plugin_host.pas per feedback_copy_dont_reinterpret.md.
Added
events.bus.TEventBus— thread-safe publish/subscribe registry (port ofTFWEventBus, lines 188-203 + 1626-1753 of canonicalfw_plugin_host.pas). Public surface:constructor Create/destructor Destroyprocedure Subscribe(const APluginName, AEventType: string; ACallback: TEventCallback)procedure Unsubscribe(const APluginName: string)procedure UnsubscribeCallback(ACallback: TEventCallback)procedure Fire(const ASourcePlugin, AEventType: string; AData: TJSONObject)function GetSubscriptionCount: Integerproperty OnBroadcast: TEventBroadcastproperty Logger: log.types.TLogProc(new — fpc-* ecosystem logger shape from fpc-log, replaces canonical'sfw_log.Log.Error(...)calls)
events.bus.TEventCallback,TEventBroadcast,TEventSubscriptiontypes (renamed from canonicalTFW*).events.versionunit withEVENTS_VERSION_MAJOR/MINOR/PATCH/STRING.docs/DEVELOPER_GUIDE.md— full developer guide covering threading, payload ownership, recursive Fire, OnBroadcast, logger plumbing, and the relationship between fpc-events and per-library typed callbacks.
Adjustments from canonical (the only behaviour-preserving changes)
| Canonical | fpc-events |
|---|---|
TFWEventBus |
TEventBus |
TFWEventCallback |
TEventCallback |
TFWEventBroadcast |
TEventBroadcast |
TFWEventSubscription |
TEventSubscription |
usesclause: droppedfw_log. Addedlog.types(from fpc-log) for the canonical ecosystem-wide logger shape. The exception-handler logging path that calledfw_log.Log.Error(...)now calls the optionalLogger: log.types.TLogProcif assigned withLevel=llError,Category='events', and the formatted message; silently swallows otherwise.
Behaviours preserved verbatim (because they LOOK Fastway-specific
but aren't, per the handoff)
APluginNameparameter onSubscribeandUnsubscribe-by-name bulk removal. Consumers without plugin semantics pass''and useUnsubscribeCallback.- Wildcard
'*'event type. OnBroadcastexternal-listener tap, fired once perFire.- Snapshot-iterate-outside-lock pattern (re/un-subscribe and
recursive
Firefrom inside a callback are safe). - Per-handler exception isolation (try/except inside the iteration, not outside).
TCriticalSection(notTMonitor/TRWLock).- No deduplication on
Subscribe. - Subscription order = insertion order.
Notes for downstream consumers
- The
Firecallback signature carriesASourcePluginas passthrough metadata. Fastway used it for provenance tagging at the call site; new consumers can pass''. ADatalifetime: the caller ofFireretains ownership. Subscribers must not free it.
Tests
tests/test_bus.pas— 44 assertions across 14 scenarios: basic delivery, subscription order, wildcard, type filtering, bulk Unsubscribe by plugin, UnsubscribeCallback by method pointer, unsubscribe-from-callback, subscribe-from-callback (snapshot semantics), recursive Fire, handler exception isolation, broadcast tap, broadcast exception isolation, threaded publishers (4 producer threads × 1000 events), and GetSubscriptionCount add/remove.
Examples
examples/pubsub.pas— minimal demo with a typed listener, wildcard audit subscriber, and bulkUnsubscribe.