Unified event system for Clojure/ClojureScript.
Combines:
Design principles:
Usage: (require '[hive.events :as ev])
;; Register effect handlers (ev/reg-fx :http (fn [request] ...)) (ev/reg-fx :db (fn [tx] ...))
;; Register event handlers (ev/reg-event-fx :user/login [ev/debug ev/validate] ; interceptors (fn [{:keys [db]} [_ credentials]] {:db (assoc db :loading? true) :http {:method :post :url "/login" :body credentials}}))
;; Dispatch events (ev/dispatch [:user/login {:email "..." :password "..."}])
Unified event system for Clojure/ClojureScript.
Combines:
- re-frame interceptor chain (portable to JVM)
- re-frame effects/coeffects system
- domino-style declarative models with async support
Design principles:
- Events as data
- Pure handlers returning effect maps
- Interceptors for cross-cutting concerns
- Async-first with core.async
Usage:
(require '[hive.events :as ev])
;; Register effect handlers
(ev/reg-fx :http (fn [request] ...))
(ev/reg-fx :db (fn [tx] ...))
;; Register event handlers
(ev/reg-event-fx :user/login
[ev/debug ev/validate] ; interceptors
(fn [{:keys [db]} [_ credentials]]
{:db (assoc db :loading? true)
:http {:method :post :url "/login" :body credentials}}))
;; Dispatch events
(ev/dispatch [:user/login {:email "..." :password "..."}])Async extensions for hive-events.
Provides advanced async patterns beyond basic dispatch:
JVM-first with core.async, graceful degradation on CLJS.
Async extensions for hive-events. Provides advanced async patterns beyond basic dispatch: - Timeout-wrapped dispatch - Retry with backoff - Debounced dispatch - Event streams (pub/sub) - Saga/process manager patterns JVM-first with core.async, graceful degradation on CLJS.
Coeffects (cofx) system - dependency injection for event handlers.
Ported from re-frame/cofx.cljc with JVM compatibility.
Coeffects are input data that handlers need but shouldn't fetch themselves. They enable pure handlers by injecting all dependencies.
Built-in coeffects:
Usage: ;; Register a coeffect handler (reg-cofx :now (fn [coeffects] (assoc coeffects :now (System/currentTimeMillis))))
;; Inject into event handler (reg-event-fx :log/timestamp [(inject-cofx :now)] (fn [{:keys [db now]} _] {:db (assoc db :last-timestamp now)}))
Coeffects (cofx) system - dependency injection for event handlers.
Ported from re-frame/cofx.cljc with JVM compatibility.
Coeffects are input data that handlers need but shouldn't fetch themselves.
They enable pure handlers by injecting all dependencies.
Built-in coeffects:
- :db - Current application state
- :event - The event being processed
Usage:
;; Register a coeffect handler
(reg-cofx :now
(fn [coeffects]
(assoc coeffects :now (System/currentTimeMillis))))
;; Inject into event handler
(reg-event-fx :log/timestamp
[(inject-cofx :now)]
(fn [{:keys [db now]} _]
{:db (assoc db :last-timestamp now)}))Deterministic FSM workflow engine for hive-events.
Adapted from yogthos/maestro — state machine runner for expressing workflows as data. Integrated with hive-events fx/cofx system.
(require '[hive.events.fsm :as fsm])
;; Define FSM spec
(def my-workflow
{:fsm {::fsm/start {:handler (fn [resources data]
(assoc data :step :ready))
:dispatches [[::process (constantly true)]]}
::process {:handler (fn [resources data]
(assoc data :result ((:compute resources) data)))
:dispatches [[::fsm/end (fn [{:keys [result]}] (some? result))]
[::fsm/error (constantly true)]]}}
:opts {:max-trace 100}})
;; Compile and run
(-> (fsm/compile my-workflow)
(fsm/run {:compute my-fn} {:data {:input 42}}))
::start — initial state::end — terminal success, returns data::halt — pausable, returns FSM state for resume::error — terminal failure(fn [resources data] new-data)(fn [resources data] {:data new-data, :fx [[:effect-id params]]})
Effects are processed through the hive.events.fx pipeline after transition.run-sub-fsm and make-sub-fsm-handlerBased on yogthos/maestro v0.2.1 (MIT License).
Deterministic FSM workflow engine for hive-events.
Adapted from yogthos/maestro — state machine runner for expressing
workflows as data. Integrated with hive-events fx/cofx system.
## Usage
```clojure
(require '[hive.events.fsm :as fsm])
;; Define FSM spec
(def my-workflow
{:fsm {::fsm/start {:handler (fn [resources data]
(assoc data :step :ready))
:dispatches [[::process (constantly true)]]}
::process {:handler (fn [resources data]
(assoc data :result ((:compute resources) data)))
:dispatches [[::fsm/end (fn [{:keys [result]}] (some? result))]
[::fsm/error (constantly true)]]}}
:opts {:max-trace 100}})
;; Compile and run
(-> (fsm/compile my-workflow)
(fsm/run {:compute my-fn} {:data {:input 42}}))
```
## Privileged States
- `::start` — initial state
- `::end` — terminal success, returns data
- `::halt` — pausable, returns FSM state for resume
- `::error` — terminal failure
## Integration with hive-events
- Handlers are pure: `(fn [resources data] new-data)`
- Handlers may return fx-enhanced results:
`(fn [resources data] {:data new-data, :fx [[:effect-id params]]})`
Effects are processed through the hive.events.fx pipeline after transition.
- Subscriptions can dispatch events on state path changes
- Pre/post hooks for interceptor-style concerns
- EDN specs compiled with SCI
- Sub-FSM composition via `run-sub-fsm` and `make-sub-fsm-handler`
Based on yogthos/maestro v0.2.1 (MIT License).Effects (fx) system - side effect handlers.
Ported from re-frame/fx.cljc with JVM compatibility.
Effects are declarative descriptions of side effects. Each effect type has a registered handler that performs the actual work.
Built-in effects:
Usage: ;; Register a custom effect handler (reg-fx :http (fn [{:keys [method url on-success]}] (http-request method url (fn [response] (dispatch [on-success response])))))
;; Event handler returns effects map (reg-event-fx :user/login (fn [{:keys [db]} [_ credentials]] {:db (assoc db :loading? true) :http {:method :post :url "/api/login" :body credentials}}))
Effects (fx) system - side effect handlers.
Ported from re-frame/fx.cljc with JVM compatibility.
Effects are declarative descriptions of side effects.
Each effect type has a registered handler that performs the actual work.
Built-in effects:
- :db - Update application state
- :dispatch - Dispatch another event
- :dispatch-n - Dispatch multiple events
Usage:
;; Register a custom effect handler
(reg-fx :http
(fn [{:keys [method url on-success]}]
(http-request method url
(fn [response]
(dispatch [on-success response])))))
;; Event handler returns effects map
(reg-event-fx :user/login
(fn [{:keys [db]} [_ credentials]]
{:db (assoc db :loading? true)
:http {:method :post :url "/api/login" :body credentials}}))Interceptor chain implementation.
Ported from re-frame/interceptor.cljc with JVM compatibility.
An interceptor is a map with optional keys:
Context is a map containing:
Interceptor chain implementation. Ported from re-frame/interceptor.cljc with JVM compatibility. An interceptor is a map with optional keys: - :id - keyword identifier - :before - fn [context] -> context (pre-processing) - :after - fn [context] -> context (post-processing) Context is a map containing: - :coeffects - input data (including :event and :db) - :effects - output data (side effects to perform) - :queue - interceptors yet to execute - :stack - interceptors already executed (for :after phase)
Minimal pluggable logging for hive-events.
Three levels: :debug, :warn, :error
Default behavior:
Override: (set-log-fn! (fn [level msg & args] ;; your logging here ))
Disable: (set-log-fn! (fn [& _]))
Restore default: (set-log-fn! nil)
Minimal pluggable logging for hive-events.
Three levels: :debug, :warn, :error
Default behavior:
- JVM: prints to *err* (stderr)
- JS: uses console.log/warn/error
Override:
(set-log-fn! (fn [level msg & args]
;; your logging here
))
Disable:
(set-log-fn! (fn [& _]))
Restore default:
(set-log-fn! nil)Multimethod-based event dispatch + pure orchestration for hive-events.
Two complementary subsystems in one namespace:
Registration:
(register-handler! event-id handler-fn) — register handler (no interceptors)(register-handler! event-id interceptors handler-fn) — with interceptors(remove-handler! event-id) — remove a registered handlerDispatch:
(dispatch-sync event) — synchronous dispatch + fx execution(event-id event) — extract event-id (dispatch fn)Query:
(handler-registered? event-id) — check if handler existsHandler signature: (fn [coeffects event] effects-map)
Compile a batch of cross-tool operations into a wave-based execution plan. Pure functions — no side effects, no IO, no requiring-resolve.
Pipeline: normalize → validate → expand-batch → assign-waves → compile-spec
(normalize-op op) — normalize string keys, auto-generate ID(validate-ops ops) — validate IDs, deps, cycles (Kahn's algorithm)(expand-batch-macro ops) — expand 'batch' command into fan-out sub-ops(assign-waves ops) — topological sort into dependency-ordered wave groups(compile-multi-spec ops) — end-to-end: validate → expand → waves → FSM spechive-events (this ns) — pure compilation (validate, sort, waves)
↓
hive-mcp tools/multi — bridge (resolve handlers, execute ops, format)
↓
hive-mcp consolidated — MCP facade (JSON schema, tool def)
SOLID: OCP — Open for extension via defmethod handlers SOLID: SRP — Pure orchestration logic, no side effects CLARITY: L — Layered: pure compilation separate from execution
Multimethod-based event dispatch + pure orchestration for hive-events. Two complementary subsystems in one namespace: ## 1. Handler Dispatch (multimethod-based) Registration: - `(register-handler! event-id handler-fn)` — register handler (no interceptors) - `(register-handler! event-id interceptors handler-fn)` — with interceptors - `(remove-handler! event-id)` — remove a registered handler Dispatch: - `(dispatch-sync event)` — synchronous dispatch + fx execution - `(event-id event)` — extract event-id (dispatch fn) Query: - `(handler-registered? event-id)` — check if handler exists Handler signature: `(fn [coeffects event] effects-map)` ## 2. Pure Orchestration (multi-op compilation) Compile a batch of cross-tool operations into a wave-based execution plan. Pure functions — no side effects, no IO, no requiring-resolve. Pipeline: normalize → validate → expand-batch → assign-waves → compile-spec - `(normalize-op op)` — normalize string keys, auto-generate ID - `(validate-ops ops)` — validate IDs, deps, cycles (Kahn's algorithm) - `(expand-batch-macro ops)` — expand 'batch' command into fan-out sub-ops - `(assign-waves ops)` — topological sort into dependency-ordered wave groups - `(compile-multi-spec ops)` — end-to-end: validate → expand → waves → FSM spec ## Architecture ``` hive-events (this ns) — pure compilation (validate, sort, waves) ↓ hive-mcp tools/multi — bridge (resolve handlers, execute ops, format) ↓ hive-mcp consolidated — MCP facade (JSON schema, tool def) ``` SOLID: OCP — Open for extension via defmethod handlers SOLID: SRP — Pure orchestration logic, no side effects CLARITY: L — Layered: pure compilation separate from execution
Observer seam for dispatched events.
The event registry holds ONE handler per event id. Observers are the extension point for cross-cutting side effects that must not own, replace or know about that handler.
Extend by implementing IEventObserver — EventIdObserver (a fixed set of
event ids) and GlobalObserver (every event) are supplied; a consumer may
register any other implementation.
Contract: observers run AFTER the handler's effects have been applied, in observer-id order, and each is isolated — an observer that throws is logged and never propagates to the handler or to sibling observers.
Observer seam for dispatched events. The event registry holds ONE handler per event id. Observers are the extension point for cross-cutting side effects that must not own, replace or know about that handler. Extend by implementing IEventObserver — `EventIdObserver` (a fixed set of event ids) and `GlobalObserver` (every event) are supplied; a consumer may register any other implementation. Contract: observers run AFTER the handler's effects have been applied, in observer-id order, and each is isolated — an observer that throws is logged and never propagates to the handler or to sibling observers.
Event router - registration and dispatch.
Ported from re-frame with JVM-first async support via core.async.
Supports two types of event handlers:
Dispatch modes:
Usage: ;; Initialize with app state atom (init! (atom {}))
;; Register handlers (reg-event-db :counter/inc (fn [db [_ amount]] (update db :count + amount)))
(reg-event-fx :user/fetch (fn [{:keys [db]} [_ user-id]] {:db (assoc db :loading? true) :http {:url (str "/users/" user-id)}}))
;; Dispatch events (dispatch [:counter/inc 5]) (dispatch-sync [:counter/inc 1])
Event router - registration and dispatch.
Ported from re-frame with JVM-first async support via core.async.
Supports two types of event handlers:
- reg-event-db: Handler receives db, returns new db
- reg-event-fx: Handler receives coeffects, returns effects map
Dispatch modes:
- dispatch: Async dispatch (queued, processed in order)
- dispatch-sync: Synchronous dispatch (immediate processing)
Usage:
;; Initialize with app state atom
(init! (atom {}))
;; Register handlers
(reg-event-db :counter/inc
(fn [db [_ amount]]
(update db :count + amount)))
(reg-event-fx :user/fetch
(fn [{:keys [db]} [_ user-id]]
{:db (assoc db :loading? true)
:http {:url (str "/users/" user-id)}}))
;; Dispatch events
(dispatch [:counter/inc 5])
(dispatch-sync [:counter/inc 1])Schema-driven event registration — the hive-events leg of the malli macro
layer. reg-event-schema registers an fx handler whose EVENT PAYLOAD (the
second element of the [id payload] event vector) is coerced + validated against
ONE registered malli schema-key by a :before interceptor, before the handler
runs — declare the event's payload schema once, its guard is wired.
hive-events is cljc/cljs-safe and malli's derive lever is clj-only, so compile-op is resolved LAZILY via requiring-resolve (guarded). When it is unresolvable — cljs, or hive-spi absent — the interceptor is a pass-through, so registration never fails and the module stays platform-safe.
Schema-driven event registration — the hive-events leg of the malli macro layer. `reg-event-schema` registers an fx handler whose EVENT PAYLOAD (the second element of the [id payload] event vector) is coerced + validated against ONE registered malli schema-key by a :before interceptor, before the handler runs — declare the event's payload schema once, its guard is wired. hive-events is cljc/cljs-safe and malli's derive lever is clj-only, so compile-op is resolved LAZILY via requiring-resolve (guarded). When it is unresolvable — cljs, or hive-spi absent — the interceptor is a pass-through, so registration never fails and the module stays platform-safe.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |