Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@
## 2025-11-27 - RC5

### Fixed

- Fixed `starfederation.datastar.clojure.adapter.http-kit2/wrap-start-responding`,
the async arity was improperly managed.

### Changes

- The internals of the Ring SSE generator have been reworked. The SSE gen won't error
if a user reuses it for different requests anymore. Documentation is in place to warn
against such reuse and this change makes for much simpler code.
- When creating SSE events we need to split on end of lines the text that will
constitute the data lines of the event. This can prevent SSE event injection
problems. The SSE machinery has been refactored so that this splitting happens
in a code path that all API functions go through instead of doing it in every
d* patch function. This way we can't forget that splitting.

## 2025-10-30 - RC4

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
(ns starfederation.datastar.clojure.api.common)
(ns starfederation.datastar.clojure.api.common
(:require
[clojure.string :as string]))

;; -----------------------------------------------------------------------------
;; Option names
Expand Down Expand Up @@ -30,7 +32,7 @@
"Add an option `v` line to the transient `data-lines!` vector.

Args:
- `data-lines`: a transient vector of data-lines that will be written in a sse
- `data-lines!`: a transient vector of data-lines that will be written in a sse
event
- `prefix`: The Datastar specific preffix for that line
- `v`: the value for that line
Expand All @@ -41,13 +43,13 @@

(defn add-data-lines!
"Add several data-lines to the `data-lines!` transient vector."
[data-lines! prefix lines-seq]
(reduce
(fn [acc part]
(conj! acc (str prefix part)))
data-lines!
lines-seq))

[data-lines! prefix ^String text]
(let [stream (.lines text)
i (.iterator stream)]
(loop [acc data-lines!]
(if (.hasNext i)
(recur (conj! acc (str prefix (.next i))))
acc))))

(defn add-boolean-option?
"Utility used to test whether an boolean option should result in a sse event
Expand All @@ -56,6 +58,3 @@
(and
(boolean? val)
(not= val default-val)))



Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
(cond-> data-lines!
(and sel (valid-selector? sel))
(common/add-opt-line! consts/selector-dataline-literal sel)

(and patch-mode (add-epm? patch-mode))
(common/add-opt-line! consts/mode-dataline-literal patch-mode)

Expand All @@ -47,8 +47,7 @@
[data-lines! element]
(cond-> data-lines!
(u/not-empty-string? element)
(common/add-data-lines! consts/elements-dataline-literal
(string/split-lines element))))
(common/add-data-lines! consts/elements-dataline-literal element)))


(defn ->patch-elements
Expand Down Expand Up @@ -91,13 +90,15 @@
(defn conj-patch-elements-seq
"Adds a the data-lines when patching a seq of strings elements."
[data-lines! elements-seq]
(cond-> data-lines!
(seq elements-seq)
(common/add-data-lines! consts/elements-dataline-literal
(eduction
(comp (mapcat string/split-lines)
(remove string/blank?))
elements-seq))))
(if (seq elements-seq)
(reduce
(fn [data-lines! elements]
(if (string/blank? elements)
data-lines!
(common/add-data-lines! data-lines! consts/elements-dataline-literal elements)))
data-lines!
elements-seq)
data-lines!))


(defn ->patch-elements-seq
Expand Down Expand Up @@ -146,5 +147,3 @@
(patch-elements! sse-gen "" (assoc opts
common/selector selector
common/patch-mode consts/element-patch-mode-remove)))


Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,15 @@
(common/add-opt-line! consts/only-if-missing-dataline-literal oim)

(u/not-empty-string? signals)
(common/add-data-lines! consts/signals-dataline-literal
(string/split-lines signals))))))
(common/add-data-lines! consts/signals-dataline-literal signals)))))



(comment
(= (->patch-signals "{'some': \n 'json'}" {})
["signals {'some': "
"signals 'json'}"]))

(defn patch-signals! [sse-gen signals-content opts]
(try
(sse/send-event! sse-gen
Expand All @@ -56,5 +55,3 @@
(if (= :get (:request-method request))
(get-in request [:query-params consts/datastar-key])
(:body request)))


Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
(ns starfederation.datastar.clojure.consts)


(def datastar-key "datastar")
(def version "1.0.0-RC.1")
(def datastar-key "datastar")


;; -----------------------------------------------------------------------------
Expand All @@ -14,10 +13,6 @@
1000)


;; -----------------------------------------------------------------------------
;; Default values
;; -----------------------------------------------------------------------------

;; -----------------------------------------------------------------------------
;; Dataline literals
;; -----------------------------------------------------------------------------
Expand Down
28 changes: 28 additions & 0 deletions src/dev/bench/split.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
(ns bench.split
(:require
[clojure.string :as string]
[starfederation.datastar.clojure.api.common :as c]))


(defn old-datalines [data-lines! prefix text]
(reduce
(fn [acc part]
(conj! acc (str prefix part)))
data-lines!
(string/split-lines text)))


(def input "hello there\n wold !\r\n How are \ryou \ntoday")
(def input-big (apply str (repeat 100 input)))

(defn bench [f input]
(println "---------------------------------------")
(dotimes [_ 20]
(time
(dotimes [_ 10000]
(f (transient []) "elements " input)))))


(comment
(bench old-datalines input-big)
(bench c/add-data-lines! input-big))