jaws

package module
v0.804.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 24, 2026 License: MIT Imports: 38 Imported by: 8

README

build coverage OpenSSF Scorecard Docs

JaWS

JavaScript and WebSockets for creating responsive webpages.

JaWS embraces a "server holds the truth" philosophy and keeps the complexity of modern browser applications on the backend. The client-side script becomes a thin transport layer that faithfully relays events and DOM updates.

Features

  • Moves web application state fully to the server.
  • Keeps the browser intentionally dumb -- no implicit trust in JavaScript logic running on the client.
  • Binds application data to UI elements using user-defined tags and type-aware binders.
  • Integrates with the standard library as well as third-party routers such as Echo.
  • Ships with a small standard library of extensible UI widgets and helpers.

The demo application is a commented, complete example.

Installation

JaWS is distributed as a standard Go module:

go get github.com/linkdata/jaws

For the standard widget APIs, see the lib/ui package documentation.

AI skill

This repository includes an AI skill under .agents/skills/jaws/. To install it in your local AI skills tree, copy both SKILL.md and agents/openai.yaml into ~/.agents/skills/jaws/.

Copying from a JaWS checkout keeps the skill baseline matched to that source. The commands below install the current development skill from main; when versioned source is available, its adjacent AI.md guides are canonical for version-specific behavior.

Using curl:

mkdir -p "$HOME/.agents/skills/jaws/agents"
curl -fsSL https://raw.githubusercontent.com/linkdata/jaws/main/.agents/skills/jaws/SKILL.md \
	-o "$HOME/.agents/skills/jaws/SKILL.md"
curl -fsSL https://raw.githubusercontent.com/linkdata/jaws/main/.agents/skills/jaws/agents/openai.yaml \
	-o "$HOME/.agents/skills/jaws/agents/openai.yaml"

Quick start

The following minimal program renders a single range input whose value stays on the server. Copy the snippet into a new module, run go mod tidy, and start it with go run .. Visiting http://localhost:8080/ demonstrates the full request lifecycle.

package main

import (
	"html/template"
	"log/slog"
	"net/http"
	"sync"

	"github.com/linkdata/jaws"
	"github.com/linkdata/jaws/lib/bind"
	"github.com/linkdata/jaws/lib/ui"
)

const indexhtml = `
<html>
  <head>{{$.HeadHTML}}</head>
  <body>{{with .Dot}}
    {{$.Range .}}
  {{end}}{{$.TailHTML}}</body>
</html>
`

type Percent uint8

func main() {
	jw, err := jaws.New() // create a default JaWS instance
	if err != nil {
		panic(err)
	}
	defer jw.Close()           // ensure we clean up
	jw.Logger = slog.Default() // optionally set the logger to use

	// parse our template and inform JaWS about it
	templates := template.Must(template.New("index").Parse(indexhtml))
	_ = jw.AddTemplateLookuper(templates)

	go jw.Serve()                                 // start the JaWS processing loop
	http.DefaultServeMux.Handle("GET /jaws/", jw) // ensure the JaWS routes are handled

	var mu sync.Mutex
	percent := Percent(50)

	http.DefaultServeMux.Handle("GET /", ui.Handler(jw, "index", bind.New(&mu, &percent)))
	slog.Error(http.ListenAndServe("localhost:8080", nil).Error())
}

Next steps usually include adding templates with AddTemplateLookuper, creating types that implement JawsRender and JawsUpdate, and introducing sessions for per-user state.

Production guidance

Before deploying a JaWS application, review the production hardening guidance.

AI and maintainer guidance

The version-matched AI guidance documents implementation invariants, lifecycle details, and links to the guide for every package. Exported API contracts remain in the Go package documentation.

Dependencies

JaWS keeps dependencies outside the standard library to a minimum:

Learn more

Documentation

Overview

Package jaws creates dynamic server-driven webpages over WebSockets.

It provides the core engine, requests, sessions, and UI interfaces and integrates with html/template and routers that support http.Handler. Standard widgets live in github.com/linkdata/jaws/lib/ui, value binding in github.com/linkdata/jaws/lib/bind, and dirty-target selection in github.com/linkdata/jaws/lib/tag.

Applications keep authoritative state on the server. Tags associate Element values with application data or logical signals for targeted dirtying, broadcasts, and lookup; see github.com/linkdata/jaws/lib/tag.

Nil values

Throughout this module, nil is unsupported for pointer receivers and values used as required operational collaborators, such as callbacks, handlers, providers, lockers, writers, file systems, contexts, and pointers to mutable values, unless an API documents a meaning for nil. Unsupported nil use is caller error and may panic. Nil slices, maps, data values, and results otherwise follow ordinary Go semantics and the relevant API. An interface containing a typed nil is non-nil; its behavior follows the receiving API and concrete type.

Index

Constants

View Source
const (
	// DefaultUpdateInterval is the default browser update interval.
	DefaultUpdateInterval = time.Millisecond * 100

	// DefaultWebSocketPingInterval is the default WebSocket read-idle interval.
	DefaultWebSocketPingInterval = time.Minute

	// DefaultWebSocketTimeout is the timeout [Jaws.Serve] passes to [Jaws.ServeWithTimeout].
	DefaultWebSocketTimeout = time.Second * 10

	// DefaultMaxPendingRequestsPerIP is the default maximum number of unclaimed
	// Requests allowed for each client IP.
	DefaultMaxPendingRequestsPerIP = 100
)
View Source
const (
	// StatusMetricActiveRequests enables dirtying [Jaws.ActiveRequestCountTag].
	StatusMetricActiveRequests uint32 = 1 << iota
	// StatusMetricPendingRequests enables dirtying [Jaws.PendingRequestCountTag].
	StatusMetricPendingRequests
	// StatusMetricSessions enables dirtying [Jaws.SessionCountTag].
	StatusMetricSessions
	// StatusMetricActiveSessions enables dirtying [Jaws.ActiveSessionCountTag].
	StatusMetricActiveSessions
	// StatusMetricErrors enables dirtying [Jaws.ErrorCountTag].
	StatusMetricErrors
)

StatusMetricAll selects every status metric.

Variables

View Source
var ErrElementStateClaimed = errors.New("jaws: element state already claimed")

ErrElementStateClaimed is returned by SetElementState when the Element already has widget state, including state of the same type.

View Source
var ErrElementStateNil = errors.New("jaws: element state must not be nil")

ErrElementStateNil is returned by SetElementState when the state to store is a nil interface, which cannot be distinguished from an unclaimed slot.

View Source
var ErrEventHandlerPanic errEventHandlerPanic

ErrEventHandlerPanic is returned by CallEventHandlers when a user event handler panics.

Match it with errors.Is. When the recovered panic value is itself an error it is available via Unwrap (and thus errors.As / errors.Is); a non-error panic value appears only in the formatted message.

View Source
var ErrEventUnhandled = errEventUnhandled{}

ErrEventUnhandled returned by InputHandler.JawsInput, ClickHandler.JawsClick or ContextMenuHandler.JawsContextMenu causes the next available handler to be invoked.

View Source
var ErrInvalidChildElement = errors.New("invalid child element")

ErrInvalidChildElement indicates an invalid child Element.

Child operations report this error when the child is nil, deleted, unregistered, the receiver itself, or belongs to another Request.

View Source
var ErrInvalidChildIndex = errors.New("invalid child index")

ErrInvalidChildIndex indicates an invalid child index.

Jaws.Insert reports this error for a negative index. Use Jaws.Append to insert at the end.

View Source
var ErrJavascriptDisabled = errors.New("javascript is disabled")

ErrJavascriptDisabled is returned when the noscript probe indicates JavaScript is disabled.

View Source
var ErrNoWebSocketRequest errNoWebSocketRequest

ErrNoWebSocketRequest is reported when Jaws.ServeWithTimeout retires a Request before Request.ServeHTTP begins WebSocket processing.

View Source
var ErrRemoveNotBroadcastable = errors.New("what.Remove cannot be broadcast")

ErrRemoveNotBroadcastable indicates an attempt to broadcast a what.Remove command.

Remove deletes the child node named by Data from the matched element and requires the child's server-side Element to be unregistered too (see Element.Remove, which calls Request.DeleteElement; the client acknowledges only the removal of the child's descendants, never the child itself). A broadcast forwards the command verbatim without that registry cleanup, and its Data names one request's child, so matched child Elements would be stranded with no reachable DOM node. Jaws.Broadcast reports this via reportMisuse and sends nothing; use Jaws.Delete to remove matched elements, or Element.Remove for the per-element child form.

View Source
var ErrReplaceNotBroadcastable = errors.New("what.Replace cannot be broadcast")

ErrReplaceNotBroadcastable indicates an attempt to broadcast a what.Replace command.

Replace swaps the whole target node for new HTML, which must carry the Element's own "id" so the server-side Element keeps a reachable DOM node (see Element.Replace, which validates exactly that). A broadcast delivers one payload to every element matching wire.Message.Dest, so no single payload can preserve each element's distinct id and the matched Elements would be stranded with no matching DOM node. Jaws.Broadcast reports this via reportMisuse and sends nothing; use Element.Replace for the per-element form.

View Source
var ErrRequestAlreadyClaimed = errors.New("request already claimed")

ErrRequestAlreadyClaimed is returned when Jaws.UseRequest is called more than once for a Request.

View Source
var ErrRequestCancelled errRequestCancelled

ErrRequestCancelled identifies a non-nil cause supplied when JaWS cancels a Request.

The error returned by context.Cause on Request.Context matches this sentinel through errors.Is and unwraps to the supplied cause. The sentinel itself carries no cause.

Cancellation originating from Jaws.BaseContext or a context installed by Request.SetContext retains that context's cause; JaWS does not wrap it with this sentinel. A JaWS cancellation without a supplied cause is context.Canceled.

View Source
var ErrRequestOverloaded = errors.New("request overloaded")

ErrRequestOverloaded indicates a Request was torn down because it could not keep up with the messages addressed to it.

A Request is overloaded when its buffered broadcast channel or its internal event-call channel fills before it can drain them. Rather than silently dropping messages, which could leave the browser and backend in inconsistent and nonreproducible states, the Request is cancelled. The one exception is the internal periodic dirty-render tick (a nil-destination Update broadcast): the dirty work has already been moved into the Request's pending dirt, so the tick is only a nudge and can be dropped when the channel is full. The dirt is still rendered — a running Request is woken by the already-buffered message and drains it on the next pass, and one still starting up drains it on its first processing pass — so no work is lost. The cancellation cause reachable via context.Cause on Request.Context wraps this sentinel, so it can be matched with errors.Is; the wrapped text identifies which channel overflowed.

View Source
var ErrReservedAttribute = errors.New("reserved attribute")

ErrReservedAttribute indicates an attempt to set or remove a framework-owned attribute through a public attribute helper.

The "id" attribute carries an Element's JaWS identity: every wire command resolves its target node with document.getElementById, so changing or removing it would strand the server-side Element with an unreachable DOM node. The Element.SetAttr, Element.RemoveAttr, Jaws.SetAttr and Jaws.RemoveAttr helpers report this via reportMisuse and send nothing.

View Source
var ErrServeAlreadyRunning = errors.New("serve loop already running")

ErrServeAlreadyRunning reports an overlapping Jaws.Serve or Jaws.ServeWithTimeout call.

