All notable changes to this project are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning. The normalized vocabularies are the public contract: adding a member is a minor bump, renaming or removing one is a major bump (see ADR 0009).
Phase 3: KV, tested on the JVM and Node. One new portable namespace,
nats-cljc.kv, speaking KV vocabulary throughout — Buckets, Entries,
Revisions — never its stream substrate (ADR 0023). All new vocabulary is
additive ⇒ minor bump (ADR 0009).
(kv conn) resolves to the handle every Bucket-lifecycle
operation flows through, verified at entry exactly like the JetStream
context: a server or account without JetStream rejects with
:jetstream-not-enabled at the handle, not on the first operation
(ADR 0017).create-bucket, open-bucket, and delete-bucket,
configured by the same portable closed kebab-keyword config maps as streams
(:bucket, :history, :ttl-ms, :storage, :replicas, …; an
unrecognized key is :unknown-config-key, an omitted :bucket is
:missing-required-key). Opening verifies the Bucket exists, rejecting with
the new operational :type :bucket-not-found — the KV face of
:stream-not-found (ADR 0023). The resolved Bucket handle binds the
Bucket's one Codec (the connection default, or an open/create-time :codec
override) — a Bucket's values are homogeneous, never per-operation choices.bucket-names, list-buckets, and bucket-status,
resolving fully-realized vectors and one normalized status map shape
(config as applied plus the :values/:bytes counters), identical on every
platform.put and get through the Bucket's Codec: put
resolves the new Revision as a bare number; get resolves an Entry — a
plain map {:bucket :key :value :revision :created :operation} — or nil
for an absent key (absence is a normal outcome to branch on, not an Error;
a stored nil stays distinguishable as {:value nil …}). A malformed key is
the new validation :type :invalid-key, pre-flight on every entry
operation.create (write only when absent — first-writer-wins
locks and initialization) and update (write only when the expected
Revision is still latest). A lost race rejects with the new operational
:type :wrong-revision carrying the contested :key — one canonical face
for both verbs, KV vocabulary rather than the substrate's
:wrong-last-sequence (ADR 0023).delete (a Tombstone: the key reads as absent
while history retains the deletion, observable as an Entry with a delete
:operation), purge (erase one key's history down to a purge marker),
both with an optional :revision guard rejecting stale guards as
:wrong-revision; purge-deletes (the Bucket-wide janitor: every
Tombstoned key's retained history removed, marker included, immediately and
deterministically on both legs); and history — the retained Entries of a
key oldest-to-newest, markers visible, each Entry carrying :delta.get takes an optional :revision pinning the
read to an exact past Revision (markers stay visible, never hidden), and
keys enumerates a Bucket's live keys, optionally restricted by a
subject-style filter.watch pushes each matching Entry to a handler under the core
subscription contract (serial delivery; a returned promise suspends the
next delivery — ADR 0007), with a closed :deliver mode (:latest default
/ :history / :updates; anything else is the new validation
:type :invalid-deliver), :keys pattern filtering (one subject-style
pattern or a non-empty vector — their union; an empty vector is the new
validation :type :invalid-keys), :ignore-deletes? to suppress marker
deliveries, and a per-watch :on-error sink with the core-subscription
override semantics. The watch handle carries an :initialized promise —
the "cache is warm" signal — and stop ends it, idempotently.@nats-io/kv is installed automatically and
version-pinned in lockstep with the core client, exactly like
@nats-io/jetstream; the KV import is confined to the KV impl namespace,
so a bundle that never requires nats-cljc.kv ships zero KV bytes
(ADR 0016).nats-cljc.jetstream/get-message: a one-shot,
promise-returning read of a stored message straight off a Stream, by stream
sequence ({:seq n}) or newest-on-subject ({:last-by-subject subj}),
resolving a pure-data {:subject :data :seq :timestamp} (plus :headers
when present). Rejects with the new operational :type :no-message-found
(err 10037) when nothing matches; pre-flight, a malformed query rejects with
the new validation :type :invalid-query. Vocabulary additions ⇒ minor bump
(ADR 0009).Internal-namespace housekeeping: the API/implementation boundary is now visible
in the namespace names themselves. Breaking only for code that required
never-public (^:no-doc) internals.
impl — never-public (^:no-doc)
namespaces now live under their area's impl segment so editor autocomplete
distinguishes API from internals: nats-cljc.{auth,error,protocol} →
nats-cljc.impl.{auth,error,protocol}, and
nats-cljc.jetstream.{acks,consumer,error,pub,pull,refill,stream} →
nats-cljc.jetstream.impl.{…}. No deprecation shims (ADR 0005, amended).
The public surface — nats-cljc.core, nats-cljc.codec,
nats-cljc.jetstream, nats-cljc.blocking.core, and the opt-in
nats-cljc.codec.<name> namespaces — is unchanged.nats-cljc.core — normalize-headers,
trim-headers, and effective-codec were public in nats-cljc.core only so
the JetStream facade could reuse them; they now live in the new
nats-cljc.impl.msg, so the public namespaces expose only API. This also
removes the JetStream facade's code dependency on nats-cljc.core.Phase 2: JetStream, tested on the JVM and Node. One new portable namespace,
nats-cljc.jetstream, mirroring the shape of nats-cljc.core.
(jetstream conn) returns one handle for the data
and management planes, verified at entry: a server round-trip on both
platforms so a server or account without JetStream fails fast with
:jetstream-not-enabled at the handle, not on the first operation (ADR 0017).create-stream, update-stream, delete-stream,
purge-stream, stream-info, list-streams, and stream-names, configured
by
portable closed kebab-keyword maps: keyword enums (:storage, :retention),
durations as integer milliseconds with the unit in the key (:max-age-ms),
and a validation error (:unknown-config-key) for any unrecognized key
instead of silent passthrough. Info maps come back normalized the same way,
with ISO-8601 timestamps on every platform.create-consumer, update-consumer,
delete-consumer, consumer-info, list-consumers, and consumer-names,
with
the same closed config-map contract (:ack-policy, :deliver-policy,
:ack-wait-ms, :filter-subjects, …).publish against the JetStream context resolves to a
PubAck {:stream :seq :duplicate :domain}. Supports :msg-id server-side
dedup, :expect optimistic concurrency (a mismatch surfaces as
:wrong-last-sequence), :timeout-ms, and per-publish :codec. A reserved
Nats-* key in user :headers is rejected pre-flight as :reserved-header.next (promise of one message or nil),
fetch (promise of a bounded vector), and consume (continuous delivery
through the same promise-return handler contract as core subscriptions — the
runtime waits for the handler's returned promise before delivering more, so
a slow handler simply slows the pull; no :slow-consumer, no async-library
dependency). consume returns a drainable, unsubscribable handle (ADR 0018).ack, nak (with optional :delay-ms), term,
and working are synchronous, idempotent, and
never throw; double-ack returns a promise of the server's confirmation for
exactly-once-adjacent processing (ADR 0019).{:subject :data :headers :js {…}}; :js carries normalized delivery
metadata (:stream, :consumer, :stream-seq, :delivery-seq,
:delivered, :pending, :redelivered, :timestamp, :domain).ordered-consumer replays a Stream gap-free with no
acks, for simple single-reader stream reads.:types identical on
every platform: :jetstream-not-enabled, :stream-not-found,
:consumer-not-found, :wrong-last-sequence, and the catch-all
:jetstream-api-error carrying the server's {:code :description}.
Consume-time side-band conditions (:heartbeats-missed,
:consumer-deleted, :exceeded-limits) route to a per-consume :on-error.
New validation :types for caller misuse: :invalid-name,
:unknown-config-key, :reserved-header, :no-ack-subject (ADR 0020).@nats-io/jetstream is installed
automatically and version-pinned in lockstep with the core client, so the
duplicate-nats-core hazard cannot arise. The JetStream import is confined to
JetStream namespaces: a core-only browser bundle ships zero JetStream bytes,
enforced by a CI guard (ADR 0016).require-ing every namespace, and the opt-in :json and :transit codec
namespaces pull dependencies (org.clojure/data.json, com.cognitect/transit-*)
that are deliberately absent from the published pom (ADR 0004's clean forced
footprint) — so loading them failed the 0.1.0 doc build. Both namespaces are now
marked ^:no-doc, which excludes them from analysis. The codecs are unchanged and
remain documented via the README, ADR 0011, and nats-cljc.codec. No API or
runtime change.First release: the Phase 1 core and Phase 1.5 blocking layer, tested on the JVM, Node, and the browser.
nats-cljc.core) — one .cljc surface that runs
unchanged on the JVM, the browser, and Node: connect, publish, subscribe,
request/reply, unsubscribe (with auto-unsubscribe), flush, drain,
close, and a subject builder. One-shot operations return the platform-native
promise (CompletableFuture on the JVM, js/Promise on ClojureScript).:queue.:edn by default with zero extra runtime dependencies; opt-in
:json and :transit codecs; custom codecs via nats-cljc.codec/register!.:on-status notifications
with a canonical :type set, identical in shape on every platform.:type set surfaced as ex-info,
identical in shape on every platform, plus a separate validation-error category
for caller misuse.nats-cljc.blocking.core) — the same
verb names, synchronous, with a pull-based subscription model.Can you improve this documentation?Edit on GitHub
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 |