Documentation
¶
Overview ¶
Package sse installs a WHATWG-compatible Server-Sent Events client (the EventSource web API) into a gojs VM.
Overview ¶
SSE is a one-way "server pushes text to client" protocol layered on a long lived HTTP response with content type text/event-stream. This package is an OPTIONAL host add-on: the VM has no dependency on it. Call Install to expose a browser-shaped EventSource constructor to scripts:
vm := gojs.New(gojs.WithPrintProvider(gojs.NewDefaultPrintProvider()))
if err := sse.Install(vm); err != nil { ... }
vm.RunString("app.js", `
const es = new EventSource("http://localhost:8080/events");
es.onopen = () => console.log("open");
es.onmessage = (e) => console.log("message", e.data);
es.addEventListener("tick", (e) => console.log("tick", e.data));
es.onerror = () => console.log("error/reconnecting");
`)
Architecture ¶
The public EventSource surface is defined as a JavaScript prelude (see prelude.go) that Install loads with vm.RunString. The prelude delegates the actual networking to two hidden natives under globalThis.__gojs_sse:
__gojs_sse.connect(url, opts, onEvent) -> handle __gojs_sse.close(handle)
connect opens the HTTP stream on its own goroutine and reports every state change back to the prelude by calling onEvent(kind, payload) via vm.Enqueue, where kind is "open", "event", or "error". The prelude turns those into DOM-style events (open/message/named/error) and dispatches them to the matching handler property (onopen/onmessage/onerror) and any listeners registered with addEventListener.
Threading ¶
The VM is single-threaded. The blocking HTTP read runs on a background goroutine that never touches any JavaScript value directly; it only marshals results onto the VM goroutine with vm.Enqueue. While a stream is open the package holds the event loop alive with vm.Pin so RunString/RunLoop do not return prematurely; close() and giving up on reconnection release the hold so the loop can end.
Reconnection ¶
On a network error or a stream that ends, the client waits the reconnection time (default 3s, overridable per-stream by a retry: field), fires an error event, and reconnects — sending Last-Event-ID when an id: has been seen. A non-200 status or a wrong content type is a fatal error: it fires a non-reconnecting error and stops. close() stops permanently. To bound automatic reconnection (and guarantee the process can exit even if a script never calls close), set a cap with WithMaxReconnects.
Index ¶
Constants ¶
const DefaultRetry = 3 * time.Second
DefaultRetry is the initial reconnection delay used when no WithRetry option is given and the server has not sent a retry: field. It matches the value browsers commonly default to.
Variables ¶
This section is empty.
Functions ¶
func Install ¶
Install exposes the EventSource constructor to scripts running in vm. It is opt-in and safe to call once per VM. Options tune the reconnection behavior and HTTP client; a plain Install(vm) uses sensible defaults (see DefaultRetry, unlimited reconnects, a timeout-free http.Client).
Types ¶
type Option ¶
type Option func(*config)
Option configures Install.
func WithHTTPClient ¶
WithHTTPClient supplies a custom *http.Client (for a custom transport, TLS config, proxy, etc.). The client must not impose a per-request Timeout, since that would abort long-lived streams; cancellation is handled via request contexts instead. If unset, a fresh client with no timeout is used.
func WithMaxReconnects ¶
WithMaxReconnects caps the number of consecutive failed reconnection attempts before the client gives up, fires a final (non-reconnecting) error, and releases the event loop. The counter resets whenever a connection succeeds. n <= 0 means unlimited reconnection (browser behavior); with unlimited reconnection a stream stays alive until the script calls close().