View Source
var ErrTooManyPendingRequests errTooManyPendingRequests

ErrTooManyPendingRequests indicates an older pending Request was evicted because its client IP had reached Jaws.MaxPendingRequestsPerIP.

View Source
var ErrValueNotFinite = errors.New("float value is not finite")

ErrValueNotFinite indicates that a Request was cancelled by a non-finite UI value.

The offending float32 or float64 is NaN or infinite. The cause reachable through context.Cause on Request.Context wraps this sentinel and can be matched with errors.Is.

View Source
var ErrValueUnchanged = errors.New("value unchanged")

ErrValueUnchanged reports a successful no-op set: there was no error, but the underlying value already equaled the desired value.

Setter-style implementations (the JawsSet / JawsSetPath methods in github.com/linkdata/jaws/lib/ui and github.com/linkdata/jawstree) return it, and callers test for it with errors.Is. It lives in this package so all implementations share one error identity.

View Source
var ErrWebSocketIPMismatch errWebSocketIPMismatch

ErrWebSocketIPMismatch is returned when the WebSocket callback for a Request arrives from a different client IP than the initial HTTP request.

View Source
var ErrWebsocketOriginMissing = errors.New("websocket request missing Origin header")

ErrWebsocketOriginMissing is returned when a WebSocket request has no Origin header.

View Source
var ErrWebsocketOriginNoInitial = errors.New("websocket Origin cannot be validated: no initial request")

ErrWebsocketOriginNoInitial is returned when origin validation cannot run because the Request has no initial HTTP request to compare against. The check fails closed rather than accepting an unverified Origin.

View Source
var ErrWebsocketOriginWrongHost = errors.New("websocket Origin host mismatch")

ErrWebsocketOriginWrongHost is returned when a WebSocket Origin host does not match the initial request host.

View Source
var ErrWebsocketOriginWrongScheme = errors.New("websocket Origin scheme mismatch")

ErrWebsocketOriginWrongScheme is returned when a WebSocket Origin scheme is unsupported or does not match the initial request's security.

Functions

func CallEventHandlers added in v0.300.0

func CallEventHandlers(ui any, elem *Element, wht what.What, value string) (err error)

CallEventHandlers calls the event handlers for the given Element.

Recovers from panics in user-provided handlers, returning them as errors. Input callback functions used directly by signature are recognized according to the dynamic-type rules documented by InputFn.

It must not run concurrently with rendering or handler registration.

func ElementState added in v0.700.0

func ElementState(elem *Element) (state any)

ElementState returns the widget state stored for elem, or nil if none was claimed.

The state belongs to the Element, not to the widget value that claimed it. ElementState therefore does not identify its caller, but that storage detail does not permit a different UI widget to update the Element; see Updater. Loading never claims: a widget that finds no state did not claim this Element. Most widgets never claim one, so a nil return says nothing about whether the Element was rendered.

Safe for concurrent use; it takes the Request lock. Only the slot itself is synchronized: whatever the stored value contains is guarded by that value's own synchronization, not by this call.

elem must be obtained from Request.NewElement. ElementState does not verify that provenance.

func NewErrUnusableUI added in v0.700.0

func NewErrUnusableUI(ui UI) error

NewErrUnusableUI returns an error if ui is nil, incomparable, or not equal to itself.

A typed nil pointer passes this check, but calling its methods may panic.

The returned error matches tag.ErrNotUsableAsTag and tag.ErrNotComparable.

func ParseParams added in v0.60.0

func ParseParams(params []any) (tags []any, handlers []any, attrs []string)

ParseParams parses the parameters passed to UI helpers when creating a new Element, returning UI tags, event handlers and HTML attributes.

ParseParams recognizes values whose dynamic type is exactly InputFn, and values implementing InputHandler, ClickHandler or ContextMenuHandler, as event handlers. It does not invoke InitialHTMLAttrHandler.JawsInitialHTMLAttr; implementing that interface does not affect parameter classification.

A nil InputFn is ignored.

A recognized event handler that is also usable as a tag is returned in both tags and handlers.

func SetElementState added in v0.700.0

func SetElementState(elem *Element, state any) error

SetElementState claims elem's widget state slot, which a widget does while rendering the Element so its updates and cleanup can find that state again.

There is one slot per Element and it cannot be replaced, only claimed: a second claim returns ErrElementStateClaimed and leaves the stored state untouched, even when the new state has the same type. At most one widget may claim a given Element, so a widget that renders an Element and delegates to another renderer on that same Element must decide which of them claims it.

A nil state returns ErrElementStateNil and stores nothing, since a nil interface is how an unclaimed slot is represented; that check comes first, so a nil state is rejected whatever the slot holds, and before elem is examined at all. A typed nil is a non-nil interface and does claim the slot.

Safe for concurrent use: concurrent claims on one Element are serialized by the Request lock and exactly one wins, the rest reporting ErrElementStateClaimed. Only the claim is synchronized; mutating the stored value afterwards is guarded by that value's own synchronization, not by this call.

When state is non-nil, elem must be obtained from Request.NewElement. SetElementState does not verify that provenance.

Types

type Auth added in v0.85.0

type Auth interface {
	// Data returns authenticated user data, or nil.
	Data() map[string]any
	// Email returns the authenticated user email, or an empty string.
	Email() string
	// IsAdmin reports whether the authenticated user has administrator access.
	IsAdmin() bool
}

Auth describes authentication data available to templates through ui.With.

type Click added in v0.400.0

type Click struct {
	// Name is the event target name. Parsing off the wire normalizes it: leading
	// and trailing whitespace is trimmed and internal whitespace runs collapse to a
	// single space. [Click.String] applies the same normalization when formatting.
	Name    string
	X       float64 // X is the browser clientX coordinate in CSS pixels.
	Y       float64 // Y is the browser clientY coordinate in CSS pixels.
	Shift   bool    // Shift reports whether the Shift key was held during the event.
	Control bool    // Control reports whether the Control key was held during the event.
	Alt     bool    // Alt reports whether the Alt key was held during the event.
}

Click identifies a browser click-like event, pointer location and modifier state.

func (Click) String added in v0.400.0

func (clk Click) String() string

String formats clk for the JaWS wire protocol.

It normalizes leading, trailing and repeated internal whitespace in Click.Name to the wire representation described by the Name field.

type ClickHandler added in v0.31.0

type ClickHandler interface {
	// JawsClick is called for non-input-origin browser clicks.
	//
	// The client sends clicks from an [Element]'s HTML element and from
	// non-form-control descendants. Clicks whose event target is an input,
	// select, textarea or option element, or inside one, are left to native
	// input handling and do not invoke JawsClick on an ancestor.
	//
	// Events that occur while the bundled client's WebSocket is not open are not
	// forwarded or replayed.
	//
	// [Click.Name] is the first name HTML attribute or 'button' textContent
	// found while walking from the event target up through its ancestors. If none
	// is found it falls back to the event target's HTML id, so it is empty only
	// when the target has no id either.
	JawsClick(elem *Element, click Click) (err error)
}

ClickHandler handles click events sent from the browser.

type ConnectFn

type ConnectFn = func(rq *Request) error

ConnectFn initializes or validates a Request after its WebSocket is accepted.

The function runs synchronously after the Request subscribes to broadcasts but before it starts processing browser messages. It may inspect or modify server-side Request, Session, and application state. After changing state that rendered Elements depend on, use Request.Dirty to schedule their updates for when message processing starts.

Broadcasts for the Request are buffered while the function runs and are processed after it returns nil. The buffer is bounded, so the function should return promptly; normal ErrRequestOverloaded handling applies if it fills.

Events before the WebSocket opens are not replayed. To prevent early interaction, initially disable native controls or make the interactive region inert, then have ConnectFn update request-local readiness and dirty the request-specific readiness tag used by the Template, or the exact Element whose custom updater removes the gate.

Returning an error aborts the Request, discards its buffered broadcasts, and closes the WebSocket connection without sending a failure message. Broadcasts already delivered to other active Requests are unaffected. The Request pointer is borrowed for the callback; the lifetime rules documented on Request apply.

type ConnectHandler added in v0.801.0

type ConnectHandler interface {
	// JawsConnect initializes or validates rq.
	JawsConnect(rq *Request) error
}

ConnectHandler initializes or validates a Request after its WebSocket is accepted.

github.com/linkdata/jaws/lib/ui.Handler discovers this optional capability only on its top-level page dot. JawsConnect has the lifecycle and permitted operations described by ConnectFn.

type Container added in v0.31.0

type Container interface {
	// JawsContains returns the current child [UI] values contained by elem.
	//
	// The returned [UI] values must be comparable and equal to themselves, since they
	// are used as map keys (see [UI] for the requirement); a child that is a nil
	// interface, not comparable at runtime, or not equal to itself (such as one holding
	// NaN) cancels the [Request] instead of being reconciled. A typed nil is usable.
	// The slice contents must not be modified after returning it. Returning a usable
	// child UI again from a later call lets the container reuse its existing live
	// [Element]. Each child must render one direct DOM node carrying its Element's JaWS
	// ID, because reconciliation removes and orders that node. The same UI may occur more
	// than once in one returned slice only when its type documents support for backing
	// multiple live Elements. A child UI must not be shared with a different [Request].
	JawsContains(elem *Element) (contents []UI)
}

Container is implemented by UI values that render a dynamic list of child UI values.

type ContextMenuHandler added in v0.400.0

type ContextMenuHandler interface {
	// JawsContextMenu is called for non-input-origin browser context menus.
	//
	// The client sends context-menu events from an [Element]'s HTML element and
	// from non-form-control descendants. Events whose target is an input, select,
	// textarea or option element, or inside one, are left to native browser
	// handling and do not invoke JawsContextMenu on an ancestor.
	//
	// Events that occur while the bundled client's WebSocket is not open are not
	// forwarded or replayed.
	JawsContextMenu(elem *Element, click Click) (err error)
}

ContextMenuHandler handles context-menu events sent from the browser.

type DefaultAuth added in v0.300.0

type DefaultAuth struct {
	// contains filtered or unexported fields
}

DefaultAuth is the permissive default Auth implementation used for templates when Jaws.MakeAuth is nil.

SECURITY: DefaultAuth.IsAdmin always returns true. Because it is substituted whenever Jaws.MakeAuth is unset, a template that gates privileged UI on {{if .Auth.IsAdmin}} will render that UI to EVERY visitor on any instance that forgot to set Jaws.MakeAuth. Data and Email are fail-safe (nil / empty); only IsAdmin is fail-open. Always set Jaws.MakeAuth in production, and treat a nil MakeAuth as "no authorization configured", not "deny".

func (*DefaultAuth) Data added in v0.300.0

func (*DefaultAuth) Data() map[string]any

Data returns no authenticated user data.

func (*DefaultAuth) Email added in v0.300.0

func (*DefaultAuth) Email() string

Email returns an empty authenticated user email.

func (*DefaultAuth) IsAdmin added in v0.300.0

func (da *DefaultAuth) IsAdmin() bool

IsAdmin returns true for every caller.

If configured with a logger, it logs one warning that Jaws.MakeAuth is unset and authorization is fail-open.

type Element added in v0.31.0

type Element struct {
	*Request // (read-only) the Request the Element belongs to
	// contains filtered or unexported fields
}

