diff --git a/CHANGELOG.md b/CHANGELOG.md index 4187bb5..1a33c52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/libraries/sdk/src/main/starfederation/datastar/clojure/api/common.clj b/libraries/sdk/src/main/starfederation/datastar/clojure/api/common.clj index ad8c4b8..7feb756 100644 --- a/libraries/sdk/src/main/starfederation/datastar/clojure/api/common.clj +++ b/libraries/sdk/src/main/starfederation/datastar/clojure/api/common.clj @@ -1,4 +1,6 @@ -(ns starfederation.datastar.clojure.api.common) +(ns starfederation.datastar.clojure.api.common + (:require + [clojure.string :as string])) ;; ----------------------------------------------------------------------------- ;; Option names @@ -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 @@ -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 @@ -56,6 +58,3 @@ (and (boolean? val) (not= val default-val))) - - - diff --git a/libraries/sdk/src/main/starfederation/datastar/clojure/api/elements.clj b/libraries/sdk/src/main/starfederation/datastar/clojure/api/elements.clj index 1774ac9..f32e581 100644 --- a/libraries/sdk/src/main/starfederation/datastar/clojure/api/elements.clj +++ b/libraries/sdk/src/main/starfederation/datastar/clojure/api/elements.clj @@ -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) @@ -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 @@ -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 @@ -146,5 +147,3 @@ (patch-elements! sse-gen "" (assoc opts common/selector selector common/patch-mode consts/element-patch-mode-remove))) - - diff --git a/libraries/sdk/src/main/starfederation/datastar/clojure/api/signals.clj b/libraries/sdk/src/main/starfederation/datastar/clojure/api/signals.clj index d7eea65..fc87c63 100644 --- a/libraries/sdk/src/main/starfederation/datastar/clojure/api/signals.clj +++ b/libraries/sdk/src/main/starfederation/datastar/clojure/api/signals.clj @@ -21,8 +21,7 @@ (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))))) @@ -30,7 +29,7 @@ (= (->patch-signals "{'some': \n 'json'}" {}) ["signals {'some': " "signals 'json'}"])) - + (defn patch-signals! [sse-gen signals-content opts] (try (sse/send-event! sse-gen @@ -56,5 +55,3 @@ (if (= :get (:request-method request)) (get-in request [:query-params consts/datastar-key]) (:body request))) - - diff --git a/libraries/sdk/src/main/starfederation/datastar/clojure/consts.clj b/libraries/sdk/src/main/starfederation/datastar/clojure/consts.clj index 2147ded..a629f2b 100644 --- a/libraries/sdk/src/main/starfederation/datastar/clojure/consts.clj +++ b/libraries/sdk/src/main/starfederation/datastar/clojure/consts.clj @@ -2,8 +2,7 @@ (ns starfederation.datastar.clojure.consts) -(def datastar-key "datastar") -(def version "1.0.0-RC.1") +(def datastar-key "datastar") ;; ----------------------------------------------------------------------------- @@ -14,10 +13,6 @@ 1000) -;; ----------------------------------------------------------------------------- -;; Default values -;; ----------------------------------------------------------------------------- - ;; ----------------------------------------------------------------------------- ;; Dataline literals ;; ----------------------------------------------------------------------------- diff --git a/src/dev/bench/split.clj b/src/dev/bench/split.clj new file mode 100644 index 0000000..364d9ec --- /dev/null +++ b/src/dev/bench/split.clj @@ -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))