devconsole

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Aug 18, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package devconsole serves the pw dev web console: one loopback listener holding an index and every pane.

The console is host-side tooling. It reads the project tree and what pw itself started, and it never asks the application process for anything, so a pane keeps answering while the application is stopped — which is most of the time between two working states.

Index

Constants

View Source
const PanePrefixHeader = "X-Pw-Pane-Prefix"

PanePrefixHeader tells a pane where it is mounted.

A pane serves itself and cannot know: the console strips its prefix before the request arrives, so a link the pane writes as an absolute path resolves against the console root and misses. Rather than have every pane guess, the mount says so, and a pane that sees this header prefixes its own links and offers the way back to the console.

Variables

This section is empty.

Functions

func AssetPane

func AssetPane(source AssetSource) http.Handler

AssetPane builds the static asset pane over source.

func TextPane

func TextPane(title, summary string, run func(context.Context) (string, error)) http.Handler

TextPane shows the output of a pw subcommand, unaltered.

It exists because policy:dev-console-boundary admits an action only where a subcommand already offers it, and the most faithful way to honour that is to run the command and show what it said. A second rendering of the same report would be a second thing to keep true, and it would disagree with the terminal the first time either changed.

The output keeps its own layout, which is what the command spent its effort on: api:cli-doctor orders findings by severity and names a remedy for each, and reflowing that into a table would lose the ordering it means.

Types

type AssetSource

type AssetSource struct {
	// Root is the project root; public/ and the Tailwind paths resolve from it.
	Root string
	// Mount is the configured public mount, or empty when pw could not
	// determine it. An undetermined mount is reported as undetermined rather
	// than assumed, because a wrong URL here is worse than no URL.
	Mount string
	// TailwindEnabled, TailwindInput, and TailwindOutput mirror the project
	// configuration. Input and Output are project-relative slash paths.
	TailwindEnabled bool
	TailwindInput   string
	TailwindOutput  string
	// Compressible decides precompression eligibility. It is injected rather
	// than reimplemented so that the pane and the build agree by construction:
	// a pane that guessed differently from api:cli-build would be worse than
	// no pane.
	Compressible func(path string) bool
}

AssetSource is what the asset pane needs to know about the project. The caller resolves it, because locating a project root and reading its configuration is pw's job and not this package's.

type Attachment

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

Attachment is what the application published about itself — the address of a pane it serves and the URL it listens on — plus the per-run token that guards both.

The application dials out to announce; the console never dials in. That is what keeps a development pane off the application's own listener while still letting one page reach it, and it is the direction the telemetry exporter already uses.

func NewAttachment

func NewAttachment(token string) *Attachment

NewAttachment prepares one. An empty token accepts no announcement, which is what a loop that generated none should do.

func (*Attachment) Address

func (a *Attachment) Address() string

Address is where the application says it is listening, or empty before it has said so.

func (*Attachment) Handler

func (a *Attachment) Handler(what string) http.Handler

Handler proxies a pane to the attached application.

The pane exists before the application does and outlives every restart, so this is an indirection over an address filled in later. A pane whose application is down says so rather than disappearing from the console.

func (*Attachment) Listening

func (a *Attachment) Listening() string

Listening is the URL the application says it bound, or empty before it has said so.

type Console

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

Console is the listener, the panes mounted on it, and the current loop state.

func New

func New(address string, project Project, panes []Pane, attach *Attachment) (*Console, error)

New binds the console listener and serves the index and every pane on it.

The address is fixed by configuration rather than reserved, so a bound port is a real failure with a real remedy rather than something to route around.

func (*Console) CanReseed

func (c *Console) CanReseed() bool

CanReseed reports whether the action is available, for the index to decide whether to offer it.

func (*Console) Close

func (c *Console) Close()

Close stops the console with the developer loop. Nothing here is persisted, so there is nothing to flush.

func (*Console) Failed

func (c *Console) Failed(phase string, text string)

Failed is the common case of Publish: a phase that produced a diagnostic.

func (*Console) Publish

func (c *Console) Publish(phase string, status Status, diagnostic *Diagnostic)

Publish records a loop transition. It is safe on a nil console, because a console that failed to listen must not turn every phase change into a branch at the call site.

func (*Console) SetReseed

func (c *Console) SetReseed(action func(context.Context) error)

SetReseed installs the action behind the index's reseed button. Nothing is offered until it is set, so a project with no datasets shows no button.

