server

package
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 29, 2026 License: MIT Imports: 34 Imported by: 0

Documentation

Overview

Package server provides the dev server with HTTP serving, file watching, and WebSocket live reload.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type APIError

type APIError struct {
	Code    string `json:"code"`
	Message string `json:"message"`
	File    string `json:"file,omitempty"`
}

APIError represents a structured error in the response.

type APIResponse

type APIResponse struct {
	Success bool      `json:"success"`
	Data    any       `json:"data,omitempty"`
	Error   *APIError `json:"error,omitempty"`
}

APIResponse is the standard JSON envelope.

type APIServer

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

APIServer serves the IPC JSON API for the desktop app.

func NewAPIServer

func NewAPIServer(pm *project.ProjectManager, hub *project.EventHub) *APIServer

NewAPIServer creates a new API server.

func (*APIServer) Port

func (s *APIServer) Port() int

Port returns the port the server is listening on.

func (*APIServer) Start

func (s *APIServer) Start(port int) (int, error)

Start begins listening on the given port. If port is 0, an ephemeral port is assigned. Returns the actual port being served on.

func (*APIServer) Stop

func (s *APIServer) Stop() error

Stop gracefully shuts down the API server.

func (*APIServer) Token added in v1.0.0

func (s *APIServer) Token() string

Token returns the per-launch API token. The spawner reads it from the startup handshake and must present it on every request.

type ChangeKind

type ChangeKind string

ChangeKind classifies a file system change.

const (
	ChangeContent  ChangeKind = "content"
	ChangeTemplate ChangeKind = "template"
	ChangeCSS      ChangeKind = "css"
	ChangeConfig   ChangeKind = "config"
	ChangeStatic   ChangeKind = "static"
	ChangePlugin   ChangeKind = "plugin"
)

type DevServer

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

DevServer runs the development HTTP server with live reload.

func New

func New(opts Options) *DevServer

New creates a new DevServer.

func (*DevServer) Ready

func (ds *DevServer) Ready() <-chan int

Ready reports the actual bound port: the port is sent once after the listener binds, then the channel is closed. If Start fails or the server is stopped before binding, the channel is closed without a value.

func (*DevServer) Start

func (ds *DevServer) Start() error

Start binds the listener, runs the initial build, starts the file watcher, and serves HTTP. It blocks until the server is stopped. The listener is bound up front so Ready() reports the actual port immediately; connections arriving before the initial build finishes queue in the accept backlog.

func (*DevServer) Stop

func (ds *DevServer) Stop() error

Stop gracefully shuts down the dev server. Safe to call before, during, or after Start; the server is single-use afterwards.

type FileChange

type FileChange struct {
	Path       string
	Paths      []string // all changed file paths in the batch (populated for content changes)
	Kind       ChangeKind
	DetectedAt time.Time // when fsnotify first reported this change
}

FileChange represents a detected filesystem change.

type Hub

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

Hub manages WebSocket client connections and broadcasts reload messages.

func NewHub

func NewHub() *Hub

NewHub creates a new WebSocket hub.

func (*Hub) Broadcast

func (h *Hub) Broadcast(msg ReloadMessage) int

Broadcast sends a reload message to all connected WebSocket clients. Returns the number of clients that received the message.

func (*Hub) BuildID added in v1.0.0

func (h *Hub) BuildID() int64

BuildID returns the ID of the latest successful build.

func (*Hub) BumpBuildID added in v1.0.0

func (h *Hub) BumpBuildID() int64

BumpBuildID records a new successful build and returns its ID. IDs are millisecond timestamps forced monotonic, so two builds completing within the same millisecond (or a clock step backwards) cannot collide.

func (*Hub) ClearPendingError

func (h *Hub) ClearPendingError()

ClearPendingError removes the stored build error after a successful rebuild.

func (*Hub) ClientCount

func (h *Hub) ClientCount() int

ClientCount returns the number of connected WebSocket clients.

func (*Hub) HandleWS

func (h *Hub) HandleWS(w http.ResponseWriter, r *http.Request)

HandleWS upgrades an HTTP connection to WebSocket and registers the client.

func (*Hub) SetPendingError

func (h *Hub) SetPendingError(msg *ReloadMessage)

SetPendingError stores a build error to replay to newly connecting clients.

type Options