Element is an instance of a Request, a UI object and a Jid.

An Element pointer supplied to a render, update or event handler is borrowed for that call. A request-scoped widget may retain child Elements it creates between its render and update calls within the same Request lifecycle, but should access them only from those calls. Do not retain an Element in longer-lived application state or pass it to background work: once the embedded Request finishes it is unregistered, so the Element receives no further broadcasts or updates, though its fields are left intact and its methods still operate on the now finished Request. Request identities are never reused, so a retained Element can never come to represent an unrelated connection.

func (*Element) AddHandlers added in v0.300.0

func (elem *Element) AddHandlers(h ...any)

AddHandlers adds the given handlers to the Element.

It must be called while the Element is being rendered, before any event can be processed for it. Handlers added after Element.JawsRender has returned (or Element.Freeze has been called) are dropped; debug builds panic.

Input callback functions used directly by signature are recognized according to the dynamic-type rules documented by InputFn.

func (*Element) Append added in v0.31.0

func (elem *Element) Append(htmlCode template.HTML)

Append appends a new HTML element as a child to the current one.

Call this while the Element is rendering or updating, when a send pass is imminent. To change the Element in response to a browser event, mark it dirty with Request.Dirty instead: a change queued directly from an event handler is flushed only when the processing loop is next woken, which on an otherwise-idle request is not guaranteed to be prompt (see [Element.queue]).

func (*Element) ApplyGetter added in v0.75.0

func (elem *Element) ApplyGetter(getter any) (tagValue any)

ApplyGetter applies getter's tag and event-handler interfaces to elem.

If getter implements tag.TagGetter, the candidate is the value returned by tag.TagGetter.JawsGetTag; otherwise the candidate is getter itself. Eligible candidates — TagGetter values, supported tag slices and runtime-comparable values — are passed to Element.Tag for normal expansion and validation. That expansion may invoke JawsGetTag again when the candidate is itself a TagGetter or a []any containing one. Other non-comparable candidates are not automatically tagged.

If getter implements InputHandler, ClickHandler, or ContextMenuHandler, it is added as an event handler. ApplyGetter does not invoke InitialHTMLAttrHandler; call Element.ApplyInitialHTMLAttr separately.

The returned tagValue does not confirm registration. It is nil if getter or its candidate is a nil interface, or if the candidate is ineligible for expansion. A successfully expandable tagValue may be retained for later dirtying only while it expands to the same keys. Retained slices must not be mutated concurrently with expansion; candidates derived from a tag.TagGetter rely on its stable-identity contract.

If the Element is already frozen and getter is an event handler, the handler is not added: in production with a Jaws.Logger configured this is queued for logging and tag processing still occurs, while debug builds and servers without a Logger panic before that processing. For a non-event-handler getter, tag processing still occurs after freezing.

func (*Element) ApplyInitialHTMLAttr added in v0.700.0

func (elem *Element) ApplyInitialHTMLAttr(getter any) (attrs []template.HTMLAttr)

ApplyInitialHTMLAttr returns getter's initial HTML attributes.

It returns nil unless getter implements InitialHTMLAttrHandler and returns a non-empty value; otherwise that value is the sole slice element. It does not apply tags or event handlers.

Callers must not hold a lock protecting getter or its source.

func (*Element) ApplyParams added in v0.60.0

func (elem *Element) ApplyParams(params []any) (attrs []template.HTMLAttr)

ApplyParams applies UI-helper parameters to elem.

For a live Element, it registers tags and event handlers and returns any HTML attributes found by ParseParams. A deleted Element applies nothing and returns nil.

On a live, frozen Element, handler params are queued for logging and dropped in production when Jaws.Logger is configured, after which tags and attributes are processed. Debug builds and servers without a Logger panic first. Params without handlers continue normally.

func (*Element) Deleted added in v0.600.0

func (elem *Element) Deleted() bool

Deleted reports whether the Element has been removed from its Request.

Element.JawsRender, Element.JawsUpdate and the queue helpers are no-ops on a deleted Element. A request-scoped widget that retains child Elements it creates between render and update calls within one Request lifecycle can use Deleted to detect and discard children removed out-of-band before reuse.

An event accepted while the Element is live may still invoke its handler after the Element is later removed from the Request.

Deleted is not a lifetime check: it does not report whether the embedded Request still represents the owning connection or make that Request safe to use after its lifecycle.

func (*Element) Freeze added in v0.500.0

func (elem *Element) Freeze()

Freeze marks the Element's handlers as final, as Element.JawsRender does on return. After Freeze, the handler-mutating methods (AddHandlers, ApplyParams, ApplyGetter) drop handlers; debug builds panic. Use this for elements registered for updates without being rendered.

func (*Element) HasTag added in v0.31.0

func (elem *Element) HasTag(tagValue any) bool

HasTag reports whether this Element has tagValue.

It reports false for a deleted Element. This is the advanced exact-key lookup described by Request.HasTag: tagValue is not expanded or validated, and an invalid value may panic.

func (*Element) InsertBefore added in v0.601.0

func (elem *Element) InsertBefore(child *Element, htmlCode template.HTML)

InsertBefore inserts new HTML immediately before child.

child must be a live, distinct Element belonging to the same Request as elem. Violations are reported as ErrInvalidChildElement, and no browser command is queued. The browser also verifies that child is a direct DOM child of elem before applying the insertion.

Call this while elem is rendering or updating, when a send pass is imminent. To insert HTML at the same child index in every element matching a tag, use Jaws.Insert.

func (*Element) JawsRender added in v0.55.0

func (elem *Element) JawsRender(w io.Writer, params []any) (err error)

JawsRender calls Renderer.JawsRender for this Element.

Do not call this yourself unless it is from within another JawsRender implementation.

A nil UI interface renders as a no-op; this arises only from Request.NewElement given a nil interface. A typed nil (a non-nil interface holding a nil pointer) is still dispatched to its Renderer, so the call panics unless that concrete type documents nil-receiver tolerance; see UI.

func (*Element) JawsUpdate added in v0.55.0

func (elem *Element) JawsUpdate()

JawsUpdate calls Updater.JawsUpdate for this Element.

Do not call this yourself unless it is from within another JawsUpdate implementation.

A nil UI interface is a no-op; a typed nil dispatches to its Updater (see Element.JawsRender).

func (*Element) Jid added in v0.31.0

func (elem *Element) Jid() jid.Jid

Jid returns the JaWS ID for this Element, unique within its Request.

func (*Element) JsCall added in v0.75.0

func (elem *Element) JsCall(jsfunc, jsonstr string)

JsCall queues a browser JavaScript function path call for the Element.

In the receiving browser, jsfunc is resolved as a path from window and called with JSON.parse(jsonstr); the Element is not passed as this or as an argument. jsfunc must be an application-controlled dot path. The browser rejects an exact "__proto__" component; put user data in jsonstr, not jsfunc.

Call this while the Element is rendering or updating, when a send pass is imminent; a call queued directly from an event handler is only flushed when the processing loop is next woken. To call JavaScript for every element matching a tag, use Jaws.JsCall.

func (*Element) Order added in v0.31.0

func (elem *Element) Order(jidList []jid.Jid)

Order reorders the HTML elements.

Call this while the Element is rendering or updating, when a send pass is imminent. To change the Element in response to a browser event, mark it dirty with Request.Dirty instead: a change queued directly from an event handler is flushed only when the processing loop is next woken, which on an otherwise-idle request is not guaranteed to be prompt (see [Element.queue]).

func (*Element) Remove added in v0.31.0

func (elem *Element) Remove(child *Element)

Remove removes child from the browser and its Request registry.

child must be a live, distinct Element belonging to the same Request as elem. Violations are reported as ErrInvalidChildElement, and neither the DOM nor the registry is changed. The caller is responsible for ensuring child is a direct DOM child of elem; the browser verifies that relationship before applying the removal.

Call this while the Element is rendering or updating, when a send pass is imminent. To change the Element in response to a browser event, mark it dirty with Request.Dirty instead: a change queued directly from an event handler is flushed only when the processing loop is next woken, which on an otherwise-idle request is not guaranteed to be prompt (see [Element.queue]).

func (*Element) RemoveAttr added in v0.31.0

func (elem *Element) RemoveAttr(attr string)

RemoveAttr queues sending a request to remove an attribute to the browser for the Element.

The framework-owned "id" attribute is rejected (ASCII case-insensitively): attempting to remove it is reported as ErrReservedAttribute via reportMisuse and nothing is sent, since it carries the Element's JaWS identity.

Call this while the Element is rendering or updating, when a send pass is imminent. To change the Element in response to a browser event, mark it dirty with Request.Dirty instead: a change queued directly from an event handler is flushed only when the processing loop is next woken, which on an otherwise-idle request is not guaranteed to be prompt (see [Element.queue]).

func (*Element) RemoveClass added in v0.31.0

func (elem *Element) RemoveClass(cls string)

RemoveClass queues sending a request to remove a class to the browser for the Element.

Call this while the Element is rendering or updating, when a send pass is imminent. To change the Element in response to a browser event, mark it dirty with Request.Dirty instead: a change queued directly from an event handler is flushed only when the processing loop is next woken, which on an otherwise-idle request is not guaranteed to be prompt (see [Element.queue]).

func (*Element) Replace added in v0.31.0

func (elem *Element) Replace(htmlCode template.HTML)

Replace replaces the Element's entire HTML DOM node with new HTML code.

A valid call recreates the target subtree even when htmlCode matches its current serialization. Browser-only state on replaced nodes, including focus, selection, scroll position, live form-control properties, programmatic listeners, expando properties, and custom-element instances, is not preserved. JaWS reattaches its own managed browser behavior.

A replacement node bearing an existing JaWS ID keeps that server-side Element registration and UI state; omitted descendant IDs are unregistered. Retained Elements are not rerendered separately, so their markup must match that state and each reused ID must identify the same logical Element.

The trusted HTML should preserve the element identity by putting the element's own JaWS ID on the replacement root element, normally as id="Jid.N". Replace is not an HTML validator: it performs only a lightweight textual guard for that expected id attribute. If the guard does not find it, the call is a programming error: debug builds panic and production builds report it via Jaws.MustLog and skip the replacement.

Call this while the Element is rendering or updating, when a send pass is imminent. To change the Element in response to a browser event, mark it dirty with Request.Dirty instead: a change queued directly from an event handler is flushed only when the processing loop is next woken, which on an otherwise-idle request is not guaranteed to be prompt.

func (*Element) SetAttr added in v0.31.0

func (elem *Element) SetAttr(attr, value string)

SetAttr queues sending a new attribute value to the browser for the Element.

The value parameter must be the unescaped logical attribute value. It is sent to the browser DOM and used as the value argument to setAttribute().

The framework-owned "id" attribute is rejected (ASCII case-insensitively): attempting to set it is reported as ErrReservedAttribute via reportMisuse and nothing is sent, since it carries the Element's JaWS identity.

Call this while the Element is rendering or updating, when a send pass is imminent. To change the Element in response to a browser event, mark it dirty with Request.Dirty instead: a change queued directly from an event handler is flushed only when the processing loop is next woken, which on an otherwise-idle request is not guaranteed to be prompt (see [Element.queue]).

func (*Element) SetClass added in v0.31.0

func (elem *Element) SetClass(cls string)

