Files
rose-ash/web/lib/browser.sx
giles 4308591982 Add host FFI primitives and web/lib DOM+browser libraries
Introduce 8 irreducible host FFI primitives that replace 40+ native DOM
and browser primitives:

  host-global    — access global object (window/document)
  host-get       — read property from host object
  host-set!      — write property on host object
  host-call      — call method on host object
  host-new       — construct host object
  host-callback  — wrap SX function as host callback
  host-typeof    — check host object type
  host-await     — await host promise

All DOM and browser operations are now expressible as SX library
functions built on these 8 primitives:

  web/lib/dom.sx     — createElement, querySelector, appendChild,
                        setAttribute, addEventListener, classList, etc.
  web/lib/browser.sx — localStorage, history, fetch, setTimeout,
                        promises, console, matchMedia, etc.

The existing native implementations remain as fallback — the library
versions shadow them in transpiled code. Incremental migration: callers
don't change, only the implementation moves from out-of-band to in-band.

JS 957+1080, Python 744, OCaml 952 — zero regressions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 09:22:57 +00:00

172 lines
4.9 KiB
Plaintext

;; ==========================================================================
;; browser.sx — Browser API library functions
;;
;; Location, history, storage, cookies, timers, fetch — all expressed
;; using the host FFI primitives. Library functions, not primitives.
;; ==========================================================================
;; --------------------------------------------------------------------------
;; Location & navigation
;; --------------------------------------------------------------------------
(define browser-location-href
(fn ()
(host-get (host-get (dom-window) "location") "href")))
(define browser-location-pathname
(fn ()
(host-get (host-get (dom-window) "location") "pathname")))
(define browser-location-origin
(fn ()
(host-get (host-get (dom-window) "location") "origin")))
(define browser-same-origin?
(fn (url)
(starts-with? url (browser-location-origin))))
(define browser-push-state
(fn (state title url)
(host-call (host-get (dom-window) "history") "pushState" state title url)))
(define browser-replace-state
(fn (state title url)
(host-call (host-get (dom-window) "history") "replaceState" state title url)))
(define browser-reload
(fn ()
(host-call (host-get (dom-window) "location") "reload")))
(define browser-navigate
(fn (url)
(host-set! (host-get (dom-window) "location") "href" url)))
;; --------------------------------------------------------------------------
;; Storage
;; --------------------------------------------------------------------------
(define local-storage-get
(fn (key)
(host-call (host-get (dom-window) "localStorage") "getItem" key)))
(define local-storage-set
(fn (key val)
(host-call (host-get (dom-window) "localStorage") "setItem" key val)))
(define local-storage-remove
(fn (key)
(host-call (host-get (dom-window) "localStorage") "removeItem" key)))
;; --------------------------------------------------------------------------
;; Timers
;; --------------------------------------------------------------------------
(define set-timeout
(fn (fn-val ms)
(host-call (dom-window) "setTimeout" (host-callback fn-val) ms)))
(define set-interval
(fn (fn-val ms)
(host-call (dom-window) "setInterval" (host-callback fn-val) ms)))
(define clear-timeout
(fn (id)
(host-call (dom-window) "clearTimeout" id)))
(define clear-interval
(fn (id)
(host-call (dom-window) "clearInterval" id)))
(define request-animation-frame
(fn (fn-val)
(host-call (dom-window) "requestAnimationFrame" (host-callback fn-val))))
;; --------------------------------------------------------------------------
;; Fetch
;; --------------------------------------------------------------------------
(define fetch-request
(fn (url opts)
(host-call (dom-window) "fetch" url opts)))
(define new-abort-controller
(fn ()
(host-new "AbortController")))
(define controller-signal
(fn (controller)
(host-get controller "signal")))
(define controller-abort
(fn (controller)
(host-call controller "abort")))
;; --------------------------------------------------------------------------
;; Promises
;; --------------------------------------------------------------------------
(define promise-then
(fn (p on-resolve on-reject)
(let ((cb-resolve (host-callback on-resolve))
(cb-reject (if on-reject (host-callback on-reject) nil)))
(if cb-reject
(host-call (host-call p "then" cb-resolve) "catch" cb-reject)
(host-call p "then" cb-resolve)))))
(define promise-resolve
(fn (val)
(host-call (host-global "Promise") "resolve" val)))
(define promise-delayed
(fn (ms val)
(host-new "Promise" (host-callback
(fn (resolve)
(set-timeout (fn () (host-call resolve "call" nil val)) ms))))))
;; --------------------------------------------------------------------------
;; Dialogs & media
;; --------------------------------------------------------------------------
(define browser-confirm
(fn (msg) (host-call (dom-window) "confirm" msg)))
(define browser-prompt
(fn (msg default)
(host-call (dom-window) "prompt" msg default)))
(define browser-media-matches?
(fn (query)
(host-get (host-call (dom-window) "matchMedia" query) "matches")))
;; --------------------------------------------------------------------------
;; JSON
;; --------------------------------------------------------------------------
(define json-parse
(fn (s)
(host-call (host-global "JSON") "parse" s)))
;; --------------------------------------------------------------------------
;; Console
;; --------------------------------------------------------------------------
(define log-info
(fn (msg)
(host-call (host-global "console") "log" (str "[sx] " msg))))
(define log-warn
(fn (msg)
(host-call (host-global "console") "warn" (str "[sx] " msg))))
(define now-ms
(fn ()
(host-call (host-global "Date") "now")))