Files
kenjreno 128ddcb4df v0.1.0: thread-safe pub/sub event bus
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.
2026-05-05 18:13:10 -07:00

97 lines
4.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 of `TFWEventBus`, lines 188-203 + 1626-1753 of canonical
`fw_plugin_host.pas`). Public surface:
- `constructor Create` / `destructor Destroy`
- `procedure 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: Integer`
- `property OnBroadcast: TEventBroadcast`
- `property Logger: log.types.TLogProc` (new — fpc-* ecosystem
logger shape from fpc-log, replaces canonical's
`fw_log.Log.Error(...)` calls)
- `events.bus.TEventCallback`, `TEventBroadcast`,
`TEventSubscription` types (renamed from canonical `TFW*`).
- `events.version` unit with `EVENTS_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` |
- **`uses` clause:** dropped `fw_log`. Added `log.types` (from
fpc-log) for the canonical ecosystem-wide logger shape. The
exception-handler logging path that called
`fw_log.Log.Error(...)` now calls the optional
`Logger: log.types.TLogProc` if assigned with
`Level=llError`, `Category='events'`, and the formatted
message; silently swallows otherwise.
### Behaviours preserved verbatim (because they LOOK Fastway-specific
but aren't, per the handoff)
- `APluginName` parameter on `Subscribe` and `Unsubscribe`-by-name
bulk removal. Consumers without plugin semantics pass `''` and
use `UnsubscribeCallback`.
- Wildcard `'*'` event type.
- `OnBroadcast` external-listener tap, fired once per `Fire`.
- Snapshot-iterate-outside-lock pattern (re/un-subscribe and
recursive `Fire` from inside a callback are safe).
- Per-handler exception isolation (try/except inside the iteration,
not outside).
- `TCriticalSection` (not `TMonitor` / `TRWLock`).
- No deduplication on `Subscribe`.
- Subscription order = insertion order.
### Notes for downstream consumers
- The `Fire` callback signature carries `ASourcePlugin` as
passthrough metadata. Fastway used it for provenance tagging at
the call site; new consumers can pass `''`.
- `AData` lifetime: the caller of `Fire` retains 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 bulk `Unsubscribe`.