🐛 Fix TypeError in get-points when content is not PathData (#8634)

The with-cache macro in impl.cljc assumed the target was always a
PathData instance (which has a cache field). When content was a plain
vector, (.-cache content) returned undefined in JS, causing:

  TypeError: Cannot read properties of undefined (reading 'get')

Fix:
- path/get-points (app.common.types.path) is now the canonical safe
  entry point: converts non-PathData content via impl/path-data and
  handles nil safely before delegating to segment/get-points
- segment/get-points remains a low-level function that expects a
  PathData instance (no defensive logic at that level)
- streams.cljs: replace direct call to path.segm/get-points with
  path/get-points so the safe conversion path is always used
- with-cache macro: guards against nil/undefined cache, falling back
  to direct evaluation for non-PathData targets

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
This commit is contained in:
Andrey Antukh 2026-03-17 09:31:10 +01:00 committed by GitHub
parent efd6d19a12
commit 0779c9ca61
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 25 additions and 14 deletions

View File

@ -190,10 +190,14 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn get-points (defn get-points
"Returns points for the given segment, faster version of "Returns points for the given content. Accepts PathData instances or
the `content->points`." plain segment vectors. Returns nil for nil content."
[content] [content]
(some-> content segment/get-points)) (when (some? content)
(let [content (if (impl/path-data? content)
content
(impl/path-data content))]
(segment/get-points content))))
(defn calc-selrect (defn calc-selrect
"Calculate selrect from a content. The content can be in a PathData "Calculate selrect from a content. The content can be in a PathData

View File

@ -52,14 +52,15 @@
[target key & expr] [target key & expr]
(if (:ns &env) (if (:ns &env)
(let [target (with-meta target {:tag 'js})] (let [target (with-meta target {:tag 'js})]
`(let [~'cache (.-cache ~target) `(let [~'cache (.-cache ~target)]
~'result (.get ~'cache ~key)] (if (some? ~'cache)
(if ~'result (let [~'result (.get ~'cache ~key)]
(do (if ~'result
~'result) ~'result
(let [~'result (do ~@expr)] (let [~'result (do ~@expr)]
(.set ~'cache ~key ~'result) (.set ~'cache ~key ~'result)
~'result)))) ~'result)))
(do ~@expr))))
`(do ~@expr))) `(do ~@expr)))
(defn- impl-transform-segment (defn- impl-transform-segment

View File

@ -279,6 +279,12 @@
(t/is (some? points)) (t/is (some? points))
(t/is (= 3 (count points)))))) (t/is (= 3 (count points))))))
(t/deftest path-get-points-plain-vector-safe
(t/testing "path/get-points does not throw for plain vector content"
(let [points (path/get-points sample-content)]
(t/is (some? points))
(t/is (= 3 (count points))))))
(defn calculate-extremities (defn calculate-extremities
"Calculate extremities for the provided content. "Calculate extremities for the provided content.
A legacy implementation used mainly as reference for testing" A legacy implementation used mainly as reference for testing"

View File

@ -68,7 +68,7 @@
(let [content (st/get-path state :content) (let [content (st/get-path state :content)
content (if (and (not preserve-move-to) content (if (and (not preserve-move-to)
(= (-> content last :command) :move-to)) (= (-> content last :command) :move-to))
(into [] (take (dec (count content)) content)) (path/content (take (dec (count content)) content))
content)] content)]
(st/set-content state content))) (st/set-content state content)))

View File

@ -8,7 +8,7 @@
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.types.path.segment :as path.segm] [app.common.types.path :as path]
[app.main.data.workspace.path.state :as pst] [app.main.data.workspace.path.state :as pst]
[app.main.snap :as snap] [app.main.snap :as snap]
[app.main.store :as st] [app.main.store :as st]
@ -167,7 +167,7 @@
ranges-stream ranges-stream
(->> content-stream (->> content-stream
(rx/filter some?) (rx/filter some?)
(rx/map path.segm/get-points) (rx/map path/get-points)
(rx/map snap/create-ranges))] (rx/map snap/create-ranges))]
(->> ms/mouse-position (->> ms/mouse-position