Liking cljdoc? Tell your friends :D

zodiac.ext.live

Zodiac extension for remuda + darkstar.

The core is plain Ring and knows nothing about zodiac; this namespace is the wiring, following the shape of zodiac-hot-reload: integrant keys, route registration, and a config transformer. No domain logic. If this file grows past wiring, 's map-of-pieces design has failed.

Usage:

(z/start {:extensions [(z.live/init {:components {:chat #'app/chat}
                                     :render-fn chassis/html
                                     :source my-source
                                     :secret "..."})]})

What it wires:

  • the engine, the pubsub bus and registry, and the fragment cache as integrant components, so they participate in zodiac's lifecycle;
  • an SSE route and an action route;
  • the flush loop that turns coalesced hints into pushes.

Components are registered as vars, deliberately

Redefining :render at a REPL must reach already-connected contexts. The engine resolves a component by name on every render and derefs it if it is deref-able, so an adapter should register vars#'app/chat, not app/chat. A plain map still works but only whole-map replacement is visible.

Registries survive reload

The live-context registry, subscription registry and cache are held in defonced atoms rather than created per init-key, because requires them to survive tools.namespace/refresh. Recreating them on reload would drop every connection — the same failure as a server restart, triggered by saving a file.

Zodiac extension for remuda + darkstar.

The core is plain Ring and knows nothing about zodiac; this
namespace is the wiring, following the shape of `zodiac-hot-reload`: integrant
keys, route registration, and a config transformer. **No domain logic.** If this
file grows past wiring, 's map-of-pieces design has failed.

Usage:

    (z/start {:extensions [(z.live/init {:components {:chat #'app/chat}
                                         :render-fn chassis/html
                                         :source my-source
                                         :secret "..."})]})

What it wires:

- the engine, the pubsub bus and registry, and the fragment cache as integrant
  components, so they participate in zodiac's lifecycle;
- an SSE route and an action route;
- the flush loop that turns coalesced hints into pushes.

## Components are registered as vars, deliberately

Redefining `:render` at a REPL must reach already-connected
contexts. The engine resolves a component by name on every render and derefs it
if it is deref-able, so **an adapter should register vars** — `#'app/chat`, not
`app/chat`. A plain map still works but only whole-map replacement is visible.

## Registries survive reload

The live-context registry, subscription registry and cache are held in
`defonce`d atoms rather than created per `init-key`, because  requires them
to survive `tools.namespace/refresh`. Recreating them on reload would drop every
connection — the same failure as a server restart, triggered by saving a file.
raw docstring

action-handlerclj

(action-handler {:keys [params] :as request})

The interaction route. Looks up the live context and dispatches an event.

Args arrive as a JSON payload and are passed through uncoerced, because their types already survived the wire. That is the whole point of the payload form: the previous query-string encoding forced every receiver to guess a type, and dev/slice guessed with a hardcoded parse-long that silently turned string args into nil.

liveId and event fall back to query params so a plain form post still works, but args are read only from the payload — a query-string arg cannot carry a type, and admitting one would reopen defect 3 through the back door.

The interaction route. Looks up the live context and dispatches an event.

Args arrive as a JSON payload and are passed through **uncoerced**,
because their types already survived the wire. That is the whole point of the
payload form: the previous query-string encoding forced every receiver to guess a
type, and `dev/slice` guessed with a hardcoded `parse-long` that silently turned
string args into nil.

`liveId` and `event` fall back to query params so a plain form post still works,
but *args* are read only from the payload — a query-string arg cannot carry a
type, and admitting one would reopen defect 3 through the back door.
sourceraw docstring

context-keyclj

Where this extension puts itself in zodiac's request context.

A var rather than a literal keyword in each caller, because a mismatched namespaced keyword fails silently — the lookup returns nil and the failure surfaces somewhere unrelated. That already cost a debugging session once in this project (a ::cache that resolved to the wrong namespace), so the key is named once and referenced.

Where this extension puts itself in zodiac's request context.

A var rather than a literal keyword in each caller, because a mismatched
namespaced keyword fails *silently* — the lookup returns nil and the failure
surfaces somewhere unrelated. That already cost a debugging session once in this
project (a `::cache` that resolved to the wrong namespace), so the key is named
once and referenced.
sourceraw docstring

initclj

(init {:keys [components render-fn source secret sse-fn signals-fn interval-ms
              bus]})

Creates a zodiac extension for zodiac-live.

Wires the engine, bus, flusher and handlers as integrant components and injects them into zodiac's request context, so a handler reaches them via [::z/context ::live].

Options:

  • :components map of name -> component (register vars, )
  • :render-fn hiccup -> string
  • :source a remuda.source/Source
  • :secret HMAC key for recovery snapshots
  • :sse-fn (fn [ctx] -> ring response) owning the transport
  • :signals-fn (fn [request] -> map) reading client signals, optional
  • :interval-ms flush interval, default 100
  • :bus a PubSub; an in-process one is created if absent
Creates a zodiac extension for zodiac-live.

Wires the engine, bus, flusher and handlers as integrant components and injects
them into zodiac's request context, so a handler reaches them via
`[::z/context ::live]`.

Options:
- `:components`  map of name -> component (register **vars**, )
- `:render-fn`   hiccup -> string
- `:source`      a `remuda.source/Source`
- `:secret`      HMAC key for recovery snapshots
- `:sse-fn`      (fn [ctx] -> ring response) owning the transport
- `:signals-fn`  (fn [request] -> map) reading client signals, optional
- `:interval-ms` flush interval, default 100
- `:bus`         a `PubSub`; an in-process one is created if absent
sourceraw docstring

live-ctxclj

(live-ctx request)

The extension's components, from zodiac's injected request context.

The extension's components, from zodiac's injected request context.
sourceraw docstring

publish!clj

(publish! live topic)

Announces that topic changed.

live is the extension's context map, from live-ctx.

Announces that `topic` changed.

`live` is the extension's context map, from `live-ctx`.
sourceraw docstring

recovery-snapshotclj

(recovery-snapshot system id secret)

A signed recovery snapshot for id, for the caller to send to the browser.

A signed recovery snapshot for `id`, for the caller to send to the browser.
sourceraw docstring

registryclj

id -> live context. Survives namespace reload.

id -> live context. Survives namespace reload.
sourceraw docstring

routesclj

(routes)
(routes {:keys [sse-path action-path]})

The routes zodiac-live needs, for the application to splice into its own route vector.

Returned rather than injected because zodiac's :routes is typically a var the application owns, and an extension reaching in to conj onto it would fight that ownership — and would break if the application passed a function rather than a vector. The application writes:

(defn routes []
  ["" (z.live/routes)
   ["/" {:handler home}]])

The handlers read their dependencies from the request context, which is how they avoid needing an integrant ref at route-definition time.

The routes zodiac-live needs, for the application to splice into its own route
vector.

Returned rather than injected because zodiac's `:routes` is typically a var the
application owns, and an extension reaching in to `conj` onto it would fight that
ownership — and would break if the application passed a function rather than a
vector. The application writes:

    (defn routes []
      ["" (z.live/routes)
       ["/" {:handler home}]])

The handlers read their dependencies from the request context, which is how they
avoid needing an integrant ref at route-definition time.
sourceraw docstring

sse-handlerclj

(sse-handler request respond _raise)

The connection route, in zodiac's async 3-arity form.

Delegates everything to the caller's sse-fn, which owns the transport, because The server must stay replaceable — this namespace must not name Jetty, http-kit, or a datastar adapter.

The connection route, in zodiac's async 3-arity form.

Delegates everything to the caller's `sse-fn`, which owns the transport, because
The server must stay replaceable — this namespace must not name
Jetty, http-kit, or a datastar adapter.
sourceraw docstring

subscribe!clj

(subscribe! live id topics)

Subscribes live context id to topics.

Throws on a missing bus rather than silently doing nothing: this runs inside an SSE on-open callback where a swallowed exception looks like a connection that opens and then sends nothing at all.

Subscribes live context `id` to `topics`.

Throws on a missing bus rather than silently doing nothing: this runs inside an
SSE `on-open` callback where a swallowed exception looks like a connection that
opens and then sends nothing at all.
sourceraw docstring

subscriptionsclj

Subscription registry. Survives namespace reload.

Subscription registry. Survives namespace reload.
sourceraw docstring

verify-snapshotclj

(verify-snapshot secret signed)

Verifies a snapshot string, returning {:ok ...}. See snapshot/verify.

Verifies a snapshot string, returning `{:ok ...}`. See `snapshot/verify`.
sourceraw docstring

view-cacheclj

Cross-viewer cache. Survives namespace reload.

Cross-viewer cache. Survives namespace reload.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close