Files
rose-ash/shared/static/wasm/sx/browser.sx
giles 8d3ab040ef Fix WASM browser: broken links (&rest bytecode) + broken reactive counter (ListRef mutation)
Two bugs fixed:

1. Links: bytecode compiler doesn't handle &rest params — treats them as
   positional, so (first rest) gets a raw string instead of a list.
   Replaced &rest with explicit optional params in all bytecode-compiled
   web SX files (dom-query, dom-add-listener, browser-push-state, etc.).
   The VM already pads missing args with Nil.

2. Reactive counter: signal-remove-sub! used (filter ...) which returns
   immutable List, but signal-add-sub! uses (append!) which only mutates
   ListRef. Subscribers silently vanished after first effect re-run.
   Fixed by adding remove! primitive that mutates ListRef in-place.

Also:
- Added evalVM API to WASM kernel (compile + run through bytecode VM)
- Added scope tracing (scope-push!/pop!/peek/context instrumentation)
- Added Playwright reactive mode for debugging island signal/DOM state
- Replaced cek-call with direct calls in core-signals.sx effect/computed
- Recompiled all 23 bytecode modules

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:08:49 +00:00

222 lines
4.9 KiB
Plaintext

(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
url-pathname
(fn
(url)
(host-get (host-new "URL" url (browser-location-origin)) "pathname")))
(define
browser-push-state
(fn
(url-or-state title url)
(if
(nil? title)
(host-call
(host-get (dom-window) "history")
"pushState"
nil
""
url-or-state)
(host-call
(host-get (dom-window) "history")
"pushState"
url-or-state
title
url))))
(define
browser-replace-state
(fn
(url-or-state title url)
(if
(nil? title)
(host-call
(host-get (dom-window) "history")
"replaceState"
nil
""
url-or-state)
(host-call
(host-get (dom-window) "history")
"replaceState"
url-or-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)))
(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)))
(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))))
(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")))
(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))))))
(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")))
(define json-parse (fn (s) (host-call (host-global "JSON") "parse" s)))
(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
console-log
(fn (msg) (host-call (host-global "console") "log" (str "[sx] " msg))))
(define now-ms (fn () (host-call (host-global "Date") "now")))
(define
schedule-idle
(fn
(f)
(let
((cb (host-callback (fn (_deadline) (f)))))
(if
(host-get (dom-window) "requestIdleCallback")
(host-call (dom-window) "requestIdleCallback" cb)
(set-timeout cb 0)))))
(define
set-cookie
(fn
(name value days)
(let
((d (or days 365))
(expires
(host-call
(host-new
"Date"
(+ (host-call (host-global "Date") "now") (* d 86400000)))
"toUTCString")))
(host-set!
(dom-document)
"cookie"
(str
name
"="
(host-call nil "encodeURIComponent" value)
";expires="
expires
";path=/;SameSite=Lax")))))
(define
get-cookie
(fn
(name)
(let
((cookies (host-get (dom-document) "cookie"))
(match
(host-call
cookies
"match"
(host-new "RegExp" (str "(?:^|;\\s*)" name "=([^;]*)")))))
(if match (host-call nil "decodeURIComponent" (host-get match 1)) nil))))