nREPL: make the built-in server middleware-extensible

The built-in nREPL stays minimal (clone/describe/eval/load-file/close) but now
composes a middleware stack so a library can add the heavier features (sessions,
interruptible-eval, completion, lookup) without bloating core.

- A middleware is (fn [handler] (fn [request] ...)); request carries :reply (a
  thread-safe send that adds id/session) plus the wire fields. List them in
  deps.edn :nrepl/middleware (symbols -> a middleware fn or a vector of them);
  jolt.nrepl composes them over the built-in handler.
- Public seam: respond, evaluate, register-ops!, new-session, err-msg.
- Per-connection send lock so middleware replying from other threads don't
  interleave bytes. describe advertises built-in + registered ops.

deps.clj surfaces :nrepl/middleware; jolt.main passes it to the server. Built-in
behavior unchanged when no middleware is declared. Runtime, no re-mint.
This commit is contained in:
Yogthos 2026-06-22 15:37:14 -04:00
parent d33277c0b2
commit 185b4fd3ca
3 changed files with 130 additions and 73 deletions

View file

@ -124,7 +124,10 @@
{:roots (vec (distinct (concat project-roots dep-roots)))
:main-opts main-opts
:tasks (:tasks edn)
:natives (vec (distinct (concat (:jolt/native edn) dep-natives)))})))
:natives (vec (distinct (concat (:jolt/native edn) dep-natives)))
;; nREPL middleware a library contributes (jolt.nrepl composes them over its
;; built-in handler) — symbols resolving to a middleware fn or a vector of them.
:nrepl-middleware (:nrepl/middleware edn)})))
(defn has-deps-edn? [project-dir]
(file-exists? (str project-dir "/deps.edn")))