Documentation
¶
Overview ¶
Package dev provides dev-mode-only helpers (livereload, debug surfaces). Everything in this package no-ops in production by design.
Index ¶
- Constants
- func DevMCPEnabled() bool
- func Enabled() bool
- func LayoutLayerEnabled() bool
- func LiveReloadEnabled() bool
- func LiveReloadHTMLInjector() router.Middleware
- func MaybeRegisterLayoutLayer(r *router.Router)
- func MaybeRegisterLiveReload(r *router.Router)
- func RegisterLayoutLayer(r *router.Router)
- func RegisterLiveReload(r *router.Router)
- func SetHeartbeatIntervalForTest(_ interface{ ... }, d time.Duration) func()
- type Withdrawal
Constants ¶
const ( LiveReloadStreamURL = "/__livereload" LiveReloadScriptURL = "/__livereload.js" )
LiveReloadStreamURL is the SSE endpoint the dev client connects to. LiveReloadScriptURL is the tiny EventSource client script.
const LayoutLayerScriptURL = "/__devlayout.js"
LayoutLayerScriptURL serves the dev layout layer: a script that draws the page's layout layers, slots, outlets, areas, owned-style scopes, islands and widgets as labeled outlines, and flags scroll boxes and elements that run past the viewport. Alt+Shift+L toggles it; the choice survives livereload's reloads.
const LiveReloadExposeEnv = "GOFASTR_DEV_LIVERELOAD_EXPOSE"
LiveReloadExposeEnv is the explicit opt-in for keeping the livereload stream on a non-loopback listener (a developer reloading a phone on the LAN). Without it, framework.App.Start withdraws the stream on such a bind (WithdrawLiveReload), and `gofastr dev`'s hold page drops its own.
Variables ¶
This section is empty.
Functions ¶
func DevMCPEnabled ¶ added in v0.25.0
func DevMCPEnabled() bool
DevMCPEnabled reports whether the dev-only MCP agent surface should auto-activate: the /mcp mount, the read-only introspection tools, the mutating control tools, and battery debug tools (battery/log) all key off this. Mirrors LiveReloadEnabled's gate:
- GOFASTR_ENV naming a production-like environment → off.
- GOFASTR_DEV must be ParseBool-truthy (set by `gofastr dev`).
- GOFASTR_DEV_MCP set to a falsy ParseBool value → opt-out even when GOFASTR_DEV is set.
- Otherwise on.
Rationale: `gofastr dev` means "a local, trusted loop where the primary consumer is the developer's own agent". The same reasoning that auto-wires livereload. Production processes never see GOFASTR_DEV, so none of this activates there.
func Enabled ¶ added in v0.40.0
func Enabled() bool
Enabled reports whether this process is running under `gofastr dev`: GOFASTR_DEV is ParseBool-truthy and GOFASTR_ENV does not name a production-like environment. This is the base predicate the per-feature gates (LiveReloadEnabled, DevMCPEnabled) refine with their own opt-outs; use it for behavior that should exist in dev and simply not exist in production (e.g. uihost strict mode's axe-coverage check, whose input is a local test artifact that never ships).
func LayoutLayerEnabled ¶ added in v0.90.0
func LayoutLayerEnabled() bool
LayoutLayerEnabled reports whether the layout layer should auto-wire: the same gate as livereload, with GOFASTR_DEV_LAYOUT=0 as its opt-out.
func LiveReloadEnabled ¶
func LiveReloadEnabled() bool
LiveReloadEnabled reports whether dev-mode livereload should auto-wire.
Rules:
- GOFASTR_ENV looking like a non-dev environment → always off (case-insensitive: production, prod, live, staging).
- GOFASTR_DEV must be set to a strconv.ParseBool truthy value (1, t, T, TRUE, true, True), typically by `gofastr dev`. Unparseable / falsy values keep livereload off so a stray GOFASTR_DEV="false" or ="no" never trips it on.
- GOFASTR_DEV_LIVERELOAD set to a falsy ParseBool value → opt-out even when GOFASTR_DEV is set.
- Otherwise on.
func LiveReloadHTMLInjector ¶ added in v0.24.0
func LiveReloadHTMLInjector() router.Middleware
LiveReloadHTMLInjector returns dev-only middleware that splices the livereload client into full HTML documents that don't already carry it. uihost pages inject the script themselves; this covers every OTHER way an app serves a page under `gofastr dev`: static.Handler file serving (the SPA and static-site shapes), widget-server pages, hand-rolled handlers that set a text/html Content-Type. So "edit → rebuild → browser refreshes" holds across the whole surface, not only uihost screens.
What is deliberately left alone:
- responses without an explicit text/html Content-Type (no sniffing);
- fragments: anything without a closing </body> (island RPC swaps, SPA-nav partials), an appended script node would corrupt the DOM the runtime swaps them into;
- encoded bodies (Content-Encoding): opaque bytes even when labeled HTML;
- HEAD and Range requests: http.ServeFile/ServeContent semantics (real Content-Length with no body, 206 byte ranges) must survive;
- streams: SSE and anything the handler Flushes pass through unbuffered, and a Flush mid-HTML abandons injection so progressive rendering keeps painting;
- hijacked connections (WebSocket upgrades): Hijack is forwarded and the wrapper never writes afterwards.
func MaybeRegisterLayoutLayer ¶ added in v0.90.0
MaybeRegisterLayoutLayer calls RegisterLayoutLayer only when LayoutLayerEnabled reports true. framework.NewApp calls it beside MaybeRegisterLiveReload.
func MaybeRegisterLiveReload ¶
MaybeRegisterLiveReload calls RegisterLiveReload only if LiveReloadEnabled() returns true. Used by framework.NewApp to auto-wire without forcing the route on every test that constructs a router.
func RegisterLayoutLayer ¶ added in v0.90.0
RegisterLayoutLayer installs the layout layer's script route on r and adds the script to the dev injector, so pages uihost does not render get it too. Idempotent, and a no-op when the host already serves the URL itself.
func RegisterLiveReload ¶
RegisterLiveReload installs the SSE endpoint and client script on r. Idempotent: safe to call multiple times on the same router. If the router already has either pattern registered (e.g. the host wired a hand-rolled livereload before upgrading), the call is a no-op so the upgrade path doesn't panic on http.ServeMux duplicate-pattern.
func SetHeartbeatIntervalForTest ¶
SetHeartbeatIntervalForTest swaps the SSE heartbeat interval and returns a restore function. For tests only. Production callers must use the default. Pass *testing.T to make the intent unambiguous.
Types ¶
type Withdrawal ¶ added in v0.96.0
type Withdrawal int
Withdrawal is what WithdrawLiveReload did.
const ( // NotRegistered: r has no livereload; nothing to withdraw. NotRegistered Withdrawal = iota // Withdrawn: this package's stream on r now answers 404. Withdrawn // HostOwned: r served its own /__livereload before // RegisterLiveReload ran, so this package installed nothing and the // host's handler is still live. Withdrawing it is the host's call. HostOwned )
func WithdrawLiveReload ¶ added in v0.96.0
func WithdrawLiveReload(r *router.Router) Withdrawal
WithdrawLiveReload turns the livereload stream on r off: from now on it answers 404, which an EventSource treats as final, so the page stops asking. framework.App.Start calls it on a non-loopback bind: the stream has no seat and no cap, and its single-operator posture does not hold for every peer that can reach the address. The result says whether a stream was withdrawn, and when one was not, why.