nandi/frqpublic Fork 0
a036bb4
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Say what every dart call is talking to

Forty-four DYNAMIC WARNINGs, and none left. Each one is ClojureDart saying it
could not resolve a member statically and fell back to dynamic dispatch,
because the target's type was lost — through `defonce`, through `await`, or
through an `or`. A type hint at the site is the whole fix.

One of them was not cosmetic, and is the reason to do this rather than
silence the output. `frq.hiccup`'s scroll follow reads `.pixels` off a
ScrollPosition; hinting the *binding* rather than the uses made a
non-nullable cast out of a `when` that is nil whenever the controller has no
clients, which is most of the time — "type 'Null' is not a subtype of type
'ScrollPosition'", thrown before the `(nil? pos)` two lines down could run.
The hint belongs where the `or` has already ruled nil out, and the comment
there says so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandi committed 2026-09-12T07:05:24-07:00 Browse files
a036bb4 parent: 6ca9b5a
modified flutter/src/frq/atproto/dart.cljd +4 -2
@@ -26,13 +26,15 @@
2626 ;; resolveHandle path carries its query string, and Uri.https would escape
2727 ;; the ? into the path.
2828 (let [uri (Uri.parse (str "https://" host path))
29- req (await (if body (.postUrl client uri) (.getUrl client uri)))]
29+ ^io/HttpClientRequest req (await (if body
30+ (.postUrl ^io/HttpClient client uri)
31+ (.getUrl ^io/HttpClient client uri)))]
3032 (.set (.-headers req) "user-agent" "frq")
3133 (.set (.-headers req) "accept" "application/json")
3234 (when body
3335 (.set (.-headers req) "content-type" "application/json")
3436 (.write req body))
35- (let [resp (await (.close req))]
37+ (let [^io/HttpClientResponse resp (await (.close req))]
3638 (await (.join (.transform resp (.-decoder conv/utf8)))))))
3739
3840 (defn ^:async resolve-handle
@@ -26,13 +26,15 @@
26 ;; resolveHandle path carries its query string, and Uri.https would escape26 ;; resolveHandle path carries its query string, and Uri.https would escape
27 ;; the ? into the path.27 ;; the ? into the path.
28 (let [uri (Uri.parse (str "https://" host path))28 (let [uri (Uri.parse (str "https://" host path))
29- req (await (if body (.postUrl client uri) (.getUrl client uri)))]29+ ^io/HttpClientRequest req (await (if body
30+ (.postUrl ^io/HttpClient client uri)
31+ (.getUrl ^io/HttpClient client uri)))]
30 (.set (.-headers req) "user-agent" "frq")32 (.set (.-headers req) "user-agent" "frq")
31 (.set (.-headers req) "accept" "application/json")33 (.set (.-headers req) "accept" "application/json")
32 (when body34 (when body
33 (.set (.-headers req) "content-type" "application/json")35 (.set (.-headers req) "content-type" "application/json")
34 (.write req body))36 (.write req body))
35- (let [resp (await (.close req))]37+ (let [^io/HttpClientResponse resp (await (.close req))]
36 (await (.join (.transform resp (.-decoder conv/utf8)))))))38 (await (.join (.transform resp (.-decoder conv/utf8)))))))
37 39
38 (defn ^:async resolve-handle40 (defn ^:async resolve-handle
modified flutter/src/frq/crypto/dart.cljd +1 -1
@@ -34,7 +34,7 @@
3434
3535 (defn- random-bytes [n]
3636 (let [out (td/Uint8List. n)]
37- (dotimes [i n] (aset out i (.nextInt rng 256)))
37+ (dotimes [i n] (aset out i (.nextInt ^math/Random rng 256)))
3838 (vec out)))
3939
4040 (defn- sha256 [bs]
@@ -34,7 +34,7 @@
34 34
35 (defn- random-bytes [n]35 (defn- random-bytes [n]
36 (let [out (td/Uint8List. n)]36 (let [out (td/Uint8List. n)]
37- (dotimes [i n] (aset out i (.nextInt rng 256)))37+ (dotimes [i n] (aset out i (.nextInt ^math/Random rng 256)))
38 (vec out)))38 (vec out)))
39 39
40 (defn- sha256 [bs]40 (defn- sha256 [bs]
modified flutter/src/frq/hiccup.cljd +9 -3
@@ -60,7 +60,7 @@
6060
6161 (defn- controller-for [k text]
6262 (let [k (or k ::anonymous)
63- c (or (get @controllers k)
63+ ^m/TextEditingController c (or (get @controllers k)
6464 (let [c (m/TextEditingController .text (or text ""))]
6565 (swap! controllers assoc k c)
6666 c))]
@@ -186,12 +186,18 @@
186186
187187 After the frame, because the extent being scrolled to is the height of
188188 content that has not been laid out yet at the point this is called."
189- [k ctrl token]
189+ [k ^m/ScrollController ctrl token]
190190 (let [mark (get @scroll-marks k ::fresh)
191+ ;; Not hinted here: a controller with no clients has no position, so
192+ ;; this is nil as often as not, and a non-nullable hint on the binding
193+ ;; is a cast that fails before `(nil? pos)` below ever gets to run.
194+ ;; The hint goes on the uses instead, where the `or` has already ruled
195+ ;; nil out.
191196 pos (when (.-hasClients ctrl) (.-position ctrl))
192197 at-end (or (= mark ::fresh)
193198 (nil? pos)
194- (>= (.-pixels pos) (- (.-maxScrollExtent pos) 24.0)))]
199+ (>= (.-pixels ^m/ScrollPosition pos)
200+ (- (.-maxScrollExtent ^m/ScrollPosition pos) 24.0)))]
195201 (when (or (not= mark token) at-end)
196202 (swap! scroll-marks assoc k token)
197203 (.addPostFrameCallback
@@ -60,7 +60,7 @@
60 60
61 (defn- controller-for [k text]61 (defn- controller-for [k text]
62 (let [k (or k ::anonymous)62 (let [k (or k ::anonymous)
63- c (or (get @controllers k)63+ ^m/TextEditingController c (or (get @controllers k)
64 (let [c (m/TextEditingController .text (or text ""))]64 (let [c (m/TextEditingController .text (or text ""))]
65 (swap! controllers assoc k c)65 (swap! controllers assoc k c)
66 c))]66 c))]
@@ -186,12 +186,18 @@
186 186
187 After the frame, because the extent being scrolled to is the height of187 After the frame, because the extent being scrolled to is the height of
188 content that has not been laid out yet at the point this is called."188 content that has not been laid out yet at the point this is called."
189- [k ctrl token]189+ [k ^m/ScrollController ctrl token]
190 (let [mark (get @scroll-marks k ::fresh)190 (let [mark (get @scroll-marks k ::fresh)
191+ ;; Not hinted here: a controller with no clients has no position, so
192+ ;; this is nil as often as not, and a non-nullable hint on the binding
193+ ;; is a cast that fails before `(nil? pos)` below ever gets to run.
194+ ;; The hint goes on the uses instead, where the `or` has already ruled
195+ ;; nil out.
191 pos (when (.-hasClients ctrl) (.-position ctrl))196 pos (when (.-hasClients ctrl) (.-position ctrl))
192 at-end (or (= mark ::fresh)197 at-end (or (= mark ::fresh)
193 (nil? pos)198 (nil? pos)
194- (>= (.-pixels pos) (- (.-maxScrollExtent pos) 24.0)))]199+ (>= (.-pixels ^m/ScrollPosition pos)
200+ (- (.-maxScrollExtent ^m/ScrollPosition pos) 24.0)))]
195 (when (or (not= mark token) at-end)201 (when (or (not= mark token) at-end)
196 (swap! scroll-marks assoc k token)202 (swap! scroll-marks assoc k token)
197 (.addPostFrameCallback203 (.addPostFrameCallback
modified flutter/src/frq/io/dart.cljd +2 -2
@@ -109,7 +109,7 @@
109109 :config-dir (fn [] dir)
110110 :file-exists? (fn [p] (.existsSync (io/File. p)))
111111 :directory? (fn [p] (.existsSync (io/Directory. p)))
112- :list-dir (fn [p] (mapv #(.-path %) (.listSync (io/Directory. p))))
112+ :list-dir (fn [p] (mapv #(.-path ^io/FileSystemEntity %) (.listSync (io/Directory. p))))
113113 :mkdirs! (fn [p] (.createSync (io/Directory. p) .recursive true))
114114 :delete-file! (fn [p] (.deleteSync (io/File. p)))
115115 :slurp slurp*
@@ -118,5 +118,5 @@
118118 :utf8-bytes (fn [s] (vec (.encode conv/utf8 (str s))))
119119 :utf8-string utf8-string
120120 :wall-nanos (fn [] (* 1000 (.-microsecondsSinceEpoch (DateTime/now))))
121- :mono-nanos (fn [] (* 1000 (.-inMicroseconds (.-elapsed uptime))))
121+ :mono-nanos (fn [] (* 1000 (.-inMicroseconds (.-elapsed ^Stopwatch uptime))))
122122 :local-offset-seconds local-offset-seconds}))
@@ -109,7 +109,7 @@
109 :config-dir (fn [] dir)109 :config-dir (fn [] dir)
110 :file-exists? (fn [p] (.existsSync (io/File. p)))110 :file-exists? (fn [p] (.existsSync (io/File. p)))
111 :directory? (fn [p] (.existsSync (io/Directory. p)))111 :directory? (fn [p] (.existsSync (io/Directory. p)))
112- :list-dir (fn [p] (mapv #(.-path %) (.listSync (io/Directory. p))))112+ :list-dir (fn [p] (mapv #(.-path ^io/FileSystemEntity %) (.listSync (io/Directory. p))))
113 :mkdirs! (fn [p] (.createSync (io/Directory. p) .recursive true))113 :mkdirs! (fn [p] (.createSync (io/Directory. p) .recursive true))
114 :delete-file! (fn [p] (.deleteSync (io/File. p)))114 :delete-file! (fn [p] (.deleteSync (io/File. p)))
115 :slurp slurp*115 :slurp slurp*
@@ -118,5 +118,5 @@
118 :utf8-bytes (fn [s] (vec (.encode conv/utf8 (str s))))118 :utf8-bytes (fn [s] (vec (.encode conv/utf8 (str s))))
119 :utf8-string utf8-string119 :utf8-string utf8-string
120 :wall-nanos (fn [] (* 1000 (.-microsecondsSinceEpoch (DateTime/now))))120 :wall-nanos (fn [] (* 1000 (.-microsecondsSinceEpoch (DateTime/now))))
121- :mono-nanos (fn [] (* 1000 (.-inMicroseconds (.-elapsed uptime))))121+ :mono-nanos (fn [] (* 1000 (.-inMicroseconds (.-elapsed ^Stopwatch uptime))))
122 :local-offset-seconds local-offset-seconds}))122 :local-offset-seconds local-offset-seconds}))
modified flutter/src/frq/main.cljd +2 -2
@@ -558,7 +558,7 @@
558558 ;; build, so nothing can catch them, and what they leave is a blank screen
559559 ;; and an empty log. Flutter routes them here instead.
560560 (set! (.-onError m/FlutterError)
561- (fn [details]
561+ (fn [^m/FlutterErrorDetails details]
562562 (m/debugPrint (str "frq: FLUTTER ERROR " (.-exception details)))
563563 (m/debugPrint (str "frq: LIBRARY " (.-library details)
564564 " CONTEXT " (.-context details)))))
@@ -573,7 +573,7 @@
573573 ;; when to ask and remembers the answer; only the awaiting is here.
574574 (profile/install-fetch!
575575 (fn [actor]
576- (.then (atproto/fetch (profile/profile-req actor))
576+ (.then ^async/Future (atproto/fetch (profile/profile-req actor))
577577 (fn [body] (profile/deliver-profile! actor body) nil)
578578 .onError (fn [_ _] (profile/deliver-profile! actor nil) nil))))
579579 ;; Before any widget is built: a cell that changes before its watch is on
@@ -558,7 +558,7 @@
558 ;; build, so nothing can catch them, and what they leave is a blank screen558 ;; build, so nothing can catch them, and what they leave is a blank screen
559 ;; and an empty log. Flutter routes them here instead.559 ;; and an empty log. Flutter routes them here instead.
560 (set! (.-onError m/FlutterError)560 (set! (.-onError m/FlutterError)
561- (fn [details]561+ (fn [^m/FlutterErrorDetails details]
562 (m/debugPrint (str "frq: FLUTTER ERROR " (.-exception details)))562 (m/debugPrint (str "frq: FLUTTER ERROR " (.-exception details)))
563 (m/debugPrint (str "frq: LIBRARY " (.-library details)563 (m/debugPrint (str "frq: LIBRARY " (.-library details)
564 " CONTEXT " (.-context details)))))564 " CONTEXT " (.-context details)))))
@@ -573,7 +573,7 @@
573 ;; when to ask and remembers the answer; only the awaiting is here.573 ;; when to ask and remembers the answer; only the awaiting is here.
574 (profile/install-fetch!574 (profile/install-fetch!
575 (fn [actor]575 (fn [actor]
576- (.then (atproto/fetch (profile/profile-req actor))576+ (.then ^async/Future (atproto/fetch (profile/profile-req actor))
577 (fn [body] (profile/deliver-profile! actor body) nil)577 (fn [body] (profile/deliver-profile! actor body) nil)
578 .onError (fn [_ _] (profile/deliver-profile! actor nil) nil))))578 .onError (fn [_ _] (profile/deliver-profile! actor nil) nil))))
579 ;; Before any widget is built: a cell that changes before its watch is on579 ;; Before any widget is built: a cell that changes before its watch is on
modified flutter/src/frq/net/dart.cljd +2 -2
@@ -67,8 +67,8 @@
6767 .cancelOnError true))
6868 sock))
6969
70-(defn send-line! [sock line]
70+(defn send-line! [^io/Socket sock line]
7171 (.write sock (str line "\r\n")))
7272
73-(defn close! [sock]
73+(defn close! [^io/Socket sock]
7474 (try (.destroy sock) (catch Exception _ nil)))
@@ -67,8 +67,8 @@
67 .cancelOnError true))67 .cancelOnError true))
68 sock))68 sock))
69 69
70-(defn send-line! [sock line]70+(defn send-line! [^io/Socket sock line]
71 (.write sock (str line "\r\n")))71 (.write sock (str line "\r\n")))
72 72
73-(defn close! [sock]73+(defn close! [^io/Socket sock]
74 (try (.destroy sock) (catch Exception _ nil)))74 (try (.destroy sock) (catch Exception _ nil)))
modified flutter/src/frq/oauth/dart.cljd +4 -4
@@ -54,8 +54,8 @@
5454 (try
5555 (on-url (core/login-url broker handle (str "http://127.0.0.1:" port)))
5656 (.listen server
57- (fn [req]
58- (let [resp (.-response req)]
57+ (fn [^io/HttpRequest req]
58+ (let [^io/HttpResponse resp (.-response req)]
5959 (if (= "POST" (.-method req))
6060 ;; The fragment the page posted back. A POST carrying
6161 ;; nothing usable is not the end of the wait the real
@@ -77,10 +77,10 @@
7777 .onDone
7878 (fn []
7979 (let [tokens (try (core/tokens-of
80- (.trim (host/utf8-string @chunks)))
80+ (.trim ^String (host/utf8-string @chunks)))
8181 (catch Object _ nil))]
8282 (.write resp (if tokens "ok" "bad payload"))
83- (.then (.close resp)
83+ (.then ^async/Future (.close resp)
8484 (fn [_]
8585 (when (and tokens
8686 (not (.-isCompleted done)))
@@ -54,8 +54,8 @@
54 (try54 (try
55 (on-url (core/login-url broker handle (str "http://127.0.0.1:" port)))55 (on-url (core/login-url broker handle (str "http://127.0.0.1:" port)))
56 (.listen server56 (.listen server
57- (fn [req]57+ (fn [^io/HttpRequest req]
58- (let [resp (.-response req)]58+ (let [^io/HttpResponse resp (.-response req)]
59 (if (= "POST" (.-method req))59 (if (= "POST" (.-method req))
60 ;; The fragment the page posted back. A POST carrying60 ;; The fragment the page posted back. A POST carrying
61 ;; nothing usable is not the end of the wait the real61 ;; nothing usable is not the end of the wait the real
@@ -77,10 +77,10 @@
77 .onDone77 .onDone
78 (fn []78 (fn []
79 (let [tokens (try (core/tokens-of79 (let [tokens (try (core/tokens-of
80- (.trim (host/utf8-string @chunks)))80+ (.trim ^String (host/utf8-string @chunks)))
81 (catch Object _ nil))]81 (catch Object _ nil))]
82 (.write resp (if tokens "ok" "bad payload"))82 (.write resp (if tokens "ok" "bad payload"))
83- (.then (.close resp)83+ (.then ^async/Future (.close resp)
84 (fn [_]84 (fn [_]
85 (when (and tokens85 (when (and tokens
86 (not (.-isCompleted done)))86 (not (.-isCompleted done)))
modified flutter/src/frq/theme.cljd +1 -1
@@ -51,7 +51,7 @@
5151 (def dim
5252 "`:dim-label` is libcosmic's caption class, which is the body colour stepped
5353 back rather than a colour of its own."
54- (.withValues on-bg .alpha 0.7))
54+ (.withValues ^m/Color on-bg .alpha 0.7))
5555
5656 (def brightness (if c/dark? m/Brightness.dark m/Brightness.light))
5757
@@ -51,7 +51,7 @@
51 (def dim51 (def dim
52 "`:dim-label` is libcosmic's caption class, which is the body colour stepped52 "`:dim-label` is libcosmic's caption class, which is the body colour stepped
53 back rather than a colour of its own."53 back rather than a colour of its own."
54- (.withValues on-bg .alpha 0.7))54+ (.withValues ^m/Color on-bg .alpha 0.7))
55 55
56 (def brightness (if c/dark? m/Brightness.dark m/Brightness.light))56 (def brightness (if c/dark? m/Brightness.dark m/Brightness.light))
57 57