Documentation
¶
Overview ¶
Package livetemplate provides a library for building real-time, reactive web applications in Go with minimal code. It uses tree-based DOM diffing to send only what changed over WebSocket or HTTP, inspired by Phoenix LiveView.
Quick Start ¶
Define your application state as a Go struct with methods for each action:
type Counter struct {
Count int
}
func (c *Counter) Increment(ctx *livetemplate.ActionContext) error {
c.Count++
return nil
}
func (c *Counter) Decrement(ctx *livetemplate.ActionContext) error {
c.Count--
return nil
}
Actions are automatically dispatched to methods matching the action name (e.g., "increment" → Increment, "add_item" → AddItem).
Create a template with `lvt-on:{event}` attributes for event binding:
<!-- counter.tmpl -->
<h1>Counter: {{.Count}}</h1>
<button lvt-on:click="increment">+</button>
<button lvt-on:click="decrement">-</button>
Wire it up in your main function:
func main() {
counter := &Counter{Count: 0}
tmpl := livetemplate.New("counter")
http.Handle("/", tmpl.Handle(counter))
http.ListenAndServe(":8080", nil)
}
How It Works ¶
LiveTemplate separates static and dynamic content in templates:
- Static content (HTML structure, unchanging text) is sent once and cached client-side
- Dynamic content (data values) is sent on every update as a minimal tree diff
- This achieves 50-90% bandwidth reduction compared to sending full HTML
The client library (TypeScript) handles WebSocket communication, event delegation, and applying DOM updates efficiently.
Tree-Based Updates ¶
Templates are parsed into a tree structure that separates statics and dynamics:
{
"s": ["<div>Count: ", "</div>"], // Statics (cached)
"0": "42" // Dynamic value
}
Subsequent updates only send changed dynamic values:
{
"0": "43" // Only the changed value
}
Key Types ¶
- Template: Manages template parsing, execution, and update generation
- Store: Interface for application state and action handlers
- ActionContext: Provides action data and utilities in Change() method
- ActionData: Type-safe data extraction and validation
- Broadcaster: Share state updates across all connected clients
- SessionStore: Per-session state management
Advanced Features ¶
- Multi-store pattern: Namespace multiple stores in one template
- Broadcasting: Real-time updates to all connected clients
- Server-side validation: Automatic error handling with go-playground/validator
- Form lifecycle events: Client-side hooks for pending, success, error, done
- Focus preservation: Maintains input focus and scroll position during updates
For complete documentation, see https://github.com/livetemplate/livetemplate
Index ¶
- Constants
- Variables
- func AssertPureState[T any](t *testing.T)
- func DispatchWithState(controller interface{}, state interface{}, ctx *Context) (interface{}, error)
- func HasActionMethod(controller interface{}, state interface{}, action string) bool
- func WSCloseStatusText(code int) string
- func WSFormatCloseMessage(closeCode int, text string) []byte
- func WSIsUnexpectedCloseError(err error, expectedCodes ...int) bool
- func WSIsUpgrade(r *http.Request) bool
- type ActionData
- func (a *ActionData) Bind(v interface{}) error
- func (a *ActionData) BindAndValidate(v interface{}, validate *validator.Validate) error
- func (a *ActionData) Get(key string) interface{}
- func (a *ActionData) GetBool(key string) bool
- func (a *ActionData) GetBoolOk(key string) (bool, bool)
- func (a *ActionData) GetFloat(key string) float64
- func (a *ActionData) GetFloatOk(key string) (float64, bool)
- func (a *ActionData) GetInt(key string) int
- func (a *ActionData) GetIntOk(key string) (int, bool)
- func (a *ActionData) GetString(key string) string
- func (a *ActionData) GetStringOk(key string) (string, bool)
- func (a *ActionData) Has(key string) bool
- func (a *ActionData) Raw() map[string]interface{}
- type AnonymousAuthenticator
- type Authenticator
- type BasicAuthenticator
- type ChallengeAuthenticator
- type Config
- type Context
- func (c *Context) Action() string
- func (c *Context) Bind(v interface{}) error
- func (c *Context) BindAndValidate(v interface{}, validate *validator.Validate) error
- func (c *Context) BroadcastAction(action string, data map[string]interface{})
- func (c *Context) DeleteCookie(name string) error
- func (c *Context) Get(key string) interface{}
- func (c *Context) GetBool(key string) bool
- func (c *Context) GetCompletedUploads(name string) []*uploadtypes.UploadEntry
- func (c *Context) GetCookie(name string) (*http.Cookie, error)
- func (c *Context) GetFloat(key string) float64
- func (c *Context) GetInt(key string) int
- func (c *Context) GetString(key string) string
- func (c *Context) Has(key string) bool
- func (c *Context) HasUploads(name string) bool
- func (c *Context) IsHTTP() bool
- func (c *Context) Redirect(url string, code int) error
- func (c *Context) Session() Session
- func (c *Context) SetCookie(cookie *http.Cookie) error
- func (c *Context) SetFlash(key, message string)
- func (c *Context) UserID() string
- func (c *Context) ValidateForm() error
- func (c *Context) WithAction(action string) *Context
- func (c *Context) WithData(data map[string]interface{}) *Context
- func (c *Context) WithFlashSetter(setter FlashSetter) *Context
- func (c *Context) WithFormSchema(schema *FormSchema) *Context
- func (c *Context) WithHTTP(w http.ResponseWriter, r *http.Request) *Context
- func (c *Context) WithSession(session Session) *Context
- func (c *Context) WithUploads(uploads UploadAccessor) *Context
- func (c *Context) WithUserID(userID string) *Context
- type DispatchError
- type EnvConfig
- type FieldError
- type FlashSetter
- type FormRule
- type FormSchema
- type GorillaOption
- type GorillaUpgrader
- type HandleOption
- type HealthChecker
- type HealthHandler
- type HealthResponse
- type HealthStatus
- type LiveHandler
- type MemorySessionStore
- func (s *MemorySessionStore) Close()
- func (s *MemorySessionStore) Delete(ctx context.Context, groupID string)
- func (s *MemorySessionStore) Get(ctx context.Context, groupID string) interface{}
- func (s *MemorySessionStore) List(ctx context.Context) []string
- func (s *MemorySessionStore) Set(ctx context.Context, groupID string, state interface{})
- type MultiError
- type Option
- func WithAllowedOrigins(origins []string) Option
- func WithAuthenticator(auth Authenticator) Option
- func WithComponentTemplates(sets ...*TemplateSet) Option
- func WithCookieMaxAge(maxAge time.Duration) Option
- func WithDevMode(enabled bool) Option
- func WithDispatchBufferSize(size int) Option
- func WithIgnoreTemplateDirs(dirs ...string) Option
- func WithLoadingDisabled() Option
- func WithMaxConnections(max int64) Option
- func WithMaxConnectionsPerGroup(max int64) Option
- func WithMessageRateLimit(messagesPerSecond float64, burstCapacity int) Option
- func WithParseFiles(files ...string) Option
- func WithPermissiveOriginCheck() Option
- func WithProgressiveEnhancement(enabled bool) Option
- func WithPubSubBroadcaster(broadcaster pubsub.Broadcaster) Option
- func WithSessionStore(store SessionStore) Option
- func WithTemplateBaseDir(dir string) Option
- func WithTrustForwardedHeaders(trust bool) Option
- func WithUpgrader(upgrader WSUpgrader) Option
- func WithUpload(name string, config uploadtypes.UploadConfig) Option
- func WithWebSocketBufferSize(size int) Option
- func WithWebSocketCompression() Option
- func WithWebSocketDisabled() Option
- type Presigner
- type RedisHealthChecker
- type RedisSessionStore
- func (s *RedisSessionStore) Close() error
- func (s *RedisSessionStore) Delete(ctx context.Context, groupID string)
- func (s *RedisSessionStore) Get(ctx context.Context, groupID string) interface{}
- func (s *RedisSessionStore) List(ctx context.Context) []string
- func (s *RedisSessionStore) Ping() error
- func (s *RedisSessionStore) PingContext(ctx context.Context) error
- func (s *RedisSessionStore) Set(ctx context.Context, groupID string, state interface{})
- type RedisSessionStoreOption
- type ResponseMetadata
- type Session
- type SessionStore
- type SessionStoreHealthChecker
- type SessionStoreOption
- type State
- type Template
- func (t *Template) Clone() (*Template, error)
- func (t *Template) Execute(wr io.Writer, data interface{}, messages ...map[string]string) error
- func (t *Template) ExecuteUpdates(wr io.Writer, data interface{}, messages ...map[string]string) error
- func (t *Template) Funcs(funcMap template.FuncMap) *Template
- func (t *Template) Handle(controller interface{}, state State, opts ...HandleOption) LiveHandler
- func (t *Template) Parse(text string) (*Template, error)
- func (t *Template) ParseFiles(filenames ...string) (*Template, error)
- func (t *Template) ParseGlob(pattern string) (*Template, error)
- func (t *Template) SetUploadRegistry(registry interface{})
- type TemplateSet
- type UpdateResponse
- type UploadAccessor
- type UploadConfig
- type UploadEntry
- type UploadMeta
- type WSCloseError
- type WSConn
- type WSUpgrader
Examples ¶
Constants ¶
const ( WSTextMessage = 1 WSBinaryMessage = 2 WSCloseMessage = 8 WSPingMessage = 9 WSPongMessage = 10 )
WebSocket message types (RFC 6455, Section 11.8)
const ( WSCloseNormalClosure = 1000 WSCloseGoingAway = 1001 WSCloseProtocolError = 1002 WSCloseAbnormalClosure = 1006 WSCloseServiceRestart = 1012 )
WebSocket close codes (RFC 6455, Section 7.4)
const CapabilityChange = "change"
CapabilityChange is the capability name for controllers with a Change() method.
const MaxBroadcastsPerAction = 100
BroadcastAction queues a broadcast to all other connections in the same session group. The named action is dispatched on each receiving connection after the current action completes successfully.
Each receiving connection runs the named action with its own per-connection state via DispatchWithState, preserving per-connection fields (e.g., CurrentUser).
Broadcasts are deferred: they execute only after the triggering action returns without error. If the action returns an error, queued broadcasts are discarded.
Constraints:
- Dispatched actions run with context.Background() — middleware-injected request values (auth tokens, tracing spans) are not available.
- BroadcastAction calls inside a dispatched action are ignored to prevent infinite broadcast storms.
- Context.With*() methods create shallow copies. Broadcasts queued after the copy diverge (append allocates a new backing array once capacity is exceeded).
Example:
func (c *ChatController) Send(state ChatState, ctx *livetemplate.Context) (ChatState, error) {
c.mu.Lock()
c.messages = append(c.messages, msg)
c.mu.Unlock()
state.Messages = c.copyMessages()
ctx.BroadcastAction("RefreshMessages", nil)
return state, nil
}
MaxBroadcastsPerAction is the maximum number of BroadcastAction calls allowed per action invocation. Excess calls are dropped with an error log.
Variables ¶
var ( // ErrNoHTTPContext is returned when HTTP methods (SetCookie, Redirect, etc.) // are called from a WebSocket action. These methods require an HTTP response // writer which is not available in WebSocket contexts. // // To set cookies or redirect, use HTTP POST forms instead of WebSocket actions. // This is consistent with security best practices - session cookies should be // HttpOnly and can only be set via HTTP responses, not JavaScript/WebSocket. ErrNoHTTPContext = errors.New("HTTP methods require HTTP context (not available in WebSocket actions)") // ErrInvalidRedirectCode is returned when Redirect is called with a non-3xx status code. ErrInvalidRedirectCode = errors.New("invalid redirect status code (must be 3xx)") // ErrInvalidRedirectURL is returned when Redirect is called with a potentially // unsafe URL that could lead to open redirect vulnerabilities. ErrInvalidRedirectURL = errors.New("invalid redirect URL (must be relative path starting with /)") )
HTTP context errors for ActionContext methods
var ErrMethodNotFound = errors.New("method not found for action")
ErrMethodNotFound is returned when Dispatch cannot find a method matching the action.
Functions ¶
func AssertPureState ¶ added in v0.7.0
AssertPureState validates that a state type contains only serializable data. Use in tests to catch accidental dependency inclusion:
func TestMyState_IsPure(t *testing.T) {
AssertPureState[MyState](t)
}
func DispatchWithState ¶ added in v0.7.0
func DispatchWithState(controller interface{}, state interface{}, ctx *Context) (interface{}, error)
DispatchWithState routes an action to a controller method with new signature.
Method signature: func(state StateType, ctx *Context) (StateType, error)
Returns the modified state and any error from the method. The controller is a singleton that holds dependencies. State is passed by value and a new state is returned.
Example:
type CounterController struct { DB *sql.DB }
func (c *CounterController) Increment(state CounterState, ctx *Context) (CounterState, error) {
state.Count++
return state, nil
}
func HasActionMethod ¶ added in v0.8.6
HasActionMethod checks if a controller has a method that can handle the given action. Uses the same signature validation as DispatchWithState: func(state, *Context) (state, error). Automatically dereferences pointer state types to match the value type used by dispatch.
func WSCloseStatusText ¶ added in v0.8.5
WSCloseStatusText returns a text description for the close code.
func WSFormatCloseMessage ¶ added in v0.8.5
WSFormatCloseMessage builds a WebSocket close frame payload. Per RFC 6455 §5.5, the close reason must be <= 123 bytes (125 - 2 for code).
func WSIsUnexpectedCloseError ¶ added in v0.8.5
WSIsUnexpectedCloseError reports whether err is a WebSocket close error with a code not in the list of expected codes.
Types ¶
type ActionData ¶
type ActionData struct {
// contains filtered or unexported fields
}
ActionData wraps action data with utilities for binding and validation
func NewActionData ¶ added in v0.5.0
func NewActionData(data map[string]interface{}) *ActionData
NewActionData creates ActionData from a map This is the public version for use by external packages like livepage
func (*ActionData) Bind ¶
func (a *ActionData) Bind(v interface{}) error
Bind unmarshals the data into a struct
func (*ActionData) BindAndValidate ¶
func (a *ActionData) BindAndValidate(v interface{}, validate *validator.Validate) error
BindAndValidate binds data to struct and validates it in one step
func (*ActionData) Get ¶
func (a *ActionData) Get(key string) interface{}
Get returns the raw value for a key
func (*ActionData) GetBool ¶
func (a *ActionData) GetBool(key string) bool
GetBool extracts a bool value. Returns false if key doesn't exist or value is not a bool.
DEPRECATED: Use GetBoolOk for explicit error handling to distinguish between missing keys, type errors, and actual false values. This method will be removed in v0.3.0.
func (*ActionData) GetBoolOk ¶ added in v0.1.3
func (a *ActionData) GetBoolOk(key string) (bool, bool)
GetBoolOk extracts a bool value with explicit success indicator. Returns (value, true) if key exists and value is a bool or boolean string. Returns (false, false) if key doesn't exist or value cannot be parsed as bool.
This method handles both boolean values and string values "true"/"false" from HTML form submissions (HTTP path uses strings, WebSocket uses booleans). String comparison is case-insensitive to handle variations like "True", "TRUE", etc.
func (*ActionData) GetFloat ¶
func (a *ActionData) GetFloat(key string) float64
GetFloat extracts a float64 value. Returns 0 if key doesn't exist or value is not a number.
DEPRECATED: Use GetFloatOk for explicit error handling to distinguish between missing keys, type errors, and actual zero values. This method will be removed in v0.3.0.
func (*ActionData) GetFloatOk ¶ added in v0.1.3
func (a *ActionData) GetFloatOk(key string) (float64, bool)
GetFloatOk extracts a float64 value with explicit success indicator. Returns (value, true) if key exists and value is a number or numeric string. Returns (0, false) if key doesn't exist or value cannot be parsed as float.
Accepts native Go numeric types (int, int32, int64, float32, float64, etc.) and numeric strings from form fields and data-* attributes. Native numeric support matters for Session.TriggerAction, where callers pass Go-native values rather than JSON-unmarshaled ones.
Precision note: float64 has a 53-bit mantissa, so integer values larger than 2^53 (int64 and uint64) lose precision during conversion. Callers needing exact large integer round-trips should use GetInt instead.
func (*ActionData) GetInt ¶
func (a *ActionData) GetInt(key string) int
GetInt extracts an int value (JSON numbers are float64). Returns 0 if key doesn't exist or value is not a number.
DEPRECATED: Use GetIntOk for explicit error handling to distinguish between missing keys, type errors, and actual zero values. This method will be removed in v0.3.0.
func (*ActionData) GetIntOk ¶ added in v0.1.3
func (a *ActionData) GetIntOk(key string) (int, bool)
GetIntOk extracts an int value with explicit success indicator. Returns (value, true) if key exists and value is a number or numeric string. Returns (0, false) if key doesn't exist or value cannot be parsed as int.
Accepts native Go numeric types (int, int32, int64, float32, float64, etc.) and numeric strings from form fields and data-* attributes. Native numeric support matters for Session.TriggerAction, where callers pass Go-native values rather than JSON-unmarshaled ones.
Overflow and bounds safety:
- Unsigned integers that exceed math.MaxInt on the current platform return (0, false) rather than silently wrapping to a negative int. This matters on 64-bit platforms for values in (math.MaxInt64, math.MaxUint64] and on 32-bit platforms for values in (math.MaxInt32, math.MaxUint32].
- int64 values outside [math.MinInt, math.MaxInt] return (0, false); this only matters on 32-bit platforms.
- float values that are NaN, infinite, out of the int range, or non-integer (e.g. 1.7) return (0, false). Silently truncating a float to an int would hide caller mistakes when a map entry meant for a string field or a float-typed field gets routed to GetInt.
func (*ActionData) GetString ¶
func (a *ActionData) GetString(key string) string
GetString extracts a string value. Returns empty string if key doesn't exist or value is not a string.
DEPRECATED: Use GetStringOk for explicit error handling to distinguish between missing keys, type errors, and actual empty strings. This method will be removed in v0.3.0.
func (*ActionData) GetStringOk ¶ added in v0.1.3
func (a *ActionData) GetStringOk(key string) (string, bool)
GetStringOk extracts a string value with explicit success indicator. Returns (value, true) if key exists and value is a string or number. Returns ("", false) if key doesn't exist or value cannot be converted to string.
This method handles both string values and JSON numbers (float64), since the client-side parseValue() may convert numeric strings like "1" to numbers.
func (*ActionData) Raw ¶
func (a *ActionData) Raw() map[string]interface{}
Raw returns the underlying map for direct access
type AnonymousAuthenticator ¶
type AnonymousAuthenticator struct{}
AnonymousAuthenticator provides browser-based session grouping for anonymous users.
This is the default authenticator and implements the most common use case: - All tabs in the same browser share data (same groupID) - Different browsers have independent data (different groupID) - No user authentication required (userID is always "")
The groupID is stored in a persistent cookie ("livetemplate-id") that survives browser restarts and lasts for 1 year. This provides seamless multi-tab experience without requiring user login.
Example behavior:
- User opens Tab 1 in Chrome → groupID = "anon-abc123"
- User opens Tab 2 in Chrome → groupID = "anon-abc123" (same cookie, shares state)
- User opens Tab 3 in Firefox → groupID = "anon-xyz789" (different browser, independent state)
func (*AnonymousAuthenticator) GetSessionGroup ¶
GetSessionGroup returns a browser-based session group ID.
If the "livetemplate-id" cookie exists, returns its value (persistent groupID). If no cookie exists, generates a new random groupID.
The cookie is set by the handler when a new groupID is generated, ensuring it persists across requests and browser restarts.
type Authenticator ¶
type Authenticator interface {
// Identify returns the user ID from the request.
// Returns "" for anonymous users.
// Returns error if authentication fails (e.g., invalid credentials).
Identify(r *http.Request) (userID string, err error)
// GetSessionGroup returns the session group ID for this user.
// Multiple requests with the same groupID share state.
//
// For anonymous users: typically returns a browser-based identifier.
// For authenticated users: typically returns userID.
//
// The groupID determines which Stores instance is used from SessionStore.
GetSessionGroup(r *http.Request, userID string) (groupID string, err error)
}
Authenticator identifies users and maps them to session groups.
Session groups are the fundamental concept for state sharing: all connections with the same groupID share the same Stores instance. Different groupIDs have completely isolated state.
The Authenticator is called for both HTTP and WebSocket requests to determine: 1. Who is the user? (userID) - can be "" for anonymous 2. Which session group should they join? (groupID)
For most applications, groupID = userID (simple 1:1 mapping), but advanced scenarios can implement custom mappings (e.g., collaborative workspaces where multiple users share one groupID).
type BasicAuthenticator ¶
type BasicAuthenticator struct {
// ValidateFunc is called to verify username/password credentials.
// Returns true if credentials are valid, false otherwise.
// Returns error for system failures (e.g., database connection error).
ValidateFunc func(username, password string) (bool, error)
// Realm is the protection space identifier in WWW-Authenticate headers.
// Default: "LiveTemplate"
Realm string
}
BasicAuthenticator provides username/password authentication.
This is a helper for integrating with existing authentication systems. It calls a user-provided validation function and maps authenticated users to session groups using a simple 1:1 mapping (groupID = userID).
Security Warnings ¶
HTTPS REQUIRED: BasicAuthenticator uses HTTP Basic Authentication, which sends credentials as base64-encoded strings. This is NOT encrypted and MUST only be used over HTTPS connections. Using HTTP Basic Auth over plain HTTP exposes credentials to network eavesdropping.
BRUTE FORCE PROTECTION: This implementation has no built-in rate limiting or account lockout. For production use, you MUST implement protection against brute force attacks through one or more of:
- Rate limiting middleware (e.g., golang.org/x/time/rate)
- Account lockout after N failed attempts
- External protection (e.g., fail2ban, CloudFlare)
- Web Application Firewall (WAF) rules
Example usage:
auth := livetemplate.NewBasicAuthenticator(func(username, password string) (bool, error) {
// Integrate with your authentication system
return db.ValidateUser(username, password)
})
tmpl := livetemplate.New("app", livetemplate.WithAuthenticator(auth))
For production use, consider implementing a custom Authenticator with: - JWT tokens - OAuth - Session cookies from existing auth middleware - Custom session group mapping logic - Built-in rate limiting and brute force protection
func NewBasicAuthenticator ¶
func NewBasicAuthenticator(validateFunc func(username, password string) (bool, error)) *BasicAuthenticator
NewBasicAuthenticator creates a BasicAuthenticator with the given validation function.
func (*BasicAuthenticator) GetSessionGroup ¶
GetSessionGroup returns userID as the session group ID (1:1 mapping).
Each authenticated user gets their own isolated session group. Multiple tabs for the same user share state. Different users have completely isolated state.
Example:
- User "alice" in Tab 1 → groupID = "alice"
- User "alice" in Tab 2 → groupID = "alice" (shares state with Tab 1)
- User "bob" in Tab 1 → groupID = "bob" (isolated from alice)
func (*BasicAuthenticator) Identify ¶
func (a *BasicAuthenticator) Identify(r *http.Request) (string, error)
Identify extracts and validates HTTP Basic Auth credentials.
Returns the username if credentials are valid. Returns error if: - No Authorization header present - Invalid Basic Auth format - Credentials validation fails - System error during validation
func (*BasicAuthenticator) WWWAuthenticate ¶ added in v0.8.8
func (a *BasicAuthenticator) WWWAuthenticate() string
WWWAuthenticate returns the WWW-Authenticate header value for HTTP Basic Auth.
type ChallengeAuthenticator ¶ added in v0.8.8
type ChallengeAuthenticator interface {
// WWWAuthenticate returns the value for the WWW-Authenticate response header.
// Example: `Basic realm="LiveTemplate"`
WWWAuthenticate() string
}
ChallengeAuthenticator is an optional interface for authenticators that require the browser to prompt for credentials (e.g., HTTP Basic Auth). When Identify returns an error, the handler checks if the authenticator implements this interface and sets the WWW-Authenticate header per RFC 7235.
type Config ¶
type Config struct {
Upgrader WSUpgrader
SessionStore SessionStore
Authenticator Authenticator // User authentication and session grouping
PubSubBroadcaster pubsub.Broadcaster // Optional: for distributed broadcasting across instances
AllowedOrigins []string // Allowed WebSocket origins (empty = allow all in dev, restrict in prod)
WebSocketDisabled bool
LoadingDisabled bool // Disables automatic loading indicator on page load
TemplateFiles []string // If set, overrides auto-discovery
TemplateBaseDir string // Base directory for template auto-discovery (default: directory of calling code via runtime.Caller)
IgnoreTemplateDirs []string // Additional directories to ignore during auto-discovery
DevMode bool // Development mode - use local client library instead of CDN
MaxConnections int64 // Maximum total connections (0 = unlimited)
MaxConnectionsPerGroup int64 // Maximum connections per group (0 = unlimited)
MessageRateLimit float64 // Messages per second per connection (0 = unlimited, default 10)
MessageRateBurst int // Burst capacity for rate limiting (default 20)
CookieMaxAge time.Duration // Session cookie max age (default: 1 year)
UploadConfigs map[string]uploadtypes.UploadConfig // Upload field configurations
WebSocketBufferSize int // WebSocket send buffer size per connection (default: 50)
ComponentTemplates []*TemplateSet // Component library templates (parsed before project templates)
ProgressiveEnhancement bool // Enable non-JS form submission support with PRG pattern (default: true)
TrustForwardedHeaders bool // Trust X-Forwarded-Proto header for scheme detection (default: true)
DispatchBufferSize int // Broadcast dispatch channel buffer per connection (default: 16)
}
Config holds template configuration options
type Context ¶ added in v0.7.0
Context provides unified context for all controller lifecycle methods. It embeds context.Context for cancellation, timeout, and request-scoped values.
Context replaces ActionContext with a single type used across: - Mount(state, ctx) - session initialization - OnConnect(state, ctx) - WebSocket connect - Action methods(state, ctx) - user interactions
func NewContext ¶ added in v0.7.0
NewContext creates a new Context for action handling.
func (*Context) Action ¶ added in v0.7.0
Action returns the action name that triggered this context.
func (*Context) BindAndValidate ¶ added in v0.7.0
BindAndValidate binds data to struct and validates it in one step. Uses the provided go-playground/validator instance for validation.
func (*Context) BroadcastAction ¶ added in v0.8.8
func (*Context) DeleteCookie ¶ added in v0.7.0
DeleteCookie removes an HTTP cookie by setting MaxAge to -1. Returns ErrNoHTTPContext if called from a WebSocket action.
func (*Context) GetCompletedUploads ¶ added in v0.7.0
func (c *Context) GetCompletedUploads(name string) []*uploadtypes.UploadEntry
GetCompletedUploads returns all completed upload entries for the given field name.
func (*Context) GetCookie ¶ added in v0.7.0
GetCookie retrieves an HTTP cookie from the request. Returns ErrNoHTTPContext if called from a WebSocket action.
func (*Context) HasUploads ¶ added in v0.7.0
HasUploads checks if there are any uploads for the given field name.
func (*Context) Redirect ¶ added in v0.7.0
Redirect sends an HTTP redirect response. Returns ErrNoHTTPContext if called from a WebSocket action. Returns ErrInvalidRedirectCode if code is not 3xx. Returns ErrInvalidRedirectURL if URL is not a valid relative path.
func (*Context) SetCookie ¶ added in v0.7.0
SetCookie sets an HTTP cookie on the response. Returns ErrNoHTTPContext if called from a WebSocket action.
func (*Context) SetFlash ¶ added in v0.7.7
SetFlash sets a flash message that will be available in templates via .lvt.Flash(key). Flash messages are page-level notifications (success, info, warning, error). Unlike field errors, flash messages don't affect ResponseMetadata.Success. Flash messages are cleared after each render, so they appear only once.
Common keys: "success", "error", "info", "warning"
Key conventions:
- Use simple, lowercase keys (e.g., "success", "error")
- Avoid keys containing colons or special characters
- Do not use keys starting with "_flash:" (reserved for internal use)
Example:
ctx.SetFlash("success", "Changes saved successfully!")
ctx.SetFlash("error", "Failed to process your request.")
func (*Context) ValidateForm ¶ added in v0.8.5
ValidateForm validates form data against HTML attributes inferred from the template. Uses validation rules extracted from HTML attributes like required, pattern, min, max, minlength, maxlength, and input type (email, url, number). Returns MultiError with field-level errors, or nil if all fields are valid.
Note: the schema must be set via WithFormSchema(ExtractFormSchema(statics)). If no schema is set, returns nil (no validation). For production validation with complex rules, use BindAndValidate() with go-playground/validator tags.
Known limitation: ExtractFormSchema merges all forms in a template into one schema. If your template has multiple forms, use BindAndValidate() instead.
func (*Context) WithAction ¶ added in v0.7.0
WithAction returns a new Context with the given action name.
func (*Context) WithFlashSetter ¶ added in v0.7.7
func (c *Context) WithFlashSetter(setter FlashSetter) *Context
WithFlashSetter returns a new Context with the given flash setter.
func (*Context) WithFormSchema ¶ added in v0.8.5
func (c *Context) WithFormSchema(schema *FormSchema) *Context
WithFormSchema returns a new Context with the given form validation schema.
func (*Context) WithHTTP ¶ added in v0.7.0
WithHTTP returns a new Context with HTTP request/response.
func (*Context) WithSession ¶ added in v0.7.0
WithSession returns a new Context with the given session.
func (*Context) WithUploads ¶ added in v0.7.0
func (c *Context) WithUploads(uploads UploadAccessor) *Context
WithUploads returns a new Context with the given upload accessor.
func (*Context) WithUserID ¶ added in v0.7.0
WithUserID returns a new Context with the given user ID.
type DispatchError ¶ added in v0.5.2
DispatchError provides context about a failed dispatch.
func (*DispatchError) Error ¶ added in v0.5.2
func (e *DispatchError) Error() string
func (*DispatchError) Unwrap ¶ added in v0.5.2
func (e *DispatchError) Unwrap() error
type EnvConfig ¶
type EnvConfig struct {
// MaxConnections is the maximum number of concurrent WebSocket connections.
// 0 means unlimited (default).
// Environment: LVT_MAX_CONNECTIONS
MaxConnections int64
// MaxConnectionsPerGroup is the maximum connections per session group.
// 0 means unlimited (default). Prevents single users from exhausting limits.
// Environment: LVT_MAX_CONNECTIONS_PER_GROUP
MaxConnectionsPerGroup int64
// AllowedOrigins is a comma-separated list of allowed WebSocket origins.
// Empty means allow all in dev mode, restrict in production.
// Environment: LVT_ALLOWED_ORIGINS
// Example: "https://example.com,https://app.example.com"
AllowedOrigins []string
// DevMode enables development mode features.
// - Uses local client library instead of CDN
// - More verbose logging
// Environment: LVT_DEV_MODE (true/false, 1/0)
DevMode bool
// WebSocketDisabled disables WebSocket connections (HTTP-only mode).
// Environment: LVT_WEBSOCKET_DISABLED (true/false, 1/0)
WebSocketDisabled bool
// LoadingDisabled disables the automatic loading indicator.
// Environment: LVT_LOADING_DISABLED (true/false, 1/0)
LoadingDisabled bool
// TemplateBaseDir is the base directory for template auto-discovery.
// Empty means use runtime.Caller detection (default).
// Environment: LVT_TEMPLATE_BASE_DIR
// Example: "/app/templates", "./templates", "."
TemplateBaseDir string
// ShutdownTimeout is the maximum duration to wait for graceful shutdown.
// Default: 30 seconds
// Environment: LVT_SHUTDOWN_TIMEOUT
// Example: "30s", "1m", "500ms"
// Note: Reserved for future use. Currently loaded and validated but not applied.
ShutdownTimeout time.Duration
// LogLevel sets the logging level (debug, info, warn, error).
// Default: "info"
// Environment: LVT_LOG_LEVEL
// Note: Reserved for future use. Currently loaded and validated but not applied.
LogLevel string
// MetricsEnabled enables Prometheus metrics export.
// Default: true
// Environment: LVT_METRICS_ENABLED (true/false, 1/0)
// Note: Reserved for future use. Currently loaded and validated but not applied.
MetricsEnabled bool
// ProgressiveEnhancement enables non-JS form submission support.
// When enabled (default: true), HTTP form submissions from non-JavaScript clients
// receive full HTML page responses using the POST-Redirect-GET pattern.
// Environment: LVT_PROGRESSIVE_ENHANCEMENT (true/false, 1/0)
ProgressiveEnhancement bool
// WebSocketBufferSize sets the send buffer size per WebSocket connection.
// Controls backpressure behavior: slow clients are disconnected when buffer is full.
// Environment: LVT_WS_BUFFER_SIZE (positive integer, default: 50)
WebSocketBufferSize int
// TrustForwardedHeaders controls whether X-Forwarded-Proto is trusted for
// scheme detection in same-origin WebSocket checks.
// Default: true (safe when behind a reverse proxy).
// Set to false if the server is directly reachable by clients without a proxy.
// Environment: LVT_TRUST_FORWARDED_HEADERS (true/false, 1/0)
TrustForwardedHeaders bool
}
EnvConfig holds environment-based configuration for LiveTemplate.
All configuration can be set via environment variables with the LVT_ prefix. This follows the 12-factor app methodology for configuration management.
func LoadEnvConfig ¶
LoadEnvConfig loads configuration from environment variables.
All environment variables are prefixed with LVT_ (LiveTemplate). Boolean values can be "true"/"false" or "1"/"0". Duration values use Go duration format (e.g., "30s", "1m").
Example:
export LVT_MAX_CONNECTIONS=10000 export LVT_ALLOWED_ORIGINS="https://example.com,https://app.example.com" export LVT_DEV_MODE=false export LVT_SHUTDOWN_TIMEOUT=30s config := livetemplate.LoadEnvConfig()
func (*EnvConfig) ToOptions ¶
ToOptions converts EnvConfig to a slice of Option functions.
This allows using environment-based configuration with the existing Option-based API.
Example:
envConfig, err := livetemplate.LoadEnvConfig()
if err != nil {
log.Fatal(err)
}
tmpl := livetemplate.New("app", envConfig.ToOptions()...)
type FieldError ¶
FieldError represents a validation error for a specific field
func NewFieldError ¶
func NewFieldError(field string, err error) FieldError
NewFieldError creates a field-specific error
func (FieldError) Error ¶
func (e FieldError) Error() string
type FlashSetter ¶ added in v0.7.7
type FlashSetter interface {
// contains filtered or unexported methods
}
FlashSetter allows setting flash messages from action handlers. Flash messages are page-level notifications (success, info, warning, error) that don't affect ResponseMetadata.Success (unlike field validation errors).
The setFlash method is intentionally unexported to ensure flash messages are only set through the Context.SetFlash() public API, maintaining consistent behavior and preventing direct message map manipulation.
type FormRule ¶ added in v0.8.5
type FormRule struct {
Field string
Required bool
InputType string // "email", "url", "number", "tel"
MinLength int // -1 if not set
MaxLength int // -1 if not set
Min float64
Max float64
HasMin bool
HasMax bool
Pattern string // raw pattern string
PatternRe *regexp.Regexp // pre-compiled pattern (nil if invalid or absent)
}
FormRule represents a validation rule inferred from HTML input attributes.
type FormSchema ¶ added in v0.8.5
type FormSchema struct {
Rules []FormRule
}
FormSchema holds validation rules inferred from template statics.
func ExtractFormSchema ¶ added in v0.8.5
func ExtractFormSchema(statics []string) *FormSchema
ExtractFormSchema scans template statics for HTML validation attributes on <input>, <textarea>, and <select> elements.
Known limitation: if a field's name attribute is a template expression (dynamic), it will be split across statics and may not be detected.
func (*FormSchema) Validate ¶ added in v0.8.5
func (s *FormSchema) Validate(data map[string]interface{}) error
Validate checks form data against the schema rules. Returns MultiError with field-level errors, or nil if valid.
type GorillaOption ¶ added in v0.8.5
GorillaOption configures the gorilla WebSocket upgrader.
func WithGorillaCheckOrigin ¶ added in v0.8.5
func WithGorillaCheckOrigin(fn func(*http.Request) bool) GorillaOption
WithGorillaCheckOrigin sets the origin check function for the gorilla upgrader.
func WithGorillaCompression ¶ added in v0.8.5
func WithGorillaCompression() GorillaOption
WithGorillaCompression enables permessage-deflate compression. Reduces bandwidth for larger payloads at the cost of CPU.
func WithGorillaReadBufferSize ¶ added in v0.8.5
func WithGorillaReadBufferSize(size int) GorillaOption
WithGorillaReadBufferSize sets the read buffer size for the gorilla upgrader.
func WithGorillaWriteBufferPool ¶ added in v0.8.5
func WithGorillaWriteBufferPool(pool websocket.BufferPool) GorillaOption
WithGorillaWriteBufferPool sets a custom write buffer pool. Pass nil to disable pooling (each connection allocates its own buffer).
func WithGorillaWriteBufferSize ¶ added in v0.8.5
func WithGorillaWriteBufferSize(size int) GorillaOption
WithGorillaWriteBufferSize sets the write buffer size for the gorilla upgrader.
type GorillaUpgrader ¶ added in v0.8.5
type GorillaUpgrader struct {
// contains filtered or unexported fields
}
GorillaUpgrader wraps gorilla/websocket.Upgrader as a WSUpgrader.
func NewGorillaUpgrader ¶ added in v0.8.5
func NewGorillaUpgrader(opts ...GorillaOption) *GorillaUpgrader
NewGorillaUpgrader creates a WSUpgrader backed by gorilla/websocket. Default buffer sizes are 1024 bytes (optimized for LiveTemplate's small payloads). Write buffers are pooled via sync.Pool to avoid per-connection allocation.
func (*GorillaUpgrader) Copy ¶ added in v0.8.5
func (g *GorillaUpgrader) Copy() *GorillaUpgrader
Copy creates a shallow copy of the upgrader to avoid mutating shared state.
func (*GorillaUpgrader) SetCheckOrigin ¶ added in v0.8.5
func (g *GorillaUpgrader) SetCheckOrigin(fn func(*http.Request) bool)
SetCheckOrigin sets the origin check function on the underlying gorilla upgrader.
func (*GorillaUpgrader) SetCompression ¶ added in v0.8.5
func (g *GorillaUpgrader) SetCompression(enabled bool)
SetCompression enables or disables permessage-deflate compression.
type HandleOption ¶ added in v0.7.0
type HandleOption func(*handleConfig)
HandleOption configures Handle behavior
func WithStore ¶ added in v0.7.0
func WithStore(store SessionStore) HandleOption
WithStore sets the session store for state persistence. Use this to configure Redis or other distributed stores.
type HealthChecker ¶
type HealthChecker interface {
// Check performs a health check and returns an error if unhealthy.
// The context may have a timeout, so checks should respect it.
Check(ctx context.Context) error
}
HealthChecker represents a component that can be health-checked.
Implementations should complete the check quickly (<100ms) to avoid blocking Kubernetes probes or load balancer health checks.
type HealthHandler ¶
type HealthHandler struct {
// contains filtered or unexported fields
}
HealthHandler provides HTTP endpoints for health checks.
Supports two types of health checks:
- Liveness (/health/live): Process is running and not deadlocked
- Readiness (/health/ready): Process can handle requests (dependencies healthy)
Kubernetes usage:
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
func NewHealthHandler ¶
func NewHealthHandler(timeout time.Duration) *HealthHandler
NewHealthHandler creates a new health check handler.
The timeout parameter specifies the maximum duration for all checks. If timeout is 0, a default of 5 seconds is used.
func (*HealthHandler) Live ¶
func (h *HealthHandler) Live(w http.ResponseWriter, r *http.Request)
Live handles the liveness probe endpoint.
Returns 200 OK if the process is alive and not deadlocked. This endpoint should always succeed unless the process is completely broken.
Example: GET /health/live Response: 200 OK with {"status":"healthy","timestamp":"2025-11-02T08:00:00Z"}
func (*HealthHandler) Ready ¶
func (h *HealthHandler) Ready(w http.ResponseWriter, r *http.Request)
Ready handles the readiness probe endpoint.
Returns 200 OK if all registered health checkers pass. Returns 503 Service Unavailable if any checker fails.
Kubernetes will not route traffic to pods that fail readiness checks.
Example: GET /health/ready Response: 200 OK with detailed check results
func (*HealthHandler) RegisterChecker ¶
func (h *HealthHandler) RegisterChecker(name string, checker HealthChecker)
RegisterChecker adds a health checker for a named component.
The name should be descriptive (e.g., "database", "redis", "session-store"). Thread-safe: can be called concurrently.
type HealthResponse ¶
type HealthResponse struct {
Status string `json:"status"` // "healthy" or "unhealthy"
Timestamp string `json:"timestamp"` // ISO 8601 timestamp
Checks []HealthStatus `json:"checks,omitempty"` // Individual component statuses
TotalTime string `json:"total_time,omitempty"` // Total check duration
}
HealthResponse represents the overall health check response.
type HealthStatus ¶
type HealthStatus struct {
Name string `json:"name"` // Component name
Status string `json:"status"` // "healthy" or "unhealthy"
Error string `json:"error,omitempty"` // Error message if unhealthy
}
HealthStatus represents the health status of a component.
type LiveHandler ¶
type LiveHandler interface {
http.Handler
// Shutdown gracefully shuts down the handler, draining connections.
//
// It performs the following steps:
// 1. Stops accepting new WebSocket connections
// 2. Sends close frames to all active WebSocket connections
// 3. Waits for in-flight requests to complete (respecting context timeout)
//
// The context timeout controls how long to wait for connections to close.
// After the timeout, remaining connections are forcefully closed.
//
// Example usage with http.Server:
// ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
// defer cancel()
// handler.Shutdown(ctx)
// server.Shutdown(ctx)
Shutdown(ctx context.Context) error
// MetricsHandler returns an http.Handler that exports Prometheus metrics.
//
// The handler responds to GET requests with metrics in Prometheus text format.
// Typically mounted at /metrics for scraping by Prometheus.
//
// Example with standard library http mux:
// mux := http.NewServeMux()
// handler := template.Handle(store)
// mux.Handle("/live", handler)
// mux.Handle("/metrics", handler.MetricsHandler())
// http.ListenAndServe(":8080", mux)
//
// Example with gorilla/mux:
// r := mux.NewRouter()
// handler := template.Handle(store)
// r.Handle("/live", handler)
// r.Handle("/metrics", handler.MetricsHandler())
MetricsHandler() http.Handler
}
LiveHandler is the interface returned by Template.Handle() It provides HTTP handling and lifecycle management for live template connections.
For server-initiated actions, implement an OnConnect(state, ctx) lifecycle method on your controller and call ctx.Session() to obtain a Session handle that can be used to trigger actions from background goroutines. See the Session interface above and docs/references/server-actions.md for details.
type MemorySessionStore ¶
type MemorySessionStore struct {
// contains filtered or unexported fields
}
MemorySessionStore is an in-memory session store with automatic cleanup.
Features: - Thread-safe for concurrent access - Tracks last access time for each group - Automatic cleanup of inactive groups (configurable TTL) - Suitable for single-instance deployments
For multi-instance deployments, use a persistent SessionStore (e.g., Redis).
func NewMemorySessionStore ¶
func NewMemorySessionStore(opts ...SessionStoreOption) *MemorySessionStore
NewMemorySessionStore creates a new in-memory session store with automatic cleanup.
Default configuration: - Cleanup TTL: 24 hours - Cleanup interval: 1 hour
The cleanup goroutine runs in the background and removes session groups that haven't been accessed within the TTL period. This prevents memory leaks from abandoned sessions.
Call Close() to stop the cleanup goroutine when shutting down.
func (*MemorySessionStore) Close ¶
func (s *MemorySessionStore) Close()
Close stops the cleanup goroutine. Should be called when shutting down the application.
func (*MemorySessionStore) Delete ¶
func (s *MemorySessionStore) Delete(ctx context.Context, groupID string)
Delete removes a session group and all its state. The context parameter is accepted for interface compliance but not used for in-memory operations.
func (*MemorySessionStore) Get ¶
func (s *MemorySessionStore) Get(ctx context.Context, groupID string) interface{}
Get retrieves the state for a session group. Updates the last access time for the group. The context parameter is accepted for interface compliance but not used for in-memory operations.
func (*MemorySessionStore) List ¶
func (s *MemorySessionStore) List(ctx context.Context) []string
List returns all active session group IDs. The context parameter is accepted for interface compliance but not used for in-memory operations.
func (*MemorySessionStore) Set ¶
func (s *MemorySessionStore) Set(ctx context.Context, groupID string, state interface{})
Set stores state for a session group. Updates the last access time for the group. The context parameter is accepted for interface compliance but not used for in-memory operations.
type MultiError ¶
type MultiError []FieldError
MultiError is a collection of field errors (implements error interface)
func ValidationToMultiError ¶
func ValidationToMultiError(err error) MultiError
ValidationToMultiError converts go-playground/validator errors to MultiError
func (MultiError) Error ¶
func (m MultiError) Error() string
type Option ¶
type Option func(*Config)
Option is a functional option for configuring a Template
func WithAllowedOrigins ¶
WithAllowedOrigins sets the allowed WebSocket origins for CORS protection.
When set, WebSocket upgrade requests will be validated against this list. Requests from origins not in the list will be rejected with 403 Forbidden.
If empty (default):
- Development: All origins allowed (permissive for local dev)
- Production: Consider setting explicitly for security
Example for production:
tmpl := livetemplate.New("app",
livetemplate.WithAllowedOrigins([]string{
"https://yourdomain.com",
"https://www.yourdomain.com",
}))
Security note: Always set this in production to prevent CSRF attacks via WebSocket.
When AllowedOrigins is not set, same-origin detection relies on X-Forwarded-Proto (if present and trusted) or r.TLS to determine the request scheme. By default, X-Forwarded-Proto is trusted (see WithTrustForwardedHeaders). If the server is directly reachable by clients without a proxy, either set WithTrustForwardedHeaders(false) to ignore forwarded headers, or use WithAllowedOrigins to explicitly list trusted origins.
func WithAuthenticator ¶
func WithAuthenticator(auth Authenticator) Option
WithAuthenticator sets a custom authenticator for user identification and session grouping.
The authenticator determines:
- Who is the user? (userID via Identify)
- Which session group should they join? (groupID via GetSessionGroup)
Default: AnonymousAuthenticator (browser-based session grouping)
Example with BasicAuthenticator:
auth := livetemplate.NewBasicAuthenticator(func(username, password string) (bool, error) {
return db.ValidateUser(username, password)
})
tmpl := livetemplate.New("app", livetemplate.WithAuthenticator(auth))
Example with custom JWT authenticator:
tmpl := livetemplate.New("app", livetemplate.WithAuthenticator(myJWTAuth))
func WithComponentTemplates ¶ added in v0.7.1
func WithComponentTemplates(sets ...*TemplateSet) Option
WithComponentTemplates registers component library templates to be parsed before project templates. This enables using pre-built UI components from the livetemplate/components library or custom component libraries.
Component templates are parsed first, then project templates are parsed on top, allowing project templates to override component templates with the same name.
Example:
import "github.com/livetemplate/lvt/components"
tmpl, err := livetemplate.New("app",
livetemplate.WithComponentTemplates(components.All()...),
)
Or with specific components:
import (
"github.com/livetemplate/lvt/components/dropdown"
"github.com/livetemplate/lvt/components/tabs"
)
tmpl, err := livetemplate.New("app",
livetemplate.WithComponentTemplates(
dropdown.Templates(),
tabs.Templates(),
),
)
Templates are parsed in the order provided. Official component templates use the naming convention "lvt:<category>:<name>:v<version>" (e.g., "lvt:dropdown:searchable:v1"). Third-party components may use their own prefix (e.g., "myorg:widget:default:v1").
func WithCookieMaxAge ¶ added in v0.1.3
WithCookieMaxAge sets the maximum age for session cookies.
The cookie is used to maintain anonymous user sessions across page reloads. Default: 365 days (1 year)
Example:
tmpl := livetemplate.New("app",
livetemplate.WithCookieMaxAge(30*24*time.Hour), // 30 days
)
func WithDevMode ¶
WithDevMode enables development mode - uses local client library instead of CDN
func WithDispatchBufferSize ¶ added in v0.8.8
WithDispatchBufferSize sets the buffer size for the broadcast dispatch channel per WebSocket connection. This is separate from the WebSocket send buffer (WithWebSocketBufferSize) because dispatch requests are less frequent. Default: 16. Increase for apps with high broadcast fan-out.
func WithIgnoreTemplateDirs ¶
WithIgnoreTemplateDirs adds directories to ignore during template auto-discovery. This is useful to skip directories containing generator templates or other non-runtime templates.
Example:
tmpl := livetemplate.New("app", livetemplate.WithIgnoreTemplateDirs("generators", "scaffolds"))
func WithLoadingDisabled ¶
func WithLoadingDisabled() Option
WithLoadingDisabled disables the automatic loading indicator shown during page initialization
func WithMaxConnections ¶
WithMaxConnections sets the maximum number of concurrent connections. 0 (default) means unlimited.
func WithMaxConnectionsPerGroup ¶
WithMaxConnectionsPerGroup sets the maximum number of connections per session group. 0 (default) means unlimited. Prevents single users from exhausting connection limits.
func WithMessageRateLimit ¶ added in v0.1.3
WithMessageRateLimit sets the rate limit for WebSocket messages per connection.
Uses token bucket algorithm: messagesPerSecond determines the rate, burstCapacity allows short bursts above the rate.
Default: 10 messages/sec with burst of 20. Set messagesPerSecond = 0 to disable rate limiting (not recommended for production).
Example:
tmpl := livetemplate.New("app",
livetemplate.WithMessageRateLimit(20, 50), // 20 msg/sec, burst of 50
)
func WithParseFiles ¶
WithParseFiles specifies template files to parse, overriding auto-discovery
func WithPermissiveOriginCheck ¶ added in v0.1.3
func WithPermissiveOriginCheck() Option
WithPermissiveOriginCheck disables origin checking for WebSocket connections.
WARNING: This allows connections from any origin and should ONLY be used in:
- Local development environments
- Testing scenarios
- Specific use cases where CSRF protection is handled externally
In production, use WithAllowedOrigins() instead to specify trusted origins.
Example:
// Development only - DO NOT use in production
tmpl := livetemplate.New("app",
livetemplate.WithDevMode(true),
livetemplate.WithPermissiveOriginCheck(),
)
func WithProgressiveEnhancement ¶ added in v0.8.1
WithProgressiveEnhancement enables or disables progressive enhancement support.
When enabled (default: true), HTTP form submissions from non-JavaScript clients receive full HTML page responses instead of JSON. This allows applications to work without JavaScript using standard HTML form submissions.
The feature uses the POST-Redirect-GET (PRG) pattern:
- Successful actions: 303 redirect to prevent duplicate submissions on refresh
- Validation errors: Re-render page with errors inline (no redirect)
Detection uses the Accept header: clients sending "application/json" receive JSON, while browsers sending "text/html" receive full HTML pages.
Example form structure for progressive enhancement:
<form method="POST">
<input type="text" name="title">
<button name="action" value="add" type="submit">Add</button>
</form>
Using an explicit action value avoids ambiguous POST parsing when other form fields submit empty strings.
The form works with both JavaScript (via WebSocket or fetch/JSON) and without JavaScript (via method="POST").
func WithPubSubBroadcaster ¶
func WithPubSubBroadcaster(broadcaster pubsub.Broadcaster) Option
WithPubSubBroadcaster enables distributed broadcasting across multiple application instances.
When set, Broadcast*, BroadcastToUsers, and BroadcastToGroup methods will publish messages to Redis Pub/Sub for distribution to all instances. Each instance subscribes to these messages and fans them out to its local connections.
This is essential for horizontal scaling - without it, broadcasts only reach connections on the same instance.
Example:
import (
"github.com/livetemplate/livetemplate"
"github.com/livetemplate/livetemplate/pubsub"
"github.com/redis/go-redis/v9"
)
redisClient := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
broadcaster := pubsub.NewRedisBroadcaster(redisClient)
tmpl := livetemplate.New("app",
livetemplate.WithPubSubBroadcaster(broadcaster),
)
func WithSessionStore ¶
func WithSessionStore(store SessionStore) Option
WithSessionStore sets a custom session store for HTTP requests
func WithTemplateBaseDir ¶ added in v0.2.1
WithTemplateBaseDir sets the base directory for template auto-discovery. This overrides the default runtime.Caller detection. Useful when running via 'go run' or when templates are in a non-standard location.
func WithTrustForwardedHeaders ¶ added in v0.8.5
WithTrustForwardedHeaders controls whether X-Forwarded-Proto is trusted for scheme detection in same-origin WebSocket checks.
Default: true (backward compatible). When true, the origin checker reads X-Forwarded-Proto to determine whether the original client connection used HTTP or HTTPS. This is safe when the server is behind a reverse proxy that sets/overwrites this header.
Set to false if the server is directly reachable by clients (no proxy) to prevent clients from forging the header. In this case, scheme detection falls back to r.TLS (non-nil = HTTPS, nil = HTTP).
This option only affects the default same-origin check. It has no effect when WithAllowedOrigins is set (explicit origins take priority).
func WithUpgrader ¶
func WithUpgrader(upgrader WSUpgrader) Option
WithUpgrader sets a custom WebSocket upgrader.
func WithUpload ¶ added in v0.3.1
func WithUpload(name string, config uploadtypes.UploadConfig) Option
WithUpload configures file upload support for a specific form field.
Upload configuration specifies validation rules, size limits, and storage options. Once configured, uploads are accessible via Context during action handling.
Example:
tmpl := livetemplate.New("profile",
livetemplate.WithUpload("avatar", livetemplate.UploadConfig{
Accept: []string{"image/png", "image/jpeg"},
MaxFileSize: 5 << 20, // 5 MB
MaxFiles: 1,
}),
)
In your controller's action method, access uploads via Context:
func (c *ProfileController) SaveProfile(state ProfileState, ctx *livetemplate.Context) (ProfileState, error) {
if ctx.HasUploads("avatar") {
for _, entry := range ctx.GetCompletedUploads("avatar") {
state.AvatarURL = moveToStorage(entry.TempPath)
}
}
return state, nil
}
func WithWebSocketBufferSize ¶ added in v0.4.0
WithWebSocketBufferSize sets the send buffer size per WebSocket connection.
The buffer queues messages for async delivery. Larger buffers handle burst traffic better but use more memory. Smaller buffers use less memory but may close slow clients more aggressively.
Default: 50 messages per connection
- Memory per connection: ~50KB (assuming 1KB avg message size)
- Memory for 100 connections: ~5MB
Recommended values:
- Low traffic / memory constrained: 10-25
- Normal traffic: 50 (default)
- High traffic / burst heavy: 100-1000
Environment variable override: LVT_WS_BUFFER_SIZE
Example:
// High-throughput application
tmpl := livetemplate.New("app", livetemplate.WithWebSocketBufferSize(100))
// Memory-constrained environment
tmpl := livetemplate.New("app", livetemplate.WithWebSocketBufferSize(10))
func WithWebSocketCompression ¶ added in v0.8.5
func WithWebSocketCompression() Option
WithWebSocketCompression enables permessage-deflate WebSocket compression. Reduces bandwidth for larger payloads at the cost of CPU. Only effective when using the default GorillaUpgrader.
func WithWebSocketDisabled ¶
func WithWebSocketDisabled() Option
WithWebSocketDisabled disables WebSocket support, forcing HTTP-only mode
type Presigner ¶ added in v0.3.1
type Presigner = uploadtypes.Presigner
Presigner generates presigned upload URLs for external storage (S3, GCS, etc). This enables direct client-to-storage uploads, bypassing the server.
Implementations should return an error if presigning fails due to:
- Invalid or expired credentials
- Network connectivity issues
- Storage service errors
- Invalid upload configuration
Example ¶
ExamplePresigner demonstrates implementing a custom presigner.
type CustomPresigner struct {
endpoint string
apiKey string
}
presign := func(p *CustomPresigner, entry *UploadEntry) (UploadMeta, error) {
return UploadMeta{
Uploader: "custom",
URL: p.endpoint + "/upload/" + entry.ID,
Headers: map[string]string{
"Authorization": "Bearer " + p.apiKey,
"Content-Type": entry.ClientType,
},
}, nil
}
presigner := &CustomPresigner{
endpoint: "https://storage.example.com",
apiKey: "secret-key",
}
entry := &UploadEntry{
ID: "entry-123",
ClientType: "image/jpeg",
}
meta, _ := presign(presigner, entry)
_ = meta.URL // https://storage.example.com/upload/entry-123
type RedisHealthChecker ¶
type RedisHealthChecker struct {
// contains filtered or unexported fields
}
RedisHealthChecker checks the health of a Redis connection.
func NewRedisHealthChecker ¶
func NewRedisHealthChecker(store *RedisSessionStore) *RedisHealthChecker
NewRedisHealthChecker creates a health checker for Redis.
type RedisSessionStore ¶
type RedisSessionStore struct {
// contains filtered or unexported fields
}
RedisSessionStore implements SessionStore using Redis for distributed session management.
Features: - Thread-safe for concurrent access - Automatic TTL refresh on access - Connection retry with exponential backoff - Hash-based storage with JSON serialization (v2 schema) - Automatic migration from legacy Gob-encoded blob format (v1) - Suitable for multi-instance deployments
Redis Key Schema (v2):
- livetemplate:session:{groupID} -> Redis HASH
- "_meta" field: JSON metadata (version, updated_at)
- "{storeName}" fields: JSON-encoded individual stores
- TTL: 24 hours (configurable)
The v2 schema uses Redis HASH to enable granular updates via HSET, which is more efficient than re-writing the entire session blob.
func NewRedisSessionStore ¶
func NewRedisSessionStore(client redis.UniversalClient, opts ...RedisSessionStoreOption) *RedisSessionStore
NewRedisSessionStore creates a new Redis-backed session store.
The client parameter can be:
- redis.Client for single-node Redis
- redis.ClusterClient for Redis Cluster
- redis.Ring for Redis Ring (sharding)
- redis.FailoverClient for Redis Sentinel
Example:
client := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
store := livetemplate.NewRedisSessionStore(client,
livetemplate.WithSessionTTL(24*time.Hour),
)
func (*RedisSessionStore) Close ¶
func (s *RedisSessionStore) Close() error
Close closes the Redis client connection and stops the refresh worker. Should be called when shutting down the application.
func (*RedisSessionStore) Delete ¶
func (s *RedisSessionStore) Delete(ctx context.Context, groupID string)
Delete removes a session group and all its state. The context is used for Redis operations and can timeout/cancel requests.
func (*RedisSessionStore) Get ¶
func (s *RedisSessionStore) Get(ctx context.Context, groupID string) interface{}
Get retrieves the state for a session group. Returns nil if the group doesn't exist or if deserialization fails. Automatically refreshes the TTL on successful access. The context is used for Redis operations and can timeout/cancel requests.
Note: RedisSessionStore now stores state as JSON-encoded data.
func (*RedisSessionStore) List ¶
func (s *RedisSessionStore) List(ctx context.Context) []string
List returns all active session group IDs. Used for broadcasting and cleanup operations. The context is used for Redis operations and can timeout/cancel requests.
func (*RedisSessionStore) Ping ¶
func (s *RedisSessionStore) Ping() error
Ping checks if the Redis connection is healthy. Used for health check integration.
func (*RedisSessionStore) PingContext ¶ added in v0.1.3
func (s *RedisSessionStore) PingContext(ctx context.Context) error
PingContext pings the Redis server with the given context. The context can be used to set timeouts or cancel the operation early.
type RedisSessionStoreOption ¶
type RedisSessionStoreOption func(*RedisSessionStore)
RedisSessionStoreOption configures RedisSessionStore
func WithMaxRetries ¶
func WithMaxRetries(maxRetries int) RedisSessionStoreOption
WithMaxRetries sets the maximum number of retry attempts for Redis operations. Default: 3
func WithRetryDelay ¶
func WithRetryDelay(delay time.Duration) RedisSessionStoreOption
WithRetryDelay sets the base delay between retry attempts. Actual delay uses exponential backoff: delay * 2^attempt Default: 100ms
func WithSessionTTL ¶
func WithSessionTTL(ttl time.Duration) RedisSessionStoreOption
WithSessionTTL sets the time-to-live for session groups. Sessions not accessed within this duration will be automatically expired by Redis. Default: 24 hours
type ResponseMetadata ¶
type ResponseMetadata = send.ResponseMetadata
ResponseMetadata contains information about the action that generated the update. This is an alias for internal/send.ResponseMetadata for backward compatibility.
type Session ¶ added in v0.5.0
type Session interface {
// TriggerAction dispatches the action to the matching controller
// method on every connection in the session group, then sends the
// updated template to each of those connections.
//
// This behaves identically to client-initiated actions: the action
// runs through the controller's action method, errors are captured,
// and diffs are sent over WebSocket to each connection.
//
// Example:
// session.TriggerAction("tick", nil)
// session.TriggerAction("new_notification", map[string]interface{}{"id": 123})
TriggerAction(action string, data map[string]interface{}) error
}
Session allows controllers to trigger server-initiated actions for connected clients. Actions triggered via Session affect every connection in the same session group (all tabs sharing one browser session, plus any additional devices that the configured Authenticator places in the same group).
This is the recommended way to implement:
- Timers and ticks
- Background job completion notifications
- Webhook-triggered updates
- Cross-tab synchronization
Scope: Session is scoped to a session group (groupID), not to a user identity (userID). For the typical anonymous flow where each browser session maps to one group via cookie, this is equivalent to "all tabs of this browser". For authenticated flows the mapping depends on how the Authenticator assigns groupIDs — a user with multiple devices may share a group across devices (by returning a stable groupID keyed on userID) or may have per-device groups (by returning a per-session groupID). Session.TriggerAction always targets the group of the Context it was obtained from, never other groups.
type SessionStore ¶
type SessionStore interface {
// Get retrieves the state for a session group.
// Returns nil if the group doesn't exist.
// The context can be used for cancellation, timeouts, and tracing.
Get(ctx context.Context, groupID string) interface{}
// Set stores state for a session group.
// Creates a new group if it doesn't exist, updates if it does.
// The context can be used for cancellation, timeouts, and tracing.
Set(ctx context.Context, groupID string, state interface{})
// Delete removes a session group and all its state.
// The context can be used for cancellation, timeouts, and tracing.
Delete(ctx context.Context, groupID string)
// List returns all active session group IDs.
// Used for broadcasting and cleanup operations.
// The context can be used for cancellation, timeouts, and tracing.
List(ctx context.Context) []string
}
SessionStore manages session groups, where each group contains Stores shared across connections.
A session group is the fundamental isolation boundary: all connections with the same groupID share the same Stores instance. Different groupIDs have completely isolated state.
For anonymous users: groupID is typically a browser-based identifier (all tabs share state). For authenticated users: groupID is typically the userID (each user has isolated state).
Thread-safety: All implementations must be safe for concurrent access from multiple goroutines.
type SessionStoreHealthChecker ¶
type SessionStoreHealthChecker struct {
// contains filtered or unexported fields
}
SessionStoreHealthChecker implements HealthChecker for SessionStore.
Checks that the session store is accessible by attempting to get a non-existent session (should return nil without error).
func NewSessionStoreHealthChecker ¶
func NewSessionStoreHealthChecker(store SessionStore) *SessionStoreHealthChecker
NewSessionStoreHealthChecker creates a health checker for a SessionStore.
type SessionStoreOption ¶
type SessionStoreOption func(*MemorySessionStore)
SessionStoreOption configures MemorySessionStore
func WithCleanupInterval ¶ added in v0.1.3
func WithCleanupInterval(interval time.Duration) SessionStoreOption
WithCleanupInterval sets how often the cleanup process runs. Lower intervals = more frequent cleanup but more CPU usage. Higher intervals = less frequent cleanup but potentially more memory usage. Default: 1 hour
func WithCleanupTTL ¶
func WithCleanupTTL(ttl time.Duration) SessionStoreOption
WithCleanupTTL sets the time-to-live for inactive session groups. Groups not accessed within this duration will be automatically cleaned up. Default: 24 hours
type State ¶ added in v0.7.0
type State interface {
encoding.BinaryMarshaler
encoding.BinaryUnmarshaler
Inner() any // Returns the underlying value for framework use
}
State is the interface for session state that can be persisted. The serialization requirement ensures state contains only pure data. Use AsState[T]() for zero-boilerplate implementation.
Controllers hold dependencies (DB, Logger, etc.) and are never cloned. State holds pure data and is cloned per session via serialization.
func AsState ¶ added in v0.7.0
AsState wraps a plain struct pointer to satisfy the State interface. Panics if the state type contains dependency fields (e.g., *sql.DB, *slog.Logger) that belong in the controller. Checks direct fields, nested structs, pointer-to-struct fields, and slice/array/map element types. Uses JSON serialization by default. For custom serialization, implement the State interface directly on your type.
The check is best-effort: it matches a fixed set of known stdlib dependency types. Custom wrappers (e.g., type AppDB struct{ *sql.DB }) or third-party types (e.g., *pgxpool.Pool) are not caught. Use AssertPureState[T]() in tests for stricter validation.
Example:
state := AsState(&TodoState{})
handler := tmpl.Handle(&TodoController{DB: db}, state)
type Template ¶
type Template struct {
// contains filtered or unexported fields
}
Template represents a live template with caching and tree-based optimization capabilities. It provides an API similar to html/template.Template but with additional ExecuteUpdates method for generating tree-based updates that can be efficiently transmitted to clients.
func Must ¶ added in v0.3.0
Must is a helper that wraps a call to New and panics if the error is non-nil. It is intended for use in variable initializations and startup code where template initialization failures should be fatal, such as:
var t = livetemplate.Must(livetemplate.New("app"))
This follows the same pattern as html/template.Must and text/template.Must.
func (*Template) Clone ¶
Clone creates a deep copy of the template with fresh state. This is useful for creating per-connection template instances that don't interfere with each other.
func (*Template) Execute ¶
Execute applies a parsed template to the specified data object, writing the output to wr. It orchestrates all 5 phases:
Phase 1: Parse (already done via Parse/ParseFiles/ParseGlob) Phase 2: Build - Generate tree structure Phase 3: Diff - Compare with cached state (no-op for first render) Phase 4: Render - Execute template to HTML Phase 5: Send - Write HTML response
Note: Phases execute in order 1→4→5→2 (Render before Build) to minimize response latency. Tree building for caching happens after sending the response.
The optional messages parameter provides context for templates via the lvt namespace. It contains both field validation errors and flash messages (prefixed with "_flash:"). Field errors affect ResponseMetadata.Success; flash messages don't.
func (*Template) ExecuteUpdates ¶
func (t *Template) ExecuteUpdates(wr io.Writer, data interface{}, messages ...map[string]string) error
ExecuteUpdates generates a tree structure of static and dynamic content that can be used by JavaScript clients to update changed parts efficiently. It orchestrates all 5 phases:
Phase 1: Parse (already done via Parse/ParseFiles/ParseGlob) Phase 2: Build - Generate tree structure (includes Phase 3: Diff internally) Phase 3: Diff - Compare with cached tree, return only changes (integrated in Build) Phase 4: Render - Execute template (integrated in Build) Phase 5: Send - Write JSON tree response
Caching behavior: - First call: Returns complete tree with static structure ("s" key) and dynamic values - Subsequent calls: Returns only dynamic values that have changed (cache-aware)
The optional messages parameter provides context for templates via the lvt namespace. It contains both field validation errors and flash messages (prefixed with "_flash:"). Field errors affect ResponseMetadata.Success; flash messages don't.
func (*Template) Funcs ¶
Funcs registers a template.FuncMap that will be applied to all template parsing and execution.
func (*Template) Handle ¶
func (t *Template) Handle(controller interface{}, state State, opts ...HandleOption) LiveHandler
Handle creates an http.Handler using the Controller+State pattern.
Controller: Singleton that holds dependencies (DB, Logger, etc.). Never cloned. State: Pure data that is cloned per session via serialization. Must be wrapped with AsState().
The Controller+State separation ensures dependencies are never accidentally shared across sessions while pure state data is cloned via serialization.
Example:
handler := tmpl.Handle(
&TodoController{DB: db, Logger: logger},
AsState(&TodoState{}),
)
http.Handle("/todos", handler)
Lifecycle methods (all optional):
- Mount(state, ctx) - Called once when session is created
- OnConnect(state, ctx) - Called on WebSocket connect/reconnect
- OnDisconnect() - Called on WebSocket disconnect
Action methods have signature: func(state StateType, ctx *Context) (StateType, error)
func (*Template) Parse ¶
Parse parses text as a template body for the template t. This matches the signature of html/template.Template.Parse().
func (*Template) ParseFiles ¶
ParseFiles parses the named files and associates the resulting templates with t. This matches the signature of html/template.Template.ParseFiles().
func (*Template) ParseGlob ¶
ParseGlob parses the template definitions from the files identified by the pattern. This matches the signature of html/template.Template.ParseGlob().
func (*Template) SetUploadRegistry ¶ added in v0.3.1
func (t *Template) SetUploadRegistry(registry interface{})
SetUploadRegistry sets the upload registry for this template instance. This should be called after cloning a template for a specific connection.
type TemplateSet ¶ added in v0.7.1
type TemplateSet struct {
// FS is the embedded filesystem containing the template files.
FS embed.FS
// Pattern is the glob pattern for matching template files within FS.
// Examples: "templates/*.tmpl", "*.tmpl"
Pattern string
// Namespace identifies the component type for this template set.
// Used for documentation and debugging purposes.
// Example: "dropdown" for templates like "lvt:dropdown:searchable:v1"
Namespace string
// Funcs provides additional template functions for this component.
// These are merged with the base template functions when parsing.
Funcs template.FuncMap
}
TemplateSet represents a collection of embedded templates from a component library. Components create TemplateSet instances to expose their templates for registration.
Example usage in a component library:
package dropdown
import "embed"
//go:embed templates/*.tmpl
var templateFS embed.FS
func Templates() *livetemplate.TemplateSet {
return &livetemplate.TemplateSet{
FS: templateFS,
Pattern: "templates/*.tmpl",
Namespace: "dropdown",
}
}
Example usage in main.go:
tmpl, err := livetemplate.New("app",
livetemplate.WithComponentTemplates(dropdown.Templates(), tabs.Templates()),
)
type UpdateResponse ¶
type UpdateResponse = send.UpdateResponse
UpdateResponse wraps a tree update with metadata for form lifecycle. Tree is an opaque type representing the update payload - the client library handles this automatically. This is an alias for internal/send.UpdateResponse for backward compatibility.
type UploadAccessor ¶ added in v0.7.0
type UploadAccessor interface {
HasUploads(name string) bool
GetCompletedUploads(name string) []*uploadtypes.UploadEntry
}
UploadAccessor provides access to upload entries during action handling.
type UploadConfig ¶ added in v0.3.1
type UploadConfig = uploadtypes.UploadConfig
UploadConfig configures file upload behavior for a specific upload field. It mirrors Phoenix LiveView's allow_upload/3 configuration pattern.
Fields:
- Accept: Allowed MIME types or extensions (e.g., []string{"image/*", ".pdf"})
- MaxEntries: Maximum number of concurrent files (0 = unlimited)
- MaxFileSize: Maximum file size in bytes (0 = unlimited)
- AutoUpload: Whether to start upload automatically on file selection
- ChunkSize: Chunk size for WebSocket uploads in bytes (default: 256KB)
- External: Optional Presigner for direct-to-storage uploads (S3, GCS, etc.)
type UploadEntry ¶ added in v0.3.1
type UploadEntry = uploadtypes.UploadEntry
UploadEntry represents a single file upload with its state and metadata. Exposed to templates via .lvt.Uploads(name).
Fields:
- ID: Unique identifier for this upload entry
- ClientName: Original filename from the client
- ClientType: MIME type reported by the client
- ClientSize: File size in bytes
- Progress: Upload progress percentage (0-100)
- Valid: Whether the upload passed validation
- Done: Whether the upload has completed
- Error: Error message if validation or upload failed
- TempPath: Server-side temporary file path (server uploads only)
- BytesRecv: Bytes received so far (for progress tracking)
- ExternalRef: Final storage reference (external uploads only, e.g., S3 URL)
- CreatedAt: When the upload entry was created
- CompletedAt: When the upload completed (zero if not done)
type UploadMeta ¶ added in v0.3.1
type UploadMeta = uploadtypes.UploadMeta
UploadMeta contains presigned upload configuration for external storage.
Fields:
- Uploader: Client-side uploader identifier (e.g., "s3", "gcs", "azure")
- URL: Presigned upload endpoint URL
- Fields: Form fields for multipart/form-data POST requests (optional)
- Headers: HTTP headers required for the upload request (e.g., Content-Type)
type WSCloseError ¶ added in v0.8.5
WSCloseError represents a WebSocket close message.
func (*WSCloseError) Error ¶ added in v0.8.5
func (e *WSCloseError) Error() string
type WSConn ¶ added in v0.8.5
type WSConn interface {
ReadMessage() (messageType int, p []byte, err error)
WriteMessage(messageType int, data []byte) error
Close() error
}
WSConn is the interface for a WebSocket connection. Implementations can wrap gorilla/websocket, gws, gobwas/ws, coder/websocket, or any other WebSocket library.
type WSUpgrader ¶ added in v0.8.5
type WSUpgrader interface {
Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (WSConn, error)
}
WSUpgrader upgrades an HTTP connection to a WebSocket connection.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
build
Package build handles tree building from parsed templates and data.
|
Package build handles tree building from parsed templates and data. |
|
context
Package context provides template execution context utilities for the LiveTemplate library.
|
Package context provides template execution context utilities for the LiveTemplate library. |
|
diff
Package diff provides tree comparison and differential update generation for LiveTemplate.
|
Package diff provides tree comparison and differential update generation for LiveTemplate. |
|
fuzz/app
Package app provides application-level state and operations for fuzz testing.
|
Package app provides application-level state and operations for fuzz testing. |
|
fuzz/generators
Package generators provides random data generation for fuzz testing.
|
Package generators provides random data generation for fuzz testing. |
|
fuzz/mutations
Package mutations provides state mutation types and operations for fuzz testing.
|
Package mutations provides state mutation types and operations for fuzz testing. |
|
jsonutil
Package jsonutil provides shared json-iterator configuration used across internal packages.
|
Package jsonutil provides shared json-iterator configuration used across internal packages. |
|
keys
Package keys provides key generation for LiveTemplate.
|
Package keys provides key generation for LiveTemplate. |
|
observe
Package observe provides operational metrics for LiveTemplate.
|
Package observe provides operational metrics for LiveTemplate. |
|
render
Package render provides HTML rendering utilities for LiveTemplate.
|
Package render provides HTML rendering utilities for LiveTemplate. |
|
send
Package send provides message formatting and serialization for LiveTemplate.
|
Package send provides message formatting and serialization for LiveTemplate. |
|
session
Package session provides connection and session management for LiveTemplate.
|
Package session provides connection and session management for LiveTemplate. |
|
util
Package util provides generic utility functions that can be used across the livetemplate codebase.
|
Package util provides generic utility functions that can be used across the livetemplate codebase. |