Add (param :as type) annotations to all fn/lambda params across SX spec
Extend the type annotation system from defcomp-only to fn/lambda params: - Infrastructure: sf-lambda, py/js-collect-params-loop, and bootstrap_py.py now recognize (name :as type) in param lists, extracting just the name - bootstrap_py.py: add _extract_param_name() helper, fix _emit_for_each_stmt - 521 type annotations across 22 .sx spec files (eval, types, adapters, transpilers, engine, orchestration, deps, signals, router, prove, etc.) - Zero behavioral change: annotations are metadata for static analysis only - All bootstrappers (Python, JS, G1) pass, 81/81 spec tests pass Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -34,7 +34,7 @@
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define dispatch-trigger-events
|
||||
(fn (el header-val)
|
||||
(fn (el (header-val :as string))
|
||||
;; Dispatch events from SX-Trigger / SX-Trigger-After-Swap headers.
|
||||
;; Value can be JSON object (name → detail) or comma-separated names.
|
||||
(when header-val
|
||||
@@ -42,12 +42,12 @@
|
||||
(if parsed
|
||||
;; JSON object: keys are event names, values are detail
|
||||
(for-each
|
||||
(fn (key)
|
||||
(fn ((key :as string))
|
||||
(dom-dispatch el key (get parsed key)))
|
||||
(keys parsed))
|
||||
;; Comma-separated event names
|
||||
(for-each
|
||||
(fn (name)
|
||||
(fn ((name :as string))
|
||||
(let ((trimmed (trim name)))
|
||||
(when (not (empty? trimmed))
|
||||
(dom-dispatch el trimmed (dict)))))
|
||||
@@ -73,7 +73,7 @@
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define execute-request
|
||||
(fn (el verbInfo extraParams)
|
||||
(fn (el (verbInfo :as dict) (extraParams :as dict))
|
||||
;; Gate checks then delegate to do-fetch.
|
||||
;; verbInfo: dict with "method" and "url" (or nil to read from element).
|
||||
;; Re-read from element in case attributes were morphed since binding.
|
||||
@@ -106,7 +106,7 @@
|
||||
|
||||
|
||||
(define do-fetch
|
||||
(fn (el verb method url extraParams)
|
||||
(fn (el (verb :as string) (method :as string) (url :as string) (extraParams :as dict))
|
||||
;; Execute the actual fetch. Manages abort, headers, body, loading state.
|
||||
(let ((sync (dom-get-attr el "sx-sync")))
|
||||
;; Abort previous if sync mode (per-element)
|
||||
@@ -140,7 +140,7 @@
|
||||
;; Merge extra params as headers
|
||||
(when extraParams
|
||||
(for-each
|
||||
(fn (k) (dict-set! headers k (get extraParams k)))
|
||||
(fn ((k :as string)) (dict-set! headers k (get extraParams k)))
|
||||
(keys extraParams)))
|
||||
|
||||
;; Content-Type
|
||||
@@ -172,7 +172,7 @@
|
||||
"cross-origin" (cross-origin? final-url)
|
||||
"preloaded" cached)
|
||||
;; Success callback
|
||||
(fn (resp-ok status get-header text)
|
||||
(fn ((resp-ok :as boolean) (status :as number) get-header (text :as string))
|
||||
(do
|
||||
(clear-loading-state el indicator disabled-elts)
|
||||
(revert-optimistic optimistic-state)
|
||||
@@ -202,7 +202,7 @@
|
||||
|
||||
|
||||
(define handle-fetch-success
|
||||
(fn (el url verb extraParams get-header text)
|
||||
(fn (el (url :as string) (verb :as string) (extraParams :as dict) get-header (text :as string))
|
||||
;; Route a successful response through the appropriate handler.
|
||||
(let ((resp-headers (process-response-headers get-header)))
|
||||
;; CSS hash update
|
||||
@@ -270,7 +270,7 @@
|
||||
|
||||
|
||||
(define handle-sx-response
|
||||
(fn (el target text swap-style use-transition)
|
||||
(fn (el target (text :as string) (swap-style :as string) (use-transition :as boolean))
|
||||
;; Handle SX-format response: strip components, extract CSS, render, swap.
|
||||
(let ((cleaned (strip-component-scripts text)))
|
||||
(let ((final (extract-response-css cleaned)))
|
||||
@@ -281,7 +281,7 @@
|
||||
(dom-append container rendered)
|
||||
;; Process OOB swaps
|
||||
(process-oob-swaps container
|
||||
(fn (t oob s)
|
||||
(fn (t oob (s :as string))
|
||||
(dispose-islands-in t)
|
||||
(swap-dom-nodes t oob s)
|
||||
(sx-hydrate t)
|
||||
@@ -301,7 +301,7 @@
|
||||
|
||||
|
||||
(define handle-html-response
|
||||
(fn (el target text swap-style use-transition)
|
||||
(fn (el target (text :as string) (swap-style :as string) (use-transition :as boolean))
|
||||
;; Handle HTML-format response: parse, OOB, select, swap.
|
||||
(let ((doc (dom-parse-html-document text)))
|
||||
(when doc
|
||||
@@ -320,7 +320,7 @@
|
||||
(dom-set-inner-html container (dom-body-inner-html doc))
|
||||
;; Process OOB swaps
|
||||
(process-oob-swaps container
|
||||
(fn (t oob s)
|
||||
(fn (t oob (s :as string))
|
||||
(dispose-islands-in t)
|
||||
(swap-dom-nodes t oob s)
|
||||
(post-swap t)))
|
||||
@@ -338,7 +338,7 @@
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define handle-retry
|
||||
(fn (el verb method url extraParams)
|
||||
(fn (el (verb :as string) (method :as string) (url :as string) (extraParams :as dict))
|
||||
;; Handle retry on failure if sx-retry is configured
|
||||
(let ((retry-attr (dom-get-attr el "sx-retry"))
|
||||
(spec (parse-retry-spec retry-attr)))
|
||||
@@ -358,12 +358,12 @@
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define bind-triggers
|
||||
(fn (el verbInfo)
|
||||
(fn (el (verbInfo :as dict))
|
||||
;; Bind triggers from sx-trigger attribute (or defaults)
|
||||
(let ((triggers (or (parse-trigger-spec (dom-get-attr el "sx-trigger"))
|
||||
(default-trigger (dom-tag-name el)))))
|
||||
(for-each
|
||||
(fn (trigger)
|
||||
(fn ((trigger :as dict))
|
||||
(let ((kind (classify-trigger trigger))
|
||||
(mods (get trigger "modifiers")))
|
||||
(cond
|
||||
@@ -393,7 +393,7 @@
|
||||
|
||||
|
||||
(define bind-event
|
||||
(fn (el event-name mods verbInfo)
|
||||
(fn (el (event-name :as string) (mods :as dict) (verbInfo :as dict))
|
||||
;; Bind a standard DOM event trigger.
|
||||
;; Handles delay, once, changed, optimistic, preventDefault.
|
||||
(let ((timer nil)
|
||||
@@ -506,12 +506,12 @@
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define process-oob-swaps
|
||||
(fn (container swap-fn)
|
||||
(fn (container (swap-fn :as lambda))
|
||||
;; Find and process out-of-band swaps in container.
|
||||
;; swap-fn is (fn (target oob-element swap-type) ...).
|
||||
(let ((oobs (find-oob-swaps container)))
|
||||
(for-each
|
||||
(fn (oob)
|
||||
(fn ((oob :as dict))
|
||||
(let ((target-id (get oob "target-id"))
|
||||
(target (dom-query-by-id target-id))
|
||||
(oob-el (get oob "element"))
|
||||
@@ -610,7 +610,7 @@
|
||||
(define _page-data-cache-ttl 30000) ;; 30 seconds in ms
|
||||
|
||||
(define page-data-cache-key
|
||||
(fn (page-name params)
|
||||
(fn ((page-name :as string) (params :as dict))
|
||||
;; Build a cache key from page name + params.
|
||||
;; Params are from route matching so order is deterministic.
|
||||
(let ((base page-name))
|
||||
@@ -618,13 +618,13 @@
|
||||
base
|
||||
(let ((parts (list)))
|
||||
(for-each
|
||||
(fn (k)
|
||||
(fn ((k :as string))
|
||||
(append! parts (str k "=" (get params k))))
|
||||
(keys params))
|
||||
(str base ":" (join "&" parts)))))))
|
||||
|
||||
(define page-data-cache-get
|
||||
(fn (cache-key)
|
||||
(fn ((cache-key :as string))
|
||||
;; Return cached data if fresh, else nil.
|
||||
(let ((entry (get _page-data-cache cache-key)))
|
||||
(if (nil? entry)
|
||||
@@ -636,7 +636,7 @@
|
||||
(get entry "data"))))))
|
||||
|
||||
(define page-data-cache-set
|
||||
(fn (cache-key data)
|
||||
(fn ((cache-key :as string) data)
|
||||
;; Store data with current timestamp.
|
||||
(dict-set! _page-data-cache cache-key
|
||||
{"data" data "ts" (now-ms)})))
|
||||
@@ -647,12 +647,12 @@
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define invalidate-page-cache
|
||||
(fn (page-name)
|
||||
(fn ((page-name :as string))
|
||||
;; Clear cached data for a page. Removes all cache entries whose key
|
||||
;; matches page-name (exact) or starts with "page-name:" (with params).
|
||||
;; Also notifies the service worker to clear its IndexedDB entries.
|
||||
(for-each
|
||||
(fn (k)
|
||||
(fn ((k :as string))
|
||||
(when (or (= k page-name) (starts-with? k (str page-name ":")))
|
||||
(dict-set! _page-data-cache k nil)))
|
||||
(keys _page-data-cache))
|
||||
@@ -667,7 +667,7 @@
|
||||
(log-info "sx:cache invalidate *")))
|
||||
|
||||
(define update-page-cache
|
||||
(fn (page-name data)
|
||||
(fn ((page-name :as string) data)
|
||||
;; Replace cached data for a page with server-provided data.
|
||||
;; Uses a bare page-name key (no params) — the server knows the
|
||||
;; canonical data shape for the page.
|
||||
@@ -676,7 +676,7 @@
|
||||
(log-info (str "sx:cache update " page-name)))))
|
||||
|
||||
(define process-cache-directives
|
||||
(fn (el resp-headers response-text)
|
||||
(fn (el (resp-headers :as dict) (response-text :as string))
|
||||
;; Process cache invalidation and update directives from both
|
||||
;; element attributes and response headers.
|
||||
;;
|
||||
@@ -722,7 +722,7 @@
|
||||
(define _optimistic-snapshots (dict))
|
||||
|
||||
(define optimistic-cache-update
|
||||
(fn (cache-key mutator)
|
||||
(fn ((cache-key :as string) (mutator :as lambda))
|
||||
;; Apply predicted mutation to cached data. Saves snapshot for rollback.
|
||||
;; Returns predicted data or nil if no cached data exists.
|
||||
(let ((cached (page-data-cache-get cache-key)))
|
||||
@@ -735,7 +735,7 @@
|
||||
predicted)))))
|
||||
|
||||
(define optimistic-cache-revert
|
||||
(fn (cache-key)
|
||||
(fn ((cache-key :as string))
|
||||
;; Revert to pre-mutation snapshot. Returns restored data or nil.
|
||||
(let ((snapshot (get _optimistic-snapshots cache-key)))
|
||||
(when snapshot
|
||||
@@ -744,12 +744,12 @@
|
||||
snapshot))))
|
||||
|
||||
(define optimistic-cache-confirm
|
||||
(fn (cache-key)
|
||||
(fn ((cache-key :as string))
|
||||
;; Server accepted — discard the rollback snapshot.
|
||||
(dict-delete! _optimistic-snapshots cache-key)))
|
||||
|
||||
(define submit-mutation
|
||||
(fn (page-name params action-name payload mutator-fn on-complete)
|
||||
(fn ((page-name :as string) (params :as dict) (action-name :as string) payload (mutator-fn :as lambda) (on-complete :as lambda))
|
||||
;; Optimistic mutation: predict locally, send to server, confirm or revert.
|
||||
;; on-complete is called with "confirmed" or "reverted" status.
|
||||
(let ((cache-key (page-data-cache-key page-name params))
|
||||
@@ -768,7 +768,7 @@
|
||||
(try-rerender-page page-name params result))
|
||||
(log-info (str "sx:optimistic confirmed " page-name))
|
||||
(when on-complete (on-complete "confirmed")))
|
||||
(fn (error)
|
||||
(fn ((error :as string))
|
||||
;; Failure: revert to snapshot
|
||||
(let ((reverted (optimistic-cache-revert cache-key)))
|
||||
(when reverted
|
||||
@@ -791,11 +791,11 @@
|
||||
(fn () _is-online))
|
||||
|
||||
(define offline-set-online!
|
||||
(fn (val)
|
||||
(fn ((val :as boolean))
|
||||
(set! _is-online val)))
|
||||
|
||||
(define offline-queue-mutation
|
||||
(fn (action-name payload page-name params mutator-fn)
|
||||
(fn ((action-name :as string) payload (page-name :as string) (params :as dict) (mutator-fn :as lambda))
|
||||
;; Queue a mutation for later sync. Apply optimistic update locally.
|
||||
(let ((cache-key (page-data-cache-key page-name params))
|
||||
(entry (dict
|
||||
@@ -816,26 +816,26 @@
|
||||
(define offline-sync
|
||||
(fn ()
|
||||
;; Replay all pending mutations. Called on reconnect.
|
||||
(let ((pending (filter (fn (e) (= (get e "status") "pending")) _offline-queue)))
|
||||
(let ((pending (filter (fn ((e :as dict)) (= (get e "status") "pending")) _offline-queue)))
|
||||
(when (not (empty? pending))
|
||||
(log-info (str "sx:offline syncing " (len pending) " mutations"))
|
||||
(for-each
|
||||
(fn (entry)
|
||||
(fn ((entry :as dict))
|
||||
(execute-action (get entry "action") (get entry "payload")
|
||||
(fn (result)
|
||||
(dict-set! entry "status" "synced")
|
||||
(log-info (str "sx:offline synced " (get entry "action"))))
|
||||
(fn (error)
|
||||
(fn ((error :as string))
|
||||
(dict-set! entry "status" "failed")
|
||||
(log-warn (str "sx:offline sync failed " (get entry "action") ": " error)))))
|
||||
pending)))))
|
||||
|
||||
(define offline-pending-count
|
||||
(fn ()
|
||||
(len (filter (fn (e) (= (get e "status") "pending")) _offline-queue))))
|
||||
(len (filter (fn ((e :as dict)) (= (get e "status") "pending")) _offline-queue))))
|
||||
|
||||
(define offline-aware-mutation
|
||||
(fn (page-name params action-name payload mutator-fn on-complete)
|
||||
(fn ((page-name :as string) (params :as dict) (action-name :as string) payload (mutator-fn :as lambda) (on-complete :as lambda))
|
||||
;; Top-level mutation function. Routes to submit-mutation when online,
|
||||
;; offline-queue-mutation when offline.
|
||||
(if _is-online
|
||||
@@ -860,7 +860,7 @@
|
||||
|
||||
|
||||
(define swap-rendered-content
|
||||
(fn (target rendered pathname)
|
||||
(fn (target rendered (pathname :as string))
|
||||
;; Swap rendered DOM content into target and run post-processing.
|
||||
;; Shared by pure and data page client routes.
|
||||
(do
|
||||
@@ -876,7 +876,7 @@
|
||||
|
||||
|
||||
(define resolve-route-target
|
||||
(fn (target-sel)
|
||||
(fn ((target-sel :as string))
|
||||
;; Resolve a target selector to a DOM element, or nil.
|
||||
(if (and target-sel (not (= target-sel "true")))
|
||||
(dom-query target-sel)
|
||||
@@ -884,17 +884,17 @@
|
||||
|
||||
|
||||
(define deps-satisfied?
|
||||
(fn (match)
|
||||
(fn ((match :as dict))
|
||||
;; Check if all component deps for a page are loaded client-side.
|
||||
(let ((deps (get match "deps"))
|
||||
(loaded (loaded-component-names)))
|
||||
(if (or (nil? deps) (empty? deps))
|
||||
true
|
||||
(every? (fn (dep) (contains? loaded dep)) deps)))))
|
||||
(every? (fn ((dep :as string)) (contains? loaded dep)) deps)))))
|
||||
|
||||
|
||||
(define try-client-route
|
||||
(fn (pathname target-sel)
|
||||
(fn ((pathname :as string) (target-sel :as string))
|
||||
;; Try to render a page client-side. Returns true if successful, false otherwise.
|
||||
;; target-sel is the CSS selector for the swap target (from sx-boost value).
|
||||
;; For pure pages: renders immediately. For :data pages: fetches data then renders.
|
||||
@@ -968,7 +968,7 @@
|
||||
(do
|
||||
(log-info (str "sx:route client+data " pathname))
|
||||
(resolve-page-data page-name params
|
||||
(fn (data)
|
||||
(fn ((data :as dict))
|
||||
(page-data-cache-set cache-key data)
|
||||
(let ((env (merge closure params data)))
|
||||
(if has-io
|
||||
@@ -1012,7 +1012,7 @@
|
||||
|
||||
|
||||
(define bind-client-route-link
|
||||
(fn (link href)
|
||||
(fn (link (href :as string))
|
||||
;; Bind a boost link with client-side routing. If the route can be
|
||||
;; rendered client-side (pure page, no :data), do so. Otherwise
|
||||
;; fall back to standard server fetch via bind-boost-link.
|
||||
@@ -1045,12 +1045,12 @@
|
||||
(let ((source (event-source-connect url el))
|
||||
(event-name (parse-sse-swap el)))
|
||||
(event-source-listen source event-name
|
||||
(fn (data)
|
||||
(fn ((data :as string))
|
||||
(bind-sse-swap el data))))))))
|
||||
|
||||
|
||||
(define bind-sse-swap
|
||||
(fn (el data)
|
||||
(fn (el (data :as string))
|
||||
;; Handle an SSE event: swap data into element
|
||||
(let ((target (resolve-target el))
|
||||
(swap-spec (parse-swap-spec
|
||||
@@ -1089,7 +1089,7 @@
|
||||
(for-each
|
||||
(fn (el)
|
||||
(for-each
|
||||
(fn (attr)
|
||||
(fn ((attr :as list))
|
||||
(let ((name (first attr))
|
||||
(body (nth attr 1)))
|
||||
(when (starts-with? name "sx-on:")
|
||||
@@ -1135,7 +1135,7 @@
|
||||
|
||||
|
||||
(define do-preload
|
||||
(fn (url headers)
|
||||
(fn ((url :as string) (headers :as dict))
|
||||
;; Execute a preload fetch into the cache
|
||||
(when (nil? (preload-cache-get _preload-cache url))
|
||||
(fetch-preload url headers _preload-cache))))
|
||||
@@ -1215,7 +1215,7 @@
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define handle-popstate
|
||||
(fn (scrollY)
|
||||
(fn ((scrollY :as number))
|
||||
;; Handle browser back/forward navigation.
|
||||
;; Derive target from [sx-boost] container or fall back to #main-panel.
|
||||
;; Try client-side route first, fall back to server fetch.
|
||||
|
||||
Reference in New Issue
Block a user