ΛↃ LAMBDACOMBINE

Symbolic Systems Infrastructure

03 · Scripts

EXECUTE-SCRIPT · CONSOLE-LOG · REDIRECT

Beyond patching elements and signals, an SSE stream can trigger client-side JavaScript. Three conveniences cover the common cases.

console-log

console-log sends a message to the browser developer console. Useful for debugging without touching the DOM.

(d*:with-sse (gen hunchentoot:*request*)
  (d*:console-log gen "message from server"))

execute-script

execute-script runs arbitrary JavaScript on the client. With :auto-remove t the injected script element is removed after execution.

(d*:with-sse (gen hunchentoot:*request*)
  (d*:execute-script gen
                     "alert('Hello from datastar-cl!')"
                     :auto-remove t))

redirect

redirect navigates the browser to a new URL. The navigation happens on the client; the server just sends the instruction.

(d*:with-sse (gen hunchentoot:*request*)
  (d*:redirect gen "/"))

All three are one-shot: with-sse exits after the body runs and the connection closes. Triggered by a button click, this fires once per click with no reconnect loop.

Live Demo

Click Log to write to your browser console (open DevTools to see it). Click Alert to trigger a browser alert box.

Full Source (standalone Hunchentoot)

This example also shows a persistent clock alongside the one-shot buttons.

;;;; -*- Mode: LISP; fill-column: 80; coding: utf-8 -*-

;;;; DEMO-SCRIPT.LISP --- execute-script / console-log / redirect

;;;; Copyright (C) 2025, 2026 Frederico Muñoz / ΛↃ lambda combine
;;;;
;;;; This file is part of datastar-cl, the Common Lisp SDK for Datastar
;;;;
;;;; License: MIT

;;; The only core SSE method not shown in the other demos. Adds buttons
;;; that trigger one-shot SSE handlers, each calling a different script
;;; variant: console-log, execute-script with auto-remove, and redirect.

(ql:quickload '(:hunchentoot :spinneret :datastar-cl/hunchentoot))

(defpackage #:clock
  (:use #:cl #:hunchentoot)
  (:local-nicknames (:sp :spinneret) (:d* :datastar-cl)))
(in-package #:clock)

(hunchentoot:define-easy-handler (index :uri "/") ()
  (setf (hunchentoot:content-type*) "text/html")
  (sp:with-html-string
    (:doctype)
    (:html
     (:head (:script :type "module" :src (d*:datastar-url)))
     (:body :data-init (d*:sse-get "/sse")
            (:h1 "Datastar-CL Script Demo")
            (:div :id "clock" "Waiting...")
            (:button :|data-on:click| (d*:sse-get "/log") "Log")
            (:button :|data-on:click| (d*:sse-get "/alert") "Alert")
            (:button :|data-on:click| (d*:sse-get "/go") "Redirect")))))

(hunchentoot:define-easy-handler (sse-handler :uri "/sse") ()
  (d*:with-sse (gen hunchentoot:*request*)
    (loop
      (multiple-value-bind (s m h) (decode-universal-time (get-universal-time))
        (d*:patch-elements gen
                           (format nil "<span>~2,'0d:~2,'0d:~2,'0d</span>" h m s)
                           :selector "#clock"
                           :mode :inner))
      (sleep 1))))

(hunchentoot:define-easy-handler (log-handler :uri "/log") ()
  (d*:with-sse (gen hunchentoot:*request*)
    (d*:console-log gen "Log button clicked at server")))

(hunchentoot:define-easy-handler (alert-handler :uri "/alert") ()
  (d*:with-sse (gen hunchentoot:*request*)
    (d*:execute-script gen "alert('Hello from datastar-cl!')" :auto-remove t)))

(hunchentoot:define-easy-handler (go-handler :uri "/go") ()
  (d*:with-sse (gen hunchentoot:*request*)
    (d*:redirect gen "/")))

(hunchentoot:start (make-instance 'hunchentoot:easy-acceptor :port 8989))
(format t "~&Server started on http://localhost:8989~%")

(defroute guide-scripts (:get :text/html))