type Options struct {
	ProjectDir     string
	OutputDir      string
	Host           string
	Port           int
	LiveReload     bool
	Version        string
	BasePath       string // normalized: "/docs/" or "/"
	BuilderFactory func() *build.SiteBuilder
	ThemeDevDirs   []string // external dirs to watch as ChangeTemplate (for --theme-dev)
	Verbose        bool     // print per-phase rebuild timings and plugin log messages
}

Options configures the dev server.

type RebuildResult

type RebuildResult struct {
	Success      bool
	Duration     time.Duration
	PageCount    int
	Warnings     []engine.ValidationWarning
	PhaseTimings []engine.PhaseTiming
	LogMessages  []engine.BuildLogEntry
	Error        error
}

RebuildResult holds the outcome of a rebuild attempt.

type Rebuilder

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

Rebuilder wraps SiteBuilder for dev-mode rebuilds. It persists the builder across content/public changes and only creates a new one when config or templates change.

Coalescing: if a rebuild is already running, incoming requests are merged into a single pending change via mergeChanges (kinds escalate, content paths union), so no change is lost while at most one follow-up rebuild runs. This prevents cascading queued rebuilds when editors emit rapid successive save events.

func NewRebuilder

func NewRebuilder(factory func() *build.SiteBuilder, projectDir string) *Rebuilder

NewRebuilder creates a Rebuilder with the given factory function. The factory is called on the first build and whenever config/template changes require a fresh SiteBuilder.

func (*Rebuilder) Rebuild

func (r *Rebuilder) Rebuild(change FileChange) (FileChange, *RebuildResult)

Rebuild runs a site build and returns the executed change with its result. Config or template changes create a fresh builder (full re-init). Content or public changes reuse the existing builder (template engine skips Load).

If a rebuild is already in progress, the change is merged into the pending slot and this call returns a nil result (the caller should skip logging/broadcasting). When the active rebuild finishes, it picks up the pending change automatically and runs it before returning; the returned change is the one the final result actually belongs to.

type ReloadMessage

type ReloadMessage struct {
	Type      ReloadType    `json:"type"`
	Path      string        `json:"path,omitempty"`
	Error     string        `json:"error,omitempty"`
	File      string        `json:"file,omitempty"`
	Line      int           `json:"line,omitempty"`
	Col       int           `json:"col,omitempty"`
	Frame     string        `json:"frame,omitempty"`
	ChangedAt int64         `json:"changedAt,omitempty"` // Unix millis when the file change was first detected
	BuildID   int64         `json:"buildId,omitempty"`   // monotonic ID of the successful build this message refers to
	Warnings  []WarningItem `json:"warnings,omitempty"`
}

ReloadMessage is sent to browsers over WebSocket.

func ToReloadMessage

func ToReloadMessage(change FileChange, result *RebuildResult, projectDir string) ReloadMessage

ToReloadMessage converts a file change and rebuild result into a ReloadMessage suitable for broadcasting to connected browsers.

type ReloadType

type ReloadType string

ReloadType classifies what kind of reload to perform.

const (
	ReloadFull    ReloadType = "reload"
	ReloadCSS     ReloadType = "css"
	ReloadError   ReloadType = "error"
	ReloadWarning ReloadType = "warning"
	// ReloadSync announces the server's latest successful build ID to a client
	// that just connected. The client compares it against the build ID embedded
	// in its page and reloads only if the page predates the build, so stale
	// tabs (reconnects, server restarts, missed broadcasts) catch up exactly
	// once and fresh tabs never reload spuriously.
	ReloadSync ReloadType = "sync"
)

type WarningItem added in v1.0.0

type WarningItem struct {
	File    string `json:"file"`
	Line    int    `json:"line"`
	Message string `json:"message"`
}

WarningItem is a single structured warning for the browser overlay.

type Watcher

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

Watcher monitors project directories for changes and triggers a callback.

func NewWatcher

func NewWatcher(projectDir, outputDir string, debounce time.Duration, onChange func([]FileChange)) *Watcher

NewWatcher creates a file watcher for the given project directory. outputDir is the build output directory (absolute or relative); changes under it are ignored so a build's own writes don't trigger a rebuild loop.

func (*Watcher) AddExternalDir

func (w *Watcher) AddExternalDir(dir string, kind ChangeKind)

AddExternalDir registers a directory outside the project tree to watch. All changes under it are classified with the given kind. Must be called before Start().

func (*Watcher) Start

func (w *Watcher) Start() error

Start begins watching project directories for changes.

func (*Watcher) Stop

func (w *Watcher) Stop()

Stop stops the file watcher.

Jump to

Keyboard shortcuts

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