WASM browser build: interned env keys, async kernel boot, bundled .sx platform
- Symbol interning in sx_types.ml: env lookups use int keys (intern/unintern) to avoid repeated string hashing in scope chain walks - sx-platform.js: poll for SxKernel availability (WASM init is async) - shell.sx: load sx_browser.bc.wasm.js when SX_USE_WASM=1 - bundle.sh: fix .sx file paths (web-signals.sx rename) - browser/dune: target byte+js+wasm modes - Bundle 23 .sx platform files for browser (dom, signals, router, boot, etc.) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
97
shared/static/wasm/sx/signals.sx
Normal file
97
shared/static/wasm/sx/signals.sx
Normal file
@@ -0,0 +1,97 @@
|
||||
;; ==========================================================================
|
||||
;; web/signals.sx — Web platform signal extensions
|
||||
;;
|
||||
;; Extends the core reactive signal spec (spec/signals.sx) with web-specific
|
||||
;; features: marsh scopes (DOM lifecycle), named stores (page-level state),
|
||||
;; event bridge (lake→island communication), and async resources.
|
||||
;;
|
||||
;; These depend on platform primitives:
|
||||
;; dom-set-data, dom-get-data, dom-listen, dom-dispatch, event-detail,
|
||||
;; promise-then
|
||||
;; ==========================================================================
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Marsh scopes — child scopes within islands
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define with-marsh-scope :effects [mutation io]
|
||||
(fn (marsh-el (body-fn :as lambda))
|
||||
(let ((disposers (list)))
|
||||
(with-island-scope
|
||||
(fn (d) (append! disposers d))
|
||||
body-fn)
|
||||
(dom-set-data marsh-el "sx-marsh-disposers" disposers))))
|
||||
|
||||
(define dispose-marsh-scope :effects [mutation io]
|
||||
(fn (marsh-el)
|
||||
(let ((disposers (dom-get-data marsh-el "sx-marsh-disposers")))
|
||||
(when disposers
|
||||
(for-each (fn ((d :as lambda)) (cek-call d nil)) disposers)
|
||||
(dom-set-data marsh-el "sx-marsh-disposers" nil)))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Named stores — page-level signal containers
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define *store-registry* (dict))
|
||||
|
||||
(define def-store :effects [mutation]
|
||||
(fn ((name :as string) (init-fn :as lambda))
|
||||
(let ((registry *store-registry*))
|
||||
(when (not (has-key? registry name))
|
||||
(set! *store-registry* (assoc registry name (cek-call init-fn nil))))
|
||||
(get *store-registry* name))))
|
||||
|
||||
(define use-store :effects []
|
||||
(fn ((name :as string))
|
||||
(if (has-key? *store-registry* name)
|
||||
(get *store-registry* name)
|
||||
(error (str "Store not found: " name
|
||||
". Call (def-store ...) before (use-store ...).")))))
|
||||
|
||||
(define clear-stores :effects [mutation]
|
||||
(fn ()
|
||||
(set! *store-registry* (dict))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Event bridge — DOM event communication for lake→island
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define emit-event :effects [io]
|
||||
(fn (el (event-name :as string) detail)
|
||||
(dom-dispatch el event-name detail)))
|
||||
|
||||
(define on-event :effects [io]
|
||||
(fn (el (event-name :as string) (handler :as lambda))
|
||||
(dom-on el event-name handler)))
|
||||
|
||||
(define bridge-event :effects [mutation io]
|
||||
(fn (el (event-name :as string) (target-signal :as signal) transform-fn)
|
||||
(effect (fn ()
|
||||
(let ((remove (dom-on el event-name
|
||||
(fn (e)
|
||||
(let ((detail (event-detail e))
|
||||
(new-val (if transform-fn
|
||||
(cek-call transform-fn (list detail))
|
||||
detail)))
|
||||
(reset! target-signal new-val))))))
|
||||
remove)))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Resource — async signal with loading/resolved/error states
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define resource :effects [mutation io]
|
||||
(fn ((fetch-fn :as lambda))
|
||||
(let ((state (signal (dict "loading" true "data" nil "error" nil))))
|
||||
(promise-then
|
||||
(cek-call fetch-fn nil)
|
||||
(fn (data)
|
||||
(reset! state (dict "loading" false "data" data "error" nil)))
|
||||
(fn (err)
|
||||
(reset! state (dict "loading" false "data" nil "error" err))))
|
||||
state)))
|
||||
Reference in New Issue
Block a user