SetClass queues sending a class to the browser for the Element.

Call this while the Element is rendering or updating, when a send pass is imminent. To change the Element in response to a browser event, mark it dirty with Request.Dirty instead: a change queued directly from an event handler is flushed only when the processing loop is next woken, which on an otherwise-idle request is not guaranteed to be prompt (see [Element.queue]).

func (*Element) SetInner added in v0.31.0

func (elem *Element) SetInner(innerHTML template.HTML)

SetInner queues new inner HTML content for the Element.

When innerHTML exactly matches the browser's current serialized inner HTML, JaWS leaves the existing descendants and their live state unchanged. Use Element.Replace when matching markup must still create new nodes.

Call this while the Element is rendering or updating, when a send pass is imminent. To change the Element in response to a browser event, mark it dirty with Request.Dirty instead: a change queued directly from an event handler is flushed only when the processing loop is next woken, which on an otherwise-idle request is not guaranteed to be prompt.

func (*Element) SetValue added in v0.31.0

func (elem *Element) SetValue(value string)

SetValue queues sending a new current input value in textual form to the browser for the Element.

Call this while the Element is rendering or updating, when a send pass is imminent. To reconcile only this Element after a browser event, call elem.Dirty(elem); this schedules JawsUpdate on the Request loop and serializes the correction with other updates. Dirty a source tag instead when shared application state changed. Calling SetValue directly from an event handler may not flush promptly and bypasses that update ordering.

func (*Element) String added in v0.31.0

func (elem *Element) String() string

String returns a debug representation of elem: its UI type, Jid, and tags.

