Script helpers
Dstar can append safe script actions over SSE for one-off browser effects, console diagnostics, and redirects.
Demo
This runs in your browser only. Signals are per-connection, so nothing you type here can reach anyone else's tab.
JSON. It reaches the helper as a map, which
Jason.encode!/1
renders into the console call.
A full page load, so it clears everything below. That is the point.
Helper log
Last action: idle
No helper has run yet.
Console mirror
Open your DevTools console (⌘⌥J) — those are the real entries. This panel is a copy, and it mirrors everything the page logs, framework noise included.
Script residue
auto_remove: false
leaves the tag in the DOM. These are the ones still there.
How it works
- execute_script/3 appends a script tag through a patch.
- console_log/3 emits structured browser console output.
- redirect/3 performs a server-commanded navigation.
- The helper scripts stay inert after they execute.
These three helpers append a one-shot <script>
through a patch: execute_script/3
runs browser code, console_log/3
emits a structured console entry, and redirect/3
commands a navigation. auto_remove: false
leaves the tag in the DOM after it has run, so you can go and look at it. The other demos
avoid these helpers so the server-rendered patch flow stays easy to inspect.
conn
|> patch_signals(%{script_runs: runs, last_script_action: "execute_script"})
|> prepend_log(runs, "execute_script/3", source)
|> execute_script(source,
auto_remove: false,
attributes: %{"data-demo-script" => "run"}
)
If this were LiveView
Both sides run browser code from the server and both perform a server-commanded navigation; they disagree about what crosses the wire.
Dstar
conn
|> patch_signals(%{script_runs: runs, last_script_action: "execute_script"})
|> prepend_log(runs, "execute_script/3", source)
|> execute_script(source,
auto_remove: false,
attributes: %{"data-demo-script" => "run"}
)
LiveView
# Illustrative LiveView equivalent — not compiled or run.
def handle_event("execute", _params, socket) do
runs = socket.assigns.script_runs + 1
{:noreply,
socket
|> assign(script_runs: runs, last_script_action: "push_event")
|> push_event("script-pulse", %{run: runs})}
end
def handle_event("console", _params, socket) do
runs = socket.assigns.script_runs + 1
{:noreply,
socket
|> assign(script_runs: runs, last_script_action: "push_event")
|> push_event("script-log", %{level: "info", run: runs})}
end
def handle_event("redirect", _params, socket) do
{:noreply, push_navigate(socket, to: ~p"/scripts?redirected=1")}
end
# The browser half is a hook, declared once in your bundle rather than
# sent down per event:
#
# Hooks.ScriptPulse = {
# mounted() {
# this.handleEvent("script-pulse", ({run}) => {
# document.documentElement.dataset.dstarScriptPulse = String(run)
# })
#
# this.handleEvent("script-log", ({level, run}) => {
# console[level]("dstar demo", {run})
# })
# }
# }