func (*Console) State

func (c *Console) State() State

State reports what the console currently holds, for callers that render it themselves.

func (*Console) URL

func (c *Console) URL() string

type Diagnostic

type Diagnostic struct {
	Text string `json:"text"`
	// File, Line, and Column are set only when the diagnostic named a
	// location. Zero values mean it did not, not that it pointed at line zero.
	File   string `json:"file,omitempty"`
	Line   int    `json:"line,omitempty"`
	Column int    `json:"column,omitempty"`
}

Diagnostic carries a failure exactly as the terminal received it. The text is never reformatted: a rewrapped compiler error is harder to read than the original, and the developer is comparing the two.

type Pane

type Pane struct {
	// Slug is the first path segment the pane is mounted under, and the id the
	// index links to.
	Slug    string
	Title   string
	Summary string
	// Handler serves the pane below its slug, with the slug already stripped.
	// A nil handler means the pane is disabled.
	Handler http.Handler
	// DisabledBy names the configuration key that would enable a disabled
	// pane. The index says this rather than hiding the pane, so a developer
	// who expected a surface learns why it is not there.
	DisabledBy string
	// Framed wraps the pane in a page carrying the console navigation, with the
	// pane itself in an iframe.
	//
	// A pane the console renders can carry the nav directly, and does. A pane it
	// does not render cannot: the telemetry viewer is a browser application with
	// its own document, so navigating to it left the developer inside a page
	// with no way back. A frame is what puts the nav above a document the
	// console does not own, and it is worth the sizing it costs only for that
	// case.
	Framed bool
	// RootPaths are absolute console paths this pane owns outside its own
	// subtree.
	//
	// This exists for one reason. The telemetry UI is a committed build whose
	// bundle resolves its API against the document origin rather than against
	// its own base, so mounting the page under a prefix does not move its
	// fetches with it. Teaching it otherwise means rebuilding the bundle,
	// which needs the Node toolchain the embedded build exists to avoid.
	RootPaths []string
}

Pane is one surface on the console. A pane is a page rather than a region of a shared document: an embedded third-party renderer ships CSS written to own a document, and giving it one costs less than isolating it inside a shell.

func (Pane) Enabled

func (p Pane) Enabled() bool

Enabled reports whether the pane has a handler. It is exported because the index template asks each pane whether to link it or explain it.

type Project

type Project struct {
	Name string
	// Environment is the APP_ENV the loop runs the application under.
	Environment string
	// ApplicationURL is where the application listens, or empty when pw could
	// not determine it. The index reports an undetermined value as
	// undetermined rather than printing a default that may be wrong.
	ApplicationURL string
	// APIDocURL is the documentation UI the application already serves, at the
	// path its configuration puts it. The console links it rather than
	// rendering the specification itself: a second renderer would be a second
	// thing to keep current with the same document, and a link cannot disagree
	// with what the application serves.
	//
	// Empty means the endpoint is off, or that its path could not be resolved.
	APIDocURL string
	// APIDocKey names the configuration key that turns the endpoint on, for the
	// index to quote when there is no URL to link.
	APIDocKey string
}

Project is what the index says about the project itself.

type State

type State struct {
	// Build changes whenever the served application changes, so a page can
	// tell whether it is looking at a stale one. It is opaque; nothing reads
	// it for meaning.
	Build string `json:"build"`
	// Phase names the loop phase in progress or last completed, using the
	// words the terminal progress region already prints.
	Phase      string      `json:"phase"`
	Status     Status      `json:"status"`
	Diagnostic *Diagnostic `json:"diagnostic,omitempty"`
	Since      time.Time   `json:"since"`
}

State is the one record the loop publishes on every phase transition. There is exactly one current State and no history: what the loop did earlier is the terminal scrollback and the telemetry pane, and a second copy of it here would be a second thing to keep true.

The record holds no configuration value, no environment variable, and no path from outside the project.

type Status

type Status string

Status is where the developer loop currently stands.

const (
	// StatusStarting covers every phase before the application is up. It is
	// not a failure, and a page waiting on one should say so rather than
	// showing an error.
	StatusStarting Status = "starting"
	// StatusHealthy means the application process is running. It does not
	// promise that a request to it would succeed.
	StatusHealthy Status = "healthy"
	StatusFailed  Status = "failed"
)

Jump to

Keyboard shortcuts

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