The tags render through tag.TagsString, so in the default build they show their types (and a pointer's address when readable; see tag.TagStringRelease), while debug and -race builds render them in full — more informative, but able to crash on a self-referential or oversized tag. String tolerates an Element whose Request is not yet set.

func (*Element) Tag added in v0.31.0

func (elem *Element) Tag(tags ...any)

Tag associates elem with tags.

It is shorthand for Request.Tag. A deleted Element ignores the call without expanding tags.

func (*Element) UI added in v0.112.1

func (elem *Element) UI() UI

UI returns the UI object.

type HandleFunc added in v0.111.6

type HandleFunc = func(pattern string, handler http.Handler)

HandleFunc matches the signature of http.ServeMux.Handle.

type InitialHTMLAttrHandler added in v0.400.0

type InitialHTMLAttrHandler interface {
	// JawsInitialHTMLAttr returns attributes for elem's initial render, or an empty string.
	//
	// Callers must not hold a lock protecting the handler or its source. The method
	// must synchronize shared state. Its result need not share a state snapshot with
	// other values read during rendering.
	JawsInitialHTMLAttr(elem *Element) (s template.HTMLAttr)
}

InitialHTMLAttrHandler provides attributes for initial Element rendering.

type InputFn added in v0.401.0

type InputFn = func(elem *Element, value string) (err error)

InputFn is the signature of an input handling function.

JaWS calls it for an input or set message received from JavaScript over the WebSocket connection, and for a hook message, which tests use to invoke the handler synchronously (see what.Hook).

When a function value is used directly as an input handler through an any-valued API such as ParseParams, Element.AddHandlers or CallEventHandlers, its dynamic type must be exactly InputFn. Convert a value of a defined function type to InputFn first, or implement InputHandler.

type InputHandler added in v0.401.0

type InputHandler interface {
	// JawsInput is called when JaWS dispatches an input-like message for an
	// [Element]. See [InputFn] for the message kinds.
	//
	// The bundled client sends input and set messages only while its WebSocket is
	// open and does not queue them for later delivery. Native changes that emit
	// neither input nor change do not invoke JawsInput.
	JawsInput(elem *Element, value string) (err error)
}

InputHandler handles input-like messages for an Element.

type Jaws

type Jaws struct {
	// CookieName is the name used for session cookies.
	//
	// It defaults to [assets.DefaultCookieName], which is derived from the
	// executable and falls back to "jaws". CookieName must be a valid, non-empty
	// HTTP cookie name; see [http.Cookie.Valid].
	CookieName  string
	AutoSession bool // Create and associate a session during a successful WebSocket upgrade when a Request has none. Defaults to false.
	// TrustForwardedHeaders enables trusted proxy header processing.
	//
	// It governs the session cookie Secure flag and WebSocket Origin scheme
	// validation through the forwarding headers recognized by
	// [secureheaders.RequestIsSecure], and the client IP used for session and
	// request binding through X-Forwarded-For and X-Real-IP. Behind a proxy that
	// terminates TLS and forwards plain HTTP, enable it and have the proxy sanitize
	// forwarding headers and set the scheme and client IP itself, or HTTPS-page
	// WebSocket upgrades are rejected. Defaults to false; enable only behind a
	// single reverse proxy you control.
	TrustForwardedHeaders bool
	Logger                Logger     // Optional logger; [Jaws.Log] dispatches Error calls asynchronously and serially
	Debug                 bool       // Enables debug HTML and reporting of otherwise-silent WebSocket transport errors. Call GenerateHeadHTML after changing it.
	MakeAuth              MakeAuthFn // Function to create ui.With.Auth for Templates. If nil, templates get the fail-open DefaultAuth (IsAdmin()==true for everyone); set it to enforce authorization. See DefaultAuth.
	// BaseContext is the parent context for Requests.
	//
	// New uses [context.Background]. If a custom context implements the optional
	// method recognized by [context.AfterFunc], that method and its returned stop
	// function must return promptly and must not synchronously call this Jaws or
	// one of its Requests, or wait for work that does. See [Request.SetContext].
	BaseContext context.Context
	// StatusMetrics selects status metrics for tag updates.
	//
	// While [Jaws.Serve] or [Jaws.ServeWithTimeout] is running, maintenance
	// drains coalesced status changes. It dirties a selected metric's tag when the
	// metric is newly selected or after its count changes. A successful WebSocket
	// acceptance also dirties the selected active-Request, pending-Request, and
	// active-Session tags. A tag may be dirtied when its count is unchanged. Changes
	// coalesce to one dirty mark per tag per pass.
	//
	// Use [atomic.Uint32.Store], [atomic.Uint32.Or], or [atomic.Uint32.And] with
	// [StatusMetricAll] or individual status metric flags. Only bits in
	// StatusMetricAll are interpreted. The zero value disables status tag updates.
	// Atomic operations may be used concurrently, including before serving.
	StatusMetrics atomic.Uint32
	// WebSocketPingInterval controls read-idle keepalive pings.
	//
	// When a WebSocket read remains pending for this interval, JaWS pings the peer.
	// Incoming data or a successful ping restarts the interval. Time spent parsing
	// or delivering already-read data does not count toward it.
	//
	// It defaults to [DefaultWebSocketPingInterval] and must be positive;
	// non-positive values do not disable probing.
	WebSocketPingInterval   time.Duration
	MaxPendingRequestsPerIP int // Maximum number of unclaimed Requests per client IP. Defaults to DefaultMaxPendingRequestsPerIP. Set <=0 to disable the cap.
	// contains filtered or unexported fields
}

Jaws holds the server-side state and configuration for a JaWS instance.

A single Jaws value coordinates template lookup, session handling and the request lifecycle that keeps the browser and backend synchronized via WebSockets. The zero value is not ready for use; construct instances with New to ensure the helper goroutines and static assets are prepared.

Except for Jaws.StatusMetrics, the exported configuration fields are ordinary fields, not live synchronized settings. Several are consulted on each connection or request (for example MaxPendingRequestsPerIP and WebSocketPingInterval), so set them all before exposing handlers, creating Requests, or starting Jaws.Serve / Jaws.ServeWithTimeout; mutating one after serving has begun is an unsynchronized write and is not supported. StatusMetrics is atomic and may be changed while serving. Methods document their own concurrency behavior and may be called concurrently when stated.

func New

func New() (jw *Jaws, err error)

New allocates a JaWS instance with the default configuration.

The returned Jaws value is ready for use: static assets are embedded, the broadcast channels and update ticker are allocated and the reusable request buffer pool is primed. You must still start the processing loop with Jaws.Serve or Jaws.ServeWithTimeout on its own goroutine before broadcasting. Call Jaws.Close when finished with the instance to free associated resources.

func (*Jaws) ActiveRequestCountTag added in v0.801.0

func (jw *Jaws) ActiveRequestCountTag() any

ActiveRequestCountTag returns this instance's active-Request count tag.

Use it with the active result from Jaws.RequestCounts. The tag is stable for the Jaws lifetime and unique to this instance and metric. Select StatusMetricActiveRequests to have maintenance dirty it; Jaws.StatusMetrics defines when.

func (*Jaws) ActiveSessionCount added in v0.801.0

func (jw *Jaws) ActiveSessionCount() (n int)

ActiveSessionCount returns the active Session count.

It counts registered Sessions attached to at least one Request whose Request.ServeHTTP loop is running. A Session shared by several running Requests counts once. A Session retained only for its disconnect grace period is inactive. It is safe for concurrent use.

func (*Jaws) ActiveSessionCountTag added in v0.801.0

func (jw *Jaws) ActiveSessionCountTag() any

ActiveSessionCountTag returns this instance's active-Session count tag.

Use it with Jaws.ActiveSessionCount. The tag is stable for the Jaws lifetime and unique to this instance and metric. Select StatusMetricActiveSessions to have maintenance dirty it; Jaws.StatusMetrics defines when.

func (*Jaws) AddTemplateLookuper added in v0.45.0

func (jw *Jaws) AddTemplateLookuper(tl TemplateLookuper) (err error)

AddTemplateLookuper adds a TemplateLookuper.

The lookuper must be comparable so it can be removed with Jaws.RemoveTemplateLookuper, and it must compare equal to itself. A value that is runtime-comparable yet not reflexively equal — a struct carrying a floating-point NaN, for example — is never matched: each add appends a new entry instead of deduplicating, and Jaws.RemoveTemplateLookuper reports success without removing it.

func (*Jaws) Alert

func (jw *Jaws) Alert(level, msg string)

Alert sends an alert to all active Request values.

The level argument should be one of Bootstrap's alert levels: primary, secondary, success, danger, warning, info, light or dark.

The level and msg are HTML-escaped before being sent, so it is safe to pass untrusted text; do not pre-escape it.

func (*Jaws) Append

func (jw *Jaws) Append(target any, html template.HTML)

Append calls the JavaScript appendChild method on all HTML elements matching target.

func (*Jaws) Broadcast

func (jw *Jaws) Broadcast(msg wire.Message)

Broadcast sends msg to the active Request and Element values selected by wire.Message.Dest.

It must not be called before the JaWS processing loop (Jaws.Serve or Jaws.ServeWithTimeout) is running. Otherwise this call may block.

All convenience helpers on Jaws that call Broadcast inherit this requirement.

A wire.Message.What of what.Replace or what.Remove is rejected (as ErrReplaceNotBroadcastable or ErrRemoveNotBroadcastable) via reportMisuse and nothing is sent: each mutates a specific element's node in a way the broadcast path cannot keep in sync with the server-side registry, stranding the matched Element values with no reachable DOM node. Use Element.Replace, or Jaws.Delete / Element.Remove, for the identity-preserving forms.

A nil wire.Message.Dest targets every active Request; a key.Key Dest targets the active Request with that identity key, and a zero key is dropped. Any other Dest is expanded into tags. Plain strings and Jid values are illegal tag types; use tag.Tag, a domain tag, or an Element method instead.

That expansion runs through Jaws.MustTagExpand, which reports a failure such as an illegal tag type through Jaws.MustLog: that panics when no Jaws.Logger is set, while with a Logger the error is queued and the message is sent to the destinations that did expand.

func (*Jaws) Close

func (jw *Jaws) Close()

Close initiates shutdown of the Jaws instance.

Jaws.Done is closed as shutdown begins. Before Close returns, the context returned by Request.Context for every current Request is canceled, including pending Requests whose WebSocket never connected. Non-running Requests become unclaimable but retain their identity while callers hold them. Active WebSocket handlers observe cancellation and finish asynchronously.

Registered Session values are invalidated and detached from their Requests, and their key/value data is permanently cleared; new Sessions cannot be created after shutdown begins.

Calls to Jaws.NewRequest after shutdown begins return Requests with already-canceled contexts that Jaws.UseRequest cannot claim. Broadcasts and sends may be discarded after Done closes. Close stops accepting errors for Logger delivery. Accepted errors continue draining asynchronously; later Jaws.Log calls are counted but not delivered. On normal return after shutdown, Jaws.Serve and Jaws.ServeWithTimeout wait for the drain. Subsequent calls to Close have no effect.

func (*Jaws) ContentSecurityPolicy added in v0.300.0

func (jw *Jaws) ContentSecurityPolicy() (s string)

ContentSecurityPolicy returns the Content-Security-Policy header value generated by Jaws.GenerateHeadHTML.

func (*Jaws) DefaultAuth added in v0.600.0

func (jw *Jaws) DefaultAuth() *DefaultAuth

DefaultAuth returns the shared fail-open DefaultAuth used for templates when Jaws.MakeAuth is nil.

The returned value logs at most one warning through the Jaws.Logger in effect when DefaultAuth is first called.

func (*Jaws) Delete added in v0.31.0

func (jw *Jaws) Delete(target any)

Delete removes the HTML element(s) matching target.

func (*Jaws) Dirty added in v0.31.0

func (jw *Jaws) Dirty(dirtyTags ...any)

Dirty schedules updates for tags and exact Elements.

The inputs are expanded through Jaws.MustTagExpand: with a Jaws.Logger configured an expansion error is queued and the partial result is still applied, while without one the call panics before anything is marked dirty. An expanded non-nil pointer to a live Element belonging to this Jaws selects only that Element; other Element pointers are ignored. Other keys select matching Elements on every live Request. Updates run on the normal batched dirty pass, so Jaws.Serve or Jaws.ServeWithTimeout must be running for delivery.

Request.Dirty is equivalent.

func (*Jaws) Done

func (jw *Jaws) Done() <-chan struct{}

Done returns a channel closed when Jaws.Close begins shutdown.

func (*Jaws) ErrorCount added in v0.801.0

func (jw *Jaws) ErrorCount() uint64

ErrorCount returns the number of errors reported to this instance.

A non-nil error reported through this instance's Jaws.Log or Jaws.MustLog increments the count, including calls through Request.Log or Request.MustLog. Counting continues without a Logger and after Jaws.Done closes; Logger delivery, latency, and panics do not affect it.

ErrorCount is safe for concurrent use. StatusMetricErrors controls tag updates, not counting.

func (*Jaws) ErrorCountTag added in v0.801.0

func (jw *Jaws) ErrorCountTag() any

ErrorCountTag returns this instance's reported-error count tag.

Use it with Jaws.ErrorCount. The tag is stable for the Jaws lifetime and unique to this instance and metric. Select StatusMetricErrors to have maintenance dirty it; Jaws.StatusMetrics defines when.

func (*Jaws) FaviconURL added in v0.111.6

func (jw *Jaws) FaviconURL() (s string)

FaviconURL returns the favicon URL discovered by Jaws.GenerateHeadHTML.

func (*Jaws) GenerateHeadHTML added in v0.5.0

func (jw *Jaws) GenerateHeadHTML(extra ...string) (err error)

GenerateHeadHTML regenerates the HTML code that goes in the HEAD section.

It emits the provided URL resources in extra according to assets.PreloadHTML, along with the JaWS JavaScript and stylesheet. Every successfully parsed URL is passed to secureheaders.BuildContentSecurityPolicyForURLs, and the resulting policy is available from Jaws.ContentSecurityPolicy. The favicon selected by assets.PreloadHTML is available from Jaws.FaviconURL.

A configured Jaws.Logger warns once for each extra URL that is omitted from the final markup or is absolute or scheme-relative and cannot contribute an explicit policy source. Warning URLs redact passwords and omit queries and fragments. Applications may load omitted resources manually. See Jaws.SecureHeadersMiddleware when automatic inference does not match the resource's request destination.

Resource URLs must come from trusted application configuration because matched scripts are executable and CSP permissions apply to origins.

If one or more URLs in extra fail to parse, GenerateHeadHTML still installs the regenerated head HTML and Content-Security-Policy with the failing resources omitted, and returns the joined parse errors.

Call GenerateHeadHTML after changing Jaws.Debug or the extra resources.

func (*Jaws) GetSession added in v0.11.0

func (jw *Jaws) GetSession(r *http.Request) (sess *Session)

GetSession returns the Session associated with the given http.Request, or nil.

Sessions are bound to the client IP (see the clientIP method). Behind a reverse proxy that connects over loopback, every request appears to come from loopback and IP binding is effectively disabled unless Jaws.TrustForwardedHeaders is enabled so the forwarded client IP is used instead.

func (*Jaws) Insert

func (jw *Jaws) Insert(target any, childIndex int, html template.HTML)

Insert inserts html before the child at childIndex in every element matching target.

target follows Jaws.Broadcast's tag rules. For request-local insertion before a known child, use Element.InsertBefore.

A negative childIndex is reported as ErrInvalidChildIndex and no message is sent. Use Jaws.Append to insert at the end. html is trusted HTML, matching Jaws.SetInner and Jaws.Append.

func (*Jaws) JsCall added in v0.114.0

func (jw *Jaws) JsCall(target any, jsfunc, jsonstr string)

JsCall calls a browser JavaScript function path for matching targets.

target selects which requests or elements receive the Call message. In each receiving browser, jsfunc is resolved as a path from window and called with JSON.parse(jsonstr); the matched element is not passed as this or as an argument. jsfunc must be an application-controlled dot path. The browser rejects an exact "__proto__" component; put user data in jsonstr, not jsfunc.

A nil target calls each active Request once. A nonzero key.Key target calls the matching active Request once without requiring a matching DOM element; a zero key is ignored. Other targets follow Jaws.Broadcast's tag rules.

func (*Jaws) Log

func (jw *Jaws) Log(err error) error

Log reports an error and returns err.

Each non-nil err increments Jaws.ErrorCount. A nil Logger or a report after Jaws.Done closes prevents delivery but not counting. Reports accepted for Logger delivery are dispatched asynchronously and FIFO-serialized for each Jaws instance. Logger.Error runs without JaWS core locks and may re-enter the same Jaws subject to the normal lifecycle rules. A panic from Logger.Error is recovered by the logging dispatcher.

Log is safe for concurrent use, including with Jaws.Close. A nil receiver or nil err is not counted or delivered. Log always returns err. The queue applies no capacity backpressure, so errors accumulate in memory when Logger.Error does not keep pace. Log retains err for delivery; callers must not mutate state exposed by err concurrently after passing it.

func (*Jaws) LookupTemplate added in v0.66.0

func (jw *Jaws) LookupTemplate(name string) *template.Template

LookupTemplate queries the known TemplateLookuper values in the order they were added and returns the first found.

func (*Jaws) MustLog added in v0.1.1

func (jw *Jaws) MustLog(err error)

MustLog passes a non-nil err to Jaws.Log, then panics if no Jaws.Logger is configured.

A nil err has no effect, including on a nil receiver. With a non-nil err, a nil receiver panics without counting; a non-nil receiver counts the error and then panics if Logger is nil. See Jaws.Log for delivery and shutdown behavior.

func (*Jaws) MustTagExpand added in v0.700.0

func (jw *Jaws) MustTagExpand(tagValue any) (result []any)

MustTagExpand expands tagValue and reports expansion errors through Jaws.MustLog.

With a Jaws.Logger configured, the error is queued and MustTagExpand returns github.com/linkdata/jaws/lib/tag.TagExpand's partial result. Without one, Jaws.MustLog panics, so the partial result never reaches the caller.

func (*Jaws) NewRequest

func (jw *Jaws) NewRequest(w http.ResponseWriter, r *http.Request) *Request

NewRequest returns a new JaWS Request.

While the Jaws instance is open, the returned Request is pending until it is claimed or retired.

NewRequest replaces w's Cache-Control header with "no-store". Call it with the response writer before writing its headers or body. Calling it after the response is committed does not change the sent headers.

Use the returned Request while rendering the initial response to register JaWS IDs and write Request.HeadHTML. Do not retain it after initial request handling and rendering; see Request.

If r is nil, the Request has no initial request, client address, or Session.

Jaws.ServeWithTimeout periodically retires idle Requests before WebSocket processing starts; Jaws.Serve uses DefaultWebSocketTimeout.

When Jaws.MaxPendingRequestsPerIP is positive and already reached, NewRequest retires the oldest idle pending Request from the same IP. If every pending Request was created or written recently, it retires the least recently written one so the configured maximum is never exceeded.

A Request created after Jaws.Close has an already-canceled context and cannot be claimed by Jaws.UseRequest.

Every call returns a distinct Request identity that is never reused for another connection. When timeout maintenance or the per-IP pending limit retires an unclaimed Request, its key remains unavailable for assignment to another Request while the retired Request is reachable; no deadline is guaranteed for later key reuse.

It panics if the crypto/rand.Reader captured by New returns an error while generating the request key. Go's default reader does not return errors.

func (*Jaws) NewSession added in v0.26.0

func (jw *Jaws) NewSession(w http.ResponseWriter, r *http.Request) (sess *Session)

NewSession creates a new Session.

All live pre-existing Session values referenced by matching cookies and bound to the request's client IP are cleared and closed. Each is closed with Session.Close, so the JaWS processing loop (Jaws.Serve or Jaws.ServeWithTimeout) must be running.

Subsequent Request values created with Jaws.NewRequest that have the cookie set and originate from the same IP will be able to access the Session. The IP comparison is the same loopback-aware, optionally forwarded-header-based match used everywhere else; see Jaws.GetSession and Jaws.TrustForwardedHeaders for the reverse-proxy caveat.

If the new Session remains current and live during cookie publication, its cookie is written to w when w is non-nil and added to r itself. This makes the Session visible to Jaws.GetSession and Jaws.NewRequest for the remainder of the same HTTP request. If a concurrent Session.Close wins first, neither w nor r receives its live cookie.

It returns nil and has no effect if r is nil or shutdown has begun; w may be nil.

It panics if the crypto/rand.Reader captured by New returns an error while generating the session ID. Go's default reader does not return errors.

func (*Jaws) Pending

func (jw *Jaws) Pending() (n int)

Pending returns the number of requests waiting for their WebSocket callbacks.

func (*Jaws) PendingRequestCountTag added in v0.801.0

func (jw *Jaws) PendingRequestCountTag() any

PendingRequestCountTag returns this instance's pending-Request count tag.

Use it with Jaws.Pending. The tag is stable for the Jaws lifetime and unique to this instance and metric. Select StatusMetricPendingRequests to have maintenance dirty it; Jaws.StatusMetrics defines when.

func (*Jaws) Redirect

func (jw *Jaws) Redirect(url string)

Redirect requests all active Request values to navigate to the given URL.

The URL is validated to be a relative path or an http/https URL; script-bearing schemes such as javascript: and protocol-relative ("//host") URLs are refused and logged rather than sent to the browser.

func (*Jaws) Reload

func (jw *Jaws) Reload()

Reload requests all active Request values to reload their current page.

func (*Jaws) RemoveAttr

func (jw *Jaws) RemoveAttr(target any, attr string)

RemoveAttr sends a request to remove the given attribute from all HTML elements matching target.

The framework-owned "id" attribute is rejected (ASCII case-insensitively): attempting to remove it is reported as ErrReservedAttribute via reportMisuse and nothing is sent, since it carries an Element's JaWS identity.

func (*Jaws) RemoveClass added in v0.31.0

func (jw *Jaws) RemoveClass(target any, cls string)

RemoveClass sends a request to remove the given class from all HTML elements matching target.

func (*Jaws) RemoveTemplateLookuper added in v0.45.0

func (jw *Jaws) RemoveTemplateLookuper(tl TemplateLookuper) (err error)

RemoveTemplateLookuper removes the given TemplateLookuper.

The lookuper is matched by equality, so a value that does not compare equal to itself — one carrying a floating-point NaN, for example — is never found and this returns nil without removing it; see Jaws.AddTemplateLookuper.

func (*Jaws) RequestCount added in v0.25.0

func (jw *Jaws) RequestCount() (n int)

RequestCount returns the total Request count.

It equals the total returned by Jaws.RequestCounts.

func (*Jaws) RequestCounts added in v0.407.0

func (jw *Jaws) RequestCounts() (total, active int)

RequestCounts returns the total and active Request counts.

The total includes pending, claimed, and active Request values. It excludes retired Requests, even if an initial HTTP handler still holds them. The active count includes Requests whose Request.ServeHTTP loop is running.

func (*Jaws) SecureHeadersMiddleware added in v0.300.0

func (jw *Jaws) SecureHeadersMiddleware(next http.Handler) http.Handler

SecureHeadersMiddleware wraps next with the JaWS security headers.

It clones secureheaders.DefaultHeaders(), replacing the Content-Security-Policy value with Jaws.ContentSecurityPolicy for each request. The generated policy applies secureheaders.ResourceDestinationAuto to resource URLs configured by Jaws.GenerateHeadHTML.

Applications needing explicit destinations can instead use secureheaders.Middleware with secureheaders.DefaultHeaders, setting its Content-Security-Policy with secureheaders.BuildContentSecurityPolicy. The replacement policy must include every external resource the page loads, including resources configured by Jaws.GenerateHeadHTML.

The returned middleware does not trust forwarded HTTPS headers. Note that the session cookie Secure flag is governed separately by Jaws.TrustForwardedHeaders (also false by default), so the two stay consistent unless you opt in.

func (*Jaws) Serve

func (jw *Jaws) Serve()

Serve calls Jaws.ServeWithTimeout with DefaultWebSocketTimeout.

See Jaws.ServeWithTimeout for lifecycle and panic behavior.

func (*Jaws) ServeHTTP added in v0.19.0

func (jw *Jaws) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP can handle the required JaWS endpoints, which all start with "/jaws/".

The method is checked per matched endpoint, not up front: the static asset and .ping endpoints answer GET and HEAD (any other method gets 405 with an Allow header), while the per-Request key and tail-script endpoints are GET-only capability URLs that fall through to 404 on any other method. An unknown path or a wrong method on a capability URL therefore 404s rather than 405s, and never reveals whether a key is valid.

func (*Jaws) ServeWithTimeout

func (jw *Jaws) ServeWithTimeout(requestTimeout time.Duration)

ServeWithTimeout begins processing requests.

requestTimeout must be an exact multiple of time.Second from time.Second through 2,147,483,646 seconds. Other values have unspecified behavior.

An overlapping Jaws.Serve or Jaws.ServeWithTimeout call reports ErrServeAlreadyRunning through Jaws.MustLog. It panics without a Logger and in debug or race builds; otherwise it returns without starting another processing loop.

Before Request.ServeHTTP begins WebSocket processing, timeout-based Request retirement is periodic and approximate, not a hard deadline. Jaws.NewRequest, a successful Jaws.UseRequest, and Request.MarkWritten mark activity using whole-second samples from the epoch established by New. Retirement is checked only during maintenance passes, so it is not timed precisely from those events.

requestTimeout also bounds each WebSocket keepalive ping and outbound write, independently of the maintenance schedule. See Jaws.WebSocketPingInterval for probe scheduling.

It is intended to run on its own goroutine and returns when Jaws.Close is called. Errors reported through Jaws.Log are queued without waiting for Logger.Error. On a normal return after shutdown, ServeWithTimeout waits for every log entry accepted before Jaws.Close to finish. A blocked Logger.Error callback therefore delays that return.

func (*Jaws) SessionCount added in v0.11.0

func (jw *Jaws) SessionCount() (n int)

SessionCount returns the number of registered Sessions.

It includes Sessions retained during their disconnect grace period.

func (*Jaws) SessionCountTag added in v0.801.0

func (jw *Jaws) SessionCountTag() any

SessionCountTag returns this instance's registered-Session count tag.

Use it with Jaws.SessionCount. The tag is stable for the Jaws lifetime and unique to this instance and metric. Select StatusMetricSessions to have maintenance dirty it; Jaws.StatusMetrics defines when.

func (*Jaws) SessionMiddleware added in v0.600.0

func (jw *Jaws) SessionMiddleware(h http.Handler) http.Handler

SessionMiddleware returns a session-creating http.Handler.

Before invoking h, it creates a JaWS Session when the request has none. If a concurrent Session.Close wins the new Session's cookie publication, h runs without that Session or its live cookie.

It is distinct from the session accessors: Jaws.GetSession and Request.Session look up an existing Session, while this wraps a handler. It composes with Jaws.SecureHeadersMiddleware.

func (*Jaws) Sessions added in v0.11.0

func (jw *Jaws) Sessions() (sessions []*Session)

Sessions returns a snapshot of all registered sessions, which may be nil.

Auto-created Session values are registered only after their initiating Request is associated.

func (*Jaws) SetAttr

func (jw *Jaws) SetAttr(target any, attr, value string)

SetAttr sends a request to replace the given attribute value in all HTML elements matching target.

The value parameter must be the unescaped logical attribute value. It is sent to the browser DOM and used as the value argument to setAttribute().

The framework-owned "id" attribute is rejected (ASCII case-insensitively): attempting to set it is reported as ErrReservedAttribute via reportMisuse and nothing is sent, since it carries an Element's JaWS identity.

func (*Jaws) SetClass added in v0.31.0

func (jw *Jaws) SetClass(target any, cls string)

SetClass sends a request to set the given class in all HTML elements matching target.

func (*Jaws) SetInner

func (jw *Jaws) SetInner(target any, innerHTML template.HTML)

SetInner replaces the inner HTML of all elements matching target.

When the HTML exactly matches an element's current serialized inner HTML, JaWS leaves its existing descendants and their live state unchanged. Use Element.Replace when matching markup must still create new nodes.

func (*Jaws) SetValue

func (jw *Jaws) SetValue(target any, value string)

SetValue sends a request to set the current input value (in textual form) of all HTML elements matching target. It sets the live DOM value/state, not the HTML "value" attribute.

func (*Jaws) Setup added in v0.111.6

func (jw *Jaws) Setup(handleFn HandleFunc, prefix string, extras ...any) (err error)

Setup configures Jaws with extra functionality and resources.

The list of extras can be strings, *url.URL, *staticserve.StaticServe or []*staticserve.StaticServe URL resources, or a SetupFunc such as jawsboot.Setup.

A value of a defined function type must be converted to SetupFunc before it is passed as an extra.

A nil SetupFunc extra is ignored.

It calls Jaws.GenerateHeadHTML with the final list of URLs, with any relative URL paths prefixed with prefix.

staticserve.StaticServe extras are local resources. Their generated URLs are slash-rooted so they match their registered handlers, including when prefix is empty. Other relative URL extras remain relative with an empty prefix. Each staticserve.StaticServe.Name is treated as a literal path, not as a pre-escaped URL; percent signs in a name are escaped as literal percent signs.

If handleFn is nil, Setup generates head HTML from the configured resources without registering any handlers.

func (*Jaws) TestServe added in v0.500.0

func (jw *Jaws) TestServe(rq *Request, onPanic func(recovered any)) (inCh chan wire.WsMsg, outCh chan wire.WsMsg, bcastCh chan wire.Message, readyCh, doneCh chan struct{})

TestServe runs rq's WebSocket message-processing loop for test harnesses, including the out-of-package harness in github.com/linkdata/jaws/jawstest.

It subscribes rq to broadcasts, waits for the running Serve loop to process the subscription, transitions rq to running with the same checked transition Request.ServeHTTP uses, then runs rq.process in a new goroutine using freshly created inbound/outbound channels, recycling rq when the loop stops.

rq must already be claimed via Jaws.UseRequest. TestServe panics — like its other setup-failure panics — if the Jaws processing loop (Jaws.Serve or Jaws.ServeWithTimeout) is not running, or if rq is not servable (unclaimed, already being served, retired, or the instance is closed).

TestServe is exported solely to let test harnesses outside package jaws drive a request loop without access to unexported internals. It is not intended for production use; it does not import any testing-only packages, so it does not pull net/http/httptest into the production build.

onPanic is called with the recovered value (nil if the loop exited normally) when the loop goroutine stops, before doneCh is closed, so a harness can publish captured panic state before any <-doneCh waiter observes it. A harness that does not expect panics should re-panic when the value is non-nil so unexpected loop panics still surface. doneCh is closed even if onPanic panics or calls runtime.Goexit.

func (*Jaws) UseRequest

func (jw *Jaws) UseRequest(jawsKey key.Key, r *http.Request) (rq *Request)

UseRequest extracts the JaWS Request with the given key from the request map if it exists and the HTTP request remote IP matches.

Call it when receiving the WebSocket connection on "/jaws/:key" to get the associated Request, and then call its Request.ServeHTTP method to process the WebSocket messages.

A successful claim marks activity used by Jaws.ServeWithTimeout while the Request waits for Request.ServeHTTP.

Returns nil if the key was not found, the request was already claimed by an earlier WebSocket callback, or the IP doesn't match, in which case you should return an HTTP "404 Not Found" status.

The returned pointer is borrowed for WebSocket handling. Do not retain it after Request.ServeHTTP returns; see Request.

type Jid added in v0.31.0

type Jid = jid.Jid // convenience alias

Jid is the identifier type used for HTML elements managed by JaWS.

It is provided as a convenience alias to the value defined in the jid subpackage so applications do not have to import that package directly when working with element IDs.

type Logger added in v0.110.1

type Logger interface {
	Info(msg string, args ...any)
	Warn(msg string, args ...any)
	Error(msg string, args ...any)
}

Logger receives JaWS diagnostics.

Jaws.Log invokes Error asynchronously in FIFO order, without JaWS core locks, and recovers panics from those calls. Error may re-enter the same Jaws instance subject to its normal lifecycle rules. Other Logger calls may be synchronous and concurrent, so implementations must be safe for concurrent use. Logger is satisfied by a *log/slog.Logger via its Info, Warn and Error methods.

Error should return promptly: one blocked callback delays later Error calls for that Jaws instance and its serving loop's final shutdown drain.

type MakeAuthFn added in v0.85.0

type MakeAuthFn = func(rq *Request) Auth

MakeAuthFn constructs an Auth value for a Request.

Set Jaws.MakeAuth to your implementation to enforce real authorization. If Jaws.MakeAuth is left nil, templates receive DefaultAuth, which is fail-open: see its documentation.

It is a type alias so a bare func value can be assigned without conversion, matching the sibling callback types ConnectFn, InputFn and HandleFunc.

type Renderer added in v0.60.0

type Renderer interface {
	// JawsRender is called once per [Element] when rendering the initial webpage.
	// Do not call this yourself unless it is from within another JawsRender implementation.
	// The engine does not invoke this once the [Element] is deleted (see [Element.Deleted]).
	//
	// A delegating renderer and its delegates may claim the Element's widget state
	// slot only once. A later claim fails with [ErrElementStateClaimed]; see
	// [SetElementState].
	JawsRender(elem *Element, w io.Writer, params []any) error
}

Renderer renders the initial HTML for a UI object.

type Request

type Request struct {
	Jaws    *Jaws   // (read-only) the JaWS instance the Request belongs to
	JawsKey key.Key // (read-only) random key assigned to this Request; routes JaWS URLs and request-targeted broadcasts only while registered
	// contains filtered or unexported fields
}

Request maintains the event, update, and broadcast state for one JaWS connection.

A Request pointer is borrowed for the lifecycle that supplied it: the initial HTTP render from Jaws.NewRequest or WebSocket handling from Jaws.UseRequest. Do not retain it in application state or use it from a background goroutine; retain Request.Context instead. Use Request.SetContext to install a context whose cancellation can end the connection.

Ending the initial HTTP render normally leaves the Request pending until Jaws.UseRequest claims it. The Request finishes when WebSocket handling ends or a non-running Request is retired. It then remains cancelled and unregistered. Its pointer identity is never reused for another connection.

func (*Request) Alert

func (rq *Request) Alert(level, msg string)

Alert attempts to show an alert message on the current request webpage if it has an HTML element with the data-jaws-alerts attribute.

The level argument should be one of Bootstrap's alert levels: primary, secondary, success, danger, warning, info, light or dark.

The level and msg are HTML-escaped before being sent, so it is safe to pass untrusted text; do not pre-escape it.

The default JaWS JavaScript only supports Bootstrap dismissible alerts.

See Request for pointer lifetime and Jaws.Broadcast for processing-loop requirements.

func (*Request) AlertError

func (rq *Request) AlertError(err error)

AlertError queues err via Jaws.Log and, if it is non-nil, also shows it to the current request as a danger-level Request.Alert.

func (*Request) Cancel added in v0.500.0

func (rq *Request) Cancel(err error)

Cancel aborts the Request.

It cancels the Request's context with the given cause (queued via Jaws.Log); the WebSocket processing loop and its goroutines observe the cancelled context and shut down asynchronously. Cancel returns immediately and does not wait for teardown or logging. It is safe to call synchronously from UI code, for example to terminate a connection that violates a server-side limit. A nil err cancels without a specific cause.

If a non-nil err wins the cancellation race, context.Cause on Request.Context matches ErrRequestCancelled and unwraps to err. See Request.Context for caller-owned parent-context causes.

Do not retain the Request for asynchronous cancellation; use Request.SetContext and retain the derived context's cancellation function instead.

func (*Request) Context

func (rq *Request) Context() (ctx context.Context)

Context returns the Request's context.

The context is derived from Jaws.BaseContext by default. Unlike the Request pointer, it may be retained by background work.

Cancellation originating from Jaws.BaseContext or a context installed by Request.SetContext retains that context's cause; JaWS does not wrap it with ErrRequestCancelled.

func (*Request) DeleteElement added in v0.300.0

func (rq *Request) DeleteElement(elem *Element)

DeleteElement removes elem from the Request element registry without queueing a browser operation.

Use Element.Remove to remove a managed DOM child and unregister it together. DeleteElement is intended for elements that were never successfully rendered, or whose DOM lifecycle is managed separately.

A nil elem is a no-op, matching Request.Tag, Request.TagExpanded and Request.TagsOf; passing the nil that Request.GetElementByJid returns for an unknown Jid is therefore safe.

func (*Request) DeleteElements added in v0.700.0

func (rq *Request) DeleteElements(elems []*Element)

DeleteElements removes all of elems from the Request element registry in a single pass, without queueing browser operations.

It is the batched form of Request.DeleteElement, for unregistering a whole subtree at once: one pass over the registry regardless of how many elements are dropped, rather than one pass per element. Nil elements and elements belonging to another Request are skipped, and repeated elements are tolerated.

func (*Request) Dirty added in v0.31.0

func (rq *Request) Dirty(dirtyTags ...any)

Dirty schedules updates through Jaws.Dirty.

The receiver does not scope tag matching to rq. See Jaws.Dirty for expansion, exact Element targeting, lifecycle, and batching behavior.

func (*Request) Get added in v0.11.0

func (rq *Request) Get(key string) any

Get is shorthand for Session.Get.

It returns the session value associated with key, or nil if no session is associated with the Request.

func (*Request) GetConnectFn added in v0.7.0

func (rq *Request) GetConnectFn() (fn ConnectFn)

GetConnectFn returns the currently set ConnectFn, or nil if none is set.

func (*Request) GetElementByJid added in v0.300.0

func (rq *Request) GetElementByJid(jid Jid) (elem *Element)

GetElementByJid returns the element with jid, or nil if it is not known.

func (*Request) GetElements added in v0.31.0

func (rq *Request) GetElements(tagValue any) (elems []*Element)

GetElements returns the Elements in rq associated with tagValue.

GetElements expands tagValue through Jaws.MustTagExpand. With a Jaws.Logger configured, it queues an expansion error and uses the partial result. Without a Logger, an expansion error causes GetElements to panic before the lookup.

Each Element registered under at least one resulting key is returned once. The returned slice is a caller-owned snapshot in unspecified order.

func (*Request) HasTag added in v0.31.0

func (rq *Request) HasTag(elem *Element, tagValue any) (yes bool)

HasTag reports whether elem has tagValue in rq.

HasTag is an advanced operation for inspecting one already-expanded tag key. It uses tagValue directly as a map key without expanding or validating it. Callers should normally pass a key returned by Request.TagsOf or tag.TagExpand. Invalid values may panic; in particular, a value that is not comparable at runtime panics.

Passing a tag.TagGetter tests that value itself, not the keys returned by tag.TagGetter.JawsGetTag. Use Request.GetElements when tagValue should be expanded.

func (*Request) HeadHTML

func (rq *Request) HeadHTML(w io.Writer) (err error)

HeadHTML writes the configured resources and Request key metadata for the page head.

HeadHTML does not modify response headers. Jaws.NewRequest sets "Cache-Control: no-store" when it creates the Request.

func (*Request) Initial added in v0.8.0

func (rq *Request) Initial() (r *http.Request)

Initial returns the Request's initial HTTP request, or nil.

func (*Request) JawsKeyString

func (rq *Request) JawsKeyString() string

JawsKeyString returns the request key in the text form used by JaWS URLs.

It tolerates a nil receiver for diagnostics only.

func (*Request) Log added in v0.300.0

func (rq *Request) Log(err error) error

Log reports an error through Jaws.Log and returns err.

A nil Request returns err without counting or delivery. See Jaws.Log for counting, delivery, and shutdown behavior.

func (*Request) MarkWritten added in v0.600.0

func (rq *Request) MarkWritten()

MarkWritten records initial-render activity for the Request.

The ui package's RequestWriter calls MarkWritten before each write. Call it before each initial HTML write made through another writer. Recorded activity affects timeout retirement before Request.ServeHTTP begins and which Request is retired when Jaws.MaxPendingRequestsPerIP is reached.

MarkWritten is safe to call concurrently. Calls never move recorded activity backward.

func (*Request) MustLog added in v0.300.0

func (rq *Request) MustLog(err error)

MustLog passes err to Jaws.MustLog.

A nil Request behaves like a nil Jaws.

func (*Request) NewElement added in v0.31.0

func (rq *Request) NewElement(ui UI) *Element

NewElement creates a new Element using the given UI object.

The UI value becomes scoped to rq and must not be used with another Request. Unless its concrete type documents support for multiple live Elements, it must not already back a live Element in rq. These ownership and multiplicity requirements are caller obligations; NewElement does not enforce them. See UI for the complete contract.

ui must be usable as a map key — comparable at runtime and equal to itself (see NewErrUnusableUI) — because the container widgets key their children by it. Those widgets validate their children and terminate the Request on an unusable one before it reaches a map, so NewElement does not re-validate on this hot path: debug builds panic on a runtime-incomparable ui as a development assertion, and a nil interface ui yields an Element that renders and updates as a no-op (see Element.JawsRender).

func (*Request) Redirect

func (rq *Request) Redirect(url string)

Redirect requests the current Request to navigate to the given URL.

The URL is validated to be a relative path or an http/https URL; script-bearing schemes such as javascript: and protocol-relative ("//host") URLs are refused and logged rather than sent to the browser.

See Request for pointer lifetime and Jaws.Broadcast for processing-loop requirements.

func (*Request) ServeHTTP

func (rq *Request) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler.

Requires Jaws.UseRequest to have been successfully called for the Request. The JaWS processing loop (Jaws.Serve or Jaws.ServeWithTimeout) must also be running so the request can subscribe to broadcasts and unsubscribe on exit.

Each inbound WebSocket message is limited to 32 KiB. The bundled client does not chunk Input, Set, Click, ContextMenu, or Remove messages; oversized messages close the connection. The limit covers the entire protocol payload after UTF-8 encoding, so no fixed application-value length is guaranteed. The resulting read-limit error is retained in the Request cancellation cause, which is passed to Jaws.Log.

Other WebSocket transport failures cancel the Request without a specific cause and are not reported through Jaws.Logger. When Jaws.Debug is true, their underlying error is retained in the Request cancellation cause, which is passed to Jaws.Log instead.

func (*Request) Session added in v0.14.0

func (rq *Request) Session() (sess *Session)

Session returns the Request's Session, or nil.

func (*Request) Set added in v0.11.0

func (rq *Request) Set(key string, value any)

Set is shorthand for Session.Set.

It associates value with key in the session; a nil value removes the key. It does nothing if no session is associated with the Request.

func (*Request) SetConnectFn added in v0.7.0

func (rq *Request) SetConnectFn(fn ConnectFn)

SetConnectFn sets the function called after the WebSocket is accepted.

A nil fn clears the callback.

See ConnectFn for the callback lifecycle and permitted operations.

func (*Request) SetContext added in v0.110.0

func (rq *Request) SetContext(fn func(oldCtx context.Context) (newCtx context.Context))

SetContext atomically transforms the Request's context.

fn receives the current context and must return a non-nil context derived from it so cancellation and deadlines continue to propagate. Cancellation or deadline expiration of the returned context wakes a running Request.ServeHTTP loop promptly, even while it is idle; no WebSocket event or broadcast is required.

If the returned context is canceled first, its cause remains unchanged; see Request.Context.

fn runs while the Request lock is held. It must not call methods on the same Request, call code that may do so, or block on work that needs the same Request. If fn panics, SetContext releases the lock and propagates the panic.

If a custom context implements the optional method recognized by context.AfterFunc, that method and its returned stop function must return promptly and must not synchronously call this Request or its Jaws, or wait for work that does. Standard-library contexts need no special handling.

Background work that must cancel the Request should create a derived context in fn and retain that context's cancellation function, not the Request pointer.

Returning a nil context is a programming error: debug builds panic and production builds report it through Jaws.MustLog and retain the current context.

func (*Request) String

func (rq *Request) String() string

String returns the Request in the form "Request<key>", using Request.JawsKeyString to encode the key. Like JawsKeyString it tolerates a nil receiver for diagnostics only; see the Request type documentation.

func (*Request) Tag added in v0.31.0

func (rq *Request) Tag(elem *Element, tagItems ...any)

Tag expands and registers tagItems with elem.

Registration is additive and does not schedule an update. Calling Tag during rendering or updating is supported, but known dependencies should normally be registered during initial rendering. Associations remain active until elem is removed or rq ends; individual associations cannot be removed.

See package github.com/linkdata/jaws/lib/tag for choosing stable dependency identities and for the registration and targeting model.

Tag expands tagItems through Jaws.MustTagExpand. With a Jaws.Logger configured, it queues an expansion error and registers the partial result. Without a Logger, an expansion error causes Tag to panic before registering anything. Use Request.TagExpanded to register keys you expanded yourself.

Tag does not expand tagItems when elem is nil or foreign, or when tagItems is empty. For a deleted elem, it still expands non-empty tagItems but registers nothing.

func (*Request) TagExpanded added in v0.300.0

func (rq *Request) TagExpanded(elem *Element, expandedTags []any)

TagExpanded registers already-expanded keys with elem.

TagExpanded is an advanced, additive API that neither expands nor validates keys and does not schedule an update. Callers should normally use Request.Tag. expandedTags must contain only keys that tag.TagExpand can emit, either from a successful expansion or from a partial result whose error the caller handled. Passing other values may panic or create registrations unreachable through the expanding lookup, dirtying, and broadcast APIs.

It is a no-op for a nil, deleted, or foreign Element.

func (*Request) TagsOf added in v0.31.0

func (rq *Request) TagsOf(elem *Element) (tags []any)

TagsOf returns a snapshot of the exact keys registered for elem in rq.

It returns nil if elem is nil or has no registered keys. The returned slice is caller-owned and has unspecified order.

func (*Request) TailHTML added in v0.79.0

func (rq *Request) TailHTML(w io.Writer) (err error)

TailHTML writes optional HTML code at the end of the page's BODY section that will immediately apply HTML attribute and class updates made during initial rendering, which minimizes flicker without having to write the correct value in templates or during Renderer.JawsRender.

It also adds a <noscript> tag that warns of reduced functionality.

type Session added in v0.11.0

type Session struct {
	// contains filtered or unexported fields
}

Session stores server-side per-user state shared by one or more requests.

A Session is bound to the remote IP that created it. Its exported methods are safe to call on a nil *Session; methods with results return the result type's zero value, and the others do nothing.

func (*Session) Broadcast added in v0.26.0

func (sess *Session) Broadcast(msg wire.Message)

Broadcast attempts to send a message to all active Request values using this session.

It must not be called before the JaWS processing loop (Jaws.Serve or Jaws.ServeWithTimeout) is running. Otherwise this call may block.

func (*Session) Clear added in v0.16.0

func (sess *Session) Clear()

Clear removes all key/value pairs from the session.

func (*Session) Close added in v0.17.0

func (sess *Session) Close() (cookie *http.Cookie)

Close invalidates and expires the Session.

Future Request values won't be able to associate with it, and Session.Cookie will return a deletion cookie.

Existing Request values already associated with the Session will ask the browser to reload the pages. This holds even for a Request whose WebSocket has not connected yet: the reload is queued on the Request and delivered when it connects. Key/value pairs in the Session are left unmodified; use Session.Clear to remove all of them.

It must not be called before the JaWS processing loop (Jaws.Serve or Jaws.ServeWithTimeout) is running, because the wake-up broadcasts may block.

Close returns a non-nil deletion cookie for a non-nil Session.

func (*Session) Cookie added in v0.11.0

func (sess *Session) Cookie() (cookie *http.Cookie)

Cookie returns a cookie for the Session. Returns a delete cookie if the Session is expired.

func (*Session) CookieValue added in v0.11.0

func (sess *Session) CookieValue() (s string)

CookieValue returns the session cookie value.

func (*Session) Get added in v0.11.0

func (sess *Session) Get(key string) (value any)

Get returns the value associated with the key, or nil.

func (*Session) ID added in v0.11.0

func (sess *Session) ID() (id uint64)

ID returns the session ID, a 64-bit random value.

func (*Session) IP added in v0.11.0

func (sess *Session) IP() (ip netip.Addr)

IP returns the remote IP the session is bound to, or the zero netip.Addr if unset.

func (*Session) Jaws added in v0.81.0

func (sess *Session) Jaws() (jw *Jaws)

Jaws returns the Jaws instance of the Session, or nil.

func (*Session) Reload added in v0.17.0

func (sess *Session) Reload()

Reload calls Session.Broadcast with a message asking browsers to reload the page. See Session.Broadcast for the processing-loop requirement.

func (*Session) Requests added in v0.37.0

func (sess *Session) Requests() (requests []*Request)

Requests returns a list of the Request values using this Session.

The returned slice is a snapshot. Its Request pointers are not pinned and may become stale immediately; see Request.

func (*Session) Set added in v0.11.0

func (sess *Session) Set(key string, value any)

Set associates value with key.

A nil value removes key.

type SetupFunc added in v0.111.6

type SetupFunc = func(jw *Jaws, handleFn HandleFunc, prefix string) (urls []*url.URL, err error)

SetupFunc is called by Jaws.Setup and allows setting up addons for JaWS.

When Jaws.Setup is called with a nil HandleFunc, setup functions receive a no-op handler registration function.

The URLs returned will be used in a call to Jaws.GenerateHeadHTML.

type TemplateLookuper added in v0.45.0

type TemplateLookuper interface {
	Lookup(name string) *template.Template
}

TemplateLookuper resolves a name to a *template.Template.

type UI added in v0.31.0

type UI interface {
	Renderer
	Updater
}

UI defines the required methods on JaWS UI objects.

A UI value is request-scoped. Once it has been used to create an Element for one Request, it must not be used to create an Element for another Request. Construct a fresh UI value for each Request.

Within its owning Request, a UI value must back at most one live Element unless its concrete type documents support for multiple live Elements. Such a type must not retain state on the shared UI value that can differ between those Elements — SetElementState gives it somewhere else to keep such state, keyed to the Element rather than to the widget, but opting in remains the concrete type's decision to document. To render the same application state more than once, construct distinct UI values that share getters, setters, handlers or tags. An Element stops being live when it is deleted or its owning Request lifecycle ends.

Application state referenced by UI values may be shared across Requests when synchronized as required.

In addition, all UI objects must be comparable and equal to themselves, so they can be used as map keys. The compile-time type must be comparable; the container widgets additionally check each child value at runtime and cancel the Request, in every build, when it is a nil interface, not comparable at runtime (for example a comparable struct holding a func in an interface field), or not equal to itself (a value holding NaN), with a cause matching github.com/linkdata/jaws/lib/tag.ErrNotUsableAsTag under errors.Is. That is the only place a raw UI value is used as a map key; outside a container Request.NewElement asserts runtime comparability in debug builds. Callers must ensure UI values are genuinely comparable and reflexive.

A typed nil (a non-nil interface holding a nil pointer) meets those key requirements, being comparable and equal to itself, so JaWS accepts one and dispatches render, update and event calls to it like any other value; only a nil UI interface is treated as a no-op. Surviving those calls with a nil receiver is a property of the concrete type rather than a requirement of this contract: a type may document that it tolerates one, and a type that does not will panic when dereferencing its fields. Passing a nil pointer of such a type is therefore a caller error, not a framework-handled case.

type Updater added in v0.60.0

type Updater interface {
	// JawsUpdate is called for an [Element] that has been marked dirty to update its HTML.
	// Do not call this yourself unless it is from within another JawsUpdate implementation.
	// The engine does not invoke this once the [Element] is deleted (see [Element.Deleted]).
	// A UI implementation that delegates rendering and updating must delegate both calls
	// to the same UI widget. Rendering elem through one widget and updating it through
	// another is unsupported.
	JawsUpdate(elem *Element)
}

Updater updates browser-side DOM for a dirty Element.

Directories

Path Synopsis
Package examples contains compile-checked examples for JaWS applications.
Package examples contains compile-checked examples for JaWS applications.
minesweeper command
Package main implements the JaWS Minesweeper demo.
Package main implements the JaWS Minesweeper demo.
Package jawsboot provides embedded Bootstrap assets for JaWS applications.
Package jawsboot provides embedded Bootstrap assets for JaWS applications.
Package jawstest provides a harness for driving a jaws.Request's WebSocket message-processing loop in tests.
Package jawstest provides a harness for driving a jaws.Request's WebSocket message-processing loop in tests.
lib
assets
Package assets provides the embedded JaWS client assets and page-resource helpers.
Package assets provides the embedded JaWS client assets and page-resource helpers.
bind
Package bind adapts Go values to JaWS getter, setter, HTML, tag, and event interfaces.
Package bind adapts Go values to JaWS getter, setter, HTML, tag, and event interfaces.
htmlio
Package htmlio writes the small HTML fragments used by standard JaWS widgets.
Package htmlio writes the small HTML fragments used by standard JaWS widgets.
jid
Package jid provides request-scoped JaWS element identifiers and HTML-writing helpers.
Package jid provides request-scoped JaWS element identifiers and HTML-writing helpers.
key
Package key encodes and parses JaWS request and session keys.
Package key encodes and parses JaWS request and session keys.
named
Package named provides named boolean values and collections used by select, option, and radio widgets.
Package named provides named boolean values and collections used by select, option, and radio widgets.
tag
Package tag expands JaWS dependency tags into keys used to associate Elements with application state and logical signals.
Package tag expands JaWS dependency tags into keys used to associate Elements with application state and logical signals.
templatereloader
Package templatereloader provides a build-aware github.com/linkdata/jaws.TemplateLookuper.
Package templatereloader provides a build-aware github.com/linkdata/jaws.TemplateLookuper.
ui
Package ui contains the standard JaWS widgets and template helpers.
Package ui contains the standard JaWS widgets and template helpers.
what
Package what defines the commands and events used by the JaWS wire protocol.
Package what defines the commands and events used by the JaWS wire protocol.
wire
Package wire formats and parses the line-based JaWS WebSocket protocol.
Package wire formats and parses the line-based JaWS WebSocket protocol.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL